xpcom/io/nsLocalFileWin.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/ArrayUtils.h"
     7 #include "mozilla/DebugOnly.h"
     8 #include "mozilla/WindowsVersion.h"
    10 #include "nsCOMPtr.h"
    11 #include "nsAutoPtr.h"
    12 #include "nsMemory.h"
    14 #include "nsLocalFile.h"
    15 #include "nsIDirectoryEnumerator.h"
    16 #include "nsNativeCharsetUtils.h"
    18 #include "nsISimpleEnumerator.h"
    19 #include "nsIComponentManager.h"
    20 #include "prio.h"
    21 #include "private/pprio.h"  // To get PR_ImportFile
    22 #include "prprf.h"
    23 #include "prmem.h"
    24 #include "nsHashKeys.h"
    26 #include "nsXPIDLString.h"
    27 #include "nsReadableUtils.h"
    29 #include <direct.h>
    30 #include <windows.h>
    31 #include <shlwapi.h>
    32 #include <aclapi.h>
    34 #include "shellapi.h"
    35 #include "shlguid.h"
    37 #include  <io.h>
    38 #include  <stdio.h>
    39 #include  <stdlib.h>
    40 #include  <mbstring.h>
    42 #include "nsXPIDLString.h"
    43 #include "prproces.h"
    44 #include "prlink.h"
    46 #include "mozilla/Mutex.h"
    47 #include "SpecialSystemDirectory.h"
    49 #include "nsTraceRefcnt.h"
    50 #include "nsXPCOMCIDInternal.h"
    51 #include "nsThreadUtils.h"
    52 #include "nsXULAppAPI.h"
    54 using namespace mozilla;
    56 #define CHECK_mWorkingPath()                    \
    57     PR_BEGIN_MACRO                              \
    58         if (mWorkingPath.IsEmpty())             \
    59             return NS_ERROR_NOT_INITIALIZED;    \
    60     PR_END_MACRO
    62 // CopyFileEx only supports unbuffered I/O in Windows Vista and above
    63 #ifndef COPY_FILE_NO_BUFFERING
    64 #define COPY_FILE_NO_BUFFERING 0x00001000
    65 #endif
    67 #ifndef FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
    68 #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  0x00002000
    69 #endif
    71 #ifndef DRIVE_REMOTE
    72 #define DRIVE_REMOTE 4
    73 #endif
    75 /**
    76  * A runnable to dispatch back to the main thread when 
    77  * AsyncLocalFileWinOperation completes.
    78 */
    79 class AsyncLocalFileWinDone : public nsRunnable
    80 {
    81 public:
    82     AsyncLocalFileWinDone() :
    83         mWorkerThread(do_GetCurrentThread())
    84     {
    85         // Objects of this type must only be created on worker threads
    86         MOZ_ASSERT(!NS_IsMainThread()); 
    87     }
    89     NS_IMETHOD Run() {
    90         // This event shuts down the worker thread and so must be main thread.
    91         MOZ_ASSERT(NS_IsMainThread());
    93         // If we don't destroy the thread when we're done with it, it will hang
    94         // around forever... and that is bad!
    95         mWorkerThread->Shutdown();
    96         return NS_OK;
    97     }
    99 private:
   100     nsCOMPtr<nsIThread> mWorkerThread;
   101 };
   103 /**
   104  * A runnable to dispatch from the main thread when an async operation should
   105  * be performed. 
   106 */
   107 class AsyncLocalFileWinOperation : public nsRunnable
   108 {
   109 public:
   110     enum FileOp { RevealOp, LaunchOp };
   112     AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::FileOp aOperation,
   113                                const nsAString &aResolvedPath) : 
   114         mOperation(aOperation),
   115         mResolvedPath(aResolvedPath)
   116     {
   117     }
   119     NS_IMETHOD Run() {
   120         MOZ_ASSERT(!NS_IsMainThread(),
   121             "AsyncLocalFileWinOperation should not be run on the main thread!");
   123         CoInitialize(nullptr);
   124         switch(mOperation) {
   125         case RevealOp: {
   126             Reveal();
   127         }
   128         break;
   129         case LaunchOp: {
   130             Launch();
   131         }
   132         break;
   133         }
   134         CoUninitialize();
   136         // Send the result back to the main thread so that it can shutdown
   137         nsCOMPtr<nsIRunnable> resultrunnable = new AsyncLocalFileWinDone();
   138         NS_DispatchToMainThread(resultrunnable);
   139         return NS_OK;
   140     }
   142 private:
   143     // Reveals the path in explorer.
   144     nsresult Reveal() 
   145     {
   146         DWORD attributes = GetFileAttributesW(mResolvedPath.get());
   147         if (INVALID_FILE_ATTRIBUTES == attributes) {
   148             return NS_ERROR_FILE_INVALID_PATH;
   149         }
   151         HRESULT hr;
   152         if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
   153             // We have a directory so we should open the directory itself.
   154             ITEMIDLIST *dir = ILCreateFromPathW(mResolvedPath.get());
   155             if (!dir) {
   156               return NS_ERROR_FAILURE;
   157             }
   159             const ITEMIDLIST* selection[] = { dir };
   160             UINT count = ArrayLength(selection);
   162             //Perform the open of the directory.
   163             hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
   164             CoTaskMemFree(dir);
   165         } else {
   166             int32_t len = mResolvedPath.Length();
   167             // We don't currently handle UNC long paths of the form \\?\ anywhere so
   168             // this should be fine.
   169             if (len > MAX_PATH) {
   170                 return NS_ERROR_FILE_INVALID_PATH;
   171             }
   172             WCHAR parentDirectoryPath[MAX_PATH + 1] = { 0 };
   173             wcsncpy(parentDirectoryPath, mResolvedPath.get(), MAX_PATH);
   174             PathRemoveFileSpecW(parentDirectoryPath);
   176             // We have a file so we should open the parent directory.
   177             ITEMIDLIST *dir = ILCreateFromPathW(parentDirectoryPath);
   178             if (!dir) {
   179                 return NS_ERROR_FAILURE;
   180             }
   182             // Set the item in the directory to select to the file we want to reveal.
   183             ITEMIDLIST *item = ILCreateFromPathW(mResolvedPath.get());
   184             if (!item) {
   185                 CoTaskMemFree(dir);
   186                 return NS_ERROR_FAILURE;
   187             }
   189             const ITEMIDLIST* selection[] = { item };
   190             UINT count = ArrayLength(selection);
   192             //Perform the selection of the file.
   193             hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
   195             CoTaskMemFree(dir);
   196             CoTaskMemFree(item);
   197         }
   199         return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
   200     }
   202     // Launches the default shell operation for the file path
   203     nsresult Launch()
   204     {
   205         // use the app registry name to launch a shell execute....
   206         SHELLEXECUTEINFOW seinfo;
   207         memset(&seinfo, 0, sizeof(seinfo));
   208         seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
   209         if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
   210           seinfo.fMask  = SEE_MASK_FLAG_LOG_USAGE;
   211         }
   212         seinfo.hwnd   = nullptr;
   213         seinfo.lpVerb = nullptr;
   214         seinfo.lpFile = mResolvedPath.get();
   215         seinfo.lpParameters =  nullptr;
   216         seinfo.lpDirectory  = nullptr;
   217         seinfo.nShow  = SW_SHOWNORMAL;
   219         // Use the directory of the file we're launching as the working
   220         // directory.  That way if we have a self extracting EXE it won't
   221         // suggest to extract to the install directory.
   222         WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
   223         wcsncpy(workingDirectory,  mResolvedPath.get(), MAX_PATH);
   224         if (PathRemoveFileSpecW(workingDirectory)) {
   225             seinfo.lpDirectory = workingDirectory;
   226         } else {
   227             NS_WARNING("Could not set working directory for launched file.");
   228         }
   230         if (ShellExecuteExW(&seinfo)) {
   231             return NS_OK;
   232         }
   233         DWORD r = GetLastError();
   234         // if the file has no association, we launch windows' 
   235         // "what do you want to do" dialog
   236         if (r == SE_ERR_NOASSOC) {
   237             nsAutoString shellArg;
   238             shellArg.Assign(NS_LITERAL_STRING("shell32.dll,OpenAs_RunDLL ") + 
   239                             mResolvedPath);
   240             seinfo.lpFile = L"RUNDLL32.EXE";
   241             seinfo.lpParameters = shellArg.get();
   242             if (ShellExecuteExW(&seinfo))
   243                 return NS_OK;
   244             r = GetLastError();
   245         }
   246         if (r < 32) {
   247             switch (r) {
   248               case 0:
   249               case SE_ERR_OOM:
   250                   return NS_ERROR_OUT_OF_MEMORY;
   251               case ERROR_FILE_NOT_FOUND:
   252                   return NS_ERROR_FILE_NOT_FOUND;
   253               case ERROR_PATH_NOT_FOUND:
   254                   return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   255               case ERROR_BAD_FORMAT:
   256                   return NS_ERROR_FILE_CORRUPTED;
   257               case SE_ERR_ACCESSDENIED:
   258                   return NS_ERROR_FILE_ACCESS_DENIED;
   259               case SE_ERR_ASSOCINCOMPLETE:
   260               case SE_ERR_NOASSOC:
   261                   return NS_ERROR_UNEXPECTED;
   262               case SE_ERR_DDEBUSY:
   263               case SE_ERR_DDEFAIL:
   264               case SE_ERR_DDETIMEOUT:
   265                   return NS_ERROR_NOT_AVAILABLE;
   266               case SE_ERR_DLLNOTFOUND:
   267                   return NS_ERROR_FAILURE;
   268               case SE_ERR_SHARE:
   269                   return NS_ERROR_FILE_IS_LOCKED;
   270               default:
   271                   return NS_ERROR_FILE_EXECUTION_FAILED;
   272             }
   273         }
   274         return NS_OK;
   275     }
   277     // Stores the operation that will be performed on the thread
   278     AsyncLocalFileWinOperation::FileOp mOperation;
   280     // Stores the path to perform the operation on
   281     nsString mResolvedPath;
   282 };
   284 class nsDriveEnumerator : public nsISimpleEnumerator
   285 {
   286 public:
   287     nsDriveEnumerator();
   288     virtual ~nsDriveEnumerator();
   289     NS_DECL_ISUPPORTS
   290     NS_DECL_NSISIMPLEENUMERATOR
   291     nsresult Init();
   292 private:
   293     /* mDrives stores the null-separated drive names.
   294      * Init sets them.
   295      * HasMoreElements checks mStartOfCurrentDrive.
   296      * GetNext advances mStartOfCurrentDrive.
   297      */
   298     nsString mDrives;
   299     nsAString::const_iterator mStartOfCurrentDrive;
   300     nsAString::const_iterator mEndOfDrivesString;
   301 };
   303 //----------------------------------------------------------------------------
   304 // short cut resolver
   305 //----------------------------------------------------------------------------
   306 class ShortcutResolver
   307 {
   308 public:
   309     ShortcutResolver();
   310     // nonvirtual since we're not subclassed
   311     ~ShortcutResolver();
   313     nsresult Init();
   314     nsresult Resolve(const WCHAR* in, WCHAR* out);
   315     nsresult SetShortcut(bool updateExisting,
   316                          const WCHAR* shortcutPath,
   317                          const WCHAR* targetPath,
   318                          const WCHAR* workingDir,
   319                          const WCHAR* args,
   320                          const WCHAR* description,
   321                          const WCHAR* iconFile,
   322                          int32_t iconIndex);
   324 private:
   325     Mutex                  mLock;
   326     nsRefPtr<IPersistFile> mPersistFile;
   327     nsRefPtr<IShellLinkW>  mShellLink;
   328 };
   330 ShortcutResolver::ShortcutResolver() :
   331     mLock("ShortcutResolver.mLock")
   332 {
   333     CoInitialize(nullptr);
   334 }
   336 ShortcutResolver::~ShortcutResolver()
   337 {
   338     CoUninitialize();
   339 }
   341 nsresult
   342 ShortcutResolver::Init()
   343 {
   344     // Get a pointer to the IPersistFile interface.
   345     if (FAILED(CoCreateInstance(CLSID_ShellLink,
   346                                 nullptr,
   347                                 CLSCTX_INPROC_SERVER,
   348                                 IID_IShellLinkW,
   349                                 getter_AddRefs(mShellLink))) ||
   350         FAILED(mShellLink->QueryInterface(IID_IPersistFile,
   351                                           getter_AddRefs(mPersistFile)))) {
   352         mShellLink = nullptr;
   353         return NS_ERROR_FAILURE;
   354     }
   355     return NS_OK;
   356 }
   358 // |out| must be an allocated buffer of size MAX_PATH
   359 nsresult
   360 ShortcutResolver::Resolve(const WCHAR* in, WCHAR* out)
   361 {
   362     if (!mShellLink)
   363         return NS_ERROR_FAILURE;
   365     MutexAutoLock lock(mLock);
   367     if (FAILED(mPersistFile->Load(in, STGM_READ)) ||
   368         FAILED(mShellLink->Resolve(nullptr, SLR_NO_UI)) ||
   369         FAILED(mShellLink->GetPath(out, MAX_PATH, nullptr, SLGP_UNCPRIORITY)))
   370         return NS_ERROR_FAILURE;
   371     return NS_OK;
   372 }
   374 nsresult
   375 ShortcutResolver::SetShortcut(bool updateExisting,
   376                               const WCHAR* shortcutPath,
   377                               const WCHAR* targetPath,
   378                               const WCHAR* workingDir,
   379                               const WCHAR* args,
   380                               const WCHAR* description,
   381                               const WCHAR* iconPath,
   382                               int32_t iconIndex)
   383 {
   384     if (!mShellLink) {
   385       return NS_ERROR_FAILURE;
   386     }
   388     if (!shortcutPath) {
   389       return NS_ERROR_FAILURE;
   390     }
   392     MutexAutoLock lock(mLock);
   394     if (updateExisting) {
   395       if (FAILED(mPersistFile->Load(shortcutPath, STGM_READWRITE))) {
   396         return NS_ERROR_FAILURE;
   397       }
   398     } else {
   399       if (!targetPath) {
   400         return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
   401       }
   403       // Since we reuse our IPersistFile, we have to clear out any values that
   404       // may be left over from previous calls to SetShortcut.
   405       if (FAILED(mShellLink->SetWorkingDirectory(L""))
   406        || FAILED(mShellLink->SetArguments(L""))
   407        || FAILED(mShellLink->SetDescription(L""))
   408        || FAILED(mShellLink->SetIconLocation(L"", 0))) {
   409         return NS_ERROR_FAILURE;
   410       }
   411     }
   413     if (targetPath && FAILED(mShellLink->SetPath(targetPath))) {
   414       return NS_ERROR_FAILURE;
   415     }
   417     if (workingDir && FAILED(mShellLink->SetWorkingDirectory(workingDir))) {
   418       return NS_ERROR_FAILURE;
   419     }
   421     if (args && FAILED(mShellLink->SetArguments(args))) {
   422       return NS_ERROR_FAILURE;
   423     }
   425     if (description && FAILED(mShellLink->SetDescription(description))) {
   426       return NS_ERROR_FAILURE;
   427     }
   429     if (iconPath && FAILED(mShellLink->SetIconLocation(iconPath, iconIndex))) {
   430       return NS_ERROR_FAILURE;
   431     }
   433     if (FAILED(mPersistFile->Save(shortcutPath,
   434                                   TRUE))) {
   435       // Second argument indicates whether the file path specified in the
   436       // first argument should become the "current working file" for this
   437       // IPersistFile
   438       return NS_ERROR_FAILURE;
   439     }
   441     return NS_OK;
   442 }
   444 static ShortcutResolver * gResolver = nullptr;
   446 static nsresult NS_CreateShortcutResolver()
   447 {
   448     gResolver = new ShortcutResolver();
   449     if (!gResolver)
   450         return NS_ERROR_OUT_OF_MEMORY;
   452     return gResolver->Init();
   453 }
   455 static void NS_DestroyShortcutResolver()
   456 {
   457     delete gResolver;
   458     gResolver = nullptr;
   459 }
   462 //-----------------------------------------------------------------------------
   463 // static helper functions
   464 //-----------------------------------------------------------------------------
   466 // certainly not all the error that can be
   467 // encountered, but many of them common ones
   468 static nsresult ConvertWinError(DWORD winErr)
   469 {
   470     nsresult rv;
   472     switch (winErr)
   473     {
   474         case ERROR_FILE_NOT_FOUND:
   475         case ERROR_PATH_NOT_FOUND:
   476         case ERROR_INVALID_DRIVE:
   477             rv = NS_ERROR_FILE_NOT_FOUND;
   478             break;
   479         case ERROR_ACCESS_DENIED:
   480         case ERROR_NOT_SAME_DEVICE:
   481             rv = NS_ERROR_FILE_ACCESS_DENIED;
   482             break;
   483         case ERROR_SHARING_VIOLATION: // CreateFile without sharing flags
   484         case ERROR_LOCK_VIOLATION: // LockFile, LockFileEx
   485             rv = NS_ERROR_FILE_IS_LOCKED;
   486             break;
   487         case ERROR_NOT_ENOUGH_MEMORY:
   488         case ERROR_INVALID_BLOCK:
   489         case ERROR_INVALID_HANDLE:
   490         case ERROR_ARENA_TRASHED:
   491             rv = NS_ERROR_OUT_OF_MEMORY;
   492             break;
   493         case ERROR_CURRENT_DIRECTORY:
   494             rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
   495             break;
   496         case ERROR_WRITE_PROTECT:
   497             rv = NS_ERROR_FILE_READ_ONLY;
   498             break;
   499         case ERROR_HANDLE_DISK_FULL:
   500             rv = NS_ERROR_FILE_TOO_BIG;
   501             break;
   502         case ERROR_FILE_EXISTS:
   503         case ERROR_ALREADY_EXISTS:
   504         case ERROR_CANNOT_MAKE:
   505             rv = NS_ERROR_FILE_ALREADY_EXISTS;
   506             break;
   507         case ERROR_FILENAME_EXCED_RANGE:
   508             rv = NS_ERROR_FILE_NAME_TOO_LONG;
   509             break;
   510         case ERROR_DIRECTORY:
   511             rv = NS_ERROR_FILE_NOT_DIRECTORY;
   512             break;
   513         case 0:
   514             rv = NS_OK;
   515             break;
   516         default:
   517             rv = NS_ERROR_FAILURE;
   518             break;
   519     }
   520     return rv;
   521 }
   523 // as suggested in the MSDN documentation on SetFilePointer
   524 static __int64 
   525 MyFileSeek64(HANDLE aHandle, __int64 aDistance, DWORD aMoveMethod)
   526 {
   527     LARGE_INTEGER li;
   529     li.QuadPart = aDistance;
   530     li.LowPart = SetFilePointer(aHandle, li.LowPart, &li.HighPart, aMoveMethod);
   531     if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
   532     {
   533         li.QuadPart = -1;
   534     }
   536     return li.QuadPart;
   537 }
   539 static bool
   540 IsShortcutPath(const nsAString &path)
   541 {
   542     // Under Windows, the shortcuts are just files with a ".lnk" extension. 
   543     // Note also that we don't resolve links in the middle of paths.
   544     // i.e. "c:\foo.lnk\bar.txt" is invalid.
   545     NS_ABORT_IF_FALSE(!path.IsEmpty(), "don't pass an empty string");
   546     int32_t len = path.Length();
   547     return len >= 4 && (StringTail(path, 4).LowerCaseEqualsASCII(".lnk"));
   548 }
   550 //-----------------------------------------------------------------------------
   551 // We need the following three definitions to make |OpenFile| convert a file 
   552 // handle to an NSPR file descriptor correctly when |O_APPEND| flag is
   553 // specified. It is defined in a private header of NSPR (primpl.h) we can't
   554 // include. As a temporary workaround until we decide how to extend
   555 // |PR_ImportFile|, we define it here. Currently, |_PR_HAVE_PEEK_BUFFER|
   556 // and |PR_STRICT_ADDR_LEN| are not defined for the 'w95'-dependent portion
   557 // of NSPR so that fields of |PRFilePrivate| #ifdef'd by them are not copied.
   558 // Similarly, |_MDFileDesc| is taken from nsprpub/pr/include/md/_win95.h.
   559 // In an unlikely case we switch to 'NT'-dependent NSPR AND this temporary 
   560 // workaround last beyond the switch, |PRFilePrivate| and |_MDFileDesc| 
   561 // need to be changed to match the definitions for WinNT.
   562 //-----------------------------------------------------------------------------
   563 typedef enum {
   564     _PR_TRI_TRUE = 1,
   565     _PR_TRI_FALSE = 0,
   566     _PR_TRI_UNKNOWN = -1
   567 } _PRTriStateBool;
   569 struct _MDFileDesc {
   570     PROsfd osfd;
   571 };
   573 struct PRFilePrivate {
   574     int32_t state;
   575     bool nonblocking;
   576     _PRTriStateBool inheritable;
   577     PRFileDesc *next;
   578     int lockCount;      /*   0: not locked
   579                          *  -1: a native lockfile call is in progress
   580                          * > 0: # times the file is locked */
   581     bool    appendMode; 
   582     _MDFileDesc md;
   583 };
   585 //-----------------------------------------------------------------------------
   586 // Six static methods defined below (OpenFile,  FileTimeToPRTime, GetFileInfo,
   587 // OpenDir, CloseDir, ReadDir) should go away once the corresponding 
   588 // UTF-16 APIs are implemented on all the supported platforms (or at least 
   589 // Windows 9x/ME) in NSPR. Currently, they're only implemented on 
   590 // Windows NT4 or later. (bug 330665)
   591 //-----------------------------------------------------------------------------
   593 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} : 
   594 // PR_Open and _PR_MD_OPEN
   595 nsresult
   596 OpenFile(const nsAFlatString &name, int osflags, int mode,
   597          PRFileDesc **fd)
   598 {
   599     int32_t access = 0;
   601     int32_t disposition = 0;
   602     int32_t attributes = 0;
   604     if (osflags & PR_SYNC) 
   605         attributes = FILE_FLAG_WRITE_THROUGH;
   606     if (osflags & PR_RDONLY || osflags & PR_RDWR)
   607         access |= GENERIC_READ;
   608     if (osflags & PR_WRONLY || osflags & PR_RDWR)
   609         access |= GENERIC_WRITE;
   611     if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
   612         disposition = CREATE_NEW;
   613     else if (osflags & PR_CREATE_FILE) {
   614         if (osflags & PR_TRUNCATE)
   615             disposition = CREATE_ALWAYS;
   616         else
   617             disposition = OPEN_ALWAYS;
   618     } else {
   619         if (osflags & PR_TRUNCATE)
   620             disposition = TRUNCATE_EXISTING;
   621         else
   622             disposition = OPEN_EXISTING;
   623     }
   625     if (osflags & nsIFile::DELETE_ON_CLOSE) {
   626         attributes |= FILE_FLAG_DELETE_ON_CLOSE;
   627     }
   629     if (osflags & nsIFile::OS_READAHEAD) {
   630         attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
   631     }
   633     // If no write permissions are requested, and if we are possibly creating
   634     // the file, then set the new file as read only.
   635     // The flag has no effect if we happen to open the file.
   636     if (!(mode & (PR_IWUSR | PR_IWGRP | PR_IWOTH)) &&
   637         disposition != OPEN_EXISTING) {
   638         attributes |= FILE_ATTRIBUTE_READONLY;
   639     }
   641     HANDLE file = ::CreateFileW(name.get(), access,
   642                                 FILE_SHARE_READ|FILE_SHARE_WRITE,
   643                                 nullptr, disposition, attributes, nullptr);
   645     if (file == INVALID_HANDLE_VALUE) { 
   646         *fd = nullptr;
   647         return ConvertWinError(GetLastError());
   648     }
   650     *fd = PR_ImportFile((PROsfd) file); 
   651     if (*fd) {
   652         // On Windows, _PR_HAVE_O_APPEND is not defined so that we have to
   653         // add it manually. (see |PR_Open| in nsprpub/pr/src/io/prfile.c)
   654         (*fd)->secret->appendMode = (PR_APPEND & osflags) ? true : false;
   655         return NS_OK;
   656     }
   658     nsresult rv = NS_ErrorAccordingToNSPR();
   660     CloseHandle(file);
   662     return rv;
   663 }
   665 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
   666 // PR_FileTimeToPRTime and _PR_FileTimeToPRTime
   667 static
   668 void FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
   669 {
   670 #ifdef __GNUC__
   671     const PRTime _pr_filetime_offset = 116444736000000000LL;
   672 #else
   673     const PRTime _pr_filetime_offset = 116444736000000000i64;
   674 #endif
   676     PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
   677     ::CopyMemory(prtm, filetime, sizeof(PRTime));
   678 #ifdef __GNUC__
   679     *prtm = (*prtm - _pr_filetime_offset) / 10LL;
   680 #else
   681     *prtm = (*prtm - _pr_filetime_offset) / 10i64;
   682 #endif
   683 }
   685 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} with some
   686 // changes : PR_GetFileInfo64, _PR_MD_GETFILEINFO64
   687 static nsresult
   688 GetFileInfo(const nsAFlatString &name, PRFileInfo64 *info)
   689 {
   690     WIN32_FILE_ATTRIBUTE_DATA fileData;
   692     if (name.IsEmpty() || name.FindCharInSet(MOZ_UTF16("?*")) != kNotFound)
   693         return NS_ERROR_INVALID_ARG;
   695     if (!::GetFileAttributesExW(name.get(), GetFileExInfoStandard, &fileData))
   696         return ConvertWinError(GetLastError());
   698     if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
   699         info->type = PR_FILE_DIRECTORY;
   700     } else {
   701         info->type = PR_FILE_FILE;
   702     }
   704     info->size = fileData.nFileSizeHigh;
   705     info->size = (info->size << 32) + fileData.nFileSizeLow;
   707     FileTimeToPRTime(&fileData.ftLastWriteTime, &info->modifyTime);
   709     if (0 == fileData.ftCreationTime.dwLowDateTime &&
   710             0 == fileData.ftCreationTime.dwHighDateTime) {
   711         info->creationTime = info->modifyTime;
   712     } else {
   713         FileTimeToPRTime(&fileData.ftCreationTime, &info->creationTime);
   714     }
   716     return NS_OK;
   717 }
   719 struct nsDir
   720 {
   721     HANDLE   handle; 
   722     WIN32_FIND_DATAW data;
   723     bool     firstEntry;
   724 };
   726 static nsresult
   727 OpenDir(const nsAFlatString &name, nsDir * *dir)
   728 {
   729     if (NS_WARN_IF(!dir))
   730         return NS_ERROR_INVALID_ARG;
   732     *dir = nullptr;
   733     if (name.Length() + 3 >= MAX_PATH)
   734         return NS_ERROR_FILE_NAME_TOO_LONG;
   736     nsDir *d  = PR_NEW(nsDir);
   737     if (!d)
   738         return NS_ERROR_OUT_OF_MEMORY;
   740     nsAutoString filename(name);
   742      //If 'name' ends in a slash or backslash, do not append
   743      //another backslash.
   744     if (filename.Last() == L'/' || filename.Last() == L'\\')
   745         filename.Append('*');
   746     else 
   747         filename.AppendLiteral("\\*");
   749     filename.ReplaceChar(L'/', L'\\');
   751     // FindFirstFileW Will have a last error of ERROR_DIRECTORY if
   752     // <file_path>\* is passed in.  If <unknown_path>\* is passed in then
   753     // ERROR_PATH_NOT_FOUND will be the last error.
   754     d->handle = ::FindFirstFileW(filename.get(), &(d->data) );
   756     if (d->handle == INVALID_HANDLE_VALUE) {
   757         PR_Free(d);
   758         return ConvertWinError(GetLastError());
   759     }
   760     d->firstEntry = true;
   762     *dir = d;
   763     return NS_OK;
   764 }
   766 static nsresult
   767 ReadDir(nsDir *dir, PRDirFlags flags, nsString& name)
   768 {
   769     name.Truncate();
   770     if (NS_WARN_IF(!dir))
   771         return NS_ERROR_INVALID_ARG;
   773     while (1) {
   774         BOOL rv;
   775         if (dir->firstEntry)
   776         {
   777             dir->firstEntry = false;
   778             rv = 1;
   779         } else
   780             rv = ::FindNextFileW(dir->handle, &(dir->data));
   782         if (rv == 0)
   783             break;
   785         const wchar_t *fileName;
   786         nsString tmp;
   787         fileName = (dir)->data.cFileName;
   789         if ((flags & PR_SKIP_DOT) &&
   790             (fileName[0] == L'.') && (fileName[1] == L'\0'))
   791             continue;
   792         if ((flags & PR_SKIP_DOT_DOT) &&
   793             (fileName[0] == L'.') && (fileName[1] == L'.') &&
   794             (fileName[2] == L'\0'))
   795             continue;
   797         DWORD attrib =  dir->data.dwFileAttributes;
   798         if ((flags & PR_SKIP_HIDDEN) && (attrib & FILE_ATTRIBUTE_HIDDEN))
   799             continue;
   801         if (fileName == tmp.get())
   802             name = tmp;
   803         else 
   804             name = fileName;
   805         return NS_OK;
   806     }
   808     DWORD err = GetLastError();
   809     return err == ERROR_NO_MORE_FILES ? NS_OK : ConvertWinError(err);
   810 }
   812 static nsresult
   813 CloseDir(nsDir *&d)
   814 {
   815     if (NS_WARN_IF(!d))
   816         return NS_ERROR_INVALID_ARG;
   818     BOOL isOk = FindClose(d->handle);
   819     // PR_DELETE also nulls out the passed in pointer.
   820     PR_DELETE(d);
   821     return isOk ? NS_OK : ConvertWinError(GetLastError());
   822 }
   824 //-----------------------------------------------------------------------------
   825 // nsDirEnumerator
   826 //-----------------------------------------------------------------------------
   828 class nsDirEnumerator MOZ_FINAL : public nsISimpleEnumerator,
   829                                   public nsIDirectoryEnumerator
   830 {
   831     public:
   833         NS_DECL_ISUPPORTS
   835         nsDirEnumerator() : mDir(nullptr)
   836         {
   837         }
   839         nsresult Init(nsIFile* parent)
   840         {
   841             nsAutoString filepath;
   842             parent->GetTarget(filepath);
   844             if (filepath.IsEmpty())
   845             {
   846                 parent->GetPath(filepath);
   847             }
   849             if (filepath.IsEmpty())
   850             {
   851                 return NS_ERROR_UNEXPECTED;
   852             }
   854             // IsDirectory is not needed here because OpenDir will return
   855             // NS_ERROR_FILE_NOT_DIRECTORY if the passed in path is a file.
   856             nsresult rv = OpenDir(filepath, &mDir);
   857             if (NS_FAILED(rv))
   858                 return rv;
   860             mParent = parent;
   861             return NS_OK;
   862         }
   864         NS_IMETHOD HasMoreElements(bool *result)
   865         {
   866             nsresult rv;
   867             if (mNext == nullptr && mDir)
   868             {
   869                 nsString name;
   870                 rv = ReadDir(mDir, PR_SKIP_BOTH, name);
   871                 if (NS_FAILED(rv))
   872                     return rv;
   873                 if (name.IsEmpty()) 
   874                 {
   875                     // end of dir entries
   876                     if (NS_FAILED(CloseDir(mDir)))
   877                         return NS_ERROR_FAILURE;
   879                     *result = false;
   880                     return NS_OK;
   881                 }
   883                 nsCOMPtr<nsIFile> file;
   884                 rv = mParent->Clone(getter_AddRefs(file));
   885                 if (NS_FAILED(rv))
   886                     return rv;
   888                 rv = file->Append(name);
   889                 if (NS_FAILED(rv))
   890                     return rv;
   892                 mNext = do_QueryInterface(file);
   893             }
   894             *result = mNext != nullptr;
   895             if (!*result) 
   896                 Close();
   897             return NS_OK;
   898         }
   900         NS_IMETHOD GetNext(nsISupports **result)
   901         {
   902             nsresult rv;
   903             bool hasMore;
   904             rv = HasMoreElements(&hasMore);
   905             if (NS_FAILED(rv)) return rv;
   907             *result = mNext;        // might return nullptr
   908             NS_IF_ADDREF(*result);
   910             mNext = nullptr;
   911             return NS_OK;
   912         }
   914         NS_IMETHOD GetNextFile(nsIFile **result)
   915         {
   916             *result = nullptr;
   917             bool hasMore = false;
   918             nsresult rv = HasMoreElements(&hasMore);
   919             if (NS_FAILED(rv) || !hasMore)
   920                 return rv;
   921             *result = mNext;
   922             NS_IF_ADDREF(*result);
   923             mNext = nullptr;
   924             return NS_OK;
   925         }
   927         NS_IMETHOD Close()
   928         {
   929             if (mDir)
   930             {
   931                 nsresult rv = CloseDir(mDir);
   932                 NS_ASSERTION(NS_SUCCEEDED(rv), "close failed");
   933                 if (NS_FAILED(rv))
   934                     return NS_ERROR_FAILURE;
   935             }
   936             return NS_OK;
   937         }
   939         // dtor can be non-virtual since there are no subclasses, but must be
   940         // public to use the class on the stack.
   941         ~nsDirEnumerator()
   942         {
   943             Close();
   944         }
   946     protected:
   947         nsDir*             mDir;
   948         nsCOMPtr<nsIFile>  mParent;
   949         nsCOMPtr<nsIFile>  mNext;
   950 };
   952 NS_IMPL_ISUPPORTS(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
   955 //-----------------------------------------------------------------------------
   956 // nsLocalFile <public>
   957 //-----------------------------------------------------------------------------
   959 nsLocalFile::nsLocalFile()
   960   : mDirty(true)
   961   , mResolveDirty(true)
   962   , mFollowSymlinks(false)
   963 {
   964 }
   966 nsresult
   967 nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
   968 {
   969     if (NS_WARN_IF(!aInstancePtr))
   970         return NS_ERROR_INVALID_ARG;
   971     if (NS_WARN_IF(outer))
   972         return NS_ERROR_NO_AGGREGATION;
   974     nsLocalFile* inst = new nsLocalFile();
   975     if (inst == nullptr)
   976         return NS_ERROR_OUT_OF_MEMORY;
   978     nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
   979     if (NS_FAILED(rv))
   980     {
   981         delete inst;
   982         return rv;
   983     }
   984     return NS_OK;
   985 }
   988 //-----------------------------------------------------------------------------
   989 // nsLocalFile::nsISupports
   990 //-----------------------------------------------------------------------------
   992 NS_IMPL_ISUPPORTS(nsLocalFile,
   993                   nsILocalFile,
   994                   nsIFile,
   995                   nsILocalFileWin,
   996                   nsIHashable)
   999 //-----------------------------------------------------------------------------
  1000 // nsLocalFile <private>
  1001 //-----------------------------------------------------------------------------
  1003 nsLocalFile::nsLocalFile(const nsLocalFile& other)
  1004   : mDirty(true)
  1005   , mResolveDirty(true)
  1006   , mFollowSymlinks(other.mFollowSymlinks)
  1007   , mWorkingPath(other.mWorkingPath)
  1011 // Resolve the shortcut file from mWorkingPath and write the path 
  1012 // it points to into mResolvedPath.
  1013 nsresult
  1014 nsLocalFile::ResolveShortcut()
  1016     // we can't do anything without the resolver
  1017     if (!gResolver)
  1018         return NS_ERROR_FAILURE;
  1020     mResolvedPath.SetLength(MAX_PATH);
  1021     if (mResolvedPath.Length() != MAX_PATH)
  1022         return NS_ERROR_OUT_OF_MEMORY;
  1024     wchar_t *resolvedPath = wwc(mResolvedPath.BeginWriting());
  1026     // resolve this shortcut
  1027     nsresult rv = gResolver->Resolve(mWorkingPath.get(), resolvedPath);
  1029     size_t len = NS_FAILED(rv) ? 0 : wcslen(resolvedPath);
  1030     mResolvedPath.SetLength(len);
  1032     return rv;
  1035 // Resolve any shortcuts and stat the resolved path. After a successful return
  1036 // the path is guaranteed valid and the members of mFileInfo64 can be used.
  1037 nsresult
  1038 nsLocalFile::ResolveAndStat()
  1040     // if we aren't dirty then we are already done
  1041     if (!mDirty)
  1042         return NS_OK;
  1044     // we can't resolve/stat anything that isn't a valid NSPR addressable path
  1045     if (mWorkingPath.IsEmpty())
  1046         return NS_ERROR_FILE_INVALID_PATH;
  1048     // this is usually correct
  1049     mResolvedPath.Assign(mWorkingPath);
  1051     // slutty hack designed to work around bug 134796 until it is fixed
  1052     nsAutoString nsprPath(mWorkingPath.get());
  1053     if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == L':') 
  1054         nsprPath.Append('\\');
  1056     // first we will see if the working path exists. If it doesn't then
  1057     // there is nothing more that can be done
  1058     nsresult rv = GetFileInfo(nsprPath, &mFileInfo64);
  1059     if (NS_FAILED(rv))
  1060         return rv;
  1062     // if this isn't a shortcut file or we aren't following symlinks then we're done 
  1063     if (!mFollowSymlinks 
  1064         || mFileInfo64.type != PR_FILE_FILE 
  1065         || !IsShortcutPath(mWorkingPath))
  1067         mDirty = false;
  1068         mResolveDirty = false;
  1069         return NS_OK;
  1072     // we need to resolve this shortcut to what it points to, this will
  1073     // set mResolvedPath. Even if it fails we need to have the resolved
  1074     // path equal to working path for those functions that always use
  1075     // the resolved path.
  1076     rv = ResolveShortcut();
  1077     if (NS_FAILED(rv))
  1079         mResolvedPath.Assign(mWorkingPath);
  1080         return rv;
  1082     mResolveDirty = false;
  1084     // get the details of the resolved path
  1085     rv = GetFileInfo(mResolvedPath, &mFileInfo64);
  1086     if (NS_FAILED(rv))
  1087         return rv;
  1089     mDirty = false;
  1090     return NS_OK;
  1093 /**
  1094  * Fills the mResolvedPath member variable with the file or symlink target
  1095  * if follow symlinks is on.  This is a copy of the Resolve parts from
  1096  * ResolveAndStat. ResolveAndStat is much slower though because of the stat.
  1098  * @return NS_OK on success.
  1099 */
  1100 nsresult
  1101 nsLocalFile::Resolve()
  1103   // if we aren't dirty then we are already done
  1104   if (!mResolveDirty) {
  1105     return NS_OK;
  1108   // we can't resolve/stat anything that isn't a valid NSPR addressable path
  1109   if (mWorkingPath.IsEmpty()) {
  1110     return NS_ERROR_FILE_INVALID_PATH;
  1113   // this is usually correct
  1114   mResolvedPath.Assign(mWorkingPath);
  1116   // if this isn't a shortcut file or we aren't following symlinks then
  1117   // we're done.
  1118   if (!mFollowSymlinks || 
  1119       !IsShortcutPath(mWorkingPath)) {
  1120     mResolveDirty = false;
  1121     return NS_OK;
  1124   // we need to resolve this shortcut to what it points to, this will
  1125   // set mResolvedPath. Even if it fails we need to have the resolved
  1126   // path equal to working path for those functions that always use
  1127   // the resolved path.
  1128   nsresult rv = ResolveShortcut();
  1129   if (NS_FAILED(rv)) {
  1130     mResolvedPath.Assign(mWorkingPath);
  1131     return rv;
  1134   mResolveDirty = false;
  1135   return NS_OK;
  1138 //-----------------------------------------------------------------------------
  1139 // nsLocalFile::nsIFile,nsILocalFile
  1140 //-----------------------------------------------------------------------------
  1142 NS_IMETHODIMP
  1143 nsLocalFile::Clone(nsIFile **file)
  1145     // Just copy-construct ourselves
  1146     *file = new nsLocalFile(*this);
  1147     if (!*file)
  1148       return NS_ERROR_OUT_OF_MEMORY;
  1150     NS_ADDREF(*file);
  1152     return NS_OK;
  1155 NS_IMETHODIMP
  1156 nsLocalFile::InitWithFile(nsIFile *aFile)
  1158     if (NS_WARN_IF(!aFile))
  1159         return NS_ERROR_INVALID_ARG;
  1161     nsAutoString path;
  1162     aFile->GetPath(path);
  1163     if (path.IsEmpty())
  1164         return NS_ERROR_INVALID_ARG;
  1165     return InitWithPath(path); 
  1168 NS_IMETHODIMP
  1169 nsLocalFile::InitWithPath(const nsAString &filePath)
  1171     MakeDirty();
  1173     nsAString::const_iterator begin, end;
  1174     filePath.BeginReading(begin);
  1175     filePath.EndReading(end);
  1177     // input string must not be empty
  1178     if (begin == end)
  1179         return NS_ERROR_FAILURE;
  1181     char16_t firstChar = *begin;
  1182     char16_t secondChar = *(++begin);
  1184     // just do a sanity check.  if it has any forward slashes, it is not a Native path
  1185     // on windows.  Also, it must have a colon at after the first char.
  1186     if (FindCharInReadable(L'/', begin, end))
  1187         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1189     if (secondChar != L':' && (secondChar != L'\\' || firstChar != L'\\'))
  1190         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1192     if (secondChar == L':') {
  1193         // Make sure we have a valid drive, later code assumes the drive letter
  1194         // is a single char a-z or A-Z.
  1195         if (PathGetDriveNumberW(filePath.Data()) == -1) {
  1196             return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1200     mWorkingPath = filePath;
  1201     // kill any trailing '\'
  1202     if (mWorkingPath.Last() == L'\\')
  1203         mWorkingPath.Truncate(mWorkingPath.Length() - 1);
  1205     return NS_OK;
  1209 NS_IMETHODIMP
  1210 nsLocalFile::OpenNSPRFileDesc(int32_t flags, int32_t mode, PRFileDesc **_retval)
  1212     nsresult rv = Resolve();
  1213     if (NS_FAILED(rv))
  1214         return rv;
  1216     return OpenFile(mResolvedPath, flags, mode, _retval);
  1220 NS_IMETHODIMP
  1221 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
  1223     nsresult rv = ResolveAndStat();
  1224     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
  1225         return rv;
  1227     *_retval = _wfopen(mResolvedPath.get(), NS_ConvertASCIItoUTF16(mode).get());
  1228     if (*_retval)
  1229         return NS_OK;
  1231     return NS_ERROR_FAILURE;
  1236 NS_IMETHODIMP
  1237 nsLocalFile::Create(uint32_t type, uint32_t attributes)
  1239     if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
  1240         return NS_ERROR_FILE_UNKNOWN_TYPE;
  1242     nsresult rv = ResolveAndStat();
  1243     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
  1244         return rv;
  1246     // create directories to target
  1247     //
  1248     // A given local file can be either one of these forms:
  1249     //
  1250     //   - normal:    X:\some\path\on\this\drive
  1251     //                       ^--- start here
  1252     //
  1253     //   - UNC path:  \\machine\volume\some\path\on\this\drive
  1254     //                                     ^--- start here
  1255     //
  1256     // Skip the first 'X:\' for the first form, and skip the first full
  1257     // '\\machine\volume\' segment for the second form.
  1259     wchar_t* path = wwc(mResolvedPath.BeginWriting());
  1261     if (path[0] == L'\\' && path[1] == L'\\')
  1263         // dealing with a UNC path here; skip past '\\machine\'
  1264         path = wcschr(path + 2, L'\\');
  1265         if (!path)
  1266             return NS_ERROR_FILE_INVALID_PATH;
  1267         ++path;
  1270     // search for first slash after the drive (or volume) name
  1271     wchar_t* slash = wcschr(path, L'\\');
  1273     nsresult directoryCreateError = NS_OK;
  1274     if (slash)
  1276         // skip the first '\\'
  1277         ++slash;
  1278         slash = wcschr(slash, L'\\');
  1280         while (slash)
  1282             *slash = L'\0';
  1284             if (!::CreateDirectoryW(mResolvedPath.get(), nullptr)) {
  1285                 rv = ConvertWinError(GetLastError());
  1286                 if (NS_ERROR_FILE_NOT_FOUND == rv &&
  1287                     NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
  1288                     // If a previous CreateDirectory failed due to access, return that.
  1289                     return NS_ERROR_FILE_ACCESS_DENIED;
  1291                 // perhaps the base path already exists, or perhaps we don't have
  1292                 // permissions to create the directory.  NOTE: access denied could
  1293                 // occur on a parent directory even though it exists.
  1294                 else if (NS_ERROR_FILE_ALREADY_EXISTS != rv &&
  1295                          NS_ERROR_FILE_ACCESS_DENIED != rv) {
  1296                     return rv;
  1299                 directoryCreateError = rv;
  1301             *slash = L'\\';
  1302             ++slash;
  1303             slash = wcschr(slash, L'\\');
  1307     if (type == NORMAL_FILE_TYPE)
  1309         PRFileDesc* file;
  1310         rv = OpenFile(mResolvedPath,
  1311                       PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes,
  1312                       &file);
  1313         if (file)
  1314             PR_Close(file);
  1316         if (rv == NS_ERROR_FILE_ACCESS_DENIED)
  1318             // need to return already-exists for directories (bug 452217)
  1319             bool isdir;
  1320             if (NS_SUCCEEDED(IsDirectory(&isdir)) && isdir)
  1321                 rv = NS_ERROR_FILE_ALREADY_EXISTS;
  1322         } else if (NS_ERROR_FILE_NOT_FOUND == rv && 
  1323                    NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
  1324             // If a previous CreateDirectory failed due to access, return that.
  1325             return NS_ERROR_FILE_ACCESS_DENIED;
  1327         return rv;
  1330     if (type == DIRECTORY_TYPE)
  1332         if (!::CreateDirectoryW(mResolvedPath.get(), nullptr)) {
  1333           rv = ConvertWinError(GetLastError());
  1334           if (NS_ERROR_FILE_NOT_FOUND == rv && 
  1335               NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
  1336               // If a previous CreateDirectory failed due to access, return that.
  1337               return NS_ERROR_FILE_ACCESS_DENIED;
  1338           } else {
  1339               return rv;
  1342         else
  1343             return NS_OK;
  1346     return NS_ERROR_FILE_UNKNOWN_TYPE;
  1350 NS_IMETHODIMP
  1351 nsLocalFile::Append(const nsAString &node)
  1353     // append this path, multiple components are not permitted
  1354     return AppendInternal(PromiseFlatString(node), false);
  1357 NS_IMETHODIMP
  1358 nsLocalFile::AppendRelativePath(const nsAString &node)
  1360     // append this path, multiple components are permitted
  1361     return AppendInternal(PromiseFlatString(node), true);
  1365 nsresult
  1366 nsLocalFile::AppendInternal(const nsAFlatString &node, bool multipleComponents)
  1368     if (node.IsEmpty())
  1369         return NS_OK;
  1371     // check the relative path for validity
  1372     if (node.First() == L'\\'                                   // can't start with an '\'
  1373         || node.FindChar(L'/') != kNotFound                     // can't contain /
  1374         || node.EqualsASCII(".."))                              // can't be ..
  1375         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1377     if (multipleComponents)
  1379         // can't contain .. as a path component. Ensure that the valid components
  1380         // "foo..foo", "..foo", and "foo.." are not falsely detected,
  1381         // but the invalid paths "..\", "foo\..", "foo\..\foo", 
  1382         // "..\foo", etc are.
  1383         NS_NAMED_LITERAL_STRING(doubleDot, "\\.."); 
  1384         nsAString::const_iterator start, end, offset;
  1385         node.BeginReading(start);
  1386         node.EndReading(end);
  1387         offset = end; 
  1388         while (FindInReadable(doubleDot, start, offset))
  1390             if (offset == end || *offset == L'\\')
  1391                 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1392             start = offset;
  1393             offset = end;
  1396         // catches the remaining cases of prefixes 
  1397         if (StringBeginsWith(node, NS_LITERAL_STRING("..\\")))
  1398             return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1400     // single components can't contain '\'
  1401     else if (node.FindChar(L'\\') != kNotFound)
  1402         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1404     MakeDirty();
  1406     mWorkingPath.Append(NS_LITERAL_STRING("\\") + node);
  1408     return NS_OK;
  1411 #define TOUPPER(u) (((u) >= L'a' && (u) <= L'z') ? \
  1412                     (u) - (L'a' - L'A') : (u))
  1414 NS_IMETHODIMP
  1415 nsLocalFile::Normalize()
  1417     // XXX See bug 187957 comment 18 for possible problems with this implementation.
  1419     if (mWorkingPath.IsEmpty())
  1420         return NS_OK;
  1422     nsAutoString path(mWorkingPath);
  1424     // find the index of the root backslash for the path. Everything before 
  1425     // this is considered fully normalized and cannot be ascended beyond 
  1426     // using ".."  For a local drive this is the first slash (e.g. "c:\").
  1427     // For a UNC path it is the slash following the share name 
  1428     // (e.g. "\\server\share\").
  1429     int32_t rootIdx = 2;        // default to local drive
  1430     if (path.First() == L'\\')   // if a share then calculate the rootIdx
  1432         rootIdx = path.FindChar(L'\\', 2);   // skip \\ in front of the server
  1433         if (rootIdx == kNotFound)
  1434             return NS_OK;                   // already normalized
  1435         rootIdx = path.FindChar(L'\\', rootIdx+1);
  1436         if (rootIdx == kNotFound)
  1437             return NS_OK;                   // already normalized
  1439     else if (path.CharAt(rootIdx) != L'\\')
  1441         // The path has been specified relative to the current working directory 
  1442         // for that drive. To normalize it, the current working directory for 
  1443         // that drive needs to be inserted before the supplied relative path
  1444         // which will provide an absolute path (and the rootIdx will still be 2).
  1445         WCHAR cwd[MAX_PATH];
  1446         WCHAR * pcwd = cwd;
  1447         int drive = TOUPPER(path.First()) - 'A' + 1;
  1448         /* We need to worry about IPH, for details read bug 419326.
  1449          * _getdrives - http://msdn2.microsoft.com/en-us/library/xdhk0xd2.aspx 
  1450          * uses a bitmask, bit 0 is 'a:'
  1451          * _chdrive - http://msdn2.microsoft.com/en-us/library/0d1409hb.aspx
  1452          * _getdcwd - http://msdn2.microsoft.com/en-us/library/7t2zk3s4.aspx
  1453          * take an int, 1 is 'a:'.
  1455          * Because of this, we need to do some math. Subtract 1 to convert from
  1456          * _chdrive/_getdcwd format to _getdrives drive numbering.
  1457          * Shift left x bits to convert from integer indexing to bitfield indexing.
  1458          * And of course, we need to find out if the drive is in the bitmask.
  1460          * If we're really unlucky, we can still lose, but only if the user
  1461          * manages to eject the drive between our call to _getdrives() and
  1462          * our *calls* to _wgetdcwd.
  1463          */
  1464         if (!((1 << (drive - 1)) & _getdrives()))
  1465             return NS_ERROR_FILE_INVALID_PATH;
  1466         if (!_wgetdcwd(drive, pcwd, MAX_PATH))
  1467             pcwd = _wgetdcwd(drive, 0, 0);
  1468         if (!pcwd)
  1469             return NS_ERROR_OUT_OF_MEMORY;
  1470         nsAutoString currentDir(pcwd);
  1471         if (pcwd != cwd)
  1472             free(pcwd);
  1474         if (currentDir.Last() == '\\')
  1475             path.Replace(0, 2, currentDir);
  1476         else
  1477             path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\"));
  1479     NS_POSTCONDITION(0 < rootIdx && rootIdx < (int32_t)path.Length(), "rootIdx is invalid");
  1480     NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid");
  1482     // if there is nothing following the root path then it is already normalized
  1483     if (rootIdx + 1 == (int32_t)path.Length())
  1484         return NS_OK;
  1486     // assign the root
  1487     const char16_t * pathBuffer = path.get();  // simplify access to the buffer
  1488     mWorkingPath.SetCapacity(path.Length()); // it won't ever grow longer
  1489     mWorkingPath.Assign(pathBuffer, rootIdx);
  1491     // Normalize the path components. The actions taken are:
  1492     //
  1493     //  "\\"    condense to single backslash
  1494     //  "."     remove from path
  1495     //  ".."    up a directory
  1496     //  "..."   remove from path (any number of dots > 2)
  1497     //
  1498     // The last form is something that Windows 95 and 98 supported and 
  1499     // is a shortcut for changing up multiple directories. Windows XP
  1500     // and ilk ignore it in a path, as is done here.
  1501     int32_t len, begin, end = rootIdx;
  1502     while (end < (int32_t)path.Length())
  1504         // find the current segment (text between the backslashes) to 
  1505         // be examined, this will set the following variables:
  1506         //  begin == index of first char in segment
  1507         //  end   == index 1 char after last char in segment
  1508         //  len   == length of segment 
  1509         begin = end + 1;
  1510         end = path.FindChar('\\', begin);
  1511         if (end == kNotFound)
  1512             end = path.Length();
  1513         len = end - begin;
  1515         // ignore double backslashes
  1516         if (len == 0)
  1517             continue;
  1519         // len != 0, and interesting paths always begin with a dot
  1520         if (pathBuffer[begin] == '.')
  1522             // ignore single dots
  1523             if (len == 1)
  1524                 continue;   
  1526             // handle multiple dots
  1527             if (len >= 2 && pathBuffer[begin+1] == L'.')
  1529                 // back up a path component on double dot
  1530                 if (len == 2)
  1532                     int32_t prev = mWorkingPath.RFindChar('\\');
  1533                     if (prev >= rootIdx)
  1534                         mWorkingPath.Truncate(prev);
  1535                     continue;
  1538                 // length is > 2 and the first two characters are dots. 
  1539                 // if the rest of the string is dots, then ignore it.
  1540                 int idx = len - 1;
  1541                 for (; idx >= 2; --idx) 
  1543                     if (pathBuffer[begin+idx] != L'.')
  1544                         break;
  1547                 // this is true if the loop above didn't break
  1548                 // and all characters in this segment are dots.
  1549                 if (idx < 2) 
  1550                     continue;
  1554         // add the current component to the path, including the preceding backslash
  1555         mWorkingPath.Append(pathBuffer + begin - 1, len + 1);
  1558     // kill trailing dots and spaces.
  1559     int32_t filePathLen = mWorkingPath.Length() - 1;
  1560     while(filePathLen > 0 && (mWorkingPath[filePathLen] == L' ' ||
  1561           mWorkingPath[filePathLen] == L'.'))
  1563         mWorkingPath.Truncate(filePathLen--);
  1566     MakeDirty();
  1567     return NS_OK;
  1570 NS_IMETHODIMP
  1571 nsLocalFile::GetLeafName(nsAString &aLeafName)
  1573     aLeafName.Truncate();
  1575     if (mWorkingPath.IsEmpty())
  1576         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1578     int32_t offset = mWorkingPath.RFindChar(L'\\');
  1580     // if the working path is just a node without any lashes.
  1581     if (offset == kNotFound)
  1582         aLeafName = mWorkingPath;
  1583     else
  1584         aLeafName = Substring(mWorkingPath, offset + 1);
  1586     return NS_OK;
  1589 NS_IMETHODIMP
  1590 nsLocalFile::SetLeafName(const nsAString &aLeafName)
  1592     MakeDirty();
  1594     if (mWorkingPath.IsEmpty())
  1595         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1597     // cannot use nsCString::RFindChar() due to 0x5c problem
  1598     int32_t offset = mWorkingPath.RFindChar(L'\\');
  1599     if (offset)
  1601         mWorkingPath.Truncate(offset+1);
  1603     mWorkingPath.Append(aLeafName);
  1605     return NS_OK;
  1609 NS_IMETHODIMP
  1610 nsLocalFile::GetPath(nsAString &_retval)
  1612     _retval = mWorkingPath;
  1613     return NS_OK;
  1616 NS_IMETHODIMP
  1617 nsLocalFile::GetCanonicalPath(nsAString &aResult)
  1619     EnsureShortPath();
  1620     aResult.Assign(mShortWorkingPath);
  1621     return NS_OK;
  1624 typedef struct {
  1625     WORD wLanguage;
  1626     WORD wCodePage;
  1627 } LANGANDCODEPAGE;
  1629 NS_IMETHODIMP
  1630 nsLocalFile::GetVersionInfoField(const char* aField, nsAString& _retval)
  1632     nsresult rv = ResolveAndStat();
  1633     if (NS_FAILED(rv))
  1634         return rv;
  1636     rv = NS_ERROR_FAILURE;
  1638     const WCHAR *path = mFollowSymlinks ? mResolvedPath.get() : mWorkingPath.get();
  1640     DWORD dummy;
  1641     DWORD size = ::GetFileVersionInfoSizeW(path, &dummy);
  1642     if (!size)
  1643         return rv;
  1645     void* ver = calloc(size, 1);
  1646     if (!ver)
  1647         return NS_ERROR_OUT_OF_MEMORY;
  1649     if (::GetFileVersionInfoW(path, 0, size, ver)) 
  1651         LANGANDCODEPAGE* translate = nullptr;
  1652         UINT pageCount;
  1653         BOOL queryResult = ::VerQueryValueW(ver, L"\\VarFileInfo\\Translation", 
  1654                                             (void**)&translate, &pageCount);
  1655         if (queryResult && translate) 
  1657             for (int32_t i = 0; i < 2; ++i) 
  1659                 wchar_t subBlock[MAX_PATH];
  1660                 _snwprintf(subBlock, MAX_PATH,
  1661                            L"\\StringFileInfo\\%04x%04x\\%s", 
  1662                            (i == 0 ? translate[0].wLanguage 
  1663                                    : ::GetUserDefaultLangID()),
  1664                            translate[0].wCodePage,
  1665                            NS_ConvertASCIItoUTF16(
  1666                                nsDependentCString(aField)).get());
  1667                 subBlock[MAX_PATH - 1] = 0;
  1668                 LPVOID value = nullptr;
  1669                 UINT size;
  1670                 queryResult = ::VerQueryValueW(ver, subBlock, &value, &size);
  1671                 if (queryResult && value)
  1673                     _retval.Assign(static_cast<char16_t*>(value));
  1674                     if (!_retval.IsEmpty()) 
  1676                         rv = NS_OK;
  1677                         break;
  1683     free(ver);
  1685     return rv;
  1688 NS_IMETHODIMP
  1689 nsLocalFile::SetShortcut(nsIFile* targetFile,
  1690                          nsIFile* workingDir,
  1691                          const char16_t* args,
  1692                          const char16_t* description,
  1693                          nsIFile* iconFile,
  1694                          int32_t iconIndex)
  1696     bool exists;
  1697     nsresult rv = this->Exists(&exists);
  1698     if (NS_FAILED(rv)) {
  1699       return rv;
  1702     const WCHAR* targetFilePath = nullptr;
  1703     const WCHAR* workingDirPath = nullptr;
  1704     const WCHAR* iconFilePath = nullptr;
  1706     nsAutoString targetFilePathAuto;
  1707     if (targetFile) {
  1708         rv = targetFile->GetPath(targetFilePathAuto);
  1709         if (NS_FAILED(rv)) {
  1710           return rv;
  1712         targetFilePath = targetFilePathAuto.get();
  1715     nsAutoString workingDirPathAuto;
  1716     if (workingDir) {
  1717         rv = workingDir->GetPath(workingDirPathAuto);
  1718         if (NS_FAILED(rv)) {
  1719           return rv;
  1721         workingDirPath = workingDirPathAuto.get();
  1724     nsAutoString iconPathAuto;
  1725     if (iconFile) {
  1726         rv = iconFile->GetPath(iconPathAuto);
  1727         if (NS_FAILED(rv)) {
  1728           return rv;
  1730         iconFilePath = iconPathAuto.get();
  1733     rv = gResolver->SetShortcut(exists,
  1734                                 mWorkingPath.get(),
  1735                                 targetFilePath,
  1736                                 workingDirPath,
  1737                                 char16ptr_t(args),
  1738                                 char16ptr_t(description),
  1739                                 iconFilePath,
  1740                                 iconFilePath? iconIndex : 0);
  1741     if (targetFilePath && NS_SUCCEEDED(rv)) {
  1742       MakeDirty();
  1745     return rv;
  1748 /** 
  1749  * Determines if the drive type for the specified file is rmeote or local.
  1751  * @param path   The path of the file to check
  1752  * @param remote Out parameter, on function success holds true if the specified
  1753  *               file path is remote, or false if the file path is local.
  1754  * @return true  on success. The return value implies absolutely nothing about
  1755  *               wether the file is local or remote.
  1756 */
  1757 static bool
  1758 IsRemoteFilePath(LPCWSTR path, bool &remote)
  1760   // Obtain the parent directory path and make sure it ends with
  1761   // a trailing backslash.
  1762   WCHAR dirPath[MAX_PATH + 1] = { 0 };
  1763   wcsncpy(dirPath, path, MAX_PATH);
  1764   if (!PathRemoveFileSpecW(dirPath)) {
  1765     return false;
  1767   size_t len = wcslen(dirPath);
  1768   // In case the dirPath holds exaclty MAX_PATH and remains unchanged, we
  1769   // recheck the required length here since we need to terminate it with
  1770   // a backslash.
  1771   if (len >= MAX_PATH) {
  1772     return false;
  1775   dirPath[len] = L'\\';
  1776   dirPath[len + 1] = L'\0';
  1777   UINT driveType = GetDriveTypeW(dirPath);
  1778   remote = driveType == DRIVE_REMOTE;
  1779   return true;
  1782 nsresult
  1783 nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent,
  1784                             const nsAString &newName, uint32_t options)
  1786     nsresult rv = NS_OK;
  1787     nsAutoString filePath;
  1789     bool move = options & (Move | Rename);
  1791     // get the path that we are going to copy to.
  1792     // Since windows does not know how to auto
  1793     // resolve shortcuts, we must work with the
  1794     // target.
  1795     nsAutoString destPath;
  1796     destParent->GetTarget(destPath);
  1798     destPath.Append('\\');
  1800     if (newName.IsEmpty())
  1802         nsAutoString aFileName;
  1803         sourceFile->GetLeafName(aFileName);
  1804         destPath.Append(aFileName);
  1806     else
  1808         destPath.Append(newName);
  1812     if (options & FollowSymlinks)
  1814         rv = sourceFile->GetTarget(filePath);
  1815         if (filePath.IsEmpty())
  1816             rv = sourceFile->GetPath(filePath);
  1818     else
  1820         rv = sourceFile->GetPath(filePath);
  1823     if (NS_FAILED(rv))
  1824         return rv;
  1826     // Pass the flag COPY_FILE_NO_BUFFERING to CopyFileEx as we may be copying
  1827     // to a SMBV2 remote drive. Without this parameter subsequent append mode
  1828     // file writes can cause the resultant file to become corrupt. We only need to do 
  1829     // this if the major version of Windows is > 5(Only Windows Vista and above 
  1830     // can support SMBV2).  With a 7200RPM hard drive:
  1831     // Copying a 1KB file with COPY_FILE_NO_BUFFERING takes about 30-60ms.
  1832     // Copying a 1KB file without COPY_FILE_NO_BUFFERING takes < 1ms.
  1833     // So we only use COPY_FILE_NO_BUFFERING when we have a remote drive.
  1834     int copyOK;
  1835     DWORD dwCopyFlags = COPY_FILE_ALLOW_DECRYPTED_DESTINATION;
  1836     if (IsVistaOrLater()) {
  1837         bool path1Remote, path2Remote;
  1838         if (!IsRemoteFilePath(filePath.get(), path1Remote) || 
  1839             !IsRemoteFilePath(destPath.get(), path2Remote) ||
  1840             path1Remote || path2Remote) {
  1841             dwCopyFlags |= COPY_FILE_NO_BUFFERING;
  1845     if (!move)
  1847         copyOK = ::CopyFileExW(filePath.get(), destPath.get(), nullptr,
  1848                                nullptr, nullptr, dwCopyFlags);
  1850     else
  1852         copyOK = ::MoveFileExW(filePath.get(), destPath.get(), MOVEFILE_REPLACE_EXISTING);
  1854         // Check if copying the source file to a different volume,
  1855         // as this could be an SMBV2 mapped drive.
  1856         if (!copyOK && GetLastError() == ERROR_NOT_SAME_DEVICE)
  1858             if (options & Rename) {
  1859                 return NS_ERROR_FILE_ACCESS_DENIED;
  1861             copyOK = CopyFileExW(filePath.get(), destPath.get(), nullptr,
  1862                                  nullptr, nullptr, dwCopyFlags);
  1864             if (copyOK)
  1865                 DeleteFileW(filePath.get());
  1869     if (!copyOK)  // CopyFileEx and MoveFileEx return zero at failure.
  1870         rv = ConvertWinError(GetLastError());
  1871     else if (move && !(options & SkipNtfsAclReset))
  1873         // Set security permissions to inherit from parent.
  1874         // Note: propagates to all children: slow for big file trees
  1875         PACL pOldDACL = nullptr;
  1876         PSECURITY_DESCRIPTOR pSD = nullptr;
  1877         ::GetNamedSecurityInfoW((LPWSTR)destPath.get(), SE_FILE_OBJECT,
  1878                                 DACL_SECURITY_INFORMATION,
  1879                                 nullptr, nullptr, &pOldDACL, nullptr, &pSD);
  1880         if (pOldDACL)
  1881             ::SetNamedSecurityInfoW((LPWSTR)destPath.get(), SE_FILE_OBJECT,
  1882                                     DACL_SECURITY_INFORMATION |
  1883                                     UNPROTECTED_DACL_SECURITY_INFORMATION,
  1884                                     nullptr, nullptr, pOldDACL, nullptr);
  1885         if (pSD)
  1886             LocalFree((HLOCAL)pSD);
  1889     return rv;
  1892 nsresult
  1893 nsLocalFile::CopyMove(nsIFile *aParentDir, const nsAString &newName, uint32_t options)
  1895     bool move = options & (Move | Rename);
  1896     bool followSymlinks = options & FollowSymlinks;
  1898     nsCOMPtr<nsIFile> newParentDir = aParentDir;
  1899     // check to see if this exists, otherwise return an error.
  1900     // we will check this by resolving.  If the user wants us
  1901     // to follow links, then we are talking about the target,
  1902     // hence we can use the |FollowSymlinks| option.
  1903     nsresult rv  = ResolveAndStat();
  1904     if (NS_FAILED(rv))
  1905         return rv;
  1907     if (!newParentDir)
  1909         // no parent was specified.  We must rename.
  1910         if (newName.IsEmpty())
  1911             return NS_ERROR_INVALID_ARG;
  1913         rv = GetParent(getter_AddRefs(newParentDir));
  1914         if (NS_FAILED(rv))
  1915             return rv;
  1918     if (!newParentDir)
  1919         return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  1921     // make sure it exists and is a directory.  Create it if not there.
  1922     bool exists;
  1923     newParentDir->Exists(&exists);
  1924     if (!exists)
  1926         rv = newParentDir->Create(DIRECTORY_TYPE, 0644);  // TODO, what permissions should we use
  1927         if (NS_FAILED(rv))
  1928             return rv;
  1930     else
  1932         bool isDir;
  1933         newParentDir->IsDirectory(&isDir);
  1934         if (!isDir)
  1936             if (followSymlinks)
  1938                 bool isLink;
  1939                 newParentDir->IsSymlink(&isLink);
  1940                 if (isLink)
  1942                     nsAutoString target;
  1943                     newParentDir->GetTarget(target);
  1945                     nsCOMPtr<nsIFile> realDest = new nsLocalFile();
  1946                     if (realDest == nullptr)
  1947                         return NS_ERROR_OUT_OF_MEMORY;
  1949                     rv = realDest->InitWithPath(target);
  1951                     if (NS_FAILED(rv))
  1952                         return rv;
  1954                     return CopyMove(realDest, newName, options);
  1957             else
  1959                 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  1964     // Try different ways to move/copy files/directories
  1965     bool done = false;
  1966     bool isDir;
  1967     IsDirectory(&isDir);
  1968     bool isSymlink;
  1969     IsSymlink(&isSymlink);
  1971     // Try to move the file or directory, or try to copy a single file (or non-followed symlink)
  1972     if (move || !isDir || (isSymlink && !followSymlinks))
  1974         // Copy/Move single file, or move a directory
  1975         if (!aParentDir) {
  1976             options |= SkipNtfsAclReset;
  1978         rv = CopySingleFile(this, newParentDir, newName, options);
  1979         done = NS_SUCCEEDED(rv);
  1980         // If we are moving a directory and that fails, fallback on directory
  1981         // enumeration.  See bug 231300 for details.
  1982         if (!done && !(move && isDir))
  1983             return rv;  
  1986     // Not able to copy or move directly, so enumerate it
  1987     if (!done)
  1989         // create a new target destination in the new parentDir;
  1990         nsCOMPtr<nsIFile> target;
  1991         rv = newParentDir->Clone(getter_AddRefs(target));
  1993         if (NS_FAILED(rv))
  1994             return rv;
  1996         nsAutoString allocatedNewName;
  1997         if (newName.IsEmpty())
  1999             bool isLink;
  2000             IsSymlink(&isLink);
  2001             if (isLink)
  2003                 nsAutoString temp;
  2004                 GetTarget(temp);
  2005                 int32_t offset = temp.RFindChar(L'\\'); 
  2006                 if (offset == kNotFound)
  2007                     allocatedNewName = temp;
  2008                 else 
  2009                     allocatedNewName = Substring(temp, offset + 1);
  2011             else
  2013                 GetLeafName(allocatedNewName);// this should be the leaf name of the
  2016         else
  2018             allocatedNewName = newName;
  2021         rv = target->Append(allocatedNewName);
  2022         if (NS_FAILED(rv))
  2023             return rv;
  2025         allocatedNewName.Truncate();
  2027         // check if the destination directory already exists
  2028         target->Exists(&exists);
  2029         if (!exists)
  2031             // if the destination directory cannot be created, return an error
  2032             rv = target->Create(DIRECTORY_TYPE, 0644);  // TODO, what permissions should we use
  2033             if (NS_FAILED(rv))
  2034                 return rv;
  2036         else
  2038             // check if the destination directory is writable and empty
  2039             bool isWritable;
  2041             target->IsWritable(&isWritable);
  2042             if (!isWritable)
  2043                 return NS_ERROR_FILE_ACCESS_DENIED;
  2045             nsCOMPtr<nsISimpleEnumerator> targetIterator;
  2046             rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
  2047             if (NS_FAILED(rv))
  2048                 return rv;
  2050             bool more;
  2051             targetIterator->HasMoreElements(&more);
  2052             // return error if target directory is not empty
  2053             if (more)
  2054                 return NS_ERROR_FILE_DIR_NOT_EMPTY;
  2057         nsDirEnumerator dirEnum;
  2059         rv = dirEnum.Init(this);
  2060         if (NS_FAILED(rv)) {
  2061             NS_WARNING("dirEnum initialization failed");
  2062             return rv;
  2065         bool more = false;
  2066         while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
  2068             nsCOMPtr<nsISupports> item;
  2069             nsCOMPtr<nsIFile> file;
  2070             dirEnum.GetNext(getter_AddRefs(item));
  2071             file = do_QueryInterface(item);
  2072             if (file)
  2074                 bool isDir, isLink;
  2076                 file->IsDirectory(&isDir);
  2077                 file->IsSymlink(&isLink);
  2079                 if (move)
  2081                     if (followSymlinks)
  2082                         return NS_ERROR_FAILURE;
  2084                     rv = file->MoveTo(target, EmptyString());
  2085                     if (NS_FAILED(rv))
  2086                         return rv;
  2088                 else
  2090                     if (followSymlinks)
  2091                         rv = file->CopyToFollowingLinks(target, EmptyString());
  2092                     else
  2093                         rv = file->CopyTo(target, EmptyString());
  2094                     if (NS_FAILED(rv))
  2095                         return rv;
  2099         // we've finished moving all the children of this directory
  2100         // in the new directory.  so now delete the directory
  2101         // note, we don't need to do a recursive delete.
  2102         // MoveTo() is recursive.  At this point,
  2103         // we've already moved the children of the current folder
  2104         // to the new location.  nothing should be left in the folder.
  2105         if (move)
  2107           rv = Remove(false /* recursive */);
  2108           if (NS_FAILED(rv))
  2109               return rv;
  2114     // If we moved, we want to adjust this.
  2115     if (move)
  2117         MakeDirty();
  2119         nsAutoString newParentPath;
  2120         newParentDir->GetPath(newParentPath);
  2122         if (newParentPath.IsEmpty())
  2123             return NS_ERROR_FAILURE;
  2125         if (newName.IsEmpty())
  2127             nsAutoString aFileName;
  2128             GetLeafName(aFileName);
  2130             InitWithPath(newParentPath);
  2131             Append(aFileName);
  2133         else
  2135             InitWithPath(newParentPath);
  2136             Append(newName);
  2140     return NS_OK;
  2143 NS_IMETHODIMP
  2144 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
  2146     return CopyMove(newParentDir, newName, 0);
  2149 NS_IMETHODIMP
  2150 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
  2152     return CopyMove(newParentDir, newName, FollowSymlinks);
  2155 NS_IMETHODIMP
  2156 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
  2158     return CopyMove(newParentDir, newName, Move);
  2161 NS_IMETHODIMP
  2162 nsLocalFile::RenameTo(nsIFile *newParentDir, const nsAString & newName)
  2164   nsCOMPtr<nsIFile> targetParentDir = newParentDir;
  2165   // check to see if this exists, otherwise return an error.
  2166   // we will check this by resolving.  If the user wants us
  2167   // to follow links, then we are talking about the target,
  2168   // hence we can use the |followSymlinks| parameter.
  2169   nsresult rv = ResolveAndStat();
  2170   if (NS_FAILED(rv)) {
  2171     return rv;
  2174   if (!targetParentDir) {
  2175     // no parent was specified.  We must rename.
  2176     if (newName.IsEmpty()) {
  2177       return NS_ERROR_INVALID_ARG;
  2179     rv = GetParent(getter_AddRefs(targetParentDir));
  2180     if (NS_FAILED(rv)) {
  2181       return rv;
  2185   if (!targetParentDir) {
  2186     return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  2189   // make sure it exists and is a directory.  Create it if not there.
  2190   bool exists;
  2191   targetParentDir->Exists(&exists);
  2192   if (!exists) {
  2193     rv = targetParentDir->Create(DIRECTORY_TYPE, 0644);
  2194     if (NS_FAILED(rv)) {
  2195       return rv;
  2197   } else {
  2198     bool isDir;
  2199     targetParentDir->IsDirectory(&isDir);
  2200     if (!isDir) {
  2201       return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  2205   uint32_t options = Rename;
  2206   if (!newParentDir) {
  2207     options |= SkipNtfsAclReset;
  2209   // Move single file, or move a directory
  2210   return CopySingleFile(this, targetParentDir, newName, options);
  2213 NS_IMETHODIMP
  2214 nsLocalFile::Load(PRLibrary * *_retval)
  2216     // Check we are correctly initialized.
  2217     CHECK_mWorkingPath();
  2219     bool isFile;
  2220     nsresult rv = IsFile(&isFile);
  2222     if (NS_FAILED(rv))
  2223         return rv;
  2225     if (! isFile)
  2226         return NS_ERROR_FILE_IS_DIRECTORY;
  2228 #ifdef NS_BUILD_REFCNT_LOGGING
  2229     nsTraceRefcnt::SetActivityIsLegal(false);
  2230 #endif
  2232     PRLibSpec libSpec;
  2233     libSpec.value.pathname_u = mResolvedPath.get();
  2234     libSpec.type = PR_LibSpec_PathnameU;
  2235     *_retval =  PR_LoadLibraryWithFlags(libSpec, 0);
  2237 #ifdef NS_BUILD_REFCNT_LOGGING
  2238     nsTraceRefcnt::SetActivityIsLegal(true);
  2239 #endif
  2241     if (*_retval)
  2242         return NS_OK;
  2243     return NS_ERROR_NULL_POINTER;
  2246 NS_IMETHODIMP
  2247 nsLocalFile::Remove(bool recursive)
  2249     // NOTE:
  2250     //
  2251     // if the working path points to a shortcut, then we will only
  2252     // delete the shortcut itself.  even if the shortcut points to
  2253     // a directory, we will not recurse into that directory or 
  2254     // delete that directory itself.  likewise, if the shortcut
  2255     // points to a normal file, we will not delete the real file.
  2256     // this is done to be consistent with the other platforms that
  2257     // behave this way.  we do this even if the followLinks attribute
  2258     // is set to true.  this helps protect against misuse that could
  2259     // lead to security bugs (e.g., bug 210588).
  2260     //
  2261     // Since shortcut files are no longer permitted to be used as unix-like
  2262     // symlinks interspersed in the path (e.g. "c:/file.lnk/foo/bar.txt")
  2263     // this processing is a lot simpler. Even if the shortcut file is 
  2264     // pointing to a directory, only the mWorkingPath value is used and so
  2265     // only the shortcut file will be deleted.
  2267     // Check we are correctly initialized.
  2268     CHECK_mWorkingPath();
  2270     bool isDir, isLink;
  2271     nsresult rv;
  2273     isDir = false;
  2274     rv = IsSymlink(&isLink);
  2275     if (NS_FAILED(rv))
  2276         return rv;
  2278     // only check to see if we have a directory if it isn't a link
  2279     if (!isLink)
  2281         rv = IsDirectory(&isDir);
  2282         if (NS_FAILED(rv))
  2283             return rv;
  2286     if (isDir)
  2288         if (recursive)
  2290             nsDirEnumerator dirEnum;
  2292             rv = dirEnum.Init(this);
  2293             if (NS_FAILED(rv))
  2294                 return rv;
  2296             bool more = false;
  2297             while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
  2299                 nsCOMPtr<nsISupports> item;
  2300                 dirEnum.GetNext(getter_AddRefs(item));
  2301                 nsCOMPtr<nsIFile> file = do_QueryInterface(item);
  2302                 if (file)
  2303                     file->Remove(recursive);
  2306         if (RemoveDirectoryW(mWorkingPath.get()) == 0)
  2307             return ConvertWinError(GetLastError());
  2309     else
  2311         if (DeleteFileW(mWorkingPath.get()) == 0)
  2312             return ConvertWinError(GetLastError());
  2315     MakeDirty();
  2316     return rv;
  2319 NS_IMETHODIMP
  2320 nsLocalFile::GetLastModifiedTime(PRTime *aLastModifiedTime)
  2322     // Check we are correctly initialized.
  2323     CHECK_mWorkingPath();
  2325     if (NS_WARN_IF(!aLastModifiedTime))
  2326         return NS_ERROR_INVALID_ARG;
  2328     // get the modified time of the target as determined by mFollowSymlinks
  2329     // If true, then this will be for the target of the shortcut file, 
  2330     // otherwise it will be for the shortcut file itself (i.e. the same 
  2331     // results as GetLastModifiedTimeOfLink)
  2333     nsresult rv = ResolveAndStat();
  2334     if (NS_FAILED(rv))
  2335         return rv;
  2337     // microseconds -> milliseconds
  2338     *aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
  2339     return NS_OK;
  2343 NS_IMETHODIMP
  2344 nsLocalFile::GetLastModifiedTimeOfLink(PRTime *aLastModifiedTime)
  2346     // Check we are correctly initialized.
  2347     CHECK_mWorkingPath();
  2349     if (NS_WARN_IF(!aLastModifiedTime))
  2350         return NS_ERROR_INVALID_ARG;
  2352     // The caller is assumed to have already called IsSymlink 
  2353     // and to have found that this file is a link. 
  2355     PRFileInfo64 info;
  2356     nsresult rv = GetFileInfo(mWorkingPath, &info);
  2357     if (NS_FAILED(rv)) 
  2358         return rv;
  2360     // microseconds -> milliseconds
  2361     *aLastModifiedTime = info.modifyTime / PR_USEC_PER_MSEC;
  2362     return NS_OK;
  2366 NS_IMETHODIMP
  2367 nsLocalFile::SetLastModifiedTime(PRTime aLastModifiedTime)
  2369     // Check we are correctly initialized.
  2370     CHECK_mWorkingPath();
  2372     nsresult rv = ResolveAndStat();
  2373     if (NS_FAILED(rv))
  2374         return rv;
  2376     // set the modified time of the target as determined by mFollowSymlinks
  2377     // If true, then this will be for the target of the shortcut file, 
  2378     // otherwise it will be for the shortcut file itself (i.e. the same 
  2379     // results as SetLastModifiedTimeOfLink)
  2381     rv = SetModDate(aLastModifiedTime, mResolvedPath.get());
  2382     if (NS_SUCCEEDED(rv))
  2383         MakeDirty();
  2385     return rv;
  2389 NS_IMETHODIMP
  2390 nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModifiedTime)
  2392     // The caller is assumed to have already called IsSymlink 
  2393     // and to have found that this file is a link. 
  2395     nsresult rv = SetModDate(aLastModifiedTime, mWorkingPath.get());
  2396     if (NS_SUCCEEDED(rv))
  2397         MakeDirty();
  2399     return rv;
  2402 nsresult
  2403 nsLocalFile::SetModDate(PRTime aLastModifiedTime, const wchar_t *filePath)
  2405     // The FILE_FLAG_BACKUP_SEMANTICS is required in order to change the
  2406     // modification time for directories.
  2407     HANDLE file = ::CreateFileW(filePath,          // pointer to name of the file
  2408                                 GENERIC_WRITE,     // access (write) mode
  2409                                 0,                 // share mode
  2410                                 nullptr,           // pointer to security attributes
  2411                                 OPEN_EXISTING,     // how to create
  2412                                 FILE_FLAG_BACKUP_SEMANTICS,  // file attributes
  2413                                 nullptr);
  2415     if (file == INVALID_HANDLE_VALUE)
  2417         return ConvertWinError(GetLastError());
  2420     FILETIME ft;
  2421     SYSTEMTIME st;
  2422     PRExplodedTime pret;
  2424     // PR_ExplodeTime expects usecs...
  2425     PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_GMTParameters, &pret);
  2426     st.wYear            = pret.tm_year;
  2427     st.wMonth           = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
  2428     st.wDayOfWeek       = pret.tm_wday;
  2429     st.wDay             = pret.tm_mday;
  2430     st.wHour            = pret.tm_hour;
  2431     st.wMinute          = pret.tm_min;
  2432     st.wSecond          = pret.tm_sec;
  2433     st.wMilliseconds    = pret.tm_usec/1000;
  2435     nsresult rv = NS_OK;
  2436     // if at least one of these fails...
  2437     if (!(SystemTimeToFileTime(&st, &ft) != 0 &&
  2438           SetFileTime(file, nullptr, &ft, &ft) != 0))
  2440       rv = ConvertWinError(GetLastError());
  2443     CloseHandle(file);
  2444     return rv;
  2447 NS_IMETHODIMP
  2448 nsLocalFile::GetPermissions(uint32_t *aPermissions)
  2450     if (NS_WARN_IF(!aPermissions))
  2451         return NS_ERROR_INVALID_ARG;
  2453     // get the permissions of the target as determined by mFollowSymlinks
  2454     // If true, then this will be for the target of the shortcut file, 
  2455     // otherwise it will be for the shortcut file itself (i.e. the same 
  2456     // results as GetPermissionsOfLink)
  2457     nsresult rv = ResolveAndStat();
  2458     if (NS_FAILED(rv))
  2459         return rv;
  2461     bool isWritable, isExecutable;
  2462     IsWritable(&isWritable);
  2463     IsExecutable(&isExecutable);
  2465     *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH;         // all read
  2466     if (isWritable)
  2467         *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH;    // all write
  2468     if (isExecutable)
  2469         *aPermissions |= PR_IXUSR|PR_IXGRP|PR_IXOTH;    // all execute
  2471     return NS_OK;
  2474 NS_IMETHODIMP
  2475 nsLocalFile::GetPermissionsOfLink(uint32_t *aPermissions)
  2477     // Check we are correctly initialized.
  2478     CHECK_mWorkingPath();
  2480     if (NS_WARN_IF(!aPermissions))
  2481         return NS_ERROR_INVALID_ARG;
  2483     // The caller is assumed to have already called IsSymlink 
  2484     // and to have found that this file is a link. It is not 
  2485     // possible for a link file to be executable.
  2487     DWORD word = ::GetFileAttributesW(mWorkingPath.get());
  2488     if (word == INVALID_FILE_ATTRIBUTES)
  2489         return NS_ERROR_FILE_INVALID_PATH;
  2491     bool isWritable = !(word & FILE_ATTRIBUTE_READONLY);
  2492     *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH;         // all read
  2493     if (isWritable)
  2494         *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH;    // all write
  2496     return NS_OK;
  2500 NS_IMETHODIMP
  2501 nsLocalFile::SetPermissions(uint32_t aPermissions)
  2503     // Check we are correctly initialized.
  2504     CHECK_mWorkingPath();
  2506     // set the permissions of the target as determined by mFollowSymlinks
  2507     // If true, then this will be for the target of the shortcut file, 
  2508     // otherwise it will be for the shortcut file itself (i.e. the same 
  2509     // results as SetPermissionsOfLink)
  2510     nsresult rv = ResolveAndStat();
  2511     if (NS_FAILED(rv))
  2512         return rv;
  2514     // windows only knows about the following permissions
  2515     int mode = 0;
  2516     if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH))    // any read
  2517         mode |= _S_IREAD;
  2518     if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH))    // any write
  2519         mode |= _S_IWRITE;
  2521     if (_wchmod(mResolvedPath.get(), mode) == -1)
  2522         return NS_ERROR_FAILURE;
  2524     return NS_OK;
  2527 NS_IMETHODIMP
  2528 nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
  2530     // The caller is assumed to have already called IsSymlink 
  2531     // and to have found that this file is a link. 
  2533     // windows only knows about the following permissions
  2534     int mode = 0;
  2535     if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH))    // any read
  2536         mode |= _S_IREAD;
  2537     if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH))    // any write
  2538         mode |= _S_IWRITE;
  2540     if (_wchmod(mWorkingPath.get(), mode) == -1)
  2541         return NS_ERROR_FAILURE;
  2543     return NS_OK;
  2547 NS_IMETHODIMP
  2548 nsLocalFile::GetFileSize(int64_t *aFileSize)
  2550     if (NS_WARN_IF(!aFileSize))
  2551         return NS_ERROR_INVALID_ARG;
  2553     nsresult rv = ResolveAndStat();
  2554     if (NS_FAILED(rv))
  2555         return rv;
  2557     *aFileSize = mFileInfo64.size;
  2558     return NS_OK;
  2562 NS_IMETHODIMP
  2563 nsLocalFile::GetFileSizeOfLink(int64_t *aFileSize)
  2565     // Check we are correctly initialized.
  2566     CHECK_mWorkingPath();
  2568     if (NS_WARN_IF(!aFileSize))
  2569         return NS_ERROR_INVALID_ARG;
  2571     // The caller is assumed to have already called IsSymlink 
  2572     // and to have found that this file is a link. 
  2574     PRFileInfo64 info;
  2575     if (NS_FAILED(GetFileInfo(mWorkingPath, &info)))
  2576         return NS_ERROR_FILE_INVALID_PATH;
  2578     *aFileSize = info.size;
  2579     return NS_OK;
  2582 NS_IMETHODIMP
  2583 nsLocalFile::SetFileSize(int64_t aFileSize)
  2585     // Check we are correctly initialized.
  2586     CHECK_mWorkingPath();
  2588     nsresult rv = ResolveAndStat();
  2589     if (NS_FAILED(rv))
  2590         return rv;
  2592     HANDLE hFile = ::CreateFileW(mResolvedPath.get(),// pointer to name of the file
  2593                                  GENERIC_WRITE,      // access (write) mode
  2594                                  FILE_SHARE_READ,    // share mode
  2595                                  nullptr,            // pointer to security attributes
  2596                                  OPEN_EXISTING,          // how to create
  2597                                  FILE_ATTRIBUTE_NORMAL,  // file attributes
  2598                                  nullptr);
  2599     if (hFile == INVALID_HANDLE_VALUE)
  2601         return ConvertWinError(GetLastError());
  2604     // seek the file pointer to the new, desired end of file
  2605     // and then truncate the file at that position
  2606     rv = NS_ERROR_FAILURE;
  2607     aFileSize = MyFileSeek64(hFile, aFileSize, FILE_BEGIN);
  2608     if (aFileSize != -1 && SetEndOfFile(hFile))
  2610         MakeDirty();
  2611         rv = NS_OK;
  2614     CloseHandle(hFile);
  2615     return rv;
  2618 NS_IMETHODIMP
  2619 nsLocalFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable)
  2621     // Check we are correctly initialized.
  2622     CHECK_mWorkingPath();
  2624     if (NS_WARN_IF(!aDiskSpaceAvailable))
  2625         return NS_ERROR_INVALID_ARG;
  2627     ResolveAndStat();
  2629     if (mFileInfo64.type == PR_FILE_FILE) {
  2630       // Since GetDiskFreeSpaceExW works only on directories, use the parent.
  2631       nsCOMPtr<nsIFile> parent;
  2632       if (NS_SUCCEEDED(GetParent(getter_AddRefs(parent))) && parent) {
  2633         return parent->GetDiskSpaceAvailable(aDiskSpaceAvailable);
  2637     ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes;
  2638     if (::GetDiskFreeSpaceExW(mResolvedPath.get(), &liFreeBytesAvailableToCaller, 
  2639                               &liTotalNumberOfBytes, nullptr))
  2641         *aDiskSpaceAvailable = liFreeBytesAvailableToCaller.QuadPart;
  2642         return NS_OK;
  2644     *aDiskSpaceAvailable = 0;
  2645     return NS_OK;
  2648 NS_IMETHODIMP
  2649 nsLocalFile::GetParent(nsIFile * *aParent)
  2651     // Check we are correctly initialized.
  2652     CHECK_mWorkingPath();
  2654     if (NS_WARN_IF(!aParent))
  2655         return NS_ERROR_INVALID_ARG;
  2657     // A two-character path must be a drive such as C:, so it has no parent
  2658     if (mWorkingPath.Length() == 2) {
  2659         *aParent = nullptr;
  2660         return NS_OK;
  2663     int32_t offset = mWorkingPath.RFindChar(char16_t('\\'));
  2664     // adding this offset check that was removed in bug 241708 fixes mail
  2665     // directories that aren't relative to/underneath the profile dir.
  2666     // e.g., on a different drive. Before you remove them, please make
  2667     // sure local mail directories that aren't underneath the profile dir work.
  2668     if (offset == kNotFound)
  2669       return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  2671     // A path of the form \\NAME is a top-level path and has no parent
  2672     if (offset == 1 && mWorkingPath[0] == L'\\') {
  2673         *aParent = nullptr;
  2674         return NS_OK;
  2677     nsAutoString parentPath(mWorkingPath);
  2679     if (offset > 0)
  2680         parentPath.Truncate(offset);
  2681     else
  2682         parentPath.AssignLiteral("\\\\.");
  2684     nsCOMPtr<nsIFile> localFile;
  2685     nsresult rv = NS_NewLocalFile(parentPath, mFollowSymlinks, getter_AddRefs(localFile));
  2687     if (NS_FAILED(rv)) {
  2688         return rv;
  2691     localFile.forget(aParent);
  2692     return NS_OK;
  2695 NS_IMETHODIMP
  2696 nsLocalFile::Exists(bool *_retval)
  2698     // Check we are correctly initialized.
  2699     CHECK_mWorkingPath();
  2701     if (NS_WARN_IF(!_retval))
  2702         return NS_ERROR_INVALID_ARG;
  2703     *_retval = false;
  2705     MakeDirty();
  2706     nsresult rv = ResolveAndStat();
  2707     *_retval = NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_IS_LOCKED;
  2709     return NS_OK;
  2712 NS_IMETHODIMP
  2713 nsLocalFile::IsWritable(bool *aIsWritable)
  2715     // Check we are correctly initialized.
  2716     CHECK_mWorkingPath();
  2718     // The read-only attribute on a FAT directory only means that it can't 
  2719     // be deleted. It is still possible to modify the contents of the directory.
  2720     nsresult rv = IsDirectory(aIsWritable);
  2721     if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
  2722       *aIsWritable = true;
  2723       return NS_OK;
  2724     } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
  2725       // If the file is normally allowed write access
  2726       // we should still return that the file is writable.
  2727     } else if (NS_FAILED(rv)) {
  2728         return rv;
  2730     if (*aIsWritable)
  2731         return NS_OK;
  2733     // writable if the file doesn't have the readonly attribute
  2734     rv = HasFileAttribute(FILE_ATTRIBUTE_READONLY, aIsWritable);
  2735     if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
  2736         *aIsWritable = false;
  2737         return NS_OK;
  2738     } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
  2739       // If the file is normally allowed write access
  2740       // we should still return that the file is writable.
  2741     } else if (NS_FAILED(rv)) {
  2742         return rv;
  2744     *aIsWritable = !*aIsWritable;
  2746     // If the read only attribute is not set, check to make sure
  2747     // we can open the file with write access.
  2748     if (*aIsWritable) {
  2749         PRFileDesc* file;
  2750         rv = OpenFile(mResolvedPath, PR_WRONLY, 0, &file);
  2751         if (NS_SUCCEEDED(rv)) {
  2752             PR_Close(file);
  2753         } else if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
  2754           *aIsWritable = false;
  2755         } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
  2756             // If it is locked and read only we would have 
  2757             // gotten access denied
  2758             *aIsWritable = true; 
  2759         } else {
  2760             return rv;
  2763     return NS_OK;
  2766 NS_IMETHODIMP
  2767 nsLocalFile::IsReadable(bool *_retval)
  2769     // Check we are correctly initialized.
  2770     CHECK_mWorkingPath();
  2772     if (NS_WARN_IF(!_retval))
  2773         return NS_ERROR_INVALID_ARG;
  2774     *_retval = false;
  2776     nsresult rv = ResolveAndStat();
  2777     if (NS_FAILED(rv))
  2778         return rv;
  2780     *_retval = true;
  2781     return NS_OK;
  2785 NS_IMETHODIMP
  2786 nsLocalFile::IsExecutable(bool *_retval)
  2788     // Check we are correctly initialized.
  2789     CHECK_mWorkingPath();
  2791     if (NS_WARN_IF(!_retval))
  2792         return NS_ERROR_INVALID_ARG;
  2793     *_retval = false;
  2795     nsresult rv;
  2797     // only files can be executables
  2798     bool isFile;
  2799     rv = IsFile(&isFile);
  2800     if (NS_FAILED(rv))
  2801         return rv;
  2802     if (!isFile)
  2803         return NS_OK;
  2805     //TODO: shouldn't we be checking mFollowSymlinks here?
  2806     bool symLink;
  2807     rv = IsSymlink(&symLink);
  2808     if (NS_FAILED(rv))
  2809         return rv;
  2811     nsAutoString path;
  2812     if (symLink)
  2813         GetTarget(path);
  2814     else
  2815         GetPath(path);
  2817     // kill trailing dots and spaces.
  2818     int32_t filePathLen = path.Length() - 1;
  2819     while(filePathLen > 0 && (path[filePathLen] == L' ' || path[filePathLen] == L'.'))
  2821         path.Truncate(filePathLen--);
  2824     // Get extension.
  2825     int32_t dotIdx = path.RFindChar(char16_t('.'));
  2826     if ( dotIdx != kNotFound ) {
  2827         // Convert extension to lower case.
  2828         char16_t *p = path.BeginWriting();
  2829         for( p+= dotIdx + 1; *p; p++ )
  2830             *p +=  (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0; 
  2832         // Search for any of the set of executable extensions.
  2833         static const char * const executableExts[] = {
  2834             "ad",
  2835             "ade",         // access project extension
  2836             "adp",
  2837             "air",         // Adobe AIR installer
  2838             "app",         // executable application
  2839             "application", // from bug 348763
  2840             "asp",
  2841             "bas",
  2842             "bat",
  2843             "chm",
  2844             "cmd",
  2845             "com",
  2846             "cpl",
  2847             "crt",
  2848             "exe",
  2849             "fxp",         // FoxPro compiled app
  2850             "hlp",
  2851             "hta",
  2852             "inf",
  2853             "ins",
  2854             "isp",
  2855             "jar",         // java application bundle
  2856             "js",
  2857             "jse",
  2858             "lnk",
  2859             "mad",         // Access Module Shortcut
  2860             "maf",         // Access
  2861             "mag",         // Access Diagram Shortcut
  2862             "mam",         // Access Macro Shortcut
  2863             "maq",         // Access Query Shortcut
  2864             "mar",         // Access Report Shortcut
  2865             "mas",         // Access Stored Procedure
  2866             "mat",         // Access Table Shortcut
  2867             "mau",         // Media Attachment Unit
  2868             "mav",         // Access View Shortcut
  2869             "maw",         // Access Data Access Page
  2870             "mda",         // Access Add-in, MDA Access 2 Workgroup
  2871             "mdb",
  2872             "mde",
  2873             "mdt",         // Access Add-in Data
  2874             "mdw",         // Access Workgroup Information
  2875             "mdz",         // Access Wizard Template
  2876             "msc",
  2877             "msh",         // Microsoft Shell
  2878             "mshxml",      // Microsoft Shell
  2879             "msi",
  2880             "msp",
  2881             "mst",
  2882             "ops",         // Office Profile Settings
  2883             "pcd",
  2884             "pif",
  2885             "plg",         // Developer Studio Build Log
  2886             "prf",         // windows system file
  2887             "prg",
  2888             "pst",
  2889             "reg",
  2890             "scf",         // Windows explorer command
  2891             "scr",
  2892             "sct",
  2893             "shb",
  2894             "shs",
  2895             "url",
  2896             "vb",
  2897             "vbe",
  2898             "vbs",
  2899             "vsd",
  2900             "vsmacros",    // Visual Studio .NET Binary-based Macro Project
  2901             "vss",
  2902             "vst",
  2903             "vsw",
  2904             "ws",
  2905             "wsc",
  2906             "wsf",
  2907             "wsh"};
  2908         nsDependentSubstring ext = Substring(path, dotIdx + 1);
  2909         for ( size_t i = 0; i < ArrayLength(executableExts); i++ ) {
  2910             if ( ext.EqualsASCII(executableExts[i])) {
  2911                 // Found a match.  Set result and quit.
  2912                 *_retval = true;
  2913                 break;
  2918     return NS_OK;
  2922 NS_IMETHODIMP
  2923 nsLocalFile::IsDirectory(bool *_retval)
  2925     return HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, _retval);
  2928 NS_IMETHODIMP
  2929 nsLocalFile::IsFile(bool *_retval)
  2931     nsresult rv = HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, _retval);
  2932     if (NS_SUCCEEDED(rv)) {
  2933         *_retval = !*_retval;
  2935     return rv;
  2938 NS_IMETHODIMP
  2939 nsLocalFile::IsHidden(bool *_retval)
  2941     return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN, _retval);
  2944 nsresult
  2945 nsLocalFile::HasFileAttribute(DWORD fileAttrib, bool *_retval)
  2947     if (NS_WARN_IF(!_retval))
  2948         return NS_ERROR_INVALID_ARG;
  2950     nsresult rv = Resolve();
  2951     if (NS_FAILED(rv)) {
  2952         return rv;
  2955     DWORD attributes = GetFileAttributesW(mResolvedPath.get());
  2956     if (INVALID_FILE_ATTRIBUTES == attributes) {
  2957         return ConvertWinError(GetLastError());
  2960     *_retval = ((attributes & fileAttrib) != 0);
  2961     return NS_OK;
  2964 NS_IMETHODIMP
  2965 nsLocalFile::IsSymlink(bool *_retval)
  2967     // Check we are correctly initialized.
  2968     CHECK_mWorkingPath();
  2970     if (NS_WARN_IF(!_retval))
  2971         return NS_ERROR_INVALID_ARG;
  2973     // unless it is a valid shortcut path it's not a symlink
  2974     if (!IsShortcutPath(mWorkingPath)) {
  2975         *_retval = false;
  2976         return NS_OK;
  2979     // we need to know if this is a file or directory
  2980     nsresult rv = ResolveAndStat();
  2981     if (NS_FAILED(rv)) {
  2982         return rv;
  2985     // We should not check mFileInfo64.type here for PR_FILE_FILE because lnk
  2986     // files can point to directories or files.  Important security checks
  2987     // depend on correctly identifying lnk files.  mFileInfo64 now holds info
  2988     // about the target of the lnk file, not the actual lnk file!
  2989     *_retval = true;
  2990     return NS_OK;
  2993 NS_IMETHODIMP
  2994 nsLocalFile::IsSpecial(bool *_retval)
  2996     return HasFileAttribute(FILE_ATTRIBUTE_SYSTEM, _retval);
  2999 NS_IMETHODIMP
  3000 nsLocalFile::Equals(nsIFile *inFile, bool *_retval)
  3002     if (NS_WARN_IF(!inFile))
  3003         return NS_ERROR_INVALID_ARG;
  3004     if (NS_WARN_IF(!_retval))
  3005         return NS_ERROR_INVALID_ARG;
  3007     EnsureShortPath();
  3009     nsCOMPtr<nsILocalFileWin> lf(do_QueryInterface(inFile));
  3010     if (!lf) {
  3011         *_retval = false;
  3012         return NS_OK;
  3015     nsAutoString inFilePath;
  3016     lf->GetCanonicalPath(inFilePath);
  3018     // Ok : Win9x
  3019     *_retval = _wcsicmp(mShortWorkingPath.get(), inFilePath.get()) == 0; 
  3021     return NS_OK;
  3025 NS_IMETHODIMP
  3026 nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval)
  3028     // Check we are correctly initialized.
  3029     CHECK_mWorkingPath();
  3031     *_retval = false;
  3033     nsAutoString myFilePath;
  3034     if (NS_FAILED(GetTarget(myFilePath)))
  3035         GetPath(myFilePath);
  3037     uint32_t myFilePathLen = myFilePath.Length();
  3039     nsAutoString inFilePath;
  3040     if (NS_FAILED(inFile->GetTarget(inFilePath)))
  3041         inFile->GetPath(inFilePath);
  3043     // make sure that the |inFile|'s path has a trailing separator.
  3044     if (inFilePath.Length() >= myFilePathLen && inFilePath[myFilePathLen] == L'\\')
  3046         if (_wcsnicmp(myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
  3048             *_retval = true;
  3053     return NS_OK;
  3057 NS_IMETHODIMP
  3058 nsLocalFile::GetTarget(nsAString &_retval)
  3060     _retval.Truncate();
  3061 #if STRICT_FAKE_SYMLINKS
  3062     bool symLink;
  3064     nsresult rv = IsSymlink(&symLink);
  3065     if (NS_FAILED(rv))
  3066         return rv;
  3068     if (!symLink)
  3070         return NS_ERROR_FILE_INVALID_PATH;
  3072 #endif
  3073     ResolveAndStat();
  3075     _retval = mResolvedPath;
  3076     return NS_OK;
  3080 /* attribute bool followLinks; */
  3081 NS_IMETHODIMP
  3082 nsLocalFile::GetFollowLinks(bool *aFollowLinks)
  3084     *aFollowLinks = mFollowSymlinks;
  3085     return NS_OK;
  3087 NS_IMETHODIMP
  3088 nsLocalFile::SetFollowLinks(bool aFollowLinks)
  3090     MakeDirty();
  3091     mFollowSymlinks = aFollowLinks;
  3092     return NS_OK;
  3096 NS_IMETHODIMP
  3097 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
  3099     nsresult rv;
  3101     *entries = nullptr;
  3102     if (mWorkingPath.EqualsLiteral("\\\\.")) {
  3103         nsDriveEnumerator *drives = new nsDriveEnumerator;
  3104         if (!drives)
  3105             return NS_ERROR_OUT_OF_MEMORY;
  3106         NS_ADDREF(drives);
  3107         rv = drives->Init();
  3108         if (NS_FAILED(rv)) {
  3109             NS_RELEASE(drives);
  3110             return rv;
  3112         *entries = drives;
  3113         return NS_OK;
  3116     nsDirEnumerator* dirEnum = new nsDirEnumerator();
  3117     if (dirEnum == nullptr)
  3118         return NS_ERROR_OUT_OF_MEMORY;
  3119     NS_ADDREF(dirEnum);
  3120     rv = dirEnum->Init(this);
  3121     if (NS_FAILED(rv))
  3123         NS_RELEASE(dirEnum);
  3124         return rv;
  3127     *entries = dirEnum;
  3129     return NS_OK;
  3132 NS_IMETHODIMP
  3133 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
  3135     CopyUTF16toUTF8(mWorkingPath, aPersistentDescriptor);
  3136     return NS_OK;
  3139 NS_IMETHODIMP
  3140 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
  3142     if (IsUTF8(aPersistentDescriptor))
  3143         return InitWithPath(NS_ConvertUTF8toUTF16(aPersistentDescriptor));
  3144     else
  3145         return InitWithNativePath(aPersistentDescriptor);
  3148 /* attrib unsigned long fileAttributesWin; */
  3149 NS_IMETHODIMP
  3150 nsLocalFile::GetFileAttributesWin(uint32_t *aAttribs)
  3152     *aAttribs = 0;
  3153     DWORD dwAttrs = GetFileAttributesW(mWorkingPath.get());
  3154     if (dwAttrs == INVALID_FILE_ATTRIBUTES)
  3155       return NS_ERROR_FILE_INVALID_PATH;
  3157     if (!(dwAttrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
  3158         *aAttribs |= WFA_SEARCH_INDEXED;
  3160     return NS_OK;
  3163 NS_IMETHODIMP
  3164 nsLocalFile::SetFileAttributesWin(uint32_t aAttribs)
  3166     DWORD dwAttrs = GetFileAttributesW(mWorkingPath.get());
  3167     if (dwAttrs == INVALID_FILE_ATTRIBUTES)
  3168       return NS_ERROR_FILE_INVALID_PATH;
  3170     if (aAttribs & WFA_SEARCH_INDEXED) {
  3171         dwAttrs &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
  3172     } else {
  3173         dwAttrs |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
  3176     if (aAttribs & WFA_READONLY) {
  3177       dwAttrs |= FILE_ATTRIBUTE_READONLY;
  3178     } else if ((aAttribs & WFA_READWRITE) &&
  3179                (dwAttrs & FILE_ATTRIBUTE_READONLY)) {
  3180       dwAttrs &= ~FILE_ATTRIBUTE_READONLY;
  3183     if (SetFileAttributesW(mWorkingPath.get(), dwAttrs) == 0)
  3184       return NS_ERROR_FAILURE;
  3185     return NS_OK;
  3189 NS_IMETHODIMP
  3190 nsLocalFile::Reveal()
  3192     // This API should be main thread only
  3193     MOZ_ASSERT(NS_IsMainThread()); 
  3195     // make sure mResolvedPath is set
  3196     nsresult rv = Resolve();
  3197     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
  3198         return rv;
  3201     // To create a new thread, get the thread manager
  3202     nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
  3203     nsCOMPtr<nsIThread> mythread;
  3204     rv = tm->NewThread(0, 0, getter_AddRefs(mythread));
  3205     if (NS_FAILED(rv)) {
  3206         return rv;
  3209     nsCOMPtr<nsIRunnable> runnable = 
  3210         new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::RevealOp,
  3211                                        mResolvedPath);
  3213     // After the dispatch, the result runnable will shut down the worker
  3214     // thread, so we can let it go.
  3215     mythread->Dispatch(runnable, NS_DISPATCH_NORMAL);
  3216     return NS_OK;
  3219 NS_IMETHODIMP
  3220 nsLocalFile::Launch()
  3222     // This API should be main thread only
  3223     MOZ_ASSERT(NS_IsMainThread()); 
  3225     // make sure mResolvedPath is set
  3226     nsresult rv = Resolve();
  3227     if (NS_FAILED(rv))
  3228         return rv;
  3230     // To create a new thread, get the thread manager
  3231     nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
  3232     nsCOMPtr<nsIThread> mythread;
  3233     rv = tm->NewThread(0, 0, getter_AddRefs(mythread));
  3234     if (NS_FAILED(rv)) {
  3235         return rv;
  3238     nsCOMPtr<nsIRunnable> runnable = 
  3239         new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::LaunchOp,
  3240                                        mResolvedPath);
  3242     // After the dispatch, the result runnable will shut down the worker
  3243     // thread, so we can let it go.
  3244     mythread->Dispatch(runnable, NS_DISPATCH_NORMAL);
  3245     return NS_OK;
  3248 nsresult
  3249 NS_NewLocalFile(const nsAString &path, bool followLinks, nsIFile* *result)
  3251     nsLocalFile* file = new nsLocalFile();
  3252     if (file == nullptr)
  3253         return NS_ERROR_OUT_OF_MEMORY;
  3254     NS_ADDREF(file);
  3256     file->SetFollowLinks(followLinks);
  3258     if (!path.IsEmpty()) {
  3259         nsresult rv = file->InitWithPath(path);
  3260         if (NS_FAILED(rv)) {
  3261             NS_RELEASE(file);
  3262             return rv;
  3266     *result = file;
  3267     return NS_OK;
  3270 //-----------------------------------------------------------------------------
  3271 // Native (lossy) interface
  3272 //-----------------------------------------------------------------------------
  3274 NS_IMETHODIMP
  3275 nsLocalFile::InitWithNativePath(const nsACString &filePath)
  3277    nsAutoString tmp;
  3278    nsresult rv = NS_CopyNativeToUnicode(filePath, tmp);
  3279    if (NS_SUCCEEDED(rv))
  3280        return InitWithPath(tmp);
  3282    return rv;
  3285 NS_IMETHODIMP
  3286 nsLocalFile::AppendNative(const nsACString &node)
  3288     nsAutoString tmp;
  3289     nsresult rv = NS_CopyNativeToUnicode(node, tmp);
  3290     if (NS_SUCCEEDED(rv))
  3291         return Append(tmp);
  3293     return rv;
  3296 NS_IMETHODIMP
  3297 nsLocalFile::AppendRelativeNativePath(const nsACString &node)
  3299     nsAutoString tmp;
  3300     nsresult rv = NS_CopyNativeToUnicode(node, tmp);
  3301     if (NS_SUCCEEDED(rv))
  3302         return AppendRelativePath(tmp);
  3303     return rv;
  3307 NS_IMETHODIMP
  3308 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
  3310     //NS_WARNING("This API is lossy. Use GetLeafName !");
  3311     nsAutoString tmp;
  3312     nsresult rv = GetLeafName(tmp);
  3313     if (NS_SUCCEEDED(rv))
  3314         rv = NS_CopyUnicodeToNative(tmp, aLeafName);
  3316     return rv;
  3319 NS_IMETHODIMP
  3320 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
  3322     nsAutoString tmp;
  3323     nsresult rv = NS_CopyNativeToUnicode(aLeafName, tmp);
  3324     if (NS_SUCCEEDED(rv))
  3325         return SetLeafName(tmp);
  3327     return rv;
  3331 NS_IMETHODIMP
  3332 nsLocalFile::GetNativePath(nsACString &_retval)
  3334     //NS_WARNING("This API is lossy. Use GetPath !");
  3335     nsAutoString tmp;
  3336     nsresult rv = GetPath(tmp);
  3337     if (NS_SUCCEEDED(rv))
  3338         rv = NS_CopyUnicodeToNative(tmp, _retval);
  3340     return rv;
  3344 NS_IMETHODIMP
  3345 nsLocalFile::GetNativeCanonicalPath(nsACString &aResult)
  3347     NS_WARNING("This method is lossy. Use GetCanonicalPath !");
  3348     EnsureShortPath();
  3349     NS_CopyUnicodeToNative(mShortWorkingPath, aResult);
  3350     return NS_OK;
  3354 NS_IMETHODIMP
  3355 nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
  3357     // Check we are correctly initialized.
  3358     CHECK_mWorkingPath();
  3360     if (newName.IsEmpty())
  3361         return CopyTo(newParentDir, EmptyString());
  3363     nsAutoString tmp;
  3364     nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
  3365     if (NS_SUCCEEDED(rv))
  3366         return CopyTo(newParentDir, tmp);
  3368     return rv;
  3371 NS_IMETHODIMP
  3372 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
  3374     if (newName.IsEmpty())
  3375         return CopyToFollowingLinks(newParentDir, EmptyString());
  3377     nsAutoString tmp;
  3378     nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
  3379     if (NS_SUCCEEDED(rv))
  3380         return CopyToFollowingLinks(newParentDir, tmp);
  3382     return rv;
  3385 NS_IMETHODIMP
  3386 nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
  3388     // Check we are correctly initialized.
  3389     CHECK_mWorkingPath();
  3391     if (newName.IsEmpty())
  3392         return MoveTo(newParentDir, EmptyString());
  3394     nsAutoString tmp;
  3395     nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
  3396     if (NS_SUCCEEDED(rv))
  3397         return MoveTo(newParentDir, tmp);
  3399     return rv;
  3402 NS_IMETHODIMP
  3403 nsLocalFile::GetNativeTarget(nsACString &_retval)
  3405     // Check we are correctly initialized.
  3406     CHECK_mWorkingPath();
  3408     NS_WARNING("This API is lossy. Use GetTarget !");
  3409     nsAutoString tmp;
  3410     nsresult rv = GetTarget(tmp);
  3411     if (NS_SUCCEEDED(rv))
  3412         rv = NS_CopyUnicodeToNative(tmp, _retval);
  3414     return rv;
  3417 nsresult
  3418 NS_NewNativeLocalFile(const nsACString &path, bool followLinks, nsIFile* *result)
  3420     nsAutoString buf;
  3421     nsresult rv = NS_CopyNativeToUnicode(path, buf);
  3422     if (NS_FAILED(rv)) {
  3423         *result = nullptr;
  3424         return rv;
  3426     return NS_NewLocalFile(buf, followLinks, result);
  3429 void
  3430 nsLocalFile::EnsureShortPath()
  3432     if (!mShortWorkingPath.IsEmpty())
  3433         return;
  3435     WCHAR shortPath[MAX_PATH + 1];
  3436     DWORD lengthNeeded = ::GetShortPathNameW(mWorkingPath.get(), shortPath,
  3437                                              ArrayLength(shortPath));
  3438     // If an error occurred then lengthNeeded is set to 0 or the length of the
  3439     // needed buffer including null termination.  If it succeeds the number of
  3440     // wide characters not including null termination is returned.
  3441     if (lengthNeeded != 0 && lengthNeeded < ArrayLength(shortPath))
  3442         mShortWorkingPath.Assign(shortPath);
  3443     else
  3444         mShortWorkingPath.Assign(mWorkingPath);
  3447 // nsIHashable
  3449 NS_IMETHODIMP
  3450 nsLocalFile::Equals(nsIHashable* aOther, bool *aResult)
  3452     nsCOMPtr<nsIFile> otherfile(do_QueryInterface(aOther));
  3453     if (!otherfile) {
  3454         *aResult = false;
  3455         return NS_OK;
  3458     return Equals(otherfile, aResult);
  3461 NS_IMETHODIMP
  3462 nsLocalFile::GetHashCode(uint32_t *aResult)
  3464     // In order for short and long path names to hash to the same value we
  3465     // always hash on the short pathname.
  3466     EnsureShortPath();
  3468     *aResult = HashString(mShortWorkingPath);
  3469     return NS_OK;
  3472 //-----------------------------------------------------------------------------
  3473 // nsLocalFile <static members>
  3474 //-----------------------------------------------------------------------------
  3476 void
  3477 nsLocalFile::GlobalInit()
  3479     DebugOnly<nsresult> rv = NS_CreateShortcutResolver();
  3480     NS_ASSERTION(NS_SUCCEEDED(rv), "Shortcut resolver could not be created");
  3483 void
  3484 nsLocalFile::GlobalShutdown()
  3486     NS_DestroyShortcutResolver();
  3489 NS_IMPL_ISUPPORTS(nsDriveEnumerator, nsISimpleEnumerator)
  3491 nsDriveEnumerator::nsDriveEnumerator()
  3495 nsDriveEnumerator::~nsDriveEnumerator()
  3499 nsresult nsDriveEnumerator::Init()
  3501     /* If the length passed to GetLogicalDriveStrings is smaller
  3502      * than the length of the string it would return, it returns
  3503      * the length required for the string. */
  3504     DWORD length = GetLogicalDriveStringsW(0, 0);
  3505     /* The string is null terminated */
  3506     if (!mDrives.SetLength(length+1, fallible_t()))
  3507         return NS_ERROR_OUT_OF_MEMORY;
  3508     if (!GetLogicalDriveStringsW(length, wwc(mDrives.BeginWriting())))
  3509         return NS_ERROR_FAILURE;
  3510     mDrives.BeginReading(mStartOfCurrentDrive);
  3511     mDrives.EndReading(mEndOfDrivesString);
  3512     return NS_OK;
  3515 NS_IMETHODIMP nsDriveEnumerator::HasMoreElements(bool *aHasMore)
  3517     *aHasMore = *mStartOfCurrentDrive != L'\0';
  3518     return NS_OK;
  3521 NS_IMETHODIMP nsDriveEnumerator::GetNext(nsISupports **aNext)
  3523     /* GetLogicalDrives stored in mDrives is a concatenation
  3524      * of null terminated strings, followed by a null terminator.
  3525      * mStartOfCurrentDrive is an iterator pointing at the first
  3526      * character of the current drive. */
  3527     if (*mStartOfCurrentDrive == L'\0') {
  3528         *aNext = nullptr;
  3529         return NS_OK;
  3532     nsAString::const_iterator driveEnd = mStartOfCurrentDrive;
  3533     FindCharInReadable(L'\0', driveEnd, mEndOfDrivesString);
  3534     nsString drive(Substring(mStartOfCurrentDrive, driveEnd));
  3535     mStartOfCurrentDrive = ++driveEnd;
  3537     nsIFile *file;
  3538     nsresult rv = NS_NewLocalFile(drive, false, &file);
  3540     *aNext = file;
  3541     return rv;

mercurial