Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / xpcom / io / nsLocalFileUnix.cpp
blobae501f40415468c5422c02257e85dd8710a5d01a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /**
8 * Implementation of nsIFile for "unixy" systems.
9 */
11 #include "nsLocalFile.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/CheckedInt.h"
16 #include "mozilla/DebugOnly.h"
17 #include "mozilla/Sprintf.h"
18 #include "mozilla/FilePreferences.h"
19 #include "prtime.h"
21 #include <sys/select.h>
22 #include <sys/stat.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <dirent.h>
30 #if defined(XP_MACOSX)
31 # include <sys/xattr.h>
32 #endif
34 #if defined(USE_LINUX_QUOTACTL)
35 # include <sys/mount.h>
36 # include <sys/quota.h>
37 # include <sys/sysmacros.h>
38 # ifndef BLOCK_SIZE
39 # define BLOCK_SIZE 1024 /* kernel block size */
40 # endif
41 #endif
43 #include "nsDirectoryServiceDefs.h"
44 #include "nsCOMPtr.h"
45 #include "nsIFile.h"
46 #include "nsString.h"
47 #include "nsIDirectoryEnumerator.h"
48 #include "nsSimpleEnumerator.h"
49 #include "private/pprio.h"
50 #include "prlink.h"
52 #ifdef MOZ_WIDGET_GTK
53 # include "nsIGIOService.h"
54 #endif
56 #ifdef MOZ_WIDGET_COCOA
57 # include <Carbon/Carbon.h>
58 # include "CocoaFileUtils.h"
59 # include "prmem.h"
60 # include "plbase64.h"
62 static nsresult MacErrorMapper(OSErr inErr);
63 #endif
65 #ifdef MOZ_WIDGET_ANDROID
66 # include "mozilla/java/GeckoAppShellWrappers.h"
67 # include "nsIMIMEService.h"
68 # include <linux/magic.h>
69 #endif
71 #include "nsNativeCharsetUtils.h"
72 #include "nsTraceRefcnt.h"
74 /**
75 * we need these for statfs()
77 #ifdef HAVE_SYS_STATVFS_H
78 # if defined(__osf__) && defined(__DECCXX)
79 extern "C" int statvfs(const char*, struct statvfs*);
80 # endif
81 # include <sys/statvfs.h>
82 #endif
84 #ifdef HAVE_SYS_STATFS_H
85 # include <sys/statfs.h>
86 #endif
88 #ifdef HAVE_SYS_VFS_H
89 # include <sys/vfs.h>
90 #endif
92 #if defined(HAVE_STATVFS64) && (!defined(LINUX) && !defined(__osf__))
93 # define STATFS statvfs64
94 # define F_BSIZE f_frsize
95 #elif defined(HAVE_STATVFS) && (!defined(LINUX) && !defined(__osf__))
96 # define STATFS statvfs
97 # define F_BSIZE f_frsize
98 #elif defined(HAVE_STATFS64)
99 # define STATFS statfs64
100 # define F_BSIZE f_bsize
101 #elif defined(HAVE_STATFS)
102 # define STATFS statfs
103 # define F_BSIZE f_bsize
104 #endif
106 using namespace mozilla;
108 #define ENSURE_STAT_CACHE() \
109 do { \
110 if (!FillStatCache()) return NSRESULT_FOR_ERRNO(); \
111 } while (0)
113 #define CHECK_mPath() \
114 do { \
115 if (mPath.IsEmpty()) return NS_ERROR_NOT_INITIALIZED; \
116 if (!FilePreferences::IsAllowedPath(mPath)) \
117 return NS_ERROR_FILE_ACCESS_DENIED; \
118 } while (0)
120 static PRTime TimespecToMillis(const struct timespec& aTimeSpec) {
121 return PRTime(aTimeSpec.tv_sec) * PR_MSEC_PER_SEC +
122 PRTime(aTimeSpec.tv_nsec) / PR_NSEC_PER_MSEC;
125 /* directory enumerator */
126 class nsDirEnumeratorUnix final : public nsSimpleEnumerator,
127 public nsIDirectoryEnumerator {
128 public:
129 nsDirEnumeratorUnix();
131 // nsISupports interface
132 NS_DECL_ISUPPORTS_INHERITED
134 // nsISimpleEnumerator interface
135 NS_DECL_NSISIMPLEENUMERATOR
137 // nsIDirectoryEnumerator interface
138 NS_DECL_NSIDIRECTORYENUMERATOR
140 NS_IMETHOD Init(nsLocalFile* aParent, bool aIgnored);
142 NS_FORWARD_NSISIMPLEENUMERATORBASE(nsSimpleEnumerator::)
144 const nsID& DefaultInterface() override { return NS_GET_IID(nsIFile); }
146 private:
147 ~nsDirEnumeratorUnix() override;
149 protected:
150 NS_IMETHOD GetNextEntry();
152 DIR* mDir;
153 struct dirent* mEntry;
154 nsCString mParentPath;
157 nsDirEnumeratorUnix::nsDirEnumeratorUnix() : mDir(nullptr), mEntry(nullptr) {}
159 nsDirEnumeratorUnix::~nsDirEnumeratorUnix() { Close(); }
161 NS_IMPL_ISUPPORTS_INHERITED(nsDirEnumeratorUnix, nsSimpleEnumerator,
162 nsIDirectoryEnumerator)
164 NS_IMETHODIMP
165 nsDirEnumeratorUnix::Init(nsLocalFile* aParent,
166 bool aResolveSymlinks /*ignored*/) {
167 nsAutoCString dirPath;
168 if (NS_FAILED(aParent->GetNativePath(dirPath)) || dirPath.IsEmpty()) {
169 return NS_ERROR_FILE_INVALID_PATH;
172 // When enumerating the directory, the paths must have a slash at the end.
173 nsAutoCString dirPathWithSlash(dirPath);
174 dirPathWithSlash.Append('/');
175 if (!FilePreferences::IsAllowedPath(dirPathWithSlash)) {
176 return NS_ERROR_FILE_ACCESS_DENIED;
179 if (NS_FAILED(aParent->GetNativePath(mParentPath))) {
180 return NS_ERROR_FAILURE;
183 mDir = opendir(dirPath.get());
184 if (!mDir) {
185 return NSRESULT_FOR_ERRNO();
187 return GetNextEntry();
190 NS_IMETHODIMP
191 nsDirEnumeratorUnix::HasMoreElements(bool* aResult) {
192 *aResult = mDir && mEntry;
193 if (!*aResult) {
194 Close();
196 return NS_OK;
199 NS_IMETHODIMP
200 nsDirEnumeratorUnix::GetNext(nsISupports** aResult) {
201 nsCOMPtr<nsIFile> file;
202 nsresult rv = GetNextFile(getter_AddRefs(file));
203 if (NS_FAILED(rv)) {
204 return rv;
206 if (!file) {
207 return NS_ERROR_FAILURE;
209 file.forget(aResult);
210 return NS_OK;
213 NS_IMETHODIMP
214 nsDirEnumeratorUnix::GetNextEntry() {
215 do {
216 errno = 0;
217 mEntry = readdir(mDir);
219 // end of dir or error
220 if (!mEntry) {
221 return NSRESULT_FOR_ERRNO();
224 // keep going past "." and ".."
225 } while (mEntry->d_name[0] == '.' &&
226 (mEntry->d_name[1] == '\0' || // .\0
227 (mEntry->d_name[1] == '.' && mEntry->d_name[2] == '\0'))); // ..\0
228 return NS_OK;
231 NS_IMETHODIMP
232 nsDirEnumeratorUnix::GetNextFile(nsIFile** aResult) {
233 nsresult rv;
234 if (!mDir || !mEntry) {
235 *aResult = nullptr;
236 return NS_OK;
239 nsCOMPtr<nsIFile> file = new nsLocalFile();
241 if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
242 NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name)))) {
243 return rv;
246 file.forget(aResult);
247 return GetNextEntry();
250 NS_IMETHODIMP
251 nsDirEnumeratorUnix::Close() {
252 if (mDir) {
253 closedir(mDir);
254 mDir = nullptr;
256 return NS_OK;
259 nsLocalFile::nsLocalFile() : mCachedStat() {}
261 nsLocalFile::nsLocalFile(const nsACString& aFilePath) : mCachedStat() {
262 InitWithNativePath(aFilePath);
265 nsLocalFile::nsLocalFile(const nsLocalFile& aOther) : mPath(aOther.mPath) {}
267 #ifdef MOZ_WIDGET_COCOA
268 NS_IMPL_ISUPPORTS(nsLocalFile, nsILocalFileMac, nsIFile)
269 #else
270 NS_IMPL_ISUPPORTS(nsLocalFile, nsIFile)
271 #endif
273 nsresult nsLocalFile::nsLocalFileConstructor(const nsIID& aIID,
274 void** aInstancePtr) {
275 if (NS_WARN_IF(!aInstancePtr)) {
276 return NS_ERROR_INVALID_ARG;
279 *aInstancePtr = nullptr;
281 nsCOMPtr<nsIFile> inst = new nsLocalFile();
282 return inst->QueryInterface(aIID, aInstancePtr);
285 bool nsLocalFile::FillStatCache() {
286 if (!FilePreferences::IsAllowedPath(mPath)) {
287 errno = EACCES;
288 return false;
291 if (STAT(mPath.get(), &mCachedStat) == -1) {
292 // try lstat it may be a symlink
293 if (LSTAT(mPath.get(), &mCachedStat) == -1) {
294 return false;
297 return true;
300 NS_IMETHODIMP
301 nsLocalFile::Clone(nsIFile** aFile) {
302 // Just copy-construct ourselves
303 RefPtr<nsLocalFile> copy = new nsLocalFile(*this);
304 copy.forget(aFile);
305 return NS_OK;
308 NS_IMETHODIMP
309 nsLocalFile::InitWithNativePath(const nsACString& aFilePath) {
310 if (!aFilePath.IsEmpty() && aFilePath.First() == '~') {
311 if (aFilePath.Length() == 1 || aFilePath.CharAt(1) == '/') {
312 // Home dir for the current user
314 nsCOMPtr<nsIFile> homeDir;
315 nsAutoCString homePath;
316 if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
317 getter_AddRefs(homeDir))) ||
318 NS_FAILED(homeDir->GetNativePath(homePath))) {
319 return NS_ERROR_FAILURE;
322 mPath = homePath;
323 if (aFilePath.Length() > 2) {
324 mPath.Append(Substring(aFilePath, 1));
326 } else {
327 // Home dir for an arbitrary user e.g. `~foo/bar` -> `/home/foo/bar`
328 // (`/Users/foo/bar` on Mac). The accurate way to get this directory
329 // is with `getpwnam`, but we would like to avoid doing blocking
330 // filesystem I/O while creating an `nsIFile`.
332 mPath =
333 #ifdef XP_MACOSX
334 "/Users/"_ns
335 #else
336 "/home/"_ns
337 #endif
338 + Substring(aFilePath, 1);
340 } else {
341 if (aFilePath.IsEmpty() || aFilePath.First() != '/') {
342 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
344 mPath = aFilePath;
347 if (!FilePreferences::IsAllowedPath(mPath)) {
348 mPath.Truncate();
349 return NS_ERROR_FILE_ACCESS_DENIED;
352 // trim off trailing slashes
353 ssize_t len = mPath.Length();
354 while ((len > 1) && (mPath[len - 1] == '/')) {
355 --len;
357 mPath.SetLength(len);
359 return NS_OK;
362 NS_IMETHODIMP
363 nsLocalFile::CreateAllAncestors(uint32_t aPermissions) {
364 if (!FilePreferences::IsAllowedPath(mPath)) {
365 return NS_ERROR_FILE_ACCESS_DENIED;
368 // <jband> I promise to play nice
369 char* buffer = mPath.BeginWriting();
370 char* slashp = buffer;
371 int mkdir_result = 0;
372 int mkdir_errno;
374 #ifdef DEBUG_NSIFILE
375 fprintf(stderr, "nsIFile: before: %s\n", buffer);
376 #endif
378 while ((slashp = strchr(slashp + 1, '/'))) {
380 * Sequences of '/' are equivalent to a single '/'.
382 if (slashp[1] == '/') {
383 continue;
387 * If the path has a trailing slash, don't make the last component,
388 * because we'll get EEXIST in Create when we try to build the final
389 * component again, and it's easier to condition the logic here than
390 * there.
392 if (slashp[1] == '\0') {
393 break;
396 /* Temporarily NUL-terminate here */
397 *slashp = '\0';
398 #ifdef DEBUG_NSIFILE
399 fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
400 #endif
401 mkdir_result = mkdir(buffer, aPermissions);
402 if (mkdir_result == -1) {
403 mkdir_errno = errno;
405 * Always set |errno| to EEXIST if the dir already exists
406 * (we have to do this here since the errno value is not consistent
407 * in all cases - various reasons like different platform,
408 * automounter-controlled dir, etc. can affect it (see bug 125489
409 * for details)).
411 if (mkdir_errno != EEXIST && access(buffer, F_OK) == 0) {
412 mkdir_errno = EEXIST;
414 #ifdef DEBUG_NSIFILE
415 fprintf(stderr, "nsIFile: errno: %d\n", mkdir_errno);
416 #endif
419 /* Put the / back */
420 *slashp = '/';
424 * We could get EEXIST for an existing file -- not directory --
425 * but that's OK: we'll get ENOTDIR when we try to make the final
426 * component of the path back in Create and error out appropriately.
428 if (mkdir_result == -1 && mkdir_errno != EEXIST) {
429 return NS_ERROR_FAILURE;
432 return NS_OK;
435 NS_IMETHODIMP
436 nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
437 PRFileDesc** aResult) {
438 if (!FilePreferences::IsAllowedPath(mPath)) {
439 return NS_ERROR_FILE_ACCESS_DENIED;
441 *aResult = PR_Open(mPath.get(), aFlags, aMode);
442 if (!*aResult) {
443 return NS_ErrorAccordingToNSPR();
446 if (aFlags & DELETE_ON_CLOSE) {
447 PR_Delete(mPath.get());
450 #if defined(HAVE_POSIX_FADVISE)
451 if (aFlags & OS_READAHEAD) {
452 posix_fadvise(PR_FileDesc2NativeHandle(*aResult), 0, 0,
453 POSIX_FADV_SEQUENTIAL);
455 #endif
456 return NS_OK;
459 NS_IMETHODIMP
460 nsLocalFile::OpenANSIFileDesc(const char* aMode, FILE** aResult) {
461 if (!FilePreferences::IsAllowedPath(mPath)) {
462 return NS_ERROR_FILE_ACCESS_DENIED;
464 *aResult = fopen(mPath.get(), aMode);
465 if (!*aResult) {
466 return NS_ERROR_FAILURE;
469 return NS_OK;
472 static int do_create(const char* aPath, int aFlags, mode_t aMode,
473 PRFileDesc** aResult) {
474 *aResult = PR_Open(aPath, aFlags, aMode);
475 return *aResult ? 0 : -1;
478 static int do_mkdir(const char* aPath, int aFlags, mode_t aMode,
479 PRFileDesc** aResult) {
480 *aResult = nullptr;
481 return mkdir(aPath, aMode);
484 nsresult nsLocalFile::CreateAndKeepOpen(uint32_t aType, int aFlags,
485 uint32_t aPermissions,
486 bool aSkipAncestors,
487 PRFileDesc** aResult) {
488 if (!FilePreferences::IsAllowedPath(mPath)) {
489 return NS_ERROR_FILE_ACCESS_DENIED;
492 if (aType != NORMAL_FILE_TYPE && aType != DIRECTORY_TYPE) {
493 return NS_ERROR_FILE_UNKNOWN_TYPE;
496 int (*createFunc)(const char*, int, mode_t, PRFileDesc**) =
497 (aType == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
499 int result = createFunc(mPath.get(), aFlags, aPermissions, aResult);
500 if (result == -1 && errno == ENOENT && !aSkipAncestors) {
502 * If we failed because of missing ancestor components, try to create
503 * them and then retry the original creation.
505 * Ancestor directories get the same permissions as the file we're
506 * creating, with the X bit set for each of (user,group,other) with
507 * an R bit in the original permissions. If you want to do anything
508 * fancy like setgid or sticky bits, do it by hand.
510 int dirperm = aPermissions;
511 if (aPermissions & S_IRUSR) {
512 dirperm |= S_IXUSR;
514 if (aPermissions & S_IRGRP) {
515 dirperm |= S_IXGRP;
517 if (aPermissions & S_IROTH) {
518 dirperm |= S_IXOTH;
521 #ifdef DEBUG_NSIFILE
522 fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", aPermissions,
523 dirperm);
524 #endif
526 if (NS_FAILED(CreateAllAncestors(dirperm))) {
527 return NS_ERROR_FAILURE;
530 #ifdef DEBUG_NSIFILE
531 fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
532 #endif
533 result = createFunc(mPath.get(), aFlags, aPermissions, aResult);
535 return NSRESULT_FOR_RETURN(result);
538 NS_IMETHODIMP
539 nsLocalFile::Create(uint32_t aType, uint32_t aPermissions,
540 bool aSkipAncestors) {
541 if (!FilePreferences::IsAllowedPath(mPath)) {
542 return NS_ERROR_FILE_ACCESS_DENIED;
545 PRFileDesc* junk = nullptr;
546 nsresult rv = CreateAndKeepOpen(
547 aType, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE | PR_EXCL, aPermissions,
548 aSkipAncestors, &junk);
549 if (junk) {
550 PR_Close(junk);
552 return rv;
555 NS_IMETHODIMP
556 nsLocalFile::AppendNative(const nsACString& aFragment) {
557 if (aFragment.IsEmpty()) {
558 return NS_OK;
561 // only one component of path can be appended and cannot append ".."
562 nsACString::const_iterator begin, end;
563 if (aFragment.EqualsASCII("..") ||
564 FindCharInReadable('/', aFragment.BeginReading(begin),
565 aFragment.EndReading(end))) {
566 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
569 return AppendRelativeNativePath(aFragment);
572 NS_IMETHODIMP
573 nsLocalFile::AppendRelativeNativePath(const nsACString& aFragment) {
574 if (aFragment.IsEmpty()) {
575 return NS_OK;
578 // No leading '/' and cannot be ".."
579 if (aFragment.First() == '/' || aFragment.EqualsASCII("..")) {
580 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
583 if (aFragment.Contains('/')) {
584 // can't contain .. as a path component. Ensure that the valid components
585 // "foo..foo", "..foo", and "foo.." are not falsely detected,
586 // but the invalid paths "../", "foo/..", "foo/../foo",
587 // "../foo", etc are.
588 constexpr auto doubleDot = "/.."_ns;
589 nsACString::const_iterator start, end, offset;
590 aFragment.BeginReading(start);
591 aFragment.EndReading(end);
592 offset = end;
593 while (FindInReadable(doubleDot, start, offset)) {
594 if (offset == end || *offset == '/') {
595 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
597 start = offset;
598 offset = end;
601 // catches the remaining cases of prefixes
602 if (StringBeginsWith(aFragment, "../"_ns)) {
603 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
607 if (!mPath.EqualsLiteral("/")) {
608 mPath.Append('/');
610 mPath.Append(aFragment);
612 return NS_OK;
615 NS_IMETHODIMP
616 nsLocalFile::Normalize() {
617 char resolved_path[PATH_MAX] = "";
618 char* resolved_path_ptr = nullptr;
620 if (!FilePreferences::IsAllowedPath(mPath)) {
621 return NS_ERROR_FILE_ACCESS_DENIED;
624 resolved_path_ptr = realpath(mPath.get(), resolved_path);
626 // if there is an error, the return is null.
627 if (!resolved_path_ptr) {
628 return NSRESULT_FOR_ERRNO();
631 mPath = resolved_path;
632 return NS_OK;
635 void nsLocalFile::LocateNativeLeafName(nsACString::const_iterator& aBegin,
636 nsACString::const_iterator& aEnd) {
637 // XXX perhaps we should cache this??
639 mPath.BeginReading(aBegin);
640 mPath.EndReading(aEnd);
642 nsACString::const_iterator it = aEnd;
643 nsACString::const_iterator stop = aBegin;
644 --stop;
645 while (--it != stop) {
646 if (*it == '/') {
647 aBegin = ++it;
648 return;
651 // else, the entire path is the leaf name (which means this
652 // isn't an absolute path... unexpected??)
655 NS_IMETHODIMP
656 nsLocalFile::GetNativeLeafName(nsACString& aLeafName) {
657 nsACString::const_iterator begin, end;
658 LocateNativeLeafName(begin, end);
659 aLeafName = Substring(begin, end);
660 return NS_OK;
663 NS_IMETHODIMP
664 nsLocalFile::SetNativeLeafName(const nsACString& aLeafName) {
665 nsACString::const_iterator begin, end;
666 LocateNativeLeafName(begin, end);
667 mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
668 return NS_OK;
671 NS_IMETHODIMP
672 nsLocalFile::GetDisplayName(nsAString& aLeafName) {
673 return GetLeafName(aLeafName);
676 nsCString nsLocalFile::NativePath() { return mPath; }
678 nsresult nsIFile::GetNativePath(nsACString& aResult) {
679 aResult = NativePath();
680 return NS_OK;
683 nsCString nsIFile::HumanReadablePath() {
684 nsCString path;
685 DebugOnly<nsresult> rv = GetNativePath(path);
686 MOZ_ASSERT(NS_SUCCEEDED(rv));
687 return path;
690 nsresult nsLocalFile::GetNativeTargetPathName(nsIFile* aNewParent,
691 const nsACString& aNewName,
692 nsACString& aResult) {
693 nsresult rv;
694 nsCOMPtr<nsIFile> oldParent;
696 if (!aNewParent) {
697 if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent)))) {
698 return rv;
700 aNewParent = oldParent.get();
701 } else {
702 // check to see if our target directory exists
703 bool targetExists;
704 if (NS_FAILED(rv = aNewParent->Exists(&targetExists))) {
705 return rv;
708 if (!targetExists) {
709 // XXX create the new directory with some permissions
710 rv = aNewParent->Create(DIRECTORY_TYPE, 0755);
711 if (NS_FAILED(rv)) {
712 return rv;
714 } else {
715 // make sure that the target is actually a directory
716 bool targetIsDirectory;
717 if (NS_FAILED(rv = aNewParent->IsDirectory(&targetIsDirectory))) {
718 return rv;
720 if (!targetIsDirectory) {
721 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
726 nsACString::const_iterator nameBegin, nameEnd;
727 if (!aNewName.IsEmpty()) {
728 aNewName.BeginReading(nameBegin);
729 aNewName.EndReading(nameEnd);
730 } else {
731 LocateNativeLeafName(nameBegin, nameEnd);
734 nsAutoCString dirName;
735 if (NS_FAILED(rv = aNewParent->GetNativePath(dirName))) {
736 return rv;
739 aResult = dirName + "/"_ns + Substring(nameBegin, nameEnd);
740 return NS_OK;
743 nsresult nsLocalFile::CopyDirectoryTo(nsIFile* aNewParent) {
744 nsresult rv;
746 * dirCheck is used for various boolean test results such as from Equals,
747 * Exists, isDir, etc.
749 bool dirCheck, isSymlink;
750 uint32_t oldPerms;
752 if (NS_FAILED(rv = IsDirectory(&dirCheck))) {
753 return rv;
755 if (!dirCheck) {
756 return CopyToNative(aNewParent, ""_ns);
759 if (NS_FAILED(rv = Equals(aNewParent, &dirCheck))) {
760 return rv;
762 if (dirCheck) {
763 // can't copy dir to itself
764 return NS_ERROR_INVALID_ARG;
767 if (NS_FAILED(rv = aNewParent->Exists(&dirCheck))) {
768 return rv;
770 // get the dirs old permissions
771 if (NS_FAILED(rv = GetPermissions(&oldPerms))) {
772 return rv;
774 if (!dirCheck) {
775 if (NS_FAILED(rv = aNewParent->Create(DIRECTORY_TYPE, oldPerms))) {
776 return rv;
778 } else { // dir exists lets try to use leaf
779 nsAutoCString leafName;
780 if (NS_FAILED(rv = GetNativeLeafName(leafName))) {
781 return rv;
783 if (NS_FAILED(rv = aNewParent->AppendNative(leafName))) {
784 return rv;
786 if (NS_FAILED(rv = aNewParent->Exists(&dirCheck))) {
787 return rv;
789 if (dirCheck) {
790 return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
792 if (NS_FAILED(rv = aNewParent->Create(DIRECTORY_TYPE, oldPerms))) {
793 return rv;
797 nsCOMPtr<nsIDirectoryEnumerator> dirIterator;
798 if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator)))) {
799 return rv;
802 nsCOMPtr<nsIFile> entry;
803 while (NS_SUCCEEDED(dirIterator->GetNextFile(getter_AddRefs(entry))) &&
804 entry) {
805 if (NS_FAILED(rv = entry->IsSymlink(&isSymlink))) {
806 return rv;
808 if (NS_FAILED(rv = entry->IsDirectory(&dirCheck))) {
809 return rv;
811 if (dirCheck && !isSymlink) {
812 nsCOMPtr<nsIFile> destClone;
813 rv = aNewParent->Clone(getter_AddRefs(destClone));
814 if (NS_SUCCEEDED(rv)) {
815 if (NS_FAILED(rv = entry->CopyToNative(destClone, ""_ns))) {
816 #ifdef DEBUG
817 nsresult rv2;
818 nsAutoCString pathName;
819 if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) {
820 return rv2;
822 printf("Operation not supported: %s\n", pathName.get());
823 #endif
824 if (rv == NS_ERROR_OUT_OF_MEMORY) {
825 return rv;
827 continue;
830 } else {
831 if (NS_FAILED(rv = entry->CopyToNative(aNewParent, ""_ns))) {
832 #ifdef DEBUG
833 nsresult rv2;
834 nsAutoCString pathName;
835 if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) {
836 return rv2;
838 printf("Operation not supported: %s\n", pathName.get());
839 #endif
840 if (rv == NS_ERROR_OUT_OF_MEMORY) {
841 return rv;
843 continue;
847 return NS_OK;
850 NS_IMETHODIMP
851 nsLocalFile::CopyToNative(nsIFile* aNewParent, const nsACString& aNewName) {
852 nsresult rv;
853 // check to make sure that this has been initialized properly
854 CHECK_mPath();
856 // we copy the parent here so 'aNewParent' remains immutable
857 nsCOMPtr<nsIFile> workParent;
858 if (aNewParent) {
859 if (NS_FAILED(rv = aNewParent->Clone(getter_AddRefs(workParent)))) {
860 return rv;
862 } else {
863 if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent)))) {
864 return rv;
868 // check to see if we are a directory or if we are a file
869 bool isDirectory;
870 if (NS_FAILED(rv = IsDirectory(&isDirectory))) {
871 return rv;
874 nsAutoCString newPathName;
875 if (isDirectory) {
876 if (!aNewName.IsEmpty()) {
877 if (NS_FAILED(rv = workParent->AppendNative(aNewName))) {
878 return rv;
880 } else {
881 if (NS_FAILED(rv = GetNativeLeafName(newPathName))) {
882 return rv;
884 if (NS_FAILED(rv = workParent->AppendNative(newPathName))) {
885 return rv;
888 if (NS_FAILED(rv = CopyDirectoryTo(workParent))) {
889 return rv;
891 } else {
892 rv = GetNativeTargetPathName(workParent, aNewName, newPathName);
893 if (NS_FAILED(rv)) {
894 return rv;
897 #ifdef DEBUG_blizzard
898 printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
899 #endif
901 // actually create the file.
902 auto* newFile = new nsLocalFile();
903 nsCOMPtr<nsIFile> fileRef(newFile); // release on exit
905 rv = newFile->InitWithNativePath(newPathName);
906 if (NS_FAILED(rv)) {
907 return rv;
910 // get the old permissions
911 uint32_t myPerms = 0;
912 rv = GetPermissions(&myPerms);
913 if (NS_FAILED(rv)) {
914 return rv;
917 // Create the new file with the old file's permissions, even if write
918 // permission is missing. We can't create with write permission and
919 // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
920 // But we can write to a read-only file on all Unix filesystems if we
921 // open it successfully for writing.
923 PRFileDesc* newFD;
924 rv = newFile->CreateAndKeepOpen(
925 NORMAL_FILE_TYPE, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, myPerms,
926 /* aSkipAncestors = */ false, &newFD);
927 if (NS_FAILED(rv)) {
928 return rv;
931 // open the old file, too
932 bool specialFile;
933 if (NS_FAILED(rv = IsSpecial(&specialFile))) {
934 PR_Close(newFD);
935 return rv;
937 if (specialFile) {
938 #ifdef DEBUG
939 printf("Operation not supported: %s\n", mPath.get());
940 #endif
941 // make sure to clean up properly
942 PR_Close(newFD);
943 return NS_OK;
946 #if defined(XP_MACOSX)
947 bool quarantined = true;
948 (void)HasXAttr("com.apple.quarantine"_ns, &quarantined);
949 #endif
951 PRFileDesc* oldFD;
952 rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
953 if (NS_FAILED(rv)) {
954 // make sure to clean up properly
955 PR_Close(newFD);
956 return rv;
959 #ifdef DEBUG_blizzard
960 int32_t totalRead = 0;
961 int32_t totalWritten = 0;
962 #endif
963 char buf[BUFSIZ];
964 int32_t bytesRead;
966 // record PR_Write() error for better error message later.
967 nsresult saved_write_error = NS_OK;
968 nsresult saved_read_error = NS_OK;
969 nsresult saved_read_close_error = NS_OK;
970 nsresult saved_write_close_error = NS_OK;
972 // DONE: Does PR_Read() return bytesRead < 0 for error?
973 // Yes., The errors from PR_Read are not so common and
974 // the value may not have correspondence in NS_ERROR_*, but
975 // we do catch it still, immediately after while() loop.
976 // We can differentiate errors pf PR_Read and PR_Write by
977 // looking at saved_write_error value. If PR_Write error occurs (and not
978 // PR_Read() error), save_write_error is not NS_OK.
980 while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
981 #ifdef DEBUG_blizzard
982 totalRead += bytesRead;
983 #endif
985 // PR_Write promises never to do a short write
986 int32_t bytesWritten = PR_Write(newFD, buf, bytesRead);
987 if (bytesWritten < 0) {
988 saved_write_error = NSRESULT_FOR_ERRNO();
989 bytesRead = -1;
990 break;
992 NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
994 #ifdef DEBUG_blizzard
995 totalWritten += bytesWritten;
996 #endif
999 // TODO/FIXME: If CIFS (and NFS?) may force read/write to return EINTR,
1000 // we are better off to prepare for retrying. But we need confirmation if
1001 // EINTR is returned.
1003 // Record error if PR_Read() failed.
1004 // Must be done before any other I/O which may reset errno.
1005 if (bytesRead < 0 && saved_write_error == NS_OK) {
1006 saved_read_error = NSRESULT_FOR_ERRNO();
1009 #ifdef DEBUG_blizzard
1010 printf("read %d bytes, wrote %d bytes\n", totalRead, totalWritten);
1011 #endif
1013 // DONE: Errors of close can occur. Read man page of
1014 // close(2);
1015 // This is likely to happen if the file system is remote file
1016 // system (NFS, CIFS, etc.) and network outage occurs.
1017 // At least, we should tell the user that filesystem/disk is
1018 // hosed (possibly due to network error, hard disk failure,
1019 // etc.) so that users can take remedial action.
1021 // close the files
1022 if (PR_Close(newFD) < 0) {
1023 saved_write_close_error = NSRESULT_FOR_ERRNO();
1024 #if DEBUG
1025 // This error merits printing.
1026 fprintf(stderr, "ERROR: PR_Close(newFD) returned error. errno = %d\n",
1027 errno);
1028 #endif
1030 #if defined(XP_MACOSX)
1031 else if (!quarantined) {
1032 // If the original file was not in quarantine, lift the quarantine that
1033 // file creation added because of LSFileQuarantineEnabled.
1034 (void)newFile->DelXAttr("com.apple.quarantine"_ns);
1036 #endif // defined(XP_MACOSX)
1038 if (PR_Close(oldFD) < 0) {
1039 saved_read_close_error = NSRESULT_FOR_ERRNO();
1040 #if DEBUG
1041 fprintf(stderr, "ERROR: PR_Close(oldFD) returned error. errno = %d\n",
1042 errno);
1043 #endif
1046 // Let us report the failure to write and read.
1047 // check for write/read error after cleaning up
1048 if (bytesRead < 0) {
1049 if (saved_write_error != NS_OK) {
1050 return saved_write_error;
1052 if (saved_read_error != NS_OK) {
1053 return saved_read_error;
1055 #if DEBUG
1056 MOZ_ASSERT(0);
1057 #endif
1060 if (saved_write_close_error != NS_OK) {
1061 return saved_write_close_error;
1063 if (saved_read_close_error != NS_OK) {
1064 return saved_read_close_error;
1067 return rv;
1070 NS_IMETHODIMP
1071 nsLocalFile::CopyToFollowingLinksNative(nsIFile* aNewParent,
1072 const nsACString& aNewName) {
1073 return CopyToNative(aNewParent, aNewName);
1076 NS_IMETHODIMP
1077 nsLocalFile::MoveToNative(nsIFile* aNewParent, const nsACString& aNewName) {
1078 nsresult rv;
1080 // check to make sure that this has been initialized properly
1081 CHECK_mPath();
1083 // check to make sure that we have a new parent
1084 nsAutoCString newPathName;
1085 rv = GetNativeTargetPathName(aNewParent, aNewName, newPathName);
1086 if (NS_FAILED(rv)) {
1087 return rv;
1090 if (!FilePreferences::IsAllowedPath(newPathName)) {
1091 return NS_ERROR_FILE_ACCESS_DENIED;
1094 // try for atomic rename, falling back to copy/delete
1095 if (rename(mPath.get(), newPathName.get()) < 0) {
1096 if (errno == EXDEV) {
1097 rv = CopyToNative(aNewParent, aNewName);
1098 if (NS_SUCCEEDED(rv)) {
1099 rv = Remove(true);
1101 } else {
1102 rv = NSRESULT_FOR_ERRNO();
1106 if (NS_SUCCEEDED(rv)) {
1107 // Adjust this
1108 mPath = newPathName;
1110 return rv;
1113 NS_IMETHODIMP
1114 nsLocalFile::MoveToFollowingLinksNative(nsIFile* aNewParent,
1115 const nsACString& aNewName) {
1116 return MoveToNative(aNewParent, aNewName);
1119 NS_IMETHODIMP
1120 nsLocalFile::Remove(bool aRecursive, uint32_t* aRemoveCount) {
1121 CHECK_mPath();
1122 ENSURE_STAT_CACHE();
1124 bool isSymLink;
1126 nsresult rv = IsSymlink(&isSymLink);
1127 if (NS_FAILED(rv)) {
1128 return rv;
1131 if (isSymLink || !S_ISDIR(mCachedStat.st_mode)) {
1132 rv = NSRESULT_FOR_RETURN(unlink(mPath.get()));
1133 if (NS_SUCCEEDED(rv) && aRemoveCount) {
1134 *aRemoveCount += 1;
1136 return rv;
1139 if (aRecursive) {
1140 auto* dir = new nsDirEnumeratorUnix();
1142 RefPtr<nsSimpleEnumerator> dirRef(dir); // release on exit
1144 rv = dir->Init(this, false);
1145 if (NS_FAILED(rv)) {
1146 return rv;
1149 bool more;
1150 while (NS_SUCCEEDED(dir->HasMoreElements(&more)) && more) {
1151 nsCOMPtr<nsISupports> item;
1152 rv = dir->GetNext(getter_AddRefs(item));
1153 if (NS_FAILED(rv)) {
1154 return NS_ERROR_FAILURE;
1157 nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
1158 if (NS_FAILED(rv)) {
1159 return NS_ERROR_FAILURE;
1161 // XXX: We care the result of the removal here while
1162 // nsLocalFileWin does not. We should align the behavior. (bug 1779696)
1163 rv = file->Remove(aRecursive, aRemoveCount);
1165 #ifdef ANDROID
1166 // See bug 580434 - Bionic gives us just deleted files
1167 if (rv == NS_ERROR_FILE_NOT_FOUND) {
1168 continue;
1170 #endif
1171 if (NS_FAILED(rv)) {
1172 return rv;
1177 rv = NSRESULT_FOR_RETURN(rmdir(mPath.get()));
1178 if (NS_SUCCEEDED(rv) && aRemoveCount) {
1179 *aRemoveCount += 1;
1181 return rv;
1184 nsresult nsLocalFile::GetTimeImpl(PRTime* aTime,
1185 nsLocalFile::TimeField aTimeField,
1186 bool aFollowLinks) {
1187 CHECK_mPath();
1188 if (NS_WARN_IF(!aTime)) {
1189 return NS_ERROR_INVALID_ARG;
1192 using StatFn = int (*)(const char*, struct STAT*);
1193 StatFn statFn = aFollowLinks ? &STAT : &LSTAT;
1195 struct STAT fileStats {};
1196 if (statFn(mPath.get(), &fileStats) < 0) {
1197 return NSRESULT_FOR_ERRNO();
1200 struct timespec* timespec;
1201 switch (aTimeField) {
1202 case TimeField::AccessedTime:
1203 #if (defined(__APPLE__) && defined(__MACH__))
1204 timespec = &fileStats.st_atimespec;
1205 #else
1206 timespec = &fileStats.st_atim;
1207 #endif
1208 break;
1210 case TimeField::ModifiedTime:
1211 #if (defined(__APPLE__) && defined(__MACH__))
1212 timespec = &fileStats.st_mtimespec;
1213 #else
1214 timespec = &fileStats.st_mtim;
1215 #endif
1216 break;
1218 default:
1219 MOZ_CRASH("Unknown TimeField");
1222 *aTime = TimespecToMillis(*timespec);
1224 return NS_OK;
1227 nsresult nsLocalFile::SetTimeImpl(PRTime aTime,
1228 nsLocalFile::TimeField aTimeField,
1229 bool aFollowLinks) {
1230 CHECK_mPath();
1232 using UtimesFn = int (*)(const char*, const timeval*);
1233 UtimesFn utimesFn = &utimes;
1235 #if HAVE_LUTIMES
1236 if (!aFollowLinks) {
1237 utimesFn = &lutimes;
1239 #endif
1241 ENSURE_STAT_CACHE();
1243 if (aTime == 0) {
1244 aTime = PR_Now();
1247 // We only want to write to a single field (accessed time or modified time),
1248 // but utimes() doesn't let you omit one. If you do, it will set that field to
1249 // the current time, which is not what we want.
1251 // So what we do is write to both fields, but copy one of the fields from our
1252 // cached stat structure.
1254 // If we are writing to the accessed time field, then we want to copy the
1255 // modified time and vice versa.
1257 timeval times[2];
1259 const size_t writeIndex = aTimeField == TimeField::AccessedTime ? 0 : 1;
1260 const size_t copyIndex = aTimeField == TimeField::AccessedTime ? 1 : 0;
1262 #if (defined(__APPLE__) && defined(__MACH__))
1263 auto* copyFrom = aTimeField == TimeField::AccessedTime
1264 ? &mCachedStat.st_mtimespec
1265 : &mCachedStat.st_atimespec;
1266 #else
1267 auto* copyFrom = aTimeField == TimeField::AccessedTime ? &mCachedStat.st_mtim
1268 : &mCachedStat.st_atim;
1269 #endif
1271 times[copyIndex].tv_sec = copyFrom->tv_sec;
1272 times[copyIndex].tv_usec = copyFrom->tv_nsec / 1000;
1274 times[writeIndex].tv_sec = aTime / PR_MSEC_PER_SEC;
1275 times[writeIndex].tv_usec = (aTime % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC;
1277 int result = utimesFn(mPath.get(), times);
1278 return NSRESULT_FOR_RETURN(result);
1281 NS_IMETHODIMP
1282 nsLocalFile::GetLastAccessedTime(PRTime* aLastAccessedTime) {
1283 return GetTimeImpl(aLastAccessedTime, TimeField::AccessedTime,
1284 /* follow links? */ true);
1287 NS_IMETHODIMP
1288 nsLocalFile::SetLastAccessedTime(PRTime aLastAccessedTime) {
1289 return SetTimeImpl(aLastAccessedTime, TimeField::AccessedTime,
1290 /* follow links? */ true);
1293 NS_IMETHODIMP
1294 nsLocalFile::GetLastAccessedTimeOfLink(PRTime* aLastAccessedTime) {
1295 return GetTimeImpl(aLastAccessedTime, TimeField::AccessedTime,
1296 /* follow links? */ false);
1299 NS_IMETHODIMP
1300 nsLocalFile::SetLastAccessedTimeOfLink(PRTime aLastAccessedTime) {
1301 return SetTimeImpl(aLastAccessedTime, TimeField::AccessedTime,
1302 /* follow links? */ false);
1305 NS_IMETHODIMP
1306 nsLocalFile::GetLastModifiedTime(PRTime* aLastModTime) {
1307 return GetTimeImpl(aLastModTime, TimeField::ModifiedTime,
1308 /* follow links? */ true);
1311 NS_IMETHODIMP
1312 nsLocalFile::SetLastModifiedTime(PRTime aLastModTime) {
1313 return SetTimeImpl(aLastModTime, TimeField::ModifiedTime,
1314 /* follow links ? */ true);
1317 NS_IMETHODIMP
1318 nsLocalFile::GetLastModifiedTimeOfLink(PRTime* aLastModTimeOfLink) {
1319 return GetTimeImpl(aLastModTimeOfLink, TimeField::ModifiedTime,
1320 /* follow link? */ false);
1323 NS_IMETHODIMP
1324 nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink) {
1325 return SetTimeImpl(aLastModTimeOfLink, TimeField::ModifiedTime,
1326 /* follow links? */ false);
1329 NS_IMETHODIMP
1330 nsLocalFile::GetCreationTime(PRTime* aCreationTime) {
1331 return GetCreationTimeImpl(aCreationTime, false);
1334 NS_IMETHODIMP
1335 nsLocalFile::GetCreationTimeOfLink(PRTime* aCreationTimeOfLink) {
1336 return GetCreationTimeImpl(aCreationTimeOfLink, /* aFollowLinks = */ true);
1339 nsresult nsLocalFile::GetCreationTimeImpl(PRTime* aCreationTime,
1340 bool aFollowLinks) {
1341 CHECK_mPath();
1342 if (NS_WARN_IF(!aCreationTime)) {
1343 return NS_ERROR_INVALID_ARG;
1346 #if defined(_DARWIN_FEATURE_64_BIT_INODE)
1347 using StatFn = int (*)(const char*, struct STAT*);
1348 StatFn statFn = aFollowLinks ? &STAT : &LSTAT;
1350 struct STAT fileStats {};
1351 if (statFn(mPath.get(), &fileStats) < 0) {
1352 return NSRESULT_FOR_ERRNO();
1355 *aCreationTime = TimespecToMillis(fileStats.st_birthtimespec);
1356 return NS_OK;
1357 #else
1358 return NS_ERROR_NOT_IMPLEMENTED;
1359 #endif
1363 * Only send back permissions bits: maybe we want to send back the whole
1364 * mode_t to permit checks against other file types?
1367 #define NORMALIZE_PERMS(mode) ((mode) & (S_IRWXU | S_IRWXG | S_IRWXO))
1369 NS_IMETHODIMP
1370 nsLocalFile::GetPermissions(uint32_t* aPermissions) {
1371 if (NS_WARN_IF(!aPermissions)) {
1372 return NS_ERROR_INVALID_ARG;
1374 ENSURE_STAT_CACHE();
1375 *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
1376 return NS_OK;
1379 NS_IMETHODIMP
1380 nsLocalFile::GetPermissionsOfLink(uint32_t* aPermissionsOfLink) {
1381 CHECK_mPath();
1382 if (NS_WARN_IF(!aPermissionsOfLink)) {
1383 return NS_ERROR_INVALID_ARG;
1386 struct STAT sbuf;
1387 if (LSTAT(mPath.get(), &sbuf) == -1) {
1388 return NSRESULT_FOR_ERRNO();
1390 *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
1391 return NS_OK;
1394 NS_IMETHODIMP
1395 nsLocalFile::SetPermissions(uint32_t aPermissions) {
1396 CHECK_mPath();
1399 * Race condition here: we should use fchmod instead, there's no way to
1400 * guarantee the name still refers to the same file.
1402 if (chmod(mPath.get(), aPermissions) >= 0) {
1403 return NS_OK;
1405 #if defined(ANDROID) && defined(STATFS)
1406 // For the time being, this is restricted for use by Android, but we
1407 // will figure out what to do for all platforms in bug 638503
1408 struct STATFS sfs;
1409 if (STATFS(mPath.get(), &sfs) < 0) {
1410 return NSRESULT_FOR_ERRNO();
1413 // if this is a FAT file system we can't set file permissions
1414 if (sfs.f_type == MSDOS_SUPER_MAGIC) {
1415 return NS_OK;
1417 #endif
1418 return NSRESULT_FOR_ERRNO();
1421 NS_IMETHODIMP
1422 nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions) {
1423 // There isn't a consistent mechanism for doing this on UNIX platforms. We
1424 // might want to carefully implement this in the future though.
1425 return NS_ERROR_NOT_IMPLEMENTED;
1428 NS_IMETHODIMP
1429 nsLocalFile::GetFileSize(int64_t* aFileSize) {
1430 if (NS_WARN_IF(!aFileSize)) {
1431 return NS_ERROR_INVALID_ARG;
1433 *aFileSize = 0;
1434 ENSURE_STAT_CACHE();
1436 if (!S_ISDIR(mCachedStat.st_mode)) {
1437 *aFileSize = (int64_t)mCachedStat.st_size;
1439 return NS_OK;
1442 NS_IMETHODIMP
1443 nsLocalFile::SetFileSize(int64_t aFileSize) {
1444 CHECK_mPath();
1446 #if defined(ANDROID)
1447 /* no truncate on bionic */
1448 int fd = open(mPath.get(), O_WRONLY);
1449 if (fd == -1) {
1450 return NSRESULT_FOR_ERRNO();
1453 int ret = ftruncate(fd, (off_t)aFileSize);
1454 close(fd);
1456 if (ret == -1) {
1457 return NSRESULT_FOR_ERRNO();
1459 #elif defined(HAVE_TRUNCATE64)
1460 if (truncate64(mPath.get(), (off64_t)aFileSize) == -1) {
1461 return NSRESULT_FOR_ERRNO();
1463 #else
1464 off_t size = (off_t)aFileSize;
1465 if (truncate(mPath.get(), size) == -1) {
1466 return NSRESULT_FOR_ERRNO();
1468 #endif
1469 return NS_OK;
1472 NS_IMETHODIMP
1473 nsLocalFile::GetFileSizeOfLink(int64_t* aFileSize) {
1474 CHECK_mPath();
1475 if (NS_WARN_IF(!aFileSize)) {
1476 return NS_ERROR_INVALID_ARG;
1479 struct STAT sbuf;
1480 if (LSTAT(mPath.get(), &sbuf) == -1) {
1481 return NSRESULT_FOR_ERRNO();
1484 *aFileSize = (int64_t)sbuf.st_size;
1485 return NS_OK;
1488 #if defined(USE_LINUX_QUOTACTL)
1490 * Searches /proc/self/mountinfo for given device (Major:Minor),
1491 * returns exported name from /dev
1493 * Fails when /proc/self/mountinfo or diven device don't exist.
1495 static bool GetDeviceName(unsigned int aDeviceMajor, unsigned int aDeviceMinor,
1496 nsACString& aDeviceName) {
1497 bool ret = false;
1499 const int kMountInfoLineLength = 200;
1500 const int kMountInfoDevPosition = 6;
1502 char mountinfoLine[kMountInfoLineLength];
1503 char deviceNum[kMountInfoLineLength];
1505 SprintfLiteral(deviceNum, "%u:%u", aDeviceMajor, aDeviceMinor);
1507 FILE* f = fopen("/proc/self/mountinfo", "rt");
1508 if (!f) {
1509 return ret;
1512 // Expects /proc/self/mountinfo in format:
1513 // 'ID ID major:minor root mountpoint flags - type devicename flags'
1514 while (fgets(mountinfoLine, kMountInfoLineLength, f)) {
1515 char* p_dev = strstr(mountinfoLine, deviceNum);
1517 for (int i = 0; i < kMountInfoDevPosition && p_dev; ++i) {
1518 p_dev = strchr(p_dev, ' ');
1519 if (p_dev) {
1520 p_dev++;
1524 if (p_dev) {
1525 char* p_dev_end = strchr(p_dev, ' ');
1526 if (p_dev_end) {
1527 *p_dev_end = '\0';
1528 aDeviceName.Assign(p_dev);
1529 ret = true;
1530 break;
1535 fclose(f);
1536 return ret;
1538 #endif
1540 #if defined(USE_LINUX_QUOTACTL)
1541 template <typename StatInfoFunc, typename QuotaInfoFunc>
1542 nsresult nsLocalFile::GetDiskInfo(StatInfoFunc&& aStatInfoFunc,
1543 QuotaInfoFunc&& aQuotaInfoFunc,
1544 int64_t* aResult)
1545 #else
1546 template <typename StatInfoFunc>
1547 nsresult nsLocalFile::GetDiskInfo(StatInfoFunc&& aStatInfoFunc,
1548 int64_t* aResult)
1549 #endif
1551 if (NS_WARN_IF(!aResult)) {
1552 return NS_ERROR_INVALID_ARG;
1555 // These systems have the operations necessary to check disk space.
1557 #ifdef STATFS
1559 // check to make sure that mPath is properly initialized
1560 CHECK_mPath();
1562 struct STATFS fs_buf;
1565 * Members of the STATFS struct that you should know about:
1566 * F_BSIZE = block size on disk.
1567 * f_bavail = number of free blocks available to a non-superuser.
1568 * f_bfree = number of total free blocks in file system.
1569 * f_blocks = number of total used or free blocks in file system.
1572 if (STATFS(mPath.get(), &fs_buf) < 0) {
1573 // The call to STATFS failed.
1574 # ifdef DEBUG
1575 printf("ERROR: GetDiskInfo: STATFS call FAILED. \n");
1576 # endif
1577 return NS_ERROR_FAILURE;
1580 CheckedInt64 statfsResult = std::forward<StatInfoFunc>(aStatInfoFunc)(fs_buf);
1581 if (!statfsResult.isValid()) {
1582 return NS_ERROR_CANNOT_CONVERT_DATA;
1585 // Assign statfsResult to *aResult in case one of the quota calls fails.
1586 *aResult = statfsResult.value();
1588 # if defined(USE_LINUX_QUOTACTL)
1590 if (!FillStatCache()) {
1591 // Returns info from statfs
1592 return NS_OK;
1595 nsAutoCString deviceName;
1596 if (!GetDeviceName(major(mCachedStat.st_dev), minor(mCachedStat.st_dev),
1597 deviceName)) {
1598 // Returns info from statfs
1599 return NS_OK;
1602 struct dqblk dq;
1603 if (!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(), getuid(),
1604 (caddr_t)&dq)
1605 # ifdef QIF_BLIMITS
1606 && dq.dqb_valid & QIF_BLIMITS
1607 # endif
1608 && dq.dqb_bhardlimit) {
1609 CheckedInt64 quotaResult = std::forward<QuotaInfoFunc>(aQuotaInfoFunc)(dq);
1610 if (!quotaResult.isValid()) {
1611 // Returns info from statfs
1612 return NS_OK;
1615 if (quotaResult.value() < *aResult) {
1616 *aResult = quotaResult.value();
1619 # endif // defined(USE_LINUX_QUOTACTL)
1621 # ifdef DEBUG_DISK_SPACE
1622 printf("DiskInfo: %lu bytes\n", *aResult);
1623 # endif
1625 return NS_OK;
1627 #else // STATFS
1629 * This platform doesn't have statfs or statvfs. I'm sure that there's
1630 * a way to check for free disk space and disk capacity on platforms that
1631 * don't have statfs (I'm SURE they have df, for example).
1633 * Until we figure out how to do that, lets be honest and say that this
1634 * command isn't implemented properly for these platforms yet.
1636 # ifdef DEBUG
1637 printf("ERROR: GetDiskInfo: Not implemented for plaforms without statfs.\n");
1638 # endif
1639 return NS_ERROR_NOT_IMPLEMENTED;
1641 #endif // STATFS
1644 NS_IMETHODIMP
1645 nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable) {
1646 return GetDiskInfo(
1647 [](const struct STATFS& aStatInfo) {
1648 return aStatInfo.f_bavail * static_cast<uint64_t>(aStatInfo.F_BSIZE);
1650 #if defined(USE_LINUX_QUOTACTL)
1651 [](const struct dqblk& aQuotaInfo) -> uint64_t {
1652 // dqb_bhardlimit is count of BLOCK_SIZE blocks, dqb_curspace is bytes
1653 const uint64_t hardlimit = aQuotaInfo.dqb_bhardlimit * BLOCK_SIZE;
1654 if (hardlimit > aQuotaInfo.dqb_curspace) {
1655 return hardlimit - aQuotaInfo.dqb_curspace;
1657 return 0;
1659 #endif
1660 aDiskSpaceAvailable);
1663 NS_IMETHODIMP
1664 nsLocalFile::GetDiskCapacity(int64_t* aDiskCapacity) {
1665 return GetDiskInfo(
1666 [](const struct STATFS& aStatInfo) {
1667 return aStatInfo.f_blocks * static_cast<uint64_t>(aStatInfo.F_BSIZE);
1669 #if defined(USE_LINUX_QUOTACTL)
1670 [](const struct dqblk& aQuotaInfo) {
1671 // dqb_bhardlimit is count of BLOCK_SIZE blocks
1672 return aQuotaInfo.dqb_bhardlimit * BLOCK_SIZE;
1674 #endif
1675 aDiskCapacity);
1678 NS_IMETHODIMP
1679 nsLocalFile::GetParent(nsIFile** aParent) {
1680 CHECK_mPath();
1681 if (NS_WARN_IF(!aParent)) {
1682 return NS_ERROR_INVALID_ARG;
1684 *aParent = nullptr;
1686 // if '/' we are at the top of the volume, return null
1687 if (mPath.EqualsLiteral("/")) {
1688 return NS_OK;
1691 // <brendan, after jband> I promise to play nice
1692 char* buffer = mPath.BeginWriting();
1693 // find the last significant slash in buffer
1694 char* slashp = strrchr(buffer, '/');
1695 NS_ASSERTION(slashp, "non-canonical path?");
1696 if (!slashp) {
1697 return NS_ERROR_FILE_INVALID_PATH;
1700 // for the case where we are at '/'
1701 if (slashp == buffer) {
1702 slashp++;
1705 // temporarily terminate buffer at the last significant slash
1706 char c = *slashp;
1707 *slashp = '\0';
1709 nsCOMPtr<nsIFile> localFile;
1710 nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true,
1711 getter_AddRefs(localFile));
1713 // make buffer whole again
1714 *slashp = c;
1716 if (NS_FAILED(rv)) {
1717 return rv;
1720 localFile.forget(aParent);
1721 return NS_OK;
1725 * The results of Exists, isWritable and isReadable are not cached.
1728 NS_IMETHODIMP
1729 nsLocalFile::Exists(bool* aResult) {
1730 CHECK_mPath();
1731 if (NS_WARN_IF(!aResult)) {
1732 return NS_ERROR_INVALID_ARG;
1735 *aResult = (access(mPath.get(), F_OK) == 0);
1736 return NS_OK;
1739 NS_IMETHODIMP
1740 nsLocalFile::IsWritable(bool* aResult) {
1741 CHECK_mPath();
1742 if (NS_WARN_IF(!aResult)) {
1743 return NS_ERROR_INVALID_ARG;
1746 *aResult = (access(mPath.get(), W_OK) == 0);
1747 if (*aResult || errno == EACCES) {
1748 return NS_OK;
1750 return NSRESULT_FOR_ERRNO();
1753 NS_IMETHODIMP
1754 nsLocalFile::IsReadable(bool* aResult) {
1755 CHECK_mPath();
1756 if (NS_WARN_IF(!aResult)) {
1757 return NS_ERROR_INVALID_ARG;
1760 *aResult = (access(mPath.get(), R_OK) == 0);
1761 if (*aResult || errno == EACCES) {
1762 return NS_OK;
1764 return NSRESULT_FOR_ERRNO();
1767 NS_IMETHODIMP
1768 nsLocalFile::IsExecutable(bool* aResult) {
1769 CHECK_mPath();
1770 if (NS_WARN_IF(!aResult)) {
1771 return NS_ERROR_INVALID_ARG;
1774 // Check extension (bug 663899). On certain platforms, the file
1775 // extension may cause the OS to treat it as executable regardless of
1776 // the execute bit, such as .jar on Mac OS X. We borrow the code from
1777 // nsLocalFileWin, slightly modified.
1779 // Don't be fooled by symlinks.
1780 bool symLink;
1781 nsresult rv = IsSymlink(&symLink);
1782 if (NS_FAILED(rv)) {
1783 return rv;
1786 nsAutoString path;
1787 if (symLink) {
1788 GetTarget(path);
1789 } else {
1790 GetPath(path);
1793 int32_t dotIdx = path.RFindChar(char16_t('.'));
1794 if (dotIdx != kNotFound) {
1795 // Convert extension to lower case.
1796 char16_t* p = path.BeginWriting();
1797 for (p += dotIdx + 1; *p; ++p) {
1798 *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0;
1801 // Search for any of the set of executable extensions.
1802 static const char* const executableExts[] = {
1803 #ifdef MOZ_WIDGET_COCOA
1804 "afploc", // Can point to other files.
1805 #endif
1806 "air", // Adobe AIR installer
1807 #ifdef MOZ_WIDGET_COCOA
1808 "atloc", // Can point to other files.
1809 "fileloc", // File location files can be used to point to other
1810 // files.
1811 "ftploc", // Can point to other files.
1812 "inetloc", // Shouldn't be able to do the same, but can, due to
1813 // macOS vulnerabilities.
1814 #endif
1815 "jar" // java application bundle
1817 nsDependentSubstring ext = Substring(path, dotIdx + 1);
1818 for (auto executableExt : executableExts) {
1819 if (ext.EqualsASCII(executableExt)) {
1820 // Found a match. Set result and quit.
1821 *aResult = true;
1822 return NS_OK;
1827 // On OS X, then query Launch Services.
1828 #ifdef MOZ_WIDGET_COCOA
1829 // Certain Mac applications, such as Classic applications, which
1830 // run under Rosetta, might not have the +x mode bit but are still
1831 // considered to be executable by Launch Services (bug 646748).
1832 CFURLRef url;
1833 if (NS_FAILED(GetCFURL(&url))) {
1834 return NS_ERROR_FAILURE;
1837 LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
1838 LSItemInfoRecord theInfo;
1839 OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo);
1840 ::CFRelease(url);
1841 if (result == noErr) {
1842 if ((theInfo.flags & kLSItemInfoIsApplication) != 0) {
1843 *aResult = true;
1844 return NS_OK;
1847 #endif
1849 // Then check the execute bit.
1850 *aResult = (access(mPath.get(), X_OK) == 0);
1851 #ifdef SOLARIS
1852 // On Solaris, access will always return 0 for root user, however
1853 // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set.
1854 // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950
1855 if (*aResult) {
1856 struct STAT buf;
1858 *aResult = (STAT(mPath.get(), &buf) == 0);
1859 if (*aResult || errno == EACCES) {
1860 *aResult = *aResult && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH));
1861 return NS_OK;
1864 return NSRESULT_FOR_ERRNO();
1866 #endif
1867 if (*aResult || errno == EACCES) {
1868 return NS_OK;
1870 return NSRESULT_FOR_ERRNO();
1873 NS_IMETHODIMP
1874 nsLocalFile::IsDirectory(bool* aResult) {
1875 if (NS_WARN_IF(!aResult)) {
1876 return NS_ERROR_INVALID_ARG;
1878 *aResult = false;
1879 ENSURE_STAT_CACHE();
1880 *aResult = S_ISDIR(mCachedStat.st_mode);
1881 return NS_OK;
1884 NS_IMETHODIMP
1885 nsLocalFile::IsFile(bool* aResult) {
1886 if (NS_WARN_IF(!aResult)) {
1887 return NS_ERROR_INVALID_ARG;
1889 *aResult = false;
1890 ENSURE_STAT_CACHE();
1891 *aResult = S_ISREG(mCachedStat.st_mode);
1892 return NS_OK;
1895 NS_IMETHODIMP
1896 nsLocalFile::IsHidden(bool* aResult) {
1897 if (NS_WARN_IF(!aResult)) {
1898 return NS_ERROR_INVALID_ARG;
1900 nsACString::const_iterator begin, end;
1901 LocateNativeLeafName(begin, end);
1902 *aResult = (*begin == '.');
1903 return NS_OK;
1906 NS_IMETHODIMP
1907 nsLocalFile::IsSymlink(bool* aResult) {
1908 if (NS_WARN_IF(!aResult)) {
1909 return NS_ERROR_INVALID_ARG;
1911 CHECK_mPath();
1913 struct STAT symStat;
1914 if (LSTAT(mPath.get(), &symStat) == -1) {
1915 return NSRESULT_FOR_ERRNO();
1917 *aResult = S_ISLNK(symStat.st_mode);
1918 return NS_OK;
1921 NS_IMETHODIMP
1922 nsLocalFile::IsSpecial(bool* aResult) {
1923 if (NS_WARN_IF(!aResult)) {
1924 return NS_ERROR_INVALID_ARG;
1926 ENSURE_STAT_CACHE();
1927 *aResult = S_ISCHR(mCachedStat.st_mode) || S_ISBLK(mCachedStat.st_mode) ||
1928 #ifdef S_ISSOCK
1929 S_ISSOCK(mCachedStat.st_mode) ||
1930 #endif
1931 S_ISFIFO(mCachedStat.st_mode);
1933 return NS_OK;
1936 NS_IMETHODIMP
1937 nsLocalFile::Equals(nsIFile* aInFile, bool* aResult) {
1938 if (NS_WARN_IF(!aInFile)) {
1939 return NS_ERROR_INVALID_ARG;
1941 if (NS_WARN_IF(!aResult)) {
1942 return NS_ERROR_INVALID_ARG;
1944 *aResult = false;
1946 nsAutoCString inPath;
1947 nsresult rv = aInFile->GetNativePath(inPath);
1948 if (NS_FAILED(rv)) {
1949 return rv;
1952 // We don't need to worry about "/foo/" vs. "/foo" here
1953 // because trailing slashes are stripped on init.
1954 *aResult = !strcmp(inPath.get(), mPath.get());
1955 return NS_OK;
1958 NS_IMETHODIMP
1959 nsLocalFile::Contains(nsIFile* aInFile, bool* aResult) {
1960 CHECK_mPath();
1961 if (NS_WARN_IF(!aInFile)) {
1962 return NS_ERROR_INVALID_ARG;
1964 if (NS_WARN_IF(!aResult)) {
1965 return NS_ERROR_INVALID_ARG;
1968 nsAutoCString inPath;
1969 nsresult rv;
1971 if (NS_FAILED(rv = aInFile->GetNativePath(inPath))) {
1972 return rv;
1975 *aResult = false;
1977 ssize_t len = mPath.Length();
1978 if (strncmp(mPath.get(), inPath.get(), len) == 0) {
1979 // Now make sure that the |aInFile|'s path has a separator at len,
1980 // which implies that it has more components after len.
1981 if (inPath[len] == '/') {
1982 *aResult = true;
1986 return NS_OK;
1989 static nsresult ReadLinkSafe(const nsCString& aTarget, int32_t aExpectedSize,
1990 nsACString& aOutBuffer) {
1991 // If we call readlink with a buffer size S it returns S, then we cannot tell
1992 // if the buffer was big enough to hold the entire path. We allocate an
1993 // additional byte so we can check if the buffer was large enough.
1994 const auto allocSize = CheckedInt<size_t>(aExpectedSize) + 1;
1995 if (!allocSize.isValid()) {
1996 return NS_ERROR_OUT_OF_MEMORY;
1999 auto result = aOutBuffer.BulkWrite(allocSize.value(), 0, false);
2000 if (result.isErr()) {
2001 return result.unwrapErr();
2004 auto handle = result.unwrap();
2006 while (true) {
2007 ssize_t bytesWritten =
2008 readlink(aTarget.get(), handle.Elements(), handle.Length());
2009 if (bytesWritten < 0) {
2010 return NSRESULT_FOR_ERRNO();
2013 // written >= 0 so it is safe to cast to size_t.
2014 if ((size_t)bytesWritten < handle.Length()) {
2015 // Target might have changed since the lstat call, or lstat might lie, see
2016 // bug 1791029.
2017 handle.Finish(bytesWritten, false);
2018 return NS_OK;
2021 // The buffer was not large enough, so double it and try again.
2022 auto restartResult = handle.RestartBulkWrite(handle.Length() * 2, 0, false);
2023 if (restartResult.isErr()) {
2024 return restartResult.unwrapErr();
2029 NS_IMETHODIMP
2030 nsLocalFile::GetNativeTarget(nsACString& aResult) {
2031 CHECK_mPath();
2032 aResult.Truncate();
2034 struct STAT symStat;
2035 if (LSTAT(mPath.get(), &symStat) == -1) {
2036 return NSRESULT_FOR_ERRNO();
2039 if (!S_ISLNK(symStat.st_mode)) {
2040 return NS_ERROR_FILE_INVALID_PATH;
2043 nsAutoCString target;
2044 nsresult rv = ReadLinkSafe(mPath, symStat.st_size, target);
2045 if (NS_FAILED(rv)) {
2046 return rv;
2049 nsCOMPtr<nsIFile> self(this);
2050 int32_t maxLinks = 40;
2051 while (true) {
2052 if (maxLinks-- == 0) {
2053 rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
2054 break;
2057 if (target[0] != '/') {
2058 nsCOMPtr<nsIFile> parent;
2059 if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent)))) {
2060 break;
2062 if (NS_FAILED(rv = parent->AppendRelativeNativePath(target))) {
2063 break;
2065 if (NS_FAILED(rv = parent->GetNativePath(aResult))) {
2066 break;
2068 self = parent;
2069 } else {
2070 aResult = target;
2073 const nsPromiseFlatCString& flatRetval = PromiseFlatCString(aResult);
2075 // Any failure in testing the current target we'll just interpret
2076 // as having reached our destiny.
2077 if (LSTAT(flatRetval.get(), &symStat) == -1) {
2078 break;
2081 // And of course we're done if it isn't a symlink.
2082 if (!S_ISLNK(symStat.st_mode)) {
2083 break;
2086 nsAutoCString newTarget;
2087 rv = ReadLinkSafe(flatRetval, symStat.st_size, newTarget);
2088 if (NS_FAILED(rv)) {
2089 break;
2092 target = newTarget;
2095 if (NS_FAILED(rv)) {
2096 aResult.Truncate();
2098 return rv;
2101 NS_IMETHODIMP
2102 nsLocalFile::GetDirectoryEntriesImpl(nsIDirectoryEnumerator** aEntries) {
2103 RefPtr<nsDirEnumeratorUnix> dir = new nsDirEnumeratorUnix();
2105 nsresult rv = dir->Init(this, false);
2106 if (NS_FAILED(rv)) {
2107 *aEntries = nullptr;
2108 } else {
2109 dir.forget(aEntries);
2112 return rv;
2115 NS_IMETHODIMP
2116 nsLocalFile::Load(PRLibrary** aResult) {
2117 CHECK_mPath();
2118 if (NS_WARN_IF(!aResult)) {
2119 return NS_ERROR_INVALID_ARG;
2122 #ifdef NS_BUILD_REFCNT_LOGGING
2123 nsTraceRefcnt::SetActivityIsLegal(false);
2124 #endif
2126 *aResult = PR_LoadLibrary(mPath.get());
2128 #ifdef NS_BUILD_REFCNT_LOGGING
2129 nsTraceRefcnt::SetActivityIsLegal(true);
2130 #endif
2132 if (!*aResult) {
2133 return NS_ERROR_FAILURE;
2135 return NS_OK;
2138 NS_IMETHODIMP
2139 nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor) {
2140 return GetNativePath(aPersistentDescriptor);
2143 NS_IMETHODIMP
2144 nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor) {
2145 #ifdef MOZ_WIDGET_COCOA
2146 if (aPersistentDescriptor.IsEmpty()) {
2147 return NS_ERROR_INVALID_ARG;
2150 // Support pathnames as user-supplied descriptors if they begin with '/'
2151 // or '~'. These characters do not collide with the base64 set used for
2152 // encoding alias records.
2153 char first = aPersistentDescriptor.First();
2154 if (first == '/' || first == '~') {
2155 return InitWithNativePath(aPersistentDescriptor);
2158 uint32_t dataSize = aPersistentDescriptor.Length();
2159 char* decodedData = PL_Base64Decode(
2160 PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nullptr);
2161 if (!decodedData) {
2162 NS_ERROR("SetPersistentDescriptor was given bad data");
2163 return NS_ERROR_FAILURE;
2166 // Cast to an alias record and resolve.
2167 AliasRecord aliasHeader = *(AliasPtr)decodedData;
2168 int32_t aliasSize = ::GetAliasSizeFromPtr(&aliasHeader);
2169 if (aliasSize >
2170 ((int32_t)dataSize * 3) / 4) { // be paranoid about having too few data
2171 PR_Free(decodedData); // PL_Base64Decode() uses PR_Malloc().
2172 return NS_ERROR_FAILURE;
2175 nsresult rv = NS_OK;
2177 // Move the now-decoded data into the Handle.
2178 // The size of the decoded data is 3/4 the size of the encoded data. See
2179 // plbase64.h
2180 Handle newHandle = nullptr;
2181 if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr) {
2182 rv = NS_ERROR_OUT_OF_MEMORY;
2184 PR_Free(decodedData); // PL_Base64Decode() uses PR_Malloc().
2185 if (NS_FAILED(rv)) {
2186 return rv;
2189 Boolean changed;
2190 FSRef resolvedFSRef;
2191 OSErr err = ::FSResolveAlias(nullptr, (AliasHandle)newHandle, &resolvedFSRef,
2192 &changed);
2194 rv = MacErrorMapper(err);
2195 DisposeHandle(newHandle);
2196 if (NS_FAILED(rv)) {
2197 return rv;
2200 return InitWithFSRef(&resolvedFSRef);
2201 #else
2202 return InitWithNativePath(aPersistentDescriptor);
2203 #endif
2206 NS_IMETHODIMP
2207 nsLocalFile::Reveal() {
2208 if (!FilePreferences::IsAllowedPath(mPath)) {
2209 return NS_ERROR_FILE_ACCESS_DENIED;
2212 #ifdef MOZ_WIDGET_GTK
2213 nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
2214 if (!giovfs) {
2215 return NS_ERROR_FAILURE;
2217 return giovfs->RevealFile(this);
2218 #elif defined(MOZ_WIDGET_COCOA)
2219 CFURLRef url;
2220 if (NS_SUCCEEDED(GetCFURL(&url))) {
2221 nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
2222 ::CFRelease(url);
2223 return rv;
2225 return NS_ERROR_FAILURE;
2226 #else
2227 return NS_ERROR_FAILURE;
2228 #endif
2231 NS_IMETHODIMP
2232 nsLocalFile::Launch() {
2233 if (!FilePreferences::IsAllowedPath(mPath)) {
2234 return NS_ERROR_FILE_ACCESS_DENIED;
2237 #ifdef MOZ_WIDGET_GTK
2238 nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
2239 if (!giovfs) {
2240 return NS_ERROR_FAILURE;
2243 return giovfs->LaunchFile(mPath);
2244 #elif defined(MOZ_WIDGET_ANDROID)
2245 // Not supported on GeckoView
2246 return NS_ERROR_NOT_IMPLEMENTED;
2247 #elif defined(MOZ_WIDGET_COCOA)
2248 CFURLRef url;
2249 if (NS_SUCCEEDED(GetCFURL(&url))) {
2250 nsresult rv = CocoaFileUtils::OpenURL(url);
2251 ::CFRelease(url);
2252 return rv;
2254 return NS_ERROR_FAILURE;
2255 #else
2256 return NS_ERROR_FAILURE;
2257 #endif
2260 nsresult NS_NewNativeLocalFile(const nsACString& aPath, bool aFollowSymlinks,
2261 nsIFile** aResult) {
2262 RefPtr<nsLocalFile> file = new nsLocalFile();
2264 if (!aPath.IsEmpty()) {
2265 nsresult rv = file->InitWithNativePath(aPath);
2266 if (NS_FAILED(rv)) {
2267 return rv;
2270 file.forget(aResult);
2271 return NS_OK;
2274 //-----------------------------------------------------------------------------
2275 // unicode support
2276 //-----------------------------------------------------------------------------
2278 #define SET_UCS(func, ucsArg) \
2280 nsAutoCString buf; \
2281 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
2282 if (NS_FAILED(rv)) return rv; \
2283 return (func)(buf); \
2286 #define GET_UCS(func, ucsArg) \
2288 nsAutoCString buf; \
2289 nsresult rv = (func)(buf); \
2290 if (NS_FAILED(rv)) return rv; \
2291 return NS_CopyNativeToUnicode(buf, ucsArg); \
2294 #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
2296 nsAutoCString buf; \
2297 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
2298 if (NS_FAILED(rv)) return rv; \
2299 return (func)(opaqueArg, buf); \
2302 // Unicode interface Wrapper
2303 nsresult nsLocalFile::InitWithPath(const nsAString& aFilePath) {
2304 SET_UCS(InitWithNativePath, aFilePath);
2306 nsresult nsLocalFile::Append(const nsAString& aNode) {
2307 SET_UCS(AppendNative, aNode);
2309 nsresult nsLocalFile::AppendRelativePath(const nsAString& aNode) {
2310 SET_UCS(AppendRelativeNativePath, aNode);
2312 nsresult nsLocalFile::GetLeafName(nsAString& aLeafName) {
2313 GET_UCS(GetNativeLeafName, aLeafName);
2315 nsresult nsLocalFile::SetLeafName(const nsAString& aLeafName) {
2316 SET_UCS(SetNativeLeafName, aLeafName);
2318 nsresult nsLocalFile::GetPath(nsAString& aResult) {
2319 return NS_CopyNativeToUnicode(mPath, aResult);
2321 nsresult nsLocalFile::CopyTo(nsIFile* aNewParentDir,
2322 const nsAString& aNewName) {
2323 SET_UCS_2ARGS_2(CopyToNative, aNewParentDir, aNewName);
2325 nsresult nsLocalFile::CopyToFollowingLinks(nsIFile* aNewParentDir,
2326 const nsAString& aNewName) {
2327 SET_UCS_2ARGS_2(CopyToFollowingLinksNative, aNewParentDir, aNewName);
2329 nsresult nsLocalFile::MoveTo(nsIFile* aNewParentDir,
2330 const nsAString& aNewName) {
2331 SET_UCS_2ARGS_2(MoveToNative, aNewParentDir, aNewName);
2333 NS_IMETHODIMP
2334 nsLocalFile::MoveToFollowingLinks(nsIFile* aNewParentDir,
2335 const nsAString& aNewName) {
2336 SET_UCS_2ARGS_2(MoveToFollowingLinksNative, aNewParentDir, aNewName);
2339 NS_IMETHODIMP
2340 nsLocalFile::RenameTo(nsIFile* aNewParentDir, const nsAString& aNewName) {
2341 SET_UCS_2ARGS_2(RenameToNative, aNewParentDir, aNewName);
2344 NS_IMETHODIMP
2345 nsLocalFile::RenameToNative(nsIFile* aNewParentDir,
2346 const nsACString& aNewName) {
2347 nsresult rv;
2349 // check to make sure that this has been initialized properly
2350 CHECK_mPath();
2352 // check to make sure that we have a new parent
2353 nsAutoCString newPathName;
2354 rv = GetNativeTargetPathName(aNewParentDir, aNewName, newPathName);
2355 if (NS_FAILED(rv)) {
2356 return rv;
2359 if (!FilePreferences::IsAllowedPath(newPathName)) {
2360 return NS_ERROR_FILE_ACCESS_DENIED;
2363 // try for atomic rename
2364 if (rename(mPath.get(), newPathName.get()) < 0) {
2365 if (errno == EXDEV) {
2366 rv = NS_ERROR_FILE_ACCESS_DENIED;
2367 } else {
2368 rv = NSRESULT_FOR_ERRNO();
2372 return rv;
2375 nsresult nsLocalFile::GetTarget(nsAString& aResult) {
2376 GET_UCS(GetNativeTarget, aResult);
2379 nsresult NS_NewLocalFile(const nsAString& aPath, bool aFollowLinks,
2380 nsIFile** aResult) {
2381 nsAutoCString buf;
2382 nsresult rv = NS_CopyUnicodeToNative(aPath, buf);
2383 if (NS_FAILED(rv)) {
2384 return rv;
2386 return NS_NewNativeLocalFile(buf, aFollowLinks, aResult);
2389 // nsILocalFileMac
2391 #ifdef MOZ_WIDGET_COCOA
2393 NS_IMETHODIMP
2394 nsLocalFile::HasXAttr(const nsACString& aAttrName, bool* aHasAttr) {
2395 NS_ENSURE_ARG_POINTER(aHasAttr);
2397 nsAutoCString attrName{aAttrName};
2399 ssize_t size = getxattr(mPath.get(), attrName.get(), nullptr, 0, 0, 0);
2400 if (size == -1) {
2401 if (errno == ENOATTR) {
2402 *aHasAttr = false;
2403 } else {
2404 return NSRESULT_FOR_ERRNO();
2406 } else {
2407 *aHasAttr = true;
2410 return NS_OK;
2413 NS_IMETHODIMP
2414 nsLocalFile::GetXAttr(const nsACString& aAttrName,
2415 nsTArray<uint8_t>& aAttrValue) {
2416 aAttrValue.Clear();
2418 nsAutoCString attrName{aAttrName};
2420 ssize_t size = getxattr(mPath.get(), attrName.get(), nullptr, 0, 0, 0);
2422 if (size == -1) {
2423 return NSRESULT_FOR_ERRNO();
2426 for (;;) {
2427 aAttrValue.SetCapacity(size);
2429 // The attribute can change between our first call and this call, so we need
2430 // to re-check the size and possibly call with a larger buffer.
2431 ssize_t newSize = getxattr(mPath.get(), attrName.get(),
2432 aAttrValue.Elements(), size, 0, 0);
2433 if (newSize == -1) {
2434 return NSRESULT_FOR_ERRNO();
2437 if (newSize <= size) {
2438 aAttrValue.SetLength(newSize);
2439 break;
2440 } else {
2441 size = newSize;
2445 return NS_OK;
2448 NS_IMETHODIMP
2449 nsLocalFile::SetXAttr(const nsACString& aAttrName,
2450 const nsTArray<uint8_t>& aAttrValue) {
2451 nsAutoCString attrName{aAttrName};
2453 if (setxattr(mPath.get(), attrName.get(), aAttrValue.Elements(),
2454 aAttrValue.Length(), 0, 0) == -1) {
2455 return NSRESULT_FOR_ERRNO();
2458 return NS_OK;
2461 NS_IMETHODIMP
2462 nsLocalFile::DelXAttr(const nsACString& aAttrName) {
2463 nsAutoCString attrName{aAttrName};
2465 // Ignore removing an attribute that does not exist.
2466 if (removexattr(mPath.get(), attrName.get(), 0) == -1) {
2467 return NSRESULT_FOR_ERRNO();
2470 return NS_OK;
2473 static nsresult MacErrorMapper(OSErr inErr) {
2474 nsresult outErr;
2476 switch (inErr) {
2477 case noErr:
2478 outErr = NS_OK;
2479 break;
2481 case fnfErr:
2482 case afpObjectNotFound:
2483 case afpDirNotFound:
2484 outErr = NS_ERROR_FILE_NOT_FOUND;
2485 break;
2487 case dupFNErr:
2488 case afpObjectExists:
2489 outErr = NS_ERROR_FILE_ALREADY_EXISTS;
2490 break;
2492 case dskFulErr:
2493 case afpDiskFull:
2494 outErr = NS_ERROR_FILE_NO_DEVICE_SPACE;
2495 break;
2497 case fLckdErr:
2498 case afpVolLocked:
2499 outErr = NS_ERROR_FILE_IS_LOCKED;
2500 break;
2502 case afpAccessDenied:
2503 outErr = NS_ERROR_FILE_ACCESS_DENIED;
2504 break;
2506 case afpDirNotEmpty:
2507 outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
2508 break;
2510 // Can't find good map for some
2511 case bdNamErr:
2512 outErr = NS_ERROR_FAILURE;
2513 break;
2515 default:
2516 outErr = NS_ERROR_FAILURE;
2517 break;
2520 return outErr;
2523 static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr) {
2524 // first see if the conversion would succeed and find the length of the
2525 // result
2526 CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
2527 CFIndex charsConverted = ::CFStringGetBytes(
2528 aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8, 0, false,
2529 nullptr, 0, &usedBufLen);
2530 if (charsConverted == inStrLen) {
2531 // all characters converted, do the actual conversion
2532 aOutStr.SetLength(usedBufLen);
2533 if (aOutStr.Length() != (unsigned int)usedBufLen) {
2534 return NS_ERROR_OUT_OF_MEMORY;
2536 UInt8* buffer = (UInt8*)aOutStr.BeginWriting();
2537 ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
2538 kCFStringEncodingUTF8, 0, false, buffer, usedBufLen,
2539 &usedBufLen);
2540 return NS_OK;
2543 return NS_ERROR_FAILURE;
2546 NS_IMETHODIMP
2547 nsLocalFile::InitWithCFURL(CFURLRef aCFURL) {
2548 UInt8 path[PATH_MAX];
2549 if (::CFURLGetFileSystemRepresentation(aCFURL, true, path, PATH_MAX)) {
2550 nsDependentCString nativePath((char*)path);
2551 return InitWithNativePath(nativePath);
2554 return NS_ERROR_FAILURE;
2557 NS_IMETHODIMP
2558 nsLocalFile::InitWithFSRef(const FSRef* aFSRef) {
2559 if (NS_WARN_IF(!aFSRef)) {
2560 return NS_ERROR_INVALID_ARG;
2563 CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
2564 if (newURLRef) {
2565 nsresult rv = InitWithCFURL(newURLRef);
2566 ::CFRelease(newURLRef);
2567 return rv;
2570 return NS_ERROR_FAILURE;
2573 NS_IMETHODIMP
2574 nsLocalFile::GetCFURL(CFURLRef* aResult) {
2575 CHECK_mPath();
2577 bool isDir;
2578 IsDirectory(&isDir);
2579 *aResult = ::CFURLCreateFromFileSystemRepresentation(
2580 kCFAllocatorDefault, (UInt8*)mPath.get(), mPath.Length(), isDir);
2582 return (*aResult ? NS_OK : NS_ERROR_FAILURE);
2585 NS_IMETHODIMP
2586 nsLocalFile::GetFSRef(FSRef* aResult) {
2587 if (NS_WARN_IF(!aResult)) {
2588 return NS_ERROR_INVALID_ARG;
2591 nsresult rv = NS_ERROR_FAILURE;
2593 CFURLRef url = nullptr;
2594 if (NS_SUCCEEDED(GetCFURL(&url))) {
2595 if (::CFURLGetFSRef(url, aResult)) {
2596 rv = NS_OK;
2598 ::CFRelease(url);
2601 return rv;
2604 NS_IMETHODIMP
2605 nsLocalFile::GetFSSpec(FSSpec* aResult) {
2606 if (NS_WARN_IF(!aResult)) {
2607 return NS_ERROR_INVALID_ARG;
2610 FSRef fsRef;
2611 nsresult rv = GetFSRef(&fsRef);
2612 if (NS_SUCCEEDED(rv)) {
2613 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nullptr, nullptr,
2614 aResult, nullptr);
2615 return MacErrorMapper(err);
2618 return rv;
2621 NS_IMETHODIMP
2622 nsLocalFile::GetFileSizeWithResFork(int64_t* aFileSizeWithResFork) {
2623 if (NS_WARN_IF(!aFileSizeWithResFork)) {
2624 return NS_ERROR_INVALID_ARG;
2627 FSRef fsRef;
2628 nsresult rv = GetFSRef(&fsRef);
2629 if (NS_FAILED(rv)) {
2630 return rv;
2633 FSCatalogInfo catalogInfo;
2634 OSErr err =
2635 ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
2636 &catalogInfo, nullptr, nullptr, nullptr);
2637 if (err != noErr) {
2638 return MacErrorMapper(err);
2641 *aFileSizeWithResFork =
2642 catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
2643 return NS_OK;
2646 NS_IMETHODIMP
2647 nsLocalFile::GetFileType(OSType* aFileType) {
2648 CFURLRef url;
2649 if (NS_SUCCEEDED(GetCFURL(&url))) {
2650 nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType);
2651 ::CFRelease(url);
2652 return rv;
2654 return NS_ERROR_FAILURE;
2657 NS_IMETHODIMP
2658 nsLocalFile::SetFileType(OSType aFileType) {
2659 CFURLRef url;
2660 if (NS_SUCCEEDED(GetCFURL(&url))) {
2661 nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType);
2662 ::CFRelease(url);
2663 return rv;
2665 return NS_ERROR_FAILURE;
2668 NS_IMETHODIMP
2669 nsLocalFile::GetFileCreator(OSType* aFileCreator) {
2670 CFURLRef url;
2671 if (NS_SUCCEEDED(GetCFURL(&url))) {
2672 nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator);
2673 ::CFRelease(url);
2674 return rv;
2676 return NS_ERROR_FAILURE;
2679 NS_IMETHODIMP
2680 nsLocalFile::SetFileCreator(OSType aFileCreator) {
2681 CFURLRef url;
2682 if (NS_SUCCEEDED(GetCFURL(&url))) {
2683 nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator);
2684 ::CFRelease(url);
2685 return rv;
2687 return NS_ERROR_FAILURE;
2690 NS_IMETHODIMP
2691 nsLocalFile::LaunchWithDoc(nsIFile* aDocToLoad, bool aLaunchInBackground) {
2692 bool isExecutable;
2693 nsresult rv = IsExecutable(&isExecutable);
2694 if (NS_FAILED(rv)) {
2695 return rv;
2697 if (!isExecutable) {
2698 return NS_ERROR_FILE_EXECUTION_FAILED;
2701 FSRef appFSRef, docFSRef;
2702 rv = GetFSRef(&appFSRef);
2703 if (NS_FAILED(rv)) {
2704 return rv;
2707 if (aDocToLoad) {
2708 nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
2709 rv = macDoc->GetFSRef(&docFSRef);
2710 if (NS_FAILED(rv)) {
2711 return rv;
2715 LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
2716 LSLaunchFSRefSpec thelaunchSpec;
2718 if (aLaunchInBackground) {
2719 theLaunchFlags |= kLSLaunchDontSwitch;
2721 memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
2723 thelaunchSpec.appRef = &appFSRef;
2724 if (aDocToLoad) {
2725 thelaunchSpec.numDocs = 1;
2726 thelaunchSpec.itemRefs = &docFSRef;
2728 thelaunchSpec.launchFlags = theLaunchFlags;
2730 OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
2731 if (err != noErr) {
2732 return MacErrorMapper(err);
2735 return NS_OK;
2738 NS_IMETHODIMP
2739 nsLocalFile::OpenDocWithApp(nsIFile* aAppToOpenWith, bool aLaunchInBackground) {
2740 FSRef docFSRef;
2741 nsresult rv = GetFSRef(&docFSRef);
2742 if (NS_FAILED(rv)) {
2743 return rv;
2746 if (!aAppToOpenWith) {
2747 OSErr err = ::LSOpenFSRef(&docFSRef, nullptr);
2748 return MacErrorMapper(err);
2751 nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
2752 if (!appFileMac) {
2753 return rv;
2756 bool isExecutable;
2757 rv = appFileMac->IsExecutable(&isExecutable);
2758 if (NS_FAILED(rv)) {
2759 return rv;
2761 if (!isExecutable) {
2762 return NS_ERROR_FILE_EXECUTION_FAILED;
2765 FSRef appFSRef;
2766 rv = appFileMac->GetFSRef(&appFSRef);
2767 if (NS_FAILED(rv)) {
2768 return rv;
2771 LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
2772 LSLaunchFSRefSpec thelaunchSpec;
2774 if (aLaunchInBackground) {
2775 theLaunchFlags |= kLSLaunchDontSwitch;
2777 memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
2779 thelaunchSpec.appRef = &appFSRef;
2780 thelaunchSpec.numDocs = 1;
2781 thelaunchSpec.itemRefs = &docFSRef;
2782 thelaunchSpec.launchFlags = theLaunchFlags;
2784 OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
2785 if (err != noErr) {
2786 return MacErrorMapper(err);
2789 return NS_OK;
2792 NS_IMETHODIMP
2793 nsLocalFile::IsPackage(bool* aResult) {
2794 if (NS_WARN_IF(!aResult)) {
2795 return NS_ERROR_INVALID_ARG;
2797 *aResult = false;
2799 CFURLRef url;
2800 nsresult rv = GetCFURL(&url);
2801 if (NS_FAILED(rv)) {
2802 return rv;
2805 LSItemInfoRecord info;
2806 OSStatus status =
2807 ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info);
2809 ::CFRelease(url);
2811 if (status != noErr) {
2812 return NS_ERROR_FAILURE;
2815 *aResult = !!(info.flags & kLSItemInfoIsPackage);
2817 return NS_OK;
2820 NS_IMETHODIMP
2821 nsLocalFile::GetBundleDisplayName(nsAString& aOutBundleName) {
2822 bool isPackage = false;
2823 nsresult rv = IsPackage(&isPackage);
2824 if (NS_FAILED(rv) || !isPackage) {
2825 return NS_ERROR_FAILURE;
2828 nsAutoString name;
2829 rv = GetLeafName(name);
2830 if (NS_FAILED(rv)) {
2831 return rv;
2834 int32_t length = name.Length();
2835 if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
2836 // 4 characters in ".app"
2837 aOutBundleName = Substring(name, 0, length - 4);
2838 } else {
2839 aOutBundleName = name;
2842 return NS_OK;
2845 NS_IMETHODIMP
2846 nsLocalFile::GetBundleIdentifier(nsACString& aOutBundleIdentifier) {
2847 nsresult rv = NS_ERROR_FAILURE;
2849 CFURLRef urlRef;
2850 if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
2851 CFBundleRef bundle = ::CFBundleCreate(nullptr, urlRef);
2852 if (bundle) {
2853 CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
2854 if (bundleIdentifier) {
2855 rv = CFStringReftoUTF8(bundleIdentifier, aOutBundleIdentifier);
2857 ::CFRelease(bundle);
2859 ::CFRelease(urlRef);
2862 return rv;
2865 NS_IMETHODIMP
2866 nsLocalFile::GetBundleContentsLastModifiedTime(int64_t* aLastModTime) {
2867 CHECK_mPath();
2868 if (NS_WARN_IF(!aLastModTime)) {
2869 return NS_ERROR_INVALID_ARG;
2872 bool isPackage = false;
2873 nsresult rv = IsPackage(&isPackage);
2874 if (NS_FAILED(rv) || !isPackage) {
2875 return GetLastModifiedTime(aLastModTime);
2878 nsAutoCString infoPlistPath(mPath);
2879 infoPlistPath.AppendLiteral("/Contents/Info.plist");
2880 PRFileInfo64 info;
2881 if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) {
2882 return GetLastModifiedTime(aLastModTime);
2884 int64_t modTime = int64_t(info.modifyTime);
2885 if (modTime == 0) {
2886 *aLastModTime = 0;
2887 } else {
2888 *aLastModTime = modTime / int64_t(PR_USEC_PER_MSEC);
2891 return NS_OK;
2894 NS_IMETHODIMP nsLocalFile::InitWithFile(nsIFile* aFile) {
2895 if (NS_WARN_IF(!aFile)) {
2896 return NS_ERROR_INVALID_ARG;
2899 nsAutoCString nativePath;
2900 nsresult rv = aFile->GetNativePath(nativePath);
2901 if (NS_FAILED(rv)) {
2902 return rv;
2905 return InitWithNativePath(nativePath);
2908 nsresult NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks,
2909 nsILocalFileMac** aResult) {
2910 RefPtr<nsLocalFile> file = new nsLocalFile();
2912 nsresult rv = file->InitWithFSRef(aFSRef);
2913 if (NS_FAILED(rv)) {
2914 return rv;
2916 file.forget(aResult);
2917 return NS_OK;
2920 nsresult NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks,
2921 nsILocalFileMac** aResult) {
2922 RefPtr<nsLocalFile> file = new nsLocalFile();
2924 nsresult rv = file->InitWithCFURL(aURL);
2925 if (NS_FAILED(rv)) {
2926 return rv;
2928 file.forget(aResult);
2929 return NS_OK;
2932 #endif