MSWindowsFileSystem.avt

Переключить прокрутку окна
Загрузить этот исходный код

/*
    Исходный код среды исполнения ПВТ-ОО.

    Этот исходный код является частью проекта ПВТ-ОО.

    Copyright © 2021 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
*/

package platform.dependent;

import avt.io.*;
import avt.io.extension.*;
import platform.independent.filesystem.*;

package service MSWindowsFileSystem(Object, FileSystem, MSWindowsFileSystemStreamFeatures)
{
    public static final int MAX_PATH = 260;

    public static boolean isDriveLetter(char c) { return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'; }

    static long toFSObjectTime(long osObjectTime) { return osObjectTime < 0L ? Long.MIN_VALUE : osObjectTime / 10000L + 0x2debe1767000L; }

    static long toOSObjectTime(long fsObjectTime) { return fsObjectTime < 0x2debe1767000L || fsObjectTime > 0x000374c83ed9f865L ? Long.MIN_VALUE : (fsObjectTime - 0x2debe1767000L) * 10000L; }

    static String toFSObjectName(String osObjectName) {
        StringBuilder result = new StringBuilder(osObjectName.length) + osObjectName;
        for(int lim = result.length - 1, int i = 0; i < lim; i++) if(result[i] == '$')
        {
            switch(result[i + 1])
            {
            case '$':
                break;
            case 'C':
            case 'c':
                result[i] = ':';
                break;
            case 'S':
            case 's':
                result[i] = '*';
                break;
            case '2':
                result[i] = '?';
                break;
            case 'Q':
            case 'q':
                result[i] = '\"';
                break;
            case 'L':
            case 'l':
                result[i] = '<';
                break;
            case 'G':
            case 'g':
                result[i] = '>';
                break;
            case 'I':
            case 'i':
                result[i] = '|';
                break;
            default:
                continue;
            }
            result.delete(i + 1);
        }
        return result.toString();
    }

    static String toOSObjectName(String fsObjectName) {
        StringBuilder result = new StringBuilder(fsObjectName.length << 1) + fsObjectName;
        for(int i = result.length; i-- > 0; )
        {
            switch(result[i])
            {
            case ':':
                result.insert(i + 1, 'C');
                break;
            case '*':
                result.insert(i + 1, 'S');
                break;
            case '?':
                result.insert(i + 1, '2');
                break;
            case '\"':
                result.insert(i + 1, 'Q');
                break;
            case '<':
                result.insert(i + 1, 'L');
                break;
            case '>':
                result.insert(i + 1, 'G');
                break;
            case '|':
                result.insert(i + 1, 'I');
                break;
            case '$':
                result.insert(i + 1, '$');
                /* fall through */
            default:
                continue;
            }
            result[i] = '$';
        }
        return result.toString();
    }

    private static String toOSObjectName(String fsObjectName, String argumentName) throws DirectoryNotFoundException { return toOSObjectName(fsObjectName, argumentName, false); }

    private static String toOSObjectName(String fsObjectName, String argumentName, boolean endingSolidusEnabled) throws DirectoryNotFoundException {
        /*Возвращаемое значение будет одного из следующих видов:
            "" - папка «Компьютер» («Мой компьютер» - в ранних версиях Windows) (рассматривается сама эта системная папка) (невозможно, если endingSolidusEnabled == true)
            "<том>:" - том диска
            "<том>:\\<папка>\\<объект>" - любой объект тома
          Если endingSolidusEnabled == true, то возможны ещё и эти виды возвращаемых значений:
            "*" - содержимое папки «Компьютер» («Мой компьютер» - в ранних версиях Windows) (рассматриваются только тома дисков и воображаемый элемент ".")
            "<том>:\\*" - содержимое тома диска (включая так же и воображаемые элементы "." и "..")
            "<том>:\\<папка>\\*" - содержимое любой папки тома
        */
        if(fsObjectName == null)
        {
            throw new NullPointerException((new StringBuilder() + "аргумент " + argumentName + " равен нулевой ссылке").toString());
        }
        if(fsObjectName.isEmpty())
        {
            throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " пуст").toString());
        }
        if(!fsObjectName.startsWith("/"))
        {
            throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " не начинается символом \'/\'").toString());
        }
        if(fsObjectName.equals("/"))
        {
            return endingSolidusEnabled ? "*" : "";
        }
        int fsObjectNameLength = fsObjectName.length;
        if(fsObjectNameLength > MAX_PATH)
        {
            throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " имеет очень большую длину").toString());
        }
        if(fsObjectName.indexOf('\0') >= 0 || fsObjectName.indexOf('\\') >= 0)
        {
            throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " содержит недопустимые символы").toString());
        }
        int pathLength = 0;
        String[] pathElements = new String[MAX_PATH];
        for(int fsObjectNameBeginIndex = 0; fsObjectNameBeginIndex < fsObjectNameLength; )
        {
            int fsObjectNameEndIndex = fsObjectName.indexOf('/', ++fsObjectNameBeginIndex);
            if(fsObjectNameEndIndex < 0) fsObjectNameEndIndex = fsObjectNameLength;
            pathElements[pathLength++] = fsObjectName.substring(fsObjectNameBeginIndex, fsObjectNameEndIndex);
            fsObjectNameBeginIndex = fsObjectNameEndIndex;
        }
        boolean endingSolidusPresent = pathLength > 0 && pathElements[pathLength - 1].isEmpty();
        if(!endingSolidusEnabled && endingSolidusPresent)
        {
            throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " завершается символом \'/\'").toString());
        }
        for(int i = pathLength; i-- > 0; )
        {
            String pathElement = pathElements[i];
            if(i < pathLength - 1 && pathElement.isEmpty())
            {
                throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " имеет недопустимое значение").toString());
            }
            if(pathElement.equals(".")) Array.copy(pathElements, i + 1, pathElements, i, --pathLength - i);
        }
        char[] resultChars = new char[MAX_PATH];
        long resultPointer = resultChars.getPointer();
        StringBuilder fspath = new StringBuilder(MAX_PATH);
        StringBuilder result = new StringBuilder(MAX_PATH << 1);
        for(int fspathLastBeginIndex = 0, int fspathLastEndIndex = 0, int resultLastBeginIndex = 0, int resultLastEndIndex = 0, int i = 0; i < pathLength; i++)
        {
            String pathElement = pathElements[i];
            if(pathElement.equals(".."))
            {
                if(resultLastEndIndex <= 0)
                {
                    throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " имеет недопустимое значение").toString());
                }
                if(resultLastEndIndex >= MAX_PATH)
                {
                    throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " не удалось преобразовать в допустимое внутреннее имя объекта").toString());
                }
                result.getChars(0, resultLastEndIndex, resultChars, 0);
                if(resultLastEndIndex <= 2)
                {
                    resultChars[resultLastEndIndex] = '\\';
                    resultChars[resultLastEndIndex + 1] = '\0';
                    if(Kernel32.getDiskFreeSpaceEx(resultPointer)[0] == 0L)
                    {
                        throw new DirectoryNotFoundException("папка не найдена") { directoryName = fspath.toString() };
                    }
                } else
                {
                    resultChars[resultLastEndIndex] = '\0';
                    int resultAttributes = Kernel32.getFileAttributes(resultPointer);
                    if(resultAttributes == Kernel32.INVALID_FILE_ATTRIBUTES || (resultAttributes & Kernel32.FILE_ATTRIBUTE_DIRECTORY) == 0)
                    {
                        throw new DirectoryNotFoundException("папка не найдена") { directoryName = fspath.toString() };
                    }
                }
                fspath.delete(fspathLastBeginIndex, fspathLastEndIndex);
                fspathLastEndIndex = fspathLastBeginIndex;
                fspathLastBeginIndex = fspath.lastIndexOf('/');
                result.delete(resultLastBeginIndex, resultLastEndIndex);
                resultLastEndIndex = resultLastBeginIndex;
                if((resultLastBeginIndex = result.lastIndexOf('\\')) < 0) resultLastBeginIndex = 0;
                continue;
            }
            if(resultLastEndIndex > 0)
            {
                fspathLastBeginIndex = fspathLastEndIndex;
                fspathLastEndIndex = (fspath + '/' + pathElement).length;
                resultLastBeginIndex = resultLastEndIndex;
                resultLastEndIndex = (result + '\\' + (pathElement.isEmpty() ? "*" : toOSObjectName(pathElement))).length;
                continue;
            }
            if(pathElement.isEmpty())
            {
                fspathLastEndIndex = (fspath + '/').length;
                resultLastEndIndex = (result + '*').length;
                continue;
            }
            if(pathElement.length != 2 || !isDriveLetter(pathElement[0]) || pathElement[1] != ':')
            {
                throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " имеет недопустимое значение").toString());
            }
            fspathLastEndIndex = (fspath + '/' + pathElement).length;
            resultLastEndIndex = (result + pathElement).length;
        }
        if(result.length >= MAX_PATH)
        {
            throw new IllegalArgumentException((new StringBuilder() + "аргумент " + argumentName + " не удалось преобразовать в допустимое внутреннее имя объекта").toString());
        }
        return endingSolidusEnabled && result.isEmpty() ? "*" : result.toString();
    }


    public void readAttributes(String objectName, ObjectAttributes objectAttr) throws ObjectNotFoundException, IOException {
        String objectNameOSSpecific = toOSObjectName(objectName, "objectName");
        int objectNameOSSpecificLength = objectNameOSSpecific.length;
        if(objectNameOSSpecificLength <= 0)
        {
            if(objectAttr != null)
            {
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_DIRECTORY)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, true);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_READ_ONLY)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, false);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_ARCHIVE)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, false);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_HIDDEN)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, false);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_SYSTEM)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, true);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME)) objectAttr.setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, Long.MIN_VALUE);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME)) objectAttr.setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, Long.MIN_VALUE);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME)) objectAttr.setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, Long.MIN_VALUE);
            }
            return;
        }
        char[] objectNameOSSpecificChars = new char[MAX_PATH];
        long objectNameOSSpecificPointer = objectNameOSSpecificChars.getPointer();
        objectNameOSSpecific.getChars(0, objectNameOSSpecificLength, objectNameOSSpecificChars, 0);
        if(objectNameOSSpecificLength <= 2)
        {
            objectNameOSSpecificChars[objectNameOSSpecificLength] = '\\';
            if(Kernel32.getDiskFreeSpaceEx(objectNameOSSpecificPointer)[0] == 0L)
            {
                throw new ObjectNotFoundException("объект не найден") { objectName = objectName };
            }
            if(objectAttr != null)
            {
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_DIRECTORY)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, true);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_READ_ONLY)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, false);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_ARCHIVE)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, true);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_HIDDEN)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, false);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_SYSTEM)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, true);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME)) objectAttr.setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, Long.MIN_VALUE);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME)) objectAttr.setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, Long.MIN_VALUE);
                if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME)) objectAttr.setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, Long.MIN_VALUE);
            }
            return;
        }
        Win32FindData findData = new Win32FindData();
        long findHandle = Kernel32.findFirstFile(objectNameOSSpecificPointer, findData.getPointer());
        if(findHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ObjectNotFoundException("объект не найден") { objectName = objectName };
        }
        if(Kernel32.findClose(findHandle) == Kernel32.NULL)
        {
            throw new IOException("ошибка ввода-вывода возникла");
        }
        if(objectAttr != null)
        {
            int attributes = findData.dwFileAttributes;
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_DIRECTORY)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, (attributes & Kernel32.FILE_ATTRIBUTE_DIRECTORY) != 0);
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_READ_ONLY)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, (attributes & Kernel32.FILE_ATTRIBUTE_READONLY) != 0);
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_ARCHIVE)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, (attributes & Kernel32.FILE_ATTRIBUTE_ARCHIVE) != 0);
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_HIDDEN)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, (attributes & Kernel32.FILE_ATTRIBUTE_HIDDEN) != 0);
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_SYSTEM)) objectAttr.setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, (attributes & Kernel32.FILE_ATTRIBUTE_SYSTEM) != 0);
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME)) objectAttr.setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, toFSObjectTime(findData.ftCreationTime));
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME)) objectAttr.setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, toFSObjectTime(findData.ftLastWriteTime));
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME)) objectAttr.setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, toFSObjectTime(findData.ftLastAccessTime));
        }
    }

    public void writeAttributes(String objectName, ObjectAttributes objectAttr) throws ReadOnlyFileSystemException, ObjectNotFoundException, ObjectWriteAttributesException, IOException {
        String objectNameOSSpecific = toOSObjectName(objectName, "objectName");
        if(objectAttr == null)
        {
            throw new NullPointerException("аргумент objectAttr равен нулевой ссылке");
        }
        int objectNameOSSpecificLength = objectNameOSSpecific.length;
        if(objectNameOSSpecificLength <= 2)
        {
            throw new ObjectWriteAttributesException("ошибка записи атрибутов объекта") { objectName = objectName };
        }
        char[] objectNameOSSpecificChars = new char[MAX_PATH];
        long objectNameOSSpecificPointer = objectNameOSSpecificChars.getPointer();
        objectNameOSSpecific.getChars(0, objectNameOSSpecificLength, objectNameOSSpecificChars, 0);
        int attributes = Kernel32.getFileAttributes(objectNameOSSpecificPointer);
        if(attributes == Kernel32.INVALID_FILE_ATTRIBUTES)
        {
            throw new ObjectNotFoundException("объект не найден") { objectName = objectName };
        }
        long fileHandle = Kernel32.createFile(objectNameOSSpecificPointer, Kernel32.GENERIC_WRITE, 0, Kernel32.NULL, Kernel32.OPEN_EXISTING, attributes | Kernel32.FILE_FLAG_BACKUP_SEMANTICS, Kernel32.NULL);
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ObjectWriteAttributesException("ошибка записи атрибутов объекта") { objectName = objectName };
        }
        try
        {
            FileTime creationTime = new FileTime() { ftFileTime = Long.MIN_VALUE };
            FileTime lastWriteTime = new FileTime() { ftFileTime = Long.MIN_VALUE };
            FileTime lastAccessTime = new FileTime() { ftFileTime = Long.MIN_VALUE };
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME)) creationTime.ftFileTime = toOSObjectTime(objectAttr.getLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME));
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME)) lastWriteTime.ftFileTime = toOSObjectTime(objectAttr.getLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME));
            if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME)) lastAccessTime.ftFileTime = toOSObjectTime(objectAttr.getLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME));
            if(Kernel32.setFileTime(fileHandle, creationTime.getPointer(), lastAccessTime.getPointer(), lastWriteTime.getPointer()) == Kernel32.NULL)
            {
                throw new ObjectWriteAttributesException("ошибка записи атрибутов объекта") { objectName = objectName };
            }
        }
        finally
        {
            if(Kernel32.closeHandle(fileHandle) == Kernel32.NULL)
            {
                throw new IOException("ошибка ввода-вывода возникла");
            }
        }
        attributes &= ~(Kernel32.FILE_ATTRIBUTE_READONLY | Kernel32.FILE_ATTRIBUTE_HIDDEN | Kernel32.FILE_ATTRIBUTE_SYSTEM | Kernel32.FILE_ATTRIBUTE_ARCHIVE);
        if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_READ_ONLY) && objectAttr.getBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY)) attributes |= Kernel32.FILE_ATTRIBUTE_READONLY;
        if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_ARCHIVE) && objectAttr.getBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE)) attributes |= Kernel32.FILE_ATTRIBUTE_ARCHIVE;
        if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_HIDDEN) && objectAttr.getBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN)) attributes |= Kernel32.FILE_ATTRIBUTE_HIDDEN;
        if(objectAttr.isSupportedAttributeId(MSWindowsFileSystemObjectAttributes.B_SYSTEM) && objectAttr.getBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM)) attributes |= Kernel32.FILE_ATTRIBUTE_SYSTEM;
        if(Kernel32.setFileAttributes(objectNameOSSpecificPointer, attributes) == Kernel32.NULL)
        {
            throw new ObjectWriteAttributesException("ошибка записи атрибутов объекта") { objectName = objectName };
        }
    }

    public void move(String oldObjectName, String newObjectName) throws ReadOnlyFileSystemException, ObjectNotFoundException, MoveOperationException, IOException {
        String oldObjectNameOSSpecific = toOSObjectName(oldObjectName, "oldObjectName");
        String newObjectNameOSSpecific = toOSObjectName(newObjectName, "newObjectName");
        int oldObjectNameOSSpecificLength = oldObjectNameOSSpecific.length;
        int newObjectNameOSSpecificLength = newObjectNameOSSpecific.length;
        if(oldObjectNameOSSpecificLength <= 2 || newObjectNameOSSpecificLength <= 2)
        {
            throw new MoveOperationException("ошибка перемещения объекта") { oldObjectName = oldObjectName, newObjectName = newObjectName };
        }
        char[] oldObjectNameOSSpecificChars = new char[MAX_PATH];
        char[] newObjectNameOSSpecificChars = new char[MAX_PATH];
        long oldObjectNameOSSpecificPointer = oldObjectNameOSSpecificChars.getPointer();
        oldObjectNameOSSpecific.getChars(0, oldObjectNameOSSpecificLength, oldObjectNameOSSpecificChars, 0);
        newObjectNameOSSpecific.getChars(0, newObjectNameOSSpecificLength, newObjectNameOSSpecificChars, 0);
        if(Kernel32.getFileAttributes(oldObjectNameOSSpecificPointer) == Kernel32.INVALID_FILE_ATTRIBUTES)
        {
            throw new ObjectNotFoundException("объект не найден") { objectName = oldObjectName };
        }
        if(Kernel32.moveFile(oldObjectNameOSSpecificPointer, newObjectNameOSSpecificChars.getPointer()) == Kernel32.NULL)
        {
            throw new MoveOperationException("ошибка перемещения объекта") { oldObjectName = oldObjectName, newObjectName = newObjectName };
        }
    }

    public void deleteDirectory(String directoryName) throws ReadOnlyFileSystemException, DirectoryNotFoundException, DirectoryDeletionException, IOException {
        String directoryNameOSSpecific = toOSObjectName(directoryName, "directoryName");
        int directoryNameOSSpecificLength = directoryNameOSSpecific.length;
        if(directoryNameOSSpecificLength <= 2)
        {
            throw new DirectoryDeletionException("ошибка удаления папки") { directoryName = directoryName };
        }
        char[] directoryNameOSSpecificChars = new char[MAX_PATH];
        long directoryNameOSSpecificPointer = directoryNameOSSpecificChars.getPointer();
        directoryNameOSSpecific.getChars(0, directoryNameOSSpecificLength, directoryNameOSSpecificChars, 0);
        if(Kernel32.getFileAttributes(directoryNameOSSpecificPointer) == Kernel32.INVALID_FILE_ATTRIBUTES)
        {
            throw new DirectoryNotFoundException("папка не найдена") { directoryName = directoryName };
        }
        if(Kernel32.removeDirectory(directoryNameOSSpecificPointer) == Kernel32.NULL)
        {
            throw new DirectoryDeletionException("ошибка удаления папки") { directoryName = directoryName };
        }
    }

    public void deleteFile(String fileName) throws ReadOnlyFileSystemException, FileNotFoundException, FileDeletionException, IOException {
        String fileNameOSSpecific = toOSObjectName(fileName, "fileName");
        int fileNameOSSpecificLength = fileNameOSSpecific.length;
        if(fileNameOSSpecificLength <= 2)
        {
            throw new FileDeletionException("ошибка удаления файла") { fileName = fileName };
        }
        char[] fileNameOSSpecificChars = new char[MAX_PATH];
        long fileNameOSSpecificPointer = fileNameOSSpecificChars.getPointer();
        fileNameOSSpecific.getChars(0, fileNameOSSpecificLength, fileNameOSSpecificChars, 0);
        if(Kernel32.getFileAttributes(fileNameOSSpecificPointer) == Kernel32.INVALID_FILE_ATTRIBUTES)
        {
            throw new FileNotFoundException("файл не найден") { fileName = fileName };
        }
        if(Kernel32.deleteFile(fileNameOSSpecificPointer) == Kernel32.NULL)
        {
            throw new FileDeletionException("ошибка удаления файла") { fileName = fileName };
        }
    }

    public void createDirectory(String directoryName) throws ReadOnlyFileSystemException, DirectoryCreationException, IOException {
        String directoryNameOSSpecific = toOSObjectName(directoryName, "directoryName");
        int directoryNameOSSpecificLength = directoryNameOSSpecific.length;
        if(directoryNameOSSpecificLength <= 2)
        {
            throw new DirectoryCreationException("ошибка создания папки") { directoryName = directoryName };
        }
        char[] directoryNameOSSpecificChars = new char[MAX_PATH];
        directoryNameOSSpecific.getChars(0, directoryNameOSSpecificLength, directoryNameOSSpecificChars, 0);
        if(Kernel32.createDirectory(directoryNameOSSpecificChars.getPointer(), Kernel32.NULL) == Kernel32.NULL)
        {
            throw new DirectoryCreationException("ошибка создания папки") { directoryName = directoryName };
        }
    }

    public boolean isReadOnly() throws IOException { return false; }

    public boolean isObjectExist(String objectName) throws IOException {
        String objectNameOSSpecific = toOSObjectName(objectName, "objectName");
        int objectNameOSSpecificLength = objectNameOSSpecific.length;
        if(objectNameOSSpecificLength <= 0) return true;
        char[] objectNameOSSpecificChars = new char[MAX_PATH];
        long objectNameOSSpecificPointer = objectNameOSSpecificChars.getPointer();
        objectNameOSSpecific.getChars(0, objectNameOSSpecificLength, objectNameOSSpecificChars, 0);
        if(objectNameOSSpecificLength <= 2)
        {
            objectNameOSSpecificChars[objectNameOSSpecificLength] = '\\';
            return Kernel32.getDiskFreeSpaceEx(objectNameOSSpecificPointer)[0] != 0L;
        }
        return Kernel32.getFileAttributes(objectNameOSSpecificPointer) != Kernel32.INVALID_FILE_ATTRIBUTES;
    }

    public boolean isObjectNameCaseSensitive() { return false; }

    public int getObjectNameMaximumLength() { return MAX_PATH; }

    public String toInternalName(String objectName) throws IOException { return toOSObjectName(objectName, "objectName", true); }

    public ObjectEnumeration findFirst() throws ObjectNotFoundException, IOException { return new MSWindowsFileSystemDriveEnumeration(); }

    public ObjectEnumeration findFirst(String objectName) throws ObjectNotFoundException, IOException {
        String objectNameOSSpecific = toOSObjectName(objectName, "objectName", true);
        int objectNameOSSpecificLength = objectNameOSSpecific.length;
        if(objectNameOSSpecificLength <= 1)
        {
            return new MSWindowsFileSystemDriveEnumeration();
        }
        char[] objectNameOSSpecificChars = new char[MAX_PATH];
        long objectNameOSSpecificPointer = objectNameOSSpecificChars.getPointer();
        objectNameOSSpecific.getChars(0, objectNameOSSpecificLength, objectNameOSSpecificChars, 0);
        if(objectNameOSSpecificLength == 2)
        {
            objectNameOSSpecificChars[2] = '\\';
            if(Kernel32.getDiskFreeSpaceEx(objectNameOSSpecificPointer)[0] == 0L)
            {
                throw new ObjectNotFoundException("объект не найден") { objectName = objectName };
            }
            return new MSWindowsFileSystemDriveInfo(objectNameOSSpecific);
        }
        if(objectNameOSSpecificLength == 4 && objectNameOSSpecific.endsWith(":\\*"))
        {
            objectNameOSSpecificChars[3] = '\0';
            if(Kernel32.getDiskFreeSpaceEx(objectNameOSSpecificPointer)[0] == 0L)
            {
                throw new ObjectNotFoundException("объект не найден") { objectName = objectName };
            }
            return new MSWindowsFileSystemDriveRootEnumeration(objectNameOSSpecificChars, objectNameOSSpecificPointer);
        }
        Win32FindData findData = new Win32FindData();
        long findPointer = findData.getPointer();
        long findHandle = Kernel32.findFirstFile(objectNameOSSpecificPointer, findPointer);
        if(findHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ObjectNotFoundException("объект не найден") { objectName = objectName };
        }
        return new MSWindowsFileSystemObjectEnumeration(findHandle, findData, findPointer);
    }

    public ByteWriter createFile(String fileName) throws ReadOnlyFileSystemException, FileCreationException, IOException {
        String fileNameOSSpecific = toOSObjectName(fileName, "fileName");
        int fileNameOSSpecificLength = fileNameOSSpecific.length;
        if(fileNameOSSpecificLength <= 0)
        {
            throw new FileNotFoundException("файл не найден") { fileName = fileName };
        }
        char[] fileNameOSSpecificChars = new char[MAX_PATH];
        fileNameOSSpecific.getChars(0, fileNameOSSpecificLength, fileNameOSSpecificChars, 0);
        long fileHandle = Kernel32.createFile(fileNameOSSpecificChars.getPointer(), Kernel32.GENERIC_WRITE, Kernel32.NULL, Kernel32.NULL, Kernel32.CREATE_NEW, Kernel32.FILE_FLAG_RANDOM_ACCESS | Kernel32.FILE_ATTRIBUTE_ARCHIVE, Kernel32.NULL);
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new FileCreationException("не удалось создать файл") { fileName = fileName };
        }
        return new MSWindowsFileSystemOutputStream(fileHandle, CLOSEABLE | SEEKABLE | TRUNCATABLE);
    }

    public ByteWriter openFileForAppending(String fileName) throws ReadOnlyFileSystemException, FileNotFoundException, IOException {
        String fileNameOSSpecific = toOSObjectName(fileName, "fileName");
        int fileNameOSSpecificLength = fileNameOSSpecific.length;
        if(fileNameOSSpecificLength <= 0)
        {
            throw new FileNotFoundException("файл не найден") { fileName = fileName };
        }
        char[] fileNameOSSpecificChars = new char[MAX_PATH];
        fileNameOSSpecific.getChars(0, fileNameOSSpecificLength, fileNameOSSpecificChars, 0);
        long fileHandle = Kernel32.createFile(fileNameOSSpecificChars.getPointer(), Kernel32.GENERIC_WRITE, Kernel32.NULL, Kernel32.NULL, Kernel32.OPEN_EXISTING, Kernel32.FILE_FLAG_RANDOM_ACCESS | 0x7fff, Kernel32.NULL);
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            int error = Kernel32.getLastError();
            if(error == Kernel32.ERROR_FILE_NOT_FOUND || error == Kernel32.ERROR_PATH_NOT_FOUND)
            {
                throw new FileNotFoundException("файл не найден") { fileName = fileName };
            }
            throw new FileOpeningException("не удалось открыть файл") { fileName = fileName };
        }
        ByteWriter result = new MSWindowsFileSystemOutputStream(fileHandle, CLOSEABLE | SEEKABLE | TRUNCATABLE);
        ((SeekExtension) result.getExtension(SeekExtension.class)).seek(0L, SeekExtension.END);
        return result;
    }

    public ByteReader openFileForReading(String fileName) throws FileNotFoundException, IOException {
        String fileNameOSSpecific = toOSObjectName(fileName, "fileName");
        int fileNameOSSpecificLength = fileNameOSSpecific.length;
        if(fileNameOSSpecificLength <= 0)
        {
            throw new FileNotFoundException("файл не найден") { fileName = fileName };
        }
        char[] fileNameOSSpecificChars = new char[MAX_PATH];
        fileNameOSSpecific.getChars(0, fileNameOSSpecificLength, fileNameOSSpecificChars, 0);
        long fileHandle = Kernel32.createFile(fileNameOSSpecificChars.getPointer(), Kernel32.GENERIC_READ, Kernel32.FILE_SHARE_READ, Kernel32.NULL, Kernel32.OPEN_EXISTING, Kernel32.FILE_FLAG_RANDOM_ACCESS | 0x7fff, Kernel32.NULL);
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            int error = Kernel32.getLastError();
            if(error == Kernel32.ERROR_FILE_NOT_FOUND || error == Kernel32.ERROR_PATH_NOT_FOUND)
            {
                throw new FileNotFoundException("файл не найден") { fileName = fileName };
            }
            throw new FileOpeningException("не удалось открыть файл") { fileName = fileName };
        }
        return new MSWindowsFileSystemInputStream(fileHandle, CLOSEABLE | SEEKABLE);
    }

    public ByteStream openFile(String fileName) throws ReadOnlyFileSystemException, FileNotFoundException, IOException {
        String fileNameOSSpecific = toOSObjectName(fileName, "fileName");
        int fileNameOSSpecificLength = fileNameOSSpecific.length;
        if(fileNameOSSpecificLength <= 0)
        {
            throw new FileNotFoundException("файл не найден") { fileName = fileName };
        }
        char[] fileNameOSSpecificChars = new char[MAX_PATH];
        fileNameOSSpecific.getChars(0, fileNameOSSpecificLength, fileNameOSSpecificChars, 0);
        long fileHandle = Kernel32.createFile(fileNameOSSpecificChars.getPointer(), Kernel32.GENERIC_READ | Kernel32.GENERIC_WRITE, Kernel32.NULL, Kernel32.NULL, Kernel32.OPEN_EXISTING, Kernel32.FILE_FLAG_RANDOM_ACCESS | 0x7fff, Kernel32.NULL);
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            int error = Kernel32.getLastError();
            if(error == Kernel32.ERROR_FILE_NOT_FOUND || error == Kernel32.ERROR_PATH_NOT_FOUND)
            {
                throw new FileNotFoundException("файл не найден") { fileName = fileName };
            }
            throw new FileOpeningException("не удалось открыть файл") { fileName = fileName };
        }
        return new MSWindowsFileSystemFileStream(fileHandle);
    }

    public AttributeSet newAttributeSet() { return new MSWindowsFileSystemObjectAttributes(); }
}

package class MSWindowsFileSystemStream(Object, Closeable, Extendable, MSWindowsFileSystemStreamFeatures)
{
    protected long fldFileHandle;
    protected final boolean fldCloseable;
    protected final MSWindowsFileSystemStreamExtension[] fldExtensions;

    (long fileHandle, int features) {
        fldFileHandle = fileHandle;
        fldCloseable = (features & CLOSEABLE) != 0;
        switch(features & (SEEKABLE | TRUNCATABLE))
        {
        case SEEKABLE | TRUNCATABLE:
            fldExtensions = new MSWindowsFileSystemStreamExtension[] { new MSWindowsFileSystemStreamPositioningExtension(fileHandle), new MSWindowsFileSystemStreamTruncateExtension(fileHandle) };
            break;
        case TRUNCATABLE:
            fldExtensions = new MSWindowsFileSystemStreamExtension[] { new MSWindowsFileSystemStreamTruncateExtension(fileHandle) };
            break;
        case SEEKABLE:
            fldExtensions = new MSWindowsFileSystemStreamExtension[] { new MSWindowsFileSystemStreamPositioningExtension(fileHandle) };
            break;
        default:
            fldExtensions = new MSWindowsFileSystemStreamExtension[0];
        }
    }

    (long fileHandle, int features, MSWindowsFileSystemStreamExtension posExt) {
        fldFileHandle = fileHandle;
        fldCloseable = (features & CLOSEABLE) != 0;
        if((features & TRUNCATABLE) != 0)
        {
            fldExtensions = new MSWindowsFileSystemStreamExtension[] { posExt, new MSWindowsFileSystemStreamTruncateExtension(fileHandle) };
        } else
        {
            fldExtensions = new MSWindowsFileSystemStreamExtension[] { posExt };
        }
    }

    public void close() throws IOException {
        if(fldCloseable)
        {
            long fileHandle = fldFileHandle;
            if(fileHandle != Kernel32.INVALID_HANDLE_VALUE)
            {
                if(Kernel32.closeHandle(fileHandle) == Kernel32.NULL)
                {
                    throw new IOException("ошибка ввода-вывода возникла");
                }
                clearHandlers();
            }
        }
    }

    public Extension[] getExtensions() {
        Extension[] result = fldExtensions;
        int length = result.length;
        Array.copy(result, 0, result = new Extension[length], 0, length);
        return result;
    }

    protected void beforeDestruction() {
        if(fldCloseable)
        {
            long fileHandle = fldFileHandle;
            if(fileHandle != Kernel32.INVALID_HANDLE_VALUE) Kernel32.closeHandle(fileHandle);
        }
    }

    protected void clearHandlers() {
        for(fldFileHandle = Kernel32.INVALID_HANDLE_VALUE, MSWindowsFileSystemStreamExtension[] exts = fldExtensions, int i = exts.length; i-- > 0; )
        {
            exts[i].fldFileHandle = Kernel32.INVALID_HANDLE_VALUE;
        }
    }
}

package interface MSWindowsFileSystemStreamFeatures(Object)
{
    public static final int SEEKABLE    = 0x01;
    public static final int TRUNCATABLE = 0x02;
    public static final int CLOSEABLE   = 0x10;
}

package class MSWindowsFileSystemInputStream(MSWindowsFileSystemStream, ByteReader)
{
    public (long stdHandle): super(stdHandle, 0) {  }

    (long fileHandle, int features): super(fileHandle, features & (CLOSEABLE | SEEKABLE)) {  }

    (long fileHandle, int features, MSWindowsFileSystemStreamExtension posExt): super(fileHandle, features & CLOSEABLE, posExt) {  }

    public int read() throws IOException {
        long fileHandle = fldFileHandle;
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ClosedFileException("файл закрыт");
        }
        int2 result = Kernel32.readFile(fileHandle, Kernel32.NULL);
        if(result[0] == Kernel32.NULL)
        {
            throw new IOException("ошибка чтения данных из файла");
        }
        return result[1];
    }

    public int read(byte[] dst, int offset, int length) throws IOException {
        if(dst == null)
        {
            throw new NullPointerException("аргумент dst равен нулевой ссылке");
        }
        Array.checkBounds(dst, offset, length);
        long fileHandle = fldFileHandle;
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ClosedFileException("файл закрыт");
        }
        if(length <= 0) return 0;
        int2 result = Kernel32.readFile(fileHandle, dst.getPointer() + offset, length, Kernel32.NULL);
        if(result[0] == Kernel32.NULL)
        {
            throw new IOException("ошибка чтения данных из файла");
        }
        int readed = result[1];
        return readed <= 0 ? -1 : readed;
    }

    public long skip(long bytesQuantity) throws IOException {
        SeekExtension ext = (SeekExtension) getExtension(SeekExtension.class);
        if(ext == null)
        {
            return ByteReader.super.skip(bytesQuantity);
        }
        if(bytesQuantity <= 0L) return 0L;
        return -ext.position() + ext.seek(bytesQuantity, SeekExtension.CURRENT);
    }
}

package class MSWindowsFileSystemOutputStream(MSWindowsFileSystemStream, ByteWriter)
{
    public (long stdHandle): super(stdHandle, 0) {  }

    (long fileHandle, int features): super(fileHandle, features) {  }

    (long fileHandle, int features, MSWindowsFileSystemStreamExtension posExt): super(fileHandle, features, posExt) {  }

    public void write(int byteData) throws IOException {
        long fileHandle = fldFileHandle;
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ClosedFileException("файл закрыт");
        }
        int2 result = Kernel32.writeFile(fileHandle, byteData, Kernel32.NULL);
        if(result[0] == Kernel32.NULL)
        {
            throw new IOException("ошибка записи данных в файл");
        }
        if(result[1] != 1)
        {
            throw new IOException("недостаточно места на диске для записи данных в файл");
        }
    }

    public void write(byte[] src, int offset, int length) throws IOException {
        if(src == null)
        {
            throw new NullPointerException("аргумент src равен нулевой ссылке");
        }
        Array.checkBounds(src, offset, length);
        long fileHandle = fldFileHandle;
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ClosedFileException("файл закрыт");
        }
        if(length <= 0) return;
        int2 result = Kernel32.writeFile(fileHandle, src.getPointer() + offset, length, Kernel32.NULL);
        if(result[0] == Kernel32.NULL)
        {
            throw new IOException("ошибка записи данных в файл");
        }
        if(result[1] != length)
        {
            throw new IOException("недостаточно места на диске для записи данных в файл");
        }
    }

    public void flush() throws IOException {
        if(fldCloseable)
        {
            long fileHandle = fldFileHandle;
            if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
            {
                throw new ClosedFileException("файл закрыт");
            }
            if(Kernel32.flushFileBuffers(fileHandle) == Kernel32.NULL)
            {
                throw new IOException("ошибка записи данных в файл");
            }
        }
    }
}

class MSWindowsFileSystemFileStream(MSWindowsFileSystemStream, ByteStream)
{
    private final MSWindowsFileSystemInputStream fldReader;
    private final MSWindowsFileSystemOutputStream fldWriter;

    (long fileHandle): super(fileHandle, CLOSEABLE | SEEKABLE) {
        MSWindowsFileSystemStreamExtension posExt = fldExtensions[0];
        fldReader = new MSWindowsFileSystemInputStream(fileHandle, 0, posExt);
        fldWriter = new MSWindowsFileSystemOutputStream(fileHandle, TRUNCATABLE, posExt);
    }

    public ByteReader reader { read = fldReader }

    public ByteWriter writer { read = fldWriter }

    protected void clearHandlers() {
        super.clearHandlers();
        fldReader.clearHandlers();
        fldWriter.clearHandlers();
    }
}

class MSWindowsFileSystemStreamExtension(Object, Extension)
{
    protected long fldFileHandle;

    protected (long fileHandle) { fldFileHandle = fileHandle; }
}

service MSWindowsFileSystemStreamPositioningExtension(MSWindowsFileSystemStreamExtension, SeekExtension, LimitedSizeExtension)
{
    public long seek(long offset, int from) throws IOException {
        if(from < BEGIN || from > END)
        {
            throw new IllegalArgumentException("аргумент from имеет недопустимое значение");
        }
        long fileHandle = fldFileHandle;
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ClosedFileException("файл закрыт");
        }
        long filePointer = Kernel32.setFilePointer(fileHandle, offset, from);
        if(filePointer == Kernel32.INVALID_SET_FILE_POINTER)
        {
            throw new IOException("ошибка ввода-вывода возникла");
        }
        return filePointer;
    }

    public long size() throws IOException {
        long fileHandle = fldFileHandle;
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ClosedFileException("файл закрыт");
        }
        long fileSize = Kernel32.getFileSize(fileHandle);
        if(fileSize == Kernel32.INVALID_FILE_SIZE)
        {
            throw new IOException("ошибка ввода-вывода возникла");
        }
        return fileSize;
    }

    public long available() throws IOException { return size() - seek(0L, CURRENT); }
}

service MSWindowsFileSystemStreamTruncateExtension(MSWindowsFileSystemStreamExtension, TruncateExtension)
{
    public void truncate() throws IOException {
        long fileHandle = fldFileHandle;
        if(fileHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            throw new ClosedFileException("файл закрыт");
        }
        if(Kernel32.setEndOfFile(fileHandle) == Kernel32.NULL)
        {
            throw new IOException("ошибка ввода-вывода возникла");
        }
    }
}

class MSWindowsFileSystemDriveEnumeration(ObjectEnumeration)
{
    private final char[] fldDriveChars;

    public (): super(new MSWindowsFileSystemObjectAttributes(), ".", 0L) {
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, true);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, false);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, false);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, false);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, true);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, Long.MIN_VALUE);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, Long.MIN_VALUE);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, Long.MIN_VALUE);
        fldDriveChars = new char[] { 'A', ':', '\0' };
    }

    public boolean findNext() throws IOException {
        for(char[] driveChars = fldDriveChars, long drivePointer = driveChars.getPointer(); driveChars[0] <= 'Z'; )
        {
            if(Kernel32.getDiskFreeSpaceEx(drivePointer)[0] == 0L)
            {
                driveChars[0]++;
                continue;
            }
            name = new String(driveChars, 0, 2);
            size = 0L;
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, true);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, false);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, true);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, false);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, true);
            setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, Long.MIN_VALUE);
            setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, Long.MIN_VALUE);
            setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, Long.MIN_VALUE);
            driveChars[0]++;
            return true;
        }
        return false;
    }
}

class MSWindowsFileSystemDriveInfo(ObjectEnumeration)
{
    public (String driveName): super(new MSWindowsFileSystemObjectAttributes(), driveName, 0L) {
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, true);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, false);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, true);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, false);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, true);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, Long.MIN_VALUE);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, Long.MIN_VALUE);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, Long.MIN_VALUE);
    }

    public boolean findNext() throws IOException { return false; }
}

class MSWindowsFileSystemCustomEnumeration(ObjectEnumeration)
{
    protected long fldFindHandle;
    protected long fldFindPointer;
    protected Win32FindData fldFindData;

    protected (): super(new MSWindowsFileSystemObjectAttributes()) {  }

    protected (String name, long size): super(new MSWindowsFileSystemObjectAttributes(), name, size) {  }

    public void close() throws IOException {
        long findHandle = fldFindHandle;
        if(findHandle != Kernel32.INVALID_HANDLE_VALUE && Kernel32.findClose(findHandle) == Kernel32.NULL)
        {
            throw new IOException("ошибка ввода-вывода возникла");
        }
        fldFindHandle = Kernel32.INVALID_HANDLE_VALUE;
    }

    public boolean findNext() throws IOException {
        if(Kernel32.findNextFile(fldFindHandle, fldFindPointer) != Kernel32.NULL)
        {
            Win32FindData findData = fldFindData;
            int attributes = findData.dwFileAttributes;
            char[] objectNameOSSpecificChars = findData.cFileName;
            name = MSWindowsFileSystem.toFSObjectName(new String(objectNameOSSpecificChars, 0, Array.indexOf('\0', objectNameOSSpecificChars, 0, 0)));
            size = ####findData.nFileSizeLow | ^^^^findData.nFileSizeHigh;
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, (attributes & Kernel32.FILE_ATTRIBUTE_DIRECTORY) != 0);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, (attributes & Kernel32.FILE_ATTRIBUTE_READONLY) != 0);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, (attributes & Kernel32.FILE_ATTRIBUTE_ARCHIVE) != 0);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, (attributes & Kernel32.FILE_ATTRIBUTE_HIDDEN) != 0);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, (attributes & Kernel32.FILE_ATTRIBUTE_SYSTEM) != 0);
            setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, MSWindowsFileSystem.toFSObjectTime(findData.ftCreationTime));
            setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, MSWindowsFileSystem.toFSObjectTime(findData.ftLastWriteTime));
            setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, MSWindowsFileSystem.toFSObjectTime(findData.ftLastAccessTime));
            return true;
        }
        if(Kernel32.getLastError() != Kernel32.ERROR_NO_MORE_FILES)
        {
            throw new IOException("следующий объект не был найден");
        }
        return false;
    }

    protected void beforeDestruction() {
        long findHandle = fldFindHandle;
        if(findHandle != Kernel32.INVALID_HANDLE_VALUE) Kernel32.findClose(findHandle);
    }
}

class MSWindowsFileSystemDriveRootEnumeration(MSWindowsFileSystemCustomEnumeration)
{
    private boolean fldFindStatus;
    private final char[] fldDrivePathChars;
    private final long fldDrivePathPointer;

    public (char[] drivePathChars, long drivePathPointer): super(".", 0L) {
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, true);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, false);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, true);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, false);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, true);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, Long.MIN_VALUE);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, Long.MIN_VALUE);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, Long.MIN_VALUE);
        fldFindHandle = Kernel32.INVALID_HANDLE_VALUE;
        fldFindPointer = (fldFindData = new Win32FindData()).getPointer();
        fldDrivePathChars = drivePathChars;
        fldDrivePathPointer = drivePathPointer;
    }

    public boolean findNext() throws IOException {
        if(!fldFindStatus)
        {
            if(Kernel32.getDiskFreeSpaceEx(fldDrivePathPointer)[0] == 0L)
            {
                throw new IOException("следующий объект не был найден");
            }
            fldFindStatus = true;
            name = "..";
            size = 0L;
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, true);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, false);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, false);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, false);
            setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, true);
            setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, Long.MIN_VALUE);
            setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, Long.MIN_VALUE);
            setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, Long.MIN_VALUE);
            return true;
        }
        if(fldFindHandle == Kernel32.INVALID_HANDLE_VALUE)
        {
            char[] drivePathChars = fldDrivePathChars;
            drivePathChars[3] = '*';
            if((fldFindHandle = Kernel32.findFirstFile(fldDrivePathPointer, fldFindPointer)) != Kernel32.INVALID_HANDLE_VALUE)
            {
                Win32FindData findData = fldFindData;
                int attributes = findData.dwFileAttributes;
                char[] objectNameOSSpecificChars = findData.cFileName;
                name = MSWindowsFileSystem.toFSObjectName(new String(objectNameOSSpecificChars, 0, Array.indexOf('\0', objectNameOSSpecificChars, 0, 0)));
                size = ####findData.nFileSizeLow | ^^^^findData.nFileSizeHigh;
                setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, (attributes & Kernel32.FILE_ATTRIBUTE_DIRECTORY) != 0);
                setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, (attributes & Kernel32.FILE_ATTRIBUTE_READONLY) != 0);
                setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, (attributes & Kernel32.FILE_ATTRIBUTE_ARCHIVE) != 0);
                setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, (attributes & Kernel32.FILE_ATTRIBUTE_HIDDEN) != 0);
                setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, (attributes & Kernel32.FILE_ATTRIBUTE_SYSTEM) != 0);
                setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, MSWindowsFileSystem.toFSObjectTime(findData.ftCreationTime));
                setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, MSWindowsFileSystem.toFSObjectTime(findData.ftLastWriteTime));
                setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, MSWindowsFileSystem.toFSObjectTime(findData.ftLastAccessTime));
                return true;
            }
            if(Kernel32.getLastError() != Kernel32.ERROR_FILE_NOT_FOUND)
            {
                throw new IOException("следующий объект не был найден");
            }
            return false;
        }
        return super.findNext();
    }
}

class MSWindowsFileSystemObjectEnumeration(MSWindowsFileSystemCustomEnumeration)
{
    public (long findHandle, Win32FindData findData, long findPointer) {
        int attributes = findData.dwFileAttributes;
        char[] objectNameOSSpecificChars = findData.cFileName;
        name = MSWindowsFileSystem.toFSObjectName(new String(objectNameOSSpecificChars, 0, Array.indexOf('\0', objectNameOSSpecificChars, 0, 0)));
        size = ####findData.nFileSizeLow | ^^^^findData.nFileSizeHigh;
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_DIRECTORY, (attributes & Kernel32.FILE_ATTRIBUTE_DIRECTORY) != 0);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_READ_ONLY, (attributes & Kernel32.FILE_ATTRIBUTE_READONLY) != 0);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_ARCHIVE, (attributes & Kernel32.FILE_ATTRIBUTE_ARCHIVE) != 0);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_HIDDEN, (attributes & Kernel32.FILE_ATTRIBUTE_HIDDEN) != 0);
        setBooleanAttribute(MSWindowsFileSystemObjectAttributes.B_SYSTEM, (attributes & Kernel32.FILE_ATTRIBUTE_SYSTEM) != 0);
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_CREATION_TIME, MSWindowsFileSystem.toFSObjectTime(findData.ftCreationTime));
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_WRITE_TIME, MSWindowsFileSystem.toFSObjectTime(findData.ftLastWriteTime));
        setLongAttribute(MSWindowsFileSystemObjectAttributes.L_LAST_ACCESS_TIME, MSWindowsFileSystem.toFSObjectTime(findData.ftLastAccessTime));
        fldFindHandle = findHandle;
        fldFindPointer = findPointer;
        fldFindData = findData;
    }
}

class MSWindowsFileSystemObjectAttributes(Object, AttributeSet)
{
    public static final String B_DIRECTORY = "bDirectory";
    public static final String B_READ_ONLY = "bReadOnly";
    public static final String B_ARCHIVE = "bArchive";
    public static final String B_HIDDEN = "bHidden";
    public static final String B_SYSTEM = "bSystem";
    public static final String L_CREATION_TIME = "lCreationTime";
    public static final String L_LAST_WRITE_TIME = "lLastWriteTime";
    public static final String L_LAST_ACCESS_TIME = "lLastAccessTime";

    private static int booleanAttributeIdToIndex(String attributeId) {
        if(B_DIRECTORY.equals(attributeId)) return 4;
        if(B_READ_ONLY.equals(attributeId)) return 0;
        if(B_ARCHIVE.equals(attributeId)) return 5;
        if(B_HIDDEN.equals(attributeId)) return 1;
        if(B_SYSTEM.equals(attributeId)) return 2;
        throw new IllegalArgumentException("неподдерживаемый атрибут типа boolean");
    }

    private static int longAttributeIdToIndex(String attributeId) {
        if(L_CREATION_TIME.equals(attributeId)) return 0;
        if(L_LAST_WRITE_TIME.equals(attributeId)) return 2;
        if(L_LAST_ACCESS_TIME.equals(attributeId)) return 1;
        throw new IllegalArgumentException("неподдерживаемый атрибут типа long");
    }


    int fldAttributes;
    final long[] fldTimes;

    public () { fldTimes = new long[3]; }

    public void setBooleanAttribute(String attributeId, boolean attributeValue) {
        int mask = 1 << booleanAttributeIdToIndex(attributeId);
        fldAttributes = attributeValue ? fldAttributes | mask : fldAttributes & ~mask;
    }

    public void setLongAttribute(String attributeId, long attributeValue) {
        int index = longAttributeIdToIndex(attributeId);
        fldTimes[index] = attributeValue;
    }

    public void setStringAttribute(String attributeId, String attributeValue) {
        throw new IllegalArgumentException("неподдерживаемый атрибут типа string");
    }

    public boolean isSupportedAttributeId(String attributeId) {
        return
            B_DIRECTORY.equals(attributeId) ||
            B_READ_ONLY.equals(attributeId) ||
            B_ARCHIVE.equals(attributeId) ||
            B_HIDDEN.equals(attributeId) ||
            B_SYSTEM.equals(attributeId) ||
            L_CREATION_TIME.equals(attributeId) ||
            L_LAST_WRITE_TIME.equals(attributeId) ||
            L_LAST_ACCESS_TIME.equals(attributeId)
        ;
    }

    public boolean getBooleanAttribute(String attributeId) {
        int mask = 1 << booleanAttributeIdToIndex(attributeId);
        return (fldAttributes & mask) != 0;
    }

    public long getLongAttribute(String attributeId) {
        int index = longAttributeIdToIndex(attributeId);
        return fldTimes[index];
    }

    public String getStringAttribute(String attributeId) {
        throw new IllegalArgumentException("неподдерживаемый атрибут типа string");
    }

    public String displayName(String attributeId) {
        if(B_DIRECTORY.equals(attributeId)) return "Папка";
        if(B_READ_ONLY.equals(attributeId)) return "Только чтение";
        if(B_ARCHIVE.equals(attributeId)) return "Архивный";
        if(B_HIDDEN.equals(attributeId)) return "Скрытый";
        if(B_SYSTEM.equals(attributeId)) return "Системный";
        if(L_CREATION_TIME.equals(attributeId)) return "Создан";
        if(L_LAST_WRITE_TIME.equals(attributeId)) return "Изменён";
        if(L_LAST_ACCESS_TIME.equals(attributeId)) return "Открыт";
        throw new IllegalArgumentException("неподдерживаемый атрибут");
    }

    public String[] getSupportedAttributeIds() {
        return new String[] {
            B_DIRECTORY, B_READ_ONLY, B_ARCHIVE, B_HIDDEN, B_SYSTEM,
            L_CREATION_TIME, L_LAST_WRITE_TIME, L_LAST_ACCESS_TIME
        };
    }
}