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 +}