xpcom/io/nsLocalFileUnix.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/io/nsLocalFileUnix.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2555 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/**
    1.10 + * Implementation of nsIFile for "unixy" systems.
    1.11 + */
    1.12 +
    1.13 +#include "mozilla/ArrayUtils.h"
    1.14 +#include "mozilla/Attributes.h"
    1.15 +
    1.16 +#include <sys/types.h>
    1.17 +#include <sys/stat.h>
    1.18 +#include <unistd.h>
    1.19 +#include <fcntl.h>
    1.20 +#include <errno.h>
    1.21 +#include <utime.h>
    1.22 +#include <dirent.h>
    1.23 +#include <ctype.h>
    1.24 +#include <locale.h>
    1.25 +#if defined(VMS)
    1.26 +    #include <fabdef.h>
    1.27 +#endif
    1.28 +
    1.29 +#if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H)
    1.30 +#define USE_LINUX_QUOTACTL
    1.31 +#include <sys/quota.h>
    1.32 +#endif
    1.33 +
    1.34 +#include "xpcom-private.h"
    1.35 +#include "nsDirectoryServiceDefs.h"
    1.36 +#include "nsCRT.h"
    1.37 +#include "nsCOMPtr.h"
    1.38 +#include "nsMemory.h"
    1.39 +#include "nsIFile.h"
    1.40 +#include "nsString.h"
    1.41 +#include "nsReadableUtils.h"
    1.42 +#include "nsLocalFile.h"
    1.43 +#include "nsIComponentManager.h"
    1.44 +#include "nsXPIDLString.h"
    1.45 +#include "prproces.h"
    1.46 +#include "nsIDirectoryEnumerator.h"
    1.47 +#include "nsISimpleEnumerator.h"
    1.48 +#include "private/pprio.h"
    1.49 +#include "prlink.h"
    1.50 +
    1.51 +#ifdef MOZ_WIDGET_GTK
    1.52 +#include "nsIGIOService.h"
    1.53 +#include "nsIGnomeVFSService.h"
    1.54 +#endif
    1.55 +
    1.56 +#ifdef MOZ_WIDGET_COCOA
    1.57 +#include <Carbon/Carbon.h>
    1.58 +#include "CocoaFileUtils.h"
    1.59 +#include "prmem.h"
    1.60 +#include "plbase64.h"
    1.61 +
    1.62 +static nsresult MacErrorMapper(OSErr inErr);
    1.63 +#endif
    1.64 +
    1.65 +#ifdef MOZ_WIDGET_ANDROID
    1.66 +#include "AndroidBridge.h"
    1.67 +#include "nsIMIMEService.h"
    1.68 +#include <linux/magic.h>
    1.69 +#endif
    1.70 +
    1.71 +#ifdef MOZ_ENABLE_CONTENTACTION
    1.72 +#include <contentaction/contentaction.h>
    1.73 +#endif
    1.74 +
    1.75 +#include "nsNativeCharsetUtils.h"
    1.76 +#include "nsTraceRefcnt.h"
    1.77 +#include "nsHashKeys.h"
    1.78 +
    1.79 +using namespace mozilla;
    1.80 +
    1.81 +#define ENSURE_STAT_CACHE()                     \
    1.82 +    PR_BEGIN_MACRO                              \
    1.83 +        if (!FillStatCache())                   \
    1.84 +             return NSRESULT_FOR_ERRNO();       \
    1.85 +    PR_END_MACRO
    1.86 +
    1.87 +#define CHECK_mPath()                           \
    1.88 +    PR_BEGIN_MACRO                              \
    1.89 +        if (mPath.IsEmpty())                    \
    1.90 +            return NS_ERROR_NOT_INITIALIZED;    \
    1.91 +    PR_END_MACRO
    1.92 +
    1.93 +/* directory enumerator */
    1.94 +class
    1.95 +nsDirEnumeratorUnix MOZ_FINAL : public nsISimpleEnumerator,
    1.96 +                                public nsIDirectoryEnumerator
    1.97 +{
    1.98 +    public:
    1.99 +    nsDirEnumeratorUnix();
   1.100 +
   1.101 +    // nsISupports interface
   1.102 +    NS_DECL_ISUPPORTS
   1.103 +
   1.104 +    // nsISimpleEnumerator interface
   1.105 +    NS_DECL_NSISIMPLEENUMERATOR
   1.106 +
   1.107 +    // nsIDirectoryEnumerator interface
   1.108 +    NS_DECL_NSIDIRECTORYENUMERATOR
   1.109 +
   1.110 +    NS_IMETHOD Init(nsLocalFile *parent, bool ignored);
   1.111 +
   1.112 +    private:
   1.113 +    ~nsDirEnumeratorUnix();
   1.114 +
   1.115 +    protected:
   1.116 +    NS_IMETHOD GetNextEntry();
   1.117 +
   1.118 +    DIR           *mDir;
   1.119 +    struct dirent *mEntry;
   1.120 +    nsCString      mParentPath;
   1.121 +};
   1.122 +
   1.123 +nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
   1.124 +                         mDir(nullptr), 
   1.125 +                         mEntry(nullptr)
   1.126 +{
   1.127 +}
   1.128 +
   1.129 +nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
   1.130 +{
   1.131 +    Close();
   1.132 +}
   1.133 +
   1.134 +NS_IMPL_ISUPPORTS(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator)
   1.135 +
   1.136 +NS_IMETHODIMP
   1.137 +nsDirEnumeratorUnix::Init(nsLocalFile *parent, bool resolveSymlinks /*ignored*/)
   1.138 +{
   1.139 +    nsAutoCString dirPath;
   1.140 +    if (NS_FAILED(parent->GetNativePath(dirPath)) ||
   1.141 +        dirPath.IsEmpty()) {
   1.142 +        return NS_ERROR_FILE_INVALID_PATH;
   1.143 +    }
   1.144 +
   1.145 +    if (NS_FAILED(parent->GetNativePath(mParentPath)))
   1.146 +        return NS_ERROR_FAILURE;
   1.147 +
   1.148 +    mDir = opendir(dirPath.get());
   1.149 +    if (!mDir)
   1.150 +        return NSRESULT_FOR_ERRNO();
   1.151 +    return GetNextEntry();
   1.152 +}
   1.153 +
   1.154 +NS_IMETHODIMP
   1.155 +nsDirEnumeratorUnix::HasMoreElements(bool *result)
   1.156 +{
   1.157 +    *result = mDir && mEntry;
   1.158 +    if (!*result)
   1.159 +        Close();
   1.160 +    return NS_OK;
   1.161 +}
   1.162 +
   1.163 +NS_IMETHODIMP
   1.164 +nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
   1.165 +{
   1.166 +    nsCOMPtr<nsIFile> file;
   1.167 +    nsresult rv = GetNextFile(getter_AddRefs(file));
   1.168 +    if (NS_FAILED(rv))
   1.169 +        return rv;
   1.170 +    NS_IF_ADDREF(*_retval = file);
   1.171 +    return NS_OK;
   1.172 +}
   1.173 +
   1.174 +NS_IMETHODIMP
   1.175 +nsDirEnumeratorUnix::GetNextEntry()
   1.176 +{
   1.177 +    do {
   1.178 +        errno = 0;
   1.179 +        mEntry = readdir(mDir);
   1.180 +
   1.181 +        // end of dir or error
   1.182 +        if (!mEntry)
   1.183 +            return NSRESULT_FOR_ERRNO();
   1.184 +
   1.185 +        // keep going past "." and ".."
   1.186 +    } while (mEntry->d_name[0] == '.'     &&
   1.187 +            (mEntry->d_name[1] == '\0'    ||   // .\0
   1.188 +            (mEntry->d_name[1] == '.'     &&
   1.189 +            mEntry->d_name[2] == '\0')));      // ..\0
   1.190 +    return NS_OK;
   1.191 +}
   1.192 +
   1.193 +NS_IMETHODIMP
   1.194 +nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval)
   1.195 +{
   1.196 +    nsresult rv;
   1.197 +    if (!mDir || !mEntry) {
   1.198 +        *_retval = nullptr;
   1.199 +        return NS_OK;
   1.200 +    }
   1.201 +
   1.202 +    nsCOMPtr<nsIFile> file = new nsLocalFile();
   1.203 +
   1.204 +    if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
   1.205 +        NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name))))
   1.206 +        return rv;
   1.207 +
   1.208 +    file.forget(_retval);
   1.209 +    return GetNextEntry();
   1.210 +}
   1.211 +
   1.212 +NS_IMETHODIMP 
   1.213 +nsDirEnumeratorUnix::Close()
   1.214 +{
   1.215 +    if (mDir) {
   1.216 +        closedir(mDir);
   1.217 +        mDir = nullptr;
   1.218 +    }
   1.219 +    return NS_OK;
   1.220 +}
   1.221 +
   1.222 +nsLocalFile::nsLocalFile()
   1.223 +{
   1.224 +}
   1.225 +
   1.226 +nsLocalFile::nsLocalFile(const nsLocalFile& other)
   1.227 +  : mPath(other.mPath)
   1.228 +{
   1.229 +}
   1.230 +
   1.231 +#ifdef MOZ_WIDGET_COCOA
   1.232 +NS_IMPL_ISUPPORTS(nsLocalFile,
   1.233 +                  nsILocalFileMac,
   1.234 +                  nsILocalFile,
   1.235 +                  nsIFile,
   1.236 +                  nsIHashable)
   1.237 +#else
   1.238 +NS_IMPL_ISUPPORTS(nsLocalFile,
   1.239 +                  nsILocalFile,
   1.240 +                  nsIFile,
   1.241 +                  nsIHashable)
   1.242 +#endif
   1.243 +
   1.244 +nsresult
   1.245 +nsLocalFile::nsLocalFileConstructor(nsISupports *outer, 
   1.246 +                                    const nsIID &aIID,
   1.247 +                                    void **aInstancePtr)
   1.248 +{
   1.249 +    if (NS_WARN_IF(!aInstancePtr))
   1.250 +        return NS_ERROR_INVALID_ARG;
   1.251 +    if (NS_WARN_IF(outer))
   1.252 +        return NS_ERROR_NO_AGGREGATION;
   1.253 +
   1.254 +    *aInstancePtr = nullptr;
   1.255 +
   1.256 +    nsCOMPtr<nsIFile> inst = new nsLocalFile();
   1.257 +    return inst->QueryInterface(aIID, aInstancePtr);
   1.258 +}
   1.259 +
   1.260 +bool 
   1.261 +nsLocalFile::FillStatCache() {
   1.262 +    if (STAT(mPath.get(), &mCachedStat) == -1) {
   1.263 +        // try lstat it may be a symlink
   1.264 +        if (LSTAT(mPath.get(), &mCachedStat) == -1) {
   1.265 +            return false;
   1.266 +        }
   1.267 +    }
   1.268 +    return true;
   1.269 +}
   1.270 +
   1.271 +NS_IMETHODIMP
   1.272 +nsLocalFile::Clone(nsIFile **file)
   1.273 +{
   1.274 +    // Just copy-construct ourselves
   1.275 +    nsRefPtr<nsLocalFile> copy = new nsLocalFile(*this);
   1.276 +    copy.forget(file);
   1.277 +    return NS_OK;
   1.278 +}
   1.279 +
   1.280 +NS_IMETHODIMP
   1.281 +nsLocalFile::InitWithNativePath(const nsACString &filePath)
   1.282 +{
   1.283 +    if (filePath.Equals("~") || Substring(filePath, 0, 2).EqualsLiteral("~/")) {
   1.284 +        nsCOMPtr<nsIFile> homeDir;
   1.285 +        nsAutoCString homePath;
   1.286 +        if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
   1.287 +                                             getter_AddRefs(homeDir))) ||
   1.288 +            NS_FAILED(homeDir->GetNativePath(homePath))) {
   1.289 +            return NS_ERROR_FAILURE;
   1.290 +        }
   1.291 + 
   1.292 +        mPath = homePath;
   1.293 +        if (filePath.Length() > 2)
   1.294 +          mPath.Append(Substring(filePath, 1, filePath.Length() - 1));
   1.295 +    } else {
   1.296 +        if (filePath.IsEmpty() || filePath.First() != '/')
   1.297 +            return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   1.298 +        mPath = filePath;
   1.299 +    }
   1.300 +
   1.301 +    // trim off trailing slashes
   1.302 +    ssize_t len = mPath.Length();
   1.303 +    while ((len > 1) && (mPath[len - 1] == '/'))
   1.304 +        --len;
   1.305 +    mPath.SetLength(len);
   1.306 +
   1.307 +    return NS_OK;
   1.308 +}
   1.309 +
   1.310 +NS_IMETHODIMP
   1.311 +nsLocalFile::CreateAllAncestors(uint32_t permissions)
   1.312 +{
   1.313 +    // <jband> I promise to play nice
   1.314 +    char *buffer = mPath.BeginWriting(),
   1.315 +         *slashp = buffer;
   1.316 +
   1.317 +#ifdef DEBUG_NSIFILE
   1.318 +    fprintf(stderr, "nsIFile: before: %s\n", buffer);
   1.319 +#endif
   1.320 +
   1.321 +    while ((slashp = strchr(slashp + 1, '/'))) {
   1.322 +        /*
   1.323 +         * Sequences of '/' are equivalent to a single '/'.
   1.324 +         */
   1.325 +        if (slashp[1] == '/')
   1.326 +            continue;
   1.327 +
   1.328 +        /*
   1.329 +         * If the path has a trailing slash, don't make the last component,
   1.330 +         * because we'll get EEXIST in Create when we try to build the final
   1.331 +         * component again, and it's easier to condition the logic here than
   1.332 +         * there.
   1.333 +         */
   1.334 +        if (slashp[1] == '\0')
   1.335 +            break;
   1.336 +
   1.337 +        /* Temporarily NUL-terminate here */
   1.338 +        *slashp = '\0';
   1.339 +#ifdef DEBUG_NSIFILE
   1.340 +        fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
   1.341 +#endif
   1.342 +        int mkdir_result = mkdir(buffer, permissions);
   1.343 +        int mkdir_errno  = errno;
   1.344 +        if (mkdir_result == -1) {
   1.345 +            /*
   1.346 +             * Always set |errno| to EEXIST if the dir already exists
   1.347 +             * (we have to do this here since the errno value is not consistent
   1.348 +             * in all cases - various reasons like different platform,
   1.349 +             * automounter-controlled dir, etc. can affect it (see bug 125489
   1.350 +             * for details)).
   1.351 +             */
   1.352 +            if (access(buffer, F_OK) == 0) {
   1.353 +                mkdir_errno = EEXIST;
   1.354 +            }
   1.355 +        }
   1.356 +
   1.357 +        /* Put the / back before we (maybe) return */
   1.358 +        *slashp = '/';
   1.359 +
   1.360 +        /*
   1.361 +         * We could get EEXIST for an existing file -- not directory --
   1.362 +         * with the name of one of our ancestors, but that's OK: we'll get
   1.363 +         * ENOTDIR when we try to make the next component in the path,
   1.364 +         * either here on back in Create, and error out appropriately.
   1.365 +         */
   1.366 +        if (mkdir_result == -1 && mkdir_errno != EEXIST)
   1.367 +            return nsresultForErrno(mkdir_errno);
   1.368 +    }
   1.369 +
   1.370 +#ifdef DEBUG_NSIFILE
   1.371 +    fprintf(stderr, "nsIFile: after: %s\n", buffer);
   1.372 +#endif
   1.373 +
   1.374 +    return NS_OK;
   1.375 +}
   1.376 +
   1.377 +NS_IMETHODIMP
   1.378 +nsLocalFile::OpenNSPRFileDesc(int32_t flags, int32_t mode, PRFileDesc **_retval)
   1.379 +{
   1.380 +    *_retval = PR_Open(mPath.get(), flags, mode);
   1.381 +    if (! *_retval)
   1.382 +        return NS_ErrorAccordingToNSPR();
   1.383 +
   1.384 +    if (flags & DELETE_ON_CLOSE) {
   1.385 +        PR_Delete(mPath.get());
   1.386 +    }
   1.387 +
   1.388 +#if defined(HAVE_POSIX_FADVISE)
   1.389 +    if (flags & OS_READAHEAD) {
   1.390 +        posix_fadvise(PR_FileDesc2NativeHandle(*_retval), 0, 0,
   1.391 +                      POSIX_FADV_SEQUENTIAL);
   1.392 +    }
   1.393 +#endif
   1.394 +    return NS_OK;
   1.395 +}
   1.396 +
   1.397 +NS_IMETHODIMP
   1.398 +nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
   1.399 +{
   1.400 +    *_retval = fopen(mPath.get(), mode);
   1.401 +    if (! *_retval)
   1.402 +        return NS_ERROR_FAILURE;
   1.403 +
   1.404 +    return NS_OK;
   1.405 +}
   1.406 +
   1.407 +static int
   1.408 +do_create(const char *path, int flags, mode_t mode, PRFileDesc **_retval)
   1.409 +{
   1.410 +    *_retval = PR_Open(path, flags, mode);
   1.411 +    return *_retval ? 0 : -1;
   1.412 +}
   1.413 +
   1.414 +static int
   1.415 +do_mkdir(const char *path, int flags, mode_t mode, PRFileDesc **_retval)
   1.416 +{
   1.417 +    *_retval = nullptr;
   1.418 +    return mkdir(path, mode);
   1.419 +}
   1.420 +
   1.421 +nsresult
   1.422 +nsLocalFile::CreateAndKeepOpen(uint32_t type, int flags,
   1.423 +                               uint32_t permissions, PRFileDesc **_retval)
   1.424 +{
   1.425 +    if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
   1.426 +        return NS_ERROR_FILE_UNKNOWN_TYPE;
   1.427 +
   1.428 +    int result;
   1.429 +    int (*createFunc)(const char *, int, mode_t, PRFileDesc **) =
   1.430 +        (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
   1.431 +
   1.432 +    result = createFunc(mPath.get(), flags, permissions, _retval);
   1.433 +    if (result == -1 && errno == ENOENT) {
   1.434 +        /*
   1.435 +         * If we failed because of missing ancestor components, try to create
   1.436 +         * them and then retry the original creation.
   1.437 +         *
   1.438 +         * Ancestor directories get the same permissions as the file we're
   1.439 +         * creating, with the X bit set for each of (user,group,other) with
   1.440 +         * an R bit in the original permissions.    If you want to do anything
   1.441 +         * fancy like setgid or sticky bits, do it by hand.
   1.442 +         */
   1.443 +        int dirperm = permissions;
   1.444 +        if (permissions & S_IRUSR)
   1.445 +            dirperm |= S_IXUSR;
   1.446 +        if (permissions & S_IRGRP)
   1.447 +            dirperm |= S_IXGRP;
   1.448 +        if (permissions & S_IROTH)
   1.449 +            dirperm |= S_IXOTH;
   1.450 +
   1.451 +#ifdef DEBUG_NSIFILE
   1.452 +        fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
   1.453 +                dirperm);
   1.454 +#endif
   1.455 +
   1.456 +        if (NS_FAILED(CreateAllAncestors(dirperm)))
   1.457 +            return NS_ERROR_FAILURE;
   1.458 +
   1.459 +#ifdef DEBUG_NSIFILE
   1.460 +        fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
   1.461 +#endif
   1.462 +        result = createFunc(mPath.get(), flags, permissions, _retval);
   1.463 +    }
   1.464 +    return NSRESULT_FOR_RETURN(result);
   1.465 +}
   1.466 +
   1.467 +NS_IMETHODIMP
   1.468 +nsLocalFile::Create(uint32_t type, uint32_t permissions)
   1.469 +{
   1.470 +    PRFileDesc *junk = nullptr;
   1.471 +    nsresult rv = CreateAndKeepOpen(type,
   1.472 +                                    PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
   1.473 +                                    PR_EXCL,
   1.474 +                                    permissions,
   1.475 +                                    &junk);
   1.476 +    if (junk)
   1.477 +        PR_Close(junk);
   1.478 +    return rv;
   1.479 +}
   1.480 +
   1.481 +NS_IMETHODIMP
   1.482 +nsLocalFile::AppendNative(const nsACString &fragment)
   1.483 +{
   1.484 +    if (fragment.IsEmpty())
   1.485 +        return NS_OK;
   1.486 +
   1.487 +    // only one component of path can be appended
   1.488 +    nsACString::const_iterator begin, end;
   1.489 +    if (FindCharInReadable('/', fragment.BeginReading(begin),
   1.490 +                                fragment.EndReading(end)))
   1.491 +        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   1.492 +
   1.493 +    return AppendRelativeNativePath(fragment);
   1.494 +}
   1.495 +
   1.496 +NS_IMETHODIMP
   1.497 +nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
   1.498 +{
   1.499 +    if (fragment.IsEmpty())
   1.500 +        return NS_OK;
   1.501 +
   1.502 +    // No leading '/' 
   1.503 +    if (fragment.First() == '/')
   1.504 +        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   1.505 +
   1.506 +    if (mPath.EqualsLiteral("/"))
   1.507 +        mPath.Append(fragment);
   1.508 +    else
   1.509 +        mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
   1.510 +
   1.511 +    return NS_OK;
   1.512 +}
   1.513 +
   1.514 +NS_IMETHODIMP
   1.515 +nsLocalFile::Normalize()
   1.516 +{
   1.517 +    char    resolved_path[PATH_MAX] = "";
   1.518 +    char *resolved_path_ptr = nullptr;
   1.519 +
   1.520 +    resolved_path_ptr = realpath(mPath.get(), resolved_path);
   1.521 +
   1.522 +    // if there is an error, the return is null.
   1.523 +    if (!resolved_path_ptr)
   1.524 +        return NSRESULT_FOR_ERRNO();
   1.525 +
   1.526 +    mPath = resolved_path;
   1.527 +    return NS_OK;
   1.528 +}
   1.529 +
   1.530 +void
   1.531 +nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin, 
   1.532 +                                  nsACString::const_iterator &end)
   1.533 +{
   1.534 +    // XXX perhaps we should cache this??
   1.535 +
   1.536 +    mPath.BeginReading(begin);
   1.537 +    mPath.EndReading(end);
   1.538 +    
   1.539 +    nsACString::const_iterator it = end;
   1.540 +    nsACString::const_iterator stop = begin;
   1.541 +    --stop;
   1.542 +    while (--it != stop) {
   1.543 +        if (*it == '/') {
   1.544 +            begin = ++it;
   1.545 +            return;
   1.546 +        }
   1.547 +    }
   1.548 +    // else, the entire path is the leaf name (which means this
   1.549 +    // isn't an absolute path... unexpected??)
   1.550 +}
   1.551 +
   1.552 +NS_IMETHODIMP
   1.553 +nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
   1.554 +{
   1.555 +    nsACString::const_iterator begin, end;
   1.556 +    LocateNativeLeafName(begin, end);
   1.557 +    aLeafName = Substring(begin, end);
   1.558 +    return NS_OK;
   1.559 +}
   1.560 +
   1.561 +NS_IMETHODIMP
   1.562 +nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
   1.563 +{
   1.564 +    nsACString::const_iterator begin, end;
   1.565 +    LocateNativeLeafName(begin, end);
   1.566 +    mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
   1.567 +    return NS_OK;
   1.568 +}
   1.569 +
   1.570 +NS_IMETHODIMP
   1.571 +nsLocalFile::GetNativePath(nsACString &_retval)
   1.572 +{
   1.573 +    _retval = mPath;
   1.574 +    return NS_OK;
   1.575 +}
   1.576 +
   1.577 +nsresult
   1.578 +nsLocalFile::GetNativeTargetPathName(nsIFile *newParent, 
   1.579 +                                     const nsACString &newName,
   1.580 +                                     nsACString &_retval)
   1.581 +{
   1.582 +    nsresult rv;
   1.583 +    nsCOMPtr<nsIFile> oldParent;
   1.584 +
   1.585 +    if (!newParent) {
   1.586 +        if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
   1.587 +            return rv;
   1.588 +        newParent = oldParent.get();
   1.589 +    } else {
   1.590 +        // check to see if our target directory exists
   1.591 +        bool targetExists;
   1.592 +        if (NS_FAILED(rv = newParent->Exists(&targetExists)))
   1.593 +            return rv;
   1.594 +
   1.595 +        if (!targetExists) {
   1.596 +            // XXX create the new directory with some permissions
   1.597 +            rv = newParent->Create(DIRECTORY_TYPE, 0755);
   1.598 +            if (NS_FAILED(rv))
   1.599 +                return rv;
   1.600 +        } else {
   1.601 +            // make sure that the target is actually a directory
   1.602 +            bool targetIsDirectory;
   1.603 +            if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
   1.604 +                return rv;
   1.605 +            if (!targetIsDirectory)
   1.606 +                return NS_ERROR_FILE_DESTINATION_NOT_DIR;
   1.607 +        }
   1.608 +    }
   1.609 +
   1.610 +    nsACString::const_iterator nameBegin, nameEnd;
   1.611 +    if (!newName.IsEmpty()) {
   1.612 +        newName.BeginReading(nameBegin);
   1.613 +        newName.EndReading(nameEnd);
   1.614 +    }
   1.615 +    else
   1.616 +        LocateNativeLeafName(nameBegin, nameEnd);
   1.617 +
   1.618 +    nsAutoCString dirName;
   1.619 +    if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
   1.620 +        return rv;
   1.621 +
   1.622 +    _retval = dirName
   1.623 +            + NS_LITERAL_CSTRING("/")
   1.624 +            + Substring(nameBegin, nameEnd);
   1.625 +    return NS_OK;
   1.626 +}
   1.627 +
   1.628 +nsresult
   1.629 +nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
   1.630 +{
   1.631 +    nsresult rv;
   1.632 +    /*
   1.633 +     * dirCheck is used for various boolean test results such as from Equals,
   1.634 +     * Exists, isDir, etc.
   1.635 +     */
   1.636 +    bool dirCheck, isSymlink;
   1.637 +    uint32_t oldPerms;
   1.638 +
   1.639 +    if (NS_FAILED(rv = IsDirectory(&dirCheck)))
   1.640 +        return rv;
   1.641 +    if (!dirCheck)
   1.642 +        return CopyToNative(newParent, EmptyCString());
   1.643 +    
   1.644 +    if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
   1.645 +        return rv;
   1.646 +    if (dirCheck) { 
   1.647 +        // can't copy dir to itself
   1.648 +        return NS_ERROR_INVALID_ARG;
   1.649 +    }
   1.650 +    
   1.651 +    if (NS_FAILED(rv = newParent->Exists(&dirCheck))) 
   1.652 +        return rv;
   1.653 +    // get the dirs old permissions
   1.654 +    if (NS_FAILED(rv = GetPermissions(&oldPerms)))
   1.655 +        return rv;
   1.656 +    if (!dirCheck) {
   1.657 +        if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
   1.658 +            return rv;
   1.659 +    } else {    // dir exists lets try to use leaf
   1.660 +        nsAutoCString leafName;
   1.661 +        if (NS_FAILED(rv = GetNativeLeafName(leafName)))
   1.662 +            return rv;
   1.663 +        if (NS_FAILED(rv = newParent->AppendNative(leafName)))
   1.664 +            return rv;
   1.665 +        if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
   1.666 +            return rv;
   1.667 +        if (dirCheck) 
   1.668 +            return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
   1.669 +        if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
   1.670 +            return rv;
   1.671 +    }
   1.672 +
   1.673 +    nsCOMPtr<nsISimpleEnumerator> dirIterator;
   1.674 +    if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
   1.675 +        return rv;
   1.676 +
   1.677 +    bool hasMore = false;
   1.678 +    while (dirIterator->HasMoreElements(&hasMore), hasMore) {
   1.679 +        nsCOMPtr<nsISupports> supports;
   1.680 +        nsCOMPtr<nsIFile> entry;
   1.681 +        rv = dirIterator->GetNext(getter_AddRefs(supports));
   1.682 +        entry = do_QueryInterface(supports);
   1.683 +        if (NS_FAILED(rv) || !entry)
   1.684 +            continue;
   1.685 +        if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
   1.686 +            return rv;
   1.687 +        if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
   1.688 +            return rv;
   1.689 +        if (dirCheck && !isSymlink) {
   1.690 +            nsCOMPtr<nsIFile> destClone;
   1.691 +            rv = newParent->Clone(getter_AddRefs(destClone));
   1.692 +            if (NS_SUCCEEDED(rv)) {
   1.693 +                if (NS_FAILED(rv = entry->CopyToNative(destClone, EmptyCString()))) {
   1.694 +#ifdef DEBUG
   1.695 +                    nsresult rv2;
   1.696 +                    nsAutoCString pathName;
   1.697 +                    if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
   1.698 +                        return rv2;
   1.699 +                    printf("Operation not supported: %s\n", pathName.get());
   1.700 +#endif
   1.701 +                    if (rv == NS_ERROR_OUT_OF_MEMORY) 
   1.702 +                        return rv;
   1.703 +                    continue;
   1.704 +                }
   1.705 +            }
   1.706 +        } else {
   1.707 +            if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
   1.708 +#ifdef DEBUG
   1.709 +                nsresult rv2;
   1.710 +                nsAutoCString pathName;
   1.711 +                if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
   1.712 +                    return rv2;
   1.713 +                printf("Operation not supported: %s\n", pathName.get());
   1.714 +#endif
   1.715 +                if (rv == NS_ERROR_OUT_OF_MEMORY) 
   1.716 +                    return rv;
   1.717 +                continue;
   1.718 +            }
   1.719 +        }
   1.720 +    }
   1.721 +    return NS_OK;
   1.722 +}
   1.723 +
   1.724 +NS_IMETHODIMP
   1.725 +nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
   1.726 +{
   1.727 +    nsresult rv;
   1.728 +    // check to make sure that this has been initialized properly
   1.729 +    CHECK_mPath();
   1.730 +
   1.731 +    // we copy the parent here so 'newParent' remains immutable
   1.732 +    nsCOMPtr <nsIFile> workParent;
   1.733 +    if (newParent) {
   1.734 +        if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
   1.735 +            return rv;
   1.736 +    } else {
   1.737 +        if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
   1.738 +            return rv;
   1.739 +    }
   1.740 +    
   1.741 +    // check to see if we are a directory or if we are a file
   1.742 +    bool isDirectory;
   1.743 +    if (NS_FAILED(rv = IsDirectory(&isDirectory)))
   1.744 +        return rv;
   1.745 +
   1.746 +    nsAutoCString newPathName;
   1.747 +    if (isDirectory) {
   1.748 +        if (!newName.IsEmpty()) {
   1.749 +            if (NS_FAILED(rv = workParent->AppendNative(newName)))
   1.750 +                return rv;
   1.751 +        } else {
   1.752 +            if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
   1.753 +                return rv;
   1.754 +            if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
   1.755 +                return rv;
   1.756 +        }
   1.757 +        if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
   1.758 +            return rv;
   1.759 +    } else {
   1.760 +        rv = GetNativeTargetPathName(workParent, newName, newPathName);
   1.761 +        if (NS_FAILED(rv)) 
   1.762 +            return rv;
   1.763 +
   1.764 +#ifdef DEBUG_blizzard
   1.765 +        printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
   1.766 +#endif
   1.767 +
   1.768 +        // actually create the file.
   1.769 +        nsLocalFile *newFile = new nsLocalFile();
   1.770 +        if (!newFile)
   1.771 +            return NS_ERROR_OUT_OF_MEMORY;
   1.772 +
   1.773 +        nsCOMPtr<nsIFile> fileRef(newFile); // release on exit
   1.774 +
   1.775 +        rv = newFile->InitWithNativePath(newPathName);
   1.776 +        if (NS_FAILED(rv))
   1.777 +            return rv;
   1.778 +
   1.779 +        // get the old permissions
   1.780 +        uint32_t myPerms;
   1.781 +        GetPermissions(&myPerms);
   1.782 +
   1.783 +        // Create the new file with the old file's permissions, even if write
   1.784 +        // permission is missing.  We can't create with write permission and
   1.785 +        // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
   1.786 +        // But we can write to a read-only file on all Unix filesystems if we
   1.787 +        // open it successfully for writing.
   1.788 +
   1.789 +        PRFileDesc *newFD;
   1.790 +        rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
   1.791 +                                        PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
   1.792 +                                        myPerms,
   1.793 +                                        &newFD);
   1.794 +        if (NS_FAILED(rv))
   1.795 +            return rv;
   1.796 +
   1.797 +        // open the old file, too
   1.798 +        bool specialFile;
   1.799 +        if (NS_FAILED(rv = IsSpecial(&specialFile))) {
   1.800 +            PR_Close(newFD);
   1.801 +            return rv;
   1.802 +        }
   1.803 +        if (specialFile) {
   1.804 +#ifdef DEBUG
   1.805 +            printf("Operation not supported: %s\n", mPath.get());
   1.806 +#endif
   1.807 +            // make sure to clean up properly
   1.808 +            PR_Close(newFD);
   1.809 +            return NS_OK;
   1.810 +        }
   1.811 +               
   1.812 +        PRFileDesc *oldFD;
   1.813 +        rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
   1.814 +        if (NS_FAILED(rv)) {
   1.815 +            // make sure to clean up properly
   1.816 +            PR_Close(newFD);
   1.817 +            return rv;
   1.818 +        }
   1.819 +
   1.820 +#ifdef DEBUG_blizzard
   1.821 +        int32_t totalRead = 0;
   1.822 +        int32_t totalWritten = 0;
   1.823 +#endif
   1.824 +        char buf[BUFSIZ];
   1.825 +        int32_t bytesRead;
   1.826 +
   1.827 +        // record PR_Write() error for better error message later.
   1.828 +        nsresult saved_write_error = NS_OK;
   1.829 +        nsresult saved_read_error = NS_OK;
   1.830 +        nsresult saved_read_close_error = NS_OK;
   1.831 +        nsresult saved_write_close_error = NS_OK;
   1.832 +
   1.833 +        // DONE: Does PR_Read() return bytesRead < 0 for error?
   1.834 +        // Yes., The errors from PR_Read are not so common and
   1.835 +        // the value may not have correspondence in NS_ERROR_*, but
   1.836 +        // we do catch it still, immediately after while() loop.
   1.837 +        // We can differentiate errors pf PR_Read and PR_Write by
   1.838 +        // looking at saved_write_error value. If PR_Write error occurs (and not
   1.839 +        // PR_Read() error), save_write_error is not NS_OK.
   1.840 +
   1.841 +        while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
   1.842 +#ifdef DEBUG_blizzard
   1.843 +            totalRead += bytesRead;
   1.844 +#endif
   1.845 +
   1.846 +            // PR_Write promises never to do a short write
   1.847 +            int32_t bytesWritten = PR_Write(newFD, buf, bytesRead);
   1.848 +            if (bytesWritten < 0) {
   1.849 +                saved_write_error = NSRESULT_FOR_ERRNO();
   1.850 +                bytesRead = -1;
   1.851 +                break;
   1.852 +            }
   1.853 +            NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
   1.854 +
   1.855 +#ifdef DEBUG_blizzard
   1.856 +            totalWritten += bytesWritten;
   1.857 +#endif
   1.858 +        }
   1.859 +
   1.860 +        // TODO/FIXME: If CIFS (and NFS?) may force read/write to return EINTR,
   1.861 +        // we are better off to prepare for retrying. But we need confirmation if
   1.862 +        // EINTR is returned.
   1.863 +
   1.864 +        // Record error if PR_Read() failed.
   1.865 +        // Must be done before any other I/O which may reset errno.
   1.866 +        if ( (bytesRead < 0) && (saved_write_error == NS_OK)) {
   1.867 +            saved_read_error = NSRESULT_FOR_ERRNO();
   1.868 +        }
   1.869 +
   1.870 +#ifdef DEBUG_blizzard
   1.871 +        printf("read %d bytes, wrote %d bytes\n",
   1.872 +                 totalRead, totalWritten);
   1.873 +#endif
   1.874 +
   1.875 +        // DONE: Errors of close can occur.  Read man page of
   1.876 +        // close(2);
   1.877 +        // This is likely to happen if the file system is remote file
   1.878 +        // system (NFS, CIFS, etc.) and network outage occurs.
   1.879 +        // At least, we should tell the user that filesystem/disk is
   1.880 +        // hosed (possibly due to network error, hard disk failure,
   1.881 +        // etc.) so that users can take remedial action.
   1.882 +
   1.883 +        // close the files
   1.884 +        if (PR_Close(newFD) < 0) {
   1.885 +            saved_write_close_error = NSRESULT_FOR_ERRNO();
   1.886 +#if DEBUG
   1.887 +            // This error merits printing.
   1.888 +            fprintf(stderr, "ERROR: PR_Close(newFD) returned error. errno = %d\n", errno);
   1.889 +#endif
   1.890 +        }
   1.891 +
   1.892 +        if (PR_Close(oldFD) < 0) {
   1.893 +            saved_read_close_error = NSRESULT_FOR_ERRNO();
   1.894 +#if DEBUG
   1.895 +            fprintf(stderr, "ERROR: PR_Close(oldFD) returned error. errno = %d\n", errno);
   1.896 +#endif
   1.897 +        }
   1.898 +
   1.899 +        // Let us report the failure to write and read.
   1.900 +        // check for write/read error after cleaning up
   1.901 +        if (bytesRead < 0) {
   1.902 +            if (saved_write_error != NS_OK)
   1.903 +                return saved_write_error;
   1.904 +            else if (saved_read_error != NS_OK)
   1.905 +                return saved_read_error;
   1.906 +#if DEBUG
   1.907 +            else                // sanity check. Die and debug.
   1.908 +                MOZ_ASSERT(0);
   1.909 +#endif
   1.910 +        }
   1.911 +
   1.912 +        if (saved_write_close_error != NS_OK)
   1.913 +            return saved_write_close_error;
   1.914 +        if (saved_read_close_error != NS_OK)
   1.915 +            return saved_read_close_error;
   1.916 +    }
   1.917 +    return rv;
   1.918 +}
   1.919 +
   1.920 +NS_IMETHODIMP
   1.921 +nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
   1.922 +{
   1.923 +    return CopyToNative(newParent, newName);
   1.924 +}
   1.925 +
   1.926 +NS_IMETHODIMP
   1.927 +nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
   1.928 +{
   1.929 +    nsresult rv;
   1.930 +
   1.931 +    // check to make sure that this has been initialized properly
   1.932 +    CHECK_mPath();
   1.933 +
   1.934 +    // check to make sure that we have a new parent
   1.935 +    nsAutoCString newPathName;
   1.936 +    rv = GetNativeTargetPathName(newParent, newName, newPathName);
   1.937 +    if (NS_FAILED(rv))
   1.938 +        return rv;
   1.939 +
   1.940 +    // try for atomic rename, falling back to copy/delete
   1.941 +    if (rename(mPath.get(), newPathName.get()) < 0) {
   1.942 +#ifdef VMS
   1.943 +        if (errno == EXDEV || errno == ENXIO) {
   1.944 +#else
   1.945 +        if (errno == EXDEV) {
   1.946 +#endif
   1.947 +            rv = CopyToNative(newParent, newName);
   1.948 +            if (NS_SUCCEEDED(rv))
   1.949 +                rv = Remove(true);
   1.950 +        } else {
   1.951 +            rv = NSRESULT_FOR_ERRNO();
   1.952 +        }
   1.953 +    }
   1.954 +
   1.955 +    if (NS_SUCCEEDED(rv)) {
   1.956 +        // Adjust this
   1.957 +        mPath = newPathName;
   1.958 +    }
   1.959 +    return rv;
   1.960 +}
   1.961 +
   1.962 +NS_IMETHODIMP
   1.963 +nsLocalFile::Remove(bool recursive)
   1.964 +{
   1.965 +    CHECK_mPath();
   1.966 +    ENSURE_STAT_CACHE();
   1.967 +
   1.968 +    bool isSymLink;
   1.969 +
   1.970 +    nsresult rv = IsSymlink(&isSymLink);
   1.971 +    if (NS_FAILED(rv))
   1.972 +        return rv;
   1.973 +
   1.974 +    if (isSymLink || !S_ISDIR(mCachedStat.st_mode))
   1.975 +        return NSRESULT_FOR_RETURN(unlink(mPath.get()));
   1.976 +
   1.977 +    if (recursive) {
   1.978 +        nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
   1.979 +
   1.980 +        nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
   1.981 +
   1.982 +        rv = dir->Init(this, false);
   1.983 +        if (NS_FAILED(rv))
   1.984 +            return rv;
   1.985 +
   1.986 +        bool more;
   1.987 +        while (dir->HasMoreElements(&more), more) {
   1.988 +            nsCOMPtr<nsISupports> item;
   1.989 +            rv = dir->GetNext(getter_AddRefs(item));
   1.990 +            if (NS_FAILED(rv))
   1.991 +                return NS_ERROR_FAILURE;
   1.992 +
   1.993 +            nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
   1.994 +            if (NS_FAILED(rv))
   1.995 +                return NS_ERROR_FAILURE;
   1.996 +            rv = file->Remove(recursive);
   1.997 +
   1.998 +#ifdef ANDROID
   1.999 +            // See bug 580434 - Bionic gives us just deleted files
  1.1000 +            if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
  1.1001 +                continue;
  1.1002 +#endif
  1.1003 +            if (NS_FAILED(rv))
  1.1004 +                return rv;
  1.1005 +        }
  1.1006 +    }
  1.1007 +
  1.1008 +    return NSRESULT_FOR_RETURN(rmdir(mPath.get()));
  1.1009 +}
  1.1010 +
  1.1011 +NS_IMETHODIMP
  1.1012 +nsLocalFile::GetLastModifiedTime(PRTime *aLastModTime)
  1.1013 +{
  1.1014 +    CHECK_mPath();
  1.1015 +    if (NS_WARN_IF(!aLastModTime))
  1.1016 +        return NS_ERROR_INVALID_ARG;
  1.1017 +
  1.1018 +    PRFileInfo64 info;
  1.1019 +    if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
  1.1020 +        return NSRESULT_FOR_ERRNO();
  1.1021 +    PRTime modTime = info.modifyTime;
  1.1022 +    if (modTime == 0)
  1.1023 +        *aLastModTime = 0;
  1.1024 +    else
  1.1025 +        *aLastModTime = modTime / PR_USEC_PER_MSEC;
  1.1026 +
  1.1027 +    return NS_OK;
  1.1028 +}
  1.1029 +
  1.1030 +NS_IMETHODIMP
  1.1031 +nsLocalFile::SetLastModifiedTime(PRTime aLastModTime)
  1.1032 +{
  1.1033 +    CHECK_mPath();
  1.1034 +
  1.1035 +    int result;
  1.1036 +    if (aLastModTime != 0) {
  1.1037 +        ENSURE_STAT_CACHE();
  1.1038 +        struct utimbuf ut;
  1.1039 +        ut.actime = mCachedStat.st_atime;
  1.1040 +
  1.1041 +        // convert milliseconds to seconds since the unix epoch
  1.1042 +        ut.modtime = (time_t)(aLastModTime / PR_MSEC_PER_SEC);
  1.1043 +        result = utime(mPath.get(), &ut);
  1.1044 +    } else {
  1.1045 +        result = utime(mPath.get(), nullptr);
  1.1046 +    }
  1.1047 +    return NSRESULT_FOR_RETURN(result);
  1.1048 +}
  1.1049 +
  1.1050 +NS_IMETHODIMP
  1.1051 +nsLocalFile::GetLastModifiedTimeOfLink(PRTime *aLastModTimeOfLink)
  1.1052 +{
  1.1053 +    CHECK_mPath();
  1.1054 +    if (NS_WARN_IF(!aLastModTimeOfLink))
  1.1055 +        return NS_ERROR_INVALID_ARG;
  1.1056 +
  1.1057 +    struct STAT sbuf;
  1.1058 +    if (LSTAT(mPath.get(), &sbuf) == -1)
  1.1059 +        return NSRESULT_FOR_ERRNO();
  1.1060 +    *aLastModTimeOfLink = PRTime(sbuf.st_mtime) * PR_MSEC_PER_SEC;
  1.1061 +
  1.1062 +    return NS_OK;
  1.1063 +}
  1.1064 +
  1.1065 +/*
  1.1066 + * utime(2) may or may not dereference symlinks, joy.
  1.1067 + */
  1.1068 +NS_IMETHODIMP
  1.1069 +nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink)
  1.1070 +{
  1.1071 +    return SetLastModifiedTime(aLastModTimeOfLink);
  1.1072 +}
  1.1073 +
  1.1074 +/*
  1.1075 + * Only send back permissions bits: maybe we want to send back the whole
  1.1076 + * mode_t to permit checks against other file types?
  1.1077 + */
  1.1078 +
  1.1079 +#define NORMALIZE_PERMS(mode)    ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
  1.1080 +
  1.1081 +NS_IMETHODIMP
  1.1082 +nsLocalFile::GetPermissions(uint32_t *aPermissions)
  1.1083 +{
  1.1084 +    if (NS_WARN_IF(!aPermissions))
  1.1085 +        return NS_ERROR_INVALID_ARG;
  1.1086 +    ENSURE_STAT_CACHE();
  1.1087 +    *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
  1.1088 +    return NS_OK;
  1.1089 +}
  1.1090 +
  1.1091 +NS_IMETHODIMP
  1.1092 +nsLocalFile::GetPermissionsOfLink(uint32_t *aPermissionsOfLink)
  1.1093 +{
  1.1094 +    CHECK_mPath();
  1.1095 +    if (NS_WARN_IF(!aPermissionsOfLink))
  1.1096 +        return NS_ERROR_INVALID_ARG;
  1.1097 +
  1.1098 +    struct STAT sbuf;
  1.1099 +    if (LSTAT(mPath.get(), &sbuf) == -1)
  1.1100 +        return NSRESULT_FOR_ERRNO();
  1.1101 +    *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
  1.1102 +    return NS_OK;
  1.1103 +}
  1.1104 +
  1.1105 +NS_IMETHODIMP
  1.1106 +nsLocalFile::SetPermissions(uint32_t aPermissions)
  1.1107 +{
  1.1108 +    CHECK_mPath();
  1.1109 +
  1.1110 +    /*
  1.1111 +     * Race condition here: we should use fchmod instead, there's no way to 
  1.1112 +     * guarantee the name still refers to the same file.
  1.1113 +     */
  1.1114 +    if (chmod(mPath.get(), aPermissions) >= 0)
  1.1115 +        return NS_OK;
  1.1116 +#if defined(ANDROID) && defined(STATFS)
  1.1117 +    // For the time being, this is restricted for use by Android, but we 
  1.1118 +    // will figure out what to do for all platforms in bug 638503
  1.1119 +    struct STATFS sfs;
  1.1120 +    if (STATFS(mPath.get(), &sfs) < 0)
  1.1121 +         return NSRESULT_FOR_ERRNO();
  1.1122 +
  1.1123 +    // if this is a FAT file system we can't set file permissions
  1.1124 +    if (sfs.f_type == MSDOS_SUPER_MAGIC )
  1.1125 +        return NS_OK;
  1.1126 +#endif
  1.1127 +    return NSRESULT_FOR_ERRNO();
  1.1128 +}
  1.1129 +
  1.1130 +NS_IMETHODIMP
  1.1131 +nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
  1.1132 +{
  1.1133 +    // There isn't a consistent mechanism for doing this on UNIX platforms. We
  1.1134 +    // might want to carefully implement this in the future though.
  1.1135 +    return NS_ERROR_NOT_IMPLEMENTED;
  1.1136 +}
  1.1137 +
  1.1138 +NS_IMETHODIMP
  1.1139 +nsLocalFile::GetFileSize(int64_t *aFileSize)
  1.1140 +{
  1.1141 +    if (NS_WARN_IF(!aFileSize))
  1.1142 +        return NS_ERROR_INVALID_ARG;
  1.1143 +    *aFileSize = 0;
  1.1144 +    ENSURE_STAT_CACHE();
  1.1145 +
  1.1146 +#if defined(VMS)
  1.1147 +    /* Only two record formats can report correct file content size */
  1.1148 +    if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
  1.1149 +        (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
  1.1150 +        return NS_ERROR_FAILURE;
  1.1151 +    }
  1.1152 +#endif
  1.1153 +
  1.1154 +    if (!S_ISDIR(mCachedStat.st_mode)) {
  1.1155 +        *aFileSize = (int64_t)mCachedStat.st_size;
  1.1156 +    }
  1.1157 +    return NS_OK;
  1.1158 +}
  1.1159 +
  1.1160 +NS_IMETHODIMP
  1.1161 +nsLocalFile::SetFileSize(int64_t aFileSize)
  1.1162 +{
  1.1163 +    CHECK_mPath();
  1.1164 +
  1.1165 +#if defined(ANDROID)
  1.1166 +    /* no truncate on bionic */
  1.1167 +    int fd = open(mPath.get(), O_WRONLY);
  1.1168 +    if (fd == -1)
  1.1169 +        return NSRESULT_FOR_ERRNO();
  1.1170 +
  1.1171 +    int ret = ftruncate(fd, (off_t)aFileSize);
  1.1172 +    close(fd);
  1.1173 +
  1.1174 +    if (ret == -1)
  1.1175 +        return NSRESULT_FOR_ERRNO();
  1.1176 +#elif defined(HAVE_TRUNCATE64)
  1.1177 +    if (truncate64(mPath.get(), (off64_t)aFileSize) == -1)
  1.1178 +        return NSRESULT_FOR_ERRNO();
  1.1179 +#else
  1.1180 +    off_t size = (off_t)aFileSize;
  1.1181 +    if (truncate(mPath.get(), size) == -1)
  1.1182 +        return NSRESULT_FOR_ERRNO();
  1.1183 +#endif
  1.1184 +    return NS_OK;
  1.1185 +}
  1.1186 +
  1.1187 +NS_IMETHODIMP
  1.1188 +nsLocalFile::GetFileSizeOfLink(int64_t *aFileSize)
  1.1189 +{
  1.1190 +    CHECK_mPath();
  1.1191 +    if (NS_WARN_IF(!aFileSize))
  1.1192 +        return NS_ERROR_INVALID_ARG;
  1.1193 +
  1.1194 +    struct STAT sbuf;
  1.1195 +    if (LSTAT(mPath.get(), &sbuf) == -1)
  1.1196 +        return NSRESULT_FOR_ERRNO();
  1.1197 +
  1.1198 +    *aFileSize = (int64_t)sbuf.st_size;
  1.1199 +    return NS_OK;
  1.1200 +}
  1.1201 +
  1.1202 +#if defined(USE_LINUX_QUOTACTL)
  1.1203 +/*
  1.1204 + * Searches /proc/self/mountinfo for given device (Major:Minor), 
  1.1205 + * returns exported name from /dev
  1.1206 + *
  1.1207 + * Fails when /proc/self/mountinfo or diven device don't exist.
  1.1208 + */
  1.1209 +static bool
  1.1210 +GetDeviceName(int deviceMajor, int deviceMinor, nsACString &deviceName)
  1.1211 +{
  1.1212 +    bool ret = false;
  1.1213 +    
  1.1214 +    const int kMountInfoLineLength = 200;
  1.1215 +    const int kMountInfoDevPosition = 6;
  1.1216 +
  1.1217 +    char mountinfo_line[kMountInfoLineLength];
  1.1218 +    char device_num[kMountInfoLineLength];
  1.1219 +    
  1.1220 +    snprintf(device_num,kMountInfoLineLength,"%d:%d", deviceMajor, deviceMinor);
  1.1221 +    
  1.1222 +    FILE *f = fopen("/proc/self/mountinfo","rt");
  1.1223 +    if(!f)
  1.1224 +        return ret;
  1.1225 +
  1.1226 +    // Expects /proc/self/mountinfo in format:
  1.1227 +    // 'ID ID major:minor root mountpoint flags - type devicename flags'
  1.1228 +    while(fgets(mountinfo_line,kMountInfoLineLength,f)) {
  1.1229 +        char *p_dev = strstr(mountinfo_line,device_num);
  1.1230 +    
  1.1231 +        int i;
  1.1232 +        for(i = 0; i < kMountInfoDevPosition && p_dev != nullptr; i++) {
  1.1233 +            p_dev = strchr(p_dev,' ');
  1.1234 +            if(p_dev)
  1.1235 +              p_dev++;
  1.1236 +        }
  1.1237 +    
  1.1238 +        if(p_dev) {
  1.1239 +            char *p_dev_end = strchr(p_dev,' ');
  1.1240 +            if(p_dev_end) {
  1.1241 +                *p_dev_end = '\0';
  1.1242 +                deviceName.Assign(p_dev);
  1.1243 +                ret = true;
  1.1244 +                break;
  1.1245 +            }
  1.1246 +        }
  1.1247 +    }
  1.1248 +    
  1.1249 +    fclose(f);
  1.1250 +    return ret; 
  1.1251 +}
  1.1252 +#endif
  1.1253 +
  1.1254 +NS_IMETHODIMP
  1.1255 +nsLocalFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable)
  1.1256 +{
  1.1257 +    if (NS_WARN_IF(!aDiskSpaceAvailable))
  1.1258 +        return NS_ERROR_INVALID_ARG;
  1.1259 +
  1.1260 +    // These systems have the operations necessary to check disk space.
  1.1261 +
  1.1262 +#ifdef STATFS
  1.1263 +
  1.1264 +    // check to make sure that mPath is properly initialized
  1.1265 +    CHECK_mPath();
  1.1266 +
  1.1267 +    struct STATFS fs_buf;
  1.1268 +
  1.1269 +    /* 
  1.1270 +     * Members of the STATFS struct that you should know about:
  1.1271 +     * F_BSIZE = block size on disk.
  1.1272 +     * f_bavail = number of free blocks available to a non-superuser.
  1.1273 +     * f_bfree = number of total free blocks in file system.
  1.1274 +     */
  1.1275 +
  1.1276 +    if (STATFS(mPath.get(), &fs_buf) < 0) {
  1.1277 +        // The call to STATFS failed.
  1.1278 +#ifdef DEBUG
  1.1279 +        printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
  1.1280 +#endif
  1.1281 +        return NS_ERROR_FAILURE;
  1.1282 +    }
  1.1283 +
  1.1284 +    *aDiskSpaceAvailable = (int64_t) fs_buf.F_BSIZE * fs_buf.f_bavail;
  1.1285 +
  1.1286 +#ifdef DEBUG_DISK_SPACE
  1.1287 +    printf("DiskSpaceAvailable: %lu bytes\n",
  1.1288 +         *aDiskSpaceAvailable);
  1.1289 +#endif
  1.1290 +
  1.1291 +#if defined(USE_LINUX_QUOTACTL)
  1.1292 +
  1.1293 +    if(!FillStatCache()) {
  1.1294 +        // Return available size from statfs
  1.1295 +        return NS_OK;
  1.1296 +    }
  1.1297 +
  1.1298 +    nsCString deviceName;
  1.1299 +    if(!GetDeviceName(major(mCachedStat.st_dev), minor(mCachedStat.st_dev), deviceName)) {
  1.1300 +        return NS_OK;
  1.1301 +    }
  1.1302 +
  1.1303 +    struct dqblk dq;
  1.1304 +    if(!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(), getuid(), (caddr_t)&dq)
  1.1305 +#ifdef QIF_BLIMITS
  1.1306 +       && dq.dqb_valid & QIF_BLIMITS
  1.1307 +#endif
  1.1308 +       && dq.dqb_bhardlimit)
  1.1309 +    {
  1.1310 +        int64_t QuotaSpaceAvailable = 0;
  1.1311 +        if (dq.dqb_bhardlimit > dq.dqb_curspace)
  1.1312 +            QuotaSpaceAvailable = int64_t(fs_buf.F_BSIZE * (dq.dqb_bhardlimit - dq.dqb_curspace));
  1.1313 +        if(QuotaSpaceAvailable < *aDiskSpaceAvailable) {
  1.1314 +            *aDiskSpaceAvailable = QuotaSpaceAvailable;
  1.1315 +        }
  1.1316 +    }
  1.1317 +#endif
  1.1318 +
  1.1319 +    return NS_OK;
  1.1320 +
  1.1321 +#else
  1.1322 +    /*
  1.1323 +     * This platform doesn't have statfs or statvfs.  I'm sure that there's
  1.1324 +     * a way to check for free disk space on platforms that don't have statfs
  1.1325 +     * (I'm SURE they have df, for example).
  1.1326 +     *
  1.1327 +     * Until we figure out how to do that, lets be honest and say that this
  1.1328 +     * command isn't implemented properly for these platforms yet.
  1.1329 +     */
  1.1330 +#ifdef DEBUG
  1.1331 +    printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
  1.1332 +#endif
  1.1333 +    return NS_ERROR_NOT_IMPLEMENTED;
  1.1334 +
  1.1335 +#endif /* STATFS */
  1.1336 +
  1.1337 +}
  1.1338 +
  1.1339 +NS_IMETHODIMP
  1.1340 +nsLocalFile::GetParent(nsIFile **aParent)
  1.1341 +{
  1.1342 +    CHECK_mPath();
  1.1343 +    if (NS_WARN_IF(!aParent))
  1.1344 +        return NS_ERROR_INVALID_ARG;
  1.1345 +    *aParent = nullptr;
  1.1346 +
  1.1347 +    // if '/' we are at the top of the volume, return null
  1.1348 +    if (mPath.Equals("/"))
  1.1349 +        return  NS_OK;
  1.1350 + 
  1.1351 +    // <brendan, after jband> I promise to play nice
  1.1352 +    char *buffer   = mPath.BeginWriting(),
  1.1353 +         *slashp   = buffer;
  1.1354 +
  1.1355 +    // find the last significant slash in buffer
  1.1356 +    slashp = strrchr(buffer, '/');
  1.1357 +    NS_ASSERTION(slashp, "non-canonical path?");
  1.1358 +    if (!slashp)
  1.1359 +        return NS_ERROR_FILE_INVALID_PATH;
  1.1360 +
  1.1361 +    // for the case where we are at '/'
  1.1362 +    if (slashp == buffer)
  1.1363 +        slashp++;
  1.1364 +
  1.1365 +    // temporarily terminate buffer at the last significant slash
  1.1366 +    char c = *slashp;
  1.1367 +    *slashp = '\0';
  1.1368 +
  1.1369 +    nsCOMPtr<nsIFile> localFile;
  1.1370 +    nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true,
  1.1371 +                                        getter_AddRefs(localFile));
  1.1372 +
  1.1373 +    // make buffer whole again
  1.1374 +    *slashp = c;
  1.1375 +
  1.1376 +    if (NS_FAILED(rv)) {
  1.1377 +        return rv;
  1.1378 +    }
  1.1379 +
  1.1380 +    localFile.forget(aParent);
  1.1381 +    return NS_OK;
  1.1382 +}
  1.1383 +
  1.1384 +/*
  1.1385 + * The results of Exists, isWritable and isReadable are not cached.
  1.1386 + */
  1.1387 +
  1.1388 +
  1.1389 +NS_IMETHODIMP
  1.1390 +nsLocalFile::Exists(bool *_retval)
  1.1391 +{
  1.1392 +    CHECK_mPath();
  1.1393 +    if (NS_WARN_IF(!_retval))
  1.1394 +        return NS_ERROR_INVALID_ARG;
  1.1395 +
  1.1396 +    *_retval = (access(mPath.get(), F_OK) == 0);
  1.1397 +    return NS_OK;
  1.1398 +}
  1.1399 +
  1.1400 +
  1.1401 +NS_IMETHODIMP
  1.1402 +nsLocalFile::IsWritable(bool *_retval)
  1.1403 +{
  1.1404 +    CHECK_mPath();
  1.1405 +    if (NS_WARN_IF(!_retval))
  1.1406 +        return NS_ERROR_INVALID_ARG;
  1.1407 +
  1.1408 +    *_retval = (access(mPath.get(), W_OK) == 0);
  1.1409 +    if (*_retval || errno == EACCES)
  1.1410 +        return NS_OK;
  1.1411 +    return NSRESULT_FOR_ERRNO();
  1.1412 +}
  1.1413 +
  1.1414 +NS_IMETHODIMP
  1.1415 +nsLocalFile::IsReadable(bool *_retval)
  1.1416 +{
  1.1417 +    CHECK_mPath();
  1.1418 +    if (NS_WARN_IF(!_retval))
  1.1419 +        return NS_ERROR_INVALID_ARG;
  1.1420 +
  1.1421 +    *_retval = (access(mPath.get(), R_OK) == 0);
  1.1422 +    if (*_retval || errno == EACCES)
  1.1423 +        return NS_OK;
  1.1424 +    return NSRESULT_FOR_ERRNO();
  1.1425 +}
  1.1426 +
  1.1427 +NS_IMETHODIMP
  1.1428 +nsLocalFile::IsExecutable(bool *_retval)
  1.1429 +{
  1.1430 +    CHECK_mPath();
  1.1431 +    if (NS_WARN_IF(!_retval))
  1.1432 +        return NS_ERROR_INVALID_ARG;
  1.1433 +
  1.1434 +    // Check extension (bug 663899). On certain platforms, the file
  1.1435 +    // extension may cause the OS to treat it as executable regardless of
  1.1436 +    // the execute bit, such as .jar on Mac OS X. We borrow the code from
  1.1437 +    // nsLocalFileWin, slightly modified.
  1.1438 +
  1.1439 +    // Don't be fooled by symlinks.
  1.1440 +    bool symLink;
  1.1441 +    nsresult rv = IsSymlink(&symLink);
  1.1442 +    if (NS_FAILED(rv))
  1.1443 +        return rv;
  1.1444 +
  1.1445 +    nsAutoString path;
  1.1446 +    if (symLink)
  1.1447 +        GetTarget(path);
  1.1448 +    else
  1.1449 +        GetPath(path);
  1.1450 +
  1.1451 +    int32_t dotIdx = path.RFindChar(char16_t('.'));
  1.1452 +    if (dotIdx != kNotFound) {
  1.1453 +        // Convert extension to lower case.
  1.1454 +        char16_t *p = path.BeginWriting();
  1.1455 +        for(p += dotIdx + 1; *p; p++)
  1.1456 +            *p +=  (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0; 
  1.1457 +        
  1.1458 +        // Search for any of the set of executable extensions.
  1.1459 +        static const char * const executableExts[] = {
  1.1460 +            "air",         // Adobe AIR installer
  1.1461 +            "jar"};        // java application bundle
  1.1462 +        nsDependentSubstring ext = Substring(path, dotIdx + 1);
  1.1463 +        for (size_t i = 0; i < ArrayLength(executableExts); i++) {
  1.1464 +            if (ext.EqualsASCII(executableExts[i])) {
  1.1465 +                // Found a match.  Set result and quit.
  1.1466 +                *_retval = true;
  1.1467 +                return NS_OK;
  1.1468 +            }
  1.1469 +        }
  1.1470 +    }
  1.1471 +
  1.1472 +    // On OS X, then query Launch Services.
  1.1473 +#ifdef MOZ_WIDGET_COCOA
  1.1474 +    // Certain Mac applications, such as Classic applications, which
  1.1475 +    // run under Rosetta, might not have the +x mode bit but are still
  1.1476 +    // considered to be executable by Launch Services (bug 646748).
  1.1477 +    CFURLRef url;
  1.1478 +    if (NS_FAILED(GetCFURL(&url))) {
  1.1479 +        return NS_ERROR_FAILURE;
  1.1480 +    }
  1.1481 +
  1.1482 +    LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
  1.1483 +    LSItemInfoRecord theInfo;
  1.1484 +    OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo);
  1.1485 +    ::CFRelease(url);
  1.1486 +    if (result == noErr) {
  1.1487 +        if ((theInfo.flags & kLSItemInfoIsApplication) != 0) {
  1.1488 +            *_retval = true;
  1.1489 +            return NS_OK;
  1.1490 +        }
  1.1491 +    }
  1.1492 +#endif
  1.1493 +
  1.1494 +    // Then check the execute bit.
  1.1495 +    *_retval = (access(mPath.get(), X_OK) == 0);
  1.1496 +#ifdef SOLARIS
  1.1497 +    // On Solaris, access will always return 0 for root user, however
  1.1498 +    // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set.
  1.1499 +    // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950
  1.1500 +    if (*_retval) {
  1.1501 +        struct STAT buf;
  1.1502 +
  1.1503 +        *_retval = (STAT(mPath.get(), &buf) == 0);
  1.1504 +        if (*_retval || errno == EACCES) {
  1.1505 +            *_retval = *_retval &&
  1.1506 +                       (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
  1.1507 +            return NS_OK;
  1.1508 +        }
  1.1509 +
  1.1510 +        return NSRESULT_FOR_ERRNO();
  1.1511 +    }
  1.1512 +#endif
  1.1513 +    if (*_retval || errno == EACCES)
  1.1514 +        return NS_OK;
  1.1515 +    return NSRESULT_FOR_ERRNO();
  1.1516 +}
  1.1517 +
  1.1518 +NS_IMETHODIMP
  1.1519 +nsLocalFile::IsDirectory(bool *_retval)
  1.1520 +{
  1.1521 +    if (NS_WARN_IF(!_retval))
  1.1522 +        return NS_ERROR_INVALID_ARG;
  1.1523 +    *_retval = false;
  1.1524 +    ENSURE_STAT_CACHE();
  1.1525 +    *_retval = S_ISDIR(mCachedStat.st_mode);
  1.1526 +    return NS_OK;
  1.1527 +}
  1.1528 +
  1.1529 +NS_IMETHODIMP
  1.1530 +nsLocalFile::IsFile(bool *_retval)
  1.1531 +{
  1.1532 +    if (NS_WARN_IF(!_retval))
  1.1533 +        return NS_ERROR_INVALID_ARG;
  1.1534 +    *_retval = false;
  1.1535 +    ENSURE_STAT_CACHE();
  1.1536 +    *_retval = S_ISREG(mCachedStat.st_mode);
  1.1537 +    return NS_OK;
  1.1538 +}
  1.1539 +
  1.1540 +NS_IMETHODIMP
  1.1541 +nsLocalFile::IsHidden(bool *_retval)
  1.1542 +{
  1.1543 +    if (NS_WARN_IF(!_retval))
  1.1544 +        return NS_ERROR_INVALID_ARG;
  1.1545 +    nsACString::const_iterator begin, end;
  1.1546 +    LocateNativeLeafName(begin, end);
  1.1547 +    *_retval = (*begin == '.');
  1.1548 +    return NS_OK;
  1.1549 +}
  1.1550 +
  1.1551 +NS_IMETHODIMP
  1.1552 +nsLocalFile::IsSymlink(bool *_retval)
  1.1553 +{
  1.1554 +    if (NS_WARN_IF(!_retval))
  1.1555 +        return NS_ERROR_INVALID_ARG;
  1.1556 +    CHECK_mPath();
  1.1557 +
  1.1558 +    struct STAT symStat;
  1.1559 +    if (LSTAT(mPath.get(), &symStat) == -1)
  1.1560 +        return NSRESULT_FOR_ERRNO();
  1.1561 +    *_retval=S_ISLNK(symStat.st_mode);
  1.1562 +    return NS_OK;
  1.1563 +}
  1.1564 +
  1.1565 +NS_IMETHODIMP
  1.1566 +nsLocalFile::IsSpecial(bool *_retval)
  1.1567 +{
  1.1568 +    if (NS_WARN_IF(!_retval))
  1.1569 +        return NS_ERROR_INVALID_ARG;
  1.1570 +    ENSURE_STAT_CACHE();
  1.1571 +    *_retval = S_ISCHR(mCachedStat.st_mode)      ||
  1.1572 +                 S_ISBLK(mCachedStat.st_mode)    ||
  1.1573 +#ifdef S_ISSOCK
  1.1574 +                 S_ISSOCK(mCachedStat.st_mode)   ||
  1.1575 +#endif
  1.1576 +                 S_ISFIFO(mCachedStat.st_mode);
  1.1577 +
  1.1578 +    return NS_OK;
  1.1579 +}
  1.1580 +
  1.1581 +NS_IMETHODIMP
  1.1582 +nsLocalFile::Equals(nsIFile *inFile, bool *_retval)
  1.1583 +{
  1.1584 +    if (NS_WARN_IF(!inFile))
  1.1585 +        return NS_ERROR_INVALID_ARG;
  1.1586 +    if (NS_WARN_IF(!_retval))
  1.1587 +        return NS_ERROR_INVALID_ARG;
  1.1588 +    *_retval = false;
  1.1589 +
  1.1590 +    nsAutoCString inPath;
  1.1591 +    nsresult rv = inFile->GetNativePath(inPath);
  1.1592 +    if (NS_FAILED(rv))
  1.1593 +        return rv;
  1.1594 +
  1.1595 +    // We don't need to worry about "/foo/" vs. "/foo" here
  1.1596 +    // because trailing slashes are stripped on init.
  1.1597 +    *_retval = !strcmp(inPath.get(), mPath.get());
  1.1598 +    return NS_OK;
  1.1599 +}
  1.1600 +
  1.1601 +NS_IMETHODIMP
  1.1602 +nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval)
  1.1603 +{
  1.1604 +    CHECK_mPath();
  1.1605 +    if (NS_WARN_IF(!inFile))
  1.1606 +        return NS_ERROR_INVALID_ARG;
  1.1607 +    if (NS_WARN_IF(!_retval))
  1.1608 +        return NS_ERROR_INVALID_ARG;
  1.1609 +
  1.1610 +    nsAutoCString inPath;
  1.1611 +    nsresult rv;
  1.1612 +
  1.1613 +    if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
  1.1614 +        return rv;
  1.1615 +
  1.1616 +    *_retval = false;
  1.1617 +
  1.1618 +    ssize_t len = mPath.Length();
  1.1619 +    if (strncmp(mPath.get(), inPath.get(), len) == 0) {
  1.1620 +        // Now make sure that the |inFile|'s path has a separator at len,
  1.1621 +        // which implies that it has more components after len.
  1.1622 +        if (inPath[len] == '/')
  1.1623 +            *_retval = true;
  1.1624 +    }
  1.1625 +
  1.1626 +    return NS_OK;
  1.1627 +}
  1.1628 +
  1.1629 +NS_IMETHODIMP
  1.1630 +nsLocalFile::GetNativeTarget(nsACString &_retval)
  1.1631 +{
  1.1632 +    CHECK_mPath();
  1.1633 +    _retval.Truncate();
  1.1634 +
  1.1635 +    struct STAT symStat;
  1.1636 +    if (LSTAT(mPath.get(), &symStat) == -1)
  1.1637 +        return NSRESULT_FOR_ERRNO();
  1.1638 +
  1.1639 +    if (!S_ISLNK(symStat.st_mode))
  1.1640 +        return NS_ERROR_FILE_INVALID_PATH;
  1.1641 +
  1.1642 +    int32_t size = (int32_t)symStat.st_size;
  1.1643 +    char *target = (char *)nsMemory::Alloc(size + 1);
  1.1644 +    if (!target)
  1.1645 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1646 +
  1.1647 +    if (readlink(mPath.get(), target, (size_t)size) < 0) {
  1.1648 +        nsMemory::Free(target);
  1.1649 +        return NSRESULT_FOR_ERRNO();
  1.1650 +    }
  1.1651 +    target[size] = '\0';
  1.1652 +
  1.1653 +    nsresult rv = NS_OK;
  1.1654 +    nsCOMPtr<nsIFile> self(this);
  1.1655 +    int32_t maxLinks = 40;
  1.1656 +    while (true) {
  1.1657 +        if (maxLinks-- == 0) {
  1.1658 +            rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
  1.1659 +            break;
  1.1660 +        }
  1.1661 +
  1.1662 +        if (target[0] != '/') {
  1.1663 +            nsCOMPtr<nsIFile> parent;
  1.1664 +            if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent))))
  1.1665 +                break;
  1.1666 +            if (NS_FAILED(rv = parent->AppendRelativeNativePath(nsDependentCString(target))))
  1.1667 +                break;
  1.1668 +            if (NS_FAILED(rv = parent->GetNativePath(_retval)))
  1.1669 +                break;
  1.1670 +            self = parent;
  1.1671 +        } else {
  1.1672 +            _retval = target;
  1.1673 +        }
  1.1674 +
  1.1675 +        const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
  1.1676 +
  1.1677 +        // Any failure in testing the current target we'll just interpret
  1.1678 +        // as having reached our destiny.
  1.1679 +        if (LSTAT(flatRetval.get(), &symStat) == -1)
  1.1680 +            break;
  1.1681 +
  1.1682 +        // And of course we're done if it isn't a symlink.
  1.1683 +        if (!S_ISLNK(symStat.st_mode))
  1.1684 +            break;
  1.1685 +
  1.1686 +        int32_t newSize = (int32_t)symStat.st_size;
  1.1687 +        if (newSize > size) {
  1.1688 +            char *newTarget = (char *)nsMemory::Realloc(target, newSize + 1);
  1.1689 +            if (!newTarget) {
  1.1690 +                rv = NS_ERROR_OUT_OF_MEMORY;
  1.1691 +                break;
  1.1692 +            }
  1.1693 +            target = newTarget;
  1.1694 +            size = newSize;
  1.1695 +        }
  1.1696 +
  1.1697 +        int32_t linkLen = readlink(flatRetval.get(), target, size);
  1.1698 +        if (linkLen == -1) {
  1.1699 +            rv = NSRESULT_FOR_ERRNO();
  1.1700 +            break;
  1.1701 +        }
  1.1702 +        target[linkLen] = '\0';
  1.1703 +    }
  1.1704 +
  1.1705 +    nsMemory::Free(target);
  1.1706 +
  1.1707 +    if (NS_FAILED(rv))
  1.1708 +        _retval.Truncate();
  1.1709 +    return rv;
  1.1710 +}
  1.1711 +
  1.1712 +NS_IMETHODIMP
  1.1713 +nsLocalFile::GetFollowLinks(bool *aFollowLinks)
  1.1714 +{
  1.1715 +    *aFollowLinks = true;
  1.1716 +    return NS_OK;
  1.1717 +}
  1.1718 +
  1.1719 +NS_IMETHODIMP
  1.1720 +nsLocalFile::SetFollowLinks(bool aFollowLinks)
  1.1721 +{
  1.1722 +    return NS_OK;
  1.1723 +}
  1.1724 +
  1.1725 +NS_IMETHODIMP
  1.1726 +nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
  1.1727 +{
  1.1728 +    nsRefPtr<nsDirEnumeratorUnix> dir = new nsDirEnumeratorUnix();
  1.1729 +
  1.1730 +    nsresult rv = dir->Init(this, false);
  1.1731 +    if (NS_FAILED(rv)) {
  1.1732 +        *entries = nullptr;
  1.1733 +    } else {
  1.1734 +        dir.forget(entries);
  1.1735 +    }
  1.1736 +
  1.1737 +    return rv;
  1.1738 +}
  1.1739 +
  1.1740 +NS_IMETHODIMP
  1.1741 +nsLocalFile::Load(PRLibrary **_retval)
  1.1742 +{
  1.1743 +    CHECK_mPath();
  1.1744 +    if (NS_WARN_IF(!_retval))
  1.1745 +        return NS_ERROR_INVALID_ARG;
  1.1746 +
  1.1747 +#ifdef NS_BUILD_REFCNT_LOGGING
  1.1748 +    nsTraceRefcnt::SetActivityIsLegal(false);
  1.1749 +#endif
  1.1750 +
  1.1751 +    *_retval = PR_LoadLibrary(mPath.get());
  1.1752 +
  1.1753 +#ifdef NS_BUILD_REFCNT_LOGGING
  1.1754 +    nsTraceRefcnt::SetActivityIsLegal(true);
  1.1755 +#endif
  1.1756 +
  1.1757 +    if (!*_retval)
  1.1758 +        return NS_ERROR_FAILURE;
  1.1759 +    return NS_OK;
  1.1760 +}
  1.1761 +
  1.1762 +NS_IMETHODIMP
  1.1763 +nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
  1.1764 +{
  1.1765 +    return GetNativePath(aPersistentDescriptor);
  1.1766 +}
  1.1767 +
  1.1768 +NS_IMETHODIMP
  1.1769 +nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
  1.1770 +{
  1.1771 +#ifdef MOZ_WIDGET_COCOA
  1.1772 +    if (aPersistentDescriptor.IsEmpty())
  1.1773 +        return NS_ERROR_INVALID_ARG;
  1.1774 +
  1.1775 +    // Support pathnames as user-supplied descriptors if they begin with '/'
  1.1776 +    // or '~'.  These characters do not collide with the base64 set used for
  1.1777 +    // encoding alias records.
  1.1778 +    char first = aPersistentDescriptor.First();
  1.1779 +    if (first == '/' || first == '~')
  1.1780 +        return InitWithNativePath(aPersistentDescriptor);
  1.1781 +
  1.1782 +    uint32_t dataSize = aPersistentDescriptor.Length();    
  1.1783 +    char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nullptr);
  1.1784 +    if (!decodedData) {
  1.1785 +        NS_ERROR("SetPersistentDescriptor was given bad data");
  1.1786 +        return NS_ERROR_FAILURE;
  1.1787 +    }
  1.1788 +
  1.1789 +    // Cast to an alias record and resolve.
  1.1790 +    AliasRecord aliasHeader = *(AliasPtr)decodedData;
  1.1791 +    int32_t aliasSize = ::GetAliasSizeFromPtr(&aliasHeader);
  1.1792 +    if (aliasSize > ((int32_t)dataSize * 3) / 4) { // be paranoid about having too few data
  1.1793 +        PR_Free(decodedData);
  1.1794 +        return NS_ERROR_FAILURE;
  1.1795 +    }
  1.1796 +
  1.1797 +    nsresult rv = NS_OK;
  1.1798 +
  1.1799 +    // Move the now-decoded data into the Handle.
  1.1800 +    // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
  1.1801 +    Handle  newHandle = nullptr;
  1.1802 +    if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr)
  1.1803 +        rv = NS_ERROR_OUT_OF_MEMORY;
  1.1804 +    PR_Free(decodedData);
  1.1805 +    if (NS_FAILED(rv))
  1.1806 +        return rv;
  1.1807 +
  1.1808 +    Boolean changed;
  1.1809 +    FSRef resolvedFSRef;
  1.1810 +    OSErr err = ::FSResolveAlias(nullptr, (AliasHandle)newHandle, &resolvedFSRef, &changed);
  1.1811 +
  1.1812 +    rv = MacErrorMapper(err);
  1.1813 +    DisposeHandle(newHandle);
  1.1814 +    if (NS_FAILED(rv))
  1.1815 +        return rv;
  1.1816 +
  1.1817 +    return InitWithFSRef(&resolvedFSRef);  
  1.1818 +#else
  1.1819 +    return InitWithNativePath(aPersistentDescriptor);
  1.1820 +#endif
  1.1821 +}
  1.1822 +
  1.1823 +NS_IMETHODIMP
  1.1824 +nsLocalFile::Reveal()
  1.1825 +{
  1.1826 +#ifdef MOZ_WIDGET_GTK
  1.1827 +    nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
  1.1828 +    nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
  1.1829 +    if (!giovfs && !gnomevfs)
  1.1830 +        return NS_ERROR_FAILURE;
  1.1831 +
  1.1832 +    bool isDirectory;
  1.1833 +    if (NS_FAILED(IsDirectory(&isDirectory)))
  1.1834 +        return NS_ERROR_FAILURE;
  1.1835 +
  1.1836 +    if (isDirectory) {
  1.1837 +        if (giovfs)
  1.1838 +            return giovfs->ShowURIForInput(mPath);
  1.1839 +        else 
  1.1840 +            /* Fallback to GnomeVFS */
  1.1841 +            return gnomevfs->ShowURIForInput(mPath);
  1.1842 +    } else if (giovfs && NS_SUCCEEDED(giovfs->OrgFreedesktopFileManager1ShowItems(mPath))) {
  1.1843 +        return NS_OK;
  1.1844 +    } else {
  1.1845 +        nsCOMPtr<nsIFile> parentDir;
  1.1846 +        nsAutoCString dirPath;
  1.1847 +        if (NS_FAILED(GetParent(getter_AddRefs(parentDir))))
  1.1848 +            return NS_ERROR_FAILURE;
  1.1849 +        if (NS_FAILED(parentDir->GetNativePath(dirPath)))
  1.1850 +            return NS_ERROR_FAILURE;
  1.1851 +
  1.1852 +        if (giovfs)
  1.1853 +            return giovfs->ShowURIForInput(dirPath);
  1.1854 +        else 
  1.1855 +            return gnomevfs->ShowURIForInput(dirPath);        
  1.1856 +    }
  1.1857 +#elif defined(MOZ_WIDGET_COCOA)
  1.1858 +    CFURLRef url;
  1.1859 +    if (NS_SUCCEEDED(GetCFURL(&url))) {
  1.1860 +      nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
  1.1861 +      ::CFRelease(url);
  1.1862 +      return rv;
  1.1863 +    }
  1.1864 +    return NS_ERROR_FAILURE;
  1.1865 +#else
  1.1866 +    return NS_ERROR_FAILURE;
  1.1867 +#endif
  1.1868 +}
  1.1869 +
  1.1870 +NS_IMETHODIMP
  1.1871 +nsLocalFile::Launch()
  1.1872 +{
  1.1873 +#ifdef MOZ_WIDGET_GTK
  1.1874 +    nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
  1.1875 +    nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
  1.1876 +    if (giovfs) {
  1.1877 +      return giovfs->ShowURIForInput(mPath);
  1.1878 +    } else if (gnomevfs) {
  1.1879 +      /* GnomeVFS fallback */
  1.1880 +      return gnomevfs->ShowURIForInput(mPath);
  1.1881 +    }
  1.1882 +    
  1.1883 +    return NS_ERROR_FAILURE;
  1.1884 +#elif defined(MOZ_ENABLE_CONTENTACTION)
  1.1885 +    QUrl uri = QUrl::fromLocalFile(QString::fromUtf8(mPath.get()));
  1.1886 +    ContentAction::Action action =
  1.1887 +        ContentAction::Action::defaultActionForFile(uri);
  1.1888 +
  1.1889 +    if (action.isValid()) {
  1.1890 +      action.trigger();
  1.1891 +      return NS_OK;
  1.1892 +    }
  1.1893 +
  1.1894 +    return NS_ERROR_FAILURE;
  1.1895 +#elif defined(MOZ_WIDGET_ANDROID)
  1.1896 +    // Try to get a mimetype, if this fails just use the file uri alone
  1.1897 +    nsresult rv;
  1.1898 +    nsAutoCString type;
  1.1899 +    nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv));
  1.1900 +    if (NS_SUCCEEDED(rv))
  1.1901 +        rv = mimeService->GetTypeFromFile(this, type);
  1.1902 +
  1.1903 +    nsAutoCString fileUri = NS_LITERAL_CSTRING("file://") + mPath;
  1.1904 +    return mozilla::widget::android::GeckoAppShell::OpenUriExternal(NS_ConvertUTF8toUTF16(fileUri),
  1.1905 +      NS_ConvertUTF8toUTF16(type)) ? NS_OK : NS_ERROR_FAILURE;
  1.1906 +#elif defined(MOZ_WIDGET_COCOA)
  1.1907 +    CFURLRef url;
  1.1908 +    if (NS_SUCCEEDED(GetCFURL(&url))) {
  1.1909 +        nsresult rv = CocoaFileUtils::OpenURL(url);
  1.1910 +        ::CFRelease(url);
  1.1911 +        return rv;
  1.1912 +    }
  1.1913 +    return NS_ERROR_FAILURE;
  1.1914 +#else
  1.1915 +    return NS_ERROR_FAILURE;
  1.1916 +#endif
  1.1917 +}
  1.1918 +
  1.1919 +nsresult
  1.1920 +NS_NewNativeLocalFile(const nsACString &path, bool followSymlinks, nsIFile **result)
  1.1921 +{
  1.1922 +    nsRefPtr<nsLocalFile> file = new nsLocalFile();
  1.1923 +
  1.1924 +    file->SetFollowLinks(followSymlinks);
  1.1925 +
  1.1926 +    if (!path.IsEmpty()) {
  1.1927 +        nsresult rv = file->InitWithNativePath(path);
  1.1928 +        if (NS_FAILED(rv)) {
  1.1929 +            return rv;
  1.1930 +        }
  1.1931 +    }
  1.1932 +    file.forget(result);
  1.1933 +    return NS_OK;
  1.1934 +}
  1.1935 +
  1.1936 +//-----------------------------------------------------------------------------
  1.1937 +// unicode support
  1.1938 +//-----------------------------------------------------------------------------
  1.1939 +
  1.1940 +#define SET_UCS(func, ucsArg) \
  1.1941 +    { \
  1.1942 +        nsAutoCString buf; \
  1.1943 +        nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
  1.1944 +        if (NS_FAILED(rv)) \
  1.1945 +            return rv; \
  1.1946 +        return (func)(buf); \
  1.1947 +    }
  1.1948 +
  1.1949 +#define GET_UCS(func, ucsArg) \
  1.1950 +    { \
  1.1951 +        nsAutoCString buf; \
  1.1952 +        nsresult rv = (func)(buf); \
  1.1953 +        if (NS_FAILED(rv)) return rv; \
  1.1954 +        return NS_CopyNativeToUnicode(buf, ucsArg); \
  1.1955 +    }
  1.1956 +
  1.1957 +#define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
  1.1958 +    { \
  1.1959 +        nsAutoCString buf; \
  1.1960 +        nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
  1.1961 +        if (NS_FAILED(rv)) \
  1.1962 +            return rv; \
  1.1963 +        return (func)(opaqueArg, buf); \
  1.1964 +    }
  1.1965 +
  1.1966 +// Unicode interface Wrapper
  1.1967 +nsresult  
  1.1968 +nsLocalFile::InitWithPath(const nsAString &filePath)
  1.1969 +{
  1.1970 +    SET_UCS(InitWithNativePath, filePath);
  1.1971 +}
  1.1972 +nsresult  
  1.1973 +nsLocalFile::Append(const nsAString &node)
  1.1974 +{
  1.1975 +    SET_UCS(AppendNative, node);
  1.1976 +}
  1.1977 +nsresult  
  1.1978 +nsLocalFile::AppendRelativePath(const nsAString &node)
  1.1979 +{
  1.1980 +    SET_UCS(AppendRelativeNativePath, node);
  1.1981 +}
  1.1982 +nsresult  
  1.1983 +nsLocalFile::GetLeafName(nsAString &aLeafName)
  1.1984 +{
  1.1985 +    GET_UCS(GetNativeLeafName, aLeafName);
  1.1986 +}
  1.1987 +nsresult  
  1.1988 +nsLocalFile::SetLeafName(const nsAString &aLeafName)
  1.1989 +{
  1.1990 +    SET_UCS(SetNativeLeafName, aLeafName);
  1.1991 +}
  1.1992 +nsresult  
  1.1993 +nsLocalFile::GetPath(nsAString &_retval)
  1.1994 +{
  1.1995 +    return NS_CopyNativeToUnicode(mPath, _retval);
  1.1996 +}
  1.1997 +nsresult  
  1.1998 +nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
  1.1999 +{
  1.2000 +    SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
  1.2001 +}
  1.2002 +nsresult  
  1.2003 +nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
  1.2004 +{
  1.2005 +    SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
  1.2006 +}
  1.2007 +nsresult  
  1.2008 +nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
  1.2009 +{
  1.2010 +    SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
  1.2011 +}
  1.2012 +
  1.2013 +NS_IMETHODIMP
  1.2014 +nsLocalFile::RenameTo(nsIFile *newParentDir, const nsAString &newName)
  1.2015 +{
  1.2016 +  nsresult rv;
  1.2017 +
  1.2018 +  // check to make sure that this has been initialized properly
  1.2019 +  CHECK_mPath();
  1.2020 +
  1.2021 +  // check to make sure that we have a new parent
  1.2022 +  nsAutoCString newPathName;
  1.2023 +  nsAutoCString newNativeName;
  1.2024 +  rv = NS_CopyUnicodeToNative(newName, newNativeName);
  1.2025 +  if (NS_FAILED(rv)) {
  1.2026 +    return rv;
  1.2027 +  }
  1.2028 +  rv = GetNativeTargetPathName(newParentDir, newNativeName, newPathName);
  1.2029 +  if (NS_FAILED(rv)) {
  1.2030 +    return rv;
  1.2031 +  }
  1.2032 +
  1.2033 +  // try for atomic rename
  1.2034 +  if (rename(mPath.get(), newPathName.get()) < 0) {
  1.2035 +#ifdef VMS
  1.2036 +    if (errno == EXDEV || errno == ENXIO) {
  1.2037 +#else
  1.2038 +    if (errno == EXDEV) {
  1.2039 +#endif
  1.2040 +      rv = NS_ERROR_FILE_ACCESS_DENIED;
  1.2041 +    } else {
  1.2042 +      rv = NSRESULT_FOR_ERRNO();
  1.2043 +    }
  1.2044 +  }
  1.2045 +
  1.2046 +  return rv;
  1.2047 +}
  1.2048 +
  1.2049 +nsresult
  1.2050 +nsLocalFile::GetTarget(nsAString &_retval)
  1.2051 +{   
  1.2052 +    GET_UCS(GetNativeTarget, _retval);
  1.2053 +}
  1.2054 +
  1.2055 +// nsIHashable
  1.2056 +
  1.2057 +NS_IMETHODIMP
  1.2058 +nsLocalFile::Equals(nsIHashable* aOther, bool *aResult)
  1.2059 +{
  1.2060 +    nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
  1.2061 +    if (!otherFile) {
  1.2062 +        *aResult = false;
  1.2063 +        return NS_OK;
  1.2064 +    }
  1.2065 +
  1.2066 +    return Equals(otherFile, aResult);
  1.2067 +}
  1.2068 +
  1.2069 +NS_IMETHODIMP
  1.2070 +nsLocalFile::GetHashCode(uint32_t *aResult)
  1.2071 +{
  1.2072 +    *aResult = HashString(mPath);
  1.2073 +    return NS_OK;
  1.2074 +}
  1.2075 +
  1.2076 +nsresult 
  1.2077 +NS_NewLocalFile(const nsAString &path, bool followLinks, nsIFile* *result)
  1.2078 +{
  1.2079 +    nsAutoCString buf;
  1.2080 +    nsresult rv = NS_CopyUnicodeToNative(path, buf);
  1.2081 +    if (NS_FAILED(rv))
  1.2082 +        return rv;
  1.2083 +    return NS_NewNativeLocalFile(buf, followLinks, result);
  1.2084 +}
  1.2085 +
  1.2086 +//-----------------------------------------------------------------------------
  1.2087 +// global init/shutdown
  1.2088 +//-----------------------------------------------------------------------------
  1.2089 +
  1.2090 +void
  1.2091 +nsLocalFile::GlobalInit()
  1.2092 +{
  1.2093 +}
  1.2094 +
  1.2095 +void
  1.2096 +nsLocalFile::GlobalShutdown()
  1.2097 +{
  1.2098 +}
  1.2099 +
  1.2100 +// nsILocalFileMac
  1.2101 +
  1.2102 +#ifdef MOZ_WIDGET_COCOA
  1.2103 +
  1.2104 +static nsresult MacErrorMapper(OSErr inErr)
  1.2105 +{
  1.2106 +  nsresult outErr;
  1.2107 +
  1.2108 +  switch (inErr)
  1.2109 +  {
  1.2110 +    case noErr:
  1.2111 +      outErr = NS_OK;
  1.2112 +      break;
  1.2113 +
  1.2114 +    case fnfErr:
  1.2115 +    case afpObjectNotFound:
  1.2116 +    case afpDirNotFound:
  1.2117 +      outErr = NS_ERROR_FILE_NOT_FOUND;
  1.2118 +      break;
  1.2119 +
  1.2120 +    case dupFNErr:
  1.2121 +    case afpObjectExists:
  1.2122 +      outErr = NS_ERROR_FILE_ALREADY_EXISTS;
  1.2123 +      break;
  1.2124 +
  1.2125 +    case dskFulErr:
  1.2126 +    case afpDiskFull:
  1.2127 +      outErr = NS_ERROR_FILE_DISK_FULL;
  1.2128 +      break;
  1.2129 +
  1.2130 +    case fLckdErr:
  1.2131 +    case afpVolLocked:
  1.2132 +      outErr = NS_ERROR_FILE_IS_LOCKED;
  1.2133 +      break;
  1.2134 +
  1.2135 +    case afpAccessDenied:
  1.2136 +      outErr = NS_ERROR_FILE_ACCESS_DENIED;
  1.2137 +      break;
  1.2138 +
  1.2139 +    case afpDirNotEmpty:
  1.2140 +      outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
  1.2141 +      break;
  1.2142 +
  1.2143 +    // Can't find good map for some
  1.2144 +    case bdNamErr:
  1.2145 +      outErr = NS_ERROR_FAILURE;
  1.2146 +      break;
  1.2147 +
  1.2148 +    default:
  1.2149 +      outErr = NS_ERROR_FAILURE;
  1.2150 +      break;
  1.2151 +  }
  1.2152 +
  1.2153 +  return outErr;
  1.2154 +}
  1.2155 +
  1.2156 +static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
  1.2157 +{
  1.2158 +  // first see if the conversion would succeed and find the length of the result
  1.2159 +  CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
  1.2160 +  CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
  1.2161 +                                              kCFStringEncodingUTF8, 0, false,
  1.2162 +                                              nullptr, 0, &usedBufLen);
  1.2163 +  if (charsConverted == inStrLen) {
  1.2164 +    // all characters converted, do the actual conversion
  1.2165 +    aOutStr.SetLength(usedBufLen);
  1.2166 +    if (aOutStr.Length() != (unsigned int)usedBufLen)
  1.2167 +      return NS_ERROR_OUT_OF_MEMORY;
  1.2168 +    UInt8 *buffer = (UInt8*)aOutStr.BeginWriting();
  1.2169 +    ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8,
  1.2170 +                       0, false, buffer, usedBufLen, &usedBufLen);
  1.2171 +    return NS_OK;
  1.2172 +  }
  1.2173 +
  1.2174 +  return NS_ERROR_FAILURE;
  1.2175 +}
  1.2176 +
  1.2177 +NS_IMETHODIMP
  1.2178 +nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
  1.2179 +{
  1.2180 +  UInt8 path[PATH_MAX];
  1.2181 +  if (::CFURLGetFileSystemRepresentation(aCFURL, false, path, PATH_MAX)) {
  1.2182 +    nsDependentCString nativePath((char*)path);
  1.2183 +    return InitWithNativePath(nativePath);
  1.2184 +  }
  1.2185 +
  1.2186 +  return NS_ERROR_FAILURE;
  1.2187 +}
  1.2188 +
  1.2189 +NS_IMETHODIMP
  1.2190 +nsLocalFile::InitWithFSRef(const FSRef *aFSRef)
  1.2191 +{
  1.2192 +  if (NS_WARN_IF(!aFSRef))
  1.2193 +      return NS_ERROR_INVALID_ARG;
  1.2194 +
  1.2195 +  CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
  1.2196 +  if (newURLRef) {
  1.2197 +    nsresult rv = InitWithCFURL(newURLRef);
  1.2198 +    ::CFRelease(newURLRef);
  1.2199 +    return rv;
  1.2200 +  }
  1.2201 +
  1.2202 +  return NS_ERROR_FAILURE;
  1.2203 +}
  1.2204 +
  1.2205 +NS_IMETHODIMP
  1.2206 +nsLocalFile::GetCFURL(CFURLRef *_retval)
  1.2207 +{
  1.2208 +  CHECK_mPath();
  1.2209 +
  1.2210 +  bool isDir;
  1.2211 +  IsDirectory(&isDir);
  1.2212 +  *_retval = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
  1.2213 +                                                       (UInt8*)mPath.get(),
  1.2214 +                                                       mPath.Length(),
  1.2215 +                                                       isDir);
  1.2216 +
  1.2217 +  return (*_retval ? NS_OK : NS_ERROR_FAILURE);
  1.2218 +}
  1.2219 +
  1.2220 +NS_IMETHODIMP
  1.2221 +nsLocalFile::GetFSRef(FSRef *_retval)
  1.2222 +{
  1.2223 +  if (NS_WARN_IF(!_retval))
  1.2224 +      return NS_ERROR_INVALID_ARG;
  1.2225 +
  1.2226 +  nsresult rv = NS_ERROR_FAILURE;
  1.2227 +
  1.2228 +  CFURLRef url = nullptr;
  1.2229 +  if (NS_SUCCEEDED(GetCFURL(&url))) {
  1.2230 +    if (::CFURLGetFSRef(url, _retval)) {
  1.2231 +      rv = NS_OK;
  1.2232 +    }
  1.2233 +    ::CFRelease(url);
  1.2234 +  }
  1.2235 +
  1.2236 +  return rv;
  1.2237 +}
  1.2238 +
  1.2239 +NS_IMETHODIMP
  1.2240 +nsLocalFile::GetFSSpec(FSSpec *_retval)
  1.2241 +{
  1.2242 +  if (NS_WARN_IF(!_retval))
  1.2243 +      return NS_ERROR_INVALID_ARG;
  1.2244 +
  1.2245 +  FSRef fsRef;
  1.2246 +  nsresult rv = GetFSRef(&fsRef);
  1.2247 +  if (NS_SUCCEEDED(rv)) {
  1.2248 +    OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nullptr, nullptr, _retval, nullptr);
  1.2249 +    return MacErrorMapper(err);
  1.2250 +  }
  1.2251 +
  1.2252 +  return rv;
  1.2253 +}
  1.2254 +
  1.2255 +NS_IMETHODIMP
  1.2256 +nsLocalFile::GetFileSizeWithResFork(int64_t *aFileSizeWithResFork)
  1.2257 +{
  1.2258 +  if (NS_WARN_IF(!aFileSizeWithResFork))
  1.2259 +      return NS_ERROR_INVALID_ARG;
  1.2260 +
  1.2261 +  FSRef fsRef;
  1.2262 +  nsresult rv = GetFSRef(&fsRef);
  1.2263 +  if (NS_FAILED(rv))
  1.2264 +    return rv;
  1.2265 +
  1.2266 +  FSCatalogInfo catalogInfo;
  1.2267 +  OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
  1.2268 +                                 &catalogInfo, nullptr, nullptr, nullptr);
  1.2269 +  if (err != noErr)
  1.2270 +    return MacErrorMapper(err);
  1.2271 +
  1.2272 +  *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
  1.2273 +  return NS_OK;
  1.2274 +}
  1.2275 +
  1.2276 +NS_IMETHODIMP
  1.2277 +nsLocalFile::GetFileType(OSType *aFileType)
  1.2278 +{
  1.2279 +  CFURLRef url;
  1.2280 +  if (NS_SUCCEEDED(GetCFURL(&url))) {
  1.2281 +    nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType);
  1.2282 +    ::CFRelease(url);
  1.2283 +    return rv;
  1.2284 +  }
  1.2285 +  return NS_ERROR_FAILURE;
  1.2286 +}
  1.2287 +  
  1.2288 +NS_IMETHODIMP
  1.2289 +nsLocalFile::SetFileType(OSType aFileType)
  1.2290 +{
  1.2291 +  CFURLRef url;
  1.2292 +  if (NS_SUCCEEDED(GetCFURL(&url))) {
  1.2293 +    nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType);
  1.2294 +    ::CFRelease(url);
  1.2295 +    return rv;
  1.2296 +  }
  1.2297 +  return NS_ERROR_FAILURE;
  1.2298 +}
  1.2299 +  
  1.2300 +NS_IMETHODIMP
  1.2301 +nsLocalFile::GetFileCreator(OSType *aFileCreator)
  1.2302 +{
  1.2303 +  CFURLRef url;
  1.2304 +  if (NS_SUCCEEDED(GetCFURL(&url))) {
  1.2305 +    nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator);
  1.2306 +    ::CFRelease(url);
  1.2307 +    return rv;
  1.2308 +  }
  1.2309 +  return NS_ERROR_FAILURE;
  1.2310 +}
  1.2311 +
  1.2312 +NS_IMETHODIMP
  1.2313 +nsLocalFile::SetFileCreator(OSType aFileCreator)
  1.2314 +{
  1.2315 +  CFURLRef url;
  1.2316 +  if (NS_SUCCEEDED(GetCFURL(&url))) {
  1.2317 +    nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator);
  1.2318 +    ::CFRelease(url);
  1.2319 +    return rv;
  1.2320 +  }
  1.2321 +  return NS_ERROR_FAILURE;
  1.2322 +}
  1.2323 +  
  1.2324 +NS_IMETHODIMP
  1.2325 +nsLocalFile::LaunchWithDoc(nsIFile *aDocToLoad, bool aLaunchInBackground)
  1.2326 +{    
  1.2327 +  bool isExecutable;
  1.2328 +  nsresult rv = IsExecutable(&isExecutable);
  1.2329 +  if (NS_FAILED(rv))
  1.2330 +    return rv;
  1.2331 +  if (!isExecutable)
  1.2332 +    return NS_ERROR_FILE_EXECUTION_FAILED;
  1.2333 +
  1.2334 +  FSRef appFSRef, docFSRef;
  1.2335 +  rv = GetFSRef(&appFSRef);
  1.2336 +  if (NS_FAILED(rv))
  1.2337 +    return rv;
  1.2338 +
  1.2339 +  if (aDocToLoad) {
  1.2340 +    nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
  1.2341 +    rv = macDoc->GetFSRef(&docFSRef);
  1.2342 +    if (NS_FAILED(rv))
  1.2343 +      return rv;
  1.2344 +  }
  1.2345 +
  1.2346 +  LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
  1.2347 +  LSLaunchFSRefSpec thelaunchSpec;
  1.2348 +
  1.2349 +  if (aLaunchInBackground)
  1.2350 +    theLaunchFlags |= kLSLaunchDontSwitch;
  1.2351 +  memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
  1.2352 +
  1.2353 +  thelaunchSpec.appRef = &appFSRef;
  1.2354 +  if (aDocToLoad) {
  1.2355 +    thelaunchSpec.numDocs = 1;
  1.2356 +    thelaunchSpec.itemRefs = &docFSRef;
  1.2357 +  }
  1.2358 +  thelaunchSpec.launchFlags = theLaunchFlags;
  1.2359 +
  1.2360 +  OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
  1.2361 +  if (err != noErr)
  1.2362 +    return MacErrorMapper(err);
  1.2363 +
  1.2364 +  return NS_OK;
  1.2365 +}
  1.2366 +
  1.2367 +NS_IMETHODIMP
  1.2368 +nsLocalFile::OpenDocWithApp(nsIFile *aAppToOpenWith, bool aLaunchInBackground)
  1.2369 +{
  1.2370 +  FSRef docFSRef;
  1.2371 +  nsresult rv = GetFSRef(&docFSRef);
  1.2372 +  if (NS_FAILED(rv))
  1.2373 +    return rv;
  1.2374 +
  1.2375 +  if (!aAppToOpenWith) {
  1.2376 +    OSErr err = ::LSOpenFSRef(&docFSRef, nullptr);
  1.2377 +    return MacErrorMapper(err);
  1.2378 +  }
  1.2379 +
  1.2380 +  nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
  1.2381 +  if (!appFileMac)
  1.2382 +    return rv;
  1.2383 +
  1.2384 +  bool isExecutable;
  1.2385 +  rv = appFileMac->IsExecutable(&isExecutable);
  1.2386 +  if (NS_FAILED(rv))
  1.2387 +    return rv;
  1.2388 +  if (!isExecutable)
  1.2389 +    return NS_ERROR_FILE_EXECUTION_FAILED;
  1.2390 +
  1.2391 +  FSRef appFSRef;
  1.2392 +  rv = appFileMac->GetFSRef(&appFSRef);
  1.2393 +  if (NS_FAILED(rv))
  1.2394 +    return rv;
  1.2395 +
  1.2396 +  LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
  1.2397 +  LSLaunchFSRefSpec thelaunchSpec;
  1.2398 +
  1.2399 +  if (aLaunchInBackground)
  1.2400 +    theLaunchFlags |= kLSLaunchDontSwitch;
  1.2401 +  memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
  1.2402 +
  1.2403 +  thelaunchSpec.appRef = &appFSRef;
  1.2404 +  thelaunchSpec.numDocs = 1;
  1.2405 +  thelaunchSpec.itemRefs = &docFSRef;
  1.2406 +  thelaunchSpec.launchFlags = theLaunchFlags;
  1.2407 +
  1.2408 +  OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
  1.2409 +  if (err != noErr)
  1.2410 +    return MacErrorMapper(err);
  1.2411 +
  1.2412 +  return NS_OK;
  1.2413 +}
  1.2414 +
  1.2415 +NS_IMETHODIMP
  1.2416 +nsLocalFile::IsPackage(bool *_retval)
  1.2417 +{
  1.2418 +  if (NS_WARN_IF(!_retval))
  1.2419 +      return NS_ERROR_INVALID_ARG;
  1.2420 +  *_retval = false;
  1.2421 +
  1.2422 +  CFURLRef url;
  1.2423 +  nsresult rv = GetCFURL(&url);
  1.2424 +  if (NS_FAILED(rv))
  1.2425 +    return rv;
  1.2426 +
  1.2427 +  LSItemInfoRecord info;
  1.2428 +  OSStatus status = ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info);
  1.2429 +
  1.2430 +  ::CFRelease(url);
  1.2431 +
  1.2432 +  if (status != noErr) {
  1.2433 +    return NS_ERROR_FAILURE;
  1.2434 +  }
  1.2435 +
  1.2436 +  *_retval = !!(info.flags & kLSItemInfoIsPackage);
  1.2437 +
  1.2438 +  return NS_OK;
  1.2439 +}
  1.2440 +
  1.2441 +NS_IMETHODIMP
  1.2442 +nsLocalFile::GetBundleDisplayName(nsAString& outBundleName)
  1.2443 +{
  1.2444 +  bool isPackage = false;
  1.2445 +  nsresult rv = IsPackage(&isPackage);
  1.2446 +  if (NS_FAILED(rv) || !isPackage)
  1.2447 +    return NS_ERROR_FAILURE;
  1.2448 +
  1.2449 +  nsAutoString name;
  1.2450 +  rv = GetLeafName(name);
  1.2451 +  if (NS_FAILED(rv))
  1.2452 +    return rv;
  1.2453 +
  1.2454 +  int32_t length = name.Length();
  1.2455 +  if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
  1.2456 +    // 4 characters in ".app"
  1.2457 +    outBundleName = Substring(name, 0, length - 4);
  1.2458 +  }
  1.2459 +  else {
  1.2460 +    outBundleName = name;
  1.2461 +  }
  1.2462 +
  1.2463 +  return NS_OK;
  1.2464 +}
  1.2465 +
  1.2466 +NS_IMETHODIMP
  1.2467 +nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier)
  1.2468 +{
  1.2469 +  nsresult rv = NS_ERROR_FAILURE;
  1.2470 +
  1.2471 +  CFURLRef urlRef;
  1.2472 +  if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
  1.2473 +    CFBundleRef bundle = ::CFBundleCreate(nullptr, urlRef);
  1.2474 +    if (bundle) {
  1.2475 +      CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
  1.2476 +      if (bundleIdentifier)
  1.2477 +        rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier);
  1.2478 +      ::CFRelease(bundle);
  1.2479 +    }
  1.2480 +    ::CFRelease(urlRef);
  1.2481 +  }
  1.2482 +
  1.2483 +  return rv;
  1.2484 +}
  1.2485 +
  1.2486 +NS_IMETHODIMP
  1.2487 +nsLocalFile::GetBundleContentsLastModifiedTime(int64_t *aLastModTime)
  1.2488 +{
  1.2489 +  CHECK_mPath();
  1.2490 +  if (NS_WARN_IF(!aLastModTime))
  1.2491 +      return NS_ERROR_INVALID_ARG;
  1.2492 +
  1.2493 +  bool isPackage = false;
  1.2494 +  nsresult rv = IsPackage(&isPackage);
  1.2495 +  if (NS_FAILED(rv) || !isPackage) {
  1.2496 +    return GetLastModifiedTime(aLastModTime);
  1.2497 +  }
  1.2498 +
  1.2499 +  nsAutoCString infoPlistPath(mPath);
  1.2500 +  infoPlistPath.AppendLiteral("/Contents/Info.plist");
  1.2501 +  PRFileInfo64 info;
  1.2502 +  if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) {
  1.2503 +    return GetLastModifiedTime(aLastModTime);
  1.2504 +  }
  1.2505 +  int64_t modTime = int64_t(info.modifyTime);
  1.2506 +  if (modTime == 0) {
  1.2507 +    *aLastModTime = 0;
  1.2508 +  } else {
  1.2509 +    *aLastModTime = modTime / int64_t(PR_USEC_PER_MSEC);
  1.2510 +  }
  1.2511 +
  1.2512 +  return NS_OK;
  1.2513 +}
  1.2514 +
  1.2515 +NS_IMETHODIMP nsLocalFile::InitWithFile(nsIFile *aFile)
  1.2516 +{
  1.2517 +  if (NS_WARN_IF(!aFile))
  1.2518 +      return NS_ERROR_INVALID_ARG;
  1.2519 +
  1.2520 +  nsAutoCString nativePath;
  1.2521 +  nsresult rv = aFile->GetNativePath(nativePath);
  1.2522 +  if (NS_FAILED(rv))
  1.2523 +    return rv;
  1.2524 +
  1.2525 +  return InitWithNativePath(nativePath);
  1.2526 +}
  1.2527 +
  1.2528 +nsresult
  1.2529 +NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks, nsILocalFileMac** result)
  1.2530 +{
  1.2531 +  nsRefPtr<nsLocalFile> file = new nsLocalFile();
  1.2532 +
  1.2533 +  file->SetFollowLinks(aFollowLinks);
  1.2534 +
  1.2535 +  nsresult rv = file->InitWithFSRef(aFSRef);
  1.2536 +  if (NS_FAILED(rv)) {
  1.2537 +    return rv;
  1.2538 +  }
  1.2539 +  file.forget(result);
  1.2540 +  return NS_OK;
  1.2541 +}
  1.2542 +
  1.2543 +nsresult
  1.2544 +NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks, nsILocalFileMac** result)
  1.2545 +{
  1.2546 +  nsRefPtr<nsLocalFile> file = new nsLocalFile();
  1.2547 +
  1.2548 +  file->SetFollowLinks(aFollowLinks);
  1.2549 +
  1.2550 +  nsresult rv = file->InitWithCFURL(aURL);
  1.2551 +  if (NS_FAILED(rv)) {
  1.2552 +    return rv;
  1.2553 +  }
  1.2554 +  file.forget(result);
  1.2555 +  return NS_OK;
  1.2556 +}
  1.2557 +
  1.2558 +#endif

mercurial