LCOV - code coverage report
Current view: top level - xpcom/io - nsLocalFileUnix.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 463 830 55.8 %
Date: 2017-07-14 16:53:18 Functions: 64 92 69.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /**
       8             :  * Implementation of nsIFile for "unixy" systems.
       9             :  */
      10             : 
      11             : #include "mozilla/ArrayUtils.h"
      12             : #include "mozilla/Attributes.h"
      13             : #include "mozilla/Sprintf.h"
      14             : 
      15             : #include <sys/types.h>
      16             : #include <sys/stat.h>
      17             : #include <unistd.h>
      18             : #include <fcntl.h>
      19             : #include <errno.h>
      20             : #include <utime.h>
      21             : #include <dirent.h>
      22             : #include <ctype.h>
      23             : #include <locale.h>
      24             : 
      25             : #if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H)
      26             : #define USE_LINUX_QUOTACTL
      27             : #include <sys/mount.h>
      28             : #include <sys/quota.h>
      29             : #include <sys/sysmacros.h>
      30             : #ifndef BLOCK_SIZE
      31             : #define BLOCK_SIZE 1024 /* kernel block size */
      32             : #endif
      33             : #endif
      34             : 
      35             : #include "xpcom-private.h"
      36             : #include "nsDirectoryServiceDefs.h"
      37             : #include "nsCRT.h"
      38             : #include "nsCOMPtr.h"
      39             : #include "nsMemory.h"
      40             : #include "nsIFile.h"
      41             : #include "nsString.h"
      42             : #include "nsReadableUtils.h"
      43             : #include "nsLocalFile.h"
      44             : #include "nsIComponentManager.h"
      45             : #include "nsXPIDLString.h"
      46             : #include "prproces.h"
      47             : #include "nsIDirectoryEnumerator.h"
      48             : #include "nsISimpleEnumerator.h"
      49             : #include "private/pprio.h"
      50             : #include "prlink.h"
      51             : 
      52             : #ifdef MOZ_WIDGET_GTK
      53             : #include "nsIGIOService.h"
      54             : #endif
      55             : 
      56             : #ifdef MOZ_WIDGET_COCOA
      57             : #include <Carbon/Carbon.h>
      58             : #include "CocoaFileUtils.h"
      59             : #include "prmem.h"
      60             : #include "plbase64.h"
      61             : 
      62             : static nsresult MacErrorMapper(OSErr inErr);
      63             : #endif
      64             : 
      65             : #ifdef MOZ_WIDGET_ANDROID
      66             : #include "GeneratedJNIWrappers.h"
      67             : #include "nsIMIMEService.h"
      68             : #include <linux/magic.h>
      69             : #endif
      70             : 
      71             : #ifdef MOZ_ENABLE_CONTENTACTION
      72             : #include <contentaction/contentaction.h>
      73             : #endif
      74             : 
      75             : #include "nsNativeCharsetUtils.h"
      76             : #include "nsTraceRefcnt.h"
      77             : #include "nsHashKeys.h"
      78             : 
      79             : using namespace mozilla;
      80             : 
      81             : #define ENSURE_STAT_CACHE()                     \
      82             :     do {                                        \
      83             :         if (!FillStatCache())                   \
      84             :              return NSRESULT_FOR_ERRNO();       \
      85             :     } while(0)
      86             : 
      87             : #define CHECK_mPath()                           \
      88             :     do {                                        \
      89             :         if (mPath.IsEmpty())                    \
      90             :             return NS_ERROR_NOT_INITIALIZED;    \
      91             :     } while(0)
      92             : 
      93             : /* directory enumerator */
      94             : class nsDirEnumeratorUnix final
      95             :   : public nsISimpleEnumerator
      96             :   , public nsIDirectoryEnumerator
      97             : {
      98             : public:
      99             :   nsDirEnumeratorUnix();
     100             : 
     101             :   // nsISupports interface
     102             :   NS_DECL_ISUPPORTS
     103             : 
     104             :   // nsISimpleEnumerator interface
     105             :   NS_DECL_NSISIMPLEENUMERATOR
     106             : 
     107             :   // nsIDirectoryEnumerator interface
     108             :   NS_DECL_NSIDIRECTORYENUMERATOR
     109             : 
     110             :   NS_IMETHOD Init(nsLocalFile* aParent, bool aIgnored);
     111             : 
     112             : private:
     113             :   ~nsDirEnumeratorUnix();
     114             : 
     115             : protected:
     116             :   NS_IMETHOD GetNextEntry();
     117             : 
     118             :   DIR*           mDir;
     119             :   struct dirent* mEntry;
     120             :   nsCString      mParentPath;
     121             : };
     122             : 
     123          17 : nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
     124             :   mDir(nullptr),
     125          17 :   mEntry(nullptr)
     126             : {
     127          17 : }
     128             : 
     129          34 : nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
     130             : {
     131          17 :   Close();
     132          17 : }
     133             : 
     134         108 : NS_IMPL_ISUPPORTS(nsDirEnumeratorUnix, nsISimpleEnumerator,
     135             :                   nsIDirectoryEnumerator)
     136             : 
     137             : NS_IMETHODIMP
     138          17 : nsDirEnumeratorUnix::Init(nsLocalFile* aParent,
     139             :                           bool aResolveSymlinks /*ignored*/)
     140             : {
     141          34 :   nsAutoCString dirPath;
     142          34 :   if (NS_FAILED(aParent->GetNativePath(dirPath)) ||
     143          17 :       dirPath.IsEmpty()) {
     144           0 :     return NS_ERROR_FILE_INVALID_PATH;
     145             :   }
     146             : 
     147          17 :   if (NS_FAILED(aParent->GetNativePath(mParentPath))) {
     148           0 :     return NS_ERROR_FAILURE;
     149             :   }
     150             : 
     151          17 :   mDir = opendir(dirPath.get());
     152          17 :   if (!mDir) {
     153           0 :     return NSRESULT_FOR_ERRNO();
     154             :   }
     155          17 :   return GetNextEntry();
     156             : }
     157             : 
     158             : NS_IMETHODIMP
     159         203 : nsDirEnumeratorUnix::HasMoreElements(bool* aResult)
     160             : {
     161         203 :   *aResult = mDir && mEntry;
     162         203 :   if (!*aResult) {
     163          15 :     Close();
     164             :   }
     165         203 :   return NS_OK;
     166             : }
     167             : 
     168             : NS_IMETHODIMP
     169         188 : nsDirEnumeratorUnix::GetNext(nsISupports** aResult)
     170             : {
     171         376 :   nsCOMPtr<nsIFile> file;
     172         188 :   nsresult rv = GetNextFile(getter_AddRefs(file));
     173         188 :   if (NS_FAILED(rv)) {
     174           0 :     return rv;
     175             :   }
     176         188 :   NS_IF_ADDREF(*aResult = file);
     177         188 :   return NS_OK;
     178             : }
     179             : 
     180             : NS_IMETHODIMP
     181         243 : nsDirEnumeratorUnix::GetNextEntry()
     182             : {
     183          34 :   do {
     184         243 :     errno = 0;
     185         243 :     mEntry = readdir(mDir);
     186             : 
     187             :     // end of dir or error
     188         243 :     if (!mEntry) {
     189          17 :       return NSRESULT_FOR_ERRNO();
     190             :     }
     191             : 
     192             :     // keep going past "." and ".."
     193         262 :   } while (mEntry->d_name[0] == '.'     &&
     194          55 :            (mEntry->d_name[1] == '\0'    ||   // .\0
     195          36 :             (mEntry->d_name[1] == '.'     &&
     196          17 :              mEntry->d_name[2] == '\0')));      // ..\0
     197         192 :   return NS_OK;
     198             : }
     199             : 
     200             : NS_IMETHODIMP
     201         194 : nsDirEnumeratorUnix::GetNextFile(nsIFile** aResult)
     202             : {
     203             :   nsresult rv;
     204         194 :   if (!mDir || !mEntry) {
     205           2 :     *aResult = nullptr;
     206           2 :     return NS_OK;
     207             :   }
     208             : 
     209         384 :   nsCOMPtr<nsIFile> file = new nsLocalFile();
     210             : 
     211         768 :   if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
     212         768 :       NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name)))) {
     213           0 :     return rv;
     214             :   }
     215             : 
     216         192 :   file.forget(aResult);
     217         192 :   return GetNextEntry();
     218             : }
     219             : 
     220             : NS_IMETHODIMP
     221          32 : nsDirEnumeratorUnix::Close()
     222             : {
     223          32 :   if (mDir) {
     224          17 :     closedir(mDir);
     225          17 :     mDir = nullptr;
     226             :   }
     227          32 :   return NS_OK;
     228             : }
     229             : 
     230        5292 : nsLocalFile::nsLocalFile()
     231             : {
     232        5292 : }
     233             : 
     234        5390 : nsLocalFile::nsLocalFile(const nsLocalFile& aOther)
     235        5390 :   : mPath(aOther.mPath)
     236             : {
     237        5390 : }
     238             : 
     239             : #ifdef MOZ_WIDGET_COCOA
     240             : NS_IMPL_ISUPPORTS(nsLocalFile,
     241             :                   nsILocalFileMac,
     242             :                   nsILocalFile,
     243             :                   nsIFile,
     244             :                   nsIHashable)
     245             : #else
     246      113359 : NS_IMPL_ISUPPORTS(nsLocalFile,
     247             :                   nsILocalFile,
     248             :                   nsIFile,
     249             :                   nsIHashable)
     250             : #endif
     251             : 
     252             : nsresult
     253          31 : nsLocalFile::nsLocalFileConstructor(nsISupports* aOuter,
     254             :                                     const nsIID& aIID,
     255             :                                     void** aInstancePtr)
     256             : {
     257          31 :   if (NS_WARN_IF(!aInstancePtr)) {
     258           0 :     return NS_ERROR_INVALID_ARG;
     259             :   }
     260          31 :   if (NS_WARN_IF(aOuter)) {
     261           0 :     return NS_ERROR_NO_AGGREGATION;
     262             :   }
     263             : 
     264          31 :   *aInstancePtr = nullptr;
     265             : 
     266          62 :   nsCOMPtr<nsIFile> inst = new nsLocalFile();
     267          31 :   return inst->QueryInterface(aIID, aInstancePtr);
     268             : }
     269             : 
     270             : bool
     271        1758 : nsLocalFile::FillStatCache()
     272             : {
     273        1758 :   if (STAT(mPath.get(), &mCachedStat) == -1) {
     274             :     // try lstat it may be a symlink
     275          10 :     if (LSTAT(mPath.get(), &mCachedStat) == -1) {
     276          10 :       return false;
     277             :     }
     278             :   }
     279        1748 :   return true;
     280             : }
     281             : 
     282             : NS_IMETHODIMP
     283        5390 : nsLocalFile::Clone(nsIFile** aFile)
     284             : {
     285             :   // Just copy-construct ourselves
     286       10780 :   RefPtr<nsLocalFile> copy = new nsLocalFile(*this);
     287        5390 :   copy.forget(aFile);
     288       10780 :   return NS_OK;
     289             : }
     290             : 
     291             : NS_IMETHODIMP
     292        5292 : nsLocalFile::InitWithNativePath(const nsACString& aFilePath)
     293             : {
     294       21168 :   if (aFilePath.EqualsLiteral("~") ||
     295       21168 :       Substring(aFilePath, 0, 2).EqualsLiteral("~/")) {
     296           8 :     nsCOMPtr<nsIFile> homeDir;
     297           8 :     nsAutoCString homePath;
     298          12 :     if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
     299          16 :                                          getter_AddRefs(homeDir))) ||
     300           4 :         NS_FAILED(homeDir->GetNativePath(homePath))) {
     301           0 :       return NS_ERROR_FAILURE;
     302             :     }
     303             : 
     304           4 :     mPath = homePath;
     305           4 :     if (aFilePath.Length() > 2) {
     306           4 :       mPath.Append(Substring(aFilePath, 1, aFilePath.Length() - 1));
     307             :     }
     308             :   } else {
     309        5288 :     if (aFilePath.IsEmpty() || aFilePath.First() != '/') {
     310          24 :       return NS_ERROR_FILE_UNRECOGNIZED_PATH;
     311             :     }
     312        5264 :     mPath = aFilePath;
     313             :   }
     314             : 
     315             :   // trim off trailing slashes
     316        5268 :   ssize_t len = mPath.Length();
     317        5270 :   while ((len > 1) && (mPath[len - 1] == '/')) {
     318           1 :     --len;
     319             :   }
     320        5268 :   mPath.SetLength(len);
     321             : 
     322        5268 :   return NS_OK;
     323             : }
     324             : 
     325             : NS_IMETHODIMP
     326           0 : nsLocalFile::CreateAllAncestors(uint32_t aPermissions)
     327             : {
     328             :   // <jband> I promise to play nice
     329           0 :   char* buffer = mPath.BeginWriting();
     330           0 :   char* slashp = buffer;
     331             : 
     332             : #ifdef DEBUG_NSIFILE
     333             :   fprintf(stderr, "nsIFile: before: %s\n", buffer);
     334             : #endif
     335             : 
     336           0 :   while ((slashp = strchr(slashp + 1, '/'))) {
     337             :     /*
     338             :      * Sequences of '/' are equivalent to a single '/'.
     339             :      */
     340           0 :     if (slashp[1] == '/') {
     341           0 :       continue;
     342             :     }
     343             : 
     344             :     /*
     345             :      * If the path has a trailing slash, don't make the last component,
     346             :      * because we'll get EEXIST in Create when we try to build the final
     347             :      * component again, and it's easier to condition the logic here than
     348             :      * there.
     349             :      */
     350           0 :     if (slashp[1] == '\0') {
     351           0 :       break;
     352             :     }
     353             : 
     354             :     /* Temporarily NUL-terminate here */
     355           0 :     *slashp = '\0';
     356             : #ifdef DEBUG_NSIFILE
     357             :     fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
     358             : #endif
     359           0 :     int mkdir_result = mkdir(buffer, aPermissions);
     360           0 :     int mkdir_errno  = errno;
     361           0 :     if (mkdir_result == -1) {
     362             :       /*
     363             :        * Always set |errno| to EEXIST if the dir already exists
     364             :        * (we have to do this here since the errno value is not consistent
     365             :        * in all cases - various reasons like different platform,
     366             :        * automounter-controlled dir, etc. can affect it (see bug 125489
     367             :        * for details)).
     368             :        */
     369           0 :       if (access(buffer, F_OK) == 0) {
     370           0 :         mkdir_errno = EEXIST;
     371             :       }
     372             :     }
     373             : 
     374             :     /* Put the / back before we (maybe) return */
     375           0 :     *slashp = '/';
     376             : 
     377             :     /*
     378             :      * We could get EEXIST for an existing file -- not directory --
     379             :      * with the name of one of our ancestors, but that's OK: we'll get
     380             :      * ENOTDIR when we try to make the next component in the path,
     381             :      * either here on back in Create, and error out appropriately.
     382             :      */
     383           0 :     if (mkdir_result == -1 && mkdir_errno != EEXIST) {
     384           0 :       return nsresultForErrno(mkdir_errno);
     385             :     }
     386             :   }
     387             : 
     388             : #ifdef DEBUG_NSIFILE
     389             :   fprintf(stderr, "nsIFile: after: %s\n", buffer);
     390             : #endif
     391             : 
     392           0 :   return NS_OK;
     393             : }
     394             : 
     395             : NS_IMETHODIMP
     396        1248 : nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
     397             :                               PRFileDesc** aResult)
     398             : {
     399        1248 :   *aResult = PR_Open(mPath.get(), aFlags, aMode);
     400        1248 :   if (!*aResult) {
     401          10 :     return NS_ErrorAccordingToNSPR();
     402             :   }
     403             : 
     404        1238 :   if (aFlags & DELETE_ON_CLOSE) {
     405           0 :     PR_Delete(mPath.get());
     406             :   }
     407             : 
     408             : #if defined(HAVE_POSIX_FADVISE)
     409        1238 :   if (aFlags & OS_READAHEAD) {
     410          54 :     posix_fadvise(PR_FileDesc2NativeHandle(*aResult), 0, 0,
     411          54 :                   POSIX_FADV_SEQUENTIAL);
     412             :   }
     413             : #endif
     414        1238 :   return NS_OK;
     415             : }
     416             : 
     417             : NS_IMETHODIMP
     418           0 : nsLocalFile::OpenANSIFileDesc(const char* aMode, FILE** aResult)
     419             : {
     420           0 :   *aResult = fopen(mPath.get(), aMode);
     421           0 :   if (!*aResult) {
     422           0 :     return NS_ERROR_FAILURE;
     423             :   }
     424             : 
     425           0 :   return NS_OK;
     426             : }
     427             : 
     428             : static int
     429          24 : do_create(const char* aPath, int aFlags, mode_t aMode, PRFileDesc** aResult)
     430             : {
     431          24 :   *aResult = PR_Open(aPath, aFlags, aMode);
     432          24 :   return *aResult ? 0 : -1;
     433             : }
     434             : 
     435             : static int
     436          25 : do_mkdir(const char* aPath, int aFlags, mode_t aMode, PRFileDesc** aResult)
     437             : {
     438          25 :   *aResult = nullptr;
     439          25 :   return mkdir(aPath, aMode);
     440             : }
     441             : 
     442             : nsresult
     443          49 : nsLocalFile::CreateAndKeepOpen(uint32_t aType, int aFlags,
     444             :                                uint32_t aPermissions, PRFileDesc** aResult)
     445             : {
     446          49 :   if (aType != NORMAL_FILE_TYPE && aType != DIRECTORY_TYPE) {
     447           0 :     return NS_ERROR_FILE_UNKNOWN_TYPE;
     448             :   }
     449             : 
     450             :   int (*createFunc)(const char*, int, mode_t, PRFileDesc**) =
     451          49 :     (aType == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
     452             : 
     453          49 :   int result = createFunc(mPath.get(), aFlags, aPermissions, aResult);
     454          49 :   if (result == -1 && errno == ENOENT) {
     455             :     /*
     456             :      * If we failed because of missing ancestor components, try to create
     457             :      * them and then retry the original creation.
     458             :      *
     459             :      * Ancestor directories get the same permissions as the file we're
     460             :      * creating, with the X bit set for each of (user,group,other) with
     461             :      * an R bit in the original permissions.    If you want to do anything
     462             :      * fancy like setgid or sticky bits, do it by hand.
     463             :      */
     464           0 :     int dirperm = aPermissions;
     465           0 :     if (aPermissions & S_IRUSR) {
     466           0 :       dirperm |= S_IXUSR;
     467             :     }
     468           0 :     if (aPermissions & S_IRGRP) {
     469           0 :       dirperm |= S_IXGRP;
     470             :     }
     471           0 :     if (aPermissions & S_IROTH) {
     472           0 :       dirperm |= S_IXOTH;
     473             :     }
     474             : 
     475             : #ifdef DEBUG_NSIFILE
     476             :     fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", aPermissions,
     477             :             dirperm);
     478             : #endif
     479             : 
     480           0 :     if (NS_FAILED(CreateAllAncestors(dirperm))) {
     481           0 :       return NS_ERROR_FAILURE;
     482             :     }
     483             : 
     484             : #ifdef DEBUG_NSIFILE
     485             :     fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
     486             : #endif
     487           0 :     result = createFunc(mPath.get(), aFlags, aPermissions, aResult);
     488             :   }
     489          49 :   return NSRESULT_FOR_RETURN(result);
     490             : }
     491             : 
     492             : NS_IMETHODIMP
     493          37 : nsLocalFile::Create(uint32_t aType, uint32_t aPermissions)
     494             : {
     495          37 :   PRFileDesc* junk = nullptr;
     496             :   nsresult rv = CreateAndKeepOpen(aType,
     497             :                                   PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
     498             :                                   PR_EXCL,
     499             :                                   aPermissions,
     500          37 :                                   &junk);
     501          37 :   if (junk) {
     502           6 :     PR_Close(junk);
     503             :   }
     504          37 :   return rv;
     505             : }
     506             : 
     507             : NS_IMETHODIMP
     508         573 : nsLocalFile::AppendNative(const nsACString& aFragment)
     509             : {
     510         573 :   if (aFragment.IsEmpty()) {
     511           0 :     return NS_OK;
     512             :   }
     513             : 
     514             :   // only one component of path can be appended
     515         573 :   nsACString::const_iterator begin, end;
     516         573 :   if (FindCharInReadable('/', aFragment.BeginReading(begin),
     517         573 :                          aFragment.EndReading(end))) {
     518           0 :     return NS_ERROR_FILE_UNRECOGNIZED_PATH;
     519             :   }
     520             : 
     521         573 :   return AppendRelativeNativePath(aFragment);
     522             : }
     523             : 
     524             : NS_IMETHODIMP
     525        1839 : nsLocalFile::AppendRelativeNativePath(const nsACString& aFragment)
     526             : {
     527        1839 :   if (aFragment.IsEmpty()) {
     528           0 :     return NS_OK;
     529             :   }
     530             : 
     531             :   // No leading '/'
     532        1839 :   if (aFragment.First() == '/') {
     533           0 :     return NS_ERROR_FILE_UNRECOGNIZED_PATH;
     534             :   }
     535             : 
     536        1839 :   if (!mPath.EqualsLiteral("/")) {
     537        1839 :     mPath.Append('/');
     538             :   }
     539        1839 :   mPath.Append(aFragment);
     540             : 
     541        1839 :   return NS_OK;
     542             : }
     543             : 
     544             : NS_IMETHODIMP
     545           6 : nsLocalFile::Normalize()
     546             : {
     547           6 :   char resolved_path[PATH_MAX] = "";
     548           6 :   char* resolved_path_ptr = nullptr;
     549             : 
     550           6 :   resolved_path_ptr = realpath(mPath.get(), resolved_path);
     551             : 
     552             :   // if there is an error, the return is null.
     553           6 :   if (!resolved_path_ptr) {
     554           0 :     return NSRESULT_FOR_ERRNO();
     555             :   }
     556             : 
     557           6 :   mPath = resolved_path;
     558           6 :   return NS_OK;
     559             : }
     560             : 
     561             : void
     562         656 : nsLocalFile::LocateNativeLeafName(nsACString::const_iterator& aBegin,
     563             :                                   nsACString::const_iterator& aEnd)
     564             : {
     565             :   // XXX perhaps we should cache this??
     566             : 
     567         656 :   mPath.BeginReading(aBegin);
     568         656 :   mPath.EndReading(aEnd);
     569             : 
     570         656 :   nsACString::const_iterator it = aEnd;
     571         656 :   nsACString::const_iterator stop = aBegin;
     572         656 :   --stop;
     573       22360 :   while (--it != stop) {
     574       11508 :     if (*it == '/') {
     575         656 :       aBegin = ++it;
     576         656 :       return;
     577             :     }
     578             :   }
     579             :   // else, the entire path is the leaf name (which means this
     580             :   // isn't an absolute path... unexpected??)
     581             : }
     582             : 
     583             : NS_IMETHODIMP
     584         633 : nsLocalFile::GetNativeLeafName(nsACString& aLeafName)
     585             : {
     586         633 :   nsACString::const_iterator begin, end;
     587         633 :   LocateNativeLeafName(begin, end);
     588         633 :   aLeafName = Substring(begin, end);
     589         633 :   return NS_OK;
     590             : }
     591             : 
     592             : NS_IMETHODIMP
     593          11 : nsLocalFile::SetNativeLeafName(const nsACString& aLeafName)
     594             : {
     595          11 :   nsACString::const_iterator begin, end;
     596          11 :   LocateNativeLeafName(begin, end);
     597          11 :   mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
     598          11 :   return NS_OK;
     599             : }
     600             : 
     601             : NS_IMETHODIMP
     602        2585 : nsLocalFile::GetNativePath(nsACString& aResult)
     603             : {
     604        2585 :   aResult = mPath;
     605        2585 :   return NS_OK;
     606             : }
     607             : 
     608             : nsresult
     609          24 : nsLocalFile::GetNativeTargetPathName(nsIFile* aNewParent,
     610             :                                      const nsACString& aNewName,
     611             :                                      nsACString& aResult)
     612             : {
     613             :   nsresult rv;
     614          48 :   nsCOMPtr<nsIFile> oldParent;
     615             : 
     616          24 :   if (!aNewParent) {
     617          12 :     if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent)))) {
     618           0 :       return rv;
     619             :     }
     620          12 :     aNewParent = oldParent.get();
     621             :   } else {
     622             :     // check to see if our target directory exists
     623             :     bool targetExists;
     624          12 :     if (NS_FAILED(rv = aNewParent->Exists(&targetExists))) {
     625           0 :       return rv;
     626             :     }
     627             : 
     628          12 :     if (!targetExists) {
     629             :       // XXX create the new directory with some permissions
     630           0 :       rv = aNewParent->Create(DIRECTORY_TYPE, 0755);
     631           0 :       if (NS_FAILED(rv)) {
     632           0 :         return rv;
     633             :       }
     634             :     } else {
     635             :       // make sure that the target is actually a directory
     636             :       bool targetIsDirectory;
     637          12 :       if (NS_FAILED(rv = aNewParent->IsDirectory(&targetIsDirectory))) {
     638           0 :         return rv;
     639             :       }
     640          12 :       if (!targetIsDirectory) {
     641           0 :         return NS_ERROR_FILE_DESTINATION_NOT_DIR;
     642             :       }
     643             :     }
     644             :   }
     645             : 
     646          24 :   nsACString::const_iterator nameBegin, nameEnd;
     647          24 :   if (!aNewName.IsEmpty()) {
     648          12 :     aNewName.BeginReading(nameBegin);
     649          12 :     aNewName.EndReading(nameEnd);
     650             :   } else {
     651          12 :     LocateNativeLeafName(nameBegin, nameEnd);
     652             :   }
     653             : 
     654          48 :   nsAutoCString dirName;
     655          24 :   if (NS_FAILED(rv = aNewParent->GetNativePath(dirName))) {
     656           0 :     return rv;
     657             :   }
     658             : 
     659          24 :   aResult = dirName + NS_LITERAL_CSTRING("/") + Substring(nameBegin, nameEnd);
     660          24 :   return NS_OK;
     661             : }
     662             : 
     663             : nsresult
     664           1 : nsLocalFile::CopyDirectoryTo(nsIFile* aNewParent)
     665             : {
     666             :   nsresult rv;
     667             :   /*
     668             :    * dirCheck is used for various boolean test results such as from Equals,
     669             :    * Exists, isDir, etc.
     670             :    */
     671             :   bool dirCheck, isSymlink;
     672             :   uint32_t oldPerms;
     673             : 
     674           1 :   if (NS_FAILED(rv = IsDirectory(&dirCheck))) {
     675           0 :     return rv;
     676             :   }
     677           1 :   if (!dirCheck) {
     678           0 :     return CopyToNative(aNewParent, EmptyCString());
     679             :   }
     680             : 
     681           1 :   if (NS_FAILED(rv = Equals(aNewParent, &dirCheck))) {
     682           0 :     return rv;
     683             :   }
     684           1 :   if (dirCheck) {
     685             :     // can't copy dir to itself
     686           0 :     return NS_ERROR_INVALID_ARG;
     687             :   }
     688             : 
     689           1 :   if (NS_FAILED(rv = aNewParent->Exists(&dirCheck))) {
     690           0 :     return rv;
     691             :   }
     692             :   // get the dirs old permissions
     693           1 :   if (NS_FAILED(rv = GetPermissions(&oldPerms))) {
     694           0 :     return rv;
     695             :   }
     696           1 :   if (!dirCheck) {
     697           1 :     if (NS_FAILED(rv = aNewParent->Create(DIRECTORY_TYPE, oldPerms))) {
     698           0 :       return rv;
     699             :     }
     700             :   } else {    // dir exists lets try to use leaf
     701           0 :     nsAutoCString leafName;
     702           0 :     if (NS_FAILED(rv = GetNativeLeafName(leafName))) {
     703           0 :       return rv;
     704             :     }
     705           0 :     if (NS_FAILED(rv = aNewParent->AppendNative(leafName))) {
     706           0 :       return rv;
     707             :     }
     708           0 :     if (NS_FAILED(rv = aNewParent->Exists(&dirCheck))) {
     709           0 :       return rv;
     710             :     }
     711           0 :     if (dirCheck) {
     712           0 :       return NS_ERROR_FILE_ALREADY_EXISTS;  // dest exists
     713             :     }
     714           0 :     if (NS_FAILED(rv = aNewParent->Create(DIRECTORY_TYPE, oldPerms))) {
     715           0 :       return rv;
     716             :     }
     717             :   }
     718             : 
     719           2 :   nsCOMPtr<nsISimpleEnumerator> dirIterator;
     720           1 :   if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator)))) {
     721           0 :     return rv;
     722             :   }
     723             : 
     724           1 :   bool hasMore = false;
     725          25 :   while (NS_SUCCEEDED(dirIterator->HasMoreElements(&hasMore)) && hasMore) {
     726          24 :     nsCOMPtr<nsISupports> supports;
     727          24 :     nsCOMPtr<nsIFile> entry;
     728          12 :     rv = dirIterator->GetNext(getter_AddRefs(supports));
     729          12 :     entry = do_QueryInterface(supports);
     730          12 :     if (NS_FAILED(rv) || !entry) {
     731           0 :       continue;
     732             :     }
     733          12 :     if (NS_FAILED(rv = entry->IsSymlink(&isSymlink))) {
     734           0 :       return rv;
     735             :     }
     736          12 :     if (NS_FAILED(rv = entry->IsDirectory(&dirCheck))) {
     737           0 :       return rv;
     738             :     }
     739          12 :     if (dirCheck && !isSymlink) {
     740           0 :       nsCOMPtr<nsIFile> destClone;
     741           0 :       rv = aNewParent->Clone(getter_AddRefs(destClone));
     742           0 :       if (NS_SUCCEEDED(rv)) {
     743           0 :         if (NS_FAILED(rv = entry->CopyToNative(destClone, EmptyCString()))) {
     744             : #ifdef DEBUG
     745             :           nsresult rv2;
     746           0 :           nsAutoCString pathName;
     747           0 :           if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) {
     748           0 :             return rv2;
     749             :           }
     750           0 :           printf("Operation not supported: %s\n", pathName.get());
     751             : #endif
     752           0 :           if (rv == NS_ERROR_OUT_OF_MEMORY) {
     753           0 :             return rv;
     754             :           }
     755           0 :           continue;
     756             :         }
     757           0 :       }
     758             :     } else {
     759          12 :       if (NS_FAILED(rv = entry->CopyToNative(aNewParent, EmptyCString()))) {
     760             : #ifdef DEBUG
     761             :         nsresult rv2;
     762           0 :         nsAutoCString pathName;
     763           0 :         if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) {
     764           0 :           return rv2;
     765             :         }
     766           0 :         printf("Operation not supported: %s\n", pathName.get());
     767             : #endif
     768           0 :         if (rv == NS_ERROR_OUT_OF_MEMORY) {
     769           0 :           return rv;
     770             :         }
     771           0 :         continue;
     772             :       }
     773             :     }
     774             :   }
     775           1 :   return NS_OK;
     776             : }
     777             : 
     778             : NS_IMETHODIMP
     779          13 : nsLocalFile::CopyToNative(nsIFile* aNewParent, const nsACString& aNewName)
     780             : {
     781             :   nsresult rv;
     782             :   // check to make sure that this has been initialized properly
     783          13 :   CHECK_mPath();
     784             : 
     785             :   // we copy the parent here so 'aNewParent' remains immutable
     786          26 :   nsCOMPtr <nsIFile> workParent;
     787          13 :   if (aNewParent) {
     788          12 :     if (NS_FAILED(rv = aNewParent->Clone(getter_AddRefs(workParent)))) {
     789           0 :       return rv;
     790             :     }
     791             :   } else {
     792           1 :     if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent)))) {
     793           0 :       return rv;
     794             :     }
     795             :   }
     796             : 
     797             :   // check to see if we are a directory or if we are a file
     798             :   bool isDirectory;
     799          13 :   if (NS_FAILED(rv = IsDirectory(&isDirectory))) {
     800           0 :     return rv;
     801             :   }
     802             : 
     803          26 :   nsAutoCString newPathName;
     804          13 :   if (isDirectory) {
     805           1 :     if (!aNewName.IsEmpty()) {
     806           1 :       if (NS_FAILED(rv = workParent->AppendNative(aNewName))) {
     807           0 :         return rv;
     808             :       }
     809             :     } else {
     810           0 :       if (NS_FAILED(rv = GetNativeLeafName(newPathName))) {
     811           0 :         return rv;
     812             :       }
     813           0 :       if (NS_FAILED(rv = workParent->AppendNative(newPathName))) {
     814           0 :         return rv;
     815             :       }
     816             :     }
     817           1 :     if (NS_FAILED(rv = CopyDirectoryTo(workParent))) {
     818           0 :       return rv;
     819             :     }
     820             :   } else {
     821          12 :     rv = GetNativeTargetPathName(workParent, aNewName, newPathName);
     822          12 :     if (NS_FAILED(rv)) {
     823           0 :       return rv;
     824             :     }
     825             : 
     826             : #ifdef DEBUG_blizzard
     827             :     printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
     828             : #endif
     829             : 
     830             :     // actually create the file.
     831          12 :     auto* newFile = new nsLocalFile();
     832          24 :     nsCOMPtr<nsIFile> fileRef(newFile); // release on exit
     833             : 
     834          12 :     rv = newFile->InitWithNativePath(newPathName);
     835          12 :     if (NS_FAILED(rv)) {
     836           0 :       return rv;
     837             :     }
     838             : 
     839             :     // get the old permissions
     840             :     uint32_t myPerms;
     841          12 :     GetPermissions(&myPerms);
     842             : 
     843             :     // Create the new file with the old file's permissions, even if write
     844             :     // permission is missing.  We can't create with write permission and
     845             :     // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
     846             :     // But we can write to a read-only file on all Unix filesystems if we
     847             :     // open it successfully for writing.
     848             : 
     849             :     PRFileDesc* newFD;
     850          12 :     rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
     851             :                                     PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
     852             :                                     myPerms,
     853          12 :                                     &newFD);
     854          12 :     if (NS_FAILED(rv)) {
     855           0 :       return rv;
     856             :     }
     857             : 
     858             :     // open the old file, too
     859             :     bool specialFile;
     860          12 :     if (NS_FAILED(rv = IsSpecial(&specialFile))) {
     861           0 :       PR_Close(newFD);
     862           0 :       return rv;
     863             :     }
     864          12 :     if (specialFile) {
     865             : #ifdef DEBUG
     866           0 :       printf("Operation not supported: %s\n", mPath.get());
     867             : #endif
     868             :       // make sure to clean up properly
     869           0 :       PR_Close(newFD);
     870           0 :       return NS_OK;
     871             :     }
     872             : 
     873             :     PRFileDesc* oldFD;
     874          12 :     rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
     875          12 :     if (NS_FAILED(rv)) {
     876             :       // make sure to clean up properly
     877           0 :       PR_Close(newFD);
     878           0 :       return rv;
     879             :     }
     880             : 
     881             : #ifdef DEBUG_blizzard
     882             :     int32_t totalRead = 0;
     883             :     int32_t totalWritten = 0;
     884             : #endif
     885             :     char buf[BUFSIZ];
     886             :     int32_t bytesRead;
     887             : 
     888             :     // record PR_Write() error for better error message later.
     889          12 :     nsresult saved_write_error = NS_OK;
     890          12 :     nsresult saved_read_error = NS_OK;
     891          12 :     nsresult saved_read_close_error = NS_OK;
     892          12 :     nsresult saved_write_close_error = NS_OK;
     893             : 
     894             :     // DONE: Does PR_Read() return bytesRead < 0 for error?
     895             :     // Yes., The errors from PR_Read are not so common and
     896             :     // the value may not have correspondence in NS_ERROR_*, but
     897             :     // we do catch it still, immediately after while() loop.
     898             :     // We can differentiate errors pf PR_Read and PR_Write by
     899             :     // looking at saved_write_error value. If PR_Write error occurs (and not
     900             :     // PR_Read() error), save_write_error is not NS_OK.
     901             : 
     902          36 :     while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
     903             : #ifdef DEBUG_blizzard
     904             :       totalRead += bytesRead;
     905             : #endif
     906             : 
     907             :       // PR_Write promises never to do a short write
     908          12 :       int32_t bytesWritten = PR_Write(newFD, buf, bytesRead);
     909          12 :       if (bytesWritten < 0) {
     910           0 :         saved_write_error = NSRESULT_FOR_ERRNO();
     911           0 :         bytesRead = -1;
     912           0 :         break;
     913             :       }
     914          12 :       NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
     915             : 
     916             : #ifdef DEBUG_blizzard
     917             :       totalWritten += bytesWritten;
     918             : #endif
     919             :     }
     920             : 
     921             :     // TODO/FIXME: If CIFS (and NFS?) may force read/write to return EINTR,
     922             :     // we are better off to prepare for retrying. But we need confirmation if
     923             :     // EINTR is returned.
     924             : 
     925             :     // Record error if PR_Read() failed.
     926             :     // Must be done before any other I/O which may reset errno.
     927          12 :     if (bytesRead < 0 && saved_write_error == NS_OK) {
     928           0 :       saved_read_error = NSRESULT_FOR_ERRNO();
     929             :     }
     930             : 
     931             : #ifdef DEBUG_blizzard
     932             :     printf("read %d bytes, wrote %d bytes\n",
     933             :            totalRead, totalWritten);
     934             : #endif
     935             : 
     936             :     // DONE: Errors of close can occur.  Read man page of
     937             :     // close(2);
     938             :     // This is likely to happen if the file system is remote file
     939             :     // system (NFS, CIFS, etc.) and network outage occurs.
     940             :     // At least, we should tell the user that filesystem/disk is
     941             :     // hosed (possibly due to network error, hard disk failure,
     942             :     // etc.) so that users can take remedial action.
     943             : 
     944             :     // close the files
     945          12 :     if (PR_Close(newFD) < 0) {
     946           0 :       saved_write_close_error = NSRESULT_FOR_ERRNO();
     947             : #if DEBUG
     948             :       // This error merits printing.
     949           0 :       fprintf(stderr, "ERROR: PR_Close(newFD) returned error. errno = %d\n", errno);
     950             : #endif
     951             :     }
     952             : 
     953          12 :     if (PR_Close(oldFD) < 0) {
     954           0 :       saved_read_close_error = NSRESULT_FOR_ERRNO();
     955             : #if DEBUG
     956           0 :       fprintf(stderr, "ERROR: PR_Close(oldFD) returned error. errno = %d\n", errno);
     957             : #endif
     958             :     }
     959             : 
     960             :     // Let us report the failure to write and read.
     961             :     // check for write/read error after cleaning up
     962          12 :     if (bytesRead < 0) {
     963           0 :       if (saved_write_error != NS_OK) {
     964           0 :         return saved_write_error;
     965             :       }
     966           0 :       if (saved_read_error != NS_OK) {
     967           0 :         return saved_read_error;
     968             :       }
     969             : #if DEBUG
     970           0 :       MOZ_ASSERT(0);
     971             : #endif
     972             :     }
     973             : 
     974          12 :     if (saved_write_close_error != NS_OK) {
     975           0 :       return saved_write_close_error;
     976             :     }
     977          12 :     if (saved_read_close_error != NS_OK) {
     978           0 :       return saved_read_close_error;
     979             :     }
     980             :   }
     981          13 :   return rv;
     982             : }
     983             : 
     984             : NS_IMETHODIMP
     985           0 : nsLocalFile::CopyToFollowingLinksNative(nsIFile* aNewParent,
     986             :                                         const nsACString& aNewName)
     987             : {
     988           0 :   return CopyToNative(aNewParent, aNewName);
     989             : }
     990             : 
     991             : NS_IMETHODIMP
     992           9 : nsLocalFile::MoveToNative(nsIFile* aNewParent, const nsACString& aNewName)
     993             : {
     994             :   nsresult rv;
     995             : 
     996             :   // check to make sure that this has been initialized properly
     997           9 :   CHECK_mPath();
     998             : 
     999             :   // check to make sure that we have a new parent
    1000          18 :   nsAutoCString newPathName;
    1001           9 :   rv = GetNativeTargetPathName(aNewParent, aNewName, newPathName);
    1002           9 :   if (NS_FAILED(rv)) {
    1003           0 :     return rv;
    1004             :   }
    1005             : 
    1006             :   // try for atomic rename, falling back to copy/delete
    1007           9 :   if (rename(mPath.get(), newPathName.get()) < 0) {
    1008           1 :     if (errno == EXDEV) {
    1009           0 :       rv = CopyToNative(aNewParent, aNewName);
    1010           0 :       if (NS_SUCCEEDED(rv)) {
    1011           0 :         rv = Remove(true);
    1012             :       }
    1013             :     } else {
    1014           1 :       rv = NSRESULT_FOR_ERRNO();
    1015             :     }
    1016             :   }
    1017             : 
    1018           9 :   if (NS_SUCCEEDED(rv)) {
    1019             :     // Adjust this
    1020           8 :     mPath = newPathName;
    1021             :   }
    1022           9 :   return rv;
    1023             : }
    1024             : 
    1025             : NS_IMETHODIMP
    1026          16 : nsLocalFile::Remove(bool aRecursive)
    1027             : {
    1028          16 :   CHECK_mPath();
    1029          16 :   ENSURE_STAT_CACHE();
    1030             : 
    1031             :   bool isSymLink;
    1032             : 
    1033          13 :   nsresult rv = IsSymlink(&isSymLink);
    1034          13 :   if (NS_FAILED(rv)) {
    1035           0 :     return rv;
    1036             :   }
    1037             : 
    1038          13 :   if (isSymLink || !S_ISDIR(mCachedStat.st_mode)) {
    1039          12 :     return NSRESULT_FOR_RETURN(unlink(mPath.get()));
    1040             :   }
    1041             : 
    1042           1 :   if (aRecursive) {
    1043           1 :     auto* dir = new nsDirEnumeratorUnix();
    1044             : 
    1045           2 :     nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
    1046             : 
    1047           1 :     rv = dir->Init(this, false);
    1048           1 :     if (NS_FAILED(rv)) {
    1049           0 :       return rv;
    1050             :     }
    1051             : 
    1052             :     bool more;
    1053          25 :     while (NS_SUCCEEDED(dir->HasMoreElements(&more)) && more) {
    1054          24 :       nsCOMPtr<nsISupports> item;
    1055          12 :       rv = dir->GetNext(getter_AddRefs(item));
    1056          12 :       if (NS_FAILED(rv)) {
    1057           0 :         return NS_ERROR_FAILURE;
    1058             :       }
    1059             : 
    1060          24 :       nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
    1061          12 :       if (NS_FAILED(rv)) {
    1062           0 :         return NS_ERROR_FAILURE;
    1063             :       }
    1064          12 :       rv = file->Remove(aRecursive);
    1065             : 
    1066             : #ifdef ANDROID
    1067             :       // See bug 580434 - Bionic gives us just deleted files
    1068             :       if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
    1069             :         continue;
    1070             :       }
    1071             : #endif
    1072          12 :       if (NS_FAILED(rv)) {
    1073           0 :         return rv;
    1074             :       }
    1075             :     }
    1076             :   }
    1077             : 
    1078           1 :   return NSRESULT_FOR_RETURN(rmdir(mPath.get()));
    1079             : }
    1080             : 
    1081             : NS_IMETHODIMP
    1082          26 : nsLocalFile::GetLastModifiedTime(PRTime* aLastModTime)
    1083             : {
    1084          26 :   CHECK_mPath();
    1085          26 :   if (NS_WARN_IF(!aLastModTime)) {
    1086           0 :     return NS_ERROR_INVALID_ARG;
    1087             :   }
    1088             : 
    1089             :   PRFileInfo64 info;
    1090          26 :   if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS) {
    1091           0 :     return NSRESULT_FOR_ERRNO();
    1092             :   }
    1093          26 :   PRTime modTime = info.modifyTime;
    1094          26 :   if (modTime == 0) {
    1095           0 :     *aLastModTime = 0;
    1096             :   } else {
    1097          26 :     *aLastModTime = modTime / PR_USEC_PER_MSEC;
    1098             :   }
    1099             : 
    1100          26 :   return NS_OK;
    1101             : }
    1102             : 
    1103             : NS_IMETHODIMP
    1104           0 : nsLocalFile::SetLastModifiedTime(PRTime aLastModTime)
    1105             : {
    1106           0 :   CHECK_mPath();
    1107             : 
    1108             :   int result;
    1109           0 :   if (aLastModTime != 0) {
    1110           0 :     ENSURE_STAT_CACHE();
    1111             :     struct utimbuf ut;
    1112           0 :     ut.actime = mCachedStat.st_atime;
    1113             : 
    1114             :     // convert milliseconds to seconds since the unix epoch
    1115           0 :     ut.modtime = (time_t)(aLastModTime / PR_MSEC_PER_SEC);
    1116           0 :     result = utime(mPath.get(), &ut);
    1117             :   } else {
    1118           0 :     result = utime(mPath.get(), nullptr);
    1119             :   }
    1120           0 :   return NSRESULT_FOR_RETURN(result);
    1121             : }
    1122             : 
    1123             : NS_IMETHODIMP
    1124           0 : nsLocalFile::GetLastModifiedTimeOfLink(PRTime* aLastModTimeOfLink)
    1125             : {
    1126           0 :   CHECK_mPath();
    1127           0 :   if (NS_WARN_IF(!aLastModTimeOfLink)) {
    1128           0 :     return NS_ERROR_INVALID_ARG;
    1129             :   }
    1130             : 
    1131             :   struct STAT sbuf;
    1132           0 :   if (LSTAT(mPath.get(), &sbuf) == -1) {
    1133           0 :     return NSRESULT_FOR_ERRNO();
    1134             :   }
    1135           0 :   *aLastModTimeOfLink = PRTime(sbuf.st_mtime) * PR_MSEC_PER_SEC;
    1136             : 
    1137           0 :   return NS_OK;
    1138             : }
    1139             : 
    1140             : /*
    1141             :  * utime(2) may or may not dereference symlinks, joy.
    1142             :  */
    1143             : NS_IMETHODIMP
    1144           0 : nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink)
    1145             : {
    1146           0 :   return SetLastModifiedTime(aLastModTimeOfLink);
    1147             : }
    1148             : 
    1149             : /*
    1150             :  * Only send back permissions bits: maybe we want to send back the whole
    1151             :  * mode_t to permit checks against other file types?
    1152             :  */
    1153             : 
    1154             : #define NORMALIZE_PERMS(mode)    ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
    1155             : 
    1156             : NS_IMETHODIMP
    1157          19 : nsLocalFile::GetPermissions(uint32_t* aPermissions)
    1158             : {
    1159          19 :   if (NS_WARN_IF(!aPermissions)) {
    1160           0 :     return NS_ERROR_INVALID_ARG;
    1161             :   }
    1162          19 :   ENSURE_STAT_CACHE();
    1163          19 :   *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
    1164          19 :   return NS_OK;
    1165             : }
    1166             : 
    1167             : NS_IMETHODIMP
    1168           0 : nsLocalFile::GetPermissionsOfLink(uint32_t* aPermissionsOfLink)
    1169             : {
    1170           0 :   CHECK_mPath();
    1171           0 :   if (NS_WARN_IF(!aPermissionsOfLink)) {
    1172           0 :     return NS_ERROR_INVALID_ARG;
    1173             :   }
    1174             : 
    1175             :   struct STAT sbuf;
    1176           0 :   if (LSTAT(mPath.get(), &sbuf) == -1) {
    1177           0 :     return NSRESULT_FOR_ERRNO();
    1178             :   }
    1179           0 :   *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
    1180           0 :   return NS_OK;
    1181             : }
    1182             : 
    1183             : NS_IMETHODIMP
    1184           0 : nsLocalFile::SetPermissions(uint32_t aPermissions)
    1185             : {
    1186           0 :   CHECK_mPath();
    1187             : 
    1188             :   /*
    1189             :    * Race condition here: we should use fchmod instead, there's no way to
    1190             :    * guarantee the name still refers to the same file.
    1191             :    */
    1192           0 :   if (chmod(mPath.get(), aPermissions) >= 0) {
    1193           0 :     return NS_OK;
    1194             :   }
    1195             : #if defined(ANDROID) && defined(STATFS)
    1196             :   // For the time being, this is restricted for use by Android, but we
    1197             :   // will figure out what to do for all platforms in bug 638503
    1198             :   struct STATFS sfs;
    1199             :   if (STATFS(mPath.get(), &sfs) < 0) {
    1200             :     return NSRESULT_FOR_ERRNO();
    1201             :   }
    1202             : 
    1203             :   // if this is a FAT file system we can't set file permissions
    1204             :   if (sfs.f_type == MSDOS_SUPER_MAGIC) {
    1205             :     return NS_OK;
    1206             :   }
    1207             : #endif
    1208           0 :   return NSRESULT_FOR_ERRNO();
    1209             : }
    1210             : 
    1211             : NS_IMETHODIMP
    1212           0 : nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
    1213             : {
    1214             :   // There isn't a consistent mechanism for doing this on UNIX platforms. We
    1215             :   // might want to carefully implement this in the future though.
    1216           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1217             : }
    1218             : 
    1219             : NS_IMETHODIMP
    1220         266 : nsLocalFile::GetFileSize(int64_t* aFileSize)
    1221             : {
    1222         266 :   if (NS_WARN_IF(!aFileSize)) {
    1223           0 :     return NS_ERROR_INVALID_ARG;
    1224             :   }
    1225         266 :   *aFileSize = 0;
    1226         266 :   ENSURE_STAT_CACHE();
    1227             : 
    1228         265 :   if (!S_ISDIR(mCachedStat.st_mode)) {
    1229         265 :     *aFileSize = (int64_t)mCachedStat.st_size;
    1230             :   }
    1231         265 :   return NS_OK;
    1232             : }
    1233             : 
    1234             : NS_IMETHODIMP
    1235           0 : nsLocalFile::SetFileSize(int64_t aFileSize)
    1236             : {
    1237           0 :   CHECK_mPath();
    1238             : 
    1239             : #if defined(ANDROID)
    1240             :   /* no truncate on bionic */
    1241             :   int fd = open(mPath.get(), O_WRONLY);
    1242             :   if (fd == -1) {
    1243             :     return NSRESULT_FOR_ERRNO();
    1244             :   }
    1245             : 
    1246             :   int ret = ftruncate(fd, (off_t)aFileSize);
    1247             :   close(fd);
    1248             : 
    1249             :   if (ret == -1) {
    1250             :     return NSRESULT_FOR_ERRNO();
    1251             :   }
    1252             : #elif defined(HAVE_TRUNCATE64)
    1253           0 :   if (truncate64(mPath.get(), (off64_t)aFileSize) == -1) {
    1254           0 :     return NSRESULT_FOR_ERRNO();
    1255             :   }
    1256             : #else
    1257             :   off_t size = (off_t)aFileSize;
    1258             :   if (truncate(mPath.get(), size) == -1) {
    1259             :     return NSRESULT_FOR_ERRNO();
    1260             :   }
    1261             : #endif
    1262           0 :   return NS_OK;
    1263             : }
    1264             : 
    1265             : NS_IMETHODIMP
    1266           0 : nsLocalFile::GetFileSizeOfLink(int64_t* aFileSize)
    1267             : {
    1268           0 :   CHECK_mPath();
    1269           0 :   if (NS_WARN_IF(!aFileSize)) {
    1270           0 :     return NS_ERROR_INVALID_ARG;
    1271             :   }
    1272             : 
    1273             :   struct STAT sbuf;
    1274           0 :   if (LSTAT(mPath.get(), &sbuf) == -1) {
    1275           0 :     return NSRESULT_FOR_ERRNO();
    1276             :   }
    1277             : 
    1278           0 :   *aFileSize = (int64_t)sbuf.st_size;
    1279           0 :   return NS_OK;
    1280             : }
    1281             : 
    1282             : #if defined(USE_LINUX_QUOTACTL)
    1283             : /*
    1284             :  * Searches /proc/self/mountinfo for given device (Major:Minor),
    1285             :  * returns exported name from /dev
    1286             :  *
    1287             :  * Fails when /proc/self/mountinfo or diven device don't exist.
    1288             :  */
    1289             : static bool
    1290          11 : GetDeviceName(unsigned int aDeviceMajor, unsigned int aDeviceMinor,
    1291             :               nsACString& aDeviceName)
    1292             : {
    1293          11 :   bool ret = false;
    1294             : 
    1295          11 :   const int kMountInfoLineLength = 200;
    1296          11 :   const int kMountInfoDevPosition = 6;
    1297             : 
    1298             :   char mountinfoLine[kMountInfoLineLength];
    1299             :   char deviceNum[kMountInfoLineLength];
    1300             : 
    1301          11 :   SprintfLiteral(deviceNum, "%u:%u", aDeviceMajor, aDeviceMinor);
    1302             : 
    1303          11 :   FILE* f = fopen("/proc/self/mountinfo", "rt");
    1304          11 :   if (!f) {
    1305           0 :     return ret;
    1306             :   }
    1307             : 
    1308             :   // Expects /proc/self/mountinfo in format:
    1309             :   // 'ID ID major:minor root mountpoint flags - type devicename flags'
    1310         121 :   while (fgets(mountinfoLine, kMountInfoLineLength, f)) {
    1311          66 :     char* p_dev = strstr(mountinfoLine, deviceNum);
    1312             : 
    1313         132 :     for (int i = 0; i < kMountInfoDevPosition && p_dev; ++i) {
    1314          66 :       p_dev = strchr(p_dev, ' ');
    1315          66 :       if (p_dev) {
    1316          66 :         p_dev++;
    1317             :       }
    1318             :     }
    1319             : 
    1320          66 :     if (p_dev) {
    1321          11 :       char* p_dev_end = strchr(p_dev, ' ');
    1322          11 :       if (p_dev_end) {
    1323          11 :         *p_dev_end = '\0';
    1324          11 :         aDeviceName.Assign(p_dev);
    1325          11 :         ret = true;
    1326          11 :         break;
    1327             :       }
    1328             :     }
    1329             :   }
    1330             : 
    1331          11 :   fclose(f);
    1332          11 :   return ret;
    1333             : }
    1334             : #endif
    1335             : 
    1336             : NS_IMETHODIMP
    1337          11 : nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable)
    1338             : {
    1339          11 :   if (NS_WARN_IF(!aDiskSpaceAvailable)) {
    1340           0 :     return NS_ERROR_INVALID_ARG;
    1341             :   }
    1342             : 
    1343             :   // These systems have the operations necessary to check disk space.
    1344             : 
    1345             : #ifdef STATFS
    1346             : 
    1347             :   // check to make sure that mPath is properly initialized
    1348          11 :   CHECK_mPath();
    1349             : 
    1350             :   struct STATFS fs_buf;
    1351             : 
    1352             :   /*
    1353             :    * Members of the STATFS struct that you should know about:
    1354             :    * F_BSIZE = block size on disk.
    1355             :    * f_bavail = number of free blocks available to a non-superuser.
    1356             :    * f_bfree = number of total free blocks in file system.
    1357             :    */
    1358             : 
    1359          11 :   if (STATFS(mPath.get(), &fs_buf) < 0) {
    1360             :     // The call to STATFS failed.
    1361             : #ifdef DEBUG
    1362           0 :     printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
    1363             : #endif
    1364           0 :     return NS_ERROR_FAILURE;
    1365             :   }
    1366             : 
    1367          11 :   *aDiskSpaceAvailable = (int64_t)fs_buf.F_BSIZE * fs_buf.f_bavail;
    1368             : 
    1369             : #ifdef DEBUG_DISK_SPACE
    1370             :   printf("DiskSpaceAvailable: %lu bytes\n",
    1371             :          *aDiskSpaceAvailable);
    1372             : #endif
    1373             : 
    1374             : #if defined(USE_LINUX_QUOTACTL)
    1375             : 
    1376          11 :   if (!FillStatCache()) {
    1377             :     // Return available size from statfs
    1378           0 :     return NS_OK;
    1379             :   }
    1380             : 
    1381          22 :   nsCString deviceName;
    1382          11 :   if (!GetDeviceName(major(mCachedStat.st_dev),
    1383          11 :                      minor(mCachedStat.st_dev),
    1384             :                      deviceName)) {
    1385           0 :     return NS_OK;
    1386             :   }
    1387             : 
    1388             :   struct dqblk dq;
    1389          22 :   if (!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(),
    1390          11 :                 getuid(), (caddr_t)&dq)
    1391             : #ifdef QIF_BLIMITS
    1392           0 :       && dq.dqb_valid & QIF_BLIMITS
    1393             : #endif
    1394          11 :       && dq.dqb_bhardlimit) {
    1395           0 :     int64_t QuotaSpaceAvailable = 0;
    1396             :     // dqb_bhardlimit is count of BLOCK_SIZE blocks, dqb_curspace is bytes
    1397           0 :     if ((BLOCK_SIZE * dq.dqb_bhardlimit) > dq.dqb_curspace)
    1398           0 :       QuotaSpaceAvailable = int64_t(BLOCK_SIZE * dq.dqb_bhardlimit - dq.dqb_curspace);
    1399           0 :     if (QuotaSpaceAvailable < *aDiskSpaceAvailable) {
    1400           0 :       *aDiskSpaceAvailable = QuotaSpaceAvailable;
    1401             :     }
    1402             :   }
    1403             : #endif
    1404             : 
    1405          11 :   return NS_OK;
    1406             : 
    1407             : #else
    1408             :   /*
    1409             :    * This platform doesn't have statfs or statvfs.  I'm sure that there's
    1410             :    * a way to check for free disk space on platforms that don't have statfs
    1411             :    * (I'm SURE they have df, for example).
    1412             :    *
    1413             :    * Until we figure out how to do that, lets be honest and say that this
    1414             :    * command isn't implemented properly for these platforms yet.
    1415             :    */
    1416             : #ifdef DEBUG
    1417             :   printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
    1418             : #endif
    1419             :   return NS_ERROR_NOT_IMPLEMENTED;
    1420             : 
    1421             : #endif /* STATFS */
    1422             : 
    1423             : }
    1424             : 
    1425             : NS_IMETHODIMP
    1426        1273 : nsLocalFile::GetParent(nsIFile** aParent)
    1427             : {
    1428        1273 :   CHECK_mPath();
    1429        1273 :   if (NS_WARN_IF(!aParent)) {
    1430           0 :     return NS_ERROR_INVALID_ARG;
    1431             :   }
    1432        1273 :   *aParent = nullptr;
    1433             : 
    1434             :   // if '/' we are at the top of the volume, return null
    1435        1273 :   if (mPath.EqualsLiteral("/")) {
    1436           0 :     return  NS_OK;
    1437             :   }
    1438             : 
    1439             :   // <brendan, after jband> I promise to play nice
    1440        1273 :   char* buffer = mPath.BeginWriting();
    1441             :   // find the last significant slash in buffer
    1442        1273 :   char* slashp = strrchr(buffer, '/');
    1443        1273 :   NS_ASSERTION(slashp, "non-canonical path?");
    1444        1273 :   if (!slashp) {
    1445           0 :     return NS_ERROR_FILE_INVALID_PATH;
    1446             :   }
    1447             : 
    1448             :   // for the case where we are at '/'
    1449        1273 :   if (slashp == buffer) {
    1450           0 :     slashp++;
    1451             :   }
    1452             : 
    1453             :   // temporarily terminate buffer at the last significant slash
    1454        1273 :   char c = *slashp;
    1455        1273 :   *slashp = '\0';
    1456             : 
    1457        2546 :   nsCOMPtr<nsIFile> localFile;
    1458        2546 :   nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true,
    1459        3819 :                                       getter_AddRefs(localFile));
    1460             : 
    1461             :   // make buffer whole again
    1462        1273 :   *slashp = c;
    1463             : 
    1464        1273 :   if (NS_FAILED(rv)) {
    1465           0 :     return rv;
    1466             :   }
    1467             : 
    1468        1273 :   localFile.forget(aParent);
    1469        1273 :   return NS_OK;
    1470             : }
    1471             : 
    1472             : /*
    1473             :  * The results of Exists, isWritable and isReadable are not cached.
    1474             :  */
    1475             : 
    1476             : 
    1477             : NS_IMETHODIMP
    1478         308 : nsLocalFile::Exists(bool* aResult)
    1479             : {
    1480         308 :   CHECK_mPath();
    1481         308 :   if (NS_WARN_IF(!aResult)) {
    1482           0 :     return NS_ERROR_INVALID_ARG;
    1483             :   }
    1484             : 
    1485         308 :   *aResult = (access(mPath.get(), F_OK) == 0);
    1486         308 :   return NS_OK;
    1487             : }
    1488             : 
    1489             : 
    1490             : NS_IMETHODIMP
    1491           0 : nsLocalFile::IsWritable(bool* aResult)
    1492             : {
    1493           0 :   CHECK_mPath();
    1494           0 :   if (NS_WARN_IF(!aResult)) {
    1495           0 :     return NS_ERROR_INVALID_ARG;
    1496             :   }
    1497             : 
    1498           0 :   *aResult = (access(mPath.get(), W_OK) == 0);
    1499           0 :   if (*aResult || errno == EACCES) {
    1500           0 :     return NS_OK;
    1501             :   }
    1502           0 :   return NSRESULT_FOR_ERRNO();
    1503             : }
    1504             : 
    1505             : NS_IMETHODIMP
    1506          31 : nsLocalFile::IsReadable(bool* aResult)
    1507             : {
    1508          31 :   CHECK_mPath();
    1509          31 :   if (NS_WARN_IF(!aResult)) {
    1510           0 :     return NS_ERROR_INVALID_ARG;
    1511             :   }
    1512             : 
    1513          31 :   *aResult = (access(mPath.get(), R_OK) == 0);
    1514          31 :   if (*aResult || errno == EACCES) {
    1515           3 :     return NS_OK;
    1516             :   }
    1517          28 :   return NSRESULT_FOR_ERRNO();
    1518             : }
    1519             : 
    1520             : NS_IMETHODIMP
    1521           0 : nsLocalFile::IsExecutable(bool* aResult)
    1522             : {
    1523           0 :   CHECK_mPath();
    1524           0 :   if (NS_WARN_IF(!aResult)) {
    1525           0 :     return NS_ERROR_INVALID_ARG;
    1526             :   }
    1527             : 
    1528             :   // Check extension (bug 663899). On certain platforms, the file
    1529             :   // extension may cause the OS to treat it as executable regardless of
    1530             :   // the execute bit, such as .jar on Mac OS X. We borrow the code from
    1531             :   // nsLocalFileWin, slightly modified.
    1532             : 
    1533             :   // Don't be fooled by symlinks.
    1534             :   bool symLink;
    1535           0 :   nsresult rv = IsSymlink(&symLink);
    1536           0 :   if (NS_FAILED(rv)) {
    1537           0 :     return rv;
    1538             :   }
    1539             : 
    1540           0 :   nsAutoString path;
    1541           0 :   if (symLink) {
    1542           0 :     GetTarget(path);
    1543             :   } else {
    1544           0 :     GetPath(path);
    1545             :   }
    1546             : 
    1547           0 :   int32_t dotIdx = path.RFindChar(char16_t('.'));
    1548           0 :   if (dotIdx != kNotFound) {
    1549             :     // Convert extension to lower case.
    1550           0 :     char16_t* p = path.BeginWriting();
    1551           0 :     for (p += dotIdx + 1; *p; ++p) {
    1552           0 :       *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0;
    1553             :     }
    1554             : 
    1555             :     // Search for any of the set of executable extensions.
    1556             :     static const char* const executableExts[] = {
    1557             :       "air",  // Adobe AIR installer
    1558             :       "jar"   // java application bundle
    1559             :     };
    1560           0 :     nsDependentSubstring ext = Substring(path, dotIdx + 1);
    1561           0 :     for (auto executableExt : executableExts) {
    1562           0 :       if (ext.EqualsASCII(executableExt)) {
    1563             :         // Found a match.  Set result and quit.
    1564           0 :         *aResult = true;
    1565           0 :         return NS_OK;
    1566             :       }
    1567             :     }
    1568             :   }
    1569             : 
    1570             :   // On OS X, then query Launch Services.
    1571             : #ifdef MOZ_WIDGET_COCOA
    1572             :   // Certain Mac applications, such as Classic applications, which
    1573             :   // run under Rosetta, might not have the +x mode bit but are still
    1574             :   // considered to be executable by Launch Services (bug 646748).
    1575             :   CFURLRef url;
    1576             :   if (NS_FAILED(GetCFURL(&url))) {
    1577             :     return NS_ERROR_FAILURE;
    1578             :   }
    1579             : 
    1580             :   LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
    1581             :   LSItemInfoRecord theInfo;
    1582             :   OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo);
    1583             :   ::CFRelease(url);
    1584             :   if (result == noErr) {
    1585             :     if ((theInfo.flags & kLSItemInfoIsApplication) != 0) {
    1586             :       *aResult = true;
    1587             :       return NS_OK;
    1588             :     }
    1589             :   }
    1590             : #endif
    1591             : 
    1592             :   // Then check the execute bit.
    1593           0 :   *aResult = (access(mPath.get(), X_OK) == 0);
    1594             : #ifdef SOLARIS
    1595             :   // On Solaris, access will always return 0 for root user, however
    1596             :   // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set.
    1597             :   // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950
    1598             :   if (*aResult) {
    1599             :     struct STAT buf;
    1600             : 
    1601             :     *aResult = (STAT(mPath.get(), &buf) == 0);
    1602             :     if (*aResult || errno == EACCES) {
    1603             :       *aResult = *aResult && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH));
    1604             :       return NS_OK;
    1605             :     }
    1606             : 
    1607             :     return NSRESULT_FOR_ERRNO();
    1608             :   }
    1609             : #endif
    1610           0 :   if (*aResult || errno == EACCES) {
    1611           0 :     return NS_OK;
    1612             :   }
    1613           0 :   return NSRESULT_FOR_ERRNO();
    1614             : }
    1615             : 
    1616             : NS_IMETHODIMP
    1617        1425 : nsLocalFile::IsDirectory(bool* aResult)
    1618             : {
    1619        1425 :   if (NS_WARN_IF(!aResult)) {
    1620           0 :     return NS_ERROR_INVALID_ARG;
    1621             :   }
    1622        1425 :   *aResult = false;
    1623        1425 :   ENSURE_STAT_CACHE();
    1624        1425 :   *aResult = S_ISDIR(mCachedStat.st_mode);
    1625        1425 :   return NS_OK;
    1626             : }
    1627             : 
    1628             : NS_IMETHODIMP
    1629           9 : nsLocalFile::IsFile(bool* aResult)
    1630             : {
    1631           9 :   if (NS_WARN_IF(!aResult)) {
    1632           0 :     return NS_ERROR_INVALID_ARG;
    1633             :   }
    1634           9 :   *aResult = false;
    1635           9 :   ENSURE_STAT_CACHE();
    1636           3 :   *aResult = S_ISREG(mCachedStat.st_mode);
    1637           3 :   return NS_OK;
    1638             : }
    1639             : 
    1640             : NS_IMETHODIMP
    1641           0 : nsLocalFile::IsHidden(bool* aResult)
    1642             : {
    1643           0 :   if (NS_WARN_IF(!aResult)) {
    1644           0 :     return NS_ERROR_INVALID_ARG;
    1645             :   }
    1646           0 :   nsACString::const_iterator begin, end;
    1647           0 :   LocateNativeLeafName(begin, end);
    1648           0 :   *aResult = (*begin == '.');
    1649           0 :   return NS_OK;
    1650             : }
    1651             : 
    1652             : NS_IMETHODIMP
    1653        1196 : nsLocalFile::IsSymlink(bool* aResult)
    1654             : {
    1655        1196 :   if (NS_WARN_IF(!aResult)) {
    1656           0 :     return NS_ERROR_INVALID_ARG;
    1657             :   }
    1658        1196 :   CHECK_mPath();
    1659             : 
    1660             :   struct STAT symStat;
    1661        1196 :   if (LSTAT(mPath.get(), &symStat) == -1) {
    1662           0 :     return NSRESULT_FOR_ERRNO();
    1663             :   }
    1664        1196 :   *aResult = S_ISLNK(symStat.st_mode);
    1665        1196 :   return NS_OK;
    1666             : }
    1667             : 
    1668             : NS_IMETHODIMP
    1669          12 : nsLocalFile::IsSpecial(bool* aResult)
    1670             : {
    1671          12 :   if (NS_WARN_IF(!aResult)) {
    1672           0 :     return NS_ERROR_INVALID_ARG;
    1673             :   }
    1674          12 :   ENSURE_STAT_CACHE();
    1675          36 :   *aResult = S_ISCHR(mCachedStat.st_mode)   ||
    1676          24 :              S_ISBLK(mCachedStat.st_mode)   ||
    1677             : #ifdef S_ISSOCK
    1678          36 :              S_ISSOCK(mCachedStat.st_mode)  ||
    1679             : #endif
    1680          12 :              S_ISFIFO(mCachedStat.st_mode);
    1681             : 
    1682          12 :   return NS_OK;
    1683             : }
    1684             : 
    1685             : NS_IMETHODIMP
    1686          13 : nsLocalFile::Equals(nsIFile* aInFile, bool* aResult)
    1687             : {
    1688          13 :   if (NS_WARN_IF(!aInFile)) {
    1689           0 :     return NS_ERROR_INVALID_ARG;
    1690             :   }
    1691          13 :   if (NS_WARN_IF(!aResult)) {
    1692           0 :     return NS_ERROR_INVALID_ARG;
    1693             :   }
    1694          13 :   *aResult = false;
    1695             : 
    1696          26 :   nsAutoCString inPath;
    1697          13 :   nsresult rv = aInFile->GetNativePath(inPath);
    1698          13 :   if (NS_FAILED(rv)) {
    1699           0 :     return rv;
    1700             :   }
    1701             : 
    1702             :   // We don't need to worry about "/foo/" vs. "/foo" here
    1703             :   // because trailing slashes are stripped on init.
    1704          13 :   *aResult = !strcmp(inPath.get(), mPath.get());
    1705          13 :   return NS_OK;
    1706             : }
    1707             : 
    1708             : NS_IMETHODIMP
    1709           0 : nsLocalFile::Contains(nsIFile* aInFile, bool* aResult)
    1710             : {
    1711           0 :   CHECK_mPath();
    1712           0 :   if (NS_WARN_IF(!aInFile)) {
    1713           0 :     return NS_ERROR_INVALID_ARG;
    1714             :   }
    1715           0 :   if (NS_WARN_IF(!aResult)) {
    1716           0 :     return NS_ERROR_INVALID_ARG;
    1717             :   }
    1718             : 
    1719           0 :   nsAutoCString inPath;
    1720             :   nsresult rv;
    1721             : 
    1722           0 :   if (NS_FAILED(rv = aInFile->GetNativePath(inPath))) {
    1723           0 :     return rv;
    1724             :   }
    1725             : 
    1726           0 :   *aResult = false;
    1727             : 
    1728           0 :   ssize_t len = mPath.Length();
    1729           0 :   if (strncmp(mPath.get(), inPath.get(), len) == 0) {
    1730             :     // Now make sure that the |aInFile|'s path has a separator at len,
    1731             :     // which implies that it has more components after len.
    1732           0 :     if (inPath[len] == '/') {
    1733           0 :       *aResult = true;
    1734             :     }
    1735             :   }
    1736             : 
    1737           0 :   return NS_OK;
    1738             : }
    1739             : 
    1740             : NS_IMETHODIMP
    1741        1092 : nsLocalFile::GetNativeTarget(nsACString& aResult)
    1742             : {
    1743        1092 :   CHECK_mPath();
    1744        1092 :   aResult.Truncate();
    1745             : 
    1746             :   struct STAT symStat;
    1747        1092 :   if (LSTAT(mPath.get(), &symStat) == -1) {
    1748           0 :     return NSRESULT_FOR_ERRNO();
    1749             :   }
    1750             : 
    1751        1092 :   if (!S_ISLNK(symStat.st_mode)) {
    1752           0 :     return NS_ERROR_FILE_INVALID_PATH;
    1753             :   }
    1754             : 
    1755        1092 :   int32_t size = (int32_t)symStat.st_size;
    1756        2184 :   nsAutoCString target;
    1757        1092 :   if (!target.SetLength(size, mozilla::fallible)) {
    1758           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1759             :   }
    1760             : 
    1761        1092 :   if (readlink(mPath.get(), target.BeginWriting(), (size_t)size) < 0) {
    1762           0 :     return NSRESULT_FOR_ERRNO();
    1763             :   }
    1764             : 
    1765        1092 :   nsresult rv = NS_OK;
    1766        2184 :   nsCOMPtr<nsIFile> self(this);
    1767        1092 :   int32_t maxLinks = 40;
    1768             :   while (true) {
    1769        1092 :     if (maxLinks-- == 0) {
    1770           0 :       rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
    1771        1092 :       break;
    1772             :     }
    1773             : 
    1774        1092 :     if (target[0] != '/') {
    1775           0 :       nsCOMPtr<nsIFile> parent;
    1776           0 :       if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent)))) {
    1777           0 :         break;
    1778             :       }
    1779           0 :       if (NS_FAILED(rv = parent->AppendRelativeNativePath(target))) {
    1780           0 :         break;
    1781             :       }
    1782           0 :       if (NS_FAILED(rv = parent->GetNativePath(aResult))) {
    1783           0 :         break;
    1784             :       }
    1785           0 :       self = parent;
    1786             :     } else {
    1787        1092 :       aResult = target;
    1788             :     }
    1789             : 
    1790        1092 :     const nsPromiseFlatCString& flatRetval = PromiseFlatCString(aResult);
    1791             : 
    1792             :     // Any failure in testing the current target we'll just interpret
    1793             :     // as having reached our destiny.
    1794        1092 :     if (LSTAT(flatRetval.get(), &symStat) == -1) {
    1795           0 :       break;
    1796             :     }
    1797             : 
    1798             :     // And of course we're done if it isn't a symlink.
    1799        1092 :     if (!S_ISLNK(symStat.st_mode)) {
    1800        1092 :       break;
    1801             :     }
    1802             : 
    1803           0 :     int32_t newSize = (int32_t)symStat.st_size;
    1804           0 :     size = newSize;
    1805           0 :     nsAutoCString newTarget;
    1806           0 :     if (!newTarget.SetLength(size, mozilla::fallible)) {
    1807           0 :       rv = NS_ERROR_OUT_OF_MEMORY;
    1808           0 :       break;
    1809             :     }
    1810             : 
    1811           0 :     int32_t linkLen = readlink(flatRetval.get(), newTarget.BeginWriting(), size);
    1812           0 :     if (linkLen == -1) {
    1813           0 :       rv = NSRESULT_FOR_ERRNO();
    1814           0 :       break;
    1815             :     }
    1816           0 :     target = newTarget;
    1817           0 :   }
    1818             : 
    1819        1092 :   if (NS_FAILED(rv)) {
    1820           0 :     aResult.Truncate();
    1821             :   }
    1822        1092 :   return rv;
    1823             : }
    1824             : 
    1825             : NS_IMETHODIMP
    1826           0 : nsLocalFile::GetFollowLinks(bool* aFollowLinks)
    1827             : {
    1828           0 :   *aFollowLinks = true;
    1829           0 :   return NS_OK;
    1830             : }
    1831             : 
    1832             : NS_IMETHODIMP
    1833        5068 : nsLocalFile::SetFollowLinks(bool aFollowLinks)
    1834             : {
    1835        5068 :   return NS_OK;
    1836             : }
    1837             : 
    1838             : NS_IMETHODIMP
    1839          16 : nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator** aEntries)
    1840             : {
    1841          32 :   RefPtr<nsDirEnumeratorUnix> dir = new nsDirEnumeratorUnix();
    1842             : 
    1843          16 :   nsresult rv = dir->Init(this, false);
    1844          16 :   if (NS_FAILED(rv)) {
    1845           0 :     *aEntries = nullptr;
    1846             :   } else {
    1847          16 :     dir.forget(aEntries);
    1848             :   }
    1849             : 
    1850          32 :   return rv;
    1851             : }
    1852             : 
    1853             : NS_IMETHODIMP
    1854           0 : nsLocalFile::Load(PRLibrary** aResult)
    1855             : {
    1856           0 :   CHECK_mPath();
    1857           0 :   if (NS_WARN_IF(!aResult)) {
    1858           0 :     return NS_ERROR_INVALID_ARG;
    1859             :   }
    1860             : 
    1861             : #ifdef NS_BUILD_REFCNT_LOGGING
    1862           0 :   nsTraceRefcnt::SetActivityIsLegal(false);
    1863             : #endif
    1864             : 
    1865           0 :   *aResult = PR_LoadLibrary(mPath.get());
    1866             : 
    1867             : #ifdef NS_BUILD_REFCNT_LOGGING
    1868           0 :   nsTraceRefcnt::SetActivityIsLegal(true);
    1869             : #endif
    1870             : 
    1871           0 :   if (!*aResult) {
    1872           0 :     return NS_ERROR_FAILURE;
    1873             :   }
    1874           0 :   return NS_OK;
    1875             : }
    1876             : 
    1877             : NS_IMETHODIMP
    1878           0 : nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor)
    1879             : {
    1880           0 :   return GetNativePath(aPersistentDescriptor);
    1881             : }
    1882             : 
    1883             : NS_IMETHODIMP
    1884           0 : nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor)
    1885             : {
    1886             : #ifdef MOZ_WIDGET_COCOA
    1887             :   if (aPersistentDescriptor.IsEmpty()) {
    1888             :     return NS_ERROR_INVALID_ARG;
    1889             :   }
    1890             : 
    1891             :   // Support pathnames as user-supplied descriptors if they begin with '/'
    1892             :   // or '~'.  These characters do not collide with the base64 set used for
    1893             :   // encoding alias records.
    1894             :   char first = aPersistentDescriptor.First();
    1895             :   if (first == '/' || first == '~') {
    1896             :     return InitWithNativePath(aPersistentDescriptor);
    1897             :   }
    1898             : 
    1899             :   uint32_t dataSize = aPersistentDescriptor.Length();
    1900             :   char* decodedData = PL_Base64Decode(
    1901             :     PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nullptr);
    1902             :   if (!decodedData) {
    1903             :     NS_ERROR("SetPersistentDescriptor was given bad data");
    1904             :     return NS_ERROR_FAILURE;
    1905             :   }
    1906             : 
    1907             :   // Cast to an alias record and resolve.
    1908             :   AliasRecord aliasHeader = *(AliasPtr)decodedData;
    1909             :   int32_t aliasSize = ::GetAliasSizeFromPtr(&aliasHeader);
    1910             :   if (aliasSize > ((int32_t)dataSize * 3) / 4) { // be paranoid about having too few data
    1911             :     PR_Free(decodedData); // PL_Base64Decode() uses PR_Malloc().
    1912             :     return NS_ERROR_FAILURE;
    1913             :   }
    1914             : 
    1915             :   nsresult rv = NS_OK;
    1916             : 
    1917             :   // Move the now-decoded data into the Handle.
    1918             :   // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
    1919             :   Handle  newHandle = nullptr;
    1920             :   if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr) {
    1921             :     rv = NS_ERROR_OUT_OF_MEMORY;
    1922             :   }
    1923             :   PR_Free(decodedData); // PL_Base64Decode() uses PR_Malloc().
    1924             :   if (NS_FAILED(rv)) {
    1925             :     return rv;
    1926             :   }
    1927             : 
    1928             :   Boolean changed;
    1929             :   FSRef resolvedFSRef;
    1930             :   OSErr err = ::FSResolveAlias(nullptr, (AliasHandle)newHandle, &resolvedFSRef,
    1931             :                                &changed);
    1932             : 
    1933             :   rv = MacErrorMapper(err);
    1934             :   DisposeHandle(newHandle);
    1935             :   if (NS_FAILED(rv)) {
    1936             :     return rv;
    1937             :   }
    1938             : 
    1939             :   return InitWithFSRef(&resolvedFSRef);
    1940             : #else
    1941           0 :   return InitWithNativePath(aPersistentDescriptor);
    1942             : #endif
    1943             : }
    1944             : 
    1945             : NS_IMETHODIMP
    1946           0 : nsLocalFile::Reveal()
    1947             : {
    1948             : #ifdef MOZ_WIDGET_GTK
    1949           0 :   nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
    1950           0 :   if (!giovfs) {
    1951           0 :     return NS_ERROR_FAILURE;
    1952             :   }
    1953             : 
    1954             :   bool isDirectory;
    1955           0 :   if (NS_FAILED(IsDirectory(&isDirectory))) {
    1956           0 :     return NS_ERROR_FAILURE;
    1957             :   }
    1958             : 
    1959           0 :   if (isDirectory) {
    1960           0 :     return giovfs->ShowURIForInput(mPath);
    1961             :   }
    1962           0 :   if (NS_SUCCEEDED(giovfs->OrgFreedesktopFileManager1ShowItems(mPath))) {
    1963           0 :     return NS_OK;
    1964             :   }
    1965           0 :   nsCOMPtr<nsIFile> parentDir;
    1966           0 :   nsAutoCString dirPath;
    1967           0 :   if (NS_FAILED(GetParent(getter_AddRefs(parentDir)))) {
    1968           0 :     return NS_ERROR_FAILURE;
    1969             :   }
    1970           0 :   if (NS_FAILED(parentDir->GetNativePath(dirPath))) {
    1971           0 :     return NS_ERROR_FAILURE;
    1972             :   }
    1973             : 
    1974           0 :   return giovfs->ShowURIForInput(dirPath);
    1975             : #elif defined(MOZ_WIDGET_COCOA)
    1976             :   CFURLRef url;
    1977             :   if (NS_SUCCEEDED(GetCFURL(&url))) {
    1978             :     nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
    1979             :     ::CFRelease(url);
    1980             :     return rv;
    1981             :   }
    1982             :   return NS_ERROR_FAILURE;
    1983             : #else
    1984             :   return NS_ERROR_FAILURE;
    1985             : #endif
    1986             : }
    1987             : 
    1988             : NS_IMETHODIMP
    1989           0 : nsLocalFile::Launch()
    1990             : {
    1991             : #ifdef MOZ_WIDGET_GTK
    1992           0 :   nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
    1993           0 :   if (!giovfs) {
    1994           0 :     return NS_ERROR_FAILURE;
    1995             :   }
    1996             : 
    1997           0 :   return giovfs->ShowURIForInput(mPath);
    1998             : #elif defined(MOZ_ENABLE_CONTENTACTION)
    1999             :   QUrl uri = QUrl::fromLocalFile(QString::fromUtf8(mPath.get()));
    2000             :   ContentAction::Action action =
    2001             :     ContentAction::Action::defaultActionForFile(uri);
    2002             : 
    2003             :   if (action.isValid()) {
    2004             :     action.trigger();
    2005             :     return NS_OK;
    2006             :   }
    2007             : 
    2008             :   return NS_ERROR_FAILURE;
    2009             : #elif defined(MOZ_WIDGET_ANDROID)
    2010             :   // Try to get a mimetype, if this fails just use the file uri alone
    2011             :   nsresult rv;
    2012             :   nsAutoCString type;
    2013             :   nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv));
    2014             :   if (NS_SUCCEEDED(rv)) {
    2015             :     rv = mimeService->GetTypeFromFile(this, type);
    2016             :   }
    2017             : 
    2018             :   nsAutoCString fileUri = NS_LITERAL_CSTRING("file://") + mPath;
    2019             :   return java::GeckoAppShell::OpenUriExternal(
    2020             :     NS_ConvertUTF8toUTF16(fileUri),
    2021             :     NS_ConvertUTF8toUTF16(type),
    2022             :     EmptyString(),
    2023             :     EmptyString(),
    2024             :     EmptyString(),
    2025             :     EmptyString()) ? NS_OK : NS_ERROR_FAILURE;
    2026             : #elif defined(MOZ_WIDGET_COCOA)
    2027             :   CFURLRef url;
    2028             :   if (NS_SUCCEEDED(GetCFURL(&url))) {
    2029             :     nsresult rv = CocoaFileUtils::OpenURL(url);
    2030             :     ::CFRelease(url);
    2031             :     return rv;
    2032             :   }
    2033             :   return NS_ERROR_FAILURE;
    2034             : #else
    2035             :   return NS_ERROR_FAILURE;
    2036             : #endif
    2037             : }
    2038             : 
    2039             : nsresult
    2040        5057 : NS_NewNativeLocalFile(const nsACString& aPath, bool aFollowSymlinks,
    2041             :                       nsIFile** aResult)
    2042             : {
    2043       10114 :   RefPtr<nsLocalFile> file = new nsLocalFile();
    2044             : 
    2045        5057 :   file->SetFollowLinks(aFollowSymlinks);
    2046             : 
    2047        5057 :   if (!aPath.IsEmpty()) {
    2048        2400 :     nsresult rv = file->InitWithNativePath(aPath);
    2049        2400 :     if (NS_FAILED(rv)) {
    2050           4 :       return rv;
    2051             :     }
    2052             :   }
    2053        5053 :   file.forget(aResult);
    2054        5053 :   return NS_OK;
    2055             : }
    2056             : 
    2057             : //-----------------------------------------------------------------------------
    2058             : // unicode support
    2059             : //-----------------------------------------------------------------------------
    2060             : 
    2061             : #define SET_UCS(func, ucsArg) \
    2062             :     { \
    2063             :         nsAutoCString buf; \
    2064             :         nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
    2065             :         if (NS_FAILED(rv)) \
    2066             :             return rv; \
    2067             :         return (func)(buf); \
    2068             :     }
    2069             : 
    2070             : #define GET_UCS(func, ucsArg) \
    2071             :     { \
    2072             :         nsAutoCString buf; \
    2073             :         nsresult rv = (func)(buf); \
    2074             :         if (NS_FAILED(rv)) return rv; \
    2075             :         return NS_CopyNativeToUnicode(buf, ucsArg); \
    2076             :     }
    2077             : 
    2078             : #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
    2079             :     { \
    2080             :         nsAutoCString buf; \
    2081             :         nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
    2082             :         if (NS_FAILED(rv)) \
    2083             :             return rv; \
    2084             :         return (func)(opaqueArg, buf); \
    2085             :     }
    2086             : 
    2087             : // Unicode interface Wrapper
    2088             : nsresult
    2089          30 : nsLocalFile::InitWithPath(const nsAString& aFilePath)
    2090             : {
    2091          30 :   SET_UCS(InitWithNativePath, aFilePath);
    2092             : }
    2093             : nsresult
    2094         144 : nsLocalFile::Append(const nsAString& aNode)
    2095             : {
    2096         144 :   SET_UCS(AppendNative, aNode);
    2097             : }
    2098             : nsresult
    2099          24 : nsLocalFile::AppendRelativePath(const nsAString& aNode)
    2100             : {
    2101          24 :   SET_UCS(AppendRelativeNativePath, aNode);
    2102             : }
    2103             : nsresult
    2104         223 : nsLocalFile::GetLeafName(nsAString& aLeafName)
    2105             : {
    2106         223 :   GET_UCS(GetNativeLeafName, aLeafName);
    2107             : }
    2108             : nsresult
    2109           1 : nsLocalFile::SetLeafName(const nsAString& aLeafName)
    2110             : {
    2111           1 :   SET_UCS(SetNativeLeafName, aLeafName);
    2112             : }
    2113             : nsresult
    2114         312 : nsLocalFile::GetPath(nsAString& aResult)
    2115             : {
    2116         312 :   return NS_CopyNativeToUnicode(mPath, aResult);
    2117             : }
    2118             : nsresult
    2119           0 : nsLocalFile::CopyTo(nsIFile* aNewParentDir, const nsAString& aNewName)
    2120             : {
    2121           0 :   SET_UCS_2ARGS_2(CopyToNative , aNewParentDir, aNewName);
    2122             : }
    2123             : nsresult
    2124           0 : nsLocalFile::CopyToFollowingLinks(nsIFile* aNewParentDir,
    2125             :                                   const nsAString& aNewName)
    2126             : {
    2127           0 :   SET_UCS_2ARGS_2(CopyToFollowingLinksNative , aNewParentDir, aNewName);
    2128             : }
    2129             : nsresult
    2130           8 : nsLocalFile::MoveTo(nsIFile* aNewParentDir, const nsAString& aNewName)
    2131             : {
    2132           8 :   SET_UCS_2ARGS_2(MoveToNative, aNewParentDir, aNewName);
    2133             : }
    2134             : 
    2135             : NS_IMETHODIMP
    2136           0 : nsLocalFile::RenameTo(nsIFile* aNewParentDir, const nsAString& aNewName)
    2137             : {
    2138           0 :   SET_UCS_2ARGS_2(RenameToNative, aNewParentDir, aNewName);
    2139             : }
    2140             : 
    2141             : NS_IMETHODIMP
    2142           3 : nsLocalFile::RenameToNative(nsIFile* aNewParentDir, const nsACString& aNewName)
    2143             : {
    2144             :   nsresult rv;
    2145             : 
    2146             :   // check to make sure that this has been initialized properly
    2147           3 :   CHECK_mPath();
    2148             : 
    2149             :   // check to make sure that we have a new parent
    2150           6 :   nsAutoCString newPathName;
    2151           3 :   rv = GetNativeTargetPathName(aNewParentDir, aNewName, newPathName);
    2152           3 :   if (NS_FAILED(rv)) {
    2153           0 :     return rv;
    2154             :   }
    2155             : 
    2156             :   // try for atomic rename
    2157           3 :   if (rename(mPath.get(), newPathName.get()) < 0) {
    2158           0 :     if (errno == EXDEV) {
    2159           0 :       rv = NS_ERROR_FILE_ACCESS_DENIED;
    2160             :     } else {
    2161           0 :       rv = NSRESULT_FOR_ERRNO();
    2162             :     }
    2163             :   }
    2164             : 
    2165           3 :   return rv;
    2166             : }
    2167             : 
    2168             : nsresult
    2169           0 : nsLocalFile::GetTarget(nsAString& aResult)
    2170             : {
    2171           0 :   GET_UCS(GetNativeTarget, aResult);
    2172             : }
    2173             : 
    2174             : // nsIHashable
    2175             : 
    2176             : NS_IMETHODIMP
    2177           0 : nsLocalFile::Equals(nsIHashable* aOther, bool* aResult)
    2178             : {
    2179           0 :   nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
    2180           0 :   if (!otherFile) {
    2181           0 :     *aResult = false;
    2182           0 :     return NS_OK;
    2183             :   }
    2184             : 
    2185           0 :   return Equals(otherFile, aResult);
    2186             : }
    2187             : 
    2188             : NS_IMETHODIMP
    2189           0 : nsLocalFile::GetHashCode(uint32_t* aResult)
    2190             : {
    2191           0 :   *aResult = HashString(mPath);
    2192           0 :   return NS_OK;
    2193             : }
    2194             : 
    2195             : nsresult
    2196           9 : NS_NewLocalFile(const nsAString& aPath, bool aFollowLinks, nsIFile** aResult)
    2197             : {
    2198          18 :   nsAutoCString buf;
    2199           9 :   nsresult rv = NS_CopyUnicodeToNative(aPath, buf);
    2200           9 :   if (NS_FAILED(rv)) {
    2201           0 :     return rv;
    2202             :   }
    2203           9 :   return NS_NewNativeLocalFile(buf, aFollowLinks, aResult);
    2204             : }
    2205             : 
    2206             : //-----------------------------------------------------------------------------
    2207             : // global init/shutdown
    2208             : //-----------------------------------------------------------------------------
    2209             : 
    2210             : void
    2211           3 : nsLocalFile::GlobalInit()
    2212             : {
    2213           3 : }
    2214             : 
    2215             : void
    2216           0 : nsLocalFile::GlobalShutdown()
    2217             : {
    2218           0 : }
    2219             : 
    2220             : // nsILocalFileMac
    2221             : 
    2222             : #ifdef MOZ_WIDGET_COCOA
    2223             : 
    2224             : static nsresult MacErrorMapper(OSErr inErr)
    2225             : {
    2226             :   nsresult outErr;
    2227             : 
    2228             :   switch (inErr) {
    2229             :     case noErr:
    2230             :       outErr = NS_OK;
    2231             :       break;
    2232             : 
    2233             :     case fnfErr:
    2234             :     case afpObjectNotFound:
    2235             :     case afpDirNotFound:
    2236             :       outErr = NS_ERROR_FILE_NOT_FOUND;
    2237             :       break;
    2238             : 
    2239             :     case dupFNErr:
    2240             :     case afpObjectExists:
    2241             :       outErr = NS_ERROR_FILE_ALREADY_EXISTS;
    2242             :       break;
    2243             : 
    2244             :     case dskFulErr:
    2245             :     case afpDiskFull:
    2246             :       outErr = NS_ERROR_FILE_DISK_FULL;
    2247             :       break;
    2248             : 
    2249             :     case fLckdErr:
    2250             :     case afpVolLocked:
    2251             :       outErr = NS_ERROR_FILE_IS_LOCKED;
    2252             :       break;
    2253             : 
    2254             :     case afpAccessDenied:
    2255             :       outErr = NS_ERROR_FILE_ACCESS_DENIED;
    2256             :       break;
    2257             : 
    2258             :     case afpDirNotEmpty:
    2259             :       outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
    2260             :       break;
    2261             : 
    2262             :     // Can't find good map for some
    2263             :     case bdNamErr:
    2264             :       outErr = NS_ERROR_FAILURE;
    2265             :       break;
    2266             : 
    2267             :     default:
    2268             :       outErr = NS_ERROR_FAILURE;
    2269             :       break;
    2270             :   }
    2271             : 
    2272             :   return outErr;
    2273             : }
    2274             : 
    2275             : static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
    2276             : {
    2277             :   // first see if the conversion would succeed and find the length of the result
    2278             :   CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
    2279             :   CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
    2280             :                                               kCFStringEncodingUTF8, 0, false,
    2281             :                                               nullptr, 0, &usedBufLen);
    2282             :   if (charsConverted == inStrLen) {
    2283             :     // all characters converted, do the actual conversion
    2284             :     aOutStr.SetLength(usedBufLen);
    2285             :     if (aOutStr.Length() != (unsigned int)usedBufLen) {
    2286             :       return NS_ERROR_OUT_OF_MEMORY;
    2287             :     }
    2288             :     UInt8* buffer = (UInt8*)aOutStr.BeginWriting();
    2289             :     ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8,
    2290             :                        0, false, buffer, usedBufLen, &usedBufLen);
    2291             :     return NS_OK;
    2292             :   }
    2293             : 
    2294             :   return NS_ERROR_FAILURE;
    2295             : }
    2296             : 
    2297             : NS_IMETHODIMP
    2298             : nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
    2299             : {
    2300             :   UInt8 path[PATH_MAX];
    2301             :   if (::CFURLGetFileSystemRepresentation(aCFURL, true, path, PATH_MAX)) {
    2302             :     nsDependentCString nativePath((char*)path);
    2303             :     return InitWithNativePath(nativePath);
    2304             :   }
    2305             : 
    2306             :   return NS_ERROR_FAILURE;
    2307             : }
    2308             : 
    2309             : NS_IMETHODIMP
    2310             : nsLocalFile::InitWithFSRef(const FSRef* aFSRef)
    2311             : {
    2312             :   if (NS_WARN_IF(!aFSRef)) {
    2313             :     return NS_ERROR_INVALID_ARG;
    2314             :   }
    2315             : 
    2316             :   CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
    2317             :   if (newURLRef) {
    2318             :     nsresult rv = InitWithCFURL(newURLRef);
    2319             :     ::CFRelease(newURLRef);
    2320             :     return rv;
    2321             :   }
    2322             : 
    2323             :   return NS_ERROR_FAILURE;
    2324             : }
    2325             : 
    2326             : NS_IMETHODIMP
    2327             : nsLocalFile::GetCFURL(CFURLRef* aResult)
    2328             : {
    2329             :   CHECK_mPath();
    2330             : 
    2331             :   bool isDir;
    2332             :   IsDirectory(&isDir);
    2333             :   *aResult = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
    2334             :                                                        (UInt8*)mPath.get(),
    2335             :                                                        mPath.Length(),
    2336             :                                                        isDir);
    2337             : 
    2338             :   return (*aResult ? NS_OK : NS_ERROR_FAILURE);
    2339             : }
    2340             : 
    2341             : NS_IMETHODIMP
    2342             : nsLocalFile::GetFSRef(FSRef* aResult)
    2343             : {
    2344             :   if (NS_WARN_IF(!aResult)) {
    2345             :     return NS_ERROR_INVALID_ARG;
    2346             :   }
    2347             : 
    2348             :   nsresult rv = NS_ERROR_FAILURE;
    2349             : 
    2350             :   CFURLRef url = nullptr;
    2351             :   if (NS_SUCCEEDED(GetCFURL(&url))) {
    2352             :     if (::CFURLGetFSRef(url, aResult)) {
    2353             :       rv = NS_OK;
    2354             :     }
    2355             :     ::CFRelease(url);
    2356             :   }
    2357             : 
    2358             :   return rv;
    2359             : }
    2360             : 
    2361             : NS_IMETHODIMP
    2362             : nsLocalFile::GetFSSpec(FSSpec* aResult)
    2363             : {
    2364             :   if (NS_WARN_IF(!aResult)) {
    2365             :     return NS_ERROR_INVALID_ARG;
    2366             :   }
    2367             : 
    2368             :   FSRef fsRef;
    2369             :   nsresult rv = GetFSRef(&fsRef);
    2370             :   if (NS_SUCCEEDED(rv)) {
    2371             :     OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nullptr, nullptr,
    2372             :                                    aResult, nullptr);
    2373             :     return MacErrorMapper(err);
    2374             :   }
    2375             : 
    2376             :   return rv;
    2377             : }
    2378             : 
    2379             : NS_IMETHODIMP
    2380             : nsLocalFile::GetFileSizeWithResFork(int64_t* aFileSizeWithResFork)
    2381             : {
    2382             :   if (NS_WARN_IF(!aFileSizeWithResFork)) {
    2383             :     return NS_ERROR_INVALID_ARG;
    2384             :   }
    2385             : 
    2386             :   FSRef fsRef;
    2387             :   nsresult rv = GetFSRef(&fsRef);
    2388             :   if (NS_FAILED(rv)) {
    2389             :     return rv;
    2390             :   }
    2391             : 
    2392             :   FSCatalogInfo catalogInfo;
    2393             :   OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
    2394             :                                  &catalogInfo, nullptr, nullptr, nullptr);
    2395             :   if (err != noErr) {
    2396             :     return MacErrorMapper(err);
    2397             :   }
    2398             : 
    2399             :   *aFileSizeWithResFork =
    2400             :     catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
    2401             :   return NS_OK;
    2402             : }
    2403             : 
    2404             : NS_IMETHODIMP
    2405             : nsLocalFile::GetFileType(OSType* aFileType)
    2406             : {
    2407             :   CFURLRef url;
    2408             :   if (NS_SUCCEEDED(GetCFURL(&url))) {
    2409             :     nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType);
    2410             :     ::CFRelease(url);
    2411             :     return rv;
    2412             :   }
    2413             :   return NS_ERROR_FAILURE;
    2414             : }
    2415             : 
    2416             : NS_IMETHODIMP
    2417             : nsLocalFile::SetFileType(OSType aFileType)
    2418             : {
    2419             :   CFURLRef url;
    2420             :   if (NS_SUCCEEDED(GetCFURL(&url))) {
    2421             :     nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType);
    2422             :     ::CFRelease(url);
    2423             :     return rv;
    2424             :   }
    2425             :   return NS_ERROR_FAILURE;
    2426             : }
    2427             : 
    2428             : NS_IMETHODIMP
    2429             : nsLocalFile::GetFileCreator(OSType* aFileCreator)
    2430             : {
    2431             :   CFURLRef url;
    2432             :   if (NS_SUCCEEDED(GetCFURL(&url))) {
    2433             :     nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator);
    2434             :     ::CFRelease(url);
    2435             :     return rv;
    2436             :   }
    2437             :   return NS_ERROR_FAILURE;
    2438             : }
    2439             : 
    2440             : NS_IMETHODIMP
    2441             : nsLocalFile::SetFileCreator(OSType aFileCreator)
    2442             : {
    2443             :   CFURLRef url;
    2444             :   if (NS_SUCCEEDED(GetCFURL(&url))) {
    2445             :     nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator);
    2446             :     ::CFRelease(url);
    2447             :     return rv;
    2448             :   }
    2449             :   return NS_ERROR_FAILURE;
    2450             : }
    2451             : 
    2452             : NS_IMETHODIMP
    2453             : nsLocalFile::LaunchWithDoc(nsIFile* aDocToLoad, bool aLaunchInBackground)
    2454             : {
    2455             :   bool isExecutable;
    2456             :   nsresult rv = IsExecutable(&isExecutable);
    2457             :   if (NS_FAILED(rv)) {
    2458             :     return rv;
    2459             :   }
    2460             :   if (!isExecutable) {
    2461             :     return NS_ERROR_FILE_EXECUTION_FAILED;
    2462             :   }
    2463             : 
    2464             :   FSRef appFSRef, docFSRef;
    2465             :   rv = GetFSRef(&appFSRef);
    2466             :   if (NS_FAILED(rv)) {
    2467             :     return rv;
    2468             :   }
    2469             : 
    2470             :   if (aDocToLoad) {
    2471             :     nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
    2472             :     rv = macDoc->GetFSRef(&docFSRef);
    2473             :     if (NS_FAILED(rv)) {
    2474             :       return rv;
    2475             :     }
    2476             :   }
    2477             : 
    2478             :   LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
    2479             :   LSLaunchFSRefSpec thelaunchSpec;
    2480             : 
    2481             :   if (aLaunchInBackground) {
    2482             :     theLaunchFlags |= kLSLaunchDontSwitch;
    2483             :   }
    2484             :   memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
    2485             : 
    2486             :   thelaunchSpec.appRef = &appFSRef;
    2487             :   if (aDocToLoad) {
    2488             :     thelaunchSpec.numDocs = 1;
    2489             :     thelaunchSpec.itemRefs = &docFSRef;
    2490             :   }
    2491             :   thelaunchSpec.launchFlags = theLaunchFlags;
    2492             : 
    2493             :   OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
    2494             :   if (err != noErr) {
    2495             :     return MacErrorMapper(err);
    2496             :   }
    2497             : 
    2498             :   return NS_OK;
    2499             : }
    2500             : 
    2501             : NS_IMETHODIMP
    2502             : nsLocalFile::OpenDocWithApp(nsIFile* aAppToOpenWith, bool aLaunchInBackground)
    2503             : {
    2504             :   FSRef docFSRef;
    2505             :   nsresult rv = GetFSRef(&docFSRef);
    2506             :   if (NS_FAILED(rv)) {
    2507             :     return rv;
    2508             :   }
    2509             : 
    2510             :   if (!aAppToOpenWith) {
    2511             :     OSErr err = ::LSOpenFSRef(&docFSRef, nullptr);
    2512             :     return MacErrorMapper(err);
    2513             :   }
    2514             : 
    2515             :   nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
    2516             :   if (!appFileMac) {
    2517             :     return rv;
    2518             :   }
    2519             : 
    2520             :   bool isExecutable;
    2521             :   rv = appFileMac->IsExecutable(&isExecutable);
    2522             :   if (NS_FAILED(rv)) {
    2523             :     return rv;
    2524             :   }
    2525             :   if (!isExecutable) {
    2526             :     return NS_ERROR_FILE_EXECUTION_FAILED;
    2527             :   }
    2528             : 
    2529             :   FSRef appFSRef;
    2530             :   rv = appFileMac->GetFSRef(&appFSRef);
    2531             :   if (NS_FAILED(rv)) {
    2532             :     return rv;
    2533             :   }
    2534             : 
    2535             :   LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
    2536             :   LSLaunchFSRefSpec thelaunchSpec;
    2537             : 
    2538             :   if (aLaunchInBackground) {
    2539             :     theLaunchFlags |= kLSLaunchDontSwitch;
    2540             :   }
    2541             :   memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
    2542             : 
    2543             :   thelaunchSpec.appRef = &appFSRef;
    2544             :   thelaunchSpec.numDocs = 1;
    2545             :   thelaunchSpec.itemRefs = &docFSRef;
    2546             :   thelaunchSpec.launchFlags = theLaunchFlags;
    2547             : 
    2548             :   OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
    2549             :   if (err != noErr) {
    2550             :     return MacErrorMapper(err);
    2551             :   }
    2552             : 
    2553             :   return NS_OK;
    2554             : }
    2555             : 
    2556             : NS_IMETHODIMP
    2557             : nsLocalFile::IsPackage(bool* aResult)
    2558             : {
    2559             :   if (NS_WARN_IF(!aResult)) {
    2560             :     return NS_ERROR_INVALID_ARG;
    2561             :   }
    2562             :   *aResult = false;
    2563             : 
    2564             :   CFURLRef url;
    2565             :   nsresult rv = GetCFURL(&url);
    2566             :   if (NS_FAILED(rv)) {
    2567             :     return rv;
    2568             :   }
    2569             : 
    2570             :   LSItemInfoRecord info;
    2571             :   OSStatus status = ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info);
    2572             : 
    2573             :   ::CFRelease(url);
    2574             : 
    2575             :   if (status != noErr) {
    2576             :     return NS_ERROR_FAILURE;
    2577             :   }
    2578             : 
    2579             :   *aResult = !!(info.flags & kLSItemInfoIsPackage);
    2580             : 
    2581             :   return NS_OK;
    2582             : }
    2583             : 
    2584             : NS_IMETHODIMP
    2585             : nsLocalFile::GetBundleDisplayName(nsAString& aOutBundleName)
    2586             : {
    2587             :   bool isPackage = false;
    2588             :   nsresult rv = IsPackage(&isPackage);
    2589             :   if (NS_FAILED(rv) || !isPackage) {
    2590             :     return NS_ERROR_FAILURE;
    2591             :   }
    2592             : 
    2593             :   nsAutoString name;
    2594             :   rv = GetLeafName(name);
    2595             :   if (NS_FAILED(rv)) {
    2596             :     return rv;
    2597             :   }
    2598             : 
    2599             :   int32_t length = name.Length();
    2600             :   if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
    2601             :     // 4 characters in ".app"
    2602             :     aOutBundleName = Substring(name, 0, length - 4);
    2603             :   } else {
    2604             :     aOutBundleName = name;
    2605             :   }
    2606             : 
    2607             :   return NS_OK;
    2608             : }
    2609             : 
    2610             : NS_IMETHODIMP
    2611             : nsLocalFile::GetBundleIdentifier(nsACString& aOutBundleIdentifier)
    2612             : {
    2613             :   nsresult rv = NS_ERROR_FAILURE;
    2614             : 
    2615             :   CFURLRef urlRef;
    2616             :   if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
    2617             :     CFBundleRef bundle = ::CFBundleCreate(nullptr, urlRef);
    2618             :     if (bundle) {
    2619             :       CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
    2620             :       if (bundleIdentifier) {
    2621             :         rv = CFStringReftoUTF8(bundleIdentifier, aOutBundleIdentifier);
    2622             :       }
    2623             :       ::CFRelease(bundle);
    2624             :     }
    2625             :     ::CFRelease(urlRef);
    2626             :   }
    2627             : 
    2628             :   return rv;
    2629             : }
    2630             : 
    2631             : NS_IMETHODIMP
    2632             : nsLocalFile::GetBundleContentsLastModifiedTime(int64_t* aLastModTime)
    2633             : {
    2634             :   CHECK_mPath();
    2635             :   if (NS_WARN_IF(!aLastModTime)) {
    2636             :     return NS_ERROR_INVALID_ARG;
    2637             :   }
    2638             : 
    2639             :   bool isPackage = false;
    2640             :   nsresult rv = IsPackage(&isPackage);
    2641             :   if (NS_FAILED(rv) || !isPackage) {
    2642             :     return GetLastModifiedTime(aLastModTime);
    2643             :   }
    2644             : 
    2645             :   nsAutoCString infoPlistPath(mPath);
    2646             :   infoPlistPath.AppendLiteral("/Contents/Info.plist");
    2647             :   PRFileInfo64 info;
    2648             :   if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) {
    2649             :     return GetLastModifiedTime(aLastModTime);
    2650             :   }
    2651             :   int64_t modTime = int64_t(info.modifyTime);
    2652             :   if (modTime == 0) {
    2653             :     *aLastModTime = 0;
    2654             :   } else {
    2655             :     *aLastModTime = modTime / int64_t(PR_USEC_PER_MSEC);
    2656             :   }
    2657             : 
    2658             :   return NS_OK;
    2659             : }
    2660             : 
    2661             : NS_IMETHODIMP nsLocalFile::InitWithFile(nsIFile* aFile)
    2662             : {
    2663             :   if (NS_WARN_IF(!aFile)) {
    2664             :     return NS_ERROR_INVALID_ARG;
    2665             :   }
    2666             : 
    2667             :   nsAutoCString nativePath;
    2668             :   nsresult rv = aFile->GetNativePath(nativePath);
    2669             :   if (NS_FAILED(rv)) {
    2670             :     return rv;
    2671             :   }
    2672             : 
    2673             :   return InitWithNativePath(nativePath);
    2674             : }
    2675             : 
    2676             : nsresult
    2677             : NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks,
    2678             :                          nsILocalFileMac** aResult)
    2679             : {
    2680             :   RefPtr<nsLocalFile> file = new nsLocalFile();
    2681             : 
    2682             :   file->SetFollowLinks(aFollowLinks);
    2683             : 
    2684             :   nsresult rv = file->InitWithFSRef(aFSRef);
    2685             :   if (NS_FAILED(rv)) {
    2686             :     return rv;
    2687             :   }
    2688             :   file.forget(aResult);
    2689             :   return NS_OK;
    2690             : }
    2691             : 
    2692             : nsresult
    2693             : NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks,
    2694             :                          nsILocalFileMac** aResult)
    2695             : {
    2696             :   RefPtr<nsLocalFile> file = new nsLocalFile();
    2697             : 
    2698             :   file->SetFollowLinks(aFollowLinks);
    2699             : 
    2700             :   nsresult rv = file->InitWithCFURL(aURL);
    2701             :   if (NS_FAILED(rv)) {
    2702             :     return rv;
    2703             :   }
    2704             :   file.forget(aResult);
    2705             :   return NS_OK;
    2706             : }
    2707             : 
    2708             : #endif

Generated by: LCOV version 1.13