xpcom/io/nsLocalFileUnix.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:b425892b50f6
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

mercurial