/* -*- 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.
*/
#ifdef MOZ_WIDGET_GTK # include "nsIGIOService.h" # ifdef MOZ_ENABLE_DBUS # include "mozilla/widget/AsyncDBus.h" # include "mozilla/WidgetUtilsGtk.h" # include <map> # endif #endif
#ifdef MOZ_WIDGET_COCOA # include <Carbon/Carbon.h> # include "CocoaFileUtils.h" # include "prmem.h" # include "plbase64.h"
/** * we need these for statfs()
*/ #ifdef HAVE_SYS_STATVFS_H # ifdefined(__osf__) && defined(__DECCXX) extern"C"int statvfs(constchar*, struct statvfs*); # endif # include <sys/statvfs.h> #endif
#ifdef HAVE_SYS_STATFS_H # include <sys/statfs.h> #endif
#ifdef HAVE_SYS_VFS_H # include <sys/vfs.h> #endif
#define ENSURE_STAT_CACHE() \ do { \ if (!FillStatCache()) return NSRESULT_FOR_ERRNO(); \
} while (0)
#define CHECK_mPath() \ do { \ if (mPath.IsEmpty()) return NS_ERROR_NOT_INITIALIZED; \ if (!FilePreferences::IsAllowedPath(mPath)) \ return NS_ERROR_FILE_ACCESS_DENIED; \
} while (0)
#ifdefined(MOZ_ENABLE_DBUS) && defined(MOZ_WIDGET_GTK) // Prefix for files exported through document portal when we are // in a sandboxed environment (Flatpak). staticconst nsCString& GetDocumentStorePath() { staticconst nsDependentCString sDocumentStorePath = [] {
nsCString storePath = nsPrintfCString("/run/user/%d/doc/", getuid()); // Intentionally put into a ToNewCString copy, rather than just making a // static nsCString to avoid leakchecking errors, since we really want to // leak this string. return nsDependentCString(ToNewCString(storePath), storePath.Length());
}(); return sDocumentStorePath;
} #endif
// When enumerating the directory, the paths must have a slash at the end.
nsAutoCString dirPathWithSlash(dirPath);
dirPathWithSlash.Append('/'); if (!FilePreferences::IsAllowedPath(dirPathWithSlash)) { return NS_ERROR_FILE_ACCESS_DENIED;
}
if (NS_FAILED(aParent->GetNativePath(mParentPath))) { return NS_ERROR_FAILURE;
}
if (STAT(mPath.get(), &mCachedStat) == -1) { // try lstat it may be a symlink if (LSTAT(mPath.get(), &mCachedStat) == -1) { returnfalse;
}
} returntrue;
}
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.IsEmpty() && aFilePath.First() == '~') { if (aFilePath.Length() == 1 || aFilePath.CharAt(1) == '/') { // Home dir for the current user
mPath = homePath; if (aFilePath.Length() > 2) {
mPath.Append(Substring(aFilePath, 1));
}
} else { // Home dir for an arbitrary user e.g. `~foo/bar` -> `/home/foo/bar` // (`/Users/foo/bar` on Mac). The accurate way to get this directory // is with `getpwnam`, but we would like to avoid doing blocking // filesystem I/O while creating an `nsIFile`.
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
mkdir_result = mkdir(buffer, aPermissions); if (mkdir_result == -1) {
mkdir_errno = errno; /* * 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 (mkdir_errno != EEXIST && access(buffer, F_OK) == 0) {
mkdir_errno = EEXIST;
} #ifdef DEBUG_NSIFILE
fprintf(stderr, "nsIFile: errno: %d\n", mkdir_errno); #endif
}
/* Put the / back */
*slashp = '/';
}
/* * We could get EEXIST for an existing file -- not directory -- * but that's OK: we'll get ENOTDIR when we try to make the final * component of the path back in Create and error out appropriately.
*/ if (mkdir_result == -1 && mkdir_errno != EEXIST) { return NS_ERROR_FAILURE;
}
int result = createFunc(mPath.get(), aFlags, aPermissions, aResult); if (result == -1 && errno == ENOENT && !aSkipAncestors) { /* * 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;
}
// only one component of path can be appended and cannot append ".."
nsACString::const_iterator begin, end; if (aFragment.EqualsASCII("..") ||
FindCharInReadable('/', aFragment.BeginReading(begin),
aFragment.EndReading(end))) { return NS_ERROR_FILE_UNRECOGNIZED_PATH;
}
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??)
}
widget::CreateDBusProxyForBus(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /* aInterfaceInfo = */ nullptr, kServiceName,
kDBusPath, kInterfaceName)
->Then(
GetCurrentSerialEventTarget(), __func__,
[this, self = RefPtr(this), docId,
retPromise](RefPtr<GDBusProxy>&& aProxy) {
RefPtr<GVariant> version = dont_AddRef(
g_dbus_proxy_get_cached_property(aProxy, "version")); if (!version ||
!g_variant_is_of_type(version, G_VARIANT_TYPE_UINT32)) {
g_printerr( "nsIFile: failed to get host path for %s: Invalid value.\n",
mPath.get());
retPromise->MaybeReject(NS_ERROR_FAILURE); return;
}
if (g_variant_get_uint32(version) < 5) {
g_printerr( "nsIFile: failed to get host path for %s: Document " "portal in version 5 is required.\n",
mPath.get());
retPromise->MaybeReject(NS_ERROR_NOT_AVAILABLE); return;
}
if (!args) {
g_printerr( "nsIFile: failed to get host path for %s: " "Invalid value.\n",
mPath.get());
retPromise->MaybeReject(NS_ERROR_FAILURE); return;
}
widget::DBusProxyCall(aProxy, "GetHostPaths", args,
G_DBUS_CALL_FLAGS_NONE, -1, /* cancellable */ nullptr)
->Then(
GetCurrentSerialEventTarget(), __func__,
[this, self = RefPtr(this), docId,
retPromise](RefPtr<GVariant>&& aResult) {
RefPtr<GVariant> result = dont_AddRef(
g_variant_get_child_value(aResult.get(), 0)); if (!g_variant_is_of_type(result,
G_VARIANT_TYPE("a{say}"))) {
g_printerr( "nsIFile: failed to get host path for %s: " "Invalid value.\n",
mPath.get());
retPromise->MaybeReject(NS_ERROR_FAILURE); return;
}
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;
}
}
}
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, ""_ns);
}
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<nsIDirectoryEnumerator> dirIterator; if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator)))) { return rv;
}
nsCOMPtr<nsIFile> entry; while (NS_SUCCEEDED(dirIterator->GetNextFile(getter_AddRefs(entry))) &&
entry) { 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, ""_ns))) { #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, ""_ns))) { #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;
}
// actually create the file. auto* 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 = 0;
rv = GetPermissions(&myPerms); if (NS_FAILED(rv)) { return rv;
}
// 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.
// 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;
}
// 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.
// 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();
}
// 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
} #ifdefined(XP_MACOSX) elseif (!quarantined) { // If the original file was not in quarantine, lift the quarantine that // file creation added because of LSFileQuarantineEnabled.
(void)newFile->DelXAttr("com.apple.quarantine"_ns);
} #endif// defined(XP_MACOSX)
// 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;
} if (saved_read_error != NS_OK) { return saved_read_error;
} #if 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;
}
// 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;
}
if (!FilePreferences::IsAllowedPath(newPathName)) { return NS_ERROR_FILE_ACCESS_DENIED;
}
// 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;
}
nsresult rv = IsSymlink(&isSymLink); if (NS_FAILED(rv)) { return rv;
}
if (isSymLink || !S_ISDIR(mCachedStat.st_mode)) {
rv = NSRESULT_FOR_RETURN(unlink(mPath.get())); if (NS_SUCCEEDED(rv) && aRemoveCount) {
*aRemoveCount += 1;
} return rv;
}
if (aRecursive) { auto* dir = new nsDirEnumeratorUnix();
RefPtr<nsSimpleEnumerator> dirRef(dir); // release on exit
rv = dir->Init(this, false); if (NS_FAILED(rv)) { return rv;
}
bool more; while (NS_SUCCEEDED(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;
} // XXX: We care the result of the removal here while // nsLocalFileWin does not. We should align the behavior. (bug 1779696)
rv = file->Remove(aRecursive, aRemoveCount);
#ifdef ANDROID // See bug 580434 - Bionic gives us just deleted files if (rv == NS_ERROR_FILE_NOT_FOUND) { continue;
} #endif if (NS_FAILED(rv)) { return rv;
}
}
}
using UtimesFn = int (*)(constchar*, const timeval*);
UtimesFn utimesFn = &utimes;
#if HAVE_LUTIMES if (!aFollowLinks) {
utimesFn = &lutimes;
} #endif
ENSURE_STAT_CACHE();
if (aTime == 0) {
aTime = PR_Now();
}
// We only want to write to a single field (accessed time or modified time), // but utimes() doesn't let you omit one. If you do, it will set that field to // the current time, which is not what we want. // // So what we do is write to both fields, but copy one of the fields from our // cached stat structure. // // If we are writing to the accessed time field, then we want to copy the // modified time and vice versa.
/* * 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;
} #ifdefined(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;
}
// 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. * f_blocks = number of total used or free blocks in file system.
*/
if (STATFS(mPath.get(), &fs_buf) < 0) { // The call to STATFS failed. # ifdef DEBUG
printf("ERROR: GetDiskInfo: STATFS call FAILED. \n"); # endif return NS_ERROR_FAILURE;
}
CheckedInt64 statfsResult = std::forward<StatInfoFunc>(aStatInfoFunc)(fs_buf); if (!statfsResult.isValid()) { return NS_ERROR_CANNOT_CONVERT_DATA;
}
// Assign statfsResult to *aResult in case one of the quota calls fails.
*aResult = statfsResult.value();
# ifdefined(USE_LINUX_QUOTACTL)
if (!FillStatCache()) { // Returns info from statfs return NS_OK;
}
nsAutoCString deviceName; if (!GetDeviceName(major(mCachedStat.st_dev), minor(mCachedStat.st_dev),
deviceName)) { // Returns info from statfs 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) {
CheckedInt64 quotaResult = std::forward<QuotaInfoFunc>(aQuotaInfoFunc)(dq); if (!quotaResult.isValid()) { // Returns info from statfs return NS_OK;
}
#else// STATFS /* * This platform doesn't have statfs or statvfs. I'm sure that there's * a way to check for free disk space and disk capacity 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: GetDiskInfo: Not implemented for plaforms without statfs.\n"); # endif return NS_ERROR_NOT_IMPLEMENTED;
// 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';
// 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;
}
// Search for any of the set of executable extensions. staticconstchar* const executableExts[] = { #ifdef MOZ_WIDGET_COCOA "afploc", // Can point to other files. #endif "air", // Adobe AIR installer #ifdef MOZ_WIDGET_COCOA "atloc", // Can point to other files. "fileloc", // File location files can be used to point to other // files. "ftploc", // Can point to other files. "inetloc", // Shouldn't be able to do the same, but can, due to // macOS vulnerabilities. #endif "jar"// java application bundle
};
nsDependentSubstring ext = Substring(path, dotIdx + 1); for (auto executableExt : executableExts) { if (ext.EqualsASCII(executableExt)) { // 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;
}
// Then check the execute bit.
*aResult = (access(mPath.get(), X_OK) == 0); #ifdef 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;
// 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;
}
static nsresult ReadLinkSafe(const nsCString& aTarget, int32_t aExpectedSize,
nsACString& aOutBuffer) { // If we call readlink with a buffer size S it returns S, then we cannot tell // if the buffer was big enough to hold the entire path. We allocate an // additional byte so we can check if the buffer was large enough. constauto allocSize = CheckedInt<size_t>(aExpectedSize) + 1; if (!allocSize.isValid()) { return NS_ERROR_OUT_OF_MEMORY;
}
auto result = aOutBuffer.BulkWrite(allocSize.value(), 0, false); if (result.isErr()) { return result.unwrapErr();
}
auto handle = result.unwrap();
while (true) {
ssize_t bytesWritten =
readlink(aTarget.get(), handle.Elements(), handle.Length()); if (bytesWritten < 0) { return NSRESULT_FOR_ERRNO();
}
// written >= 0 so it is safe to cast to size_t. if ((size_t)bytesWritten < handle.Length()) { // Target might have changed since the lstat call, or lstat might lie, see // bug 1791029.
handle.Finish(bytesWritten, false); return NS_OK;
}
// The buffer was not large enough, so double it and try again. auto restartResult = handle.RestartBulkWrite(handle.Length() * 2, 0, false); if (restartResult.isErr()) { return restartResult.unwrapErr();
}
}
}
// 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;
}
// 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); // PL_Base64Decode() uses PR_Malloc(). 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); // PL_Base64Decode() uses PR_Malloc(). if (NS_FAILED(rv)) { return rv;
}
return giovfs->LaunchFile(mPath); #elifdefined(MOZ_WIDGET_ANDROID) // Not supported on GeckoView return NS_ERROR_NOT_IMPLEMENTED; #elifdefined(MOZ_WIDGET_COCOA)
CFURLRef url; if (NS_SUCCEEDED(GetCFURL(&url))) {
nsresult rv = CocoaFileUtils::OpenURL(url);
--> --------------------
--> maximum size reached
--> --------------------
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.53Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.