xpcom/io/nsLocalFileUnix.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /**
     7  * Implementation of nsIFile for "unixy" systems.
     8  */
    10 #include "mozilla/ArrayUtils.h"
    11 #include "mozilla/Attributes.h"
    13 #include <sys/types.h>
    14 #include <sys/stat.h>
    15 #include <unistd.h>
    16 #include <fcntl.h>
    17 #include <errno.h>
    18 #include <utime.h>
    19 #include <dirent.h>
    20 #include <ctype.h>
    21 #include <locale.h>
    22 #if defined(VMS)
    23     #include <fabdef.h>
    24 #endif
    26 #if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H)
    27 #define USE_LINUX_QUOTACTL
    28 #include <sys/quota.h>
    29 #endif
    31 #include "xpcom-private.h"
    32 #include "nsDirectoryServiceDefs.h"
    33 #include "nsCRT.h"
    34 #include "nsCOMPtr.h"
    35 #include "nsMemory.h"
    36 #include "nsIFile.h"
    37 #include "nsString.h"
    38 #include "nsReadableUtils.h"
    39 #include "nsLocalFile.h"
    40 #include "nsIComponentManager.h"
    41 #include "nsXPIDLString.h"
    42 #include "prproces.h"
    43 #include "nsIDirectoryEnumerator.h"
    44 #include "nsISimpleEnumerator.h"
    45 #include "private/pprio.h"
    46 #include "prlink.h"
    48 #ifdef MOZ_WIDGET_GTK
    49 #include "nsIGIOService.h"
    50 #include "nsIGnomeVFSService.h"
    51 #endif
    53 #ifdef MOZ_WIDGET_COCOA
    54 #include <Carbon/Carbon.h>
    55 #include "CocoaFileUtils.h"
    56 #include "prmem.h"
    57 #include "plbase64.h"
    59 static nsresult MacErrorMapper(OSErr inErr);
    60 #endif
    62 #ifdef MOZ_WIDGET_ANDROID
    63 #include "AndroidBridge.h"
    64 #include "nsIMIMEService.h"
    65 #include <linux/magic.h>
    66 #endif
    68 #ifdef MOZ_ENABLE_CONTENTACTION
    69 #include <contentaction/contentaction.h>
    70 #endif
    72 #include "nsNativeCharsetUtils.h"
    73 #include "nsTraceRefcnt.h"
    74 #include "nsHashKeys.h"
    76 using namespace mozilla;
    78 #define ENSURE_STAT_CACHE()                     \
    79     PR_BEGIN_MACRO                              \
    80         if (!FillStatCache())                   \
    81              return NSRESULT_FOR_ERRNO();       \
    82     PR_END_MACRO
    84 #define CHECK_mPath()                           \
    85     PR_BEGIN_MACRO                              \
    86         if (mPath.IsEmpty())                    \
    87             return NS_ERROR_NOT_INITIALIZED;    \
    88     PR_END_MACRO
    90 /* directory enumerator */
    91 class
    92 nsDirEnumeratorUnix MOZ_FINAL : public nsISimpleEnumerator,
    93                                 public nsIDirectoryEnumerator
    94 {
    95     public:
    96     nsDirEnumeratorUnix();
    98     // nsISupports interface
    99     NS_DECL_ISUPPORTS
   101     // nsISimpleEnumerator interface
   102     NS_DECL_NSISIMPLEENUMERATOR
   104     // nsIDirectoryEnumerator interface
   105     NS_DECL_NSIDIRECTORYENUMERATOR
   107     NS_IMETHOD Init(nsLocalFile *parent, bool ignored);
   109     private:
   110     ~nsDirEnumeratorUnix();
   112     protected:
   113     NS_IMETHOD GetNextEntry();
   115     DIR           *mDir;
   116     struct dirent *mEntry;
   117     nsCString      mParentPath;
   118 };
   120 nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
   121                          mDir(nullptr), 
   122                          mEntry(nullptr)
   123 {
   124 }
   126 nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
   127 {
   128     Close();
   129 }
   131 NS_IMPL_ISUPPORTS(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator)
   133 NS_IMETHODIMP
   134 nsDirEnumeratorUnix::Init(nsLocalFile *parent, bool resolveSymlinks /*ignored*/)
   135 {
   136     nsAutoCString dirPath;
   137     if (NS_FAILED(parent->GetNativePath(dirPath)) ||
   138         dirPath.IsEmpty()) {
   139         return NS_ERROR_FILE_INVALID_PATH;
   140     }
   142     if (NS_FAILED(parent->GetNativePath(mParentPath)))
   143         return NS_ERROR_FAILURE;
   145     mDir = opendir(dirPath.get());
   146     if (!mDir)
   147         return NSRESULT_FOR_ERRNO();
   148     return GetNextEntry();
   149 }
   151 NS_IMETHODIMP
   152 nsDirEnumeratorUnix::HasMoreElements(bool *result)
   153 {
   154     *result = mDir && mEntry;
   155     if (!*result)
   156         Close();
   157     return NS_OK;
   158 }
   160 NS_IMETHODIMP
   161 nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
   162 {
   163     nsCOMPtr<nsIFile> file;
   164     nsresult rv = GetNextFile(getter_AddRefs(file));
   165     if (NS_FAILED(rv))
   166         return rv;
   167     NS_IF_ADDREF(*_retval = file);
   168     return NS_OK;
   169 }
   171 NS_IMETHODIMP
   172 nsDirEnumeratorUnix::GetNextEntry()
   173 {
   174     do {
   175         errno = 0;
   176         mEntry = readdir(mDir);
   178         // end of dir or error
   179         if (!mEntry)
   180             return NSRESULT_FOR_ERRNO();
   182         // keep going past "." and ".."
   183     } while (mEntry->d_name[0] == '.'     &&
   184             (mEntry->d_name[1] == '\0'    ||   // .\0
   185             (mEntry->d_name[1] == '.'     &&
   186             mEntry->d_name[2] == '\0')));      // ..\0
   187     return NS_OK;
   188 }
   190 NS_IMETHODIMP
   191 nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval)
   192 {
   193     nsresult rv;
   194     if (!mDir || !mEntry) {
   195         *_retval = nullptr;
   196         return NS_OK;
   197     }
   199     nsCOMPtr<nsIFile> file = new nsLocalFile();
   201     if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
   202         NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name))))
   203         return rv;
   205     file.forget(_retval);
   206     return GetNextEntry();
   207 }
   209 NS_IMETHODIMP 
   210 nsDirEnumeratorUnix::Close()
   211 {
   212     if (mDir) {
   213         closedir(mDir);
   214         mDir = nullptr;
   215     }
   216     return NS_OK;
   217 }
   219 nsLocalFile::nsLocalFile()
   220 {
   221 }
   223 nsLocalFile::nsLocalFile(const nsLocalFile& other)
   224   : mPath(other.mPath)
   225 {
   226 }
   228 #ifdef MOZ_WIDGET_COCOA
   229 NS_IMPL_ISUPPORTS(nsLocalFile,
   230                   nsILocalFileMac,
   231                   nsILocalFile,
   232                   nsIFile,
   233                   nsIHashable)
   234 #else
   235 NS_IMPL_ISUPPORTS(nsLocalFile,
   236                   nsILocalFile,
   237                   nsIFile,
   238                   nsIHashable)
   239 #endif
   241 nsresult
   242 nsLocalFile::nsLocalFileConstructor(nsISupports *outer, 
   243                                     const nsIID &aIID,
   244                                     void **aInstancePtr)
   245 {
   246     if (NS_WARN_IF(!aInstancePtr))
   247         return NS_ERROR_INVALID_ARG;
   248     if (NS_WARN_IF(outer))
   249         return NS_ERROR_NO_AGGREGATION;
   251     *aInstancePtr = nullptr;
   253     nsCOMPtr<nsIFile> inst = new nsLocalFile();
   254     return inst->QueryInterface(aIID, aInstancePtr);
   255 }
   257 bool 
   258 nsLocalFile::FillStatCache() {
   259     if (STAT(mPath.get(), &mCachedStat) == -1) {
   260         // try lstat it may be a symlink
   261         if (LSTAT(mPath.get(), &mCachedStat) == -1) {
   262             return false;
   263         }
   264     }
   265     return true;
   266 }
   268 NS_IMETHODIMP
   269 nsLocalFile::Clone(nsIFile **file)
   270 {
   271     // Just copy-construct ourselves
   272     nsRefPtr<nsLocalFile> copy = new nsLocalFile(*this);
   273     copy.forget(file);
   274     return NS_OK;
   275 }
   277 NS_IMETHODIMP
   278 nsLocalFile::InitWithNativePath(const nsACString &filePath)
   279 {
   280     if (filePath.Equals("~") || Substring(filePath, 0, 2).EqualsLiteral("~/")) {
   281         nsCOMPtr<nsIFile> homeDir;
   282         nsAutoCString homePath;
   283         if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
   284                                              getter_AddRefs(homeDir))) ||
   285             NS_FAILED(homeDir->GetNativePath(homePath))) {
   286             return NS_ERROR_FAILURE;
   287         }
   289         mPath = homePath;
   290         if (filePath.Length() > 2)
   291           mPath.Append(Substring(filePath, 1, filePath.Length() - 1));
   292     } else {
   293         if (filePath.IsEmpty() || filePath.First() != '/')
   294             return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   295         mPath = filePath;
   296     }
   298     // trim off trailing slashes
   299     ssize_t len = mPath.Length();
   300     while ((len > 1) && (mPath[len - 1] == '/'))
   301         --len;
   302     mPath.SetLength(len);
   304     return NS_OK;
   305 }
   307 NS_IMETHODIMP
   308 nsLocalFile::CreateAllAncestors(uint32_t permissions)
   309 {
   310     // <jband> I promise to play nice
   311     char *buffer = mPath.BeginWriting(),
   312          *slashp = buffer;
   314 #ifdef DEBUG_NSIFILE
   315     fprintf(stderr, "nsIFile: before: %s\n", buffer);
   316 #endif
   318     while ((slashp = strchr(slashp + 1, '/'))) {
   319         /*
   320          * Sequences of '/' are equivalent to a single '/'.
   321          */
   322         if (slashp[1] == '/')
   323             continue;
   325         /*
   326          * If the path has a trailing slash, don't make the last component,
   327          * because we'll get EEXIST in Create when we try to build the final
   328          * component again, and it's easier to condition the logic here than
   329          * there.
   330          */
   331         if (slashp[1] == '\0')
   332             break;
   334         /* Temporarily NUL-terminate here */
   335         *slashp = '\0';
   336 #ifdef DEBUG_NSIFILE
   337         fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
   338 #endif
   339         int mkdir_result = mkdir(buffer, permissions);
   340         int mkdir_errno  = errno;
   341         if (mkdir_result == -1) {
   342             /*
   343              * Always set |errno| to EEXIST if the dir already exists
   344              * (we have to do this here since the errno value is not consistent
   345              * in all cases - various reasons like different platform,
   346              * automounter-controlled dir, etc. can affect it (see bug 125489
   347              * for details)).
   348              */
   349             if (access(buffer, F_OK) == 0) {
   350                 mkdir_errno = EEXIST;
   351             }
   352         }
   354         /* Put the / back before we (maybe) return */
   355         *slashp = '/';
   357         /*
   358          * We could get EEXIST for an existing file -- not directory --
   359          * with the name of one of our ancestors, but that's OK: we'll get
   360          * ENOTDIR when we try to make the next component in the path,
   361          * either here on back in Create, and error out appropriately.
   362          */
   363         if (mkdir_result == -1 && mkdir_errno != EEXIST)
   364             return nsresultForErrno(mkdir_errno);
   365     }
   367 #ifdef DEBUG_NSIFILE
   368     fprintf(stderr, "nsIFile: after: %s\n", buffer);
   369 #endif
   371     return NS_OK;
   372 }
   374 NS_IMETHODIMP
   375 nsLocalFile::OpenNSPRFileDesc(int32_t flags, int32_t mode, PRFileDesc **_retval)
   376 {
   377     *_retval = PR_Open(mPath.get(), flags, mode);
   378     if (! *_retval)
   379         return NS_ErrorAccordingToNSPR();
   381     if (flags & DELETE_ON_CLOSE) {
   382         PR_Delete(mPath.get());
   383     }
   385 #if defined(HAVE_POSIX_FADVISE)
   386     if (flags & OS_READAHEAD) {
   387         posix_fadvise(PR_FileDesc2NativeHandle(*_retval), 0, 0,
   388                       POSIX_FADV_SEQUENTIAL);
   389     }
   390 #endif
   391     return NS_OK;
   392 }
   394 NS_IMETHODIMP
   395 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
   396 {
   397     *_retval = fopen(mPath.get(), mode);
   398     if (! *_retval)
   399         return NS_ERROR_FAILURE;
   401     return NS_OK;
   402 }
   404 static int
   405 do_create(const char *path, int flags, mode_t mode, PRFileDesc **_retval)
   406 {
   407     *_retval = PR_Open(path, flags, mode);
   408     return *_retval ? 0 : -1;
   409 }
   411 static int
   412 do_mkdir(const char *path, int flags, mode_t mode, PRFileDesc **_retval)
   413 {
   414     *_retval = nullptr;
   415     return mkdir(path, mode);
   416 }
   418 nsresult
   419 nsLocalFile::CreateAndKeepOpen(uint32_t type, int flags,
   420                                uint32_t permissions, PRFileDesc **_retval)
   421 {
   422     if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
   423         return NS_ERROR_FILE_UNKNOWN_TYPE;
   425     int result;
   426     int (*createFunc)(const char *, int, mode_t, PRFileDesc **) =
   427         (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
   429     result = createFunc(mPath.get(), flags, permissions, _retval);
   430     if (result == -1 && errno == ENOENT) {
   431         /*
   432          * If we failed because of missing ancestor components, try to create
   433          * them and then retry the original creation.
   434          *
   435          * Ancestor directories get the same permissions as the file we're
   436          * creating, with the X bit set for each of (user,group,other) with
   437          * an R bit in the original permissions.    If you want to do anything
   438          * fancy like setgid or sticky bits, do it by hand.
   439          */
   440         int dirperm = permissions;
   441         if (permissions & S_IRUSR)
   442             dirperm |= S_IXUSR;
   443         if (permissions & S_IRGRP)
   444             dirperm |= S_IXGRP;
   445         if (permissions & S_IROTH)
   446             dirperm |= S_IXOTH;
   448 #ifdef DEBUG_NSIFILE
   449         fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
   450                 dirperm);
   451 #endif
   453         if (NS_FAILED(CreateAllAncestors(dirperm)))
   454             return NS_ERROR_FAILURE;
   456 #ifdef DEBUG_NSIFILE
   457         fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
   458 #endif
   459         result = createFunc(mPath.get(), flags, permissions, _retval);
   460     }
   461     return NSRESULT_FOR_RETURN(result);
   462 }
   464 NS_IMETHODIMP
   465 nsLocalFile::Create(uint32_t type, uint32_t permissions)
   466 {
   467     PRFileDesc *junk = nullptr;
   468     nsresult rv = CreateAndKeepOpen(type,
   469                                     PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
   470                                     PR_EXCL,
   471                                     permissions,
   472                                     &junk);
   473     if (junk)
   474         PR_Close(junk);
   475     return rv;
   476 }
   478 NS_IMETHODIMP
   479 nsLocalFile::AppendNative(const nsACString &fragment)
   480 {
   481     if (fragment.IsEmpty())
   482         return NS_OK;
   484     // only one component of path can be appended
   485     nsACString::const_iterator begin, end;
   486     if (FindCharInReadable('/', fragment.BeginReading(begin),
   487                                 fragment.EndReading(end)))
   488         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   490     return AppendRelativeNativePath(fragment);
   491 }
   493 NS_IMETHODIMP
   494 nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
   495 {
   496     if (fragment.IsEmpty())
   497         return NS_OK;
   499     // No leading '/' 
   500     if (fragment.First() == '/')
   501         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   503     if (mPath.EqualsLiteral("/"))
   504         mPath.Append(fragment);
   505     else
   506         mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
   508     return NS_OK;
   509 }
   511 NS_IMETHODIMP
   512 nsLocalFile::Normalize()
   513 {
   514     char    resolved_path[PATH_MAX] = "";
   515     char *resolved_path_ptr = nullptr;
   517     resolved_path_ptr = realpath(mPath.get(), resolved_path);
   519     // if there is an error, the return is null.
   520     if (!resolved_path_ptr)
   521         return NSRESULT_FOR_ERRNO();
   523     mPath = resolved_path;
   524     return NS_OK;
   525 }
   527 void
   528 nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin, 
   529                                   nsACString::const_iterator &end)
   530 {
   531     // XXX perhaps we should cache this??
   533     mPath.BeginReading(begin);
   534     mPath.EndReading(end);
   536     nsACString::const_iterator it = end;
   537     nsACString::const_iterator stop = begin;
   538     --stop;
   539     while (--it != stop) {
   540         if (*it == '/') {
   541             begin = ++it;
   542             return;
   543         }
   544     }
   545     // else, the entire path is the leaf name (which means this
   546     // isn't an absolute path... unexpected??)
   547 }
   549 NS_IMETHODIMP
   550 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
   551 {
   552     nsACString::const_iterator begin, end;
   553     LocateNativeLeafName(begin, end);
   554     aLeafName = Substring(begin, end);
   555     return NS_OK;
   556 }
   558 NS_IMETHODIMP
   559 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
   560 {
   561     nsACString::const_iterator begin, end;
   562     LocateNativeLeafName(begin, end);
   563     mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
   564     return NS_OK;
   565 }
   567 NS_IMETHODIMP
   568 nsLocalFile::GetNativePath(nsACString &_retval)
   569 {
   570     _retval = mPath;
   571     return NS_OK;
   572 }
   574 nsresult
   575 nsLocalFile::GetNativeTargetPathName(nsIFile *newParent, 
   576                                      const nsACString &newName,
   577                                      nsACString &_retval)
   578 {
   579     nsresult rv;
   580     nsCOMPtr<nsIFile> oldParent;
   582     if (!newParent) {
   583         if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
   584             return rv;
   585         newParent = oldParent.get();
   586     } else {
   587         // check to see if our target directory exists
   588         bool targetExists;
   589         if (NS_FAILED(rv = newParent->Exists(&targetExists)))
   590             return rv;
   592         if (!targetExists) {
   593             // XXX create the new directory with some permissions
   594             rv = newParent->Create(DIRECTORY_TYPE, 0755);
   595             if (NS_FAILED(rv))
   596                 return rv;
   597         } else {
   598             // make sure that the target is actually a directory
   599             bool targetIsDirectory;
   600             if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
   601                 return rv;
   602             if (!targetIsDirectory)
   603                 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
   604         }
   605     }
   607     nsACString::const_iterator nameBegin, nameEnd;
   608     if (!newName.IsEmpty()) {
   609         newName.BeginReading(nameBegin);
   610         newName.EndReading(nameEnd);
   611     }
   612     else
   613         LocateNativeLeafName(nameBegin, nameEnd);
   615     nsAutoCString dirName;
   616     if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
   617         return rv;
   619     _retval = dirName
   620             + NS_LITERAL_CSTRING("/")
   621             + Substring(nameBegin, nameEnd);
   622     return NS_OK;
   623 }
   625 nsresult
   626 nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
   627 {
   628     nsresult rv;
   629     /*
   630      * dirCheck is used for various boolean test results such as from Equals,
   631      * Exists, isDir, etc.
   632      */
   633     bool dirCheck, isSymlink;
   634     uint32_t oldPerms;
   636     if (NS_FAILED(rv = IsDirectory(&dirCheck)))
   637         return rv;
   638     if (!dirCheck)
   639         return CopyToNative(newParent, EmptyCString());
   641     if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
   642         return rv;
   643     if (dirCheck) { 
   644         // can't copy dir to itself
   645         return NS_ERROR_INVALID_ARG;
   646     }
   648     if (NS_FAILED(rv = newParent->Exists(&dirCheck))) 
   649         return rv;
   650     // get the dirs old permissions
   651     if (NS_FAILED(rv = GetPermissions(&oldPerms)))
   652         return rv;
   653     if (!dirCheck) {
   654         if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
   655             return rv;
   656     } else {    // dir exists lets try to use leaf
   657         nsAutoCString leafName;
   658         if (NS_FAILED(rv = GetNativeLeafName(leafName)))
   659             return rv;
   660         if (NS_FAILED(rv = newParent->AppendNative(leafName)))
   661             return rv;
   662         if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
   663             return rv;
   664         if (dirCheck) 
   665             return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
   666         if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
   667             return rv;
   668     }
   670     nsCOMPtr<nsISimpleEnumerator> dirIterator;
   671     if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
   672         return rv;
   674     bool hasMore = false;
   675     while (dirIterator->HasMoreElements(&hasMore), hasMore) {
   676         nsCOMPtr<nsISupports> supports;
   677         nsCOMPtr<nsIFile> entry;
   678         rv = dirIterator->GetNext(getter_AddRefs(supports));
   679         entry = do_QueryInterface(supports);
   680         if (NS_FAILED(rv) || !entry)
   681             continue;
   682         if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
   683             return rv;
   684         if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
   685             return rv;
   686         if (dirCheck && !isSymlink) {
   687             nsCOMPtr<nsIFile> destClone;
   688             rv = newParent->Clone(getter_AddRefs(destClone));
   689             if (NS_SUCCEEDED(rv)) {
   690                 if (NS_FAILED(rv = entry->CopyToNative(destClone, EmptyCString()))) {
   691 #ifdef DEBUG
   692                     nsresult rv2;
   693                     nsAutoCString pathName;
   694                     if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
   695                         return rv2;
   696                     printf("Operation not supported: %s\n", pathName.get());
   697 #endif
   698                     if (rv == NS_ERROR_OUT_OF_MEMORY) 
   699                         return rv;
   700                     continue;
   701                 }
   702             }
   703         } else {
   704             if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
   705 #ifdef DEBUG
   706                 nsresult rv2;
   707                 nsAutoCString pathName;
   708                 if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
   709                     return rv2;
   710                 printf("Operation not supported: %s\n", pathName.get());
   711 #endif
   712                 if (rv == NS_ERROR_OUT_OF_MEMORY) 
   713                     return rv;
   714                 continue;
   715             }
   716         }
   717     }
   718     return NS_OK;
   719 }
   721 NS_IMETHODIMP
   722 nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
   723 {
   724     nsresult rv;
   725     // check to make sure that this has been initialized properly
   726     CHECK_mPath();
   728     // we copy the parent here so 'newParent' remains immutable
   729     nsCOMPtr <nsIFile> workParent;
   730     if (newParent) {
   731         if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
   732             return rv;
   733     } else {
   734         if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
   735             return rv;
   736     }
   738     // check to see if we are a directory or if we are a file
   739     bool isDirectory;
   740     if (NS_FAILED(rv = IsDirectory(&isDirectory)))
   741         return rv;
   743     nsAutoCString newPathName;
   744     if (isDirectory) {
   745         if (!newName.IsEmpty()) {
   746             if (NS_FAILED(rv = workParent->AppendNative(newName)))
   747                 return rv;
   748         } else {
   749             if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
   750                 return rv;
   751             if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
   752                 return rv;
   753         }
   754         if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
   755             return rv;
   756     } else {
   757         rv = GetNativeTargetPathName(workParent, newName, newPathName);
   758         if (NS_FAILED(rv)) 
   759             return rv;
   761 #ifdef DEBUG_blizzard
   762         printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
   763 #endif
   765         // actually create the file.
   766         nsLocalFile *newFile = new nsLocalFile();
   767         if (!newFile)
   768             return NS_ERROR_OUT_OF_MEMORY;
   770         nsCOMPtr<nsIFile> fileRef(newFile); // release on exit
   772         rv = newFile->InitWithNativePath(newPathName);
   773         if (NS_FAILED(rv))
   774             return rv;
   776         // get the old permissions
   777         uint32_t myPerms;
   778         GetPermissions(&myPerms);
   780         // Create the new file with the old file's permissions, even if write
   781         // permission is missing.  We can't create with write permission and
   782         // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
   783         // But we can write to a read-only file on all Unix filesystems if we
   784         // open it successfully for writing.
   786         PRFileDesc *newFD;
   787         rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
   788                                         PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
   789                                         myPerms,
   790                                         &newFD);
   791         if (NS_FAILED(rv))
   792             return rv;
   794         // open the old file, too
   795         bool specialFile;
   796         if (NS_FAILED(rv = IsSpecial(&specialFile))) {
   797             PR_Close(newFD);
   798             return rv;
   799         }
   800         if (specialFile) {
   801 #ifdef DEBUG
   802             printf("Operation not supported: %s\n", mPath.get());
   803 #endif
   804             // make sure to clean up properly
   805             PR_Close(newFD);
   806             return NS_OK;
   807         }
   809         PRFileDesc *oldFD;
   810         rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
   811         if (NS_FAILED(rv)) {
   812             // make sure to clean up properly
   813             PR_Close(newFD);
   814             return rv;
   815         }
   817 #ifdef DEBUG_blizzard
   818         int32_t totalRead = 0;
   819         int32_t totalWritten = 0;
   820 #endif
   821         char buf[BUFSIZ];
   822         int32_t bytesRead;
   824         // record PR_Write() error for better error message later.
   825         nsresult saved_write_error = NS_OK;
   826         nsresult saved_read_error = NS_OK;
   827         nsresult saved_read_close_error = NS_OK;
   828         nsresult saved_write_close_error = NS_OK;
   830         // DONE: Does PR_Read() return bytesRead < 0 for error?
   831         // Yes., The errors from PR_Read are not so common and
   832         // the value may not have correspondence in NS_ERROR_*, but
   833         // we do catch it still, immediately after while() loop.
   834         // We can differentiate errors pf PR_Read and PR_Write by
   835         // looking at saved_write_error value. If PR_Write error occurs (and not
   836         // PR_Read() error), save_write_error is not NS_OK.
   838         while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
   839 #ifdef DEBUG_blizzard
   840             totalRead += bytesRead;
   841 #endif
   843             // PR_Write promises never to do a short write
   844             int32_t bytesWritten = PR_Write(newFD, buf, bytesRead);
   845             if (bytesWritten < 0) {
   846                 saved_write_error = NSRESULT_FOR_ERRNO();
   847                 bytesRead = -1;
   848                 break;
   849             }
   850             NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
   852 #ifdef DEBUG_blizzard
   853             totalWritten += bytesWritten;
   854 #endif
   855         }
   857         // TODO/FIXME: If CIFS (and NFS?) may force read/write to return EINTR,
   858         // we are better off to prepare for retrying. But we need confirmation if
   859         // EINTR is returned.
   861         // Record error if PR_Read() failed.
   862         // Must be done before any other I/O which may reset errno.
   863         if ( (bytesRead < 0) && (saved_write_error == NS_OK)) {
   864             saved_read_error = NSRESULT_FOR_ERRNO();
   865         }
   867 #ifdef DEBUG_blizzard
   868         printf("read %d bytes, wrote %d bytes\n",
   869                  totalRead, totalWritten);
   870 #endif
   872         // DONE: Errors of close can occur.  Read man page of
   873         // close(2);
   874         // This is likely to happen if the file system is remote file
   875         // system (NFS, CIFS, etc.) and network outage occurs.
   876         // At least, we should tell the user that filesystem/disk is
   877         // hosed (possibly due to network error, hard disk failure,
   878         // etc.) so that users can take remedial action.
   880         // close the files
   881         if (PR_Close(newFD) < 0) {
   882             saved_write_close_error = NSRESULT_FOR_ERRNO();
   883 #if DEBUG
   884             // This error merits printing.
   885             fprintf(stderr, "ERROR: PR_Close(newFD) returned error. errno = %d\n", errno);
   886 #endif
   887         }
   889         if (PR_Close(oldFD) < 0) {
   890             saved_read_close_error = NSRESULT_FOR_ERRNO();
   891 #if DEBUG
   892             fprintf(stderr, "ERROR: PR_Close(oldFD) returned error. errno = %d\n", errno);
   893 #endif
   894         }
   896         // Let us report the failure to write and read.
   897         // check for write/read error after cleaning up
   898         if (bytesRead < 0) {
   899             if (saved_write_error != NS_OK)
   900                 return saved_write_error;
   901             else if (saved_read_error != NS_OK)
   902                 return saved_read_error;
   903 #if DEBUG
   904             else                // sanity check. Die and debug.
   905                 MOZ_ASSERT(0);
   906 #endif
   907         }
   909         if (saved_write_close_error != NS_OK)
   910             return saved_write_close_error;
   911         if (saved_read_close_error != NS_OK)
   912             return saved_read_close_error;
   913     }
   914     return rv;
   915 }
   917 NS_IMETHODIMP
   918 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
   919 {
   920     return CopyToNative(newParent, newName);
   921 }
   923 NS_IMETHODIMP
   924 nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
   925 {
   926     nsresult rv;
   928     // check to make sure that this has been initialized properly
   929     CHECK_mPath();
   931     // check to make sure that we have a new parent
   932     nsAutoCString newPathName;
   933     rv = GetNativeTargetPathName(newParent, newName, newPathName);
   934     if (NS_FAILED(rv))
   935         return rv;
   937     // try for atomic rename, falling back to copy/delete
   938     if (rename(mPath.get(), newPathName.get()) < 0) {
   939 #ifdef VMS
   940         if (errno == EXDEV || errno == ENXIO) {
   941 #else
   942         if (errno == EXDEV) {
   943 #endif
   944             rv = CopyToNative(newParent, newName);
   945             if (NS_SUCCEEDED(rv))
   946                 rv = Remove(true);
   947         } else {
   948             rv = NSRESULT_FOR_ERRNO();
   949         }
   950     }
   952     if (NS_SUCCEEDED(rv)) {
   953         // Adjust this
   954         mPath = newPathName;
   955     }
   956     return rv;
   957 }
   959 NS_IMETHODIMP
   960 nsLocalFile::Remove(bool recursive)
   961 {
   962     CHECK_mPath();
   963     ENSURE_STAT_CACHE();
   965     bool isSymLink;
   967     nsresult rv = IsSymlink(&isSymLink);
   968     if (NS_FAILED(rv))
   969         return rv;
   971     if (isSymLink || !S_ISDIR(mCachedStat.st_mode))
   972         return NSRESULT_FOR_RETURN(unlink(mPath.get()));
   974     if (recursive) {
   975         nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
   977         nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
   979         rv = dir->Init(this, false);
   980         if (NS_FAILED(rv))
   981             return rv;
   983         bool more;
   984         while (dir->HasMoreElements(&more), more) {
   985             nsCOMPtr<nsISupports> item;
   986             rv = dir->GetNext(getter_AddRefs(item));
   987             if (NS_FAILED(rv))
   988                 return NS_ERROR_FAILURE;
   990             nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
   991             if (NS_FAILED(rv))
   992                 return NS_ERROR_FAILURE;
   993             rv = file->Remove(recursive);
   995 #ifdef ANDROID
   996             // See bug 580434 - Bionic gives us just deleted files
   997             if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
   998                 continue;
   999 #endif
  1000             if (NS_FAILED(rv))
  1001                 return rv;
  1005     return NSRESULT_FOR_RETURN(rmdir(mPath.get()));
  1008 NS_IMETHODIMP
  1009 nsLocalFile::GetLastModifiedTime(PRTime *aLastModTime)
  1011     CHECK_mPath();
  1012     if (NS_WARN_IF(!aLastModTime))
  1013         return NS_ERROR_INVALID_ARG;
  1015     PRFileInfo64 info;
  1016     if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
  1017         return NSRESULT_FOR_ERRNO();
  1018     PRTime modTime = info.modifyTime;
  1019     if (modTime == 0)
  1020         *aLastModTime = 0;
  1021     else
  1022         *aLastModTime = modTime / PR_USEC_PER_MSEC;
  1024     return NS_OK;
  1027 NS_IMETHODIMP
  1028 nsLocalFile::SetLastModifiedTime(PRTime aLastModTime)
  1030     CHECK_mPath();
  1032     int result;
  1033     if (aLastModTime != 0) {
  1034         ENSURE_STAT_CACHE();
  1035         struct utimbuf ut;
  1036         ut.actime = mCachedStat.st_atime;
  1038         // convert milliseconds to seconds since the unix epoch
  1039         ut.modtime = (time_t)(aLastModTime / PR_MSEC_PER_SEC);
  1040         result = utime(mPath.get(), &ut);
  1041     } else {
  1042         result = utime(mPath.get(), nullptr);
  1044     return NSRESULT_FOR_RETURN(result);
  1047 NS_IMETHODIMP
  1048 nsLocalFile::GetLastModifiedTimeOfLink(PRTime *aLastModTimeOfLink)
  1050     CHECK_mPath();
  1051     if (NS_WARN_IF(!aLastModTimeOfLink))
  1052         return NS_ERROR_INVALID_ARG;
  1054     struct STAT sbuf;
  1055     if (LSTAT(mPath.get(), &sbuf) == -1)
  1056         return NSRESULT_FOR_ERRNO();
  1057     *aLastModTimeOfLink = PRTime(sbuf.st_mtime) * PR_MSEC_PER_SEC;
  1059     return NS_OK;
  1062 /*
  1063  * utime(2) may or may not dereference symlinks, joy.
  1064  */
  1065 NS_IMETHODIMP
  1066 nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink)
  1068     return SetLastModifiedTime(aLastModTimeOfLink);
  1071 /*
  1072  * Only send back permissions bits: maybe we want to send back the whole
  1073  * mode_t to permit checks against other file types?
  1074  */
  1076 #define NORMALIZE_PERMS(mode)    ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
  1078 NS_IMETHODIMP
  1079 nsLocalFile::GetPermissions(uint32_t *aPermissions)
  1081     if (NS_WARN_IF(!aPermissions))
  1082         return NS_ERROR_INVALID_ARG;
  1083     ENSURE_STAT_CACHE();
  1084     *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
  1085     return NS_OK;
  1088 NS_IMETHODIMP
  1089 nsLocalFile::GetPermissionsOfLink(uint32_t *aPermissionsOfLink)
  1091     CHECK_mPath();
  1092     if (NS_WARN_IF(!aPermissionsOfLink))
  1093         return NS_ERROR_INVALID_ARG;
  1095     struct STAT sbuf;
  1096     if (LSTAT(mPath.get(), &sbuf) == -1)
  1097         return NSRESULT_FOR_ERRNO();
  1098     *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
  1099     return NS_OK;
  1102 NS_IMETHODIMP
  1103 nsLocalFile::SetPermissions(uint32_t aPermissions)
  1105     CHECK_mPath();
  1107     /*
  1108      * Race condition here: we should use fchmod instead, there's no way to 
  1109      * guarantee the name still refers to the same file.
  1110      */
  1111     if (chmod(mPath.get(), aPermissions) >= 0)
  1112         return NS_OK;
  1113 #if defined(ANDROID) && defined(STATFS)
  1114     // For the time being, this is restricted for use by Android, but we 
  1115     // will figure out what to do for all platforms in bug 638503
  1116     struct STATFS sfs;
  1117     if (STATFS(mPath.get(), &sfs) < 0)
  1118          return NSRESULT_FOR_ERRNO();
  1120     // if this is a FAT file system we can't set file permissions
  1121     if (sfs.f_type == MSDOS_SUPER_MAGIC )
  1122         return NS_OK;
  1123 #endif
  1124     return NSRESULT_FOR_ERRNO();
  1127 NS_IMETHODIMP
  1128 nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
  1130     // There isn't a consistent mechanism for doing this on UNIX platforms. We
  1131     // might want to carefully implement this in the future though.
  1132     return NS_ERROR_NOT_IMPLEMENTED;
  1135 NS_IMETHODIMP
  1136 nsLocalFile::GetFileSize(int64_t *aFileSize)
  1138     if (NS_WARN_IF(!aFileSize))
  1139         return NS_ERROR_INVALID_ARG;
  1140     *aFileSize = 0;
  1141     ENSURE_STAT_CACHE();
  1143 #if defined(VMS)
  1144     /* Only two record formats can report correct file content size */
  1145     if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
  1146         (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
  1147         return NS_ERROR_FAILURE;
  1149 #endif
  1151     if (!S_ISDIR(mCachedStat.st_mode)) {
  1152         *aFileSize = (int64_t)mCachedStat.st_size;
  1154     return NS_OK;
  1157 NS_IMETHODIMP
  1158 nsLocalFile::SetFileSize(int64_t aFileSize)
  1160     CHECK_mPath();
  1162 #if defined(ANDROID)
  1163     /* no truncate on bionic */
  1164     int fd = open(mPath.get(), O_WRONLY);
  1165     if (fd == -1)
  1166         return NSRESULT_FOR_ERRNO();
  1168     int ret = ftruncate(fd, (off_t)aFileSize);
  1169     close(fd);
  1171     if (ret == -1)
  1172         return NSRESULT_FOR_ERRNO();
  1173 #elif defined(HAVE_TRUNCATE64)
  1174     if (truncate64(mPath.get(), (off64_t)aFileSize) == -1)
  1175         return NSRESULT_FOR_ERRNO();
  1176 #else
  1177     off_t size = (off_t)aFileSize;
  1178     if (truncate(mPath.get(), size) == -1)
  1179         return NSRESULT_FOR_ERRNO();
  1180 #endif
  1181     return NS_OK;
  1184 NS_IMETHODIMP
  1185 nsLocalFile::GetFileSizeOfLink(int64_t *aFileSize)
  1187     CHECK_mPath();
  1188     if (NS_WARN_IF(!aFileSize))
  1189         return NS_ERROR_INVALID_ARG;
  1191     struct STAT sbuf;
  1192     if (LSTAT(mPath.get(), &sbuf) == -1)
  1193         return NSRESULT_FOR_ERRNO();
  1195     *aFileSize = (int64_t)sbuf.st_size;
  1196     return NS_OK;
  1199 #if defined(USE_LINUX_QUOTACTL)
  1200 /*
  1201  * Searches /proc/self/mountinfo for given device (Major:Minor), 
  1202  * returns exported name from /dev
  1204  * Fails when /proc/self/mountinfo or diven device don't exist.
  1205  */
  1206 static bool
  1207 GetDeviceName(int deviceMajor, int deviceMinor, nsACString &deviceName)
  1209     bool ret = false;
  1211     const int kMountInfoLineLength = 200;
  1212     const int kMountInfoDevPosition = 6;
  1214     char mountinfo_line[kMountInfoLineLength];
  1215     char device_num[kMountInfoLineLength];
  1217     snprintf(device_num,kMountInfoLineLength,"%d:%d", deviceMajor, deviceMinor);
  1219     FILE *f = fopen("/proc/self/mountinfo","rt");
  1220     if(!f)
  1221         return ret;
  1223     // Expects /proc/self/mountinfo in format:
  1224     // 'ID ID major:minor root mountpoint flags - type devicename flags'
  1225     while(fgets(mountinfo_line,kMountInfoLineLength,f)) {
  1226         char *p_dev = strstr(mountinfo_line,device_num);
  1228         int i;
  1229         for(i = 0; i < kMountInfoDevPosition && p_dev != nullptr; i++) {
  1230             p_dev = strchr(p_dev,' ');
  1231             if(p_dev)
  1232               p_dev++;
  1235         if(p_dev) {
  1236             char *p_dev_end = strchr(p_dev,' ');
  1237             if(p_dev_end) {
  1238                 *p_dev_end = '\0';
  1239                 deviceName.Assign(p_dev);
  1240                 ret = true;
  1241                 break;
  1246     fclose(f);
  1247     return ret; 
  1249 #endif
  1251 NS_IMETHODIMP
  1252 nsLocalFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable)
  1254     if (NS_WARN_IF(!aDiskSpaceAvailable))
  1255         return NS_ERROR_INVALID_ARG;
  1257     // These systems have the operations necessary to check disk space.
  1259 #ifdef STATFS
  1261     // check to make sure that mPath is properly initialized
  1262     CHECK_mPath();
  1264     struct STATFS fs_buf;
  1266     /* 
  1267      * Members of the STATFS struct that you should know about:
  1268      * F_BSIZE = block size on disk.
  1269      * f_bavail = number of free blocks available to a non-superuser.
  1270      * f_bfree = number of total free blocks in file system.
  1271      */
  1273     if (STATFS(mPath.get(), &fs_buf) < 0) {
  1274         // The call to STATFS failed.
  1275 #ifdef DEBUG
  1276         printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
  1277 #endif
  1278         return NS_ERROR_FAILURE;
  1281     *aDiskSpaceAvailable = (int64_t) fs_buf.F_BSIZE * fs_buf.f_bavail;
  1283 #ifdef DEBUG_DISK_SPACE
  1284     printf("DiskSpaceAvailable: %lu bytes\n",
  1285          *aDiskSpaceAvailable);
  1286 #endif
  1288 #if defined(USE_LINUX_QUOTACTL)
  1290     if(!FillStatCache()) {
  1291         // Return available size from statfs
  1292         return NS_OK;
  1295     nsCString deviceName;
  1296     if(!GetDeviceName(major(mCachedStat.st_dev), minor(mCachedStat.st_dev), deviceName)) {
  1297         return NS_OK;
  1300     struct dqblk dq;
  1301     if(!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(), getuid(), (caddr_t)&dq)
  1302 #ifdef QIF_BLIMITS
  1303        && dq.dqb_valid & QIF_BLIMITS
  1304 #endif
  1305        && dq.dqb_bhardlimit)
  1307         int64_t QuotaSpaceAvailable = 0;
  1308         if (dq.dqb_bhardlimit > dq.dqb_curspace)
  1309             QuotaSpaceAvailable = int64_t(fs_buf.F_BSIZE * (dq.dqb_bhardlimit - dq.dqb_curspace));
  1310         if(QuotaSpaceAvailable < *aDiskSpaceAvailable) {
  1311             *aDiskSpaceAvailable = QuotaSpaceAvailable;
  1314 #endif
  1316     return NS_OK;
  1318 #else
  1319     /*
  1320      * This platform doesn't have statfs or statvfs.  I'm sure that there's
  1321      * a way to check for free disk space on platforms that don't have statfs
  1322      * (I'm SURE they have df, for example).
  1324      * Until we figure out how to do that, lets be honest and say that this
  1325      * command isn't implemented properly for these platforms yet.
  1326      */
  1327 #ifdef DEBUG
  1328     printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
  1329 #endif
  1330     return NS_ERROR_NOT_IMPLEMENTED;
  1332 #endif /* STATFS */
  1336 NS_IMETHODIMP
  1337 nsLocalFile::GetParent(nsIFile **aParent)
  1339     CHECK_mPath();
  1340     if (NS_WARN_IF(!aParent))
  1341         return NS_ERROR_INVALID_ARG;
  1342     *aParent = nullptr;
  1344     // if '/' we are at the top of the volume, return null
  1345     if (mPath.Equals("/"))
  1346         return  NS_OK;
  1348     // <brendan, after jband> I promise to play nice
  1349     char *buffer   = mPath.BeginWriting(),
  1350          *slashp   = buffer;
  1352     // find the last significant slash in buffer
  1353     slashp = strrchr(buffer, '/');
  1354     NS_ASSERTION(slashp, "non-canonical path?");
  1355     if (!slashp)
  1356         return NS_ERROR_FILE_INVALID_PATH;
  1358     // for the case where we are at '/'
  1359     if (slashp == buffer)
  1360         slashp++;
  1362     // temporarily terminate buffer at the last significant slash
  1363     char c = *slashp;
  1364     *slashp = '\0';
  1366     nsCOMPtr<nsIFile> localFile;
  1367     nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true,
  1368                                         getter_AddRefs(localFile));
  1370     // make buffer whole again
  1371     *slashp = c;
  1373     if (NS_FAILED(rv)) {
  1374         return rv;
  1377     localFile.forget(aParent);
  1378     return NS_OK;
  1381 /*
  1382  * The results of Exists, isWritable and isReadable are not cached.
  1383  */
  1386 NS_IMETHODIMP
  1387 nsLocalFile::Exists(bool *_retval)
  1389     CHECK_mPath();
  1390     if (NS_WARN_IF(!_retval))
  1391         return NS_ERROR_INVALID_ARG;
  1393     *_retval = (access(mPath.get(), F_OK) == 0);
  1394     return NS_OK;
  1398 NS_IMETHODIMP
  1399 nsLocalFile::IsWritable(bool *_retval)
  1401     CHECK_mPath();
  1402     if (NS_WARN_IF(!_retval))
  1403         return NS_ERROR_INVALID_ARG;
  1405     *_retval = (access(mPath.get(), W_OK) == 0);
  1406     if (*_retval || errno == EACCES)
  1407         return NS_OK;
  1408     return NSRESULT_FOR_ERRNO();
  1411 NS_IMETHODIMP
  1412 nsLocalFile::IsReadable(bool *_retval)
  1414     CHECK_mPath();
  1415     if (NS_WARN_IF(!_retval))
  1416         return NS_ERROR_INVALID_ARG;
  1418     *_retval = (access(mPath.get(), R_OK) == 0);
  1419     if (*_retval || errno == EACCES)
  1420         return NS_OK;
  1421     return NSRESULT_FOR_ERRNO();
  1424 NS_IMETHODIMP
  1425 nsLocalFile::IsExecutable(bool *_retval)
  1427     CHECK_mPath();
  1428     if (NS_WARN_IF(!_retval))
  1429         return NS_ERROR_INVALID_ARG;
  1431     // Check extension (bug 663899). On certain platforms, the file
  1432     // extension may cause the OS to treat it as executable regardless of
  1433     // the execute bit, such as .jar on Mac OS X. We borrow the code from
  1434     // nsLocalFileWin, slightly modified.
  1436     // Don't be fooled by symlinks.
  1437     bool symLink;
  1438     nsresult rv = IsSymlink(&symLink);
  1439     if (NS_FAILED(rv))
  1440         return rv;
  1442     nsAutoString path;
  1443     if (symLink)
  1444         GetTarget(path);
  1445     else
  1446         GetPath(path);
  1448     int32_t dotIdx = path.RFindChar(char16_t('.'));
  1449     if (dotIdx != kNotFound) {
  1450         // Convert extension to lower case.
  1451         char16_t *p = path.BeginWriting();
  1452         for(p += dotIdx + 1; *p; p++)
  1453             *p +=  (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0; 
  1455         // Search for any of the set of executable extensions.
  1456         static const char * const executableExts[] = {
  1457             "air",         // Adobe AIR installer
  1458             "jar"};        // java application bundle
  1459         nsDependentSubstring ext = Substring(path, dotIdx + 1);
  1460         for (size_t i = 0; i < ArrayLength(executableExts); i++) {
  1461             if (ext.EqualsASCII(executableExts[i])) {
  1462                 // Found a match.  Set result and quit.
  1463                 *_retval = true;
  1464                 return NS_OK;
  1469     // On OS X, then query Launch Services.
  1470 #ifdef MOZ_WIDGET_COCOA
  1471     // Certain Mac applications, such as Classic applications, which
  1472     // run under Rosetta, might not have the +x mode bit but are still
  1473     // considered to be executable by Launch Services (bug 646748).
  1474     CFURLRef url;
  1475     if (NS_FAILED(GetCFURL(&url))) {
  1476         return NS_ERROR_FAILURE;
  1479     LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
  1480     LSItemInfoRecord theInfo;
  1481     OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo);
  1482     ::CFRelease(url);
  1483     if (result == noErr) {
  1484         if ((theInfo.flags & kLSItemInfoIsApplication) != 0) {
  1485             *_retval = true;
  1486             return NS_OK;
  1489 #endif
  1491     // Then check the execute bit.
  1492     *_retval = (access(mPath.get(), X_OK) == 0);
  1493 #ifdef SOLARIS
  1494     // On Solaris, access will always return 0 for root user, however
  1495     // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set.
  1496     // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950
  1497     if (*_retval) {
  1498         struct STAT buf;
  1500         *_retval = (STAT(mPath.get(), &buf) == 0);
  1501         if (*_retval || errno == EACCES) {
  1502             *_retval = *_retval &&
  1503                        (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
  1504             return NS_OK;
  1507         return NSRESULT_FOR_ERRNO();
  1509 #endif
  1510     if (*_retval || errno == EACCES)
  1511         return NS_OK;
  1512     return NSRESULT_FOR_ERRNO();
  1515 NS_IMETHODIMP
  1516 nsLocalFile::IsDirectory(bool *_retval)
  1518     if (NS_WARN_IF(!_retval))
  1519         return NS_ERROR_INVALID_ARG;
  1520     *_retval = false;
  1521     ENSURE_STAT_CACHE();
  1522     *_retval = S_ISDIR(mCachedStat.st_mode);
  1523     return NS_OK;
  1526 NS_IMETHODIMP
  1527 nsLocalFile::IsFile(bool *_retval)
  1529     if (NS_WARN_IF(!_retval))
  1530         return NS_ERROR_INVALID_ARG;
  1531     *_retval = false;
  1532     ENSURE_STAT_CACHE();
  1533     *_retval = S_ISREG(mCachedStat.st_mode);
  1534     return NS_OK;
  1537 NS_IMETHODIMP
  1538 nsLocalFile::IsHidden(bool *_retval)
  1540     if (NS_WARN_IF(!_retval))
  1541         return NS_ERROR_INVALID_ARG;
  1542     nsACString::const_iterator begin, end;
  1543     LocateNativeLeafName(begin, end);
  1544     *_retval = (*begin == '.');
  1545     return NS_OK;
  1548 NS_IMETHODIMP
  1549 nsLocalFile::IsSymlink(bool *_retval)
  1551     if (NS_WARN_IF(!_retval))
  1552         return NS_ERROR_INVALID_ARG;
  1553     CHECK_mPath();
  1555     struct STAT symStat;
  1556     if (LSTAT(mPath.get(), &symStat) == -1)
  1557         return NSRESULT_FOR_ERRNO();
  1558     *_retval=S_ISLNK(symStat.st_mode);
  1559     return NS_OK;
  1562 NS_IMETHODIMP
  1563 nsLocalFile::IsSpecial(bool *_retval)
  1565     if (NS_WARN_IF(!_retval))
  1566         return NS_ERROR_INVALID_ARG;
  1567     ENSURE_STAT_CACHE();
  1568     *_retval = S_ISCHR(mCachedStat.st_mode)      ||
  1569                  S_ISBLK(mCachedStat.st_mode)    ||
  1570 #ifdef S_ISSOCK
  1571                  S_ISSOCK(mCachedStat.st_mode)   ||
  1572 #endif
  1573                  S_ISFIFO(mCachedStat.st_mode);
  1575     return NS_OK;
  1578 NS_IMETHODIMP
  1579 nsLocalFile::Equals(nsIFile *inFile, bool *_retval)
  1581     if (NS_WARN_IF(!inFile))
  1582         return NS_ERROR_INVALID_ARG;
  1583     if (NS_WARN_IF(!_retval))
  1584         return NS_ERROR_INVALID_ARG;
  1585     *_retval = false;
  1587     nsAutoCString inPath;
  1588     nsresult rv = inFile->GetNativePath(inPath);
  1589     if (NS_FAILED(rv))
  1590         return rv;
  1592     // We don't need to worry about "/foo/" vs. "/foo" here
  1593     // because trailing slashes are stripped on init.
  1594     *_retval = !strcmp(inPath.get(), mPath.get());
  1595     return NS_OK;
  1598 NS_IMETHODIMP
  1599 nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval)
  1601     CHECK_mPath();
  1602     if (NS_WARN_IF(!inFile))
  1603         return NS_ERROR_INVALID_ARG;
  1604     if (NS_WARN_IF(!_retval))
  1605         return NS_ERROR_INVALID_ARG;
  1607     nsAutoCString inPath;
  1608     nsresult rv;
  1610     if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
  1611         return rv;
  1613     *_retval = false;
  1615     ssize_t len = mPath.Length();
  1616     if (strncmp(mPath.get(), inPath.get(), len) == 0) {
  1617         // Now make sure that the |inFile|'s path has a separator at len,
  1618         // which implies that it has more components after len.
  1619         if (inPath[len] == '/')
  1620             *_retval = true;
  1623     return NS_OK;
  1626 NS_IMETHODIMP
  1627 nsLocalFile::GetNativeTarget(nsACString &_retval)
  1629     CHECK_mPath();
  1630     _retval.Truncate();
  1632     struct STAT symStat;
  1633     if (LSTAT(mPath.get(), &symStat) == -1)
  1634         return NSRESULT_FOR_ERRNO();
  1636     if (!S_ISLNK(symStat.st_mode))
  1637         return NS_ERROR_FILE_INVALID_PATH;
  1639     int32_t size = (int32_t)symStat.st_size;
  1640     char *target = (char *)nsMemory::Alloc(size + 1);
  1641     if (!target)
  1642         return NS_ERROR_OUT_OF_MEMORY;
  1644     if (readlink(mPath.get(), target, (size_t)size) < 0) {
  1645         nsMemory::Free(target);
  1646         return NSRESULT_FOR_ERRNO();
  1648     target[size] = '\0';
  1650     nsresult rv = NS_OK;
  1651     nsCOMPtr<nsIFile> self(this);
  1652     int32_t maxLinks = 40;
  1653     while (true) {
  1654         if (maxLinks-- == 0) {
  1655             rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
  1656             break;
  1659         if (target[0] != '/') {
  1660             nsCOMPtr<nsIFile> parent;
  1661             if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent))))
  1662                 break;
  1663             if (NS_FAILED(rv = parent->AppendRelativeNativePath(nsDependentCString(target))))
  1664                 break;
  1665             if (NS_FAILED(rv = parent->GetNativePath(_retval)))
  1666                 break;
  1667             self = parent;
  1668         } else {
  1669             _retval = target;
  1672         const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
  1674         // Any failure in testing the current target we'll just interpret
  1675         // as having reached our destiny.
  1676         if (LSTAT(flatRetval.get(), &symStat) == -1)
  1677             break;
  1679         // And of course we're done if it isn't a symlink.
  1680         if (!S_ISLNK(symStat.st_mode))
  1681             break;
  1683         int32_t newSize = (int32_t)symStat.st_size;
  1684         if (newSize > size) {
  1685             char *newTarget = (char *)nsMemory::Realloc(target, newSize + 1);
  1686             if (!newTarget) {
  1687                 rv = NS_ERROR_OUT_OF_MEMORY;
  1688                 break;
  1690             target = newTarget;
  1691             size = newSize;
  1694         int32_t linkLen = readlink(flatRetval.get(), target, size);
  1695         if (linkLen == -1) {
  1696             rv = NSRESULT_FOR_ERRNO();
  1697             break;
  1699         target[linkLen] = '\0';
  1702     nsMemory::Free(target);
  1704     if (NS_FAILED(rv))
  1705         _retval.Truncate();
  1706     return rv;
  1709 NS_IMETHODIMP
  1710 nsLocalFile::GetFollowLinks(bool *aFollowLinks)
  1712     *aFollowLinks = true;
  1713     return NS_OK;
  1716 NS_IMETHODIMP
  1717 nsLocalFile::SetFollowLinks(bool aFollowLinks)
  1719     return NS_OK;
  1722 NS_IMETHODIMP
  1723 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
  1725     nsRefPtr<nsDirEnumeratorUnix> dir = new nsDirEnumeratorUnix();
  1727     nsresult rv = dir->Init(this, false);
  1728     if (NS_FAILED(rv)) {
  1729         *entries = nullptr;
  1730     } else {
  1731         dir.forget(entries);
  1734     return rv;
  1737 NS_IMETHODIMP
  1738 nsLocalFile::Load(PRLibrary **_retval)
  1740     CHECK_mPath();
  1741     if (NS_WARN_IF(!_retval))
  1742         return NS_ERROR_INVALID_ARG;
  1744 #ifdef NS_BUILD_REFCNT_LOGGING
  1745     nsTraceRefcnt::SetActivityIsLegal(false);
  1746 #endif
  1748     *_retval = PR_LoadLibrary(mPath.get());
  1750 #ifdef NS_BUILD_REFCNT_LOGGING
  1751     nsTraceRefcnt::SetActivityIsLegal(true);
  1752 #endif
  1754     if (!*_retval)
  1755         return NS_ERROR_FAILURE;
  1756     return NS_OK;
  1759 NS_IMETHODIMP
  1760 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
  1762     return GetNativePath(aPersistentDescriptor);
  1765 NS_IMETHODIMP
  1766 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
  1768 #ifdef MOZ_WIDGET_COCOA
  1769     if (aPersistentDescriptor.IsEmpty())
  1770         return NS_ERROR_INVALID_ARG;
  1772     // Support pathnames as user-supplied descriptors if they begin with '/'
  1773     // or '~'.  These characters do not collide with the base64 set used for
  1774     // encoding alias records.
  1775     char first = aPersistentDescriptor.First();
  1776     if (first == '/' || first == '~')
  1777         return InitWithNativePath(aPersistentDescriptor);
  1779     uint32_t dataSize = aPersistentDescriptor.Length();    
  1780     char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nullptr);
  1781     if (!decodedData) {
  1782         NS_ERROR("SetPersistentDescriptor was given bad data");
  1783         return NS_ERROR_FAILURE;
  1786     // Cast to an alias record and resolve.
  1787     AliasRecord aliasHeader = *(AliasPtr)decodedData;
  1788     int32_t aliasSize = ::GetAliasSizeFromPtr(&aliasHeader);
  1789     if (aliasSize > ((int32_t)dataSize * 3) / 4) { // be paranoid about having too few data
  1790         PR_Free(decodedData);
  1791         return NS_ERROR_FAILURE;
  1794     nsresult rv = NS_OK;
  1796     // Move the now-decoded data into the Handle.
  1797     // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
  1798     Handle  newHandle = nullptr;
  1799     if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr)
  1800         rv = NS_ERROR_OUT_OF_MEMORY;
  1801     PR_Free(decodedData);
  1802     if (NS_FAILED(rv))
  1803         return rv;
  1805     Boolean changed;
  1806     FSRef resolvedFSRef;
  1807     OSErr err = ::FSResolveAlias(nullptr, (AliasHandle)newHandle, &resolvedFSRef, &changed);
  1809     rv = MacErrorMapper(err);
  1810     DisposeHandle(newHandle);
  1811     if (NS_FAILED(rv))
  1812         return rv;
  1814     return InitWithFSRef(&resolvedFSRef);  
  1815 #else
  1816     return InitWithNativePath(aPersistentDescriptor);
  1817 #endif
  1820 NS_IMETHODIMP
  1821 nsLocalFile::Reveal()
  1823 #ifdef MOZ_WIDGET_GTK
  1824     nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
  1825     nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
  1826     if (!giovfs && !gnomevfs)
  1827         return NS_ERROR_FAILURE;
  1829     bool isDirectory;
  1830     if (NS_FAILED(IsDirectory(&isDirectory)))
  1831         return NS_ERROR_FAILURE;
  1833     if (isDirectory) {
  1834         if (giovfs)
  1835             return giovfs->ShowURIForInput(mPath);
  1836         else 
  1837             /* Fallback to GnomeVFS */
  1838             return gnomevfs->ShowURIForInput(mPath);
  1839     } else if (giovfs && NS_SUCCEEDED(giovfs->OrgFreedesktopFileManager1ShowItems(mPath))) {
  1840         return NS_OK;
  1841     } else {
  1842         nsCOMPtr<nsIFile> parentDir;
  1843         nsAutoCString dirPath;
  1844         if (NS_FAILED(GetParent(getter_AddRefs(parentDir))))
  1845             return NS_ERROR_FAILURE;
  1846         if (NS_FAILED(parentDir->GetNativePath(dirPath)))
  1847             return NS_ERROR_FAILURE;
  1849         if (giovfs)
  1850             return giovfs->ShowURIForInput(dirPath);
  1851         else 
  1852             return gnomevfs->ShowURIForInput(dirPath);        
  1854 #elif defined(MOZ_WIDGET_COCOA)
  1855     CFURLRef url;
  1856     if (NS_SUCCEEDED(GetCFURL(&url))) {
  1857       nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
  1858       ::CFRelease(url);
  1859       return rv;
  1861     return NS_ERROR_FAILURE;
  1862 #else
  1863     return NS_ERROR_FAILURE;
  1864 #endif
  1867 NS_IMETHODIMP
  1868 nsLocalFile::Launch()
  1870 #ifdef MOZ_WIDGET_GTK
  1871     nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
  1872     nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
  1873     if (giovfs) {
  1874       return giovfs->ShowURIForInput(mPath);
  1875     } else if (gnomevfs) {
  1876       /* GnomeVFS fallback */
  1877       return gnomevfs->ShowURIForInput(mPath);
  1880     return NS_ERROR_FAILURE;
  1881 #elif defined(MOZ_ENABLE_CONTENTACTION)
  1882     QUrl uri = QUrl::fromLocalFile(QString::fromUtf8(mPath.get()));
  1883     ContentAction::Action action =
  1884         ContentAction::Action::defaultActionForFile(uri);
  1886     if (action.isValid()) {
  1887       action.trigger();
  1888       return NS_OK;
  1891     return NS_ERROR_FAILURE;
  1892 #elif defined(MOZ_WIDGET_ANDROID)
  1893     // Try to get a mimetype, if this fails just use the file uri alone
  1894     nsresult rv;
  1895     nsAutoCString type;
  1896     nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv));
  1897     if (NS_SUCCEEDED(rv))
  1898         rv = mimeService->GetTypeFromFile(this, type);
  1900     nsAutoCString fileUri = NS_LITERAL_CSTRING("file://") + mPath;
  1901     return mozilla::widget::android::GeckoAppShell::OpenUriExternal(NS_ConvertUTF8toUTF16(fileUri),
  1902       NS_ConvertUTF8toUTF16(type)) ? NS_OK : NS_ERROR_FAILURE;
  1903 #elif defined(MOZ_WIDGET_COCOA)
  1904     CFURLRef url;
  1905     if (NS_SUCCEEDED(GetCFURL(&url))) {
  1906         nsresult rv = CocoaFileUtils::OpenURL(url);
  1907         ::CFRelease(url);
  1908         return rv;
  1910     return NS_ERROR_FAILURE;
  1911 #else
  1912     return NS_ERROR_FAILURE;
  1913 #endif
  1916 nsresult
  1917 NS_NewNativeLocalFile(const nsACString &path, bool followSymlinks, nsIFile **result)
  1919     nsRefPtr<nsLocalFile> file = new nsLocalFile();
  1921     file->SetFollowLinks(followSymlinks);
  1923     if (!path.IsEmpty()) {
  1924         nsresult rv = file->InitWithNativePath(path);
  1925         if (NS_FAILED(rv)) {
  1926             return rv;
  1929     file.forget(result);
  1930     return NS_OK;
  1933 //-----------------------------------------------------------------------------
  1934 // unicode support
  1935 //-----------------------------------------------------------------------------
  1937 #define SET_UCS(func, ucsArg) \
  1938     { \
  1939         nsAutoCString buf; \
  1940         nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
  1941         if (NS_FAILED(rv)) \
  1942             return rv; \
  1943         return (func)(buf); \
  1946 #define GET_UCS(func, ucsArg) \
  1947     { \
  1948         nsAutoCString buf; \
  1949         nsresult rv = (func)(buf); \
  1950         if (NS_FAILED(rv)) return rv; \
  1951         return NS_CopyNativeToUnicode(buf, ucsArg); \
  1954 #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
  1955     { \
  1956         nsAutoCString buf; \
  1957         nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
  1958         if (NS_FAILED(rv)) \
  1959             return rv; \
  1960         return (func)(opaqueArg, buf); \
  1963 // Unicode interface Wrapper
  1964 nsresult  
  1965 nsLocalFile::InitWithPath(const nsAString &filePath)
  1967     SET_UCS(InitWithNativePath, filePath);
  1969 nsresult  
  1970 nsLocalFile::Append(const nsAString &node)
  1972     SET_UCS(AppendNative, node);
  1974 nsresult  
  1975 nsLocalFile::AppendRelativePath(const nsAString &node)
  1977     SET_UCS(AppendRelativeNativePath, node);
  1979 nsresult  
  1980 nsLocalFile::GetLeafName(nsAString &aLeafName)
  1982     GET_UCS(GetNativeLeafName, aLeafName);
  1984 nsresult  
  1985 nsLocalFile::SetLeafName(const nsAString &aLeafName)
  1987     SET_UCS(SetNativeLeafName, aLeafName);
  1989 nsresult  
  1990 nsLocalFile::GetPath(nsAString &_retval)
  1992     return NS_CopyNativeToUnicode(mPath, _retval);
  1994 nsresult  
  1995 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
  1997     SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
  1999 nsresult  
  2000 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
  2002     SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
  2004 nsresult  
  2005 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
  2007     SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
  2010 NS_IMETHODIMP
  2011 nsLocalFile::RenameTo(nsIFile *newParentDir, const nsAString &newName)
  2013   nsresult rv;
  2015   // check to make sure that this has been initialized properly
  2016   CHECK_mPath();
  2018   // check to make sure that we have a new parent
  2019   nsAutoCString newPathName;
  2020   nsAutoCString newNativeName;
  2021   rv = NS_CopyUnicodeToNative(newName, newNativeName);
  2022   if (NS_FAILED(rv)) {
  2023     return rv;
  2025   rv = GetNativeTargetPathName(newParentDir, newNativeName, newPathName);
  2026   if (NS_FAILED(rv)) {
  2027     return rv;
  2030   // try for atomic rename
  2031   if (rename(mPath.get(), newPathName.get()) < 0) {
  2032 #ifdef VMS
  2033     if (errno == EXDEV || errno == ENXIO) {
  2034 #else
  2035     if (errno == EXDEV) {
  2036 #endif
  2037       rv = NS_ERROR_FILE_ACCESS_DENIED;
  2038     } else {
  2039       rv = NSRESULT_FOR_ERRNO();
  2043   return rv;
  2046 nsresult
  2047 nsLocalFile::GetTarget(nsAString &_retval)
  2049     GET_UCS(GetNativeTarget, _retval);
  2052 // nsIHashable
  2054 NS_IMETHODIMP
  2055 nsLocalFile::Equals(nsIHashable* aOther, bool *aResult)
  2057     nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
  2058     if (!otherFile) {
  2059         *aResult = false;
  2060         return NS_OK;
  2063     return Equals(otherFile, aResult);
  2066 NS_IMETHODIMP
  2067 nsLocalFile::GetHashCode(uint32_t *aResult)
  2069     *aResult = HashString(mPath);
  2070     return NS_OK;
  2073 nsresult 
  2074 NS_NewLocalFile(const nsAString &path, bool followLinks, nsIFile* *result)
  2076     nsAutoCString buf;
  2077     nsresult rv = NS_CopyUnicodeToNative(path, buf);
  2078     if (NS_FAILED(rv))
  2079         return rv;
  2080     return NS_NewNativeLocalFile(buf, followLinks, result);
  2083 //-----------------------------------------------------------------------------
  2084 // global init/shutdown
  2085 //-----------------------------------------------------------------------------
  2087 void
  2088 nsLocalFile::GlobalInit()
  2092 void
  2093 nsLocalFile::GlobalShutdown()
  2097 // nsILocalFileMac
  2099 #ifdef MOZ_WIDGET_COCOA
  2101 static nsresult MacErrorMapper(OSErr inErr)
  2103   nsresult outErr;
  2105   switch (inErr)
  2107     case noErr:
  2108       outErr = NS_OK;
  2109       break;
  2111     case fnfErr:
  2112     case afpObjectNotFound:
  2113     case afpDirNotFound:
  2114       outErr = NS_ERROR_FILE_NOT_FOUND;
  2115       break;
  2117     case dupFNErr:
  2118     case afpObjectExists:
  2119       outErr = NS_ERROR_FILE_ALREADY_EXISTS;
  2120       break;
  2122     case dskFulErr:
  2123     case afpDiskFull:
  2124       outErr = NS_ERROR_FILE_DISK_FULL;
  2125       break;
  2127     case fLckdErr:
  2128     case afpVolLocked:
  2129       outErr = NS_ERROR_FILE_IS_LOCKED;
  2130       break;
  2132     case afpAccessDenied:
  2133       outErr = NS_ERROR_FILE_ACCESS_DENIED;
  2134       break;
  2136     case afpDirNotEmpty:
  2137       outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
  2138       break;
  2140     // Can't find good map for some
  2141     case bdNamErr:
  2142       outErr = NS_ERROR_FAILURE;
  2143       break;
  2145     default:
  2146       outErr = NS_ERROR_FAILURE;
  2147       break;
  2150   return outErr;
  2153 static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
  2155   // first see if the conversion would succeed and find the length of the result
  2156   CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
  2157   CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
  2158                                               kCFStringEncodingUTF8, 0, false,
  2159                                               nullptr, 0, &usedBufLen);
  2160   if (charsConverted == inStrLen) {
  2161     // all characters converted, do the actual conversion
  2162     aOutStr.SetLength(usedBufLen);
  2163     if (aOutStr.Length() != (unsigned int)usedBufLen)
  2164       return NS_ERROR_OUT_OF_MEMORY;
  2165     UInt8 *buffer = (UInt8*)aOutStr.BeginWriting();
  2166     ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8,
  2167                        0, false, buffer, usedBufLen, &usedBufLen);
  2168     return NS_OK;
  2171   return NS_ERROR_FAILURE;
  2174 NS_IMETHODIMP
  2175 nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
  2177   UInt8 path[PATH_MAX];
  2178   if (::CFURLGetFileSystemRepresentation(aCFURL, false, path, PATH_MAX)) {
  2179     nsDependentCString nativePath((char*)path);
  2180     return InitWithNativePath(nativePath);
  2183   return NS_ERROR_FAILURE;
  2186 NS_IMETHODIMP
  2187 nsLocalFile::InitWithFSRef(const FSRef *aFSRef)
  2189   if (NS_WARN_IF(!aFSRef))
  2190       return NS_ERROR_INVALID_ARG;
  2192   CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
  2193   if (newURLRef) {
  2194     nsresult rv = InitWithCFURL(newURLRef);
  2195     ::CFRelease(newURLRef);
  2196     return rv;
  2199   return NS_ERROR_FAILURE;
  2202 NS_IMETHODIMP
  2203 nsLocalFile::GetCFURL(CFURLRef *_retval)
  2205   CHECK_mPath();
  2207   bool isDir;
  2208   IsDirectory(&isDir);
  2209   *_retval = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
  2210                                                        (UInt8*)mPath.get(),
  2211                                                        mPath.Length(),
  2212                                                        isDir);
  2214   return (*_retval ? NS_OK : NS_ERROR_FAILURE);
  2217 NS_IMETHODIMP
  2218 nsLocalFile::GetFSRef(FSRef *_retval)
  2220   if (NS_WARN_IF(!_retval))
  2221       return NS_ERROR_INVALID_ARG;
  2223   nsresult rv = NS_ERROR_FAILURE;
  2225   CFURLRef url = nullptr;
  2226   if (NS_SUCCEEDED(GetCFURL(&url))) {
  2227     if (::CFURLGetFSRef(url, _retval)) {
  2228       rv = NS_OK;
  2230     ::CFRelease(url);
  2233   return rv;
  2236 NS_IMETHODIMP
  2237 nsLocalFile::GetFSSpec(FSSpec *_retval)
  2239   if (NS_WARN_IF(!_retval))
  2240       return NS_ERROR_INVALID_ARG;
  2242   FSRef fsRef;
  2243   nsresult rv = GetFSRef(&fsRef);
  2244   if (NS_SUCCEEDED(rv)) {
  2245     OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nullptr, nullptr, _retval, nullptr);
  2246     return MacErrorMapper(err);
  2249   return rv;
  2252 NS_IMETHODIMP
  2253 nsLocalFile::GetFileSizeWithResFork(int64_t *aFileSizeWithResFork)
  2255   if (NS_WARN_IF(!aFileSizeWithResFork))
  2256       return NS_ERROR_INVALID_ARG;
  2258   FSRef fsRef;
  2259   nsresult rv = GetFSRef(&fsRef);
  2260   if (NS_FAILED(rv))
  2261     return rv;
  2263   FSCatalogInfo catalogInfo;
  2264   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
  2265                                  &catalogInfo, nullptr, nullptr, nullptr);
  2266   if (err != noErr)
  2267     return MacErrorMapper(err);
  2269   *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
  2270   return NS_OK;
  2273 NS_IMETHODIMP
  2274 nsLocalFile::GetFileType(OSType *aFileType)
  2276   CFURLRef url;
  2277   if (NS_SUCCEEDED(GetCFURL(&url))) {
  2278     nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType);
  2279     ::CFRelease(url);
  2280     return rv;
  2282   return NS_ERROR_FAILURE;
  2285 NS_IMETHODIMP
  2286 nsLocalFile::SetFileType(OSType aFileType)
  2288   CFURLRef url;
  2289   if (NS_SUCCEEDED(GetCFURL(&url))) {
  2290     nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType);
  2291     ::CFRelease(url);
  2292     return rv;
  2294   return NS_ERROR_FAILURE;
  2297 NS_IMETHODIMP
  2298 nsLocalFile::GetFileCreator(OSType *aFileCreator)
  2300   CFURLRef url;
  2301   if (NS_SUCCEEDED(GetCFURL(&url))) {
  2302     nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator);
  2303     ::CFRelease(url);
  2304     return rv;
  2306   return NS_ERROR_FAILURE;
  2309 NS_IMETHODIMP
  2310 nsLocalFile::SetFileCreator(OSType aFileCreator)
  2312   CFURLRef url;
  2313   if (NS_SUCCEEDED(GetCFURL(&url))) {
  2314     nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator);
  2315     ::CFRelease(url);
  2316     return rv;
  2318   return NS_ERROR_FAILURE;
  2321 NS_IMETHODIMP
  2322 nsLocalFile::LaunchWithDoc(nsIFile *aDocToLoad, bool aLaunchInBackground)
  2324   bool isExecutable;
  2325   nsresult rv = IsExecutable(&isExecutable);
  2326   if (NS_FAILED(rv))
  2327     return rv;
  2328   if (!isExecutable)
  2329     return NS_ERROR_FILE_EXECUTION_FAILED;
  2331   FSRef appFSRef, docFSRef;
  2332   rv = GetFSRef(&appFSRef);
  2333   if (NS_FAILED(rv))
  2334     return rv;
  2336   if (aDocToLoad) {
  2337     nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
  2338     rv = macDoc->GetFSRef(&docFSRef);
  2339     if (NS_FAILED(rv))
  2340       return rv;
  2343   LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
  2344   LSLaunchFSRefSpec thelaunchSpec;
  2346   if (aLaunchInBackground)
  2347     theLaunchFlags |= kLSLaunchDontSwitch;
  2348   memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
  2350   thelaunchSpec.appRef = &appFSRef;
  2351   if (aDocToLoad) {
  2352     thelaunchSpec.numDocs = 1;
  2353     thelaunchSpec.itemRefs = &docFSRef;
  2355   thelaunchSpec.launchFlags = theLaunchFlags;
  2357   OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
  2358   if (err != noErr)
  2359     return MacErrorMapper(err);
  2361   return NS_OK;
  2364 NS_IMETHODIMP
  2365 nsLocalFile::OpenDocWithApp(nsIFile *aAppToOpenWith, bool aLaunchInBackground)
  2367   FSRef docFSRef;
  2368   nsresult rv = GetFSRef(&docFSRef);
  2369   if (NS_FAILED(rv))
  2370     return rv;
  2372   if (!aAppToOpenWith) {
  2373     OSErr err = ::LSOpenFSRef(&docFSRef, nullptr);
  2374     return MacErrorMapper(err);
  2377   nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
  2378   if (!appFileMac)
  2379     return rv;
  2381   bool isExecutable;
  2382   rv = appFileMac->IsExecutable(&isExecutable);
  2383   if (NS_FAILED(rv))
  2384     return rv;
  2385   if (!isExecutable)
  2386     return NS_ERROR_FILE_EXECUTION_FAILED;
  2388   FSRef appFSRef;
  2389   rv = appFileMac->GetFSRef(&appFSRef);
  2390   if (NS_FAILED(rv))
  2391     return rv;
  2393   LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
  2394   LSLaunchFSRefSpec thelaunchSpec;
  2396   if (aLaunchInBackground)
  2397     theLaunchFlags |= kLSLaunchDontSwitch;
  2398   memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
  2400   thelaunchSpec.appRef = &appFSRef;
  2401   thelaunchSpec.numDocs = 1;
  2402   thelaunchSpec.itemRefs = &docFSRef;
  2403   thelaunchSpec.launchFlags = theLaunchFlags;
  2405   OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
  2406   if (err != noErr)
  2407     return MacErrorMapper(err);
  2409   return NS_OK;
  2412 NS_IMETHODIMP
  2413 nsLocalFile::IsPackage(bool *_retval)
  2415   if (NS_WARN_IF(!_retval))
  2416       return NS_ERROR_INVALID_ARG;
  2417   *_retval = false;
  2419   CFURLRef url;
  2420   nsresult rv = GetCFURL(&url);
  2421   if (NS_FAILED(rv))
  2422     return rv;
  2424   LSItemInfoRecord info;
  2425   OSStatus status = ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info);
  2427   ::CFRelease(url);
  2429   if (status != noErr) {
  2430     return NS_ERROR_FAILURE;
  2433   *_retval = !!(info.flags & kLSItemInfoIsPackage);
  2435   return NS_OK;
  2438 NS_IMETHODIMP
  2439 nsLocalFile::GetBundleDisplayName(nsAString& outBundleName)
  2441   bool isPackage = false;
  2442   nsresult rv = IsPackage(&isPackage);
  2443   if (NS_FAILED(rv) || !isPackage)
  2444     return NS_ERROR_FAILURE;
  2446   nsAutoString name;
  2447   rv = GetLeafName(name);
  2448   if (NS_FAILED(rv))
  2449     return rv;
  2451   int32_t length = name.Length();
  2452   if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
  2453     // 4 characters in ".app"
  2454     outBundleName = Substring(name, 0, length - 4);
  2456   else {
  2457     outBundleName = name;
  2460   return NS_OK;
  2463 NS_IMETHODIMP
  2464 nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier)
  2466   nsresult rv = NS_ERROR_FAILURE;
  2468   CFURLRef urlRef;
  2469   if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
  2470     CFBundleRef bundle = ::CFBundleCreate(nullptr, urlRef);
  2471     if (bundle) {
  2472       CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
  2473       if (bundleIdentifier)
  2474         rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier);
  2475       ::CFRelease(bundle);
  2477     ::CFRelease(urlRef);
  2480   return rv;
  2483 NS_IMETHODIMP
  2484 nsLocalFile::GetBundleContentsLastModifiedTime(int64_t *aLastModTime)
  2486   CHECK_mPath();
  2487   if (NS_WARN_IF(!aLastModTime))
  2488       return NS_ERROR_INVALID_ARG;
  2490   bool isPackage = false;
  2491   nsresult rv = IsPackage(&isPackage);
  2492   if (NS_FAILED(rv) || !isPackage) {
  2493     return GetLastModifiedTime(aLastModTime);
  2496   nsAutoCString infoPlistPath(mPath);
  2497   infoPlistPath.AppendLiteral("/Contents/Info.plist");
  2498   PRFileInfo64 info;
  2499   if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) {
  2500     return GetLastModifiedTime(aLastModTime);
  2502   int64_t modTime = int64_t(info.modifyTime);
  2503   if (modTime == 0) {
  2504     *aLastModTime = 0;
  2505   } else {
  2506     *aLastModTime = modTime / int64_t(PR_USEC_PER_MSEC);
  2509   return NS_OK;
  2512 NS_IMETHODIMP nsLocalFile::InitWithFile(nsIFile *aFile)
  2514   if (NS_WARN_IF(!aFile))
  2515       return NS_ERROR_INVALID_ARG;
  2517   nsAutoCString nativePath;
  2518   nsresult rv = aFile->GetNativePath(nativePath);
  2519   if (NS_FAILED(rv))
  2520     return rv;
  2522   return InitWithNativePath(nativePath);
  2525 nsresult
  2526 NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks, nsILocalFileMac** result)
  2528   nsRefPtr<nsLocalFile> file = new nsLocalFile();
  2530   file->SetFollowLinks(aFollowLinks);
  2532   nsresult rv = file->InitWithFSRef(aFSRef);
  2533   if (NS_FAILED(rv)) {
  2534     return rv;
  2536   file.forget(result);
  2537   return NS_OK;
  2540 nsresult
  2541 NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks, nsILocalFileMac** result)
  2543   nsRefPtr<nsLocalFile> file = new nsLocalFile();
  2545   file->SetFollowLinks(aFollowLinks);
  2547   nsresult rv = file->InitWithCFURL(aURL);
  2548   if (NS_FAILED(rv)) {
  2549     return rv;
  2551   file.forget(result);
  2552   return NS_OK;
  2555 #endif

mercurial