xpcom/io/nsLocalFileWin.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/io/nsLocalFileWin.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3542 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/ArrayUtils.h"
    1.10 +#include "mozilla/DebugOnly.h"
    1.11 +#include "mozilla/WindowsVersion.h"
    1.12 +
    1.13 +#include "nsCOMPtr.h"
    1.14 +#include "nsAutoPtr.h"
    1.15 +#include "nsMemory.h"
    1.16 +
    1.17 +#include "nsLocalFile.h"
    1.18 +#include "nsIDirectoryEnumerator.h"
    1.19 +#include "nsNativeCharsetUtils.h"
    1.20 +
    1.21 +#include "nsISimpleEnumerator.h"
    1.22 +#include "nsIComponentManager.h"
    1.23 +#include "prio.h"
    1.24 +#include "private/pprio.h"  // To get PR_ImportFile
    1.25 +#include "prprf.h"
    1.26 +#include "prmem.h"
    1.27 +#include "nsHashKeys.h"
    1.28 +
    1.29 +#include "nsXPIDLString.h"
    1.30 +#include "nsReadableUtils.h"
    1.31 +
    1.32 +#include <direct.h>
    1.33 +#include <windows.h>
    1.34 +#include <shlwapi.h>
    1.35 +#include <aclapi.h>
    1.36 +
    1.37 +#include "shellapi.h"
    1.38 +#include "shlguid.h"
    1.39 +
    1.40 +#include  <io.h>
    1.41 +#include  <stdio.h>
    1.42 +#include  <stdlib.h>
    1.43 +#include  <mbstring.h>
    1.44 +
    1.45 +#include "nsXPIDLString.h"
    1.46 +#include "prproces.h"
    1.47 +#include "prlink.h"
    1.48 +
    1.49 +#include "mozilla/Mutex.h"
    1.50 +#include "SpecialSystemDirectory.h"
    1.51 +
    1.52 +#include "nsTraceRefcnt.h"
    1.53 +#include "nsXPCOMCIDInternal.h"
    1.54 +#include "nsThreadUtils.h"
    1.55 +#include "nsXULAppAPI.h"
    1.56 +
    1.57 +using namespace mozilla;
    1.58 +
    1.59 +#define CHECK_mWorkingPath()                    \
    1.60 +    PR_BEGIN_MACRO                              \
    1.61 +        if (mWorkingPath.IsEmpty())             \
    1.62 +            return NS_ERROR_NOT_INITIALIZED;    \
    1.63 +    PR_END_MACRO
    1.64 +
    1.65 +// CopyFileEx only supports unbuffered I/O in Windows Vista and above
    1.66 +#ifndef COPY_FILE_NO_BUFFERING
    1.67 +#define COPY_FILE_NO_BUFFERING 0x00001000
    1.68 +#endif
    1.69 +
    1.70 +#ifndef FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
    1.71 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  0x00002000
    1.72 +#endif
    1.73 +
    1.74 +#ifndef DRIVE_REMOTE
    1.75 +#define DRIVE_REMOTE 4
    1.76 +#endif
    1.77 +
    1.78 +/**
    1.79 + * A runnable to dispatch back to the main thread when 
    1.80 + * AsyncLocalFileWinOperation completes.
    1.81 +*/
    1.82 +class AsyncLocalFileWinDone : public nsRunnable
    1.83 +{
    1.84 +public:
    1.85 +    AsyncLocalFileWinDone() :
    1.86 +        mWorkerThread(do_GetCurrentThread())
    1.87 +    {
    1.88 +        // Objects of this type must only be created on worker threads
    1.89 +        MOZ_ASSERT(!NS_IsMainThread()); 
    1.90 +    }
    1.91 +
    1.92 +    NS_IMETHOD Run() {
    1.93 +        // This event shuts down the worker thread and so must be main thread.
    1.94 +        MOZ_ASSERT(NS_IsMainThread());
    1.95 +
    1.96 +        // If we don't destroy the thread when we're done with it, it will hang
    1.97 +        // around forever... and that is bad!
    1.98 +        mWorkerThread->Shutdown();
    1.99 +        return NS_OK;
   1.100 +    }
   1.101 +
   1.102 +private:
   1.103 +    nsCOMPtr<nsIThread> mWorkerThread;
   1.104 +};
   1.105 +
   1.106 +/**
   1.107 + * A runnable to dispatch from the main thread when an async operation should
   1.108 + * be performed. 
   1.109 +*/
   1.110 +class AsyncLocalFileWinOperation : public nsRunnable
   1.111 +{
   1.112 +public:
   1.113 +    enum FileOp { RevealOp, LaunchOp };
   1.114 +
   1.115 +    AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::FileOp aOperation,
   1.116 +                               const nsAString &aResolvedPath) : 
   1.117 +        mOperation(aOperation),
   1.118 +        mResolvedPath(aResolvedPath)
   1.119 +    {
   1.120 +    }
   1.121 +
   1.122 +    NS_IMETHOD Run() {
   1.123 +        MOZ_ASSERT(!NS_IsMainThread(),
   1.124 +            "AsyncLocalFileWinOperation should not be run on the main thread!");
   1.125 +
   1.126 +        CoInitialize(nullptr);
   1.127 +        switch(mOperation) {
   1.128 +        case RevealOp: {
   1.129 +            Reveal();
   1.130 +        }
   1.131 +        break;
   1.132 +        case LaunchOp: {
   1.133 +            Launch();
   1.134 +        }
   1.135 +        break;
   1.136 +        }
   1.137 +        CoUninitialize();
   1.138 +
   1.139 +        // Send the result back to the main thread so that it can shutdown
   1.140 +        nsCOMPtr<nsIRunnable> resultrunnable = new AsyncLocalFileWinDone();
   1.141 +        NS_DispatchToMainThread(resultrunnable);
   1.142 +        return NS_OK;
   1.143 +    }
   1.144 +
   1.145 +private:
   1.146 +    // Reveals the path in explorer.
   1.147 +    nsresult Reveal() 
   1.148 +    {
   1.149 +        DWORD attributes = GetFileAttributesW(mResolvedPath.get());
   1.150 +        if (INVALID_FILE_ATTRIBUTES == attributes) {
   1.151 +            return NS_ERROR_FILE_INVALID_PATH;
   1.152 +        }
   1.153 +
   1.154 +        HRESULT hr;
   1.155 +        if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
   1.156 +            // We have a directory so we should open the directory itself.
   1.157 +            ITEMIDLIST *dir = ILCreateFromPathW(mResolvedPath.get());
   1.158 +            if (!dir) {
   1.159 +              return NS_ERROR_FAILURE;
   1.160 +            }
   1.161 +
   1.162 +            const ITEMIDLIST* selection[] = { dir };
   1.163 +            UINT count = ArrayLength(selection);
   1.164 +
   1.165 +            //Perform the open of the directory.
   1.166 +            hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
   1.167 +            CoTaskMemFree(dir);
   1.168 +        } else {
   1.169 +            int32_t len = mResolvedPath.Length();
   1.170 +            // We don't currently handle UNC long paths of the form \\?\ anywhere so
   1.171 +            // this should be fine.
   1.172 +            if (len > MAX_PATH) {
   1.173 +                return NS_ERROR_FILE_INVALID_PATH;
   1.174 +            }
   1.175 +            WCHAR parentDirectoryPath[MAX_PATH + 1] = { 0 };
   1.176 +            wcsncpy(parentDirectoryPath, mResolvedPath.get(), MAX_PATH);
   1.177 +            PathRemoveFileSpecW(parentDirectoryPath);
   1.178 +
   1.179 +            // We have a file so we should open the parent directory.
   1.180 +            ITEMIDLIST *dir = ILCreateFromPathW(parentDirectoryPath);
   1.181 +            if (!dir) {
   1.182 +                return NS_ERROR_FAILURE;
   1.183 +            }
   1.184 +
   1.185 +            // Set the item in the directory to select to the file we want to reveal.
   1.186 +            ITEMIDLIST *item = ILCreateFromPathW(mResolvedPath.get());
   1.187 +            if (!item) {
   1.188 +                CoTaskMemFree(dir);
   1.189 +                return NS_ERROR_FAILURE;
   1.190 +            }
   1.191 +            
   1.192 +            const ITEMIDLIST* selection[] = { item };
   1.193 +            UINT count = ArrayLength(selection);
   1.194 +
   1.195 +            //Perform the selection of the file.
   1.196 +            hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
   1.197 +
   1.198 +            CoTaskMemFree(dir);
   1.199 +            CoTaskMemFree(item);
   1.200 +        }
   1.201 +        
   1.202 +        return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
   1.203 +    }
   1.204 +    
   1.205 +    // Launches the default shell operation for the file path
   1.206 +    nsresult Launch()
   1.207 +    {
   1.208 +        // use the app registry name to launch a shell execute....
   1.209 +        SHELLEXECUTEINFOW seinfo;
   1.210 +        memset(&seinfo, 0, sizeof(seinfo));
   1.211 +        seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
   1.212 +        if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
   1.213 +          seinfo.fMask  = SEE_MASK_FLAG_LOG_USAGE;
   1.214 +        }
   1.215 +        seinfo.hwnd   = nullptr;
   1.216 +        seinfo.lpVerb = nullptr;
   1.217 +        seinfo.lpFile = mResolvedPath.get();
   1.218 +        seinfo.lpParameters =  nullptr;
   1.219 +        seinfo.lpDirectory  = nullptr;
   1.220 +        seinfo.nShow  = SW_SHOWNORMAL;
   1.221 +
   1.222 +        // Use the directory of the file we're launching as the working
   1.223 +        // directory.  That way if we have a self extracting EXE it won't
   1.224 +        // suggest to extract to the install directory.
   1.225 +        WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
   1.226 +        wcsncpy(workingDirectory,  mResolvedPath.get(), MAX_PATH);
   1.227 +        if (PathRemoveFileSpecW(workingDirectory)) {
   1.228 +            seinfo.lpDirectory = workingDirectory;
   1.229 +        } else {
   1.230 +            NS_WARNING("Could not set working directory for launched file.");
   1.231 +        }
   1.232 +        
   1.233 +        if (ShellExecuteExW(&seinfo)) {
   1.234 +            return NS_OK;
   1.235 +        }
   1.236 +        DWORD r = GetLastError();
   1.237 +        // if the file has no association, we launch windows' 
   1.238 +        // "what do you want to do" dialog
   1.239 +        if (r == SE_ERR_NOASSOC) {
   1.240 +            nsAutoString shellArg;
   1.241 +            shellArg.Assign(NS_LITERAL_STRING("shell32.dll,OpenAs_RunDLL ") + 
   1.242 +                            mResolvedPath);
   1.243 +            seinfo.lpFile = L"RUNDLL32.EXE";
   1.244 +            seinfo.lpParameters = shellArg.get();
   1.245 +            if (ShellExecuteExW(&seinfo))
   1.246 +                return NS_OK;
   1.247 +            r = GetLastError();
   1.248 +        }
   1.249 +        if (r < 32) {
   1.250 +            switch (r) {
   1.251 +              case 0:
   1.252 +              case SE_ERR_OOM:
   1.253 +                  return NS_ERROR_OUT_OF_MEMORY;
   1.254 +              case ERROR_FILE_NOT_FOUND:
   1.255 +                  return NS_ERROR_FILE_NOT_FOUND;
   1.256 +              case ERROR_PATH_NOT_FOUND:
   1.257 +                  return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   1.258 +              case ERROR_BAD_FORMAT:
   1.259 +                  return NS_ERROR_FILE_CORRUPTED;
   1.260 +              case SE_ERR_ACCESSDENIED:
   1.261 +                  return NS_ERROR_FILE_ACCESS_DENIED;
   1.262 +              case SE_ERR_ASSOCINCOMPLETE:
   1.263 +              case SE_ERR_NOASSOC:
   1.264 +                  return NS_ERROR_UNEXPECTED;
   1.265 +              case SE_ERR_DDEBUSY:
   1.266 +              case SE_ERR_DDEFAIL:
   1.267 +              case SE_ERR_DDETIMEOUT:
   1.268 +                  return NS_ERROR_NOT_AVAILABLE;
   1.269 +              case SE_ERR_DLLNOTFOUND:
   1.270 +                  return NS_ERROR_FAILURE;
   1.271 +              case SE_ERR_SHARE:
   1.272 +                  return NS_ERROR_FILE_IS_LOCKED;
   1.273 +              default:
   1.274 +                  return NS_ERROR_FILE_EXECUTION_FAILED;
   1.275 +            }
   1.276 +        }
   1.277 +        return NS_OK;
   1.278 +    }
   1.279 +
   1.280 +    // Stores the operation that will be performed on the thread
   1.281 +    AsyncLocalFileWinOperation::FileOp mOperation;
   1.282 +
   1.283 +    // Stores the path to perform the operation on
   1.284 +    nsString mResolvedPath;
   1.285 +};
   1.286 +
   1.287 +class nsDriveEnumerator : public nsISimpleEnumerator
   1.288 +{
   1.289 +public:
   1.290 +    nsDriveEnumerator();
   1.291 +    virtual ~nsDriveEnumerator();
   1.292 +    NS_DECL_ISUPPORTS
   1.293 +    NS_DECL_NSISIMPLEENUMERATOR
   1.294 +    nsresult Init();
   1.295 +private:
   1.296 +    /* mDrives stores the null-separated drive names.
   1.297 +     * Init sets them.
   1.298 +     * HasMoreElements checks mStartOfCurrentDrive.
   1.299 +     * GetNext advances mStartOfCurrentDrive.
   1.300 +     */
   1.301 +    nsString mDrives;
   1.302 +    nsAString::const_iterator mStartOfCurrentDrive;
   1.303 +    nsAString::const_iterator mEndOfDrivesString;
   1.304 +};
   1.305 +
   1.306 +//----------------------------------------------------------------------------
   1.307 +// short cut resolver
   1.308 +//----------------------------------------------------------------------------
   1.309 +class ShortcutResolver
   1.310 +{
   1.311 +public:
   1.312 +    ShortcutResolver();
   1.313 +    // nonvirtual since we're not subclassed
   1.314 +    ~ShortcutResolver();
   1.315 +
   1.316 +    nsresult Init();
   1.317 +    nsresult Resolve(const WCHAR* in, WCHAR* out);
   1.318 +    nsresult SetShortcut(bool updateExisting,
   1.319 +                         const WCHAR* shortcutPath,
   1.320 +                         const WCHAR* targetPath,
   1.321 +                         const WCHAR* workingDir,
   1.322 +                         const WCHAR* args,
   1.323 +                         const WCHAR* description,
   1.324 +                         const WCHAR* iconFile,
   1.325 +                         int32_t iconIndex);
   1.326 +
   1.327 +private:
   1.328 +    Mutex                  mLock;
   1.329 +    nsRefPtr<IPersistFile> mPersistFile;
   1.330 +    nsRefPtr<IShellLinkW>  mShellLink;
   1.331 +};
   1.332 +
   1.333 +ShortcutResolver::ShortcutResolver() :
   1.334 +    mLock("ShortcutResolver.mLock")
   1.335 +{
   1.336 +    CoInitialize(nullptr);
   1.337 +}
   1.338 +
   1.339 +ShortcutResolver::~ShortcutResolver()
   1.340 +{
   1.341 +    CoUninitialize();
   1.342 +}
   1.343 +
   1.344 +nsresult
   1.345 +ShortcutResolver::Init()
   1.346 +{
   1.347 +    // Get a pointer to the IPersistFile interface.
   1.348 +    if (FAILED(CoCreateInstance(CLSID_ShellLink,
   1.349 +                                nullptr,
   1.350 +                                CLSCTX_INPROC_SERVER,
   1.351 +                                IID_IShellLinkW,
   1.352 +                                getter_AddRefs(mShellLink))) ||
   1.353 +        FAILED(mShellLink->QueryInterface(IID_IPersistFile,
   1.354 +                                          getter_AddRefs(mPersistFile)))) {
   1.355 +        mShellLink = nullptr;
   1.356 +        return NS_ERROR_FAILURE;
   1.357 +    }
   1.358 +    return NS_OK;
   1.359 +}
   1.360 +
   1.361 +// |out| must be an allocated buffer of size MAX_PATH
   1.362 +nsresult
   1.363 +ShortcutResolver::Resolve(const WCHAR* in, WCHAR* out)
   1.364 +{
   1.365 +    if (!mShellLink)
   1.366 +        return NS_ERROR_FAILURE;
   1.367 +
   1.368 +    MutexAutoLock lock(mLock);
   1.369 +
   1.370 +    if (FAILED(mPersistFile->Load(in, STGM_READ)) ||
   1.371 +        FAILED(mShellLink->Resolve(nullptr, SLR_NO_UI)) ||
   1.372 +        FAILED(mShellLink->GetPath(out, MAX_PATH, nullptr, SLGP_UNCPRIORITY)))
   1.373 +        return NS_ERROR_FAILURE;
   1.374 +    return NS_OK;
   1.375 +}
   1.376 +
   1.377 +nsresult
   1.378 +ShortcutResolver::SetShortcut(bool updateExisting,
   1.379 +                              const WCHAR* shortcutPath,
   1.380 +                              const WCHAR* targetPath,
   1.381 +                              const WCHAR* workingDir,
   1.382 +                              const WCHAR* args,
   1.383 +                              const WCHAR* description,
   1.384 +                              const WCHAR* iconPath,
   1.385 +                              int32_t iconIndex)
   1.386 +{
   1.387 +    if (!mShellLink) {
   1.388 +      return NS_ERROR_FAILURE;
   1.389 +    }
   1.390 +
   1.391 +    if (!shortcutPath) {
   1.392 +      return NS_ERROR_FAILURE;
   1.393 +    }
   1.394 +
   1.395 +    MutexAutoLock lock(mLock);
   1.396 +
   1.397 +    if (updateExisting) {
   1.398 +      if (FAILED(mPersistFile->Load(shortcutPath, STGM_READWRITE))) {
   1.399 +        return NS_ERROR_FAILURE;
   1.400 +      }
   1.401 +    } else {
   1.402 +      if (!targetPath) {
   1.403 +        return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
   1.404 +      }
   1.405 +
   1.406 +      // Since we reuse our IPersistFile, we have to clear out any values that
   1.407 +      // may be left over from previous calls to SetShortcut.
   1.408 +      if (FAILED(mShellLink->SetWorkingDirectory(L""))
   1.409 +       || FAILED(mShellLink->SetArguments(L""))
   1.410 +       || FAILED(mShellLink->SetDescription(L""))
   1.411 +       || FAILED(mShellLink->SetIconLocation(L"", 0))) {
   1.412 +        return NS_ERROR_FAILURE;
   1.413 +      }
   1.414 +    }
   1.415 +
   1.416 +    if (targetPath && FAILED(mShellLink->SetPath(targetPath))) {
   1.417 +      return NS_ERROR_FAILURE;
   1.418 +    }
   1.419 +
   1.420 +    if (workingDir && FAILED(mShellLink->SetWorkingDirectory(workingDir))) {
   1.421 +      return NS_ERROR_FAILURE;
   1.422 +    }
   1.423 +
   1.424 +    if (args && FAILED(mShellLink->SetArguments(args))) {
   1.425 +      return NS_ERROR_FAILURE;
   1.426 +    }
   1.427 +
   1.428 +    if (description && FAILED(mShellLink->SetDescription(description))) {
   1.429 +      return NS_ERROR_FAILURE;
   1.430 +    }
   1.431 +
   1.432 +    if (iconPath && FAILED(mShellLink->SetIconLocation(iconPath, iconIndex))) {
   1.433 +      return NS_ERROR_FAILURE;
   1.434 +    }
   1.435 +
   1.436 +    if (FAILED(mPersistFile->Save(shortcutPath,
   1.437 +                                  TRUE))) {
   1.438 +      // Second argument indicates whether the file path specified in the
   1.439 +      // first argument should become the "current working file" for this
   1.440 +      // IPersistFile
   1.441 +      return NS_ERROR_FAILURE;
   1.442 +    }
   1.443 +
   1.444 +    return NS_OK;
   1.445 +}
   1.446 +
   1.447 +static ShortcutResolver * gResolver = nullptr;
   1.448 +
   1.449 +static nsresult NS_CreateShortcutResolver()
   1.450 +{
   1.451 +    gResolver = new ShortcutResolver();
   1.452 +    if (!gResolver)
   1.453 +        return NS_ERROR_OUT_OF_MEMORY;
   1.454 +
   1.455 +    return gResolver->Init();
   1.456 +}
   1.457 +
   1.458 +static void NS_DestroyShortcutResolver()
   1.459 +{
   1.460 +    delete gResolver;
   1.461 +    gResolver = nullptr;
   1.462 +}
   1.463 +
   1.464 +
   1.465 +//-----------------------------------------------------------------------------
   1.466 +// static helper functions
   1.467 +//-----------------------------------------------------------------------------
   1.468 +
   1.469 +// certainly not all the error that can be
   1.470 +// encountered, but many of them common ones
   1.471 +static nsresult ConvertWinError(DWORD winErr)
   1.472 +{
   1.473 +    nsresult rv;
   1.474 +
   1.475 +    switch (winErr)
   1.476 +    {
   1.477 +        case ERROR_FILE_NOT_FOUND:
   1.478 +        case ERROR_PATH_NOT_FOUND:
   1.479 +        case ERROR_INVALID_DRIVE:
   1.480 +            rv = NS_ERROR_FILE_NOT_FOUND;
   1.481 +            break;
   1.482 +        case ERROR_ACCESS_DENIED:
   1.483 +        case ERROR_NOT_SAME_DEVICE:
   1.484 +            rv = NS_ERROR_FILE_ACCESS_DENIED;
   1.485 +            break;
   1.486 +        case ERROR_SHARING_VIOLATION: // CreateFile without sharing flags
   1.487 +        case ERROR_LOCK_VIOLATION: // LockFile, LockFileEx
   1.488 +            rv = NS_ERROR_FILE_IS_LOCKED;
   1.489 +            break;
   1.490 +        case ERROR_NOT_ENOUGH_MEMORY:
   1.491 +        case ERROR_INVALID_BLOCK:
   1.492 +        case ERROR_INVALID_HANDLE:
   1.493 +        case ERROR_ARENA_TRASHED:
   1.494 +            rv = NS_ERROR_OUT_OF_MEMORY;
   1.495 +            break;
   1.496 +        case ERROR_CURRENT_DIRECTORY:
   1.497 +            rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
   1.498 +            break;
   1.499 +        case ERROR_WRITE_PROTECT:
   1.500 +            rv = NS_ERROR_FILE_READ_ONLY;
   1.501 +            break;
   1.502 +        case ERROR_HANDLE_DISK_FULL:
   1.503 +            rv = NS_ERROR_FILE_TOO_BIG;
   1.504 +            break;
   1.505 +        case ERROR_FILE_EXISTS:
   1.506 +        case ERROR_ALREADY_EXISTS:
   1.507 +        case ERROR_CANNOT_MAKE:
   1.508 +            rv = NS_ERROR_FILE_ALREADY_EXISTS;
   1.509 +            break;
   1.510 +        case ERROR_FILENAME_EXCED_RANGE:
   1.511 +            rv = NS_ERROR_FILE_NAME_TOO_LONG;
   1.512 +            break;
   1.513 +        case ERROR_DIRECTORY:
   1.514 +            rv = NS_ERROR_FILE_NOT_DIRECTORY;
   1.515 +            break;
   1.516 +        case 0:
   1.517 +            rv = NS_OK;
   1.518 +            break;
   1.519 +        default:
   1.520 +            rv = NS_ERROR_FAILURE;
   1.521 +            break;
   1.522 +    }
   1.523 +    return rv;
   1.524 +}
   1.525 +
   1.526 +// as suggested in the MSDN documentation on SetFilePointer
   1.527 +static __int64 
   1.528 +MyFileSeek64(HANDLE aHandle, __int64 aDistance, DWORD aMoveMethod)
   1.529 +{
   1.530 +    LARGE_INTEGER li;
   1.531 +
   1.532 +    li.QuadPart = aDistance;
   1.533 +    li.LowPart = SetFilePointer(aHandle, li.LowPart, &li.HighPart, aMoveMethod);
   1.534 +    if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
   1.535 +    {
   1.536 +        li.QuadPart = -1;
   1.537 +    }
   1.538 +
   1.539 +    return li.QuadPart;
   1.540 +}
   1.541 +
   1.542 +static bool
   1.543 +IsShortcutPath(const nsAString &path)
   1.544 +{
   1.545 +    // Under Windows, the shortcuts are just files with a ".lnk" extension. 
   1.546 +    // Note also that we don't resolve links in the middle of paths.
   1.547 +    // i.e. "c:\foo.lnk\bar.txt" is invalid.
   1.548 +    NS_ABORT_IF_FALSE(!path.IsEmpty(), "don't pass an empty string");
   1.549 +    int32_t len = path.Length();
   1.550 +    return len >= 4 && (StringTail(path, 4).LowerCaseEqualsASCII(".lnk"));
   1.551 +}
   1.552 +
   1.553 +//-----------------------------------------------------------------------------
   1.554 +// We need the following three definitions to make |OpenFile| convert a file 
   1.555 +// handle to an NSPR file descriptor correctly when |O_APPEND| flag is
   1.556 +// specified. It is defined in a private header of NSPR (primpl.h) we can't
   1.557 +// include. As a temporary workaround until we decide how to extend
   1.558 +// |PR_ImportFile|, we define it here. Currently, |_PR_HAVE_PEEK_BUFFER|
   1.559 +// and |PR_STRICT_ADDR_LEN| are not defined for the 'w95'-dependent portion
   1.560 +// of NSPR so that fields of |PRFilePrivate| #ifdef'd by them are not copied.
   1.561 +// Similarly, |_MDFileDesc| is taken from nsprpub/pr/include/md/_win95.h.
   1.562 +// In an unlikely case we switch to 'NT'-dependent NSPR AND this temporary 
   1.563 +// workaround last beyond the switch, |PRFilePrivate| and |_MDFileDesc| 
   1.564 +// need to be changed to match the definitions for WinNT.
   1.565 +//-----------------------------------------------------------------------------
   1.566 +typedef enum {
   1.567 +    _PR_TRI_TRUE = 1,
   1.568 +    _PR_TRI_FALSE = 0,
   1.569 +    _PR_TRI_UNKNOWN = -1
   1.570 +} _PRTriStateBool;
   1.571 +
   1.572 +struct _MDFileDesc {
   1.573 +    PROsfd osfd;
   1.574 +};
   1.575 +
   1.576 +struct PRFilePrivate {
   1.577 +    int32_t state;
   1.578 +    bool nonblocking;
   1.579 +    _PRTriStateBool inheritable;
   1.580 +    PRFileDesc *next;
   1.581 +    int lockCount;      /*   0: not locked
   1.582 +                         *  -1: a native lockfile call is in progress
   1.583 +                         * > 0: # times the file is locked */
   1.584 +    bool    appendMode; 
   1.585 +    _MDFileDesc md;
   1.586 +};
   1.587 +
   1.588 +//-----------------------------------------------------------------------------
   1.589 +// Six static methods defined below (OpenFile,  FileTimeToPRTime, GetFileInfo,
   1.590 +// OpenDir, CloseDir, ReadDir) should go away once the corresponding 
   1.591 +// UTF-16 APIs are implemented on all the supported platforms (or at least 
   1.592 +// Windows 9x/ME) in NSPR. Currently, they're only implemented on 
   1.593 +// Windows NT4 or later. (bug 330665)
   1.594 +//-----------------------------------------------------------------------------
   1.595 +
   1.596 +// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} : 
   1.597 +// PR_Open and _PR_MD_OPEN
   1.598 +nsresult
   1.599 +OpenFile(const nsAFlatString &name, int osflags, int mode,
   1.600 +         PRFileDesc **fd)
   1.601 +{
   1.602 +    int32_t access = 0;
   1.603 +
   1.604 +    int32_t disposition = 0;
   1.605 +    int32_t attributes = 0;
   1.606 +
   1.607 +    if (osflags & PR_SYNC) 
   1.608 +        attributes = FILE_FLAG_WRITE_THROUGH;
   1.609 +    if (osflags & PR_RDONLY || osflags & PR_RDWR)
   1.610 +        access |= GENERIC_READ;
   1.611 +    if (osflags & PR_WRONLY || osflags & PR_RDWR)
   1.612 +        access |= GENERIC_WRITE;
   1.613 +
   1.614 +    if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
   1.615 +        disposition = CREATE_NEW;
   1.616 +    else if (osflags & PR_CREATE_FILE) {
   1.617 +        if (osflags & PR_TRUNCATE)
   1.618 +            disposition = CREATE_ALWAYS;
   1.619 +        else
   1.620 +            disposition = OPEN_ALWAYS;
   1.621 +    } else {
   1.622 +        if (osflags & PR_TRUNCATE)
   1.623 +            disposition = TRUNCATE_EXISTING;
   1.624 +        else
   1.625 +            disposition = OPEN_EXISTING;
   1.626 +    }
   1.627 +
   1.628 +    if (osflags & nsIFile::DELETE_ON_CLOSE) {
   1.629 +        attributes |= FILE_FLAG_DELETE_ON_CLOSE;
   1.630 +    }
   1.631 +
   1.632 +    if (osflags & nsIFile::OS_READAHEAD) {
   1.633 +        attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
   1.634 +    }
   1.635 +
   1.636 +    // If no write permissions are requested, and if we are possibly creating
   1.637 +    // the file, then set the new file as read only.
   1.638 +    // The flag has no effect if we happen to open the file.
   1.639 +    if (!(mode & (PR_IWUSR | PR_IWGRP | PR_IWOTH)) &&
   1.640 +        disposition != OPEN_EXISTING) {
   1.641 +        attributes |= FILE_ATTRIBUTE_READONLY;
   1.642 +    }
   1.643 +
   1.644 +    HANDLE file = ::CreateFileW(name.get(), access,
   1.645 +                                FILE_SHARE_READ|FILE_SHARE_WRITE,
   1.646 +                                nullptr, disposition, attributes, nullptr);
   1.647 +
   1.648 +    if (file == INVALID_HANDLE_VALUE) { 
   1.649 +        *fd = nullptr;
   1.650 +        return ConvertWinError(GetLastError());
   1.651 +    }
   1.652 +
   1.653 +    *fd = PR_ImportFile((PROsfd) file); 
   1.654 +    if (*fd) {
   1.655 +        // On Windows, _PR_HAVE_O_APPEND is not defined so that we have to
   1.656 +        // add it manually. (see |PR_Open| in nsprpub/pr/src/io/prfile.c)
   1.657 +        (*fd)->secret->appendMode = (PR_APPEND & osflags) ? true : false;
   1.658 +        return NS_OK;
   1.659 +    }
   1.660 +
   1.661 +    nsresult rv = NS_ErrorAccordingToNSPR();
   1.662 +
   1.663 +    CloseHandle(file);
   1.664 +
   1.665 +    return rv;
   1.666 +}
   1.667 +
   1.668 +// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
   1.669 +// PR_FileTimeToPRTime and _PR_FileTimeToPRTime
   1.670 +static
   1.671 +void FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
   1.672 +{
   1.673 +#ifdef __GNUC__
   1.674 +    const PRTime _pr_filetime_offset = 116444736000000000LL;
   1.675 +#else
   1.676 +    const PRTime _pr_filetime_offset = 116444736000000000i64;
   1.677 +#endif
   1.678 +
   1.679 +    PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
   1.680 +    ::CopyMemory(prtm, filetime, sizeof(PRTime));
   1.681 +#ifdef __GNUC__
   1.682 +    *prtm = (*prtm - _pr_filetime_offset) / 10LL;
   1.683 +#else
   1.684 +    *prtm = (*prtm - _pr_filetime_offset) / 10i64;
   1.685 +#endif
   1.686 +}
   1.687 +
   1.688 +// copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} with some
   1.689 +// changes : PR_GetFileInfo64, _PR_MD_GETFILEINFO64
   1.690 +static nsresult
   1.691 +GetFileInfo(const nsAFlatString &name, PRFileInfo64 *info)
   1.692 +{
   1.693 +    WIN32_FILE_ATTRIBUTE_DATA fileData;
   1.694 +
   1.695 +    if (name.IsEmpty() || name.FindCharInSet(MOZ_UTF16("?*")) != kNotFound)
   1.696 +        return NS_ERROR_INVALID_ARG;
   1.697 +
   1.698 +    if (!::GetFileAttributesExW(name.get(), GetFileExInfoStandard, &fileData))
   1.699 +        return ConvertWinError(GetLastError());
   1.700 +
   1.701 +    if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
   1.702 +        info->type = PR_FILE_DIRECTORY;
   1.703 +    } else {
   1.704 +        info->type = PR_FILE_FILE;
   1.705 +    }
   1.706 +
   1.707 +    info->size = fileData.nFileSizeHigh;
   1.708 +    info->size = (info->size << 32) + fileData.nFileSizeLow;
   1.709 +
   1.710 +    FileTimeToPRTime(&fileData.ftLastWriteTime, &info->modifyTime);
   1.711 +
   1.712 +    if (0 == fileData.ftCreationTime.dwLowDateTime &&
   1.713 +            0 == fileData.ftCreationTime.dwHighDateTime) {
   1.714 +        info->creationTime = info->modifyTime;
   1.715 +    } else {
   1.716 +        FileTimeToPRTime(&fileData.ftCreationTime, &info->creationTime);
   1.717 +    }
   1.718 +
   1.719 +    return NS_OK;
   1.720 +}
   1.721 +
   1.722 +struct nsDir
   1.723 +{
   1.724 +    HANDLE   handle; 
   1.725 +    WIN32_FIND_DATAW data;
   1.726 +    bool     firstEntry;
   1.727 +};
   1.728 +
   1.729 +static nsresult
   1.730 +OpenDir(const nsAFlatString &name, nsDir * *dir)
   1.731 +{
   1.732 +    if (NS_WARN_IF(!dir))
   1.733 +        return NS_ERROR_INVALID_ARG;
   1.734 +
   1.735 +    *dir = nullptr;
   1.736 +    if (name.Length() + 3 >= MAX_PATH)
   1.737 +        return NS_ERROR_FILE_NAME_TOO_LONG;
   1.738 +
   1.739 +    nsDir *d  = PR_NEW(nsDir);
   1.740 +    if (!d)
   1.741 +        return NS_ERROR_OUT_OF_MEMORY;
   1.742 +
   1.743 +    nsAutoString filename(name);
   1.744 +
   1.745 +     //If 'name' ends in a slash or backslash, do not append
   1.746 +     //another backslash.
   1.747 +    if (filename.Last() == L'/' || filename.Last() == L'\\')
   1.748 +        filename.Append('*');
   1.749 +    else 
   1.750 +        filename.AppendLiteral("\\*");
   1.751 +
   1.752 +    filename.ReplaceChar(L'/', L'\\');
   1.753 +
   1.754 +    // FindFirstFileW Will have a last error of ERROR_DIRECTORY if
   1.755 +    // <file_path>\* is passed in.  If <unknown_path>\* is passed in then
   1.756 +    // ERROR_PATH_NOT_FOUND will be the last error.
   1.757 +    d->handle = ::FindFirstFileW(filename.get(), &(d->data) );
   1.758 +
   1.759 +    if (d->handle == INVALID_HANDLE_VALUE) {
   1.760 +        PR_Free(d);
   1.761 +        return ConvertWinError(GetLastError());
   1.762 +    }
   1.763 +    d->firstEntry = true;
   1.764 +
   1.765 +    *dir = d;
   1.766 +    return NS_OK;
   1.767 +}
   1.768 +
   1.769 +static nsresult
   1.770 +ReadDir(nsDir *dir, PRDirFlags flags, nsString& name)
   1.771 +{
   1.772 +    name.Truncate();
   1.773 +    if (NS_WARN_IF(!dir))
   1.774 +        return NS_ERROR_INVALID_ARG;
   1.775 +
   1.776 +    while (1) {
   1.777 +        BOOL rv;
   1.778 +        if (dir->firstEntry)
   1.779 +        {
   1.780 +            dir->firstEntry = false;
   1.781 +            rv = 1;
   1.782 +        } else
   1.783 +            rv = ::FindNextFileW(dir->handle, &(dir->data));
   1.784 +
   1.785 +        if (rv == 0)
   1.786 +            break;
   1.787 +
   1.788 +        const wchar_t *fileName;
   1.789 +        nsString tmp;
   1.790 +        fileName = (dir)->data.cFileName;
   1.791 +
   1.792 +        if ((flags & PR_SKIP_DOT) &&
   1.793 +            (fileName[0] == L'.') && (fileName[1] == L'\0'))
   1.794 +            continue;
   1.795 +        if ((flags & PR_SKIP_DOT_DOT) &&
   1.796 +            (fileName[0] == L'.') && (fileName[1] == L'.') &&
   1.797 +            (fileName[2] == L'\0'))
   1.798 +            continue;
   1.799 +
   1.800 +        DWORD attrib =  dir->data.dwFileAttributes;
   1.801 +        if ((flags & PR_SKIP_HIDDEN) && (attrib & FILE_ATTRIBUTE_HIDDEN))
   1.802 +            continue;
   1.803 +
   1.804 +        if (fileName == tmp.get())
   1.805 +            name = tmp;
   1.806 +        else 
   1.807 +            name = fileName;
   1.808 +        return NS_OK;
   1.809 +    }
   1.810 +
   1.811 +    DWORD err = GetLastError();
   1.812 +    return err == ERROR_NO_MORE_FILES ? NS_OK : ConvertWinError(err);
   1.813 +}
   1.814 +
   1.815 +static nsresult
   1.816 +CloseDir(nsDir *&d)
   1.817 +{
   1.818 +    if (NS_WARN_IF(!d))
   1.819 +        return NS_ERROR_INVALID_ARG;
   1.820 +
   1.821 +    BOOL isOk = FindClose(d->handle);
   1.822 +    // PR_DELETE also nulls out the passed in pointer.
   1.823 +    PR_DELETE(d);
   1.824 +    return isOk ? NS_OK : ConvertWinError(GetLastError());
   1.825 +}
   1.826 +
   1.827 +//-----------------------------------------------------------------------------
   1.828 +// nsDirEnumerator
   1.829 +//-----------------------------------------------------------------------------
   1.830 +
   1.831 +class nsDirEnumerator MOZ_FINAL : public nsISimpleEnumerator,
   1.832 +                                  public nsIDirectoryEnumerator
   1.833 +{
   1.834 +    public:
   1.835 +
   1.836 +        NS_DECL_ISUPPORTS
   1.837 +
   1.838 +        nsDirEnumerator() : mDir(nullptr)
   1.839 +        {
   1.840 +        }
   1.841 +
   1.842 +        nsresult Init(nsIFile* parent)
   1.843 +        {
   1.844 +            nsAutoString filepath;
   1.845 +            parent->GetTarget(filepath);
   1.846 +
   1.847 +            if (filepath.IsEmpty())
   1.848 +            {
   1.849 +                parent->GetPath(filepath);
   1.850 +            }
   1.851 +
   1.852 +            if (filepath.IsEmpty())
   1.853 +            {
   1.854 +                return NS_ERROR_UNEXPECTED;
   1.855 +            }
   1.856 +
   1.857 +            // IsDirectory is not needed here because OpenDir will return
   1.858 +            // NS_ERROR_FILE_NOT_DIRECTORY if the passed in path is a file.
   1.859 +            nsresult rv = OpenDir(filepath, &mDir);
   1.860 +            if (NS_FAILED(rv))
   1.861 +                return rv;
   1.862 +
   1.863 +            mParent = parent;
   1.864 +            return NS_OK;
   1.865 +        }
   1.866 +
   1.867 +        NS_IMETHOD HasMoreElements(bool *result)
   1.868 +        {
   1.869 +            nsresult rv;
   1.870 +            if (mNext == nullptr && mDir)
   1.871 +            {
   1.872 +                nsString name;
   1.873 +                rv = ReadDir(mDir, PR_SKIP_BOTH, name);
   1.874 +                if (NS_FAILED(rv))
   1.875 +                    return rv;
   1.876 +                if (name.IsEmpty()) 
   1.877 +                {
   1.878 +                    // end of dir entries
   1.879 +                    if (NS_FAILED(CloseDir(mDir)))
   1.880 +                        return NS_ERROR_FAILURE;
   1.881 +
   1.882 +                    *result = false;
   1.883 +                    return NS_OK;
   1.884 +                }
   1.885 +
   1.886 +                nsCOMPtr<nsIFile> file;
   1.887 +                rv = mParent->Clone(getter_AddRefs(file));
   1.888 +                if (NS_FAILED(rv))
   1.889 +                    return rv;
   1.890 +
   1.891 +                rv = file->Append(name);
   1.892 +                if (NS_FAILED(rv))
   1.893 +                    return rv;
   1.894 +
   1.895 +                mNext = do_QueryInterface(file);
   1.896 +            }
   1.897 +            *result = mNext != nullptr;
   1.898 +            if (!*result) 
   1.899 +                Close();
   1.900 +            return NS_OK;
   1.901 +        }
   1.902 +
   1.903 +        NS_IMETHOD GetNext(nsISupports **result)
   1.904 +        {
   1.905 +            nsresult rv;
   1.906 +            bool hasMore;
   1.907 +            rv = HasMoreElements(&hasMore);
   1.908 +            if (NS_FAILED(rv)) return rv;
   1.909 +
   1.910 +            *result = mNext;        // might return nullptr
   1.911 +            NS_IF_ADDREF(*result);
   1.912 +
   1.913 +            mNext = nullptr;
   1.914 +            return NS_OK;
   1.915 +        }
   1.916 +
   1.917 +        NS_IMETHOD GetNextFile(nsIFile **result)
   1.918 +        {
   1.919 +            *result = nullptr;
   1.920 +            bool hasMore = false;
   1.921 +            nsresult rv = HasMoreElements(&hasMore);
   1.922 +            if (NS_FAILED(rv) || !hasMore)
   1.923 +                return rv;
   1.924 +            *result = mNext;
   1.925 +            NS_IF_ADDREF(*result);
   1.926 +            mNext = nullptr;
   1.927 +            return NS_OK;
   1.928 +        }
   1.929 +
   1.930 +        NS_IMETHOD Close()
   1.931 +        {
   1.932 +            if (mDir)
   1.933 +            {
   1.934 +                nsresult rv = CloseDir(mDir);
   1.935 +                NS_ASSERTION(NS_SUCCEEDED(rv), "close failed");
   1.936 +                if (NS_FAILED(rv))
   1.937 +                    return NS_ERROR_FAILURE;
   1.938 +            }
   1.939 +            return NS_OK;
   1.940 +        }
   1.941 +
   1.942 +        // dtor can be non-virtual since there are no subclasses, but must be
   1.943 +        // public to use the class on the stack.
   1.944 +        ~nsDirEnumerator()
   1.945 +        {
   1.946 +            Close();
   1.947 +        }
   1.948 +
   1.949 +    protected:
   1.950 +        nsDir*             mDir;
   1.951 +        nsCOMPtr<nsIFile>  mParent;
   1.952 +        nsCOMPtr<nsIFile>  mNext;
   1.953 +};
   1.954 +
   1.955 +NS_IMPL_ISUPPORTS(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
   1.956 +
   1.957 +
   1.958 +//-----------------------------------------------------------------------------
   1.959 +// nsLocalFile <public>
   1.960 +//-----------------------------------------------------------------------------
   1.961 +
   1.962 +nsLocalFile::nsLocalFile()
   1.963 +  : mDirty(true)
   1.964 +  , mResolveDirty(true)
   1.965 +  , mFollowSymlinks(false)
   1.966 +{
   1.967 +}
   1.968 +
   1.969 +nsresult
   1.970 +nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
   1.971 +{
   1.972 +    if (NS_WARN_IF(!aInstancePtr))
   1.973 +        return NS_ERROR_INVALID_ARG;
   1.974 +    if (NS_WARN_IF(outer))
   1.975 +        return NS_ERROR_NO_AGGREGATION;
   1.976 +
   1.977 +    nsLocalFile* inst = new nsLocalFile();
   1.978 +    if (inst == nullptr)
   1.979 +        return NS_ERROR_OUT_OF_MEMORY;
   1.980 +
   1.981 +    nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
   1.982 +    if (NS_FAILED(rv))
   1.983 +    {
   1.984 +        delete inst;
   1.985 +        return rv;
   1.986 +    }
   1.987 +    return NS_OK;
   1.988 +}
   1.989 +
   1.990 +
   1.991 +//-----------------------------------------------------------------------------
   1.992 +// nsLocalFile::nsISupports
   1.993 +//-----------------------------------------------------------------------------
   1.994 +
   1.995 +NS_IMPL_ISUPPORTS(nsLocalFile,
   1.996 +                  nsILocalFile,
   1.997 +                  nsIFile,
   1.998 +                  nsILocalFileWin,
   1.999 +                  nsIHashable)
  1.1000 +
  1.1001 +
  1.1002 +//-----------------------------------------------------------------------------
  1.1003 +// nsLocalFile <private>
  1.1004 +//-----------------------------------------------------------------------------
  1.1005 +
  1.1006 +nsLocalFile::nsLocalFile(const nsLocalFile& other)
  1.1007 +  : mDirty(true)
  1.1008 +  , mResolveDirty(true)
  1.1009 +  , mFollowSymlinks(other.mFollowSymlinks)
  1.1010 +  , mWorkingPath(other.mWorkingPath)
  1.1011 +{
  1.1012 +}
  1.1013 +
  1.1014 +// Resolve the shortcut file from mWorkingPath and write the path 
  1.1015 +// it points to into mResolvedPath.
  1.1016 +nsresult
  1.1017 +nsLocalFile::ResolveShortcut()
  1.1018 +{
  1.1019 +    // we can't do anything without the resolver
  1.1020 +    if (!gResolver)
  1.1021 +        return NS_ERROR_FAILURE;
  1.1022 +
  1.1023 +    mResolvedPath.SetLength(MAX_PATH);
  1.1024 +    if (mResolvedPath.Length() != MAX_PATH)
  1.1025 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1026 +
  1.1027 +    wchar_t *resolvedPath = wwc(mResolvedPath.BeginWriting());
  1.1028 +
  1.1029 +    // resolve this shortcut
  1.1030 +    nsresult rv = gResolver->Resolve(mWorkingPath.get(), resolvedPath);
  1.1031 +
  1.1032 +    size_t len = NS_FAILED(rv) ? 0 : wcslen(resolvedPath);
  1.1033 +    mResolvedPath.SetLength(len);
  1.1034 +
  1.1035 +    return rv;
  1.1036 +}
  1.1037 +
  1.1038 +// Resolve any shortcuts and stat the resolved path. After a successful return
  1.1039 +// the path is guaranteed valid and the members of mFileInfo64 can be used.
  1.1040 +nsresult
  1.1041 +nsLocalFile::ResolveAndStat()
  1.1042 +{
  1.1043 +    // if we aren't dirty then we are already done
  1.1044 +    if (!mDirty)
  1.1045 +        return NS_OK;
  1.1046 +
  1.1047 +    // we can't resolve/stat anything that isn't a valid NSPR addressable path
  1.1048 +    if (mWorkingPath.IsEmpty())
  1.1049 +        return NS_ERROR_FILE_INVALID_PATH;
  1.1050 +
  1.1051 +    // this is usually correct
  1.1052 +    mResolvedPath.Assign(mWorkingPath);
  1.1053 +
  1.1054 +    // slutty hack designed to work around bug 134796 until it is fixed
  1.1055 +    nsAutoString nsprPath(mWorkingPath.get());
  1.1056 +    if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == L':') 
  1.1057 +        nsprPath.Append('\\');
  1.1058 +
  1.1059 +    // first we will see if the working path exists. If it doesn't then
  1.1060 +    // there is nothing more that can be done
  1.1061 +    nsresult rv = GetFileInfo(nsprPath, &mFileInfo64);
  1.1062 +    if (NS_FAILED(rv))
  1.1063 +        return rv;
  1.1064 +
  1.1065 +    // if this isn't a shortcut file or we aren't following symlinks then we're done 
  1.1066 +    if (!mFollowSymlinks 
  1.1067 +        || mFileInfo64.type != PR_FILE_FILE 
  1.1068 +        || !IsShortcutPath(mWorkingPath))
  1.1069 +    {
  1.1070 +        mDirty = false;
  1.1071 +        mResolveDirty = false;
  1.1072 +        return NS_OK;
  1.1073 +    }
  1.1074 +
  1.1075 +    // we need to resolve this shortcut to what it points to, this will
  1.1076 +    // set mResolvedPath. Even if it fails we need to have the resolved
  1.1077 +    // path equal to working path for those functions that always use
  1.1078 +    // the resolved path.
  1.1079 +    rv = ResolveShortcut();
  1.1080 +    if (NS_FAILED(rv))
  1.1081 +    {
  1.1082 +        mResolvedPath.Assign(mWorkingPath);
  1.1083 +        return rv;
  1.1084 +    }
  1.1085 +    mResolveDirty = false;
  1.1086 +
  1.1087 +    // get the details of the resolved path
  1.1088 +    rv = GetFileInfo(mResolvedPath, &mFileInfo64);
  1.1089 +    if (NS_FAILED(rv))
  1.1090 +        return rv;
  1.1091 +
  1.1092 +    mDirty = false;
  1.1093 +    return NS_OK;
  1.1094 +}
  1.1095 +
  1.1096 +/**
  1.1097 + * Fills the mResolvedPath member variable with the file or symlink target
  1.1098 + * if follow symlinks is on.  This is a copy of the Resolve parts from
  1.1099 + * ResolveAndStat. ResolveAndStat is much slower though because of the stat.
  1.1100 + *
  1.1101 + * @return NS_OK on success.
  1.1102 +*/
  1.1103 +nsresult
  1.1104 +nsLocalFile::Resolve()
  1.1105 +{
  1.1106 +  // if we aren't dirty then we are already done
  1.1107 +  if (!mResolveDirty) {
  1.1108 +    return NS_OK;
  1.1109 +  }
  1.1110 +
  1.1111 +  // we can't resolve/stat anything that isn't a valid NSPR addressable path
  1.1112 +  if (mWorkingPath.IsEmpty()) {
  1.1113 +    return NS_ERROR_FILE_INVALID_PATH;
  1.1114 +  }
  1.1115 +  
  1.1116 +  // this is usually correct
  1.1117 +  mResolvedPath.Assign(mWorkingPath);
  1.1118 +
  1.1119 +  // if this isn't a shortcut file or we aren't following symlinks then
  1.1120 +  // we're done.
  1.1121 +  if (!mFollowSymlinks || 
  1.1122 +      !IsShortcutPath(mWorkingPath)) {
  1.1123 +    mResolveDirty = false;
  1.1124 +    return NS_OK;
  1.1125 +  }
  1.1126 +
  1.1127 +  // we need to resolve this shortcut to what it points to, this will
  1.1128 +  // set mResolvedPath. Even if it fails we need to have the resolved
  1.1129 +  // path equal to working path for those functions that always use
  1.1130 +  // the resolved path.
  1.1131 +  nsresult rv = ResolveShortcut();
  1.1132 +  if (NS_FAILED(rv)) {
  1.1133 +    mResolvedPath.Assign(mWorkingPath);
  1.1134 +    return rv;
  1.1135 +  }
  1.1136 +
  1.1137 +  mResolveDirty = false;
  1.1138 +  return NS_OK;
  1.1139 +}
  1.1140 +
  1.1141 +//-----------------------------------------------------------------------------
  1.1142 +// nsLocalFile::nsIFile,nsILocalFile
  1.1143 +//-----------------------------------------------------------------------------
  1.1144 +
  1.1145 +NS_IMETHODIMP
  1.1146 +nsLocalFile::Clone(nsIFile **file)
  1.1147 +{
  1.1148 +    // Just copy-construct ourselves
  1.1149 +    *file = new nsLocalFile(*this);
  1.1150 +    if (!*file)
  1.1151 +      return NS_ERROR_OUT_OF_MEMORY;
  1.1152 +
  1.1153 +    NS_ADDREF(*file);
  1.1154 +    
  1.1155 +    return NS_OK;
  1.1156 +}
  1.1157 +
  1.1158 +NS_IMETHODIMP
  1.1159 +nsLocalFile::InitWithFile(nsIFile *aFile)
  1.1160 +{
  1.1161 +    if (NS_WARN_IF(!aFile))
  1.1162 +        return NS_ERROR_INVALID_ARG;
  1.1163 +    
  1.1164 +    nsAutoString path;
  1.1165 +    aFile->GetPath(path);
  1.1166 +    if (path.IsEmpty())
  1.1167 +        return NS_ERROR_INVALID_ARG;
  1.1168 +    return InitWithPath(path); 
  1.1169 +}
  1.1170 +
  1.1171 +NS_IMETHODIMP
  1.1172 +nsLocalFile::InitWithPath(const nsAString &filePath)
  1.1173 +{
  1.1174 +    MakeDirty();
  1.1175 +
  1.1176 +    nsAString::const_iterator begin, end;
  1.1177 +    filePath.BeginReading(begin);
  1.1178 +    filePath.EndReading(end);
  1.1179 +
  1.1180 +    // input string must not be empty
  1.1181 +    if (begin == end)
  1.1182 +        return NS_ERROR_FAILURE;
  1.1183 +
  1.1184 +    char16_t firstChar = *begin;
  1.1185 +    char16_t secondChar = *(++begin);
  1.1186 +
  1.1187 +    // just do a sanity check.  if it has any forward slashes, it is not a Native path
  1.1188 +    // on windows.  Also, it must have a colon at after the first char.
  1.1189 +    if (FindCharInReadable(L'/', begin, end))
  1.1190 +        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.1191 +
  1.1192 +    if (secondChar != L':' && (secondChar != L'\\' || firstChar != L'\\'))
  1.1193 +        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.1194 +
  1.1195 +    if (secondChar == L':') {
  1.1196 +        // Make sure we have a valid drive, later code assumes the drive letter
  1.1197 +        // is a single char a-z or A-Z.
  1.1198 +        if (PathGetDriveNumberW(filePath.Data()) == -1) {
  1.1199 +            return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.1200 +        }
  1.1201 +    }
  1.1202 +
  1.1203 +    mWorkingPath = filePath;
  1.1204 +    // kill any trailing '\'
  1.1205 +    if (mWorkingPath.Last() == L'\\')
  1.1206 +        mWorkingPath.Truncate(mWorkingPath.Length() - 1);
  1.1207 +
  1.1208 +    return NS_OK;
  1.1209 +
  1.1210 +}
  1.1211 +
  1.1212 +NS_IMETHODIMP
  1.1213 +nsLocalFile::OpenNSPRFileDesc(int32_t flags, int32_t mode, PRFileDesc **_retval)
  1.1214 +{
  1.1215 +    nsresult rv = Resolve();
  1.1216 +    if (NS_FAILED(rv))
  1.1217 +        return rv;
  1.1218 +
  1.1219 +    return OpenFile(mResolvedPath, flags, mode, _retval);
  1.1220 +}
  1.1221 +
  1.1222 +
  1.1223 +NS_IMETHODIMP
  1.1224 +nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
  1.1225 +{
  1.1226 +    nsresult rv = ResolveAndStat();
  1.1227 +    if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
  1.1228 +        return rv;
  1.1229 +
  1.1230 +    *_retval = _wfopen(mResolvedPath.get(), NS_ConvertASCIItoUTF16(mode).get());
  1.1231 +    if (*_retval)
  1.1232 +        return NS_OK;
  1.1233 +
  1.1234 +    return NS_ERROR_FAILURE;
  1.1235 +}
  1.1236 +
  1.1237 +
  1.1238 +
  1.1239 +NS_IMETHODIMP
  1.1240 +nsLocalFile::Create(uint32_t type, uint32_t attributes)
  1.1241 +{
  1.1242 +    if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
  1.1243 +        return NS_ERROR_FILE_UNKNOWN_TYPE;
  1.1244 +
  1.1245 +    nsresult rv = ResolveAndStat();
  1.1246 +    if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
  1.1247 +        return rv;
  1.1248 +
  1.1249 +    // create directories to target
  1.1250 +    //
  1.1251 +    // A given local file can be either one of these forms:
  1.1252 +    //
  1.1253 +    //   - normal:    X:\some\path\on\this\drive
  1.1254 +    //                       ^--- start here
  1.1255 +    //
  1.1256 +    //   - UNC path:  \\machine\volume\some\path\on\this\drive
  1.1257 +    //                                     ^--- start here
  1.1258 +    //
  1.1259 +    // Skip the first 'X:\' for the first form, and skip the first full
  1.1260 +    // '\\machine\volume\' segment for the second form.
  1.1261 +
  1.1262 +    wchar_t* path = wwc(mResolvedPath.BeginWriting());
  1.1263 +
  1.1264 +    if (path[0] == L'\\' && path[1] == L'\\')
  1.1265 +    {
  1.1266 +        // dealing with a UNC path here; skip past '\\machine\'
  1.1267 +        path = wcschr(path + 2, L'\\');
  1.1268 +        if (!path)
  1.1269 +            return NS_ERROR_FILE_INVALID_PATH;
  1.1270 +        ++path;
  1.1271 +    }
  1.1272 +
  1.1273 +    // search for first slash after the drive (or volume) name
  1.1274 +    wchar_t* slash = wcschr(path, L'\\');
  1.1275 +
  1.1276 +    nsresult directoryCreateError = NS_OK;
  1.1277 +    if (slash)
  1.1278 +    {
  1.1279 +        // skip the first '\\'
  1.1280 +        ++slash;
  1.1281 +        slash = wcschr(slash, L'\\');
  1.1282 +
  1.1283 +        while (slash)
  1.1284 +        {
  1.1285 +            *slash = L'\0';
  1.1286 +
  1.1287 +            if (!::CreateDirectoryW(mResolvedPath.get(), nullptr)) {
  1.1288 +                rv = ConvertWinError(GetLastError());
  1.1289 +                if (NS_ERROR_FILE_NOT_FOUND == rv &&
  1.1290 +                    NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
  1.1291 +                    // If a previous CreateDirectory failed due to access, return that.
  1.1292 +                    return NS_ERROR_FILE_ACCESS_DENIED;
  1.1293 +                }
  1.1294 +                // perhaps the base path already exists, or perhaps we don't have
  1.1295 +                // permissions to create the directory.  NOTE: access denied could
  1.1296 +                // occur on a parent directory even though it exists.
  1.1297 +                else if (NS_ERROR_FILE_ALREADY_EXISTS != rv &&
  1.1298 +                         NS_ERROR_FILE_ACCESS_DENIED != rv) {
  1.1299 +                    return rv;
  1.1300 +                }
  1.1301 +
  1.1302 +                directoryCreateError = rv;
  1.1303 +            }
  1.1304 +            *slash = L'\\';
  1.1305 +            ++slash;
  1.1306 +            slash = wcschr(slash, L'\\');
  1.1307 +        }
  1.1308 +    }
  1.1309 +
  1.1310 +    if (type == NORMAL_FILE_TYPE)
  1.1311 +    {
  1.1312 +        PRFileDesc* file;
  1.1313 +        rv = OpenFile(mResolvedPath,
  1.1314 +                      PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes,
  1.1315 +                      &file);
  1.1316 +        if (file)
  1.1317 +            PR_Close(file);
  1.1318 +
  1.1319 +        if (rv == NS_ERROR_FILE_ACCESS_DENIED)
  1.1320 +        {
  1.1321 +            // need to return already-exists for directories (bug 452217)
  1.1322 +            bool isdir;
  1.1323 +            if (NS_SUCCEEDED(IsDirectory(&isdir)) && isdir)
  1.1324 +                rv = NS_ERROR_FILE_ALREADY_EXISTS;
  1.1325 +        } else if (NS_ERROR_FILE_NOT_FOUND == rv && 
  1.1326 +                   NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
  1.1327 +            // If a previous CreateDirectory failed due to access, return that.
  1.1328 +            return NS_ERROR_FILE_ACCESS_DENIED;
  1.1329 +        }
  1.1330 +        return rv;
  1.1331 +    }
  1.1332 +
  1.1333 +    if (type == DIRECTORY_TYPE)
  1.1334 +    {
  1.1335 +        if (!::CreateDirectoryW(mResolvedPath.get(), nullptr)) {
  1.1336 +          rv = ConvertWinError(GetLastError());
  1.1337 +          if (NS_ERROR_FILE_NOT_FOUND == rv && 
  1.1338 +              NS_ERROR_FILE_ACCESS_DENIED == directoryCreateError) {
  1.1339 +              // If a previous CreateDirectory failed due to access, return that.
  1.1340 +              return NS_ERROR_FILE_ACCESS_DENIED;
  1.1341 +          } else {
  1.1342 +              return rv;
  1.1343 +          }
  1.1344 +        }
  1.1345 +        else
  1.1346 +            return NS_OK;
  1.1347 +    }
  1.1348 +
  1.1349 +    return NS_ERROR_FILE_UNKNOWN_TYPE;
  1.1350 +}
  1.1351 +
  1.1352 +
  1.1353 +NS_IMETHODIMP
  1.1354 +nsLocalFile::Append(const nsAString &node)
  1.1355 +{
  1.1356 +    // append this path, multiple components are not permitted
  1.1357 +    return AppendInternal(PromiseFlatString(node), false);
  1.1358 +}
  1.1359 +
  1.1360 +NS_IMETHODIMP
  1.1361 +nsLocalFile::AppendRelativePath(const nsAString &node)
  1.1362 +{
  1.1363 +    // append this path, multiple components are permitted
  1.1364 +    return AppendInternal(PromiseFlatString(node), true);
  1.1365 +}
  1.1366 +
  1.1367 +
  1.1368 +nsresult
  1.1369 +nsLocalFile::AppendInternal(const nsAFlatString &node, bool multipleComponents)
  1.1370 +{
  1.1371 +    if (node.IsEmpty())
  1.1372 +        return NS_OK;
  1.1373 +
  1.1374 +    // check the relative path for validity
  1.1375 +    if (node.First() == L'\\'                                   // can't start with an '\'
  1.1376 +        || node.FindChar(L'/') != kNotFound                     // can't contain /
  1.1377 +        || node.EqualsASCII(".."))                              // can't be ..
  1.1378 +        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.1379 +
  1.1380 +    if (multipleComponents)
  1.1381 +    {
  1.1382 +        // can't contain .. as a path component. Ensure that the valid components
  1.1383 +        // "foo..foo", "..foo", and "foo.." are not falsely detected,
  1.1384 +        // but the invalid paths "..\", "foo\..", "foo\..\foo", 
  1.1385 +        // "..\foo", etc are.
  1.1386 +        NS_NAMED_LITERAL_STRING(doubleDot, "\\.."); 
  1.1387 +        nsAString::const_iterator start, end, offset;
  1.1388 +        node.BeginReading(start);
  1.1389 +        node.EndReading(end);
  1.1390 +        offset = end; 
  1.1391 +        while (FindInReadable(doubleDot, start, offset))
  1.1392 +        {
  1.1393 +            if (offset == end || *offset == L'\\')
  1.1394 +                return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.1395 +            start = offset;
  1.1396 +            offset = end;
  1.1397 +        }
  1.1398 +        
  1.1399 +        // catches the remaining cases of prefixes 
  1.1400 +        if (StringBeginsWith(node, NS_LITERAL_STRING("..\\")))
  1.1401 +            return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.1402 +    }
  1.1403 +    // single components can't contain '\'
  1.1404 +    else if (node.FindChar(L'\\') != kNotFound)
  1.1405 +        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.1406 +
  1.1407 +    MakeDirty();
  1.1408 +    
  1.1409 +    mWorkingPath.Append(NS_LITERAL_STRING("\\") + node);
  1.1410 +    
  1.1411 +    return NS_OK;
  1.1412 +}
  1.1413 +
  1.1414 +#define TOUPPER(u) (((u) >= L'a' && (u) <= L'z') ? \
  1.1415 +                    (u) - (L'a' - L'A') : (u))
  1.1416 +
  1.1417 +NS_IMETHODIMP
  1.1418 +nsLocalFile::Normalize()
  1.1419 +{
  1.1420 +    // XXX See bug 187957 comment 18 for possible problems with this implementation.
  1.1421 +    
  1.1422 +    if (mWorkingPath.IsEmpty())
  1.1423 +        return NS_OK;
  1.1424 +
  1.1425 +    nsAutoString path(mWorkingPath);
  1.1426 +
  1.1427 +    // find the index of the root backslash for the path. Everything before 
  1.1428 +    // this is considered fully normalized and cannot be ascended beyond 
  1.1429 +    // using ".."  For a local drive this is the first slash (e.g. "c:\").
  1.1430 +    // For a UNC path it is the slash following the share name 
  1.1431 +    // (e.g. "\\server\share\").
  1.1432 +    int32_t rootIdx = 2;        // default to local drive
  1.1433 +    if (path.First() == L'\\')   // if a share then calculate the rootIdx
  1.1434 +    {
  1.1435 +        rootIdx = path.FindChar(L'\\', 2);   // skip \\ in front of the server
  1.1436 +        if (rootIdx == kNotFound)
  1.1437 +            return NS_OK;                   // already normalized
  1.1438 +        rootIdx = path.FindChar(L'\\', rootIdx+1);
  1.1439 +        if (rootIdx == kNotFound)
  1.1440 +            return NS_OK;                   // already normalized
  1.1441 +    }
  1.1442 +    else if (path.CharAt(rootIdx) != L'\\')
  1.1443 +    {
  1.1444 +        // The path has been specified relative to the current working directory 
  1.1445 +        // for that drive. To normalize it, the current working directory for 
  1.1446 +        // that drive needs to be inserted before the supplied relative path
  1.1447 +        // which will provide an absolute path (and the rootIdx will still be 2).
  1.1448 +        WCHAR cwd[MAX_PATH];
  1.1449 +        WCHAR * pcwd = cwd;
  1.1450 +        int drive = TOUPPER(path.First()) - 'A' + 1;
  1.1451 +        /* We need to worry about IPH, for details read bug 419326.
  1.1452 +         * _getdrives - http://msdn2.microsoft.com/en-us/library/xdhk0xd2.aspx 
  1.1453 +         * uses a bitmask, bit 0 is 'a:'
  1.1454 +         * _chdrive - http://msdn2.microsoft.com/en-us/library/0d1409hb.aspx
  1.1455 +         * _getdcwd - http://msdn2.microsoft.com/en-us/library/7t2zk3s4.aspx
  1.1456 +         * take an int, 1 is 'a:'.
  1.1457 +         *
  1.1458 +         * Because of this, we need to do some math. Subtract 1 to convert from
  1.1459 +         * _chdrive/_getdcwd format to _getdrives drive numbering.
  1.1460 +         * Shift left x bits to convert from integer indexing to bitfield indexing.
  1.1461 +         * And of course, we need to find out if the drive is in the bitmask.
  1.1462 +         *
  1.1463 +         * If we're really unlucky, we can still lose, but only if the user
  1.1464 +         * manages to eject the drive between our call to _getdrives() and
  1.1465 +         * our *calls* to _wgetdcwd.
  1.1466 +         */
  1.1467 +        if (!((1 << (drive - 1)) & _getdrives()))
  1.1468 +            return NS_ERROR_FILE_INVALID_PATH;
  1.1469 +        if (!_wgetdcwd(drive, pcwd, MAX_PATH))
  1.1470 +            pcwd = _wgetdcwd(drive, 0, 0);
  1.1471 +        if (!pcwd)
  1.1472 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1473 +        nsAutoString currentDir(pcwd);
  1.1474 +        if (pcwd != cwd)
  1.1475 +            free(pcwd);
  1.1476 +
  1.1477 +        if (currentDir.Last() == '\\')
  1.1478 +            path.Replace(0, 2, currentDir);
  1.1479 +        else
  1.1480 +            path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\"));
  1.1481 +    }
  1.1482 +    NS_POSTCONDITION(0 < rootIdx && rootIdx < (int32_t)path.Length(), "rootIdx is invalid");
  1.1483 +    NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid");
  1.1484 +
  1.1485 +    // if there is nothing following the root path then it is already normalized
  1.1486 +    if (rootIdx + 1 == (int32_t)path.Length())
  1.1487 +        return NS_OK;
  1.1488 +
  1.1489 +    // assign the root
  1.1490 +    const char16_t * pathBuffer = path.get();  // simplify access to the buffer
  1.1491 +    mWorkingPath.SetCapacity(path.Length()); // it won't ever grow longer
  1.1492 +    mWorkingPath.Assign(pathBuffer, rootIdx);
  1.1493 +
  1.1494 +    // Normalize the path components. The actions taken are:
  1.1495 +    //
  1.1496 +    //  "\\"    condense to single backslash
  1.1497 +    //  "."     remove from path
  1.1498 +    //  ".."    up a directory
  1.1499 +    //  "..."   remove from path (any number of dots > 2)
  1.1500 +    //
  1.1501 +    // The last form is something that Windows 95 and 98 supported and 
  1.1502 +    // is a shortcut for changing up multiple directories. Windows XP
  1.1503 +    // and ilk ignore it in a path, as is done here.
  1.1504 +    int32_t len, begin, end = rootIdx;
  1.1505 +    while (end < (int32_t)path.Length())
  1.1506 +    {
  1.1507 +        // find the current segment (text between the backslashes) to 
  1.1508 +        // be examined, this will set the following variables:
  1.1509 +        //  begin == index of first char in segment
  1.1510 +        //  end   == index 1 char after last char in segment
  1.1511 +        //  len   == length of segment 
  1.1512 +        begin = end + 1;
  1.1513 +        end = path.FindChar('\\', begin);
  1.1514 +        if (end == kNotFound)
  1.1515 +            end = path.Length();
  1.1516 +        len = end - begin;
  1.1517 +        
  1.1518 +        // ignore double backslashes
  1.1519 +        if (len == 0)
  1.1520 +            continue;
  1.1521 +        
  1.1522 +        // len != 0, and interesting paths always begin with a dot
  1.1523 +        if (pathBuffer[begin] == '.')
  1.1524 +        {
  1.1525 +            // ignore single dots
  1.1526 +            if (len == 1)
  1.1527 +                continue;   
  1.1528 +
  1.1529 +            // handle multiple dots
  1.1530 +            if (len >= 2 && pathBuffer[begin+1] == L'.')
  1.1531 +            {
  1.1532 +                // back up a path component on double dot
  1.1533 +                if (len == 2)
  1.1534 +                {
  1.1535 +                    int32_t prev = mWorkingPath.RFindChar('\\');
  1.1536 +                    if (prev >= rootIdx)
  1.1537 +                        mWorkingPath.Truncate(prev);
  1.1538 +                    continue;
  1.1539 +                }
  1.1540 +
  1.1541 +                // length is > 2 and the first two characters are dots. 
  1.1542 +                // if the rest of the string is dots, then ignore it.
  1.1543 +                int idx = len - 1;
  1.1544 +                for (; idx >= 2; --idx) 
  1.1545 +                {
  1.1546 +                    if (pathBuffer[begin+idx] != L'.')
  1.1547 +                        break;
  1.1548 +                }
  1.1549 +
  1.1550 +                // this is true if the loop above didn't break
  1.1551 +                // and all characters in this segment are dots.
  1.1552 +                if (idx < 2) 
  1.1553 +                    continue;
  1.1554 +            }
  1.1555 +        }
  1.1556 +
  1.1557 +        // add the current component to the path, including the preceding backslash
  1.1558 +        mWorkingPath.Append(pathBuffer + begin - 1, len + 1);
  1.1559 +    }
  1.1560 +
  1.1561 +    // kill trailing dots and spaces.
  1.1562 +    int32_t filePathLen = mWorkingPath.Length() - 1;
  1.1563 +    while(filePathLen > 0 && (mWorkingPath[filePathLen] == L' ' ||
  1.1564 +          mWorkingPath[filePathLen] == L'.'))
  1.1565 +    {
  1.1566 +        mWorkingPath.Truncate(filePathLen--);
  1.1567 +    } 
  1.1568 +
  1.1569 +    MakeDirty();
  1.1570 +    return NS_OK;
  1.1571 +}
  1.1572 +
  1.1573 +NS_IMETHODIMP
  1.1574 +nsLocalFile::GetLeafName(nsAString &aLeafName)
  1.1575 +{
  1.1576 +    aLeafName.Truncate();
  1.1577 +
  1.1578 +    if (mWorkingPath.IsEmpty())
  1.1579 +        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.1580 +
  1.1581 +    int32_t offset = mWorkingPath.RFindChar(L'\\');
  1.1582 +
  1.1583 +    // if the working path is just a node without any lashes.
  1.1584 +    if (offset == kNotFound)
  1.1585 +        aLeafName = mWorkingPath;
  1.1586 +    else
  1.1587 +        aLeafName = Substring(mWorkingPath, offset + 1);
  1.1588 +
  1.1589 +    return NS_OK;
  1.1590 +}
  1.1591 +
  1.1592 +NS_IMETHODIMP
  1.1593 +nsLocalFile::SetLeafName(const nsAString &aLeafName)
  1.1594 +{
  1.1595 +    MakeDirty();
  1.1596 +
  1.1597 +    if (mWorkingPath.IsEmpty())
  1.1598 +        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.1599 +
  1.1600 +    // cannot use nsCString::RFindChar() due to 0x5c problem
  1.1601 +    int32_t offset = mWorkingPath.RFindChar(L'\\');
  1.1602 +    if (offset)
  1.1603 +    {
  1.1604 +        mWorkingPath.Truncate(offset+1);
  1.1605 +    }
  1.1606 +    mWorkingPath.Append(aLeafName);
  1.1607 +
  1.1608 +    return NS_OK;
  1.1609 +}
  1.1610 +
  1.1611 +
  1.1612 +NS_IMETHODIMP
  1.1613 +nsLocalFile::GetPath(nsAString &_retval)
  1.1614 +{
  1.1615 +    _retval = mWorkingPath;
  1.1616 +    return NS_OK;
  1.1617 +}
  1.1618 +
  1.1619 +NS_IMETHODIMP
  1.1620 +nsLocalFile::GetCanonicalPath(nsAString &aResult)
  1.1621 +{
  1.1622 +    EnsureShortPath();
  1.1623 +    aResult.Assign(mShortWorkingPath);
  1.1624 +    return NS_OK;
  1.1625 +}
  1.1626 +
  1.1627 +typedef struct {
  1.1628 +    WORD wLanguage;
  1.1629 +    WORD wCodePage;
  1.1630 +} LANGANDCODEPAGE;
  1.1631 +
  1.1632 +NS_IMETHODIMP
  1.1633 +nsLocalFile::GetVersionInfoField(const char* aField, nsAString& _retval)
  1.1634 +{
  1.1635 +    nsresult rv = ResolveAndStat();
  1.1636 +    if (NS_FAILED(rv))
  1.1637 +        return rv;
  1.1638 +
  1.1639 +    rv = NS_ERROR_FAILURE;
  1.1640 +
  1.1641 +    const WCHAR *path = mFollowSymlinks ? mResolvedPath.get() : mWorkingPath.get();
  1.1642 +
  1.1643 +    DWORD dummy;
  1.1644 +    DWORD size = ::GetFileVersionInfoSizeW(path, &dummy);
  1.1645 +    if (!size)
  1.1646 +        return rv;
  1.1647 +
  1.1648 +    void* ver = calloc(size, 1);
  1.1649 +    if (!ver)
  1.1650 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1651 +
  1.1652 +    if (::GetFileVersionInfoW(path, 0, size, ver)) 
  1.1653 +    {
  1.1654 +        LANGANDCODEPAGE* translate = nullptr;
  1.1655 +        UINT pageCount;
  1.1656 +        BOOL queryResult = ::VerQueryValueW(ver, L"\\VarFileInfo\\Translation", 
  1.1657 +                                            (void**)&translate, &pageCount);
  1.1658 +        if (queryResult && translate) 
  1.1659 +        {
  1.1660 +            for (int32_t i = 0; i < 2; ++i) 
  1.1661 +            { 
  1.1662 +                wchar_t subBlock[MAX_PATH];
  1.1663 +                _snwprintf(subBlock, MAX_PATH,
  1.1664 +                           L"\\StringFileInfo\\%04x%04x\\%s", 
  1.1665 +                           (i == 0 ? translate[0].wLanguage 
  1.1666 +                                   : ::GetUserDefaultLangID()),
  1.1667 +                           translate[0].wCodePage,
  1.1668 +                           NS_ConvertASCIItoUTF16(
  1.1669 +                               nsDependentCString(aField)).get());
  1.1670 +                subBlock[MAX_PATH - 1] = 0;
  1.1671 +                LPVOID value = nullptr;
  1.1672 +                UINT size;
  1.1673 +                queryResult = ::VerQueryValueW(ver, subBlock, &value, &size);
  1.1674 +                if (queryResult && value)
  1.1675 +                {
  1.1676 +                    _retval.Assign(static_cast<char16_t*>(value));
  1.1677 +                    if (!_retval.IsEmpty()) 
  1.1678 +                    {
  1.1679 +                        rv = NS_OK;
  1.1680 +                        break;
  1.1681 +                    }
  1.1682 +                }
  1.1683 +            }
  1.1684 +        }
  1.1685 +    }
  1.1686 +    free(ver);
  1.1687 +    
  1.1688 +    return rv;
  1.1689 +}
  1.1690 +
  1.1691 +NS_IMETHODIMP
  1.1692 +nsLocalFile::SetShortcut(nsIFile* targetFile,
  1.1693 +                         nsIFile* workingDir,
  1.1694 +                         const char16_t* args,
  1.1695 +                         const char16_t* description,
  1.1696 +                         nsIFile* iconFile,
  1.1697 +                         int32_t iconIndex)
  1.1698 +{
  1.1699 +    bool exists;
  1.1700 +    nsresult rv = this->Exists(&exists);
  1.1701 +    if (NS_FAILED(rv)) {
  1.1702 +      return rv;
  1.1703 +    }
  1.1704 +
  1.1705 +    const WCHAR* targetFilePath = nullptr;
  1.1706 +    const WCHAR* workingDirPath = nullptr;
  1.1707 +    const WCHAR* iconFilePath = nullptr;
  1.1708 +
  1.1709 +    nsAutoString targetFilePathAuto;
  1.1710 +    if (targetFile) {
  1.1711 +        rv = targetFile->GetPath(targetFilePathAuto);
  1.1712 +        if (NS_FAILED(rv)) {
  1.1713 +          return rv;
  1.1714 +        }
  1.1715 +        targetFilePath = targetFilePathAuto.get();
  1.1716 +    }
  1.1717 +
  1.1718 +    nsAutoString workingDirPathAuto;
  1.1719 +    if (workingDir) {
  1.1720 +        rv = workingDir->GetPath(workingDirPathAuto);
  1.1721 +        if (NS_FAILED(rv)) {
  1.1722 +          return rv;
  1.1723 +        }
  1.1724 +        workingDirPath = workingDirPathAuto.get();
  1.1725 +    }
  1.1726 +
  1.1727 +    nsAutoString iconPathAuto;
  1.1728 +    if (iconFile) {
  1.1729 +        rv = iconFile->GetPath(iconPathAuto);
  1.1730 +        if (NS_FAILED(rv)) {
  1.1731 +          return rv;
  1.1732 +        }
  1.1733 +        iconFilePath = iconPathAuto.get();
  1.1734 +    }
  1.1735 +
  1.1736 +    rv = gResolver->SetShortcut(exists,
  1.1737 +                                mWorkingPath.get(),
  1.1738 +                                targetFilePath,
  1.1739 +                                workingDirPath,
  1.1740 +                                char16ptr_t(args),
  1.1741 +                                char16ptr_t(description),
  1.1742 +                                iconFilePath,
  1.1743 +                                iconFilePath? iconIndex : 0);
  1.1744 +    if (targetFilePath && NS_SUCCEEDED(rv)) {
  1.1745 +      MakeDirty();
  1.1746 +    }
  1.1747 +
  1.1748 +    return rv;
  1.1749 +}
  1.1750 +
  1.1751 +/** 
  1.1752 + * Determines if the drive type for the specified file is rmeote or local.
  1.1753 + * 
  1.1754 + * @param path   The path of the file to check
  1.1755 + * @param remote Out parameter, on function success holds true if the specified
  1.1756 + *               file path is remote, or false if the file path is local.
  1.1757 + * @return true  on success. The return value implies absolutely nothing about
  1.1758 + *               wether the file is local or remote.
  1.1759 +*/
  1.1760 +static bool
  1.1761 +IsRemoteFilePath(LPCWSTR path, bool &remote)
  1.1762 +{
  1.1763 +  // Obtain the parent directory path and make sure it ends with
  1.1764 +  // a trailing backslash.
  1.1765 +  WCHAR dirPath[MAX_PATH + 1] = { 0 };
  1.1766 +  wcsncpy(dirPath, path, MAX_PATH);
  1.1767 +  if (!PathRemoveFileSpecW(dirPath)) {
  1.1768 +    return false;
  1.1769 +  }
  1.1770 +  size_t len = wcslen(dirPath);
  1.1771 +  // In case the dirPath holds exaclty MAX_PATH and remains unchanged, we
  1.1772 +  // recheck the required length here since we need to terminate it with
  1.1773 +  // a backslash.
  1.1774 +  if (len >= MAX_PATH) {
  1.1775 +    return false;
  1.1776 +  }
  1.1777 +
  1.1778 +  dirPath[len] = L'\\';
  1.1779 +  dirPath[len + 1] = L'\0';
  1.1780 +  UINT driveType = GetDriveTypeW(dirPath);
  1.1781 +  remote = driveType == DRIVE_REMOTE;
  1.1782 +  return true;
  1.1783 +}
  1.1784 +
  1.1785 +nsresult
  1.1786 +nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent,
  1.1787 +                            const nsAString &newName, uint32_t options)
  1.1788 +{
  1.1789 +    nsresult rv = NS_OK;
  1.1790 +    nsAutoString filePath;
  1.1791 +
  1.1792 +    bool move = options & (Move | Rename);
  1.1793 +
  1.1794 +    // get the path that we are going to copy to.
  1.1795 +    // Since windows does not know how to auto
  1.1796 +    // resolve shortcuts, we must work with the
  1.1797 +    // target.
  1.1798 +    nsAutoString destPath;
  1.1799 +    destParent->GetTarget(destPath);
  1.1800 +
  1.1801 +    destPath.Append('\\');
  1.1802 +
  1.1803 +    if (newName.IsEmpty())
  1.1804 +    {
  1.1805 +        nsAutoString aFileName;
  1.1806 +        sourceFile->GetLeafName(aFileName);
  1.1807 +        destPath.Append(aFileName);
  1.1808 +    }
  1.1809 +    else
  1.1810 +    {
  1.1811 +        destPath.Append(newName);
  1.1812 +    }
  1.1813 +
  1.1814 +
  1.1815 +    if (options & FollowSymlinks)
  1.1816 +    {
  1.1817 +        rv = sourceFile->GetTarget(filePath);
  1.1818 +        if (filePath.IsEmpty())
  1.1819 +            rv = sourceFile->GetPath(filePath);
  1.1820 +    }
  1.1821 +    else
  1.1822 +    {
  1.1823 +        rv = sourceFile->GetPath(filePath);
  1.1824 +    }
  1.1825 +
  1.1826 +    if (NS_FAILED(rv))
  1.1827 +        return rv;
  1.1828 +
  1.1829 +    // Pass the flag COPY_FILE_NO_BUFFERING to CopyFileEx as we may be copying
  1.1830 +    // to a SMBV2 remote drive. Without this parameter subsequent append mode
  1.1831 +    // file writes can cause the resultant file to become corrupt. We only need to do 
  1.1832 +    // this if the major version of Windows is > 5(Only Windows Vista and above 
  1.1833 +    // can support SMBV2).  With a 7200RPM hard drive:
  1.1834 +    // Copying a 1KB file with COPY_FILE_NO_BUFFERING takes about 30-60ms.
  1.1835 +    // Copying a 1KB file without COPY_FILE_NO_BUFFERING takes < 1ms.
  1.1836 +    // So we only use COPY_FILE_NO_BUFFERING when we have a remote drive.
  1.1837 +    int copyOK;
  1.1838 +    DWORD dwCopyFlags = COPY_FILE_ALLOW_DECRYPTED_DESTINATION;
  1.1839 +    if (IsVistaOrLater()) {
  1.1840 +        bool path1Remote, path2Remote;
  1.1841 +        if (!IsRemoteFilePath(filePath.get(), path1Remote) || 
  1.1842 +            !IsRemoteFilePath(destPath.get(), path2Remote) ||
  1.1843 +            path1Remote || path2Remote) {
  1.1844 +            dwCopyFlags |= COPY_FILE_NO_BUFFERING;
  1.1845 +        }
  1.1846 +    }
  1.1847 +    
  1.1848 +    if (!move)
  1.1849 +    {
  1.1850 +        copyOK = ::CopyFileExW(filePath.get(), destPath.get(), nullptr,
  1.1851 +                               nullptr, nullptr, dwCopyFlags);
  1.1852 +    }
  1.1853 +    else
  1.1854 +    {
  1.1855 +        copyOK = ::MoveFileExW(filePath.get(), destPath.get(), MOVEFILE_REPLACE_EXISTING);
  1.1856 +
  1.1857 +        // Check if copying the source file to a different volume,
  1.1858 +        // as this could be an SMBV2 mapped drive.
  1.1859 +        if (!copyOK && GetLastError() == ERROR_NOT_SAME_DEVICE)
  1.1860 +        {
  1.1861 +            if (options & Rename) {
  1.1862 +                return NS_ERROR_FILE_ACCESS_DENIED;
  1.1863 +            }
  1.1864 +            copyOK = CopyFileExW(filePath.get(), destPath.get(), nullptr,
  1.1865 +                                 nullptr, nullptr, dwCopyFlags);
  1.1866 +
  1.1867 +            if (copyOK)
  1.1868 +                DeleteFileW(filePath.get());
  1.1869 +        }
  1.1870 +    }
  1.1871 +
  1.1872 +    if (!copyOK)  // CopyFileEx and MoveFileEx return zero at failure.
  1.1873 +        rv = ConvertWinError(GetLastError());
  1.1874 +    else if (move && !(options & SkipNtfsAclReset))
  1.1875 +    {
  1.1876 +        // Set security permissions to inherit from parent.
  1.1877 +        // Note: propagates to all children: slow for big file trees
  1.1878 +        PACL pOldDACL = nullptr;
  1.1879 +        PSECURITY_DESCRIPTOR pSD = nullptr;
  1.1880 +        ::GetNamedSecurityInfoW((LPWSTR)destPath.get(), SE_FILE_OBJECT,
  1.1881 +                                DACL_SECURITY_INFORMATION,
  1.1882 +                                nullptr, nullptr, &pOldDACL, nullptr, &pSD);
  1.1883 +        if (pOldDACL)
  1.1884 +            ::SetNamedSecurityInfoW((LPWSTR)destPath.get(), SE_FILE_OBJECT,
  1.1885 +                                    DACL_SECURITY_INFORMATION |
  1.1886 +                                    UNPROTECTED_DACL_SECURITY_INFORMATION,
  1.1887 +                                    nullptr, nullptr, pOldDACL, nullptr);
  1.1888 +        if (pSD)
  1.1889 +            LocalFree((HLOCAL)pSD);
  1.1890 +    }
  1.1891 +
  1.1892 +    return rv;
  1.1893 +}
  1.1894 +
  1.1895 +nsresult
  1.1896 +nsLocalFile::CopyMove(nsIFile *aParentDir, const nsAString &newName, uint32_t options)
  1.1897 +{
  1.1898 +    bool move = options & (Move | Rename);
  1.1899 +    bool followSymlinks = options & FollowSymlinks;
  1.1900 +
  1.1901 +    nsCOMPtr<nsIFile> newParentDir = aParentDir;
  1.1902 +    // check to see if this exists, otherwise return an error.
  1.1903 +    // we will check this by resolving.  If the user wants us
  1.1904 +    // to follow links, then we are talking about the target,
  1.1905 +    // hence we can use the |FollowSymlinks| option.
  1.1906 +    nsresult rv  = ResolveAndStat();
  1.1907 +    if (NS_FAILED(rv))
  1.1908 +        return rv;
  1.1909 +
  1.1910 +    if (!newParentDir)
  1.1911 +    {
  1.1912 +        // no parent was specified.  We must rename.
  1.1913 +        if (newName.IsEmpty())
  1.1914 +            return NS_ERROR_INVALID_ARG;
  1.1915 +
  1.1916 +        rv = GetParent(getter_AddRefs(newParentDir));
  1.1917 +        if (NS_FAILED(rv))
  1.1918 +            return rv;
  1.1919 +    }
  1.1920 +
  1.1921 +    if (!newParentDir)
  1.1922 +        return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  1.1923 +
  1.1924 +    // make sure it exists and is a directory.  Create it if not there.
  1.1925 +    bool exists;
  1.1926 +    newParentDir->Exists(&exists);
  1.1927 +    if (!exists)
  1.1928 +    {
  1.1929 +        rv = newParentDir->Create(DIRECTORY_TYPE, 0644);  // TODO, what permissions should we use
  1.1930 +        if (NS_FAILED(rv))
  1.1931 +            return rv;
  1.1932 +    }
  1.1933 +    else
  1.1934 +    {
  1.1935 +        bool isDir;
  1.1936 +        newParentDir->IsDirectory(&isDir);
  1.1937 +        if (!isDir)
  1.1938 +        {
  1.1939 +            if (followSymlinks)
  1.1940 +            {
  1.1941 +                bool isLink;
  1.1942 +                newParentDir->IsSymlink(&isLink);
  1.1943 +                if (isLink)
  1.1944 +                {
  1.1945 +                    nsAutoString target;
  1.1946 +                    newParentDir->GetTarget(target);
  1.1947 +
  1.1948 +                    nsCOMPtr<nsIFile> realDest = new nsLocalFile();
  1.1949 +                    if (realDest == nullptr)
  1.1950 +                        return NS_ERROR_OUT_OF_MEMORY;
  1.1951 +
  1.1952 +                    rv = realDest->InitWithPath(target);
  1.1953 +
  1.1954 +                    if (NS_FAILED(rv))
  1.1955 +                        return rv;
  1.1956 +
  1.1957 +                    return CopyMove(realDest, newName, options);
  1.1958 +                }
  1.1959 +            }
  1.1960 +            else
  1.1961 +            {
  1.1962 +                return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  1.1963 +            }
  1.1964 +        }
  1.1965 +    }
  1.1966 +
  1.1967 +    // Try different ways to move/copy files/directories
  1.1968 +    bool done = false;
  1.1969 +    bool isDir;
  1.1970 +    IsDirectory(&isDir);
  1.1971 +    bool isSymlink;
  1.1972 +    IsSymlink(&isSymlink);
  1.1973 +
  1.1974 +    // Try to move the file or directory, or try to copy a single file (or non-followed symlink)
  1.1975 +    if (move || !isDir || (isSymlink && !followSymlinks))
  1.1976 +    {
  1.1977 +        // Copy/Move single file, or move a directory
  1.1978 +        if (!aParentDir) {
  1.1979 +            options |= SkipNtfsAclReset;
  1.1980 +        }
  1.1981 +        rv = CopySingleFile(this, newParentDir, newName, options);
  1.1982 +        done = NS_SUCCEEDED(rv);
  1.1983 +        // If we are moving a directory and that fails, fallback on directory
  1.1984 +        // enumeration.  See bug 231300 for details.
  1.1985 +        if (!done && !(move && isDir))
  1.1986 +            return rv;  
  1.1987 +    }
  1.1988 +    
  1.1989 +    // Not able to copy or move directly, so enumerate it
  1.1990 +    if (!done)
  1.1991 +    {
  1.1992 +        // create a new target destination in the new parentDir;
  1.1993 +        nsCOMPtr<nsIFile> target;
  1.1994 +        rv = newParentDir->Clone(getter_AddRefs(target));
  1.1995 +
  1.1996 +        if (NS_FAILED(rv))
  1.1997 +            return rv;
  1.1998 +
  1.1999 +        nsAutoString allocatedNewName;
  1.2000 +        if (newName.IsEmpty())
  1.2001 +        {
  1.2002 +            bool isLink;
  1.2003 +            IsSymlink(&isLink);
  1.2004 +            if (isLink)
  1.2005 +            {
  1.2006 +                nsAutoString temp;
  1.2007 +                GetTarget(temp);
  1.2008 +                int32_t offset = temp.RFindChar(L'\\'); 
  1.2009 +                if (offset == kNotFound)
  1.2010 +                    allocatedNewName = temp;
  1.2011 +                else 
  1.2012 +                    allocatedNewName = Substring(temp, offset + 1);
  1.2013 +            }
  1.2014 +            else
  1.2015 +            {
  1.2016 +                GetLeafName(allocatedNewName);// this should be the leaf name of the
  1.2017 +            }
  1.2018 +        }
  1.2019 +        else
  1.2020 +        {
  1.2021 +            allocatedNewName = newName;
  1.2022 +        }
  1.2023 +
  1.2024 +        rv = target->Append(allocatedNewName);
  1.2025 +        if (NS_FAILED(rv))
  1.2026 +            return rv;
  1.2027 +
  1.2028 +        allocatedNewName.Truncate();
  1.2029 +
  1.2030 +        // check if the destination directory already exists
  1.2031 +        target->Exists(&exists);
  1.2032 +        if (!exists)
  1.2033 +        {
  1.2034 +            // if the destination directory cannot be created, return an error
  1.2035 +            rv = target->Create(DIRECTORY_TYPE, 0644);  // TODO, what permissions should we use
  1.2036 +            if (NS_FAILED(rv))
  1.2037 +                return rv;
  1.2038 +        }
  1.2039 +        else
  1.2040 +        {
  1.2041 +            // check if the destination directory is writable and empty
  1.2042 +            bool isWritable;
  1.2043 +
  1.2044 +            target->IsWritable(&isWritable);
  1.2045 +            if (!isWritable)
  1.2046 +                return NS_ERROR_FILE_ACCESS_DENIED;
  1.2047 +
  1.2048 +            nsCOMPtr<nsISimpleEnumerator> targetIterator;
  1.2049 +            rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
  1.2050 +            if (NS_FAILED(rv))
  1.2051 +                return rv;
  1.2052 +
  1.2053 +            bool more;
  1.2054 +            targetIterator->HasMoreElements(&more);
  1.2055 +            // return error if target directory is not empty
  1.2056 +            if (more)
  1.2057 +                return NS_ERROR_FILE_DIR_NOT_EMPTY;
  1.2058 +        }
  1.2059 +
  1.2060 +        nsDirEnumerator dirEnum;
  1.2061 +
  1.2062 +        rv = dirEnum.Init(this);
  1.2063 +        if (NS_FAILED(rv)) {
  1.2064 +            NS_WARNING("dirEnum initialization failed");
  1.2065 +            return rv;
  1.2066 +        }
  1.2067 +
  1.2068 +        bool more = false;
  1.2069 +        while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
  1.2070 +        {
  1.2071 +            nsCOMPtr<nsISupports> item;
  1.2072 +            nsCOMPtr<nsIFile> file;
  1.2073 +            dirEnum.GetNext(getter_AddRefs(item));
  1.2074 +            file = do_QueryInterface(item);
  1.2075 +            if (file)
  1.2076 +            {
  1.2077 +                bool isDir, isLink;
  1.2078 +
  1.2079 +                file->IsDirectory(&isDir);
  1.2080 +                file->IsSymlink(&isLink);
  1.2081 +
  1.2082 +                if (move)
  1.2083 +                {
  1.2084 +                    if (followSymlinks)
  1.2085 +                        return NS_ERROR_FAILURE;
  1.2086 +
  1.2087 +                    rv = file->MoveTo(target, EmptyString());
  1.2088 +                    if (NS_FAILED(rv))
  1.2089 +                        return rv;
  1.2090 +                }
  1.2091 +                else
  1.2092 +                {
  1.2093 +                    if (followSymlinks)
  1.2094 +                        rv = file->CopyToFollowingLinks(target, EmptyString());
  1.2095 +                    else
  1.2096 +                        rv = file->CopyTo(target, EmptyString());
  1.2097 +                    if (NS_FAILED(rv))
  1.2098 +                        return rv;
  1.2099 +                }
  1.2100 +            }
  1.2101 +        }
  1.2102 +        // we've finished moving all the children of this directory
  1.2103 +        // in the new directory.  so now delete the directory
  1.2104 +        // note, we don't need to do a recursive delete.
  1.2105 +        // MoveTo() is recursive.  At this point,
  1.2106 +        // we've already moved the children of the current folder
  1.2107 +        // to the new location.  nothing should be left in the folder.
  1.2108 +        if (move)
  1.2109 +        {
  1.2110 +          rv = Remove(false /* recursive */);
  1.2111 +          if (NS_FAILED(rv))
  1.2112 +              return rv;
  1.2113 +        }
  1.2114 +    }
  1.2115 +
  1.2116 +
  1.2117 +    // If we moved, we want to adjust this.
  1.2118 +    if (move)
  1.2119 +    {
  1.2120 +        MakeDirty();
  1.2121 +
  1.2122 +        nsAutoString newParentPath;
  1.2123 +        newParentDir->GetPath(newParentPath);
  1.2124 +
  1.2125 +        if (newParentPath.IsEmpty())
  1.2126 +            return NS_ERROR_FAILURE;
  1.2127 +
  1.2128 +        if (newName.IsEmpty())
  1.2129 +        {
  1.2130 +            nsAutoString aFileName;
  1.2131 +            GetLeafName(aFileName);
  1.2132 +
  1.2133 +            InitWithPath(newParentPath);
  1.2134 +            Append(aFileName);
  1.2135 +        }
  1.2136 +        else
  1.2137 +        {
  1.2138 +            InitWithPath(newParentPath);
  1.2139 +            Append(newName);
  1.2140 +        }
  1.2141 +    }
  1.2142 +
  1.2143 +    return NS_OK;
  1.2144 +}
  1.2145 +
  1.2146 +NS_IMETHODIMP
  1.2147 +nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
  1.2148 +{
  1.2149 +    return CopyMove(newParentDir, newName, 0);
  1.2150 +}
  1.2151 +
  1.2152 +NS_IMETHODIMP
  1.2153 +nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
  1.2154 +{
  1.2155 +    return CopyMove(newParentDir, newName, FollowSymlinks);
  1.2156 +}
  1.2157 +
  1.2158 +NS_IMETHODIMP
  1.2159 +nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
  1.2160 +{
  1.2161 +    return CopyMove(newParentDir, newName, Move);
  1.2162 +}
  1.2163 +
  1.2164 +NS_IMETHODIMP
  1.2165 +nsLocalFile::RenameTo(nsIFile *newParentDir, const nsAString & newName)
  1.2166 +{
  1.2167 +  nsCOMPtr<nsIFile> targetParentDir = newParentDir;
  1.2168 +  // check to see if this exists, otherwise return an error.
  1.2169 +  // we will check this by resolving.  If the user wants us
  1.2170 +  // to follow links, then we are talking about the target,
  1.2171 +  // hence we can use the |followSymlinks| parameter.
  1.2172 +  nsresult rv = ResolveAndStat();
  1.2173 +  if (NS_FAILED(rv)) {
  1.2174 +    return rv;
  1.2175 +  }
  1.2176 +
  1.2177 +  if (!targetParentDir) {
  1.2178 +    // no parent was specified.  We must rename.
  1.2179 +    if (newName.IsEmpty()) {
  1.2180 +      return NS_ERROR_INVALID_ARG;
  1.2181 +    }
  1.2182 +    rv = GetParent(getter_AddRefs(targetParentDir));
  1.2183 +    if (NS_FAILED(rv)) {
  1.2184 +      return rv;
  1.2185 +    }
  1.2186 +  }
  1.2187 +
  1.2188 +  if (!targetParentDir) {
  1.2189 +    return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  1.2190 +  }
  1.2191 +
  1.2192 +  // make sure it exists and is a directory.  Create it if not there.
  1.2193 +  bool exists;
  1.2194 +  targetParentDir->Exists(&exists);
  1.2195 +  if (!exists) {
  1.2196 +    rv = targetParentDir->Create(DIRECTORY_TYPE, 0644);
  1.2197 +    if (NS_FAILED(rv)) {
  1.2198 +      return rv;
  1.2199 +    }
  1.2200 +  } else {
  1.2201 +    bool isDir;
  1.2202 +    targetParentDir->IsDirectory(&isDir);
  1.2203 +    if (!isDir) {
  1.2204 +      return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  1.2205 +    }
  1.2206 +  }
  1.2207 +
  1.2208 +  uint32_t options = Rename;
  1.2209 +  if (!newParentDir) {
  1.2210 +    options |= SkipNtfsAclReset;
  1.2211 +  }
  1.2212 +  // Move single file, or move a directory
  1.2213 +  return CopySingleFile(this, targetParentDir, newName, options);
  1.2214 +}
  1.2215 +
  1.2216 +NS_IMETHODIMP
  1.2217 +nsLocalFile::Load(PRLibrary * *_retval)
  1.2218 +{
  1.2219 +    // Check we are correctly initialized.
  1.2220 +    CHECK_mWorkingPath();
  1.2221 +
  1.2222 +    bool isFile;
  1.2223 +    nsresult rv = IsFile(&isFile);
  1.2224 +
  1.2225 +    if (NS_FAILED(rv))
  1.2226 +        return rv;
  1.2227 +
  1.2228 +    if (! isFile)
  1.2229 +        return NS_ERROR_FILE_IS_DIRECTORY;
  1.2230 +
  1.2231 +#ifdef NS_BUILD_REFCNT_LOGGING
  1.2232 +    nsTraceRefcnt::SetActivityIsLegal(false);
  1.2233 +#endif
  1.2234 +
  1.2235 +    PRLibSpec libSpec;
  1.2236 +    libSpec.value.pathname_u = mResolvedPath.get();
  1.2237 +    libSpec.type = PR_LibSpec_PathnameU;
  1.2238 +    *_retval =  PR_LoadLibraryWithFlags(libSpec, 0);
  1.2239 +
  1.2240 +#ifdef NS_BUILD_REFCNT_LOGGING
  1.2241 +    nsTraceRefcnt::SetActivityIsLegal(true);
  1.2242 +#endif
  1.2243 +
  1.2244 +    if (*_retval)
  1.2245 +        return NS_OK;
  1.2246 +    return NS_ERROR_NULL_POINTER;
  1.2247 +}
  1.2248 +
  1.2249 +NS_IMETHODIMP
  1.2250 +nsLocalFile::Remove(bool recursive)
  1.2251 +{
  1.2252 +    // NOTE:
  1.2253 +    //
  1.2254 +    // if the working path points to a shortcut, then we will only
  1.2255 +    // delete the shortcut itself.  even if the shortcut points to
  1.2256 +    // a directory, we will not recurse into that directory or 
  1.2257 +    // delete that directory itself.  likewise, if the shortcut
  1.2258 +    // points to a normal file, we will not delete the real file.
  1.2259 +    // this is done to be consistent with the other platforms that
  1.2260 +    // behave this way.  we do this even if the followLinks attribute
  1.2261 +    // is set to true.  this helps protect against misuse that could
  1.2262 +    // lead to security bugs (e.g., bug 210588).
  1.2263 +    //
  1.2264 +    // Since shortcut files are no longer permitted to be used as unix-like
  1.2265 +    // symlinks interspersed in the path (e.g. "c:/file.lnk/foo/bar.txt")
  1.2266 +    // this processing is a lot simpler. Even if the shortcut file is 
  1.2267 +    // pointing to a directory, only the mWorkingPath value is used and so
  1.2268 +    // only the shortcut file will be deleted.
  1.2269 +
  1.2270 +    // Check we are correctly initialized.
  1.2271 +    CHECK_mWorkingPath();
  1.2272 +
  1.2273 +    bool isDir, isLink;
  1.2274 +    nsresult rv;
  1.2275 +    
  1.2276 +    isDir = false;
  1.2277 +    rv = IsSymlink(&isLink);
  1.2278 +    if (NS_FAILED(rv))
  1.2279 +        return rv;
  1.2280 +
  1.2281 +    // only check to see if we have a directory if it isn't a link
  1.2282 +    if (!isLink)
  1.2283 +    {
  1.2284 +        rv = IsDirectory(&isDir);
  1.2285 +        if (NS_FAILED(rv))
  1.2286 +            return rv;
  1.2287 +    }
  1.2288 +
  1.2289 +    if (isDir)
  1.2290 +    {
  1.2291 +        if (recursive)
  1.2292 +        {
  1.2293 +            nsDirEnumerator dirEnum;
  1.2294 +
  1.2295 +            rv = dirEnum.Init(this);
  1.2296 +            if (NS_FAILED(rv))
  1.2297 +                return rv;
  1.2298 +
  1.2299 +            bool more = false;
  1.2300 +            while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
  1.2301 +            {
  1.2302 +                nsCOMPtr<nsISupports> item;
  1.2303 +                dirEnum.GetNext(getter_AddRefs(item));
  1.2304 +                nsCOMPtr<nsIFile> file = do_QueryInterface(item);
  1.2305 +                if (file)
  1.2306 +                    file->Remove(recursive);
  1.2307 +            }
  1.2308 +        }
  1.2309 +        if (RemoveDirectoryW(mWorkingPath.get()) == 0)
  1.2310 +            return ConvertWinError(GetLastError());
  1.2311 +    }
  1.2312 +    else
  1.2313 +    {
  1.2314 +        if (DeleteFileW(mWorkingPath.get()) == 0)
  1.2315 +            return ConvertWinError(GetLastError());
  1.2316 +    }
  1.2317 +
  1.2318 +    MakeDirty();
  1.2319 +    return rv;
  1.2320 +}
  1.2321 +
  1.2322 +NS_IMETHODIMP
  1.2323 +nsLocalFile::GetLastModifiedTime(PRTime *aLastModifiedTime)
  1.2324 +{
  1.2325 +    // Check we are correctly initialized.
  1.2326 +    CHECK_mWorkingPath();
  1.2327 +
  1.2328 +    if (NS_WARN_IF(!aLastModifiedTime))
  1.2329 +        return NS_ERROR_INVALID_ARG;
  1.2330 + 
  1.2331 +    // get the modified time of the target as determined by mFollowSymlinks
  1.2332 +    // If true, then this will be for the target of the shortcut file, 
  1.2333 +    // otherwise it will be for the shortcut file itself (i.e. the same 
  1.2334 +    // results as GetLastModifiedTimeOfLink)
  1.2335 +
  1.2336 +    nsresult rv = ResolveAndStat();
  1.2337 +    if (NS_FAILED(rv))
  1.2338 +        return rv;
  1.2339 +
  1.2340 +    // microseconds -> milliseconds
  1.2341 +    *aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
  1.2342 +    return NS_OK;
  1.2343 +}
  1.2344 +
  1.2345 +
  1.2346 +NS_IMETHODIMP
  1.2347 +nsLocalFile::GetLastModifiedTimeOfLink(PRTime *aLastModifiedTime)
  1.2348 +{
  1.2349 +    // Check we are correctly initialized.
  1.2350 +    CHECK_mWorkingPath();
  1.2351 +
  1.2352 +    if (NS_WARN_IF(!aLastModifiedTime))
  1.2353 +        return NS_ERROR_INVALID_ARG;
  1.2354 + 
  1.2355 +    // The caller is assumed to have already called IsSymlink 
  1.2356 +    // and to have found that this file is a link. 
  1.2357 +
  1.2358 +    PRFileInfo64 info;
  1.2359 +    nsresult rv = GetFileInfo(mWorkingPath, &info);
  1.2360 +    if (NS_FAILED(rv)) 
  1.2361 +        return rv;
  1.2362 +
  1.2363 +    // microseconds -> milliseconds
  1.2364 +    *aLastModifiedTime = info.modifyTime / PR_USEC_PER_MSEC;
  1.2365 +    return NS_OK;
  1.2366 +}
  1.2367 +
  1.2368 +
  1.2369 +NS_IMETHODIMP
  1.2370 +nsLocalFile::SetLastModifiedTime(PRTime aLastModifiedTime)
  1.2371 +{
  1.2372 +    // Check we are correctly initialized.
  1.2373 +    CHECK_mWorkingPath();
  1.2374 +
  1.2375 +    nsresult rv = ResolveAndStat();
  1.2376 +    if (NS_FAILED(rv))
  1.2377 +        return rv;
  1.2378 +
  1.2379 +    // set the modified time of the target as determined by mFollowSymlinks
  1.2380 +    // If true, then this will be for the target of the shortcut file, 
  1.2381 +    // otherwise it will be for the shortcut file itself (i.e. the same 
  1.2382 +    // results as SetLastModifiedTimeOfLink)
  1.2383 +
  1.2384 +    rv = SetModDate(aLastModifiedTime, mResolvedPath.get());
  1.2385 +    if (NS_SUCCEEDED(rv))
  1.2386 +        MakeDirty();
  1.2387 +
  1.2388 +    return rv;
  1.2389 +}
  1.2390 +
  1.2391 +
  1.2392 +NS_IMETHODIMP
  1.2393 +nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModifiedTime)
  1.2394 +{
  1.2395 +    // The caller is assumed to have already called IsSymlink 
  1.2396 +    // and to have found that this file is a link. 
  1.2397 +
  1.2398 +    nsresult rv = SetModDate(aLastModifiedTime, mWorkingPath.get());
  1.2399 +    if (NS_SUCCEEDED(rv))
  1.2400 +        MakeDirty();
  1.2401 +
  1.2402 +    return rv;
  1.2403 +}
  1.2404 +
  1.2405 +nsresult
  1.2406 +nsLocalFile::SetModDate(PRTime aLastModifiedTime, const wchar_t *filePath)
  1.2407 +{
  1.2408 +    // The FILE_FLAG_BACKUP_SEMANTICS is required in order to change the
  1.2409 +    // modification time for directories.
  1.2410 +    HANDLE file = ::CreateFileW(filePath,          // pointer to name of the file
  1.2411 +                                GENERIC_WRITE,     // access (write) mode
  1.2412 +                                0,                 // share mode
  1.2413 +                                nullptr,           // pointer to security attributes
  1.2414 +                                OPEN_EXISTING,     // how to create
  1.2415 +                                FILE_FLAG_BACKUP_SEMANTICS,  // file attributes
  1.2416 +                                nullptr);
  1.2417 +
  1.2418 +    if (file == INVALID_HANDLE_VALUE)
  1.2419 +    {
  1.2420 +        return ConvertWinError(GetLastError());
  1.2421 +    }
  1.2422 +
  1.2423 +    FILETIME ft;
  1.2424 +    SYSTEMTIME st;
  1.2425 +    PRExplodedTime pret;
  1.2426 +
  1.2427 +    // PR_ExplodeTime expects usecs...
  1.2428 +    PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_GMTParameters, &pret);
  1.2429 +    st.wYear            = pret.tm_year;
  1.2430 +    st.wMonth           = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
  1.2431 +    st.wDayOfWeek       = pret.tm_wday;
  1.2432 +    st.wDay             = pret.tm_mday;
  1.2433 +    st.wHour            = pret.tm_hour;
  1.2434 +    st.wMinute          = pret.tm_min;
  1.2435 +    st.wSecond          = pret.tm_sec;
  1.2436 +    st.wMilliseconds    = pret.tm_usec/1000;
  1.2437 +
  1.2438 +    nsresult rv = NS_OK;
  1.2439 +    // if at least one of these fails...
  1.2440 +    if (!(SystemTimeToFileTime(&st, &ft) != 0 &&
  1.2441 +          SetFileTime(file, nullptr, &ft, &ft) != 0))
  1.2442 +    {
  1.2443 +      rv = ConvertWinError(GetLastError());
  1.2444 +    }
  1.2445 +
  1.2446 +    CloseHandle(file);
  1.2447 +    return rv;
  1.2448 +}
  1.2449 +
  1.2450 +NS_IMETHODIMP
  1.2451 +nsLocalFile::GetPermissions(uint32_t *aPermissions)
  1.2452 +{
  1.2453 +    if (NS_WARN_IF(!aPermissions))
  1.2454 +        return NS_ERROR_INVALID_ARG;
  1.2455 +
  1.2456 +    // get the permissions of the target as determined by mFollowSymlinks
  1.2457 +    // If true, then this will be for the target of the shortcut file, 
  1.2458 +    // otherwise it will be for the shortcut file itself (i.e. the same 
  1.2459 +    // results as GetPermissionsOfLink)
  1.2460 +    nsresult rv = ResolveAndStat();
  1.2461 +    if (NS_FAILED(rv))
  1.2462 +        return rv;
  1.2463 +
  1.2464 +    bool isWritable, isExecutable;
  1.2465 +    IsWritable(&isWritable);
  1.2466 +    IsExecutable(&isExecutable);
  1.2467 +
  1.2468 +    *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH;         // all read
  1.2469 +    if (isWritable)
  1.2470 +        *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH;    // all write
  1.2471 +    if (isExecutable)
  1.2472 +        *aPermissions |= PR_IXUSR|PR_IXGRP|PR_IXOTH;    // all execute
  1.2473 +
  1.2474 +    return NS_OK;
  1.2475 +}
  1.2476 +
  1.2477 +NS_IMETHODIMP
  1.2478 +nsLocalFile::GetPermissionsOfLink(uint32_t *aPermissions)
  1.2479 +{
  1.2480 +    // Check we are correctly initialized.
  1.2481 +    CHECK_mWorkingPath();
  1.2482 +
  1.2483 +    if (NS_WARN_IF(!aPermissions))
  1.2484 +        return NS_ERROR_INVALID_ARG;
  1.2485 +
  1.2486 +    // The caller is assumed to have already called IsSymlink 
  1.2487 +    // and to have found that this file is a link. It is not 
  1.2488 +    // possible for a link file to be executable.
  1.2489 +
  1.2490 +    DWORD word = ::GetFileAttributesW(mWorkingPath.get());
  1.2491 +    if (word == INVALID_FILE_ATTRIBUTES)
  1.2492 +        return NS_ERROR_FILE_INVALID_PATH;
  1.2493 +
  1.2494 +    bool isWritable = !(word & FILE_ATTRIBUTE_READONLY);
  1.2495 +    *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH;         // all read
  1.2496 +    if (isWritable)
  1.2497 +        *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH;    // all write
  1.2498 +
  1.2499 +    return NS_OK;
  1.2500 +}
  1.2501 +
  1.2502 +
  1.2503 +NS_IMETHODIMP
  1.2504 +nsLocalFile::SetPermissions(uint32_t aPermissions)
  1.2505 +{
  1.2506 +    // Check we are correctly initialized.
  1.2507 +    CHECK_mWorkingPath();
  1.2508 +
  1.2509 +    // set the permissions of the target as determined by mFollowSymlinks
  1.2510 +    // If true, then this will be for the target of the shortcut file, 
  1.2511 +    // otherwise it will be for the shortcut file itself (i.e. the same 
  1.2512 +    // results as SetPermissionsOfLink)
  1.2513 +    nsresult rv = ResolveAndStat();
  1.2514 +    if (NS_FAILED(rv))
  1.2515 +        return rv;
  1.2516 +
  1.2517 +    // windows only knows about the following permissions
  1.2518 +    int mode = 0;
  1.2519 +    if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH))    // any read
  1.2520 +        mode |= _S_IREAD;
  1.2521 +    if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH))    // any write
  1.2522 +        mode |= _S_IWRITE;
  1.2523 +
  1.2524 +    if (_wchmod(mResolvedPath.get(), mode) == -1)
  1.2525 +        return NS_ERROR_FAILURE;
  1.2526 +
  1.2527 +    return NS_OK;
  1.2528 +}
  1.2529 +
  1.2530 +NS_IMETHODIMP
  1.2531 +nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
  1.2532 +{
  1.2533 +    // The caller is assumed to have already called IsSymlink 
  1.2534 +    // and to have found that this file is a link. 
  1.2535 +
  1.2536 +    // windows only knows about the following permissions
  1.2537 +    int mode = 0;
  1.2538 +    if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH))    // any read
  1.2539 +        mode |= _S_IREAD;
  1.2540 +    if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH))    // any write
  1.2541 +        mode |= _S_IWRITE;
  1.2542 +
  1.2543 +    if (_wchmod(mWorkingPath.get(), mode) == -1)
  1.2544 +        return NS_ERROR_FAILURE;
  1.2545 +
  1.2546 +    return NS_OK;
  1.2547 +}
  1.2548 +
  1.2549 +
  1.2550 +NS_IMETHODIMP
  1.2551 +nsLocalFile::GetFileSize(int64_t *aFileSize)
  1.2552 +{
  1.2553 +    if (NS_WARN_IF(!aFileSize))
  1.2554 +        return NS_ERROR_INVALID_ARG;
  1.2555 +
  1.2556 +    nsresult rv = ResolveAndStat();
  1.2557 +    if (NS_FAILED(rv))
  1.2558 +        return rv;
  1.2559 +
  1.2560 +    *aFileSize = mFileInfo64.size;
  1.2561 +    return NS_OK;
  1.2562 +}
  1.2563 +
  1.2564 +
  1.2565 +NS_IMETHODIMP
  1.2566 +nsLocalFile::GetFileSizeOfLink(int64_t *aFileSize)
  1.2567 +{
  1.2568 +    // Check we are correctly initialized.
  1.2569 +    CHECK_mWorkingPath();
  1.2570 +
  1.2571 +    if (NS_WARN_IF(!aFileSize))
  1.2572 +        return NS_ERROR_INVALID_ARG;
  1.2573 +
  1.2574 +    // The caller is assumed to have already called IsSymlink 
  1.2575 +    // and to have found that this file is a link. 
  1.2576 +
  1.2577 +    PRFileInfo64 info;
  1.2578 +    if (NS_FAILED(GetFileInfo(mWorkingPath, &info)))
  1.2579 +        return NS_ERROR_FILE_INVALID_PATH;
  1.2580 +
  1.2581 +    *aFileSize = info.size;
  1.2582 +    return NS_OK;
  1.2583 +}
  1.2584 +
  1.2585 +NS_IMETHODIMP
  1.2586 +nsLocalFile::SetFileSize(int64_t aFileSize)
  1.2587 +{
  1.2588 +    // Check we are correctly initialized.
  1.2589 +    CHECK_mWorkingPath();
  1.2590 +
  1.2591 +    nsresult rv = ResolveAndStat();
  1.2592 +    if (NS_FAILED(rv))
  1.2593 +        return rv;
  1.2594 +
  1.2595 +    HANDLE hFile = ::CreateFileW(mResolvedPath.get(),// pointer to name of the file
  1.2596 +                                 GENERIC_WRITE,      // access (write) mode
  1.2597 +                                 FILE_SHARE_READ,    // share mode
  1.2598 +                                 nullptr,            // pointer to security attributes
  1.2599 +                                 OPEN_EXISTING,          // how to create
  1.2600 +                                 FILE_ATTRIBUTE_NORMAL,  // file attributes
  1.2601 +                                 nullptr);
  1.2602 +    if (hFile == INVALID_HANDLE_VALUE)
  1.2603 +    {
  1.2604 +        return ConvertWinError(GetLastError());
  1.2605 +    }
  1.2606 +
  1.2607 +    // seek the file pointer to the new, desired end of file
  1.2608 +    // and then truncate the file at that position
  1.2609 +    rv = NS_ERROR_FAILURE;
  1.2610 +    aFileSize = MyFileSeek64(hFile, aFileSize, FILE_BEGIN);
  1.2611 +    if (aFileSize != -1 && SetEndOfFile(hFile))
  1.2612 +    {
  1.2613 +        MakeDirty();
  1.2614 +        rv = NS_OK;
  1.2615 +    }
  1.2616 +
  1.2617 +    CloseHandle(hFile);
  1.2618 +    return rv;
  1.2619 +}
  1.2620 +
  1.2621 +NS_IMETHODIMP
  1.2622 +nsLocalFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable)
  1.2623 +{
  1.2624 +    // Check we are correctly initialized.
  1.2625 +    CHECK_mWorkingPath();
  1.2626 +
  1.2627 +    if (NS_WARN_IF(!aDiskSpaceAvailable))
  1.2628 +        return NS_ERROR_INVALID_ARG;
  1.2629 +
  1.2630 +    ResolveAndStat();
  1.2631 +
  1.2632 +    if (mFileInfo64.type == PR_FILE_FILE) {
  1.2633 +      // Since GetDiskFreeSpaceExW works only on directories, use the parent.
  1.2634 +      nsCOMPtr<nsIFile> parent;
  1.2635 +      if (NS_SUCCEEDED(GetParent(getter_AddRefs(parent))) && parent) {
  1.2636 +        return parent->GetDiskSpaceAvailable(aDiskSpaceAvailable);
  1.2637 +      }
  1.2638 +    }
  1.2639 +
  1.2640 +    ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes;
  1.2641 +    if (::GetDiskFreeSpaceExW(mResolvedPath.get(), &liFreeBytesAvailableToCaller, 
  1.2642 +                              &liTotalNumberOfBytes, nullptr))
  1.2643 +    {
  1.2644 +        *aDiskSpaceAvailable = liFreeBytesAvailableToCaller.QuadPart;
  1.2645 +        return NS_OK;
  1.2646 +    }
  1.2647 +    *aDiskSpaceAvailable = 0;
  1.2648 +    return NS_OK;
  1.2649 +}
  1.2650 +
  1.2651 +NS_IMETHODIMP
  1.2652 +nsLocalFile::GetParent(nsIFile * *aParent)
  1.2653 +{
  1.2654 +    // Check we are correctly initialized.
  1.2655 +    CHECK_mWorkingPath();
  1.2656 +
  1.2657 +    if (NS_WARN_IF(!aParent))
  1.2658 +        return NS_ERROR_INVALID_ARG;
  1.2659 +
  1.2660 +    // A two-character path must be a drive such as C:, so it has no parent
  1.2661 +    if (mWorkingPath.Length() == 2) {
  1.2662 +        *aParent = nullptr;
  1.2663 +        return NS_OK;
  1.2664 +    }
  1.2665 +
  1.2666 +    int32_t offset = mWorkingPath.RFindChar(char16_t('\\'));
  1.2667 +    // adding this offset check that was removed in bug 241708 fixes mail
  1.2668 +    // directories that aren't relative to/underneath the profile dir.
  1.2669 +    // e.g., on a different drive. Before you remove them, please make
  1.2670 +    // sure local mail directories that aren't underneath the profile dir work.
  1.2671 +    if (offset == kNotFound)
  1.2672 +      return NS_ERROR_FILE_UNRECOGNIZED_PATH;
  1.2673 +
  1.2674 +    // A path of the form \\NAME is a top-level path and has no parent
  1.2675 +    if (offset == 1 && mWorkingPath[0] == L'\\') {
  1.2676 +        *aParent = nullptr;
  1.2677 +        return NS_OK;
  1.2678 +    }
  1.2679 +
  1.2680 +    nsAutoString parentPath(mWorkingPath);
  1.2681 +
  1.2682 +    if (offset > 0)
  1.2683 +        parentPath.Truncate(offset);
  1.2684 +    else
  1.2685 +        parentPath.AssignLiteral("\\\\.");
  1.2686 +
  1.2687 +    nsCOMPtr<nsIFile> localFile;
  1.2688 +    nsresult rv = NS_NewLocalFile(parentPath, mFollowSymlinks, getter_AddRefs(localFile));
  1.2689 +
  1.2690 +    if (NS_FAILED(rv)) {
  1.2691 +        return rv;
  1.2692 +    }
  1.2693 +
  1.2694 +    localFile.forget(aParent);
  1.2695 +    return NS_OK;
  1.2696 +}
  1.2697 +
  1.2698 +NS_IMETHODIMP
  1.2699 +nsLocalFile::Exists(bool *_retval)
  1.2700 +{
  1.2701 +    // Check we are correctly initialized.
  1.2702 +    CHECK_mWorkingPath();
  1.2703 +
  1.2704 +    if (NS_WARN_IF(!_retval))
  1.2705 +        return NS_ERROR_INVALID_ARG;
  1.2706 +    *_retval = false;
  1.2707 +
  1.2708 +    MakeDirty();
  1.2709 +    nsresult rv = ResolveAndStat();
  1.2710 +    *_retval = NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_IS_LOCKED;
  1.2711 +
  1.2712 +    return NS_OK;
  1.2713 +}
  1.2714 +
  1.2715 +NS_IMETHODIMP
  1.2716 +nsLocalFile::IsWritable(bool *aIsWritable)
  1.2717 +{
  1.2718 +    // Check we are correctly initialized.
  1.2719 +    CHECK_mWorkingPath();
  1.2720 +
  1.2721 +    // The read-only attribute on a FAT directory only means that it can't 
  1.2722 +    // be deleted. It is still possible to modify the contents of the directory.
  1.2723 +    nsresult rv = IsDirectory(aIsWritable);
  1.2724 +    if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
  1.2725 +      *aIsWritable = true;
  1.2726 +      return NS_OK;
  1.2727 +    } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
  1.2728 +      // If the file is normally allowed write access
  1.2729 +      // we should still return that the file is writable.
  1.2730 +    } else if (NS_FAILED(rv)) {
  1.2731 +        return rv;
  1.2732 +    }
  1.2733 +    if (*aIsWritable)
  1.2734 +        return NS_OK;
  1.2735 +
  1.2736 +    // writable if the file doesn't have the readonly attribute
  1.2737 +    rv = HasFileAttribute(FILE_ATTRIBUTE_READONLY, aIsWritable);
  1.2738 +    if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
  1.2739 +        *aIsWritable = false;
  1.2740 +        return NS_OK;
  1.2741 +    } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
  1.2742 +      // If the file is normally allowed write access
  1.2743 +      // we should still return that the file is writable.
  1.2744 +    } else if (NS_FAILED(rv)) {
  1.2745 +        return rv;
  1.2746 +    }
  1.2747 +    *aIsWritable = !*aIsWritable;
  1.2748 +
  1.2749 +    // If the read only attribute is not set, check to make sure
  1.2750 +    // we can open the file with write access.
  1.2751 +    if (*aIsWritable) {
  1.2752 +        PRFileDesc* file;
  1.2753 +        rv = OpenFile(mResolvedPath, PR_WRONLY, 0, &file);
  1.2754 +        if (NS_SUCCEEDED(rv)) {
  1.2755 +            PR_Close(file);
  1.2756 +        } else if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
  1.2757 +          *aIsWritable = false;
  1.2758 +        } else if (rv == NS_ERROR_FILE_IS_LOCKED) {
  1.2759 +            // If it is locked and read only we would have 
  1.2760 +            // gotten access denied
  1.2761 +            *aIsWritable = true; 
  1.2762 +        } else {
  1.2763 +            return rv;
  1.2764 +        }
  1.2765 +    }
  1.2766 +    return NS_OK;
  1.2767 +}
  1.2768 +
  1.2769 +NS_IMETHODIMP
  1.2770 +nsLocalFile::IsReadable(bool *_retval)
  1.2771 +{
  1.2772 +    // Check we are correctly initialized.
  1.2773 +    CHECK_mWorkingPath();
  1.2774 +
  1.2775 +    if (NS_WARN_IF(!_retval))
  1.2776 +        return NS_ERROR_INVALID_ARG;
  1.2777 +    *_retval = false;
  1.2778 +
  1.2779 +    nsresult rv = ResolveAndStat();
  1.2780 +    if (NS_FAILED(rv))
  1.2781 +        return rv;
  1.2782 +
  1.2783 +    *_retval = true;
  1.2784 +    return NS_OK;
  1.2785 +}
  1.2786 +
  1.2787 +
  1.2788 +NS_IMETHODIMP
  1.2789 +nsLocalFile::IsExecutable(bool *_retval)
  1.2790 +{
  1.2791 +    // Check we are correctly initialized.
  1.2792 +    CHECK_mWorkingPath();
  1.2793 +
  1.2794 +    if (NS_WARN_IF(!_retval))
  1.2795 +        return NS_ERROR_INVALID_ARG;
  1.2796 +    *_retval = false;
  1.2797 +    
  1.2798 +    nsresult rv;
  1.2799 +
  1.2800 +    // only files can be executables
  1.2801 +    bool isFile;
  1.2802 +    rv = IsFile(&isFile);
  1.2803 +    if (NS_FAILED(rv))
  1.2804 +        return rv;
  1.2805 +    if (!isFile)
  1.2806 +        return NS_OK;
  1.2807 +
  1.2808 +    //TODO: shouldn't we be checking mFollowSymlinks here?
  1.2809 +    bool symLink;
  1.2810 +    rv = IsSymlink(&symLink);
  1.2811 +    if (NS_FAILED(rv))
  1.2812 +        return rv;
  1.2813 +
  1.2814 +    nsAutoString path;
  1.2815 +    if (symLink)
  1.2816 +        GetTarget(path);
  1.2817 +    else
  1.2818 +        GetPath(path);
  1.2819 +
  1.2820 +    // kill trailing dots and spaces.
  1.2821 +    int32_t filePathLen = path.Length() - 1;
  1.2822 +    while(filePathLen > 0 && (path[filePathLen] == L' ' || path[filePathLen] == L'.'))
  1.2823 +    {
  1.2824 +        path.Truncate(filePathLen--);
  1.2825 +    } 
  1.2826 +
  1.2827 +    // Get extension.
  1.2828 +    int32_t dotIdx = path.RFindChar(char16_t('.'));
  1.2829 +    if ( dotIdx != kNotFound ) {
  1.2830 +        // Convert extension to lower case.
  1.2831 +        char16_t *p = path.BeginWriting();
  1.2832 +        for( p+= dotIdx + 1; *p; p++ )
  1.2833 +            *p +=  (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0; 
  1.2834 +        
  1.2835 +        // Search for any of the set of executable extensions.
  1.2836 +        static const char * const executableExts[] = {
  1.2837 +            "ad",
  1.2838 +            "ade",         // access project extension
  1.2839 +            "adp",
  1.2840 +            "air",         // Adobe AIR installer
  1.2841 +            "app",         // executable application
  1.2842 +            "application", // from bug 348763
  1.2843 +            "asp",
  1.2844 +            "bas",
  1.2845 +            "bat",
  1.2846 +            "chm",
  1.2847 +            "cmd",
  1.2848 +            "com",
  1.2849 +            "cpl",
  1.2850 +            "crt",
  1.2851 +            "exe",
  1.2852 +            "fxp",         // FoxPro compiled app
  1.2853 +            "hlp",
  1.2854 +            "hta",
  1.2855 +            "inf",
  1.2856 +            "ins",
  1.2857 +            "isp",
  1.2858 +            "jar",         // java application bundle
  1.2859 +            "js",
  1.2860 +            "jse",
  1.2861 +            "lnk",
  1.2862 +            "mad",         // Access Module Shortcut
  1.2863 +            "maf",         // Access
  1.2864 +            "mag",         // Access Diagram Shortcut
  1.2865 +            "mam",         // Access Macro Shortcut
  1.2866 +            "maq",         // Access Query Shortcut
  1.2867 +            "mar",         // Access Report Shortcut
  1.2868 +            "mas",         // Access Stored Procedure
  1.2869 +            "mat",         // Access Table Shortcut
  1.2870 +            "mau",         // Media Attachment Unit
  1.2871 +            "mav",         // Access View Shortcut
  1.2872 +            "maw",         // Access Data Access Page
  1.2873 +            "mda",         // Access Add-in, MDA Access 2 Workgroup
  1.2874 +            "mdb",
  1.2875 +            "mde",
  1.2876 +            "mdt",         // Access Add-in Data
  1.2877 +            "mdw",         // Access Workgroup Information
  1.2878 +            "mdz",         // Access Wizard Template
  1.2879 +            "msc",
  1.2880 +            "msh",         // Microsoft Shell
  1.2881 +            "mshxml",      // Microsoft Shell
  1.2882 +            "msi",
  1.2883 +            "msp",
  1.2884 +            "mst",
  1.2885 +            "ops",         // Office Profile Settings
  1.2886 +            "pcd",
  1.2887 +            "pif",
  1.2888 +            "plg",         // Developer Studio Build Log
  1.2889 +            "prf",         // windows system file
  1.2890 +            "prg",
  1.2891 +            "pst",
  1.2892 +            "reg",
  1.2893 +            "scf",         // Windows explorer command
  1.2894 +            "scr",
  1.2895 +            "sct",
  1.2896 +            "shb",
  1.2897 +            "shs",
  1.2898 +            "url",
  1.2899 +            "vb",
  1.2900 +            "vbe",
  1.2901 +            "vbs",
  1.2902 +            "vsd",
  1.2903 +            "vsmacros",    // Visual Studio .NET Binary-based Macro Project
  1.2904 +            "vss",
  1.2905 +            "vst",
  1.2906 +            "vsw",
  1.2907 +            "ws",
  1.2908 +            "wsc",
  1.2909 +            "wsf",
  1.2910 +            "wsh"};
  1.2911 +        nsDependentSubstring ext = Substring(path, dotIdx + 1);
  1.2912 +        for ( size_t i = 0; i < ArrayLength(executableExts); i++ ) {
  1.2913 +            if ( ext.EqualsASCII(executableExts[i])) {
  1.2914 +                // Found a match.  Set result and quit.
  1.2915 +                *_retval = true;
  1.2916 +                break;
  1.2917 +            }
  1.2918 +        }
  1.2919 +    }
  1.2920 +
  1.2921 +    return NS_OK;
  1.2922 +}
  1.2923 +
  1.2924 +
  1.2925 +NS_IMETHODIMP
  1.2926 +nsLocalFile::IsDirectory(bool *_retval)
  1.2927 +{
  1.2928 +    return HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, _retval);
  1.2929 +}
  1.2930 +
  1.2931 +NS_IMETHODIMP
  1.2932 +nsLocalFile::IsFile(bool *_retval)
  1.2933 +{
  1.2934 +    nsresult rv = HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY, _retval);
  1.2935 +    if (NS_SUCCEEDED(rv)) {
  1.2936 +        *_retval = !*_retval;
  1.2937 +    }
  1.2938 +    return rv;
  1.2939 +}
  1.2940 +
  1.2941 +NS_IMETHODIMP
  1.2942 +nsLocalFile::IsHidden(bool *_retval)
  1.2943 +{
  1.2944 +    return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN, _retval);
  1.2945 +}
  1.2946 +
  1.2947 +nsresult
  1.2948 +nsLocalFile::HasFileAttribute(DWORD fileAttrib, bool *_retval)
  1.2949 +{
  1.2950 +    if (NS_WARN_IF(!_retval))
  1.2951 +        return NS_ERROR_INVALID_ARG;
  1.2952 +
  1.2953 +    nsresult rv = Resolve();
  1.2954 +    if (NS_FAILED(rv)) {
  1.2955 +        return rv;
  1.2956 +    }
  1.2957 +
  1.2958 +    DWORD attributes = GetFileAttributesW(mResolvedPath.get());
  1.2959 +    if (INVALID_FILE_ATTRIBUTES == attributes) {
  1.2960 +        return ConvertWinError(GetLastError());
  1.2961 +    }
  1.2962 +
  1.2963 +    *_retval = ((attributes & fileAttrib) != 0);
  1.2964 +    return NS_OK;
  1.2965 +}
  1.2966 +
  1.2967 +NS_IMETHODIMP
  1.2968 +nsLocalFile::IsSymlink(bool *_retval)
  1.2969 +{
  1.2970 +    // Check we are correctly initialized.
  1.2971 +    CHECK_mWorkingPath();
  1.2972 +
  1.2973 +    if (NS_WARN_IF(!_retval))
  1.2974 +        return NS_ERROR_INVALID_ARG;
  1.2975 +
  1.2976 +    // unless it is a valid shortcut path it's not a symlink
  1.2977 +    if (!IsShortcutPath(mWorkingPath)) {
  1.2978 +        *_retval = false;
  1.2979 +        return NS_OK;
  1.2980 +    }
  1.2981 +
  1.2982 +    // we need to know if this is a file or directory
  1.2983 +    nsresult rv = ResolveAndStat();
  1.2984 +    if (NS_FAILED(rv)) {
  1.2985 +        return rv;
  1.2986 +    }
  1.2987 +
  1.2988 +    // We should not check mFileInfo64.type here for PR_FILE_FILE because lnk
  1.2989 +    // files can point to directories or files.  Important security checks
  1.2990 +    // depend on correctly identifying lnk files.  mFileInfo64 now holds info
  1.2991 +    // about the target of the lnk file, not the actual lnk file!
  1.2992 +    *_retval = true;
  1.2993 +    return NS_OK;
  1.2994 +}
  1.2995 +
  1.2996 +NS_IMETHODIMP
  1.2997 +nsLocalFile::IsSpecial(bool *_retval)
  1.2998 +{
  1.2999 +    return HasFileAttribute(FILE_ATTRIBUTE_SYSTEM, _retval);
  1.3000 +}
  1.3001 +
  1.3002 +NS_IMETHODIMP
  1.3003 +nsLocalFile::Equals(nsIFile *inFile, bool *_retval)
  1.3004 +{
  1.3005 +    if (NS_WARN_IF(!inFile))
  1.3006 +        return NS_ERROR_INVALID_ARG;
  1.3007 +    if (NS_WARN_IF(!_retval))
  1.3008 +        return NS_ERROR_INVALID_ARG;
  1.3009 +
  1.3010 +    EnsureShortPath();
  1.3011 +
  1.3012 +    nsCOMPtr<nsILocalFileWin> lf(do_QueryInterface(inFile));
  1.3013 +    if (!lf) {
  1.3014 +        *_retval = false;
  1.3015 +        return NS_OK;
  1.3016 +    }
  1.3017 +
  1.3018 +    nsAutoString inFilePath;
  1.3019 +    lf->GetCanonicalPath(inFilePath);
  1.3020 +
  1.3021 +    // Ok : Win9x
  1.3022 +    *_retval = _wcsicmp(mShortWorkingPath.get(), inFilePath.get()) == 0; 
  1.3023 +
  1.3024 +    return NS_OK;
  1.3025 +}
  1.3026 +
  1.3027 +
  1.3028 +NS_IMETHODIMP
  1.3029 +nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval)
  1.3030 +{
  1.3031 +    // Check we are correctly initialized.
  1.3032 +    CHECK_mWorkingPath();
  1.3033 +
  1.3034 +    *_retval = false;
  1.3035 +
  1.3036 +    nsAutoString myFilePath;
  1.3037 +    if (NS_FAILED(GetTarget(myFilePath)))
  1.3038 +        GetPath(myFilePath);
  1.3039 +
  1.3040 +    uint32_t myFilePathLen = myFilePath.Length();
  1.3041 +
  1.3042 +    nsAutoString inFilePath;
  1.3043 +    if (NS_FAILED(inFile->GetTarget(inFilePath)))
  1.3044 +        inFile->GetPath(inFilePath);
  1.3045 +
  1.3046 +    // make sure that the |inFile|'s path has a trailing separator.
  1.3047 +    if (inFilePath.Length() >= myFilePathLen && inFilePath[myFilePathLen] == L'\\')
  1.3048 +    {
  1.3049 +        if (_wcsnicmp(myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
  1.3050 +        {
  1.3051 +            *_retval = true;
  1.3052 +        }
  1.3053 +
  1.3054 +    }
  1.3055 +
  1.3056 +    return NS_OK;
  1.3057 +}
  1.3058 +
  1.3059 +
  1.3060 +NS_IMETHODIMP
  1.3061 +nsLocalFile::GetTarget(nsAString &_retval)
  1.3062 +{
  1.3063 +    _retval.Truncate();
  1.3064 +#if STRICT_FAKE_SYMLINKS
  1.3065 +    bool symLink;
  1.3066 +
  1.3067 +    nsresult rv = IsSymlink(&symLink);
  1.3068 +    if (NS_FAILED(rv))
  1.3069 +        return rv;
  1.3070 +
  1.3071 +    if (!symLink)
  1.3072 +    {
  1.3073 +        return NS_ERROR_FILE_INVALID_PATH;
  1.3074 +    }
  1.3075 +#endif
  1.3076 +    ResolveAndStat();
  1.3077 +
  1.3078 +    _retval = mResolvedPath;
  1.3079 +    return NS_OK;
  1.3080 +}
  1.3081 +
  1.3082 +
  1.3083 +/* attribute bool followLinks; */
  1.3084 +NS_IMETHODIMP
  1.3085 +nsLocalFile::GetFollowLinks(bool *aFollowLinks)
  1.3086 +{
  1.3087 +    *aFollowLinks = mFollowSymlinks;
  1.3088 +    return NS_OK;
  1.3089 +}
  1.3090 +NS_IMETHODIMP
  1.3091 +nsLocalFile::SetFollowLinks(bool aFollowLinks)
  1.3092 +{
  1.3093 +    MakeDirty();
  1.3094 +    mFollowSymlinks = aFollowLinks;
  1.3095 +    return NS_OK;
  1.3096 +}
  1.3097 +
  1.3098 +
  1.3099 +NS_IMETHODIMP
  1.3100 +nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
  1.3101 +{
  1.3102 +    nsresult rv;
  1.3103 +
  1.3104 +    *entries = nullptr;
  1.3105 +    if (mWorkingPath.EqualsLiteral("\\\\.")) {
  1.3106 +        nsDriveEnumerator *drives = new nsDriveEnumerator;
  1.3107 +        if (!drives)
  1.3108 +            return NS_ERROR_OUT_OF_MEMORY;
  1.3109 +        NS_ADDREF(drives);
  1.3110 +        rv = drives->Init();
  1.3111 +        if (NS_FAILED(rv)) {
  1.3112 +            NS_RELEASE(drives);
  1.3113 +            return rv;
  1.3114 +        }
  1.3115 +        *entries = drives;
  1.3116 +        return NS_OK;
  1.3117 +    }
  1.3118 +
  1.3119 +    nsDirEnumerator* dirEnum = new nsDirEnumerator();
  1.3120 +    if (dirEnum == nullptr)
  1.3121 +        return NS_ERROR_OUT_OF_MEMORY;
  1.3122 +    NS_ADDREF(dirEnum);
  1.3123 +    rv = dirEnum->Init(this);
  1.3124 +    if (NS_FAILED(rv))
  1.3125 +    {
  1.3126 +        NS_RELEASE(dirEnum);
  1.3127 +        return rv;
  1.3128 +    }
  1.3129 +
  1.3130 +    *entries = dirEnum;
  1.3131 +
  1.3132 +    return NS_OK;
  1.3133 +}
  1.3134 +
  1.3135 +NS_IMETHODIMP
  1.3136 +nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
  1.3137 +{
  1.3138 +    CopyUTF16toUTF8(mWorkingPath, aPersistentDescriptor);
  1.3139 +    return NS_OK;
  1.3140 +}   
  1.3141 +    
  1.3142 +NS_IMETHODIMP
  1.3143 +nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
  1.3144 +{
  1.3145 +    if (IsUTF8(aPersistentDescriptor))
  1.3146 +        return InitWithPath(NS_ConvertUTF8toUTF16(aPersistentDescriptor));
  1.3147 +    else
  1.3148 +        return InitWithNativePath(aPersistentDescriptor);
  1.3149 +}   
  1.3150 +
  1.3151 +/* attrib unsigned long fileAttributesWin; */
  1.3152 +NS_IMETHODIMP
  1.3153 +nsLocalFile::GetFileAttributesWin(uint32_t *aAttribs)
  1.3154 +{
  1.3155 +    *aAttribs = 0;
  1.3156 +    DWORD dwAttrs = GetFileAttributesW(mWorkingPath.get());
  1.3157 +    if (dwAttrs == INVALID_FILE_ATTRIBUTES)
  1.3158 +      return NS_ERROR_FILE_INVALID_PATH;
  1.3159 +
  1.3160 +    if (!(dwAttrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
  1.3161 +        *aAttribs |= WFA_SEARCH_INDEXED;
  1.3162 +
  1.3163 +    return NS_OK;
  1.3164 +}   
  1.3165 +    
  1.3166 +NS_IMETHODIMP
  1.3167 +nsLocalFile::SetFileAttributesWin(uint32_t aAttribs)
  1.3168 +{
  1.3169 +    DWORD dwAttrs = GetFileAttributesW(mWorkingPath.get());
  1.3170 +    if (dwAttrs == INVALID_FILE_ATTRIBUTES)
  1.3171 +      return NS_ERROR_FILE_INVALID_PATH;
  1.3172 +
  1.3173 +    if (aAttribs & WFA_SEARCH_INDEXED) {
  1.3174 +        dwAttrs &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
  1.3175 +    } else {
  1.3176 +        dwAttrs |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
  1.3177 +    }
  1.3178 +
  1.3179 +    if (aAttribs & WFA_READONLY) {
  1.3180 +      dwAttrs |= FILE_ATTRIBUTE_READONLY;
  1.3181 +    } else if ((aAttribs & WFA_READWRITE) &&
  1.3182 +               (dwAttrs & FILE_ATTRIBUTE_READONLY)) {
  1.3183 +      dwAttrs &= ~FILE_ATTRIBUTE_READONLY;
  1.3184 +    }
  1.3185 +
  1.3186 +    if (SetFileAttributesW(mWorkingPath.get(), dwAttrs) == 0)
  1.3187 +      return NS_ERROR_FAILURE;
  1.3188 +    return NS_OK;
  1.3189 +}
  1.3190 +
  1.3191 +
  1.3192 +NS_IMETHODIMP
  1.3193 +nsLocalFile::Reveal()
  1.3194 +{
  1.3195 +    // This API should be main thread only
  1.3196 +    MOZ_ASSERT(NS_IsMainThread()); 
  1.3197 +
  1.3198 +    // make sure mResolvedPath is set
  1.3199 +    nsresult rv = Resolve();
  1.3200 +    if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
  1.3201 +        return rv;
  1.3202 +    }
  1.3203 +
  1.3204 +    // To create a new thread, get the thread manager
  1.3205 +    nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
  1.3206 +    nsCOMPtr<nsIThread> mythread;
  1.3207 +    rv = tm->NewThread(0, 0, getter_AddRefs(mythread));
  1.3208 +    if (NS_FAILED(rv)) {
  1.3209 +        return rv;
  1.3210 +    }
  1.3211 +
  1.3212 +    nsCOMPtr<nsIRunnable> runnable = 
  1.3213 +        new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::RevealOp,
  1.3214 +                                       mResolvedPath);
  1.3215 +
  1.3216 +    // After the dispatch, the result runnable will shut down the worker
  1.3217 +    // thread, so we can let it go.
  1.3218 +    mythread->Dispatch(runnable, NS_DISPATCH_NORMAL);
  1.3219 +    return NS_OK;
  1.3220 +}
  1.3221 +
  1.3222 +NS_IMETHODIMP
  1.3223 +nsLocalFile::Launch()
  1.3224 +{
  1.3225 +    // This API should be main thread only
  1.3226 +    MOZ_ASSERT(NS_IsMainThread()); 
  1.3227 +
  1.3228 +    // make sure mResolvedPath is set
  1.3229 +    nsresult rv = Resolve();
  1.3230 +    if (NS_FAILED(rv))
  1.3231 +        return rv;
  1.3232 +
  1.3233 +    // To create a new thread, get the thread manager
  1.3234 +    nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
  1.3235 +    nsCOMPtr<nsIThread> mythread;
  1.3236 +    rv = tm->NewThread(0, 0, getter_AddRefs(mythread));
  1.3237 +    if (NS_FAILED(rv)) {
  1.3238 +        return rv;
  1.3239 +    }
  1.3240 +
  1.3241 +    nsCOMPtr<nsIRunnable> runnable = 
  1.3242 +        new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::LaunchOp,
  1.3243 +                                       mResolvedPath);
  1.3244 +
  1.3245 +    // After the dispatch, the result runnable will shut down the worker
  1.3246 +    // thread, so we can let it go.
  1.3247 +    mythread->Dispatch(runnable, NS_DISPATCH_NORMAL);
  1.3248 +    return NS_OK;
  1.3249 +}
  1.3250 +
  1.3251 +nsresult
  1.3252 +NS_NewLocalFile(const nsAString &path, bool followLinks, nsIFile* *result)
  1.3253 +{
  1.3254 +    nsLocalFile* file = new nsLocalFile();
  1.3255 +    if (file == nullptr)
  1.3256 +        return NS_ERROR_OUT_OF_MEMORY;
  1.3257 +    NS_ADDREF(file);
  1.3258 +    
  1.3259 +    file->SetFollowLinks(followLinks);
  1.3260 +
  1.3261 +    if (!path.IsEmpty()) {
  1.3262 +        nsresult rv = file->InitWithPath(path);
  1.3263 +        if (NS_FAILED(rv)) {
  1.3264 +            NS_RELEASE(file);
  1.3265 +            return rv;
  1.3266 +        }
  1.3267 +    }
  1.3268 +
  1.3269 +    *result = file;
  1.3270 +    return NS_OK;
  1.3271 +}
  1.3272 +
  1.3273 +//-----------------------------------------------------------------------------
  1.3274 +// Native (lossy) interface
  1.3275 +//-----------------------------------------------------------------------------
  1.3276 +  
  1.3277 +NS_IMETHODIMP
  1.3278 +nsLocalFile::InitWithNativePath(const nsACString &filePath)
  1.3279 +{
  1.3280 +   nsAutoString tmp;
  1.3281 +   nsresult rv = NS_CopyNativeToUnicode(filePath, tmp);
  1.3282 +   if (NS_SUCCEEDED(rv))
  1.3283 +       return InitWithPath(tmp);
  1.3284 +
  1.3285 +   return rv;
  1.3286 +}
  1.3287 +
  1.3288 +NS_IMETHODIMP
  1.3289 +nsLocalFile::AppendNative(const nsACString &node)
  1.3290 +{
  1.3291 +    nsAutoString tmp;
  1.3292 +    nsresult rv = NS_CopyNativeToUnicode(node, tmp);
  1.3293 +    if (NS_SUCCEEDED(rv))
  1.3294 +        return Append(tmp);
  1.3295 +
  1.3296 +    return rv;
  1.3297 +}
  1.3298 +
  1.3299 +NS_IMETHODIMP
  1.3300 +nsLocalFile::AppendRelativeNativePath(const nsACString &node)
  1.3301 +{
  1.3302 +    nsAutoString tmp;
  1.3303 +    nsresult rv = NS_CopyNativeToUnicode(node, tmp);
  1.3304 +    if (NS_SUCCEEDED(rv))
  1.3305 +        return AppendRelativePath(tmp);
  1.3306 +    return rv;
  1.3307 +}
  1.3308 +
  1.3309 +
  1.3310 +NS_IMETHODIMP
  1.3311 +nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
  1.3312 +{
  1.3313 +    //NS_WARNING("This API is lossy. Use GetLeafName !");
  1.3314 +    nsAutoString tmp;
  1.3315 +    nsresult rv = GetLeafName(tmp);
  1.3316 +    if (NS_SUCCEEDED(rv))
  1.3317 +        rv = NS_CopyUnicodeToNative(tmp, aLeafName);
  1.3318 +
  1.3319 +    return rv;
  1.3320 +}
  1.3321 +
  1.3322 +NS_IMETHODIMP
  1.3323 +nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
  1.3324 +{
  1.3325 +    nsAutoString tmp;
  1.3326 +    nsresult rv = NS_CopyNativeToUnicode(aLeafName, tmp);
  1.3327 +    if (NS_SUCCEEDED(rv))
  1.3328 +        return SetLeafName(tmp);
  1.3329 +
  1.3330 +    return rv;
  1.3331 +}
  1.3332 +
  1.3333 +
  1.3334 +NS_IMETHODIMP
  1.3335 +nsLocalFile::GetNativePath(nsACString &_retval)
  1.3336 +{
  1.3337 +    //NS_WARNING("This API is lossy. Use GetPath !");
  1.3338 +    nsAutoString tmp;
  1.3339 +    nsresult rv = GetPath(tmp);
  1.3340 +    if (NS_SUCCEEDED(rv))
  1.3341 +        rv = NS_CopyUnicodeToNative(tmp, _retval);
  1.3342 +
  1.3343 +    return rv;
  1.3344 +}
  1.3345 +
  1.3346 +
  1.3347 +NS_IMETHODIMP
  1.3348 +nsLocalFile::GetNativeCanonicalPath(nsACString &aResult)
  1.3349 +{
  1.3350 +    NS_WARNING("This method is lossy. Use GetCanonicalPath !");
  1.3351 +    EnsureShortPath();
  1.3352 +    NS_CopyUnicodeToNative(mShortWorkingPath, aResult);
  1.3353 +    return NS_OK;
  1.3354 +}
  1.3355 +
  1.3356 +
  1.3357 +NS_IMETHODIMP
  1.3358 +nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
  1.3359 +{
  1.3360 +    // Check we are correctly initialized.
  1.3361 +    CHECK_mWorkingPath();
  1.3362 +
  1.3363 +    if (newName.IsEmpty())
  1.3364 +        return CopyTo(newParentDir, EmptyString());
  1.3365 +
  1.3366 +    nsAutoString tmp;
  1.3367 +    nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
  1.3368 +    if (NS_SUCCEEDED(rv))
  1.3369 +        return CopyTo(newParentDir, tmp);
  1.3370 +
  1.3371 +    return rv;
  1.3372 +}
  1.3373 +
  1.3374 +NS_IMETHODIMP
  1.3375 +nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
  1.3376 +{
  1.3377 +    if (newName.IsEmpty())
  1.3378 +        return CopyToFollowingLinks(newParentDir, EmptyString());
  1.3379 +
  1.3380 +    nsAutoString tmp;
  1.3381 +    nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
  1.3382 +    if (NS_SUCCEEDED(rv))
  1.3383 +        return CopyToFollowingLinks(newParentDir, tmp);
  1.3384 +
  1.3385 +    return rv;
  1.3386 +}
  1.3387 +
  1.3388 +NS_IMETHODIMP
  1.3389 +nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
  1.3390 +{
  1.3391 +    // Check we are correctly initialized.
  1.3392 +    CHECK_mWorkingPath();
  1.3393 +
  1.3394 +    if (newName.IsEmpty())
  1.3395 +        return MoveTo(newParentDir, EmptyString());
  1.3396 +
  1.3397 +    nsAutoString tmp;
  1.3398 +    nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
  1.3399 +    if (NS_SUCCEEDED(rv))
  1.3400 +        return MoveTo(newParentDir, tmp);
  1.3401 +
  1.3402 +    return rv;
  1.3403 +}
  1.3404 +
  1.3405 +NS_IMETHODIMP
  1.3406 +nsLocalFile::GetNativeTarget(nsACString &_retval)
  1.3407 +{
  1.3408 +    // Check we are correctly initialized.
  1.3409 +    CHECK_mWorkingPath();
  1.3410 +
  1.3411 +    NS_WARNING("This API is lossy. Use GetTarget !");
  1.3412 +    nsAutoString tmp;
  1.3413 +    nsresult rv = GetTarget(tmp);
  1.3414 +    if (NS_SUCCEEDED(rv))
  1.3415 +        rv = NS_CopyUnicodeToNative(tmp, _retval);
  1.3416 +
  1.3417 +    return rv;
  1.3418 +}
  1.3419 +
  1.3420 +nsresult
  1.3421 +NS_NewNativeLocalFile(const nsACString &path, bool followLinks, nsIFile* *result)
  1.3422 +{
  1.3423 +    nsAutoString buf;
  1.3424 +    nsresult rv = NS_CopyNativeToUnicode(path, buf);
  1.3425 +    if (NS_FAILED(rv)) {
  1.3426 +        *result = nullptr;
  1.3427 +        return rv;
  1.3428 +    }
  1.3429 +    return NS_NewLocalFile(buf, followLinks, result);
  1.3430 +}
  1.3431 +
  1.3432 +void
  1.3433 +nsLocalFile::EnsureShortPath()
  1.3434 +{
  1.3435 +    if (!mShortWorkingPath.IsEmpty())
  1.3436 +        return;
  1.3437 +
  1.3438 +    WCHAR shortPath[MAX_PATH + 1];
  1.3439 +    DWORD lengthNeeded = ::GetShortPathNameW(mWorkingPath.get(), shortPath,
  1.3440 +                                             ArrayLength(shortPath));
  1.3441 +    // If an error occurred then lengthNeeded is set to 0 or the length of the
  1.3442 +    // needed buffer including null termination.  If it succeeds the number of
  1.3443 +    // wide characters not including null termination is returned.
  1.3444 +    if (lengthNeeded != 0 && lengthNeeded < ArrayLength(shortPath))
  1.3445 +        mShortWorkingPath.Assign(shortPath);
  1.3446 +    else
  1.3447 +        mShortWorkingPath.Assign(mWorkingPath);
  1.3448 +}
  1.3449 +
  1.3450 +// nsIHashable
  1.3451 +
  1.3452 +NS_IMETHODIMP
  1.3453 +nsLocalFile::Equals(nsIHashable* aOther, bool *aResult)
  1.3454 +{
  1.3455 +    nsCOMPtr<nsIFile> otherfile(do_QueryInterface(aOther));
  1.3456 +    if (!otherfile) {
  1.3457 +        *aResult = false;
  1.3458 +        return NS_OK;
  1.3459 +    }
  1.3460 +
  1.3461 +    return Equals(otherfile, aResult);
  1.3462 +}
  1.3463 +
  1.3464 +NS_IMETHODIMP
  1.3465 +nsLocalFile::GetHashCode(uint32_t *aResult)
  1.3466 +{
  1.3467 +    // In order for short and long path names to hash to the same value we
  1.3468 +    // always hash on the short pathname.
  1.3469 +    EnsureShortPath();
  1.3470 +
  1.3471 +    *aResult = HashString(mShortWorkingPath);
  1.3472 +    return NS_OK;
  1.3473 +}
  1.3474 +
  1.3475 +//-----------------------------------------------------------------------------
  1.3476 +// nsLocalFile <static members>
  1.3477 +//-----------------------------------------------------------------------------
  1.3478 +
  1.3479 +void
  1.3480 +nsLocalFile::GlobalInit()
  1.3481 +{
  1.3482 +    DebugOnly<nsresult> rv = NS_CreateShortcutResolver();
  1.3483 +    NS_ASSERTION(NS_SUCCEEDED(rv), "Shortcut resolver could not be created");
  1.3484 +}
  1.3485 +
  1.3486 +void
  1.3487 +nsLocalFile::GlobalShutdown()
  1.3488 +{
  1.3489 +    NS_DestroyShortcutResolver();
  1.3490 +}
  1.3491 +
  1.3492 +NS_IMPL_ISUPPORTS(nsDriveEnumerator, nsISimpleEnumerator)
  1.3493 +
  1.3494 +nsDriveEnumerator::nsDriveEnumerator()
  1.3495 +{
  1.3496 +}
  1.3497 +
  1.3498 +nsDriveEnumerator::~nsDriveEnumerator()
  1.3499 +{
  1.3500 +}
  1.3501 +
  1.3502 +nsresult nsDriveEnumerator::Init()
  1.3503 +{
  1.3504 +    /* If the length passed to GetLogicalDriveStrings is smaller
  1.3505 +     * than the length of the string it would return, it returns
  1.3506 +     * the length required for the string. */
  1.3507 +    DWORD length = GetLogicalDriveStringsW(0, 0);
  1.3508 +    /* The string is null terminated */
  1.3509 +    if (!mDrives.SetLength(length+1, fallible_t()))
  1.3510 +        return NS_ERROR_OUT_OF_MEMORY;
  1.3511 +    if (!GetLogicalDriveStringsW(length, wwc(mDrives.BeginWriting())))
  1.3512 +        return NS_ERROR_FAILURE;
  1.3513 +    mDrives.BeginReading(mStartOfCurrentDrive);
  1.3514 +    mDrives.EndReading(mEndOfDrivesString);
  1.3515 +    return NS_OK;
  1.3516 +}
  1.3517 +
  1.3518 +NS_IMETHODIMP nsDriveEnumerator::HasMoreElements(bool *aHasMore)
  1.3519 +{
  1.3520 +    *aHasMore = *mStartOfCurrentDrive != L'\0';
  1.3521 +    return NS_OK;
  1.3522 +}
  1.3523 +
  1.3524 +NS_IMETHODIMP nsDriveEnumerator::GetNext(nsISupports **aNext)
  1.3525 +{
  1.3526 +    /* GetLogicalDrives stored in mDrives is a concatenation
  1.3527 +     * of null terminated strings, followed by a null terminator.
  1.3528 +     * mStartOfCurrentDrive is an iterator pointing at the first
  1.3529 +     * character of the current drive. */
  1.3530 +    if (*mStartOfCurrentDrive == L'\0') {
  1.3531 +        *aNext = nullptr;
  1.3532 +        return NS_OK;
  1.3533 +    }
  1.3534 +
  1.3535 +    nsAString::const_iterator driveEnd = mStartOfCurrentDrive;
  1.3536 +    FindCharInReadable(L'\0', driveEnd, mEndOfDrivesString);
  1.3537 +    nsString drive(Substring(mStartOfCurrentDrive, driveEnd));
  1.3538 +    mStartOfCurrentDrive = ++driveEnd;
  1.3539 +
  1.3540 +    nsIFile *file;
  1.3541 +    nsresult rv = NS_NewLocalFile(drive, false, &file);
  1.3542 +
  1.3543 +    *aNext = file;
  1.3544 +    return rv;
  1.3545 +}

mercurial