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