From 2228a4ce174bfb39e9f74d9a396fb49de732c0f0 Mon Sep 17 00:00:00 2001 From: Kathy Brade Date: Tue, 24 Feb 2015 13:50:23 -0500 Subject: [PATCH] Bug 14631: Improve profile access error messages. Instead of always reporting that the profile is locked, display specific messages for "access denied" and "read-only file system". --- .../profile/profileSelection.properties | 5 + toolkit/xre/nsAppRunner.cpp | 133 +++++++++++++++--- 2 files changed, 117 insertions(+), 21 deletions(-) diff --git a/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties b/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties index e36353a935c67..ff7bc3b15cb95 100644 --- a/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties +++ b/toolkit/locales/en-US/chrome/mozapps/profile/profileSelection.properties @@ -12,6 +12,11 @@ restartMessageUnlocker=%S is already running, but is not responding. The old %S restartMessageNoUnlockerMac=A copy of %S is already open. Only one copy of %S can be open at a time. restartMessageUnlockerMac=A copy of %S is already open. The running copy of %S will quit in order to open this one. +# LOCALIZATION NOTE (profileProblemTitle, profileReadOnly, profileReadOnlyMac, profileAccessDenied): Messages displayed when the browser profile cannot be accessed or written to. %S is the application name. +profileProblemTitle=%S Profile Problem +profileReadOnly=You cannot run %S from a read-only file system. Please copy %S to another location before trying to use it. +profileReadOnlyMac=You cannot run %S from a read-only file system. Please copy %S to your Desktop or Applications folder before trying to use it. +profileAccessDenied=%S does not have permission to access the profile. Please adjust your file system permissions and try again. # Profile manager # LOCALIZATION NOTE (profileTooltip): First %S is the profile name, second %S is the path to the profile folder. profileTooltip=Profile: ā€˜%Sā€™ - Path: ā€˜%Sā€™ diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 4979e16523510..f6ae5376453dc 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -1826,6 +1826,14 @@ static nsresult LaunchChild(nsINativeAppSupport* aNative, return NS_ERROR_LAUNCHED_CHILD_PROCESS; } +enum ProfileStatus { + PROFILE_STATUS_OK, + PROFILE_STATUS_ACCESS_DENIED, + PROFILE_STATUS_READ_ONLY, + PROFILE_STATUS_IS_LOCKED, + PROFILE_STATUS_OTHER_ERROR +}; + static const char kProfileProperties[] = "chrome://mozapps/locale/profile/profileSelection.properties"; @@ -1864,9 +1872,9 @@ private: } // namespace static ReturnAbortOnError -ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, - nsIProfileUnlocker* aUnlocker, - nsINativeAppSupport* aNative, nsIProfileLock* *aResult) +ProfileErrorDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, + ProfileStatus aStatus, nsIProfileUnlocker* aUnlocker, + nsINativeAppSupport* aNative, nsIProfileLock* *aResult) { nsresult rv; @@ -1893,18 +1901,31 @@ ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, nsXPIDLString killMessage; #ifndef XP_MACOSX - sb->FormatStringFromName(aUnlocker ? u"restartMessageUnlocker" - : u"restartMessageNoUnlocker", - params, 2, getter_Copies(killMessage)); + static const char16_t kRestartUnlocker[] = MOZ_UTF16("restartMessageUnlocker"); + static const char16_t kRestartNoUnlocker[] = MOZ_UTF16("restartMessageNoUnlocker"); + static const char16_t kReadOnly[] = MOZ_UTF16("profileReadOnly"); #else - sb->FormatStringFromName(aUnlocker ? u"restartMessageUnlockerMac" - : u"restartMessageNoUnlockerMac", - params, 2, getter_Copies(killMessage)); + static const char16_t kRestartUnlocker[] = MOZ_UTF16("restartMessageUnlockerMac"); + static const char16_t kRestartNoUnlocker[] = MOZ_UTF16("restartMessageNoUnlockerMac"); + static const char16_t kReadOnly[] = MOZ_UTF16("profileReadOnlyMac"); #endif + static const char16_t kAccessDenied[] = MOZ_UTF16("profileAccessDenied"); + + const char16_t *errorKey = aUnlocker ? kRestartUnlocker + : kRestartNoUnlocker; + if (PROFILE_STATUS_READ_ONLY == aStatus) + errorKey = kReadOnly; + else if (PROFILE_STATUS_ACCESS_DENIED == aStatus) + errorKey = kAccessDenied; + sb->FormatStringFromName(errorKey, params, 2, getter_Copies(killMessage)); - nsXPIDLString killTitle; - sb->FormatStringFromName(u"restartTitle", - params, 1, getter_Copies(killTitle)); + const char16_t *titleKey = ((PROFILE_STATUS_READ_ONLY == aStatus) || + (PROFILE_STATUS_ACCESS_DENIED == aStatus)) + ? MOZ_UTF16("profileProblemTitle") + : MOZ_UTF16("restartTitle"); + + nsXPIDLString killTitle; + sb->FormatStringFromName(titleKey, params, 1, getter_Copies(killTitle)); if (!killMessage || !killTitle) return NS_ERROR_FAILURE; @@ -2005,8 +2026,9 @@ ProfileMissingDialog(nsINativeAppSupport* aNative) } static nsresult -ProfileLockedDialog(nsIToolkitProfile* aProfile, nsIProfileUnlocker* aUnlocker, - nsINativeAppSupport* aNative, nsIProfileLock* *aResult) +ProfileErrorDialog(nsIToolkitProfile* aProfile, ProfileStatus aStatus, + nsIProfileUnlocker* aUnlocker, nsINativeAppSupport* aNative, + nsIProfileLock* *aResult) { nsCOMPtr profileDir; nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir)); @@ -2022,8 +2044,8 @@ ProfileLockedDialog(nsIToolkitProfile* aProfile, nsIProfileUnlocker* aUnlocker, rv = aProfile->GetLocalDir(getter_AddRefs(profileLocalDir)); if (NS_FAILED(rv)) return rv; - return ProfileLockedDialog(profileDir, profileLocalDir, aUnlocker, aNative, - aResult); + return ProfileErrorDialog(profileDir, profileLocalDir, aStatus, aUnlocker, + aNative, aResult); } static const char kProfileManagerURL[] = @@ -2163,6 +2185,53 @@ SetCurrentProfileAsDefault(nsIToolkitProfileService* aProfileSvc, return rv; } +// Check for write permission to the profile directory by trying to create a +// new file (after ensuring that no file with the same name exists). +static ProfileStatus CheckProfileWriteAccess(nsIFile* aProfileDir) +{ +#if defined(XP_UNIX) + NS_NAMED_LITERAL_STRING(writeTestFileName, ".parentwritetest"); +#else + NS_NAMED_LITERAL_STRING(writeTestFileName, "parent.writetest"); +#endif + + nsCOMPtr writeTestFile; + nsresult rv = aProfileDir->Clone(getter_AddRefs(writeTestFile)); + if (NS_SUCCEEDED(rv)) + rv = writeTestFile->Append(writeTestFileName); + + if (NS_SUCCEEDED(rv)) { + bool doesExist = false; + rv = writeTestFile->Exists(&doesExist); + if (NS_SUCCEEDED(rv) && doesExist) + rv = writeTestFile->Remove(true); + } + + if (NS_SUCCEEDED(rv)) { + rv = writeTestFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666); + (void)writeTestFile->Remove(true); + } + + ProfileStatus status = NS_SUCCEEDED(rv) ? PROFILE_STATUS_OK + : PROFILE_STATUS_OTHER_ERROR; + if (NS_ERROR_FILE_ACCESS_DENIED == rv) + status = PROFILE_STATUS_ACCESS_DENIED; + else if (NS_ERROR_FILE_READ_ONLY == rv) + status = PROFILE_STATUS_READ_ONLY; + + return status; +} + +static ProfileStatus CheckProfileWriteAccess(nsIToolkitProfile* aProfile) +{ + nsCOMPtr profileDir; + nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir)); + if (NS_FAILED(rv)) + return PROFILE_STATUS_OTHER_ERROR; + + return CheckProfileWriteAccess(profileDir); +} + static bool gDoMigration = false; static bool gDoProfileReset = false; static nsAutoCString gResetOldProfileName; @@ -2278,6 +2347,15 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n gDoProfileReset = false; } + nsCOMPtr profile; + rv = aProfileSvc->GetProfileByName(nsDependentCString(arg), + getter_AddRefs(profile)); + if (NS_SUCCEEDED(rv)) { + ProfileStatus status = CheckProfileWriteAccess(profile); + if (PROFILE_STATUS_OK != status) + return ProfileErrorDialog(profile, status, nullptr, aNative, aResult); + } + nsCOMPtr lf; rv = XRE_GetFileFromPath(arg, getter_AddRefs(lf)); NS_ENSURE_SUCCESS(rv, rv); @@ -2292,13 +2370,18 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n NS_ENSURE_SUCCESS(rv, rv); } + ProfileStatus status = CheckProfileWriteAccess(lf); + if (PROFILE_STATUS_OK != status) + return ProfileErrorDialog(lf, lf, status, nullptr, aNative, aResult); + // If a profile path is specified directory on the command line, then // assume that the temp directory is the same as the given directory. rv = NS_LockProfilePath(lf, lf, getter_AddRefs(unlocker), aResult); if (NS_SUCCEEDED(rv)) return rv; - return ProfileLockedDialog(lf, lf, unlocker, aNative, aResult); + return ProfileErrorDialog(lf, lf, PROFILE_STATUS_IS_LOCKED, unlocker, + aNative, aResult); } ar = CheckArg("createprofile", true, &arg); @@ -2387,7 +2470,8 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n nsCOMPtr unlocker; rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock); if (NS_FAILED(rv)) - return ProfileLockedDialog(profile, unlocker, aNative, &tempProfileLock); + return ProfileErrorDialog(profile, PROFILE_STATUS_IS_LOCKED, + unlocker, aNative, &tempProfileLock); } nsCOMPtr newProfile; @@ -2415,7 +2499,8 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n return NS_OK; } - return ProfileLockedDialog(profile, unlocker, aNative, aResult); + return ProfileErrorDialog(profile, PROFILE_STATUS_IS_LOCKED, unlocker, + aNative, aResult); } if (CanShowProfileManager()) { @@ -2495,7 +2580,8 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n nsCOMPtr unlocker; rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock); if (NS_FAILED(rv)) - return ProfileLockedDialog(profile, unlocker, aNative, &tempProfileLock); + return ProfileErrorDialog(profile, PROFILE_STATUS_IS_LOCKED, + unlocker, aNative, &tempProfileLock); } nsCOMPtr newProfile; @@ -2515,6 +2601,10 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n } } + ProfileStatus status = CheckProfileWriteAccess(profile); + if (PROFILE_STATUS_OK != status) + return ProfileErrorDialog(profile, status, nullptr, aNative, aResult); + // If you close Firefox and very quickly reopen it, the old Firefox may // still be closing down. Rather than immediately showing the // "Firefox is running but is not responding" message, we spend a few @@ -2540,7 +2630,8 @@ SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, n PR_Sleep(kLockRetrySleepMS); } while (TimeStamp::Now() - start < TimeDuration::FromSeconds(kLockRetrySeconds)); - return ProfileLockedDialog(profile, unlocker, aNative, aResult); + return ProfileErrorDialog(profile, PROFILE_STATUS_IS_LOCKED, unlocker, + aNative, aResult); } } -- GitLab