Exemplo n.º 1
0
            /// <summary>
            /// Try to link to an existing central name file.
            /// If the central name file exist, and the link limit is not exceeded, the destination file is linked to the central name file, and the return value is true.
            /// In this case, the backup of the destination file is considered finished.
            /// If the central name file does not exist, or linking fails due to an exceeded link limit, the return value is false.
            /// </summary>
            /// <returns>True if linking of the destination file to the existing central name file succeeded;, otherwise, false.</returns>
            /// <exception cref="System.IO.IOException">Error creating hard link from {_centralNameFileName} to {_destinationFileName}, ErrorCode: {hlr}</exception>
            public bool TryToLinkToExistingNameFile()
            {
                var fileInfoCentralNameFileName = new FileInfo(_centralNameFileName);

                // Compare the FileInfo of the central name file with the FileInfo of the source file
                // only if both match, we can link the destination file name to the centralNameFile
                if (fileInfoCentralNameFileName.Exists && FileUtilities.AreMetaDataMatching(fileInfoCentralNameFileName, _sourceFile))
                {
                    var hlr = FileUtilities.CreateHardLink(_centralNameFileName, _destinationFileName);
                    if (hlr == 0)
                    {
                        return(true);
                    }
                    else
                    {
                        if (hlr != FileUtilities.ERROR_TOO_MANY_LINKS) // ignore TooManyLinks error, instead let the content file be created anew
                        {
                            throw new System.IO.IOException($"Error creating hard link from {_centralNameFileName} to {_destinationFileName}, ErrorCode: {hlr}");
                        }
                    }
                }
                return(false);
            }
            public void Write()
            {
                try
                {
                    if (File.Exists(_centralStorageFileName)) // central storage file already exists
                    {
                        // make a hardlink from the central storage file to the source file
                        // but first we have to delete the source file
                        if (_sourceFile.IsReadOnly)
                        {
                            _sourceFile.IsReadOnly = false;
                        }
                        _sourceFile.Delete();
                        var hlr = FileUtilities.CreateHardLink(_centralStorageFileName, _sourceFile.FullName);
                        if (hlr != 0)
                        {
                            if (hlr == FileUtilities.ERROR_TOO_MANY_LINKS)
                            {
                                // if the hardlink limit is exceeded, we need to make a full copy of the source file
                                // effectively replacing the central storage file
                                var tempFileName = FileUtilities.FileRenameToTemporaryFileInSameFolder(_centralStorageFileName);
                                File.Copy(tempFileName, _centralStorageFileName);
                                File.Delete(tempFileName);
                                // now, try to create the hardlink again, now there should be no hardlink limit error any more
                                var hlr2 = FileUtilities.CreateHardLink(_centralStorageFileName, _sourceFile.FullName);
                                if (hlr2 != 0)
                                {
                                    throw new System.IO.IOException($"Error create hard link from {_sourceFile.FullName} to {_centralStorageFileName}, ErrorCode: {hlr2}");
                                }
                            }
                            else
                            {
                                throw new System.IO.IOException($"Error create hard link from {_sourceFile.FullName} to {_centralStorageFileName}, ErrorCode: {hlr}");
                            }
                        }
                    }
                    else // Central storage file does not exist yet
                    {
                        if (!Directory.Exists(_centralStorageFolder))
                        {
                            Directory.CreateDirectory(_centralStorageFolder);
                        }

                        // create a hard link from the existing (old) backup file to the central storage file
                        int hlr = FileUtilities.CreateHardLink(_sourceFile.FullName, _centralStorageFileName);
                        if (hlr != 0)
                        {
                            if (hlr == FileUtilities.ERROR_TOO_MANY_LINKS)
                            {
                                // if the hardlink limit is exceeded, we need to make a full copy instead of a
                                // hard link
                                File.Copy(_sourceFile.FullName, _centralStorageFileName);
                            }
                            else
                            {
                                throw new System.IO.IOException($"Error create hard link from {_sourceFile.FullName} to {_centralStorageFileName}");
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    OnFailed(ex);
                }
            }
Exemplo n.º 3
0
            /// <summary>
            /// Writes the backup file, the central content file and the central name file.
            /// </summary>
            /// <remarks>
            /// All three files, the backup file, the central content file and the central name file have the same data content and are (ideally) linked by hard links.
            /// The name of the backup file is the same as the name of the original file.
            /// The name of the central content file is determined by the hash of the file content.
            /// The name of the central name file is determined by the hash of the original file's metadata (length, last write time, attributes, and full name).
            /// </remarks>
            public void Write()
            {
                try
                {
                    var backupMode = _parent._backupMode;
                    if (backupMode == BackupMode.Fast && TryToLinkToExistingNameFile())
                    {
                        NextStation = NextStation.Finished;
                    }
                    else
                    {
                        if (!IsContentFileNameKnown)
                        {
                            NextStation = NextStation.Reader;
                        }
                        else
                        {
                            bool isRepeatRequired = false;
                            do
                            {
                                if (!File.Exists(_centralContentFileName))
                                {
                                    if (!Directory.Exists(_centralContentFolder))
                                    {
                                        Directory.CreateDirectory(_centralContentFolder);
                                    }

                                    if (_bytesInBuffer == (_streamLength + FileUtilities.BufferSpaceForLengthWriteTimeAndFileAttributes) && _buffer is { } buffer)
                                    {
                                        using (var destStream = new FileStream(_centralContentFileName, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
                                        {
                                            destStream.Write(_buffer, FileUtilities.BufferSpaceForLengthWriteTimeAndFileAttributes, _bytesInBuffer - FileUtilities.BufferSpaceForLengthWriteTimeAndFileAttributes);
                                        }
                                        // Set attributes on the destination file, such as last write time, and attributes.
                                        File.SetLastWriteTimeUtc(_centralContentFileName, _sourceFile.LastWriteTimeUtc);
                                        File.SetAttributes(_centralContentFileName, _sourceFile.Attributes & FileUtilities.FileAttributeMask);
                                    }
                                    else
                                    {
                                        // we do not use the destination stream here
                                        File.Copy(_sourceFile.FullName, _centralContentFileName);
                                        // note: when copying, attributes and LastWriteTime is already set.
                                    }
                                }

                                {
                                    isRepeatRequired = false;
                                    // Central storage file now exists, so create a hard link to it in the backup directory
                                    var hlr = FileUtilities.CreateHardLink(_centralContentFileName, _destinationFileName);
                                    if (hlr != 0)
                                    {
                                        if (hlr == FileUtilities.ERROR_TOO_MANY_LINKS) // is hard link limit exceeded?
                                        {
                                            // in order to circumvent the hard link limit, we delete the file from the central storage, and create it anew
                                            File.Delete(_centralContentFileName);
                                            isRepeatRequired = true;
                                            continue;
                                        }
                                        else
                                        {
                                            throw new System.IO.IOException($"Error creating hard link {_destinationFileName}, ErrorCode: {hlr}");
                                        }
                                    }
                                }

                                // Handle the name file (except when in BackupMode.SecureNoNameFile)
                                if (backupMode == BackupMode.Fast && !File.Exists(_centralNameFileName))
                                {
                                    var folder = Path.GetDirectoryName(_centralNameFileName);
                                    if (!Directory.Exists(folder))
                                    {
                                        Directory.CreateDirectory(folder);
                                    }
                                    var hlr = FileUtilities.CreateHardLink(_centralContentFileName, _centralNameFileName);
                                    if (hlr != 0)
                                    {
                                        if (hlr == FileUtilities.ERROR_TOO_MANY_LINKS)
                                        {
                                            File.Delete(_centralNameFileName);
                                        }
                                        else
                                        {
                                            throw new System.IO.IOException($"Error creating hard link {_destinationFileName}, ErrorCode: {hlr}");
                                        }
                                    }
                                }
                            } while (isRepeatRequired);
                            _stream?.Close();
                            _stream?.Dispose();
                            NextStation = NextStation.Finished;
                        }
                    }
                }