1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/DebugOnly.h"
9 #include "mozilla/WindowsVersion.h"
12 #include "nsAutoPtr.h"
15 #include "nsLocalFile.h"
16 #include "nsIDirectoryEnumerator.h"
17 #include "nsNativeCharsetUtils.h"
19 #include "nsISimpleEnumerator.h"
20 #include "nsIComponentManager.h"
22 #include "private/pprio.h" // To get PR_ImportFile
25 #include "nsHashKeys.h"
27 #include "nsXPIDLString.h"
28 #include "nsReadableUtils.h"
43 #include "nsXPIDLString.h"
47 #include "mozilla/Mutex.h"
48 #include "SpecialSystemDirectory.h"
50 #include "nsTraceRefcnt.h"
51 #include "nsXPCOMCIDInternal.h"
52 #include "nsThreadUtils.h"
53 #include "nsXULAppAPI.h"
55 using namespace mozilla
;
57 #define CHECK_mWorkingPath() \
59 if (mWorkingPath.IsEmpty()) \
60 return NS_ERROR_NOT_INITIALIZED; \
63 // CopyFileEx only supports unbuffered I/O in Windows Vista and above
64 #ifndef COPY_FILE_NO_BUFFERING
65 #define COPY_FILE_NO_BUFFERING 0x00001000
68 #ifndef FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
69 #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
73 #define DRIVE_REMOTE 4
77 * A runnable to dispatch back to the main thread when
78 * AsyncLocalFileWinOperation completes.
80 class AsyncLocalFileWinDone
: public nsRunnable
83 AsyncLocalFileWinDone() :
84 mWorkerThread(do_GetCurrentThread())
86 // Objects of this type must only be created on worker threads
87 MOZ_ASSERT(!NS_IsMainThread());
92 // This event shuts down the worker thread and so must be main thread.
93 MOZ_ASSERT(NS_IsMainThread());
95 // If we don't destroy the thread when we're done with it, it will hang
96 // around forever... and that is bad!
97 mWorkerThread
->Shutdown();
102 nsCOMPtr
<nsIThread
> mWorkerThread
;
106 * A runnable to dispatch from the main thread when an async operation should
109 class AsyncLocalFileWinOperation
: public nsRunnable
112 enum FileOp
{ RevealOp
, LaunchOp
};
114 AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::FileOp aOperation
,
115 const nsAString
& aResolvedPath
) :
116 mOperation(aOperation
),
117 mResolvedPath(aResolvedPath
)
123 MOZ_ASSERT(!NS_IsMainThread(),
124 "AsyncLocalFileWinOperation should not be run on the main thread!");
126 CoInitialize(nullptr);
127 switch (mOperation
) {
139 // Send the result back to the main thread so that it can shutdown
140 nsCOMPtr
<nsIRunnable
> resultrunnable
= new AsyncLocalFileWinDone();
141 NS_DispatchToMainThread(resultrunnable
);
146 // Reveals the path in explorer.
149 DWORD attributes
= GetFileAttributesW(mResolvedPath
.get());
150 if (INVALID_FILE_ATTRIBUTES
== attributes
) {
151 return NS_ERROR_FILE_INVALID_PATH
;
155 if (attributes
& FILE_ATTRIBUTE_DIRECTORY
) {
156 // We have a directory so we should open the directory itself.
157 ITEMIDLIST
* dir
= ILCreateFromPathW(mResolvedPath
.get());
159 return NS_ERROR_FAILURE
;
162 const ITEMIDLIST
* selection
[] = { dir
};
163 UINT count
= ArrayLength(selection
);
165 //Perform the open of the directory.
166 hr
= SHOpenFolderAndSelectItems(dir
, count
, selection
, 0);
169 int32_t len
= mResolvedPath
.Length();
170 // We don't currently handle UNC long paths of the form \\?\ anywhere so
171 // this should be fine.
172 if (len
> MAX_PATH
) {
173 return NS_ERROR_FILE_INVALID_PATH
;
175 WCHAR parentDirectoryPath
[MAX_PATH
+ 1] = { 0 };
176 wcsncpy(parentDirectoryPath
, mResolvedPath
.get(), MAX_PATH
);
177 PathRemoveFileSpecW(parentDirectoryPath
);
179 // We have a file so we should open the parent directory.
180 ITEMIDLIST
* dir
= ILCreateFromPathW(parentDirectoryPath
);
182 return NS_ERROR_FAILURE
;
185 // Set the item in the directory to select to the file we want to reveal.
186 ITEMIDLIST
* item
= ILCreateFromPathW(mResolvedPath
.get());
189 return NS_ERROR_FAILURE
;
192 const ITEMIDLIST
* selection
[] = { item
};
193 UINT count
= ArrayLength(selection
);
195 //Perform the selection of the file.
196 hr
= SHOpenFolderAndSelectItems(dir
, count
, selection
, 0);
202 return SUCCEEDED(hr
) ? NS_OK
: NS_ERROR_FAILURE
;
205 // Launches the default shell operation for the file path
208 // use the app registry name to launch a shell execute....
209 SHELLEXECUTEINFOW seinfo
;
210 memset(&seinfo
, 0, sizeof(seinfo
));
211 seinfo
.cbSize
= sizeof(SHELLEXECUTEINFOW
);
212 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro
) {
213 seinfo
.fMask
= SEE_MASK_FLAG_LOG_USAGE
;
215 seinfo
.hwnd
= nullptr;
216 seinfo
.lpVerb
= nullptr;
217 seinfo
.lpFile
= mResolvedPath
.get();
218 seinfo
.lpParameters
= nullptr;
219 seinfo
.lpDirectory
= nullptr;
220 seinfo
.nShow
= SW_SHOWNORMAL
;
222 // Use the directory of the file we're launching as the working
223 // directory. That way if we have a self extracting EXE it won't
224 // suggest to extract to the install directory.
225 WCHAR workingDirectory
[MAX_PATH
+ 1] = { L
'\0' };
226 wcsncpy(workingDirectory
, mResolvedPath
.get(), MAX_PATH
);
227 if (PathRemoveFileSpecW(workingDirectory
)) {
228 seinfo
.lpDirectory
= workingDirectory
;
230 NS_WARNING("Could not set working directory for launched file.");
233 if (ShellExecuteExW(&seinfo
)) {
236 DWORD r
= GetLastError();
237 // if the file has no association, we launch windows'
238 // "what do you want to do" dialog
239 if (r
== SE_ERR_NOASSOC
) {
240 nsAutoString shellArg
;
241 shellArg
.AssignLiteral(MOZ_UTF16("shell32.dll,OpenAs_RunDLL "));
242 shellArg
.Append(mResolvedPath
);
243 seinfo
.lpFile
= L
"RUNDLL32.EXE";
244 seinfo
.lpParameters
= shellArg
.get();
245 if (ShellExecuteExW(&seinfo
)) {
254 return NS_ERROR_OUT_OF_MEMORY
;
255 case ERROR_FILE_NOT_FOUND
:
256 return NS_ERROR_FILE_NOT_FOUND
;
257 case ERROR_PATH_NOT_FOUND
:
258 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
259 case ERROR_BAD_FORMAT
:
260 return NS_ERROR_FILE_CORRUPTED
;
261 case SE_ERR_ACCESSDENIED
:
262 return NS_ERROR_FILE_ACCESS_DENIED
;
263 case SE_ERR_ASSOCINCOMPLETE
:
265 return NS_ERROR_UNEXPECTED
;
268 case SE_ERR_DDETIMEOUT
:
269 return NS_ERROR_NOT_AVAILABLE
;
270 case SE_ERR_DLLNOTFOUND
:
271 return NS_ERROR_FAILURE
;
273 return NS_ERROR_FILE_IS_LOCKED
;
275 return NS_ERROR_FILE_EXECUTION_FAILED
;
281 // Stores the operation that will be performed on the thread
282 AsyncLocalFileWinOperation::FileOp mOperation
;
284 // Stores the path to perform the operation on
285 nsString mResolvedPath
;
288 class nsDriveEnumerator
: public nsISimpleEnumerator
293 NS_DECL_NSISIMPLEENUMERATOR
296 virtual ~nsDriveEnumerator();
298 /* mDrives stores the null-separated drive names.
300 * HasMoreElements checks mStartOfCurrentDrive.
301 * GetNext advances mStartOfCurrentDrive.
304 nsAString::const_iterator mStartOfCurrentDrive
;
305 nsAString::const_iterator mEndOfDrivesString
;
308 //----------------------------------------------------------------------------
309 // short cut resolver
310 //----------------------------------------------------------------------------
311 class ShortcutResolver
315 // nonvirtual since we're not subclassed
319 nsresult
Resolve(const WCHAR
* aIn
, WCHAR
* aOut
);
320 nsresult
SetShortcut(bool aUpdateExisting
,
321 const WCHAR
* aShortcutPath
,
322 const WCHAR
* aTargetPath
,
323 const WCHAR
* aWorkingDir
,
325 const WCHAR
* aDescription
,
326 const WCHAR
* aIconFile
,
331 nsRefPtr
<IPersistFile
> mPersistFile
;
332 nsRefPtr
<IShellLinkW
> mShellLink
;
335 ShortcutResolver::ShortcutResolver() :
336 mLock("ShortcutResolver.mLock")
338 CoInitialize(nullptr);
341 ShortcutResolver::~ShortcutResolver()
347 ShortcutResolver::Init()
349 // Get a pointer to the IPersistFile interface.
350 if (FAILED(CoCreateInstance(CLSID_ShellLink
,
352 CLSCTX_INPROC_SERVER
,
354 getter_AddRefs(mShellLink
))) ||
355 FAILED(mShellLink
->QueryInterface(IID_IPersistFile
,
356 getter_AddRefs(mPersistFile
)))) {
357 mShellLink
= nullptr;
358 return NS_ERROR_FAILURE
;
363 // |out| must be an allocated buffer of size MAX_PATH
365 ShortcutResolver::Resolve(const WCHAR
* aIn
, WCHAR
* aOut
)
368 return NS_ERROR_FAILURE
;
371 MutexAutoLock
lock(mLock
);
373 if (FAILED(mPersistFile
->Load(aIn
, STGM_READ
)) ||
374 FAILED(mShellLink
->Resolve(nullptr, SLR_NO_UI
)) ||
375 FAILED(mShellLink
->GetPath(aOut
, MAX_PATH
, nullptr, SLGP_UNCPRIORITY
))) {
376 return NS_ERROR_FAILURE
;
382 ShortcutResolver::SetShortcut(bool aUpdateExisting
,
383 const WCHAR
* aShortcutPath
,
384 const WCHAR
* aTargetPath
,
385 const WCHAR
* aWorkingDir
,
387 const WCHAR
* aDescription
,
388 const WCHAR
* aIconPath
,
392 return NS_ERROR_FAILURE
;
395 if (!aShortcutPath
) {
396 return NS_ERROR_FAILURE
;
399 MutexAutoLock
lock(mLock
);
401 if (aUpdateExisting
) {
402 if (FAILED(mPersistFile
->Load(aShortcutPath
, STGM_READWRITE
))) {
403 return NS_ERROR_FAILURE
;
407 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
;
410 // Since we reuse our IPersistFile, we have to clear out any values that
411 // may be left over from previous calls to SetShortcut.
412 if (FAILED(mShellLink
->SetWorkingDirectory(L
"")) ||
413 FAILED(mShellLink
->SetArguments(L
"")) ||
414 FAILED(mShellLink
->SetDescription(L
"")) ||
415 FAILED(mShellLink
->SetIconLocation(L
"", 0))) {
416 return NS_ERROR_FAILURE
;
420 if (aTargetPath
&& FAILED(mShellLink
->SetPath(aTargetPath
))) {
421 return NS_ERROR_FAILURE
;
424 if (aWorkingDir
&& FAILED(mShellLink
->SetWorkingDirectory(aWorkingDir
))) {
425 return NS_ERROR_FAILURE
;
428 if (aArgs
&& FAILED(mShellLink
->SetArguments(aArgs
))) {
429 return NS_ERROR_FAILURE
;
432 if (aDescription
&& FAILED(mShellLink
->SetDescription(aDescription
))) {
433 return NS_ERROR_FAILURE
;
436 if (aIconPath
&& FAILED(mShellLink
->SetIconLocation(aIconPath
, aIconIndex
))) {
437 return NS_ERROR_FAILURE
;
440 if (FAILED(mPersistFile
->Save(aShortcutPath
,
442 // Second argument indicates whether the file path specified in the
443 // first argument should become the "current working file" for this
445 return NS_ERROR_FAILURE
;
451 static ShortcutResolver
* gResolver
= nullptr;
454 NS_CreateShortcutResolver()
456 gResolver
= new ShortcutResolver();
458 return NS_ERROR_OUT_OF_MEMORY
;
461 return gResolver
->Init();
465 NS_DestroyShortcutResolver()
472 //-----------------------------------------------------------------------------
473 // static helper functions
474 //-----------------------------------------------------------------------------
476 // certainly not all the error that can be
477 // encountered, but many of them common ones
479 ConvertWinError(DWORD aWinErr
)
484 case ERROR_FILE_NOT_FOUND
:
485 case ERROR_PATH_NOT_FOUND
:
486 case ERROR_INVALID_DRIVE
:
487 rv
= NS_ERROR_FILE_NOT_FOUND
;
489 case ERROR_ACCESS_DENIED
:
490 case ERROR_NOT_SAME_DEVICE
:
491 rv
= NS_ERROR_FILE_ACCESS_DENIED
;
493 case ERROR_SHARING_VIOLATION
: // CreateFile without sharing flags
494 case ERROR_LOCK_VIOLATION
: // LockFile, LockFileEx
495 rv
= NS_ERROR_FILE_IS_LOCKED
;
497 case ERROR_NOT_ENOUGH_MEMORY
:
498 case ERROR_INVALID_BLOCK
:
499 case ERROR_INVALID_HANDLE
:
500 case ERROR_ARENA_TRASHED
:
501 rv
= NS_ERROR_OUT_OF_MEMORY
;
503 case ERROR_CURRENT_DIRECTORY
:
504 rv
= NS_ERROR_FILE_DIR_NOT_EMPTY
;
506 case ERROR_WRITE_PROTECT
:
507 rv
= NS_ERROR_FILE_READ_ONLY
;
509 case ERROR_HANDLE_DISK_FULL
:
510 rv
= NS_ERROR_FILE_TOO_BIG
;
512 case ERROR_FILE_EXISTS
:
513 case ERROR_ALREADY_EXISTS
:
514 case ERROR_CANNOT_MAKE
:
515 rv
= NS_ERROR_FILE_ALREADY_EXISTS
;
517 case ERROR_FILENAME_EXCED_RANGE
:
518 rv
= NS_ERROR_FILE_NAME_TOO_LONG
;
520 case ERROR_DIRECTORY
:
521 rv
= NS_ERROR_FILE_NOT_DIRECTORY
;
527 rv
= NS_ERROR_FAILURE
;
533 // as suggested in the MSDN documentation on SetFilePointer
535 MyFileSeek64(HANDLE aHandle
, __int64 aDistance
, DWORD aMoveMethod
)
539 li
.QuadPart
= aDistance
;
540 li
.LowPart
= SetFilePointer(aHandle
, li
.LowPart
, &li
.HighPart
, aMoveMethod
);
541 if (li
.LowPart
== INVALID_SET_FILE_POINTER
&& GetLastError() != NO_ERROR
) {
549 IsShortcutPath(const nsAString
& aPath
)
551 // Under Windows, the shortcuts are just files with a ".lnk" extension.
552 // Note also that we don't resolve links in the middle of paths.
553 // i.e. "c:\foo.lnk\bar.txt" is invalid.
554 NS_ABORT_IF_FALSE(!aPath
.IsEmpty(), "don't pass an empty string");
555 int32_t len
= aPath
.Length();
556 return len
>= 4 && (StringTail(aPath
, 4).LowerCaseEqualsASCII(".lnk"));
559 //-----------------------------------------------------------------------------
560 // We need the following three definitions to make |OpenFile| convert a file
561 // handle to an NSPR file descriptor correctly when |O_APPEND| flag is
562 // specified. It is defined in a private header of NSPR (primpl.h) we can't
563 // include. As a temporary workaround until we decide how to extend
564 // |PR_ImportFile|, we define it here. Currently, |_PR_HAVE_PEEK_BUFFER|
565 // and |PR_STRICT_ADDR_LEN| are not defined for the 'w95'-dependent portion
566 // of NSPR so that fields of |PRFilePrivate| #ifdef'd by them are not copied.
567 // Similarly, |_MDFileDesc| is taken from nsprpub/pr/include/md/_win95.h.
568 // In an unlikely case we switch to 'NT'-dependent NSPR AND this temporary
569 // workaround last beyond the switch, |PRFilePrivate| and |_MDFileDesc|
570 // need to be changed to match the definitions for WinNT.
571 //-----------------------------------------------------------------------------
588 _PRTriStateBool inheritable
;
590 int lockCount
; /* 0: not locked
591 * -1: a native lockfile call is in progress
592 * > 0: # times the file is locked */
597 //-----------------------------------------------------------------------------
598 // Six static methods defined below (OpenFile, FileTimeToPRTime, GetFileInfo,
599 // OpenDir, CloseDir, ReadDir) should go away once the corresponding
600 // UTF-16 APIs are implemented on all the supported platforms (or at least
601 // Windows 9x/ME) in NSPR. Currently, they're only implemented on
602 // Windows NT4 or later. (bug 330665)
603 //-----------------------------------------------------------------------------
605 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
606 // PR_Open and _PR_MD_OPEN
608 OpenFile(const nsAFlatString
& aName
, int aOsflags
, int aMode
, PRFileDesc
** aFd
)
612 int32_t disposition
= 0;
613 int32_t attributes
= 0;
615 if (aOsflags
& PR_SYNC
) {
616 attributes
= FILE_FLAG_WRITE_THROUGH
;
618 if (aOsflags
& PR_RDONLY
|| aOsflags
& PR_RDWR
) {
619 access
|= GENERIC_READ
;
621 if (aOsflags
& PR_WRONLY
|| aOsflags
& PR_RDWR
) {
622 access
|= GENERIC_WRITE
;
625 if (aOsflags
& PR_CREATE_FILE
&& aOsflags
& PR_EXCL
) {
626 disposition
= CREATE_NEW
;
627 } else if (aOsflags
& PR_CREATE_FILE
) {
628 if (aOsflags
& PR_TRUNCATE
) {
629 disposition
= CREATE_ALWAYS
;
631 disposition
= OPEN_ALWAYS
;
634 if (aOsflags
& PR_TRUNCATE
) {
635 disposition
= TRUNCATE_EXISTING
;
637 disposition
= OPEN_EXISTING
;
641 if (aOsflags
& nsIFile::DELETE_ON_CLOSE
) {
642 attributes
|= FILE_FLAG_DELETE_ON_CLOSE
;
645 if (aOsflags
& nsIFile::OS_READAHEAD
) {
646 attributes
|= FILE_FLAG_SEQUENTIAL_SCAN
;
649 // If no write permissions are requested, and if we are possibly creating
650 // the file, then set the new file as read only.
651 // The flag has no effect if we happen to open the file.
652 if (!(aMode
& (PR_IWUSR
| PR_IWGRP
| PR_IWOTH
)) &&
653 disposition
!= OPEN_EXISTING
) {
654 attributes
|= FILE_ATTRIBUTE_READONLY
;
657 HANDLE file
= ::CreateFileW(aName
.get(), access
,
658 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
659 nullptr, disposition
, attributes
, nullptr);
661 if (file
== INVALID_HANDLE_VALUE
) {
663 return ConvertWinError(GetLastError());
666 *aFd
= PR_ImportFile((PROsfd
) file
);
668 // On Windows, _PR_HAVE_O_APPEND is not defined so that we have to
669 // add it manually. (see |PR_Open| in nsprpub/pr/src/io/prfile.c)
670 (*aFd
)->secret
->appendMode
= (PR_APPEND
& aOsflags
) ? true : false;
674 nsresult rv
= NS_ErrorAccordingToNSPR();
681 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
682 // PR_FileTimeToPRTime and _PR_FileTimeToPRTime
684 FileTimeToPRTime(const FILETIME
* aFiletime
, PRTime
* aPrtm
)
687 const PRTime _pr_filetime_offset
= 116444736000000000LL;
689 const PRTime _pr_filetime_offset
= 116444736000000000i64
;
692 PR_ASSERT(sizeof(FILETIME
) == sizeof(PRTime
));
693 ::CopyMemory(aPrtm
, aFiletime
, sizeof(PRTime
));
695 *aPrtm
= (*aPrtm
- _pr_filetime_offset
) / 10LL;
697 *aPrtm
= (*aPrtm
- _pr_filetime_offset
) / 10i64
;
701 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} with some
702 // changes : PR_GetFileInfo64, _PR_MD_GETFILEINFO64
704 GetFileInfo(const nsAFlatString
& aName
, PRFileInfo64
* aInfo
)
706 WIN32_FILE_ATTRIBUTE_DATA fileData
;
708 if (aName
.IsEmpty() || aName
.FindCharInSet(MOZ_UTF16("?*")) != kNotFound
) {
709 return NS_ERROR_INVALID_ARG
;
712 if (!::GetFileAttributesExW(aName
.get(), GetFileExInfoStandard
, &fileData
)) {
713 return ConvertWinError(GetLastError());
716 if (fileData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
717 aInfo
->type
= PR_FILE_DIRECTORY
;
719 aInfo
->type
= PR_FILE_FILE
;
722 aInfo
->size
= fileData
.nFileSizeHigh
;
723 aInfo
->size
= (aInfo
->size
<< 32) + fileData
.nFileSizeLow
;
725 FileTimeToPRTime(&fileData
.ftLastWriteTime
, &aInfo
->modifyTime
);
727 if (0 == fileData
.ftCreationTime
.dwLowDateTime
&&
728 0 == fileData
.ftCreationTime
.dwHighDateTime
) {
729 aInfo
->creationTime
= aInfo
->modifyTime
;
731 FileTimeToPRTime(&fileData
.ftCreationTime
, &aInfo
->creationTime
);
740 WIN32_FIND_DATAW data
;
745 OpenDir(const nsAFlatString
& aName
, nsDir
** aDir
)
747 if (NS_WARN_IF(!aDir
)) {
748 return NS_ERROR_INVALID_ARG
;
752 if (aName
.Length() + 3 >= MAX_PATH
) {
753 return NS_ERROR_FILE_NAME_TOO_LONG
;
756 nsDir
* d
= PR_NEW(nsDir
);
758 return NS_ERROR_OUT_OF_MEMORY
;
761 nsAutoString
filename(aName
);
763 // If |aName| ends in a slash or backslash, do not append another backslash.
764 if (filename
.Last() == L
'/' || filename
.Last() == L
'\\') {
765 filename
.Append('*');
767 filename
.AppendLiteral("\\*");
770 filename
.ReplaceChar(L
'/', L
'\\');
772 // FindFirstFileW Will have a last error of ERROR_DIRECTORY if
773 // <file_path>\* is passed in. If <unknown_path>\* is passed in then
774 // ERROR_PATH_NOT_FOUND will be the last error.
775 d
->handle
= ::FindFirstFileW(filename
.get(), &(d
->data
));
777 if (d
->handle
== INVALID_HANDLE_VALUE
) {
779 return ConvertWinError(GetLastError());
781 d
->firstEntry
= true;
788 ReadDir(nsDir
* aDir
, PRDirFlags aFlags
, nsString
& aName
)
791 if (NS_WARN_IF(!aDir
)) {
792 return NS_ERROR_INVALID_ARG
;
797 if (aDir
->firstEntry
) {
798 aDir
->firstEntry
= false;
801 rv
= ::FindNextFileW(aDir
->handle
, &(aDir
->data
));
808 const wchar_t* fileName
;
810 fileName
= (aDir
)->data
.cFileName
;
812 if ((aFlags
& PR_SKIP_DOT
) &&
813 (fileName
[0] == L
'.') && (fileName
[1] == L
'\0')) {
816 if ((aFlags
& PR_SKIP_DOT_DOT
) &&
817 (fileName
[0] == L
'.') && (fileName
[1] == L
'.') &&
818 (fileName
[2] == L
'\0')) {
822 DWORD attrib
= aDir
->data
.dwFileAttributes
;
823 if ((aFlags
& PR_SKIP_HIDDEN
) && (attrib
& FILE_ATTRIBUTE_HIDDEN
)) {
827 if (fileName
== tmp
.get()) {
835 DWORD err
= GetLastError();
836 return err
== ERROR_NO_MORE_FILES
? NS_OK
: ConvertWinError(err
);
840 CloseDir(nsDir
*& aDir
)
842 if (NS_WARN_IF(!aDir
)) {
843 return NS_ERROR_INVALID_ARG
;
846 BOOL isOk
= FindClose(aDir
->handle
);
847 // PR_DELETE also nulls out the passed in pointer.
849 return isOk
? NS_OK
: ConvertWinError(GetLastError());
852 //-----------------------------------------------------------------------------
854 //-----------------------------------------------------------------------------
856 class nsDirEnumerator MOZ_FINAL
857 : public nsISimpleEnumerator
858 , public nsIDirectoryEnumerator
869 nsDirEnumerator() : mDir(nullptr)
873 nsresult
Init(nsIFile
* aParent
)
875 nsAutoString filepath
;
876 aParent
->GetTarget(filepath
);
878 if (filepath
.IsEmpty()) {
879 aParent
->GetPath(filepath
);
882 if (filepath
.IsEmpty()) {
883 return NS_ERROR_UNEXPECTED
;
886 // IsDirectory is not needed here because OpenDir will return
887 // NS_ERROR_FILE_NOT_DIRECTORY if the passed in path is a file.
888 nsresult rv
= OpenDir(filepath
, &mDir
);
897 NS_IMETHOD
HasMoreElements(bool* aResult
)
900 if (!mNext
&& mDir
) {
902 rv
= ReadDir(mDir
, PR_SKIP_BOTH
, name
);
906 if (name
.IsEmpty()) {
907 // end of dir entries
908 if (NS_FAILED(CloseDir(mDir
))) {
909 return NS_ERROR_FAILURE
;
916 nsCOMPtr
<nsIFile
> file
;
917 rv
= mParent
->Clone(getter_AddRefs(file
));
922 rv
= file
->Append(name
);
927 mNext
= do_QueryInterface(file
);
929 *aResult
= mNext
!= nullptr;
936 NS_IMETHOD
GetNext(nsISupports
** aResult
)
940 rv
= HasMoreElements(&hasMore
);
945 *aResult
= mNext
; // might return nullptr
946 NS_IF_ADDREF(*aResult
);
952 NS_IMETHOD
GetNextFile(nsIFile
** aResult
)
955 bool hasMore
= false;
956 nsresult rv
= HasMoreElements(&hasMore
);
957 if (NS_FAILED(rv
) || !hasMore
) {
961 NS_IF_ADDREF(*aResult
);
969 nsresult rv
= CloseDir(mDir
);
970 NS_ASSERTION(NS_SUCCEEDED(rv
), "close failed");
972 return NS_ERROR_FAILURE
;
980 nsCOMPtr
<nsIFile
> mParent
;
981 nsCOMPtr
<nsIFile
> mNext
;
984 NS_IMPL_ISUPPORTS(nsDirEnumerator
, nsISimpleEnumerator
, nsIDirectoryEnumerator
)
987 //-----------------------------------------------------------------------------
988 // nsLocalFile <public>
989 //-----------------------------------------------------------------------------
991 nsLocalFile::nsLocalFile()
993 , mResolveDirty(true)
994 , mFollowSymlinks(false)
999 nsLocalFile::nsLocalFileConstructor(nsISupports
* aOuter
, const nsIID
& aIID
,
1000 void** aInstancePtr
)
1002 if (NS_WARN_IF(!aInstancePtr
)) {
1003 return NS_ERROR_INVALID_ARG
;
1005 if (NS_WARN_IF(aOuter
)) {
1006 return NS_ERROR_NO_AGGREGATION
;
1009 nsLocalFile
* inst
= new nsLocalFile();
1011 return NS_ERROR_OUT_OF_MEMORY
;
1014 nsresult rv
= inst
->QueryInterface(aIID
, aInstancePtr
);
1015 if (NS_FAILED(rv
)) {
1023 //-----------------------------------------------------------------------------
1024 // nsLocalFile::nsISupports
1025 //-----------------------------------------------------------------------------
1027 NS_IMPL_ISUPPORTS(nsLocalFile
,
1034 //-----------------------------------------------------------------------------
1035 // nsLocalFile <private>
1036 //-----------------------------------------------------------------------------
1038 nsLocalFile::nsLocalFile(const nsLocalFile
& aOther
)
1040 , mResolveDirty(true)
1041 , mFollowSymlinks(aOther
.mFollowSymlinks
)
1042 , mWorkingPath(aOther
.mWorkingPath
)
1046 // Resolve the shortcut file from mWorkingPath and write the path
1047 // it points to into mResolvedPath.
1049 nsLocalFile::ResolveShortcut()
1051 // we can't do anything without the resolver
1053 return NS_ERROR_FAILURE
;
1056 mResolvedPath
.SetLength(MAX_PATH
);
1057 if (mResolvedPath
.Length() != MAX_PATH
) {
1058 return NS_ERROR_OUT_OF_MEMORY
;
1061 wchar_t* resolvedPath
= wwc(mResolvedPath
.BeginWriting());
1063 // resolve this shortcut
1064 nsresult rv
= gResolver
->Resolve(mWorkingPath
.get(), resolvedPath
);
1066 size_t len
= NS_FAILED(rv
) ? 0 : wcslen(resolvedPath
);
1067 mResolvedPath
.SetLength(len
);
1072 // Resolve any shortcuts and stat the resolved path. After a successful return
1073 // the path is guaranteed valid and the members of mFileInfo64 can be used.
1075 nsLocalFile::ResolveAndStat()
1077 // if we aren't dirty then we are already done
1082 // we can't resolve/stat anything that isn't a valid NSPR addressable path
1083 if (mWorkingPath
.IsEmpty()) {
1084 return NS_ERROR_FILE_INVALID_PATH
;
1087 // this is usually correct
1088 mResolvedPath
.Assign(mWorkingPath
);
1090 // slutty hack designed to work around bug 134796 until it is fixed
1091 nsAutoString
nsprPath(mWorkingPath
.get());
1092 if (mWorkingPath
.Length() == 2 && mWorkingPath
.CharAt(1) == L
':') {
1093 nsprPath
.Append('\\');
1096 // first we will see if the working path exists. If it doesn't then
1097 // there is nothing more that can be done
1098 nsresult rv
= GetFileInfo(nsprPath
, &mFileInfo64
);
1099 if (NS_FAILED(rv
)) {
1103 // if this isn't a shortcut file or we aren't following symlinks then we're done
1104 if (!mFollowSymlinks
||
1105 mFileInfo64
.type
!= PR_FILE_FILE
||
1106 !IsShortcutPath(mWorkingPath
)) {
1108 mResolveDirty
= false;
1112 // we need to resolve this shortcut to what it points to, this will
1113 // set mResolvedPath. Even if it fails we need to have the resolved
1114 // path equal to working path for those functions that always use
1115 // the resolved path.
1116 rv
= ResolveShortcut();
1117 if (NS_FAILED(rv
)) {
1118 mResolvedPath
.Assign(mWorkingPath
);
1121 mResolveDirty
= false;
1123 // get the details of the resolved path
1124 rv
= GetFileInfo(mResolvedPath
, &mFileInfo64
);
1125 if (NS_FAILED(rv
)) {
1134 * Fills the mResolvedPath member variable with the file or symlink target
1135 * if follow symlinks is on. This is a copy of the Resolve parts from
1136 * ResolveAndStat. ResolveAndStat is much slower though because of the stat.
1138 * @return NS_OK on success.
1141 nsLocalFile::Resolve()
1143 // if we aren't dirty then we are already done
1144 if (!mResolveDirty
) {
1148 // we can't resolve/stat anything that isn't a valid NSPR addressable path
1149 if (mWorkingPath
.IsEmpty()) {
1150 return NS_ERROR_FILE_INVALID_PATH
;
1153 // this is usually correct
1154 mResolvedPath
.Assign(mWorkingPath
);
1156 // if this isn't a shortcut file or we aren't following symlinks then
1158 if (!mFollowSymlinks
||
1159 !IsShortcutPath(mWorkingPath
)) {
1160 mResolveDirty
= false;
1164 // we need to resolve this shortcut to what it points to, this will
1165 // set mResolvedPath. Even if it fails we need to have the resolved
1166 // path equal to working path for those functions that always use
1167 // the resolved path.
1168 nsresult rv
= ResolveShortcut();
1169 if (NS_FAILED(rv
)) {
1170 mResolvedPath
.Assign(mWorkingPath
);
1174 mResolveDirty
= false;
1178 //-----------------------------------------------------------------------------
1179 // nsLocalFile::nsIFile,nsILocalFile
1180 //-----------------------------------------------------------------------------
1183 nsLocalFile::Clone(nsIFile
** aFile
)
1185 // Just copy-construct ourselves
1186 *aFile
= new nsLocalFile(*this);
1188 return NS_ERROR_OUT_OF_MEMORY
;
1197 nsLocalFile::InitWithFile(nsIFile
* aFile
)
1199 if (NS_WARN_IF(!aFile
)) {
1200 return NS_ERROR_INVALID_ARG
;
1204 aFile
->GetPath(path
);
1205 if (path
.IsEmpty()) {
1206 return NS_ERROR_INVALID_ARG
;
1208 return InitWithPath(path
);
1212 nsLocalFile::InitWithPath(const nsAString
& aFilePath
)
1216 nsAString::const_iterator begin
, end
;
1217 aFilePath
.BeginReading(begin
);
1218 aFilePath
.EndReading(end
);
1220 // input string must not be empty
1222 return NS_ERROR_FAILURE
;
1225 char16_t firstChar
= *begin
;
1226 char16_t secondChar
= *(++begin
);
1228 // just do a sanity check. if it has any forward slashes, it is not a Native path
1229 // on windows. Also, it must have a colon at after the first char.
1230 if (FindCharInReadable(L
'/', begin
, end
)) {
1231 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1234 if (secondChar
!= L
':' && (secondChar
!= L
'\\' || firstChar
!= L
'\\')) {
1235 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1238 if (secondChar
== L
':') {
1239 // Make sure we have a valid drive, later code assumes the drive letter
1240 // is a single char a-z or A-Z.
1241 if (PathGetDriveNumberW(aFilePath
.Data()) == -1) {
1242 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1246 mWorkingPath
= aFilePath
;
1247 // kill any trailing '\'
1248 if (mWorkingPath
.Last() == L
'\\') {
1249 mWorkingPath
.Truncate(mWorkingPath
.Length() - 1);
1257 nsLocalFile::OpenNSPRFileDesc(int32_t aFlags
, int32_t aMode
,
1258 PRFileDesc
** aResult
)
1260 nsresult rv
= Resolve();
1261 if (NS_FAILED(rv
)) {
1265 return OpenFile(mResolvedPath
, aFlags
, aMode
, aResult
);
1270 nsLocalFile::OpenANSIFileDesc(const char* aMode
, FILE** aResult
)
1272 nsresult rv
= ResolveAndStat();
1273 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
) {
1277 *aResult
= _wfopen(mResolvedPath
.get(), NS_ConvertASCIItoUTF16(aMode
).get());
1282 return NS_ERROR_FAILURE
;
1288 nsLocalFile::Create(uint32_t aType
, uint32_t aAttributes
)
1290 if (aType
!= NORMAL_FILE_TYPE
&& aType
!= DIRECTORY_TYPE
) {
1291 return NS_ERROR_FILE_UNKNOWN_TYPE
;
1294 nsresult rv
= ResolveAndStat();
1295 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
) {
1299 // create directories to target
1301 // A given local file can be either one of these forms:
1303 // - normal: X:\some\path\on\this\drive
1306 // - UNC path: \\machine\volume\some\path\on\this\drive
1309 // Skip the first 'X:\' for the first form, and skip the first full
1310 // '\\machine\volume\' segment for the second form.
1312 wchar_t* path
= wwc(mResolvedPath
.BeginWriting());
1314 if (path
[0] == L
'\\' && path
[1] == L
'\\') {
1315 // dealing with a UNC path here; skip past '\\machine\'
1316 path
= wcschr(path
+ 2, L
'\\');
1318 return NS_ERROR_FILE_INVALID_PATH
;
1323 // search for first slash after the drive (or volume) name
1324 wchar_t* slash
= wcschr(path
, L
'\\');
1326 nsresult directoryCreateError
= NS_OK
;
1328 // skip the first '\\'
1330 slash
= wcschr(slash
, L
'\\');
1335 if (!::CreateDirectoryW(mResolvedPath
.get(), nullptr)) {
1336 rv
= ConvertWinError(GetLastError());
1337 if (NS_ERROR_FILE_NOT_FOUND
== rv
&&
1338 NS_ERROR_FILE_ACCESS_DENIED
== directoryCreateError
) {
1339 // If a previous CreateDirectory failed due to access, return that.
1340 return NS_ERROR_FILE_ACCESS_DENIED
;
1342 // perhaps the base path already exists, or perhaps we don't have
1343 // permissions to create the directory. NOTE: access denied could
1344 // occur on a parent directory even though it exists.
1345 else if (rv
!= NS_ERROR_FILE_ALREADY_EXISTS
&&
1346 rv
!= NS_ERROR_FILE_ACCESS_DENIED
) {
1350 directoryCreateError
= rv
;
1354 slash
= wcschr(slash
, L
'\\');
1358 if (aType
== NORMAL_FILE_TYPE
) {
1360 rv
= OpenFile(mResolvedPath
,
1361 PR_RDONLY
| PR_CREATE_FILE
| PR_APPEND
| PR_EXCL
,
1362 aAttributes
, &file
);
1367 if (rv
== NS_ERROR_FILE_ACCESS_DENIED
) {
1368 // need to return already-exists for directories (bug 452217)
1370 if (NS_SUCCEEDED(IsDirectory(&isdir
)) && isdir
) {
1371 rv
= NS_ERROR_FILE_ALREADY_EXISTS
;
1373 } else if (NS_ERROR_FILE_NOT_FOUND
== rv
&&
1374 NS_ERROR_FILE_ACCESS_DENIED
== directoryCreateError
) {
1375 // If a previous CreateDirectory failed due to access, return that.
1376 return NS_ERROR_FILE_ACCESS_DENIED
;
1381 if (aType
== DIRECTORY_TYPE
) {
1382 if (!::CreateDirectoryW(mResolvedPath
.get(), nullptr)) {
1383 rv
= ConvertWinError(GetLastError());
1384 if (NS_ERROR_FILE_NOT_FOUND
== rv
&&
1385 NS_ERROR_FILE_ACCESS_DENIED
== directoryCreateError
) {
1386 // If a previous CreateDirectory failed due to access, return that.
1387 return NS_ERROR_FILE_ACCESS_DENIED
;
1394 return NS_ERROR_FILE_UNKNOWN_TYPE
;
1399 nsLocalFile::Append(const nsAString
& aNode
)
1401 // append this path, multiple components are not permitted
1402 return AppendInternal(PromiseFlatString(aNode
), false);
1406 nsLocalFile::AppendRelativePath(const nsAString
& aNode
)
1408 // append this path, multiple components are permitted
1409 return AppendInternal(PromiseFlatString(aNode
), true);
1414 nsLocalFile::AppendInternal(const nsAFlatString
& aNode
,
1415 bool aMultipleComponents
)
1417 if (aNode
.IsEmpty()) {
1421 // check the relative path for validity
1422 if (aNode
.First() == L
'\\' || // can't start with an '\'
1423 aNode
.FindChar(L
'/') != kNotFound
|| // can't contain /
1424 aNode
.EqualsASCII("..")) { // can't be ..
1425 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1428 if (aMultipleComponents
) {
1429 // can't contain .. as a path component. Ensure that the valid components
1430 // "foo..foo", "..foo", and "foo.." are not falsely detected,
1431 // but the invalid paths "..\", "foo\..", "foo\..\foo",
1432 // "..\foo", etc are.
1433 NS_NAMED_LITERAL_STRING(doubleDot
, "\\..");
1434 nsAString::const_iterator start
, end
, offset
;
1435 aNode
.BeginReading(start
);
1436 aNode
.EndReading(end
);
1438 while (FindInReadable(doubleDot
, start
, offset
)) {
1439 if (offset
== end
|| *offset
== L
'\\') {
1440 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1446 // catches the remaining cases of prefixes
1447 if (StringBeginsWith(aNode
, NS_LITERAL_STRING("..\\"))) {
1448 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1451 // single components can't contain '\'
1452 else if (aNode
.FindChar(L
'\\') != kNotFound
) {
1453 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1458 mWorkingPath
.Append('\\');
1459 mWorkingPath
.Append(aNode
);
1464 #define TOUPPER(u) (((u) >= L'a' && (u) <= L'z') ? \
1465 (u) - (L'a' - L'A') : (u))
1468 nsLocalFile::Normalize()
1470 // XXX See bug 187957 comment 18 for possible problems with this implementation.
1472 if (mWorkingPath
.IsEmpty()) {
1476 nsAutoString
path(mWorkingPath
);
1478 // find the index of the root backslash for the path. Everything before
1479 // this is considered fully normalized and cannot be ascended beyond
1480 // using ".." For a local drive this is the first slash (e.g. "c:\").
1481 // For a UNC path it is the slash following the share name
1482 // (e.g. "\\server\share\").
1483 int32_t rootIdx
= 2; // default to local drive
1484 if (path
.First() == L
'\\') { // if a share then calculate the rootIdx
1485 rootIdx
= path
.FindChar(L
'\\', 2); // skip \\ in front of the server
1486 if (rootIdx
== kNotFound
) {
1487 return NS_OK
; // already normalized
1489 rootIdx
= path
.FindChar(L
'\\', rootIdx
+ 1);
1490 if (rootIdx
== kNotFound
) {
1491 return NS_OK
; // already normalized
1493 } else if (path
.CharAt(rootIdx
) != L
'\\') {
1494 // The path has been specified relative to the current working directory
1495 // for that drive. To normalize it, the current working directory for
1496 // that drive needs to be inserted before the supplied relative path
1497 // which will provide an absolute path (and the rootIdx will still be 2).
1498 WCHAR cwd
[MAX_PATH
];
1500 int drive
= TOUPPER(path
.First()) - 'A' + 1;
1501 /* We need to worry about IPH, for details read bug 419326.
1502 * _getdrives - http://msdn2.microsoft.com/en-us/library/xdhk0xd2.aspx
1503 * uses a bitmask, bit 0 is 'a:'
1504 * _chdrive - http://msdn2.microsoft.com/en-us/library/0d1409hb.aspx
1505 * _getdcwd - http://msdn2.microsoft.com/en-us/library/7t2zk3s4.aspx
1506 * take an int, 1 is 'a:'.
1508 * Because of this, we need to do some math. Subtract 1 to convert from
1509 * _chdrive/_getdcwd format to _getdrives drive numbering.
1510 * Shift left x bits to convert from integer indexing to bitfield indexing.
1511 * And of course, we need to find out if the drive is in the bitmask.
1513 * If we're really unlucky, we can still lose, but only if the user
1514 * manages to eject the drive between our call to _getdrives() and
1515 * our *calls* to _wgetdcwd.
1517 if (!((1 << (drive
- 1)) & _getdrives())) {
1518 return NS_ERROR_FILE_INVALID_PATH
;
1520 if (!_wgetdcwd(drive
, pcwd
, MAX_PATH
)) {
1521 pcwd
= _wgetdcwd(drive
, 0, 0);
1524 return NS_ERROR_OUT_OF_MEMORY
;
1526 nsAutoString
currentDir(pcwd
);
1531 if (currentDir
.Last() == '\\') {
1532 path
.Replace(0, 2, currentDir
);
1534 path
.Replace(0, 2, currentDir
+ NS_LITERAL_STRING("\\"));
1537 NS_POSTCONDITION(0 < rootIdx
&& rootIdx
< (int32_t)path
.Length(), "rootIdx is invalid");
1538 NS_POSTCONDITION(path
.CharAt(rootIdx
) == '\\', "rootIdx is invalid");
1540 // if there is nothing following the root path then it is already normalized
1541 if (rootIdx
+ 1 == (int32_t)path
.Length()) {
1546 const char16_t
* pathBuffer
= path
.get(); // simplify access to the buffer
1547 mWorkingPath
.SetCapacity(path
.Length()); // it won't ever grow longer
1548 mWorkingPath
.Assign(pathBuffer
, rootIdx
);
1550 // Normalize the path components. The actions taken are:
1552 // "\\" condense to single backslash
1553 // "." remove from path
1554 // ".." up a directory
1555 // "..." remove from path (any number of dots > 2)
1557 // The last form is something that Windows 95 and 98 supported and
1558 // is a shortcut for changing up multiple directories. Windows XP
1559 // and ilk ignore it in a path, as is done here.
1560 int32_t len
, begin
, end
= rootIdx
;
1561 while (end
< (int32_t)path
.Length()) {
1562 // find the current segment (text between the backslashes) to
1563 // be examined, this will set the following variables:
1564 // begin == index of first char in segment
1565 // end == index 1 char after last char in segment
1566 // len == length of segment
1568 end
= path
.FindChar('\\', begin
);
1569 if (end
== kNotFound
) {
1570 end
= path
.Length();
1574 // ignore double backslashes
1579 // len != 0, and interesting paths always begin with a dot
1580 if (pathBuffer
[begin
] == '.') {
1581 // ignore single dots
1586 // handle multiple dots
1587 if (len
>= 2 && pathBuffer
[begin
+ 1] == L
'.') {
1588 // back up a path component on double dot
1590 int32_t prev
= mWorkingPath
.RFindChar('\\');
1591 if (prev
>= rootIdx
) {
1592 mWorkingPath
.Truncate(prev
);
1597 // length is > 2 and the first two characters are dots.
1598 // if the rest of the string is dots, then ignore it.
1600 for (; idx
>= 2; --idx
) {
1601 if (pathBuffer
[begin
+ idx
] != L
'.') {
1606 // this is true if the loop above didn't break
1607 // and all characters in this segment are dots.
1614 // add the current component to the path, including the preceding backslash
1615 mWorkingPath
.Append(pathBuffer
+ begin
- 1, len
+ 1);
1618 // kill trailing dots and spaces.
1619 int32_t filePathLen
= mWorkingPath
.Length() - 1;
1620 while (filePathLen
> 0 && (mWorkingPath
[filePathLen
] == L
' ' ||
1621 mWorkingPath
[filePathLen
] == L
'.')) {
1622 mWorkingPath
.Truncate(filePathLen
--);
1630 nsLocalFile::GetLeafName(nsAString
& aLeafName
)
1632 aLeafName
.Truncate();
1634 if (mWorkingPath
.IsEmpty()) {
1635 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1638 int32_t offset
= mWorkingPath
.RFindChar(L
'\\');
1640 // if the working path is just a node without any lashes.
1641 if (offset
== kNotFound
) {
1642 aLeafName
= mWorkingPath
;
1644 aLeafName
= Substring(mWorkingPath
, offset
+ 1);
1651 nsLocalFile::SetLeafName(const nsAString
& aLeafName
)
1655 if (mWorkingPath
.IsEmpty()) {
1656 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
1659 // cannot use nsCString::RFindChar() due to 0x5c problem
1660 int32_t offset
= mWorkingPath
.RFindChar(L
'\\');
1662 mWorkingPath
.Truncate(offset
+ 1);
1664 mWorkingPath
.Append(aLeafName
);
1671 nsLocalFile::GetPath(nsAString
& aResult
)
1673 aResult
= mWorkingPath
;
1678 nsLocalFile::GetCanonicalPath(nsAString
& aResult
)
1681 aResult
.Assign(mShortWorkingPath
);
1692 nsLocalFile::GetVersionInfoField(const char* aField
, nsAString
& aResult
)
1694 nsresult rv
= ResolveAndStat();
1695 if (NS_FAILED(rv
)) {
1699 rv
= NS_ERROR_FAILURE
;
1702 mFollowSymlinks
? mResolvedPath
.get() : mWorkingPath
.get();
1705 DWORD size
= ::GetFileVersionInfoSizeW(path
, &dummy
);
1710 void* ver
= calloc(size
, 1);
1712 return NS_ERROR_OUT_OF_MEMORY
;
1715 if (::GetFileVersionInfoW(path
, 0, size
, ver
)) {
1716 LANGANDCODEPAGE
* translate
= nullptr;
1718 BOOL queryResult
= ::VerQueryValueW(ver
, L
"\\VarFileInfo\\Translation",
1719 (void**)&translate
, &pageCount
);
1720 if (queryResult
&& translate
) {
1721 for (int32_t i
= 0; i
< 2; ++i
) {
1722 wchar_t subBlock
[MAX_PATH
];
1723 _snwprintf(subBlock
, MAX_PATH
,
1724 L
"\\StringFileInfo\\%04x%04x\\%s",
1725 (i
== 0 ? translate
[0].wLanguage
: ::GetUserDefaultLangID()),
1726 translate
[0].wCodePage
,
1727 NS_ConvertASCIItoUTF16(
1728 nsDependentCString(aField
)).get());
1729 subBlock
[MAX_PATH
- 1] = 0;
1730 LPVOID value
= nullptr;
1732 queryResult
= ::VerQueryValueW(ver
, subBlock
, &value
, &size
);
1733 if (queryResult
&& value
) {
1734 aResult
.Assign(static_cast<char16_t
*>(value
));
1735 if (!aResult
.IsEmpty()) {
1749 nsLocalFile::SetShortcut(nsIFile
* aTargetFile
,
1750 nsIFile
* aWorkingDir
,
1751 const char16_t
* aArgs
,
1752 const char16_t
* aDescription
,
1757 nsresult rv
= this->Exists(&exists
);
1758 if (NS_FAILED(rv
)) {
1762 const WCHAR
* targetFilePath
= nullptr;
1763 const WCHAR
* workingDirPath
= nullptr;
1764 const WCHAR
* iconFilePath
= nullptr;
1766 nsAutoString targetFilePathAuto
;
1768 rv
= aTargetFile
->GetPath(targetFilePathAuto
);
1769 if (NS_FAILED(rv
)) {
1772 targetFilePath
= targetFilePathAuto
.get();
1775 nsAutoString workingDirPathAuto
;
1777 rv
= aWorkingDir
->GetPath(workingDirPathAuto
);
1778 if (NS_FAILED(rv
)) {
1781 workingDirPath
= workingDirPathAuto
.get();
1784 nsAutoString iconPathAuto
;
1786 rv
= aIconFile
->GetPath(iconPathAuto
);
1787 if (NS_FAILED(rv
)) {
1790 iconFilePath
= iconPathAuto
.get();
1793 rv
= gResolver
->SetShortcut(exists
,
1798 char16ptr_t(aDescription
),
1800 iconFilePath
? aIconIndex
: 0);
1801 if (targetFilePath
&& NS_SUCCEEDED(rv
)) {
1809 * Determines if the drive type for the specified file is rmeote or local.
1811 * @param path The path of the file to check
1812 * @param remote Out parameter, on function success holds true if the specified
1813 * file path is remote, or false if the file path is local.
1814 * @return true on success. The return value implies absolutely nothing about
1815 * wether the file is local or remote.
1818 IsRemoteFilePath(LPCWSTR aPath
, bool& aRemote
)
1820 // Obtain the parent directory path and make sure it ends with
1821 // a trailing backslash.
1822 WCHAR dirPath
[MAX_PATH
+ 1] = { 0 };
1823 wcsncpy(dirPath
, aPath
, MAX_PATH
);
1824 if (!PathRemoveFileSpecW(dirPath
)) {
1827 size_t len
= wcslen(dirPath
);
1828 // In case the dirPath holds exaclty MAX_PATH and remains unchanged, we
1829 // recheck the required length here since we need to terminate it with
1831 if (len
>= MAX_PATH
) {
1835 dirPath
[len
] = L
'\\';
1836 dirPath
[len
+ 1] = L
'\0';
1837 UINT driveType
= GetDriveTypeW(dirPath
);
1838 aRemote
= driveType
== DRIVE_REMOTE
;
1843 nsLocalFile::CopySingleFile(nsIFile
* aSourceFile
, nsIFile
* aDestParent
,
1844 const nsAString
& aNewName
, uint32_t aOptions
)
1846 nsresult rv
= NS_OK
;
1847 nsAutoString filePath
;
1849 bool move
= aOptions
& (Move
| Rename
);
1851 // get the path that we are going to copy to.
1852 // Since windows does not know how to auto
1853 // resolve shortcuts, we must work with the
1855 nsAutoString destPath
;
1856 aDestParent
->GetTarget(destPath
);
1858 destPath
.Append('\\');
1860 if (aNewName
.IsEmpty()) {
1861 nsAutoString aFileName
;
1862 aSourceFile
->GetLeafName(aFileName
);
1863 destPath
.Append(aFileName
);
1865 destPath
.Append(aNewName
);
1869 if (aOptions
& FollowSymlinks
) {
1870 rv
= aSourceFile
->GetTarget(filePath
);
1871 if (filePath
.IsEmpty()) {
1872 rv
= aSourceFile
->GetPath(filePath
);
1875 rv
= aSourceFile
->GetPath(filePath
);
1878 if (NS_FAILED(rv
)) {
1882 // Pass the flag COPY_FILE_NO_BUFFERING to CopyFileEx as we may be copying
1883 // to a SMBV2 remote drive. Without this parameter subsequent append mode
1884 // file writes can cause the resultant file to become corrupt. We only need to do
1885 // this if the major version of Windows is > 5(Only Windows Vista and above
1886 // can support SMBV2). With a 7200RPM hard drive:
1887 // Copying a 1KB file with COPY_FILE_NO_BUFFERING takes about 30-60ms.
1888 // Copying a 1KB file without COPY_FILE_NO_BUFFERING takes < 1ms.
1889 // So we only use COPY_FILE_NO_BUFFERING when we have a remote drive.
1891 DWORD dwCopyFlags
= COPY_FILE_ALLOW_DECRYPTED_DESTINATION
;
1892 if (IsVistaOrLater()) {
1893 bool path1Remote
, path2Remote
;
1894 if (!IsRemoteFilePath(filePath
.get(), path1Remote
) ||
1895 !IsRemoteFilePath(destPath
.get(), path2Remote
) ||
1896 path1Remote
|| path2Remote
) {
1897 dwCopyFlags
|= COPY_FILE_NO_BUFFERING
;
1902 copyOK
= ::CopyFileExW(filePath
.get(), destPath
.get(), nullptr,
1903 nullptr, nullptr, dwCopyFlags
);
1905 copyOK
= ::MoveFileExW(filePath
.get(), destPath
.get(),
1906 MOVEFILE_REPLACE_EXISTING
);
1908 // Check if copying the source file to a different volume,
1909 // as this could be an SMBV2 mapped drive.
1910 if (!copyOK
&& GetLastError() == ERROR_NOT_SAME_DEVICE
) {
1911 if (aOptions
& Rename
) {
1912 return NS_ERROR_FILE_ACCESS_DENIED
;
1914 copyOK
= CopyFileExW(filePath
.get(), destPath
.get(), nullptr,
1915 nullptr, nullptr, dwCopyFlags
);
1918 DeleteFileW(filePath
.get());
1923 if (!copyOK
) { // CopyFileEx and MoveFileEx return zero at failure.
1924 rv
= ConvertWinError(GetLastError());
1925 } else if (move
&& !(aOptions
& SkipNtfsAclReset
)) {
1926 // Set security permissions to inherit from parent.
1927 // Note: propagates to all children: slow for big file trees
1928 PACL pOldDACL
= nullptr;
1929 PSECURITY_DESCRIPTOR pSD
= nullptr;
1930 ::GetNamedSecurityInfoW((LPWSTR
)destPath
.get(), SE_FILE_OBJECT
,
1931 DACL_SECURITY_INFORMATION
,
1932 nullptr, nullptr, &pOldDACL
, nullptr, &pSD
);
1934 ::SetNamedSecurityInfoW((LPWSTR
)destPath
.get(), SE_FILE_OBJECT
,
1935 DACL_SECURITY_INFORMATION
|
1936 UNPROTECTED_DACL_SECURITY_INFORMATION
,
1937 nullptr, nullptr, pOldDACL
, nullptr);
1939 LocalFree((HLOCAL
)pSD
);
1947 nsLocalFile::CopyMove(nsIFile
* aParentDir
, const nsAString
& aNewName
,
1950 bool move
= aOptions
& (Move
| Rename
);
1951 bool followSymlinks
= aOptions
& FollowSymlinks
;
1953 nsCOMPtr
<nsIFile
> newParentDir
= aParentDir
;
1954 // check to see if this exists, otherwise return an error.
1955 // we will check this by resolving. If the user wants us
1956 // to follow links, then we are talking about the target,
1957 // hence we can use the |FollowSymlinks| option.
1958 nsresult rv
= ResolveAndStat();
1959 if (NS_FAILED(rv
)) {
1963 if (!newParentDir
) {
1964 // no parent was specified. We must rename.
1965 if (aNewName
.IsEmpty()) {
1966 return NS_ERROR_INVALID_ARG
;
1969 rv
= GetParent(getter_AddRefs(newParentDir
));
1970 if (NS_FAILED(rv
)) {
1975 if (!newParentDir
) {
1976 return NS_ERROR_FILE_DESTINATION_NOT_DIR
;
1979 // make sure it exists and is a directory. Create it if not there.
1981 newParentDir
->Exists(&exists
);
1983 rv
= newParentDir
->Create(DIRECTORY_TYPE
, 0644); // TODO, what permissions should we use
1984 if (NS_FAILED(rv
)) {
1989 newParentDir
->IsDirectory(&isDir
);
1991 if (followSymlinks
) {
1993 newParentDir
->IsSymlink(&isLink
);
1995 nsAutoString target
;
1996 newParentDir
->GetTarget(target
);
1998 nsCOMPtr
<nsIFile
> realDest
= new nsLocalFile();
2000 return NS_ERROR_OUT_OF_MEMORY
;
2003 rv
= realDest
->InitWithPath(target
);
2005 if (NS_FAILED(rv
)) {
2009 return CopyMove(realDest
, aNewName
, aOptions
);
2012 return NS_ERROR_FILE_DESTINATION_NOT_DIR
;
2017 // Try different ways to move/copy files/directories
2020 IsDirectory(&isDir
);
2022 IsSymlink(&isSymlink
);
2024 // Try to move the file or directory, or try to copy a single file (or non-followed symlink)
2025 if (move
|| !isDir
|| (isSymlink
&& !followSymlinks
)) {
2026 // Copy/Move single file, or move a directory
2028 aOptions
|= SkipNtfsAclReset
;
2030 rv
= CopySingleFile(this, newParentDir
, aNewName
, aOptions
);
2031 done
= NS_SUCCEEDED(rv
);
2032 // If we are moving a directory and that fails, fallback on directory
2033 // enumeration. See bug 231300 for details.
2034 if (!done
&& !(move
&& isDir
)) {
2039 // Not able to copy or move directly, so enumerate it
2041 // create a new target destination in the new parentDir;
2042 nsCOMPtr
<nsIFile
> target
;
2043 rv
= newParentDir
->Clone(getter_AddRefs(target
));
2045 if (NS_FAILED(rv
)) {
2049 nsAutoString allocatedNewName
;
2050 if (aNewName
.IsEmpty()) {
2056 int32_t offset
= temp
.RFindChar(L
'\\');
2057 if (offset
== kNotFound
) {
2058 allocatedNewName
= temp
;
2060 allocatedNewName
= Substring(temp
, offset
+ 1);
2063 GetLeafName(allocatedNewName
);// this should be the leaf name of the
2066 allocatedNewName
= aNewName
;
2069 rv
= target
->Append(allocatedNewName
);
2070 if (NS_FAILED(rv
)) {
2074 allocatedNewName
.Truncate();
2076 // check if the destination directory already exists
2077 target
->Exists(&exists
);
2079 // if the destination directory cannot be created, return an error
2080 rv
= target
->Create(DIRECTORY_TYPE
, 0644); // TODO, what permissions should we use
2081 if (NS_FAILED(rv
)) {
2085 // check if the destination directory is writable and empty
2088 target
->IsWritable(&isWritable
);
2090 return NS_ERROR_FILE_ACCESS_DENIED
;
2093 nsCOMPtr
<nsISimpleEnumerator
> targetIterator
;
2094 rv
= target
->GetDirectoryEntries(getter_AddRefs(targetIterator
));
2095 if (NS_FAILED(rv
)) {
2100 targetIterator
->HasMoreElements(&more
);
2101 // return error if target directory is not empty
2103 return NS_ERROR_FILE_DIR_NOT_EMPTY
;
2107 nsRefPtr
<nsDirEnumerator
> dirEnum
= new nsDirEnumerator();
2109 rv
= dirEnum
->Init(this);
2110 if (NS_FAILED(rv
)) {
2111 NS_WARNING("dirEnum initialization failed");
2116 while (NS_SUCCEEDED(dirEnum
->HasMoreElements(&more
)) && more
) {
2117 nsCOMPtr
<nsISupports
> item
;
2118 nsCOMPtr
<nsIFile
> file
;
2119 dirEnum
->GetNext(getter_AddRefs(item
));
2120 file
= do_QueryInterface(item
);
2124 file
->IsDirectory(&isDir
);
2125 file
->IsSymlink(&isLink
);
2128 if (followSymlinks
) {
2129 return NS_ERROR_FAILURE
;
2132 rv
= file
->MoveTo(target
, EmptyString());
2133 if (NS_FAILED(rv
)) {
2137 if (followSymlinks
) {
2138 rv
= file
->CopyToFollowingLinks(target
, EmptyString());
2140 rv
= file
->CopyTo(target
, EmptyString());
2142 if (NS_FAILED(rv
)) {
2148 // we've finished moving all the children of this directory
2149 // in the new directory. so now delete the directory
2150 // note, we don't need to do a recursive delete.
2151 // MoveTo() is recursive. At this point,
2152 // we've already moved the children of the current folder
2153 // to the new location. nothing should be left in the folder.
2155 rv
= Remove(false /* recursive */);
2156 if (NS_FAILED(rv
)) {
2163 // If we moved, we want to adjust this.
2167 nsAutoString newParentPath
;
2168 newParentDir
->GetPath(newParentPath
);
2170 if (newParentPath
.IsEmpty()) {
2171 return NS_ERROR_FAILURE
;
2174 if (aNewName
.IsEmpty()) {
2175 nsAutoString aFileName
;
2176 GetLeafName(aFileName
);
2178 InitWithPath(newParentPath
);
2181 InitWithPath(newParentPath
);
2190 nsLocalFile::CopyTo(nsIFile
* aNewParentDir
, const nsAString
& aNewName
)
2192 return CopyMove(aNewParentDir
, aNewName
, 0);
2196 nsLocalFile::CopyToFollowingLinks(nsIFile
* aNewParentDir
,
2197 const nsAString
& aNewName
)
2199 return CopyMove(aNewParentDir
, aNewName
, FollowSymlinks
);
2203 nsLocalFile::MoveTo(nsIFile
* aNewParentDir
, const nsAString
& aNewName
)
2205 return CopyMove(aNewParentDir
, aNewName
, Move
);
2209 nsLocalFile::RenameTo(nsIFile
* aNewParentDir
, const nsAString
& aNewName
)
2211 nsCOMPtr
<nsIFile
> targetParentDir
= aNewParentDir
;
2212 // check to see if this exists, otherwise return an error.
2213 // we will check this by resolving. If the user wants us
2214 // to follow links, then we are talking about the target,
2215 // hence we can use the |followSymlinks| parameter.
2216 nsresult rv
= ResolveAndStat();
2217 if (NS_FAILED(rv
)) {
2221 if (!targetParentDir
) {
2222 // no parent was specified. We must rename.
2223 if (aNewName
.IsEmpty()) {
2224 return NS_ERROR_INVALID_ARG
;
2226 rv
= GetParent(getter_AddRefs(targetParentDir
));
2227 if (NS_FAILED(rv
)) {
2232 if (!targetParentDir
) {
2233 return NS_ERROR_FILE_DESTINATION_NOT_DIR
;
2236 // make sure it exists and is a directory. Create it if not there.
2238 targetParentDir
->Exists(&exists
);
2240 rv
= targetParentDir
->Create(DIRECTORY_TYPE
, 0644);
2241 if (NS_FAILED(rv
)) {
2246 targetParentDir
->IsDirectory(&isDir
);
2248 return NS_ERROR_FILE_DESTINATION_NOT_DIR
;
2252 uint32_t options
= Rename
;
2253 if (!aNewParentDir
) {
2254 options
|= SkipNtfsAclReset
;
2256 // Move single file, or move a directory
2257 return CopySingleFile(this, targetParentDir
, aNewName
, options
);
2261 nsLocalFile::Load(PRLibrary
** aResult
)
2263 // Check we are correctly initialized.
2264 CHECK_mWorkingPath();
2267 nsresult rv
= IsFile(&isFile
);
2269 if (NS_FAILED(rv
)) {
2274 return NS_ERROR_FILE_IS_DIRECTORY
;
2277 #ifdef NS_BUILD_REFCNT_LOGGING
2278 nsTraceRefcnt::SetActivityIsLegal(false);
2282 libSpec
.value
.pathname_u
= mResolvedPath
.get();
2283 libSpec
.type
= PR_LibSpec_PathnameU
;
2284 *aResult
= PR_LoadLibraryWithFlags(libSpec
, 0);
2286 #ifdef NS_BUILD_REFCNT_LOGGING
2287 nsTraceRefcnt::SetActivityIsLegal(true);
2293 return NS_ERROR_NULL_POINTER
;
2297 nsLocalFile::Remove(bool aRecursive
)
2301 // if the working path points to a shortcut, then we will only
2302 // delete the shortcut itself. even if the shortcut points to
2303 // a directory, we will not recurse into that directory or
2304 // delete that directory itself. likewise, if the shortcut
2305 // points to a normal file, we will not delete the real file.
2306 // this is done to be consistent with the other platforms that
2307 // behave this way. we do this even if the followLinks attribute
2308 // is set to true. this helps protect against misuse that could
2309 // lead to security bugs (e.g., bug 210588).
2311 // Since shortcut files are no longer permitted to be used as unix-like
2312 // symlinks interspersed in the path (e.g. "c:/file.lnk/foo/bar.txt")
2313 // this processing is a lot simpler. Even if the shortcut file is
2314 // pointing to a directory, only the mWorkingPath value is used and so
2315 // only the shortcut file will be deleted.
2317 // Check we are correctly initialized.
2318 CHECK_mWorkingPath();
2324 rv
= IsSymlink(&isLink
);
2325 if (NS_FAILED(rv
)) {
2329 // only check to see if we have a directory if it isn't a link
2331 rv
= IsDirectory(&isDir
);
2332 if (NS_FAILED(rv
)) {
2339 nsRefPtr
<nsDirEnumerator
> dirEnum
= new nsDirEnumerator();
2341 rv
= dirEnum
->Init(this);
2342 if (NS_FAILED(rv
)) {
2347 while (NS_SUCCEEDED(dirEnum
->HasMoreElements(&more
)) && more
) {
2348 nsCOMPtr
<nsISupports
> item
;
2349 dirEnum
->GetNext(getter_AddRefs(item
));
2350 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(item
);
2352 file
->Remove(aRecursive
);
2356 if (RemoveDirectoryW(mWorkingPath
.get()) == 0) {
2357 return ConvertWinError(GetLastError());
2360 if (DeleteFileW(mWorkingPath
.get()) == 0) {
2361 return ConvertWinError(GetLastError());
2370 nsLocalFile::GetLastModifiedTime(PRTime
* aLastModifiedTime
)
2372 // Check we are correctly initialized.
2373 CHECK_mWorkingPath();
2375 if (NS_WARN_IF(!aLastModifiedTime
)) {
2376 return NS_ERROR_INVALID_ARG
;
2379 // get the modified time of the target as determined by mFollowSymlinks
2380 // If true, then this will be for the target of the shortcut file,
2381 // otherwise it will be for the shortcut file itself (i.e. the same
2382 // results as GetLastModifiedTimeOfLink)
2384 nsresult rv
= ResolveAndStat();
2385 if (NS_FAILED(rv
)) {
2389 // microseconds -> milliseconds
2390 *aLastModifiedTime
= mFileInfo64
.modifyTime
/ PR_USEC_PER_MSEC
;
2396 nsLocalFile::GetLastModifiedTimeOfLink(PRTime
* aLastModifiedTime
)
2398 // Check we are correctly initialized.
2399 CHECK_mWorkingPath();
2401 if (NS_WARN_IF(!aLastModifiedTime
)) {
2402 return NS_ERROR_INVALID_ARG
;
2405 // The caller is assumed to have already called IsSymlink
2406 // and to have found that this file is a link.
2409 nsresult rv
= GetFileInfo(mWorkingPath
, &info
);
2410 if (NS_FAILED(rv
)) {
2414 // microseconds -> milliseconds
2415 *aLastModifiedTime
= info
.modifyTime
/ PR_USEC_PER_MSEC
;
2421 nsLocalFile::SetLastModifiedTime(PRTime aLastModifiedTime
)
2423 // Check we are correctly initialized.
2424 CHECK_mWorkingPath();
2426 nsresult rv
= ResolveAndStat();
2427 if (NS_FAILED(rv
)) {
2431 // set the modified time of the target as determined by mFollowSymlinks
2432 // If true, then this will be for the target of the shortcut file,
2433 // otherwise it will be for the shortcut file itself (i.e. the same
2434 // results as SetLastModifiedTimeOfLink)
2436 rv
= SetModDate(aLastModifiedTime
, mResolvedPath
.get());
2437 if (NS_SUCCEEDED(rv
)) {
2446 nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModifiedTime
)
2448 // The caller is assumed to have already called IsSymlink
2449 // and to have found that this file is a link.
2451 nsresult rv
= SetModDate(aLastModifiedTime
, mWorkingPath
.get());
2452 if (NS_SUCCEEDED(rv
)) {
2460 nsLocalFile::SetModDate(PRTime aLastModifiedTime
, const wchar_t* aFilePath
)
2462 // The FILE_FLAG_BACKUP_SEMANTICS is required in order to change the
2463 // modification time for directories.
2464 HANDLE file
= ::CreateFileW(aFilePath
, // pointer to name of the file
2465 GENERIC_WRITE
, // access (write) mode
2467 nullptr, // pointer to security attributes
2468 OPEN_EXISTING
, // how to create
2469 FILE_FLAG_BACKUP_SEMANTICS
, // file attributes
2472 if (file
== INVALID_HANDLE_VALUE
) {
2473 return ConvertWinError(GetLastError());
2478 PRExplodedTime pret
;
2480 // PR_ExplodeTime expects usecs...
2481 PR_ExplodeTime(aLastModifiedTime
* PR_USEC_PER_MSEC
, PR_GMTParameters
, &pret
);
2482 st
.wYear
= pret
.tm_year
;
2483 st
.wMonth
= pret
.tm_month
+ 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
2484 st
.wDayOfWeek
= pret
.tm_wday
;
2485 st
.wDay
= pret
.tm_mday
;
2486 st
.wHour
= pret
.tm_hour
;
2487 st
.wMinute
= pret
.tm_min
;
2488 st
.wSecond
= pret
.tm_sec
;
2489 st
.wMilliseconds
= pret
.tm_usec
/ 1000;
2491 nsresult rv
= NS_OK
;
2492 // if at least one of these fails...
2493 if (!(SystemTimeToFileTime(&st
, &ft
) != 0 &&
2494 SetFileTime(file
, nullptr, &ft
, &ft
) != 0)) {
2495 rv
= ConvertWinError(GetLastError());
2503 nsLocalFile::GetPermissions(uint32_t* aPermissions
)
2505 if (NS_WARN_IF(!aPermissions
)) {
2506 return NS_ERROR_INVALID_ARG
;
2509 // get the permissions of the target as determined by mFollowSymlinks
2510 // If true, then this will be for the target of the shortcut file,
2511 // otherwise it will be for the shortcut file itself (i.e. the same
2512 // results as GetPermissionsOfLink)
2513 nsresult rv
= ResolveAndStat();
2514 if (NS_FAILED(rv
)) {
2518 bool isWritable
, isExecutable
;
2519 IsWritable(&isWritable
);
2520 IsExecutable(&isExecutable
);
2522 *aPermissions
= PR_IRUSR
| PR_IRGRP
| PR_IROTH
; // all read
2524 *aPermissions
|= PR_IWUSR
| PR_IWGRP
| PR_IWOTH
; // all write
2527 *aPermissions
|= PR_IXUSR
| PR_IXGRP
| PR_IXOTH
; // all execute
2534 nsLocalFile::GetPermissionsOfLink(uint32_t* aPermissions
)
2536 // Check we are correctly initialized.
2537 CHECK_mWorkingPath();
2539 if (NS_WARN_IF(!aPermissions
)) {
2540 return NS_ERROR_INVALID_ARG
;
2543 // The caller is assumed to have already called IsSymlink
2544 // and to have found that this file is a link. It is not
2545 // possible for a link file to be executable.
2547 DWORD word
= ::GetFileAttributesW(mWorkingPath
.get());
2548 if (word
== INVALID_FILE_ATTRIBUTES
) {
2549 return NS_ERROR_FILE_INVALID_PATH
;
2552 bool isWritable
= !(word
& FILE_ATTRIBUTE_READONLY
);
2553 *aPermissions
= PR_IRUSR
| PR_IRGRP
| PR_IROTH
; // all read
2555 *aPermissions
|= PR_IWUSR
| PR_IWGRP
| PR_IWOTH
; // all write
2563 nsLocalFile::SetPermissions(uint32_t aPermissions
)
2565 // Check we are correctly initialized.
2566 CHECK_mWorkingPath();
2568 // set the permissions of the target as determined by mFollowSymlinks
2569 // If true, then this will be for the target of the shortcut file,
2570 // otherwise it will be for the shortcut file itself (i.e. the same
2571 // results as SetPermissionsOfLink)
2572 nsresult rv
= ResolveAndStat();
2573 if (NS_FAILED(rv
)) {
2577 // windows only knows about the following permissions
2579 if (aPermissions
& (PR_IRUSR
| PR_IRGRP
| PR_IROTH
)) { // any read
2582 if (aPermissions
& (PR_IWUSR
| PR_IWGRP
| PR_IWOTH
)) { // any write
2586 if (_wchmod(mResolvedPath
.get(), mode
) == -1) {
2587 return NS_ERROR_FAILURE
;
2594 nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions
)
2596 // The caller is assumed to have already called IsSymlink
2597 // and to have found that this file is a link.
2599 // windows only knows about the following permissions
2601 if (aPermissions
& (PR_IRUSR
| PR_IRGRP
| PR_IROTH
)) { // any read
2604 if (aPermissions
& (PR_IWUSR
| PR_IWGRP
| PR_IWOTH
)) { // any write
2608 if (_wchmod(mWorkingPath
.get(), mode
) == -1) {
2609 return NS_ERROR_FAILURE
;
2617 nsLocalFile::GetFileSize(int64_t* aFileSize
)
2619 if (NS_WARN_IF(!aFileSize
)) {
2620 return NS_ERROR_INVALID_ARG
;
2623 nsresult rv
= ResolveAndStat();
2624 if (NS_FAILED(rv
)) {
2628 *aFileSize
= mFileInfo64
.size
;
2634 nsLocalFile::GetFileSizeOfLink(int64_t* aFileSize
)
2636 // Check we are correctly initialized.
2637 CHECK_mWorkingPath();
2639 if (NS_WARN_IF(!aFileSize
)) {
2640 return NS_ERROR_INVALID_ARG
;
2643 // The caller is assumed to have already called IsSymlink
2644 // and to have found that this file is a link.
2647 if (NS_FAILED(GetFileInfo(mWorkingPath
, &info
))) {
2648 return NS_ERROR_FILE_INVALID_PATH
;
2651 *aFileSize
= info
.size
;
2656 nsLocalFile::SetFileSize(int64_t aFileSize
)
2658 // Check we are correctly initialized.
2659 CHECK_mWorkingPath();
2661 nsresult rv
= ResolveAndStat();
2662 if (NS_FAILED(rv
)) {
2666 HANDLE hFile
= ::CreateFileW(mResolvedPath
.get(),// pointer to name of the file
2667 GENERIC_WRITE
, // access (write) mode
2668 FILE_SHARE_READ
, // share mode
2669 nullptr, // pointer to security attributes
2670 OPEN_EXISTING
, // how to create
2671 FILE_ATTRIBUTE_NORMAL
, // file attributes
2673 if (hFile
== INVALID_HANDLE_VALUE
) {
2674 return ConvertWinError(GetLastError());
2677 // seek the file pointer to the new, desired end of file
2678 // and then truncate the file at that position
2679 rv
= NS_ERROR_FAILURE
;
2680 aFileSize
= MyFileSeek64(hFile
, aFileSize
, FILE_BEGIN
);
2681 if (aFileSize
!= -1 && SetEndOfFile(hFile
)) {
2691 nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable
)
2693 // Check we are correctly initialized.
2694 CHECK_mWorkingPath();
2696 if (NS_WARN_IF(!aDiskSpaceAvailable
)) {
2697 return NS_ERROR_INVALID_ARG
;
2702 if (mFileInfo64
.type
== PR_FILE_FILE
) {
2703 // Since GetDiskFreeSpaceExW works only on directories, use the parent.
2704 nsCOMPtr
<nsIFile
> parent
;
2705 if (NS_SUCCEEDED(GetParent(getter_AddRefs(parent
))) && parent
) {
2706 return parent
->GetDiskSpaceAvailable(aDiskSpaceAvailable
);
2710 ULARGE_INTEGER liFreeBytesAvailableToCaller
, liTotalNumberOfBytes
;
2711 if (::GetDiskFreeSpaceExW(mResolvedPath
.get(), &liFreeBytesAvailableToCaller
,
2712 &liTotalNumberOfBytes
, nullptr)) {
2713 *aDiskSpaceAvailable
= liFreeBytesAvailableToCaller
.QuadPart
;
2716 *aDiskSpaceAvailable
= 0;
2721 nsLocalFile::GetParent(nsIFile
** aParent
)
2723 // Check we are correctly initialized.
2724 CHECK_mWorkingPath();
2726 if (NS_WARN_IF(!aParent
)) {
2727 return NS_ERROR_INVALID_ARG
;
2730 // A two-character path must be a drive such as C:, so it has no parent
2731 if (mWorkingPath
.Length() == 2) {
2736 int32_t offset
= mWorkingPath
.RFindChar(char16_t('\\'));
2737 // adding this offset check that was removed in bug 241708 fixes mail
2738 // directories that aren't relative to/underneath the profile dir.
2739 // e.g., on a different drive. Before you remove them, please make
2740 // sure local mail directories that aren't underneath the profile dir work.
2741 if (offset
== kNotFound
) {
2742 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
2745 // A path of the form \\NAME is a top-level path and has no parent
2746 if (offset
== 1 && mWorkingPath
[0] == L
'\\') {
2751 nsAutoString
parentPath(mWorkingPath
);
2754 parentPath
.Truncate(offset
);
2756 parentPath
.AssignLiteral("\\\\.");
2759 nsCOMPtr
<nsIFile
> localFile
;
2760 nsresult rv
= NS_NewLocalFile(parentPath
, mFollowSymlinks
,
2761 getter_AddRefs(localFile
));
2763 if (NS_FAILED(rv
)) {
2767 localFile
.forget(aParent
);
2772 nsLocalFile::Exists(bool* aResult
)
2774 // Check we are correctly initialized.
2775 CHECK_mWorkingPath();
2777 if (NS_WARN_IF(!aResult
)) {
2778 return NS_ERROR_INVALID_ARG
;
2783 nsresult rv
= ResolveAndStat();
2784 *aResult
= NS_SUCCEEDED(rv
) || rv
== NS_ERROR_FILE_IS_LOCKED
;
2790 nsLocalFile::IsWritable(bool* aIsWritable
)
2792 // Check we are correctly initialized.
2793 CHECK_mWorkingPath();
2795 // The read-only attribute on a FAT directory only means that it can't
2796 // be deleted. It is still possible to modify the contents of the directory.
2797 nsresult rv
= IsDirectory(aIsWritable
);
2798 if (rv
== NS_ERROR_FILE_ACCESS_DENIED
) {
2799 *aIsWritable
= true;
2801 } else if (rv
== NS_ERROR_FILE_IS_LOCKED
) {
2802 // If the file is normally allowed write access
2803 // we should still return that the file is writable.
2804 } else if (NS_FAILED(rv
)) {
2811 // writable if the file doesn't have the readonly attribute
2812 rv
= HasFileAttribute(FILE_ATTRIBUTE_READONLY
, aIsWritable
);
2813 if (rv
== NS_ERROR_FILE_ACCESS_DENIED
) {
2814 *aIsWritable
= false;
2816 } else if (rv
== NS_ERROR_FILE_IS_LOCKED
) {
2817 // If the file is normally allowed write access
2818 // we should still return that the file is writable.
2819 } else if (NS_FAILED(rv
)) {
2822 *aIsWritable
= !*aIsWritable
;
2824 // If the read only attribute is not set, check to make sure
2825 // we can open the file with write access.
2828 rv
= OpenFile(mResolvedPath
, PR_WRONLY
, 0, &file
);
2829 if (NS_SUCCEEDED(rv
)) {
2831 } else if (rv
== NS_ERROR_FILE_ACCESS_DENIED
) {
2832 *aIsWritable
= false;
2833 } else if (rv
== NS_ERROR_FILE_IS_LOCKED
) {
2834 // If it is locked and read only we would have
2835 // gotten access denied
2836 *aIsWritable
= true;
2845 nsLocalFile::IsReadable(bool* aResult
)
2847 // Check we are correctly initialized.
2848 CHECK_mWorkingPath();
2850 if (NS_WARN_IF(!aResult
)) {
2851 return NS_ERROR_INVALID_ARG
;
2855 nsresult rv
= ResolveAndStat();
2856 if (NS_FAILED(rv
)) {
2866 nsLocalFile::IsExecutable(bool* aResult
)
2868 // Check we are correctly initialized.
2869 CHECK_mWorkingPath();
2871 if (NS_WARN_IF(!aResult
)) {
2872 return NS_ERROR_INVALID_ARG
;
2878 // only files can be executables
2880 rv
= IsFile(&isFile
);
2881 if (NS_FAILED(rv
)) {
2888 //TODO: shouldn't we be checking mFollowSymlinks here?
2890 rv
= IsSymlink(&symLink
);
2891 if (NS_FAILED(rv
)) {
2902 // kill trailing dots and spaces.
2903 int32_t filePathLen
= path
.Length() - 1;
2904 while (filePathLen
> 0 && (path
[filePathLen
] == L
' ' ||
2905 path
[filePathLen
] == L
'.')) {
2906 path
.Truncate(filePathLen
--);
2910 int32_t dotIdx
= path
.RFindChar(char16_t('.'));
2911 if (dotIdx
!= kNotFound
) {
2912 // Convert extension to lower case.
2913 char16_t
* p
= path
.BeginWriting();
2914 for (p
+= dotIdx
+ 1; *p
; ++p
) {
2915 *p
+= (*p
>= L
'A' && *p
<= L
'Z') ? 'a' - 'A' : 0;
2918 // Search for any of the set of executable extensions.
2919 static const char* const executableExts
[] = {
2921 "ade", // access project extension
2923 "air", // Adobe AIR installer
2924 "app", // executable application
2925 "application", // from bug 348763
2935 "fxp", // FoxPro compiled app
2941 "jar", // java application bundle
2945 "mad", // Access Module Shortcut
2947 "mag", // Access Diagram Shortcut
2948 "mam", // Access Macro Shortcut
2949 "maq", // Access Query Shortcut
2950 "mar", // Access Report Shortcut
2951 "mas", // Access Stored Procedure
2952 "mat", // Access Table Shortcut
2953 "mau", // Media Attachment Unit
2954 "mav", // Access View Shortcut
2955 "maw", // Access Data Access Page
2956 "mda", // Access Add-in, MDA Access 2 Workgroup
2959 "mdt", // Access Add-in Data
2960 "mdw", // Access Workgroup Information
2961 "mdz", // Access Wizard Template
2963 "msh", // Microsoft Shell
2964 "mshxml", // Microsoft Shell
2968 "ops", // Office Profile Settings
2971 "plg", // Developer Studio Build Log
2972 "prf", // windows system file
2976 "scf", // Windows explorer command
2986 "vsmacros", // Visual Studio .NET Binary-based Macro Project
2995 nsDependentSubstring ext
= Substring(path
, dotIdx
+ 1);
2996 for (size_t i
= 0; i
< ArrayLength(executableExts
); ++i
) {
2997 if (ext
.EqualsASCII(executableExts
[i
])) {
2998 // Found a match. Set result and quit.
3010 nsLocalFile::IsDirectory(bool* aResult
)
3012 return HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY
, aResult
);
3016 nsLocalFile::IsFile(bool* aResult
)
3018 nsresult rv
= HasFileAttribute(FILE_ATTRIBUTE_DIRECTORY
, aResult
);
3019 if (NS_SUCCEEDED(rv
)) {
3020 *aResult
= !*aResult
;
3026 nsLocalFile::IsHidden(bool* aResult
)
3028 return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN
, aResult
);
3032 nsLocalFile::HasFileAttribute(DWORD aFileAttrib
, bool* aResult
)
3034 if (NS_WARN_IF(!aResult
)) {
3035 return NS_ERROR_INVALID_ARG
;
3038 nsresult rv
= Resolve();
3039 if (NS_FAILED(rv
)) {
3043 DWORD attributes
= GetFileAttributesW(mResolvedPath
.get());
3044 if (INVALID_FILE_ATTRIBUTES
== attributes
) {
3045 return ConvertWinError(GetLastError());
3048 *aResult
= ((attributes
& aFileAttrib
) != 0);
3053 nsLocalFile::IsSymlink(bool* aResult
)
3055 // Check we are correctly initialized.
3056 CHECK_mWorkingPath();
3058 if (NS_WARN_IF(!aResult
)) {
3059 return NS_ERROR_INVALID_ARG
;
3062 // unless it is a valid shortcut path it's not a symlink
3063 if (!IsShortcutPath(mWorkingPath
)) {
3068 // we need to know if this is a file or directory
3069 nsresult rv
= ResolveAndStat();
3070 if (NS_FAILED(rv
)) {
3074 // We should not check mFileInfo64.type here for PR_FILE_FILE because lnk
3075 // files can point to directories or files. Important security checks
3076 // depend on correctly identifying lnk files. mFileInfo64 now holds info
3077 // about the target of the lnk file, not the actual lnk file!
3083 nsLocalFile::IsSpecial(bool* aResult
)
3085 return HasFileAttribute(FILE_ATTRIBUTE_SYSTEM
, aResult
);
3089 nsLocalFile::Equals(nsIFile
* aInFile
, bool* aResult
)
3091 if (NS_WARN_IF(!aInFile
)) {
3092 return NS_ERROR_INVALID_ARG
;
3094 if (NS_WARN_IF(!aResult
)) {
3095 return NS_ERROR_INVALID_ARG
;
3100 nsCOMPtr
<nsILocalFileWin
> lf(do_QueryInterface(aInFile
));
3106 nsAutoString inFilePath
;
3107 lf
->GetCanonicalPath(inFilePath
);
3110 *aResult
= _wcsicmp(mShortWorkingPath
.get(), inFilePath
.get()) == 0;
3117 nsLocalFile::Contains(nsIFile
* aInFile
, bool* aResult
)
3119 // Check we are correctly initialized.
3120 CHECK_mWorkingPath();
3124 nsAutoString myFilePath
;
3125 if (NS_FAILED(GetTarget(myFilePath
))) {
3126 GetPath(myFilePath
);
3129 uint32_t myFilePathLen
= myFilePath
.Length();
3131 nsAutoString inFilePath
;
3132 if (NS_FAILED(aInFile
->GetTarget(inFilePath
))) {
3133 aInFile
->GetPath(inFilePath
);
3136 // make sure that the |aInFile|'s path has a trailing separator.
3137 if (inFilePath
.Length() >= myFilePathLen
&&
3138 inFilePath
[myFilePathLen
] == L
'\\') {
3139 if (_wcsnicmp(myFilePath
.get(), inFilePath
.get(), myFilePathLen
) == 0) {
3150 nsLocalFile::GetTarget(nsAString
& aResult
)
3153 #if STRICT_FAKE_SYMLINKS
3156 nsresult rv
= IsSymlink(&symLink
);
3157 if (NS_FAILED(rv
)) {
3162 return NS_ERROR_FILE_INVALID_PATH
;
3167 aResult
= mResolvedPath
;
3172 /* attribute bool followLinks; */
3174 nsLocalFile::GetFollowLinks(bool* aFollowLinks
)
3176 *aFollowLinks
= mFollowSymlinks
;
3180 nsLocalFile::SetFollowLinks(bool aFollowLinks
)
3183 mFollowSymlinks
= aFollowLinks
;
3189 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator
** aEntries
)
3193 *aEntries
= nullptr;
3194 if (mWorkingPath
.EqualsLiteral("\\\\.")) {
3195 nsDriveEnumerator
* drives
= new nsDriveEnumerator
;
3197 return NS_ERROR_OUT_OF_MEMORY
;
3200 rv
= drives
->Init();
3201 if (NS_FAILED(rv
)) {
3209 nsRefPtr
<nsDirEnumerator
> dirEnum
= new nsDirEnumerator();
3210 rv
= dirEnum
->Init(this);
3211 if (NS_FAILED(rv
)) {
3215 dirEnum
.forget(aEntries
);
3221 nsLocalFile::GetPersistentDescriptor(nsACString
& aPersistentDescriptor
)
3223 CopyUTF16toUTF8(mWorkingPath
, aPersistentDescriptor
);
3228 nsLocalFile::SetPersistentDescriptor(const nsACString
& aPersistentDescriptor
)
3230 if (IsUTF8(aPersistentDescriptor
)) {
3231 return InitWithPath(NS_ConvertUTF8toUTF16(aPersistentDescriptor
));
3233 return InitWithNativePath(aPersistentDescriptor
);
3237 /* attrib unsigned long fileAttributesWin; */
3239 nsLocalFile::GetFileAttributesWin(uint32_t* aAttribs
)
3242 DWORD dwAttrs
= GetFileAttributesW(mWorkingPath
.get());
3243 if (dwAttrs
== INVALID_FILE_ATTRIBUTES
) {
3244 return NS_ERROR_FILE_INVALID_PATH
;
3247 if (!(dwAttrs
& FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
)) {
3248 *aAttribs
|= WFA_SEARCH_INDEXED
;
3255 nsLocalFile::SetFileAttributesWin(uint32_t aAttribs
)
3257 DWORD dwAttrs
= GetFileAttributesW(mWorkingPath
.get());
3258 if (dwAttrs
== INVALID_FILE_ATTRIBUTES
) {
3259 return NS_ERROR_FILE_INVALID_PATH
;
3262 if (aAttribs
& WFA_SEARCH_INDEXED
) {
3263 dwAttrs
&= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
;
3265 dwAttrs
|= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
;
3268 if (aAttribs
& WFA_READONLY
) {
3269 dwAttrs
|= FILE_ATTRIBUTE_READONLY
;
3270 } else if ((aAttribs
& WFA_READWRITE
) &&
3271 (dwAttrs
& FILE_ATTRIBUTE_READONLY
)) {
3272 dwAttrs
&= ~FILE_ATTRIBUTE_READONLY
;
3275 if (SetFileAttributesW(mWorkingPath
.get(), dwAttrs
) == 0) {
3276 return NS_ERROR_FAILURE
;
3283 nsLocalFile::Reveal()
3285 // This API should be main thread only
3286 MOZ_ASSERT(NS_IsMainThread());
3288 // make sure mResolvedPath is set
3289 nsresult rv
= Resolve();
3290 if (NS_FAILED(rv
) && rv
!= NS_ERROR_FILE_NOT_FOUND
) {
3294 // To create a new thread, get the thread manager
3295 nsCOMPtr
<nsIThreadManager
> tm
= do_GetService(NS_THREADMANAGER_CONTRACTID
);
3296 nsCOMPtr
<nsIThread
> mythread
;
3297 rv
= tm
->NewThread(0, 0, getter_AddRefs(mythread
));
3298 if (NS_FAILED(rv
)) {
3302 nsCOMPtr
<nsIRunnable
> runnable
=
3303 new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::RevealOp
,
3306 // After the dispatch, the result runnable will shut down the worker
3307 // thread, so we can let it go.
3308 mythread
->Dispatch(runnable
, NS_DISPATCH_NORMAL
);
3313 nsLocalFile::Launch()
3315 // This API should be main thread only
3316 MOZ_ASSERT(NS_IsMainThread());
3318 // make sure mResolvedPath is set
3319 nsresult rv
= Resolve();
3320 if (NS_FAILED(rv
)) {
3324 // To create a new thread, get the thread manager
3325 nsCOMPtr
<nsIThreadManager
> tm
= do_GetService(NS_THREADMANAGER_CONTRACTID
);
3326 nsCOMPtr
<nsIThread
> mythread
;
3327 rv
= tm
->NewThread(0, 0, getter_AddRefs(mythread
));
3328 if (NS_FAILED(rv
)) {
3332 nsCOMPtr
<nsIRunnable
> runnable
=
3333 new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::LaunchOp
,
3336 // After the dispatch, the result runnable will shut down the worker
3337 // thread, so we can let it go.
3338 mythread
->Dispatch(runnable
, NS_DISPATCH_NORMAL
);
3343 NS_NewLocalFile(const nsAString
& aPath
, bool aFollowLinks
, nsIFile
** aResult
)
3345 nsLocalFile
* file
= new nsLocalFile();
3347 return NS_ERROR_OUT_OF_MEMORY
;
3351 file
->SetFollowLinks(aFollowLinks
);
3353 if (!aPath
.IsEmpty()) {
3354 nsresult rv
= file
->InitWithPath(aPath
);
3355 if (NS_FAILED(rv
)) {
3365 //-----------------------------------------------------------------------------
3366 // Native (lossy) interface
3367 //-----------------------------------------------------------------------------
3370 nsLocalFile::InitWithNativePath(const nsACString
& aFilePath
)
3373 nsresult rv
= NS_CopyNativeToUnicode(aFilePath
, tmp
);
3374 if (NS_SUCCEEDED(rv
)) {
3375 return InitWithPath(tmp
);
3382 nsLocalFile::AppendNative(const nsACString
& aNode
)
3385 nsresult rv
= NS_CopyNativeToUnicode(aNode
, tmp
);
3386 if (NS_SUCCEEDED(rv
)) {
3394 nsLocalFile::AppendRelativeNativePath(const nsACString
& aNode
)
3397 nsresult rv
= NS_CopyNativeToUnicode(aNode
, tmp
);
3398 if (NS_SUCCEEDED(rv
)) {
3399 return AppendRelativePath(tmp
);
3406 nsLocalFile::GetNativeLeafName(nsACString
& aLeafName
)
3408 //NS_WARNING("This API is lossy. Use GetLeafName !");
3410 nsresult rv
= GetLeafName(tmp
);
3411 if (NS_SUCCEEDED(rv
)) {
3412 rv
= NS_CopyUnicodeToNative(tmp
, aLeafName
);
3419 nsLocalFile::SetNativeLeafName(const nsACString
& aLeafName
)
3422 nsresult rv
= NS_CopyNativeToUnicode(aLeafName
, tmp
);
3423 if (NS_SUCCEEDED(rv
)) {
3424 return SetLeafName(tmp
);
3432 nsLocalFile::GetNativePath(nsACString
& aResult
)
3434 //NS_WARNING("This API is lossy. Use GetPath !");
3436 nsresult rv
= GetPath(tmp
);
3437 if (NS_SUCCEEDED(rv
)) {
3438 rv
= NS_CopyUnicodeToNative(tmp
, aResult
);
3446 nsLocalFile::GetNativeCanonicalPath(nsACString
& aResult
)
3448 NS_WARNING("This method is lossy. Use GetCanonicalPath !");
3450 NS_CopyUnicodeToNative(mShortWorkingPath
, aResult
);
3456 nsLocalFile::CopyToNative(nsIFile
* aNewParentDir
, const nsACString
& aNewName
)
3458 // Check we are correctly initialized.
3459 CHECK_mWorkingPath();
3461 if (aNewName
.IsEmpty()) {
3462 return CopyTo(aNewParentDir
, EmptyString());
3466 nsresult rv
= NS_CopyNativeToUnicode(aNewName
, tmp
);
3467 if (NS_SUCCEEDED(rv
)) {
3468 return CopyTo(aNewParentDir
, tmp
);
3475 nsLocalFile::CopyToFollowingLinksNative(nsIFile
* aNewParentDir
,
3476 const nsACString
& aNewName
)
3478 if (aNewName
.IsEmpty()) {
3479 return CopyToFollowingLinks(aNewParentDir
, EmptyString());
3483 nsresult rv
= NS_CopyNativeToUnicode(aNewName
, tmp
);
3484 if (NS_SUCCEEDED(rv
)) {
3485 return CopyToFollowingLinks(aNewParentDir
, tmp
);
3492 nsLocalFile::MoveToNative(nsIFile
* aNewParentDir
, const nsACString
& aNewName
)
3494 // Check we are correctly initialized.
3495 CHECK_mWorkingPath();
3497 if (aNewName
.IsEmpty()) {
3498 return MoveTo(aNewParentDir
, EmptyString());
3502 nsresult rv
= NS_CopyNativeToUnicode(aNewName
, tmp
);
3503 if (NS_SUCCEEDED(rv
)) {
3504 return MoveTo(aNewParentDir
, tmp
);
3511 nsLocalFile::GetNativeTarget(nsACString
& aResult
)
3513 // Check we are correctly initialized.
3514 CHECK_mWorkingPath();
3516 NS_WARNING("This API is lossy. Use GetTarget !");
3518 nsresult rv
= GetTarget(tmp
);
3519 if (NS_SUCCEEDED(rv
)) {
3520 rv
= NS_CopyUnicodeToNative(tmp
, aResult
);
3527 NS_NewNativeLocalFile(const nsACString
& aPath
, bool aFollowLinks
,
3531 nsresult rv
= NS_CopyNativeToUnicode(aPath
, buf
);
3532 if (NS_FAILED(rv
)) {
3536 return NS_NewLocalFile(buf
, aFollowLinks
, aResult
);
3540 nsLocalFile::EnsureShortPath()
3542 if (!mShortWorkingPath
.IsEmpty()) {
3546 WCHAR shortPath
[MAX_PATH
+ 1];
3547 DWORD lengthNeeded
= ::GetShortPathNameW(mWorkingPath
.get(), shortPath
,
3548 ArrayLength(shortPath
));
3549 // If an error occurred then lengthNeeded is set to 0 or the length of the
3550 // needed buffer including null termination. If it succeeds the number of
3551 // wide characters not including null termination is returned.
3552 if (lengthNeeded
!= 0 && lengthNeeded
< ArrayLength(shortPath
)) {
3553 mShortWorkingPath
.Assign(shortPath
);
3555 mShortWorkingPath
.Assign(mWorkingPath
);
3562 nsLocalFile::Equals(nsIHashable
* aOther
, bool* aResult
)
3564 nsCOMPtr
<nsIFile
> otherfile(do_QueryInterface(aOther
));
3570 return Equals(otherfile
, aResult
);
3574 nsLocalFile::GetHashCode(uint32_t* aResult
)
3576 // In order for short and long path names to hash to the same value we
3577 // always hash on the short pathname.
3580 *aResult
= HashString(mShortWorkingPath
);
3584 //-----------------------------------------------------------------------------
3585 // nsLocalFile <static members>
3586 //-----------------------------------------------------------------------------
3589 nsLocalFile::GlobalInit()
3591 DebugOnly
<nsresult
> rv
= NS_CreateShortcutResolver();
3592 NS_ASSERTION(NS_SUCCEEDED(rv
), "Shortcut resolver could not be created");
3596 nsLocalFile::GlobalShutdown()
3598 NS_DestroyShortcutResolver();
3601 NS_IMPL_ISUPPORTS(nsDriveEnumerator
, nsISimpleEnumerator
)
3603 nsDriveEnumerator::nsDriveEnumerator()
3607 nsDriveEnumerator::~nsDriveEnumerator()
3612 nsDriveEnumerator::Init()
3614 /* If the length passed to GetLogicalDriveStrings is smaller
3615 * than the length of the string it would return, it returns
3616 * the length required for the string. */
3617 DWORD length
= GetLogicalDriveStringsW(0, 0);
3618 /* The string is null terminated */
3619 if (!mDrives
.SetLength(length
+ 1, fallible_t())) {
3620 return NS_ERROR_OUT_OF_MEMORY
;
3622 if (!GetLogicalDriveStringsW(length
, wwc(mDrives
.BeginWriting()))) {
3623 return NS_ERROR_FAILURE
;
3625 mDrives
.BeginReading(mStartOfCurrentDrive
);
3626 mDrives
.EndReading(mEndOfDrivesString
);
3631 nsDriveEnumerator::HasMoreElements(bool* aHasMore
)
3633 *aHasMore
= *mStartOfCurrentDrive
!= L
'\0';
3638 nsDriveEnumerator::GetNext(nsISupports
** aNext
)
3640 /* GetLogicalDrives stored in mDrives is a concatenation
3641 * of null terminated strings, followed by a null terminator.
3642 * mStartOfCurrentDrive is an iterator pointing at the first
3643 * character of the current drive. */
3644 if (*mStartOfCurrentDrive
== L
'\0') {
3649 nsAString::const_iterator driveEnd
= mStartOfCurrentDrive
;
3650 FindCharInReadable(L
'\0', driveEnd
, mEndOfDrivesString
);
3651 nsString
drive(Substring(mStartOfCurrentDrive
, driveEnd
));
3652 mStartOfCurrentDrive
= ++driveEnd
;
3655 nsresult rv
= NS_NewLocalFile(drive
, false, &file
);