/src/mozilla-central/xpcom/io/nsLocalFileUnix.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | /** |
8 | | * Implementation of nsIFile for "unixy" systems. |
9 | | */ |
10 | | |
11 | | #include "mozilla/ArrayUtils.h" |
12 | | #include "mozilla/Attributes.h" |
13 | | #include "mozilla/DebugOnly.h" |
14 | | #include "mozilla/Sprintf.h" |
15 | | #include "mozilla/FilePreferences.h" |
16 | | |
17 | | #include <sys/types.h> |
18 | | #include <sys/stat.h> |
19 | | #include <unistd.h> |
20 | | #include <fcntl.h> |
21 | | #include <errno.h> |
22 | | #include <utime.h> |
23 | | #include <dirent.h> |
24 | | #include <ctype.h> |
25 | | #include <locale.h> |
26 | | |
27 | | #if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H) |
28 | | #define USE_LINUX_QUOTACTL |
29 | | #include <sys/mount.h> |
30 | | #include <sys/quota.h> |
31 | | #include <sys/sysmacros.h> |
32 | | #ifndef BLOCK_SIZE |
33 | | #define BLOCK_SIZE 1024 /* kernel block size */ |
34 | | #endif |
35 | | #endif |
36 | | |
37 | | #include "xpcom-private.h" |
38 | | #include "nsDirectoryServiceDefs.h" |
39 | | #include "nsCRT.h" |
40 | | #include "nsCOMPtr.h" |
41 | | #include "nsMemory.h" |
42 | | #include "nsIFile.h" |
43 | | #include "nsString.h" |
44 | | #include "nsReadableUtils.h" |
45 | | #include "nsLocalFile.h" |
46 | | #include "nsIComponentManager.h" |
47 | | #include "prproces.h" |
48 | | #include "nsIDirectoryEnumerator.h" |
49 | | #include "nsSimpleEnumerator.h" |
50 | | #include "private/pprio.h" |
51 | | #include "prlink.h" |
52 | | |
53 | | #ifdef MOZ_WIDGET_GTK |
54 | | #include "nsIGIOService.h" |
55 | | #endif |
56 | | |
57 | | #ifdef MOZ_WIDGET_COCOA |
58 | | #include <Carbon/Carbon.h> |
59 | | #include "CocoaFileUtils.h" |
60 | | #include "prmem.h" |
61 | | #include "plbase64.h" |
62 | | |
63 | | static nsresult MacErrorMapper(OSErr inErr); |
64 | | #endif |
65 | | |
66 | | #ifdef MOZ_WIDGET_ANDROID |
67 | | #include "GeneratedJNIWrappers.h" |
68 | | #include "nsIMIMEService.h" |
69 | | #include <linux/magic.h> |
70 | | #endif |
71 | | |
72 | | #include "nsNativeCharsetUtils.h" |
73 | | #include "nsTraceRefcnt.h" |
74 | | #include "nsHashKeys.h" |
75 | | |
76 | | using namespace mozilla; |
77 | | |
78 | | #define ENSURE_STAT_CACHE() \ |
79 | 6 | do { \ |
80 | 6 | if (!FillStatCache()) \ |
81 | 6 | return NSRESULT_FOR_ERRNO(); \ |
82 | 6 | } while(0) |
83 | | |
84 | | #define CHECK_mPath() \ |
85 | 36 | do { \ |
86 | 36 | if (mPath.IsEmpty()) \ |
87 | 36 | return NS_ERROR_NOT_INITIALIZED; \ |
88 | 36 | if (!FilePreferences::IsAllowedPath(mPath)) \ |
89 | 36 | return NS_ERROR_FILE_ACCESS_DENIED; \ |
90 | 36 | } while(0) |
91 | | |
92 | | /* directory enumerator */ |
93 | | class nsDirEnumeratorUnix final |
94 | | : public nsSimpleEnumerator |
95 | | , public nsIDirectoryEnumerator |
96 | | { |
97 | | public: |
98 | | nsDirEnumeratorUnix(); |
99 | | |
100 | | // nsISupports interface |
101 | | NS_DECL_ISUPPORTS_INHERITED |
102 | | |
103 | | // nsISimpleEnumerator interface |
104 | | NS_DECL_NSISIMPLEENUMERATOR |
105 | | |
106 | | // nsIDirectoryEnumerator interface |
107 | | NS_DECL_NSIDIRECTORYENUMERATOR |
108 | | |
109 | | NS_IMETHOD Init(nsLocalFile* aParent, bool aIgnored); |
110 | | |
111 | | NS_FORWARD_NSISIMPLEENUMERATORBASE(nsSimpleEnumerator::) |
112 | | |
113 | 0 | const nsID& DefaultInterface() override { return NS_GET_IID(nsIFile); } |
114 | | |
115 | | private: |
116 | | ~nsDirEnumeratorUnix() override; |
117 | | |
118 | | protected: |
119 | | NS_IMETHOD GetNextEntry(); |
120 | | |
121 | | DIR* mDir; |
122 | | struct dirent* mEntry; |
123 | | nsCString mParentPath; |
124 | | }; |
125 | | |
126 | | nsDirEnumeratorUnix::nsDirEnumeratorUnix() : |
127 | | mDir(nullptr), |
128 | | mEntry(nullptr) |
129 | 3 | { |
130 | 3 | } |
131 | | |
132 | | nsDirEnumeratorUnix::~nsDirEnumeratorUnix() |
133 | 3 | { |
134 | 3 | Close(); |
135 | 3 | } |
136 | | |
137 | | NS_IMPL_ISUPPORTS_INHERITED(nsDirEnumeratorUnix, nsSimpleEnumerator, |
138 | | nsIDirectoryEnumerator) |
139 | | |
140 | | NS_IMETHODIMP |
141 | | nsDirEnumeratorUnix::Init(nsLocalFile* aParent, |
142 | | bool aResolveSymlinks /*ignored*/) |
143 | 3 | { |
144 | 3 | nsAutoCString dirPath; |
145 | 3 | if (NS_FAILED(aParent->GetNativePath(dirPath)) || |
146 | 3 | dirPath.IsEmpty()) { |
147 | 0 | return NS_ERROR_FILE_INVALID_PATH; |
148 | 0 | } |
149 | 3 | |
150 | 3 | // When enumerating the directory, the paths must have a slash at the end. |
151 | 3 | nsAutoCString dirPathWithSlash(dirPath); |
152 | 3 | dirPathWithSlash.Append('/'); |
153 | 3 | if (!FilePreferences::IsAllowedPath(dirPathWithSlash)) { |
154 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
155 | 0 | } |
156 | 3 | |
157 | 3 | if (NS_FAILED(aParent->GetNativePath(mParentPath))) { |
158 | 0 | return NS_ERROR_FAILURE; |
159 | 0 | } |
160 | 3 | |
161 | 3 | mDir = opendir(dirPath.get()); |
162 | 3 | if (!mDir) { |
163 | 0 | return NSRESULT_FOR_ERRNO(); |
164 | 0 | } |
165 | 3 | return GetNextEntry(); |
166 | 3 | } |
167 | | |
168 | | NS_IMETHODIMP |
169 | | nsDirEnumeratorUnix::HasMoreElements(bool* aResult) |
170 | 0 | { |
171 | 0 | *aResult = mDir && mEntry; |
172 | 0 | if (!*aResult) { |
173 | 0 | Close(); |
174 | 0 | } |
175 | 0 | return NS_OK; |
176 | 0 | } |
177 | | |
178 | | NS_IMETHODIMP |
179 | | nsDirEnumeratorUnix::GetNext(nsISupports** aResult) |
180 | 0 | { |
181 | 0 | nsCOMPtr<nsIFile> file; |
182 | 0 | nsresult rv = GetNextFile(getter_AddRefs(file)); |
183 | 0 | if (NS_FAILED(rv)) { |
184 | 0 | return rv; |
185 | 0 | } |
186 | 0 | if (!file) { |
187 | 0 | return NS_ERROR_FAILURE; |
188 | 0 | } |
189 | 0 | file.forget(aResult); |
190 | 0 | return NS_OK; |
191 | 0 | } |
192 | | |
193 | | NS_IMETHODIMP |
194 | | nsDirEnumeratorUnix::GetNextEntry() |
195 | 6 | { |
196 | 12 | do { |
197 | 12 | errno = 0; |
198 | 12 | mEntry = readdir(mDir); |
199 | 12 | |
200 | 12 | // end of dir or error |
201 | 12 | if (!mEntry) { |
202 | 3 | return NSRESULT_FOR_ERRNO(); |
203 | 3 | } |
204 | 9 | |
205 | 9 | // keep going past "." and ".." |
206 | 9 | } while (mEntry->d_name[0] == '.' && |
207 | 9 | (mEntry->d_name[1] == '\0' || // .\0 |
208 | 6 | (mEntry->d_name[1] == '.' && |
209 | 3 | mEntry->d_name[2] == '\0'))); // ..\0 |
210 | 6 | return NS_OK; |
211 | 6 | } |
212 | | |
213 | | NS_IMETHODIMP |
214 | | nsDirEnumeratorUnix::GetNextFile(nsIFile** aResult) |
215 | 6 | { |
216 | 6 | nsresult rv; |
217 | 6 | if (!mDir || !mEntry) { |
218 | 3 | *aResult = nullptr; |
219 | 3 | return NS_OK; |
220 | 3 | } |
221 | 3 | |
222 | 3 | nsCOMPtr<nsIFile> file = new nsLocalFile(); |
223 | 3 | |
224 | 3 | if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) || |
225 | 3 | NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name)))) { |
226 | 0 | return rv; |
227 | 0 | } |
228 | 3 | |
229 | 3 | file.forget(aResult); |
230 | 3 | return GetNextEntry(); |
231 | 3 | } |
232 | | |
233 | | NS_IMETHODIMP |
234 | | nsDirEnumeratorUnix::Close() |
235 | 3 | { |
236 | 3 | if (mDir) { |
237 | 3 | closedir(mDir); |
238 | 3 | mDir = nullptr; |
239 | 3 | } |
240 | 3 | return NS_OK; |
241 | 3 | } |
242 | | |
243 | | nsLocalFile::nsLocalFile() |
244 | | : mCachedStat() |
245 | 69 | { |
246 | 69 | } |
247 | | |
248 | | nsLocalFile::nsLocalFile(const nsACString& aFilePath) |
249 | | : mCachedStat() |
250 | 0 | { |
251 | 0 | InitWithNativePath(aFilePath); |
252 | 0 | } |
253 | | |
254 | | nsLocalFile::nsLocalFile(const nsLocalFile& aOther) |
255 | | : mPath(aOther.mPath) |
256 | 104 | { |
257 | 104 | } |
258 | | |
259 | | #ifdef MOZ_WIDGET_COCOA |
260 | | NS_IMPL_ISUPPORTS(nsLocalFile, |
261 | | nsILocalFileMac, |
262 | | nsIFile, |
263 | | nsIHashable) |
264 | | #else |
265 | | NS_IMPL_ISUPPORTS(nsLocalFile, |
266 | | nsIFile, |
267 | | nsIHashable) |
268 | | #endif |
269 | | |
270 | | nsresult |
271 | | nsLocalFile::nsLocalFileConstructor(nsISupports* aOuter, |
272 | | const nsIID& aIID, |
273 | | void** aInstancePtr) |
274 | 12 | { |
275 | 12 | if (NS_WARN_IF(!aInstancePtr)) { |
276 | 0 | return NS_ERROR_INVALID_ARG; |
277 | 0 | } |
278 | 12 | if (NS_WARN_IF(aOuter)) { |
279 | 0 | return NS_ERROR_NO_AGGREGATION; |
280 | 0 | } |
281 | 12 | |
282 | 12 | *aInstancePtr = nullptr; |
283 | 12 | |
284 | 12 | nsCOMPtr<nsIFile> inst = new nsLocalFile(); |
285 | 12 | return inst->QueryInterface(aIID, aInstancePtr); |
286 | 12 | } |
287 | | |
288 | | bool |
289 | | nsLocalFile::FillStatCache() |
290 | 6 | { |
291 | 6 | if (!FilePreferences::IsAllowedPath(mPath)) { |
292 | 0 | errno = EACCES; |
293 | 0 | return false; |
294 | 0 | } |
295 | 6 | |
296 | 6 | if (STAT(mPath.get(), &mCachedStat) == -1) { |
297 | 0 | // try lstat it may be a symlink |
298 | 0 | if (LSTAT(mPath.get(), &mCachedStat) == -1) { |
299 | 0 | return false; |
300 | 0 | } |
301 | 6 | } |
302 | 6 | return true; |
303 | 6 | } |
304 | | |
305 | | NS_IMETHODIMP |
306 | | nsLocalFile::Clone(nsIFile** aFile) |
307 | 104 | { |
308 | 104 | // Just copy-construct ourselves |
309 | 104 | RefPtr<nsLocalFile> copy = new nsLocalFile(*this); |
310 | 104 | copy.forget(aFile); |
311 | 104 | return NS_OK; |
312 | 104 | } |
313 | | |
314 | | NS_IMETHODIMP |
315 | | nsLocalFile::InitWithNativePath(const nsACString& aFilePath) |
316 | 69 | { |
317 | 69 | if (aFilePath.EqualsLiteral("~") || |
318 | 69 | Substring(aFilePath, 0, 2).EqualsLiteral("~/")) { |
319 | 0 | nsCOMPtr<nsIFile> homeDir; |
320 | 0 | nsAutoCString homePath; |
321 | 0 | if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR, |
322 | 0 | getter_AddRefs(homeDir))) || |
323 | 0 | NS_FAILED(homeDir->GetNativePath(homePath))) { |
324 | 0 | return NS_ERROR_FAILURE; |
325 | 0 | } |
326 | 0 | |
327 | 0 | mPath = homePath; |
328 | 0 | if (aFilePath.Length() > 2) { |
329 | 0 | mPath.Append(Substring(aFilePath, 1, aFilePath.Length() - 1)); |
330 | 0 | } |
331 | 69 | } else { |
332 | 69 | if (aFilePath.IsEmpty() || aFilePath.First() != '/') { |
333 | 0 | return NS_ERROR_FILE_UNRECOGNIZED_PATH; |
334 | 0 | } |
335 | 69 | mPath = aFilePath; |
336 | 69 | } |
337 | 69 | |
338 | 69 | if (!FilePreferences::IsAllowedPath(mPath)) { |
339 | 0 | mPath.Truncate(); |
340 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
341 | 0 | } |
342 | 69 | |
343 | 69 | // trim off trailing slashes |
344 | 69 | ssize_t len = mPath.Length(); |
345 | 69 | while ((len > 1) && (mPath[len - 1] == '/')) { |
346 | 0 | --len; |
347 | 0 | } |
348 | 69 | mPath.SetLength(len); |
349 | 69 | |
350 | 69 | return NS_OK; |
351 | 69 | } |
352 | | |
353 | | NS_IMETHODIMP |
354 | | nsLocalFile::CreateAllAncestors(uint32_t aPermissions) |
355 | 0 | { |
356 | 0 | if (!FilePreferences::IsAllowedPath(mPath)) { |
357 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
358 | 0 | } |
359 | 0 | |
360 | 0 | // <jband> I promise to play nice |
361 | 0 | char* buffer = mPath.BeginWriting(); |
362 | 0 | char* slashp = buffer; |
363 | 0 |
|
364 | | #ifdef DEBUG_NSIFILE |
365 | | fprintf(stderr, "nsIFile: before: %s\n", buffer); |
366 | | #endif |
367 | |
|
368 | 0 | while ((slashp = strchr(slashp + 1, '/'))) { |
369 | 0 | /* |
370 | 0 | * Sequences of '/' are equivalent to a single '/'. |
371 | 0 | */ |
372 | 0 | if (slashp[1] == '/') { |
373 | 0 | continue; |
374 | 0 | } |
375 | 0 | |
376 | 0 | /* |
377 | 0 | * If the path has a trailing slash, don't make the last component, |
378 | 0 | * because we'll get EEXIST in Create when we try to build the final |
379 | 0 | * component again, and it's easier to condition the logic here than |
380 | 0 | * there. |
381 | 0 | */ |
382 | 0 | if (slashp[1] == '\0') { |
383 | 0 | break; |
384 | 0 | } |
385 | 0 | |
386 | 0 | /* Temporarily NUL-terminate here */ |
387 | 0 | *slashp = '\0'; |
388 | | #ifdef DEBUG_NSIFILE |
389 | | fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer); |
390 | | #endif |
391 | | int mkdir_result = mkdir(buffer, aPermissions); |
392 | 0 | int mkdir_errno = errno; |
393 | 0 | if (mkdir_result == -1) { |
394 | 0 | /* |
395 | 0 | * Always set |errno| to EEXIST if the dir already exists |
396 | 0 | * (we have to do this here since the errno value is not consistent |
397 | 0 | * in all cases - various reasons like different platform, |
398 | 0 | * automounter-controlled dir, etc. can affect it (see bug 125489 |
399 | 0 | * for details)). |
400 | 0 | */ |
401 | 0 | if (access(buffer, F_OK) == 0) { |
402 | 0 | mkdir_errno = EEXIST; |
403 | 0 | } |
404 | 0 | } |
405 | 0 |
|
406 | 0 | /* Put the / back before we (maybe) return */ |
407 | 0 | *slashp = '/'; |
408 | 0 |
|
409 | 0 | /* |
410 | 0 | * We could get EEXIST for an existing file -- not directory -- |
411 | 0 | * with the name of one of our ancestors, but that's OK: we'll get |
412 | 0 | * ENOTDIR when we try to make the next component in the path, |
413 | 0 | * either here on back in Create, and error out appropriately. |
414 | 0 | */ |
415 | 0 | if (mkdir_result == -1 && mkdir_errno != EEXIST) { |
416 | 0 | return nsresultForErrno(mkdir_errno); |
417 | 0 | } |
418 | 0 | } |
419 | 0 |
|
420 | | #ifdef DEBUG_NSIFILE |
421 | | fprintf(stderr, "nsIFile: after: %s\n", buffer); |
422 | | #endif |
423 | |
|
424 | 0 | return NS_OK; |
425 | 0 | } |
426 | | |
427 | | NS_IMETHODIMP |
428 | | nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode, |
429 | | PRFileDesc** aResult) |
430 | 21 | { |
431 | 21 | if (!FilePreferences::IsAllowedPath(mPath)) { |
432 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
433 | 0 | } |
434 | 21 | *aResult = PR_Open(mPath.get(), aFlags, aMode); |
435 | 21 | if (!*aResult) { |
436 | 0 | return NS_ErrorAccordingToNSPR(); |
437 | 0 | } |
438 | 21 | |
439 | 21 | if (aFlags & DELETE_ON_CLOSE) { |
440 | 0 | PR_Delete(mPath.get()); |
441 | 0 | } |
442 | 21 | |
443 | 21 | #if defined(HAVE_POSIX_FADVISE) |
444 | 21 | if (aFlags & OS_READAHEAD) { |
445 | 0 | posix_fadvise(PR_FileDesc2NativeHandle(*aResult), 0, 0, |
446 | 0 | POSIX_FADV_SEQUENTIAL); |
447 | 0 | } |
448 | 21 | #endif |
449 | 21 | return NS_OK; |
450 | 21 | } |
451 | | |
452 | | NS_IMETHODIMP |
453 | | nsLocalFile::OpenANSIFileDesc(const char* aMode, FILE** aResult) |
454 | 0 | { |
455 | 0 | if (!FilePreferences::IsAllowedPath(mPath)) { |
456 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
457 | 0 | } |
458 | 0 | *aResult = fopen(mPath.get(), aMode); |
459 | 0 | if (!*aResult) { |
460 | 0 | return NS_ERROR_FAILURE; |
461 | 0 | } |
462 | 0 | |
463 | 0 | return NS_OK; |
464 | 0 | } |
465 | | |
466 | | static int |
467 | | do_create(const char* aPath, int aFlags, mode_t aMode, PRFileDesc** aResult) |
468 | 0 | { |
469 | 0 | *aResult = PR_Open(aPath, aFlags, aMode); |
470 | 0 | return *aResult ? 0 : -1; |
471 | 0 | } |
472 | | |
473 | | static int |
474 | | do_mkdir(const char* aPath, int aFlags, mode_t aMode, PRFileDesc** aResult) |
475 | 21 | { |
476 | 21 | *aResult = nullptr; |
477 | 21 | return mkdir(aPath, aMode); |
478 | 21 | } |
479 | | |
480 | | nsresult |
481 | | nsLocalFile::CreateAndKeepOpen(uint32_t aType, int aFlags, |
482 | | uint32_t aPermissions, PRFileDesc** aResult) |
483 | 21 | { |
484 | 21 | if (!FilePreferences::IsAllowedPath(mPath)) { |
485 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
486 | 0 | } |
487 | 21 | |
488 | 21 | if (aType != NORMAL_FILE_TYPE && aType != DIRECTORY_TYPE) { |
489 | 0 | return NS_ERROR_FILE_UNKNOWN_TYPE; |
490 | 0 | } |
491 | 21 | |
492 | 21 | int (*createFunc)(const char*, int, mode_t, PRFileDesc**) = |
493 | 21 | (aType == NORMAL_FILE_TYPE) ? do_create : do_mkdir; |
494 | 21 | |
495 | 21 | int result = createFunc(mPath.get(), aFlags, aPermissions, aResult); |
496 | 21 | if (result == -1 && errno == ENOENT) { |
497 | 0 | /* |
498 | 0 | * If we failed because of missing ancestor components, try to create |
499 | 0 | * them and then retry the original creation. |
500 | 0 | * |
501 | 0 | * Ancestor directories get the same permissions as the file we're |
502 | 0 | * creating, with the X bit set for each of (user,group,other) with |
503 | 0 | * an R bit in the original permissions. If you want to do anything |
504 | 0 | * fancy like setgid or sticky bits, do it by hand. |
505 | 0 | */ |
506 | 0 | int dirperm = aPermissions; |
507 | 0 | if (aPermissions & S_IRUSR) { |
508 | 0 | dirperm |= S_IXUSR; |
509 | 0 | } |
510 | 0 | if (aPermissions & S_IRGRP) { |
511 | 0 | dirperm |= S_IXGRP; |
512 | 0 | } |
513 | 0 | if (aPermissions & S_IROTH) { |
514 | 0 | dirperm |= S_IXOTH; |
515 | 0 | } |
516 | 0 |
|
517 | | #ifdef DEBUG_NSIFILE |
518 | | fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", aPermissions, |
519 | | dirperm); |
520 | | #endif |
521 | |
|
522 | 0 | if (NS_FAILED(CreateAllAncestors(dirperm))) { |
523 | 0 | return NS_ERROR_FAILURE; |
524 | 0 | } |
525 | 0 | |
526 | | #ifdef DEBUG_NSIFILE |
527 | | fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get()); |
528 | | #endif |
529 | 0 | result = createFunc(mPath.get(), aFlags, aPermissions, aResult); |
530 | 0 | } |
531 | 21 | return NSRESULT_FOR_RETURN(result); |
532 | 21 | } |
533 | | |
534 | | NS_IMETHODIMP |
535 | | nsLocalFile::Create(uint32_t aType, uint32_t aPermissions) |
536 | 21 | { |
537 | 21 | if (!FilePreferences::IsAllowedPath(mPath)) { |
538 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
539 | 0 | } |
540 | 21 | |
541 | 21 | PRFileDesc* junk = nullptr; |
542 | 21 | nsresult rv = CreateAndKeepOpen(aType, |
543 | 21 | PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE | |
544 | 21 | PR_EXCL, |
545 | 21 | aPermissions, |
546 | 21 | &junk); |
547 | 21 | if (junk) { |
548 | 0 | PR_Close(junk); |
549 | 0 | } |
550 | 21 | return rv; |
551 | 21 | } |
552 | | |
553 | | NS_IMETHODIMP |
554 | | nsLocalFile::AppendNative(const nsACString& aFragment) |
555 | 66 | { |
556 | 66 | if (aFragment.IsEmpty()) { |
557 | 0 | return NS_OK; |
558 | 0 | } |
559 | 66 | |
560 | 66 | // only one component of path can be appended |
561 | 66 | nsACString::const_iterator begin, end; |
562 | 66 | if (FindCharInReadable('/', aFragment.BeginReading(begin), |
563 | 66 | aFragment.EndReading(end))) { |
564 | 0 | return NS_ERROR_FILE_UNRECOGNIZED_PATH; |
565 | 0 | } |
566 | 66 | |
567 | 66 | return AppendRelativeNativePath(aFragment); |
568 | 66 | } |
569 | | |
570 | | NS_IMETHODIMP |
571 | | nsLocalFile::AppendRelativeNativePath(const nsACString& aFragment) |
572 | 72 | { |
573 | 72 | if (aFragment.IsEmpty()) { |
574 | 0 | return NS_OK; |
575 | 0 | } |
576 | 72 | |
577 | 72 | // No leading '/' |
578 | 72 | if (aFragment.First() == '/') { |
579 | 0 | return NS_ERROR_FILE_UNRECOGNIZED_PATH; |
580 | 0 | } |
581 | 72 | |
582 | 72 | if (!mPath.EqualsLiteral("/")) { |
583 | 72 | mPath.Append('/'); |
584 | 72 | } |
585 | 72 | mPath.Append(aFragment); |
586 | 72 | |
587 | 72 | return NS_OK; |
588 | 72 | } |
589 | | |
590 | | NS_IMETHODIMP |
591 | | nsLocalFile::Normalize() |
592 | 0 | { |
593 | 0 | char resolved_path[PATH_MAX] = ""; |
594 | 0 | char* resolved_path_ptr = nullptr; |
595 | 0 |
|
596 | 0 | if (!FilePreferences::IsAllowedPath(mPath)) { |
597 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
598 | 0 | } |
599 | 0 | |
600 | 0 | resolved_path_ptr = realpath(mPath.get(), resolved_path); |
601 | 0 |
|
602 | 0 | // if there is an error, the return is null. |
603 | 0 | if (!resolved_path_ptr) { |
604 | 0 | return NSRESULT_FOR_ERRNO(); |
605 | 0 | } |
606 | 0 |
|
607 | 0 | mPath = resolved_path; |
608 | 0 | return NS_OK; |
609 | 0 | } |
610 | | |
611 | | void |
612 | | nsLocalFile::LocateNativeLeafName(nsACString::const_iterator& aBegin, |
613 | | nsACString::const_iterator& aEnd) |
614 | 24 | { |
615 | 24 | // XXX perhaps we should cache this?? |
616 | 24 | |
617 | 24 | mPath.BeginReading(aBegin); |
618 | 24 | mPath.EndReading(aEnd); |
619 | 24 | |
620 | 24 | nsACString::const_iterator it = aEnd; |
621 | 24 | nsACString::const_iterator stop = aBegin; |
622 | 24 | --stop; |
623 | 396 | while (--it != stop) { |
624 | 396 | if (*it == '/') { |
625 | 24 | aBegin = ++it; |
626 | 24 | return; |
627 | 24 | } |
628 | 396 | } |
629 | 24 | // else, the entire path is the leaf name (which means this |
630 | 24 | // isn't an absolute path... unexpected??) |
631 | 24 | } |
632 | | |
633 | | NS_IMETHODIMP |
634 | | nsLocalFile::GetNativeLeafName(nsACString& aLeafName) |
635 | 9 | { |
636 | 9 | nsACString::const_iterator begin, end; |
637 | 9 | LocateNativeLeafName(begin, end); |
638 | 9 | aLeafName = Substring(begin, end); |
639 | 9 | return NS_OK; |
640 | 9 | } |
641 | | |
642 | | NS_IMETHODIMP |
643 | | nsLocalFile::SetNativeLeafName(const nsACString& aLeafName) |
644 | 15 | { |
645 | 15 | nsACString::const_iterator begin, end; |
646 | 15 | LocateNativeLeafName(begin, end); |
647 | 15 | mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName); |
648 | 15 | return NS_OK; |
649 | 15 | } |
650 | | |
651 | | nsCString |
652 | | nsLocalFile::NativePath() |
653 | 394 | { |
654 | 394 | return mPath; |
655 | 394 | } |
656 | | |
657 | | nsresult |
658 | | nsIFile::GetNativePath(nsACString& aResult) |
659 | 394 | { |
660 | 394 | aResult = NativePath(); |
661 | 394 | return NS_OK; |
662 | 394 | } |
663 | | |
664 | | nsCString |
665 | | nsIFile::HumanReadablePath() |
666 | 0 | { |
667 | 0 | nsCString path; |
668 | 0 | DebugOnly<nsresult> rv = GetNativePath(path); |
669 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
670 | 0 | return path; |
671 | 0 | } |
672 | | |
673 | | nsresult |
674 | | nsLocalFile::GetNativeTargetPathName(nsIFile* aNewParent, |
675 | | const nsACString& aNewName, |
676 | | nsACString& aResult) |
677 | 0 | { |
678 | 0 | nsresult rv; |
679 | 0 | nsCOMPtr<nsIFile> oldParent; |
680 | 0 |
|
681 | 0 | if (!aNewParent) { |
682 | 0 | if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent)))) { |
683 | 0 | return rv; |
684 | 0 | } |
685 | 0 | aNewParent = oldParent.get(); |
686 | 0 | } else { |
687 | 0 | // check to see if our target directory exists |
688 | 0 | bool targetExists; |
689 | 0 | if (NS_FAILED(rv = aNewParent->Exists(&targetExists))) { |
690 | 0 | return rv; |
691 | 0 | } |
692 | 0 | |
693 | 0 | if (!targetExists) { |
694 | 0 | // XXX create the new directory with some permissions |
695 | 0 | rv = aNewParent->Create(DIRECTORY_TYPE, 0755); |
696 | 0 | if (NS_FAILED(rv)) { |
697 | 0 | return rv; |
698 | 0 | } |
699 | 0 | } else { |
700 | 0 | // make sure that the target is actually a directory |
701 | 0 | bool targetIsDirectory; |
702 | 0 | if (NS_FAILED(rv = aNewParent->IsDirectory(&targetIsDirectory))) { |
703 | 0 | return rv; |
704 | 0 | } |
705 | 0 | if (!targetIsDirectory) { |
706 | 0 | return NS_ERROR_FILE_DESTINATION_NOT_DIR; |
707 | 0 | } |
708 | 0 | } |
709 | 0 | } |
710 | 0 | |
711 | 0 | nsACString::const_iterator nameBegin, nameEnd; |
712 | 0 | if (!aNewName.IsEmpty()) { |
713 | 0 | aNewName.BeginReading(nameBegin); |
714 | 0 | aNewName.EndReading(nameEnd); |
715 | 0 | } else { |
716 | 0 | LocateNativeLeafName(nameBegin, nameEnd); |
717 | 0 | } |
718 | 0 |
|
719 | 0 | nsAutoCString dirName; |
720 | 0 | if (NS_FAILED(rv = aNewParent->GetNativePath(dirName))) { |
721 | 0 | return rv; |
722 | 0 | } |
723 | 0 | |
724 | 0 | aResult = dirName + NS_LITERAL_CSTRING("/") + Substring(nameBegin, nameEnd); |
725 | 0 | return NS_OK; |
726 | 0 | } |
727 | | |
728 | | nsresult |
729 | | nsLocalFile::CopyDirectoryTo(nsIFile* aNewParent) |
730 | 0 | { |
731 | 0 | nsresult rv; |
732 | 0 | /* |
733 | 0 | * dirCheck is used for various boolean test results such as from Equals, |
734 | 0 | * Exists, isDir, etc. |
735 | 0 | */ |
736 | 0 | bool dirCheck, isSymlink; |
737 | 0 | uint32_t oldPerms; |
738 | 0 |
|
739 | 0 | if (NS_FAILED(rv = IsDirectory(&dirCheck))) { |
740 | 0 | return rv; |
741 | 0 | } |
742 | 0 | if (!dirCheck) { |
743 | 0 | return CopyToNative(aNewParent, EmptyCString()); |
744 | 0 | } |
745 | 0 | |
746 | 0 | if (NS_FAILED(rv = Equals(aNewParent, &dirCheck))) { |
747 | 0 | return rv; |
748 | 0 | } |
749 | 0 | if (dirCheck) { |
750 | 0 | // can't copy dir to itself |
751 | 0 | return NS_ERROR_INVALID_ARG; |
752 | 0 | } |
753 | 0 | |
754 | 0 | if (NS_FAILED(rv = aNewParent->Exists(&dirCheck))) { |
755 | 0 | return rv; |
756 | 0 | } |
757 | 0 | // get the dirs old permissions |
758 | 0 | if (NS_FAILED(rv = GetPermissions(&oldPerms))) { |
759 | 0 | return rv; |
760 | 0 | } |
761 | 0 | if (!dirCheck) { |
762 | 0 | if (NS_FAILED(rv = aNewParent->Create(DIRECTORY_TYPE, oldPerms))) { |
763 | 0 | return rv; |
764 | 0 | } |
765 | 0 | } else { // dir exists lets try to use leaf |
766 | 0 | nsAutoCString leafName; |
767 | 0 | if (NS_FAILED(rv = GetNativeLeafName(leafName))) { |
768 | 0 | return rv; |
769 | 0 | } |
770 | 0 | if (NS_FAILED(rv = aNewParent->AppendNative(leafName))) { |
771 | 0 | return rv; |
772 | 0 | } |
773 | 0 | if (NS_FAILED(rv = aNewParent->Exists(&dirCheck))) { |
774 | 0 | return rv; |
775 | 0 | } |
776 | 0 | if (dirCheck) { |
777 | 0 | return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists |
778 | 0 | } |
779 | 0 | if (NS_FAILED(rv = aNewParent->Create(DIRECTORY_TYPE, oldPerms))) { |
780 | 0 | return rv; |
781 | 0 | } |
782 | 0 | } |
783 | 0 | |
784 | 0 | nsCOMPtr<nsIDirectoryEnumerator> dirIterator; |
785 | 0 | if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator)))) { |
786 | 0 | return rv; |
787 | 0 | } |
788 | 0 | |
789 | 0 | nsCOMPtr<nsIFile> entry; |
790 | 0 | while (NS_SUCCEEDED(dirIterator->GetNextFile(getter_AddRefs(entry))) && entry) { |
791 | 0 | if (NS_FAILED(rv = entry->IsSymlink(&isSymlink))) { |
792 | 0 | return rv; |
793 | 0 | } |
794 | 0 | if (NS_FAILED(rv = entry->IsDirectory(&dirCheck))) { |
795 | 0 | return rv; |
796 | 0 | } |
797 | 0 | if (dirCheck && !isSymlink) { |
798 | 0 | nsCOMPtr<nsIFile> destClone; |
799 | 0 | rv = aNewParent->Clone(getter_AddRefs(destClone)); |
800 | 0 | if (NS_SUCCEEDED(rv)) { |
801 | 0 | if (NS_FAILED(rv = entry->CopyToNative(destClone, EmptyCString()))) { |
802 | | #ifdef DEBUG |
803 | | nsresult rv2; |
804 | | nsAutoCString pathName; |
805 | | if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) { |
806 | | return rv2; |
807 | | } |
808 | | printf("Operation not supported: %s\n", pathName.get()); |
809 | | #endif |
810 | 0 | if (rv == NS_ERROR_OUT_OF_MEMORY) { |
811 | 0 | return rv; |
812 | 0 | } |
813 | 0 | continue; |
814 | 0 | } |
815 | 0 | } |
816 | 0 | } else { |
817 | 0 | if (NS_FAILED(rv = entry->CopyToNative(aNewParent, EmptyCString()))) { |
818 | | #ifdef DEBUG |
819 | | nsresult rv2; |
820 | | nsAutoCString pathName; |
821 | | if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) { |
822 | | return rv2; |
823 | | } |
824 | | printf("Operation not supported: %s\n", pathName.get()); |
825 | | #endif |
826 | 0 | if (rv == NS_ERROR_OUT_OF_MEMORY) { |
827 | 0 | return rv; |
828 | 0 | } |
829 | 0 | continue; |
830 | 0 | } |
831 | 0 | } |
832 | 0 | } |
833 | 0 | return NS_OK; |
834 | 0 | } |
835 | | |
836 | | NS_IMETHODIMP |
837 | | nsLocalFile::CopyToNative(nsIFile* aNewParent, const nsACString& aNewName) |
838 | 0 | { |
839 | 0 | nsresult rv; |
840 | 0 | // check to make sure that this has been initialized properly |
841 | 0 | CHECK_mPath(); |
842 | 0 |
|
843 | 0 | // we copy the parent here so 'aNewParent' remains immutable |
844 | 0 | nsCOMPtr <nsIFile> workParent; |
845 | 0 | if (aNewParent) { |
846 | 0 | if (NS_FAILED(rv = aNewParent->Clone(getter_AddRefs(workParent)))) { |
847 | 0 | return rv; |
848 | 0 | } |
849 | 0 | } else { |
850 | 0 | if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent)))) { |
851 | 0 | return rv; |
852 | 0 | } |
853 | 0 | } |
854 | 0 | |
855 | 0 | // check to see if we are a directory or if we are a file |
856 | 0 | bool isDirectory; |
857 | 0 | if (NS_FAILED(rv = IsDirectory(&isDirectory))) { |
858 | 0 | return rv; |
859 | 0 | } |
860 | 0 | |
861 | 0 | nsAutoCString newPathName; |
862 | 0 | if (isDirectory) { |
863 | 0 | if (!aNewName.IsEmpty()) { |
864 | 0 | if (NS_FAILED(rv = workParent->AppendNative(aNewName))) { |
865 | 0 | return rv; |
866 | 0 | } |
867 | 0 | } else { |
868 | 0 | if (NS_FAILED(rv = GetNativeLeafName(newPathName))) { |
869 | 0 | return rv; |
870 | 0 | } |
871 | 0 | if (NS_FAILED(rv = workParent->AppendNative(newPathName))) { |
872 | 0 | return rv; |
873 | 0 | } |
874 | 0 | } |
875 | 0 | if (NS_FAILED(rv = CopyDirectoryTo(workParent))) { |
876 | 0 | return rv; |
877 | 0 | } |
878 | 0 | } else { |
879 | 0 | rv = GetNativeTargetPathName(workParent, aNewName, newPathName); |
880 | 0 | if (NS_FAILED(rv)) { |
881 | 0 | return rv; |
882 | 0 | } |
883 | 0 | |
884 | | #ifdef DEBUG_blizzard |
885 | | printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get()); |
886 | | #endif |
887 | | |
888 | 0 | // actually create the file. |
889 | 0 | auto* newFile = new nsLocalFile(); |
890 | 0 | nsCOMPtr<nsIFile> fileRef(newFile); // release on exit |
891 | 0 |
|
892 | 0 | rv = newFile->InitWithNativePath(newPathName); |
893 | 0 | if (NS_FAILED(rv)) { |
894 | 0 | return rv; |
895 | 0 | } |
896 | 0 | |
897 | 0 | // get the old permissions |
898 | 0 | uint32_t myPerms; |
899 | 0 | GetPermissions(&myPerms); |
900 | 0 |
|
901 | 0 | // Create the new file with the old file's permissions, even if write |
902 | 0 | // permission is missing. We can't create with write permission and |
903 | 0 | // then change back to myPerm on all filesystems (FAT on Linux, e.g.). |
904 | 0 | // But we can write to a read-only file on all Unix filesystems if we |
905 | 0 | // open it successfully for writing. |
906 | 0 |
|
907 | 0 | PRFileDesc* newFD; |
908 | 0 | rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE, |
909 | 0 | PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, |
910 | 0 | myPerms, |
911 | 0 | &newFD); |
912 | 0 | if (NS_FAILED(rv)) { |
913 | 0 | return rv; |
914 | 0 | } |
915 | 0 | |
916 | 0 | // open the old file, too |
917 | 0 | bool specialFile; |
918 | 0 | if (NS_FAILED(rv = IsSpecial(&specialFile))) { |
919 | 0 | PR_Close(newFD); |
920 | 0 | return rv; |
921 | 0 | } |
922 | 0 | if (specialFile) { |
923 | | #ifdef DEBUG |
924 | | printf("Operation not supported: %s\n", mPath.get()); |
925 | | #endif |
926 | | // make sure to clean up properly |
927 | 0 | PR_Close(newFD); |
928 | 0 | return NS_OK; |
929 | 0 | } |
930 | 0 |
|
931 | 0 | PRFileDesc* oldFD; |
932 | 0 | rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD); |
933 | 0 | if (NS_FAILED(rv)) { |
934 | 0 | // make sure to clean up properly |
935 | 0 | PR_Close(newFD); |
936 | 0 | return rv; |
937 | 0 | } |
938 | 0 | |
939 | | #ifdef DEBUG_blizzard |
940 | | int32_t totalRead = 0; |
941 | | int32_t totalWritten = 0; |
942 | | #endif |
943 | 0 | char buf[BUFSIZ]; |
944 | 0 | int32_t bytesRead; |
945 | 0 |
|
946 | 0 | // record PR_Write() error for better error message later. |
947 | 0 | nsresult saved_write_error = NS_OK; |
948 | 0 | nsresult saved_read_error = NS_OK; |
949 | 0 | nsresult saved_read_close_error = NS_OK; |
950 | 0 | nsresult saved_write_close_error = NS_OK; |
951 | 0 |
|
952 | 0 | // DONE: Does PR_Read() return bytesRead < 0 for error? |
953 | 0 | // Yes., The errors from PR_Read are not so common and |
954 | 0 | // the value may not have correspondence in NS_ERROR_*, but |
955 | 0 | // we do catch it still, immediately after while() loop. |
956 | 0 | // We can differentiate errors pf PR_Read and PR_Write by |
957 | 0 | // looking at saved_write_error value. If PR_Write error occurs (and not |
958 | 0 | // PR_Read() error), save_write_error is not NS_OK. |
959 | 0 |
|
960 | 0 | while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) { |
961 | | #ifdef DEBUG_blizzard |
962 | | totalRead += bytesRead; |
963 | | #endif |
964 | |
|
965 | 0 | // PR_Write promises never to do a short write |
966 | 0 | int32_t bytesWritten = PR_Write(newFD, buf, bytesRead); |
967 | 0 | if (bytesWritten < 0) { |
968 | 0 | saved_write_error = NSRESULT_FOR_ERRNO(); |
969 | 0 | bytesRead = -1; |
970 | 0 | break; |
971 | 0 | } |
972 | 0 | NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?"); |
973 | 0 |
|
974 | | #ifdef DEBUG_blizzard |
975 | | totalWritten += bytesWritten; |
976 | | #endif |
977 | | } |
978 | 0 |
|
979 | 0 | // TODO/FIXME: If CIFS (and NFS?) may force read/write to return EINTR, |
980 | 0 | // we are better off to prepare for retrying. But we need confirmation if |
981 | 0 | // EINTR is returned. |
982 | 0 |
|
983 | 0 | // Record error if PR_Read() failed. |
984 | 0 | // Must be done before any other I/O which may reset errno. |
985 | 0 | if (bytesRead < 0 && saved_write_error == NS_OK) { |
986 | 0 | saved_read_error = NSRESULT_FOR_ERRNO(); |
987 | 0 | } |
988 | 0 |
|
989 | | #ifdef DEBUG_blizzard |
990 | | printf("read %d bytes, wrote %d bytes\n", |
991 | | totalRead, totalWritten); |
992 | | #endif |
993 | |
|
994 | 0 | // DONE: Errors of close can occur. Read man page of |
995 | 0 | // close(2); |
996 | 0 | // This is likely to happen if the file system is remote file |
997 | 0 | // system (NFS, CIFS, etc.) and network outage occurs. |
998 | 0 | // At least, we should tell the user that filesystem/disk is |
999 | 0 | // hosed (possibly due to network error, hard disk failure, |
1000 | 0 | // etc.) so that users can take remedial action. |
1001 | 0 |
|
1002 | 0 | // close the files |
1003 | 0 | if (PR_Close(newFD) < 0) { |
1004 | 0 | saved_write_close_error = NSRESULT_FOR_ERRNO(); |
1005 | | #if DEBUG |
1006 | | // This error merits printing. |
1007 | | fprintf(stderr, "ERROR: PR_Close(newFD) returned error. errno = %d\n", errno); |
1008 | | #endif |
1009 | | } |
1010 | 0 |
|
1011 | 0 | if (PR_Close(oldFD) < 0) { |
1012 | 0 | saved_read_close_error = NSRESULT_FOR_ERRNO(); |
1013 | | #if DEBUG |
1014 | | fprintf(stderr, "ERROR: PR_Close(oldFD) returned error. errno = %d\n", errno); |
1015 | | #endif |
1016 | | } |
1017 | 0 |
|
1018 | 0 | // Let us report the failure to write and read. |
1019 | 0 | // check for write/read error after cleaning up |
1020 | 0 | if (bytesRead < 0) { |
1021 | 0 | if (saved_write_error != NS_OK) { |
1022 | 0 | return saved_write_error; |
1023 | 0 | } |
1024 | 0 | if (saved_read_error != NS_OK) { |
1025 | 0 | return saved_read_error; |
1026 | 0 | } |
1027 | | #if DEBUG |
1028 | | MOZ_ASSERT(0); |
1029 | | #endif |
1030 | | } |
1031 | 0 |
|
1032 | 0 | if (saved_write_close_error != NS_OK) { |
1033 | 0 | return saved_write_close_error; |
1034 | 0 | } |
1035 | 0 | if (saved_read_close_error != NS_OK) { |
1036 | 0 | return saved_read_close_error; |
1037 | 0 | } |
1038 | 0 | } |
1039 | 0 | return rv; |
1040 | 0 | } |
1041 | | |
1042 | | NS_IMETHODIMP |
1043 | | nsLocalFile::CopyToFollowingLinksNative(nsIFile* aNewParent, |
1044 | | const nsACString& aNewName) |
1045 | 0 | { |
1046 | 0 | return CopyToNative(aNewParent, aNewName); |
1047 | 0 | } |
1048 | | |
1049 | | NS_IMETHODIMP |
1050 | | nsLocalFile::MoveToNative(nsIFile* aNewParent, const nsACString& aNewName) |
1051 | 0 | { |
1052 | 0 | nsresult rv; |
1053 | 0 |
|
1054 | 0 | // check to make sure that this has been initialized properly |
1055 | 0 | CHECK_mPath(); |
1056 | 0 |
|
1057 | 0 | // check to make sure that we have a new parent |
1058 | 0 | nsAutoCString newPathName; |
1059 | 0 | rv = GetNativeTargetPathName(aNewParent, aNewName, newPathName); |
1060 | 0 | if (NS_FAILED(rv)) { |
1061 | 0 | return rv; |
1062 | 0 | } |
1063 | 0 | |
1064 | 0 | if (!FilePreferences::IsAllowedPath(newPathName)) { |
1065 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
1066 | 0 | } |
1067 | 0 | |
1068 | 0 | // try for atomic rename, falling back to copy/delete |
1069 | 0 | if (rename(mPath.get(), newPathName.get()) < 0) { |
1070 | 0 | if (errno == EXDEV) { |
1071 | 0 | rv = CopyToNative(aNewParent, aNewName); |
1072 | 0 | if (NS_SUCCEEDED(rv)) { |
1073 | 0 | rv = Remove(true); |
1074 | 0 | } |
1075 | 0 | } else { |
1076 | 0 | rv = NSRESULT_FOR_ERRNO(); |
1077 | 0 | } |
1078 | 0 | } |
1079 | 0 |
|
1080 | 0 | if (NS_SUCCEEDED(rv)) { |
1081 | 0 | // Adjust this |
1082 | 0 | mPath = newPathName; |
1083 | 0 | } |
1084 | 0 | return rv; |
1085 | 0 | } |
1086 | | |
1087 | | NS_IMETHODIMP |
1088 | | nsLocalFile::Remove(bool aRecursive) |
1089 | 0 | { |
1090 | 0 | CHECK_mPath(); |
1091 | 0 | ENSURE_STAT_CACHE(); |
1092 | 0 |
|
1093 | 0 | bool isSymLink; |
1094 | 0 |
|
1095 | 0 | nsresult rv = IsSymlink(&isSymLink); |
1096 | 0 | if (NS_FAILED(rv)) { |
1097 | 0 | return rv; |
1098 | 0 | } |
1099 | 0 | |
1100 | 0 | if (isSymLink || !S_ISDIR(mCachedStat.st_mode)) { |
1101 | 0 | return NSRESULT_FOR_RETURN(unlink(mPath.get())); |
1102 | 0 | } |
1103 | 0 |
|
1104 | 0 | if (aRecursive) { |
1105 | 0 | auto* dir = new nsDirEnumeratorUnix(); |
1106 | 0 |
|
1107 | 0 | RefPtr<nsSimpleEnumerator> dirRef(dir); // release on exit |
1108 | 0 |
|
1109 | 0 | rv = dir->Init(this, false); |
1110 | 0 | if (NS_FAILED(rv)) { |
1111 | 0 | return rv; |
1112 | 0 | } |
1113 | 0 | |
1114 | 0 | bool more; |
1115 | 0 | while (NS_SUCCEEDED(dir->HasMoreElements(&more)) && more) { |
1116 | 0 | nsCOMPtr<nsISupports> item; |
1117 | 0 | rv = dir->GetNext(getter_AddRefs(item)); |
1118 | 0 | if (NS_FAILED(rv)) { |
1119 | 0 | return NS_ERROR_FAILURE; |
1120 | 0 | } |
1121 | 0 | |
1122 | 0 | nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv); |
1123 | 0 | if (NS_FAILED(rv)) { |
1124 | 0 | return NS_ERROR_FAILURE; |
1125 | 0 | } |
1126 | 0 | rv = file->Remove(aRecursive); |
1127 | 0 |
|
1128 | | #ifdef ANDROID |
1129 | | // See bug 580434 - Bionic gives us just deleted files |
1130 | | if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) { |
1131 | | continue; |
1132 | | } |
1133 | | #endif |
1134 | 0 | if (NS_FAILED(rv)) { |
1135 | 0 | return rv; |
1136 | 0 | } |
1137 | 0 | } |
1138 | 0 | } |
1139 | 0 |
|
1140 | 0 | return NSRESULT_FOR_RETURN(rmdir(mPath.get())); |
1141 | 0 | } |
1142 | | |
1143 | | NS_IMETHODIMP |
1144 | | nsLocalFile::GetLastModifiedTime(PRTime* aLastModTime) |
1145 | 0 | { |
1146 | 0 | CHECK_mPath(); |
1147 | 0 | if (NS_WARN_IF(!aLastModTime)) { |
1148 | 0 | return NS_ERROR_INVALID_ARG; |
1149 | 0 | } |
1150 | 0 | |
1151 | 0 | PRFileInfo64 info; |
1152 | 0 | if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS) { |
1153 | 0 | return NSRESULT_FOR_ERRNO(); |
1154 | 0 | } |
1155 | 0 | PRTime modTime = info.modifyTime; |
1156 | 0 | if (modTime == 0) { |
1157 | 0 | *aLastModTime = 0; |
1158 | 0 | } else { |
1159 | 0 | *aLastModTime = modTime / PR_USEC_PER_MSEC; |
1160 | 0 | } |
1161 | 0 |
|
1162 | 0 | return NS_OK; |
1163 | 0 | } |
1164 | | |
1165 | | NS_IMETHODIMP |
1166 | | nsLocalFile::SetLastModifiedTime(PRTime aLastModTime) |
1167 | 0 | { |
1168 | 0 | CHECK_mPath(); |
1169 | 0 |
|
1170 | 0 | int result; |
1171 | 0 | if (aLastModTime != 0) { |
1172 | 0 | ENSURE_STAT_CACHE(); |
1173 | 0 | struct utimbuf ut; |
1174 | 0 | ut.actime = mCachedStat.st_atime; |
1175 | 0 |
|
1176 | 0 | // convert milliseconds to seconds since the unix epoch |
1177 | 0 | ut.modtime = (time_t)(aLastModTime / PR_MSEC_PER_SEC); |
1178 | 0 | result = utime(mPath.get(), &ut); |
1179 | 0 | } else { |
1180 | 0 | result = utime(mPath.get(), nullptr); |
1181 | 0 | } |
1182 | 0 | return NSRESULT_FOR_RETURN(result); |
1183 | 0 | } |
1184 | | |
1185 | | NS_IMETHODIMP |
1186 | | nsLocalFile::GetLastModifiedTimeOfLink(PRTime* aLastModTimeOfLink) |
1187 | 0 | { |
1188 | 0 | CHECK_mPath(); |
1189 | 0 | if (NS_WARN_IF(!aLastModTimeOfLink)) { |
1190 | 0 | return NS_ERROR_INVALID_ARG; |
1191 | 0 | } |
1192 | 0 | |
1193 | 0 | struct STAT sbuf; |
1194 | 0 | if (LSTAT(mPath.get(), &sbuf) == -1) { |
1195 | 0 | return NSRESULT_FOR_ERRNO(); |
1196 | 0 | } |
1197 | 0 | *aLastModTimeOfLink = PRTime(sbuf.st_mtime) * PR_MSEC_PER_SEC; |
1198 | 0 |
|
1199 | 0 | return NS_OK; |
1200 | 0 | } |
1201 | | |
1202 | | /* |
1203 | | * utime(2) may or may not dereference symlinks, joy. |
1204 | | */ |
1205 | | NS_IMETHODIMP |
1206 | | nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink) |
1207 | 0 | { |
1208 | 0 | return SetLastModifiedTime(aLastModTimeOfLink); |
1209 | 0 | } |
1210 | | |
1211 | | /* |
1212 | | * Only send back permissions bits: maybe we want to send back the whole |
1213 | | * mode_t to permit checks against other file types? |
1214 | | */ |
1215 | | |
1216 | 0 | #define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO)) |
1217 | | |
1218 | | NS_IMETHODIMP |
1219 | | nsLocalFile::GetPermissions(uint32_t* aPermissions) |
1220 | 0 | { |
1221 | 0 | if (NS_WARN_IF(!aPermissions)) { |
1222 | 0 | return NS_ERROR_INVALID_ARG; |
1223 | 0 | } |
1224 | 0 | ENSURE_STAT_CACHE(); |
1225 | 0 | *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode); |
1226 | 0 | return NS_OK; |
1227 | 0 | } |
1228 | | |
1229 | | NS_IMETHODIMP |
1230 | | nsLocalFile::GetPermissionsOfLink(uint32_t* aPermissionsOfLink) |
1231 | 0 | { |
1232 | 0 | CHECK_mPath(); |
1233 | 0 | if (NS_WARN_IF(!aPermissionsOfLink)) { |
1234 | 0 | return NS_ERROR_INVALID_ARG; |
1235 | 0 | } |
1236 | 0 | |
1237 | 0 | struct STAT sbuf; |
1238 | 0 | if (LSTAT(mPath.get(), &sbuf) == -1) { |
1239 | 0 | return NSRESULT_FOR_ERRNO(); |
1240 | 0 | } |
1241 | 0 | *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode); |
1242 | 0 | return NS_OK; |
1243 | 0 | } |
1244 | | |
1245 | | NS_IMETHODIMP |
1246 | | nsLocalFile::SetPermissions(uint32_t aPermissions) |
1247 | 0 | { |
1248 | 0 | CHECK_mPath(); |
1249 | 0 |
|
1250 | 0 | /* |
1251 | 0 | * Race condition here: we should use fchmod instead, there's no way to |
1252 | 0 | * guarantee the name still refers to the same file. |
1253 | 0 | */ |
1254 | 0 | if (chmod(mPath.get(), aPermissions) >= 0) { |
1255 | 0 | return NS_OK; |
1256 | 0 | } |
1257 | | #if defined(ANDROID) && defined(STATFS) |
1258 | | // For the time being, this is restricted for use by Android, but we |
1259 | | // will figure out what to do for all platforms in bug 638503 |
1260 | | struct STATFS sfs; |
1261 | | if (STATFS(mPath.get(), &sfs) < 0) { |
1262 | | return NSRESULT_FOR_ERRNO(); |
1263 | | } |
1264 | | |
1265 | | // if this is a FAT file system we can't set file permissions |
1266 | | if (sfs.f_type == MSDOS_SUPER_MAGIC) { |
1267 | | return NS_OK; |
1268 | | } |
1269 | | #endif |
1270 | 0 | return NSRESULT_FOR_ERRNO(); |
1271 | 0 | } |
1272 | | |
1273 | | NS_IMETHODIMP |
1274 | | nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions) |
1275 | 0 | { |
1276 | 0 | // There isn't a consistent mechanism for doing this on UNIX platforms. We |
1277 | 0 | // might want to carefully implement this in the future though. |
1278 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
1279 | 0 | } |
1280 | | |
1281 | | NS_IMETHODIMP |
1282 | | nsLocalFile::GetFileSize(int64_t* aFileSize) |
1283 | 0 | { |
1284 | 0 | if (NS_WARN_IF(!aFileSize)) { |
1285 | 0 | return NS_ERROR_INVALID_ARG; |
1286 | 0 | } |
1287 | 0 | *aFileSize = 0; |
1288 | 0 | ENSURE_STAT_CACHE(); |
1289 | 0 |
|
1290 | 0 | if (!S_ISDIR(mCachedStat.st_mode)) { |
1291 | 0 | *aFileSize = (int64_t)mCachedStat.st_size; |
1292 | 0 | } |
1293 | 0 | return NS_OK; |
1294 | 0 | } |
1295 | | |
1296 | | NS_IMETHODIMP |
1297 | | nsLocalFile::SetFileSize(int64_t aFileSize) |
1298 | 0 | { |
1299 | 0 | CHECK_mPath(); |
1300 | 0 |
|
1301 | | #if defined(ANDROID) |
1302 | | /* no truncate on bionic */ |
1303 | | int fd = open(mPath.get(), O_WRONLY); |
1304 | | if (fd == -1) { |
1305 | | return NSRESULT_FOR_ERRNO(); |
1306 | | } |
1307 | | |
1308 | | int ret = ftruncate(fd, (off_t)aFileSize); |
1309 | | close(fd); |
1310 | | |
1311 | | if (ret == -1) { |
1312 | | return NSRESULT_FOR_ERRNO(); |
1313 | | } |
1314 | | #elif defined(HAVE_TRUNCATE64) |
1315 | 0 | if (truncate64(mPath.get(), (off64_t)aFileSize) == -1) { |
1316 | 0 | return NSRESULT_FOR_ERRNO(); |
1317 | 0 | } |
1318 | | #else |
1319 | | off_t size = (off_t)aFileSize; |
1320 | | if (truncate(mPath.get(), size) == -1) { |
1321 | | return NSRESULT_FOR_ERRNO(); |
1322 | | } |
1323 | | #endif |
1324 | 0 | return NS_OK; |
1325 | 0 | } |
1326 | | |
1327 | | NS_IMETHODIMP |
1328 | | nsLocalFile::GetFileSizeOfLink(int64_t* aFileSize) |
1329 | 0 | { |
1330 | 0 | CHECK_mPath(); |
1331 | 0 | if (NS_WARN_IF(!aFileSize)) { |
1332 | 0 | return NS_ERROR_INVALID_ARG; |
1333 | 0 | } |
1334 | 0 | |
1335 | 0 | struct STAT sbuf; |
1336 | 0 | if (LSTAT(mPath.get(), &sbuf) == -1) { |
1337 | 0 | return NSRESULT_FOR_ERRNO(); |
1338 | 0 | } |
1339 | 0 |
|
1340 | 0 | *aFileSize = (int64_t)sbuf.st_size; |
1341 | 0 | return NS_OK; |
1342 | 0 | } |
1343 | | |
1344 | | #if defined(USE_LINUX_QUOTACTL) |
1345 | | /* |
1346 | | * Searches /proc/self/mountinfo for given device (Major:Minor), |
1347 | | * returns exported name from /dev |
1348 | | * |
1349 | | * Fails when /proc/self/mountinfo or diven device don't exist. |
1350 | | */ |
1351 | | static bool |
1352 | | GetDeviceName(unsigned int aDeviceMajor, unsigned int aDeviceMinor, |
1353 | | nsACString& aDeviceName) |
1354 | 0 | { |
1355 | 0 | bool ret = false; |
1356 | 0 |
|
1357 | 0 | const int kMountInfoLineLength = 200; |
1358 | 0 | const int kMountInfoDevPosition = 6; |
1359 | 0 |
|
1360 | 0 | char mountinfoLine[kMountInfoLineLength]; |
1361 | 0 | char deviceNum[kMountInfoLineLength]; |
1362 | 0 |
|
1363 | 0 | SprintfLiteral(deviceNum, "%u:%u", aDeviceMajor, aDeviceMinor); |
1364 | 0 |
|
1365 | 0 | FILE* f = fopen("/proc/self/mountinfo", "rt"); |
1366 | 0 | if (!f) { |
1367 | 0 | return ret; |
1368 | 0 | } |
1369 | 0 | |
1370 | 0 | // Expects /proc/self/mountinfo in format: |
1371 | 0 | // 'ID ID major:minor root mountpoint flags - type devicename flags' |
1372 | 0 | while (fgets(mountinfoLine, kMountInfoLineLength, f)) { |
1373 | 0 | char* p_dev = strstr(mountinfoLine, deviceNum); |
1374 | 0 |
|
1375 | 0 | for (int i = 0; i < kMountInfoDevPosition && p_dev; ++i) { |
1376 | 0 | p_dev = strchr(p_dev, ' '); |
1377 | 0 | if (p_dev) { |
1378 | 0 | p_dev++; |
1379 | 0 | } |
1380 | 0 | } |
1381 | 0 |
|
1382 | 0 | if (p_dev) { |
1383 | 0 | char* p_dev_end = strchr(p_dev, ' '); |
1384 | 0 | if (p_dev_end) { |
1385 | 0 | *p_dev_end = '\0'; |
1386 | 0 | aDeviceName.Assign(p_dev); |
1387 | 0 | ret = true; |
1388 | 0 | break; |
1389 | 0 | } |
1390 | 0 | } |
1391 | 0 | } |
1392 | 0 |
|
1393 | 0 | fclose(f); |
1394 | 0 | return ret; |
1395 | 0 | } |
1396 | | #endif |
1397 | | |
1398 | | NS_IMETHODIMP |
1399 | | nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable) |
1400 | 0 | { |
1401 | 0 | if (NS_WARN_IF(!aDiskSpaceAvailable)) { |
1402 | 0 | return NS_ERROR_INVALID_ARG; |
1403 | 0 | } |
1404 | 0 | |
1405 | 0 | // These systems have the operations necessary to check disk space. |
1406 | 0 | |
1407 | 0 | #ifdef STATFS |
1408 | 0 | |
1409 | 0 | // check to make sure that mPath is properly initialized |
1410 | 0 | CHECK_mPath(); |
1411 | 0 |
|
1412 | 0 | struct STATFS fs_buf; |
1413 | 0 |
|
1414 | 0 | /* |
1415 | 0 | * Members of the STATFS struct that you should know about: |
1416 | 0 | * F_BSIZE = block size on disk. |
1417 | 0 | * f_bavail = number of free blocks available to a non-superuser. |
1418 | 0 | * f_bfree = number of total free blocks in file system. |
1419 | 0 | */ |
1420 | 0 |
|
1421 | 0 | if (STATFS(mPath.get(), &fs_buf) < 0) { |
1422 | 0 | // The call to STATFS failed. |
1423 | | #ifdef DEBUG |
1424 | | printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n"); |
1425 | | #endif |
1426 | | return NS_ERROR_FAILURE; |
1427 | 0 | } |
1428 | 0 |
|
1429 | 0 | *aDiskSpaceAvailable = (int64_t)fs_buf.F_BSIZE * fs_buf.f_bavail; |
1430 | 0 |
|
1431 | | #ifdef DEBUG_DISK_SPACE |
1432 | | printf("DiskSpaceAvailable: %lu bytes\n", |
1433 | | *aDiskSpaceAvailable); |
1434 | | #endif |
1435 | |
|
1436 | 0 | #if defined(USE_LINUX_QUOTACTL) |
1437 | 0 |
|
1438 | 0 | if (!FillStatCache()) { |
1439 | 0 | // Return available size from statfs |
1440 | 0 | return NS_OK; |
1441 | 0 | } |
1442 | 0 | |
1443 | 0 | nsCString deviceName; |
1444 | 0 | if (!GetDeviceName(major(mCachedStat.st_dev), |
1445 | 0 | minor(mCachedStat.st_dev), |
1446 | 0 | deviceName)) { |
1447 | 0 | return NS_OK; |
1448 | 0 | } |
1449 | 0 | |
1450 | 0 | struct dqblk dq; |
1451 | 0 | if (!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(), |
1452 | 0 | getuid(), (caddr_t)&dq) |
1453 | 0 | #ifdef QIF_BLIMITS |
1454 | 0 | && dq.dqb_valid & QIF_BLIMITS |
1455 | 0 | #endif |
1456 | 0 | && dq.dqb_bhardlimit) { |
1457 | 0 | int64_t QuotaSpaceAvailable = 0; |
1458 | 0 | // dqb_bhardlimit is count of BLOCK_SIZE blocks, dqb_curspace is bytes |
1459 | 0 | if ((BLOCK_SIZE * dq.dqb_bhardlimit) > dq.dqb_curspace) |
1460 | 0 | QuotaSpaceAvailable = int64_t(BLOCK_SIZE * dq.dqb_bhardlimit - dq.dqb_curspace); |
1461 | 0 | if (QuotaSpaceAvailable < *aDiskSpaceAvailable) { |
1462 | 0 | *aDiskSpaceAvailable = QuotaSpaceAvailable; |
1463 | 0 | } |
1464 | 0 | } |
1465 | 0 | #endif |
1466 | 0 |
|
1467 | 0 | return NS_OK; |
1468 | 0 |
|
1469 | | #else |
1470 | | /* |
1471 | | * This platform doesn't have statfs or statvfs. I'm sure that there's |
1472 | | * a way to check for free disk space on platforms that don't have statfs |
1473 | | * (I'm SURE they have df, for example). |
1474 | | * |
1475 | | * Until we figure out how to do that, lets be honest and say that this |
1476 | | * command isn't implemented properly for these platforms yet. |
1477 | | */ |
1478 | | #ifdef DEBUG |
1479 | | printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n"); |
1480 | | #endif |
1481 | | return NS_ERROR_NOT_IMPLEMENTED; |
1482 | | |
1483 | | #endif /* STATFS */ |
1484 | |
|
1485 | 0 | } |
1486 | | |
1487 | | NS_IMETHODIMP |
1488 | | nsLocalFile::GetParent(nsIFile** aParent) |
1489 | 15 | { |
1490 | 15 | CHECK_mPath(); |
1491 | 15 | if (NS_WARN_IF(!aParent)) { |
1492 | 0 | return NS_ERROR_INVALID_ARG; |
1493 | 0 | } |
1494 | 15 | *aParent = nullptr; |
1495 | 15 | |
1496 | 15 | // if '/' we are at the top of the volume, return null |
1497 | 15 | if (mPath.EqualsLiteral("/")) { |
1498 | 0 | return NS_OK; |
1499 | 0 | } |
1500 | 15 | |
1501 | 15 | // <brendan, after jband> I promise to play nice |
1502 | 15 | char* buffer = mPath.BeginWriting(); |
1503 | 15 | // find the last significant slash in buffer |
1504 | 15 | char* slashp = strrchr(buffer, '/'); |
1505 | 15 | NS_ASSERTION(slashp, "non-canonical path?"); |
1506 | 15 | if (!slashp) { |
1507 | 0 | return NS_ERROR_FILE_INVALID_PATH; |
1508 | 0 | } |
1509 | 15 | |
1510 | 15 | // for the case where we are at '/' |
1511 | 15 | if (slashp == buffer) { |
1512 | 0 | slashp++; |
1513 | 0 | } |
1514 | 15 | |
1515 | 15 | // temporarily terminate buffer at the last significant slash |
1516 | 15 | char c = *slashp; |
1517 | 15 | *slashp = '\0'; |
1518 | 15 | |
1519 | 15 | nsCOMPtr<nsIFile> localFile; |
1520 | 15 | nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true, |
1521 | 15 | getter_AddRefs(localFile)); |
1522 | 15 | |
1523 | 15 | // make buffer whole again |
1524 | 15 | *slashp = c; |
1525 | 15 | |
1526 | 15 | if (NS_FAILED(rv)) { |
1527 | 0 | return rv; |
1528 | 0 | } |
1529 | 15 | |
1530 | 15 | localFile.forget(aParent); |
1531 | 15 | return NS_OK; |
1532 | 15 | } |
1533 | | |
1534 | | /* |
1535 | | * The results of Exists, isWritable and isReadable are not cached. |
1536 | | */ |
1537 | | |
1538 | | |
1539 | | NS_IMETHODIMP |
1540 | | nsLocalFile::Exists(bool* aResult) |
1541 | 21 | { |
1542 | 21 | CHECK_mPath(); |
1543 | 21 | if (NS_WARN_IF(!aResult)) { |
1544 | 0 | return NS_ERROR_INVALID_ARG; |
1545 | 0 | } |
1546 | 21 | |
1547 | 21 | *aResult = (access(mPath.get(), F_OK) == 0); |
1548 | 21 | return NS_OK; |
1549 | 21 | } |
1550 | | |
1551 | | |
1552 | | NS_IMETHODIMP |
1553 | | nsLocalFile::IsWritable(bool* aResult) |
1554 | 0 | { |
1555 | 0 | CHECK_mPath(); |
1556 | 0 | if (NS_WARN_IF(!aResult)) { |
1557 | 0 | return NS_ERROR_INVALID_ARG; |
1558 | 0 | } |
1559 | 0 | |
1560 | 0 | *aResult = (access(mPath.get(), W_OK) == 0); |
1561 | 0 | if (*aResult || errno == EACCES) { |
1562 | 0 | return NS_OK; |
1563 | 0 | } |
1564 | 0 | return NSRESULT_FOR_ERRNO(); |
1565 | 0 | } |
1566 | | |
1567 | | NS_IMETHODIMP |
1568 | | nsLocalFile::IsReadable(bool* aResult) |
1569 | 0 | { |
1570 | 0 | CHECK_mPath(); |
1571 | 0 | if (NS_WARN_IF(!aResult)) { |
1572 | 0 | return NS_ERROR_INVALID_ARG; |
1573 | 0 | } |
1574 | 0 | |
1575 | 0 | *aResult = (access(mPath.get(), R_OK) == 0); |
1576 | 0 | if (*aResult || errno == EACCES) { |
1577 | 0 | return NS_OK; |
1578 | 0 | } |
1579 | 0 | return NSRESULT_FOR_ERRNO(); |
1580 | 0 | } |
1581 | | |
1582 | | NS_IMETHODIMP |
1583 | | nsLocalFile::IsExecutable(bool* aResult) |
1584 | 0 | { |
1585 | 0 | CHECK_mPath(); |
1586 | 0 | if (NS_WARN_IF(!aResult)) { |
1587 | 0 | return NS_ERROR_INVALID_ARG; |
1588 | 0 | } |
1589 | 0 | |
1590 | 0 | // Check extension (bug 663899). On certain platforms, the file |
1591 | 0 | // extension may cause the OS to treat it as executable regardless of |
1592 | 0 | // the execute bit, such as .jar on Mac OS X. We borrow the code from |
1593 | 0 | // nsLocalFileWin, slightly modified. |
1594 | 0 | |
1595 | 0 | // Don't be fooled by symlinks. |
1596 | 0 | bool symLink; |
1597 | 0 | nsresult rv = IsSymlink(&symLink); |
1598 | 0 | if (NS_FAILED(rv)) { |
1599 | 0 | return rv; |
1600 | 0 | } |
1601 | 0 | |
1602 | 0 | nsAutoString path; |
1603 | 0 | if (symLink) { |
1604 | 0 | GetTarget(path); |
1605 | 0 | } else { |
1606 | 0 | GetPath(path); |
1607 | 0 | } |
1608 | 0 |
|
1609 | 0 | int32_t dotIdx = path.RFindChar(char16_t('.')); |
1610 | 0 | if (dotIdx != kNotFound) { |
1611 | 0 | // Convert extension to lower case. |
1612 | 0 | char16_t* p = path.BeginWriting(); |
1613 | 0 | for (p += dotIdx + 1; *p; ++p) { |
1614 | 0 | *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0; |
1615 | 0 | } |
1616 | 0 |
|
1617 | 0 | // Search for any of the set of executable extensions. |
1618 | 0 | static const char* const executableExts[] = { |
1619 | 0 | "air", // Adobe AIR installer |
1620 | 0 | "jar" // java application bundle |
1621 | 0 | }; |
1622 | 0 | nsDependentSubstring ext = Substring(path, dotIdx + 1); |
1623 | 0 | for (auto executableExt : executableExts) { |
1624 | 0 | if (ext.EqualsASCII(executableExt)) { |
1625 | 0 | // Found a match. Set result and quit. |
1626 | 0 | *aResult = true; |
1627 | 0 | return NS_OK; |
1628 | 0 | } |
1629 | 0 | } |
1630 | 0 | } |
1631 | 0 |
|
1632 | 0 | // On OS X, then query Launch Services. |
1633 | | #ifdef MOZ_WIDGET_COCOA |
1634 | | // Certain Mac applications, such as Classic applications, which |
1635 | | // run under Rosetta, might not have the +x mode bit but are still |
1636 | | // considered to be executable by Launch Services (bug 646748). |
1637 | | CFURLRef url; |
1638 | | if (NS_FAILED(GetCFURL(&url))) { |
1639 | | return NS_ERROR_FAILURE; |
1640 | | } |
1641 | | |
1642 | | LSRequestedInfo theInfoRequest = kLSRequestAllInfo; |
1643 | | LSItemInfoRecord theInfo; |
1644 | | OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo); |
1645 | | ::CFRelease(url); |
1646 | | if (result == noErr) { |
1647 | | if ((theInfo.flags & kLSItemInfoIsApplication) != 0) { |
1648 | | *aResult = true; |
1649 | | return NS_OK; |
1650 | | } |
1651 | | } |
1652 | | #endif |
1653 | |
|
1654 | 0 | // Then check the execute bit. |
1655 | 0 | *aResult = (access(mPath.get(), X_OK) == 0); |
1656 | | #ifdef SOLARIS |
1657 | | // On Solaris, access will always return 0 for root user, however |
1658 | | // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set. |
1659 | | // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950 |
1660 | | if (*aResult) { |
1661 | | struct STAT buf; |
1662 | | |
1663 | | *aResult = (STAT(mPath.get(), &buf) == 0); |
1664 | | if (*aResult || errno == EACCES) { |
1665 | | *aResult = *aResult && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)); |
1666 | | return NS_OK; |
1667 | | } |
1668 | | |
1669 | | return NSRESULT_FOR_ERRNO(); |
1670 | | } |
1671 | | #endif |
1672 | 0 | if (*aResult || errno == EACCES) { |
1673 | 0 | return NS_OK; |
1674 | 0 | } |
1675 | 0 | return NSRESULT_FOR_ERRNO(); |
1676 | 0 | } |
1677 | | |
1678 | | NS_IMETHODIMP |
1679 | | nsLocalFile::IsDirectory(bool* aResult) |
1680 | 0 | { |
1681 | 0 | if (NS_WARN_IF(!aResult)) { |
1682 | 0 | return NS_ERROR_INVALID_ARG; |
1683 | 0 | } |
1684 | 0 | *aResult = false; |
1685 | 0 | ENSURE_STAT_CACHE(); |
1686 | 0 | *aResult = S_ISDIR(mCachedStat.st_mode); |
1687 | 0 | return NS_OK; |
1688 | 0 | } |
1689 | | |
1690 | | NS_IMETHODIMP |
1691 | | nsLocalFile::IsFile(bool* aResult) |
1692 | 6 | { |
1693 | 6 | if (NS_WARN_IF(!aResult)) { |
1694 | 0 | return NS_ERROR_INVALID_ARG; |
1695 | 0 | } |
1696 | 6 | *aResult = false; |
1697 | 6 | ENSURE_STAT_CACHE(); |
1698 | 6 | *aResult = S_ISREG(mCachedStat.st_mode); |
1699 | 6 | return NS_OK; |
1700 | 6 | } |
1701 | | |
1702 | | NS_IMETHODIMP |
1703 | | nsLocalFile::IsHidden(bool* aResult) |
1704 | 0 | { |
1705 | 0 | if (NS_WARN_IF(!aResult)) { |
1706 | 0 | return NS_ERROR_INVALID_ARG; |
1707 | 0 | } |
1708 | 0 | nsACString::const_iterator begin, end; |
1709 | 0 | LocateNativeLeafName(begin, end); |
1710 | 0 | *aResult = (*begin == '.'); |
1711 | 0 | return NS_OK; |
1712 | 0 | } |
1713 | | |
1714 | | NS_IMETHODIMP |
1715 | | nsLocalFile::IsSymlink(bool* aResult) |
1716 | 0 | { |
1717 | 0 | if (NS_WARN_IF(!aResult)) { |
1718 | 0 | return NS_ERROR_INVALID_ARG; |
1719 | 0 | } |
1720 | 0 | CHECK_mPath(); |
1721 | 0 |
|
1722 | 0 | struct STAT symStat; |
1723 | 0 | if (LSTAT(mPath.get(), &symStat) == -1) { |
1724 | 0 | return NSRESULT_FOR_ERRNO(); |
1725 | 0 | } |
1726 | 0 | *aResult = S_ISLNK(symStat.st_mode); |
1727 | 0 | return NS_OK; |
1728 | 0 | } |
1729 | | |
1730 | | NS_IMETHODIMP |
1731 | | nsLocalFile::IsSpecial(bool* aResult) |
1732 | 0 | { |
1733 | 0 | if (NS_WARN_IF(!aResult)) { |
1734 | 0 | return NS_ERROR_INVALID_ARG; |
1735 | 0 | } |
1736 | 0 | ENSURE_STAT_CACHE(); |
1737 | 0 | *aResult = S_ISCHR(mCachedStat.st_mode) || |
1738 | 0 | S_ISBLK(mCachedStat.st_mode) || |
1739 | 0 | #ifdef S_ISSOCK |
1740 | 0 | S_ISSOCK(mCachedStat.st_mode) || |
1741 | 0 | #endif |
1742 | 0 | S_ISFIFO(mCachedStat.st_mode); |
1743 | 0 |
|
1744 | 0 | return NS_OK; |
1745 | 0 | } |
1746 | | |
1747 | | NS_IMETHODIMP |
1748 | | nsLocalFile::Equals(nsIFile* aInFile, bool* aResult) |
1749 | 7 | { |
1750 | 7 | if (NS_WARN_IF(!aInFile)) { |
1751 | 0 | return NS_ERROR_INVALID_ARG; |
1752 | 0 | } |
1753 | 7 | if (NS_WARN_IF(!aResult)) { |
1754 | 0 | return NS_ERROR_INVALID_ARG; |
1755 | 0 | } |
1756 | 7 | *aResult = false; |
1757 | 7 | |
1758 | 7 | nsAutoCString inPath; |
1759 | 7 | nsresult rv = aInFile->GetNativePath(inPath); |
1760 | 7 | if (NS_FAILED(rv)) { |
1761 | 0 | return rv; |
1762 | 0 | } |
1763 | 7 | |
1764 | 7 | // We don't need to worry about "/foo/" vs. "/foo" here |
1765 | 7 | // because trailing slashes are stripped on init. |
1766 | 7 | *aResult = !strcmp(inPath.get(), mPath.get()); |
1767 | 7 | return NS_OK; |
1768 | 7 | } |
1769 | | |
1770 | | NS_IMETHODIMP |
1771 | | nsLocalFile::Contains(nsIFile* aInFile, bool* aResult) |
1772 | 0 | { |
1773 | 0 | CHECK_mPath(); |
1774 | 0 | if (NS_WARN_IF(!aInFile)) { |
1775 | 0 | return NS_ERROR_INVALID_ARG; |
1776 | 0 | } |
1777 | 0 | if (NS_WARN_IF(!aResult)) { |
1778 | 0 | return NS_ERROR_INVALID_ARG; |
1779 | 0 | } |
1780 | 0 | |
1781 | 0 | nsAutoCString inPath; |
1782 | 0 | nsresult rv; |
1783 | 0 |
|
1784 | 0 | if (NS_FAILED(rv = aInFile->GetNativePath(inPath))) { |
1785 | 0 | return rv; |
1786 | 0 | } |
1787 | 0 | |
1788 | 0 | *aResult = false; |
1789 | 0 |
|
1790 | 0 | ssize_t len = mPath.Length(); |
1791 | 0 | if (strncmp(mPath.get(), inPath.get(), len) == 0) { |
1792 | 0 | // Now make sure that the |aInFile|'s path has a separator at len, |
1793 | 0 | // which implies that it has more components after len. |
1794 | 0 | if (inPath[len] == '/') { |
1795 | 0 | *aResult = true; |
1796 | 0 | } |
1797 | 0 | } |
1798 | 0 |
|
1799 | 0 | return NS_OK; |
1800 | 0 | } |
1801 | | |
1802 | | NS_IMETHODIMP |
1803 | | nsLocalFile::GetNativeTarget(nsACString& aResult) |
1804 | 0 | { |
1805 | 0 | CHECK_mPath(); |
1806 | 0 | aResult.Truncate(); |
1807 | 0 |
|
1808 | 0 | struct STAT symStat; |
1809 | 0 | if (LSTAT(mPath.get(), &symStat) == -1) { |
1810 | 0 | return NSRESULT_FOR_ERRNO(); |
1811 | 0 | } |
1812 | 0 |
|
1813 | 0 | if (!S_ISLNK(symStat.st_mode)) { |
1814 | 0 | return NS_ERROR_FILE_INVALID_PATH; |
1815 | 0 | } |
1816 | 0 | |
1817 | 0 | int32_t size = (int32_t)symStat.st_size; |
1818 | 0 | nsAutoCString target; |
1819 | 0 | if (!target.SetLength(size, mozilla::fallible)) { |
1820 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1821 | 0 | } |
1822 | 0 | |
1823 | 0 | if (readlink(mPath.get(), target.BeginWriting(), (size_t)size) < 0) { |
1824 | 0 | return NSRESULT_FOR_ERRNO(); |
1825 | 0 | } |
1826 | 0 |
|
1827 | 0 | nsresult rv = NS_OK; |
1828 | 0 | nsCOMPtr<nsIFile> self(this); |
1829 | 0 | int32_t maxLinks = 40; |
1830 | 0 | while (true) { |
1831 | 0 | if (maxLinks-- == 0) { |
1832 | 0 | rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK; |
1833 | 0 | break; |
1834 | 0 | } |
1835 | 0 | |
1836 | 0 | if (target[0] != '/') { |
1837 | 0 | nsCOMPtr<nsIFile> parent; |
1838 | 0 | if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent)))) { |
1839 | 0 | break; |
1840 | 0 | } |
1841 | 0 | if (NS_FAILED(rv = parent->AppendRelativeNativePath(target))) { |
1842 | 0 | break; |
1843 | 0 | } |
1844 | 0 | if (NS_FAILED(rv = parent->GetNativePath(aResult))) { |
1845 | 0 | break; |
1846 | 0 | } |
1847 | 0 | self = parent; |
1848 | 0 | } else { |
1849 | 0 | aResult = target; |
1850 | 0 | } |
1851 | 0 |
|
1852 | 0 | const nsPromiseFlatCString& flatRetval = PromiseFlatCString(aResult); |
1853 | 0 |
|
1854 | 0 | // Any failure in testing the current target we'll just interpret |
1855 | 0 | // as having reached our destiny. |
1856 | 0 | if (LSTAT(flatRetval.get(), &symStat) == -1) { |
1857 | 0 | break; |
1858 | 0 | } |
1859 | 0 | |
1860 | 0 | // And of course we're done if it isn't a symlink. |
1861 | 0 | if (!S_ISLNK(symStat.st_mode)) { |
1862 | 0 | break; |
1863 | 0 | } |
1864 | 0 | |
1865 | 0 | int32_t newSize = (int32_t)symStat.st_size; |
1866 | 0 | size = newSize; |
1867 | 0 | nsAutoCString newTarget; |
1868 | 0 | if (!newTarget.SetLength(size, mozilla::fallible)) { |
1869 | 0 | rv = NS_ERROR_OUT_OF_MEMORY; |
1870 | 0 | break; |
1871 | 0 | } |
1872 | 0 | |
1873 | 0 | int32_t linkLen = readlink(flatRetval.get(), newTarget.BeginWriting(), size); |
1874 | 0 | if (linkLen == -1) { |
1875 | 0 | rv = NSRESULT_FOR_ERRNO(); |
1876 | 0 | break; |
1877 | 0 | } |
1878 | 0 | target = newTarget; |
1879 | 0 | } |
1880 | 0 |
|
1881 | 0 | if (NS_FAILED(rv)) { |
1882 | 0 | aResult.Truncate(); |
1883 | 0 | } |
1884 | 0 | return rv; |
1885 | 0 | } |
1886 | | |
1887 | | NS_IMETHODIMP |
1888 | | nsLocalFile::GetFollowLinks(bool* aFollowLinks) |
1889 | 0 | { |
1890 | 0 | *aFollowLinks = true; |
1891 | 0 | return NS_OK; |
1892 | 0 | } |
1893 | | |
1894 | | NS_IMETHODIMP |
1895 | | nsLocalFile::SetFollowLinks(bool aFollowLinks) |
1896 | 54 | { |
1897 | 54 | return NS_OK; |
1898 | 54 | } |
1899 | | |
1900 | | NS_IMETHODIMP |
1901 | | nsLocalFile::GetDirectoryEntriesImpl(nsIDirectoryEnumerator** aEntries) |
1902 | 3 | { |
1903 | 3 | RefPtr<nsDirEnumeratorUnix> dir = new nsDirEnumeratorUnix(); |
1904 | 3 | |
1905 | 3 | nsresult rv = dir->Init(this, false); |
1906 | 3 | if (NS_FAILED(rv)) { |
1907 | 0 | *aEntries = nullptr; |
1908 | 3 | } else { |
1909 | 3 | dir.forget(aEntries); |
1910 | 3 | } |
1911 | 3 | |
1912 | 3 | return rv; |
1913 | 3 | } |
1914 | | |
1915 | | NS_IMETHODIMP |
1916 | | nsLocalFile::Load(PRLibrary** aResult) |
1917 | 0 | { |
1918 | 0 | CHECK_mPath(); |
1919 | 0 | if (NS_WARN_IF(!aResult)) { |
1920 | 0 | return NS_ERROR_INVALID_ARG; |
1921 | 0 | } |
1922 | 0 | |
1923 | | #ifdef NS_BUILD_REFCNT_LOGGING |
1924 | | nsTraceRefcnt::SetActivityIsLegal(false); |
1925 | | #endif |
1926 | | |
1927 | 0 | *aResult = PR_LoadLibrary(mPath.get()); |
1928 | 0 |
|
1929 | | #ifdef NS_BUILD_REFCNT_LOGGING |
1930 | | nsTraceRefcnt::SetActivityIsLegal(true); |
1931 | | #endif |
1932 | |
|
1933 | 0 | if (!*aResult) { |
1934 | 0 | return NS_ERROR_FAILURE; |
1935 | 0 | } |
1936 | 0 | return NS_OK; |
1937 | 0 | } |
1938 | | |
1939 | | NS_IMETHODIMP |
1940 | | nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor) |
1941 | 5 | { |
1942 | 5 | return GetNativePath(aPersistentDescriptor); |
1943 | 5 | } |
1944 | | |
1945 | | NS_IMETHODIMP |
1946 | | nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor) |
1947 | 0 | { |
1948 | | #ifdef MOZ_WIDGET_COCOA |
1949 | | if (aPersistentDescriptor.IsEmpty()) { |
1950 | | return NS_ERROR_INVALID_ARG; |
1951 | | } |
1952 | | |
1953 | | // Support pathnames as user-supplied descriptors if they begin with '/' |
1954 | | // or '~'. These characters do not collide with the base64 set used for |
1955 | | // encoding alias records. |
1956 | | char first = aPersistentDescriptor.First(); |
1957 | | if (first == '/' || first == '~') { |
1958 | | return InitWithNativePath(aPersistentDescriptor); |
1959 | | } |
1960 | | |
1961 | | uint32_t dataSize = aPersistentDescriptor.Length(); |
1962 | | char* decodedData = PL_Base64Decode( |
1963 | | PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nullptr); |
1964 | | if (!decodedData) { |
1965 | | NS_ERROR("SetPersistentDescriptor was given bad data"); |
1966 | | return NS_ERROR_FAILURE; |
1967 | | } |
1968 | | |
1969 | | // Cast to an alias record and resolve. |
1970 | | AliasRecord aliasHeader = *(AliasPtr)decodedData; |
1971 | | int32_t aliasSize = ::GetAliasSizeFromPtr(&aliasHeader); |
1972 | | if (aliasSize > ((int32_t)dataSize * 3) / 4) { // be paranoid about having too few data |
1973 | | PR_Free(decodedData); // PL_Base64Decode() uses PR_Malloc(). |
1974 | | return NS_ERROR_FAILURE; |
1975 | | } |
1976 | | |
1977 | | nsresult rv = NS_OK; |
1978 | | |
1979 | | // Move the now-decoded data into the Handle. |
1980 | | // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h |
1981 | | Handle newHandle = nullptr; |
1982 | | if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr) { |
1983 | | rv = NS_ERROR_OUT_OF_MEMORY; |
1984 | | } |
1985 | | PR_Free(decodedData); // PL_Base64Decode() uses PR_Malloc(). |
1986 | | if (NS_FAILED(rv)) { |
1987 | | return rv; |
1988 | | } |
1989 | | |
1990 | | Boolean changed; |
1991 | | FSRef resolvedFSRef; |
1992 | | OSErr err = ::FSResolveAlias(nullptr, (AliasHandle)newHandle, &resolvedFSRef, |
1993 | | &changed); |
1994 | | |
1995 | | rv = MacErrorMapper(err); |
1996 | | DisposeHandle(newHandle); |
1997 | | if (NS_FAILED(rv)) { |
1998 | | return rv; |
1999 | | } |
2000 | | |
2001 | | return InitWithFSRef(&resolvedFSRef); |
2002 | | #else |
2003 | | return InitWithNativePath(aPersistentDescriptor); |
2004 | 0 | #endif |
2005 | 0 | } |
2006 | | |
2007 | | NS_IMETHODIMP |
2008 | | nsLocalFile::Reveal() |
2009 | 0 | { |
2010 | 0 | if (!FilePreferences::IsAllowedPath(mPath)) { |
2011 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
2012 | 0 | } |
2013 | 0 | |
2014 | 0 | #ifdef MOZ_WIDGET_GTK |
2015 | 0 | nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); |
2016 | 0 | if (!giovfs) { |
2017 | 0 | return NS_ERROR_FAILURE; |
2018 | 0 | } |
2019 | 0 | |
2020 | 0 | bool isDirectory; |
2021 | 0 | if (NS_FAILED(IsDirectory(&isDirectory))) { |
2022 | 0 | return NS_ERROR_FAILURE; |
2023 | 0 | } |
2024 | 0 | |
2025 | 0 | if (isDirectory) { |
2026 | 0 | return giovfs->ShowURIForInput(mPath); |
2027 | 0 | } |
2028 | 0 | if (NS_SUCCEEDED(giovfs->OrgFreedesktopFileManager1ShowItems(mPath))) { |
2029 | 0 | return NS_OK; |
2030 | 0 | } |
2031 | 0 | nsCOMPtr<nsIFile> parentDir; |
2032 | 0 | nsAutoCString dirPath; |
2033 | 0 | if (NS_FAILED(GetParent(getter_AddRefs(parentDir)))) { |
2034 | 0 | return NS_ERROR_FAILURE; |
2035 | 0 | } |
2036 | 0 | if (NS_FAILED(parentDir->GetNativePath(dirPath))) { |
2037 | 0 | return NS_ERROR_FAILURE; |
2038 | 0 | } |
2039 | 0 | |
2040 | 0 | return giovfs->ShowURIForInput(dirPath); |
2041 | | #elif defined(MOZ_WIDGET_COCOA) |
2042 | | CFURLRef url; |
2043 | | if (NS_SUCCEEDED(GetCFURL(&url))) { |
2044 | | nsresult rv = CocoaFileUtils::RevealFileInFinder(url); |
2045 | | ::CFRelease(url); |
2046 | | return rv; |
2047 | | } |
2048 | | return NS_ERROR_FAILURE; |
2049 | | #else |
2050 | | return NS_ERROR_FAILURE; |
2051 | | #endif |
2052 | | } |
2053 | | |
2054 | | NS_IMETHODIMP |
2055 | | nsLocalFile::Launch() |
2056 | 0 | { |
2057 | 0 | if (!FilePreferences::IsAllowedPath(mPath)) { |
2058 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
2059 | 0 | } |
2060 | 0 | |
2061 | 0 | #ifdef MOZ_WIDGET_GTK |
2062 | 0 | nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); |
2063 | 0 | if (!giovfs) { |
2064 | 0 | return NS_ERROR_FAILURE; |
2065 | 0 | } |
2066 | 0 | |
2067 | 0 | return giovfs->ShowURIForInput(mPath); |
2068 | | #elif defined(MOZ_WIDGET_ANDROID) |
2069 | | // Try to get a mimetype, if this fails just use the file uri alone |
2070 | | nsresult rv; |
2071 | | nsAutoCString type; |
2072 | | nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv)); |
2073 | | if (NS_SUCCEEDED(rv)) { |
2074 | | rv = mimeService->GetTypeFromFile(this, type); |
2075 | | } |
2076 | | |
2077 | | nsAutoCString fileUri = NS_LITERAL_CSTRING("file://") + mPath; |
2078 | | return java::GeckoAppShell::OpenUriExternal( |
2079 | | NS_ConvertUTF8toUTF16(fileUri), |
2080 | | NS_ConvertUTF8toUTF16(type), |
2081 | | EmptyString(), |
2082 | | EmptyString(), |
2083 | | EmptyString(), |
2084 | | EmptyString()) ? NS_OK : NS_ERROR_FAILURE; |
2085 | | #elif defined(MOZ_WIDGET_COCOA) |
2086 | | CFURLRef url; |
2087 | | if (NS_SUCCEEDED(GetCFURL(&url))) { |
2088 | | nsresult rv = CocoaFileUtils::OpenURL(url); |
2089 | | ::CFRelease(url); |
2090 | | return rv; |
2091 | | } |
2092 | | return NS_ERROR_FAILURE; |
2093 | | #else |
2094 | | return NS_ERROR_FAILURE; |
2095 | | #endif |
2096 | | } |
2097 | | |
2098 | | nsresult |
2099 | | NS_NewNativeLocalFile(const nsACString& aPath, bool aFollowSymlinks, |
2100 | | nsIFile** aResult) |
2101 | 54 | { |
2102 | 54 | RefPtr<nsLocalFile> file = new nsLocalFile(); |
2103 | 54 | |
2104 | 54 | file->SetFollowLinks(aFollowSymlinks); |
2105 | 54 | |
2106 | 54 | if (!aPath.IsEmpty()) { |
2107 | 45 | nsresult rv = file->InitWithNativePath(aPath); |
2108 | 45 | if (NS_FAILED(rv)) { |
2109 | 0 | return rv; |
2110 | 0 | } |
2111 | 54 | } |
2112 | 54 | file.forget(aResult); |
2113 | 54 | return NS_OK; |
2114 | 54 | } |
2115 | | |
2116 | | //----------------------------------------------------------------------------- |
2117 | | // unicode support |
2118 | | //----------------------------------------------------------------------------- |
2119 | | |
2120 | | #define SET_UCS(func, ucsArg) \ |
2121 | 21 | { \ |
2122 | 21 | nsAutoCString buf; \ |
2123 | 21 | nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \ |
2124 | 21 | if (NS_FAILED(rv)) \ |
2125 | 21 | return rv; \ |
2126 | 21 | return (func)(buf); \ |
2127 | 21 | } |
2128 | | |
2129 | | #define GET_UCS(func, ucsArg) \ |
2130 | 3 | { \ |
2131 | 3 | nsAutoCString buf; \ |
2132 | 3 | nsresult rv = (func)(buf); \ |
2133 | 3 | if (NS_FAILED(rv)) return rv; \ |
2134 | 3 | return NS_CopyNativeToUnicode(buf, ucsArg); \ |
2135 | 3 | } |
2136 | | |
2137 | | #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \ |
2138 | 0 | { \ |
2139 | 0 | nsAutoCString buf; \ |
2140 | 0 | nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \ |
2141 | 0 | if (NS_FAILED(rv)) \ |
2142 | 0 | return rv; \ |
2143 | 0 | return (func)(opaqueArg, buf); \ |
2144 | 0 | } |
2145 | | |
2146 | | // Unicode interface Wrapper |
2147 | | nsresult |
2148 | | nsLocalFile::InitWithPath(const nsAString& aFilePath) |
2149 | 0 | { |
2150 | 0 | SET_UCS(InitWithNativePath, aFilePath); |
2151 | 0 | } |
2152 | | nsresult |
2153 | | nsLocalFile::Append(const nsAString& aNode) |
2154 | 12 | { |
2155 | 12 | SET_UCS(AppendNative, aNode); |
2156 | 0 | } |
2157 | | nsresult |
2158 | | nsLocalFile::AppendRelativePath(const nsAString& aNode) |
2159 | 0 | { |
2160 | 0 | SET_UCS(AppendRelativeNativePath, aNode); |
2161 | 0 | } |
2162 | | nsresult |
2163 | | nsLocalFile::GetLeafName(nsAString& aLeafName) |
2164 | 3 | { |
2165 | 3 | GET_UCS(GetNativeLeafName, aLeafName); |
2166 | 0 | } |
2167 | | nsresult |
2168 | | nsLocalFile::SetLeafName(const nsAString& aLeafName) |
2169 | 9 | { |
2170 | 9 | SET_UCS(SetNativeLeafName, aLeafName); |
2171 | 0 | } |
2172 | | nsresult |
2173 | | nsLocalFile::GetPath(nsAString& aResult) |
2174 | 18 | { |
2175 | 18 | return NS_CopyNativeToUnicode(mPath, aResult); |
2176 | 18 | } |
2177 | | nsresult |
2178 | | nsLocalFile::CopyTo(nsIFile* aNewParentDir, const nsAString& aNewName) |
2179 | 0 | { |
2180 | 0 | SET_UCS_2ARGS_2(CopyToNative , aNewParentDir, aNewName); |
2181 | 0 | } |
2182 | | nsresult |
2183 | | nsLocalFile::CopyToFollowingLinks(nsIFile* aNewParentDir, |
2184 | | const nsAString& aNewName) |
2185 | 0 | { |
2186 | 0 | SET_UCS_2ARGS_2(CopyToFollowingLinksNative , aNewParentDir, aNewName); |
2187 | 0 | } |
2188 | | nsresult |
2189 | | nsLocalFile::MoveTo(nsIFile* aNewParentDir, const nsAString& aNewName) |
2190 | 0 | { |
2191 | 0 | SET_UCS_2ARGS_2(MoveToNative, aNewParentDir, aNewName); |
2192 | 0 | } |
2193 | | |
2194 | | NS_IMETHODIMP |
2195 | | nsLocalFile::RenameTo(nsIFile* aNewParentDir, const nsAString& aNewName) |
2196 | 0 | { |
2197 | 0 | SET_UCS_2ARGS_2(RenameToNative, aNewParentDir, aNewName); |
2198 | 0 | } |
2199 | | |
2200 | | NS_IMETHODIMP |
2201 | | nsLocalFile::RenameToNative(nsIFile* aNewParentDir, const nsACString& aNewName) |
2202 | 0 | { |
2203 | 0 | nsresult rv; |
2204 | 0 |
|
2205 | 0 | // check to make sure that this has been initialized properly |
2206 | 0 | CHECK_mPath(); |
2207 | 0 |
|
2208 | 0 | // check to make sure that we have a new parent |
2209 | 0 | nsAutoCString newPathName; |
2210 | 0 | rv = GetNativeTargetPathName(aNewParentDir, aNewName, newPathName); |
2211 | 0 | if (NS_FAILED(rv)) { |
2212 | 0 | return rv; |
2213 | 0 | } |
2214 | 0 | |
2215 | 0 | if (!FilePreferences::IsAllowedPath(newPathName)) { |
2216 | 0 | return NS_ERROR_FILE_ACCESS_DENIED; |
2217 | 0 | } |
2218 | 0 | |
2219 | 0 | // try for atomic rename |
2220 | 0 | if (rename(mPath.get(), newPathName.get()) < 0) { |
2221 | 0 | if (errno == EXDEV) { |
2222 | 0 | rv = NS_ERROR_FILE_ACCESS_DENIED; |
2223 | 0 | } else { |
2224 | 0 | rv = NSRESULT_FOR_ERRNO(); |
2225 | 0 | } |
2226 | 0 | } |
2227 | 0 |
|
2228 | 0 | return rv; |
2229 | 0 | } |
2230 | | |
2231 | | nsresult |
2232 | | nsLocalFile::GetTarget(nsAString& aResult) |
2233 | 0 | { |
2234 | 0 | GET_UCS(GetNativeTarget, aResult); |
2235 | 0 | } |
2236 | | |
2237 | | // nsIHashable |
2238 | | |
2239 | | NS_IMETHODIMP |
2240 | | nsLocalFile::Equals(nsIHashable* aOther, bool* aResult) |
2241 | 0 | { |
2242 | 0 | nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther)); |
2243 | 0 | if (!otherFile) { |
2244 | 0 | *aResult = false; |
2245 | 0 | return NS_OK; |
2246 | 0 | } |
2247 | 0 | |
2248 | 0 | return Equals(otherFile, aResult); |
2249 | 0 | } |
2250 | | |
2251 | | NS_IMETHODIMP |
2252 | | nsLocalFile::GetHashCode(uint32_t* aResult) |
2253 | 0 | { |
2254 | 0 | *aResult = HashString(mPath); |
2255 | 0 | return NS_OK; |
2256 | 0 | } |
2257 | | |
2258 | | nsresult |
2259 | | NS_NewLocalFile(const nsAString& aPath, bool aFollowLinks, nsIFile** aResult) |
2260 | 6 | { |
2261 | 6 | nsAutoCString buf; |
2262 | 6 | nsresult rv = NS_CopyUnicodeToNative(aPath, buf); |
2263 | 6 | if (NS_FAILED(rv)) { |
2264 | 0 | return rv; |
2265 | 0 | } |
2266 | 6 | return NS_NewNativeLocalFile(buf, aFollowLinks, aResult); |
2267 | 6 | } |
2268 | | |
2269 | | // nsILocalFileMac |
2270 | | |
2271 | | #ifdef MOZ_WIDGET_COCOA |
2272 | | |
2273 | | static nsresult MacErrorMapper(OSErr inErr) |
2274 | | { |
2275 | | nsresult outErr; |
2276 | | |
2277 | | switch (inErr) { |
2278 | | case noErr: |
2279 | | outErr = NS_OK; |
2280 | | break; |
2281 | | |
2282 | | case fnfErr: |
2283 | | case afpObjectNotFound: |
2284 | | case afpDirNotFound: |
2285 | | outErr = NS_ERROR_FILE_NOT_FOUND; |
2286 | | break; |
2287 | | |
2288 | | case dupFNErr: |
2289 | | case afpObjectExists: |
2290 | | outErr = NS_ERROR_FILE_ALREADY_EXISTS; |
2291 | | break; |
2292 | | |
2293 | | case dskFulErr: |
2294 | | case afpDiskFull: |
2295 | | outErr = NS_ERROR_FILE_DISK_FULL; |
2296 | | break; |
2297 | | |
2298 | | case fLckdErr: |
2299 | | case afpVolLocked: |
2300 | | outErr = NS_ERROR_FILE_IS_LOCKED; |
2301 | | break; |
2302 | | |
2303 | | case afpAccessDenied: |
2304 | | outErr = NS_ERROR_FILE_ACCESS_DENIED; |
2305 | | break; |
2306 | | |
2307 | | case afpDirNotEmpty: |
2308 | | outErr = NS_ERROR_FILE_DIR_NOT_EMPTY; |
2309 | | break; |
2310 | | |
2311 | | // Can't find good map for some |
2312 | | case bdNamErr: |
2313 | | outErr = NS_ERROR_FAILURE; |
2314 | | break; |
2315 | | |
2316 | | default: |
2317 | | outErr = NS_ERROR_FAILURE; |
2318 | | break; |
2319 | | } |
2320 | | |
2321 | | return outErr; |
2322 | | } |
2323 | | |
2324 | | static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr) |
2325 | | { |
2326 | | // first see if the conversion would succeed and find the length of the result |
2327 | | CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef); |
2328 | | CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), |
2329 | | kCFStringEncodingUTF8, 0, false, |
2330 | | nullptr, 0, &usedBufLen); |
2331 | | if (charsConverted == inStrLen) { |
2332 | | // all characters converted, do the actual conversion |
2333 | | aOutStr.SetLength(usedBufLen); |
2334 | | if (aOutStr.Length() != (unsigned int)usedBufLen) { |
2335 | | return NS_ERROR_OUT_OF_MEMORY; |
2336 | | } |
2337 | | UInt8* buffer = (UInt8*)aOutStr.BeginWriting(); |
2338 | | ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8, |
2339 | | 0, false, buffer, usedBufLen, &usedBufLen); |
2340 | | return NS_OK; |
2341 | | } |
2342 | | |
2343 | | return NS_ERROR_FAILURE; |
2344 | | } |
2345 | | |
2346 | | NS_IMETHODIMP |
2347 | | nsLocalFile::InitWithCFURL(CFURLRef aCFURL) |
2348 | | { |
2349 | | UInt8 path[PATH_MAX]; |
2350 | | if (::CFURLGetFileSystemRepresentation(aCFURL, true, path, PATH_MAX)) { |
2351 | | nsDependentCString nativePath((char*)path); |
2352 | | return InitWithNativePath(nativePath); |
2353 | | } |
2354 | | |
2355 | | return NS_ERROR_FAILURE; |
2356 | | } |
2357 | | |
2358 | | NS_IMETHODIMP |
2359 | | nsLocalFile::InitWithFSRef(const FSRef* aFSRef) |
2360 | | { |
2361 | | if (NS_WARN_IF(!aFSRef)) { |
2362 | | return NS_ERROR_INVALID_ARG; |
2363 | | } |
2364 | | |
2365 | | CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef); |
2366 | | if (newURLRef) { |
2367 | | nsresult rv = InitWithCFURL(newURLRef); |
2368 | | ::CFRelease(newURLRef); |
2369 | | return rv; |
2370 | | } |
2371 | | |
2372 | | return NS_ERROR_FAILURE; |
2373 | | } |
2374 | | |
2375 | | NS_IMETHODIMP |
2376 | | nsLocalFile::GetCFURL(CFURLRef* aResult) |
2377 | | { |
2378 | | CHECK_mPath(); |
2379 | | |
2380 | | bool isDir; |
2381 | | IsDirectory(&isDir); |
2382 | | *aResult = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, |
2383 | | (UInt8*)mPath.get(), |
2384 | | mPath.Length(), |
2385 | | isDir); |
2386 | | |
2387 | | return (*aResult ? NS_OK : NS_ERROR_FAILURE); |
2388 | | } |
2389 | | |
2390 | | NS_IMETHODIMP |
2391 | | nsLocalFile::GetFSRef(FSRef* aResult) |
2392 | | { |
2393 | | if (NS_WARN_IF(!aResult)) { |
2394 | | return NS_ERROR_INVALID_ARG; |
2395 | | } |
2396 | | |
2397 | | nsresult rv = NS_ERROR_FAILURE; |
2398 | | |
2399 | | CFURLRef url = nullptr; |
2400 | | if (NS_SUCCEEDED(GetCFURL(&url))) { |
2401 | | if (::CFURLGetFSRef(url, aResult)) { |
2402 | | rv = NS_OK; |
2403 | | } |
2404 | | ::CFRelease(url); |
2405 | | } |
2406 | | |
2407 | | return rv; |
2408 | | } |
2409 | | |
2410 | | NS_IMETHODIMP |
2411 | | nsLocalFile::GetFSSpec(FSSpec* aResult) |
2412 | | { |
2413 | | if (NS_WARN_IF(!aResult)) { |
2414 | | return NS_ERROR_INVALID_ARG; |
2415 | | } |
2416 | | |
2417 | | FSRef fsRef; |
2418 | | nsresult rv = GetFSRef(&fsRef); |
2419 | | if (NS_SUCCEEDED(rv)) { |
2420 | | OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nullptr, nullptr, |
2421 | | aResult, nullptr); |
2422 | | return MacErrorMapper(err); |
2423 | | } |
2424 | | |
2425 | | return rv; |
2426 | | } |
2427 | | |
2428 | | NS_IMETHODIMP |
2429 | | nsLocalFile::GetFileSizeWithResFork(int64_t* aFileSizeWithResFork) |
2430 | | { |
2431 | | if (NS_WARN_IF(!aFileSizeWithResFork)) { |
2432 | | return NS_ERROR_INVALID_ARG; |
2433 | | } |
2434 | | |
2435 | | FSRef fsRef; |
2436 | | nsresult rv = GetFSRef(&fsRef); |
2437 | | if (NS_FAILED(rv)) { |
2438 | | return rv; |
2439 | | } |
2440 | | |
2441 | | FSCatalogInfo catalogInfo; |
2442 | | OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes, |
2443 | | &catalogInfo, nullptr, nullptr, nullptr); |
2444 | | if (err != noErr) { |
2445 | | return MacErrorMapper(err); |
2446 | | } |
2447 | | |
2448 | | *aFileSizeWithResFork = |
2449 | | catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize; |
2450 | | return NS_OK; |
2451 | | } |
2452 | | |
2453 | | NS_IMETHODIMP |
2454 | | nsLocalFile::GetFileType(OSType* aFileType) |
2455 | | { |
2456 | | CFURLRef url; |
2457 | | if (NS_SUCCEEDED(GetCFURL(&url))) { |
2458 | | nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType); |
2459 | | ::CFRelease(url); |
2460 | | return rv; |
2461 | | } |
2462 | | return NS_ERROR_FAILURE; |
2463 | | } |
2464 | | |
2465 | | NS_IMETHODIMP |
2466 | | nsLocalFile::SetFileType(OSType aFileType) |
2467 | | { |
2468 | | CFURLRef url; |
2469 | | if (NS_SUCCEEDED(GetCFURL(&url))) { |
2470 | | nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType); |
2471 | | ::CFRelease(url); |
2472 | | return rv; |
2473 | | } |
2474 | | return NS_ERROR_FAILURE; |
2475 | | } |
2476 | | |
2477 | | NS_IMETHODIMP |
2478 | | nsLocalFile::GetFileCreator(OSType* aFileCreator) |
2479 | | { |
2480 | | CFURLRef url; |
2481 | | if (NS_SUCCEEDED(GetCFURL(&url))) { |
2482 | | nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator); |
2483 | | ::CFRelease(url); |
2484 | | return rv; |
2485 | | } |
2486 | | return NS_ERROR_FAILURE; |
2487 | | } |
2488 | | |
2489 | | NS_IMETHODIMP |
2490 | | nsLocalFile::SetFileCreator(OSType aFileCreator) |
2491 | | { |
2492 | | CFURLRef url; |
2493 | | if (NS_SUCCEEDED(GetCFURL(&url))) { |
2494 | | nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator); |
2495 | | ::CFRelease(url); |
2496 | | return rv; |
2497 | | } |
2498 | | return NS_ERROR_FAILURE; |
2499 | | } |
2500 | | |
2501 | | NS_IMETHODIMP |
2502 | | nsLocalFile::LaunchWithDoc(nsIFile* aDocToLoad, bool aLaunchInBackground) |
2503 | | { |
2504 | | bool isExecutable; |
2505 | | nsresult rv = IsExecutable(&isExecutable); |
2506 | | if (NS_FAILED(rv)) { |
2507 | | return rv; |
2508 | | } |
2509 | | if (!isExecutable) { |
2510 | | return NS_ERROR_FILE_EXECUTION_FAILED; |
2511 | | } |
2512 | | |
2513 | | FSRef appFSRef, docFSRef; |
2514 | | rv = GetFSRef(&appFSRef); |
2515 | | if (NS_FAILED(rv)) { |
2516 | | return rv; |
2517 | | } |
2518 | | |
2519 | | if (aDocToLoad) { |
2520 | | nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad); |
2521 | | rv = macDoc->GetFSRef(&docFSRef); |
2522 | | if (NS_FAILED(rv)) { |
2523 | | return rv; |
2524 | | } |
2525 | | } |
2526 | | |
2527 | | LSLaunchFlags theLaunchFlags = kLSLaunchDefaults; |
2528 | | LSLaunchFSRefSpec thelaunchSpec; |
2529 | | |
2530 | | if (aLaunchInBackground) { |
2531 | | theLaunchFlags |= kLSLaunchDontSwitch; |
2532 | | } |
2533 | | memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec)); |
2534 | | |
2535 | | thelaunchSpec.appRef = &appFSRef; |
2536 | | if (aDocToLoad) { |
2537 | | thelaunchSpec.numDocs = 1; |
2538 | | thelaunchSpec.itemRefs = &docFSRef; |
2539 | | } |
2540 | | thelaunchSpec.launchFlags = theLaunchFlags; |
2541 | | |
2542 | | OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr); |
2543 | | if (err != noErr) { |
2544 | | return MacErrorMapper(err); |
2545 | | } |
2546 | | |
2547 | | return NS_OK; |
2548 | | } |
2549 | | |
2550 | | NS_IMETHODIMP |
2551 | | nsLocalFile::OpenDocWithApp(nsIFile* aAppToOpenWith, bool aLaunchInBackground) |
2552 | | { |
2553 | | FSRef docFSRef; |
2554 | | nsresult rv = GetFSRef(&docFSRef); |
2555 | | if (NS_FAILED(rv)) { |
2556 | | return rv; |
2557 | | } |
2558 | | |
2559 | | if (!aAppToOpenWith) { |
2560 | | OSErr err = ::LSOpenFSRef(&docFSRef, nullptr); |
2561 | | return MacErrorMapper(err); |
2562 | | } |
2563 | | |
2564 | | nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv); |
2565 | | if (!appFileMac) { |
2566 | | return rv; |
2567 | | } |
2568 | | |
2569 | | bool isExecutable; |
2570 | | rv = appFileMac->IsExecutable(&isExecutable); |
2571 | | if (NS_FAILED(rv)) { |
2572 | | return rv; |
2573 | | } |
2574 | | if (!isExecutable) { |
2575 | | return NS_ERROR_FILE_EXECUTION_FAILED; |
2576 | | } |
2577 | | |
2578 | | FSRef appFSRef; |
2579 | | rv = appFileMac->GetFSRef(&appFSRef); |
2580 | | if (NS_FAILED(rv)) { |
2581 | | return rv; |
2582 | | } |
2583 | | |
2584 | | LSLaunchFlags theLaunchFlags = kLSLaunchDefaults; |
2585 | | LSLaunchFSRefSpec thelaunchSpec; |
2586 | | |
2587 | | if (aLaunchInBackground) { |
2588 | | theLaunchFlags |= kLSLaunchDontSwitch; |
2589 | | } |
2590 | | memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec)); |
2591 | | |
2592 | | thelaunchSpec.appRef = &appFSRef; |
2593 | | thelaunchSpec.numDocs = 1; |
2594 | | thelaunchSpec.itemRefs = &docFSRef; |
2595 | | thelaunchSpec.launchFlags = theLaunchFlags; |
2596 | | |
2597 | | OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr); |
2598 | | if (err != noErr) { |
2599 | | return MacErrorMapper(err); |
2600 | | } |
2601 | | |
2602 | | return NS_OK; |
2603 | | } |
2604 | | |
2605 | | NS_IMETHODIMP |
2606 | | nsLocalFile::IsPackage(bool* aResult) |
2607 | | { |
2608 | | if (NS_WARN_IF(!aResult)) { |
2609 | | return NS_ERROR_INVALID_ARG; |
2610 | | } |
2611 | | *aResult = false; |
2612 | | |
2613 | | CFURLRef url; |
2614 | | nsresult rv = GetCFURL(&url); |
2615 | | if (NS_FAILED(rv)) { |
2616 | | return rv; |
2617 | | } |
2618 | | |
2619 | | LSItemInfoRecord info; |
2620 | | OSStatus status = ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info); |
2621 | | |
2622 | | ::CFRelease(url); |
2623 | | |
2624 | | if (status != noErr) { |
2625 | | return NS_ERROR_FAILURE; |
2626 | | } |
2627 | | |
2628 | | *aResult = !!(info.flags & kLSItemInfoIsPackage); |
2629 | | |
2630 | | return NS_OK; |
2631 | | } |
2632 | | |
2633 | | NS_IMETHODIMP |
2634 | | nsLocalFile::GetBundleDisplayName(nsAString& aOutBundleName) |
2635 | | { |
2636 | | bool isPackage = false; |
2637 | | nsresult rv = IsPackage(&isPackage); |
2638 | | if (NS_FAILED(rv) || !isPackage) { |
2639 | | return NS_ERROR_FAILURE; |
2640 | | } |
2641 | | |
2642 | | nsAutoString name; |
2643 | | rv = GetLeafName(name); |
2644 | | if (NS_FAILED(rv)) { |
2645 | | return rv; |
2646 | | } |
2647 | | |
2648 | | int32_t length = name.Length(); |
2649 | | if (Substring(name, length - 4, length).EqualsLiteral(".app")) { |
2650 | | // 4 characters in ".app" |
2651 | | aOutBundleName = Substring(name, 0, length - 4); |
2652 | | } else { |
2653 | | aOutBundleName = name; |
2654 | | } |
2655 | | |
2656 | | return NS_OK; |
2657 | | } |
2658 | | |
2659 | | NS_IMETHODIMP |
2660 | | nsLocalFile::GetBundleIdentifier(nsACString& aOutBundleIdentifier) |
2661 | | { |
2662 | | nsresult rv = NS_ERROR_FAILURE; |
2663 | | |
2664 | | CFURLRef urlRef; |
2665 | | if (NS_SUCCEEDED(GetCFURL(&urlRef))) { |
2666 | | CFBundleRef bundle = ::CFBundleCreate(nullptr, urlRef); |
2667 | | if (bundle) { |
2668 | | CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle); |
2669 | | if (bundleIdentifier) { |
2670 | | rv = CFStringReftoUTF8(bundleIdentifier, aOutBundleIdentifier); |
2671 | | } |
2672 | | ::CFRelease(bundle); |
2673 | | } |
2674 | | ::CFRelease(urlRef); |
2675 | | } |
2676 | | |
2677 | | return rv; |
2678 | | } |
2679 | | |
2680 | | NS_IMETHODIMP |
2681 | | nsLocalFile::GetBundleContentsLastModifiedTime(int64_t* aLastModTime) |
2682 | | { |
2683 | | CHECK_mPath(); |
2684 | | if (NS_WARN_IF(!aLastModTime)) { |
2685 | | return NS_ERROR_INVALID_ARG; |
2686 | | } |
2687 | | |
2688 | | bool isPackage = false; |
2689 | | nsresult rv = IsPackage(&isPackage); |
2690 | | if (NS_FAILED(rv) || !isPackage) { |
2691 | | return GetLastModifiedTime(aLastModTime); |
2692 | | } |
2693 | | |
2694 | | nsAutoCString infoPlistPath(mPath); |
2695 | | infoPlistPath.AppendLiteral("/Contents/Info.plist"); |
2696 | | PRFileInfo64 info; |
2697 | | if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) { |
2698 | | return GetLastModifiedTime(aLastModTime); |
2699 | | } |
2700 | | int64_t modTime = int64_t(info.modifyTime); |
2701 | | if (modTime == 0) { |
2702 | | *aLastModTime = 0; |
2703 | | } else { |
2704 | | *aLastModTime = modTime / int64_t(PR_USEC_PER_MSEC); |
2705 | | } |
2706 | | |
2707 | | return NS_OK; |
2708 | | } |
2709 | | |
2710 | | NS_IMETHODIMP nsLocalFile::InitWithFile(nsIFile* aFile) |
2711 | | { |
2712 | | if (NS_WARN_IF(!aFile)) { |
2713 | | return NS_ERROR_INVALID_ARG; |
2714 | | } |
2715 | | |
2716 | | nsAutoCString nativePath; |
2717 | | nsresult rv = aFile->GetNativePath(nativePath); |
2718 | | if (NS_FAILED(rv)) { |
2719 | | return rv; |
2720 | | } |
2721 | | |
2722 | | return InitWithNativePath(nativePath); |
2723 | | } |
2724 | | |
2725 | | nsresult |
2726 | | NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks, |
2727 | | nsILocalFileMac** aResult) |
2728 | | { |
2729 | | RefPtr<nsLocalFile> file = new nsLocalFile(); |
2730 | | |
2731 | | file->SetFollowLinks(aFollowLinks); |
2732 | | |
2733 | | nsresult rv = file->InitWithFSRef(aFSRef); |
2734 | | if (NS_FAILED(rv)) { |
2735 | | return rv; |
2736 | | } |
2737 | | file.forget(aResult); |
2738 | | return NS_OK; |
2739 | | } |
2740 | | |
2741 | | nsresult |
2742 | | NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks, |
2743 | | nsILocalFileMac** aResult) |
2744 | | { |
2745 | | RefPtr<nsLocalFile> file = new nsLocalFile(); |
2746 | | |
2747 | | file->SetFollowLinks(aFollowLinks); |
2748 | | |
2749 | | nsresult rv = file->InitWithCFURL(aURL); |
2750 | | if (NS_FAILED(rv)) { |
2751 | | return rv; |
2752 | | } |
2753 | | file.forget(aResult); |
2754 | | return NS_OK; |
2755 | | } |
2756 | | |
2757 | | #endif |