예제 #1
0
        //
        // Summary:
        // Update the `LastWrittenAt`,`LastSize`,`LastStatus`,`LastUpdatedAt`,`LastChecksum`
        // properties of each file in `files` according to the actual state of the file in the filesystem.
        //
        // NOTE: This function has a side effect - It updates properties of items from `files`.
        //
        private void DoUpdateBackupPlanFilesStatus(LinkedList <Models.BackupPlanFile> files, bool isNewVersion)
        {
            Assert.IsNotNull(files);

            ISession session = NHibernateHelper.GetSession();
            BackupedFileRepository daoBackupedFile = new BackupedFileRepository(session);

            BlockPerfStats stats = new BlockPerfStats();

            stats.Begin();

            // Check all files.
            LinkedListNode <Models.BackupPlanFile> node = files.First;

            while (node != null)
            {
                var next = node.Next;
                Models.BackupPlanFile entry = node.Value;
                // TODO(jweyrich): Measure whether `daoBackupedFile.GetLatestVersion(entry)` is faster or not,
                //                 and whether "entry.Versions.anything" would cause all related version to be fetched.
#if false
                Models.BackupedFile lastVersion = entry.Versions != null && entry.Versions.Count > 0
                                        ? entry.Versions.Last() : null;
#else
                // This may be a version that has not COMPLETED the transfer.
                Models.BackupedFile lastVersion = entry.Id.HasValue ? daoBackupedFile.GetLatestVersion(entry) : null;
#endif

                // Throw if the operation was canceled.
                CancellationToken.ThrowIfCancellationRequested();

                //
                // Check what happened to the file.
                //

                bool fileExistsOnFilesystem            = FileManager.FileExists(entry.Path);
                Models.BackupFileStatus?changeStatusTo = null;

                try
                {
                    //
                    // Update file properties
                    //
                    if (fileExistsOnFilesystem)
                    {
                        try
                        {
                            DateTime fileLastWrittenAt = FileManager.UnsafeGetFileLastWriteTimeUtc(entry.Path);
                            long     fileLength        = FileManager.UnsafeGetFileSize(entry.Path);

                            entry.LastWrittenAt = fileLastWrittenAt;
                            entry.LastSize      = fileLength;
                        }
                        catch (Exception ex)
                        {
                            string message = string.Format("Caught an exception while retrieving file properties: {0}", ex.Message);

                            Results.OnFileFailed(this, new FileVersionerEventArgs {
                                FilePath = entry.Path, FileSize = 0
                            }, message);
                            logger.Warn(message);

                            throw;
                        }

                        try
                        {
                            // Skip files larger than `MAX_FILESIZE_TO_HASH`.
                            int result = BigInteger.Compare(entry.LastSize, MAX_FILESIZE_TO_HASH);
                            if (result < 0)
                            {
                                entry.LastChecksum = CalculateHashForFile(entry.Path);
                            }
                        }
                        catch (Exception ex)
                        {
                            string message = string.Format("Caught an exception while calculating the file hash: {0}", ex.Message);

                            Results.OnFileFailed(this, new FileVersionerEventArgs {
                                FilePath = entry.Path, FileSize = 0
                            }, message);
                            logger.Warn(message);

                            throw;
                        }

                        Results.OnFileCompleted(this, new FileVersionerEventArgs {
                            FilePath = entry.Path, FileSize = entry.LastSize
                        });
                    }

                    //
                    // Update file status
                    //
                    if (lastVersion != null)                     // File was backed up at least once in the past?
                    {
                        switch (entry.LastStatus)
                        {
                        case Models.BackupFileStatus.DELETED:                               // File was marked as DELETED by a previous backup?
                            if (fileExistsOnFilesystem)                                     // Exists?
                            {
                                changeStatusTo = Models.BackupFileStatus.ADDED;
                            }
                            break;

                        case Models.BackupFileStatus.REMOVED:                               // File was marked as REMOVED by a previous backup?
                            if (fileExistsOnFilesystem)                                     // Exists?
                            {
                                changeStatusTo = Models.BackupFileStatus.ADDED;
                            }
                            else
                            {
                                // QUESTION: Do we really care to transition REMOVED to DELETED?
                                changeStatusTo = Models.BackupFileStatus.DELETED;
                            }
                            break;

                        default:                                 // ADDED, MODIFIED, UNCHANGED
                            if (fileExistsOnFilesystem)          // Exists?
                            {
                                // DO NOT verify whether the file changed for a `ResumeBackupOperation`,
                                // only for `NewBackupOperation`.
                                if (isNewVersion)
                                {
                                    if (IsFileModified(entry, lastVersion))                                             // Modified?
                                    {
                                        changeStatusTo = Models.BackupFileStatus.MODIFIED;
                                    }
                                    else if (NeedsToRetryFile(entry, lastVersion))                                             // Didn't complete last file transfer?
                                    {
                                        changeStatusTo = Models.BackupFileStatus.MODIFIED;
                                    }
                                    else                                             // Not modified?
                                    {
                                        changeStatusTo = Models.BackupFileStatus.UNCHANGED;
                                    }
                                }
                            }
                            else                                     // Deleted from filesystem?
                            {
                                changeStatusTo = Models.BackupFileStatus.DELETED;
                            }
                            break;
                        }
                    }
                    else                            // Adding to this backup?
                    {
                        if (fileExistsOnFilesystem) // Exists?
                        {
                            changeStatusTo = Models.BackupFileStatus.ADDED;
                        }
                        else
                        {
                            // Error? Can't add a non-existent file to the plan.
                        }
                    }

                    if (changeStatusTo.HasValue)
                    {
                        entry.LastStatus = changeStatusTo.Value;
                        entry.UpdatedAt  = DateTime.UtcNow;
                    }
                }
                catch (Exception ex)
                {
                    FailedFile <Models.BackupPlanFile> failedEntry = new FailedFile <Models.BackupPlanFile>(entry, ex.Message, ex);
                    ChangeSet.FailedFiles.AddLast(failedEntry);

                    // Remove this entry from `files` as it clearly failed.
                    files.Remove(node);                     // Complexity is O(1)
                }

                node = next;
            }

            stats.End();
        }