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