michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * Implementation of nsIFile for "unixy" systems. michael@0: */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #if defined(VMS) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H) michael@0: #define USE_LINUX_QUOTACTL michael@0: #include michael@0: #endif michael@0: michael@0: #include "xpcom-private.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsCRT.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsMemory.h" michael@0: #include "nsIFile.h" michael@0: #include "nsString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsLocalFile.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "prproces.h" michael@0: #include "nsIDirectoryEnumerator.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: #include "private/pprio.h" michael@0: #include "prlink.h" michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include "nsIGIOService.h" michael@0: #include "nsIGnomeVFSService.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_COCOA michael@0: #include michael@0: #include "CocoaFileUtils.h" michael@0: #include "prmem.h" michael@0: #include "plbase64.h" michael@0: michael@0: static nsresult MacErrorMapper(OSErr inErr); michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: #include "AndroidBridge.h" michael@0: #include "nsIMIMEService.h" michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef MOZ_ENABLE_CONTENTACTION michael@0: #include michael@0: #endif michael@0: michael@0: #include "nsNativeCharsetUtils.h" michael@0: #include "nsTraceRefcnt.h" michael@0: #include "nsHashKeys.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #define ENSURE_STAT_CACHE() \ michael@0: PR_BEGIN_MACRO \ michael@0: if (!FillStatCache()) \ michael@0: return NSRESULT_FOR_ERRNO(); \ michael@0: PR_END_MACRO michael@0: michael@0: #define CHECK_mPath() \ michael@0: PR_BEGIN_MACRO \ michael@0: if (mPath.IsEmpty()) \ michael@0: return NS_ERROR_NOT_INITIALIZED; \ michael@0: PR_END_MACRO michael@0: michael@0: /* directory enumerator */ michael@0: class michael@0: nsDirEnumeratorUnix MOZ_FINAL : public nsISimpleEnumerator, michael@0: public nsIDirectoryEnumerator michael@0: { michael@0: public: michael@0: nsDirEnumeratorUnix(); michael@0: michael@0: // nsISupports interface michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // nsISimpleEnumerator interface michael@0: NS_DECL_NSISIMPLEENUMERATOR michael@0: michael@0: // nsIDirectoryEnumerator interface michael@0: NS_DECL_NSIDIRECTORYENUMERATOR michael@0: michael@0: NS_IMETHOD Init(nsLocalFile *parent, bool ignored); michael@0: michael@0: private: michael@0: ~nsDirEnumeratorUnix(); michael@0: michael@0: protected: michael@0: NS_IMETHOD GetNextEntry(); michael@0: michael@0: DIR *mDir; michael@0: struct dirent *mEntry; michael@0: nsCString mParentPath; michael@0: }; michael@0: michael@0: nsDirEnumeratorUnix::nsDirEnumeratorUnix() : michael@0: mDir(nullptr), michael@0: mEntry(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsDirEnumeratorUnix::~nsDirEnumeratorUnix() michael@0: { michael@0: Close(); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator) michael@0: michael@0: NS_IMETHODIMP michael@0: nsDirEnumeratorUnix::Init(nsLocalFile *parent, bool resolveSymlinks /*ignored*/) michael@0: { michael@0: nsAutoCString dirPath; michael@0: if (NS_FAILED(parent->GetNativePath(dirPath)) || michael@0: dirPath.IsEmpty()) { michael@0: return NS_ERROR_FILE_INVALID_PATH; michael@0: } michael@0: michael@0: if (NS_FAILED(parent->GetNativePath(mParentPath))) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: mDir = opendir(dirPath.get()); michael@0: if (!mDir) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: return GetNextEntry(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDirEnumeratorUnix::HasMoreElements(bool *result) michael@0: { michael@0: *result = mDir && mEntry; michael@0: if (!*result) michael@0: Close(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDirEnumeratorUnix::GetNext(nsISupports **_retval) michael@0: { michael@0: nsCOMPtr file; michael@0: nsresult rv = GetNextFile(getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: NS_IF_ADDREF(*_retval = file); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDirEnumeratorUnix::GetNextEntry() michael@0: { michael@0: do { michael@0: errno = 0; michael@0: mEntry = readdir(mDir); michael@0: michael@0: // end of dir or error michael@0: if (!mEntry) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: michael@0: // keep going past "." and ".." michael@0: } while (mEntry->d_name[0] == '.' && michael@0: (mEntry->d_name[1] == '\0' || // .\0 michael@0: (mEntry->d_name[1] == '.' && michael@0: mEntry->d_name[2] == '\0'))); // ..\0 michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval) michael@0: { michael@0: nsresult rv; michael@0: if (!mDir || !mEntry) { michael@0: *_retval = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr file = new nsLocalFile(); michael@0: michael@0: if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) || michael@0: NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name)))) michael@0: return rv; michael@0: michael@0: file.forget(_retval); michael@0: return GetNextEntry(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDirEnumeratorUnix::Close() michael@0: { michael@0: if (mDir) { michael@0: closedir(mDir); michael@0: mDir = nullptr; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsLocalFile::nsLocalFile() michael@0: { michael@0: } michael@0: michael@0: nsLocalFile::nsLocalFile(const nsLocalFile& other) michael@0: : mPath(other.mPath) michael@0: { michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_COCOA michael@0: NS_IMPL_ISUPPORTS(nsLocalFile, michael@0: nsILocalFileMac, michael@0: nsILocalFile, michael@0: nsIFile, michael@0: nsIHashable) michael@0: #else michael@0: NS_IMPL_ISUPPORTS(nsLocalFile, michael@0: nsILocalFile, michael@0: nsIFile, michael@0: nsIHashable) michael@0: #endif michael@0: michael@0: nsresult michael@0: nsLocalFile::nsLocalFileConstructor(nsISupports *outer, michael@0: const nsIID &aIID, michael@0: void **aInstancePtr) michael@0: { michael@0: if (NS_WARN_IF(!aInstancePtr)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: if (NS_WARN_IF(outer)) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: *aInstancePtr = nullptr; michael@0: michael@0: nsCOMPtr inst = new nsLocalFile(); michael@0: return inst->QueryInterface(aIID, aInstancePtr); michael@0: } michael@0: michael@0: bool michael@0: nsLocalFile::FillStatCache() { michael@0: if (STAT(mPath.get(), &mCachedStat) == -1) { michael@0: // try lstat it may be a symlink michael@0: if (LSTAT(mPath.get(), &mCachedStat) == -1) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Clone(nsIFile **file) michael@0: { michael@0: // Just copy-construct ourselves michael@0: nsRefPtr copy = new nsLocalFile(*this); michael@0: copy.forget(file); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::InitWithNativePath(const nsACString &filePath) michael@0: { michael@0: if (filePath.Equals("~") || Substring(filePath, 0, 2).EqualsLiteral("~/")) { michael@0: nsCOMPtr homeDir; michael@0: nsAutoCString homePath; michael@0: if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR, michael@0: getter_AddRefs(homeDir))) || michael@0: NS_FAILED(homeDir->GetNativePath(homePath))) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mPath = homePath; michael@0: if (filePath.Length() > 2) michael@0: mPath.Append(Substring(filePath, 1, filePath.Length() - 1)); michael@0: } else { michael@0: if (filePath.IsEmpty() || filePath.First() != '/') michael@0: return NS_ERROR_FILE_UNRECOGNIZED_PATH; michael@0: mPath = filePath; michael@0: } michael@0: michael@0: // trim off trailing slashes michael@0: ssize_t len = mPath.Length(); michael@0: while ((len > 1) && (mPath[len - 1] == '/')) michael@0: --len; michael@0: mPath.SetLength(len); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::CreateAllAncestors(uint32_t permissions) michael@0: { michael@0: // I promise to play nice michael@0: char *buffer = mPath.BeginWriting(), michael@0: *slashp = buffer; michael@0: michael@0: #ifdef DEBUG_NSIFILE michael@0: fprintf(stderr, "nsIFile: before: %s\n", buffer); michael@0: #endif michael@0: michael@0: while ((slashp = strchr(slashp + 1, '/'))) { michael@0: /* michael@0: * Sequences of '/' are equivalent to a single '/'. michael@0: */ michael@0: if (slashp[1] == '/') michael@0: continue; michael@0: michael@0: /* michael@0: * If the path has a trailing slash, don't make the last component, michael@0: * because we'll get EEXIST in Create when we try to build the final michael@0: * component again, and it's easier to condition the logic here than michael@0: * there. michael@0: */ michael@0: if (slashp[1] == '\0') michael@0: break; michael@0: michael@0: /* Temporarily NUL-terminate here */ michael@0: *slashp = '\0'; michael@0: #ifdef DEBUG_NSIFILE michael@0: fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer); michael@0: #endif michael@0: int mkdir_result = mkdir(buffer, permissions); michael@0: int mkdir_errno = errno; michael@0: if (mkdir_result == -1) { michael@0: /* michael@0: * Always set |errno| to EEXIST if the dir already exists michael@0: * (we have to do this here since the errno value is not consistent michael@0: * in all cases - various reasons like different platform, michael@0: * automounter-controlled dir, etc. can affect it (see bug 125489 michael@0: * for details)). michael@0: */ michael@0: if (access(buffer, F_OK) == 0) { michael@0: mkdir_errno = EEXIST; michael@0: } michael@0: } michael@0: michael@0: /* Put the / back before we (maybe) return */ michael@0: *slashp = '/'; michael@0: michael@0: /* michael@0: * We could get EEXIST for an existing file -- not directory -- michael@0: * with the name of one of our ancestors, but that's OK: we'll get michael@0: * ENOTDIR when we try to make the next component in the path, michael@0: * either here on back in Create, and error out appropriately. michael@0: */ michael@0: if (mkdir_result == -1 && mkdir_errno != EEXIST) michael@0: return nsresultForErrno(mkdir_errno); michael@0: } michael@0: michael@0: #ifdef DEBUG_NSIFILE michael@0: fprintf(stderr, "nsIFile: after: %s\n", buffer); michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::OpenNSPRFileDesc(int32_t flags, int32_t mode, PRFileDesc **_retval) michael@0: { michael@0: *_retval = PR_Open(mPath.get(), flags, mode); michael@0: if (! *_retval) michael@0: return NS_ErrorAccordingToNSPR(); michael@0: michael@0: if (flags & DELETE_ON_CLOSE) { michael@0: PR_Delete(mPath.get()); michael@0: } michael@0: michael@0: #if defined(HAVE_POSIX_FADVISE) michael@0: if (flags & OS_READAHEAD) { michael@0: posix_fadvise(PR_FileDesc2NativeHandle(*_retval), 0, 0, michael@0: POSIX_FADV_SEQUENTIAL); michael@0: } michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval) michael@0: { michael@0: *_retval = fopen(mPath.get(), mode); michael@0: if (! *_retval) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static int michael@0: do_create(const char *path, int flags, mode_t mode, PRFileDesc **_retval) michael@0: { michael@0: *_retval = PR_Open(path, flags, mode); michael@0: return *_retval ? 0 : -1; michael@0: } michael@0: michael@0: static int michael@0: do_mkdir(const char *path, int flags, mode_t mode, PRFileDesc **_retval) michael@0: { michael@0: *_retval = nullptr; michael@0: return mkdir(path, mode); michael@0: } michael@0: michael@0: nsresult michael@0: nsLocalFile::CreateAndKeepOpen(uint32_t type, int flags, michael@0: uint32_t permissions, PRFileDesc **_retval) michael@0: { michael@0: if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE) michael@0: return NS_ERROR_FILE_UNKNOWN_TYPE; michael@0: michael@0: int result; michael@0: int (*createFunc)(const char *, int, mode_t, PRFileDesc **) = michael@0: (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir; michael@0: michael@0: result = createFunc(mPath.get(), flags, permissions, _retval); michael@0: if (result == -1 && errno == ENOENT) { michael@0: /* michael@0: * If we failed because of missing ancestor components, try to create michael@0: * them and then retry the original creation. michael@0: * michael@0: * Ancestor directories get the same permissions as the file we're michael@0: * creating, with the X bit set for each of (user,group,other) with michael@0: * an R bit in the original permissions. If you want to do anything michael@0: * fancy like setgid or sticky bits, do it by hand. michael@0: */ michael@0: int dirperm = permissions; michael@0: if (permissions & S_IRUSR) michael@0: dirperm |= S_IXUSR; michael@0: if (permissions & S_IRGRP) michael@0: dirperm |= S_IXGRP; michael@0: if (permissions & S_IROTH) michael@0: dirperm |= S_IXOTH; michael@0: michael@0: #ifdef DEBUG_NSIFILE michael@0: fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions, michael@0: dirperm); michael@0: #endif michael@0: michael@0: if (NS_FAILED(CreateAllAncestors(dirperm))) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: #ifdef DEBUG_NSIFILE michael@0: fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get()); michael@0: #endif michael@0: result = createFunc(mPath.get(), flags, permissions, _retval); michael@0: } michael@0: return NSRESULT_FOR_RETURN(result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Create(uint32_t type, uint32_t permissions) michael@0: { michael@0: PRFileDesc *junk = nullptr; michael@0: nsresult rv = CreateAndKeepOpen(type, michael@0: PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE | michael@0: PR_EXCL, michael@0: permissions, michael@0: &junk); michael@0: if (junk) michael@0: PR_Close(junk); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::AppendNative(const nsACString &fragment) michael@0: { michael@0: if (fragment.IsEmpty()) michael@0: return NS_OK; michael@0: michael@0: // only one component of path can be appended michael@0: nsACString::const_iterator begin, end; michael@0: if (FindCharInReadable('/', fragment.BeginReading(begin), michael@0: fragment.EndReading(end))) michael@0: return NS_ERROR_FILE_UNRECOGNIZED_PATH; michael@0: michael@0: return AppendRelativeNativePath(fragment); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::AppendRelativeNativePath(const nsACString &fragment) michael@0: { michael@0: if (fragment.IsEmpty()) michael@0: return NS_OK; michael@0: michael@0: // No leading '/' michael@0: if (fragment.First() == '/') michael@0: return NS_ERROR_FILE_UNRECOGNIZED_PATH; michael@0: michael@0: if (mPath.EqualsLiteral("/")) michael@0: mPath.Append(fragment); michael@0: else michael@0: mPath.Append(NS_LITERAL_CSTRING("/") + fragment); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Normalize() michael@0: { michael@0: char resolved_path[PATH_MAX] = ""; michael@0: char *resolved_path_ptr = nullptr; michael@0: michael@0: resolved_path_ptr = realpath(mPath.get(), resolved_path); michael@0: michael@0: // if there is an error, the return is null. michael@0: if (!resolved_path_ptr) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: michael@0: mPath = resolved_path; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin, michael@0: nsACString::const_iterator &end) michael@0: { michael@0: // XXX perhaps we should cache this?? michael@0: michael@0: mPath.BeginReading(begin); michael@0: mPath.EndReading(end); michael@0: michael@0: nsACString::const_iterator it = end; michael@0: nsACString::const_iterator stop = begin; michael@0: --stop; michael@0: while (--it != stop) { michael@0: if (*it == '/') { michael@0: begin = ++it; michael@0: return; michael@0: } michael@0: } michael@0: // else, the entire path is the leaf name (which means this michael@0: // isn't an absolute path... unexpected??) michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetNativeLeafName(nsACString &aLeafName) michael@0: { michael@0: nsACString::const_iterator begin, end; michael@0: LocateNativeLeafName(begin, end); michael@0: aLeafName = Substring(begin, end); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetNativeLeafName(const nsACString &aLeafName) michael@0: { michael@0: nsACString::const_iterator begin, end; michael@0: LocateNativeLeafName(begin, end); michael@0: mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetNativePath(nsACString &_retval) michael@0: { michael@0: _retval = mPath; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsLocalFile::GetNativeTargetPathName(nsIFile *newParent, michael@0: const nsACString &newName, michael@0: nsACString &_retval) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr oldParent; michael@0: michael@0: if (!newParent) { michael@0: if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent)))) michael@0: return rv; michael@0: newParent = oldParent.get(); michael@0: } else { michael@0: // check to see if our target directory exists michael@0: bool targetExists; michael@0: if (NS_FAILED(rv = newParent->Exists(&targetExists))) michael@0: return rv; michael@0: michael@0: if (!targetExists) { michael@0: // XXX create the new directory with some permissions michael@0: rv = newParent->Create(DIRECTORY_TYPE, 0755); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } else { michael@0: // make sure that the target is actually a directory michael@0: bool targetIsDirectory; michael@0: if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory))) michael@0: return rv; michael@0: if (!targetIsDirectory) michael@0: return NS_ERROR_FILE_DESTINATION_NOT_DIR; michael@0: } michael@0: } michael@0: michael@0: nsACString::const_iterator nameBegin, nameEnd; michael@0: if (!newName.IsEmpty()) { michael@0: newName.BeginReading(nameBegin); michael@0: newName.EndReading(nameEnd); michael@0: } michael@0: else michael@0: LocateNativeLeafName(nameBegin, nameEnd); michael@0: michael@0: nsAutoCString dirName; michael@0: if (NS_FAILED(rv = newParent->GetNativePath(dirName))) michael@0: return rv; michael@0: michael@0: _retval = dirName michael@0: + NS_LITERAL_CSTRING("/") michael@0: + Substring(nameBegin, nameEnd); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsLocalFile::CopyDirectoryTo(nsIFile *newParent) michael@0: { michael@0: nsresult rv; michael@0: /* michael@0: * dirCheck is used for various boolean test results such as from Equals, michael@0: * Exists, isDir, etc. michael@0: */ michael@0: bool dirCheck, isSymlink; michael@0: uint32_t oldPerms; michael@0: michael@0: if (NS_FAILED(rv = IsDirectory(&dirCheck))) michael@0: return rv; michael@0: if (!dirCheck) michael@0: return CopyToNative(newParent, EmptyCString()); michael@0: michael@0: if (NS_FAILED(rv = Equals(newParent, &dirCheck))) michael@0: return rv; michael@0: if (dirCheck) { michael@0: // can't copy dir to itself michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: if (NS_FAILED(rv = newParent->Exists(&dirCheck))) michael@0: return rv; michael@0: // get the dirs old permissions michael@0: if (NS_FAILED(rv = GetPermissions(&oldPerms))) michael@0: return rv; michael@0: if (!dirCheck) { michael@0: if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms))) michael@0: return rv; michael@0: } else { // dir exists lets try to use leaf michael@0: nsAutoCString leafName; michael@0: if (NS_FAILED(rv = GetNativeLeafName(leafName))) michael@0: return rv; michael@0: if (NS_FAILED(rv = newParent->AppendNative(leafName))) michael@0: return rv; michael@0: if (NS_FAILED(rv = newParent->Exists(&dirCheck))) michael@0: return rv; michael@0: if (dirCheck) michael@0: return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists michael@0: if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms))) michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr dirIterator; michael@0: if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator)))) michael@0: return rv; michael@0: michael@0: bool hasMore = false; michael@0: while (dirIterator->HasMoreElements(&hasMore), hasMore) { michael@0: nsCOMPtr supports; michael@0: nsCOMPtr entry; michael@0: rv = dirIterator->GetNext(getter_AddRefs(supports)); michael@0: entry = do_QueryInterface(supports); michael@0: if (NS_FAILED(rv) || !entry) michael@0: continue; michael@0: if (NS_FAILED(rv = entry->IsSymlink(&isSymlink))) michael@0: return rv; michael@0: if (NS_FAILED(rv = entry->IsDirectory(&dirCheck))) michael@0: return rv; michael@0: if (dirCheck && !isSymlink) { michael@0: nsCOMPtr destClone; michael@0: rv = newParent->Clone(getter_AddRefs(destClone)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (NS_FAILED(rv = entry->CopyToNative(destClone, EmptyCString()))) { michael@0: #ifdef DEBUG michael@0: nsresult rv2; michael@0: nsAutoCString pathName; michael@0: if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) michael@0: return rv2; michael@0: printf("Operation not supported: %s\n", pathName.get()); michael@0: #endif michael@0: if (rv == NS_ERROR_OUT_OF_MEMORY) michael@0: return rv; michael@0: continue; michael@0: } michael@0: } michael@0: } else { michael@0: if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) { michael@0: #ifdef DEBUG michael@0: nsresult rv2; michael@0: nsAutoCString pathName; michael@0: if (NS_FAILED(rv2 = entry->GetNativePath(pathName))) michael@0: return rv2; michael@0: printf("Operation not supported: %s\n", pathName.get()); michael@0: #endif michael@0: if (rv == NS_ERROR_OUT_OF_MEMORY) michael@0: return rv; michael@0: continue; michael@0: } michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName) michael@0: { michael@0: nsresult rv; michael@0: // check to make sure that this has been initialized properly michael@0: CHECK_mPath(); michael@0: michael@0: // we copy the parent here so 'newParent' remains immutable michael@0: nsCOMPtr workParent; michael@0: if (newParent) { michael@0: if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent)))) michael@0: return rv; michael@0: } else { michael@0: if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent)))) michael@0: return rv; michael@0: } michael@0: michael@0: // check to see if we are a directory or if we are a file michael@0: bool isDirectory; michael@0: if (NS_FAILED(rv = IsDirectory(&isDirectory))) michael@0: return rv; michael@0: michael@0: nsAutoCString newPathName; michael@0: if (isDirectory) { michael@0: if (!newName.IsEmpty()) { michael@0: if (NS_FAILED(rv = workParent->AppendNative(newName))) michael@0: return rv; michael@0: } else { michael@0: if (NS_FAILED(rv = GetNativeLeafName(newPathName))) michael@0: return rv; michael@0: if (NS_FAILED(rv = workParent->AppendNative(newPathName))) michael@0: return rv; michael@0: } michael@0: if (NS_FAILED(rv = CopyDirectoryTo(workParent))) michael@0: return rv; michael@0: } else { michael@0: rv = GetNativeTargetPathName(workParent, newName, newPathName); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: #ifdef DEBUG_blizzard michael@0: printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get()); michael@0: #endif michael@0: michael@0: // actually create the file. michael@0: nsLocalFile *newFile = new nsLocalFile(); michael@0: if (!newFile) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsCOMPtr fileRef(newFile); // release on exit michael@0: michael@0: rv = newFile->InitWithNativePath(newPathName); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // get the old permissions michael@0: uint32_t myPerms; michael@0: GetPermissions(&myPerms); michael@0: michael@0: // Create the new file with the old file's permissions, even if write michael@0: // permission is missing. We can't create with write permission and michael@0: // then change back to myPerm on all filesystems (FAT on Linux, e.g.). michael@0: // But we can write to a read-only file on all Unix filesystems if we michael@0: // open it successfully for writing. michael@0: michael@0: PRFileDesc *newFD; michael@0: rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE, michael@0: PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, michael@0: myPerms, michael@0: &newFD); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // open the old file, too michael@0: bool specialFile; michael@0: if (NS_FAILED(rv = IsSpecial(&specialFile))) { michael@0: PR_Close(newFD); michael@0: return rv; michael@0: } michael@0: if (specialFile) { michael@0: #ifdef DEBUG michael@0: printf("Operation not supported: %s\n", mPath.get()); michael@0: #endif michael@0: // make sure to clean up properly michael@0: PR_Close(newFD); michael@0: return NS_OK; michael@0: } michael@0: michael@0: PRFileDesc *oldFD; michael@0: rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD); michael@0: if (NS_FAILED(rv)) { michael@0: // make sure to clean up properly michael@0: PR_Close(newFD); michael@0: return rv; michael@0: } michael@0: michael@0: #ifdef DEBUG_blizzard michael@0: int32_t totalRead = 0; michael@0: int32_t totalWritten = 0; michael@0: #endif michael@0: char buf[BUFSIZ]; michael@0: int32_t bytesRead; michael@0: michael@0: // record PR_Write() error for better error message later. michael@0: nsresult saved_write_error = NS_OK; michael@0: nsresult saved_read_error = NS_OK; michael@0: nsresult saved_read_close_error = NS_OK; michael@0: nsresult saved_write_close_error = NS_OK; michael@0: michael@0: // DONE: Does PR_Read() return bytesRead < 0 for error? michael@0: // Yes., The errors from PR_Read are not so common and michael@0: // the value may not have correspondence in NS_ERROR_*, but michael@0: // we do catch it still, immediately after while() loop. michael@0: // We can differentiate errors pf PR_Read and PR_Write by michael@0: // looking at saved_write_error value. If PR_Write error occurs (and not michael@0: // PR_Read() error), save_write_error is not NS_OK. michael@0: michael@0: while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) { michael@0: #ifdef DEBUG_blizzard michael@0: totalRead += bytesRead; michael@0: #endif michael@0: michael@0: // PR_Write promises never to do a short write michael@0: int32_t bytesWritten = PR_Write(newFD, buf, bytesRead); michael@0: if (bytesWritten < 0) { michael@0: saved_write_error = NSRESULT_FOR_ERRNO(); michael@0: bytesRead = -1; michael@0: break; michael@0: } michael@0: NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?"); michael@0: michael@0: #ifdef DEBUG_blizzard michael@0: totalWritten += bytesWritten; michael@0: #endif michael@0: } michael@0: michael@0: // TODO/FIXME: If CIFS (and NFS?) may force read/write to return EINTR, michael@0: // we are better off to prepare for retrying. But we need confirmation if michael@0: // EINTR is returned. michael@0: michael@0: // Record error if PR_Read() failed. michael@0: // Must be done before any other I/O which may reset errno. michael@0: if ( (bytesRead < 0) && (saved_write_error == NS_OK)) { michael@0: saved_read_error = NSRESULT_FOR_ERRNO(); michael@0: } michael@0: michael@0: #ifdef DEBUG_blizzard michael@0: printf("read %d bytes, wrote %d bytes\n", michael@0: totalRead, totalWritten); michael@0: #endif michael@0: michael@0: // DONE: Errors of close can occur. Read man page of michael@0: // close(2); michael@0: // This is likely to happen if the file system is remote file michael@0: // system (NFS, CIFS, etc.) and network outage occurs. michael@0: // At least, we should tell the user that filesystem/disk is michael@0: // hosed (possibly due to network error, hard disk failure, michael@0: // etc.) so that users can take remedial action. michael@0: michael@0: // close the files michael@0: if (PR_Close(newFD) < 0) { michael@0: saved_write_close_error = NSRESULT_FOR_ERRNO(); michael@0: #if DEBUG michael@0: // This error merits printing. michael@0: fprintf(stderr, "ERROR: PR_Close(newFD) returned error. errno = %d\n", errno); michael@0: #endif michael@0: } michael@0: michael@0: if (PR_Close(oldFD) < 0) { michael@0: saved_read_close_error = NSRESULT_FOR_ERRNO(); michael@0: #if DEBUG michael@0: fprintf(stderr, "ERROR: PR_Close(oldFD) returned error. errno = %d\n", errno); michael@0: #endif michael@0: } michael@0: michael@0: // Let us report the failure to write and read. michael@0: // check for write/read error after cleaning up michael@0: if (bytesRead < 0) { michael@0: if (saved_write_error != NS_OK) michael@0: return saved_write_error; michael@0: else if (saved_read_error != NS_OK) michael@0: return saved_read_error; michael@0: #if DEBUG michael@0: else // sanity check. Die and debug. michael@0: MOZ_ASSERT(0); michael@0: #endif michael@0: } michael@0: michael@0: if (saved_write_close_error != NS_OK) michael@0: return saved_write_close_error; michael@0: if (saved_read_close_error != NS_OK) michael@0: return saved_read_close_error; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName) michael@0: { michael@0: return CopyToNative(newParent, newName); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // check to make sure that this has been initialized properly michael@0: CHECK_mPath(); michael@0: michael@0: // check to make sure that we have a new parent michael@0: nsAutoCString newPathName; michael@0: rv = GetNativeTargetPathName(newParent, newName, newPathName); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // try for atomic rename, falling back to copy/delete michael@0: if (rename(mPath.get(), newPathName.get()) < 0) { michael@0: #ifdef VMS michael@0: if (errno == EXDEV || errno == ENXIO) { michael@0: #else michael@0: if (errno == EXDEV) { michael@0: #endif michael@0: rv = CopyToNative(newParent, newName); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = Remove(true); michael@0: } else { michael@0: rv = NSRESULT_FOR_ERRNO(); michael@0: } michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // Adjust this michael@0: mPath = newPathName; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Remove(bool recursive) michael@0: { michael@0: CHECK_mPath(); michael@0: ENSURE_STAT_CACHE(); michael@0: michael@0: bool isSymLink; michael@0: michael@0: nsresult rv = IsSymlink(&isSymLink); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (isSymLink || !S_ISDIR(mCachedStat.st_mode)) michael@0: return NSRESULT_FOR_RETURN(unlink(mPath.get())); michael@0: michael@0: if (recursive) { michael@0: nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix(); michael@0: michael@0: nsCOMPtr dirRef(dir); // release on exit michael@0: michael@0: rv = dir->Init(this, false); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: bool more; michael@0: while (dir->HasMoreElements(&more), more) { michael@0: nsCOMPtr item; michael@0: rv = dir->GetNext(getter_AddRefs(item)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr file = do_QueryInterface(item, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: rv = file->Remove(recursive); michael@0: michael@0: #ifdef ANDROID michael@0: // See bug 580434 - Bionic gives us just deleted files michael@0: if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) michael@0: continue; michael@0: #endif michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: return NSRESULT_FOR_RETURN(rmdir(mPath.get())); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetLastModifiedTime(PRTime *aLastModTime) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!aLastModTime)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: PRFileInfo64 info; michael@0: if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: PRTime modTime = info.modifyTime; michael@0: if (modTime == 0) michael@0: *aLastModTime = 0; michael@0: else michael@0: *aLastModTime = modTime / PR_USEC_PER_MSEC; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetLastModifiedTime(PRTime aLastModTime) michael@0: { michael@0: CHECK_mPath(); michael@0: michael@0: int result; michael@0: if (aLastModTime != 0) { michael@0: ENSURE_STAT_CACHE(); michael@0: struct utimbuf ut; michael@0: ut.actime = mCachedStat.st_atime; michael@0: michael@0: // convert milliseconds to seconds since the unix epoch michael@0: ut.modtime = (time_t)(aLastModTime / PR_MSEC_PER_SEC); michael@0: result = utime(mPath.get(), &ut); michael@0: } else { michael@0: result = utime(mPath.get(), nullptr); michael@0: } michael@0: return NSRESULT_FOR_RETURN(result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetLastModifiedTimeOfLink(PRTime *aLastModTimeOfLink) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!aLastModTimeOfLink)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: struct STAT sbuf; michael@0: if (LSTAT(mPath.get(), &sbuf) == -1) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: *aLastModTimeOfLink = PRTime(sbuf.st_mtime) * PR_MSEC_PER_SEC; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * utime(2) may or may not dereference symlinks, joy. michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink) michael@0: { michael@0: return SetLastModifiedTime(aLastModTimeOfLink); michael@0: } michael@0: michael@0: /* michael@0: * Only send back permissions bits: maybe we want to send back the whole michael@0: * mode_t to permit checks against other file types? michael@0: */ michael@0: michael@0: #define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO)) michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetPermissions(uint32_t *aPermissions) michael@0: { michael@0: if (NS_WARN_IF(!aPermissions)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: ENSURE_STAT_CACHE(); michael@0: *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetPermissionsOfLink(uint32_t *aPermissionsOfLink) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!aPermissionsOfLink)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: struct STAT sbuf; michael@0: if (LSTAT(mPath.get(), &sbuf) == -1) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetPermissions(uint32_t aPermissions) michael@0: { michael@0: CHECK_mPath(); michael@0: michael@0: /* michael@0: * Race condition here: we should use fchmod instead, there's no way to michael@0: * guarantee the name still refers to the same file. michael@0: */ michael@0: if (chmod(mPath.get(), aPermissions) >= 0) michael@0: return NS_OK; michael@0: #if defined(ANDROID) && defined(STATFS) michael@0: // For the time being, this is restricted for use by Android, but we michael@0: // will figure out what to do for all platforms in bug 638503 michael@0: struct STATFS sfs; michael@0: if (STATFS(mPath.get(), &sfs) < 0) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: michael@0: // if this is a FAT file system we can't set file permissions michael@0: if (sfs.f_type == MSDOS_SUPER_MAGIC ) michael@0: return NS_OK; michael@0: #endif michael@0: return NSRESULT_FOR_ERRNO(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions) michael@0: { michael@0: // There isn't a consistent mechanism for doing this on UNIX platforms. We michael@0: // might want to carefully implement this in the future though. michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetFileSize(int64_t *aFileSize) michael@0: { michael@0: if (NS_WARN_IF(!aFileSize)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: *aFileSize = 0; michael@0: ENSURE_STAT_CACHE(); michael@0: michael@0: #if defined(VMS) michael@0: /* Only two record formats can report correct file content size */ michael@0: if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) && michael@0: (mCachedStat.st_fab_rfm != FAB$C_STMCR)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: #endif michael@0: michael@0: if (!S_ISDIR(mCachedStat.st_mode)) { michael@0: *aFileSize = (int64_t)mCachedStat.st_size; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetFileSize(int64_t aFileSize) michael@0: { michael@0: CHECK_mPath(); michael@0: michael@0: #if defined(ANDROID) michael@0: /* no truncate on bionic */ michael@0: int fd = open(mPath.get(), O_WRONLY); michael@0: if (fd == -1) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: michael@0: int ret = ftruncate(fd, (off_t)aFileSize); michael@0: close(fd); michael@0: michael@0: if (ret == -1) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: #elif defined(HAVE_TRUNCATE64) michael@0: if (truncate64(mPath.get(), (off64_t)aFileSize) == -1) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: #else michael@0: off_t size = (off_t)aFileSize; michael@0: if (truncate(mPath.get(), size) == -1) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetFileSizeOfLink(int64_t *aFileSize) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!aFileSize)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: struct STAT sbuf; michael@0: if (LSTAT(mPath.get(), &sbuf) == -1) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: michael@0: *aFileSize = (int64_t)sbuf.st_size; michael@0: return NS_OK; michael@0: } michael@0: michael@0: #if defined(USE_LINUX_QUOTACTL) michael@0: /* michael@0: * Searches /proc/self/mountinfo for given device (Major:Minor), michael@0: * returns exported name from /dev michael@0: * michael@0: * Fails when /proc/self/mountinfo or diven device don't exist. michael@0: */ michael@0: static bool michael@0: GetDeviceName(int deviceMajor, int deviceMinor, nsACString &deviceName) michael@0: { michael@0: bool ret = false; michael@0: michael@0: const int kMountInfoLineLength = 200; michael@0: const int kMountInfoDevPosition = 6; michael@0: michael@0: char mountinfo_line[kMountInfoLineLength]; michael@0: char device_num[kMountInfoLineLength]; michael@0: michael@0: snprintf(device_num,kMountInfoLineLength,"%d:%d", deviceMajor, deviceMinor); michael@0: michael@0: FILE *f = fopen("/proc/self/mountinfo","rt"); michael@0: if(!f) michael@0: return ret; michael@0: michael@0: // Expects /proc/self/mountinfo in format: michael@0: // 'ID ID major:minor root mountpoint flags - type devicename flags' michael@0: while(fgets(mountinfo_line,kMountInfoLineLength,f)) { michael@0: char *p_dev = strstr(mountinfo_line,device_num); michael@0: michael@0: int i; michael@0: for(i = 0; i < kMountInfoDevPosition && p_dev != nullptr; i++) { michael@0: p_dev = strchr(p_dev,' '); michael@0: if(p_dev) michael@0: p_dev++; michael@0: } michael@0: michael@0: if(p_dev) { michael@0: char *p_dev_end = strchr(p_dev,' '); michael@0: if(p_dev_end) { michael@0: *p_dev_end = '\0'; michael@0: deviceName.Assign(p_dev); michael@0: ret = true; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: fclose(f); michael@0: return ret; michael@0: } michael@0: #endif michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable) michael@0: { michael@0: if (NS_WARN_IF(!aDiskSpaceAvailable)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // These systems have the operations necessary to check disk space. michael@0: michael@0: #ifdef STATFS michael@0: michael@0: // check to make sure that mPath is properly initialized michael@0: CHECK_mPath(); michael@0: michael@0: struct STATFS fs_buf; michael@0: michael@0: /* michael@0: * Members of the STATFS struct that you should know about: michael@0: * F_BSIZE = block size on disk. michael@0: * f_bavail = number of free blocks available to a non-superuser. michael@0: * f_bfree = number of total free blocks in file system. michael@0: */ michael@0: michael@0: if (STATFS(mPath.get(), &fs_buf) < 0) { michael@0: // The call to STATFS failed. michael@0: #ifdef DEBUG michael@0: printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n"); michael@0: #endif michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: *aDiskSpaceAvailable = (int64_t) fs_buf.F_BSIZE * fs_buf.f_bavail; michael@0: michael@0: #ifdef DEBUG_DISK_SPACE michael@0: printf("DiskSpaceAvailable: %lu bytes\n", michael@0: *aDiskSpaceAvailable); michael@0: #endif michael@0: michael@0: #if defined(USE_LINUX_QUOTACTL) michael@0: michael@0: if(!FillStatCache()) { michael@0: // Return available size from statfs michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCString deviceName; michael@0: if(!GetDeviceName(major(mCachedStat.st_dev), minor(mCachedStat.st_dev), deviceName)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: struct dqblk dq; michael@0: if(!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(), getuid(), (caddr_t)&dq) michael@0: #ifdef QIF_BLIMITS michael@0: && dq.dqb_valid & QIF_BLIMITS michael@0: #endif michael@0: && dq.dqb_bhardlimit) michael@0: { michael@0: int64_t QuotaSpaceAvailable = 0; michael@0: if (dq.dqb_bhardlimit > dq.dqb_curspace) michael@0: QuotaSpaceAvailable = int64_t(fs_buf.F_BSIZE * (dq.dqb_bhardlimit - dq.dqb_curspace)); michael@0: if(QuotaSpaceAvailable < *aDiskSpaceAvailable) { michael@0: *aDiskSpaceAvailable = QuotaSpaceAvailable; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: michael@0: #else michael@0: /* michael@0: * This platform doesn't have statfs or statvfs. I'm sure that there's michael@0: * a way to check for free disk space on platforms that don't have statfs michael@0: * (I'm SURE they have df, for example). michael@0: * michael@0: * Until we figure out how to do that, lets be honest and say that this michael@0: * command isn't implemented properly for these platforms yet. michael@0: */ michael@0: #ifdef DEBUG michael@0: printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n"); michael@0: #endif michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: michael@0: #endif /* STATFS */ michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetParent(nsIFile **aParent) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!aParent)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: *aParent = nullptr; michael@0: michael@0: // if '/' we are at the top of the volume, return null michael@0: if (mPath.Equals("/")) michael@0: return NS_OK; michael@0: michael@0: // I promise to play nice michael@0: char *buffer = mPath.BeginWriting(), michael@0: *slashp = buffer; michael@0: michael@0: // find the last significant slash in buffer michael@0: slashp = strrchr(buffer, '/'); michael@0: NS_ASSERTION(slashp, "non-canonical path?"); michael@0: if (!slashp) michael@0: return NS_ERROR_FILE_INVALID_PATH; michael@0: michael@0: // for the case where we are at '/' michael@0: if (slashp == buffer) michael@0: slashp++; michael@0: michael@0: // temporarily terminate buffer at the last significant slash michael@0: char c = *slashp; michael@0: *slashp = '\0'; michael@0: michael@0: nsCOMPtr localFile; michael@0: nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true, michael@0: getter_AddRefs(localFile)); michael@0: michael@0: // make buffer whole again michael@0: *slashp = c; michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: localFile.forget(aParent); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * The results of Exists, isWritable and isReadable are not cached. michael@0: */ michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Exists(bool *_retval) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: *_retval = (access(mPath.get(), F_OK) == 0); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::IsWritable(bool *_retval) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: *_retval = (access(mPath.get(), W_OK) == 0); michael@0: if (*_retval || errno == EACCES) michael@0: return NS_OK; michael@0: return NSRESULT_FOR_ERRNO(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::IsReadable(bool *_retval) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: *_retval = (access(mPath.get(), R_OK) == 0); michael@0: if (*_retval || errno == EACCES) michael@0: return NS_OK; michael@0: return NSRESULT_FOR_ERRNO(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::IsExecutable(bool *_retval) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Check extension (bug 663899). On certain platforms, the file michael@0: // extension may cause the OS to treat it as executable regardless of michael@0: // the execute bit, such as .jar on Mac OS X. We borrow the code from michael@0: // nsLocalFileWin, slightly modified. michael@0: michael@0: // Don't be fooled by symlinks. michael@0: bool symLink; michael@0: nsresult rv = IsSymlink(&symLink); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsAutoString path; michael@0: if (symLink) michael@0: GetTarget(path); michael@0: else michael@0: GetPath(path); michael@0: michael@0: int32_t dotIdx = path.RFindChar(char16_t('.')); michael@0: if (dotIdx != kNotFound) { michael@0: // Convert extension to lower case. michael@0: char16_t *p = path.BeginWriting(); michael@0: for(p += dotIdx + 1; *p; p++) michael@0: *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0; michael@0: michael@0: // Search for any of the set of executable extensions. michael@0: static const char * const executableExts[] = { michael@0: "air", // Adobe AIR installer michael@0: "jar"}; // java application bundle michael@0: nsDependentSubstring ext = Substring(path, dotIdx + 1); michael@0: for (size_t i = 0; i < ArrayLength(executableExts); i++) { michael@0: if (ext.EqualsASCII(executableExts[i])) { michael@0: // Found a match. Set result and quit. michael@0: *_retval = true; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // On OS X, then query Launch Services. michael@0: #ifdef MOZ_WIDGET_COCOA michael@0: // Certain Mac applications, such as Classic applications, which michael@0: // run under Rosetta, might not have the +x mode bit but are still michael@0: // considered to be executable by Launch Services (bug 646748). michael@0: CFURLRef url; michael@0: if (NS_FAILED(GetCFURL(&url))) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: LSRequestedInfo theInfoRequest = kLSRequestAllInfo; michael@0: LSItemInfoRecord theInfo; michael@0: OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo); michael@0: ::CFRelease(url); michael@0: if (result == noErr) { michael@0: if ((theInfo.flags & kLSItemInfoIsApplication) != 0) { michael@0: *_retval = true; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // Then check the execute bit. michael@0: *_retval = (access(mPath.get(), X_OK) == 0); michael@0: #ifdef SOLARIS michael@0: // On Solaris, access will always return 0 for root user, however michael@0: // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set. michael@0: // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950 michael@0: if (*_retval) { michael@0: struct STAT buf; michael@0: michael@0: *_retval = (STAT(mPath.get(), &buf) == 0); michael@0: if (*_retval || errno == EACCES) { michael@0: *_retval = *_retval && michael@0: (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH )); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NSRESULT_FOR_ERRNO(); michael@0: } michael@0: #endif michael@0: if (*_retval || errno == EACCES) michael@0: return NS_OK; michael@0: return NSRESULT_FOR_ERRNO(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::IsDirectory(bool *_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: *_retval = false; michael@0: ENSURE_STAT_CACHE(); michael@0: *_retval = S_ISDIR(mCachedStat.st_mode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::IsFile(bool *_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: *_retval = false; michael@0: ENSURE_STAT_CACHE(); michael@0: *_retval = S_ISREG(mCachedStat.st_mode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::IsHidden(bool *_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: nsACString::const_iterator begin, end; michael@0: LocateNativeLeafName(begin, end); michael@0: *_retval = (*begin == '.'); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::IsSymlink(bool *_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: CHECK_mPath(); michael@0: michael@0: struct STAT symStat; michael@0: if (LSTAT(mPath.get(), &symStat) == -1) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: *_retval=S_ISLNK(symStat.st_mode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::IsSpecial(bool *_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: ENSURE_STAT_CACHE(); michael@0: *_retval = S_ISCHR(mCachedStat.st_mode) || michael@0: S_ISBLK(mCachedStat.st_mode) || michael@0: #ifdef S_ISSOCK michael@0: S_ISSOCK(mCachedStat.st_mode) || michael@0: #endif michael@0: S_ISFIFO(mCachedStat.st_mode); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Equals(nsIFile *inFile, bool *_retval) michael@0: { michael@0: if (NS_WARN_IF(!inFile)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: *_retval = false; michael@0: michael@0: nsAutoCString inPath; michael@0: nsresult rv = inFile->GetNativePath(inPath); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // We don't need to worry about "/foo/" vs. "/foo" here michael@0: // because trailing slashes are stripped on init. michael@0: *_retval = !strcmp(inPath.get(), mPath.get()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!inFile)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsAutoCString inPath; michael@0: nsresult rv; michael@0: michael@0: if (NS_FAILED(rv = inFile->GetNativePath(inPath))) michael@0: return rv; michael@0: michael@0: *_retval = false; michael@0: michael@0: ssize_t len = mPath.Length(); michael@0: if (strncmp(mPath.get(), inPath.get(), len) == 0) { michael@0: // Now make sure that the |inFile|'s path has a separator at len, michael@0: // which implies that it has more components after len. michael@0: if (inPath[len] == '/') michael@0: *_retval = true; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetNativeTarget(nsACString &_retval) michael@0: { michael@0: CHECK_mPath(); michael@0: _retval.Truncate(); michael@0: michael@0: struct STAT symStat; michael@0: if (LSTAT(mPath.get(), &symStat) == -1) michael@0: return NSRESULT_FOR_ERRNO(); michael@0: michael@0: if (!S_ISLNK(symStat.st_mode)) michael@0: return NS_ERROR_FILE_INVALID_PATH; michael@0: michael@0: int32_t size = (int32_t)symStat.st_size; michael@0: char *target = (char *)nsMemory::Alloc(size + 1); michael@0: if (!target) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: if (readlink(mPath.get(), target, (size_t)size) < 0) { michael@0: nsMemory::Free(target); michael@0: return NSRESULT_FOR_ERRNO(); michael@0: } michael@0: target[size] = '\0'; michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr self(this); michael@0: int32_t maxLinks = 40; michael@0: while (true) { michael@0: if (maxLinks-- == 0) { michael@0: rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK; michael@0: break; michael@0: } michael@0: michael@0: if (target[0] != '/') { michael@0: nsCOMPtr parent; michael@0: if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent)))) michael@0: break; michael@0: if (NS_FAILED(rv = parent->AppendRelativeNativePath(nsDependentCString(target)))) michael@0: break; michael@0: if (NS_FAILED(rv = parent->GetNativePath(_retval))) michael@0: break; michael@0: self = parent; michael@0: } else { michael@0: _retval = target; michael@0: } michael@0: michael@0: const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval); michael@0: michael@0: // Any failure in testing the current target we'll just interpret michael@0: // as having reached our destiny. michael@0: if (LSTAT(flatRetval.get(), &symStat) == -1) michael@0: break; michael@0: michael@0: // And of course we're done if it isn't a symlink. michael@0: if (!S_ISLNK(symStat.st_mode)) michael@0: break; michael@0: michael@0: int32_t newSize = (int32_t)symStat.st_size; michael@0: if (newSize > size) { michael@0: char *newTarget = (char *)nsMemory::Realloc(target, newSize + 1); michael@0: if (!newTarget) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: break; michael@0: } michael@0: target = newTarget; michael@0: size = newSize; michael@0: } michael@0: michael@0: int32_t linkLen = readlink(flatRetval.get(), target, size); michael@0: if (linkLen == -1) { michael@0: rv = NSRESULT_FOR_ERRNO(); michael@0: break; michael@0: } michael@0: target[linkLen] = '\0'; michael@0: } michael@0: michael@0: nsMemory::Free(target); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: _retval.Truncate(); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetFollowLinks(bool *aFollowLinks) michael@0: { michael@0: *aFollowLinks = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetFollowLinks(bool aFollowLinks) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries) michael@0: { michael@0: nsRefPtr dir = new nsDirEnumeratorUnix(); michael@0: michael@0: nsresult rv = dir->Init(this, false); michael@0: if (NS_FAILED(rv)) { michael@0: *entries = nullptr; michael@0: } else { michael@0: dir.forget(entries); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Load(PRLibrary **_retval) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsTraceRefcnt::SetActivityIsLegal(false); michael@0: #endif michael@0: michael@0: *_retval = PR_LoadLibrary(mPath.get()); michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsTraceRefcnt::SetActivityIsLegal(true); michael@0: #endif michael@0: michael@0: if (!*_retval) michael@0: return NS_ERROR_FAILURE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor) michael@0: { michael@0: return GetNativePath(aPersistentDescriptor); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor) michael@0: { michael@0: #ifdef MOZ_WIDGET_COCOA michael@0: if (aPersistentDescriptor.IsEmpty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // Support pathnames as user-supplied descriptors if they begin with '/' michael@0: // or '~'. These characters do not collide with the base64 set used for michael@0: // encoding alias records. michael@0: char first = aPersistentDescriptor.First(); michael@0: if (first == '/' || first == '~') michael@0: return InitWithNativePath(aPersistentDescriptor); michael@0: michael@0: uint32_t dataSize = aPersistentDescriptor.Length(); michael@0: char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nullptr); michael@0: if (!decodedData) { michael@0: NS_ERROR("SetPersistentDescriptor was given bad data"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Cast to an alias record and resolve. michael@0: AliasRecord aliasHeader = *(AliasPtr)decodedData; michael@0: int32_t aliasSize = ::GetAliasSizeFromPtr(&aliasHeader); michael@0: if (aliasSize > ((int32_t)dataSize * 3) / 4) { // be paranoid about having too few data michael@0: PR_Free(decodedData); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Move the now-decoded data into the Handle. michael@0: // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h michael@0: Handle newHandle = nullptr; michael@0: if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr) michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: PR_Free(decodedData); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: Boolean changed; michael@0: FSRef resolvedFSRef; michael@0: OSErr err = ::FSResolveAlias(nullptr, (AliasHandle)newHandle, &resolvedFSRef, &changed); michael@0: michael@0: rv = MacErrorMapper(err); michael@0: DisposeHandle(newHandle); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return InitWithFSRef(&resolvedFSRef); michael@0: #else michael@0: return InitWithNativePath(aPersistentDescriptor); michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Reveal() michael@0: { michael@0: #ifdef MOZ_WIDGET_GTK michael@0: nsCOMPtr giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); michael@0: nsCOMPtr gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID); michael@0: if (!giovfs && !gnomevfs) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: bool isDirectory; michael@0: if (NS_FAILED(IsDirectory(&isDirectory))) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (isDirectory) { michael@0: if (giovfs) michael@0: return giovfs->ShowURIForInput(mPath); michael@0: else michael@0: /* Fallback to GnomeVFS */ michael@0: return gnomevfs->ShowURIForInput(mPath); michael@0: } else if (giovfs && NS_SUCCEEDED(giovfs->OrgFreedesktopFileManager1ShowItems(mPath))) { michael@0: return NS_OK; michael@0: } else { michael@0: nsCOMPtr parentDir; michael@0: nsAutoCString dirPath; michael@0: if (NS_FAILED(GetParent(getter_AddRefs(parentDir)))) michael@0: return NS_ERROR_FAILURE; michael@0: if (NS_FAILED(parentDir->GetNativePath(dirPath))) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (giovfs) michael@0: return giovfs->ShowURIForInput(dirPath); michael@0: else michael@0: return gnomevfs->ShowURIForInput(dirPath); michael@0: } michael@0: #elif defined(MOZ_WIDGET_COCOA) michael@0: CFURLRef url; michael@0: if (NS_SUCCEEDED(GetCFURL(&url))) { michael@0: nsresult rv = CocoaFileUtils::RevealFileInFinder(url); michael@0: ::CFRelease(url); michael@0: return rv; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: #else michael@0: return NS_ERROR_FAILURE; michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Launch() michael@0: { michael@0: #ifdef MOZ_WIDGET_GTK michael@0: nsCOMPtr giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID); michael@0: nsCOMPtr gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID); michael@0: if (giovfs) { michael@0: return giovfs->ShowURIForInput(mPath); michael@0: } else if (gnomevfs) { michael@0: /* GnomeVFS fallback */ michael@0: return gnomevfs->ShowURIForInput(mPath); michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: #elif defined(MOZ_ENABLE_CONTENTACTION) michael@0: QUrl uri = QUrl::fromLocalFile(QString::fromUtf8(mPath.get())); michael@0: ContentAction::Action action = michael@0: ContentAction::Action::defaultActionForFile(uri); michael@0: michael@0: if (action.isValid()) { michael@0: action.trigger(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: #elif defined(MOZ_WIDGET_ANDROID) michael@0: // Try to get a mimetype, if this fails just use the file uri alone michael@0: nsresult rv; michael@0: nsAutoCString type; michael@0: nsCOMPtr mimeService(do_GetService("@mozilla.org/mime;1", &rv)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = mimeService->GetTypeFromFile(this, type); michael@0: michael@0: nsAutoCString fileUri = NS_LITERAL_CSTRING("file://") + mPath; michael@0: return mozilla::widget::android::GeckoAppShell::OpenUriExternal(NS_ConvertUTF8toUTF16(fileUri), michael@0: NS_ConvertUTF8toUTF16(type)) ? NS_OK : NS_ERROR_FAILURE; michael@0: #elif defined(MOZ_WIDGET_COCOA) michael@0: CFURLRef url; michael@0: if (NS_SUCCEEDED(GetCFURL(&url))) { michael@0: nsresult rv = CocoaFileUtils::OpenURL(url); michael@0: ::CFRelease(url); michael@0: return rv; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: #else michael@0: return NS_ERROR_FAILURE; michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewNativeLocalFile(const nsACString &path, bool followSymlinks, nsIFile **result) michael@0: { michael@0: nsRefPtr file = new nsLocalFile(); michael@0: michael@0: file->SetFollowLinks(followSymlinks); michael@0: michael@0: if (!path.IsEmpty()) { michael@0: nsresult rv = file->InitWithNativePath(path); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: file.forget(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // unicode support michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: #define SET_UCS(func, ucsArg) \ michael@0: { \ michael@0: nsAutoCString buf; \ michael@0: nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \ michael@0: if (NS_FAILED(rv)) \ michael@0: return rv; \ michael@0: return (func)(buf); \ michael@0: } michael@0: michael@0: #define GET_UCS(func, ucsArg) \ michael@0: { \ michael@0: nsAutoCString buf; \ michael@0: nsresult rv = (func)(buf); \ michael@0: if (NS_FAILED(rv)) return rv; \ michael@0: return NS_CopyNativeToUnicode(buf, ucsArg); \ michael@0: } michael@0: michael@0: #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \ michael@0: { \ michael@0: nsAutoCString buf; \ michael@0: nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \ michael@0: if (NS_FAILED(rv)) \ michael@0: return rv; \ michael@0: return (func)(opaqueArg, buf); \ michael@0: } michael@0: michael@0: // Unicode interface Wrapper michael@0: nsresult michael@0: nsLocalFile::InitWithPath(const nsAString &filePath) michael@0: { michael@0: SET_UCS(InitWithNativePath, filePath); michael@0: } michael@0: nsresult michael@0: nsLocalFile::Append(const nsAString &node) michael@0: { michael@0: SET_UCS(AppendNative, node); michael@0: } michael@0: nsresult michael@0: nsLocalFile::AppendRelativePath(const nsAString &node) michael@0: { michael@0: SET_UCS(AppendRelativeNativePath, node); michael@0: } michael@0: nsresult michael@0: nsLocalFile::GetLeafName(nsAString &aLeafName) michael@0: { michael@0: GET_UCS(GetNativeLeafName, aLeafName); michael@0: } michael@0: nsresult michael@0: nsLocalFile::SetLeafName(const nsAString &aLeafName) michael@0: { michael@0: SET_UCS(SetNativeLeafName, aLeafName); michael@0: } michael@0: nsresult michael@0: nsLocalFile::GetPath(nsAString &_retval) michael@0: { michael@0: return NS_CopyNativeToUnicode(mPath, _retval); michael@0: } michael@0: nsresult michael@0: nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName) michael@0: { michael@0: SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName); michael@0: } michael@0: nsresult michael@0: nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName) michael@0: { michael@0: SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName); michael@0: } michael@0: nsresult michael@0: nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName) michael@0: { michael@0: SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::RenameTo(nsIFile *newParentDir, const nsAString &newName) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // check to make sure that this has been initialized properly michael@0: CHECK_mPath(); michael@0: michael@0: // check to make sure that we have a new parent michael@0: nsAutoCString newPathName; michael@0: nsAutoCString newNativeName; michael@0: rv = NS_CopyUnicodeToNative(newName, newNativeName); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: rv = GetNativeTargetPathName(newParentDir, newNativeName, newPathName); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: // try for atomic rename michael@0: if (rename(mPath.get(), newPathName.get()) < 0) { michael@0: #ifdef VMS michael@0: if (errno == EXDEV || errno == ENXIO) { michael@0: #else michael@0: if (errno == EXDEV) { michael@0: #endif michael@0: rv = NS_ERROR_FILE_ACCESS_DENIED; michael@0: } else { michael@0: rv = NSRESULT_FOR_ERRNO(); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsLocalFile::GetTarget(nsAString &_retval) michael@0: { michael@0: GET_UCS(GetNativeTarget, _retval); michael@0: } michael@0: michael@0: // nsIHashable michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::Equals(nsIHashable* aOther, bool *aResult) michael@0: { michael@0: nsCOMPtr otherFile(do_QueryInterface(aOther)); michael@0: if (!otherFile) { michael@0: *aResult = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: return Equals(otherFile, aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetHashCode(uint32_t *aResult) michael@0: { michael@0: *aResult = HashString(mPath); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewLocalFile(const nsAString &path, bool followLinks, nsIFile* *result) michael@0: { michael@0: nsAutoCString buf; michael@0: nsresult rv = NS_CopyUnicodeToNative(path, buf); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: return NS_NewNativeLocalFile(buf, followLinks, result); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // global init/shutdown michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: void michael@0: nsLocalFile::GlobalInit() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsLocalFile::GlobalShutdown() michael@0: { michael@0: } michael@0: michael@0: // nsILocalFileMac michael@0: michael@0: #ifdef MOZ_WIDGET_COCOA michael@0: michael@0: static nsresult MacErrorMapper(OSErr inErr) michael@0: { michael@0: nsresult outErr; michael@0: michael@0: switch (inErr) michael@0: { michael@0: case noErr: michael@0: outErr = NS_OK; michael@0: break; michael@0: michael@0: case fnfErr: michael@0: case afpObjectNotFound: michael@0: case afpDirNotFound: michael@0: outErr = NS_ERROR_FILE_NOT_FOUND; michael@0: break; michael@0: michael@0: case dupFNErr: michael@0: case afpObjectExists: michael@0: outErr = NS_ERROR_FILE_ALREADY_EXISTS; michael@0: break; michael@0: michael@0: case dskFulErr: michael@0: case afpDiskFull: michael@0: outErr = NS_ERROR_FILE_DISK_FULL; michael@0: break; michael@0: michael@0: case fLckdErr: michael@0: case afpVolLocked: michael@0: outErr = NS_ERROR_FILE_IS_LOCKED; michael@0: break; michael@0: michael@0: case afpAccessDenied: michael@0: outErr = NS_ERROR_FILE_ACCESS_DENIED; michael@0: break; michael@0: michael@0: case afpDirNotEmpty: michael@0: outErr = NS_ERROR_FILE_DIR_NOT_EMPTY; michael@0: break; michael@0: michael@0: // Can't find good map for some michael@0: case bdNamErr: michael@0: outErr = NS_ERROR_FAILURE; michael@0: break; michael@0: michael@0: default: michael@0: outErr = NS_ERROR_FAILURE; michael@0: break; michael@0: } michael@0: michael@0: return outErr; michael@0: } michael@0: michael@0: static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr) michael@0: { michael@0: // first see if the conversion would succeed and find the length of the result michael@0: CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef); michael@0: CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), michael@0: kCFStringEncodingUTF8, 0, false, michael@0: nullptr, 0, &usedBufLen); michael@0: if (charsConverted == inStrLen) { michael@0: // all characters converted, do the actual conversion michael@0: aOutStr.SetLength(usedBufLen); michael@0: if (aOutStr.Length() != (unsigned int)usedBufLen) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: UInt8 *buffer = (UInt8*)aOutStr.BeginWriting(); michael@0: ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8, michael@0: 0, false, buffer, usedBufLen, &usedBufLen); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::InitWithCFURL(CFURLRef aCFURL) michael@0: { michael@0: UInt8 path[PATH_MAX]; michael@0: if (::CFURLGetFileSystemRepresentation(aCFURL, false, path, PATH_MAX)) { michael@0: nsDependentCString nativePath((char*)path); michael@0: return InitWithNativePath(nativePath); michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::InitWithFSRef(const FSRef *aFSRef) michael@0: { michael@0: if (NS_WARN_IF(!aFSRef)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef); michael@0: if (newURLRef) { michael@0: nsresult rv = InitWithCFURL(newURLRef); michael@0: ::CFRelease(newURLRef); michael@0: return rv; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetCFURL(CFURLRef *_retval) michael@0: { michael@0: CHECK_mPath(); michael@0: michael@0: bool isDir; michael@0: IsDirectory(&isDir); michael@0: *_retval = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, michael@0: (UInt8*)mPath.get(), michael@0: mPath.Length(), michael@0: isDir); michael@0: michael@0: return (*_retval ? NS_OK : NS_ERROR_FAILURE); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetFSRef(FSRef *_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: CFURLRef url = nullptr; michael@0: if (NS_SUCCEEDED(GetCFURL(&url))) { michael@0: if (::CFURLGetFSRef(url, _retval)) { michael@0: rv = NS_OK; michael@0: } michael@0: ::CFRelease(url); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetFSSpec(FSSpec *_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: FSRef fsRef; michael@0: nsresult rv = GetFSRef(&fsRef); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nullptr, nullptr, _retval, nullptr); michael@0: return MacErrorMapper(err); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetFileSizeWithResFork(int64_t *aFileSizeWithResFork) michael@0: { michael@0: if (NS_WARN_IF(!aFileSizeWithResFork)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: FSRef fsRef; michael@0: nsresult rv = GetFSRef(&fsRef); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: FSCatalogInfo catalogInfo; michael@0: OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes, michael@0: &catalogInfo, nullptr, nullptr, nullptr); michael@0: if (err != noErr) michael@0: return MacErrorMapper(err); michael@0: michael@0: *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetFileType(OSType *aFileType) michael@0: { michael@0: CFURLRef url; michael@0: if (NS_SUCCEEDED(GetCFURL(&url))) { michael@0: nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType); michael@0: ::CFRelease(url); michael@0: return rv; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetFileType(OSType aFileType) michael@0: { michael@0: CFURLRef url; michael@0: if (NS_SUCCEEDED(GetCFURL(&url))) { michael@0: nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType); michael@0: ::CFRelease(url); michael@0: return rv; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetFileCreator(OSType *aFileCreator) michael@0: { michael@0: CFURLRef url; michael@0: if (NS_SUCCEEDED(GetCFURL(&url))) { michael@0: nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator); michael@0: ::CFRelease(url); michael@0: return rv; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::SetFileCreator(OSType aFileCreator) michael@0: { michael@0: CFURLRef url; michael@0: if (NS_SUCCEEDED(GetCFURL(&url))) { michael@0: nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator); michael@0: ::CFRelease(url); michael@0: return rv; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::LaunchWithDoc(nsIFile *aDocToLoad, bool aLaunchInBackground) michael@0: { michael@0: bool isExecutable; michael@0: nsresult rv = IsExecutable(&isExecutable); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (!isExecutable) michael@0: return NS_ERROR_FILE_EXECUTION_FAILED; michael@0: michael@0: FSRef appFSRef, docFSRef; michael@0: rv = GetFSRef(&appFSRef); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (aDocToLoad) { michael@0: nsCOMPtr macDoc = do_QueryInterface(aDocToLoad); michael@0: rv = macDoc->GetFSRef(&docFSRef); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: LSLaunchFlags theLaunchFlags = kLSLaunchDefaults; michael@0: LSLaunchFSRefSpec thelaunchSpec; michael@0: michael@0: if (aLaunchInBackground) michael@0: theLaunchFlags |= kLSLaunchDontSwitch; michael@0: memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec)); michael@0: michael@0: thelaunchSpec.appRef = &appFSRef; michael@0: if (aDocToLoad) { michael@0: thelaunchSpec.numDocs = 1; michael@0: thelaunchSpec.itemRefs = &docFSRef; michael@0: } michael@0: thelaunchSpec.launchFlags = theLaunchFlags; michael@0: michael@0: OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr); michael@0: if (err != noErr) michael@0: return MacErrorMapper(err); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::OpenDocWithApp(nsIFile *aAppToOpenWith, bool aLaunchInBackground) michael@0: { michael@0: FSRef docFSRef; michael@0: nsresult rv = GetFSRef(&docFSRef); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (!aAppToOpenWith) { michael@0: OSErr err = ::LSOpenFSRef(&docFSRef, nullptr); michael@0: return MacErrorMapper(err); michael@0: } michael@0: michael@0: nsCOMPtr appFileMac = do_QueryInterface(aAppToOpenWith, &rv); michael@0: if (!appFileMac) michael@0: return rv; michael@0: michael@0: bool isExecutable; michael@0: rv = appFileMac->IsExecutable(&isExecutable); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (!isExecutable) michael@0: return NS_ERROR_FILE_EXECUTION_FAILED; michael@0: michael@0: FSRef appFSRef; michael@0: rv = appFileMac->GetFSRef(&appFSRef); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: LSLaunchFlags theLaunchFlags = kLSLaunchDefaults; michael@0: LSLaunchFSRefSpec thelaunchSpec; michael@0: michael@0: if (aLaunchInBackground) michael@0: theLaunchFlags |= kLSLaunchDontSwitch; michael@0: memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec)); michael@0: michael@0: thelaunchSpec.appRef = &appFSRef; michael@0: thelaunchSpec.numDocs = 1; michael@0: thelaunchSpec.itemRefs = &docFSRef; michael@0: thelaunchSpec.launchFlags = theLaunchFlags; michael@0: michael@0: OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr); michael@0: if (err != noErr) michael@0: return MacErrorMapper(err); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::IsPackage(bool *_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: *_retval = false; michael@0: michael@0: CFURLRef url; michael@0: nsresult rv = GetCFURL(&url); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: LSItemInfoRecord info; michael@0: OSStatus status = ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info); michael@0: michael@0: ::CFRelease(url); michael@0: michael@0: if (status != noErr) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: *_retval = !!(info.flags & kLSItemInfoIsPackage); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetBundleDisplayName(nsAString& outBundleName) michael@0: { michael@0: bool isPackage = false; michael@0: nsresult rv = IsPackage(&isPackage); michael@0: if (NS_FAILED(rv) || !isPackage) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsAutoString name; michael@0: rv = GetLeafName(name); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: int32_t length = name.Length(); michael@0: if (Substring(name, length - 4, length).EqualsLiteral(".app")) { michael@0: // 4 characters in ".app" michael@0: outBundleName = Substring(name, 0, length - 4); michael@0: } michael@0: else { michael@0: outBundleName = name; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: CFURLRef urlRef; michael@0: if (NS_SUCCEEDED(GetCFURL(&urlRef))) { michael@0: CFBundleRef bundle = ::CFBundleCreate(nullptr, urlRef); michael@0: if (bundle) { michael@0: CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle); michael@0: if (bundleIdentifier) michael@0: rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier); michael@0: ::CFRelease(bundle); michael@0: } michael@0: ::CFRelease(urlRef); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsLocalFile::GetBundleContentsLastModifiedTime(int64_t *aLastModTime) michael@0: { michael@0: CHECK_mPath(); michael@0: if (NS_WARN_IF(!aLastModTime)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: bool isPackage = false; michael@0: nsresult rv = IsPackage(&isPackage); michael@0: if (NS_FAILED(rv) || !isPackage) { michael@0: return GetLastModifiedTime(aLastModTime); michael@0: } michael@0: michael@0: nsAutoCString infoPlistPath(mPath); michael@0: infoPlistPath.AppendLiteral("/Contents/Info.plist"); michael@0: PRFileInfo64 info; michael@0: if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) { michael@0: return GetLastModifiedTime(aLastModTime); michael@0: } michael@0: int64_t modTime = int64_t(info.modifyTime); michael@0: if (modTime == 0) { michael@0: *aLastModTime = 0; michael@0: } else { michael@0: *aLastModTime = modTime / int64_t(PR_USEC_PER_MSEC); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsLocalFile::InitWithFile(nsIFile *aFile) michael@0: { michael@0: if (NS_WARN_IF(!aFile)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsAutoCString nativePath; michael@0: nsresult rv = aFile->GetNativePath(nativePath); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return InitWithNativePath(nativePath); michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks, nsILocalFileMac** result) michael@0: { michael@0: nsRefPtr file = new nsLocalFile(); michael@0: michael@0: file->SetFollowLinks(aFollowLinks); michael@0: michael@0: nsresult rv = file->InitWithFSRef(aFSRef); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: file.forget(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks, nsILocalFileMac** result) michael@0: { michael@0: nsRefPtr file = new nsLocalFile(); michael@0: michael@0: file->SetFollowLinks(aFollowLinks); michael@0: michael@0: nsresult rv = file->InitWithCFURL(aURL); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: file.forget(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #endif