From a978be0193a392f359eada3c07f0ffc1e5d8f064 Mon Sep 17 00:00:00 2001 From: "brendan%mozilla.org" Date: Wed, 1 May 2002 22:50:20 +0000 Subject: [PATCH] Avoid opening and trying to reopen in CopyTo, so we work on FAT32 filesystems mounted on Linux (e.g.; 134163, r=dougt, sr=waterson, a=asa). git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_0_BRANCH@120472 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/xpcom/io/nsLocalFileUnix.cpp | 211 +++++++++++++-------------- mozilla/xpcom/io/nsLocalFileUnix.h | 6 +- 2 files changed, 110 insertions(+), 107 deletions(-) diff --git a/mozilla/xpcom/io/nsLocalFileUnix.cpp b/mozilla/xpcom/io/nsLocalFileUnix.cpp index 6dcd8770c59..636d2fcfc3d 100644 --- a/mozilla/xpcom/io/nsLocalFileUnix.cpp +++ b/mozilla/xpcom/io/nsLocalFileUnix.cpp @@ -29,13 +29,10 @@ */ /** - * Implementation of nsIFile for ``Unixy'' systems. + * Implementation of nsIFile for ``Unixy'' systems. */ -/** - * we're going to need some autoconf loving, - * I can just tell - */ +// We're going to need some autoconf loving, I can just tell. #include #include #include @@ -286,17 +283,17 @@ nsLocalFile::CreateAllAncestors(PRUint32 permissions) #endif while ((slashp = strchr(slashp + 1, '/'))) { - /** - * Sequences of '/' are equivalent to a single '/'. + /* + * Sequences of '/' are equivalent to a single '/'. */ if (slashp[1] == '/') continue; - /** - * If the path has a trailing slash, don't make the last component here, - * because we'll get EEXISTS in Create when we try to build the final - * component again, and it's easier to condition the logic here than - * there. + /* + * If the path has a trailing slash, don't make the last component, + * because we'll get EEXIST in Create when we try to build the final + * component again, and it's easier to condition the logic here than + * there. */ if (slashp[1] == '\0') break; @@ -311,11 +308,11 @@ nsLocalFile::CreateAllAncestors(PRUint32 permissions) /* Put the / back before we (maybe) return */ *slashp = '/'; - /** - * We could get EEXISTS for an existing file -- not directory -- - * with the name of one of our ancestors, but that's OK: we'll get - * ENOTDIR when we try to make the next component in the path, - * either here on back in Create, and error out appropriately. + /* + * We could get EEXIST for an existing file -- not directory -- + * with the name of one of our ancestors, but that's OK: we'll get + * ENOTDIR when we try to make the next component in the path, + * either here on back in Create, and error out appropriately. */ if (result == -1 && errno != EEXIST) return NSRESULT_FOR_ERRNO(); @@ -352,40 +349,42 @@ nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval) return NS_OK; } -static int exclusive_create(const char *path, mode_t mode) +static int +do_create(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval) { - int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode); - if (fd >= 0) - close(fd); - return fd; + *_retval = PR_Open(path, flags, mode); + return *_retval ? 0 : -1; } -static int exclusive_mkdir(const char *path, mode_t mode) +static int +do_mkdir(const char *path, PRIntn flags, mode_t mode, PRFileDesc **_retval) { + *_retval = nsnull; return mkdir(path, mode); } -NS_IMETHODIMP -nsLocalFile::Create(PRUint32 type, PRUint32 permissions) +nsresult +nsLocalFile::CreateAndKeepOpen(PRUint32 type, PRIntn flags, + PRUint32 permissions, PRFileDesc **_retval) { CHECK_mPath(); if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE) return NS_ERROR_FILE_UNKNOWN_TYPE; int result; - int (*exclusiveCreateFunc)(const char *, mode_t) = - (type == NORMAL_FILE_TYPE) ? exclusive_create : exclusive_mkdir; + int (*createFunc)(const char *, PRIntn, mode_t, PRFileDesc **) = + (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir; - result = exclusiveCreateFunc(mPath.get(), permissions); + result = createFunc(mPath.get(), flags, permissions, _retval); if (result == -1 && errno == ENOENT) { - /** - * If we failed because of missing ancestor components, try to create - * them and then retry the original creation. + /* + * If we failed because of missing ancestor components, try to create + * them and then retry the original creation. * - * Ancestor directories get the same permissions as the file we're - * creating, with the X bit set for each of (user,group,other) with - * an R bit in the original permissions. If you want to do anything - * fancy like setgid or sticky bits, do it by hand. + * Ancestor directories get the same permissions as the file we're + * creating, with the X bit set for each of (user,group,other) with + * an R bit in the original permissions. If you want to do anything + * fancy like setgid or sticky bits, do it by hand. */ int dirperm = permissions; if (permissions & S_IRUSR) @@ -406,12 +405,26 @@ nsLocalFile::Create(PRUint32 type, PRUint32 permissions) #ifdef DEBUG_NSIFILE fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get()); #endif - result = exclusiveCreateFunc(mPath.get(), permissions); + result = createFunc(mPath.get(), flags, permissions, _retval); } return NSRESULT_FOR_RETURN(result); } +NS_IMETHODIMP +nsLocalFile::Create(PRUint32 type, PRUint32 permissions) +{ + PRFileDesc *junk = nsnull; + nsresult rv = CreateAndKeepOpen(type, + PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE | + PR_EXCL, + permissions, + &junk); + if (junk) + PR_Close(junk); + return rv; +} + NS_IMETHODIMP nsLocalFile::Append(const char *fragment) { @@ -602,9 +615,9 @@ nsresult nsLocalFile::CopyDirectoryTo(nsIFile *newParent) { nsresult rv; - /** - * dirCheck is used for various bool - * tests like Equals, Exists, isDir etc + /* + * dirCheck is used for various boolean test results such as from Equals, + * Exists, isDir, etc. */ PRBool dirCheck, isSymlink; PRUint32 oldPerms; @@ -732,66 +745,52 @@ nsLocalFile::CopyTo(nsIFile *newParent, const char *newName) #endif // actually create the file. - nsLocalFile *newFile = new nsLocalFile(); - if (!newFile) { + nsCOMPtr newFile = new nsLocalFile(); + if (!newFile) return NS_ERROR_OUT_OF_MEMORY; - } - NS_ADDREF(newFile); rv = newFile->InitWithPath(newPathName); - if (NS_FAILED(rv)) { - NS_RELEASE(newFile); + if (NS_FAILED(rv)) return rv; - } // get the old permissions PRUint32 myPerms; GetPermissions(&myPerms); - // create the new file with user rwx permissions - rv = newFile->Create(NORMAL_FILE_TYPE, S_IRWXU); - if (NS_FAILED(rv)) { - NS_RELEASE(newFile); - return rv; - } + // Create the new file with the old file's permissions, even if write + // permission is missing. We can't create with write permission and + // then change back to myPerm on all filesystems (FAT on Linux, e.g.). + // But we can write to a read-only file on all Unix filesystems if we + // open it successfully for writing. - // open the new file. - PRInt32 openFlags = PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE; - PRInt32 modeFlags = myPerms; PRFileDesc *newFD; - - rv = newFile->OpenNSPRFileDesc(openFlags, S_IRWXU, &newFD); - if (NS_FAILED(rv)) { - NS_RELEASE(newFile); - return rv; - } - - // set the permissions of newFile to original files - if (NS_FAILED(rv = newFile->SetPermissions(myPerms))) + rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE, + PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, + myPerms, + &newFD); + if (NS_FAILED(rv)) return rv; // open the old file, too - openFlags = PR_RDONLY; - PRFileDesc *oldFD; - PRBool specialFile; - if (NS_FAILED(rv = IsSpecial(&specialFile))) + if (NS_FAILED(rv = IsSpecial(&specialFile))) { + PR_Close(newFD); return rv; + } if (specialFile) { #ifdef DEBUG printf("Operation not supported: %s\n", mPath.get()); #endif // make sure to clean up properly PR_Close(newFD); - NS_RELEASE(newFile); return NS_OK; } - rv = OpenNSPRFileDesc(openFlags, modeFlags, &oldFD); + PRFileDesc *oldFD; + rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD); if (NS_FAILED(rv)) { // make sure to clean up properly PR_Close(newFD); - NS_RELEASE(newFile); return rv; } @@ -829,9 +828,6 @@ nsLocalFile::CopyTo(nsIFile *newParent, const char *newName) PR_Close(newFD); PR_Close(oldFD); - // free our resources - NS_RELEASE(newFile); - // check for read (or write) error after cleaning up if (bytesRead < 0) return NS_ERROR_OUT_OF_MEMORY; @@ -988,19 +984,18 @@ nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModTimeOfLink) return NS_OK; } -/** - * utime(2) may or may not dereference symlinks, joy. +/* + * utime(2) may or may not dereference symlinks, joy. */ - NS_IMETHODIMP nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModTimeOfLink) { return SetLastModifiedTime(aLastModTimeOfLink); } -/** - * only send back permissions bits: maybe we want to send back the whole - * mode_t to permit checks against other file types? +/* + * Only send back permissions bits: maybe we want to send back the whole + * mode_t to permit checks against other file types? */ #define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO)) @@ -1033,7 +1028,14 @@ nsLocalFile::SetPermissions(PRUint32 aPermissions) CHECK_mPath(); InvalidateCache(); - return NSRESULT_FOR_RETURN(chmod(mPath.get(), aPermissions)); + + /* + * Race condition here: we should use fchmod instead, there's no way to + * guarantee the name still refers to the same file. + */ + if (chmod(mPath.get(), aPermissions) < 0) + return NSRESULT_FOR_ERRNO(); + return NS_OK; } NS_IMETHODIMP @@ -1106,17 +1108,17 @@ nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable) struct STATFS fs_buf; - /** - * Members of the STATFS struct that you should know about: - * f_bsize = block size on disk. - * f_bavail = number of free blocks available to a non-superuser. - * f_bfree = number of total free blocks in file system. + /* + * Members of the STATFS struct that you should know about: + * f_bsize = block size on disk. + * f_bavail = number of free blocks available to a non-superuser. + * f_bfree = number of total free blocks in file system. */ if (STATFS(mPath.get(), &fs_buf) < 0) { // The call to STATFS failed. #ifdef DEBUG - printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n"); + printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n"); #endif return NS_ERROR_FAILURE; } @@ -1125,28 +1127,26 @@ nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable) fs_buf.f_bsize * (fs_buf.f_bavail - 1)); #endif - /** - * The number of Bytes free = The number of free blocks available to - * a non-superuser, minus one as a fudge factor, multiplied by the size - * of the beforementioned blocks. + /* + * The number of bytes free == The number of free blocks available to + * a non-superuser, minus one as a fudge factor, multiplied by the size + * of the aforementioned blocks. */ + PRInt64 bsize, bavail; - PRInt64 bsize,bavail; - LL_I2L( bsize, fs_buf.f_bsize ); - LL_I2L( bavail, fs_buf.f_bavail - 1 ); - LL_MUL( *aDiskSpaceAvailable, bsize, bavail ); + LL_I2L(bsize, fs_buf.f_bsize); + LL_I2L(bavail, fs_buf.f_bavail - 1); + LL_MUL(*aDiskSpaceAvailable, bsize, bavail); return NS_OK; #else - /** - * This platform doesn't have statfs or statvfs. - * I'm sure that there's a way to check for free disk space - * on platforms that don't have statfs - * (I'm SURE they have df, for example). + /* + * This platform doesn't have statfs or statvfs. I'm sure that there's + * a way to check for free disk space on platforms that don't have statfs + * (I'm SURE they have df, for example). * - * Until we figure out how to do that, lets be honest - * and say that this command isn't implemented - * properly for these platforms yet. + * Until we figure out how to do that, lets be honest and say that this + * command isn't implemented properly for these platforms yet. */ #ifdef DEBUG printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n"); @@ -1202,9 +1202,8 @@ nsLocalFile::GetParent(nsIFile **aParent) return rv; } -/** - * The results of Exists, isWritable and isReadable - * are not cached. +/* + * The results of Exists, isWritable and isReadable are not cached. */ NS_IMETHODIMP diff --git a/mozilla/xpcom/io/nsLocalFileUnix.h b/mozilla/xpcom/io/nsLocalFileUnix.h index c5de6cfdc3a..f1711238aaa 100644 --- a/mozilla/xpcom/io/nsLocalFileUnix.h +++ b/mozilla/xpcom/io/nsLocalFileUnix.h @@ -73,7 +73,7 @@ inline nsresult nsresultForErrno(int err) { - switch(err) { + switch (err) { case 0: return NS_OK; case ENOENT: @@ -86,7 +86,9 @@ nsresultForErrno(int err) #endif /* ENOLINK */ case EEXIST: return NS_ERROR_FILE_ALREADY_EXISTS; + case EPERM: case EACCES: + return NS_ERROR_FILE_ACCESS_DENIED; default: return NS_ERROR_FAILURE; } @@ -127,6 +129,8 @@ protected: void InvalidateCache() { mHaveCachedStat = PR_FALSE; } nsresult FillStatCache(); + nsresult CreateAndKeepOpen(PRUint32 type, PRIntn flags, + PRUint32 permissions, PRFileDesc **_retval); }; #endif /* _nsLocalFileUNIX_H_ */