diff options
author | Reinhard Tartler <siretart@tauware.de> | 2020-05-10 19:17:17 -0400 |
---|---|---|
committer | Reinhard Tartler <siretart@tauware.de> | 2020-05-10 19:17:17 -0400 |
commit | 228bc06db9e262b24a6c235b1e036cc8ef78565f (patch) | |
tree | 3703ceb787cf9345600fae3939afeccca7c47719 /lib/bbackupd/BackupClientDirectoryRecord.cpp | |
parent | ffb043c482053896b0190ea5ddc1cf2ba70cb4f0 (diff) | |
parent | c5c04da3823c36f03408821cbc5019ec18613922 (diff) |
Merge commit 'c5c04da3'debian/0.13__git20200326.g8e8b63c-1
Diffstat (limited to 'lib/bbackupd/BackupClientDirectoryRecord.cpp')
-rw-r--r-- | lib/bbackupd/BackupClientDirectoryRecord.cpp | 330 |
1 files changed, 213 insertions, 117 deletions
diff --git a/lib/bbackupd/BackupClientDirectoryRecord.cpp b/lib/bbackupd/BackupClientDirectoryRecord.cpp index 94cb7965..50b5a4bc 100644 --- a/lib/bbackupd/BackupClientDirectoryRecord.cpp +++ b/lib/bbackupd/BackupClientDirectoryRecord.cpp @@ -159,6 +159,9 @@ void BackupClientDirectoryRecord::SyncDirectory( THROW_EXCEPTION(BackupStoreException, SignalReceived) } + std::string local_path_non_vss = ConvertVssPathToRealPath(rLocalPath, + rBackupLocation); + // Start by making some flag changes, marking this sync as not done, // and on the immediate sub directories. mSyncDone = false; @@ -192,8 +195,7 @@ void BackupClientDirectoryRecord::SyncDirectory( // just ignore this error. In a future scan, this // deletion will be noticed, deleted from server, // and this object deleted. - rNotifier.NotifyDirStatFailed(this, - ConvertVssPathToRealPath(rLocalPath, rBackupLocation), + rNotifier.NotifyDirStatFailed(this, local_path_non_vss, strerror(errno)); return; } @@ -208,7 +210,7 @@ void BackupClientDirectoryRecord::SyncDirectory( BackupClientInodeToIDMap &idMap( rParams.mrContext.GetNewIDMap()); idMap.AddToMap(dest_st.st_ino, mObjectID, ContainingDirectoryID, - ConvertVssPathToRealPath(rLocalPath, rBackupLocation)); + local_path_non_vss); } // Add attributes to checksum currentStateChecksum.Add(&dest_st.st_mode, @@ -243,9 +245,7 @@ void BackupClientDirectoryRecord::SyncDirectory( DIR *dirHandle = 0; try { - std::string nonVssDirPath = ConvertVssPathToRealPath(rLocalPath, - rBackupLocation); - rNotifier.NotifyScanDirectory(this, nonVssDirPath); + rNotifier.NotifyScanDirectory(this, local_path_non_vss); dirHandle = ::opendir(rLocalPath.c_str()); if(dirHandle == 0) @@ -253,21 +253,17 @@ void BackupClientDirectoryRecord::SyncDirectory( // Report the error (logs and eventual email to administrator) if (errno == EACCES) { - rNotifier.NotifyDirListFailed(this, - nonVssDirPath, + rNotifier.NotifyDirListFailed(this, local_path_non_vss, "Access denied"); } else { - rNotifier.NotifyDirListFailed(this, - nonVssDirPath, + rNotifier.NotifyDirListFailed(this, local_path_non_vss, strerror(errno)); } - // Report the error (logs and eventual email - // to administrator) - SetErrorWhenReadingFilesystemObject(rParams, - nonVssDirPath); + SetErrorWhenReadingFilesystemObject(rParams, local_path_non_vss); + // Ignore this directory for now. return; } @@ -327,15 +323,49 @@ void BackupClientDirectoryRecord::SyncDirectory( try { // Want to get the directory listing? + bool download_dir = false; + if(ThisDirHasJustBeenCreated) { // Avoid sending another command to the server when we know it's empty apDirOnStore.reset(new BackupStoreDirectory(mObjectID, ContainingDirectoryID)); + BOX_TRACE("No need to download directory " << + BOX_FORMAT_OBJECTID(mObjectID) << " because it has just been " + "created, so we know it's empty"); + ASSERT(!download_dir); } // Consider asking the store for it - else if(!mInitialSyncDone || checksumDifferent || - downloadDirectoryRecordBecauseOfFutureFiles) + else if(!mInitialSyncDone) + { + BOX_TRACE("Downloading directory listing of " << local_path_non_vss << + " (" << BOX_FORMAT_OBJECTID(mObjectID) << " because we haven't " + "done an initial sync yet"); + download_dir = true; + } + else if(checksumDifferent) + { + BOX_TRACE("Downloading directory listing of " << local_path_non_vss << + " (" << BOX_FORMAT_OBJECTID(mObjectID) << " because its contents " + "have changed locally"); + download_dir = true; + } + else if(downloadDirectoryRecordBecauseOfFutureFiles) + { + BOX_TRACE("Downloading directory listing of " << local_path_non_vss << + " (" << BOX_FORMAT_OBJECTID(mObjectID) << " because it contains " + "files with dates in the future"); + download_dir = true; + } + else + { + BOX_TRACE("Not downloading directory listing of " << local_path_non_vss << + " (" << BOX_FORMAT_OBJECTID(mObjectID) << " because our cached " + "copy appears to still be valid"); + ASSERT(!download_dir); + } + + if(download_dir) { apDirOnStore = FetchDirectoryListing(rParams); } @@ -766,17 +796,17 @@ bool BackupClientDirectoryRecord::UpdateItems( // Decrypt all the directory entries. // It would be nice to be able to just compare the encrypted versions, however this doesn't work - // in practise because there can be multiple encodings of the same filename using different + // in practise because there can be multiple encodings of the same filename using different // methods (although each method will result in the same string for the same filename.) This // happens when the server fixes a broken store, and gives plain text generated filenames. // So if we didn't do things like this, then you wouldn't be able to recover from bad things // happening with the server. DecryptedEntriesMap_t decryptedEntries; - if(pDirOnStore != 0) + if(pDirOnStore != NULL) { BackupStoreDirectory::Iterator i(*pDirOnStore); - BackupStoreDirectory::Entry *en = 0; - while((en = i.Next()) != 0) + BackupStoreDirectory::Entry *en = NULL; + while((en = i.Next()) != NULL) { std::string filenameClear; try @@ -837,9 +867,9 @@ bool BackupClientDirectoryRecord::UpdateItems( // See if it's in the listing (if we have one) BackupStoreFilenameClear storeFilename(*f); - BackupStoreDirectory::Entry *en = 0; + BackupStoreDirectory::Entry *en = NULL; int64_t latestObjectID = 0; - if(pDirOnStore != 0) + if(pDirOnStore != NULL) { DecryptedEntriesMap_t::iterator i(decryptedEntries.find(*f)); if(i != decryptedEntries.end()) @@ -850,85 +880,32 @@ bool BackupClientDirectoryRecord::UpdateItems( } // Check that the entry which might have been found is in fact a file - if((en != 0) && !(en->IsFile())) + if((en != NULL) && !(en->IsFile())) { // Directory exists in the place of this file -- sort it out - RemoveDirectoryInPlaceOfFile(rParams, pDirOnStore, - en, *f); - en = 0; + RemoveDirectoryInPlaceOfFile(rParams, pDirOnStore, en, *f); + en = NULL; + latestObjectID = 0; } // Check for renaming? - if(pDirOnStore != 0 && en == 0) + if(pDirOnStore != 0 && en == NULL) { // We now know... // 1) File has just been added // 2) It's not in the store - - // Do we know about the inode number? - const BackupClientInodeToIDMap &idMap(rContext.GetCurrentIDMap()); - int64_t renameObjectID = 0, renameInDirectory = 0; - if(idMap.Lookup(inodeNum, renameObjectID, renameInDirectory)) + ASSERT(latestObjectID == 0); + en = CheckForRename(rContext, pDirOnStore, storeFilename, inodeNum, + nonVssFilePath); + if(en != NULL) { - // Look up on the server to get the name, to build the local filename - std::string localPotentialOldName; - bool isDir = false; - bool isCurrentVersion = false; - box_time_t srvModTime = 0, srvAttributesHash = 0; - BackupStoreFilenameClear oldLeafname; - if(rContext.FindFilename(renameObjectID, renameInDirectory, - localPotentialOldName, isDir, isCurrentVersion, - &srvModTime, &srvAttributesHash, &oldLeafname)) - { - // Only interested if it's a file and the latest version - if(!isDir && isCurrentVersion) - { - // Check that the object we found in the ID map doesn't exist on disc - EMU_STRUCT_STAT st; - if(EMU_STAT(localPotentialOldName.c_str(), &st) != 0 && errno == ENOENT) - { - // Doesn't exist locally, but does exist on the server. - // Therefore we can safely rename it to this new file. - - // Get the connection to the server - BackupProtocolCallable &connection(rContext.GetConnection()); - - // Only do this step if there is room on the server. - // This step will be repeated later when there is space available - if(!rContext.StorageLimitExceeded()) - { - // Rename the existing files (ie include old versions) on the server - connection.QueryMoveObject(renameObjectID, - renameInDirectory, - mObjectID /* move to this directory */, - BackupProtocolMoveObject::Flags_MoveAllWithSameName | - BackupProtocolMoveObject::Flags_AllowMoveOverDeletedObject, - storeFilename); - - // Stop the attempt to delete the file in the original location - BackupClientDeleteList &rdelList(rContext.GetDeleteList()); - rdelList.StopFileDeletion(renameInDirectory, oldLeafname); - - // Create new entry in the directory for it - // -- will be near enough what's actually on the server for the rest to work. - en = pDirOnStore->AddEntry(storeFilename, - srvModTime, renameObjectID, - 0 /* size in blocks unknown, but not needed */, - BackupStoreDirectory::Entry::Flags_File, - srvAttributesHash); - - // Store the object ID for the inode lookup map later - latestObjectID = renameObjectID; - } - } - } - } + latestObjectID = en->GetObjectID(); } } - + // Is it in the mPendingEntries list? box_time_t pendingFirstSeenTime = 0; // ie not seen - if(mpPendingEntries != 0) + if(mpPendingEntries != NULL) { std::map<std::string, box_time_t>::const_iterator i(mpPendingEntries->find(*f)); if(i != mpPendingEntries->end()) @@ -939,7 +916,7 @@ bool BackupClientDirectoryRecord::UpdateItems( } // If pDirOnStore == 0, then this must have been after an initial sync: - ASSERT(pDirOnStore != 0 || mInitialSyncDone); + ASSERT(pDirOnStore != NULL || mInitialSyncDone); // So, if pDirOnStore == 0, then we know that everything before syncPeriodStart // is either on the server, or in the toupload list. If the directory had changed, // we'd have got a directory listing. @@ -962,20 +939,20 @@ bool BackupClientDirectoryRecord::UpdateItems( // Only upload a file if the mod time locally is // different to that on the server. - if (en == 0 || en->GetModificationTime() != modTime) + if(en == NULL || en->GetModificationTime() != modTime) { // Check the file modified within the acceptable time period we're checking // If the file isn't on the server, the acceptable time starts at zero. // Check pDirOnStore and en, because if we didn't download a directory listing, // pDirOnStore will be zero, but we know it's on the server. - if (modTime < rParams.mSyncPeriodEnd) + if(modTime < rParams.mSyncPeriodEnd) { - if (pDirOnStore != 0 && en == 0) + if(pDirOnStore != NULL && en == NULL) { doUpload = true; decisionReason = "not on server"; } - else if (modTime >= rParams.mSyncPeriodStart) + else if(modTime >= rParams.mSyncPeriodStart) { doUpload = true; decisionReason = "modified since last sync"; @@ -984,12 +961,19 @@ bool BackupClientDirectoryRecord::UpdateItems( // However, just in case things are continually // modified, we check the first seen time. - // The two compares of syncPeriodEnd and - // pendingFirstSeenTime are because the values - // are unsigned. - if (!doUpload && - pendingFirstSeenTime != 0 && + if(!doUpload && pendingFirstSeenTime != 0) + { + BOX_TRACE("Current period ends at " << + FormatTime(rParams.mSyncPeriodEnd, false, true) << + " and file has been pending since " << + FormatTime(pendingFirstSeenTime, false, true)); + } + + if(!doUpload && pendingFirstSeenTime != 0 && + // The two compares of syncPeriodEnd and + // pendingFirstSeenTime are because the values + // are unsigned. rParams.mSyncPeriodEnd > pendingFirstSeenTime && (rParams.mSyncPeriodEnd - pendingFirstSeenTime) > rParams.mMaxUploadWait) @@ -1005,7 +989,7 @@ bool BackupClientDirectoryRecord::UpdateItems( // will pick up the fact it has been added, so the // store listing will be available when this happens. - if (!doUpload && + if(!doUpload && modTime <= rParams.mSyncPeriodStart && en != 0 && en->GetModificationTime() != modTime) @@ -1014,11 +998,11 @@ bool BackupClientDirectoryRecord::UpdateItems( decisionReason = "mod time changed"; } - // And just to catch really badly off clocks in + // And just to catch really badly off clocks in // the future for file server clients, // just upload the file if it's madly in the future. - if (!doUpload && modTime > + if(!doUpload && modTime > rParams.mUploadAfterThisTimeInTheFuture) { doUpload = true; @@ -1026,14 +1010,14 @@ bool BackupClientDirectoryRecord::UpdateItems( } } - if (en != 0 && en->GetModificationTime() == modTime) + if(en != NULL && en->GetModificationTime() == modTime) { doUpload = false; decisionReason = "not modified since last upload"; } - else if (!doUpload) + else if(!doUpload) { - if (modTime > rParams.mSyncPeriodEnd) + if(modTime > rParams.mSyncPeriodEnd) { box_time_t now = GetCurrentBoxTime(); int age = BoxTimeToSeconds(now - @@ -1060,7 +1044,7 @@ bool BackupClientDirectoryRecord::UpdateItems( bool fileSynced = true; - if (doUpload) + if(doUpload) { // Upload needed, don't mark sync success until // we've actually done it @@ -1094,7 +1078,7 @@ bool BackupClientDirectoryRecord::UpdateItems( attributesHash, noPreviousVersionOnServer); - if (latestObjectID == 0) + if(latestObjectID == 0) { // storage limit exceeded rParams.mrContext.SetStorageLimitExceeded(); @@ -1118,7 +1102,7 @@ bool BackupClientDirectoryRecord::UpdateItems( } catch(BoxException &e) { - if (e.GetType() == BackupStoreException::ExceptionType && + if(e.GetType() == BackupStoreException::ExceptionType && e.GetSubType() == BackupStoreException::SignalReceived) { // abort requested, pass the @@ -1185,7 +1169,7 @@ bool BackupClientDirectoryRecord::UpdateItems( } catch (BoxException &e) { - BOX_ERROR("Failed to read or store file attributes " + BOX_ERROR("Failed to read or store file attributes " "for '" << nonVssFilePath << "', will try again " "later"); } @@ -1199,7 +1183,8 @@ bool BackupClientDirectoryRecord::UpdateItems( { mpPendingEntries = new std::map<std::string, box_time_t>; } - // Adding to mPendingEntries list + + // Adding to mPendingEntries list if(pendingFirstSeenTime == 0) { // Haven't seen this before -- add to list! @@ -1236,7 +1221,7 @@ bool BackupClientDirectoryRecord::UpdateItems( if(currentIDMap.Lookup(inodeNum, objid, dirid)) { // Found - if (dirid != mObjectID) + if(dirid != mObjectID) { BOX_WARNING("Found conflicting parent ID for " "file ID " << inodeNum << " (" << @@ -1260,11 +1245,10 @@ bool BackupClientDirectoryRecord::UpdateItems( if(latestObjectID != 0) { - BOX_TRACE("Storing uploaded file ID " << - inodeNum << " (" << nonVssFilePath << ") " - "in ID map as object " << - latestObjectID << " with parent " << - mObjectID); + BOX_TRACE("Storing uploaded file ID " << inodeNum << " (" << + nonVssFilePath << ") in ID map as object " << + BOX_FORMAT_OBJECTID(latestObjectID) << " with parent " << + BOX_FORMAT_OBJECTID(mObjectID)); idMap.AddToMap(inodeNum, latestObjectID, mObjectID /* containing directory */, nonVssFilePath); @@ -1272,7 +1256,7 @@ bool BackupClientDirectoryRecord::UpdateItems( } - if (fileSynced) + if(fileSynced) { rNotifier.NotifyFileSynchronised(this, nonVssFilePath, fileSize); @@ -1403,7 +1387,7 @@ bool BackupClientDirectoryRecord::UpdateItems( doCreateDirectoryRecord = (subDirObjectID != 0); } - if (doCreateDirectoryRecord) + if(doCreateDirectoryRecord) { // New an object for this psubDirRecord = new BackupClientDirectoryRecord(subDirObjectID, *d); @@ -1496,7 +1480,7 @@ bool BackupClientDirectoryRecord::UpdateItems( mSubDirectories.erase(e); delete rec; - BOX_TRACE("Deleted directory record for " << + BOX_TRACE("Deleted directory record for " << nonVssLocalName); } } @@ -1507,6 +1491,118 @@ bool BackupClientDirectoryRecord::UpdateItems( return allUpdatedSuccessfully; } +// Returns NULL if not renamed, or the new BackupStoreDirectory::Entry in p_dir (containing the ID +// of the renamed (moved) object) otherwise. +BackupStoreDirectory::Entry* BackupClientDirectoryRecord::CheckForRename( + BackupClientContext& context, BackupStoreDirectory* p_dir, + const BackupStoreFilenameClear& remote_filename, InodeRefType inode_num, + const std::string& local_path_non_vss) +{ + // We now know... + // 1) File has just been added + // 2) It's not in the store + + // Do we know about the inode number? + const BackupClientInodeToIDMap &idMap(context.GetCurrentIDMap()); + int64_t prev_object_id = 0, prev_dir_id = 0; + if(!idMap.Lookup(inode_num, prev_object_id, prev_dir_id)) + { + return NULL; + } + + std::ostringstream msg_prefix_buf; + msg_prefix_buf << local_path_non_vss << ": have seen inode " << inode_num << " before, " + "with ID " << BOX_FORMAT_OBJECTID(prev_object_id) << " in directory " << + BOX_FORMAT_OBJECTID(prev_dir_id); + std::string msg_prefix = msg_prefix_buf.str(); + + std::ostringstream msg_suffix_buf; + msg_suffix_buf << ", so will not move to directory " << BOX_FORMAT_OBJECTID(mObjectID); + std::string msg_suffix = msg_suffix_buf.str(); + + // We've seen this inode number before. Look up on the server to get the filename, to + // reconstruct the local filename that it had when it was backed up before (elsewhere): + std::string possible_prev_local_name; + bool was_a_dir = false; + bool was_current_version = false; + box_time_t remote_mod_time = 0, remote_attr_hash = 0; + BackupStoreFilenameClear prev_remote_name; + if(!context.FindFilename(prev_object_id, prev_dir_id, possible_prev_local_name, was_a_dir, + was_current_version, &remote_mod_time, &remote_attr_hash, &prev_remote_name)) + { + BOX_TRACE(msg_prefix << ", but that no longer exists on the server, so cannot find " + "corresponding local file to check for rename"); + return NULL; + } + + // Only interested if it's a file and the latest version + if(was_a_dir || !was_current_version) + { + BOX_TRACE(msg_prefix << ", but that was " << + (was_a_dir ? "a directory" : "not the latest version") << + msg_suffix); + return NULL; + } + + // Check that the object we found in the ID map doesn't exist on disc + EMU_STRUCT_STAT st; + if(EMU_STAT(possible_prev_local_name.c_str(), &st) == 0) + { + BOX_TRACE(msg_prefix << ", but that was for " << possible_prev_local_name << " " + "which still exists locally (most likely moved and replaced)" << msg_suffix); + return NULL; + } + + if(errno != ENOENT) + { + BOX_TRACE(BOX_SYS_ERROR_MESSAGE(msg_prefix << ", but that was for " << + possible_prev_local_name << " which we cannot access" << msg_suffix)); + return NULL; + } + + // Doesn't exist locally, but does exist on the server. + // Therefore we can safely rename it to this new file. + + // Get the connection to the server + BackupProtocolCallable &connection(context.GetConnection()); + + // Only do this step if there is room on the server. + // This step will be repeated later when there is space available + if(context.StorageLimitExceeded()) + { + BOX_TRACE(possible_prev_local_name << " appears to have been renamed to " << + local_path_non_vss << ", but our account on the server is full, " + "so not moving object " << + BOX_FORMAT_OBJECTID(prev_object_id) << " from directory " << + BOX_FORMAT_OBJECTID(prev_dir_id) << " to " << + BOX_FORMAT_OBJECTID(mObjectID)); + return NULL; + } + + // Rename the existing files (ie include old versions) on the server + connection.QueryMoveObject(prev_object_id, prev_dir_id, + mObjectID /* move to this directory */, + BackupProtocolMoveObject::Flags_MoveAllWithSameName | + BackupProtocolMoveObject::Flags_AllowMoveOverDeletedObject, + remote_filename); + + // Stop the attempt to delete the file in the original location + BackupClientDeleteList &rdelList(context.GetDeleteList()); + rdelList.StopFileDeletion(prev_dir_id, prev_remote_name); + + BOX_TRACE(possible_prev_local_name << " appears to have been renamed to " << + local_path_non_vss << ", so moving object " << + BOX_FORMAT_OBJECTID(prev_object_id) << " from directory " << + BOX_FORMAT_OBJECTID(prev_dir_id) << " to " << + BOX_FORMAT_OBJECTID(mObjectID)); + + // Create new entry in the directory for it: will be near enough what's actually on the + // server for the rest to work. + return p_dir->AddEntry(remote_filename, remote_mod_time, prev_object_id, + 0 /* size in blocks unknown, but not needed */, + BackupStoreDirectory::Entry::Flags_File, remote_attr_hash); +} + int64_t BackupClientDirectoryRecord::CreateRemoteDir(const std::string& localDirPath, const std::string& nonVssDirPath, const std::string& remoteDirPath, BackupStoreFilenameClear& storeFilename, bool* pHaveJustCreatedDirOnServer, |