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/. */
8 * Implementation of nsIFile for "unixy" systems.
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"
21 #include <sys/select.h>
24 #include <sys/types.h>
30 #if defined(XP_MACOSX)
31 # include <sys/xattr.h>
34 #if defined(USE_LINUX_QUOTACTL)
35 # include <sys/mount.h>
36 # include <sys/quota.h>
37 # include <sys/sysmacros.h>
39 # define BLOCK_SIZE 1024 /* kernel block size */
43 #include "nsDirectoryServiceDefs.h"
47 #include "nsIDirectoryEnumerator.h"
48 #include "nsSimpleEnumerator.h"
49 #include "private/pprio.h"
53 # include "nsIGIOService.h"
56 #ifdef MOZ_WIDGET_COCOA
57 # include <Carbon/Carbon.h>
58 # include "CocoaFileUtils.h"
60 # include "plbase64.h"
62 static nsresult
MacErrorMapper(OSErr inErr
);
65 #ifdef MOZ_WIDGET_ANDROID
66 # include "mozilla/java/GeckoAppShellWrappers.h"
67 # include "nsIMIMEService.h"
68 # include <linux/magic.h>
71 #include "nsNativeCharsetUtils.h"
72 #include "nsTraceRefcnt.h"
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
*);
81 # include <sys/statvfs.h>
84 #ifdef HAVE_SYS_STATFS_H
85 # include <sys/statfs.h>
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
106 using namespace mozilla
;
108 #define ENSURE_STAT_CACHE() \
110 if (!FillStatCache()) return NSRESULT_FOR_ERRNO(); \
113 #define CHECK_mPath() \
115 if (mPath.IsEmpty()) return NS_ERROR_NOT_INITIALIZED; \
116 if (!FilePreferences::IsAllowedPath(mPath)) \
117 return NS_ERROR_FILE_ACCESS_DENIED; \
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
{
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
); }
147 ~nsDirEnumeratorUnix() override
;
150 NS_IMETHOD
GetNextEntry();
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
)
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());
185 return NSRESULT_FOR_ERRNO();
187 return GetNextEntry();
191 nsDirEnumeratorUnix::HasMoreElements(bool* aResult
) {
192 *aResult
= mDir
&& mEntry
;
200 nsDirEnumeratorUnix::GetNext(nsISupports
** aResult
) {
201 nsCOMPtr
<nsIFile
> file
;
202 nsresult rv
= GetNextFile(getter_AddRefs(file
));
207 return NS_ERROR_FAILURE
;
209 file
.forget(aResult
);
214 nsDirEnumeratorUnix::GetNextEntry() {
217 mEntry
= readdir(mDir
);
219 // end of dir or error
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
232 nsDirEnumeratorUnix::GetNextFile(nsIFile
** aResult
) {
234 if (!mDir
|| !mEntry
) {
239 nsCOMPtr
<nsIFile
> file
= new nsLocalFile();
241 if (NS_FAILED(rv
= file
->InitWithNativePath(mParentPath
)) ||
242 NS_FAILED(rv
= file
->AppendNative(nsDependentCString(mEntry
->d_name
)))) {
246 file
.forget(aResult
);
247 return GetNextEntry();
251 nsDirEnumeratorUnix::Close() {
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
)
270 NS_IMPL_ISUPPORTS(nsLocalFile
, nsIFile
)
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
)) {
291 if (STAT(mPath
.get(), &mCachedStat
) == -1) {
292 // try lstat it may be a symlink
293 if (LSTAT(mPath
.get(), &mCachedStat
) == -1) {
301 nsLocalFile::Clone(nsIFile
** aFile
) {
302 // Just copy-construct ourselves
303 RefPtr
<nsLocalFile
> copy
= new nsLocalFile(*this);
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
;
323 if (aFilePath
.Length() > 2) {
324 mPath
.Append(Substring(aFilePath
, 1));
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`.
338 + Substring(aFilePath
, 1);
341 if (aFilePath
.IsEmpty() || aFilePath
.First() != '/') {
342 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
347 if (!FilePreferences::IsAllowedPath(mPath
)) {
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] == '/')) {
357 mPath
.SetLength(len
);
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;
375 fprintf(stderr
, "nsIFile: before: %s\n", buffer
);
378 while ((slashp
= strchr(slashp
+ 1, '/'))) {
380 * Sequences of '/' are equivalent to a single '/'.
382 if (slashp
[1] == '/') {
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
392 if (slashp
[1] == '\0') {
396 /* Temporarily NUL-terminate here */
399 fprintf(stderr
, "nsIFile: mkdir(\"%s\")\n", buffer
);
401 mkdir_result
= mkdir(buffer
, aPermissions
);
402 if (mkdir_result
== -1) {
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
411 if (mkdir_errno
!= EEXIST
&& access(buffer
, F_OK
) == 0) {
412 mkdir_errno
= EEXIST
;
415 fprintf(stderr
, "nsIFile: errno: %d\n", mkdir_errno
);
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
;
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
);
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
);
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
);
466 return NS_ERROR_FAILURE
;
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
) {
481 return mkdir(aPath
, aMode
);
484 nsresult
nsLocalFile::CreateAndKeepOpen(uint32_t aType
, int aFlags
,
485 uint32_t aPermissions
,
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
) {
514 if (aPermissions
& S_IRGRP
) {
517 if (aPermissions
& S_IROTH
) {
522 fprintf(stderr
, "nsIFile: perm = %o, dirperm = %o\n", aPermissions
,
526 if (NS_FAILED(CreateAllAncestors(dirperm
))) {
527 return NS_ERROR_FAILURE
;
531 fprintf(stderr
, "nsIFile: Create(\"%s\") again\n", mPath
.get());
533 result
= createFunc(mPath
.get(), aFlags
, aPermissions
, aResult
);
535 return NSRESULT_FOR_RETURN(result
);
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
);
556 nsLocalFile::AppendNative(const nsACString
& aFragment
) {
557 if (aFragment
.IsEmpty()) {
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
);
573 nsLocalFile::AppendRelativeNativePath(const nsACString
& aFragment
) {
574 if (aFragment
.IsEmpty()) {
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
);
593 while (FindInReadable(doubleDot
, start
, offset
)) {
594 if (offset
== end
|| *offset
== '/') {
595 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
601 // catches the remaining cases of prefixes
602 if (StringBeginsWith(aFragment
, "../"_ns
)) {
603 return NS_ERROR_FILE_UNRECOGNIZED_PATH
;
607 if (!mPath
.EqualsLiteral("/")) {
610 mPath
.Append(aFragment
);
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
;
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
;
645 while (--it
!= stop
) {
651 // else, the entire path is the leaf name (which means this
652 // isn't an absolute path... unexpected??)
656 nsLocalFile::GetNativeLeafName(nsACString
& aLeafName
) {
657 nsACString::const_iterator begin
, end
;
658 LocateNativeLeafName(begin
, end
);
659 aLeafName
= Substring(begin
, end
);
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
);
672 nsLocalFile::GetDisplayName(nsAString
& aLeafName
) {
673 return GetLeafName(aLeafName
);
676 nsCString
nsLocalFile::NativePath() { return mPath
; }
678 nsresult
nsIFile::GetNativePath(nsACString
& aResult
) {
679 aResult
= NativePath();
683 nsCString
nsIFile::HumanReadablePath() {
685 DebugOnly
<nsresult
> rv
= GetNativePath(path
);
686 MOZ_ASSERT(NS_SUCCEEDED(rv
));
690 nsresult
nsLocalFile::GetNativeTargetPathName(nsIFile
* aNewParent
,
691 const nsACString
& aNewName
,
692 nsACString
& aResult
) {
694 nsCOMPtr
<nsIFile
> oldParent
;
697 if (NS_FAILED(rv
= GetParent(getter_AddRefs(oldParent
)))) {
700 aNewParent
= oldParent
.get();
702 // check to see if our target directory exists
704 if (NS_FAILED(rv
= aNewParent
->Exists(&targetExists
))) {
709 // XXX create the new directory with some permissions
710 rv
= aNewParent
->Create(DIRECTORY_TYPE
, 0755);
715 // make sure that the target is actually a directory
716 bool targetIsDirectory
;
717 if (NS_FAILED(rv
= aNewParent
->IsDirectory(&targetIsDirectory
))) {
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
);
731 LocateNativeLeafName(nameBegin
, nameEnd
);
734 nsAutoCString dirName
;
735 if (NS_FAILED(rv
= aNewParent
->GetNativePath(dirName
))) {
739 aResult
= dirName
+ "/"_ns
+ Substring(nameBegin
, nameEnd
);
743 nsresult
nsLocalFile::CopyDirectoryTo(nsIFile
* aNewParent
) {
746 * dirCheck is used for various boolean test results such as from Equals,
747 * Exists, isDir, etc.
749 bool dirCheck
, isSymlink
;
752 if (NS_FAILED(rv
= IsDirectory(&dirCheck
))) {
756 return CopyToNative(aNewParent
, ""_ns
);
759 if (NS_FAILED(rv
= Equals(aNewParent
, &dirCheck
))) {
763 // can't copy dir to itself
764 return NS_ERROR_INVALID_ARG
;
767 if (NS_FAILED(rv
= aNewParent
->Exists(&dirCheck
))) {
770 // get the dirs old permissions
771 if (NS_FAILED(rv
= GetPermissions(&oldPerms
))) {
775 if (NS_FAILED(rv
= aNewParent
->Create(DIRECTORY_TYPE
, oldPerms
))) {
778 } else { // dir exists lets try to use leaf
779 nsAutoCString leafName
;
780 if (NS_FAILED(rv
= GetNativeLeafName(leafName
))) {
783 if (NS_FAILED(rv
= aNewParent
->AppendNative(leafName
))) {
786 if (NS_FAILED(rv
= aNewParent
->Exists(&dirCheck
))) {
790 return NS_ERROR_FILE_ALREADY_EXISTS
; // dest exists
792 if (NS_FAILED(rv
= aNewParent
->Create(DIRECTORY_TYPE
, oldPerms
))) {
797 nsCOMPtr
<nsIDirectoryEnumerator
> dirIterator
;
798 if (NS_FAILED(rv
= GetDirectoryEntries(getter_AddRefs(dirIterator
)))) {
802 nsCOMPtr
<nsIFile
> entry
;
803 while (NS_SUCCEEDED(dirIterator
->GetNextFile(getter_AddRefs(entry
))) &&
805 if (NS_FAILED(rv
= entry
->IsSymlink(&isSymlink
))) {
808 if (NS_FAILED(rv
= entry
->IsDirectory(&dirCheck
))) {
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
))) {
818 nsAutoCString pathName
;
819 if (NS_FAILED(rv2
= entry
->GetNativePath(pathName
))) {
822 printf("Operation not supported: %s\n", pathName
.get());
824 if (rv
== NS_ERROR_OUT_OF_MEMORY
) {
831 if (NS_FAILED(rv
= entry
->CopyToNative(aNewParent
, ""_ns
))) {
834 nsAutoCString pathName
;
835 if (NS_FAILED(rv2
= entry
->GetNativePath(pathName
))) {
838 printf("Operation not supported: %s\n", pathName
.get());
840 if (rv
== NS_ERROR_OUT_OF_MEMORY
) {
851 nsLocalFile::CopyToNative(nsIFile
* aNewParent
, const nsACString
& aNewName
) {
853 // check to make sure that this has been initialized properly
856 // we copy the parent here so 'aNewParent' remains immutable
857 nsCOMPtr
<nsIFile
> workParent
;
859 if (NS_FAILED(rv
= aNewParent
->Clone(getter_AddRefs(workParent
)))) {
863 if (NS_FAILED(rv
= GetParent(getter_AddRefs(workParent
)))) {
868 // check to see if we are a directory or if we are a file
870 if (NS_FAILED(rv
= IsDirectory(&isDirectory
))) {
874 nsAutoCString newPathName
;
876 if (!aNewName
.IsEmpty()) {
877 if (NS_FAILED(rv
= workParent
->AppendNative(aNewName
))) {
881 if (NS_FAILED(rv
= GetNativeLeafName(newPathName
))) {
884 if (NS_FAILED(rv
= workParent
->AppendNative(newPathName
))) {
888 if (NS_FAILED(rv
= CopyDirectoryTo(workParent
))) {
892 rv
= GetNativeTargetPathName(workParent
, aNewName
, newPathName
);
897 #ifdef DEBUG_blizzard
898 printf("nsLocalFile::CopyTo() %s -> %s\n", mPath
.get(), newPathName
.get());
901 // actually create the file.
902 auto* newFile
= new nsLocalFile();
903 nsCOMPtr
<nsIFile
> fileRef(newFile
); // release on exit
905 rv
= newFile
->InitWithNativePath(newPathName
);
910 // get the old permissions
911 uint32_t myPerms
= 0;
912 rv
= GetPermissions(&myPerms
);
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.
924 rv
= newFile
->CreateAndKeepOpen(
925 NORMAL_FILE_TYPE
, PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
, myPerms
,
926 /* aSkipAncestors = */ false, &newFD
);
931 // open the old file, too
933 if (NS_FAILED(rv
= IsSpecial(&specialFile
))) {
939 printf("Operation not supported: %s\n", mPath
.get());
941 // make sure to clean up properly
946 #if defined(XP_MACOSX)
947 bool quarantined
= true;
948 (void)HasXAttr("com.apple.quarantine"_ns
, &quarantined
);
952 rv
= OpenNSPRFileDesc(PR_RDONLY
, myPerms
, &oldFD
);
954 // make sure to clean up properly
959 #ifdef DEBUG_blizzard
960 int32_t totalRead
= 0;
961 int32_t totalWritten
= 0;
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
;
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();
992 NS_ASSERTION(bytesWritten
== bytesRead
, "short PR_Write?");
994 #ifdef DEBUG_blizzard
995 totalWritten
+= bytesWritten
;
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
);
1013 // DONE: Errors of close can occur. Read man page of
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.
1022 if (PR_Close(newFD
) < 0) {
1023 saved_write_close_error
= NSRESULT_FOR_ERRNO();
1025 // This error merits printing.
1026 fprintf(stderr
, "ERROR: PR_Close(newFD) returned error. errno = %d\n",
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();
1041 fprintf(stderr
, "ERROR: PR_Close(oldFD) returned error. errno = %d\n",
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
;
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
;
1071 nsLocalFile::CopyToFollowingLinksNative(nsIFile
* aNewParent
,
1072 const nsACString
& aNewName
) {
1073 return CopyToNative(aNewParent
, aNewName
);
1077 nsLocalFile::MoveToNative(nsIFile
* aNewParent
, const nsACString
& aNewName
) {
1080 // check to make sure that this has been initialized properly
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
)) {
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
)) {
1102 rv
= NSRESULT_FOR_ERRNO();
1106 if (NS_SUCCEEDED(rv
)) {
1108 mPath
= newPathName
;
1114 nsLocalFile::MoveToFollowingLinksNative(nsIFile
* aNewParent
,
1115 const nsACString
& aNewName
) {
1116 return MoveToNative(aNewParent
, aNewName
);
1120 nsLocalFile::Remove(bool aRecursive
, uint32_t* aRemoveCount
) {
1122 ENSURE_STAT_CACHE();
1126 nsresult rv
= IsSymlink(&isSymLink
);
1127 if (NS_FAILED(rv
)) {
1131 if (isSymLink
|| !S_ISDIR(mCachedStat
.st_mode
)) {
1132 rv
= NSRESULT_FOR_RETURN(unlink(mPath
.get()));
1133 if (NS_SUCCEEDED(rv
) && aRemoveCount
) {
1140 auto* dir
= new nsDirEnumeratorUnix();
1142 RefPtr
<nsSimpleEnumerator
> dirRef(dir
); // release on exit
1144 rv
= dir
->Init(this, false);
1145 if (NS_FAILED(rv
)) {
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
);
1166 // See bug 580434 - Bionic gives us just deleted files
1167 if (rv
== NS_ERROR_FILE_NOT_FOUND
) {
1171 if (NS_FAILED(rv
)) {
1177 rv
= NSRESULT_FOR_RETURN(rmdir(mPath
.get()));
1178 if (NS_SUCCEEDED(rv
) && aRemoveCount
) {
1184 nsresult
nsLocalFile::GetTimeImpl(PRTime
* aTime
,
1185 nsLocalFile::TimeField aTimeField
,
1186 bool aFollowLinks
) {
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
;
1206 timespec
= &fileStats
.st_atim
;
1210 case TimeField::ModifiedTime
:
1211 #if (defined(__APPLE__) && defined(__MACH__))
1212 timespec
= &fileStats
.st_mtimespec
;
1214 timespec
= &fileStats
.st_mtim
;
1219 MOZ_CRASH("Unknown TimeField");
1222 *aTime
= TimespecToMillis(*timespec
);
1227 nsresult
nsLocalFile::SetTimeImpl(PRTime aTime
,
1228 nsLocalFile::TimeField aTimeField
,
1229 bool aFollowLinks
) {
1232 using UtimesFn
= int (*)(const char*, const timeval
*);
1233 UtimesFn utimesFn
= &utimes
;
1236 if (!aFollowLinks
) {
1237 utimesFn
= &lutimes
;
1241 ENSURE_STAT_CACHE();
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.
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
;
1267 auto* copyFrom
= aTimeField
== TimeField::AccessedTime
? &mCachedStat
.st_mtim
1268 : &mCachedStat
.st_atim
;
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
);
1282 nsLocalFile::GetLastAccessedTime(PRTime
* aLastAccessedTime
) {
1283 return GetTimeImpl(aLastAccessedTime
, TimeField::AccessedTime
,
1284 /* follow links? */ true);
1288 nsLocalFile::SetLastAccessedTime(PRTime aLastAccessedTime
) {
1289 return SetTimeImpl(aLastAccessedTime
, TimeField::AccessedTime
,
1290 /* follow links? */ true);
1294 nsLocalFile::GetLastAccessedTimeOfLink(PRTime
* aLastAccessedTime
) {
1295 return GetTimeImpl(aLastAccessedTime
, TimeField::AccessedTime
,
1296 /* follow links? */ false);
1300 nsLocalFile::SetLastAccessedTimeOfLink(PRTime aLastAccessedTime
) {
1301 return SetTimeImpl(aLastAccessedTime
, TimeField::AccessedTime
,
1302 /* follow links? */ false);
1306 nsLocalFile::GetLastModifiedTime(PRTime
* aLastModTime
) {
1307 return GetTimeImpl(aLastModTime
, TimeField::ModifiedTime
,
1308 /* follow links? */ true);
1312 nsLocalFile::SetLastModifiedTime(PRTime aLastModTime
) {
1313 return SetTimeImpl(aLastModTime
, TimeField::ModifiedTime
,
1314 /* follow links ? */ true);
1318 nsLocalFile::GetLastModifiedTimeOfLink(PRTime
* aLastModTimeOfLink
) {
1319 return GetTimeImpl(aLastModTimeOfLink
, TimeField::ModifiedTime
,
1320 /* follow link? */ false);
1324 nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink
) {
1325 return SetTimeImpl(aLastModTimeOfLink
, TimeField::ModifiedTime
,
1326 /* follow links? */ false);
1330 nsLocalFile::GetCreationTime(PRTime
* aCreationTime
) {
1331 return GetCreationTimeImpl(aCreationTime
, false);
1335 nsLocalFile::GetCreationTimeOfLink(PRTime
* aCreationTimeOfLink
) {
1336 return GetCreationTimeImpl(aCreationTimeOfLink
, /* aFollowLinks = */ true);
1339 nsresult
nsLocalFile::GetCreationTimeImpl(PRTime
* aCreationTime
,
1340 bool aFollowLinks
) {
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
);
1358 return NS_ERROR_NOT_IMPLEMENTED
;
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))
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
);
1380 nsLocalFile::GetPermissionsOfLink(uint32_t* aPermissionsOfLink
) {
1382 if (NS_WARN_IF(!aPermissionsOfLink
)) {
1383 return NS_ERROR_INVALID_ARG
;
1387 if (LSTAT(mPath
.get(), &sbuf
) == -1) {
1388 return NSRESULT_FOR_ERRNO();
1390 *aPermissionsOfLink
= NORMALIZE_PERMS(sbuf
.st_mode
);
1395 nsLocalFile::SetPermissions(uint32_t aPermissions
) {
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) {
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
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
) {
1418 return NSRESULT_FOR_ERRNO();
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
;
1429 nsLocalFile::GetFileSize(int64_t* aFileSize
) {
1430 if (NS_WARN_IF(!aFileSize
)) {
1431 return NS_ERROR_INVALID_ARG
;
1434 ENSURE_STAT_CACHE();
1436 if (!S_ISDIR(mCachedStat
.st_mode
)) {
1437 *aFileSize
= (int64_t)mCachedStat
.st_size
;
1443 nsLocalFile::SetFileSize(int64_t aFileSize
) {
1446 #if defined(ANDROID)
1447 /* no truncate on bionic */
1448 int fd
= open(mPath
.get(), O_WRONLY
);
1450 return NSRESULT_FOR_ERRNO();
1453 int ret
= ftruncate(fd
, (off_t
)aFileSize
);
1457 return NSRESULT_FOR_ERRNO();
1459 #elif defined(HAVE_TRUNCATE64)
1460 if (truncate64(mPath
.get(), (off64_t
)aFileSize
) == -1) {
1461 return NSRESULT_FOR_ERRNO();
1464 off_t size
= (off_t
)aFileSize
;
1465 if (truncate(mPath
.get(), size
) == -1) {
1466 return NSRESULT_FOR_ERRNO();
1473 nsLocalFile::GetFileSizeOfLink(int64_t* aFileSize
) {
1475 if (NS_WARN_IF(!aFileSize
)) {
1476 return NS_ERROR_INVALID_ARG
;
1480 if (LSTAT(mPath
.get(), &sbuf
) == -1) {
1481 return NSRESULT_FOR_ERRNO();
1484 *aFileSize
= (int64_t)sbuf
.st_size
;
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
) {
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");
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
, ' ');
1525 char* p_dev_end
= strchr(p_dev
, ' ');
1528 aDeviceName
.Assign(p_dev
);
1540 #if defined(USE_LINUX_QUOTACTL)
1541 template <typename StatInfoFunc
, typename QuotaInfoFunc
>
1542 nsresult
nsLocalFile::GetDiskInfo(StatInfoFunc
&& aStatInfoFunc
,
1543 QuotaInfoFunc
&& aQuotaInfoFunc
,
1546 template <typename StatInfoFunc
>
1547 nsresult
nsLocalFile::GetDiskInfo(StatInfoFunc
&& aStatInfoFunc
,
1551 if (NS_WARN_IF(!aResult
)) {
1552 return NS_ERROR_INVALID_ARG
;
1555 // These systems have the operations necessary to check disk space.
1559 // check to make sure that mPath is properly initialized
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.
1575 printf("ERROR: GetDiskInfo: STATFS call FAILED. \n");
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
1595 nsAutoCString deviceName
;
1596 if (!GetDeviceName(major(mCachedStat
.st_dev
), minor(mCachedStat
.st_dev
),
1598 // Returns info from statfs
1603 if (!quotactl(QCMD(Q_GETQUOTA
, USRQUOTA
), deviceName
.get(), getuid(),
1606 && dq
.dqb_valid
& QIF_BLIMITS
1608 && dq
.dqb_bhardlimit
) {
1609 CheckedInt64 quotaResult
= std::forward
<QuotaInfoFunc
>(aQuotaInfoFunc
)(dq
);
1610 if (!quotaResult
.isValid()) {
1611 // Returns info from statfs
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
);
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.
1637 printf("ERROR: GetDiskInfo: Not implemented for plaforms without statfs.\n");
1639 return NS_ERROR_NOT_IMPLEMENTED
;
1645 nsLocalFile::GetDiskSpaceAvailable(int64_t* aDiskSpaceAvailable
) {
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
;
1660 aDiskSpaceAvailable
);
1664 nsLocalFile::GetDiskCapacity(int64_t* aDiskCapacity
) {
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
;
1679 nsLocalFile::GetParent(nsIFile
** aParent
) {
1681 if (NS_WARN_IF(!aParent
)) {
1682 return NS_ERROR_INVALID_ARG
;
1686 // if '/' we are at the top of the volume, return null
1687 if (mPath
.EqualsLiteral("/")) {
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?");
1697 return NS_ERROR_FILE_INVALID_PATH
;
1700 // for the case where we are at '/'
1701 if (slashp
== buffer
) {
1705 // temporarily terminate buffer at the last significant slash
1709 nsCOMPtr
<nsIFile
> localFile
;
1710 nsresult rv
= NS_NewNativeLocalFile(nsDependentCString(buffer
), true,
1711 getter_AddRefs(localFile
));
1713 // make buffer whole again
1716 if (NS_FAILED(rv
)) {
1720 localFile
.forget(aParent
);
1725 * The results of Exists, isWritable and isReadable are not cached.
1729 nsLocalFile::Exists(bool* aResult
) {
1731 if (NS_WARN_IF(!aResult
)) {
1732 return NS_ERROR_INVALID_ARG
;
1735 *aResult
= (access(mPath
.get(), F_OK
) == 0);
1740 nsLocalFile::IsWritable(bool* aResult
) {
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
) {
1750 return NSRESULT_FOR_ERRNO();
1754 nsLocalFile::IsReadable(bool* aResult
) {
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
) {
1764 return NSRESULT_FOR_ERRNO();
1768 nsLocalFile::IsExecutable(bool* aResult
) {
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.
1781 nsresult rv
= IsSymlink(&symLink
);
1782 if (NS_FAILED(rv
)) {
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.
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
1811 "ftploc", // Can point to other files.
1812 "inetloc", // Shouldn't be able to do the same, but can, due to
1813 // macOS vulnerabilities.
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.
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).
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
);
1841 if (result
== noErr
) {
1842 if ((theInfo
.flags
& kLSItemInfoIsApplication
) != 0) {
1849 // Then check the execute bit.
1850 *aResult
= (access(mPath
.get(), X_OK
) == 0);
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
1858 *aResult
= (STAT(mPath
.get(), &buf
) == 0);
1859 if (*aResult
|| errno
== EACCES
) {
1860 *aResult
= *aResult
&& (buf
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
));
1864 return NSRESULT_FOR_ERRNO();
1867 if (*aResult
|| errno
== EACCES
) {
1870 return NSRESULT_FOR_ERRNO();
1874 nsLocalFile::IsDirectory(bool* aResult
) {
1875 if (NS_WARN_IF(!aResult
)) {
1876 return NS_ERROR_INVALID_ARG
;
1879 ENSURE_STAT_CACHE();
1880 *aResult
= S_ISDIR(mCachedStat
.st_mode
);
1885 nsLocalFile::IsFile(bool* aResult
) {
1886 if (NS_WARN_IF(!aResult
)) {
1887 return NS_ERROR_INVALID_ARG
;
1890 ENSURE_STAT_CACHE();
1891 *aResult
= S_ISREG(mCachedStat
.st_mode
);
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
== '.');
1907 nsLocalFile::IsSymlink(bool* aResult
) {
1908 if (NS_WARN_IF(!aResult
)) {
1909 return NS_ERROR_INVALID_ARG
;
1913 struct STAT symStat
;
1914 if (LSTAT(mPath
.get(), &symStat
) == -1) {
1915 return NSRESULT_FOR_ERRNO();
1917 *aResult
= S_ISLNK(symStat
.st_mode
);
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
) ||
1929 S_ISSOCK(mCachedStat
.st_mode
) ||
1931 S_ISFIFO(mCachedStat
.st_mode
);
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
;
1946 nsAutoCString inPath
;
1947 nsresult rv
= aInFile
->GetNativePath(inPath
);
1948 if (NS_FAILED(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());
1959 nsLocalFile::Contains(nsIFile
* aInFile
, bool* aResult
) {
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
;
1971 if (NS_FAILED(rv
= aInFile
->GetNativePath(inPath
))) {
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
] == '/') {
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();
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
2017 handle
.Finish(bytesWritten
, false);
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();
2030 nsLocalFile::GetNativeTarget(nsACString
& aResult
) {
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
)) {
2049 nsCOMPtr
<nsIFile
> self(this);
2050 int32_t maxLinks
= 40;
2052 if (maxLinks
-- == 0) {
2053 rv
= NS_ERROR_FILE_UNRESOLVABLE_SYMLINK
;
2057 if (target
[0] != '/') {
2058 nsCOMPtr
<nsIFile
> parent
;
2059 if (NS_FAILED(rv
= self
->GetParent(getter_AddRefs(parent
)))) {
2062 if (NS_FAILED(rv
= parent
->AppendRelativeNativePath(target
))) {
2065 if (NS_FAILED(rv
= parent
->GetNativePath(aResult
))) {
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) {
2081 // And of course we're done if it isn't a symlink.
2082 if (!S_ISLNK(symStat
.st_mode
)) {
2086 nsAutoCString newTarget
;
2087 rv
= ReadLinkSafe(flatRetval
, symStat
.st_size
, newTarget
);
2088 if (NS_FAILED(rv
)) {
2095 if (NS_FAILED(rv
)) {
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;
2109 dir
.forget(aEntries
);
2116 nsLocalFile::Load(PRLibrary
** aResult
) {
2118 if (NS_WARN_IF(!aResult
)) {
2119 return NS_ERROR_INVALID_ARG
;
2122 #ifdef NS_BUILD_REFCNT_LOGGING
2123 nsTraceRefcnt::SetActivityIsLegal(false);
2126 *aResult
= PR_LoadLibrary(mPath
.get());
2128 #ifdef NS_BUILD_REFCNT_LOGGING
2129 nsTraceRefcnt::SetActivityIsLegal(true);
2133 return NS_ERROR_FAILURE
;
2139 nsLocalFile::GetPersistentDescriptor(nsACString
& aPersistentDescriptor
) {
2140 return GetNativePath(aPersistentDescriptor
);
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);
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
);
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
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
)) {
2190 FSRef resolvedFSRef
;
2191 OSErr err
= ::FSResolveAlias(nullptr, (AliasHandle
)newHandle
, &resolvedFSRef
,
2194 rv
= MacErrorMapper(err
);
2195 DisposeHandle(newHandle
);
2196 if (NS_FAILED(rv
)) {
2200 return InitWithFSRef(&resolvedFSRef
);
2202 return InitWithNativePath(aPersistentDescriptor
);
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
);
2215 return NS_ERROR_FAILURE
;
2217 return giovfs
->RevealFile(this);
2218 #elif defined(MOZ_WIDGET_COCOA)
2220 if (NS_SUCCEEDED(GetCFURL(&url
))) {
2221 nsresult rv
= CocoaFileUtils::RevealFileInFinder(url
);
2225 return NS_ERROR_FAILURE
;
2227 return NS_ERROR_FAILURE
;
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
);
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)
2249 if (NS_SUCCEEDED(GetCFURL(&url
))) {
2250 nsresult rv
= CocoaFileUtils::OpenURL(url
);
2254 return NS_ERROR_FAILURE
;
2256 return NS_ERROR_FAILURE
;
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
)) {
2270 file
.forget(aResult
);
2274 //-----------------------------------------------------------------------------
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
);
2334 nsLocalFile::MoveToFollowingLinks(nsIFile
* aNewParentDir
,
2335 const nsAString
& aNewName
) {
2336 SET_UCS_2ARGS_2(MoveToFollowingLinksNative
, aNewParentDir
, aNewName
);
2340 nsLocalFile::RenameTo(nsIFile
* aNewParentDir
, const nsAString
& aNewName
) {
2341 SET_UCS_2ARGS_2(RenameToNative
, aNewParentDir
, aNewName
);
2345 nsLocalFile::RenameToNative(nsIFile
* aNewParentDir
,
2346 const nsACString
& aNewName
) {
2349 // check to make sure that this has been initialized properly
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
)) {
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
;
2368 rv
= NSRESULT_FOR_ERRNO();
2375 nsresult
nsLocalFile::GetTarget(nsAString
& aResult
) {
2376 GET_UCS(GetNativeTarget
, aResult
);
2379 nsresult
NS_NewLocalFile(const nsAString
& aPath
, bool aFollowLinks
,
2380 nsIFile
** aResult
) {
2382 nsresult rv
= NS_CopyUnicodeToNative(aPath
, buf
);
2383 if (NS_FAILED(rv
)) {
2386 return NS_NewNativeLocalFile(buf
, aFollowLinks
, aResult
);
2391 #ifdef MOZ_WIDGET_COCOA
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);
2401 if (errno
== ENOATTR
) {
2404 return NSRESULT_FOR_ERRNO();
2414 nsLocalFile::GetXAttr(const nsACString
& aAttrName
,
2415 nsTArray
<uint8_t>& aAttrValue
) {
2418 nsAutoCString attrName
{aAttrName
};
2420 ssize_t size
= getxattr(mPath
.get(), attrName
.get(), nullptr, 0, 0, 0);
2423 return NSRESULT_FOR_ERRNO();
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
);
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();
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();
2473 static nsresult
MacErrorMapper(OSErr inErr
) {
2482 case afpObjectNotFound
:
2483 case afpDirNotFound
:
2484 outErr
= NS_ERROR_FILE_NOT_FOUND
;
2488 case afpObjectExists
:
2489 outErr
= NS_ERROR_FILE_ALREADY_EXISTS
;
2494 outErr
= NS_ERROR_FILE_NO_DEVICE_SPACE
;
2499 outErr
= NS_ERROR_FILE_IS_LOCKED
;
2502 case afpAccessDenied
:
2503 outErr
= NS_ERROR_FILE_ACCESS_DENIED
;
2506 case afpDirNotEmpty
:
2507 outErr
= NS_ERROR_FILE_DIR_NOT_EMPTY
;
2510 // Can't find good map for some
2512 outErr
= NS_ERROR_FAILURE
;
2516 outErr
= NS_ERROR_FAILURE
;
2523 static nsresult
CFStringReftoUTF8(CFStringRef aInStrRef
, nsACString
& aOutStr
) {
2524 // first see if the conversion would succeed and find the length of the
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
,
2543 return NS_ERROR_FAILURE
;
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
;
2558 nsLocalFile::InitWithFSRef(const FSRef
* aFSRef
) {
2559 if (NS_WARN_IF(!aFSRef
)) {
2560 return NS_ERROR_INVALID_ARG
;
2563 CFURLRef newURLRef
= ::CFURLCreateFromFSRef(kCFAllocatorDefault
, aFSRef
);
2565 nsresult rv
= InitWithCFURL(newURLRef
);
2566 ::CFRelease(newURLRef
);
2570 return NS_ERROR_FAILURE
;
2574 nsLocalFile::GetCFURL(CFURLRef
* aResult
) {
2578 IsDirectory(&isDir
);
2579 *aResult
= ::CFURLCreateFromFileSystemRepresentation(
2580 kCFAllocatorDefault
, (UInt8
*)mPath
.get(), mPath
.Length(), isDir
);
2582 return (*aResult
? NS_OK
: NS_ERROR_FAILURE
);
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
)) {
2605 nsLocalFile::GetFSSpec(FSSpec
* aResult
) {
2606 if (NS_WARN_IF(!aResult
)) {
2607 return NS_ERROR_INVALID_ARG
;
2611 nsresult rv
= GetFSRef(&fsRef
);
2612 if (NS_SUCCEEDED(rv
)) {
2613 OSErr err
= ::FSGetCatalogInfo(&fsRef
, kFSCatInfoNone
, nullptr, nullptr,
2615 return MacErrorMapper(err
);
2622 nsLocalFile::GetFileSizeWithResFork(int64_t* aFileSizeWithResFork
) {
2623 if (NS_WARN_IF(!aFileSizeWithResFork
)) {
2624 return NS_ERROR_INVALID_ARG
;
2628 nsresult rv
= GetFSRef(&fsRef
);
2629 if (NS_FAILED(rv
)) {
2633 FSCatalogInfo catalogInfo
;
2635 ::FSGetCatalogInfo(&fsRef
, kFSCatInfoDataSizes
+ kFSCatInfoRsrcSizes
,
2636 &catalogInfo
, nullptr, nullptr, nullptr);
2638 return MacErrorMapper(err
);
2641 *aFileSizeWithResFork
=
2642 catalogInfo
.dataLogicalSize
+ catalogInfo
.rsrcLogicalSize
;
2647 nsLocalFile::GetFileType(OSType
* aFileType
) {
2649 if (NS_SUCCEEDED(GetCFURL(&url
))) {
2650 nsresult rv
= CocoaFileUtils::GetFileTypeCode(url
, aFileType
);
2654 return NS_ERROR_FAILURE
;
2658 nsLocalFile::SetFileType(OSType aFileType
) {
2660 if (NS_SUCCEEDED(GetCFURL(&url
))) {
2661 nsresult rv
= CocoaFileUtils::SetFileTypeCode(url
, aFileType
);
2665 return NS_ERROR_FAILURE
;
2669 nsLocalFile::GetFileCreator(OSType
* aFileCreator
) {
2671 if (NS_SUCCEEDED(GetCFURL(&url
))) {
2672 nsresult rv
= CocoaFileUtils::GetFileCreatorCode(url
, aFileCreator
);
2676 return NS_ERROR_FAILURE
;
2680 nsLocalFile::SetFileCreator(OSType aFileCreator
) {
2682 if (NS_SUCCEEDED(GetCFURL(&url
))) {
2683 nsresult rv
= CocoaFileUtils::SetFileCreatorCode(url
, aFileCreator
);
2687 return NS_ERROR_FAILURE
;
2691 nsLocalFile::LaunchWithDoc(nsIFile
* aDocToLoad
, bool aLaunchInBackground
) {
2693 nsresult rv
= IsExecutable(&isExecutable
);
2694 if (NS_FAILED(rv
)) {
2697 if (!isExecutable
) {
2698 return NS_ERROR_FILE_EXECUTION_FAILED
;
2701 FSRef appFSRef
, docFSRef
;
2702 rv
= GetFSRef(&appFSRef
);
2703 if (NS_FAILED(rv
)) {
2708 nsCOMPtr
<nsILocalFileMac
> macDoc
= do_QueryInterface(aDocToLoad
);
2709 rv
= macDoc
->GetFSRef(&docFSRef
);
2710 if (NS_FAILED(rv
)) {
2715 LSLaunchFlags theLaunchFlags
= kLSLaunchDefaults
;
2716 LSLaunchFSRefSpec thelaunchSpec
;
2718 if (aLaunchInBackground
) {
2719 theLaunchFlags
|= kLSLaunchDontSwitch
;
2721 memset(&thelaunchSpec
, 0, sizeof(LSLaunchFSRefSpec
));
2723 thelaunchSpec
.appRef
= &appFSRef
;
2725 thelaunchSpec
.numDocs
= 1;
2726 thelaunchSpec
.itemRefs
= &docFSRef
;
2728 thelaunchSpec
.launchFlags
= theLaunchFlags
;
2730 OSErr err
= ::LSOpenFromRefSpec(&thelaunchSpec
, nullptr);
2732 return MacErrorMapper(err
);
2739 nsLocalFile::OpenDocWithApp(nsIFile
* aAppToOpenWith
, bool aLaunchInBackground
) {
2741 nsresult rv
= GetFSRef(&docFSRef
);
2742 if (NS_FAILED(rv
)) {
2746 if (!aAppToOpenWith
) {
2747 OSErr err
= ::LSOpenFSRef(&docFSRef
, nullptr);
2748 return MacErrorMapper(err
);
2751 nsCOMPtr
<nsILocalFileMac
> appFileMac
= do_QueryInterface(aAppToOpenWith
, &rv
);
2757 rv
= appFileMac
->IsExecutable(&isExecutable
);
2758 if (NS_FAILED(rv
)) {
2761 if (!isExecutable
) {
2762 return NS_ERROR_FILE_EXECUTION_FAILED
;
2766 rv
= appFileMac
->GetFSRef(&appFSRef
);
2767 if (NS_FAILED(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);
2786 return MacErrorMapper(err
);
2793 nsLocalFile::IsPackage(bool* aResult
) {
2794 if (NS_WARN_IF(!aResult
)) {
2795 return NS_ERROR_INVALID_ARG
;
2800 nsresult rv
= GetCFURL(&url
);
2801 if (NS_FAILED(rv
)) {
2805 LSItemInfoRecord info
;
2807 ::LSCopyItemInfoForURL(url
, kLSRequestBasicFlagsOnly
, &info
);
2811 if (status
!= noErr
) {
2812 return NS_ERROR_FAILURE
;
2815 *aResult
= !!(info
.flags
& kLSItemInfoIsPackage
);
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
;
2829 rv
= GetLeafName(name
);
2830 if (NS_FAILED(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);
2839 aOutBundleName
= name
;
2846 nsLocalFile::GetBundleIdentifier(nsACString
& aOutBundleIdentifier
) {
2847 nsresult rv
= NS_ERROR_FAILURE
;
2850 if (NS_SUCCEEDED(GetCFURL(&urlRef
))) {
2851 CFBundleRef bundle
= ::CFBundleCreate(nullptr, urlRef
);
2853 CFStringRef bundleIdentifier
= ::CFBundleGetIdentifier(bundle
);
2854 if (bundleIdentifier
) {
2855 rv
= CFStringReftoUTF8(bundleIdentifier
, aOutBundleIdentifier
);
2857 ::CFRelease(bundle
);
2859 ::CFRelease(urlRef
);
2866 nsLocalFile::GetBundleContentsLastModifiedTime(int64_t* aLastModTime
) {
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");
2881 if (PR_GetFileInfo64(infoPlistPath
.get(), &info
) != PR_SUCCESS
) {
2882 return GetLastModifiedTime(aLastModTime
);
2884 int64_t modTime
= int64_t(info
.modifyTime
);
2888 *aLastModTime
= modTime
/ int64_t(PR_USEC_PER_MSEC
);
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
)) {
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
)) {
2916 file
.forget(aResult
);
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
)) {
2928 file
.forget(aResult
);