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