platform.independent.osservices.pas

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

{
    platform.independent.osservices — модуль с основными сервисами
    операционных систем.

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

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

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

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

unit platform.independent.osservices;

    {$MODE OBJFPC}

interface

    {%region} uses
        {$IFDEF WINDOWS}
        windows
        {$ELSE}
        unixtype,
        baseunix
        {$ENDIF},
        pascalx.lang,
        pascalx.lang.table,
        pascalx.io,
        {$IFNDEF WINDOWS}
        pascalx.io.bytearray,
        {$ENDIF}
        pascalx.io.extension,
        platform.independent.filesystem;
    {%endregion}

    {$TYPEINFO ON}
    {$CALLING REGISTER}

    const UNIT_NAME = 'platform.independent.osservices';

    {%region} type
        MemoryRegion                 = interface;
        FileSystemRoot               = class;
        Environment                  = class;
        Process                      = class;
        TimeBase                     = class;
        MemoryManager                = class;
        SystemInfo                   = class;
        IllegalProcessStateException = class;
        OutOfResourcesError          = class;

        MemoryRegion_Array1d   = specialize DynamicArray<MemoryRegion>;
        FileSystemRoot_Array1d = specialize DynamicArray<FileSystemRoot>;

        MemoryRegion_Collection1d   = specialize Collection<MemoryRegion>;
        FileSystemRoot_Collection1d = specialize Collection<FileSystemRoot>;

        MemoryRegion = interface(RefCountInterface) ['{FBE08BC1-5205-41C9-A23A-673BE4145C1E}']
            procedure setProtectionBits(newProtectionBits: int);
            function getProtectionBit(index: int): boolean;
            function getProtectionBits(): int;
            function getAddress(): long;
            function getSize(): long;
            function pointerTo(offset: long): Pointer;
            property shared: boolean index 3 read getProtectionBit;
            property readable: boolean index 0 read getProtectionBit;
            property writeable: boolean index 1 read getProtectionBit;
            property executable: boolean index 2 read getProtectionBit;
            property protectionBits: int read getProtectionBits write setProtectionBits;
            property address: long read getAddress;
            property size: long read getSize;
        end;

        FileSystemRoot = class abstract(&Object)
        {$IFNDEF WINDOWS}
        private
            class function deescapeMountPoint(const point: UnicodeString): UnicodeString; static;
            class function getRootPathOf(const objectPath: UnicodeString): UnicodeString; static;
        {$ENDIF}
        public
            class function isObjectPathCaseSensitive(): boolean; static;
            class function isInternalPathFull(const internalPath: UnicodeString): boolean; static;
            class function toInternalPath(const objectPath: UnicodeString): UnicodeString; static;
            class function toObjectPath(const internalPath: UnicodeString): UnicodeString; static;
            class function getUserCacheDir(): UnicodeString; static;
            class function getUserLocalDir(): UnicodeString; static;
            class function getUserConfigDir(): UnicodeString; static;
            class function enumerate(): FileSystemRoot_Collection1d; static;
            class function get(const objectPath: UnicodeString): FileSystemRoot; static; { Внимание! Допустим полный путь к объекту! }
        protected
            fldPath: UnicodeString;
            function getName(): UnicodeString; virtual; abstract;
            function getFileSystem(): FileSystem; virtual; abstract;
        public
            constructor create(const path: UnicodeString);
        published
            property name: UnicodeString read getName;
            property path: UnicodeString read fldPath;
            property fileSystem: platform.independent.filesystem.FileSystem read getFileSystem;
        end;

        Environment = class(&Object)
        protected type
            HashtableOfUnicodeStringToUnicodeString = specialize HashtableOfUnicodeString<UnicodeString>;
        protected
            fldMonitor: Mutex;
            fldVariables: HashtableOfUnicodeStringToUnicodeString;
            procedure setVariable(const name, value: UnicodeString); virtual;
            function getLength(): int; virtual;
            function getVariable(const name: UnicodeString): UnicodeString; virtual;
        public
            constructor create();
            destructor destroy; override;
            procedure clear(); virtual;
            procedure assign(anot: Environment); virtual;
            function equals(anot: TObject): boolean; override;
            function toString(): AnsiString; override;
            function isEmulation(): boolean; virtual;
            function isCaseSensitive(): boolean; virtual;
            function contains(const name: UnicodeString): boolean; virtual;
            function variableAt(index: int): UnicodeString; virtual;
            property length: int read getLength;
            property variable[const name: UnicodeString]: UnicodeString read getVariable write setVariable; default;
        end;

        Process = class(&Object)
        {$IFNDEF WINDOWS}
        private type
            Pint = ^int;
        private
            class function getExitCodeProcess(processId: int; status: Pint): boolean; static;
            class function safeDup2(oldFD, newFD: int): int; static;
            class function safeWaitPid(processId: int; status: Pint; options: int): int; static;
        {$ENDIF}
        public const
            MIN_PRIORITY  = int(1);
            LOW_PRIORITY  = int(3);
            NORM_PRIORITY = int(5);
            HIGH_PRIORITY = int(7);
            MAX_PRIORITY  = int(9);
        public const
            STILL_ACTIVE = int(259);
        public
            class function hasInstance(): boolean; static;
            class function createPipe(): ByteStream; static;
            class function current(): Process; static;
        private
            fldStarting: boolean;
            {$IFDEF WINDOWS}
            fldProcessHandle: system.THandle;
            fldThreadHandle: system.THandle;
            {$ELSE}
            fldProcessId: int;
            {$ENDIF}
            fldStandardInput: ByteReader;
            fldStandardOutput: ByteWriter;
            fldStandardError: ByteWriter;
        protected
            fldPriority: int;
            fldWorkingDirectory: UnicodeString;
            fldCommandLine: UnicodeString_Array1d;
            fldEnvironment: Environment;
            procedure setPriority(newPriority: int); virtual;
            procedure setWorkingDirectory(const newWorkingDirectory: UnicodeString); virtual;
            procedure setCommandLine(const newCommandLine: UnicodeString_Array1d); virtual;
            procedure setEnvironment(newEnvironment: Environment); virtual;
            procedure setStandardInput(newStandardInput: ByteReader); virtual;
            procedure setStandardOutput(newStandardOutput: ByteWriter); virtual;
            procedure setStandardError(newStandardError: ByteWriter); virtual;
            procedure clearStarting();
            function getPriority(): int; virtual;
            function getWorkingDirectory(): UnicodeString; virtual;
            function createEnvironment(): Environment; virtual;
            function isStarting(): boolean;
            function getCommandLine(): UnicodeString_Array1d;
        public
            constructor create();
            destructor destroy; override;
            procedure start(); virtual;
            procedure join(); virtual;
            procedure join(timeInMillis: long); virtual;
            procedure join(timeInMillis: long; timeInNanos: int); virtual;
            function isTerminated(): boolean; virtual;
            function getExitCode(): int; virtual;
        published
            property priority: int read getPriority write setPriority;
            property workingDirectory: UnicodeString read getWorkingDirectory write setWorkingDirectory;
            property commandLine: UnicodeString_Array1d read getCommandLine write setCommandLine;
            property environment: platform.independent.osservices.Environment read fldEnvironment write setEnvironment;
            property standardInput: ByteReader read fldStandardInput write setStandardInput;
            property standardOutput: ByteWriter read fldStandardOutput write setStandardOutput;
            property standardError: ByteWriter read fldStandardError write setStandardError;
        end;

        TimeBase = class sealed
        public
            class function currentOffsetInMillis(): int; static;
            class function currentTimeInMillis(): long; static;
        end;

        MemoryManager = class sealed
        private const
            MEMORY_START_ADDRESS = long(0);
            MEMORY_LIMIT_ADDRESS = long($800000000000);
        public const
            NOACCESS   = int($00);
            READABLE   = int($01);
            WRITEABLE  = int($02);
            EXECUTABLE = int($04);
            SHARED     = int($08);
            RESERVED   = int($10);
            DOWN       = int($20);
        public const
            PAGE_SIZE = long($1000);
        public
            class procedure deallocate(region: MemoryRegion; flags: int = 0); static;
            class function enumerate(): MemoryRegion_Collection1d; static;
            class function allocate(address, size: long; flags: int): MemoryRegion; static;
        end;

        SystemInfo = class sealed
        private
            class procedure initialize(); static;
        public
            class function getProcessorCodeBits(): int; static;
            class function getProcessorNumberOfCores(): int; static;
            class function getProcessorBrandString(): AnsiString; static;
            class function getOperatingSystemName(): AnsiString; static;
            class function getOperatingSystemVersion(): AnsiString; static;
        end;

        IllegalProcessStateException = class(IllegalStateException);

        OutOfResourcesError = class(MemoryError)
        protected
            procedure freeAllow(); override;
            procedure freeDisallow(); override;
        end;
    {%endregion}

implementation

    {$R *.res}
    {$IFDEF WINDOWS}{$R *@windows.res}{$ELSE}{$R *@unix.res}{$ENDIF}

    {$WARN 7104 OFF} { позволить обращение к локальным переменным через [rbp-<смещение>] }

    {$ASMMODE INTEL}

    {$TYPEINFO OFF}
    {$CALLING REGISTER}

    {%region} type
        MemoryRegionCollection   = class;
        MemoryRegionDescriptor   = class;
        FileSystemRootCollection = class;
        FileSystemRootDescriptor = class;
        VolumeFileSystem         = class;
        VolumeRequiredAttributes = class;
        VolumeObjectAttributes   = class;
        VolumeObjectEnumeration  = class;
        Extensions               = class;
        HandleInputStream        = class;
        HandleOutputStream       = class;
        HandleBidirectStream     = class;
        FileSeekExtension        = class;
        FileInputStream          = class;
        FileOutputStream         = class;
        FileBidirectStream       = class;
        FileBidirectStreamReader = class;
        FileBidirectStreamWriter = class;
        PipeStream               = class;
        CurrentEnvironment       = class;
        CurrentProcess           = class;

        LongRecord = packed record
            case int of
            0: (
                value: long;
            );
            1: (
                slow: int;
                shigh: int;
            );
            2: (
                ulow: system.DWord;
                uhigh: system.DWord;
            );
        end;

        TimeRecord = packed record
            millisecond: system.UInt16;
            minute: system.UInt8;
            hour: system.UInt8;
            day: system.UInt8;
            month: system.UInt8;
            year: system.UInt16;
        end;

        HashtableOfUnicodeStringToFileSystemRoot = specialize HashtableOfUnicodeString<FileSystemRoot>;

        MemoryRegionCollection = class sealed(RefCountObject, MemoryRegion_Collection1d)
        private
            fldLength: int;
            fldRegions: MemoryRegion_Array1d;
        public
            constructor create(const regions: MemoryRegion_Array1d; length: int);
            procedure copyInto(const dstArray; dstOffset: int);
            function getLength(): int;
            function componentAt(index: int): MemoryRegion;
            function toArray(): MemoryRegion_Array1d;
        end;

        MemoryRegionDescriptor = class sealed(RefCountObject, MemoryRegion)
        private
            fldAddress: long;
            fldSize: long;
            {$IFNDEF WINDOWS}
            function getProtectionString(): AnsiString;
            {$ENDIF}
        public
            constructor create(address, size: long);
            procedure setProtectionBits(newProtectionBits: int);
            function equals(anot: TObject): boolean; override;
            function getHashCode(): long; override;
            function toString(): AnsiString; override;
            function getProtectionBit(index: int): boolean;
            function getProtectionBits(): int;
            function getAddress(): long;
            function getSize(): long;
            function pointerTo(offset: long): Pointer;
        end;

        FileSystemRootCollection = class sealed(RefCountObject, FileSystemRoot_Collection1d)
        private
            fldLength: int;
            fldRoots: FileSystemRoot_Array1d;
        public
            constructor create(const roots: FileSystemRoot_Array1d; length: int);
            procedure copyInto(const dstArray; dstOffset: int);
            function getLength(): int;
            function componentAt(index: int): FileSystemRoot;
            function toArray(): FileSystemRoot_Array1d;
        end;

        FileSystemRootDescriptor = class sealed(FileSystemRoot)
        private
            {$IFDEF WINDOWS}
            fldCanonical: UnicodeString;
            {$ENDIF}
            fldFileSystem: VolumeFileSystem;
        protected
            function getName(): UnicodeString; override;
            function getFileSystem(): FileSystem; override;
        public
            constructor create(const argPath, argCurrentDirectory: UnicodeString);
            destructor destroy; override;
        end;

        VolumeFileSystem = class sealed(&Object, FileSystem)
        private const
            AT_OBJECT    = int(0); { тип аргумента — объект }
            AT_FILE      = int(1); { тип аргумента — файл }
            AT_DIRECTORY = int(2); { тип аргумента — папка }
        private const
            OBJECT_NAME_MAXIMUM_LENGTH = int({$IFDEF WINDOWS}255{$ELSE}1023{$ENDIF});
        {$IFDEF WINDOWS}
        private
            class function isComponentReserved(const component: UnicodeString): boolean; static;
        {$ENDIF}
        private
            { поля типа UnicodeString хранят пути во внутреннем формате операционной системы }
            {$IFDEF WINDOWS}
            fldCurrentUnreliableBlock: boolean;
            fldCurrentHandle: long;
            {$ELSE}
            fldObjectNameMaximumLength: int;
            {$ENDIF}
            fldCanonicalRootPath: AnsiString;
            fldInternalRootPath: UnicodeString;
            fldCurrentDirectory: UnicodeString;
            {$IFDEF WINDOWS}
            fldCurrentMonitor: Mutex;
            procedure tryMakeCurrentReliableBlock();
            {$ENDIF}
            function toVolumeFullPath(const objectName: UnicodeString): UnicodeString;
            function makeInternalFullPathAndIsExist(const volumeFullPath: UnicodeString): UnicodeString;
            function makeInternalFullPathAndCheckCreat(const volumeFullPath: UnicodeString): UnicodeString;
            function makeInternalFullPathAndCheckExist(const volumeFullPath: UnicodeString; argumentType: int): UnicodeString;
        public
            constructor create(const rootPath, currentDirectory: UnicodeString);
            {$IFDEF WINDOWS}
            destructor destroy; override;
            {$ENDIF}
            procedure changeCurrentDirectory(const directoryPath: UnicodeString);
            procedure readAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
            procedure writeAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
            procedure move(const objectOldName, objectNewName: UnicodeString);
            procedure deleteFile(const fileName: UnicodeString);
            procedure deleteDirectory(const directoryName: UnicodeString);
            procedure createDirectory(const directoryName: UnicodeString);
            function isAttached(): boolean;
            function isReadOnly(): boolean;
            function isObjectNameCaseSensitive(): boolean;
            function isObjectExists(const objectName: UnicodeString): boolean;
            function isObjectNameValid(const objectName: UnicodeString): boolean;
            function isInternalNameFull(const internalName: UnicodeString): boolean;
            function isInternalNameValid(const internalName: UnicodeString): boolean;
            function getObjectNameMaximumLength(): int;
            function totalSize(): long;
            function usedSize(): long;
            function availableSize(): long;
            function getCurrentDirectory(): UnicodeString;
            function toInternalName(const objectName: UnicodeString): UnicodeString;
            function toObjectName(const internalName: UnicodeString): UnicodeString;
            function findFirst(const objectPath: UnicodeString): ObjectEnumeration;
            function createFile(const fileName: UnicodeString): ByteWriter;
            function rewriteFile(const fileName: UnicodeString): ByteWriter;
            function openFileForAppend(const fileName: UnicodeString): ByteWriter;
            function openFileForRead(const fileName: UnicodeString): ByteReader;
            function openFile(const fileName: UnicodeString): ByteStream;
            function newAttributes(): Attributes;
        end;

        VolumeRequiredAttributes = class(RequiredAttributes)
        public const
        {$IFDEF WINDOWS}
            B_HIDDEN        = AnsiString('bHidden');
            B_SYSTEM        = AnsiString('bSystem');
            B_ARCHIVE       = AnsiString('bArchive');
            L_CREATION_TIME = AnsiString('lCreationTime');
        public
            class function toObjectTime(internalTime: long): long; static;
            class function toInternalTime(objectTime: long): long; static;
        {$ELSE}
            B_OWNER_READABLE   = AnsiString('bOwnerReadable');
            B_OWNER_WRITEABLE  = AnsiString('bOwnerWriteable');
            B_OWNER_EXECUTABLE = AnsiString('bOwnerExecutable');
            B_GROUP_READABLE   = AnsiString('bGroupReadable');
            B_GROUP_WRITEABLE  = AnsiString('bGroupWriteable');
            B_GROUP_EXECUTABLE = AnsiString('bGroupExecutable');
            B_OTHER_READABLE   = AnsiString('bOtherReadable');
            B_OTHER_WRITEABLE  = AnsiString('bOtherWriteable');
            B_OTHER_EXECUTABLE = AnsiString('bOtherExecutable');
        {$ENDIF}
        end;

        VolumeObjectAttributes = class sealed(VolumeRequiredAttributes, Attributes)
        private
            class var attrHashes: long_Array1d;
            class var attrIds: AnsiString_Array1d;
            class procedure initialize(); static;
            class procedure finalize(); static;
            class procedure stringAttributeIdIsInvalid(const attributeId: AnsiString); static;
            class function booleanAttributeIdToIndex(const attributeId: AnsiString): int; static;
            class function longAttributeIdToIndex(const attributeId: AnsiString): int; static;
        private
            fldAttributes: int;
            fldTimes: long_Array1d;
        public
            constructor create();
            procedure setBooleanAttribute(const attributeId: AnsiString; attributeValue: boolean);
            procedure setLongAttribute(const attributeId: AnsiString; attributeValue: long);
            procedure setStringAttribute(const attributeId: AnsiString; const attributeValue: UnicodeString);
            function isSupportedAttributeId(const attributeId: AnsiString): boolean;
            function getBooleanAttribute(const attributeId: AnsiString): boolean;
            function getLongAttribute(const attributeId: AnsiString): long;
            function getStringAttribute(const attributeId: AnsiString): UnicodeString;
            function displayName(const attributeId: AnsiString): UnicodeString;
            function getSupportedAttributeIds(): AnsiString_Array1d;
        end;

        VolumeObjectEnumeration = class sealed(ObjectEnumeration)
        private
            {$IFDEF WINDOWS}
            fldHandle: long;
            {$ELSE}
            fldFullDirectoryPath: AnsiString;
            fldHandle: baseunix.PDir;
            {$ENDIF}
            procedure setAttributes(const info);
        public
            {$IFDEF WINDOWS}
            constructor create(handle: long; const info);
            {$ELSE}
            constructor create(const fullDirectoryPath: AnsiString; handle: baseunix.PDir; const info);
            {$ENDIF}
            procedure close(); override;
            function findNext(): boolean; override;
        end;

        Extensions = class(&Object, Extendable)
        protected
            fldExtensions: TObject_Array1d;
        public
            function getExtensions(): Extension_Array1d;
            function getExtension(const typ: ShortString): Extension;
        end;

        HandleInputStream = class(Extensions, ByteReader)
        protected
            fldHandle: long;
        public
            constructor create(handle: long);
            procedure close(); virtual;
            function skip(bytesQuantity: long): long; virtual;
            function read(): int;
            function read(const dst: byte_Array1d): int;
            function read(const dst: byte_Array1d; offset, length: int): int;
        end;

        HandleOutputStream = class(Extensions, ByteWriter)
        protected
            fldHandle: long;
            fldRootPath: AnsiString;
        public
            constructor create(handle: long; const rootPath: AnsiString = '');
            procedure close(); virtual;
            procedure flush(); virtual;
            procedure write(byteData: int);
            procedure write(const src: byte_Array1d);
            procedure write(const src: byte_Array1d; offset, length: int);
        end;

        HandleBidirectStream = class(Extensions, ByteStream)
        protected
            fldHandleForRead: long;
            fldHandleForWrite: long;
            fldReader: HandleInputStream;
            fldWriter: HandleOutputStream;
        public
            constructor create(handleForRead, handleForWrite: long);
            destructor destroy; override;
            procedure close();
            function getReader(): ByteReader;
            function getWriter(): ByteWriter;
        end;

        FileSeekExtension = class(&Object, Extension, LimitedSizeExtension, SeekExtension)
        private
            fldHandle: long;
        public
            constructor create(handle: long);
            function available(): long;
            function seek(offset: long; from: SeekFrom): long;
            function position(): long;
            function size(): long;
        end;

        FileInputStream = class(HandleInputStream, Extension, MarkExtension)
        private
            fldOwnedSeekable: boolean;
            fldMarked: long;
        public
            constructor create(handle: long; seekable: FileSeekExtension = nil);
            destructor destroy; override;
            procedure close(); override;
            procedure reset();
            procedure mark(transferLimit: int);
            function skip(bytesQuantity: long): long; override;
        end;

        FileOutputStream = class(HandleOutputStream, Extension, TruncateExtension, MarkExtension)
        private
            fldOwnedSeekable: boolean;
            fldMarked: long;
            fldReader: FileInputStream;
        public
            constructor create(handle: long; const rootPath: AnsiString = ''; seekable: FileSeekExtension = nil; reader: FileInputStream = nil);
            destructor destroy; override;
            procedure close(); override;
            procedure flush(); override;
            procedure truncate();
            procedure reset();
            procedure mark(transferLimit: int);
        end;

        FileBidirectStream = class(HandleBidirectStream)
        public
            constructor create(handle: long; const rootPath: AnsiString = '');
            destructor destroy; override;
        end;

        FileBidirectStreamReader = class sealed(FileInputStream)
        public
            procedure close(); override;
        end;

        FileBidirectStreamWriter = class sealed(FileOutputStream)
        public
            procedure close(); override;
        end;

        PipeStream = class sealed(HandleBidirectStream)
        public
            constructor create(handleForRead, handleForWrite: long);
        end;

        CurrentEnvironment = class sealed(Environment)
        private
            procedure update();
        {$IFDEF WINDOWS}
        protected
            procedure setVariable(const name, value: UnicodeString); override;
        public
            constructor create();
            procedure clear(); override;
            procedure assign(anot: Environment); override;
        {$ELSE}
        public
            constructor create();
            function isEmulation(): boolean; override;
        {$ENDIF}
        end;

        CurrentProcess = class sealed(Process)
        private
            class var instance: CurrentProcess;
            class procedure initialize(); static;
            class procedure finalize(); static;
            class function parseCommandLine(): UnicodeString_Array1d; static;
        private
            fldStandardInputRef: TObject;
            fldStandardOutputRef: TObject;
            fldStandardErrorRef: TObject;
            {$IFDEF WINDOWS}
            fldWorkingDirectoryMonitor: Mutex;
            {$ENDIF}
        protected
            {$IFDEF WINDOWS}
            procedure setPriority(newPriority: int); override;
            {$ENDIF}
            procedure setWorkingDirectory(const newWorkingDirectory: UnicodeString); override;
            procedure setCommandLine(const newCommandLine: UnicodeString_Array1d); override;
            procedure setStandardInput(newStandardInput: ByteReader); override;
            procedure setStandardOutput(newStandardOutput: ByteWriter); override;
            procedure setStandardError(newStandardError: ByteWriter); override;
            {$IFDEF WINDOWS}
            function getPriority(): int; override;
            {$ENDIF}
            function getWorkingDirectory(): UnicodeString; override;
            function createEnvironment(): Environment; override;
        public
            constructor create();
            destructor destroy; override;
            function isTerminated(): boolean; override;
            function getExitCode(): int; override;
        end;
    {%endregion}

    {%region} var
        errorOutOfResources: OutOfResourcesError;

        fileSystemRootCount: int = {$IFDEF WINDOWS}int('Z') - int('A') + 1{$ELSE}1{$ENDIF};
        fileSystemRootArray: FileSystemRoot_Array1d;
        fileSystemRootTable: HashtableOfUnicodeStringToFileSystemRoot;
        fileSystemRootMonitor: Mutex;

        cpuNumberOfCores: int;
        cpuBrandString: AnsiString;

        osVersion: AnsiString;
    {%endregion}

    {%region  routines — exception}
        procedure exceptionInitialize();
        begin
            errorOutOfResources := OutOfResourcesError.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, '!machine-error.out-of-resources'));
            errorOutOfResources.freeDisallow();
        end;

        procedure exceptionFinalize();
        begin
            errorOutOfResources.freeAllow();
            errorOutOfResources.free();
        end;
    {%endregion}

    {%region  routines — fileSystemRoot}
        procedure fileSystemRootInitialize();
        begin
            fileSystemRootArray := FileSystemRoot_Array1d(&Array.newTObject1d($1f));
            fileSystemRootTable := HashtableOfUnicodeStringToFileSystemRoot.create();
            fileSystemRootMonitor := Mutex.create();
        end;

        procedure fileSystemRootFinalize();
        var
            index: int;
        begin
            for index := fileSystemRootCount - 1 downto 0 do begin
                fileSystemRootArray[index].free();
            end;
            fileSystemRootArray := nil;
            fileSystemRootTable.free();
            fileSystemRootMonitor.free();
        end;

        procedure fileSystemRootRegister(const path: UnicodeString; descriptor: FileSystemRoot);
        {$IFDEF WINDOWS}
        var
            index: int;
        begin
            index := int(path[2]) - int('A');
            fileSystemRootArray[index] := descriptor;
            fileSystemRootTable[path] := descriptor;
        end
        {$ELSE}
        var
            fileSystemRootCopy: FileSystemRoot_Array1d;
        begin
            if fileSystemRootCount = system.length(fileSystemRootArray) then begin
                fileSystemRootCopy := FileSystemRoot_Array1d(&Array.newTObject1d((fileSystemRootCount shl 1) or 1));
                &Array.copyObjects(fileSystemRootArray, 0, fileSystemRootCopy, 0, fileSystemRootCount);
                fileSystemRootArray := fileSystemRootCopy;
            end;
            fileSystemRootArray[fileSystemRootCount] := descriptor;
            fileSystemRootTable[path] := descriptor;
            inc(fileSystemRootCount);
        end
        {$ENDIF};

        procedure fileSystemRootCreateWorkingRoot();
        {$IFDEF WINDOWS}
        var
            volumeLetter: uchar;
            volumeIndex: int;
            rootPath: UnicodeString;
            workingDirectory: UnicodeString;
            volumeDescriptor: FileSystemRoot;
        begin
            workingDirectory := CurrentProcess.instance.workingDirectory;
            if (workingDirectory.length < 4) or (workingDirectory[1] <> '/') or (workingDirectory[3] <> ':') or (workingDirectory[4] <> '/') then exit;
            volumeLetter := workingDirectory[2];
            if ((volumeLetter < 'A') or (volumeLetter > 'Z')) and ((volumeLetter < 'a') or (volumeLetter > 'z')) then exit;
            volumeLetter := CoUChar.toUpperCase(volumeLetter);
            volumeIndex := int(volumeLetter) - int('A');
            rootPath := '/' + volumeLetter + ':';
            volumeDescriptor := FileSystemRootDescriptor.create(rootPath, workingDirectory.substring(4));
            fileSystemRootArray[volumeIndex] := volumeDescriptor;
            fileSystemRootTable[rootPath] := volumeDescriptor;
        end
        {$ELSE}
        var
            position: int;
            lastRootLength: int;
            currentRootLength: int;
            lastRootPath: UnicodeString;
            currentRootPath: UnicodeString;
            workingDirectory: UnicodeString;
            volumeDescriptor: FileSystemRoot;
        begin
            workingDirectory := CurrentProcess.instance.workingDirectory;
            lastRootLength := 0;
            lastRootPath := '';
            with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
                repeat
                    currentRootPath := readln();
                    if currentRootPath.length <= 0 then break;
                    position := currentRootPath.indexOf(#$0020) + 1;
                    currentRootPath := FileSystemRoot.deescapeMountPoint(currentRootPath.substring(position, currentRootPath.indexOf(#$0020, position)));
                    if currentRootPath.endsWith('/') then currentRootPath := currentRootPath.substring(1, currentRootPath.length);
                    currentRootLength := currentRootPath.length;
                    if workingDirectory.startsWith(currentRootPath) and (workingDirectory[currentRootLength + 1] = '/') and (currentRootLength > lastRootLength) then begin
                        lastRootLength := currentRootLength;
                        lastRootPath := currentRootPath;
                    end;
                until false;
            finally
                close();
            end;
            volumeDescriptor := FileSystemRootDescriptor.create(lastRootPath, workingDirectory.substring(lastRootLength + 1));
            fileSystemRootArray[0] := volumeDescriptor;
            fileSystemRootTable[lastRootPath] := volumeDescriptor;
        end
        {$ENDIF};
    {%endregion}

    {%region  routines — time}
        function timeDaysQuantityIn(year, month: int): int;
        var
            rem: int;
        begin
            case month + 1 of
            1, 3, 5, 7, 8, 10, 12: begin
                result := 31;
            end;
            4, 6, 9, 11: begin
                result := 30;
            end;
            2: begin
                inc(year);
                if (year mod 100) = 0 then begin
                    rem := 400;
                end else begin
                    rem := 4;
                end;
                if (year mod rem) = 0 then begin
                    result := 29;
                    exit;
                end;
                result := 28;
            end;
            else
                result := 0;
            end;
        end;

        function timeToPackedFields(timeInMillis: long): long;
        var
            year: int;
            month: int;
            day: int;
            hour: int;
            minute: int;
            millisecond: int;
            days: int;
            rem: long;
        begin
            rem := timeInMillis div 86400000;
            year := int((rem div 146097) * 400);
            rem := rem mod 146097;
            if rem >= 146096 then begin
                inc(year, 399);
                rem := 365;
            end else begin
                inc(year, int((rem div 36524) * 100));
                rem := rem mod 36524;
                inc(year, int((rem div 1461) * 4));
                rem := rem mod 1461;
                if rem >= 1460 then begin
                    inc(year, 3);
                    rem := 365;
                end else begin
                    inc(year, int(rem div 365));
                    rem := rem mod 365;
                end;
            end;
            month := 0;
            repeat
                days := timeDaysQuantityIn(year, month);
                if rem < long(days) then break;
                dec(rem, long(days));
                inc(month);
            until false;
            inc(year);
            inc(month);
            day := int(rem) + 1;
            rem := timeInMillis mod 86400000;
            hour := int(rem div 3600000);
            rem := rem mod 3600000;
            minute := int(rem div 60000);
            millisecond := int(rem mod 60000);
            result := (long(year) shl 48) or (long(month) shl 40) or (long(day) shl 32) or (long(hour) shl 24) or (long(minute) shl 16) or long(millisecond);
        end;

        function timeElapsedInMillis(year, month, day, hour, minute, millisecond: int): long;
        var
            yer: int;
            mnt: int;
            days: int;
        begin
            dec(year);
            dec(month);
            dec(day);
            year := year and $ffff;
            yer := year;
            days := 146097 * (year div 400);
            year := year mod 400;
            days := days + 36524 * (year div 100);
            year := year mod 100;
            days := days + 1461 * (year div 4) + 365 * (year mod 4) + day;
            for mnt := 0 to month - 1 do days := days + timeDaysQuantityIn(yer, mnt);
            result := 86400000 * long(days) + long(3600000 * hour + 60000 * minute + millisecond);
        end;
    {%endregion}

    {%region  routines — CPU}
        function cpuReadBrandStringTo(dst: system.PAnsiChar): int; assembler; nostackframe;
        asm
            {$IFDEF WINDOWS}
                        dd          $000010c8
                        mov         qword   [rbp-$08], rbx
                        mov         qword   [rbp-$10], rdi
                        mov         rdi,    rcx
            {$ELSE}
                        dd          $000008c8
                        mov         qword   [rbp-$08], rbx
            {$ENDIF}
                        mov         eax,    $80000000
                        cpuid
                        test        eax,    $80000000
                        jz          @00
                        cmp         eax,    $80000004
                        jge         @01
                @00:    mov         eax,    $00000000
                        jmp         @exit
                @01:    mov         eax,    $80000002
                        cpuid
                        mov         dword   [rdi+$00], eax
                        mov         dword   [rdi+$04], ebx
                        mov         dword   [rdi+$08], ecx
                        mov         dword   [rdi+$0c], edx
                        mov         eax,    $80000003
                        cpuid
                        mov         dword   [rdi+$10], eax
                        mov         dword   [rdi+$14], ebx
                        mov         dword   [rdi+$18], ecx
                        mov         dword   [rdi+$1c], edx
                        mov         eax,    $80000004
                        cpuid
                        mov         dword   [rdi+$20], eax
                        mov         dword   [rdi+$24], ebx
                        mov         dword   [rdi+$28], ecx
                        mov         dword   [rdi+$2c], edx
                        mov         eax,    $00000000
                        mov         ecx,    $00000030
                        mov         rdx,    rdi
                        cld
                        repne scasb
                        je          @02
                        mov         eax,    $00000030
                        jmp         @exit
                @02:    lea         rax,    [rdi-$01]
                        sub         rax,    rdx
              @exit:
            {$IFDEF WINDOWS}
                        mov         rdi,    [rbp-$10]
            {$ENDIF}
                        mov         rbx,    [rbp-$08]
                        leave
        end;
    {%endregion}

    {$IFDEF WINDOWS}

    {%region  OS API — additional}
        const
            TH32CS_SNAPPROCESS = system.DWord($00000002);

        const
            STATUS_FILE_IS_A_DIRECTORY = system.DWord($c00000ba);
            STATUS_NOT_A_DIRECTORY     = system.DWord($c0000103);

        {$PACKRECORDS C}

        type
            PProcessEntry32 = ^ProcessEntry32;

            ProcessEntry32 = record
                dwSize: system.DWord;
                cntUsage: system.DWord;
                th32ProcessId: system.DWord;
                th32DefaultHeapId: system.PtrUInt;
                th32ModuleId: system.DWord;
                cntThreads: system.DWord;
                th32ParentProcessId: system.DWord;
                pcPriClassBase: system.LongInt;
                dwFlags: system.DWord;
                szExeFile: array [0..windows.MAX_PATH - 1] of system.Char;
            end;

        {$PACKRECORDS DEFAULT}
        {$CALLING STDCALL}

        function createToolhelp32Snapshot(dwFlags, th32ProcessID: system.DWord): system.THandle; external windows.KERNEL32 name 'CreateToolhelp32Snapshot';

        function process32First(hSnapshot: system.THandle; lpProcessEntry: PProcessEntry32): system.LongBool; external windows.KERNEL32 name 'Process32First';

        function process32Next(hSnapshot: system.THandle; lpProcessEntry: PProcessEntry32): system.LongBool; external windows.KERNEL32 name 'Process32Next';

        function setEnvironmentStringsW(newEnvironment: windows.LPWCH): windows.BOOL; external windows.KERNEL32 name 'SetEnvironmentStringsW';

        function getLastStatus(): system.DWord; assembler; nostackframe;
        asm
                        xor         eax,    eax
                        mov         rax,    gs:[rax+$30]
                        mov         eax,    [rax+$1250]
        end;

        {$CALLING REGISTER}
    {%endregion}

    {%region  FileSystemRoot }
        class function FileSystemRoot.isObjectPathCaseSensitive(): boolean;
        begin
            result := false;
        end;

        class function FileSystemRoot.isInternalPathFull(const internalPath: UnicodeString): boolean;
        var
            volumeLetter: uchar;
            length: int;
        begin
            length := internalPath.length;
            if (length >= 2) and (internalPath[2] = ':') and ((length <= 2) or (internalPath[3] = '\')) then begin
                volumeLetter := internalPath[1];
                result := (volumeLetter >= 'A') and (volumeLetter <= 'Z') or (volumeLetter >= 'a') and (volumeLetter <= 'z');
                exit;
            end;
            result := false;
        end;

        class function FileSystemRoot.toInternalPath(const objectPath: UnicodeString): UnicodeString;
        var
            internalPath: UnicodeString;
        begin
            internalPath := objectPath.copy();
            if internalPath.startsWith('/') then internalPath := internalPath.substring(2);
            result := internalPath.replaceAll('/', '\');
        end;

        class function FileSystemRoot.toObjectPath(const internalPath: UnicodeString): UnicodeString;
        var
            objectPath: UnicodeString;
        begin
            objectPath := internalPath.copy();
            if not objectPath.startsWith('\') then objectPath := '/' + objectPath;
            result := objectPath.replaceAll('\', '/');
        end;

        class function FileSystemRoot.getUserCacheDir(): UnicodeString;
        begin
            result := getUserLocalDir();
        end;

        class function FileSystemRoot.getUserLocalDir(): UnicodeString;
        label
            break_label0;
        var
            pos: int;
            checkPath: UnicodeString;
            subdirPath: UnicodeString;
            objectPath: UnicodeString;
            internalPath: UnicodeString;
        begin
            begin
                with CurrentProcess.instance.environment do begin
                    internalPath := variable['LocalAppData'];
                    if internalPath.length <= 0 then begin
                        internalPath := variable['UserProfile'];
                        if not internalPath.endsWith('\') then internalPath := internalPath + '\';
                        internalPath := internalPath + 'AppData\Local\';
                        goto break_label0;
                    end;
                end;
                if not internalPath.endsWith('\') then internalPath := internalPath + '\';
            end;
            break_label0:
            objectPath := toObjectPath(internalPath);
            with get(objectPath), fileSystem do begin
                subdirPath := objectPath.substring(path.length + 1);
                pos := 1;
                repeat
                    pos := subdirPath.indexOf('/', pos + 1);
                    if pos < 1 then break;
                    checkPath := subdirPath.substring(1, pos);
                    if not isObjectExists(checkPath) then createDirectory(checkPath);
                until false;
            end;
            result := objectPath;
        end;

        class function FileSystemRoot.getUserConfigDir(): UnicodeString;
        label
            break_label0;
        var
            pos: int;
            checkPath: UnicodeString;
            subdirPath: UnicodeString;
            objectPath: UnicodeString;
            internalPath: UnicodeString;
        begin
            begin
                with CurrentProcess.instance.environment do begin
                    internalPath := variable['AppData'];
                    if internalPath.length <= 0 then begin
                        internalPath := variable['UserProfile'];
                        if not internalPath.endsWith('\') then internalPath := internalPath + '\';
                        internalPath := internalPath + 'AppData\Roaming\';
                        goto break_label0;
                    end;
                end;
                if not internalPath.endsWith('\') then internalPath := internalPath + '\';
            end;
            break_label0:
            objectPath := toObjectPath(internalPath);
            with get(objectPath), fileSystem do begin
                subdirPath := objectPath.substring(path.length + 1);
                pos := 1;
                repeat
                    pos := subdirPath.indexOf('/', pos + 1);
                    if pos < 1 then break;
                    checkPath := subdirPath.substring(1, pos);
                    if not isObjectExists(checkPath) then createDirectory(checkPath);
                until false;
            end;
            result := objectPath;
        end;

        class function FileSystemRoot.enumerate(): FileSystemRoot_Collection1d;
        var
            volumeLetter: uchar;
            volumeIndex: int;
            rootsLength: int;
            internalRootPath: UnicodeString;
            standardRootPath: UnicodeString;
            rootsArray: FileSystemRoot_Array1d;
            volumeDescriptor: FileSystemRoot;
        begin
            internalRootPath := UnicodeString(#0':\').copy();
            volumeIndex := 0;
            rootsLength := 0;
            rootsArray := FileSystemRoot_Array1d(&Array.newTObject1d(int('Z') - int('A') + 1));
            fileSystemRootMonitor.beginSynchronized();
            try
                for volumeLetter := 'A' to 'Z' do begin
                    internalRootPath[1] := volumeLetter;
                    if windows.getDriveTypeW(system.PWideChar(internalRootPath)) > windows.DRIVE_NO_ROOT_DIR then begin
                        volumeDescriptor := fileSystemRootArray[volumeIndex];
                        if volumeDescriptor = nil then begin
                            standardRootPath := '/' + volumeLetter + ':';
                            volumeDescriptor := FileSystemRootDescriptor.create(standardRootPath, '/');
                            fileSystemRootRegister(standardRootPath, volumeDescriptor);
                        end;
                        rootsArray[rootsLength] := volumeDescriptor;
                        inc(rootsLength);
                    end;
                    inc(volumeIndex);
                end;
            finally
                fileSystemRootMonitor.endSynchronized();
            end;
            result := FileSystemRootCollection.create(rootsArray, rootsLength);
        end;

        class function FileSystemRoot.get(const objectPath: UnicodeString): FileSystemRoot;
        var
            volumeLetter: uchar;
            volumeIndex: int;
            length: int;
            internalRootPath: UnicodeString;
            standardRootPath: UnicodeString;
            volumeDescriptor: FileSystemRoot;
        begin
            length := objectPath.length;
            if (length < 3) or (objectPath[1] <> '/') then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectPath')
                ]));
            end;
            volumeLetter := objectPath[2];
            if ((volumeLetter < 'A') or (volumeLetter > 'Z')) and ((volumeLetter < 'a') or (volumeLetter > 'z')) or (objectPath[3] <> ':') or (length >= 4) and (objectPath[4] <> '/') then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectPath')
                ]));
            end;
            volumeLetter := CoUChar.toUpperCase(volumeLetter);
            volumeIndex := int(volumeLetter) - int('A');
            fileSystemRootMonitor.beginSynchronized();
            try
                volumeDescriptor := fileSystemRootArray[volumeIndex];
                if volumeDescriptor = nil then begin
                    standardRootPath := '/' + volumeLetter + ':';
                    internalRootPath := volumeLetter + ':\';
                    if not windows.getDriveTypeW(system.PWideChar(internalRootPath)) <= windows.DRIVE_NO_ROOT_DIR then begin
                        raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                            CoAnsiString.create(internalRootPath.toUTF8())
                        ]));
                    end;
                    volumeDescriptor := FileSystemRootDescriptor.create(standardRootPath, '/');
                    fileSystemRootRegister(standardRootPath, volumeDescriptor);
                end;
            finally
                fileSystemRootMonitor.endSynchronized();
            end;
            result := volumeDescriptor;
        end;

        constructor FileSystemRoot.create(const path: UnicodeString);
        begin
            inherited create();
            fldPath := path;
        end;
    {%endregion}

    {%region  Environment }
        procedure Environment.setVariable(const name, value: UnicodeString);
        var
            index: int;
            vname: UnicodeString;
            vars: HashtableOfUnicodeStringToUnicodeString;
            emon: Mutex;
        begin
            if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
            end;
            if value.indexOf(#0) > 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('value') ]));
            end;
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                if vars.contains(name) then begin
                    vars[name] := value;
                    exit;
                end;
                for index := vars.length - 1 downto 0 do begin
                    vname := vars.keyAt(index);
                    if name.equalsIgnoreCase(vname) then begin
                        vars[vname] := value;
                        exit;
                    end;
                end;
                vars[name] := value;
            finally
                emon.endSynchronized();
            end;
        end;

        function Environment.getLength(): int;
        begin
            result := fldVariables.length;
        end;

        function Environment.getVariable(const name: UnicodeString): UnicodeString;
        var
            index: int;
            value: UnicodeString;
            vname: UnicodeString;
            vars: HashtableOfUnicodeStringToUnicodeString;
            emon: Mutex;
        begin
            if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
            end;
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                value := vars[name];
                if value.length > 0 then begin
                    result := value;
                    exit;
                end;
                for index := vars.length - 1 downto 0 do begin
                    vname := vars.keyAt(index);
                    if name.equalsIgnoreCase(vname) then begin
                        result := vars[vname];
                        exit;
                    end;
                end;
            finally
                emon.endSynchronized();
            end;
            result := '';
        end;

        constructor Environment.create();
        begin
            inherited create();
            fldMonitor := Mutex.create();
            fldVariables := HashtableOfUnicodeStringToUnicodeString.create();
        end;

        destructor Environment.destroy;
        begin
            fldMonitor.free();
            fldVariables.free();
            inherited destroy;
        end;

        procedure Environment.clear();
        var
            emon: Mutex;
        begin
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                fldVariables.clear();
            finally
                emon.endSynchronized();
            end;
        end;

        procedure Environment.assign(anot: Environment);
        var
            index: int;
            vname: UnicodeString;
            vars: HashtableOfUnicodeStringToUnicodeString;
            emon: Mutex;
        begin
            if anot = nil then begin
                raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('anot') ]));
            end;
            if anot = self then exit;
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                vars.clear();
                for index := 0 to anot.length - 1 do begin
                    vname := anot.variableAt(index);
                    vars[vname] := anot[vname];
                end;
            finally
                emon.endSynchronized();
            end;
        end;

        function Environment.equals(anot: TObject): boolean;
        label
            break_label0;
        var
            count: int;
            aindex: int;
            vindex: int;
            aname: UnicodeString;
            vname: UnicodeString;
            avalue: UnicodeString;
            vvalue: UnicodeString;
            vars: HashtableOfUnicodeStringToUnicodeString;
            aenv: Environment;
            emon: Mutex;
        begin
            if not(anot is Environment) then begin
                result := false;
                exit;
            end;
            if anot = self then begin
                result := true;
                exit;
            end;
            aenv := Environment(anot);
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                count := aenv.length;
                if vars.length <> count then begin
                    result := false;
                    exit;
                end;
                dec(count);
                for aindex := count downto 0 do begin
                    aname := aenv.variableAt(aindex);
                    avalue := aenv[aname];
                    vvalue := vars[aname];
                    if vvalue.length > 0 then begin
                        if vvalue.equalsIgnoreCase(avalue) then continue;
                        result := false;
                        exit;
                    end;
                    begin
                        for vindex := count downto 0 do begin
                            vname := vars.keyAt(vindex);
                            if vname.equalsIgnoreCase(aname) then goto break_label0;
                        end;
                        result := false;
                        exit;
                    end;
                    break_label0:
                    if not vars[vname].equalsIgnoreCase(avalue) then begin
                        result := false;
                        exit;
                    end;
                end;
            finally
                emon.endSynchronized();
            end;
            result := true;
        end;

        function Environment.toString(): AnsiString;
        var
            index: int;
            text: UnicodeString;
            name: UnicodeString;
            vars: HashtableOfUnicodeStringToUnicodeString;
            emon: Mutex;
        begin
            text := '';
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                for index := 0 to vars.length - 1 do begin
                    name := vars.keyAt(index);
                    text := text + name + '=' + vars[name] + CoUnicodeString.LINE_ENDING;
                end;
            finally
                emon.endSynchronized();
            end;
            result := text.toUTF8();
        end;

        function Environment.isEmulation(): boolean;
        begin
            result := false;
        end;

        function Environment.isCaseSensitive(): boolean;
        begin
            result := false;
        end;

        function Environment.contains(const name: UnicodeString): boolean;
        var
            index: int;
            vars: HashtableOfUnicodeStringToUnicodeString;
            emon: Mutex;
        begin
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                if vars.contains(name) then begin
                    result := true;
                    exit;
                end;
                for index := vars.length - 1 downto 0 do if vars.keyAt(index).equalsIgnoreCase(name) then begin
                    result := true;
                    exit;
                end;
            finally
                emon.endSynchronized();
            end;
            result := false;
        end;

        function Environment.variableAt(index: int): UnicodeString;
        var
            emon: Mutex;
        begin
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                result := fldVariables.keyAt(index);
            finally
                emon.endSynchronized();
            end;
        end;
    {%endregion}

    {%region  Process }
        class function Process.hasInstance(): boolean;
        var
            thisModuleFullPath: UnicodeString;
            anotModuleFullPath: UnicodeString;
            getModuleFileNameExW: function(hProcess, hModule: system.THandle; lpFileName: system.PWideChar; nSize: system.DWord): system.DWord; stdcall;
            moduleHandle: system.THandle;
            moduleFullPath: system.PWideChar;
            thisProcessId: system.DWord;
            anotProcessId: system.DWord;
            processInfo: ProcessEntry32;
            processHandle: system.THandle;
            snapshotHandle: system.THandle;
        begin
            moduleHandle := windows.getModuleHandleW(windows.KERNEL32);
            Pointer(getModuleFileNameExW) := windows.getProcAddress(moduleHandle, 'GetModuleFileNameExW');
            if getModuleFileNameExW = nil then begin
                moduleHandle := windows.loadLibraryW('psapi.dll');
                Pointer(getModuleFileNameExW) := windows.getProcAddress(moduleHandle, 'GetModuleFileNameExW');
            end;
            thisProcessId := windows.getCurrentProcessId();
            moduleFullPath := nil;
            snapshotHandle := createToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
            try
                moduleFullPath := system.getMemory(sizeof(uchar) * (windows.MAX_PATH + 1));
                &Array.zeroRaw(processInfo, sizeof(ProcessEntry32));
                processInfo.dwSize := sizeof(ProcessEntry32);
                windows.getModuleFileNameW(0, moduleFullPath, windows.MAX_PATH + 1);
                thisModuleFullPath := UnicodeString(moduleFullPath);
                if process32First(snapshotHandle, @processInfo) then repeat
                    anotProcessId := processInfo.th32ProcessId;
                    if thisProcessId = anotProcessId then continue;
                    processHandle := windows.openProcess(windows.PROCESS_QUERY_INFORMATION or windows.PROCESS_QUERY_LIMITED_INFORMATION or windows.PROCESS_VM_READ, false, anotProcessId);
                    if processHandle = 0 then continue;
                    try
                        moduleFullPath[0] := #$0000;
                        getModuleFileNameExW(processHandle, 0, moduleFullPath, windows.MAX_PATH + 1);
                    finally
                        windows.closeHandle(processHandle);
                    end;
                    anotModuleFullPath := UnicodeString(moduleFullPath);
                    if thisModuleFullPath = anotModuleFullPath then begin
                        result := true;
                        exit;
                    end;
                until not process32Next(snapshotHandle, @processInfo);
            finally
                windows.closeHandle(snapshotHandle);
                if moduleFullPath <> nil then system.freeMemory(moduleFullPath);
            end;
            result := false;
        end;

        class function Process.createPipe(): ByteStream;
        var
            handleForRead: system.THandle;
            handleForWrite: system.THandle;
        begin
            handleForRead := 0;
            handleForWrite := 0;
            if not windows.createPipe(@handleForRead, @handleForWrite, nil, $100000) then begin
                raise errorOutOfResources;
            end;
            result := PipeStream.create(long(handleForRead), long(handleForWrite));
        end;

        class function Process.current(): Process;
        begin
            result := CurrentProcess.instance;
        end;

        procedure Process.setPriority(newPriority: int);
        begin
            if newPriority < MIN_PRIORITY then newPriority := MIN_PRIORITY;
            if newPriority > MAX_PRIORITY then newPriority := MAX_PRIORITY;
            fldPriority := newPriority;
        end;

        procedure Process.setWorkingDirectory(const newWorkingDirectory: UnicodeString);
        begin
            fldWorkingDirectory := newWorkingDirectory.copy();
        end;

        procedure Process.setCommandLine(const newCommandLine: UnicodeString_Array1d);
        var
            volumeLetter: uchar;
            index: int;
            count: int;
            element: UnicodeString;
            locCommandLine: UnicodeString_Array1d;
        begin
            count := system.length(newCommandLine);
            locCommandLine := nil;
            for index := 0 to count - 1 do begin
                element := newCommandLine[index].copy();
                if (element.indexOf('"') > 0) or (element.indexOf(#0) > 0) then begin
                    raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                        CoAnsiString.create('commandLine'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
                    ]));
                end;
                if index = 0 then begin
                    if (element.length <= 3) or (element[2] <> ':') or (element[3] <> '\') then begin
                        raise IllegalPropertyValueException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-property.command-line'));
                    end;
                    volumeLetter := element[1];
                    if ((volumeLetter < 'A') or (volumeLetter > 'Z')) and ((volumeLetter < 'a') or (volumeLetter > 'z')) then begin
                        raise IllegalPropertyValueException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-property.command-line'));
                    end;
                    locCommandLine := &Array.newUnicodeString1d(count);
                end;
                locCommandLine[index] := element;
            end;
            fldCommandLine := locCommandLine;
        end;

        procedure Process.setEnvironment(newEnvironment: Environment);
        begin
            fldEnvironment.assign(newEnvironment);
        end;

        procedure Process.setStandardInput(newStandardInput: ByteReader);
        begin
            if (newStandardInput <> nil) and not Lang.isInstance(newStandardInput, HandleInputStream) then begin
                raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                    CoAnsiString.create('standardInput'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
                ]));
            end;
            fldStandardInput := newStandardInput;
        end;

        procedure Process.setStandardOutput(newStandardOutput: ByteWriter);
        begin
            if (newStandardOutput <> nil) and not Lang.isInstance(newStandardOutput, HandleOutputStream) then begin
                raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                    CoAnsiString.create('standardOutput'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
                ]));
            end;
            fldStandardOutput := newStandardOutput;
        end;

        procedure Process.setStandardError(newStandardError: ByteWriter);
        begin
            if (newStandardError <> nil) and not Lang.isInstance(newStandardError, HandleOutputStream) then begin
                raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                    CoAnsiString.create('standardError'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
                ]));
            end;
            fldStandardError := newStandardError;
        end;

        procedure Process.clearStarting();
        begin
            fldStarting := false;
        end;

        function Process.getPriority(): int;
        begin
            result := fldPriority;
        end;

        function Process.getWorkingDirectory(): UnicodeString;
        begin
            result := fldWorkingDirectory.copy();
        end;

        function Process.createEnvironment(): Environment;
        begin
            result := nil;
        end;

        function Process.isStarting(): boolean; assembler; nostackframe;
        asm
                        mov         eax,    true
                        xchg        byte    [rcx+offset fldStarting], al
                        movzx       eax,    al
        end;

        function Process.getCommandLine(): UnicodeString_Array1d;
        var
            index: int;
            count: int;
            commandLineData: UnicodeString_Array1d;
            commandLineCopy: UnicodeString_Array1d;
        begin
            commandLineData := fldCommandLine;
            count := system.length(commandLineData);
            commandLineCopy := &Array.newUnicodeString1d(count);
            for index := count - 1 downto 0 do begin
                commandLineCopy[index] := commandLineData[index].copy();
            end;
            result := commandLineCopy;
        end;

        constructor Process.create();
        var
            locCurrentProcess: Process;
            locEnvironment: Environment;
        begin
            inherited create();
            locEnvironment := createEnvironment();
            if locEnvironment = nil then begin
                locEnvironment := platform.independent.osservices.Environment.create();
            end;
            locCurrentProcess := CurrentProcess.instance;
            if locCurrentProcess <> nil then begin
                locEnvironment.assign(locCurrentProcess.environment);
                fldWorkingDirectory := locCurrentProcess.workingDirectory;
                fldStandardInput := locCurrentProcess.fldStandardInput;
                fldStandardOutput := locCurrentProcess.fldStandardOutput;
                fldStandardError := locCurrentProcess.fldStandardError;
            end;
            fldProcessHandle := windows.INVALID_HANDLE_VALUE;
            fldPriority := NORM_PRIORITY;
            fldEnvironment := locEnvironment;
        end;

        destructor Process.destroy;
        var
            processHandle: system.THandle;
        begin
            processHandle := fldProcessHandle;
            if processHandle <> windows.INVALID_HANDLE_VALUE then begin
                windows.closeHandle(processHandle);
                windows.closeHandle(fldThreadHandle);
            end;
            fldEnvironment.free();
            inherited destroy;
        end;

        procedure Process.start();
        var
            index: int;
            count: int;
            processExitCode: int;
            priorityClass: int;
            locWorkingDirectory: UnicodeString;
            commandLineString: UnicodeString;
            environmentString: UnicodeString;
            element: UnicodeString;
            commandLineData: UnicodeString_Array1d;
            environmentData: Environment;
            processHandle: system.THandle;
            stdInStream: HandleInputStream;
            stdInHandle: system.THandle;
            stdOutStream: HandleOutputStream;
            stdOutHandle: system.THandle;
            stdErrStream: HandleOutputStream;
            stdErrHandle: system.THandle;
            startupInfo: windows.TStartupInfoW;
            processInfo: windows.TProcessInformation;
        begin
            if isStarting() then begin
                raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
            end;
            try
                processExitCode := 0;
                processHandle := fldProcessHandle;
                if (processHandle <> windows.INVALID_HANDLE_VALUE) and windows.getExitCodeProcess(processHandle, @processExitCode) and (processExitCode = STILL_ACTIVE) then begin
                    raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
                end;
                commandLineData := fldCommandLine;
                count := system.length(commandLineData);
                if count <= 0 then begin
                    raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process.command-line'));
                end;
                { приоритет }
                case fldPriority of
                MIN_PRIORITY - 0:
                    priorityClass := windows.IDLE_PRIORITY_CLASS;
                LOW_PRIORITY - 1..
                LOW_PRIORITY - 0:
                    priorityClass := windows.BELOW_NORMAL_PRIORITY_CLASS;
                NORM_PRIORITY - 1..
                NORM_PRIORITY + 1:
                    priorityClass := windows.NORMAL_PRIORITY_CLASS;
                HIGH_PRIORITY + 0..
                HIGH_PRIORITY + 1:
                    priorityClass := windows.ABOVE_NORMAL_PRIORITY_CLASS;
                else
                    priorityClass := windows.HIGH_PRIORITY_CLASS;
                end;
                { рабочая папка }
                locWorkingDirectory := FileSystemRoot.toInternalPath(fldWorkingDirectory);
                { командная строка }
                commandLineString := '';
                for index := 0 to count - 1 do begin
                    if index > 0 then begin
                        commandLineString := commandLineString + ' ';
                    end;
                    element := commandLineData[index];
                    if (element.length <= 0) or (element.indexOf(' ') > 0) then begin
                        element := '"' + element + '"';
                    end;
                    commandLineString := commandLineString + element;
                end;
                { переменные среды }
                environmentData := fldEnvironment;
                count := environmentData.length;
                environmentString := '';
                for index := 0 to count - 1 do begin
                    element := environmentData.variableAt(index);
                    environmentString := environmentString + element + '=' + environmentData[element] + #0;
                end;
                environmentString := environmentString + #0;
                { стандартные потоки ввода-вывода }
                stdInStream := HandleInputStream(Lang.cast(fldStandardInput, HandleInputStream));
                stdInHandle := windows.INVALID_HANDLE_VALUE;
                if stdInStream <> nil then begin
                    stdInHandle := system.THandle(stdInStream.fldHandle);
                    windows.setHandleInformation(stdInHandle, windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT);
                end;
                stdOutStream := HandleOutputStream(Lang.cast(fldStandardOutput, HandleOutputStream));
                stdOutHandle := windows.INVALID_HANDLE_VALUE;
                if stdOutStream <> nil then begin
                    stdOutHandle := system.THandle(stdOutStream.fldHandle);
                    windows.setHandleInformation(stdOutHandle, windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT);
                end;
                stdErrStream := HandleOutputStream(Lang.cast(fldStandardError, HandleOutputStream));
                stdErrHandle := windows.INVALID_HANDLE_VALUE;
                if stdErrStream <> nil then begin
                    stdErrHandle := system.THandle(stdErrStream.fldHandle);
                    windows.setHandleInformation(stdErrHandle, windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT);
                end;
                { создание процесса }
                with startupInfo do begin
                    cb := sizeof(windows.TStartupInfoW);
                    lpReserved := nil;
                    lpDesktop := nil;
                    lpTitle := nil;
                    dwX := 0;
                    dwY := 0;
                    dwXSize := 0;
                    dwYSize := 0;
                    dwXCountChars := 0;
                    dwYCountChars := 0;
                    dwFillAttribute := 0;
                    dwFlags := windows.STARTF_USESTDHANDLES;
                    wShowWindow := 0;
                    cbReserved2 := 0;
                    lpReserved2 := nil;
                    hStdInput := stdInHandle;
                    hStdOutput := stdOutHandle;
                    hStdError := stdErrHandle;
                end;
                with processInfo do begin
                    hProcess := 0;
                    hThread := 0;
                    dwProcessId := 0;
                    dwThreadId := 0;
                end;
                if not windows.createProcessW(
                    nil,
                    system.PWideChar(commandLineString),
                    nil,
                    nil,
                    true,
                    windows.CREATE_UNICODE_ENVIRONMENT or system.DWord(priorityClass),
                    system.PWideChar(environmentString),
                    system.PWideChar(locWorkingDirectory),
                    @startupInfo,
                    @processInfo
                ) then case windows.getLastError() of
                windows.ERROR_FILE_NOT_FOUND,
                windows.ERROR_PATH_NOT_FOUND:
                    raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                        CoAnsiString.create(commandLineData[0].toUTF8())
                    ]));
                windows.ERROR_DIRECTORY:
                    raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                        CoAnsiString.create(locWorkingDirectory.toUTF8())
                    ]));
                else
                    raise errorOutOfResources;
                end;
                if processHandle <> windows.INVALID_HANDLE_VALUE then begin
                    windows.closeHandle(processHandle);
                    windows.closeHandle(fldThreadHandle);
                end;
                with processInfo do begin
                    fldProcessHandle := hProcess;
                    fldThreadHandle := hThread;
                end;
            finally
                clearStarting();
            end;
        end;

        procedure Process.join();
        var
            processHandle: system.THandle;
        begin
            processHandle := fldProcessHandle;
            if processHandle = windows.INVALID_HANDLE_VALUE then begin
                raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
            end;
            windows.waitForSingleObject(processHandle, windows.INFINITE);
        end;

        procedure Process.join(timeInMillis: long);
        var
            processHandle: system.THandle;
        begin
            processHandle := fldProcessHandle;
            if processHandle = windows.INVALID_HANDLE_VALUE then begin
                raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
            end;
            if timeInMillis < 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInMillis') ]));
            end;
            if timeInMillis > $fffffffe then timeInMillis := $fffffffe;
            if timeInMillis = 0 then timeInMillis := -1;
            windows.waitForSingleObject(processHandle, system.DWord(timeInMillis));
        end;

        procedure Process.join(timeInMillis: long; timeInNanos: int);
        var
            processHandle: system.THandle;
        begin
            processHandle := fldProcessHandle;
            if processHandle = windows.INVALID_HANDLE_VALUE then begin
                raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
            end;
            if timeInMillis < 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInMillis') ]));
            end;
            if (timeInNanos < 0) or (timeInNanos > 999999) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInNanos') ]));
            end;
            if (timeInMillis < CoLong.MAX_VALUE) and (timeInNanos >= 500000) or (timeInMillis = 0) and (timeInNanos > 0) then inc(timeInMillis);
            if timeInMillis > $fffffffe then timeInMillis := $fffffffe;
            if timeInMillis = 0 then timeInMillis := -1;
            windows.waitForSingleObject(processHandle, system.DWord(timeInMillis));
        end;

        function Process.isTerminated(): boolean;
        var
            processHandle: system.THandle;
        begin
            processHandle := fldProcessHandle;
            result := (processHandle <> windows.INVALID_HANDLE_VALUE) and (windows.waitForSingleObject(processHandle, 0) = windows.WAIT_OBJECT_0);
        end;

        function Process.getExitCode(): int;
        var
            processExitCode: int;
            processHandle: system.THandle;
        begin
            processExitCode := 0;
            processHandle := fldProcessHandle;
            if (processHandle = windows.INVALID_HANDLE_VALUE) or not windows.getExitCodeProcess(processHandle, @processExitCode) then begin
                result := 0;
                exit;
            end;
            result := processExitCode;
        end;
    {%endregion}

    {%region  TimeBase }
        class function TimeBase.currentOffsetInMillis(): int;
        var
            timeZoneInfo: windows.TTimeZoneInformation;
        begin
            &Array.zeroRaw(timeZoneInfo, sizeof(windows.TTimeZoneInformation));
            windows.getTimeZoneInformation(@timeZoneInfo);
            result := -60000 * timeZoneInfo.bias;
        end;

        class function TimeBase.currentTimeInMillis(): long;
        var
            time: windows.TSystemTime;
        begin
            &Array.zeroRaw(time, sizeof(windows.TSystemTime));
            windows.getSystemTime(@time);
            with time do result := timeElapsedInMillis(year, month, day, hour, minute, 1000 * second + millisecond);
        end;
    {%endregion}

    {%region  MemoryManager }
        class procedure MemoryManager.deallocate(region: MemoryRegion; flags: int);
        var
            address: long;
            size: long;
            freeFlags: system.DWord;
            reference: Pointer absolute address;
        begin
            if region = nil then begin
                raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('region') ]));
            end;
            if not(region is MemoryRegionDescriptor) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('region') ]));
            end;
            address := region.address;
            size := region.size;
            if (flags and RESERVED) <> 0 then begin
                freeFlags := windows.MEM_DECOMMIT;
            end else begin
                size := 0;
                freeFlags := windows.MEM_RELEASE;
            end;
            windows.virtualFree(reference, size, freeFlags);
        end;

        class function MemoryManager.enumerate(): MemoryRegion_Collection1d;
        var
            regionsLength: int;
            address: long;
            size: long;
            regionsData: MemoryRegion_Array1d;
            regionsCopy: MemoryRegion_Array1d;
            memoryInfo: windows.TMemoryBasicInformation;
            reference: Pointer absolute address;
        begin
            address := MEMORY_START_ADDRESS;
            regionsLength := 0;
            regionsData := MemoryRegion_Array1d(&Array.newIUnknown1d($7f));
            &Array.zeroRaw(memoryInfo, sizeof(windows.TMemoryBasicInformation));
            while windows.virtualQuery(reference, @memoryInfo, sizeof(windows.TMemoryBasicInformation)) > 0 do begin
                reference := memoryInfo.baseAddress;
                size := memoryInfo.regionSize;
                if memoryInfo.state <> windows.MEM_FREE then begin
                    if regionsLength = system.length(regionsData) then begin
                        regionsCopy := MemoryRegion_Array1d(&Array.newIUnknown1d((regionsLength shl 1) or 1));
                        &Array.copyUnknowns(regionsData, 0, regionsCopy, 0, regionsLength);
                        regionsData := regionsCopy;
                    end;
                    regionsData[regionsLength] := MemoryRegionDescriptor.create(address, size);
                    inc(regionsLength);
                end;
                inc(address, size);
                if address >= MEMORY_LIMIT_ADDRESS then break;
            end;
            result := MemoryRegionCollection.create(regionsData, regionsLength);
        end;

        class function MemoryManager.allocate(address, size: long; flags: int): MemoryRegion;
        var
            allocationFlags: system.DWord;
            allocationProtect: system.DWord;
            reference: Pointer absolute address;
        begin
            if (size < 0) or (size > MEMORY_LIMIT_ADDRESS - MEMORY_START_ADDRESS) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('size') ]));
            end;
            if (address <> 0) and ((address + size > MEMORY_LIMIT_ADDRESS) or (address < MEMORY_START_ADDRESS)) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('address') ]));
            end;
            address := address and -PAGE_SIZE;
            if size = 0 then size := 1;
            size := size + (-size and (PAGE_SIZE - 1));
            if (flags and RESERVED) <> 0 then begin
                allocationFlags := windows.MEM_RESERVE;
                if (flags and (READABLE + WRITEABLE + EXECUTABLE)) <> 0 then inc(allocationFlags, windows.MEM_COMMIT);
            end else begin
                allocationFlags := windows.MEM_COMMIT;
            end;
            if (flags and DOWN) <> 0 then begin
                inc(allocationFlags, windows.MEM_TOP_DOWN);
            end;
            case flags and (READABLE + WRITEABLE + EXECUTABLE) of
            READABLE:
                allocationProtect := windows.PAGE_READONLY;
            WRITEABLE,
            READABLE + WRITEABLE:
                allocationProtect := windows.PAGE_READWRITE;
            EXECUTABLE:
                allocationProtect := windows.PAGE_EXECUTE;
            EXECUTABLE + READABLE:
                allocationProtect := windows.PAGE_EXECUTE_READ;
            EXECUTABLE + WRITEABLE,
            EXECUTABLE + READABLE + WRITEABLE:
                allocationProtect := windows.PAGE_EXECUTE_READWRITE;
            else
                allocationProtect := windows.PAGE_NOACCESS;
            end;
            reference := windows.virtualAlloc(reference, system.PtrUInt(size), allocationFlags, allocationProtect);
            if reference = nil then case windows.getLastError() of
            windows.NO_ERROR: ;
            windows.ERROR_NOT_ENOUGH_MEMORY:
                raise errorOutOfResources;
            else
                raise IllegalArgumentException.create(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.passed'));
            end;
            result := MemoryRegionDescriptor.create(address, size);
        end;
    {%endregion}

    {%region  SystemInfo }
        class procedure SystemInfo.initialize();
        var
            ntMajorVersion: int absolute $7ffe026c;
            ntMinorVersion: int absolute $7ffe0270;
            ntBuildNumber: int absolute $7ffe0260;
            cpuBrandLength: int;
            cpuBrandArray: system.PAnsiChar;
            sysInfo: windows.TSystemInfo;
        begin
            { количество потоков процессора }
            &Array.zeroRaw(sysInfo, sizeof(windows.TSystemInfo));
            windows.getSystemInfo(@sysInfo);
            cpuNumberOfCores := int(sysInfo.dwNumberOfProcessors);
            { название модели процессора }
            cpuBrandArray := system.PAnsiChar(system.getMemory($30 * sizeof(char)));
            try
                cpuBrandLength := cpuReadBrandStringTo(cpuBrandArray);
                cpuBrandString := AnsiString.create(cpuBrandLength);
                &Array.copyRaw(cpuBrandArray^, cpuBrandString[1], cpuBrandLength * sizeof(char));
            finally
                system.freeMemory(cpuBrandArray);
            end;
            { версия операционной системы }
            osVersion := CoInt.toString(ntMajorVersion) + '.' + CoInt.toString(ntMinorVersion) + '.' + CoInt.toString(ntBuildNumber);
        end;

        class function SystemInfo.getProcessorCodeBits(): int;
        begin
            result := 64;
        end;

        class function SystemInfo.getProcessorNumberOfCores(): int;
        begin
            result := cpuNumberOfCores;
        end;

        class function SystemInfo.getProcessorBrandString(): AnsiString;
        begin
            result := cpuBrandString;
        end;

        class function SystemInfo.getOperatingSystemName(): AnsiString;
        begin
            result := 'Microsoft Windows';
        end;

        class function SystemInfo.getOperatingSystemVersion(): AnsiString;
        begin
            result := osVersion;
        end;
    {%endregion}

    {%region  MemoryRegionDescriptor }
        constructor MemoryRegionDescriptor.create(address, size: long);
        begin
            inherited create();
            fldAddress := address;
            fldSize := size;
        end;

        procedure MemoryRegionDescriptor.setProtectionBits(newProtectionBits: int);
        var
            address: long;
            newProtect: system.DWord;
            oldProtect: system.DWord;
            reference: Pointer absolute address;
        begin
            address := fldAddress;
            case newProtectionBits and (MemoryManager.READABLE + MemoryManager.WRITEABLE + MemoryManager.EXECUTABLE) of
            MemoryManager.READABLE:
                newProtect := windows.PAGE_READONLY;
            MemoryManager.WRITEABLE,
            MemoryManager.READABLE + MemoryManager.WRITEABLE:
                newProtect := windows.PAGE_READWRITE;
            MemoryManager.EXECUTABLE:
                newProtect := windows.PAGE_EXECUTE;
            MemoryManager.EXECUTABLE + MemoryManager.READABLE:
                newProtect := windows.PAGE_EXECUTE_READ;
            MemoryManager.EXECUTABLE + MemoryManager.WRITEABLE,
            MemoryManager.EXECUTABLE + MemoryManager.READABLE + MemoryManager.WRITEABLE:
                newProtect := windows.PAGE_EXECUTE_READWRITE;
            else
                newProtect := windows.PAGE_NOACCESS;
            end;
            oldProtect := 0;
            windows.virtualProtect(reference, system.PtrUInt(fldSize), newProtect, @oldProtect);
        end;

        function MemoryRegionDescriptor.equals(anot: TObject): boolean;
        var
            mreg: MemoryRegionDescriptor;
        begin
            if not(anot is MemoryRegionDescriptor) then begin
                result := false;
                exit;
            end;
            if anot = self then begin
                result := true;
                exit;
            end;
            mreg := MemoryRegionDescriptor(anot);
            result := (fldAddress = mreg.fldAddress) and (fldSize = mreg.fldSize);
        end;

        function MemoryRegionDescriptor.getHashCode(): long;
        begin
            result := fldAddress xor CoLong.rol(fldSize, 35);
        end;

        function MemoryRegionDescriptor.toString(): AnsiString;
        var
            protect: int;
            address: long;
            flags: char_Array1d;
        begin
            protect := getProtectionBits();
            address := fldAddress;
            flags := &Array.newChar1d(4);
            if (protect and MemoryManager.READABLE) <> 0 then begin
                flags[0] := 'r';
            end else begin
                flags[0] := '-';
            end;
            if (protect and MemoryManager.WRITEABLE) <> 0 then begin
                flags[1] := 'w';
            end else begin
                flags[1] := '-';
            end;
            if (protect and MemoryManager.EXECUTABLE) <> 0 then begin
                flags[2] := 'x';
            end else begin
                flags[2] := '-';
            end;
            if (protect and MemoryManager.SHARED) <> 0 then begin
                flags[3] := 's';
            end else begin
                flags[3] := 'p';
            end;
            result := CoLong.toHexString(address) + '-' + CoLong.toHexString(address + fldSize) + #$20 + AnsiString.create(flags);
        end;

        function MemoryRegionDescriptor.getProtectionBit(index: int): boolean;
        begin
            result := (getProtectionBits() and (1 shl index)) <> 0;
        end;

        function MemoryRegionDescriptor.getProtectionBits(): int;
        var
            address: long;
            memoryInfo: windows.TMemoryBasicInformation;
            reference: Pointer absolute address;
        begin
            address := fldAddress;
            &Array.zeroRaw(memoryInfo, sizeof(windows.TMemoryBasicInformation));
            if windows.virtualQuery(reference, @memoryInfo, sizeof(windows.TMemoryBasicInformation)) <= 0 then begin
                result := 0;
                exit;
            end;
            case memoryInfo.protect of
            windows.PAGE_READONLY:
                result := MemoryManager.READABLE;
            windows.PAGE_READWRITE:
                result := MemoryManager.READABLE + MemoryManager.WRITEABLE;
            windows.PAGE_WRITECOPY:
                result := MemoryManager.READABLE + MemoryManager.WRITEABLE + MemoryManager.SHARED;
            windows.PAGE_EXECUTE:
                result := MemoryManager.EXECUTABLE;
            windows.PAGE_EXECUTE_READ:
                result := MemoryManager.EXECUTABLE + MemoryManager.READABLE;
            windows.PAGE_EXECUTE_READWRITE:
                result := MemoryManager.EXECUTABLE + MemoryManager.READABLE + MemoryManager.WRITEABLE;
            windows.PAGE_EXECUTE_WRITECOPY:
                result := MemoryManager.EXECUTABLE + MemoryManager.READABLE + MemoryManager.WRITEABLE + MemoryManager.SHARED;
            else
                result := 0;
            end;
        end;

        function MemoryRegionDescriptor.getAddress(): long;
        begin
            result := fldAddress;
        end;

        function MemoryRegionDescriptor.getSize(): long;
        begin
            result := fldSize;
        end;

        function MemoryRegionDescriptor.pointerTo(offset: long): Pointer;
        var
            address: long;
            reference: Pointer absolute address;
        begin
            if (offset < 0) or (offset > fldSize) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('offset') ]));
            end;
            address := fldAddress + offset;
            result := reference;
        end;
    {%endregion}

    {%region  FileSystemRootDescriptor }
        function FileSystemRootDescriptor.getName(): UnicodeString;
        var
            locName: UnicodeString;
            volName: system.PWideChar;
        begin
            locName := '';
            volName := system.PWideChar(system.getMemory(sizeof(uchar) * (windows.MAX_PATH + 1)));
            try
                if windows.getVolumeInformationW(system.PWideChar(fldCanonical), volName, windows.MAX_PATH + 1, nil, nil, nil, nil, 0) then begin
                    locName := UnicodeString(volName);
                end;
            finally
                system.freeMemory(volName);
            end;
            result := locName;
        end;

        function FileSystemRootDescriptor.getFileSystem(): FileSystem;
        begin
            result := fldFileSystem;
        end;

        constructor FileSystemRootDescriptor.create(const argPath, argCurrentDirectory: UnicodeString);
        begin
            inherited create(argPath);
            fldCanonical := argPath.substring(2) + '\';
            fldFileSystem := VolumeFileSystem.create(toInternalPath(argPath), argCurrentDirectory.replaceAll('/', '\'));
        end;

        destructor FileSystemRootDescriptor.destroy;
        begin
            fldFileSystem.free();
            inherited destroy;
        end;
    {%endregion}

    {%region  VolumeFileSystem }
        class function VolumeFileSystem.isComponentReserved(const component: UnicodeString): boolean;
        const
            reserved: array [0..27] of UnicodeString = (
                'con', 'prn', 'aux', 'nul',
                'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9', 'com'#$00b9, 'com'#$00b2, 'com'#$00b3,
                'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9', 'lpt'#$00b9, 'lpt'#$00b2, 'lpt'#$00b3
            );
        var
            index: int;
            length: int;
            name: UnicodeString;
        begin
            length := component.length;
            if length > 0 then case component[length] of
            #$0020: begin
                result := true;
                exit;
            end;
            '.': begin
                result := (length <> 1) and ((length <> 2) or (component[1] <> '.'));
                exit;
            end;
            end;
            index := component.indexOf('.');
            if index <= 0 then begin
                name := component;
            end else begin
                name := component.substring(1, index);
            end;
            length := name.length;
            if (length < 3) or (length > 4) then begin
                result := false;
                exit;
            end;
            for index := 0 to 27 do if reserved[index].equalsIgnoreCase(name) then begin
                result := true;
                exit;
            end;
            result := false;
        end;

        procedure VolumeFileSystem.tryMakeCurrentReliableBlock();
        label
            break_label0;
        var
            length: int;
            internalFullPath: UnicodeString;
            oldHandle: system.THandle;
            newHandle: system.THandle;
            cmon: Mutex;
        begin
            cmon := fldCurrentMonitor;
            cmon.beginSynchronized();
            try
                if fldCurrentUnreliableBlock then begin
                    internalFullPath := fldCurrentDirectory;
                    length := internalFullPath.length;
                    if length <= 1 then begin
                        fldCurrentUnreliableBlock := false;
                        goto break_label0;
                    end;
                    internalFullPath := fldInternalRootPath + internalFullPath.substring(1, length);
                    newHandle := windows.createFileW(
                        system.PWideChar(internalFullPath),
                        { windows.DELETE } $00010000,
                        windows.FILE_SHARE_READ or windows.FILE_SHARE_WRITE,
                        nil,
                        windows.OPEN_EXISTING,
                        windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
                        0
                    );
                    if newHandle <> windows.INVALID_HANDLE_VALUE then begin
                        fldCurrentUnreliableBlock := false;
                    end else newHandle := windows.createFileW(
                        system.PWideChar(internalFullPath),
                        0,
                        0,
                        nil,
                        windows.OPEN_EXISTING,
                        windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
                        0
                    );
                    oldHandle := system.THandle(fldCurrentHandle);
                    if oldHandle <> windows.INVALID_HANDLE_VALUE then windows.closeHandle(oldHandle);
                    fldCurrentHandle := long(newHandle);
                end;
                break_label0:
            finally
                cmon.endSynchronized();
            end;
        end;

        function VolumeFileSystem.toVolumeFullPath(const objectName: UnicodeString): UnicodeString;
        var
            internalName: UnicodeString;
        begin
            internalName := toInternalName(objectName);
            if isInternalNameFull(internalName) then begin
                result := internalName;
                exit;
            end;
            result := fldCurrentDirectory + internalName;
        end;

        function VolumeFileSystem.makeInternalFullPathAndIsExist(const volumeFullPath: UnicodeString): UnicodeString;
        label
            break_label0;
        var
            attributes: int;
            dotPosition: int;
            reductionPosition: int;
            internalRootPath: UnicodeString;
            internalFullPath: UnicodeString;
        begin
            internalRootPath := fldInternalRootPath;
            internalFullPath := volumeFullPath;
            dotPosition := 1;
            repeat
                dotPosition := internalFullPath.indexOf('\.\', dotPosition);
                if dotPosition <= 0 then begin
                    if not internalFullPath.endsWith('\.') then break;
                    dotPosition := internalFullPath.length - 1;
                end;
                internalFullPath := internalFullPath.substring(1, dotPosition) + internalFullPath.substring(dotPosition + 2);
            until false;
            dotPosition := 1;
            repeat
                dotPosition := internalFullPath.indexOf('\..\', dotPosition);
                if dotPosition <= 0 then begin
                    if not internalFullPath.endsWith('\..') then break;
                    dotPosition := internalFullPath.length - 2;
                end;
                begin
                    if dotPosition > 1 then begin
                        attributes := int(windows.getFileAttributesW(system.PWideChar(internalRootPath + internalFullPath.substring(1, dotPosition))));
                        if (attributes <> int(windows.INVALID_FILE_ATTRIBUTES)) and ((attributes and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0) then goto break_label0;
                        case windows.getLastError() of
                        windows.ERROR_FILE_NOT_FOUND,
                        windows.ERROR_PATH_NOT_FOUND: ;
                        windows.ERROR_NOT_READY:
                            raise FileSystemNotAttachedException.create(AnsiString.format(
                                AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                                    CoAnsiString.create(fldCanonicalRootPath)
                                ]
                            ));
                        else
                            raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                        end;
                    end;
                    result := '';
                    exit;
                end;
                break_label0:
                reductionPosition := internalFullPath.lastIndexOf('\', dotPosition - 1);
                internalFullPath := internalFullPath.substring(1, reductionPosition) + internalFullPath.substring(dotPosition + 3);
                dotPosition := reductionPosition;
            until false;
            result := internalRootPath + internalFullPath;
        end;

        function VolumeFileSystem.makeInternalFullPathAndCheckCreat(const volumeFullPath: UnicodeString): UnicodeString;
        var
            position: int;
            checkFullPath: UnicodeString;
        begin
            position := volumeFullPath.lastIndexOf('\') + 1;
            checkFullPath := makeInternalFullPathAndCheckExist(volumeFullPath.substring(1, position), AT_DIRECTORY) + '.';
            if windows.getFileAttributesW(system.PWideChar(checkFullPath)) = windows.INVALID_FILE_ATTRIBUTES then case windows.getLastError() of
            windows.ERROR_FILE_NOT_FOUND,
            windows.ERROR_PATH_NOT_FOUND:
                raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                    CoAnsiString.create(checkFullPath.toUTF8())
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := checkFullPath.substring(1, checkFullPath.length) + volumeFullPath.substring(position);
        end;

        function VolumeFileSystem.makeInternalFullPathAndCheckExist(const volumeFullPath: UnicodeString; argumentType: int): UnicodeString;
        label
            break_label0;
        var
            attributes: int;
            dotPosition: int;
            reductionPosition: int;
            internalRootPath: UnicodeString;
            internalFullPath: UnicodeString;
        begin
            internalRootPath := fldInternalRootPath;
            internalFullPath := volumeFullPath;
            dotPosition := 1;
            repeat
                dotPosition := internalFullPath.indexOf('\.\', dotPosition);
                if dotPosition <= 0 then begin
                    if not internalFullPath.endsWith('\.') then break;
                    dotPosition := internalFullPath.length - 1;
                end;
                internalFullPath := internalFullPath.substring(1, dotPosition) + internalFullPath.substring(dotPosition + 2);
            until false;
            dotPosition := 1;
            repeat
                dotPosition := internalFullPath.indexOf('\..\', dotPosition);
                if dotPosition <= 0 then begin
                    if not internalFullPath.endsWith('\..') then break;
                    dotPosition := internalFullPath.length - 2;
                end;
                begin
                    if dotPosition > 1 then begin
                        attributes := int(windows.getFileAttributesW(system.PWideChar(internalRootPath + internalFullPath.substring(1, dotPosition))));
                        if (attributes <> int(windows.INVALID_FILE_ATTRIBUTES)) and ((attributes and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0) then goto break_label0;
                        case windows.getLastError() of
                        windows.ERROR_FILE_NOT_FOUND,
                        windows.ERROR_PATH_NOT_FOUND: ;
                        windows.ERROR_NOT_READY:
                            raise FileSystemNotAttachedException.create(AnsiString.format(
                                AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                                    CoAnsiString.create(fldCanonicalRootPath)
                                ]
                            ));
                        else
                            raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                        end;
                    end;
                    case argumentType of
                    AT_FILE:
                        raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                            CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
                        ]));
                    AT_DIRECTORY:
                        raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                            CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
                        ]));
                    else
                        raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                            CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
                        ]));
                    end;
                end;
                break_label0:
                reductionPosition := internalFullPath.lastIndexOf('\', dotPosition - 1);
                internalFullPath := internalFullPath.substring(1, reductionPosition) + internalFullPath.substring(dotPosition + 3);
                dotPosition := reductionPosition;
            until false;
            result := internalRootPath + internalFullPath;
        end;

        constructor VolumeFileSystem.create(const rootPath, currentDirectory: UnicodeString);
        begin
            inherited create();
            fldCurrentUnreliableBlock := true;
            fldCurrentHandle := long(windows.INVALID_HANDLE_VALUE);
            fldCanonicalRootPath := rootPath.toUTF8() + '\';
            fldInternalRootPath := rootPath;
            fldCurrentDirectory := currentDirectory;
            fldCurrentMonitor := Mutex.create();
            tryMakeCurrentReliableBlock();
        end;

        destructor VolumeFileSystem.destroy;
        var
            currentHandle: system.THandle;
        begin
            currentHandle := system.THandle(fldCurrentHandle);
            if currentHandle <> windows.INVALID_HANDLE_VALUE then begin
                windows.closeHandle(currentHandle);
            end;
            fldCurrentMonitor.free();
            inherited destroy;
        end;

        procedure VolumeFileSystem.changeCurrentDirectory(const directoryPath: UnicodeString);
        label
            break_label0;
        var
            unreliableBlock: boolean;
            rootPathLength: int;
            internalFullPath: UnicodeString;
            newCurrentDirectory: UnicodeString;
            oldHandle: system.THandle;
            newHandle: system.THandle;
            cmon: Mutex;
        begin
            if directoryPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryPath')
                ]));
            end;
            if not isObjectNameValid(directoryPath) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('directoryPath')
                ]));
            end;
            internalFullPath := toVolumeFullPath(directoryPath);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryPath')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_DIRECTORY);
            if internalFullPath.endsWith('\') then internalFullPath := internalFullPath.substring(1, internalFullPath.length);
            rootPathLength := fldInternalRootPath.length;
            newCurrentDirectory := internalFullPath.substring(rootPathLength + 1) + '\';
            cmon := fldCurrentMonitor;
            cmon.beginSynchronized();
            try
                if not fldCurrentDirectory.equalsIgnoreCase(newCurrentDirectory) then begin
                    unreliableBlock := false;
                    newHandle := windows.INVALID_HANDLE_VALUE;
                    if internalFullPath.length > rootPathLength then begin
                        unreliableBlock := true;
                        newHandle := windows.createFileW(
                            system.PWideChar(internalFullPath),
                            { windows.DELETE } $00010000,
                            windows.FILE_SHARE_READ or windows.FILE_SHARE_WRITE,
                            nil,
                            windows.OPEN_EXISTING,
                            windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
                            0
                        );
                        if newHandle <> windows.INVALID_HANDLE_VALUE then begin
                            unreliableBlock := false;
                        end else newHandle := windows.createFileW(
                            system.PWideChar(internalFullPath),
                            0,
                            0,
                            nil,
                            windows.OPEN_EXISTING,
                            windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
                            0
                        );
                        begin
                            if newHandle <> windows.INVALID_HANDLE_VALUE then begin
                                if (windows.getFileAttributesW(system.PWideChar(internalFullPath)) and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0 then goto break_label0;
                                windows.closeHandle(newHandle);
                            end else begin
                                case windows.getLastError() of
                                windows.ERROR_FILE_NOT_FOUND,
                                windows.ERROR_PATH_NOT_FOUND: ;
                                windows.ERROR_NOT_READY:
                                    raise FileSystemNotAttachedException.create(AnsiString.format(
                                        AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                                            CoAnsiString.create(fldCanonicalRootPath)
                                        ]
                                    ));
                                else
                                    raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                                end;
                            end;
                            raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                                CoAnsiString.create(internalFullPath.toUTF8())
                            ]));
                        end;
                        break_label0:
                    end;
                    oldHandle := system.THandle(fldCurrentHandle);
                    if oldHandle <> windows.INVALID_HANDLE_VALUE then windows.closeHandle(oldHandle);
                    fldCurrentUnreliableBlock := unreliableBlock;
                    fldCurrentDirectory := newCurrentDirectory;
                    fldCurrentHandle := long(newHandle);
                end;
            finally
                cmon.endSynchronized();
            end;
        end;

        procedure VolumeFileSystem.readAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
        var
            objectReadTime: boolean;
            standardNameLength: int;
            internalAttributes: int;
            internalCreationTime: long;
            internalLastWriteTime: long;
            internalLastAccessTime: long;
            internalFullPath: UnicodeString;
            objectHandle: system.THandle;
        begin
            standardNameLength := objectName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(objectName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_OBJECT);
            objectHandle := windows.createFileW(
                system.PWideChar(internalFullPath),
                windows.GENERIC_READ,
                windows.FILE_SHARE_READ or windows.FILE_SHARE_WRITE or windows.FILE_SHARE_DELETE,
                nil,
                windows.OPEN_EXISTING,
                windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
                0
            );
            if objectHandle = windows.INVALID_HANDLE_VALUE then case windows.getLastError() of
            windows.ERROR_FILE_NOT_FOUND,
            windows.ERROR_PATH_NOT_FOUND:
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            internalAttributes := windows.getFileAttributesW(system.PWideChar(internalFullPath));
            internalCreationTime := 0;
            internalLastWriteTime := 0;
            internalLastAccessTime := 0;
            objectReadTime := windows.getFileTime(objectHandle, @internalCreationTime, @internalLastAccessTime, @internalLastWriteTime);
            if not windows.closeHandle(objectHandle) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if (internalAttributes = int(windows.INVALID_FILE_ATTRIBUTES)) or not objectReadTime then begin
                raise ObjectReadAttributesException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'object.read-attributes'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            end;
            if objectAttr = nil then exit;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_DIRECTORY) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, (internalAttributes and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_READ_ONLY) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, (internalAttributes and windows.FILE_ATTRIBUTE_READONLY) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_ARCHIVE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_ARCHIVE, (internalAttributes and windows.FILE_ATTRIBUTE_ARCHIVE) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_HIDDEN) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_HIDDEN, (internalAttributes and windows.FILE_ATTRIBUTE_HIDDEN) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_SYSTEM) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_SYSTEM, (internalAttributes and windows.FILE_ATTRIBUTE_SYSTEM) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_CREATION_TIME) then begin
                objectAttr.setLongAttribute(VolumeRequiredAttributes.L_CREATION_TIME, VolumeRequiredAttributes.toObjectTime(internalCreationTime));
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_WRITE_TIME) then begin
                objectAttr.setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, VolumeRequiredAttributes.toObjectTime(internalLastWriteTime));
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) then begin
                objectAttr.setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, VolumeRequiredAttributes.toObjectTime(internalLastAccessTime));
            end;
        end;

        procedure VolumeFileSystem.writeAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
        var
            objectWriteTime: boolean;
            objectWriteAttr: boolean;
            standardNameLength: int;
            internalAttributes: int;
            internalCreationTime: long;
            internalLastWriteTime: long;
            internalLastAccessTime: long;
            internalFullPath: UnicodeString;
            objectHandle: system.THandle;
        begin
            standardNameLength := objectName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(objectName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if objectAttr = nil then begin
                raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('objectAttr') ]));
            end;
            internalAttributes := 0;
            internalCreationTime := 0;
            internalLastWriteTime := 0;
            internalLastAccessTime := 0;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_DIRECTORY) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY) then begin
                inc(internalAttributes, windows.FILE_ATTRIBUTE_DIRECTORY);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_READ_ONLY) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY) then begin
                inc(internalAttributes, windows.FILE_ATTRIBUTE_READONLY);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_ARCHIVE) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_ARCHIVE) then begin
                inc(internalAttributes, windows.FILE_ATTRIBUTE_ARCHIVE);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_HIDDEN) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_HIDDEN) then begin
                inc(internalAttributes, windows.FILE_ATTRIBUTE_HIDDEN);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_SYSTEM) and objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_SYSTEM) then begin
                inc(internalAttributes, windows.FILE_ATTRIBUTE_SYSTEM);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_CREATION_TIME) then begin
                internalCreationTime := VolumeRequiredAttributes.toInternalTime(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_CREATION_TIME));
                if internalCreationTime < 0 then internalCreationTime := 0;
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_WRITE_TIME) then begin
                internalLastWriteTime := VolumeRequiredAttributes.toInternalTime(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME));
                if internalLastWriteTime < 0 then internalLastWriteTime := 0;
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) then begin
                internalLastAccessTime := VolumeRequiredAttributes.toInternalTime(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME));
                if internalLastAccessTime < 0 then internalLastAccessTime := 0;
            end;
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_OBJECT);
            objectWriteAttr := windows.setFileAttributesW(system.PWideChar(internalFullPath), FILE_ATTRIBUTE_NORMAL);
            objectHandle := windows.createFileW(
                system.PWideChar(internalFullPath),
                windows.GENERIC_WRITE,
                windows.FILE_SHARE_READ or windows.FILE_SHARE_WRITE or windows.FILE_SHARE_DELETE,
                nil,
                windows.OPEN_EXISTING,
                windows.FILE_ATTRIBUTE_NORMAL or windows.FILE_FLAG_BACKUP_SEMANTICS,
                0
            );
            if objectHandle = windows.INVALID_HANDLE_VALUE then case windows.getLastError() of
            windows.ERROR_FILE_NOT_FOUND,
            windows.ERROR_PATH_NOT_FOUND:
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            windows.ERROR_WRITE_PROTECT:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            objectWriteTime := windows.setFileTime(objectHandle, @internalCreationTime, @internalLastAccessTime, @internalLastWriteTime);
            objectWriteAttr := windows.setFileAttributesW(system.PWideChar(internalFullPath), system.DWord(internalAttributes)) and objectWriteAttr;
            if not windows.closeHandle(objectHandle) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if not objectWriteAttr or not objectWriteTime then begin
                raise ObjectWriteAttributesException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'object.write-attributes'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            end;
        end;

        procedure VolumeFileSystem.move(const objectOldName, objectNewName: UnicodeString);
        var
            standardNameLength: int;
            internalOldFullPath: UnicodeString;
            internalNewFullPath: UnicodeString;
        begin
            standardNameLength := objectOldName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectOldName')
                ]));
            end;
            if (standardNameLength <= 0) or objectOldName.endsWith('/') or not isObjectNameValid(objectOldName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectOldName')
                ]));
            end;
            internalOldFullPath := toVolumeFullPath(objectOldName);
            if internalOldFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectOldName')
                ]));
            end;
            standardNameLength := objectNewName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectNewName')
                ]));
            end;
            if (standardNameLength <= 0) or objectNewName.endsWith('/') or not isObjectNameValid(objectNewName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectNewName')
                ]));
            end;
            internalNewFullPath := toVolumeFullPath(objectNewName);
            if internalNewFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectNewName')
                ]));
            end;
            tryMakeCurrentReliableBlock();
            internalOldFullPath := makeInternalFullPathAndCheckExist(internalOldFullPath, AT_OBJECT);
            internalNewFullPath := makeInternalFullPathAndCheckCreat(internalNewFullPath);
            if not windows.moveFileW(system.PWideChar(internalOldFullPath), system.PWideChar(internalNewFullPath)) then case windows.getLastError() of
            windows.ERROR_FILE_NOT_FOUND,
            windows.ERROR_PATH_NOT_FOUND:
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(internalOldFullPath.toUTF8())
                ]));
            windows.ERROR_FILE_EXISTS,
            windows.ERROR_ALREADY_EXISTS:
                raise MoveOperationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'move'), [
                    CoAnsiString.create(internalOldFullPath.toUTF8()), CoAnsiString.create(internalNewFullPath.toUTF8())
                ]));
            windows.ERROR_DISK_FULL:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            windows.ERROR_WRITE_PROTECT:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure VolumeFileSystem.deleteFile(const fileName: UnicodeString);
        var
            standardNameLength: int;
            internalFullPath: UnicodeString;
        begin
            standardNameLength := fileName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(fileName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_FILE);
            if not windows.deleteFileW(system.PWideChar(internalFullPath)) then begin
                case windows.getLastError() of
                windows.ERROR_FILE_NOT_FOUND,
                windows.ERROR_PATH_NOT_FOUND:
                    raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                        CoAnsiString.create(internalFullPath.toUTF8())
                    ]));
                windows.ERROR_WRITE_PROTECT:
                    raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                windows.ERROR_NOT_READY:
                    raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                else
                    if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
                        raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                    end;
                end;
                raise FileDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.deletion'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            end;
        end;

        procedure VolumeFileSystem.deleteDirectory(const directoryName: UnicodeString);
        var
            standardNameLength: int;
            internalFullPath: UnicodeString;
        begin
            standardNameLength := directoryName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            if (standardNameLength <= 0) or directoryName.endsWith('/') or not isObjectNameValid(directoryName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(directoryName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            tryMakeCurrentReliableBlock();
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_DIRECTORY);
            if not windows.removeDirectoryW(system.PWideChar(internalFullPath)) then begin
                case windows.getLastError() of
                windows.ERROR_DIR_NOT_EMPTY: ;
                windows.ERROR_FILE_NOT_FOUND,
                windows.ERROR_PATH_NOT_FOUND:
                    raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                        CoAnsiString.create(internalFullPath.toUTF8())
                    ]));
                windows.ERROR_WRITE_PROTECT:
                    raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                windows.ERROR_NOT_READY:
                    raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                else
                    if getLastStatus() <> STATUS_NOT_A_DIRECTORY then begin
                        raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                    end;
                end;
                raise DirectoryDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.deletion'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            end;
        end;

        procedure VolumeFileSystem.createDirectory(const directoryName: UnicodeString);
        var
            standardNameLength: int;
            internalFullPath: UnicodeString;
        begin
            standardNameLength := directoryName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            if (standardNameLength <= 0) or directoryName.endsWith('/') or not isObjectNameValid(directoryName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(directoryName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckCreat(internalFullPath);
            if not windows.createDirectoryW(system.PWideChar(internalFullPath), nil) then case windows.getLastError() of
            windows.ERROR_FILE_EXISTS,
            windows.ERROR_ALREADY_EXISTS:
                raise DirectoryCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.creation'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            windows.ERROR_DISK_FULL:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            windows.ERROR_WRITE_PROTECT:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        function VolumeFileSystem.isAttached(): boolean;
        begin
            windows.setErrorMode(windows.SEM_FAILCRITICALERRORS);
            if not windows.getVolumeInformationA(system.PAnsiChar(fldCanonicalRootPath), nil, 0, nil, nil, nil, nil, 0) then case windows.getLastError() of
            windows.ERROR_NOT_READY: begin
                result := false;
                exit;
            end;
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := true;
        end;

        function VolumeFileSystem.isReadOnly(): boolean;
        var
            flags: int;
            rootPath: AnsiString;
        begin
            flags := 0;
            rootPath := fldCanonicalRootPath;
            windows.setErrorMode(windows.SEM_FAILCRITICALERRORS);
            if not windows.getVolumeInformationA(system.PAnsiChar(rootPath), nil, 0, nil, nil, @flags, nil, 0) then case windows.getLastError() of
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(rootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := (flags and { windows.FILE_READ_ONLY_VOLUME } $00080000) <> 0;
        end;

        function VolumeFileSystem.isObjectNameCaseSensitive(): boolean;
        var
            rootPath: AnsiString;
        begin
            rootPath := fldCanonicalRootPath;
            windows.setErrorMode(windows.SEM_FAILCRITICALERRORS);
            if not windows.getVolumeInformationA(system.PAnsiChar(rootPath), nil, 0, nil, nil, nil, nil, 0) then case windows.getLastError() of
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(rootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := false;
        end;

        function VolumeFileSystem.isObjectExists(const objectName: UnicodeString): boolean;
        var
            standardNameLength: int;
            internalFullPath: UnicodeString;
        begin
            standardNameLength := objectName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(objectName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndIsExist(internalFullPath);
            if internalFullPath = '' then begin
                result := false;
                exit;
            end;
            if windows.getFileAttributesW(system.PWideChar(internalFullPath)) = windows.INVALID_FILE_ATTRIBUTES then case windows.getLastError() of
            windows.ERROR_FILE_NOT_FOUND,
            windows.ERROR_PATH_NOT_FOUND: begin
                result := false;
                exit;
            end;
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := true;
        end;

        function VolumeFileSystem.isObjectNameValid(const objectName: UnicodeString): boolean;
        var
            index: int;
            length: int;
            beginIndex: int;
            endIndex: int;
        begin
            length := objectName.length;
            if (length > OBJECT_NAME_MAXIMUM_LENGTH) or (objectName.indexOf('//') > 0) then begin
                result := false;
                exit;
            end;
            for index := 0 to length - 1 do case objectName[index + 1] of
            #$0000..#$001f, '<', '>', ':', '"', '\', '|', '?', '*': begin
                result := false;
                exit;
            end;
            end;
            if length > 0 then begin
                beginIndex := 0;
                if objectName[1] = '/' then inc(beginIndex);
                while (beginIndex >= 0) and (beginIndex < length) do begin
                    endIndex := objectName.indexOf('/', beginIndex + 1) - 1;
                    if endIndex < 0 then endIndex := length;
                    if isComponentReserved(objectName.substring(beginIndex + 1, endIndex + 1)) then begin
                        result := false;
                        exit;
                    end;
                    beginIndex := endIndex + 1;
                end;
            end;
            result := true;
        end;

        function VolumeFileSystem.isInternalNameFull(const internalName: UnicodeString): boolean;
        begin
            result := internalName.startsWith('\');
        end;

        function VolumeFileSystem.isInternalNameValid(const internalName: UnicodeString): boolean;
        var
            index: int;
            length: int;
            beginIndex: int;
            endIndex: int;
        begin
            length := internalName.length;
            if (length > OBJECT_NAME_MAXIMUM_LENGTH) or (internalName.indexOf('\\') > 0) then begin
                result := false;
                exit;
            end;
            for index := 0 to length - 1 do case internalName[index + 1] of
            #$0000..#$001f, '<', '>', ':', '"', '/', '|', '?', '*': begin
                result := false;
                exit;
            end;
            end;
            if length > 0 then begin
                beginIndex := 0;
                if internalName[1] = '\' then inc(beginIndex);
                while (beginIndex >= 0) and (beginIndex < length) do begin
                    endIndex := internalName.indexOf('\', beginIndex + 1) - 1;
                    if endIndex < 0 then endIndex := length;
                    if isComponentReserved(internalName.substring(beginIndex + 1, endIndex + 1)) then begin
                        result := false;
                        exit;
                    end;
                    beginIndex := endIndex + 1;
                end;
            end;
            result := true;
        end;

        function VolumeFileSystem.getObjectNameMaximumLength(): int;
        begin
            result := OBJECT_NAME_MAXIMUM_LENGTH;
        end;

        function VolumeFileSystem.totalSize(): long;
        var
            bytesAvailable: long;
            bytesTotal: long;
            bytesFree: long;
            rootPath: AnsiString;
        begin
            bytesAvailable := 0;
            bytesTotal := 0;
            bytesFree := 0;
            rootPath := fldCanonicalRootPath;
            if not windows.getDiskFreeSpaceExA(system.PAnsiChar(rootPath), @bytesAvailable, @bytesTotal, @bytesFree) then case windows.getLastError() of
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(rootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := bytesTotal;
        end;

        function VolumeFileSystem.usedSize(): long;
        var
            bytesAvailable: long;
            bytesTotal: long;
            bytesFree: long;
            rootPath: AnsiString;
        begin
            bytesAvailable := 0;
            bytesTotal := 0;
            bytesFree := 0;
            rootPath := fldCanonicalRootPath;
            if not windows.getDiskFreeSpaceExA(system.PAnsiChar(rootPath), @bytesAvailable, @bytesTotal, @bytesFree) then case windows.getLastError() of
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(rootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := bytesTotal - bytesFree;
        end;

        function VolumeFileSystem.availableSize(): long;
        var
            bytesAvailable: long;
            bytesTotal: long;
            bytesFree: long;
            rootPath: AnsiString;
        begin
            bytesAvailable := 0;
            bytesTotal := 0;
            bytesFree := 0;
            rootPath := fldCanonicalRootPath;
            if not windows.getDiskFreeSpaceExA(system.PAnsiChar(rootPath), @bytesAvailable, @bytesTotal, @bytesFree) then case windows.getLastError() of
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(rootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := bytesFree;
        end;

        function VolumeFileSystem.getCurrentDirectory(): UnicodeString;
        begin
            result := toObjectName(fldCurrentDirectory);
        end;

        function VolumeFileSystem.toInternalName(const objectName: UnicodeString): UnicodeString;
        begin
            result := objectName.replaceAll('/', '\');
        end;

        function VolumeFileSystem.toObjectName(const internalName: UnicodeString): UnicodeString;
        begin
            result := internalName.replaceAll('\', '/');
        end;

        function VolumeFileSystem.findFirst(const objectPath: UnicodeString): ObjectEnumeration;
        var
            internalFullPath: UnicodeString;
            findHandle: system.THandle;
            findData: windows.TWin32FindDataW;
        begin
            if objectPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectPath')
                ]));
            end;
            if not isObjectNameValid(objectPath) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectPath')
                ]));
            end;
            internalFullPath := toVolumeFullPath(objectPath);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectPath')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_OBJECT);
            if internalFullPath.endsWith('\') then internalFullPath := internalFullPath + '*';
            &Array.zeroRaw(findData, sizeof(windows.TWin32FindDataW));
            findHandle := windows.findFirstFileW(system.PWideChar(internalFullPath), @findData);
            if findHandle = windows.INVALID_HANDLE_VALUE then case windows.getLastError() of
            windows.ERROR_FILE_NOT_FOUND,
            windows.ERROR_PATH_NOT_FOUND:
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            windows.ERROR_NOT_READY:
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := VolumeObjectEnumeration.create(long(findHandle), findData);
        end;

        function VolumeFileSystem.createFile(const fileName: UnicodeString): ByteWriter;
        var
            standardNameLength: int;
            internalFullPath: UnicodeString;
            fileHandle: system.THandle;
        begin
            standardNameLength := fileName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(fileName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckCreat(internalFullPath);
            fileHandle := windows.createFileW(
                system.PWideChar(internalFullPath),
                windows.GENERIC_WRITE,
                0,
                nil,
                windows.CREATE_NEW,
                windows.FILE_ATTRIBUTE_NORMAL,
                0
            );
            if fileHandle = windows.INVALID_HANDLE_VALUE then begin
                case windows.getLastError() of
                windows.ERROR_FILE_EXISTS,
                windows.ERROR_ALREADY_EXISTS: ;
                windows.ERROR_DISK_FULL:
                    raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                windows.ERROR_WRITE_PROTECT:
                    raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                windows.ERROR_NOT_READY:
                    raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                else
                    if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
                        raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                    end;
                end;
                raise FileCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.creation'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            end;
            result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
        end;

        function VolumeFileSystem.rewriteFile(const fileName: UnicodeString): ByteWriter;
        var
            standardNameLength: int;
            internalFullPath: UnicodeString;
            fileHandle: system.THandle;
        begin
            standardNameLength := fileName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(fileName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckCreat(internalFullPath);
            fileHandle := windows.createFileW(
                system.PWideChar(internalFullPath),
                windows.GENERIC_WRITE,
                0,
                nil,
                windows.CREATE_ALWAYS,
                windows.FILE_ATTRIBUTE_NORMAL,
                0
            );
            if fileHandle = windows.INVALID_HANDLE_VALUE then begin
                case windows.getLastError() of
                windows.ERROR_DISK_FULL:
                    raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                windows.ERROR_WRITE_PROTECT:
                    raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                windows.ERROR_NOT_READY:
                    raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                else
                    if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
                        raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                    end;
                end;
                raise FileCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.creation'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            end;
            result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
        end;

        function VolumeFileSystem.openFileForAppend(const fileName: UnicodeString): ByteWriter;
        var
            standardNameLength: int;
            internalFullPath: UnicodeString;
            fileHandle: system.THandle;
            filePosition: LongRecord;
        begin
            standardNameLength := fileName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(fileName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_FILE);
            fileHandle := windows.createFileW(
                system.PWideChar(internalFullPath),
                windows.GENERIC_WRITE,
                0,
                nil,
                windows.OPEN_EXISTING,
                windows.FILE_ATTRIBUTE_NORMAL,
                0
            );
            if fileHandle = windows.INVALID_HANDLE_VALUE then begin
                case windows.getLastError() of
                windows.ERROR_FILE_NOT_FOUND,
                windows.ERROR_PATH_NOT_FOUND:
                    raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                        CoAnsiString.create(internalFullPath.toUTF8())
                    ]));
                windows.ERROR_WRITE_PROTECT:
                    raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                windows.ERROR_NOT_READY:
                    raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                else
                    if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
                        raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                    end;
                end;
                raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            end;
            filePosition.value := 0;
            windows.setFilePointer(fileHandle, filePosition.slow, @(filePosition.shigh), windows.FILE_END);
            result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
        end;

        function VolumeFileSystem.openFileForRead(const fileName: UnicodeString): ByteReader;
        var
            standardNameLength: int;
            internalFullPath: UnicodeString;
            fileHandle: system.THandle;
        begin
            standardNameLength := fileName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(fileName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_FILE);
            fileHandle := windows.createFileW(
                system.PWideChar(internalFullPath),
                windows.GENERIC_READ,
                windows.FILE_SHARE_READ,
                nil,
                windows.OPEN_EXISTING,
                windows.FILE_ATTRIBUTE_NORMAL,
                0
            );
            if fileHandle = windows.INVALID_HANDLE_VALUE then begin
                case windows.getLastError() of
                windows.ERROR_FILE_NOT_FOUND,
                windows.ERROR_PATH_NOT_FOUND:
                    raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                        CoAnsiString.create(internalFullPath.toUTF8())
                    ]));
                windows.ERROR_NOT_READY:
                    raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                else
                    if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
                        raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                    end;
                end;
                raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            end;
            result := FileInputStream.create(long(fileHandle));
        end;

        function VolumeFileSystem.openFile(const fileName: UnicodeString): ByteStream;
        var
            standardNameLength: int;
            internalFullPath: UnicodeString;
            fileHandle: system.THandle;
        begin
            standardNameLength := fileName.length;
            if standardNameLength > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := toVolumeFullPath(fileName);
            if internalFullPath.length > OBJECT_NAME_MAXIMUM_LENGTH then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_FILE);
            fileHandle := windows.createFileW(
                system.PWideChar(internalFullPath),
                windows.GENERIC_READ or windows.GENERIC_WRITE,
                0,
                nil,
                windows.OPEN_EXISTING,
                windows.FILE_ATTRIBUTE_NORMAL,
                0
            );
            if fileHandle = windows.INVALID_HANDLE_VALUE then begin
                case windows.getLastError() of
                windows.ERROR_FILE_NOT_FOUND,
                windows.ERROR_PATH_NOT_FOUND:
                    raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                        CoAnsiString.create(internalFullPath.toUTF8())
                    ]));
                windows.ERROR_WRITE_PROTECT:
                    raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                windows.ERROR_NOT_READY:
                    raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                        CoAnsiString.create(fldCanonicalRootPath)
                    ]));
                else
                    if getLastStatus() <> STATUS_FILE_IS_A_DIRECTORY then begin
                        raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                    end;
                end;
                raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
                    CoAnsiString.create(internalFullPath.toUTF8())
                ]));
            end;
            result := FileBidirectStream.create(long(fileHandle), fldCanonicalRootPath);
        end;

        function VolumeFileSystem.newAttributes(): Attributes;
        begin
            result := VolumeObjectAttributes.create();
        end;
    {%endregion}

    {%region  VolumeRequiredAttributes }
        class function VolumeRequiredAttributes.toObjectTime(internalTime: long): long;
        begin
            if internalTime < 0 then begin
                result := CoLong.MIN_VALUE;
                exit;
            end;
            result := internalTime div 10000 + $2debe1767000;
        end;

        class function VolumeRequiredAttributes.toInternalTime(objectTime: long): long;
        begin
            if (objectTime < $2debe1767000) or (objectTime > $000374c83ed9f865) then begin
                result := CoLong.MIN_VALUE;
                exit;
            end;
            result := (objectTime - $2debe1767000) * 10000;
        end;
    {%endregion}

    {%region  VolumeObjectAttributes }
        class procedure VolumeObjectAttributes.initialize();
        var
            index: int;
        begin
            attrIds := [ B_READ_ONLY, B_HIDDEN, B_SYSTEM, '', B_DIRECTORY, B_ARCHIVE, '', '', L_CREATION_TIME, L_LAST_ACCESS_TIME, L_LAST_WRITE_TIME ];
            index := system.length(attrIds);
            attrHashes := &Array.newLong1d(index);
            for index := index - 1 downto 0 do begin
                attrHashes[index] := (CoAnsiString.create(attrIds[index]) as RefCountInterface).getHashCode();
            end;
        end;

        class procedure VolumeObjectAttributes.finalize();
        begin
            attrIds := nil;
            attrHashes := nil;
        end;

        class procedure VolumeObjectAttributes.stringAttributeIdIsInvalid(const attributeId: AnsiString);
        begin
            if attributeId.length <= 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
            end;
            if attributeId.startsWith('s') then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
                    CoAnsiString.create(attributeId)
                ]));
            end;
            raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
                CoAnsiString.create(attributeId)
            ]));
        end;

        class function VolumeObjectAttributes.booleanAttributeIdToIndex(const attributeId: AnsiString): int;
        var
            index: int;
        begin
            if attributeId.length <= 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
            end;
            index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 8);
            if (index >= 0) and (attributeId = attrIds[index]) then begin
                result := index - 0;
                exit;
            end;
            if attributeId.startsWith('b') then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
                    CoAnsiString.create(attributeId)
                ]));
            end;
            raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
                CoAnsiString.create(attributeId)
            ]));
        end;

        class function VolumeObjectAttributes.longAttributeIdToIndex(const attributeId: AnsiString): int;
        var
            index: int;
        begin
            if attributeId.length <= 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
            end;
            index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 8, 8);
            if (index >= 0) and (attributeId = attrIds[index]) then begin
                result := index - 8;
                exit;
            end;
            if attributeId.startsWith('l') then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
                    CoAnsiString.create(attributeId)
                ]));
            end;
            raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
                CoAnsiString.create(attributeId)
            ]));
        end;

        constructor VolumeObjectAttributes.create();
        begin
            inherited create();
            fldTimes := &Array.newLong1d(3);
        end;

        procedure VolumeObjectAttributes.setBooleanAttribute(const attributeId: AnsiString; attributeValue: boolean);
        var
            mask: int;
        begin
            mask := 1 shl booleanAttributeIdToIndex(attributeId);
            if attributeValue then begin
                fldAttributes := fldAttributes or mask;
                exit;
            end;
            fldAttributes := fldAttributes and not mask;
        end;

        procedure VolumeObjectAttributes.setLongAttribute(const attributeId: AnsiString; attributeValue: long);
        var
            index: int;
        begin
            index := longAttributeIdToIndex(attributeId);
            fldTimes[index] := attributeValue;
        end;

        procedure VolumeObjectAttributes.setStringAttribute(const attributeId: AnsiString; const attributeValue: UnicodeString);
        begin
            stringAttributeIdIsInvalid(attributeId);
        end;

        function VolumeObjectAttributes.isSupportedAttributeId(const attributeId: AnsiString): boolean;
        var
            index: int;
        begin
            if attributeId.length <= 0 then begin
                result := false;
                exit;
            end;
            index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 0);
            result := (index >= 0) and (attributeId = attrIds[index]);
        end;

        function VolumeObjectAttributes.getBooleanAttribute(const attributeId: AnsiString): boolean;
        var
            mask: int;
        begin
            mask := 1 shl booleanAttributeIdToIndex(attributeId);
            result := (fldAttributes and mask) <> 0;
        end;

        function VolumeObjectAttributes.getLongAttribute(const attributeId: AnsiString): long;
        var
            index: int;
        begin
            index := longAttributeIdToIndex(attributeId);
            result := fldTimes[index];
        end;

        function VolumeObjectAttributes.getStringAttribute(const attributeId: AnsiString): UnicodeString;
        begin
            stringAttributeIdIsInvalid(attributeId);
            result := ''; { недостижимый код }
        end;

        function VolumeObjectAttributes.displayName(const attributeId: AnsiString): UnicodeString;
        var
            index: int;
        begin
            if attributeId.length <= 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
            end;
            index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 0);
            if (index >= 0) and (attributeId = attrIds[index]) then case index of
             0: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.read-only');
                exit;
            end;
             1: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.hidden');
                exit;
            end;
             2: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.system');
                exit;
            end;
             4: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.directory');
                exit;
            end;
             5: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.archive');
                exit;
            end;
             8: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.creation-time');
                exit;
            end;
             9: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.last-access-time');
                exit;
            end;
            10: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.last-write-time');
                exit;
            end;
            end;
            raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
                CoAnsiString.create(attributeId)
            ]));
        end;

        function VolumeObjectAttributes.getSupportedAttributeIds(): AnsiString_Array1d;
        begin
            result := [ B_DIRECTORY, B_READ_ONLY, B_ARCHIVE, B_HIDDEN, B_SYSTEM, L_LAST_ACCESS_TIME, L_LAST_WRITE_TIME, L_CREATION_TIME ];
        end;
    {%endregion}

    {%region  VolumeObjectEnumeration }
        procedure VolumeObjectEnumeration.setAttributes(const info);
        var
            attr: int;
            findData: windows.TWin32FindDataW absolute info;
        begin
            attr := int(findData.dwFileAttributes);
            name := UnicodeString(system.PWideChar(@(findData.cFileName)));
            if (attr and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0 then begin
                size := 0;
            end else begin
                size := long(findData.nFileSizeLow) + (long(findData.nFileSizeHigh) shl 32);
            end;
            setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, (attr and windows.FILE_ATTRIBUTE_DIRECTORY) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, (attr and windows.FILE_ATTRIBUTE_READONLY) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_ARCHIVE, (attr and windows.FILE_ATTRIBUTE_ARCHIVE) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_HIDDEN, (attr and windows.FILE_ATTRIBUTE_HIDDEN) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_SYSTEM, (attr and windows.FILE_ATTRIBUTE_SYSTEM) <> 0);
            setLongAttribute(VolumeRequiredAttributes.L_CREATION_TIME, VolumeRequiredAttributes.toObjectTime(long(findData.ftCreationTime)));
            setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, VolumeRequiredAttributes.toObjectTime(long(findData.ftLastWriteTime)));
            setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, VolumeRequiredAttributes.toObjectTime(long(findData.ftLastAccessTime)));
        end;

        constructor VolumeObjectEnumeration.create(handle: long; const info);
        begin
            inherited create(VolumeObjectAttributes.create());
            fldHandle := handle;
            setAttributes(info);
        end;

        procedure VolumeObjectEnumeration.close();
        var
            handle: system.THandle;
        begin
            handle := system.THandle(fldHandle);
            destroy;
            if not windows.findClose(handle) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        function VolumeObjectEnumeration.findNext(): boolean;
        var
            findData: windows.TWin32FindDataW;
        begin
            &Array.zeroRaw(findData, sizeof(windows.TWin32FindDataW));
            if windows.findNextFileW(system.THandle(fldHandle), @findData) then begin
                setAttributes(findData);
                result := true;
                exit;
            end;
            if windows.getLastError() <> windows.ERROR_NO_MORE_FILES then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := false;
        end;
    {%endregion}

    {%region  HandleInputStream }
        constructor HandleInputStream.create(handle: long);
        begin
            inherited create();
            fldHandle := handle;
        end;

        procedure HandleInputStream.close();
        begin
        end;

        function HandleInputStream.skip(bytesQuantity: long): long;
        var
            data: int;
            skiped: long;
            readed: system.DWord;
            handle: system.THandle;
        begin
            if bytesQuantity <= 0 then begin
                result := 0;
                exit;
            end;
            data := 0;
            skiped := 0;
            readed := 0;
            handle := system.THandle(fldHandle);
            repeat
                if not windows.readFile(handle, data, 1, readed, nil) then begin
                    if skiped > 0 then break;
                    raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                end;
                if readed = 0 then break;
                inc(skiped);
                dec(bytesQuantity);
            until bytesQuantity > 0;
            result := skiped;
        end;

        function HandleInputStream.read(): int;
        var
            data: int;
            readed: system.DWord;
        begin
            data := 0;
            readed := 0;
            if not windows.readFile(system.THandle(fldHandle), data, 1, readed, nil) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if readed = 0 then begin
                result := -1;
                exit;
            end;
            result := data;
        end;

        function HandleInputStream.read(const dst: byte_Array1d): int;
        begin
            result := read(dst, 0, system.length(dst));
        end;

        function HandleInputStream.read(const dst: byte_Array1d; offset, length: int): int;
        var
            readed: system.DWord;
        begin
            &Array.checkBounds(dst, offset, length);
            if length <= 0 then begin
                result := 0;
                exit;
            end;
            readed := 0;
            if not windows.readFile(system.THandle(fldHandle), dst[offset], system.DWord(length), readed, nil) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if readed = 0 then begin
                result := -1;
                exit;
            end;
            result := int(readed);
        end;
    {%endregion}

    {%region  HandleOutputStream }
        constructor HandleOutputStream.create(handle: long; const rootPath: AnsiString);
        begin
            inherited create();
            fldHandle := handle;
            fldRootPath := rootPath;
        end;

        procedure HandleOutputStream.close();
        begin
        end;

        procedure HandleOutputStream.flush();
        begin
        end;

        procedure HandleOutputStream.write(byteData: int);
        var
            writed: system.DWord;
        begin
            writed := 0;
            if not windows.writeFile(system.THandle(fldHandle), byteData, 1, writed, nil) then case windows.getLastError() of
            windows.ERROR_DISK_FULL:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if writed < 1 then begin
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldRootPath)
                ]));
            end;
        end;

        procedure HandleOutputStream.write(const src: byte_Array1d);
        begin
            write(src, 0, system.length(src));
        end;

        procedure HandleOutputStream.write(const src: byte_Array1d; offset, length: int);
        var
            writed: system.DWord;
        begin
            &Array.checkBounds(src, offset, length);
            if length <= 0 then exit;
            writed := 0;
            if not windows.writeFile(system.THandle(fldHandle), src[offset], system.DWord(length), writed, nil) then case windows.getLastError() of
            windows.ERROR_DISK_FULL:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if writed < system.DWord(length) then begin
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldRootPath)
                ]));
            end;
        end;
    {%endregion}

    {%region  HandleBidirectStream }
        constructor HandleBidirectStream.create(handleForRead, handleForWrite: long);
        begin
            inherited create();
            fldHandleForRead := handleForRead;
            fldHandleForWrite := handleForWrite;
        end;

        destructor HandleBidirectStream.destroy;
        begin
            fldReader.free();
            fldWriter.free();
            inherited destroy;
        end;

        procedure HandleBidirectStream.close();
        var
            handleForRead: system.THandle;
            handleForWrite: system.THandle;
        begin
            handleForRead := system.THandle(fldHandleForRead);
            handleForWrite := system.THandle(fldHandleForWrite);
            destroy;
            if not windows.closeHandle(handleForRead) or (handleForRead <> handleForWrite) and not windows.closeHandle(handleForWrite) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        function HandleBidirectStream.getReader(): ByteReader;
        begin
            result := fldReader;
        end;

        function HandleBidirectStream.getWriter(): ByteWriter;
        begin
            result := fldWriter;
        end;
    {%endregion}

    {%region  FileSeekExtension }
        constructor FileSeekExtension.create(handle: long);
        begin
            inherited create();
            fldHandle := handle;
        end;

        function FileSeekExtension.available(): long;
        var
            handle: system.THandle;
            fileSize: LongRecord;
            filePosition: LongRecord;
        begin
            handle := system.THandle(fldHandle);
            fileSize.value := 0;
            fileSize.ulow := windows.getFileSize(handle, @(fileSize.uhigh));
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            filePosition.value := 0;
            filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := fileSize.value - filePosition.value;
        end;

        function FileSeekExtension.seek(offset: long; from: SeekFrom): long;
        var
            locLength: long;
            locPosition: long;
            handle: system.THandle;
            filePosition: LongRecord;
        begin
            if (from < SeekFrom.sfBegin) or (from > SeekFrom.sfEnd) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('from') ]));
            end;
            handle := system.THandle(fldHandle);
            filePosition.value := 0;
            filePosition.ulow := windows.getFileSize(handle, @(filePosition.uhigh));
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            locLength := filePosition.value;
            case from of
            SeekFrom.sfBegin:
                locPosition := offset;
            SeekFrom.sfEnd:
                locPosition := offset + locLength;
            else
                filePosition.value := 0;
                filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
                if windows.getLastError() <> windows.NO_ERROR then begin
                    raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                end;
                locPosition := offset + filePosition.value;
            end;
            if locPosition < 0 then locPosition := 0;
            if locPosition > locLength then locPosition := locLength;
            filePosition.value := locPosition;
            filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_BEGIN);
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := filePosition.value;
        end;

        function FileSeekExtension.position(): long;
        var
            filePosition: LongRecord;
        begin
            filePosition.value := 0;
            filePosition.ulow := windows.setFilePointer(system.THandle(fldHandle), filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := filePosition.value;
        end;

        function FileSeekExtension.size(): long;
        var
            fileSize: LongRecord;
        begin
            fileSize.value := 0;
            fileSize.ulow := windows.getFileSize(system.THandle(fldHandle), @(fileSize.uhigh));
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := fileSize.value;
        end;
    {%endregion}

    {%region  FileInputStream }
        constructor FileInputStream.create(handle: long; seekable: FileSeekExtension);
        begin
            inherited create(handle);
            if seekable <> nil then begin
                fldOwnedSeekable := false;
            end else begin
                seekable := FileSeekExtension.create(handle);
                fldOwnedSeekable := true;
            end;
            fldExtensions := [ self, seekable ];
        end;

        destructor FileInputStream.destroy;
        begin
            if fldOwnedSeekable then begin
                fldExtensions[1].free();
            end;
            inherited destroy;
        end;

        procedure FileInputStream.close();
        var
            handle: system.THandle;
        begin
            handle := system.THandle(fldHandle);
            destroy;
            if not windows.closeHandle(handle) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure FileInputStream.reset();
        begin
            FileSeekExtension(fldExtensions[1]).seek(fldMarked, SeekFrom.sfBegin);
        end;

        procedure FileInputStream.mark(transferLimit: int);
        var
            filePosition: LongRecord;
        begin
            filePosition.value := 0;
            filePosition.ulow := windows.setFilePointer(system.THandle(fldHandle), filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
            if windows.getLastError() <> windows.NO_ERROR then begin
                fldMarked := 0;
                exit;
            end;
            fldMarked := filePosition.value;
        end;

        function FileInputStream.skip(bytesQuantity: long): long;
        var
            length: long;
            position: long;
            remainder: long;
            handle: system.THandle;
            filePosition: LongRecord;
        begin
            if bytesQuantity <= 0 then begin
                result := 0;
                exit;
            end;
            handle := system.THandle(fldHandle);
            filePosition.value := 0;
            filePosition.ulow := windows.getFileSize(handle, @(filePosition.uhigh));
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            length := filePosition.value;
            filePosition.value := 0;
            filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            position := filePosition.value;
            remainder := length - position;
            if bytesQuantity > remainder then bytesQuantity := remainder;
            filePosition.value := position + bytesQuantity;
            filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_BEGIN);
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := filePosition.value - position;
        end;
    {%endregion}

    {%region  FileOutputStream }
        constructor FileOutputStream.create(handle: long; const rootPath: AnsiString; seekable: FileSeekExtension; reader: FileInputStream);
        begin
            inherited create(handle, rootPath);
            if seekable <> nil then begin
                fldOwnedSeekable := false;
            end else begin
                seekable := FileSeekExtension.create(handle);
                fldOwnedSeekable := true;
            end;
            fldReader := reader;
            fldExtensions := [ self, seekable ];
        end;

        destructor FileOutputStream.destroy;
        begin
            if fldOwnedSeekable then begin
                fldExtensions[1].free();
            end;
            inherited destroy;
        end;

        procedure FileOutputStream.close();
        var
            handle: system.THandle;
        begin
            handle := system.THandle(fldHandle);
            destroy;
            if not windows.closeHandle(handle) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure FileOutputStream.flush();
        begin
            if not windows.flushFileBuffers(system.THandle(fldHandle)) then case windows.getLastError() of
            windows.ERROR_DISK_FULL:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure FileOutputStream.truncate();
        var
            locLength: long;
            handle: system.THandle;
            filePosition: LongRecord absolute locLength;
            reader: FileInputStream;
        begin
            handle := system.THandle(fldHandle);
            if not windows.setEndOfFile(handle) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            filePosition.value := 0;
            filePosition.ulow := windows.setFilePointer(handle, filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
            if windows.getLastError() <> windows.NO_ERROR then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            reader := fldReader;
            if fldMarked > locLength then fldMarked := locLength;
            if (reader <> nil) and (reader.fldMarked > locLength) then reader.fldMarked := locLength;
        end;

        procedure FileOutputStream.reset();
        begin
            FileSeekExtension(fldExtensions[1]).seek(fldMarked, SeekFrom.sfBegin);
        end;

        procedure FileOutputStream.mark(transferLimit: int);
        var
            filePosition: LongRecord;
        begin
            filePosition.value := 0;
            filePosition.ulow := windows.setFilePointer(system.THandle(fldHandle), filePosition.slow, @(filePosition.shigh), windows.FILE_CURRENT);
            if windows.getLastError() <> windows.NO_ERROR then begin
                fldMarked := 0;
                exit;
            end;
            fldMarked := filePosition.value;
        end;
    {%endregion}

    {%region  CurrentEnvironment }
        procedure CurrentEnvironment.update();
        var
            index: int;
            str: UnicodeString;
            name: UnicodeString;
            value: UnicodeString;
            ptr: system.PWideChar;
            block: system.PWideChar;
            vars: HashtableOfUnicodeStringToUnicodeString;
            emon: Mutex;
        begin
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                vars.clear();
                block := windows.getEnvironmentStringsW();
                try
                    ptr := block;
                    while ptr[0] <> #$0000 do begin
                        str := UnicodeString(ptr);
                        index := str.indexOf('=');
                        name := str.substring(1, index).trim();
                        value := str.substring(index + 1).trim();
                        vars[name] := value;
                        inc(ptr, str.length + 1);
                    end;
                finally
                    windows.freeEnvironmentStringsW(block);
                end;
            finally
                emon.endSynchronized();
            end;
        end;

        procedure CurrentEnvironment.setVariable(const name, value: UnicodeString);
        begin
            if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
            end;
            if value.indexOf(#0) > 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('value') ]));
            end;
            if windows.setEnvironmentVariableW(system.PWideChar(name), system.PWideChar(value)) then update();
        end;

        constructor CurrentEnvironment.create();
        begin
            inherited create();
            update();
        end;

        procedure CurrentEnvironment.clear();
        begin
            if setEnvironmentStringsW(#$0000) then update();
        end;

        procedure CurrentEnvironment.assign(anot: Environment);
        var
            index: int;
            name: UnicodeString;
            block: UnicodeString;
        begin
            if anot = nil then begin
                raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('anot') ]));
            end;
            if anot = self then exit;
            block := '';
            for index := 0 to anot.length - 1 do begin
                name := anot.variableAt(index);
                block := block + name + '=' + anot[name] + #$0000;
            end;
            block := block + #$0000;
            if setEnvironmentStringsW(system.PWideChar(block)) then update();
        end;
    {%endregion}

    {%region  CurrentProcess }
        class procedure CurrentProcess.initialize();
        begin
            instance := CurrentProcess.create();
        end;

        class procedure CurrentProcess.finalize();
        begin
            instance.free();
        end;

        class function CurrentProcess.parseCommandLine(): UnicodeString_Array1d;
        const
            SPACE = uchar(#$0020);
            QUOTE = uchar(#$0022);
        var
            quoted: boolean;
            curr: uchar;
            prev: uchar;
            index: int;
            length: int;
            commandLineLength: int;
            commandLineChars: UnicodeString;
            argument: UnicodeString;
            arguments: UnicodeString_Array1d;
            moduleFileName: system.PWideChar;
        begin
            commandLineChars := UnicodeString(system.PWideChar(windows.getCommandLineW()));
            commandLineLength := commandLineChars.length;
            length := 0;
            quoted := false;
            prev := SPACE;
            for index := 0 to commandLineLength - 1 do begin
                curr := commandLineChars[index + 1];
                if curr = QUOTE then begin
                    quoted := not quoted;
                end else
                if not quoted and (prev <> SPACE) and (curr = SPACE) then begin
                    inc(length);
                end;
                if (commandLineLength - index = 1) and (curr <> SPACE) then begin
                    inc(length);
                end;
                prev := curr;
            end;
            arguments := &Array.newUnicodeString1d(length);
            argument := '';
            length := 0;
            quoted := false;
            prev := SPACE;
            for index := 0 to commandLineLength - 1 do begin
                curr := commandLineChars[index + 1];
                if curr = QUOTE then begin
                    quoted := not quoted;
                end else
                if quoted or (curr <> SPACE) then begin
                    argument := argument + curr;
                end else
                if prev <> SPACE then begin
                    arguments[length] := argument;
                    inc(length);
                    argument := '';
                end;
                if (commandLineLength - index = 1) and (curr <> SPACE) then begin
                    arguments[length] := argument;
                    inc(length);
                end;
                prev := curr;
            end;
            moduleFileName := system.PWideChar(system.getMemory(sizeof(uchar) * (windows.MAX_PATH + 1)));
            try
                windows.getModuleFileNameW(0, moduleFileName, windows.MAX_PATH + 1);
                if length <= 0 then begin
                    result := [ UnicodeString(moduleFileName) ];
                    exit;
                end;
                arguments[0] := UnicodeString(moduleFileName);
            finally
                system.freeMemory(moduleFileName);
            end;
            result := arguments;
        end;

        procedure CurrentProcess.setPriority(newPriority: int);
        var
            priorityClass: int;
        begin
            if newPriority < MIN_PRIORITY then newPriority := MIN_PRIORITY;
            if newPriority > MAX_PRIORITY then newPriority := MAX_PRIORITY;
            case newPriority of
            MIN_PRIORITY - 0:
                priorityClass := windows.IDLE_PRIORITY_CLASS;
            LOW_PRIORITY - 1..
            LOW_PRIORITY - 0:
                priorityClass := windows.BELOW_NORMAL_PRIORITY_CLASS;
            NORM_PRIORITY - 1..
            NORM_PRIORITY + 1:
                priorityClass := windows.NORMAL_PRIORITY_CLASS;
            HIGH_PRIORITY + 0..
            HIGH_PRIORITY + 1:
                priorityClass := windows.ABOVE_NORMAL_PRIORITY_CLASS;
            else
                priorityClass := windows.HIGH_PRIORITY_CLASS;
            end;
            windows.setPriorityClass(windows.getCurrentProcess(), system.DWord(priorityClass));
        end;

        procedure CurrentProcess.setWorkingDirectory(const newWorkingDirectory: UnicodeString);
        var
            success: boolean;
            directory: UnicodeString;
            cdmon: Mutex;
        begin
            if not newWorkingDirectory.startsWith('/') then begin
                raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                    CoAnsiString.create('workingDirectory'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
                ]));
            end;
            directory := FileSystemRoot.toInternalPath(newWorkingDirectory);
            cdmon := fldWorkingDirectoryMonitor;
            cdmon.beginSynchronized();
            try
                success := windows.setCurrentDirectoryW(system.PWideChar(directory));
            finally
                cdmon.endSynchronized();
            end;
            if not success then begin
                raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                    CoAnsiString.create('workingDirectory'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
                ]));
            end;
        end;

        procedure CurrentProcess.setCommandLine(const newCommandLine: UnicodeString_Array1d);
        begin
            raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
                CoAnsiString.create('commandLine'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
            ]));
        end;

        procedure CurrentProcess.setStandardInput(newStandardInput: ByteReader);
        begin
            raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
                CoAnsiString.create('standardInput'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
            ]));
        end;

        procedure CurrentProcess.setStandardOutput(newStandardOutput: ByteWriter);
        begin
            raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
                CoAnsiString.create('standardOutput'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
            ]));
        end;

        procedure CurrentProcess.setStandardError(newStandardError: ByteWriter);
        begin
            raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
                CoAnsiString.create('standardError'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
            ]));
        end;

        function CurrentProcess.getPriority(): int;
        begin
            case windows.getPriorityClass(windows.getCurrentProcess()) of
            windows.IDLE_PRIORITY_CLASS:
                result := MIN_PRIORITY;
            windows.BELOW_NORMAL_PRIORITY_CLASS:
                result := LOW_PRIORITY;
            windows.ABOVE_NORMAL_PRIORITY_CLASS:
                result := HIGH_PRIORITY;
            windows.HIGH_PRIORITY_CLASS:
                result := MAX_PRIORITY;
            else
                result := NORM_PRIORITY;
            end;
        end;

        function CurrentProcess.getWorkingDirectory(): UnicodeString;
        var
            length: int;
            directory: UnicodeString;
            cdmon: Mutex;
        begin
            cdmon := fldWorkingDirectoryMonitor;
            cdmon.beginSynchronized();
            try
                length := windows.getCurrentDirectoryW(0, nil) - 1;
                directory := UnicodeString.create(length);
                windows.getCurrentDirectoryW(length + 1, @(directory[1]));
            finally
                cdmon.endSynchronized();
            end;
            if not directory.endsWith('\') then directory := directory + '\';
            result := FileSystemRoot.toObjectPath(directory);
        end;

        function CurrentProcess.createEnvironment(): Environment;
        begin
            result := CurrentEnvironment.create();
        end;

        constructor CurrentProcess.create();
        var
            locStdIn: HandleInputStream;
            locStdOut: HandleOutputStream;
            locStdErr: HandleOutputStream;
        begin
            inherited create();
            locStdIn := HandleInputStream.create(long(windows.getStdHandle(windows.STD_INPUT_HANDLE)));
            locStdOut := HandleOutputStream.create(long(windows.getStdHandle(windows.STD_OUTPUT_HANDLE)));
            locStdErr := HandleOutputStream.create(long(windows.getStdHandle(windows.STD_ERROR_HANDLE)));
            fldStarting := true;
            fldCommandLine := parseCommandLine();
            fldStandardInput := locStdIn;
            fldStandardOutput := locStdOut;
            fldStandardError := locStdErr;
            fldStandardInputRef := locStdIn;
            fldStandardOutputRef := locStdOut;
            fldStandardErrorRef := locStdErr;
            fldWorkingDirectoryMonitor := Mutex.create();
        end;

        destructor CurrentProcess.destroy;
        begin
            fldStandardInputRef.free();
            fldStandardOutputRef.free();
            fldStandardErrorRef.free();
            fldWorkingDirectoryMonitor.free();
            inherited destroy;
        end;

        function CurrentProcess.isTerminated(): boolean;
        begin
            result := false;
        end;

        function CurrentProcess.getExitCode(): int;
        begin
            result := STILL_ACTIVE;
        end;
    {%endregion}

    {$ELSE}

    {%region  OS API — additional}
        const
            LIBC = 'libc';

        const
            SYSCALL_NR_FSYNC        =  long(74);
            SYSCALL_NR_GETTIMEOFDAY =  long(96);
            SYSCALL_NR_STATFS       = long(137);

        const
            DT_DIR = int(4);

        {$PACKRECORDS C}

        type
            PTM = ^TM;

            TM = record
                tm_sec: int;
                tm_min: int;
                tm_hour: int;
                tm_mday: int;
                tm_mon: int;
                tm_year: int;
                tm_wday: int;
                tm_yday: int;
                tm_isdst: int;
                tm_gmtoff: long;
                tm_tm_zone: system.PAnsiChar;
            end;

        {$PACKRECORDS DEFAULT}
        {$CALLING CDECL}

        function doSyscall(__sysnr, __arg1: long): long; external name 'FPC_SYSCALL1';

        function doSyscall(__sysnr, __arg1, __arg2: long): long; external name 'FPC_SYSCALL2';

        function gmtTime(__time: baseunix.PTime_t; __result: PTM): PTM; external LIBC name 'gmtime_r';

        function localTime(__time: baseunix.PTime_t; __result: PTM): PTM; external LIBC name 'localtime_r';

        function unixTime(__time: PTM): baseunix.Time_t; external LIBC name 'mktime';

        {$CALLING REGISTER}
    {%endregion}

    {%region  FileSystemRoot }
        class function FileSystemRoot.deescapeMountPoint(const point: UnicodeString): UnicodeString;
        var
            character: uchar;
            code: int;
            length: int;
            position: int;
            deescaped: UnicodeString;
        begin
            deescaped := point;
            length := deescaped.length - 3;
            position := length + 1;
            repeat
                position := deescaped.lastIndexOf('\', position - 1);
                if (position <= 0) or (position > length) then break;
                code:= 0;
                character := deescaped[position + 1];
                if (character < '0') or (character > '7') then continue;
                code := (code shl 3) + (int(character) - int('0'));
                character := deescaped[position + 2];
                if (character < '0') or (character > '7') then continue;
                code := (code shl 3) + (int(character) - int('0'));
                character := deescaped[position + 3];
                if (character < '0') or (character > '7') then continue;
                code := (code shl 3) + (int(character) - int('0'));
                deescaped := deescaped.substring(1, position) + uchar(code) + deescaped.substring(position + 4);
            until false;
            result := deescaped;
        end;

        class function FileSystemRoot.getRootPathOf(const objectPath: UnicodeString): UnicodeString;
        var
            position: int;
            objectLength: int;
            lastRootLength: int;
            currentRootLength: int;
            lastRootPath: UnicodeString;
            currentRootPath: UnicodeString;
        begin
            objectLength := objectPath.length;
            lastRootLength := 0;
            lastRootPath := '';
            with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
                repeat
                    currentRootPath := readln();
                    if currentRootPath.length <= 0 then break;
                    position := currentRootPath.indexOf(#$0020) + 1;
                    currentRootPath := deescapeMountPoint(currentRootPath.substring(position, currentRootPath.indexOf(#$0020, position)));
                    if currentRootPath.endsWith('/') then currentRootPath := currentRootPath.substring(1, currentRootPath.length);
                    currentRootLength := currentRootPath.length;
                    if objectPath.startsWith(currentRootPath) and ((objectLength <= currentRootLength) or (objectPath[currentRootLength + 1] = '/')) and (currentRootLength > lastRootLength) then begin
                        lastRootLength := currentRootLength;
                        lastRootPath := currentRootPath;
                    end;
                until false;
            finally
                close();
            end;
            result := lastRootPath;
        end;

        class function FileSystemRoot.isObjectPathCaseSensitive(): boolean;
        begin
            result := true;
        end;

        class function FileSystemRoot.isInternalPathFull(const internalPath: UnicodeString): boolean;
        begin
            result := internalPath.startsWith('/');
        end;

        class function FileSystemRoot.toInternalPath(const objectPath: UnicodeString): UnicodeString;
        begin
            result := objectPath.copy();
        end;

        class function FileSystemRoot.toObjectPath(const internalPath: UnicodeString): UnicodeString;
        begin
            result := internalPath.copy();
        end;

        class function FileSystemRoot.getUserCacheDir(): UnicodeString;
        label
            break_label0;
        var
            pos: int;
            checkPath: UnicodeString;
            subDirPath: UnicodeString;
            objectPath: UnicodeString;
            internalPath: UnicodeString;
        begin
            begin
                with CurrentProcess.instance.environment do begin
                    internalPath := variable['XDG_CACHE_HOME'];
                    if internalPath.length <= 0 then begin
                        internalPath := variable['HOME'];
                        if not internalPath.endsWith('/') then internalPath := internalPath + '/';
                        internalPath := internalPath + '.cache/';
                        goto break_label0;
                    end;
                end;
                if not internalPath.endsWith('/') then internalPath := internalPath + '/';
            end;
            break_label0:
            objectPath := toObjectPath(internalPath);
            with get(objectPath), fileSystem do begin
                subDirPath := objectPath.substring(path.length + 1);
                pos := 1;
                repeat
                    pos := subDirPath.indexOf('/', pos + 1);
                    if pos < 1 then break;
                    checkPath := subDirPath.substring(1, pos);
                    if not isObjectExists(checkPath) then createDirectory(checkPath);
                until false;
            end;
            result := objectPath;
        end;

        class function FileSystemRoot.getUserLocalDir(): UnicodeString;
        label
            break_label0;
        var
            pos: int;
            checkPath: UnicodeString;
            subDirPath: UnicodeString;
            objectPath: UnicodeString;
            internalPath: UnicodeString;
        begin
            begin
                with CurrentProcess.instance.environment do begin
                    internalPath := variable['XDG_DATA_HOME'];
                    if internalPath.length <= 0 then begin
                        internalPath := variable['HOME'];
                        if not internalPath.endsWith('/') then internalPath := internalPath + '/';
                        internalPath := internalPath + '.local/share/';
                        goto break_label0;
                    end;
                end;
                if not internalPath.endsWith('/') then internalPath := internalPath + '/';
            end;
            break_label0:
            objectPath := toObjectPath(internalPath);
            with get(objectPath), fileSystem do begin
                subDirPath := objectPath.substring(path.length + 1);
                pos := 1;
                repeat
                    pos := subDirPath.indexOf('/', pos + 1);
                    if pos < 1 then break;
                    checkPath := subDirPath.substring(1, pos);
                    if not isObjectExists(checkPath) then createDirectory(checkPath);
                until false;
            end;
            result := objectPath;
        end;

        class function FileSystemRoot.getUserConfigDir(): UnicodeString;
        label
            break_label0;
        var
            pos: int;
            checkPath: UnicodeString;
            subDirPath: UnicodeString;
            objectPath: UnicodeString;
            internalPath: UnicodeString;
        begin
            begin
                with CurrentProcess.instance.environment do begin
                    internalPath := variable['XDG_CONFIG_HOME'];
                    if internalPath.length <= 0 then begin
                        internalPath := variable['HOME'];
                        if not internalPath.endsWith('/') then internalPath := internalPath + '/';
                        internalPath := internalPath + '.config/';
                        goto break_label0;
                    end;
                end;
                if not internalPath.endsWith('/') then internalPath := internalPath + '/';
            end;
            break_label0:
            objectPath := toObjectPath(internalPath);
            with get(objectPath), fileSystem do begin
                subDirPath := objectPath.substring(path.length + 1);
                pos := 1;
                repeat
                    pos := subDirPath.indexOf('/', pos + 1);
                    if pos < 1 then break;
                    checkPath := subDirPath.substring(1, pos);
                    if not isObjectExists(checkPath) then createDirectory(checkPath);
                until false;
            end;
            result := objectPath;
        end;

        class function FileSystemRoot.enumerate(): FileSystemRoot_Collection1d;
        var
            position: int;
            rootsLength: int;
            mountPoint: UnicodeString;
            rootsCreated: HashtableOfUnicodeStringToFileSystemRoot;
            rootsArray: FileSystemRoot_Array1d;
            rootsCopy: FileSystemRoot_Array1d;
            volumeDescriptor: FileSystemRoot;
        begin
            rootsLength := 0;
            rootsArray := FileSystemRoot_Array1d(&Array.newTObject1d($1f));
            rootsCreated := HashtableOfUnicodeStringToFileSystemRoot.create();
            try
                fileSystemRootMonitor.beginSynchronized();
                try
                    with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
                        repeat
                            mountPoint := readln();
                            if mountPoint.length <= 0 then break;
                            position := mountPoint.indexOf(#$0020) + 1;
                            mountPoint := deescapeMountPoint(mountPoint.substring(position, mountPoint.indexOf(#$0020, position)));
                            if mountPoint.endsWith('/') then mountPoint := mountPoint.substring(1, mountPoint.length);
                            volumeDescriptor := fileSystemRootTable[mountPoint];
                            if volumeDescriptor = nil then begin
                                volumeDescriptor := FileSystemRootDescriptor.create(mountPoint, '/');
                                fileSystemRootRegister(mountPoint, volumeDescriptor);
                                rootsCreated[mountPoint] := volumeDescriptor;
                            end;
                            if rootsLength = system.length(rootsArray) then begin
                                rootsCopy := FileSystemRoot_Array1d(&Array.newTObject1d((rootsLength shl 1) or 1));
                                &Array.copyObjects(rootsArray, 0, rootsCopy, 0, rootsLength);
                                rootsArray := rootsCopy;
                            end;
                            rootsArray[rootsLength] := volumeDescriptor;
                            inc(rootsLength);
                        until false;
                    finally
                        close();
                    end;
                finally
                    fileSystemRootMonitor.endSynchronized();
                end;
            finally
                rootsCreated.free();
            end;
            result := FileSystemRootCollection.create(rootsArray, rootsLength);
        end;

        class function FileSystemRoot.get(const objectPath: UnicodeString): FileSystemRoot;
        var
            rootPath: UnicodeString;
            volumeDescriptor: FileSystemRoot;
        begin
            rootPath := getRootPathOf(objectPath);
            fileSystemRootMonitor.beginSynchronized();
            try
                volumeDescriptor := fileSystemRootTable[rootPath];
                if volumeDescriptor = nil then begin
                    volumeDescriptor := FileSystemRootDescriptor.create(rootPath, '/');
                    fileSystemRootRegister(rootPath, volumeDescriptor);
                end;
            finally
                fileSystemRootMonitor.endSynchronized();
            end;
            result := volumeDescriptor;
        end;

        constructor FileSystemRoot.create(const path: UnicodeString);
        begin
            inherited create();
            fldPath := path;
        end;
    {%endregion}

    {%region  Environment }
        procedure Environment.setVariable(const name, value: UnicodeString);
        var
            emon: Mutex;
        begin
            if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
            end;
            if value.indexOf(#0) > 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('value') ]));
            end;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                fldVariables[name] := value;
            finally
                emon.endSynchronized();
            end;
        end;

        function Environment.getLength(): int;
        begin
            result := fldVariables.length;
        end;

        function Environment.getVariable(const name: UnicodeString): UnicodeString;
        var
            emon: Mutex;
        begin
            if (name.indexOf(#0) > 0) or (name.indexOf('=') > 0) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('name') ]));
            end;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                result := fldVariables[name];
            finally
                emon.endSynchronized();
            end;
        end;

        constructor Environment.create();
        begin
            inherited create();
            fldMonitor := Mutex.create();
            fldVariables := HashtableOfUnicodeStringToUnicodeString.create();
        end;

        destructor Environment.destroy;
        begin
            fldMonitor.free();
            fldVariables.free();
            inherited destroy;
        end;

        procedure Environment.clear();
        var
            emon: Mutex;
        begin
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                fldVariables.clear();
            finally
                emon.endSynchronized();
            end;
        end;

        procedure Environment.assign(anot: Environment);
        var
            index: int;
            vname: UnicodeString;
            vars: HashtableOfUnicodeStringToUnicodeString;
            emon: Mutex;
        begin
            if anot = nil then begin
                raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('anot') ]));
            end;
            if anot = self then exit;
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                vars.clear();
                for index := 0 to anot.length - 1 do begin
                    vname := anot.variableAt(index);
                    vars[vname] := anot[vname];
                end;
            finally
                emon.endSynchronized();
            end;
        end;

        function Environment.equals(anot: TObject): boolean;
        var
            count: int;
            aindex: int;
            aname: UnicodeString;
            vars: HashtableOfUnicodeStringToUnicodeString;
            aenv: Environment;
            emon: Mutex;
        begin
            if not(anot is Environment) then begin
                result := false;
                exit;
            end;
            if anot = self then begin
                result := true;
                exit;
            end;
            aenv := Environment(anot);
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                count := aenv.length;
                if vars.length <> count then begin
                    result := false;
                    exit;
                end;
                for aindex := count - 1 downto 0 do begin
                    aname := aenv.variableAt(aindex);
                    if aenv[aname] <> vars[aname] then begin
                        result := false;
                        exit;
                    end;
                end;
            finally
                emon.endSynchronized();
            end;
            result := true;
        end;

        function Environment.toString(): AnsiString;
        var
            index: int;
            text: UnicodeString;
            name: UnicodeString;
            vars: HashtableOfUnicodeStringToUnicodeString;
            emon: Mutex;
        begin
            text := '';
            vars := fldVariables;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                for index := 0 to vars.length - 1 do begin
                    name := vars.keyAt(index);
                    text := text + name + '=' + vars[name] + CoUnicodeString.LINE_ENDING;
                end;
            finally
                emon.endSynchronized();
            end;
            result := text.toUTF8();
        end;

        function Environment.isEmulation(): boolean;
        begin
            result := false;
        end;

        function Environment.isCaseSensitive(): boolean;
        begin
            result := true;
        end;

        function Environment.contains(const name: UnicodeString): boolean;
        var
            emon: Mutex;
        begin
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                result := fldVariables.contains(name);
            finally
                emon.endSynchronized();
            end;
        end;

        function Environment.variableAt(index: int): UnicodeString;
        var
            emon: Mutex;
        begin
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                result := fldVariables.keyAt(index);
            finally
                emon.endSynchronized();
            end;
        end;
    {%endregion}

    {%region  Process }
        class function Process.getExitCodeProcess(processId: int; status: Pint): boolean;
        var
            resultId: int;
        begin
            resultId := safeWaitPid(processId, status, baseunix.WNOHANG);
            if resultId = 0 then begin
                status^ := STILL_ACTIVE;
                result := true;
                exit;
            end;
            if resultId < 0 then begin
                status^ := 0;
                result := false;
                exit;
            end;
            result := true;
        end;

        class function Process.safeDup2(oldFD, newFD: int): int;
        var
            callResult: int;
        begin
            repeat
                callResult := baseunix.fpDup2(oldFD, newFD);
            until (callResult <> -1) or (baseunix.fpGetErrNo() <> baseunix.ESysEINTR);
            result := callResult;
        end;

        class function Process.safeWaitPid(processId: int; status: Pint; options: int): int;
        var
            callResult: int;
        begin
            repeat
                callResult := baseunix.fpWaitPid(processId, status, options);
            until (callResult <> -1) or (baseunix.fpGetErrNo() <> baseunix.ESysEINTR);
            result := callResult;
        end;

        class function Process.hasInstance(): boolean;
            function isDigitalName(const name: AnsiString): boolean; inline;
            var
                idx: int;
            begin
                for idx := 0 to name.length - 1 do if not CoChar.isDigit(name[idx + 1]) then begin
                    result := false;
                    exit;
                end;
                result := true;
            end;
        var
            objectType: int;
            objectName: AnsiString;
            currentProcessId: AnsiString;
            currentExecutablePath: AnsiString;
            objectInfo: baseunix.PDirEnt;
            directoryInfo: baseunix.PDir;
        begin
            currentProcessId := CoInt.toString(baseunix.fpGetPid());
            currentExecutablePath := baseunix.fpReadLink('/proc/self/exe');
            directoryInfo := baseunix.fpOpenDir('/proc/');
            if directoryInfo = nil then begin
                result := false;
                exit;
            end;
            try
                repeat
                    objectInfo := baseunix.fpReadDir(directoryInfo^);
                    if objectInfo = nil then break;
                    objectName := AnsiString(system.PAnsiChar(@(objectInfo^.d_name)));
                    objectType := objectInfo^.d_type;
                    if
                        (objectName <> '.') and (objectName <> '..') and (objectType = DT_DIR) and
                        isDigitalName(objectName) and (currentProcessId <> objectName) and (currentExecutablePath = baseunix.fpReadLink('/proc/' + objectName + '/exe'))
                    then begin
                        result := true;
                        exit;
                    end;
                until false;
            finally
                baseunix.fpCloseDir(directoryInfo^);
            end;
            result := false;
        end;

        class function Process.createPipe(): ByteStream;
        var
            handles: baseunix.TFilDes;
        begin
            &Array.zeroRaw(handles, sizeof(baseunix.TFilDes));
            if baseunix.fpPipe(handles) <> 0 then begin
                raise errorOutOfResources;
            end;
            result := PipeStream.create(handles[0], handles[1]);
        end;

        class function Process.current(): Process;
        begin
            result := CurrentProcess.instance;
        end;

        procedure Process.setPriority(newPriority: int);
        begin
            if newPriority < MIN_PRIORITY then newPriority := MIN_PRIORITY;
            if newPriority > MAX_PRIORITY then newPriority := MAX_PRIORITY;
            fldPriority := newPriority;
        end;

        procedure Process.setWorkingDirectory(const newWorkingDirectory: UnicodeString);
        begin
            fldWorkingDirectory := newWorkingDirectory.copy();
        end;

        procedure Process.setCommandLine(const newCommandLine: UnicodeString_Array1d);
        var
            index: int;
            count: int;
            element: UnicodeString;
            locCommandLine: UnicodeString_Array1d;
        begin
            count := system.length(newCommandLine);
            locCommandLine := nil;
            for index := 0 to count - 1 do begin
                element := newCommandLine[index].copy();
                if (element.indexOf('"') > 0) or (element.indexOf(#0) > 0) then begin
                    raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                        CoAnsiString.create('commandLine'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
                    ]));
                end;
                if index = 0 then begin
                    if not element.startsWith('/') then begin
                        raise IllegalPropertyValueException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-property.command-line'));
                    end;
                    locCommandLine := &Array.newUnicodeString1d(count);
                end;
                locCommandLine[index] := element;
            end;
            fldCommandLine := locCommandLine;
        end;

        procedure Process.setEnvironment(newEnvironment: Environment);
        begin
            fldEnvironment.assign(newEnvironment);
        end;

        procedure Process.setStandardInput(newStandardInput: ByteReader);
        begin
            if (newStandardInput <> nil) and not Lang.isInstance(newStandardInput, HandleInputStream) then begin
                raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                    CoAnsiString.create('standardInput'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
                ]));
            end;
            fldStandardInput := newStandardInput;
        end;

        procedure Process.setStandardOutput(newStandardOutput: ByteWriter);
        begin
            if (newStandardOutput <> nil) and not Lang.isInstance(newStandardOutput, HandleOutputStream) then begin
                raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                    CoAnsiString.create('standardOutput'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
                ]));
            end;
            fldStandardOutput := newStandardOutput;
        end;

        procedure Process.setStandardError(newStandardError: ByteWriter);
        begin
            if (newStandardError <> nil) and not Lang.isInstance(newStandardError, HandleOutputStream) then begin
                raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                    CoAnsiString.create('standardError'), CoAnsiString.create(Lang.classFor(Process).getCanonicalName())
                ]));
            end;
            fldStandardError := newStandardError;
        end;

        procedure Process.clearStarting();
        begin
            fldStarting := false;
        end;

        function Process.getPriority(): int;
        begin
            result := fldPriority;
        end;

        function Process.getWorkingDirectory(): UnicodeString;
        begin
            result := fldWorkingDirectory.copy();
        end;

        function Process.createEnvironment(): Environment;
        begin
            result := nil;
        end;

        function Process.isStarting(): boolean; assembler; nostackframe;
        asm
                        mov         eax,    true
                        xchg        byte    [rdi+offset fldStarting], al
                        movzx       eax,    al
        end;

        function Process.getCommandLine(): UnicodeString_Array1d;
        var
            index: int;
            count: int;
            commandLineData: UnicodeString_Array1d;
            commandLineCopy: UnicodeString_Array1d;
        begin
            commandLineData := fldCommandLine;
            count := system.length(commandLineData);
            commandLineCopy := &Array.newUnicodeString1d(count);
            for index := count - 1 downto 0 do begin
                commandLineCopy[index] := commandLineData[index].copy();
            end;
            result := commandLineCopy;
        end;

        constructor Process.create();
        var
            locCurrentProcess: Process;
            locEnvironment: Environment;
        begin
            inherited create();
            locEnvironment := createEnvironment();
            if locEnvironment = nil then begin
                locEnvironment := platform.independent.osservices.Environment.create();
            end;
            locCurrentProcess := CurrentProcess.instance;
            if locCurrentProcess <> nil then begin
                locEnvironment.assign(locCurrentProcess.environment);
                fldWorkingDirectory := locCurrentProcess.workingDirectory;
                fldStandardInput := locCurrentProcess.fldStandardInput;
                fldStandardOutput := locCurrentProcess.fldStandardOutput;
                fldStandardError := locCurrentProcess.fldStandardError;
            end;
            fldPriority := NORM_PRIORITY;
            fldEnvironment := locEnvironment;
        end;

        destructor Process.destroy;
        begin
            fldEnvironment.free();
            inherited destroy;
        end;

        procedure Process.start();
        var
            stdInHandle: int;
            stdOutHandle: int;
            stdErrHandle: int;
            index: int;
            count: int;
            processId: int;
            processExitCode: int;
            locWorkingDirectory: AnsiString;
            element: UnicodeString;
            commandLineArray: AnsiString_Array1d;
            environmentArray: AnsiString_Array1d;
            commandLineData: UnicodeString_Array1d;
            environmentData: Environment;
            stdInStream: HandleInputStream;
            stdOutStream: HandleOutputStream;
            stdErrStream: HandleOutputStream;
            attributes: baseunix.Stat;
        begin
            if isStarting() then begin
                raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
            end;
            try
                processExitCode := 0;
                processId := fldProcessId;
                if (processId > 0) and getExitCodeProcess(processId, @processExitCode) and (processExitCode = STILL_ACTIVE) then begin
                    raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
                end;
                commandLineData := fldCommandLine;
                count := system.length(commandLineData);
                if count <= 0 then begin
                    raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process.command-line'));
                end;
                { приоритет }
                { === приоритеты процессов не поддерживаются здесь === }
                { рабочая папка }
                locWorkingDirectory := FileSystemRoot.toInternalPath(fldWorkingDirectory).toUTF8();
                { командная строка }
                commandLineArray := &Array.newAnsiString1d(count + 1);
                for index := 0 to count - 1 do begin
                    commandLineArray[index] := commandLineData[index].toUTF8();
                end;
                { переменные среды }
                environmentData := fldEnvironment;
                count := environmentData.length;
                environmentArray := &Array.newAnsiString1d(count + 1);
                for index := 0 to count - 1 do begin
                    element := environmentData.variableAt(index);
                    environmentArray[index] := (element + '=' + environmentData[element]).toUTF8();
                end;
                { стандартные потоки ввода-вывода }
                stdInStream := HandleInputStream(Lang.cast(fldStandardInput, HandleInputStream));
                stdInHandle := -1;
                if stdInStream <> nil then stdInHandle := int(stdInStream.fldHandle);
                stdOutStream := HandleOutputStream(Lang.cast(fldStandardOutput, HandleOutputStream));
                stdOutHandle := -1;
                if stdOutStream <> nil then stdOutHandle := int(stdOutStream.fldHandle);
                stdErrStream := HandleOutputStream(Lang.cast(fldStandardError, HandleOutputStream));
                stdErrHandle := -1;
                if stdErrStream <> nil then stdErrHandle := int(stdErrStream.fldHandle);
                { создание процесса }
                &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
                if (baseunix.fpStat(commandLineArray[0], attributes) <> 0) or ((attributes.st_mode and baseunix.S_IFREG) = 0) then begin
                    raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                        CoAnsiString.create(commandLineArray[0])
                    ]));
                end;
                if (attributes.st_mode and (baseunix.S_IXUSR + baseunix.S_IXGRP + baseunix.S_IXOTH)) <> (baseunix.S_IXUSR + baseunix.S_IXGRP + baseunix.S_IXOTH) then begin
                    raise FileOperationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.operation'), [
                        CoAnsiString.create(commandLineArray[0])
                    ]));
                end;
                if (baseunix.fpStat(locWorkingDirectory, attributes) <> 0) or ((attributes.st_mode and baseunix.S_IFDIR) = 0) then begin
                    raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                        CoAnsiString.create(locWorkingDirectory)
                    ]));
                end;
                processId := baseunix.fpFork();
                if processId < 0 then begin
                    raise errorOutOfResources;
                end;
                if processId > 0 then begin
                    { этот процесс }
                    fldProcessId := processId;
                end else begin
                    { новый процесс }
                    baseunix.fpChDir(locWorkingDirectory);
                    if stdInHandle >= 0 then safeDup2(stdInHandle, 0);
                    if stdOutHandle >= 0 then safeDup2(stdOutHandle, 1);
                    if stdErrHandle >= 0 then safeDup2(stdErrHandle, 2);
                    baseunix.fpExecve(commandLineArray[0], system.PPChar(commandLineArray), system.PPChar(environmentArray));
                    baseunix.fpExit(127);
                end;
            finally
                clearStarting();
            end;
        end;

        procedure Process.join();
        var
            processId: int;
        begin
            processId := fldProcessId;
            if processId <= 0 then begin
                raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
            end;
            safeWaitPid(processId, nil, 0);
        end;

        procedure Process.join(timeInMillis: long);
        var
            processId: int;
        begin
            processId := fldProcessId;
            if processId <= 0 then begin
                raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
            end;
            if timeInMillis < 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInMillis') ]));
            end;
            if timeInMillis > $fffffffe then timeInMillis := $fffffffe;
            if timeInMillis = 0 then timeInMillis := -1;
            repeat
                Thread.sleep(1);
                if safeWaitPid(processId, nil, baseunix.WNOHANG) > 0 then break;
                if timeInMillis > 0 then dec(timeInMillis);
            until timeInMillis = 0;
        end;

        procedure Process.join(timeInMillis: long; timeInNanos: int);
        var
            processId: int;
        begin
            processId := fldProcessId;
            if processId <= 0 then begin
                raise IllegalProcessStateException.create(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'illegal-state.process'));
            end;
            if timeInMillis < 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInMillis') ]));
            end;
            if (timeInNanos < 0) or (timeInNanos > 999999) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('timeInNanos') ]));
            end;
            if (timeInMillis < CoLong.MAX_VALUE) and (timeInNanos >= 500000) or (timeInMillis = 0) and (timeInNanos > 0) then inc(timeInMillis);
            if timeInMillis > $fffffffe then timeInMillis := $fffffffe;
            if timeInMillis = 0 then timeInMillis := -1;
            repeat
                Thread.sleep(1);
                if safeWaitPid(processId, nil, baseunix.WNOHANG) > 0 then break;
                if timeInMillis > 0 then dec(timeInMillis);
            until timeInMillis = 0;
        end;

        function Process.isTerminated(): boolean;
        var
            processId: int;
        begin
            processId := fldProcessId;
            result := (processId > 0) and (safeWaitPid(processId, nil, baseunix.WNOHANG) > 0);
        end;

        function Process.getExitCode(): int;
        var
            processId: int;
            processExitCode: int;
        begin
            processExitCode := 0;
            processId := fldProcessId;
            if (processId <= 0) or not getExitCodeProcess(processId, @processExitCode) then begin
                result := 0;
                exit;
            end;
            result := processExitCode;
        end;
    {%endregion}

    {%region  TimeBase }
        class function TimeBase.currentOffsetInMillis(): int;
        var
            timeStruct: TM;
            timeInSeconds: baseunix.Time_t;
        begin
            &Array.zeroRaw(timeStruct, sizeof(TM));
            timeInSeconds := baseunix.fpTime();
            localTime(@timeInSeconds, @timeStruct);
            result := 1000 * int(timeStruct.tm_gmtoff);
        end;

        class function TimeBase.currentTimeInMillis(): long;
        var
            timeStruct: TM;
            timeInMicros: baseunix.TimeVal;
        begin
            &Array.zeroRaw(timeStruct, sizeof(TM));
            &Array.zeroRaw(timeInMicros, sizeof(baseunix.TimeVal));
            doSyscall(SYSCALL_NR_GETTIMEOFDAY, long(@timeInMicros), long(nil));
            gmtTime(@(timeInMicros.tv_sec), @timeStruct);
            with timeStruct do result := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(timeInMicros.tv_usec) div 1000);
        end;
    {%endregion}

    {%region  MemoryManager }
        class procedure MemoryManager.deallocate(region: MemoryRegion; flags: int);
        var
            address: long;
            size: long;
            reference: Pointer absolute address;
        begin
            if region = nil then begin
                raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('region') ]));
            end;
            if not(region is MemoryRegionDescriptor) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('region') ]));
            end;
            address := region.address;
            size := region.size;
            if (flags and RESERVED) <> 0 then begin
                baseunix.fpMProtect(reference, unixtype.size_t(size), baseunix.PROT_NONE);
                exit;
            end;
            baseunix.fpMUnmap(reference, baseunix.size_t(size));
        end;

        class function MemoryManager.enumerate(): MemoryRegion_Collection1d;
        var
            regionsLength: int;
            beginPosition: int;
            endPosition: int;
            beginAddress: long;
            endAddress: long;
            line: UnicodeString;
            regionsData: MemoryRegion_Array1d;
            regionsCopy: MemoryRegion_Array1d;
        begin
            regionsLength := 0;
            regionsData := MemoryRegion_Array1d(&Array.newIUnknown1d($7f));
            try
                with DataInputStream.create(FileInputStream.create(baseunix.fpOpen(system.PAnsiChar('/proc/self/maps'), baseunix.O_RDONLY))), bigEndianDataInput do try
                    repeat
                        line := readln();
                        if line.length <= 0 then break;
                        beginPosition := line.indexOf('-');
                        endPosition := line.indexOf(#$0020, beginPosition + 1);
                        beginAddress := CoLong.parseUnsigned(line.substring(1, beginPosition).toUTF8(), $10);
                        endAddress := CoLong.parseUnsigned(line.substring(beginPosition + 1, endPosition).toUTF8(), $10);
                        if regionsLength = system.length(regionsData) then begin
                            regionsCopy := MemoryRegion_Array1d(&Array.newIUnknown1d((regionsLength shl 1) or 1));
                            &Array.copyUnknowns(regionsData, 0, regionsCopy, 0, regionsLength);
                            regionsData := regionsCopy;
                        end;
                        regionsData[regionsLength] := MemoryRegionDescriptor.create(beginAddress, endAddress - beginAddress);
                        inc(regionsLength);
                    until false;
                finally
                    close();
                end;
            except
            end;
            result := MemoryRegionCollection.create(regionsData, regionsLength);
        end;

        class function MemoryManager.allocate(address, size: long; flags: int): MemoryRegion;
        var
            allocationProtect: int;
            reference: Pointer absolute address;
        begin
            if (size < 0) or (size > MEMORY_LIMIT_ADDRESS - MEMORY_START_ADDRESS) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('size') ]));
            end;
            if (address + size > MEMORY_LIMIT_ADDRESS) or (address < MEMORY_START_ADDRESS) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('address') ]));
            end;
            address := address and -PAGE_SIZE;
            if size = 0 then size := 1;
            size := size + (-size and (PAGE_SIZE - 1));
            allocationProtect := baseunix.PROT_NONE;
            if (flags and READABLE) <> 0 then begin
                inc(allocationProtect, baseunix.PROT_READ);
            end;
            if (flags and WRITEABLE) <> 0 then begin
                inc(allocationProtect, baseunix.PROT_WRITE);
            end;
            if (flags and EXECUTABLE) <> 0 then begin
                inc(allocationProtect, baseunix.PROT_EXEC);
            end;
            if ((flags and RESERVED) <> 0) or (baseunix.fpMProtect(reference, unixtype.size_t(size), allocationProtect) <> 0) then begin
                reference := baseunix.fpMMap(reference, unixtype.size_t(size), allocationProtect, baseunix.MAP_PRIVATE + baseunix.MAP_ANONYMOUS, 0, 0);
                if address = -1 then case baseunix.fpGetErrNo() of
                baseunix.ESYSENOMEM:
                    raise errorOutOfResources;
                else
                    raise IllegalArgumentException.create(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.passed'));
                end;
            end;
            result := MemoryRegionDescriptor.create(address, size);
        end;
    {%endregion}

    {%region  SystemInfo }
        class procedure SystemInfo.initialize();
        var
            readed: boolean;
            cpuBrandLength: int;
            line: UnicodeString;
            cpuBrandArray: system.PAnsiChar;
        begin
            try
                { количество потоков процессора }
                cpuNumberOfCores := 0;
                with DataInputStream.create(FileInputStream.create(baseunix.fpOpen(system.PAnsiChar('/proc/cpuinfo'), baseunix.O_RDONLY))), bigEndianDataInput do try
                    repeat
                        line := readln();
                        if line.length <= 0 then break;
                        readed := false;
                        repeat
                            if not readed and (line.substring(1, line.indexOf(':')).trim() = 'processor') then begin
                                inc(cpuNumberOfCores);
                                readed := true;
                            end;
                            line := readln();
                            if line.length <= 0 then break;
                        until false;
                    until false;
                finally
                    close();
                end;
                { название модели процессора }
                cpuBrandArray := system.PAnsiChar(system.getMemory($30 * sizeof(char)));
                try
                    cpuBrandLength := cpuReadBrandStringTo(cpuBrandArray);
                    cpuBrandString := AnsiString.create(cpuBrandLength);
                    &Array.copyRaw(cpuBrandArray^, cpuBrandString[1], cpuBrandLength * sizeof(char));
                finally
                    system.freeMemory(cpuBrandArray);
                end;
                { версия операционной системы }
                osVersion := '';
                with DataInputStream.create(FileInputStream.create(baseunix.fpOpen(system.PAnsiChar('/proc/version'), baseunix.O_RDONLY))), bigEndianDataInput do try
                    repeat
                        line := read();
                        if line = 'version' then begin
                            osVersion := read().toUTF8();
                            break;
                        end;
                    until false;
                finally
                    close();
                end;
            except
            end;
        end;

        class function SystemInfo.getProcessorCodeBits(): int;
        begin
            result := 64;
        end;

        class function SystemInfo.getProcessorNumberOfCores(): int;
        begin
            result := cpuNumberOfCores;
        end;

        class function SystemInfo.getProcessorBrandString(): AnsiString;
        begin
            result := cpuBrandString;
        end;

        class function SystemInfo.getOperatingSystemName(): AnsiString;
        begin
            result := 'GNU Linux';
        end;

        class function SystemInfo.getOperatingSystemVersion(): AnsiString;
        begin
            result := osVersion;
        end;
    {%endregion}

    {%region  MemoryRegionDescriptor }
        function MemoryRegionDescriptor.getProtectionString(): AnsiString;
        var
            beginPosition: int;
            endPosition: int;
            address: long;
            line: UnicodeString;
        begin
            address := fldAddress;
            try
                with DataInputStream.create(FileInputStream.create(baseunix.fpOpen(system.PAnsiChar('/proc/self/maps'), baseunix.O_RDONLY))), bigEndianDataInput do try
                    repeat
                        line := readln();
                        if line.length <= 0 then break;
                        endPosition := line.indexOf('-');
                        if CoLong.parseUnsigned(line.substring(1, endPosition).toUTF8(), $10) = address then begin
                            beginPosition := line.indexOf(#$0020, endPosition + 1);
                            result := line.substring(beginPosition + 1, beginPosition + 5).toUTF8();
                            exit;
                        end;
                    until false;
                finally
                    close();
                end;
            except
            end;
            result := '----';
        end;

        constructor MemoryRegionDescriptor.create(address, size: long);
        begin
            inherited create();
            fldAddress := address;
            fldSize := size;
        end;

        procedure MemoryRegionDescriptor.setProtectionBits(newProtectionBits: int);
        var
            protect: int;
            address: long;
            reference: Pointer absolute address;
        begin
            address := fldAddress;
            protect := baseunix.PROT_NONE;
            if (newProtectionBits and MemoryManager.READABLE) <> 0 then begin
                inc(protect, baseunix.PROT_READ);
            end;
            if (newProtectionBits and MemoryManager.WRITEABLE) <> 0 then begin
                inc(protect, baseunix.PROT_WRITE);
            end;
            if (newProtectionBits and MemoryManager.EXECUTABLE) <> 0 then begin
                inc(protect, baseunix.PROT_EXEC);
            end;
            baseunix.fpMProtect(reference, unixtype.size_t(fldSize), protect);
        end;

        function MemoryRegionDescriptor.equals(anot: TObject): boolean;
        var
            amrd: MemoryRegionDescriptor;
        begin
            if not(anot is MemoryRegionDescriptor) then begin
                result := false;
                exit;
            end;
            if anot = self then begin
                result := true;
                exit;
            end;
            amrd := MemoryRegionDescriptor(anot);
            result := (fldAddress = amrd.fldAddress) and (fldSize = amrd.fldSize);
        end;

        function MemoryRegionDescriptor.getHashCode(): long;
        begin
            result := fldAddress xor CoLong.rol(fldSize, 35);
        end;

        function MemoryRegionDescriptor.toString(): AnsiString;
        var
            address: long;
        begin
            address := fldAddress;
            result := CoLong.toHexString(address) + '-' + CoLong.toHexString(address + fldSize) + #$20 + getProtectionString();
        end;

        function MemoryRegionDescriptor.getProtectionBit(index: int): boolean;
        begin
            if (index < 0) or (index > 3) then begin
                result := false;
                exit;
            end;
            result := getProtectionString()[index + 1] <> '-';
        end;

        function MemoryRegionDescriptor.getProtectionBits(): int;
        var
            index: int;
            protect: int;
            str: AnsiString;
        begin
            protect := 0;
            str := getProtectionString();
            for index := 0 to 3 do if str[index + 1] <> '-' then begin
                inc(protect, 1 shl index);
            end;
            result := protect;
        end;

        function MemoryRegionDescriptor.getAddress(): long;
        begin
            result := fldAddress;
        end;

        function MemoryRegionDescriptor.getSize(): long;
        begin
            result := fldSize;
        end;

        function MemoryRegionDescriptor.pointerTo(offset: long): Pointer;
        var
            address: long;
            reference: Pointer absolute address;
        begin
            if (offset < 0) or (offset > fldSize) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('offset') ]));
            end;
            address := fldAddress + offset;
            result := reference;
        end;
    {%endregion}

    {%region  FileSystemRootDescriptor }
        function FileSystemRootDescriptor.getName(): UnicodeString;
        begin
            result := '';
        end;

        function FileSystemRootDescriptor.getFileSystem(): FileSystem;
        begin
            result := fldFileSystem;
        end;

        constructor FileSystemRootDescriptor.create(const argPath, argCurrentDirectory: UnicodeString);
        begin
            inherited create(argPath);
            fldFileSystem := VolumeFileSystem.create(toInternalPath(argPath), argCurrentDirectory.copy());
        end;

        destructor FileSystemRootDescriptor.destroy;
        begin
            fldFileSystem.free();
            inherited destroy;
        end;
    {%endregion}

    {%region  VolumeFileSystem }
        function VolumeFileSystem.toVolumeFullPath(const objectName: UnicodeString): UnicodeString;
        var
            internalName: UnicodeString;
        begin
            internalName := toInternalName(objectName);
            if isInternalNameFull(internalName) then begin
                result := internalName;
                exit;
            end;
            result := fldCurrentDirectory + internalName;
        end;

        function VolumeFileSystem.makeInternalFullPathAndIsExist(const volumeFullPath: UnicodeString): UnicodeString;
        label
            break_label0;
        var
            error: int;
            dotPosition: int;
            reductionPosition: int;
            internalRootPath: UnicodeString;
            internalFullPath: UnicodeString;
            internalResultPath: UnicodeString;
            attributes: baseunix.Stat;
        begin
            internalRootPath := fldInternalRootPath;
            internalFullPath := volumeFullPath;
            dotPosition := 1;
            repeat
                dotPosition := internalFullPath.indexOf('/./', dotPosition);
                if dotPosition <= 0 then begin
                    if not internalFullPath.endsWith('/.') then break;
                    dotPosition := internalFullPath.length - 1;
                end;
                internalFullPath := internalFullPath.substring(1, dotPosition) + internalFullPath.substring(dotPosition + 2);
            until false;
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            dotPosition := 1;
            repeat
                dotPosition := internalFullPath.indexOf('/../', dotPosition);
                if dotPosition <= 0 then begin
                    if not internalFullPath.endsWith('/..') then break;
                    dotPosition := internalFullPath.length - 2;
                end;
                begin
                    if dotPosition > 1 then begin
                        error := baseunix.fpStat(system.PAnsiChar((internalRootPath + internalFullPath.substring(1, dotPosition)).toUTF8()), attributes);
                        if (error = 0) and ((attributes.st_mode and baseunix.S_IFDIR) <> 0) then goto break_label0;
                        case baseunix.fpGetErrNo() of
                        baseunix.ESysENOENT,
                        baseunix.ESysENOTDIR: ;
                        else
                            raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                        end;
                    end;
                    result := '';
                    exit;
                end;
                break_label0:
                reductionPosition := internalFullPath.lastIndexOf('/', dotPosition - 1);
                internalFullPath := internalFullPath.substring(1, reductionPosition) + internalFullPath.substring(dotPosition + 3);
                dotPosition := reductionPosition;
            until false;
            internalResultPath := internalRootPath + internalFullPath;
            if FileSystemRoot.getRootPathOf(internalResultPath) <> internalRootPath then begin
                result := '';
                exit;
            end;
            if internalResultPath = '' then begin
                result := '/.';
                exit;
            end;
            result := internalResultPath;
        end;

        function VolumeFileSystem.makeInternalFullPathAndCheckCreat(const volumeFullPath: UnicodeString): UnicodeString;
        var
            position: int;
            utfFullPath: AnsiString;
            checkFullPath: UnicodeString;
            attributes: baseunix.Stat;
        begin
            position := volumeFullPath.lastIndexOf('/') + 1;
            checkFullPath := makeInternalFullPathAndCheckExist(volumeFullPath.substring(1, position), AT_DIRECTORY) + '.';
            utfFullPath := checkFullPath.toUTF8();
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := checkFullPath.substring(1, checkFullPath.length) + volumeFullPath.substring(position);
        end;

        function VolumeFileSystem.makeInternalFullPathAndCheckExist(const volumeFullPath: UnicodeString; argumentType: int): UnicodeString;
        label
            break_label0;
        var
            error: int;
            dotPosition: int;
            reductionPosition: int;
            internalRootPath: UnicodeString;
            internalFullPath: UnicodeString;
            internalResultPath: UnicodeString;
            attributes: baseunix.Stat;
        begin
            internalRootPath := fldInternalRootPath;
            internalFullPath := volumeFullPath;
            dotPosition := 1;
            repeat
                dotPosition := internalFullPath.indexOf('/./', dotPosition);
                if dotPosition <= 0 then begin
                    if not internalFullPath.endsWith('/.') then break;
                    dotPosition := internalFullPath.length - 1;
                end;
                internalFullPath := internalFullPath.substring(1, dotPosition) + internalFullPath.substring(dotPosition + 2);
            until false;
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            dotPosition := 1;
            repeat
                dotPosition := internalFullPath.indexOf('/../', dotPosition);
                if dotPosition <= 0 then begin
                    if not internalFullPath.endsWith('/..') then break;
                    dotPosition := internalFullPath.length - 2;
                end;
                begin
                    if dotPosition > 1 then begin
                        error := baseunix.fpStat(system.PAnsiChar((internalRootPath + internalFullPath.substring(1, dotPosition)).toUTF8()), attributes);
                        if (error = 0) and ((attributes.st_mode and baseunix.S_IFDIR) <> 0) then goto break_label0;
                        case baseunix.fpGetErrNo() of
                        baseunix.ESysENOENT,
                        baseunix.ESysENOTDIR: ;
                        else
                            raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                        end;
                    end;
                    case argumentType of
                    AT_FILE:
                        raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                            CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
                        ]));
                    AT_DIRECTORY:
                        raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                            CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
                        ]));
                    else
                        raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                            CoAnsiString.create((internalRootPath + internalFullPath).toUTF8())
                        ]));
                    end;
                end;
                break_label0:
                reductionPosition := internalFullPath.lastIndexOf('/', dotPosition - 1);
                internalFullPath := internalFullPath.substring(1, reductionPosition) + internalFullPath.substring(dotPosition + 3);
                dotPosition := reductionPosition;
            until false;
            internalResultPath := internalRootPath + internalFullPath;
            if FileSystemRoot.getRootPathOf(internalResultPath) <> internalRootPath then case argumentType of
            AT_FILE:
                raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'not-found.file'), [
                    CoAnsiString.create(internalFullPath.toUTF8()), CoAnsiString.create(fldCanonicalRootPath)
                ]));
            AT_DIRECTORY:
                raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'not-found.directory'), [
                    CoAnsiString.create(internalFullPath.toUTF8()), CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(internalFullPath.toUTF8()), CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            if internalResultPath = '' then begin
                result := '/.';
                exit;
            end;
            result := internalResultPath;
        end;

        constructor VolumeFileSystem.create(const rootPath, currentDirectory: UnicodeString);
        begin
            inherited create();
            fldObjectNameMaximumLength := OBJECT_NAME_MAXIMUM_LENGTH - rootPath.length;
            if rootPath = '' then begin
                fldCanonicalRootPath := '/';
            end else begin
                fldCanonicalRootPath := rootPath.toUTF8();
            end;
            fldInternalRootPath := rootPath;
            fldCurrentDirectory := currentDirectory;
        end;

        procedure VolumeFileSystem.changeCurrentDirectory(const directoryPath: UnicodeString);
        var
            maximumNameLength: int;
            utfFullPath: AnsiString;
            internalFullPath: UnicodeString;
            attributes: baseunix.Stat;
        begin
            maximumNameLength := fldObjectNameMaximumLength;
            if directoryPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryPath')
                ]));
            end;
            if not isObjectNameValid(directoryPath) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('directoryPath')
                ]));
            end;
            internalFullPath := toVolumeFullPath(directoryPath);
            if internalFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryPath')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            internalFullPath := makeInternalFullPathAndCheckExist(internalFullPath, AT_DIRECTORY);
            if internalFullPath.endsWith('/') then internalFullPath := internalFullPath.substring(1, internalFullPath.length);
            utfFullPath := internalFullPath.toUTF8();
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if (attributes.st_mode and baseunix.S_IFDIR) = 0 then begin
                raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            end;
            fldCurrentDirectory := internalFullPath.substring(fldInternalRootPath.length + 1) + '/';
        end;

        procedure VolumeFileSystem.readAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
        var
            maximumNameLength: int;
            standardNameLength: int;
            internalAttributes: int;
            timeInMillis: long;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
            internalLastAccessTime: TM;
            internalLastWriteTime: TM;
            attributes: baseunix.Stat;
        begin
            standardNameLength := objectName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(objectName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_OBJECT).toUTF8();
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            &Array.zeroRaw(internalLastAccessTime, sizeof(TM));
            &Array.zeroRaw(internalLastWriteTime, sizeof(TM));
            if (gmtTime(@(attributes.st_atime), @internalLastAccessTime) = nil) or (gmtTime(@(attributes.st_mtime), @internalLastWriteTime) = nil) then begin
                raise ObjectReadAttributesException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'object.read-attributes'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            end;
            if objectAttr = nil then exit;
            internalAttributes := int(attributes.st_mode);
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_DIRECTORY) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, (internalAttributes and baseunix.S_IFDIR) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_READ_ONLY) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, (internalAttributes and (baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH)) = 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_READABLE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_READABLE, (internalAttributes and baseunix.S_IRUSR) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_WRITEABLE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_WRITEABLE, (internalAttributes and baseunix.S_IWUSR) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_EXECUTABLE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_EXECUTABLE, (internalAttributes and baseunix.S_IXUSR) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_READABLE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_READABLE, (internalAttributes and baseunix.S_IRGRP) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_WRITEABLE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_WRITEABLE, (internalAttributes and baseunix.S_IWGRP) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_EXECUTABLE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_EXECUTABLE, (internalAttributes and baseunix.S_IXGRP) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_READABLE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_READABLE, (internalAttributes and baseunix.S_IROTH) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_WRITEABLE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_WRITEABLE, (internalAttributes and baseunix.S_IWOTH) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_EXECUTABLE) then begin
                objectAttr.setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_EXECUTABLE, (internalAttributes and baseunix.S_IXOTH) <> 0);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) then begin
                with internalLastAccessTime do timeInMillis := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(attributes.st_atime_nsec) div 1000000);
                objectAttr.setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, timeInMillis);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_WRITE_TIME) then begin
                with internalLastWriteTime do timeInMillis := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(attributes.st_mtime_nsec) div 1000000);
                objectAttr.setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, timeInMillis);
            end;
        end;

        procedure VolumeFileSystem.writeAttributes(const objectName: UnicodeString; objectAttr: ObjectAttributes);
        var
            objectWriteTime: boolean;
            objectWriteAttr: boolean;
            toWriteAttributes: int;
            maximumNameLength: int;
            standardNameLength: int;
            internalAttributes: int;
            timePacked: long;
            timeOffset: long;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
            internalTime: TM;
            attributes: baseunix.Stat;
            toWriteTimes: baseunix.UTimBuf;
            timeFields: TimeRecord absolute timePacked;
        begin
            standardNameLength := objectName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(objectName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if objectAttr = nil then begin
                raise NullPointerException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'null-pointer.argument'), [ CoAnsiString.create('objectAttr') ]));
            end;
            toWriteAttributes := baseunix.S_IRWXU or baseunix.S_IRGRP or baseunix.S_IXGRP or baseunix.S_IROTH or baseunix.S_IXOTH;
            &Array.zeroRaw(toWriteTimes, sizeof(baseunix.UTimBuf));
            &Array.zeroRaw(internalTime, sizeof(TM));
            timeOffset := TimeBase.currentOffsetInMillis();
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_READABLE) then begin
                toWriteAttributes := toWriteAttributes and not baseunix.S_IRUSR;
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OWNER_READABLE) then inc(toWriteAttributes, baseunix.S_IRUSR);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_WRITEABLE) then begin
                toWriteAttributes := toWriteAttributes and not baseunix.S_IWUSR;
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OWNER_WRITEABLE) then inc(toWriteAttributes, baseunix.S_IWUSR);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OWNER_EXECUTABLE) then begin
                toWriteAttributes := toWriteAttributes and not baseunix.S_IXUSR;
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OWNER_EXECUTABLE) then inc(toWriteAttributes, baseunix.S_IXUSR);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_READABLE) then begin
                toWriteAttributes := toWriteAttributes and not baseunix.S_IRGRP;
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_GROUP_READABLE) then inc(toWriteAttributes, baseunix.S_IRGRP);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_WRITEABLE) then begin
                toWriteAttributes := toWriteAttributes and not baseunix.S_IWGRP;
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_GROUP_WRITEABLE) then inc(toWriteAttributes, baseunix.S_IWGRP);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_GROUP_EXECUTABLE) then begin
                toWriteAttributes := toWriteAttributes and not baseunix.S_IXGRP;
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_GROUP_EXECUTABLE) then inc(toWriteAttributes, baseunix.S_IXGRP);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_READABLE) then begin
                toWriteAttributes := toWriteAttributes and not baseunix.S_IROTH;
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OTHER_READABLE) then inc(toWriteAttributes, baseunix.S_IROTH);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_WRITEABLE) then begin
                toWriteAttributes := toWriteAttributes and not baseunix.S_IWOTH;
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OTHER_WRITEABLE) then inc(toWriteAttributes, baseunix.S_IWOTH);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_OTHER_EXECUTABLE) then begin
                toWriteAttributes := toWriteAttributes and not baseunix.S_IXOTH;
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_OTHER_EXECUTABLE) then inc(toWriteAttributes, baseunix.S_IXOTH);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.B_READ_ONLY) then begin
                if objectAttr.getBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY) then begin
                    toWriteAttributes := toWriteAttributes and not(baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH);
                end else begin
                    toWriteAttributes := toWriteAttributes or baseunix.S_IWUSR;
                end;
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) then begin
                timePacked := timeToPackedFields(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME) + timeOffset);
                internalTime.tm_sec := timeFields.millisecond div 1000;
                internalTime.tm_min := timeFields.minute;
                internalTime.tm_hour := timeFields.hour;
                internalTime.tm_mday := timeFields.day;
                internalTime.tm_mon := timeFields.month - 1;
                internalTime.tm_year := timeFields.year - 1900;
                toWriteTimes.actime := unixTime(@internalTime);
            end;
            if objectAttr.isSupportedAttributeId(VolumeRequiredAttributes.L_LAST_WRITE_TIME) then begin
                timePacked := timeToPackedFields(objectAttr.getLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME) + timeOffset);
                internalTime.tm_sec := timeFields.millisecond div 1000;
                internalTime.tm_min := timeFields.minute;
                internalTime.tm_hour := timeFields.hour;
                internalTime.tm_mday := timeFields.day;
                internalTime.tm_mon := timeFields.month - 1;
                internalTime.tm_year := timeFields.year - 1900;
                toWriteTimes.modtime := unixTime(@internalTime);
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_OBJECT).toUTF8();
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            internalAttributes := int(attributes.st_mode) and (baseunix.S_IRWXO or baseunix.S_IRWXG or baseunix.S_IRWXU or baseunix.S_ISUID or baseunix.S_ISGID or baseunix.S_ISVTX);
            if ((internalAttributes and baseunix.S_IWUSR) = 0) and (baseunix.fpChMod(system.PAnsiChar(utfFullPath), internalAttributes or baseunix.S_IWUSR) <> 0) then case baseunix.fpGetErrNo() of
            baseunix.ESysEROFS:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            objectWriteTime := (toWriteTimes.actime <> -1) and (toWriteTimes.modtime <> -1) and (baseunix.fpUTime(system.PAnsiChar(utfFullPath), @toWriteTimes) = 0);
            objectWriteAttr := baseunix.fpChMod(system.PAnsiChar(utfFullPath), toWriteAttributes or internalAttributes and (baseunix.S_ISUID or baseunix.S_ISGID or baseunix.S_ISVTX)) = 0;
            if not objectWriteTime or not objectWriteAttr then begin
                raise ObjectWriteAttributesException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'object.write-attributes'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            end;
        end;

        procedure VolumeFileSystem.move(const objectOldName, objectNewName: UnicodeString);
        var
            maximumNameLength: int;
            standardNameLength: int;
            utfOldFullPath: AnsiString;
            utfNewFullPath: AnsiString;
            volumeOldFullPath: UnicodeString;
            volumeNewFullPath: UnicodeString;
        begin
            standardNameLength := objectOldName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectOldName')
                ]));
            end;
            if (standardNameLength <= 0) or objectOldName.endsWith('/') or not isObjectNameValid(objectOldName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectOldName')
                ]));
            end;
            volumeOldFullPath := toVolumeFullPath(objectOldName);
            if volumeOldFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectOldName')
                ]));
            end;
            standardNameLength := objectNewName.length;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectNewName')
                ]));
            end;
            if (standardNameLength <= 0) or objectNewName.endsWith('/') or not isObjectNameValid(objectNewName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectNewName')
                ]));
            end;
            volumeNewFullPath := toVolumeFullPath(objectNewName);
            if volumeNewFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectNewName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfOldFullPath := makeInternalFullPathAndCheckExist(volumeOldFullPath, AT_OBJECT).toUTF8();
            utfNewFullPath := makeInternalFullPathAndCheckCreat(volumeNewFullPath).toUTF8();
            if baseunix.fpRename(system.PAnsiChar(utfOldFullPath), system.PAnsiChar(utfNewFullPath)) <> 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(utfOldFullPath)
                ]));
            baseunix.ESysEEXIST:
                raise MoveOperationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'move'), [
                    CoAnsiString.create(utfOldFullPath), CoAnsiString.create(utfNewFullPath)
                ]));
            baseunix.ESysENOSPC:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            baseunix.ESysEROFS:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure VolumeFileSystem.deleteFile(const fileName: UnicodeString);
        var
            maximumNameLength: int;
            standardNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
        begin
            standardNameLength := fileName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(fileName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_FILE).toUTF8();
            if baseunix.fpUnlink(system.PAnsiChar(utfFullPath)) <> 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysEISDIR:
                raise FileDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.deletion'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysEROFS:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure VolumeFileSystem.deleteDirectory(const directoryName: UnicodeString);
        var
            maximumNameLength: int;
            standardNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
            attributes: baseunix.Stat;
        begin
            standardNameLength := directoryName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            if (standardNameLength <= 0) or directoryName.endsWith('/') or not isObjectNameValid(directoryName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(directoryName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_DIRECTORY).toUTF8();
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            if (baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) = 0) and ((attributes.st_mode and baseunix.S_IFDIR) = 0) then begin
                raise DirectoryDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.deletion'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            end;
            if baseunix.fpRmDir(system.PAnsiChar(utfFullPath)) <> 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise DirectoryNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.directory'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysENOTEMPTY:
                raise DirectoryDeletionException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.deletion'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysEROFS:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure VolumeFileSystem.createDirectory(const directoryName: UnicodeString);
        const
            DEFAULT_RIGHTS = baseunix.TMode(baseunix.S_IRWXU or baseunix.S_IRGRP or baseunix.S_IXGRP or baseunix.S_IROTH or baseunix.S_IXOTH);
        var
            maximumNameLength: int;
            standardNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
        begin
            standardNameLength := directoryName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            if (standardNameLength <= 0) or directoryName.endsWith('/') or not isObjectNameValid(directoryName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(directoryName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('directoryName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckCreat(volumeFullPath).toUTF8();
            if baseunix.fpMkDir(system.PAnsiChar(utfFullPath), DEFAULT_RIGHTS) <> 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysEEXIST:
                raise DirectoryCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'directory.creation'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysENOSPC:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            baseunix.ESysEROFS:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        function VolumeFileSystem.isAttached(): boolean;
        var
            position: int;
            rootPath: UnicodeString;
            mountPoint: UnicodeString;
        begin
            rootPath := fldInternalRootPath;
            with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
                repeat
                    mountPoint := readln();
                    if mountPoint.length <= 0 then break;
                    position := mountPoint.indexOf(#$0020) + 1;
                    mountPoint := FileSystemRoot.deescapeMountPoint(mountPoint.substring(position, mountPoint.indexOf(#$0020, position)));
                    if mountPoint.endsWith('/') then mountPoint := mountPoint.substring(1, mountPoint.length);
                    if mountPoint = rootPath then begin
                        result := true;
                        exit;
                    end;
                until false;
            finally
                close();
            end;
            result := false;
        end;

        function VolumeFileSystem.isReadOnly(): boolean;
        const
            READ_ONLY = UnicodeString('ro');
        var
            position0: int;
            position1: int;
            rootPath: UnicodeString;
            mountPoint: UnicodeString;
            mountParams: UnicodeString;
        begin
            rootPath := fldInternalRootPath;
            with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
                repeat
                    mountParams := readln();
                    if mountParams.length <= 0 then break;
                    position0 := mountParams.indexOf(#$0020) + 1;
                    position1 := mountParams.indexOf(#$0020, position0);
                    mountPoint := FileSystemRoot.deescapeMountPoint(mountParams.substring(position0, position1));
                    if mountPoint.endsWith('/') then mountPoint := mountPoint.substring(1, mountPoint.length);
                    if mountPoint = rootPath then begin
                        position0 := mountParams.indexOf(#$0020, position1 + 1) + 1;
                        mountParams := mountParams.substring(position0, mountParams.indexOf(#$0020, position0));
                        result := (mountParams = READ_ONLY) or mountParams.startsWith(READ_ONLY + ',') or (mountParams.indexOf(',' + READ_ONLY + ',') > 0) or mountParams.endsWith(',' + READ_ONLY);
                        exit;
                    end;
                until false;
            finally
                close();
            end;
            raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                CoAnsiString.create(fldCanonicalRootPath)
            ]));
        end;

        function VolumeFileSystem.isObjectNameCaseSensitive(): boolean;
        const
            { список нечувствительных к имени объекта файловых систем }
            caseInsensitiveFSTypes: array [0..6] of UnicodeString = (
                'exfat', 'hpfs', 'iso9660', 'msdos', 'ntfs', 'umsdos', 'vfat'
            );
        var
            index: int;
            position0: int;
            position1: int;
            rootPath: UnicodeString;
            mountPoint: UnicodeString;
            mountFSType: UnicodeString;
        begin
            rootPath := fldInternalRootPath;
            with DataInputStream.create(FileInputStream.create(baseunix.fpOpen('/proc/mounts', baseunix.O_RDONLY))), littleEndianDataInput do try
                repeat
                    mountFSType := readln();
                    if mountFSType.length <= 0 then break;
                    position0 := mountFSType.indexOf(#$0020) + 1;
                    position1 := mountFSType.indexOf(#$0020, position0);
                    mountPoint := FileSystemRoot.deescapeMountPoint(mountFSType.substring(position0, position1));
                    if mountPoint.endsWith('/') then mountPoint := mountPoint.substring(1, mountPoint.length);
                    if mountPoint = rootPath then begin
                        inc(position1);
                        mountFSType := mountFSType.substring(position1, mountFSType.indexOf(#$0020, position1));
                        for index := 0 to 6 do if caseInsensitiveFSTypes[index].equalsIgnoreCase(mountFSType) then begin
                            result := false;
                            exit;
                        end;
                        result := true;
                        exit;
                    end;
                until false;
            finally
                close();
            end;
            raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                CoAnsiString.create(fldCanonicalRootPath)
            ]));
        end;

        function VolumeFileSystem.isObjectExists(const objectName: UnicodeString): boolean;
        var
            maximumNameLength: int;
            standardNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
            attributes: baseunix.Stat;
        begin
            standardNameLength := objectName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if (standardNameLength <= 0) or objectName.endsWith('/') or not isObjectNameValid(objectName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(objectName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndIsExist(volumeFullPath).toUTF8();
            if utfFullPath = '' then begin
                result := false;
                exit;
            end;
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR: begin
                result := false;
                exit;
            end;
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := true;
        end;

        function VolumeFileSystem.isObjectNameValid(const objectName: UnicodeString): boolean;
        const
            COMPONENT_NAME_MAXIMUM_LENGTH = int(255);
        var
            index: int;
            length: int;
            beginIndex: int;
            endIndex: int;
            utfName: AnsiString;
        begin
            if (objectName.length > fldObjectNameMaximumLength) or (objectName.indexOf('//') > 0) then begin
                result := false;
                exit;
            end;
            utfName := objectName.toUTF8();
            length := utfName.length;
            for index := 0 to length - 1 do if utfName[index + 1] = #$00 then begin
                result := false;
                exit;
            end;
            if length > 0 then begin
                beginIndex := 0;
                if utfName[1] = '/' then inc(beginIndex);
                while (beginIndex >= 0) and (beginIndex < length) do begin
                    endIndex := utfName.indexOf('/', beginIndex + 1) - 1;
                    if endIndex < 0 then endIndex := length;
                    if endIndex - beginIndex > COMPONENT_NAME_MAXIMUM_LENGTH then begin
                        result := false;
                        exit;
                    end;
                    beginIndex := endIndex + 1;
                end;
            end;
            result := true;
        end;

        function VolumeFileSystem.isInternalNameFull(const internalName: UnicodeString): boolean;
        begin
            result := internalName.startsWith('/');
        end;

        function VolumeFileSystem.isInternalNameValid(const internalName: UnicodeString): boolean;
        const
            COMPONENT_NAME_MAXIMUM_LENGTH = int(255);
        var
            index: int;
            length: int;
            beginIndex: int;
            endIndex: int;
            utfName: AnsiString;
        begin
            if (internalName.length > fldObjectNameMaximumLength) or (internalName.indexOf('//') > 0) then begin
                result := false;
                exit;
            end;
            utfName := internalName.toUTF8();
            length := utfName.length;
            for index := 0 to length - 1 do if utfName[index + 1] = #$00 then begin
                result := false;
                exit;
            end;
            if length > 0 then begin
                beginIndex := 0;
                if utfName[1] = '/' then inc(beginIndex);
                while (beginIndex >= 0) and (beginIndex < length) do begin
                    endIndex := utfName.indexOf('/', beginIndex + 1) - 1;
                    if endIndex < 0 then endIndex := length;
                    if endIndex - beginIndex > COMPONENT_NAME_MAXIMUM_LENGTH then begin
                        result := false;
                        exit;
                    end;
                    beginIndex := endIndex + 1;
                end;
            end;
            result := true;
        end;

        function VolumeFileSystem.getObjectNameMaximumLength(): int;
        begin
            result := fldObjectNameMaximumLength;
        end;

        function VolumeFileSystem.totalSize(): long;
        var
            utfRootPath: AnsiString;
            volumeInfo: baseunix.TStatFS;
        begin
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfRootPath := fldInternalRootPath.toUTF8() + '/.';
            &Array.zeroRaw(volumeInfo, sizeof(baseunix.TStatFS));
            if doSyscall(SYSCALL_NR_STATFS, long(system.PAnsiChar(utfRootPath)), long(@volumeInfo)) <> 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := volumeInfo.bsize * long(volumeInfo.blocks);
        end;

        function VolumeFileSystem.usedSize(): long;
        var
            utfRootPath: AnsiString;
            volumeInfo: baseunix.TStatFS;
        begin
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfRootPath := fldInternalRootPath.toUTF8() + '/.';
            &Array.zeroRaw(volumeInfo, sizeof(baseunix.TStatFS));
            if doSyscall(SYSCALL_NR_STATFS, long(system.PAnsiChar(utfRootPath)), long(@volumeInfo)) <> 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := volumeInfo.bsize * long(volumeInfo.blocks - volumeInfo.bfree);
        end;

        function VolumeFileSystem.availableSize(): long;
        var
            utfRootPath: AnsiString;
            volumeInfo: baseunix.TStatFS;
        begin
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfRootPath := fldInternalRootPath.toUTF8() + '/.';
            &Array.zeroRaw(volumeInfo, sizeof(baseunix.TStatFS));
            if doSyscall(SYSCALL_NR_STATFS, long(system.PAnsiChar(utfRootPath)), long(@volumeInfo)) <> 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := volumeInfo.bsize * long(volumeInfo.bfree);
        end;

        function VolumeFileSystem.getCurrentDirectory(): UnicodeString;
        begin
            result := fldCurrentDirectory.copy();
        end;

        function VolumeFileSystem.toInternalName(const objectName: UnicodeString): UnicodeString;
        begin
            result := objectName;
        end;

        function VolumeFileSystem.toObjectName(const internalName: UnicodeString): UnicodeString;
        begin
            result := internalName;
        end;

        function VolumeFileSystem.findFirst(const objectPath: UnicodeString): ObjectEnumeration;
        var
            position: int;
            maximumNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
            attributes: baseunix.Stat;
            findHandle: baseunix.PDir;
            findData: baseunix.PDirEnt;
        begin
            maximumNameLength := fldObjectNameMaximumLength;
            if objectPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectPath')
                ]));
            end;
            if not isObjectNameValid(objectPath) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('objectPath')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(objectPath);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('objectPath')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_OBJECT).toUTF8();
            if not utfFullPath.endsWith('/') then begin
                &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
                if baseunix.fpStat(system.PAnsiChar(utfFullPath), attributes) <> 0 then case baseunix.fpGetErrNo() of
                baseunix.ESysENOENT,
                baseunix.ESysENOTDIR:
                    raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                        CoAnsiString.create(utfFullPath)
                    ]));
                else
                    raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                end;
                findData := system.getMemory(sizeof(baseunix.DirEnt));
                try
                    &Array.zeroRaw(findData^, sizeof(baseunix.DirEnt));
                    position := utfFullPath.lastIndexOf('/') + 1;
                    if (attributes.st_mode and baseunix.S_IFDIR) <> 0 then findData^.d_type := DT_DIR;
                    &Array.copyRaw(utfFullPath[position], findData^.d_name, sizeof(char) * (utfFullPath.length - position + 1));
                    result := VolumeObjectEnumeration.create(utfFullPath.substring(1, position), nil, findData^);
                finally
                    system.freeMemory(findData);
                end;
                exit;
            end;
            findHandle := baseunix.fpOpenDir(system.PAnsiChar(utfFullPath));
            if findHandle = nil then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise ObjectNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.object'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            findData := baseunix.fpReadDir(findHandle^);
            result := VolumeObjectEnumeration.create(utfFullPath, findHandle, findData^);
        end;

        function VolumeFileSystem.createFile(const fileName: UnicodeString): ByteWriter;
        const
            DEFAULT_RIGHTS = baseunix.TMode(baseunix.S_IRWXU or baseunix.S_IRGRP or baseunix.S_IXGRP or baseunix.S_IROTH or baseunix.S_IXOTH);
        var
            fileHandle: int;
            maximumNameLength: int;
            standardNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
        begin
            standardNameLength := fileName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(fileName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckCreat(volumeFullPath).toUTF8();
            fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_WRONLY or baseunix.O_CREAT or baseunix.O_EXCL, DEFAULT_RIGHTS);
            if fileHandle < 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysEEXIST,
            baseunix.ESysEISDIR:
                raise FileCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.creation'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysENOSPC:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            baseunix.ESysEROFS:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
        end;

        function VolumeFileSystem.rewriteFile(const fileName: UnicodeString): ByteWriter;
        const
            DEFAULT_RIGHTS = baseunix.TMode(baseunix.S_IRWXU or baseunix.S_IRGRP or baseunix.S_IXGRP or baseunix.S_IROTH or baseunix.S_IXOTH);
        var
            fileHandle: int;
            maximumNameLength: int;
            standardNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
        begin
            standardNameLength := fileName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(fileName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckCreat(volumeFullPath).toUTF8();
            fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_WRONLY or baseunix.O_CREAT or baseunix.O_TRUNC, DEFAULT_RIGHTS);
            if fileHandle < 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysEISDIR:
                raise FileCreationException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.creation'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysENOSPC:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            baseunix.ESysEROFS:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
        end;

        function VolumeFileSystem.openFileForAppend(const fileName: UnicodeString): ByteWriter;
        var
            fileHandle: int;
            maximumNameLength: int;
            standardNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
        begin
            standardNameLength := fileName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(fileName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_FILE).toUTF8();
            fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_WRONLY);
            if fileHandle < 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysEISDIR:
                raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysEROFS:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            baseunix.fpLSeek(fileHandle, 0, baseunix.SEEK_END);
            result := FileOutputStream.create(long(fileHandle), fldCanonicalRootPath);
        end;

        function VolumeFileSystem.openFileForRead(const fileName: UnicodeString): ByteReader;
        var
            fileHandle: int;
            maximumNameLength: int;
            standardNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
            attributes: baseunix.Stat;
        begin
            standardNameLength := fileName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(fileName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_FILE).toUTF8();
            fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_RDONLY or baseunix.O_NONBLOCK);
            if fileHandle < 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            if (baseunix.fpFStat(fileHandle, attributes) <> 0) or ((attributes.st_mode and baseunix.S_IFDIR) <> 0) then begin
                baseunix.fpClose(fileHandle);
                raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            end;
            result := FileInputStream.create(long(fileHandle));
        end;

        function VolumeFileSystem.openFile(const fileName: UnicodeString): ByteStream;
        var
            fileHandle: int;
            maximumNameLength: int;
            standardNameLength: int;
            utfFullPath: AnsiString;
            volumeFullPath: UnicodeString;
        begin
            standardNameLength := fileName.length;
            maximumNameLength := fldObjectNameMaximumLength;
            if standardNameLength > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if (standardNameLength <= 0) or fileName.endsWith('/') or not isObjectNameValid(fileName) then begin
                raise InvalidObjectNameException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            volumeFullPath := toVolumeFullPath(fileName);
            if volumeFullPath.length > maximumNameLength then begin
                raise ObjectNameTooLongException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.object-name.too-long'), [
                    CoAnsiString.create('fileName')
                ]));
            end;
            if not isAttached() then begin
                raise FileSystemNotAttachedException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.not-attached'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            end;
            utfFullPath := makeInternalFullPathAndCheckExist(volumeFullPath, AT_FILE).toUTF8();
            fileHandle := baseunix.fpOpen(system.PAnsiChar(utfFullPath), baseunix.O_RDWR);
            if fileHandle < 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysEISDIR:
                raise FileOpeningException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'file.opening'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysENOENT,
            baseunix.ESysENOTDIR:
                raise FileNotFoundException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'not-found.file'), [
                    CoAnsiString.create(utfFullPath)
                ]));
            baseunix.ESysEROFS:
                raise FileSystemReadOnlyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.read-only'), [
                    CoAnsiString.create(fldCanonicalRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := FileBidirectStream.create(long(fileHandle), fldCanonicalRootPath);
        end;

        function VolumeFileSystem.newAttributes(): Attributes;
        begin
            result := VolumeObjectAttributes.create();
        end;
    {%endregion}

    {%region  VolumeObjectAttributes }
        class procedure VolumeObjectAttributes.initialize();
        var
            index: int;
        begin
            attrIds := [
                B_OTHER_EXECUTABLE, B_OTHER_WRITEABLE, B_OTHER_READABLE,
                B_GROUP_EXECUTABLE, B_GROUP_WRITEABLE, B_GROUP_READABLE,
                B_OWNER_EXECUTABLE, B_OWNER_WRITEABLE, B_OWNER_READABLE,
                '', '', '', '', '', B_DIRECTORY, '',
                L_LAST_ACCESS_TIME, L_LAST_WRITE_TIME
            ];
            index := system.length(attrIds);
            attrHashes := &Array.newLong1d(index);
            for index := index - 1 downto 0 do begin
                attrHashes[index] := (CoAnsiString.create(attrIds[index]) as RefCountInterface).getHashCode();
            end;
        end;

        class procedure VolumeObjectAttributes.finalize();
        begin
            attrIds := nil;
            attrHashes := nil;
        end;

        class procedure VolumeObjectAttributes.stringAttributeIdIsInvalid(const attributeId: AnsiString);
        begin
            if attributeId.length <= 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
            end;
            if attributeId.startsWith('s') then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
                    CoAnsiString.create(attributeId)
                ]));
            end;
            raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
                CoAnsiString.create(attributeId)
            ]));
        end;

        class function VolumeObjectAttributes.booleanAttributeIdToIndex(const attributeId: AnsiString): int;
        var
            index: int;
        begin
            if attributeId.length <= 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
            end;
            index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 16);
            if (index >= 0) and (attributeId = attrIds[index]) then begin
                result := index - 0;
                exit;
            end;
            if attributeId.startsWith('b') then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
                    CoAnsiString.create(attributeId)
                ]));
            end;
            raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
                CoAnsiString.create(attributeId)
            ]));
        end;

        class function VolumeObjectAttributes.longAttributeIdToIndex(const attributeId: AnsiString): int;
        var
            index: int;
        begin
            if attributeId.length <= 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
            end;
            index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 16, 8);
            if (index >= 0) and (attributeId = attrIds[index]) then begin
                result := index - 16;
                exit;
            end;
            if attributeId.startsWith('l') then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
                    CoAnsiString.create(attributeId)
                ]));
            end;
            raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.type'), [
                CoAnsiString.create(attributeId)
            ]));
        end;

        constructor VolumeObjectAttributes.create();
        begin
            inherited create();
            fldTimes := &Array.newLong1d(2);
        end;

        procedure VolumeObjectAttributes.setBooleanAttribute(const attributeId: AnsiString; attributeValue: boolean);
        var
            mask: int;
        begin
            if attributeId <> B_READ_ONLY then begin
                mask := 1 shl booleanAttributeIdToIndex(attributeId);
            end else begin
                if attributeValue then begin
                    mask := baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH;
                end else begin
                    mask := baseunix.S_IWUSR;
                end;
                attributeValue := not attributeValue;
            end;
            if attributeValue then begin
                fldAttributes := fldAttributes or mask;
                exit;
            end;
            fldAttributes := fldAttributes and not mask;
        end;

        procedure VolumeObjectAttributes.setLongAttribute(const attributeId: AnsiString; attributeValue: long);
        var
            index: int;
        begin
            index := longAttributeIdToIndex(attributeId);
            fldTimes[index] := attributeValue;
        end;

        procedure VolumeObjectAttributes.setStringAttribute(const attributeId: AnsiString; const attributeValue: UnicodeString);
        begin
            stringAttributeIdIsInvalid(attributeId);
        end;

        function VolumeObjectAttributes.isSupportedAttributeId(const attributeId: AnsiString): boolean;
        var
            index: int;
        begin
            if attributeId.length <= 0 then begin
                result := false;
                exit;
            end;
            if attributeId = B_READ_ONLY then begin
                result := true;
                exit;
            end;
            index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 0);
            result := (index >= 0) and (attributeId = attrIds[index]);
        end;

        function VolumeObjectAttributes.getBooleanAttribute(const attributeId: AnsiString): boolean;
        var
            mask: int;
        begin
            if attributeId = B_READ_ONLY then begin
                mask := baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH;
                result := (fldAttributes and mask) = 0;
                exit;
            end;
            mask := 1 shl booleanAttributeIdToIndex(attributeId);
            result := (fldAttributes and mask) <> 0;
        end;

        function VolumeObjectAttributes.getLongAttribute(const attributeId: AnsiString): long;
        var
            index: int;
        begin
            index := longAttributeIdToIndex(attributeId);
            result := fldTimes[index];
        end;

        function VolumeObjectAttributes.getStringAttribute(const attributeId: AnsiString): UnicodeString;
        begin
            stringAttributeIdIsInvalid(attributeId);
            result := ''; { недостижимый код }
        end;

        function VolumeObjectAttributes.displayName(const attributeId: AnsiString): UnicodeString;
        var
            index: int;
        begin
            if attributeId.length <= 0 then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('attributeId') ]));
            end;
            if attributeId = B_READ_ONLY then begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.read-only');
                exit;
            end;
            index := &Array.indexOf((CoAnsiString.create(attributeId) as RefCountInterface).getHashCode(), attrHashes, 0, 0);
            if (index >= 0) and (attributeId = attrIds[index]) then case index of
             0: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.other-executable');
                exit;
            end;
             1: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.other-writeable');
                exit;
            end;
             2: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.other-readable');
                exit;
            end;
             3: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.group-executable');
                exit;
            end;
             4: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.group-writeable');
                exit;
            end;
             5: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.group-readable');
                exit;
            end;
             6: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.owner-executable');
                exit;
            end;
             7: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.owner-writeable');
                exit;
            end;
             8: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.osservices.UNIT_NAME, 'property.owner-readable');
                exit;
            end;
            14: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.directory');
                exit;
            end;
            16: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.last-access-time');
                exit;
            end;
            17: begin
                result := UResource.readUnitResourceAsUnicodeString(platform.independent.filesystem.UNIT_NAME, 'property.last-write-time');
                exit;
            end;
            end;
            raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.filesystem.UNIT_NAME, 'illegal-argument.attribute.id'), [
                CoAnsiString.create(attributeId)
            ]));
        end;

        function VolumeObjectAttributes.getSupportedAttributeIds(): AnsiString_Array1d;
        begin
            result := [
                B_OTHER_EXECUTABLE, B_OTHER_WRITEABLE, B_OTHER_READABLE, B_GROUP_EXECUTABLE, B_GROUP_WRITEABLE, B_GROUP_READABLE,
                B_OWNER_EXECUTABLE, B_OWNER_WRITEABLE, B_OWNER_READABLE, B_DIRECTORY, B_READ_ONLY, L_LAST_ACCESS_TIME, L_LAST_WRITE_TIME
            ];
        end;
    {%endregion}

    {%region  VolumeObjectEnumeration }
        procedure VolumeObjectEnumeration.setAttributes(const info);
        var
            attr: int;
            timeInMillis: long;
            path: AnsiString;
            internalLastAccessTime: TM;
            internalLastWriteTime: TM;
            attributes: baseunix.Stat;
            findData: baseunix.DirEnt absolute info;
        begin
            path := AnsiString(system.PAnsiChar(@(findData.d_name)));
            name := path.toUTF16();
            path := fldFullDirectoryPath + path;
            &Array.zeroRaw(attributes, sizeof(baseunix.Stat));
            if baseunix.fpStat(system.PAnsiChar(path), attributes) <> 0 then begin
                size := 0;
                setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, int(findData.d_type) = DT_DIR);
                setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, true);
                setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_READABLE, false);
                setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_WRITEABLE, false);
                setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_EXECUTABLE, false);
                setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_READABLE, false);
                setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_WRITEABLE, false);
                setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_EXECUTABLE, false);
                setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_READABLE, false);
                setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_WRITEABLE, false);
                setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_EXECUTABLE, false);
                setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, 0);
                setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, 0);
                exit;
            end;
            attr := int(attributes.st_mode);
            if (attr and baseunix.S_IFDIR) <> 0 then begin
                size := 0;
            end else begin
                size := attributes.st_size;
            end;
            setBooleanAttribute(VolumeRequiredAttributes.B_DIRECTORY, (attr and baseunix.S_IFDIR) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_READ_ONLY, (attr and (baseunix.S_IWUSR or baseunix.S_IWGRP or baseunix.S_IWOTH)) = 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_READABLE, (attr and baseunix.S_IRUSR) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_WRITEABLE, (attr and baseunix.S_IWUSR) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_OWNER_EXECUTABLE, (attr and baseunix.S_IXUSR) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_READABLE, (attr and baseunix.S_IRGRP) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_WRITEABLE, (attr and baseunix.S_IWGRP) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_GROUP_EXECUTABLE, (attr and baseunix.S_IXGRP) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_READABLE, (attr and baseunix.S_IROTH) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_WRITEABLE, (attr and baseunix.S_IWOTH) <> 0);
            setBooleanAttribute(VolumeRequiredAttributes.B_OTHER_EXECUTABLE, (attr and baseunix.S_IXOTH) <> 0);
            &Array.zeroRaw(internalLastAccessTime, sizeof(TM));
            &Array.zeroRaw(internalLastWriteTime, sizeof(TM));
            if (gmtTime(@(attributes.st_atime), @internalLastAccessTime) = nil) or (gmtTime(@(attributes.st_mtime), @internalLastWriteTime) = nil) then begin
                setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, 0);
                setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, 0);
                exit;
            end;
            with internalLastAccessTime do timeInMillis := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(attributes.st_atime_nsec) div 1000000);
            setLongAttribute(VolumeRequiredAttributes.L_LAST_ACCESS_TIME, timeInMillis);
            with internalLastWriteTime do timeInMillis := timeElapsedInMillis(tm_year + 1900, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec * 1000 + int(attributes.st_mtime_nsec) div 1000000);
            setLongAttribute(VolumeRequiredAttributes.L_LAST_WRITE_TIME, timeInMillis);
        end;

        constructor VolumeObjectEnumeration.create(const fullDirectoryPath: AnsiString; handle: baseunix.PDir; const info);
        begin
            inherited create(VolumeObjectAttributes.create());
            fldFullDirectoryPath := fullDirectoryPath;
            fldHandle := handle;
            setAttributes(info);
        end;

        procedure VolumeObjectEnumeration.close();
        var
            handle: baseunix.PDir;
        begin
            handle := fldHandle;
            destroy;
            if (handle <> nil) and (baseunix.fpCloseDir(handle^) <> 0) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        function VolumeObjectEnumeration.findNext(): boolean;
        var
            root: boolean;
            onam: AnsiString;
            path: UnicodeString;
            full: UnicodeString;
            findHandle: baseunix.PDir;
            findData: baseunix.PDirEnt;
        begin
            findHandle := fldHandle;
            if findHandle = nil then begin
                result := false;
                exit;
            end;
            path := fldFullDirectoryPath.toUTF16();
            root := FileSystemRoot.getRootPathOf(path) = path.substring(1, path.length);
            repeat
                baseunix.fpSetErrNo(0);
                findData := baseunix.fpReadDir(findHandle^);
                if findData = nil then break;
                onam := AnsiString(system.PAnsiChar(@(findData^.d_name)));
                full := path + onam.toUTF16();
                if (not root or (onam <> '..')) and (FileSystemRoot.getRootPathOf(full) <> full) then begin
                    setAttributes(findData^);
                    result := true;
                    exit;
                end;
            until false;
            if baseunix.fpGetErrNo() <> 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := false;
        end;
    {%endregion}

    {%region  HandleInputStream }
        constructor HandleInputStream.create(handle: long);
        begin
            inherited create();
            fldHandle := handle;
        end;

        procedure HandleInputStream.close();
        begin
        end;

        function HandleInputStream.skip(bytesQuantity: long): long;
        var
            data: int;
            readed: int;
            handle: int;
            skiped: long;
        begin
            if bytesQuantity <= 0 then begin
                result := 0;
                exit;
            end;
            data := 0;
            readed := 0;
            skiped := 0;
            handle := int(fldHandle);
            repeat
                readed := int(baseunix.fpRead(handle, data, 1));
                if readed < 0 then begin
                    if skiped > 0 then break;
                    raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
                end;
                if readed = 0 then break;
                inc(skiped);
                dec(bytesQuantity);
            until bytesQuantity > 0;
            result := skiped;
        end;

        function HandleInputStream.read(): int;
        var
            data: int;
            readed: int;
        begin
            data := 0;
            readed := int(baseunix.fpRead(int(fldHandle), data, 1));
            if readed < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if readed = 0 then begin
                result := -1;
                exit;
            end;
            result := data;
        end;

        function HandleInputStream.read(const dst: byte_Array1d): int;
        begin
            result := read(dst, 0, system.length(dst));
        end;

        function HandleInputStream.read(const dst: byte_Array1d; offset, length: int): int;
        var
            readed: int;
        begin
            &Array.checkBounds(dst, offset, length);
            if length <= 0 then begin
                result := 0;
                exit;
            end;
            readed := int(baseunix.fpRead(int(fldHandle), dst[offset], unixtype.TSize(length)));
            if readed < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if readed = 0 then begin
                result := -1;
                exit;
            end;
            result := readed;
        end;
    {%endregion}

    {%region  HandleOutputStream }
        constructor HandleOutputStream.create(handle: long; const rootPath: AnsiString);
        begin
            inherited create();
            fldHandle := handle;
            fldRootPath := rootPath;
        end;

        procedure HandleOutputStream.close();
        begin
        end;

        procedure HandleOutputStream.flush();
        begin
        end;

        procedure HandleOutputStream.write(byteData: int);
        var
            writed: int;
        begin
            writed := int(baseunix.fpWrite(int(fldHandle), byteData, 1));
            if writed < 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOSPC:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if writed < 1 then begin
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldRootPath)
                ]));
            end;
        end;

        procedure HandleOutputStream.write(const src: byte_Array1d);
        begin
            write(src, 0, system.length(src));
        end;

        procedure HandleOutputStream.write(const src: byte_Array1d; offset, length: int);
        var
            writed: int;
        begin
            &Array.checkBounds(src, offset, length);
            if length <= 0 then exit;
            writed := int(baseunix.fpWrite(int(fldHandle), src[offset], unixtype.TSize(length)));
            if writed < 0 then case baseunix.fpGetErrNo() of
            baseunix.ESysENOSPC:
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldRootPath)
                ]));
            else
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            if writed < length then begin
                raise FileSystemFullException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(platform.independent.osservices.UNIT_NAME, 'file-system.full'), [
                    CoAnsiString.create(fldRootPath)
                ]));
            end;
        end;
    {%endregion}

    {%region  HandleBidirectStream }
        constructor HandleBidirectStream.create(handleForRead, handleForWrite: long);
        begin
            inherited create();
            fldHandleForRead := handleForRead;
            fldHandleForWrite := handleForWrite;
        end;

        destructor HandleBidirectStream.destroy;
        begin
            fldReader.free();
            fldWriter.free();
            inherited destroy;
        end;

        procedure HandleBidirectStream.close();
        var
            handleForRead: int;
            handleForWrite: int;
        begin
            handleForRead := int(fldHandleForRead);
            handleForWrite := int(fldHandleForWrite);
            destroy;
            if (baseunix.fpClose(handleForRead) < 0) or (handleForRead <> handleForWrite) and (baseunix.fpClose(handleForWrite) < 0) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        function HandleBidirectStream.getReader(): ByteReader;
        begin
            result := fldReader;
        end;

        function HandleBidirectStream.getWriter(): ByteWriter;
        begin
            result := fldWriter;
        end;
    {%endregion}

    {%region  FileSeekExtension }
        constructor FileSeekExtension.create(handle: long);
        begin
            inherited create();
            fldHandle := handle;
        end;

        function FileSeekExtension.available(): long;
        var
            handle: int;
            locLength: long;
            locPosition: long;
        begin
            handle := int(fldHandle);
            locPosition := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
            if locPosition < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_END);
            if locLength < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            locPosition := baseunix.fpLSeek(handle, locPosition, baseunix.SEEK_SET);
            if locPosition < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := locLength - locPosition;
        end;

        function FileSeekExtension.seek(offset: long; from: SeekFrom): long;
        var
            handle: int;
            locLength: long;
            locPosition: long;
        begin
            if (from < SeekFrom.sfBegin) or (from > SeekFrom.sfEnd) then begin
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('from') ]));
            end;
            handle := int(fldHandle);
            locPosition := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
            if locPosition < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_END);
            if locLength < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            case from of
            SeekFrom.sfBegin:
                locPosition := offset;
            SeekFrom.sfEnd:
                locPosition := offset + locLength;
            else
                locPosition := offset + locPosition;
            end;
            if locPosition < 0 then locPosition := 0;
            if locPosition > locLength then locPosition := locLength;
            locPosition := baseunix.fpLSeek(handle, locPosition, baseunix.SEEK_SET);
            if locPosition < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := locPosition;
        end;

        function FileSeekExtension.position(): long;
        var
            locPosition: long;
        begin
            locPosition := baseunix.fpLSeek(int(fldHandle), 0, baseunix.SEEK_CUR);
            if locPosition < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := locPosition;
        end;

        function FileSeekExtension.size(): long;
        var
            handle: int;
            locLength: long;
            locPosition: long;
        begin
            handle := int(fldHandle);
            locPosition := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
            if locPosition < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_END);
            if locLength < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            locPosition := baseunix.fpLSeek(handle, locPosition, baseunix.SEEK_SET);
            if locPosition < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := locLength;
        end;
    {%endregion}

    {%region  FileInputStream }
        constructor FileInputStream.create(handle: long; seekable: FileSeekExtension);
        begin
            inherited create(handle);
            if seekable <> nil then begin
                fldOwnedSeekable := false;
            end else begin
                seekable := FileSeekExtension.create(handle);
                fldOwnedSeekable := true;
            end;
            fldExtensions := [ self, seekable ];
        end;

        destructor FileInputStream.destroy;
        begin
            if fldOwnedSeekable then begin
                fldExtensions[1].free();
            end;
            inherited destroy;
        end;

        procedure FileInputStream.close();
        var
            handle: int;
        begin
            handle := int(fldHandle);
            destroy;
            if baseunix.fpClose(handle) < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure FileInputStream.reset();
        begin
            FileSeekExtension(fldExtensions[1]).seek(fldMarked, SeekFrom.sfBegin);
        end;

        procedure FileInputStream.mark(transferLimit: int);
        var
            locPosition: long;
        begin
            locPosition := baseunix.fpLSeek(int(fldHandle), 0, baseunix.SEEK_CUR);
            if locPosition < 0 then begin
                fldMarked := 0;
                exit;
            end;
            fldMarked := locPosition;
        end;

        function FileInputStream.skip(bytesQuantity: long): long;
        var
            handle: int;
            locLength: long;
            oldPosition: long;
            newPosition: long;
        begin
            if bytesQuantity <= 0 then begin
                result := 0;
                exit;
            end;
            handle := int(fldHandle);
            oldPosition := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
            if oldPosition < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_END);
            if locLength < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            newPosition := bytesQuantity + oldPosition;
            if (newPosition < 0) or (newPosition > locLength) then newPosition := locLength;
            newPosition := baseunix.fpLSeek(handle, newPosition, baseunix.SEEK_SET);
            if newPosition < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            result := newPosition - oldPosition;
        end;
    {%endregion}

    {%region  FileOutputStream }
        constructor FileOutputStream.create(handle: long; const rootPath: AnsiString; seekable: FileSeekExtension; reader: FileInputStream);
        begin
            inherited create(handle, rootPath);
            if seekable <> nil then begin
                fldOwnedSeekable := false;
            end else begin
                seekable := FileSeekExtension.create(handle);
                fldOwnedSeekable := true;
            end;
            fldReader := reader;
            fldExtensions := [ self, seekable ];
        end;

        destructor FileOutputStream.destroy;
        begin
            if fldOwnedSeekable then begin
                fldExtensions[1].free();
            end;
            inherited destroy;
        end;

        procedure FileOutputStream.close();
        var
            handle: int;
        begin
            handle := int(fldHandle);
            destroy;
            if baseunix.fpClose(handle) < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure FileOutputStream.flush();
        begin
            if doSyscall(SYSCALL_NR_FSYNC, int(fldHandle)) < 0 then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
        end;

        procedure FileOutputStream.truncate();
        var
            handle: int;
            locLength: long;
            reader: FileInputStream;
        begin
            handle := int(fldHandle);
            locLength := baseunix.fpLSeek(handle, 0, baseunix.SEEK_CUR);
            if (locLength < 0) or (baseunix.fpFTruncate(handle, locLength) < 0) then begin
                raise IOException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io'));
            end;
            reader := fldReader;
            if fldMarked > locLength then fldMarked := locLength;
            if (reader <> nil) and (reader.fldMarked > locLength) then reader.fldMarked := locLength;
        end;

        procedure FileOutputStream.reset();
        begin
            FileSeekExtension(fldExtensions[1]).seek(fldMarked, SeekFrom.sfBegin);
        end;

        procedure FileOutputStream.mark(transferLimit: int);
        var
            locPosition: long;
        begin
            locPosition := baseunix.fpLSeek(int(fldHandle), 0, baseunix.SEEK_CUR);
            if locPosition < 0 then begin
                fldMarked := 0;
                exit;
            end;
            fldMarked := locPosition;
        end;
    {%endregion}

    {%region  CurrentEnvironment }
        procedure CurrentEnvironment.update();
        const
            BUFFER_LENGTH = int($0200);
        var
            index: int;
            readed: int;
            str: UnicodeString;
            name: UnicodeString;
            value: UnicodeString;
            buffer: byte_Array1d;
            stream: byte_Array1d;
            ptr: system.PAnsiChar;
            vars: HashtableOfUnicodeStringToUnicodeString;
            emon: Mutex;
        begin
            stream := nil;
            try
                buffer := &Array.newByte1d(BUFFER_LENGTH);
                with ByteArrayOutputStream.create() do try
                    with FileInputStream.create(baseunix.fpOpen('/proc/self/environ', baseunix.O_RDONLY)) do try
                        repeat
                            readed := read(buffer, 0, BUFFER_LENGTH);
                            if readed < 0 then break;
                            write(buffer, 0, readed);
                        until false;
                    finally
                        close();
                    end;
                    write(0); { Последний нулевой байт обязателен! Нужен для работы нижеследующего кода. }
                    stream := toByteArray();
                finally
                    close();
                end;
                buffer := nil;
            except
            end;
            emon := fldMonitor;
            emon.beginSynchronized();
            try
                vars := fldVariables;
                vars.clear();
                if stream <> nil then begin
                    ptr := @(stream[0]);
                    while ptr[0] <> #$00 do begin
                        str := AnsiString(ptr).toUTF16();
                        index := str.indexOf('=');
                        name := str.substring(1, index).trim();
                        value := str.substring(index + 1).trim();
                        vars[name] := value;
                        inc(ptr, system.length(ptr) + 1);
                    end;
                end;
            finally
                emon.endSynchronized();
            end;
        end;

        constructor CurrentEnvironment.create();
        begin
            inherited create();
            update();
        end;

        function CurrentEnvironment.isEmulation(): boolean;
        begin
            result := true;
        end;
    {%endregion}

    {%region  CurrentProcess }
        class procedure CurrentProcess.initialize();
        begin
            instance := CurrentProcess.create();
        end;

        class procedure CurrentProcess.finalize();
        begin
            instance.free();
        end;

        class function CurrentProcess.parseCommandLine(): UnicodeString_Array1d;
        const
            BUFFER_LENGTH = int($0200);
        var
            length: int;
            readed: int;
            buffer: byte_Array1d;
            stream: byte_Array1d;
            ptr: system.PAnsiChar;
            block: system.PAnsiChar;
            arguments: UnicodeString_Array1d;
        begin
            stream := nil;
            try
                buffer := &Array.newByte1d(BUFFER_LENGTH);
                with ByteArrayOutputStream.create() do try
                    with FileInputStream.create(baseunix.fpOpen('/proc/self/cmdline', baseunix.O_RDONLY)) do try
                        repeat
                            readed := read(buffer, 0, BUFFER_LENGTH);
                            if readed < 0 then break;
                            write(buffer, 0, readed);
                        until false;
                    finally
                        close();
                    end;
                    write(0); { Последний нулевой байт обязателен! Нужен для работы нижеследующего кода. }
                    stream := toByteArray();
                finally
                    close();
                end;
                buffer := nil;
            except
            end;
            length := 0;
            arguments := nil;
            if stream <> nil then begin
                block := @(stream[0]);
                ptr := block;
                while ptr[0] <> #$00 do begin
                    inc(ptr, system.length(ptr) + 1);
                    inc(length);
                end;
                arguments := &Array.newUnicodeString1d(length);
                length := 0;
                ptr := block;
                while ptr[0] <> #$00 do begin
                    arguments[length] := AnsiString(ptr).toUTF16();
                    inc(ptr, system.length(ptr) + 1);
                    inc(length);
                end;
            end;
            if length <= 0 then begin
                result := [ AnsiString(baseunix.fpReadLink('/proc/self/exe')).toUTF16() ];
                exit;
            end;
            arguments[0] := AnsiString(baseunix.fpReadLink('/proc/self/exe')).toUTF16();
            result := arguments;
        end;

        procedure CurrentProcess.setWorkingDirectory(const newWorkingDirectory: UnicodeString);
        begin
            if not newWorkingDirectory.startsWith('/') or (baseunix.fpChDir(FileSystemRoot.toInternalPath(newWorkingDirectory).toUTF8()) <> 0) then begin
                raise IllegalPropertyValueException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument.property'), [
                    CoAnsiString.create('workingDirectory'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
                ]));
            end;
        end;

        procedure CurrentProcess.setCommandLine(const newCommandLine: UnicodeString_Array1d);
        begin
            raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
                CoAnsiString.create('commandLine'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
            ]));
        end;

        procedure CurrentProcess.setStandardInput(newStandardInput: ByteReader);
        begin
            raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
                CoAnsiString.create('standardInput'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
            ]));
        end;

        procedure CurrentProcess.setStandardOutput(newStandardOutput: ByteWriter);
        begin
            raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
                CoAnsiString.create('standardOutput'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
            ]));
        end;

        procedure CurrentProcess.setStandardError(newStandardError: ByteWriter);
        begin
            raise ReadOnlyPropertyException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'unsupported-operation.property.read-only'), [
                CoAnsiString.create('standardError'), CoAnsiString.create(Lang.classFor(CurrentProcess).getCanonicalName())
            ]));
        end;

        function CurrentProcess.getWorkingDirectory(): UnicodeString;
        var
            path: AnsiString;
        begin
            path := baseunix.fpReadLink('/proc/self/cwd');
            if not path.endsWith('/') then path := path + '/';
            result := FileSystemRoot.toObjectPath(path.toUTF16());
        end;

        function CurrentProcess.createEnvironment(): Environment;
        begin
            result := CurrentEnvironment.create();
        end;

        constructor CurrentProcess.create();
        var
            locStdIn: HandleInputStream;
            locStdOut: HandleOutputStream;
            locStdErr: HandleOutputStream;
        begin
            inherited create();
            locStdIn := HandleInputStream.create(0);
            locStdOut := HandleOutputStream.create(1);
            locStdErr := HandleOutputStream.create(2);
            fldStarting := true;
            fldCommandLine := parseCommandLine();
            fldStandardInput := locStdIn;
            fldStandardOutput := locStdOut;
            fldStandardError := locStdErr;
            fldStandardInputRef := locStdIn;
            fldStandardOutputRef := locStdOut;
            fldStandardErrorRef := locStdErr;
        end;

        destructor CurrentProcess.destroy;
        begin
            fldStandardInputRef.free();
            fldStandardOutputRef.free();
            fldStandardErrorRef.free();
            inherited destroy;
        end;

        function CurrentProcess.isTerminated(): boolean;
        begin
            result := false;
        end;

        function CurrentProcess.getExitCode(): int;
        begin
            result := STILL_ACTIVE;
        end;
    {%endregion}

    {$ENDIF}

    {%region  OutOfResourcesError}
        procedure OutOfResourcesError.freeAllow();
        begin
            inherited freeAllow();
        end;

        procedure OutOfResourcesError.freeDisallow();
        begin
            inherited freeDisallow();
        end;
    {%endregion}

    {%region  MemoryRegionCollection }
        constructor MemoryRegionCollection.create(const regions: MemoryRegion_Array1d; length: int);
        begin
            inherited create();
            fldLength := length;
            fldRegions := regions;
        end;

        procedure MemoryRegionCollection.copyInto(const dstArray; dstOffset: int);
        begin
            &Array.copyUnknowns(fldRegions, 0, dstArray, dstOffset, fldLength);
        end;

        function MemoryRegionCollection.getLength(): int;
        begin
            result := fldLength;
        end;

        function MemoryRegionCollection.componentAt(index: int): MemoryRegion;
        begin
            if (index < 0) or (index >= fldLength) then begin
                raise ArrayIndexOutOfBoundsException.create(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'out-of-bounds.array-index'));
            end;
            result := fldRegions[index];
        end;

        function MemoryRegionCollection.toArray(): MemoryRegion_Array1d;
        var
            length: int;
            regions: MemoryRegion_Array1d;
        begin
            length := fldLength;
            regions := MemoryRegion_Array1d(&Array.newIUnknown1d(length));
            &Array.copyUnknowns(fldRegions, 0, regions, 0, length);
            result := regions;
        end;
    {%endregion}

    {%region  FileSystemRootCollection }
        constructor FileSystemRootCollection.create(const roots: FileSystemRoot_Array1d; length: int);
        begin
            inherited create();
            fldLength := length;
            fldRoots := roots;
        end;

        procedure FileSystemRootCollection.copyInto(const dstArray; dstOffset: int);
        begin
            &Array.copyObjects(fldRoots, 0, dstArray, dstOffset, fldLength);
        end;

        function FileSystemRootCollection.getLength(): int;
        begin
            result := fldLength;
        end;

        function FileSystemRootCollection.componentAt(index: int): FileSystemRoot;
        begin
            if (index < 0) or (index >= fldLength) then begin
                raise ArrayIndexOutOfBoundsException.create(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'out-of-bounds.array-index'));
            end;
            result := fldRoots[index];
        end;

        function FileSystemRootCollection.toArray(): FileSystemRoot_Array1d;
        var
            length: int;
            roots: FileSystemRoot_Array1d;
        begin
            length := fldLength;
            roots := FileSystemRoot_Array1d(&Array.newTObject1d(length));
            &Array.copyObjects(fldRoots, 0, roots, 0, length);
            result := roots;
        end;
    {%endregion}

    {%region  Extensions }
        function Extensions.getExtensions(): Extension_Array1d;
        var
            index: int;
            length: int;
            source: TObject_Array1d;
            extensions: Extension_Array1d;
        begin
            source := fldExtensions;
            length := system.length(source);
            extensions := Extension_Array1d(&Array.newISimple1d(length));
            for index := length - 1 downto 0 do begin
                extensions[index] := source[index] as Extension;
            end;
            result := extensions;
        end;

        function Extensions.getExtension(const typ: ShortString): Extension;
        var
            index: int;
            source: TObject_Array1d;
            ext: Extension;
        begin
            source := fldExtensions;
            for index := 0 to system.length(source) - 1 do begin
                ext := source[index] as Extension;
                if Lang.isInstance(ext, typ) then begin
                    result := Extension(Lang.cast(ext, typ));
                    exit;
                end;
            end;
            result := nil;
        end;
    {%endregion}

    {%region  FileBidirectStream }
        constructor FileBidirectStream.create(handle: long; const rootPath: AnsiString);
        var
            reader: FileInputStream;
            writer: FileOutputStream;
            seekable: FileSeekExtension;
        begin
            inherited create(handle, handle);
            seekable := FileSeekExtension.create(handle);
            reader := FileBidirectStreamReader.create(handle, seekable);
            writer := FileBidirectStreamWriter.create(handle, rootPath, seekable, reader);
            fldReader := reader;
            fldWriter := writer;
            fldExtensions := [ seekable ];
        end;

        destructor FileBidirectStream.destroy;
        begin
            fldExtensions[0].free();
            inherited destroy;
        end;
    {%endregion}

    {%region  FileBidirectStreamReader }
        procedure FileBidirectStreamReader.close();
        begin
        end;
    {%endregion}

    {%region  FileBidirectStreamWriter }
        procedure FileBidirectStreamWriter.close();
        begin
        end;
    {%endregion}

    {%region  PipeStream }
        constructor PipeStream.create(handleForRead, handleForWrite: long);
        begin
            inherited create(handleForRead, handleForWrite);
            fldReader := HandleInputStream.create(handleForRead);
            fldWriter := HandleOutputStream.create(handleForWrite);
        end;
    {%endregion}

    {%region} initialization
        Lang.registerInterfaces([
            typeInfo(MemoryRegion)
        ]);

        exceptionInitialize();
        fileSystemRootInitialize();
        VolumeObjectAttributes.initialize();
        SystemInfo.initialize();
        CurrentProcess.initialize();

        fileSystemRootCreateWorkingRoot();
    {%endregion}

    {%region} finalization
        CurrentProcess.finalize();
        VolumeObjectAttributes.finalize();
        fileSystemRootFinalize();
        exceptionFinalize();
    {%endregion}

end.