mirror of
https://github.com/abraunegg/onedrive
synced 2026-03-14 14:35:46 +01:00
Enhance displayFileSystemErrorMessage() to include details of the actual path (#3574)
* Enhance displayFileSystemErrorMessage() to include details of the actual path that generated the error message to make diagnostics easier when a file system issue is generated * Add SQLITE_READONLY as a case to catch if the database file is read-only
This commit is contained in:
parent
0c57b78ee1
commit
6396864bfc
7 changed files with 266 additions and 90 deletions
26
src/config.d
26
src/config.d
|
|
@ -723,6 +723,9 @@ class ApplicationConfig {
|
|||
|
||||
// Create a backup of the 'config' file if it does not exist
|
||||
void createBackupConfigFile() {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
if (!getValueBool("dry_run")) {
|
||||
// Is there a backup of the config file if the config file exists?
|
||||
if (exists(applicableConfigFilePath)) {
|
||||
|
|
@ -734,7 +737,7 @@ class ApplicationConfig {
|
|||
configBackupFile.setAttributes(convertedPermissionValue);
|
||||
} catch (FileException e) {
|
||||
// filesystem error
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, configBackupFile, FsErrorSeverity.warning);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1906,6 +1909,9 @@ class ApplicationConfig {
|
|||
// Check the application configuration for any changes that need to trigger a --resync
|
||||
// This function is only called if --resync is not present
|
||||
bool applicationChangeWhereResyncRequired() {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// Default is that no resync is required
|
||||
bool resyncRequired = false;
|
||||
|
||||
|
|
@ -1972,11 +1978,11 @@ class ApplicationConfig {
|
|||
} catch (FileException e) {
|
||||
// filesystem error
|
||||
failedToReadBackupConfig = true;
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, configBackupFile, FsErrorSeverity.warning);
|
||||
} catch (std.exception.ErrnoException e) {
|
||||
// filesystem error
|
||||
failedToReadBackupConfig = true;
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, configBackupFile, FsErrorSeverity.warning);
|
||||
}
|
||||
|
||||
scope(exit) {
|
||||
|
|
@ -2172,6 +2178,9 @@ class ApplicationConfig {
|
|||
|
||||
// For each of the config files, update the hash data in the hash files
|
||||
void updateHashContentsForConfigFiles() {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// Are we in a --dry-run scenario?
|
||||
if (!getValueBool("dry_run")) {
|
||||
// Not a dry-run scenario, update the applicable files
|
||||
|
|
@ -2185,7 +2194,7 @@ class ApplicationConfig {
|
|||
configHashFile.setAttributes(convertedPermissionValue);
|
||||
} catch (FileException e) {
|
||||
// filesystem error
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, configHashFile, FsErrorSeverity.warning);
|
||||
}
|
||||
}
|
||||
// Update 'sync_list' files
|
||||
|
|
@ -2198,7 +2207,7 @@ class ApplicationConfig {
|
|||
syncListHashFile.setAttributes(convertedPermissionValue);
|
||||
} catch (FileException e) {
|
||||
// filesystem error
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, syncListHashFile, FsErrorSeverity.warning);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -2209,6 +2218,9 @@ class ApplicationConfig {
|
|||
|
||||
// Create any required hash files for files that help us determine if the configuration has changed since last run
|
||||
void createRequiredInitialConfigurationHashFiles() {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// Does a 'config' file exist with a valid hash file
|
||||
if (exists(applicableConfigFilePath)) {
|
||||
if (!exists(configHashFile)) {
|
||||
|
|
@ -2219,7 +2231,7 @@ class ApplicationConfig {
|
|||
configHashFile.setAttributes(convertedPermissionValue);
|
||||
} catch (FileException e) {
|
||||
// filesystem error
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, configHashFile, FsErrorSeverity.warning);
|
||||
}
|
||||
}
|
||||
// Generate the runtime hash for the 'config' file
|
||||
|
|
@ -2236,7 +2248,7 @@ class ApplicationConfig {
|
|||
syncListHashFile.setAttributes(convertedPermissionValue);
|
||||
} catch (FileException e) {
|
||||
// filesystem error
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, syncListHashFile, FsErrorSeverity.warning);
|
||||
}
|
||||
}
|
||||
// Generate the runtime hash for the 'sync_list' file
|
||||
|
|
|
|||
|
|
@ -446,6 +446,7 @@ final class ItemDatabase {
|
|||
|
||||
switch (exception.errorCode) {
|
||||
case 7: // SQLITE_FULL
|
||||
case 8: // SQLITE_READONLY
|
||||
case 10: // SQLITE_SCHEMA
|
||||
case 11: // SQLITE_CORRUPT
|
||||
case 17: // SQLITE_IOERR
|
||||
|
|
|
|||
14
src/main.d
14
src/main.d
|
|
@ -852,8 +852,18 @@ int main(string[] cliArgs) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Change the working directory to the 'sync_dir' as configured
|
||||
chdir(runtimeSyncDirectory);
|
||||
// Try and change to the working directory to the 'sync_dir' as configured
|
||||
try {
|
||||
chdir(runtimeSyncDirectory);
|
||||
// A FileSystem exception was thrown when attempting to change to the configured 'sync_dir'
|
||||
} catch (FileException e) {
|
||||
// Log error message
|
||||
addLogEntry("FATAL: Unable to change to the configured local 'sync_dir' directory: " ~ runtimeSyncDirectory);
|
||||
// A file system exception was generated
|
||||
displayFileSystemErrorMessage(e.msg, strip(getFunctionName!({})), runtimeSyncDirectory, FsErrorSeverity.fatal);
|
||||
// Use exit scopes to shutdown API as if we are unable to change to the 'sync_dir' we need to exit
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Do we need to validate the runtimeSyncDirectory to check for the presence of a '.nosync' file
|
||||
checkForNoMountScenario();
|
||||
|
|
|
|||
|
|
@ -360,6 +360,9 @@ final class Monitor {
|
|||
|
||||
// Recursively add this path to be monitored
|
||||
private void addRecursive(string dirname) {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// skip non existing/disappeared items
|
||||
if (!exists(dirname)) {
|
||||
if (verboseLogging) {addLogEntry("Not adding non-existing/disappeared directory: " ~ dirname, ["verbose"]);}
|
||||
|
|
@ -452,17 +455,24 @@ final class Monitor {
|
|||
// Catch any FileException error which is generated
|
||||
} catch (std.file.FileException e) {
|
||||
// Standard filesystem error
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, dirname);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse directory to test if this should have an inotify watch added
|
||||
private void traverseDirectory(string dirname) {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// Current path for error logging
|
||||
string currentPath;
|
||||
|
||||
// Try and get all the directory entities for this path
|
||||
try {
|
||||
auto pathList = dirEntries(dirname, SpanMode.shallow, false);
|
||||
foreach(DirEntry entry; pathList) {
|
||||
currentPath = entry.name;
|
||||
if (entry.isDir) {
|
||||
if (debugLogging) {addLogEntry("Calling addRecursive() for this directory: " ~ entry.name, ["debug"]);}
|
||||
addRecursive(entry.name);
|
||||
|
|
@ -471,7 +481,7 @@ final class Monitor {
|
|||
// Catch any FileException error which is generated
|
||||
} catch (std.file.FileException e) {
|
||||
// Standard filesystem error
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, currentPath);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
// Issue #1154 handling
|
||||
|
|
@ -486,7 +496,7 @@ final class Monitor {
|
|||
forceExit();
|
||||
} else {
|
||||
// some other error
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, currentPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -486,6 +486,9 @@ class OneDriveApi {
|
|||
|
||||
// Authenticate this client against Microsoft OneDrive API using one of the 3 authentication methods this client supports
|
||||
bool authorise() {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// Has the client been configured to use Intune SSO via Microsoft Identity Broker (microsoft-identity-broker) dbus session
|
||||
if (appConfig.getValueBool("use_intune_sso")) {
|
||||
// The client is configured to use Intune SSO via Microsoft Identity Broker dbus session
|
||||
|
|
@ -697,7 +700,7 @@ class OneDriveApi {
|
|||
char[] response;
|
||||
// What URL should be presented to the user to access
|
||||
string url = authUrl ~ "?client_id=" ~ clientId ~ authScope ~ redirectUrl;
|
||||
// Configure automated authentication if --auth-files authUrl:responseUrl is being used
|
||||
// Configure automated authentication if --auth-files authUrlFilePath:responseUrlFilePath is being used
|
||||
string authFilesString = appConfig.getValueString("auth_files");
|
||||
string authResponseString = appConfig.getValueString("auth_response");
|
||||
|
||||
|
|
@ -706,48 +709,60 @@ class OneDriveApi {
|
|||
response = cast(char[]) authResponseString;
|
||||
} else if (authFilesString != "") {
|
||||
string[] authFiles = authFilesString.split(":");
|
||||
string authUrl = authFiles[0];
|
||||
string responseUrl = authFiles[1];
|
||||
string authUrlFilePath = authFiles[0];
|
||||
string responseUrlFilePath = authFiles[1];
|
||||
|
||||
try {
|
||||
auto authUrlFile = File(authUrl, "w");
|
||||
auto authUrlFile = File(authUrlFilePath, "w");
|
||||
authUrlFile.write(url);
|
||||
authUrlFile.close();
|
||||
} catch (FileException exception) {
|
||||
// There was a file system error
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(exception.msg, thisFunctionName, authUrlFilePath);
|
||||
// Must force exit here, allow logging to be done
|
||||
forceExit();
|
||||
} catch (ErrnoException exception) {
|
||||
// There was a file system error
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(exception.msg, thisFunctionName, authUrlFilePath);
|
||||
// Must force exit here, allow logging to be done
|
||||
forceExit();
|
||||
}
|
||||
|
||||
|
||||
// Log we are now waiting
|
||||
addLogEntry("Client requires authentication before proceeding. Waiting for --auth-files elements to be available.");
|
||||
|
||||
while (!exists(responseUrl)) {
|
||||
while (!exists(responseUrlFilePath)) {
|
||||
Thread.sleep(dur!("msecs")(100));
|
||||
}
|
||||
|
||||
// read response from provided from OneDrive
|
||||
try {
|
||||
response = cast(char[]) read(responseUrl);
|
||||
response = cast(char[]) read(responseUrlFilePath);
|
||||
} catch (OneDriveException exception) {
|
||||
// exception generated
|
||||
displayOneDriveErrorMessage(exception.msg, getFunctionName!({}));
|
||||
displayOneDriveErrorMessage(exception.msg, thisFunctionName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to remove old files
|
||||
// try to remove auth files one at a time
|
||||
try {
|
||||
std.file.remove(authUrl);
|
||||
std.file.remove(responseUrl);
|
||||
std.file.remove(authUrlFilePath);
|
||||
|
||||
} catch (FileException exception) {
|
||||
addLogEntry("Cannot remove files " ~ authUrl ~ " " ~ responseUrl);
|
||||
addLogEntry("Cannot remove --auth-files elements - details below");
|
||||
// There was a file system error - display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, thisFunctionName, authUrlFilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
std.file.remove(responseUrlFilePath);
|
||||
} catch (FileException exception) {
|
||||
addLogEntry("Cannot remove --auth-files elements - details below");
|
||||
// There was a file system error - display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, thisFunctionName, responseUrlFilePath);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -794,8 +809,10 @@ class OneDriveApi {
|
|||
|
||||
// Process Intune JSON response data
|
||||
void processIntuneResponse(JSONValue intuneBrokerJSONData) {
|
||||
// Use the provided JSON data and configure elements, save JSON data to disk for reuse
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// Use the provided JSON data and configure elements, save JSON data to disk for reuse
|
||||
long expiresOnMs = intuneBrokerJSONData["expiresOn"].integer();
|
||||
// Convert to SysTime
|
||||
SysTime expiryTime = SysTime.fromUnixTime(expiresOnMs / 1000);
|
||||
|
|
@ -821,7 +838,7 @@ class OneDriveApi {
|
|||
appConfig.intuneAccountDetailsFilePath.setAttributes(appConfig.returnSecureFilePermission());
|
||||
} catch (FileException exception) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(exception.msg, thisFunctionName, appConfig.intuneAccountDetailsFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1153,6 +1170,9 @@ class OneDriveApi {
|
|||
|
||||
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_get_content
|
||||
void downloadById(const(char)[] driveId, const(char)[] itemId, string saveToPath, long fileSize, JSONValue onlineHash, long resumeOffset = 0) {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// We pass through to 'downloadFile()'
|
||||
// - resumeOffset
|
||||
// - onlineHash
|
||||
|
|
@ -1166,7 +1186,7 @@ class OneDriveApi {
|
|||
remove(saveToPath);
|
||||
} catch (FileException exception) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(exception.msg, thisFunctionName, saveToPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1190,7 +1210,7 @@ class OneDriveApi {
|
|||
}
|
||||
} catch (FileException exception) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(exception.msg, thisFunctionName, parentalPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1242,6 +1262,10 @@ class OneDriveApi {
|
|||
}
|
||||
|
||||
private void acquireToken(char[] postData) {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// Configure the response JSON
|
||||
JSONValue response;
|
||||
|
||||
// Log what we are doing
|
||||
|
|
@ -1266,7 +1290,7 @@ class OneDriveApi {
|
|||
// There was a HTTP 5xx Server Side Error - retry
|
||||
acquireToken(postData);
|
||||
} else {
|
||||
displayOneDriveErrorMessage(exception.msg, getFunctionName!({}));
|
||||
displayOneDriveErrorMessage(exception.msg, thisFunctionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1327,6 +1351,9 @@ class OneDriveApi {
|
|||
|
||||
// Process the authentication JSON
|
||||
private void processAuthenticationJSON(JSONValue response) {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// Use 'access_token' and set in the application configuration
|
||||
appConfig.accessToken = "bearer " ~ strip(response["access_token"].str);
|
||||
|
||||
|
|
@ -1363,7 +1390,7 @@ class OneDriveApi {
|
|||
appConfig.refreshTokenFilePath.setAttributes(appConfig.returnSecureFilePermission());
|
||||
} catch (FileException exception) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(exception.msg, thisFunctionName, appConfig.refreshTokenFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1708,12 +1735,14 @@ class OneDriveApi {
|
|||
|
||||
// Save the resume download data
|
||||
private void saveResumeDownloadFile(string threadResumeDownloadFilePath, JSONValue resumeDownloadData) {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
try {
|
||||
std.file.write(threadResumeDownloadFilePath, resumeDownloadData.toString());
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, threadResumeDownloadFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2036,11 +2065,10 @@ class OneDriveApi {
|
|||
throw new OneDriveException(response.statusLine.code, response.statusLine.reason, response);
|
||||
}
|
||||
|
||||
// A FileSystem exception was thrown
|
||||
} catch (ErrnoException exception) {
|
||||
// There was a file system error
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, callingFunction);
|
||||
// A FileSystem exception was thrown from somewhere
|
||||
} catch (FileException exception) {
|
||||
// There was a file system error - display the error message
|
||||
displayFileSystemErrorMessage(exception.msg, callingFunction, ""); // as we have no file path reference here, use a blank input
|
||||
throw new OneDriveException(0, "There was a file system error during OneDrive request: " ~ exception.msg, response);
|
||||
|
||||
// A OneDriveError was thrown
|
||||
|
|
|
|||
58
src/sync.d
58
src/sync.d
|
|
@ -3149,7 +3149,7 @@ class SyncEngine {
|
|||
saveDatabaseItem(newDatabaseItem);
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, newItemPath);
|
||||
}
|
||||
} else {
|
||||
// we dont create the directory, but we need to track that we 'faked it'
|
||||
|
|
@ -3497,7 +3497,7 @@ class SyncEngine {
|
|||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, existingItemPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3853,12 +3853,12 @@ class SyncEngine {
|
|||
} catch (FileException e) {
|
||||
// There was a file system error
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, newItemPath);
|
||||
downloadFailed = true;
|
||||
} catch (ErrnoException e) {
|
||||
// There was a file system error
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, newItemPath);
|
||||
downloadFailed = true;
|
||||
}
|
||||
|
||||
|
|
@ -4640,7 +4640,7 @@ class SyncEngine {
|
|||
rmdirRecurse(path);
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5420,7 +5420,7 @@ class SyncEngine {
|
|||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, localFilePath);
|
||||
}
|
||||
} else {
|
||||
// Directory does not exist locally, but it is in our database as a dbItem containing all the data was passed into this function
|
||||
|
|
@ -7064,7 +7064,7 @@ class SyncEngine {
|
|||
}
|
||||
} catch (FileException e) {
|
||||
// filesystem error
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, localFilePath);
|
||||
}
|
||||
} else {
|
||||
// As this is a unique thread, the sessionFilePath for where we save the data needs to be unique
|
||||
|
|
@ -7097,8 +7097,8 @@ class SyncEngine {
|
|||
displayOneDriveErrorMessage(exception.msg, thisFunctionName);
|
||||
}
|
||||
} catch (FileException e) {
|
||||
addLogEntry("DEBUG TO REMOVE: Modified file upload FileException Handling (Create the Upload Session)");
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
// Display filesystem exception error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, threadUploadSessionFilePath);
|
||||
}
|
||||
|
||||
// Do we have a valid session URL that we can use ?
|
||||
|
|
@ -7121,8 +7121,8 @@ class SyncEngine {
|
|||
displayOneDriveErrorMessage(exception.msg, thisFunctionName);
|
||||
|
||||
} catch (FileException e) {
|
||||
addLogEntry("DEBUG TO REMOVE: Modified file upload FileException Handling (Perform the Upload using the session)");
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
// Display filesystem exception error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, threadUploadSessionFilePath);
|
||||
}
|
||||
} else {
|
||||
// Create session Upload URL failed
|
||||
|
|
@ -7731,6 +7731,9 @@ class SyncEngine {
|
|||
// Before we traverse this 'path', we need to make a last check to see if this was just excluded
|
||||
bool skipFolderTraverse = skipBusinessSharedFolder(path);
|
||||
|
||||
// Current path for error logging
|
||||
string currentPath;
|
||||
|
||||
if (!unwanted) {
|
||||
// At this point, this path, we want to scan for new data as it is not excluded
|
||||
if (isDir(path)) {
|
||||
|
|
@ -7751,6 +7754,9 @@ class SyncEngine {
|
|||
try {
|
||||
auto directoryEntries = dirEntries(path, SpanMode.depth, false);
|
||||
foreach (DirEntry child; directoryEntries) {
|
||||
// set for error logging
|
||||
currentPath = child.name;
|
||||
|
||||
// what sort of child is this?
|
||||
if (isDir(child.name)) {
|
||||
addLogEntry("Removing local directory: " ~ child.name);
|
||||
|
|
@ -7766,7 +7772,7 @@ class SyncEngine {
|
|||
attrIsDir(child.linkAttributes) ? rmdir(child.name) : safeRemove(child.name);
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, currentPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7785,14 +7791,14 @@ class SyncEngine {
|
|||
rmdirRecurse(path);
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, path);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, currentPath);
|
||||
|
||||
// Display function processing time if configured to do so
|
||||
if (appConfig.getValueBool("display_processing_time") && debugLogging) {
|
||||
|
|
@ -7813,14 +7819,14 @@ class SyncEngine {
|
|||
try {
|
||||
auto directoryEntries = dirEntries(path, SpanMode.shallow, false);
|
||||
foreach (DirEntry entry; directoryEntries) {
|
||||
string thisPath = entry.name;
|
||||
scanPathForNewData(thisPath);
|
||||
currentPath = entry.name;
|
||||
scanPathForNewData(entry.name);
|
||||
}
|
||||
// Clear directoryEntries
|
||||
object.destroy(directoryEntries);
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, currentPath);
|
||||
|
||||
// Display function processing time if configured to do so
|
||||
if (appConfig.getValueBool("display_processing_time") && debugLogging) {
|
||||
|
|
@ -7947,14 +7953,14 @@ class SyncEngine {
|
|||
try {
|
||||
auto directoryEntries = dirEntries(path, SpanMode.shallow, false);
|
||||
foreach (DirEntry entry; directoryEntries) {
|
||||
string thisPath = entry.name;
|
||||
scanPathForNewData(thisPath);
|
||||
currentPath = entry.name;
|
||||
scanPathForNewData(entry.name);
|
||||
}
|
||||
// Clear directoryEntries
|
||||
object.destroy(directoryEntries);
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, currentPath);
|
||||
|
||||
// Display function processing time if configured to do so
|
||||
if (appConfig.getValueBool("display_processing_time") && debugLogging) {
|
||||
|
|
@ -9331,7 +9337,7 @@ class SyncEngine {
|
|||
} catch (FileException e) {
|
||||
// display the error message
|
||||
addLogEntry("Uploading new file: " ~ fileToUpload ~ " ... failed!", ["info", "notify"]);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, fileToUpload);
|
||||
|
||||
// OneDrive API Instance Cleanup - Shutdown API, free curl object and memory
|
||||
uploadFileOneDriveApiInstance.releaseCurlEngine();
|
||||
|
|
@ -9367,7 +9373,7 @@ class SyncEngine {
|
|||
} catch (FileException e) {
|
||||
// display the error message
|
||||
addLogEntry("Uploading new file: " ~ fileToUpload ~ " ... failed!", ["info", "notify"]);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, fileToUpload);
|
||||
}
|
||||
|
||||
// Do we have a valid session URL that we can use ?
|
||||
|
|
@ -9625,7 +9631,7 @@ class SyncEngine {
|
|||
std.file.write(threadUploadSessionFilePath, uploadSessionData.toString());
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, threadUploadSessionFilePath);
|
||||
}
|
||||
|
||||
// Display function processing time if configured to do so
|
||||
|
|
@ -9844,13 +9850,13 @@ class SyncEngine {
|
|||
|
||||
} catch (std.exception.ErrnoException e) {
|
||||
// There was a file system error - display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, newUploadSession["localPath"].str);
|
||||
return uploadResponse;
|
||||
}
|
||||
} catch (ErrnoException e) {
|
||||
// There was a file system error
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, uploadSessionData["localPath"].str);
|
||||
uploadResponse = null;
|
||||
return uploadResponse;
|
||||
}
|
||||
|
|
@ -11582,7 +11588,7 @@ class SyncEngine {
|
|||
}
|
||||
} catch (FileException e) {
|
||||
// filesystem generated an error message - display error message
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, path);
|
||||
} catch (OneDriveException e) {
|
||||
if (e.httpStatusCode == 404) {
|
||||
addLogEntry(e.msg);
|
||||
|
|
|
|||
159
src/util.d
159
src/util.d
|
|
@ -64,6 +64,13 @@ shared static this() {
|
|||
deviceName = Socket.hostName;
|
||||
}
|
||||
|
||||
// To assist with filesystem severity issues, configure an enum that can be used
|
||||
enum FsErrorSeverity {
|
||||
warning,
|
||||
error,
|
||||
fatal
|
||||
}
|
||||
|
||||
// Creates a safe backup of the given item, and only performs the function if not in a --dry-run scenario.
|
||||
// If the path already ends with "-<deviceName>-safeBackup-####", the counter is incremented
|
||||
// instead of appending another "-<deviceName>-safeBackup-".
|
||||
|
|
@ -216,7 +223,7 @@ void safeRemove(const(char)[] path) {
|
|||
return; // nothing to do
|
||||
}
|
||||
// Anything else is noteworthy (EISDIR, EACCES, etc.)
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName);
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, to!string(path));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -547,6 +554,9 @@ bool retryInternetConnectivityTest(ApplicationConfig appConfig) {
|
|||
// https://github.com/abraunegg/onedrive/issues/113
|
||||
// returns true if file can be accessed
|
||||
bool readLocalFile(string path) {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// What is the file size
|
||||
if (getSize(path) != 0) {
|
||||
try {
|
||||
|
|
@ -561,7 +571,7 @@ bool readLocalFile(string path) {
|
|||
}
|
||||
} catch (std.file.FileException e) {
|
||||
// Unable to read the file, log the error message
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, path);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -1000,29 +1010,126 @@ void handleClientUnauthorised(int httpStatusCode, JSONValue errorMessage) {
|
|||
}
|
||||
|
||||
// Parse and display error message received from the local file system
|
||||
void displayFileSystemErrorMessage(string message, string callingFunction) {
|
||||
addLogEntry(); // used rather than writeln
|
||||
addLogEntry("ERROR: The local file system returned an error with the following message:");
|
||||
void displayFileSystemErrorMessage(string message, string callingFunction, string contextPath, FsErrorSeverity severity = FsErrorSeverity.error) {
|
||||
// Separate this block from surrounding log output
|
||||
addLogEntry();
|
||||
|
||||
auto errorArray = splitLines(message);
|
||||
// Safely get the error message
|
||||
string errorMessage = errorArray.length > 0 ? to!string(errorArray[0]) : "No error message available";
|
||||
addLogEntry(" Error Message: " ~ errorMessage);
|
||||
|
||||
// Log the calling function
|
||||
addLogEntry(" Calling Function: " ~ callingFunction);
|
||||
// Header prefix for logging accuracy
|
||||
string headerPrefix = severity == FsErrorSeverity.warning ? "WARNING"
|
||||
: severity == FsErrorSeverity.fatal ? "FATAL"
|
||||
: "ERROR";
|
||||
|
||||
// Filesystem logging header
|
||||
addLogEntry(headerPrefix ~ ": The local file system returned an error with the following details:");
|
||||
|
||||
// Calling context (helps correlate where this came from)
|
||||
if (!callingFunction.empty) {
|
||||
addLogEntry(" Calling Function: " ~ callingFunction);
|
||||
}
|
||||
|
||||
try {
|
||||
// Safely check for disk space
|
||||
ulong localActualFreeSpace = to!ulong(getAvailableDiskSpace("."));
|
||||
if (localActualFreeSpace == 0) {
|
||||
// Must force exit here, allow logging to be done
|
||||
forceExit();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Handle exceptions from disk space check or type conversion
|
||||
addLogEntry(" Exception in disk space check: " ~ e.msg);
|
||||
}
|
||||
// Path context (the *thing* we were operating on)
|
||||
if (!contextPath.empty) {
|
||||
addLogEntry(" Path: " ~ contextPath);
|
||||
} else {
|
||||
addLogEntry(" Path: (not available)");
|
||||
}
|
||||
|
||||
// Primary error message (first line) + any additional lines
|
||||
string errorMessage = message;
|
||||
string[] errorLines;
|
||||
try {
|
||||
errorLines = splitLines(message);
|
||||
} catch (Exception e) {
|
||||
// splitLines should not fail, but never let logging throw
|
||||
addLogEntry(" NOTE: Failed to split file system exception message into lines: " ~ e.msg);
|
||||
}
|
||||
|
||||
// If we have lines to process
|
||||
if (errorLines.length > 0) {
|
||||
// First line: usually the most useful
|
||||
errorMessage = to!string(errorLines[0]);
|
||||
addLogEntry(" Error Message: " ~ errorMessage);
|
||||
|
||||
// Remaining lines (if any) often contain errno / path / syscall details
|
||||
if (errorLines.length > 1) {
|
||||
addLogEntry(" Error Details:");
|
||||
foreach (i, line; errorLines[1 .. $]) {
|
||||
// Avoid logging empty lines, but keep order
|
||||
if (!line.empty) {
|
||||
addLogEntry(" - " ~ to!string(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addLogEntry(" Error Message: No error message available");
|
||||
}
|
||||
|
||||
// Disk space diagnostics (best-effort)
|
||||
// We intentionally probe both the current directory and the target path directory when possible.
|
||||
try {
|
||||
// Always check the current working directory as a baseline
|
||||
ulong freeCwd = to!ulong(getAvailableDiskSpace("."));
|
||||
addLogEntry(" Disk Space (CWD): " ~ to!string(freeCwd) ~ " bytes available");
|
||||
|
||||
// If we have a context path, also check its parent directory when possible.
|
||||
// We keep this conservative: if anything throws, just log the exception.
|
||||
if (!contextPath.empty) {
|
||||
string targetProbePath = contextPath;
|
||||
|
||||
// If it's a file path, probe the parent directory (where writes/renames happen).
|
||||
// Avoid throwing if parentDir isn't available or contextPath is weird.
|
||||
try {
|
||||
// std.path.dirName handles both file/dir paths; if it returns ".", keep as-is.
|
||||
import std.path : dirName;
|
||||
auto parent = dirName(contextPath);
|
||||
if (!parent.empty) targetProbePath = parent;
|
||||
} catch (Exception e) {
|
||||
addLogEntry(" NOTE: Failed to derive parent directory from path: " ~ e.msg);
|
||||
}
|
||||
|
||||
ulong freeTarget = to!ulong(getAvailableDiskSpace(targetProbePath));
|
||||
addLogEntry(" Disk Space (Path): " ~ to!string(freeTarget) ~ " bytes available (parent path: " ~ targetProbePath ~ ")");
|
||||
|
||||
// Preserve existing behaviour: if disk space check returns 0, force exit.
|
||||
// (Assumes getAvailableDiskSpace returns 0 on a hard failure in your implementation.)
|
||||
if (freeTarget == 0 || freeCwd == 0) {
|
||||
// Must force exit here, allow logging to be done
|
||||
forceExit();
|
||||
}
|
||||
} else {
|
||||
// Preserve existing behaviour: if disk space check returns 0, force exit.
|
||||
if (freeCwd == 0) {
|
||||
forceExit();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Handle exceptions from disk space check or type conversion
|
||||
addLogEntry(" NOTE: Exception during disk space check: " ~ e.msg);
|
||||
}
|
||||
|
||||
// Add note for WARNING messages
|
||||
if (headerPrefix == "WARNING") {
|
||||
addLogEntry();
|
||||
addLogEntry("NOTE: This error is non-fatal; the client will continue to operate, but this may affect future operations if not resolved");
|
||||
addLogEntry();
|
||||
}
|
||||
|
||||
// Add note for ERROR messages
|
||||
if (headerPrefix == "ERROR") {
|
||||
addLogEntry();
|
||||
addLogEntry("NOTE: This error requires attention; the client may continue running, but functionality is impaired and the issue should be resolved.");
|
||||
addLogEntry();
|
||||
}
|
||||
|
||||
// Add note for FATAL messages
|
||||
if (headerPrefix == "FATAL") {
|
||||
addLogEntry();
|
||||
addLogEntry("NOTE: This error is fatal; the client cannot continue and this issue must be corrected before retrying. The client will now attempt to exit in a safe and orderly manner.");
|
||||
addLogEntry();
|
||||
}
|
||||
|
||||
// Separate this block from surrounding log output
|
||||
addLogEntry();
|
||||
}
|
||||
|
||||
// Display the POSIX Error Message
|
||||
|
|
@ -1911,6 +2018,8 @@ bool isBadCurlVersion(string curlVersion) {
|
|||
|
||||
// Set the timestamp of the provided path to ensure this is done in a consistent manner
|
||||
void setLocalPathTimestamp(bool dryRun, string inputPath, SysTime newTimeStamp) {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
SysTime updatedModificationTime;
|
||||
bool makeTimestampChange = false;
|
||||
|
|
@ -1980,7 +2089,7 @@ void setLocalPathTimestamp(bool dryRun, string inputPath, SysTime newTimeStamp)
|
|||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, inputPath);
|
||||
}
|
||||
|
||||
SysTime newAccessTime;
|
||||
|
|
@ -1995,7 +2104,7 @@ void setLocalPathTimestamp(bool dryRun, string inputPath, SysTime newTimeStamp)
|
|||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, inputPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue