public override int Overwrite(object fileNode,
                                      object fileDesc,
                                      uint fileAttributes,
                                      bool replaceFileAttributes,
                                      ulong allocationSize,
                                      out FileInfo fileInfo)
        {
            if (fileNode is FileNode node &&
                fileDesc is FileMetadata metadata)
            {
                Logger.LogTrace($"{nameof ( Overwrite )} \"{metadata . Name}\".");

                metadata.Size = 0;
                if (replaceFileAttributes)
                {
                    metadata.Attributes = fileAttributes;
                }
                else
                {
                    metadata.Attributes |= fileAttributes;
                }

                FlushMetadata( );

                fileInfo = metadata.FileInfo;

                return(STATUS_SUCCESS);
            }

            throw GetIoExceptionWithNtStatus(STATUS_INVALID_HANDLE);
        }
        public override int Flush(object fileNode, object fileDesc, out FileInfo fileInfo)
        {
            Logger.LogTrace($"{nameof ( Flush )} {( fileDesc as FileMetadata ) ? . Name}");

            if (fileNode is FileNode node)
            {
                OnetimeTask flushTask = new OnetimeTask(node.Flush, default);
                TaskDispatcher.Dispatch(flushTask);
            }

            FlushMetadata( );

            if (fileDesc is FileMetadata metadata)
            {
                fileInfo = metadata.FileInfo;
            }
            else
            {
                throw GetIoExceptionWithNtStatus(STATUS_INVALID_HANDLE);
            }

            return(STATUS_SUCCESS);
        }
        public override bool ReadDirectoryEntry(object fileNode,
                                                object fileDesc,
                                                string pattern,
                                                string marker,
                                                ref object context,
                                                out string fileName,
                                                out FileInfo fileInfo)
        {
            if (fileDesc is FileMetadata metadata)
            {
                if (!(context is EnumerateDirectoryContext currentContext))
                {
                    Logger.LogTrace(
                        $"{nameof ( ReadDirectoryEntry )} of \"{metadata . Name}\".");

                    if (null != pattern)
                    {
                        pattern = pattern.Replace('<', '*').
                                  Replace('>', '?').
                                  Replace('"', '.');
                    }
                    else
                    {
                        pattern = "*";
                    }

                    Regex fileNamePatternRegex = FindFilesPatternToRegex.Convert(pattern);

                    string pathPrefix;

                    if (metadata.Name.EndsWith("\\"))
                    {
                        pathPrefix = metadata.Name;
                    }
                    else
                    {
                        pathPrefix = $"{metadata . Name}\\";
                    }

                    string escapedPathPrefix = Regex.Escape(pathPrefix);
                    Regex  pathPrefixRegex   = new Regex(
                        @$ "^{escapedPathPrefix}([^\\]+)$",
                        RegexOptions.Compiled);

                    IEnumerable <FileMetadata> fileInFolder;
                    lock ( DataContext )
                    {
                        fileInFolder = DataContext.
                                       FileMetadata.
                                       Where((fileMetadata) => !fileMetadata.IsDeleted).
                                       Where(
                            fileMetadata
                            => fileMetadata.
                            Name.StartsWith(pathPrefix)).
                                       OrderBy(fileMetadata => fileMetadata.Name).
                                       AsEnumerable( );
                    }

                    List <(string FileName, FileMetadata FileMetadata)> availableFiles =
                        fileInFolder.Select(
                            fileMetadata =>
                    {
                        Match pathPrefixMatch =
                            pathPrefixRegex.Match(
                                fileMetadata.
                                Name);

                        if (pathPrefixMatch.Success)
                        {
                            string matchedFileName = pathPrefixMatch.
                                                     Groups [1].
                                                     Value;


                            Match fileNameMatch =
                                fileNamePatternRegex.Match(
                                    matchedFileName);

                            if (fileNameMatch.Success)
                            {
                                return(FileName: matchedFileName,
                                       FileMetadata: fileMetadata);
                            }
                        }

                        return(default);
        public override int Write(object fileNode,
                                  object fileDesc,
                                  IntPtr buffer,
                                  ulong offset,
                                  uint length,
                                  bool writeToEndOfFile,
                                  bool constrainedIo,
                                  out uint bytesTransferred,
                                  out FileInfo fileInfo)
        {
            if (fileNode is FileNode node &&
                fileDesc is FileMetadata metadata)
            {
                Logger.LogTrace(
                    $"Request {nameof ( Write )} to \"{metadata . Name}\", start from {offset} with length {length}({( ( long ) length ) . BytesCountToHumanString ( )}).");

                if (writeToEndOfFile)
                {
                    offset = ( ulong )metadata.Size;
                }

                if (!constrainedIo)
                {
                    ResizeFile(
                        node,
                        Math.Max(metadata.Size, ( long )offset + length));
                    length = ( uint )Math.Min(metadata.Size - ( long )offset, length);
                }

                int startBlockSequence = ( int )(offset / ( ulong )BlockSize);

                int endBlockSequence =
                    ( int )((offset + length + ( ulong )BlockSize - 1)
                            / ( ulong )BlockSize);

                int currentByteSequence = 0;

                int currentBlockSequence = startBlockSequence;

                CachedBlock currentBlock = node.GetBlock(currentBlockSequence);

                #region firstBlock

                int firstByteStartFrom = ( int )(offset % ( ulong )BlockSize);

                int currentBlockCopyByteCount = ( int )Math.Min(
                    BlockSize - firstByteStartFrom,
                    length
                    - currentByteSequence);

                currentBlock.IsModified = true;

                Marshal.Copy(
                    buffer + currentByteSequence,
                    currentBlock.Content,
                    firstByteStartFrom,
                    currentBlockCopyByteCount);

                currentByteSequence += currentBlockCopyByteCount;

                currentBlockSequence++;

                #endregion

                firstByteStartFrom = 0;

                for ( ; currentBlockSequence < endBlockSequence; currentBlockSequence++)
                {
                    currentBlock = node.GetBlock(currentBlockSequence);

                    currentBlockCopyByteCount = ( int )Math.Min(
                        BlockSize - firstByteStartFrom,
                        length
                        - currentByteSequence);


                    currentBlock.IsModified = true;

                    Marshal.Copy(
                        buffer + currentByteSequence,
                        currentBlock.Content,
                        firstByteStartFrom,
                        currentBlockCopyByteCount);

                    currentByteSequence += currentBlockCopyByteCount;
                }

                bytesTransferred = ( uint )currentByteSequence;

                fileInfo = metadata.FileInfo;

                Logger.LogDebug(
                    $"Written to \"{metadata . Name}\", start from {offset} with length {bytesTransferred}({( ( long ) bytesTransferred ) . BytesCountToHumanString ( )}).");

                return(STATUS_SUCCESS);
            }

            throw GetIoExceptionWithNtStatus(STATUS_INVALID_HANDLE);
        }
        public override int Create(string fileName,
                                   uint createOptions,
                                   uint grantedAccess,
                                   uint fileAttributes,
                                   byte []     securityDescriptor,
                                   ulong allocationSize,
                                   out object fileNode,
                                   out object fileDesc,
                                   out FileInfo fileInfo,
                                   out string normalizedName)
        {
            string normalizedFileName =
                fileName.Normalize(NormalizationForm.FormD).TrimEnd('\\');

            List <string> directoryDependency = normalizedFileName.
                                                Split(
                '\\',
                StringSplitOptions.RemoveEmptyEntries).
                                                SkipLast(1).
                                                ToList( );

            StringBuilder currentDirectory = new StringBuilder("\\");

            foreach (string path in directoryDependency)
            {
                currentDirectory.Append(path);

                string currentDirectoryFileName = currentDirectory.ToString( );

                FileMetadata directoryMetadata;
                lock ( DataContext )
                {
                    directoryMetadata = DataContext.FileMetadata.SingleOrDefault(
                        fileMetadata
                        => fileMetadata.
                        Name
                        == currentDirectoryFileName);
                }

                if (directoryMetadata is null)
                {
                    CreateDirectory(currentDirectoryFileName);
                }

                currentDirectory.Append('\\');
            }

            FileMetadata metadata;

            lock ( DataContext )
            {
                metadata = DataContext.FileMetadata.SingleOrDefault(
                    (fileMetadata)
                    => fileMetadata.Name
                    == normalizedFileName);
            }

            if (metadata != null)
            {
                if ((createOptions & FILE_DIRECTORY_FILE) != 0)
                {
                    //Directory

                    fileNode = null;
                }
                else
                {
                    //File

                    if (FileNodes.TryGetValue(metadata.Guid, out FileNode node))
                    {
                        node.ReferenceCount++;
                        node.ClosedTime = null;
                        fileNode        = node;
                    }
                    else
                    {
                        node = new FileNode(metadata);
                        lock ( DataContext )
                        {
                            node.Blocks = DataContext.BlockMetadata.
                                          Where(
                                block
                                => block.File
                                == metadata.Guid).
                                          OrderBy(
                                block
                                => block.BlockSequence).
                                          ToList( );
                        }

                        lock ( FileNodes )
                        {
                            FileNodes.Add(metadata.Guid, node);
                        }

                        fileNode = node;
                    }
                }
            }
            else
            {
                if ((createOptions & FILE_DIRECTORY_FILE) != 0)
                {
                    //Directory
                    metadata = CreateDirectory(
                        normalizedFileName,
                        fileAttributes,
                        securityDescriptor);

                    fileNode = null;
                }
                else
                {
                    //File

                    int allocatedBlockCount =
                        ( int )((( long )allocationSize + BlockSize - 1) / BlockSize);

                    metadata = CreateFile(
                        normalizedFileName,
                        allocatedBlockCount,
                        fileAttributes,
                        securityDescriptor);

                    List <BlockMetadata> blocks = new List <BlockMetadata> (allocatedBlockCount);

                    for (int sequenceNumber = 0;
                         sequenceNumber < allocatedBlockCount;
                         sequenceNumber++)
                    {
                        blocks.Add(CreateBlock(metadata.Guid, sequenceNumber));
                    }

                    FileNode node = new FileNode(metadata)
                    {
                        Blocks = blocks
                    };

                    FileNodes.Add(metadata.Guid, node);

                    fileNode = node;

                    FlushMetadata( );
                }
            }

            fileDesc       = metadata;
            fileInfo       = metadata.FileInfo;
            normalizedName = normalizedFileName;

            return(STATUS_SUCCESS);
        }