summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorReinhard Tartler <siretart@tauware.de>2020-05-10 15:21:30 -0400
committerReinhard Tartler <siretart@tauware.de>2020-05-10 15:21:30 -0400
commit657715ed754d69235aac62cd36f514f6dc65aab8 (patch)
tree96e6f410510f5d009de950fee644ba32ae79d593 /lib
parent6017757bc079f4446aa77bc5c0855c52741280f4 (diff)
New upstream version 0.13~~git20200326.g8e8b63c
Diffstat (limited to 'lib')
-rw-r--r--lib/backupclient/BackupClientRestore.cpp6
-rw-r--r--lib/backupstore/BackupClientFileAttributes.cpp2
-rw-r--r--lib/backupstore/BackupStoreFile.cpp2
-rw-r--r--lib/backupstore/BackupStoreRefCountDatabase.cpp6
-rw-r--r--lib/bbackupd/BackupClientDirectoryRecord.cpp330
-rw-r--r--lib/bbackupd/BackupClientDirectoryRecord.h5
-rw-r--r--lib/bbackupd/BackupDaemon.cpp222
-rw-r--r--lib/bbackupquery/BackupQueries.cpp12
-rw-r--r--lib/common/BannerText.cpp14
-rw-r--r--lib/common/BannerText.h43
-rw-r--r--lib/common/Box.h16
-rw-r--r--lib/common/BoxPlatform.h4
-rw-r--r--lib/common/InvisibleTempFileStream.cpp2
-rw-r--r--lib/common/MainHelper.h10
-rw-r--r--lib/common/NamedLock.cpp4
-rw-r--r--lib/common/Test.cpp4
-rw-r--r--lib/raidfile/RaidFileWrite.cpp30
-rw-r--r--lib/server/Daemon.cpp2
-rw-r--r--lib/server/ServerControl.cpp4
-rw-r--r--lib/server/ServerStream.h2
-rw-r--r--lib/win32/emu.h22
-rwxr-xr-xlib/win32/getopt_long.cpp1092
-rwxr-xr-xlib/win32/messages.rc4
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