diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/backupclient/BackupClientRestore.cpp | 6 | ||||
-rw-r--r-- | lib/backupstore/BackupClientFileAttributes.cpp | 2 | ||||
-rw-r--r-- | lib/backupstore/BackupStoreFile.cpp | 2 | ||||
-rw-r--r-- | lib/backupstore/BackupStoreRefCountDatabase.cpp | 6 | ||||
-rw-r--r-- | lib/bbackupd/BackupClientDirectoryRecord.cpp | 330 | ||||
-rw-r--r-- | lib/bbackupd/BackupClientDirectoryRecord.h | 5 | ||||
-rw-r--r-- | lib/bbackupd/BackupDaemon.cpp | 222 | ||||
-rw-r--r-- | lib/bbackupquery/BackupQueries.cpp | 12 | ||||
-rw-r--r-- | lib/common/BannerText.cpp | 14 | ||||
-rw-r--r-- | lib/common/BannerText.h | 43 | ||||
-rw-r--r-- | lib/common/Box.h | 16 | ||||
-rw-r--r-- | lib/common/BoxPlatform.h | 4 | ||||
-rw-r--r-- | lib/common/InvisibleTempFileStream.cpp | 2 | ||||
-rw-r--r-- | lib/common/MainHelper.h | 10 | ||||
-rw-r--r-- | lib/common/NamedLock.cpp | 4 | ||||
-rw-r--r-- | lib/common/Test.cpp | 4 | ||||
-rw-r--r-- | lib/raidfile/RaidFileWrite.cpp | 30 | ||||
-rw-r--r-- | lib/server/Daemon.cpp | 2 | ||||
-rw-r--r-- | lib/server/ServerControl.cpp | 4 | ||||
-rw-r--r-- | lib/server/ServerStream.h | 2 | ||||
-rw-r--r-- | lib/win32/emu.h | 22 | ||||
-rwxr-xr-x | lib/win32/getopt_long.cpp | 1092 | ||||
-rwxr-xr-x | lib/win32/messages.rc | 4 |
23 files changed, 993 insertions, 845 deletions
diff --git a/lib/backupclient/BackupClientRestore.cpp b/lib/backupclient/BackupClientRestore.cpp index d3300604..295a4252 100644 --- a/lib/backupclient/BackupClientRestore.cpp +++ b/lib/backupclient/BackupClientRestore.cpp @@ -277,7 +277,7 @@ static int BackupClientRestoreDir(BackupProtocolCallable &rConnection, "out of the way of restored directory. " "Use specific restore with ID to " "restore this object."); - if(::unlink(rLocalDirectoryName.c_str()) != 0) + if(EMU_UNLINK(rLocalDirectoryName.c_str()) != 0) { BOX_LOG_SYS_ERROR("Failed to delete " "file '" << @@ -513,7 +513,7 @@ static int BackupClientRestoreDir(BackupProtocolCallable &rConnection, // files already there. if(ObjectExists(localFilename) != ObjectExists_NoObject && - ::unlink(localFilename.c_str()) != 0) + EMU_UNLINK(localFilename.c_str()) != 0) { BOX_LOG_SYS_ERROR("Failed to delete " "file '" << localFilename << @@ -912,7 +912,7 @@ int BackupClientRestore(BackupProtocolCallable &rConnection, } // Delete the resume information file - ::unlink(params.mRestoreResumeInfoFilename.c_str()); + EMU_UNLINK(params.mRestoreResumeInfoFilename.c_str()); return params.ContinuedAfterError ? Restore_CompleteWithErrors : Restore_Complete; diff --git a/lib/backupstore/BackupClientFileAttributes.cpp b/lib/backupstore/BackupClientFileAttributes.cpp index 37140301..431e5587 100644 --- a/lib/backupstore/BackupClientFileAttributes.cpp +++ b/lib/backupstore/BackupClientFileAttributes.cpp @@ -814,7 +814,7 @@ void BackupClientFileAttributes::WriteAttributes(const std::string& Filename, Filename << "'"); #else // Make a symlink, first deleting anything in the way - ::unlink(Filename.c_str()); + EMU_UNLINK(Filename.c_str()); if(::symlink((char*)(pattr + 1), Filename.c_str()) != 0) { BOX_LOG_SYS_ERROR("Failed to symlink '" << Filename << diff --git a/lib/backupstore/BackupStoreFile.cpp b/lib/backupstore/BackupStoreFile.cpp index 99562685..a3e23204 100644 --- a/lib/backupstore/BackupStoreFile.cpp +++ b/lib/backupstore/BackupStoreFile.cpp @@ -677,7 +677,7 @@ void BackupStoreFile::DecodeFile(IOStream &rEncodedFile, const char *DecodedFile } catch(...) { - ::unlink(DecodedFilename); + EMU_UNLINK(DecodedFilename); throw; } } diff --git a/lib/backupstore/BackupStoreRefCountDatabase.cpp b/lib/backupstore/BackupStoreRefCountDatabase.cpp index 86da0943..cba989ed 100644 --- a/lib/backupstore/BackupStoreRefCountDatabase.cpp +++ b/lib/backupstore/BackupStoreRefCountDatabase.cpp @@ -68,7 +68,7 @@ void BackupStoreRefCountDatabase::Commit() std::string Final_Filename = GetFilename(mAccount, false); #ifdef WIN32 - if(FileExists(Final_Filename) && unlink(Final_Filename.c_str()) != 0) + if(FileExists(Final_Filename) && EMU_UNLINK(Final_Filename.c_str()) != 0) { THROW_EMU_FILE_ERROR("Failed to delete old permanent refcount " "database file", mFilename, CommonException, @@ -106,7 +106,7 @@ void BackupStoreRefCountDatabase::Discard() mapDatabaseFile.reset(); } - if(unlink(mFilename.c_str()) != 0) + if(EMU_UNLINK(mFilename.c_str()) != 0) { THROW_EMU_FILE_ERROR("Failed to delete temporary refcount " "database file", mFilename, CommonException, @@ -187,7 +187,7 @@ std::auto_ptr<BackupStoreRefCountDatabase> { BOX_WARNING(BOX_FILE_MESSAGE(Filename, "Overwriting existing " "temporary reference count database")); - if (unlink(Filename.c_str()) != 0) + if(EMU_UNLINK(Filename.c_str()) != 0) { THROW_SYS_FILE_ERROR("Failed to delete old temporary " "reference count database file", Filename, 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, diff --git a/lib/bbackupd/BackupClientDirectoryRecord.h b/lib/bbackupd/BackupClientDirectoryRecord.h index 865fc747..7308d7d1 100644 --- a/lib/bbackupd/BackupClientDirectoryRecord.h +++ b/lib/bbackupd/BackupClientDirectoryRecord.h @@ -163,6 +163,7 @@ private: void UpdateAttributes(SyncParams &rParams, BackupStoreDirectory *pDirOnStore, const std::string &rLocalPath); + protected: // to allow tests to hook in before UpdateItems() runs virtual bool UpdateItems(SyncParams &rParams, const std::string &rLocalPath, @@ -172,7 +173,11 @@ protected: // to allow tests to hook in before UpdateItems() runs std::vector<BackupStoreDirectory::Entry *> &rEntriesLeftOver, std::vector<std::string> &rFiles, const std::vector<std::string> &rDirs); + private: + BackupStoreDirectory::Entry* CheckForRename(BackupClientContext& context, + BackupStoreDirectory* p_dir, const BackupStoreFilenameClear& remote_filename, + InodeRefType inode_num, const std::string& local_path_non_vss); int64_t CreateRemoteDir(const std::string& localDirPath, const std::string& nonVssDirPath, const std::string& remoteDirPath, diff --git a/lib/bbackupd/BackupDaemon.cpp b/lib/bbackupd/BackupDaemon.cpp index d75aa381..f4dcf270 100644 --- a/lib/bbackupd/BackupDaemon.cpp +++ b/lib/bbackupd/BackupDaemon.cpp @@ -531,7 +531,7 @@ void BackupDaemon::Run() mapCommandSocketInfo->mListeningSocket.Listen( socketName); #else - ::unlink(socketName); + EMU_UNLINK(socketName); mapCommandSocketInfo->mListeningSocket.Listen( Socket::TypeUNIX, socketName); #endif @@ -2503,12 +2503,32 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con } } - const Configuration& rConfig( - rLocationsConf.GetSubConfiguration(*pLocName)); + const Configuration& rConfig(rLocationsConf.GetSubConfiguration(*pLocName)); std::auto_ptr<Location> apLoc; + BackupStoreFilenameClear dirname(*pLocName); // generate the filename + bool local_dir_exists = true; + int64_t existing_remote_dir_id = 0; + + box_time_t attrModTime = 0; + BackupClientFileAttributes attr; + try { + // Does this exist on the server? Remove from dir object early, so that if + // we fail to stat the local directory, we still don't consider to remote + // one for deletion. + BackupStoreDirectory::Iterator iter(dir); + BackupStoreDirectory::Entry *en = iter.FindMatchingClearName(dirname); + if(en != NULL) + { + existing_remote_dir_id = en->GetObjectID(); + + // Delete the entry from the directory, so we get a list of + // unused root directories at the end of this. + dir.DeleteEntry(existing_remote_dir_id); + } + if(pLoc == NULL) { // Create a record for it @@ -2523,59 +2543,42 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con pLoc->mPath = rConfig.GetKeyValue("Path"); } - // Read the exclude lists from the Configuration - pLoc->mapExcludeFiles.reset(BackupClientMakeExcludeList_Files(rConfig)); - pLoc->mapExcludeDirs.reset(BackupClientMakeExcludeList_Dirs(rConfig)); + // Get the directory's attributes and modification time. We need this to + // check whether the local directory exists, even if we don't have + // stat[v]fs(). Otherwise we must skip it. + attr.ReadAttributes(pLoc->mPath.c_str(), + true /* directories have zero mod times */, + 0 /* not interested in mod time */, + &attrModTime /* get the attribute modification time */); - // Does this exist on the server? - // Remove from dir object early, so that if we fail - // to stat the local directory, we still don't - // consider to remote one for deletion. - BackupStoreDirectory::Iterator iter(dir); - BackupStoreFilenameClear dirname(pLoc->mName); // generate the filename - BackupStoreDirectory::Entry *en = iter.FindMatchingClearName(dirname); - int64_t oid = 0; - if(en != 0) - { - oid = en->GetObjectID(); - - // Delete the entry from the directory, so we get a list of - // unused root directories at the end of this. - dir.DeleteEntry(oid); - } - // Do a fsstat on the pathname to find out which mount it's on { #if defined HAVE_STRUCT_STATFS_F_MNTONNAME || defined HAVE_STRUCT_STATVFS_F_MNTONNAME || defined WIN32 // BSD style statfs -- includes mount point, which is nice. -#ifdef HAVE_STRUCT_STATVFS_F_MNTONNAME +# ifdef HAVE_STRUCT_STATVFS_F_MNTONNAME struct statvfs s; if(::statvfs(pLoc->mPath.c_str(), &s) != 0) -#else // HAVE_STRUCT_STATVFS_F_MNTONNAME +# else // HAVE_STRUCT_STATVFS_F_MNTONNAME struct statfs s; if(::statfs(pLoc->mPath.c_str(), &s) != 0) -#endif // HAVE_STRUCT_STATVFS_F_MNTONNAME +# endif // HAVE_STRUCT_STATVFS_F_MNTONNAME { - THROW_SYS_ERROR("Failed to stat path " - "'" << pLoc->mPath << "' " - "for location " - "'" << pLoc->mName << "'", + THROW_SYS_FILE_ERROR(pLoc->mPath, "Failed to stat local path", CommonException, OSFileError); } // Where the filesystem is mounted std::string mountName(s.f_mntonname); -#else // !HAVE_STRUCT_STATFS_F_MNTONNAME && !WIN32 +#else // !HAVE_STRUCT_STAT*FS_F_MNTONNAME && !WIN32 // Warn in logs if the directory isn't absolute if(pLoc->mPath[0] != '/') { - BOX_WARNING("Location path '" - << pLoc->mPath - << "' is not absolute"); + BOX_WARNING(BOX_FILE_MESSAGE(pLoc->mPath, + "location path is not absolute")); } // Go through the mount points found, and find a suitable one std::string mountName("/"); @@ -2596,12 +2599,10 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con break; } } - BOX_TRACE("mount point chosen for " - << pLoc->mPath << " is " - << mountName); + BOX_TRACE("mount point chosen for " << pLoc->mPath << + " is " << mountName); } - -#endif +#endif // !HAVE_STRUCT_STAT*FS_F_MNTONNAME && !WIN32 // Got it? std::map<std::string, int>::iterator f(mounts.find(mountName)); @@ -2623,89 +2624,68 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con ++numIDMaps; } } - - // Does this exist on the server? - if(en == 0) - { - // Doesn't exist, so it has to be created on the server. Let's go! - // First, get the directory's attributes and modification time - box_time_t attrModTime = 0; - BackupClientFileAttributes attr; - try - { - attr.ReadAttributes(pLoc->mPath.c_str(), - true /* directories have zero mod times */, - 0 /* not interested in mod time */, - &attrModTime /* get the attribute modification time */); - } - catch (BoxException &e) - { - BOX_ERROR("Failed to get attributes " - "for path '" << pLoc->mPath - << "', skipping location '" << - pLoc->mName << "'"); - throw; - } - - // Execute create directory command - try - { - std::auto_ptr<IOStream> attrStream( - new MemBlockStream(attr)); - std::auto_ptr<BackupProtocolSuccess> - dirCreate(connection.QueryCreateDirectory( - BACKUPSTORE_ROOT_DIRECTORY_ID, // containing directory - attrModTime, dirname, attrStream)); - - // Object ID for later creation - oid = dirCreate->GetObjectID(); - } - catch (BoxException &e) - { - BOX_ERROR("Failed to create remote " - "directory '/" << pLoc->mName << - "', skipping location '" << - pLoc->mName << "'"); - throw; - } + } + catch (std::exception &e) + { + BOX_ERROR("Failed to configure location '" << pLoc->mName << "': " << + e.what()); + local_dir_exists = false; + } + catch(...) + { + BOX_ERROR("Failed to configure location '" << pLoc->mName << "': please " + "check for previous errors"); + local_dir_exists = false; + } - } + // Remove it from the temporary list to avoid deletion, even if it doesn't actually + // exist locally at this time: + tmpLocations.remove(pLoc); - // Create and store the directory object for the root of this location - ASSERT(oid != 0); - if(pLoc->mapDirectoryRecord.get() == NULL) - { - pLoc->mapDirectoryRecord.reset( - new BackupClientDirectoryRecord(oid, *pLocName)); - } - - // Remove it from the temporary list to avoid deletion - tmpLocations.remove(pLoc); + // Does this exist on the server? + if(!local_dir_exists) + { + mReadErrorsOnFilesystemObjects = true; + // Don't try to create it remotely, just skip it. + continue; + } - // Push it back on the vector of locations - mLocations.push_back(pLoc); + if(existing_remote_dir_id == 0) + { + // Doesn't exist, so it has to be created on the server. Let's go! - if(apLoc.get() != NULL) - { - // Don't delete it now! - apLoc.release(); - } + // Create the remote directory for this location. If this fails, then we + // abort the whole backup, otherwise the error isn't reported sufficiently + // severely for test_bbackupd_responds_to_connection_failure_in_process_s3. + std::auto_ptr<IOStream> attrStream(new MemBlockStream(attr)); + std::auto_ptr<BackupProtocolSuccess> dirCreate( + connection.QueryCreateDirectory( + BACKUPSTORE_ROOT_DIRECTORY_ID, // containing directory + attrModTime, dirname, attrStream)); + + // Object ID for later creation + existing_remote_dir_id = dirCreate->GetObjectID(); } - catch (std::exception &e) + + // Create and store the directory object for the root of this location + ASSERT(existing_remote_dir_id != 0); + if(pLoc->mapDirectoryRecord.get() == NULL) { - BOX_ERROR("Failed to configure location '" - << pLoc->mName << "' path '" - << pLoc->mPath << "': " << e.what() << - ": please check for previous errors"); - mReadErrorsOnFilesystemObjects = true; + pLoc->mapDirectoryRecord.reset( + new BackupClientDirectoryRecord(existing_remote_dir_id, *pLocName)); } - catch(...) + + // Read the exclude lists from the Configuration + pLoc->mapExcludeFiles.reset(BackupClientMakeExcludeList_Files(rConfig)); + pLoc->mapExcludeDirs.reset(BackupClientMakeExcludeList_Dirs(rConfig)); + + // Push it back on the vector of locations + mLocations.push_back(pLoc); + + if(apLoc.get() != NULL) { - BOX_ERROR("Failed to configure location '" - << pLoc->mName << "' path '" - << pLoc->mPath << "': please check for " - "previous errors"); - mReadErrorsOnFilesystemObjects = true; + // Don't delete it now! + apLoc.release(); } } @@ -2714,8 +2694,7 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con i = tmpLocations.begin(); i != tmpLocations.end(); i++) { - BOX_INFO("Removing obsolete location from memory: " << - (*i)->mName); + BOX_INFO("Removing obsolete location from memory: " << (*i)->mName); delete *i; } @@ -2745,8 +2724,7 @@ void BackupDaemon::SetupLocations(BackupClientContext &rClientContext, const Con SecondsToBoxTime(mDeleteRedundantLocationsAfter); } - int secs = BoxTimeToSeconds(mDeleteUnusedRootDirEntriesAfter - - now); + int secs = BoxTimeToSeconds(mDeleteUnusedRootDirEntriesAfter - now); BOX_NOTICE(dir.GetNumberOfEntries() << " redundant locations " "in root directory found, will delete from store " @@ -2829,7 +2807,7 @@ void BackupDaemon::FillIDMapVector(std::vector<BackupClientInodeToIDMap *> &rVec BOX_NOTICE("Found an incomplete ID map " "database, deleting it to start " "afresh: " << filename); - if(unlink(filename.c_str()) != 0) + if(EMU_UNLINK(filename.c_str()) != 0) { BOX_LOG_NATIVE_ERROR(BOX_FILE_MESSAGE( filename, "Failed to delete " @@ -2878,14 +2856,14 @@ void BackupDaemon::DeleteCorruptBerkelyDbFiles() // Delete the file BOX_TRACE("Deleting " << filename); - ::unlink(filename.c_str()); + EMU_UNLINK(filename.c_str()); // Add a suffix for the new map filename += ".n"; // Delete that too BOX_TRACE("Deleting " << filename); - ::unlink(filename.c_str()); + EMU_UNLINK(filename.c_str()); } } @@ -3636,7 +3614,7 @@ bool BackupDaemon::DeleteStoreObjectInfo() const } // Actually delete it - if(::unlink(storeObjectInfoFile.c_str()) != 0) + if(EMU_UNLINK(storeObjectInfoFile.c_str()) != 0) { BOX_LOG_SYS_ERROR("Failed to delete the old " "StoreObjectInfoFile: " << storeObjectInfoFile); diff --git a/lib/bbackupquery/BackupQueries.cpp b/lib/bbackupquery/BackupQueries.cpp index bcb1827e..f7a511c9 100644 --- a/lib/bbackupquery/BackupQueries.cpp +++ b/lib/bbackupquery/BackupQueries.cpp @@ -1003,17 +1003,17 @@ void BackupQueries::CommandGetObject(const std::vector<std::string> &args, const { BOX_ERROR("Object ID " << BOX_FORMAT_OBJECTID(id) << " does not exist on store."); - ::unlink(args[1].c_str()); + EMU_UNLINK(args[1].c_str()); } else { BOX_ERROR("Error occured fetching object."); - ::unlink(args[1].c_str()); + EMU_UNLINK(args[1].c_str()); } } catch(...) { - ::unlink(args[1].c_str()); + EMU_UNLINK(args[1].c_str()); BOX_ERROR("Error occured fetching object."); } } @@ -1242,18 +1242,18 @@ void BackupQueries::CommandGet(std::vector<std::string> args, const bool *opts) { BOX_ERROR("Failed to fetch file: " << e.what()); - ::unlink(localName.c_str()); + EMU_UNLINK(localName.c_str()); } catch(std::exception &e) { BOX_ERROR("Failed to fetch file: " << e.what()); - ::unlink(localName.c_str()); + EMU_UNLINK(localName.c_str()); } catch(...) { BOX_ERROR("Failed to fetch file: unknown error"); - ::unlink(localName.c_str()); + EMU_UNLINK(localName.c_str()); } } diff --git a/lib/common/BannerText.cpp b/lib/common/BannerText.cpp new file mode 100644 index 00000000..9ec2c0d7 --- /dev/null +++ b/lib/common/BannerText.cpp @@ -0,0 +1,14 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BannerText.cpp +// Purpose: Banner text for daemons and utilities +// Created: 2018-08-16 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include "BannerText.h" + +#pragma message("Build signature: " BOX_BUILD_SIGNATURE) diff --git a/lib/common/BannerText.h b/lib/common/BannerText.h index 9ca0c11c..a2e412a8 100644 --- a/lib/common/BannerText.h +++ b/lib/common/BannerText.h @@ -14,9 +14,48 @@ # include "BoxVersion.h" #endif +// Already included by BoxPlatform.h: +#include <stdint.h> + +// Yes, you need two function macros to stringify an expanded macro value. +// https://stackoverflow.com/questions/5459868/concatenate-int-to-string-using-c-preprocessor +#define BOX_STRINGIFY_HELPER(x) #x +#define BOX_STRINGIFY(x) BOX_STRINGIFY_HELPER(x) + +// How to identify a 64-bit build: https://stackoverflow.com/a/687902/648162 +#if UINTPTR_MAX == (4294967295U) +# define BOX_BUILD_BITS 32 +#elif UINTPTR_MAX == (18446744073709551615UL) +# define BOX_BUILD_BITS 64 +#else +# pragma message ("UINTPTR_MAX = " BOX_STRINGIFY(UINTPTR_MAX)) +# error Unknown architecture pointer size +#endif + +#ifdef BOX_RELEASE_BUILD +# define BOX_BUILD_TYPE Release +#else +# define BOX_BUILD_TYPE Debug +#endif + +#define STRINGIFY1(x) #x +#define STRINGIFY2(x) STRINGIFY1(x) +#ifdef _MSC_VER +# define BOX_COMPILER "MSVC " STRINGIFY2(_MSC_VER) +#elif defined __GNUC__ +# define BOX_COMPILER "GCC " __VERSION__ +#elif defined __VERSION__ +// It might be an integer, not a string! +# define BOX_COMPILER "Unknown " BOX_STRINGIFY(__VERSION__) +#else +# define BOX_COMPILER "Unknown" +#endif + +#define BOX_BUILD_SIGNATURE BOX_COMPILER " " BOX_STRINGIFY(BOX_BUILD_BITS) "bit " BOX_STRINGIFY(BOX_BUILD_TYPE) + #define BANNER_TEXT(UtilityName) \ - "Box " UtilityName " v" BOX_VERSION ", (c) Ben Summers and " \ - "contributors 2003-2014" + "Box Backup " UtilityName " v" BOX_VERSION "\n" \ + "(c) Ben Summers and contributors 2003-2020. Build type: " BOX_BUILD_SIGNATURE #endif // BANNERTEXT__H diff --git a/lib/common/Box.h b/lib/common/Box.h index 8ce2a625..5f629790 100644 --- a/lib/common/Box.h +++ b/lib/common/Box.h @@ -39,7 +39,7 @@ #include "CommonException.h" #include "Logging.h" -#ifndef BOX_RELEASE_BUILD +#ifndef BOX_RELEASE_BUILD // this is a debug build: extern bool AssertFailuresToSyslog; #define ASSERT_FAILS_TO_SYSLOG_ON {AssertFailuresToSyslog = true;} void BoxDebugAssertFailed(const char *cond, const char *file, int line); @@ -70,7 +70,7 @@ // Exception names #define EXCEPTION_CODENAMES_EXTENDED -#else +#else // this is a release build: #define ASSERT_FAILS_TO_SYSLOG_ON #define ASSERT(cond) @@ -80,19 +80,19 @@ // Box Backup builds release get extra information for exception logging #define EXCEPTION_CODENAMES_EXTENDED #define EXCEPTION_CODENAMES_EXTENDED_WITH_DESCRIPTION + + #ifdef BOX_MEMORY_LEAK_TESTING + #error BOX_MEMORY_LEAK_TESTING should not already be defined in release builds! + #endif #endif -#if defined DEBUG_LEAKS +#if defined DEBUG_LEAKS // optionally enable memory leak debugging even in release builds #ifdef PLATFORM_DISABLE_MEM_LEAK_TESTING #error Compiling with DEBUG_LEAKS enabled, but not supported on this platform #else #define BOX_MEMORY_LEAK_TESTING #endif -#elif defined BOX_RELEASE_BUILD - #ifndef PLATFORM_DISABLE_MEM_LEAK_TESTING - #define BOX_MEMORY_LEAK_TESTING - #endif -#endif // DEBUG_LEAKS || BOX_RELEASE_BUILD +#endif // DEBUG_LEAKS #ifdef BOX_MEMORY_LEAK_TESTING // Memory leak testing diff --git a/lib/common/BoxPlatform.h b/lib/common/BoxPlatform.h index f7c74bfc..c86573f6 100644 --- a/lib/common/BoxPlatform.h +++ b/lib/common/BoxPlatform.h @@ -48,6 +48,10 @@ #include <sys/types.h> #endif +// Need to define this before including stdint.h to ensure access to UINTPTR_MAX in C99: +// https://stackoverflow.com/questions/986426/what-do-stdc-limit-macros-and-stdc-constant-macros-mean +#define __STDC_LIMIT_MACROS + #ifdef HAVE_INTTYPES_H #include <inttypes.h> #else diff --git a/lib/common/InvisibleTempFileStream.cpp b/lib/common/InvisibleTempFileStream.cpp index 1a9d6d5a..d6d04489 100644 --- a/lib/common/InvisibleTempFileStream.cpp +++ b/lib/common/InvisibleTempFileStream.cpp @@ -31,7 +31,7 @@ InvisibleTempFileStream::InvisibleTempFileStream(const std::string& Filename, #endif { #ifndef WIN32 - if(unlink(Filename.c_str()) != 0) + if(EMU_UNLINK(Filename.c_str()) != 0) { MEMLEAKFINDER_NOT_A_LEAK(this); THROW_EXCEPTION(CommonException, OSFileOpenError) diff --git a/lib/common/MainHelper.h b/lib/common/MainHelper.h index 0303090e..f52607bf 100644 --- a/lib/common/MainHelper.h +++ b/lib/common/MainHelper.h @@ -16,13 +16,19 @@ # include "BoxVersion.h" #endif +#include "BannerText.h" #include "BoxException.h" #include "Logging.h" #define MAINHELPER_START \ - if(argc == 2 && ::strcmp(argv[1], "--version") == 0) \ - { printf(BOX_VERSION "\n"); return 0; } \ + /* need to init memleakfinder early because we already called MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT */ \ MEMLEAKFINDER_INIT \ + if(argc == 2 && ::strcmp(argv[1], "--version") == 0) \ + { \ + printf("Version: " BOX_VERSION "\n"); \ + printf("Build: " BOX_BUILD_SIGNATURE "\n"); \ + return 0; \ + } \ MEMLEAKFINDER_START \ try { diff --git a/lib/common/NamedLock.cpp b/lib/common/NamedLock.cpp index 8e672ff5..08d7c461 100644 --- a/lib/common/NamedLock.cpp +++ b/lib/common/NamedLock.cpp @@ -256,7 +256,7 @@ void NamedLock::ReleaseLock() // Windows, and there we need to close the file before deleting it, // otherwise the system won't let us delete it. - if(::unlink(mFileName.c_str()) != 0) + if(EMU_UNLINK(mFileName.c_str()) != 0) { THROW_EMU_ERROR( BOX_FILE_MESSAGE(mFileName, "Failed to delete lockfile"), @@ -287,7 +287,7 @@ void NamedLock::ReleaseLock() // On Windows we need to close the file before deleting it, otherwise // the system won't let us delete it. - if(::unlink(mFileName.c_str()) != 0) + if(EMU_UNLINK(mFileName.c_str()) != 0) { THROW_EMU_ERROR( BOX_FILE_MESSAGE(mFileName, "Failed to delete lockfile"), diff --git a/lib/common/Test.cpp b/lib/common/Test.cpp index 3d1620a1..c6b28738 100644 --- a/lib/common/Test.cpp +++ b/lib/common/Test.cpp @@ -116,7 +116,7 @@ bool setUp(const char* function_name) int filetype = ObjectExists(filepath); if(filetype == ObjectExists_File) { - if(::unlink(filepath.c_str()) != 0) + if(EMU_UNLINK(filepath.c_str()) != 0) { TEST_FAIL_WITH_MESSAGE(BOX_SYS_ERROR_MESSAGE("Failed to delete " "test fixture file: unlink(\"" << filepath << "\")")); @@ -428,7 +428,7 @@ void TestRemoteProcessMemLeaksFunc(const char *filename, } // Delete it - ::unlink(filename); + EMU_UNLINK(filename); } #endif } diff --git a/lib/raidfile/RaidFileWrite.cpp b/lib/raidfile/RaidFileWrite.cpp index 8f95ba65..3d661956 100644 --- a/lib/raidfile/RaidFileWrite.cpp +++ b/lib/raidfile/RaidFileWrite.cpp @@ -358,7 +358,7 @@ void RaidFileWrite::Commit(bool ConvertToRaidNow) #ifdef WIN32 // need to delete the target first - if(::unlink(renameTo.c_str()) != 0) + if(EMU_UNLINK(renameTo.c_str()) != 0) { DWORD errorNumber = GetLastError(); if (errorNumber != ERROR_FILE_NOT_FOUND) @@ -421,9 +421,9 @@ void RaidFileWrite::Discard() #ifdef WIN32 // On Win32 we must close it first if (::close(mOSFileHandle) != 0 || - ::unlink(writeFilename.c_str()) != 0) + EMU_UNLINK(writeFilename.c_str()) != 0) #else // !WIN32 - if (::unlink(writeFilename.c_str()) != 0 || + if (EMU_UNLINK(writeFilename.c_str()) != 0 || ::close(mOSFileHandle) != 0) #endif // !WIN32 { @@ -670,7 +670,7 @@ void RaidFileWrite::TransformToRaidStorage() // Must delete before renaming #define CHECK_UNLINK(file) \ { \ - if (::unlink(file) != 0 && errno != ENOENT) \ + if (EMU_UNLINK(file) != 0 && errno != ENOENT) \ { \ THROW_EMU_ERROR("Failed to unlink raidfile " \ "stripe: " << file, RaidFileException, \ @@ -695,7 +695,7 @@ void RaidFileWrite::TransformToRaidStorage() writeFile.Close(); // Finally delete the write file - if(::unlink(writeFilename.c_str()) != 0) + if(EMU_UNLINK(writeFilename.c_str()) != 0) { BOX_LOG_SYS_ERROR("Failed to delete file: " << writeFilename); @@ -705,12 +705,12 @@ void RaidFileWrite::TransformToRaidStorage() catch(...) { // Unlink all the dodgy files - ::unlink(stripe1Filename.c_str()); - ::unlink(stripe2Filename.c_str()); - ::unlink(parityFilename.c_str()); - ::unlink(stripe1FilenameW.c_str()); - ::unlink(stripe2FilenameW.c_str()); - ::unlink(parityFilenameW.c_str()); + EMU_UNLINK(stripe1Filename.c_str()); + EMU_UNLINK(stripe2Filename.c_str()); + EMU_UNLINK(parityFilename.c_str()); + EMU_UNLINK(stripe1FilenameW.c_str()); + EMU_UNLINK(stripe2FilenameW.c_str()); + EMU_UNLINK(parityFilenameW.c_str()); // and send the error on its way throw; @@ -754,7 +754,7 @@ void RaidFileWrite::Delete() // Attempt to delete it bool deletedSomething = false; - if(::unlink(writeFilename.c_str()) == 0) + if(EMU_UNLINK(writeFilename.c_str()) == 0) { deletedSomething = true; } @@ -769,15 +769,15 @@ void RaidFileWrite::Delete() std::string stripe1Filename(RaidFileUtil::MakeRaidComponentName(rdiscSet, mFilename, 0 % TRANSFORM_NUMBER_DISCS_REQUIRED)); std::string stripe2Filename(RaidFileUtil::MakeRaidComponentName(rdiscSet, mFilename, 1 % TRANSFORM_NUMBER_DISCS_REQUIRED)); std::string parityFilename(RaidFileUtil::MakeRaidComponentName(rdiscSet, mFilename, 2 % TRANSFORM_NUMBER_DISCS_REQUIRED)); - if(::unlink(stripe1Filename.c_str()) == 0) + if(EMU_UNLINK(stripe1Filename.c_str()) == 0) { deletedSomething = true; } - if(::unlink(stripe2Filename.c_str()) == 0) + if(EMU_UNLINK(stripe2Filename.c_str()) == 0) { deletedSomething = true; } - if(::unlink(parityFilename.c_str()) == 0) + if(EMU_UNLINK(parityFilename.c_str()) == 0) { deletedSomething = true; } diff --git a/lib/server/Daemon.cpp b/lib/server/Daemon.cpp index 76141b6f..748e6e47 100644 --- a/lib/server/Daemon.cpp +++ b/lib/server/Daemon.cpp @@ -702,7 +702,7 @@ int Daemon::Main(const std::string &rConfigFileName) } // Delete the PID file - ::unlink(pidFileName.c_str()); + EMU_UNLINK(pidFileName.c_str()); // Log BOX_NOTICE("Terminating daemon"); diff --git a/lib/server/ServerControl.cpp b/lib/server/ServerControl.cpp index b6cadab3..958302b8 100644 --- a/lib/server/ServerControl.cpp +++ b/lib/server/ServerControl.cpp @@ -241,7 +241,7 @@ bool KillServer(const std::string& pid_file, bool WaitForProcess) #ifdef WIN32 if(WaitForProcess) { - int unlink_result = unlink(pid_file.c_str()); + int unlink_result = EMU_UNLINK(pid_file.c_str()); TEST_EQUAL_LINE(0, unlink_result, std::string("unlink ") + pid_file); if(unlink_result != 0) { @@ -439,7 +439,7 @@ bool StopDaemon(int current_pid, const std::string& pid_file, TEST_THAT_OR(!ServerIsAlive(current_pid), return false); #ifdef WIN32 - int unlink_result = unlink(pid_file.c_str()); + int unlink_result = EMU_UNLINK(pid_file.c_str()); TEST_EQUAL_LINE(0, unlink_result, std::string("unlink ") + pid_file); if(unlink_result != 0) { diff --git a/lib/server/ServerStream.h b/lib/server/ServerStream.h index db8beaf2..bf508311 100644 --- a/lib/server/ServerStream.h +++ b/lib/server/ServerStream.h @@ -208,7 +208,7 @@ public: } // unlink anything there - ::unlink(c[1].c_str()); + EMU_UNLINK(c[1].c_str()); psocket->Listen(Socket::TypeUNIX, c[1].c_str()); #endif // WIN32 diff --git a/lib/win32/emu.h b/lib/win32/emu.h index 91793004..9b6ec117 100644 --- a/lib/win32/emu.h +++ b/lib/win32/emu.h @@ -4,14 +4,18 @@ #ifdef WIN32 #define EMU_STRUCT_STAT struct emu_stat - #define EMU_STAT emu_stat - #define EMU_FSTAT emu_fstat - #define EMU_LSTAT emu_stat + #define EMU_STAT emu_stat + #define EMU_FSTAT emu_fstat + #define EMU_LSTAT emu_stat + #define EMU_LINK emu_link + #define EMU_UNLINK emu_unlink #else #define EMU_STRUCT_STAT struct stat - #define EMU_STAT ::stat - #define EMU_FSTAT ::fstat - #define EMU_LSTAT ::lstat + #define EMU_STAT ::stat + #define EMU_FSTAT ::fstat + #define EMU_LSTAT ::lstat + #define EMU_LINK ::link + #define EMU_UNLINK ::unlink #endif #if ! defined EMU_INCLUDE && defined WIN32 @@ -352,13 +356,15 @@ int emu_rename (const char* pOldName, const char* pNewName); #define chdir(directory) emu_chdir (directory) #define mkdir(path, mode) emu_mkdir (path) -#define link(oldpath, newpath) emu_link (oldpath, newpath) -#define unlink(file) emu_unlink (file) #define utimes(buffer, times) emu_utimes (buffer, times) #define chmod(file, mode) emu_chmod (file, mode) #define getcwd(buffer, size) emu_getcwd (buffer, size) #define rename(oldname, newname) emu_rename (oldname, newname) +// link() and unlink() conflict with Boost if implemented using macros like +// the others above, so I've removed the macros and you need to use EMU_LINK +// and EMU_UNLINK everywhere. + // Not safe to replace stat/fstat/lstat on mingw at least, as struct stat // has a 16-bit st_ino and we need a 64-bit one. // diff --git a/lib/win32/getopt_long.cpp b/lib/win32/getopt_long.cpp index 825de2a0..af2833a1 100755 --- a/lib/win32/getopt_long.cpp +++ b/lib/win32/getopt_long.cpp @@ -1,546 +1,546 @@ -/* $OpenBSD: getopt_long.c,v 1.20 2005/10/25 15:49:37 jmc Exp $ */ -/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ -// Adapted for Box Backup by Chris Wilson <chris+boxbackup@qwirx.com> - -/* - * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Sponsored in part by the Defense Advanced Research Projects - * Agency (DARPA) and Air Force Research Laboratory, Air Force - * Materiel Command, USAF, under agreement number F39502-99-1-0512. - */ -/*- - * Copyright (c) 2000 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Dieter Baron and Thomas Klausner. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -// #include "Box.h" -#include "emu.h" - -#include <errno.h> -#include <stdarg.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include "box_getopt.h" - -#ifdef REPLACE_GETOPT // until end of file - -int opterr = 1; /* if error message should be printed */ -int optind = 1; /* index into parent argv vector */ -int optopt = '?'; /* character checked for validity */ -int optreset; /* reset getopt */ -char *optarg; /* argument associated with option */ - -#define PRINT_ERROR ((opterr) && (*options != ':')) - -#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ -#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ -#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ - -/* return values */ -#define BADCH (int)'?' -#define BADARG ((*options == ':') ? (int)':' : (int)'?') -#define INORDER (int)1 - -#define EMSG "" - -static void warnx(const char* fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "\n"); -} - -static int getopt_internal(int, char * const *, const char *, - const struct option *, int *, int); -static int parse_long_options(char * const *, const char *, - const struct option *, int *, int); -static int gcd(int, int); -static void permute_args(int, int, int, char * const *); - -static char *place = EMSG; /* option letter processing */ - -/* XXX: set optreset to 1 rather than these two */ -static int nonopt_start = -1; /* first non option argument (for permute) */ -static int nonopt_end = -1; /* first option after non options (for permute) */ - -/* Error messages */ -static const char recargchar[] = "option requires an argument -- %c"; -static const char recargstring[] = "option requires an argument -- %s"; -static const char ambig[] = "ambiguous option -- %.*s"; -static const char noarg[] = "option doesn't take an argument -- %.*s"; -static const char illoptchar[] = "unknown option -- %c"; -static const char illoptstring[] = "unknown option -- %s"; - -/* - * Compute the greatest common divisor of a and b. - */ -static int -gcd(int a, int b) -{ - int c; - - c = a % b; - while (c != 0) { - a = b; - b = c; - c = a % b; - } - - return (b); -} - -/* - * Exchange the block from nonopt_start to nonopt_end with the block - * from nonopt_end to opt_end (keeping the same order of arguments - * in each block). - */ -static void -permute_args(int panonopt_start, int panonopt_end, int opt_end, - char * const *nargv) -{ - int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; - char *swap; - - /* - * compute lengths of blocks and number and size of cycles - */ - nnonopts = panonopt_end - panonopt_start; - nopts = opt_end - panonopt_end; - ncycle = gcd(nnonopts, nopts); - cyclelen = (opt_end - panonopt_start) / ncycle; - - for (i = 0; i < ncycle; i++) { - cstart = panonopt_end+i; - pos = cstart; - for (j = 0; j < cyclelen; j++) { - if (pos >= panonopt_end) - pos -= nnonopts; - else - pos += nopts; - swap = nargv[pos]; - /* LINTED const cast */ - ((char **) nargv)[pos] = nargv[cstart]; - /* LINTED const cast */ - ((char **)nargv)[cstart] = swap; - } - } -} - -/* - * parse_long_options -- - * Parse long options in argc/argv argument vector. - * Returns -1 if short_too is set and the option does not match long_options. - */ -static int -parse_long_options(char * const *nargv, const char *options, - const struct option *long_options, int *idx, int short_too) -{ - char *current_argv, *has_equal; - size_t current_argv_len; - int i, match; - - current_argv = place; - match = -1; - - optind++; - - if ((has_equal = strchr(current_argv, '=')) != NULL) { - /* argument found (--option=arg) */ - current_argv_len = has_equal - current_argv; - has_equal++; - } else - current_argv_len = strlen(current_argv); - - for (i = 0; long_options[i].name; i++) { - /* find matching long option */ - if (strncmp(current_argv, long_options[i].name, - current_argv_len)) - continue; - - if (strlen(long_options[i].name) == current_argv_len) { - /* exact match */ - match = i; - break; - } - /* - * If this is a known short option, don't allow - * a partial match of a single character. - */ - if (short_too && current_argv_len == 1) - continue; - - if (match == -1) /* partial match */ - match = i; - else { - /* ambiguous abbreviation */ - if (PRINT_ERROR) - warnx(ambig, (int)current_argv_len, - current_argv); - optopt = 0; - return (BADCH); - } - } - if (match != -1) { /* option found */ - if (long_options[match].has_arg == no_argument - && has_equal) { - if (PRINT_ERROR) - warnx(noarg, (int)current_argv_len, - current_argv); - /* - * XXX: GNU sets optopt to val regardless of flag - */ - if (long_options[match].flag == NULL) - optopt = long_options[match].val; - else - optopt = 0; - return (BADARG); - } - if (long_options[match].has_arg == required_argument || - long_options[match].has_arg == optional_argument) { - if (has_equal) - optarg = has_equal; - else if (long_options[match].has_arg == - required_argument) { - /* - * optional argument doesn't use next nargv - */ - optarg = nargv[optind++]; - } - } - if ((long_options[match].has_arg == required_argument) - && (optarg == NULL)) { - /* - * Missing argument; leading ':' indicates no error - * should be generated. - */ - if (PRINT_ERROR) - warnx(recargstring, - current_argv); - /* - * XXX: GNU sets optopt to val regardless of flag - */ - if (long_options[match].flag == NULL) - optopt = long_options[match].val; - else - optopt = 0; - --optind; - return (BADARG); - } - } else { /* unknown option */ - if (short_too) { - --optind; - return (-1); - } - if (PRINT_ERROR) - warnx(illoptstring, current_argv); - optopt = 0; - return (BADCH); - } - if (idx) - *idx = match; - if (long_options[match].flag) { - *long_options[match].flag = long_options[match].val; - return (0); - } else - return (long_options[match].val); -} - -/* - * getopt_internal -- - * Parse argc/argv argument vector. Called by user level routines. - */ -static int -getopt_internal(int nargc, char * const *nargv, const char *options, - const struct option *long_options, int *idx, int flags) -{ - const char * oli; /* option letter list index */ - int optchar, short_too; - static int posixly_correct = -1; - - if (options == NULL) - return (-1); - - /* - * Disable GNU extensions if POSIXLY_CORRECT is set or options - * string begins with a '+'. - */ - if (posixly_correct == -1) - posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); - if (posixly_correct || *options == '+') - flags &= ~FLAG_PERMUTE; - else if (*options == '-') - flags |= FLAG_ALLARGS; - if (*options == '+' || *options == '-') - options++; - - /* - * XXX Some GNU programs (like cvs) set optind to 0 instead of - * XXX using optreset. Work around this braindamage. - */ - if (optind == 0) - optind = optreset = 1; - - optarg = NULL; - if (optreset) - nonopt_start = nonopt_end = -1; -start: - if (optreset || !*place) { /* update scanning pointer */ - optreset = 0; - if (optind >= nargc) { /* end of argument vector */ - place = EMSG; - if (nonopt_end != -1) { - /* do permutation, if we have to */ - permute_args(nonopt_start, nonopt_end, - optind, nargv); - optind -= nonopt_end - nonopt_start; - } - else if (nonopt_start != -1) { - /* - * If we skipped non-options, set optind - * to the first of them. - */ - optind = nonopt_start; - } - nonopt_start = nonopt_end = -1; - return (-1); - } - if (*(place = nargv[optind]) != '-' || - (place[1] == '\0' && strchr(options, '-') == NULL)) { - place = EMSG; /* found non-option */ - if (flags & FLAG_ALLARGS) { - /* - * GNU extension: - * return non-option as argument to option 1 - */ - optarg = nargv[optind++]; - return (INORDER); - } - if (!(flags & FLAG_PERMUTE)) { - /* - * If no permutation wanted, stop parsing - * at first non-option. - */ - return (-1); - } - /* do permutation */ - if (nonopt_start == -1) - nonopt_start = optind; - else if (nonopt_end != -1) { - permute_args(nonopt_start, nonopt_end, - optind, nargv); - nonopt_start = optind - - (nonopt_end - nonopt_start); - nonopt_end = -1; - } - optind++; - /* process next argument */ - goto start; - } - if (nonopt_start != -1 && nonopt_end == -1) - nonopt_end = optind; - - /* - * If we have "-" do nothing, if "--" we are done. - */ - if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { - optind++; - place = EMSG; - /* - * We found an option (--), so if we skipped - * non-options, we have to permute. - */ - if (nonopt_end != -1) { - permute_args(nonopt_start, nonopt_end, - optind, nargv); - optind -= nonopt_end - nonopt_start; - } - nonopt_start = nonopt_end = -1; - return (-1); - } - } - - /* - * Check long options if: - * 1) we were passed some - * 2) the arg is not just "-" - * 3) either the arg starts with -- we are getopt_long_only() - */ - if (long_options != NULL && place != nargv[optind] && - (*place == '-' || (flags & FLAG_LONGONLY))) { - short_too = 0; - if (*place == '-') - place++; /* --foo long option */ - else if (*place != ':' && strchr(options, *place) != NULL) - short_too = 1; /* could be short option too */ - - optchar = parse_long_options(nargv, options, long_options, - idx, short_too); - if (optchar != -1) { - place = EMSG; - return (optchar); - } - } - - if ((optchar = (int)*place++) == (int)':' || - optchar == (int)'-' && *place != '\0' || - (oli = strchr(options, optchar)) == NULL) { - /* - * If the user specified "-" and '-' isn't listed in - * options, return -1 (non-option) as per POSIX. - * Otherwise, it is an unknown option character (or ':'). - */ - if (optchar == (int)'-' && *place == '\0') - return (-1); - if (!*place) - ++optind; - if (PRINT_ERROR) - warnx(illoptchar, optchar); - optopt = optchar; - return (BADCH); - } - if (long_options != NULL && optchar == 'W' && oli[1] == ';') { - /* -W long-option */ - if (*place) /* no space */ - /* NOTHING */; - else if (++optind >= nargc) { /* no arg */ - place = EMSG; - if (PRINT_ERROR) - warnx(recargchar, optchar); - optopt = optchar; - return (BADARG); - } else /* white space */ - place = nargv[optind]; - optchar = parse_long_options(nargv, options, long_options, - idx, 0); - place = EMSG; - return (optchar); - } - if (*++oli != ':') { /* doesn't take argument */ - if (!*place) - ++optind; - } else { /* takes (optional) argument */ - optarg = NULL; - if (*place) /* no white space */ - optarg = place; - /* XXX: disable test for :: if PC? (GNU doesn't) */ - else if (oli[1] != ':') { /* arg not optional */ - if (++optind >= nargc) { /* no arg */ - place = EMSG; - if (PRINT_ERROR) - warnx(recargchar, optchar); - optopt = optchar; - return (BADARG); - } else - optarg = nargv[optind]; - } else if (!(flags & FLAG_PERMUTE)) { - /* - * If permutation is disabled, we can accept an - * optional arg separated by whitespace so long - * as it does not start with a dash (-). - */ - if (optind + 1 < nargc && *nargv[optind + 1] != '-') - optarg = nargv[++optind]; - } - place = EMSG; - ++optind; - } - /* dump back option letter */ - return (optchar); -} - -/* - * getopt -- - * Parse argc/argv argument vector. - * - * [eventually this will replace the BSD getopt] - */ -int -getopt(int nargc, char * const *nargv, const char *options) -{ - - /* - * We don't pass FLAG_PERMUTE to getopt_internal() since - * the BSD getopt(3) (unlike GNU) has never done this. - * - * Furthermore, since many privileged programs call getopt() - * before dropping privileges it makes sense to keep things - * as simple (and bug-free) as possible. - */ - return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); -} - -/* - * getopt_long -- - * Parse argc/argv argument vector. - */ -int -getopt_long(int nargc, char * const *nargv, const char *options, - const struct option *long_options, int *idx) -{ - - return (getopt_internal(nargc, nargv, options, long_options, idx, - FLAG_PERMUTE)); -} - -/* - * getopt_long_only -- - * Parse argc/argv argument vector. - */ -int -getopt_long_only(int nargc, char * const *nargv, const char *options, - const struct option *long_options, int *idx) -{ - - return (getopt_internal(nargc, nargv, options, long_options, idx, - FLAG_PERMUTE|FLAG_LONGONLY)); -} - -#endif // REPLACE_GETOPT +/* $OpenBSD: getopt_long.c,v 1.20 2005/10/25 15:49:37 jmc Exp $ */
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
+// Adapted for Box Backup by Chris Wilson <chris+boxbackup@qwirx.com>
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// #include "Box.h"
+#include "emu.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "box_getopt.h"
+
+#ifdef REPLACE_GETOPT // until end of file
+
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = '?'; /* character checked for validity */
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+
+#define PRINT_ERROR ((opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define EMSG ""
+
+static void warnx(const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static int getopt_internal(int, char * const *, const char *,
+ const struct option *, int *, int);
+static int parse_long_options(char * const *, const char *,
+ const struct option *, int *, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b)
+{
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return (b);
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end,
+ char * const *nargv)
+{
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end+i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ ((char **) nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ ((char **)nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+ * parse_long_options --
+ * Parse long options in argc/argv argument vector.
+ * Returns -1 if short_too is set and the option does not match long_options.
+ */
+static int
+parse_long_options(char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int short_too)
+{
+ char *current_argv, *has_equal;
+ size_t current_argv_len;
+ int i, match;
+
+ current_argv = place;
+ match = -1;
+
+ optind++;
+
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name,
+ current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ /* exact match */
+ match = i;
+ break;
+ }
+ /*
+ * If this is a known short option, don't allow
+ * a partial match of a single character.
+ */
+ if (short_too && current_argv_len == 1)
+ continue;
+
+ if (match == -1) /* partial match */
+ match = i;
+ else {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR)
+ warnx(ambig, (int)current_argv_len,
+ current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument
+ && has_equal) {
+ if (PRINT_ERROR)
+ warnx(noarg, (int)current_argv_len,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ return (BADARG);
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else if (long_options[match].has_arg ==
+ required_argument) {
+ /*
+ * optional argument doesn't use next nargv
+ */
+ optarg = nargv[optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument; leading ':' indicates no error
+ * should be generated.
+ */
+ if (PRINT_ERROR)
+ warnx(recargstring,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ --optind;
+ return (BADARG);
+ }
+ } else { /* unknown option */
+ if (short_too) {
+ --optind;
+ return (-1);
+ }
+ if (PRINT_ERROR)
+ warnx(illoptstring, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ if (idx)
+ *idx = match;
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ return (0);
+ } else
+ return (long_options[match].val);
+}
+
+/*
+ * getopt_internal --
+ * Parse argc/argv argument vector. Called by user level routines.
+ */
+static int
+getopt_internal(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int flags)
+{
+ const char * oli; /* option letter list index */
+ int optchar, short_too;
+ static int posixly_correct = -1;
+
+ if (options == NULL)
+ return (-1);
+
+ /*
+ * Disable GNU extensions if POSIXLY_CORRECT is set or options
+ * string begins with a '+'.
+ */
+ if (posixly_correct == -1)
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+ if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+ else if (*options == '-')
+ flags |= FLAG_ALLARGS;
+ if (*options == '+' || *options == '-')
+ options++;
+
+ /*
+ * XXX Some GNU programs (like cvs) set optind to 0 instead of
+ * XXX using optreset. Work around this braindamage.
+ */
+ if (optind == 0)
+ optind = optreset = 1;
+
+ optarg = NULL;
+ if (optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set optind
+ * to the first of them.
+ */
+ optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ if (*(place = nargv[optind]) != '-' ||
+ (place[1] == '\0' && strchr(options, '-') == NULL)) {
+ place = EMSG; /* found non-option */
+ if (flags & FLAG_ALLARGS) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ optarg = nargv[optind++];
+ return (INORDER);
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If no permutation wanted, stop parsing
+ * at first non-option.
+ */
+ return (-1);
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ nonopt_start = optind -
+ (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = optind;
+
+ /*
+ * If we have "-" do nothing, if "--" we are done.
+ */
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ optind++;
+ place = EMSG;
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ }
+
+ /*
+ * Check long options if:
+ * 1) we were passed some
+ * 2) the arg is not just "-"
+ * 3) either the arg starts with -- we are getopt_long_only()
+ */
+ if (long_options != NULL && place != nargv[optind] &&
+ (*place == '-' || (flags & FLAG_LONGONLY))) {
+ short_too = 0;
+ if (*place == '-')
+ place++; /* --foo long option */
+ else if (*place != ':' && strchr(options, *place) != NULL)
+ short_too = 1; /* could be short option too */
+
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, short_too);
+ if (optchar != -1) {
+ place = EMSG;
+ return (optchar);
+ }
+ }
+
+ if ((optchar = (int)*place++) == (int)':' ||
+ optchar == (int)'-' && *place != '\0' ||
+ (oli = strchr(options, optchar)) == NULL) {
+ /*
+ * If the user specified "-" and '-' isn't listed in
+ * options, return -1 (non-option) as per POSIX.
+ * Otherwise, it is an unknown option character (or ':').
+ */
+ if (optchar == (int)'-' && *place == '\0')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (PRINT_ERROR)
+ warnx(illoptchar, optchar);
+ optopt = optchar;
+ return (BADCH);
+ }
+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+ /* -W long-option */
+ if (*place) /* no space */
+ /* NOTHING */;
+ else if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else /* white space */
+ place = nargv[optind];
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, 0);
+ place = EMSG;
+ return (optchar);
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++optind;
+ } else { /* takes (optional) argument */
+ optarg = NULL;
+ if (*place) /* no white space */
+ optarg = place;
+ /* XXX: disable test for :: if PC? (GNU doesn't) */
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else
+ optarg = nargv[optind];
+ } else if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If permutation is disabled, we can accept an
+ * optional arg separated by whitespace so long
+ * as it does not start with a dash (-).
+ */
+ if (optind + 1 < nargc && *nargv[optind + 1] != '-')
+ optarg = nargv[++optind];
+ }
+ place = EMSG;
+ ++optind;
+ }
+ /* dump back option letter */
+ return (optchar);
+}
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the BSD getopt]
+ */
+int
+getopt(int nargc, char * const *nargv, const char *options)
+{
+
+ /*
+ * We don't pass FLAG_PERMUTE to getopt_internal() since
+ * the BSD getopt(3) (unlike GNU) has never done this.
+ *
+ * Furthermore, since many privileged programs call getopt()
+ * before dropping privileges it makes sense to keep things
+ * as simple (and bug-free) as possible.
+ */
+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE));
+}
+
+/*
+ * getopt_long_only --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long_only(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE|FLAG_LONGONLY));
+}
+
+#endif // REPLACE_GETOPT
diff --git a/lib/win32/messages.rc b/lib/win32/messages.rc index 0885a897..116522b7 100755 --- a/lib/win32/messages.rc +++ b/lib/win32/messages.rc @@ -1,2 +1,2 @@ -LANGUAGE 0x9,0x1 -1 11 MSG00001.bin +LANGUAGE 0x9,0x1
+1 11 MSG00001.bin
|