示例#1
0
 public FileScanner(string startDir, Database database, Logger logger, string name, BackendBase[] backends, string fileIgnorePattern, HashSet<string> ignoredFiles, HashSet<string> ignoredFolders) {
     this.Database = database;
     this.treeTraverser = new TreeTraverser(startDir, fileIgnorePattern, ignoredFiles, ignoredFolders);
     this.fileDatabase = new FileDatabase(database);
     this.backends = backends;
     foreach (BackendBase backend in this.backends) {
         backend.CopyProgress += new BackendBase.CopyProgressEventHandler(Backend_CopyProgress);
     }
     this.Logger = logger;
     this.Name = name;
 }
示例#2
0
        public void Restore(bool overwrite, bool overwriteOnlyIfOlder, bool purge)
        {
            // We can only restore from one backend...
            if (this.backends.Length != 1)
            {
                throw new ArgumentException("Cannot perform a restore when more than one backend given");
            }
            BackendBase backend = this.backends[0];

            int curFolderId = 0;

            FileDatabase.FolderStatus folderStatus;
            FileDatabase.FileStatusWithLastModified fileStatus;
            bool     copyFile = false;
            string   fileMd5;
            string   destMd5;
            DateTime copyFileLastModified;

            TreeTraverser.FolderEntry folder;
            TreeTraverser.FileEntry   file;

            foreach (BackendBase.EntityRecord entity in backend.ListFilesFolders())
            {
                if (this.Cancelled)
                {
                    return;
                }

                if (entity.Type == BackendBase.Entity.Folder)
                {
                    folder = this.treeTraverser.CreateFolderEntry(entity.Path);
                    this.reportBackupAction(new BackupActionItem(null, folder.RelPath, BackupActionEntity.Folder, BackupActionOperation.Add));
                    if (!Directory.Exists(folder.FullPath))
                    {
                        this.Logger.Info("Restoring folder: {0}", folder.RelPath);
                        try {
                            backend.RestoreFolder(folder.RelPath, folder.FullPath);
                        }
                        catch (BackupOperationException e) { this.handleOperationException(new BackupOperationException(folder.RelPath, e.Message)); }
                    }
                    // Regardless of whether the folder was created or not, we need some data for the file bit
                    // Also, if the folder didn't exist in the database, now's the time to add it
                    folderStatus = this.fileDatabase.InspectFolder(folder.RelPath);
                    if (folderStatus.Id >= 0)
                    {
                        curFolderId = folderStatus.Id;
                    }
                    else
                    {
                        curFolderId = this.fileDatabase.AddFolder(folder.RelPath);
                    }
                }
                else
                {
                    file = this.treeTraverser.CreateFileEntry(entity.Path);
                    // Ignore this: we don't want to restore our own backup DB
                    if (file.RelPath == Path.GetFileName(Database.FilePath))
                    {
                        continue;
                    }
                    destMd5 = null;

                    this.reportBackupAction(new BackupActionItem(null, file.RelPath, BackupActionEntity.File, BackupActionOperation.Add));
                    if (File.Exists(file.FullPath))
                    {
                        // Right, now we need to find some info about the file, if we can
                        fileStatus = this.fileDatabase.InspectFileWithLastModified(curFolderId, file.RelPath, file.LastModified);

                        if (overwrite)
                        {
                            switch (fileStatus.FileModStatus)
                            {
                            case FileDatabase.FileModStatus.New:
                                // No record of the file in the database. Therefore we're copying it if the md5's don't match
                                copyFile = true;
                                break;

                            case FileDatabase.FileModStatus.Older:
                                // File on filesystem is older than in the database. Therefore we're copying it if the md5's don't match
                                copyFile = true;
                                break;

                            case FileDatabase.FileModStatus.Newer:
                            case FileDatabase.FileModStatus.Unmodified:
                                // File on the filesystem is newer or the same age as in the database. Therefore copy only if !overwriteOnlyIfOlder and the md5s don't match
                                copyFile = !overwriteOnlyIfOlder;
                                break;
                            }
                        }
                        else
                        {
                            copyFile = false;
                        }

                        if (copyFile)
                        {
                            fileMd5 = fileStatus.MD5 == null?backend.FileMD5(file.RelPath) : fileStatus.MD5;

                            // If it's still null, we have no way of getting the md5 from anywhere, so just copy the damn thing
                            // If not, test the file
                            if (fileMd5 != null)
                            {
                                destMd5 = file.GetMD5((percent) => {
                                    this.reportBackupAction(new BackupActionItem(null, file.RelPath, BackupActionEntity.File, BackupActionOperation.Hash, null, percent));
                                    return(!this.Cancelled);
                                });
                                if (this.Cancelled)
                                {
                                    return;
                                }
                                if (fileMd5 == destMd5)
                                {
                                    copyFile = false;
                                    this.Logger.Info("Skipping file as identical: {0}", file.RelPath);
                                }
                                else
                                {
                                    this.Logger.Info("File exists but is being overwritten: {0}", file.RelPath);
                                }
                            }
                            else
                            {
                                this.Logger.Info("File exists but is being overwritten: {0}", file.RelPath);
                            }
                        }
                    }
                    else
                    {
                        // No file in dest, so automatically copy
                        copyFile = true;
                        // Still need to see if we can get its last modified time, if the record exists at all
                        // We don't care about the modified status, so just feed in anything
                        fileStatus = this.fileDatabase.InspectFileWithLastModified(curFolderId, file.RelPath, DateTime.Now);
                        this.Logger.Info("Restoring file: {0}", file.RelPath);
                    }

                    // We need to set the utime of the existing/new file.
                    // If we've got a DB record, use that. otherwise ask the backend. Otherwise now
                    // The backend's FileLastModified defaults to DateTime.UtcNow, which is the default we want, so keep that
                    copyFileLastModified = fileStatus.MD5 == null?backend.FileLastModified(file.RelPath) : fileStatus.LastModified;

                    if (!copyFile)
                    {
                        // If the file exists, but we didn't write it, touch it instead
                        if (File.Exists(file.FullPath))
                        {
                            file.LastModified = copyFileLastModified;
                        }
                    }
                    else
                    {
                        // So. we definitely decided to copy.
                        try {
                            backend.RestoreFile(file.RelPath, file.FullPath, copyFileLastModified);
                        }
                        catch (BackupOperationException e) { this.handleOperationException(e); }
                    }

                    // The only condition we need to update the DB is for insertions, as if the record exists already we match the dest to the DB
                    // Therefore we need a file hash. Yay.
                    if (fileStatus.MD5 == null)
                    {
                        // If we haven't found the MD5 of the file yet, calculate it now. Look at the file on the dest, as it's likely quicker
                        if (destMd5 == null)
                        {
                            destMd5 = file.GetMD5((percent) => {
                                this.reportBackupAction(new BackupActionItem(null, file.RelPath, BackupActionEntity.File, BackupActionOperation.Hash, null, percent));
                                return(!this.Cancelled);
                            });
                            if (this.Cancelled)
                            {
                                return;
                            }
                        }

                        this.fileDatabase.AddFile(curFolderId, file.RelPath, copyFileLastModified, destMd5);
                    }
                }
            }

            if (purge)
            {
                IEnumerable <string> recordedFolders = this.fileDatabase.RecordedFolders().Select(x => x.Path);
                IEnumerable <string> recordedFiles   = this.fileDatabase.RecordedFiles().Select(x => x.Path);

                foreach (TreeTraverser.FolderEntry purgeFolder in this.treeTraverser.ListFolders())
                {
                    foreach (TreeTraverser.FileEntry purgeFile in purgeFolder.GetFiles())
                    {
                        if (!recordedFiles.Contains(purgeFile.RelPath))
                        {
                            this.Logger.Info("Deleting file {0}:", purgeFile.RelPath);
                            try {
                                this.treeTraverser.DeleteFile(purgeFile.RelPath);
                            }
                            catch (BackupOperationException e) { this.handleOperationException(e); }
                        }
                    }
                    this.Logger.Info("Deleting folder {0}:", purgeFolder.RelPath);
                    if (!recordedFolders.Contains(purgeFolder.RelPath))
                    {
                        this.treeTraverser.DeleteFolder(purgeFolder.RelPath);
                    }
                }
            }
        }
示例#3
0
        private bool createFromAlternate(TreeTraverser.FileEntry file, string fileMD5, bool update, BackendBase backend)
        {
            // if update is true, we're updating the dest file. otherwise we're adding it
            // Return true if we made use of an alternate, or false if we did nothing
            string logAction = update ? "Updated" : "Added";

            FileDatabase.FileRecord[] alternates = this.fileDatabase.SearchForAlternates(fileMD5).ToArray();
            if (alternates.Length == 0)
            {
                return(false);
            }

            // Now, each alternate may not in fact exist on the backend, or may be changed. We can't assume stuff like this
            // So, loop through them all, and attempt to use it. Bail after the first successful one

            bool foundGoodAlternate = false;

            foreach (FileDatabase.FileRecord alternate in alternates)
            {
                // First, does it even exist on the backend? Skip to the next one if not
                if (!backend.TestFile(alternate.Path, file.LastModified, fileMD5))
                {
                    continue;
                }

                // Next: Is is a copy or a move?
                if (this.treeTraverser.FileExists(alternate.Path))
                {
                    // It's a copy
                    this.reportBackupAction(new BackupActionItem(alternate.Path, file.RelPath, BackupActionEntity.File, BackupActionOperation.Copy, backend.Name));
                    if (backend.CreateFromAlternateCopy(file.RelPath, alternate.Path))
                    {
                        this.Logger.Info("{0}: {1} file: {2} from alternate {3} (copy)", backend.Name, logAction, file.RelPath, alternate.Path);
                    }
                    else
                    {
                        backend.CreateFile(file.RelPath, file.FullPath, file.LastModified, fileMD5, file.Attributes);
                        this.Logger.Info("{0}: {1} file: {2} (backend refused alternate {3})", backend.Name, logAction, file.RelPath, alternate.Path);
                    }
                }
                else
                {
                    // It's a move
                    this.reportBackupAction(new BackupActionItem(alternate.Path, file.RelPath, BackupActionEntity.File, BackupActionOperation.Move, backend.Name));
                    backend.CreateFromAlternateMove(file.RelPath, alternate.Path);
                    this.Logger.Info("{0}: {1} file: {2} from alternate {3} (move)", backend.Name, logAction, file.RelPath, alternate.Path);
                    this.fileDatabase.DeleteFile(alternate.Id);
                }

                // We're all golden
                foundGoodAlternate = true;
                break;
            }

            return(foundGoodAlternate);
        }
示例#4
0
 public static void BackupDatabase(string databasePath, BackendBase[] backends) {
     foreach (BackendBase backend in backends) {
         string dbFile = Database.GetExportableFile(databasePath, backend.StripFilesFoldersOnDBBackup);
         backend.BackupDatabase(Path.GetFileName(databasePath), dbFile);
     }
 }
示例#5
0
        private bool createFromAlternate(TreeTraverser.FileEntry file, string fileMD5, bool update, BackendBase backend) {
            // if update is true, we're updating the dest file. otherwise we're adding it
            // Return true if we made use of an alternate, or false if we did nothing
            string logAction = update ? "Updated" : "Added";

            FileDatabase.FileRecord[] alternates = this.fileDatabase.SearchForAlternates(fileMD5).ToArray();
            if (alternates.Length == 0)
                return false;

            // Now, each alternate may not in fact exist on the backend, or may be changed. We can't assume stuff like this
            // So, loop through them all, and attempt to use it. Bail after the first successful one

            bool foundGoodAlternate = false;

            foreach (FileDatabase.FileRecord alternate in alternates) {
                // First, does it even exist on the backend? Skip to the next one if not
                if (!backend.TestFile(alternate.Path, file.LastModified, fileMD5))
                    continue;

                // Next: Is is a copy or a move?
                if (this.treeTraverser.FileExists(alternate.Path)) {
                    // It's a copy
                    this.reportBackupAction(new BackupActionItem(alternate.Path, file.RelPath, BackupActionEntity.File, BackupActionOperation.Copy, backend.Name));
                    if (backend.CreateFromAlternateCopy(file.RelPath, alternate.Path))
                        this.Logger.Info("{0}: {1} file: {2} from alternate {3} (copy)", backend.Name, logAction, file.RelPath, alternate.Path);
                    else {
                        backend.CreateFile(file.RelPath, file.FullPath, file.LastModified, fileMD5, file.Attributes);
                        this.Logger.Info("{0}: {1} file: {2} (backend refused alternate {3})", backend.Name, logAction, file.RelPath, alternate.Path);
                    }
                }
                else {
                    // It's a move
                    this.reportBackupAction(new BackupActionItem(alternate.Path, file.RelPath, BackupActionEntity.File, BackupActionOperation.Move, backend.Name));
                    backend.CreateFromAlternateMove(file.RelPath, alternate.Path);
                    this.Logger.Info("{0}: {1} file: {2} from alternate {3} (move)", backend.Name, logAction, file.RelPath, alternate.Path);
                    this.fileDatabase.DeleteFile(alternate.Id);
                }

                // We're all golden
                foundGoodAlternate = true;
                break;
            }

            return foundGoodAlternate;
        }