xpcom/io/nsLocalFileUnix.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /**
michael@0 7 * Implementation of nsIFile for "unixy" systems.
michael@0 8 */
michael@0 9
michael@0 10 #include "mozilla/ArrayUtils.h"
michael@0 11 #include "mozilla/Attributes.h"
michael@0 12
michael@0 13 #include <sys/types.h>
michael@0 14 #include <sys/stat.h>
michael@0 15 #include <unistd.h>
michael@0 16 #include <fcntl.h>
michael@0 17 #include <errno.h>
michael@0 18 #include <utime.h>
michael@0 19 #include <dirent.h>
michael@0 20 #include <ctype.h>
michael@0 21 #include <locale.h>
michael@0 22 #if defined(VMS)
michael@0 23 #include <fabdef.h>
michael@0 24 #endif
michael@0 25
michael@0 26 #if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H)
michael@0 27 #define USE_LINUX_QUOTACTL
michael@0 28 #include <sys/quota.h>
michael@0 29 #endif
michael@0 30
michael@0 31 #include "xpcom-private.h"
michael@0 32 #include "nsDirectoryServiceDefs.h"
michael@0 33 #include "nsCRT.h"
michael@0 34 #include "nsCOMPtr.h"
michael@0 35 #include "nsMemory.h"
michael@0 36 #include "nsIFile.h"
michael@0 37 #include "nsString.h"
michael@0 38 #include "nsReadableUtils.h"
michael@0 39 #include "nsLocalFile.h"
michael@0 40 #include "nsIComponentManager.h"
michael@0 41 #include "nsXPIDLString.h"
michael@0 42 #include "prproces.h"
michael@0 43 #include "nsIDirectoryEnumerator.h"
michael@0 44 #include "nsISimpleEnumerator.h"
michael@0 45 #include "private/pprio.h"
michael@0 46 #include "prlink.h"
michael@0 47
michael@0 48 #ifdef MOZ_WIDGET_GTK
michael@0 49 #include "nsIGIOService.h"
michael@0 50 #include "nsIGnomeVFSService.h"
michael@0 51 #endif
michael@0 52
michael@0 53 #ifdef MOZ_WIDGET_COCOA
michael@0 54 #include <Carbon/Carbon.h>
michael@0 55 #include "CocoaFileUtils.h"
michael@0 56 #include "prmem.h"
michael@0 57 #include "plbase64.h"
michael@0 58
michael@0 59 static nsresult MacErrorMapper(OSErr inErr);
michael@0 60 #endif
michael@0 61
michael@0 62 #ifdef MOZ_WIDGET_ANDROID
michael@0 63 #include "AndroidBridge.h"
michael@0 64 #include "nsIMIMEService.h"
michael@0 65 #include <linux/magic.h>
michael@0 66 #endif
michael@0 67
michael@0 68 #ifdef MOZ_ENABLE_CONTENTACTION
michael@0 69 #include <contentaction/contentaction.h>
michael@0 70 #endif
michael@0 71
michael@0 72 #include "nsNativeCharsetUtils.h"
michael@0 73 #include "nsTraceRefcnt.h"
michael@0 74 #include "nsHashKeys.h"
michael@0 75
michael@0 76 using namespace mozilla;
michael@0 77
michael@0 78 #define ENSURE_STAT_CACHE() \
michael@0 79 PR_BEGIN_MACRO \
michael@0 80 if (!FillStatCache()) \
michael@0 81 return NSRESULT_FOR_ERRNO(); \
michael@0 82 PR_END_MACRO
michael@0 83
michael@0 84 #define CHECK_mPath() \
michael@0 85 PR_BEGIN_MACRO \
michael@0 86 if (mPath.IsEmpty()) \
michael@0 87 return NS_ERROR_NOT_INITIALIZED; \
michael@0 88 PR_END_MACRO
michael@0 89
michael@0 90 /* directory enumerator */
michael@0 91 class
michael@0 92 nsDirEnumeratorUnix MOZ_FINAL : public nsISimpleEnumerator,
michael@0 93 public nsIDirectoryEnumerator
michael@0 94 {
michael@0 95 public:
michael@0 96 nsDirEnumeratorUnix();
michael@0 97
michael@0 98 // nsISupports interface
michael@0 99 NS_DECL_ISUPPORTS
michael@0 100
michael@0 101 // nsISimpleEnumerator interface
michael@0 102 NS_DECL_NSISIMPLEENUMERATOR
michael@0 103
michael@0 104 // nsIDirectoryEnumerator interface
michael@0 105 NS_DECL_NSIDIRECTORYENUMERATOR
michael@0 106
michael@0 107 NS_IMETHOD Init(nsLocalFile *parent, bool ignored);
michael@0 108
michael@0 109 private:
michael@0 110 ~nsDirEnumeratorUnix();
michael@0 111
michael@0 112 protected:
michael@0 113 NS_IMETHOD GetNextEntry();
michael@0 114
michael@0 115 DIR *mDir;
michael@0 116 struct dirent *mEntry;
michael@0 117 nsCString mParentPath;
michael@0 118 };
michael@0 119
michael@0 120 nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
michael@0 121 mDir(nullptr),
michael@0 122 mEntry(nullptr)
michael@0 123 {
michael@0 124 }
michael@0 125
michael@0 126 nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
michael@0 127 {
michael@0 128 Close();
michael@0 129 }
michael@0 130
michael@0 131 NS_IMPL_ISUPPORTS(nsDirEnumeratorUnix, nsISimpleEnumerator, nsIDirectoryEnumerator)
michael@0 132
michael@0 133 NS_IMETHODIMP
michael@0 134 nsDirEnumeratorUnix::Init(nsLocalFile *parent, bool resolveSymlinks /*ignored*/)
michael@0 135 {
michael@0 136 nsAutoCString dirPath;
michael@0 137 if (NS_FAILED(parent->GetNativePath(dirPath)) ||
michael@0 138 dirPath.IsEmpty()) {
michael@0 139 return NS_ERROR_FILE_INVALID_PATH;
michael@0 140 }
michael@0 141
michael@0 142 if (NS_FAILED(parent->GetNativePath(mParentPath)))
michael@0 143 return NS_ERROR_FAILURE;
michael@0 144
michael@0 145 mDir = opendir(dirPath.get());
michael@0 146 if (!mDir)
michael@0 147 return NSRESULT_FOR_ERRNO();
michael@0 148 return GetNextEntry();
michael@0 149 }
michael@0 150
michael@0 151 NS_IMETHODIMP
michael@0 152 nsDirEnumeratorUnix::HasMoreElements(bool *result)
michael@0 153 {
michael@0 154 *result = mDir && mEntry;
michael@0 155 if (!*result)
michael@0 156 Close();
michael@0 157 return NS_OK;
michael@0 158 }
michael@0 159
michael@0 160 NS_IMETHODIMP
michael@0 161 nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
michael@0 162 {
michael@0 163 nsCOMPtr<nsIFile> file;
michael@0 164 nsresult rv = GetNextFile(getter_AddRefs(file));
michael@0 165 if (NS_FAILED(rv))
michael@0 166 return rv;
michael@0 167 NS_IF_ADDREF(*_retval = file);
michael@0 168 return NS_OK;
michael@0 169 }
michael@0 170
michael@0 171 NS_IMETHODIMP
michael@0 172 nsDirEnumeratorUnix::GetNextEntry()
michael@0 173 {
michael@0 174 do {
michael@0 175 errno = 0;
michael@0 176 mEntry = readdir(mDir);
michael@0 177
michael@0 178 // end of dir or error
michael@0 179 if (!mEntry)
michael@0 180 return NSRESULT_FOR_ERRNO();
michael@0 181
michael@0 182 // keep going past "." and ".."
michael@0 183 } while (mEntry->d_name[0] == '.' &&
michael@0 184 (mEntry->d_name[1] == '\0' || // .\0
michael@0 185 (mEntry->d_name[1] == '.' &&
michael@0 186 mEntry->d_name[2] == '\0'))); // ..\0
michael@0 187 return NS_OK;
michael@0 188 }
michael@0 189
michael@0 190 NS_IMETHODIMP
michael@0 191 nsDirEnumeratorUnix::GetNextFile(nsIFile **_retval)
michael@0 192 {
michael@0 193 nsresult rv;
michael@0 194 if (!mDir || !mEntry) {
michael@0 195 *_retval = nullptr;
michael@0 196 return NS_OK;
michael@0 197 }
michael@0 198
michael@0 199 nsCOMPtr<nsIFile> file = new nsLocalFile();
michael@0 200
michael@0 201 if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
michael@0 202 NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name))))
michael@0 203 return rv;
michael@0 204
michael@0 205 file.forget(_retval);
michael@0 206 return GetNextEntry();
michael@0 207 }
michael@0 208
michael@0 209 NS_IMETHODIMP
michael@0 210 nsDirEnumeratorUnix::Close()
michael@0 211 {
michael@0 212 if (mDir) {
michael@0 213 closedir(mDir);
michael@0 214 mDir = nullptr;
michael@0 215 }
michael@0 216 return NS_OK;
michael@0 217 }
michael@0 218
michael@0 219 nsLocalFile::nsLocalFile()
michael@0 220 {
michael@0 221 }
michael@0 222
michael@0 223 nsLocalFile::nsLocalFile(const nsLocalFile& other)
michael@0 224 : mPath(other.mPath)
michael@0 225 {
michael@0 226 }
michael@0 227
michael@0 228 #ifdef MOZ_WIDGET_COCOA
michael@0 229 NS_IMPL_ISUPPORTS(nsLocalFile,
michael@0 230 nsILocalFileMac,
michael@0 231 nsILocalFile,
michael@0 232 nsIFile,
michael@0 233 nsIHashable)
michael@0 234 #else
michael@0 235 NS_IMPL_ISUPPORTS(nsLocalFile,
michael@0 236 nsILocalFile,
michael@0 237 nsIFile,
michael@0 238 nsIHashable)
michael@0 239 #endif
michael@0 240
michael@0 241 nsresult
michael@0 242 nsLocalFile::nsLocalFileConstructor(nsISupports *outer,
michael@0 243 const nsIID &aIID,
michael@0 244 void **aInstancePtr)
michael@0 245 {
michael@0 246 if (NS_WARN_IF(!aInstancePtr))
michael@0 247 return NS_ERROR_INVALID_ARG;
michael@0 248 if (NS_WARN_IF(outer))
michael@0 249 return NS_ERROR_NO_AGGREGATION;
michael@0 250
michael@0 251 *aInstancePtr = nullptr;
michael@0 252
michael@0 253 nsCOMPtr<nsIFile> inst = new nsLocalFile();
michael@0 254 return inst->QueryInterface(aIID, aInstancePtr);
michael@0 255 }
michael@0 256
michael@0 257 bool
michael@0 258 nsLocalFile::FillStatCache() {
michael@0 259 if (STAT(mPath.get(), &mCachedStat) == -1) {
michael@0 260 // try lstat it may be a symlink
michael@0 261 if (LSTAT(mPath.get(), &mCachedStat) == -1) {
michael@0 262 return false;
michael@0 263 }
michael@0 264 }
michael@0 265 return true;
michael@0 266 }
michael@0 267
michael@0 268 NS_IMETHODIMP
michael@0 269 nsLocalFile::Clone(nsIFile **file)
michael@0 270 {
michael@0 271 // Just copy-construct ourselves
michael@0 272 nsRefPtr<nsLocalFile> copy = new nsLocalFile(*this);
michael@0 273 copy.forget(file);
michael@0 274 return NS_OK;
michael@0 275 }
michael@0 276
michael@0 277 NS_IMETHODIMP
michael@0 278 nsLocalFile::InitWithNativePath(const nsACString &filePath)
michael@0 279 {
michael@0 280 if (filePath.Equals("~") || Substring(filePath, 0, 2).EqualsLiteral("~/")) {
michael@0 281 nsCOMPtr<nsIFile> homeDir;
michael@0 282 nsAutoCString homePath;
michael@0 283 if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
michael@0 284 getter_AddRefs(homeDir))) ||
michael@0 285 NS_FAILED(homeDir->GetNativePath(homePath))) {
michael@0 286 return NS_ERROR_FAILURE;
michael@0 287 }
michael@0 288
michael@0 289 mPath = homePath;
michael@0 290 if (filePath.Length() > 2)
michael@0 291 mPath.Append(Substring(filePath, 1, filePath.Length() - 1));
michael@0 292 } else {
michael@0 293 if (filePath.IsEmpty() || filePath.First() != '/')
michael@0 294 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
michael@0 295 mPath = filePath;
michael@0 296 }
michael@0 297
michael@0 298 // trim off trailing slashes
michael@0 299 ssize_t len = mPath.Length();
michael@0 300 while ((len > 1) && (mPath[len - 1] == '/'))
michael@0 301 --len;
michael@0 302 mPath.SetLength(len);
michael@0 303
michael@0 304 return NS_OK;
michael@0 305 }
michael@0 306
michael@0 307 NS_IMETHODIMP
michael@0 308 nsLocalFile::CreateAllAncestors(uint32_t permissions)
michael@0 309 {
michael@0 310 // <jband> I promise to play nice
michael@0 311 char *buffer = mPath.BeginWriting(),
michael@0 312 *slashp = buffer;
michael@0 313
michael@0 314 #ifdef DEBUG_NSIFILE
michael@0 315 fprintf(stderr, "nsIFile: before: %s\n", buffer);
michael@0 316 #endif
michael@0 317
michael@0 318 while ((slashp = strchr(slashp + 1, '/'))) {
michael@0 319 /*
michael@0 320 * Sequences of '/' are equivalent to a single '/'.
michael@0 321 */
michael@0 322 if (slashp[1] == '/')
michael@0 323 continue;
michael@0 324
michael@0 325 /*
michael@0 326 * If the path has a trailing slash, don't make the last component,
michael@0 327 * because we'll get EEXIST in Create when we try to build the final
michael@0 328 * component again, and it's easier to condition the logic here than
michael@0 329 * there.
michael@0 330 */
michael@0 331 if (slashp[1] == '\0')
michael@0 332 break;
michael@0 333
michael@0 334 /* Temporarily NUL-terminate here */
michael@0 335 *slashp = '\0';
michael@0 336 #ifdef DEBUG_NSIFILE
michael@0 337 fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
michael@0 338 #endif
michael@0 339 int mkdir_result = mkdir(buffer, permissions);
michael@0 340 int mkdir_errno = errno;
michael@0 341 if (mkdir_result == -1) {
michael@0 342 /*
michael@0 343 * Always set |errno| to EEXIST if the dir already exists
michael@0 344 * (we have to do this here since the errno value is not consistent
michael@0 345 * in all cases - various reasons like different platform,
michael@0 346 * automounter-controlled dir, etc. can affect it (see bug 125489
michael@0 347 * for details)).
michael@0 348 */
michael@0 349 if (access(buffer, F_OK) == 0) {
michael@0 350 mkdir_errno = EEXIST;
michael@0 351 }
michael@0 352 }
michael@0 353
michael@0 354 /* Put the / back before we (maybe) return */
michael@0 355 *slashp = '/';
michael@0 356
michael@0 357 /*
michael@0 358 * We could get EEXIST for an existing file -- not directory --
michael@0 359 * with the name of one of our ancestors, but that's OK: we'll get
michael@0 360 * ENOTDIR when we try to make the next component in the path,
michael@0 361 * either here on back in Create, and error out appropriately.
michael@0 362 */
michael@0 363 if (mkdir_result == -1 && mkdir_errno != EEXIST)
michael@0 364 return nsresultForErrno(mkdir_errno);
michael@0 365 }
michael@0 366
michael@0 367 #ifdef DEBUG_NSIFILE
michael@0 368 fprintf(stderr, "nsIFile: after: %s\n", buffer);
michael@0 369 #endif
michael@0 370
michael@0 371 return NS_OK;
michael@0 372 }
michael@0 373
michael@0 374 NS_IMETHODIMP
michael@0 375 nsLocalFile::OpenNSPRFileDesc(int32_t flags, int32_t mode, PRFileDesc **_retval)
michael@0 376 {
michael@0 377 *_retval = PR_Open(mPath.get(), flags, mode);
michael@0 378 if (! *_retval)
michael@0 379 return NS_ErrorAccordingToNSPR();
michael@0 380
michael@0 381 if (flags & DELETE_ON_CLOSE) {
michael@0 382 PR_Delete(mPath.get());
michael@0 383 }
michael@0 384
michael@0 385 #if defined(HAVE_POSIX_FADVISE)
michael@0 386 if (flags & OS_READAHEAD) {
michael@0 387 posix_fadvise(PR_FileDesc2NativeHandle(*_retval), 0, 0,
michael@0 388 POSIX_FADV_SEQUENTIAL);
michael@0 389 }
michael@0 390 #endif
michael@0 391 return NS_OK;
michael@0 392 }
michael@0 393
michael@0 394 NS_IMETHODIMP
michael@0 395 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval)
michael@0 396 {
michael@0 397 *_retval = fopen(mPath.get(), mode);
michael@0 398 if (! *_retval)
michael@0 399 return NS_ERROR_FAILURE;
michael@0 400
michael@0 401 return NS_OK;
michael@0 402 }
michael@0 403
michael@0 404 static int
michael@0 405 do_create(const char *path, int flags, mode_t mode, PRFileDesc **_retval)
michael@0 406 {
michael@0 407 *_retval = PR_Open(path, flags, mode);
michael@0 408 return *_retval ? 0 : -1;
michael@0 409 }
michael@0 410
michael@0 411 static int
michael@0 412 do_mkdir(const char *path, int flags, mode_t mode, PRFileDesc **_retval)
michael@0 413 {
michael@0 414 *_retval = nullptr;
michael@0 415 return mkdir(path, mode);
michael@0 416 }
michael@0 417
michael@0 418 nsresult
michael@0 419 nsLocalFile::CreateAndKeepOpen(uint32_t type, int flags,
michael@0 420 uint32_t permissions, PRFileDesc **_retval)
michael@0 421 {
michael@0 422 if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
michael@0 423 return NS_ERROR_FILE_UNKNOWN_TYPE;
michael@0 424
michael@0 425 int result;
michael@0 426 int (*createFunc)(const char *, int, mode_t, PRFileDesc **) =
michael@0 427 (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
michael@0 428
michael@0 429 result = createFunc(mPath.get(), flags, permissions, _retval);
michael@0 430 if (result == -1 && errno == ENOENT) {
michael@0 431 /*
michael@0 432 * If we failed because of missing ancestor components, try to create
michael@0 433 * them and then retry the original creation.
michael@0 434 *
michael@0 435 * Ancestor directories get the same permissions as the file we're
michael@0 436 * creating, with the X bit set for each of (user,group,other) with
michael@0 437 * an R bit in the original permissions. If you want to do anything
michael@0 438 * fancy like setgid or sticky bits, do it by hand.
michael@0 439 */
michael@0 440 int dirperm = permissions;
michael@0 441 if (permissions & S_IRUSR)
michael@0 442 dirperm |= S_IXUSR;
michael@0 443 if (permissions & S_IRGRP)
michael@0 444 dirperm |= S_IXGRP;
michael@0 445 if (permissions & S_IROTH)
michael@0 446 dirperm |= S_IXOTH;
michael@0 447
michael@0 448 #ifdef DEBUG_NSIFILE
michael@0 449 fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
michael@0 450 dirperm);
michael@0 451 #endif
michael@0 452
michael@0 453 if (NS_FAILED(CreateAllAncestors(dirperm)))
michael@0 454 return NS_ERROR_FAILURE;
michael@0 455
michael@0 456 #ifdef DEBUG_NSIFILE
michael@0 457 fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
michael@0 458 #endif
michael@0 459 result = createFunc(mPath.get(), flags, permissions, _retval);
michael@0 460 }
michael@0 461 return NSRESULT_FOR_RETURN(result);
michael@0 462 }
michael@0 463
michael@0 464 NS_IMETHODIMP
michael@0 465 nsLocalFile::Create(uint32_t type, uint32_t permissions)
michael@0 466 {
michael@0 467 PRFileDesc *junk = nullptr;
michael@0 468 nsresult rv = CreateAndKeepOpen(type,
michael@0 469 PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE |
michael@0 470 PR_EXCL,
michael@0 471 permissions,
michael@0 472 &junk);
michael@0 473 if (junk)
michael@0 474 PR_Close(junk);
michael@0 475 return rv;
michael@0 476 }
michael@0 477
michael@0 478 NS_IMETHODIMP
michael@0 479 nsLocalFile::AppendNative(const nsACString &fragment)
michael@0 480 {
michael@0 481 if (fragment.IsEmpty())
michael@0 482 return NS_OK;
michael@0 483
michael@0 484 // only one component of path can be appended
michael@0 485 nsACString::const_iterator begin, end;
michael@0 486 if (FindCharInReadable('/', fragment.BeginReading(begin),
michael@0 487 fragment.EndReading(end)))
michael@0 488 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
michael@0 489
michael@0 490 return AppendRelativeNativePath(fragment);
michael@0 491 }
michael@0 492
michael@0 493 NS_IMETHODIMP
michael@0 494 nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
michael@0 495 {
michael@0 496 if (fragment.IsEmpty())
michael@0 497 return NS_OK;
michael@0 498
michael@0 499 // No leading '/'
michael@0 500 if (fragment.First() == '/')
michael@0 501 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
michael@0 502
michael@0 503 if (mPath.EqualsLiteral("/"))
michael@0 504 mPath.Append(fragment);
michael@0 505 else
michael@0 506 mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
michael@0 507
michael@0 508 return NS_OK;
michael@0 509 }
michael@0 510
michael@0 511 NS_IMETHODIMP
michael@0 512 nsLocalFile::Normalize()
michael@0 513 {
michael@0 514 char resolved_path[PATH_MAX] = "";
michael@0 515 char *resolved_path_ptr = nullptr;
michael@0 516
michael@0 517 resolved_path_ptr = realpath(mPath.get(), resolved_path);
michael@0 518
michael@0 519 // if there is an error, the return is null.
michael@0 520 if (!resolved_path_ptr)
michael@0 521 return NSRESULT_FOR_ERRNO();
michael@0 522
michael@0 523 mPath = resolved_path;
michael@0 524 return NS_OK;
michael@0 525 }
michael@0 526
michael@0 527 void
michael@0 528 nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin,
michael@0 529 nsACString::const_iterator &end)
michael@0 530 {
michael@0 531 // XXX perhaps we should cache this??
michael@0 532
michael@0 533 mPath.BeginReading(begin);
michael@0 534 mPath.EndReading(end);
michael@0 535
michael@0 536 nsACString::const_iterator it = end;
michael@0 537 nsACString::const_iterator stop = begin;
michael@0 538 --stop;
michael@0 539 while (--it != stop) {
michael@0 540 if (*it == '/') {
michael@0 541 begin = ++it;
michael@0 542 return;
michael@0 543 }
michael@0 544 }
michael@0 545 // else, the entire path is the leaf name (which means this
michael@0 546 // isn't an absolute path... unexpected??)
michael@0 547 }
michael@0 548
michael@0 549 NS_IMETHODIMP
michael@0 550 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
michael@0 551 {
michael@0 552 nsACString::const_iterator begin, end;
michael@0 553 LocateNativeLeafName(begin, end);
michael@0 554 aLeafName = Substring(begin, end);
michael@0 555 return NS_OK;
michael@0 556 }
michael@0 557
michael@0 558 NS_IMETHODIMP
michael@0 559 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
michael@0 560 {
michael@0 561 nsACString::const_iterator begin, end;
michael@0 562 LocateNativeLeafName(begin, end);
michael@0 563 mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
michael@0 564 return NS_OK;
michael@0 565 }
michael@0 566
michael@0 567 NS_IMETHODIMP
michael@0 568 nsLocalFile::GetNativePath(nsACString &_retval)
michael@0 569 {
michael@0 570 _retval = mPath;
michael@0 571 return NS_OK;
michael@0 572 }
michael@0 573
michael@0 574 nsresult
michael@0 575 nsLocalFile::GetNativeTargetPathName(nsIFile *newParent,
michael@0 576 const nsACString &newName,
michael@0 577 nsACString &_retval)
michael@0 578 {
michael@0 579 nsresult rv;
michael@0 580 nsCOMPtr<nsIFile> oldParent;
michael@0 581
michael@0 582 if (!newParent) {
michael@0 583 if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
michael@0 584 return rv;
michael@0 585 newParent = oldParent.get();
michael@0 586 } else {
michael@0 587 // check to see if our target directory exists
michael@0 588 bool targetExists;
michael@0 589 if (NS_FAILED(rv = newParent->Exists(&targetExists)))
michael@0 590 return rv;
michael@0 591
michael@0 592 if (!targetExists) {
michael@0 593 // XXX create the new directory with some permissions
michael@0 594 rv = newParent->Create(DIRECTORY_TYPE, 0755);
michael@0 595 if (NS_FAILED(rv))
michael@0 596 return rv;
michael@0 597 } else {
michael@0 598 // make sure that the target is actually a directory
michael@0 599 bool targetIsDirectory;
michael@0 600 if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
michael@0 601 return rv;
michael@0 602 if (!targetIsDirectory)
michael@0 603 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
michael@0 604 }
michael@0 605 }
michael@0 606
michael@0 607 nsACString::const_iterator nameBegin, nameEnd;
michael@0 608 if (!newName.IsEmpty()) {
michael@0 609 newName.BeginReading(nameBegin);
michael@0 610 newName.EndReading(nameEnd);
michael@0 611 }
michael@0 612 else
michael@0 613 LocateNativeLeafName(nameBegin, nameEnd);
michael@0 614
michael@0 615 nsAutoCString dirName;
michael@0 616 if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
michael@0 617 return rv;
michael@0 618
michael@0 619 _retval = dirName
michael@0 620 + NS_LITERAL_CSTRING("/")
michael@0 621 + Substring(nameBegin, nameEnd);
michael@0 622 return NS_OK;
michael@0 623 }
michael@0 624
michael@0 625 nsresult
michael@0 626 nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
michael@0 627 {
michael@0 628 nsresult rv;
michael@0 629 /*
michael@0 630 * dirCheck is used for various boolean test results such as from Equals,
michael@0 631 * Exists, isDir, etc.
michael@0 632 */
michael@0 633 bool dirCheck, isSymlink;
michael@0 634 uint32_t oldPerms;
michael@0 635
michael@0 636 if (NS_FAILED(rv = IsDirectory(&dirCheck)))
michael@0 637 return rv;
michael@0 638 if (!dirCheck)
michael@0 639 return CopyToNative(newParent, EmptyCString());
michael@0 640
michael@0 641 if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
michael@0 642 return rv;
michael@0 643 if (dirCheck) {
michael@0 644 // can't copy dir to itself
michael@0 645 return NS_ERROR_INVALID_ARG;
michael@0 646 }
michael@0 647
michael@0 648 if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
michael@0 649 return rv;
michael@0 650 // get the dirs old permissions
michael@0 651 if (NS_FAILED(rv = GetPermissions(&oldPerms)))
michael@0 652 return rv;
michael@0 653 if (!dirCheck) {
michael@0 654 if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
michael@0 655 return rv;
michael@0 656 } else { // dir exists lets try to use leaf
michael@0 657 nsAutoCString leafName;
michael@0 658 if (NS_FAILED(rv = GetNativeLeafName(leafName)))
michael@0 659 return rv;
michael@0 660 if (NS_FAILED(rv = newParent->AppendNative(leafName)))
michael@0 661 return rv;
michael@0 662 if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
michael@0 663 return rv;
michael@0 664 if (dirCheck)
michael@0 665 return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
michael@0 666 if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
michael@0 667 return rv;
michael@0 668 }
michael@0 669
michael@0 670 nsCOMPtr<nsISimpleEnumerator> dirIterator;
michael@0 671 if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
michael@0 672 return rv;
michael@0 673
michael@0 674 bool hasMore = false;
michael@0 675 while (dirIterator->HasMoreElements(&hasMore), hasMore) {
michael@0 676 nsCOMPtr<nsISupports> supports;
michael@0 677 nsCOMPtr<nsIFile> entry;
michael@0 678 rv = dirIterator->GetNext(getter_AddRefs(supports));
michael@0 679 entry = do_QueryInterface(supports);
michael@0 680 if (NS_FAILED(rv) || !entry)
michael@0 681 continue;
michael@0 682 if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
michael@0 683 return rv;
michael@0 684 if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
michael@0 685 return rv;
michael@0 686 if (dirCheck && !isSymlink) {
michael@0 687 nsCOMPtr<nsIFile> destClone;
michael@0 688 rv = newParent->Clone(getter_AddRefs(destClone));
michael@0 689 if (NS_SUCCEEDED(rv)) {
michael@0 690 if (NS_FAILED(rv = entry->CopyToNative(destClone, EmptyCString()))) {
michael@0 691 #ifdef DEBUG
michael@0 692 nsresult rv2;
michael@0 693 nsAutoCString pathName;
michael@0 694 if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
michael@0 695 return rv2;
michael@0 696 printf("Operation not supported: %s\n", pathName.get());
michael@0 697 #endif
michael@0 698 if (rv == NS_ERROR_OUT_OF_MEMORY)
michael@0 699 return rv;
michael@0 700 continue;
michael@0 701 }
michael@0 702 }
michael@0 703 } else {
michael@0 704 if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
michael@0 705 #ifdef DEBUG
michael@0 706 nsresult rv2;
michael@0 707 nsAutoCString pathName;
michael@0 708 if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
michael@0 709 return rv2;
michael@0 710 printf("Operation not supported: %s\n", pathName.get());
michael@0 711 #endif
michael@0 712 if (rv == NS_ERROR_OUT_OF_MEMORY)
michael@0 713 return rv;
michael@0 714 continue;
michael@0 715 }
michael@0 716 }
michael@0 717 }
michael@0 718 return NS_OK;
michael@0 719 }
michael@0 720
michael@0 721 NS_IMETHODIMP
michael@0 722 nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
michael@0 723 {
michael@0 724 nsresult rv;
michael@0 725 // check to make sure that this has been initialized properly
michael@0 726 CHECK_mPath();
michael@0 727
michael@0 728 // we copy the parent here so 'newParent' remains immutable
michael@0 729 nsCOMPtr <nsIFile> workParent;
michael@0 730 if (newParent) {
michael@0 731 if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
michael@0 732 return rv;
michael@0 733 } else {
michael@0 734 if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
michael@0 735 return rv;
michael@0 736 }
michael@0 737
michael@0 738 // check to see if we are a directory or if we are a file
michael@0 739 bool isDirectory;
michael@0 740 if (NS_FAILED(rv = IsDirectory(&isDirectory)))
michael@0 741 return rv;
michael@0 742
michael@0 743 nsAutoCString newPathName;
michael@0 744 if (isDirectory) {
michael@0 745 if (!newName.IsEmpty()) {
michael@0 746 if (NS_FAILED(rv = workParent->AppendNative(newName)))
michael@0 747 return rv;
michael@0 748 } else {
michael@0 749 if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
michael@0 750 return rv;
michael@0 751 if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
michael@0 752 return rv;
michael@0 753 }
michael@0 754 if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
michael@0 755 return rv;
michael@0 756 } else {
michael@0 757 rv = GetNativeTargetPathName(workParent, newName, newPathName);
michael@0 758 if (NS_FAILED(rv))
michael@0 759 return rv;
michael@0 760
michael@0 761 #ifdef DEBUG_blizzard
michael@0 762 printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
michael@0 763 #endif
michael@0 764
michael@0 765 // actually create the file.
michael@0 766 nsLocalFile *newFile = new nsLocalFile();
michael@0 767 if (!newFile)
michael@0 768 return NS_ERROR_OUT_OF_MEMORY;
michael@0 769
michael@0 770 nsCOMPtr<nsIFile> fileRef(newFile); // release on exit
michael@0 771
michael@0 772 rv = newFile->InitWithNativePath(newPathName);
michael@0 773 if (NS_FAILED(rv))
michael@0 774 return rv;
michael@0 775
michael@0 776 // get the old permissions
michael@0 777 uint32_t myPerms;
michael@0 778 GetPermissions(&myPerms);
michael@0 779
michael@0 780 // Create the new file with the old file's permissions, even if write
michael@0 781 // permission is missing. We can't create with write permission and
michael@0 782 // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
michael@0 783 // But we can write to a read-only file on all Unix filesystems if we
michael@0 784 // open it successfully for writing.
michael@0 785
michael@0 786 PRFileDesc *newFD;
michael@0 787 rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
michael@0 788 PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE,
michael@0 789 myPerms,
michael@0 790 &newFD);
michael@0 791 if (NS_FAILED(rv))
michael@0 792 return rv;
michael@0 793
michael@0 794 // open the old file, too
michael@0 795 bool specialFile;
michael@0 796 if (NS_FAILED(rv = IsSpecial(&specialFile))) {
michael@0 797 PR_Close(newFD);
michael@0 798 return rv;
michael@0 799 }
michael@0 800 if (specialFile) {
michael@0 801 #ifdef DEBUG
michael@0 802 printf("Operation not supported: %s\n", mPath.get());
michael@0 803 #endif
michael@0 804 // make sure to clean up properly
michael@0 805 PR_Close(newFD);
michael@0 806 return NS_OK;
michael@0 807 }
michael@0 808
michael@0 809 PRFileDesc *oldFD;
michael@0 810 rv = OpenNSPRFileDesc(PR_RDONLY, myPerms, &oldFD);
michael@0 811 if (NS_FAILED(rv)) {
michael@0 812 // make sure to clean up properly
michael@0 813 PR_Close(newFD);
michael@0 814 return rv;
michael@0 815 }
michael@0 816
michael@0 817 #ifdef DEBUG_blizzard
michael@0 818 int32_t totalRead = 0;
michael@0 819 int32_t totalWritten = 0;
michael@0 820 #endif
michael@0 821 char buf[BUFSIZ];
michael@0 822 int32_t bytesRead;
michael@0 823
michael@0 824 // record PR_Write() error for better error message later.
michael@0 825 nsresult saved_write_error = NS_OK;
michael@0 826 nsresult saved_read_error = NS_OK;
michael@0 827 nsresult saved_read_close_error = NS_OK;
michael@0 828 nsresult saved_write_close_error = NS_OK;
michael@0 829
michael@0 830 // DONE: Does PR_Read() return bytesRead < 0 for error?
michael@0 831 // Yes., The errors from PR_Read are not so common and
michael@0 832 // the value may not have correspondence in NS_ERROR_*, but
michael@0 833 // we do catch it still, immediately after while() loop.
michael@0 834 // We can differentiate errors pf PR_Read and PR_Write by
michael@0 835 // looking at saved_write_error value. If PR_Write error occurs (and not
michael@0 836 // PR_Read() error), save_write_error is not NS_OK.
michael@0 837
michael@0 838 while ((bytesRead = PR_Read(oldFD, buf, BUFSIZ)) > 0) {
michael@0 839 #ifdef DEBUG_blizzard
michael@0 840 totalRead += bytesRead;
michael@0 841 #endif
michael@0 842
michael@0 843 // PR_Write promises never to do a short write
michael@0 844 int32_t bytesWritten = PR_Write(newFD, buf, bytesRead);
michael@0 845 if (bytesWritten < 0) {
michael@0 846 saved_write_error = NSRESULT_FOR_ERRNO();
michael@0 847 bytesRead = -1;
michael@0 848 break;
michael@0 849 }
michael@0 850 NS_ASSERTION(bytesWritten == bytesRead, "short PR_Write?");
michael@0 851
michael@0 852 #ifdef DEBUG_blizzard
michael@0 853 totalWritten += bytesWritten;
michael@0 854 #endif
michael@0 855 }
michael@0 856
michael@0 857 // TODO/FIXME: If CIFS (and NFS?) may force read/write to return EINTR,
michael@0 858 // we are better off to prepare for retrying. But we need confirmation if
michael@0 859 // EINTR is returned.
michael@0 860
michael@0 861 // Record error if PR_Read() failed.
michael@0 862 // Must be done before any other I/O which may reset errno.
michael@0 863 if ( (bytesRead < 0) && (saved_write_error == NS_OK)) {
michael@0 864 saved_read_error = NSRESULT_FOR_ERRNO();
michael@0 865 }
michael@0 866
michael@0 867 #ifdef DEBUG_blizzard
michael@0 868 printf("read %d bytes, wrote %d bytes\n",
michael@0 869 totalRead, totalWritten);
michael@0 870 #endif
michael@0 871
michael@0 872 // DONE: Errors of close can occur. Read man page of
michael@0 873 // close(2);
michael@0 874 // This is likely to happen if the file system is remote file
michael@0 875 // system (NFS, CIFS, etc.) and network outage occurs.
michael@0 876 // At least, we should tell the user that filesystem/disk is
michael@0 877 // hosed (possibly due to network error, hard disk failure,
michael@0 878 // etc.) so that users can take remedial action.
michael@0 879
michael@0 880 // close the files
michael@0 881 if (PR_Close(newFD) < 0) {
michael@0 882 saved_write_close_error = NSRESULT_FOR_ERRNO();
michael@0 883 #if DEBUG
michael@0 884 // This error merits printing.
michael@0 885 fprintf(stderr, "ERROR: PR_Close(newFD) returned error. errno = %d\n", errno);
michael@0 886 #endif
michael@0 887 }
michael@0 888
michael@0 889 if (PR_Close(oldFD) < 0) {
michael@0 890 saved_read_close_error = NSRESULT_FOR_ERRNO();
michael@0 891 #if DEBUG
michael@0 892 fprintf(stderr, "ERROR: PR_Close(oldFD) returned error. errno = %d\n", errno);
michael@0 893 #endif
michael@0 894 }
michael@0 895
michael@0 896 // Let us report the failure to write and read.
michael@0 897 // check for write/read error after cleaning up
michael@0 898 if (bytesRead < 0) {
michael@0 899 if (saved_write_error != NS_OK)
michael@0 900 return saved_write_error;
michael@0 901 else if (saved_read_error != NS_OK)
michael@0 902 return saved_read_error;
michael@0 903 #if DEBUG
michael@0 904 else // sanity check. Die and debug.
michael@0 905 MOZ_ASSERT(0);
michael@0 906 #endif
michael@0 907 }
michael@0 908
michael@0 909 if (saved_write_close_error != NS_OK)
michael@0 910 return saved_write_close_error;
michael@0 911 if (saved_read_close_error != NS_OK)
michael@0 912 return saved_read_close_error;
michael@0 913 }
michael@0 914 return rv;
michael@0 915 }
michael@0 916
michael@0 917 NS_IMETHODIMP
michael@0 918 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
michael@0 919 {
michael@0 920 return CopyToNative(newParent, newName);
michael@0 921 }
michael@0 922
michael@0 923 NS_IMETHODIMP
michael@0 924 nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
michael@0 925 {
michael@0 926 nsresult rv;
michael@0 927
michael@0 928 // check to make sure that this has been initialized properly
michael@0 929 CHECK_mPath();
michael@0 930
michael@0 931 // check to make sure that we have a new parent
michael@0 932 nsAutoCString newPathName;
michael@0 933 rv = GetNativeTargetPathName(newParent, newName, newPathName);
michael@0 934 if (NS_FAILED(rv))
michael@0 935 return rv;
michael@0 936
michael@0 937 // try for atomic rename, falling back to copy/delete
michael@0 938 if (rename(mPath.get(), newPathName.get()) < 0) {
michael@0 939 #ifdef VMS
michael@0 940 if (errno == EXDEV || errno == ENXIO) {
michael@0 941 #else
michael@0 942 if (errno == EXDEV) {
michael@0 943 #endif
michael@0 944 rv = CopyToNative(newParent, newName);
michael@0 945 if (NS_SUCCEEDED(rv))
michael@0 946 rv = Remove(true);
michael@0 947 } else {
michael@0 948 rv = NSRESULT_FOR_ERRNO();
michael@0 949 }
michael@0 950 }
michael@0 951
michael@0 952 if (NS_SUCCEEDED(rv)) {
michael@0 953 // Adjust this
michael@0 954 mPath = newPathName;
michael@0 955 }
michael@0 956 return rv;
michael@0 957 }
michael@0 958
michael@0 959 NS_IMETHODIMP
michael@0 960 nsLocalFile::Remove(bool recursive)
michael@0 961 {
michael@0 962 CHECK_mPath();
michael@0 963 ENSURE_STAT_CACHE();
michael@0 964
michael@0 965 bool isSymLink;
michael@0 966
michael@0 967 nsresult rv = IsSymlink(&isSymLink);
michael@0 968 if (NS_FAILED(rv))
michael@0 969 return rv;
michael@0 970
michael@0 971 if (isSymLink || !S_ISDIR(mCachedStat.st_mode))
michael@0 972 return NSRESULT_FOR_RETURN(unlink(mPath.get()));
michael@0 973
michael@0 974 if (recursive) {
michael@0 975 nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
michael@0 976
michael@0 977 nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
michael@0 978
michael@0 979 rv = dir->Init(this, false);
michael@0 980 if (NS_FAILED(rv))
michael@0 981 return rv;
michael@0 982
michael@0 983 bool more;
michael@0 984 while (dir->HasMoreElements(&more), more) {
michael@0 985 nsCOMPtr<nsISupports> item;
michael@0 986 rv = dir->GetNext(getter_AddRefs(item));
michael@0 987 if (NS_FAILED(rv))
michael@0 988 return NS_ERROR_FAILURE;
michael@0 989
michael@0 990 nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
michael@0 991 if (NS_FAILED(rv))
michael@0 992 return NS_ERROR_FAILURE;
michael@0 993 rv = file->Remove(recursive);
michael@0 994
michael@0 995 #ifdef ANDROID
michael@0 996 // See bug 580434 - Bionic gives us just deleted files
michael@0 997 if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
michael@0 998 continue;
michael@0 999 #endif
michael@0 1000 if (NS_FAILED(rv))
michael@0 1001 return rv;
michael@0 1002 }
michael@0 1003 }
michael@0 1004
michael@0 1005 return NSRESULT_FOR_RETURN(rmdir(mPath.get()));
michael@0 1006 }
michael@0 1007
michael@0 1008 NS_IMETHODIMP
michael@0 1009 nsLocalFile::GetLastModifiedTime(PRTime *aLastModTime)
michael@0 1010 {
michael@0 1011 CHECK_mPath();
michael@0 1012 if (NS_WARN_IF(!aLastModTime))
michael@0 1013 return NS_ERROR_INVALID_ARG;
michael@0 1014
michael@0 1015 PRFileInfo64 info;
michael@0 1016 if (PR_GetFileInfo64(mPath.get(), &info) != PR_SUCCESS)
michael@0 1017 return NSRESULT_FOR_ERRNO();
michael@0 1018 PRTime modTime = info.modifyTime;
michael@0 1019 if (modTime == 0)
michael@0 1020 *aLastModTime = 0;
michael@0 1021 else
michael@0 1022 *aLastModTime = modTime / PR_USEC_PER_MSEC;
michael@0 1023
michael@0 1024 return NS_OK;
michael@0 1025 }
michael@0 1026
michael@0 1027 NS_IMETHODIMP
michael@0 1028 nsLocalFile::SetLastModifiedTime(PRTime aLastModTime)
michael@0 1029 {
michael@0 1030 CHECK_mPath();
michael@0 1031
michael@0 1032 int result;
michael@0 1033 if (aLastModTime != 0) {
michael@0 1034 ENSURE_STAT_CACHE();
michael@0 1035 struct utimbuf ut;
michael@0 1036 ut.actime = mCachedStat.st_atime;
michael@0 1037
michael@0 1038 // convert milliseconds to seconds since the unix epoch
michael@0 1039 ut.modtime = (time_t)(aLastModTime / PR_MSEC_PER_SEC);
michael@0 1040 result = utime(mPath.get(), &ut);
michael@0 1041 } else {
michael@0 1042 result = utime(mPath.get(), nullptr);
michael@0 1043 }
michael@0 1044 return NSRESULT_FOR_RETURN(result);
michael@0 1045 }
michael@0 1046
michael@0 1047 NS_IMETHODIMP
michael@0 1048 nsLocalFile::GetLastModifiedTimeOfLink(PRTime *aLastModTimeOfLink)
michael@0 1049 {
michael@0 1050 CHECK_mPath();
michael@0 1051 if (NS_WARN_IF(!aLastModTimeOfLink))
michael@0 1052 return NS_ERROR_INVALID_ARG;
michael@0 1053
michael@0 1054 struct STAT sbuf;
michael@0 1055 if (LSTAT(mPath.get(), &sbuf) == -1)
michael@0 1056 return NSRESULT_FOR_ERRNO();
michael@0 1057 *aLastModTimeOfLink = PRTime(sbuf.st_mtime) * PR_MSEC_PER_SEC;
michael@0 1058
michael@0 1059 return NS_OK;
michael@0 1060 }
michael@0 1061
michael@0 1062 /*
michael@0 1063 * utime(2) may or may not dereference symlinks, joy.
michael@0 1064 */
michael@0 1065 NS_IMETHODIMP
michael@0 1066 nsLocalFile::SetLastModifiedTimeOfLink(PRTime aLastModTimeOfLink)
michael@0 1067 {
michael@0 1068 return SetLastModifiedTime(aLastModTimeOfLink);
michael@0 1069 }
michael@0 1070
michael@0 1071 /*
michael@0 1072 * Only send back permissions bits: maybe we want to send back the whole
michael@0 1073 * mode_t to permit checks against other file types?
michael@0 1074 */
michael@0 1075
michael@0 1076 #define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
michael@0 1077
michael@0 1078 NS_IMETHODIMP
michael@0 1079 nsLocalFile::GetPermissions(uint32_t *aPermissions)
michael@0 1080 {
michael@0 1081 if (NS_WARN_IF(!aPermissions))
michael@0 1082 return NS_ERROR_INVALID_ARG;
michael@0 1083 ENSURE_STAT_CACHE();
michael@0 1084 *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
michael@0 1085 return NS_OK;
michael@0 1086 }
michael@0 1087
michael@0 1088 NS_IMETHODIMP
michael@0 1089 nsLocalFile::GetPermissionsOfLink(uint32_t *aPermissionsOfLink)
michael@0 1090 {
michael@0 1091 CHECK_mPath();
michael@0 1092 if (NS_WARN_IF(!aPermissionsOfLink))
michael@0 1093 return NS_ERROR_INVALID_ARG;
michael@0 1094
michael@0 1095 struct STAT sbuf;
michael@0 1096 if (LSTAT(mPath.get(), &sbuf) == -1)
michael@0 1097 return NSRESULT_FOR_ERRNO();
michael@0 1098 *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
michael@0 1099 return NS_OK;
michael@0 1100 }
michael@0 1101
michael@0 1102 NS_IMETHODIMP
michael@0 1103 nsLocalFile::SetPermissions(uint32_t aPermissions)
michael@0 1104 {
michael@0 1105 CHECK_mPath();
michael@0 1106
michael@0 1107 /*
michael@0 1108 * Race condition here: we should use fchmod instead, there's no way to
michael@0 1109 * guarantee the name still refers to the same file.
michael@0 1110 */
michael@0 1111 if (chmod(mPath.get(), aPermissions) >= 0)
michael@0 1112 return NS_OK;
michael@0 1113 #if defined(ANDROID) && defined(STATFS)
michael@0 1114 // For the time being, this is restricted for use by Android, but we
michael@0 1115 // will figure out what to do for all platforms in bug 638503
michael@0 1116 struct STATFS sfs;
michael@0 1117 if (STATFS(mPath.get(), &sfs) < 0)
michael@0 1118 return NSRESULT_FOR_ERRNO();
michael@0 1119
michael@0 1120 // if this is a FAT file system we can't set file permissions
michael@0 1121 if (sfs.f_type == MSDOS_SUPER_MAGIC )
michael@0 1122 return NS_OK;
michael@0 1123 #endif
michael@0 1124 return NSRESULT_FOR_ERRNO();
michael@0 1125 }
michael@0 1126
michael@0 1127 NS_IMETHODIMP
michael@0 1128 nsLocalFile::SetPermissionsOfLink(uint32_t aPermissions)
michael@0 1129 {
michael@0 1130 // There isn't a consistent mechanism for doing this on UNIX platforms. We
michael@0 1131 // might want to carefully implement this in the future though.
michael@0 1132 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 1133 }
michael@0 1134
michael@0 1135 NS_IMETHODIMP
michael@0 1136 nsLocalFile::GetFileSize(int64_t *aFileSize)
michael@0 1137 {
michael@0 1138 if (NS_WARN_IF(!aFileSize))
michael@0 1139 return NS_ERROR_INVALID_ARG;
michael@0 1140 *aFileSize = 0;
michael@0 1141 ENSURE_STAT_CACHE();
michael@0 1142
michael@0 1143 #if defined(VMS)
michael@0 1144 /* Only two record formats can report correct file content size */
michael@0 1145 if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
michael@0 1146 (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
michael@0 1147 return NS_ERROR_FAILURE;
michael@0 1148 }
michael@0 1149 #endif
michael@0 1150
michael@0 1151 if (!S_ISDIR(mCachedStat.st_mode)) {
michael@0 1152 *aFileSize = (int64_t)mCachedStat.st_size;
michael@0 1153 }
michael@0 1154 return NS_OK;
michael@0 1155 }
michael@0 1156
michael@0 1157 NS_IMETHODIMP
michael@0 1158 nsLocalFile::SetFileSize(int64_t aFileSize)
michael@0 1159 {
michael@0 1160 CHECK_mPath();
michael@0 1161
michael@0 1162 #if defined(ANDROID)
michael@0 1163 /* no truncate on bionic */
michael@0 1164 int fd = open(mPath.get(), O_WRONLY);
michael@0 1165 if (fd == -1)
michael@0 1166 return NSRESULT_FOR_ERRNO();
michael@0 1167
michael@0 1168 int ret = ftruncate(fd, (off_t)aFileSize);
michael@0 1169 close(fd);
michael@0 1170
michael@0 1171 if (ret == -1)
michael@0 1172 return NSRESULT_FOR_ERRNO();
michael@0 1173 #elif defined(HAVE_TRUNCATE64)
michael@0 1174 if (truncate64(mPath.get(), (off64_t)aFileSize) == -1)
michael@0 1175 return NSRESULT_FOR_ERRNO();
michael@0 1176 #else
michael@0 1177 off_t size = (off_t)aFileSize;
michael@0 1178 if (truncate(mPath.get(), size) == -1)
michael@0 1179 return NSRESULT_FOR_ERRNO();
michael@0 1180 #endif
michael@0 1181 return NS_OK;
michael@0 1182 }
michael@0 1183
michael@0 1184 NS_IMETHODIMP
michael@0 1185 nsLocalFile::GetFileSizeOfLink(int64_t *aFileSize)
michael@0 1186 {
michael@0 1187 CHECK_mPath();
michael@0 1188 if (NS_WARN_IF(!aFileSize))
michael@0 1189 return NS_ERROR_INVALID_ARG;
michael@0 1190
michael@0 1191 struct STAT sbuf;
michael@0 1192 if (LSTAT(mPath.get(), &sbuf) == -1)
michael@0 1193 return NSRESULT_FOR_ERRNO();
michael@0 1194
michael@0 1195 *aFileSize = (int64_t)sbuf.st_size;
michael@0 1196 return NS_OK;
michael@0 1197 }
michael@0 1198
michael@0 1199 #if defined(USE_LINUX_QUOTACTL)
michael@0 1200 /*
michael@0 1201 * Searches /proc/self/mountinfo for given device (Major:Minor),
michael@0 1202 * returns exported name from /dev
michael@0 1203 *
michael@0 1204 * Fails when /proc/self/mountinfo or diven device don't exist.
michael@0 1205 */
michael@0 1206 static bool
michael@0 1207 GetDeviceName(int deviceMajor, int deviceMinor, nsACString &deviceName)
michael@0 1208 {
michael@0 1209 bool ret = false;
michael@0 1210
michael@0 1211 const int kMountInfoLineLength = 200;
michael@0 1212 const int kMountInfoDevPosition = 6;
michael@0 1213
michael@0 1214 char mountinfo_line[kMountInfoLineLength];
michael@0 1215 char device_num[kMountInfoLineLength];
michael@0 1216
michael@0 1217 snprintf(device_num,kMountInfoLineLength,"%d:%d", deviceMajor, deviceMinor);
michael@0 1218
michael@0 1219 FILE *f = fopen("/proc/self/mountinfo","rt");
michael@0 1220 if(!f)
michael@0 1221 return ret;
michael@0 1222
michael@0 1223 // Expects /proc/self/mountinfo in format:
michael@0 1224 // 'ID ID major:minor root mountpoint flags - type devicename flags'
michael@0 1225 while(fgets(mountinfo_line,kMountInfoLineLength,f)) {
michael@0 1226 char *p_dev = strstr(mountinfo_line,device_num);
michael@0 1227
michael@0 1228 int i;
michael@0 1229 for(i = 0; i < kMountInfoDevPosition && p_dev != nullptr; i++) {
michael@0 1230 p_dev = strchr(p_dev,' ');
michael@0 1231 if(p_dev)
michael@0 1232 p_dev++;
michael@0 1233 }
michael@0 1234
michael@0 1235 if(p_dev) {
michael@0 1236 char *p_dev_end = strchr(p_dev,' ');
michael@0 1237 if(p_dev_end) {
michael@0 1238 *p_dev_end = '\0';
michael@0 1239 deviceName.Assign(p_dev);
michael@0 1240 ret = true;
michael@0 1241 break;
michael@0 1242 }
michael@0 1243 }
michael@0 1244 }
michael@0 1245
michael@0 1246 fclose(f);
michael@0 1247 return ret;
michael@0 1248 }
michael@0 1249 #endif
michael@0 1250
michael@0 1251 NS_IMETHODIMP
michael@0 1252 nsLocalFile::GetDiskSpaceAvailable(int64_t *aDiskSpaceAvailable)
michael@0 1253 {
michael@0 1254 if (NS_WARN_IF(!aDiskSpaceAvailable))
michael@0 1255 return NS_ERROR_INVALID_ARG;
michael@0 1256
michael@0 1257 // These systems have the operations necessary to check disk space.
michael@0 1258
michael@0 1259 #ifdef STATFS
michael@0 1260
michael@0 1261 // check to make sure that mPath is properly initialized
michael@0 1262 CHECK_mPath();
michael@0 1263
michael@0 1264 struct STATFS fs_buf;
michael@0 1265
michael@0 1266 /*
michael@0 1267 * Members of the STATFS struct that you should know about:
michael@0 1268 * F_BSIZE = block size on disk.
michael@0 1269 * f_bavail = number of free blocks available to a non-superuser.
michael@0 1270 * f_bfree = number of total free blocks in file system.
michael@0 1271 */
michael@0 1272
michael@0 1273 if (STATFS(mPath.get(), &fs_buf) < 0) {
michael@0 1274 // The call to STATFS failed.
michael@0 1275 #ifdef DEBUG
michael@0 1276 printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
michael@0 1277 #endif
michael@0 1278 return NS_ERROR_FAILURE;
michael@0 1279 }
michael@0 1280
michael@0 1281 *aDiskSpaceAvailable = (int64_t) fs_buf.F_BSIZE * fs_buf.f_bavail;
michael@0 1282
michael@0 1283 #ifdef DEBUG_DISK_SPACE
michael@0 1284 printf("DiskSpaceAvailable: %lu bytes\n",
michael@0 1285 *aDiskSpaceAvailable);
michael@0 1286 #endif
michael@0 1287
michael@0 1288 #if defined(USE_LINUX_QUOTACTL)
michael@0 1289
michael@0 1290 if(!FillStatCache()) {
michael@0 1291 // Return available size from statfs
michael@0 1292 return NS_OK;
michael@0 1293 }
michael@0 1294
michael@0 1295 nsCString deviceName;
michael@0 1296 if(!GetDeviceName(major(mCachedStat.st_dev), minor(mCachedStat.st_dev), deviceName)) {
michael@0 1297 return NS_OK;
michael@0 1298 }
michael@0 1299
michael@0 1300 struct dqblk dq;
michael@0 1301 if(!quotactl(QCMD(Q_GETQUOTA, USRQUOTA), deviceName.get(), getuid(), (caddr_t)&dq)
michael@0 1302 #ifdef QIF_BLIMITS
michael@0 1303 && dq.dqb_valid & QIF_BLIMITS
michael@0 1304 #endif
michael@0 1305 && dq.dqb_bhardlimit)
michael@0 1306 {
michael@0 1307 int64_t QuotaSpaceAvailable = 0;
michael@0 1308 if (dq.dqb_bhardlimit > dq.dqb_curspace)
michael@0 1309 QuotaSpaceAvailable = int64_t(fs_buf.F_BSIZE * (dq.dqb_bhardlimit - dq.dqb_curspace));
michael@0 1310 if(QuotaSpaceAvailable < *aDiskSpaceAvailable) {
michael@0 1311 *aDiskSpaceAvailable = QuotaSpaceAvailable;
michael@0 1312 }
michael@0 1313 }
michael@0 1314 #endif
michael@0 1315
michael@0 1316 return NS_OK;
michael@0 1317
michael@0 1318 #else
michael@0 1319 /*
michael@0 1320 * This platform doesn't have statfs or statvfs. I'm sure that there's
michael@0 1321 * a way to check for free disk space on platforms that don't have statfs
michael@0 1322 * (I'm SURE they have df, for example).
michael@0 1323 *
michael@0 1324 * Until we figure out how to do that, lets be honest and say that this
michael@0 1325 * command isn't implemented properly for these platforms yet.
michael@0 1326 */
michael@0 1327 #ifdef DEBUG
michael@0 1328 printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
michael@0 1329 #endif
michael@0 1330 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 1331
michael@0 1332 #endif /* STATFS */
michael@0 1333
michael@0 1334 }
michael@0 1335
michael@0 1336 NS_IMETHODIMP
michael@0 1337 nsLocalFile::GetParent(nsIFile **aParent)
michael@0 1338 {
michael@0 1339 CHECK_mPath();
michael@0 1340 if (NS_WARN_IF(!aParent))
michael@0 1341 return NS_ERROR_INVALID_ARG;
michael@0 1342 *aParent = nullptr;
michael@0 1343
michael@0 1344 // if '/' we are at the top of the volume, return null
michael@0 1345 if (mPath.Equals("/"))
michael@0 1346 return NS_OK;
michael@0 1347
michael@0 1348 // <brendan, after jband> I promise to play nice
michael@0 1349 char *buffer = mPath.BeginWriting(),
michael@0 1350 *slashp = buffer;
michael@0 1351
michael@0 1352 // find the last significant slash in buffer
michael@0 1353 slashp = strrchr(buffer, '/');
michael@0 1354 NS_ASSERTION(slashp, "non-canonical path?");
michael@0 1355 if (!slashp)
michael@0 1356 return NS_ERROR_FILE_INVALID_PATH;
michael@0 1357
michael@0 1358 // for the case where we are at '/'
michael@0 1359 if (slashp == buffer)
michael@0 1360 slashp++;
michael@0 1361
michael@0 1362 // temporarily terminate buffer at the last significant slash
michael@0 1363 char c = *slashp;
michael@0 1364 *slashp = '\0';
michael@0 1365
michael@0 1366 nsCOMPtr<nsIFile> localFile;
michael@0 1367 nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), true,
michael@0 1368 getter_AddRefs(localFile));
michael@0 1369
michael@0 1370 // make buffer whole again
michael@0 1371 *slashp = c;
michael@0 1372
michael@0 1373 if (NS_FAILED(rv)) {
michael@0 1374 return rv;
michael@0 1375 }
michael@0 1376
michael@0 1377 localFile.forget(aParent);
michael@0 1378 return NS_OK;
michael@0 1379 }
michael@0 1380
michael@0 1381 /*
michael@0 1382 * The results of Exists, isWritable and isReadable are not cached.
michael@0 1383 */
michael@0 1384
michael@0 1385
michael@0 1386 NS_IMETHODIMP
michael@0 1387 nsLocalFile::Exists(bool *_retval)
michael@0 1388 {
michael@0 1389 CHECK_mPath();
michael@0 1390 if (NS_WARN_IF(!_retval))
michael@0 1391 return NS_ERROR_INVALID_ARG;
michael@0 1392
michael@0 1393 *_retval = (access(mPath.get(), F_OK) == 0);
michael@0 1394 return NS_OK;
michael@0 1395 }
michael@0 1396
michael@0 1397
michael@0 1398 NS_IMETHODIMP
michael@0 1399 nsLocalFile::IsWritable(bool *_retval)
michael@0 1400 {
michael@0 1401 CHECK_mPath();
michael@0 1402 if (NS_WARN_IF(!_retval))
michael@0 1403 return NS_ERROR_INVALID_ARG;
michael@0 1404
michael@0 1405 *_retval = (access(mPath.get(), W_OK) == 0);
michael@0 1406 if (*_retval || errno == EACCES)
michael@0 1407 return NS_OK;
michael@0 1408 return NSRESULT_FOR_ERRNO();
michael@0 1409 }
michael@0 1410
michael@0 1411 NS_IMETHODIMP
michael@0 1412 nsLocalFile::IsReadable(bool *_retval)
michael@0 1413 {
michael@0 1414 CHECK_mPath();
michael@0 1415 if (NS_WARN_IF(!_retval))
michael@0 1416 return NS_ERROR_INVALID_ARG;
michael@0 1417
michael@0 1418 *_retval = (access(mPath.get(), R_OK) == 0);
michael@0 1419 if (*_retval || errno == EACCES)
michael@0 1420 return NS_OK;
michael@0 1421 return NSRESULT_FOR_ERRNO();
michael@0 1422 }
michael@0 1423
michael@0 1424 NS_IMETHODIMP
michael@0 1425 nsLocalFile::IsExecutable(bool *_retval)
michael@0 1426 {
michael@0 1427 CHECK_mPath();
michael@0 1428 if (NS_WARN_IF(!_retval))
michael@0 1429 return NS_ERROR_INVALID_ARG;
michael@0 1430
michael@0 1431 // Check extension (bug 663899). On certain platforms, the file
michael@0 1432 // extension may cause the OS to treat it as executable regardless of
michael@0 1433 // the execute bit, such as .jar on Mac OS X. We borrow the code from
michael@0 1434 // nsLocalFileWin, slightly modified.
michael@0 1435
michael@0 1436 // Don't be fooled by symlinks.
michael@0 1437 bool symLink;
michael@0 1438 nsresult rv = IsSymlink(&symLink);
michael@0 1439 if (NS_FAILED(rv))
michael@0 1440 return rv;
michael@0 1441
michael@0 1442 nsAutoString path;
michael@0 1443 if (symLink)
michael@0 1444 GetTarget(path);
michael@0 1445 else
michael@0 1446 GetPath(path);
michael@0 1447
michael@0 1448 int32_t dotIdx = path.RFindChar(char16_t('.'));
michael@0 1449 if (dotIdx != kNotFound) {
michael@0 1450 // Convert extension to lower case.
michael@0 1451 char16_t *p = path.BeginWriting();
michael@0 1452 for(p += dotIdx + 1; *p; p++)
michael@0 1453 *p += (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0;
michael@0 1454
michael@0 1455 // Search for any of the set of executable extensions.
michael@0 1456 static const char * const executableExts[] = {
michael@0 1457 "air", // Adobe AIR installer
michael@0 1458 "jar"}; // java application bundle
michael@0 1459 nsDependentSubstring ext = Substring(path, dotIdx + 1);
michael@0 1460 for (size_t i = 0; i < ArrayLength(executableExts); i++) {
michael@0 1461 if (ext.EqualsASCII(executableExts[i])) {
michael@0 1462 // Found a match. Set result and quit.
michael@0 1463 *_retval = true;
michael@0 1464 return NS_OK;
michael@0 1465 }
michael@0 1466 }
michael@0 1467 }
michael@0 1468
michael@0 1469 // On OS X, then query Launch Services.
michael@0 1470 #ifdef MOZ_WIDGET_COCOA
michael@0 1471 // Certain Mac applications, such as Classic applications, which
michael@0 1472 // run under Rosetta, might not have the +x mode bit but are still
michael@0 1473 // considered to be executable by Launch Services (bug 646748).
michael@0 1474 CFURLRef url;
michael@0 1475 if (NS_FAILED(GetCFURL(&url))) {
michael@0 1476 return NS_ERROR_FAILURE;
michael@0 1477 }
michael@0 1478
michael@0 1479 LSRequestedInfo theInfoRequest = kLSRequestAllInfo;
michael@0 1480 LSItemInfoRecord theInfo;
michael@0 1481 OSStatus result = ::LSCopyItemInfoForURL(url, theInfoRequest, &theInfo);
michael@0 1482 ::CFRelease(url);
michael@0 1483 if (result == noErr) {
michael@0 1484 if ((theInfo.flags & kLSItemInfoIsApplication) != 0) {
michael@0 1485 *_retval = true;
michael@0 1486 return NS_OK;
michael@0 1487 }
michael@0 1488 }
michael@0 1489 #endif
michael@0 1490
michael@0 1491 // Then check the execute bit.
michael@0 1492 *_retval = (access(mPath.get(), X_OK) == 0);
michael@0 1493 #ifdef SOLARIS
michael@0 1494 // On Solaris, access will always return 0 for root user, however
michael@0 1495 // the file is only executable if S_IXUSR | S_IXGRP | S_IXOTH is set.
michael@0 1496 // See bug 351950, https://bugzilla.mozilla.org/show_bug.cgi?id=351950
michael@0 1497 if (*_retval) {
michael@0 1498 struct STAT buf;
michael@0 1499
michael@0 1500 *_retval = (STAT(mPath.get(), &buf) == 0);
michael@0 1501 if (*_retval || errno == EACCES) {
michael@0 1502 *_retval = *_retval &&
michael@0 1503 (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
michael@0 1504 return NS_OK;
michael@0 1505 }
michael@0 1506
michael@0 1507 return NSRESULT_FOR_ERRNO();
michael@0 1508 }
michael@0 1509 #endif
michael@0 1510 if (*_retval || errno == EACCES)
michael@0 1511 return NS_OK;
michael@0 1512 return NSRESULT_FOR_ERRNO();
michael@0 1513 }
michael@0 1514
michael@0 1515 NS_IMETHODIMP
michael@0 1516 nsLocalFile::IsDirectory(bool *_retval)
michael@0 1517 {
michael@0 1518 if (NS_WARN_IF(!_retval))
michael@0 1519 return NS_ERROR_INVALID_ARG;
michael@0 1520 *_retval = false;
michael@0 1521 ENSURE_STAT_CACHE();
michael@0 1522 *_retval = S_ISDIR(mCachedStat.st_mode);
michael@0 1523 return NS_OK;
michael@0 1524 }
michael@0 1525
michael@0 1526 NS_IMETHODIMP
michael@0 1527 nsLocalFile::IsFile(bool *_retval)
michael@0 1528 {
michael@0 1529 if (NS_WARN_IF(!_retval))
michael@0 1530 return NS_ERROR_INVALID_ARG;
michael@0 1531 *_retval = false;
michael@0 1532 ENSURE_STAT_CACHE();
michael@0 1533 *_retval = S_ISREG(mCachedStat.st_mode);
michael@0 1534 return NS_OK;
michael@0 1535 }
michael@0 1536
michael@0 1537 NS_IMETHODIMP
michael@0 1538 nsLocalFile::IsHidden(bool *_retval)
michael@0 1539 {
michael@0 1540 if (NS_WARN_IF(!_retval))
michael@0 1541 return NS_ERROR_INVALID_ARG;
michael@0 1542 nsACString::const_iterator begin, end;
michael@0 1543 LocateNativeLeafName(begin, end);
michael@0 1544 *_retval = (*begin == '.');
michael@0 1545 return NS_OK;
michael@0 1546 }
michael@0 1547
michael@0 1548 NS_IMETHODIMP
michael@0 1549 nsLocalFile::IsSymlink(bool *_retval)
michael@0 1550 {
michael@0 1551 if (NS_WARN_IF(!_retval))
michael@0 1552 return NS_ERROR_INVALID_ARG;
michael@0 1553 CHECK_mPath();
michael@0 1554
michael@0 1555 struct STAT symStat;
michael@0 1556 if (LSTAT(mPath.get(), &symStat) == -1)
michael@0 1557 return NSRESULT_FOR_ERRNO();
michael@0 1558 *_retval=S_ISLNK(symStat.st_mode);
michael@0 1559 return NS_OK;
michael@0 1560 }
michael@0 1561
michael@0 1562 NS_IMETHODIMP
michael@0 1563 nsLocalFile::IsSpecial(bool *_retval)
michael@0 1564 {
michael@0 1565 if (NS_WARN_IF(!_retval))
michael@0 1566 return NS_ERROR_INVALID_ARG;
michael@0 1567 ENSURE_STAT_CACHE();
michael@0 1568 *_retval = S_ISCHR(mCachedStat.st_mode) ||
michael@0 1569 S_ISBLK(mCachedStat.st_mode) ||
michael@0 1570 #ifdef S_ISSOCK
michael@0 1571 S_ISSOCK(mCachedStat.st_mode) ||
michael@0 1572 #endif
michael@0 1573 S_ISFIFO(mCachedStat.st_mode);
michael@0 1574
michael@0 1575 return NS_OK;
michael@0 1576 }
michael@0 1577
michael@0 1578 NS_IMETHODIMP
michael@0 1579 nsLocalFile::Equals(nsIFile *inFile, bool *_retval)
michael@0 1580 {
michael@0 1581 if (NS_WARN_IF(!inFile))
michael@0 1582 return NS_ERROR_INVALID_ARG;
michael@0 1583 if (NS_WARN_IF(!_retval))
michael@0 1584 return NS_ERROR_INVALID_ARG;
michael@0 1585 *_retval = false;
michael@0 1586
michael@0 1587 nsAutoCString inPath;
michael@0 1588 nsresult rv = inFile->GetNativePath(inPath);
michael@0 1589 if (NS_FAILED(rv))
michael@0 1590 return rv;
michael@0 1591
michael@0 1592 // We don't need to worry about "/foo/" vs. "/foo" here
michael@0 1593 // because trailing slashes are stripped on init.
michael@0 1594 *_retval = !strcmp(inPath.get(), mPath.get());
michael@0 1595 return NS_OK;
michael@0 1596 }
michael@0 1597
michael@0 1598 NS_IMETHODIMP
michael@0 1599 nsLocalFile::Contains(nsIFile *inFile, bool recur, bool *_retval)
michael@0 1600 {
michael@0 1601 CHECK_mPath();
michael@0 1602 if (NS_WARN_IF(!inFile))
michael@0 1603 return NS_ERROR_INVALID_ARG;
michael@0 1604 if (NS_WARN_IF(!_retval))
michael@0 1605 return NS_ERROR_INVALID_ARG;
michael@0 1606
michael@0 1607 nsAutoCString inPath;
michael@0 1608 nsresult rv;
michael@0 1609
michael@0 1610 if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
michael@0 1611 return rv;
michael@0 1612
michael@0 1613 *_retval = false;
michael@0 1614
michael@0 1615 ssize_t len = mPath.Length();
michael@0 1616 if (strncmp(mPath.get(), inPath.get(), len) == 0) {
michael@0 1617 // Now make sure that the |inFile|'s path has a separator at len,
michael@0 1618 // which implies that it has more components after len.
michael@0 1619 if (inPath[len] == '/')
michael@0 1620 *_retval = true;
michael@0 1621 }
michael@0 1622
michael@0 1623 return NS_OK;
michael@0 1624 }
michael@0 1625
michael@0 1626 NS_IMETHODIMP
michael@0 1627 nsLocalFile::GetNativeTarget(nsACString &_retval)
michael@0 1628 {
michael@0 1629 CHECK_mPath();
michael@0 1630 _retval.Truncate();
michael@0 1631
michael@0 1632 struct STAT symStat;
michael@0 1633 if (LSTAT(mPath.get(), &symStat) == -1)
michael@0 1634 return NSRESULT_FOR_ERRNO();
michael@0 1635
michael@0 1636 if (!S_ISLNK(symStat.st_mode))
michael@0 1637 return NS_ERROR_FILE_INVALID_PATH;
michael@0 1638
michael@0 1639 int32_t size = (int32_t)symStat.st_size;
michael@0 1640 char *target = (char *)nsMemory::Alloc(size + 1);
michael@0 1641 if (!target)
michael@0 1642 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1643
michael@0 1644 if (readlink(mPath.get(), target, (size_t)size) < 0) {
michael@0 1645 nsMemory::Free(target);
michael@0 1646 return NSRESULT_FOR_ERRNO();
michael@0 1647 }
michael@0 1648 target[size] = '\0';
michael@0 1649
michael@0 1650 nsresult rv = NS_OK;
michael@0 1651 nsCOMPtr<nsIFile> self(this);
michael@0 1652 int32_t maxLinks = 40;
michael@0 1653 while (true) {
michael@0 1654 if (maxLinks-- == 0) {
michael@0 1655 rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
michael@0 1656 break;
michael@0 1657 }
michael@0 1658
michael@0 1659 if (target[0] != '/') {
michael@0 1660 nsCOMPtr<nsIFile> parent;
michael@0 1661 if (NS_FAILED(rv = self->GetParent(getter_AddRefs(parent))))
michael@0 1662 break;
michael@0 1663 if (NS_FAILED(rv = parent->AppendRelativeNativePath(nsDependentCString(target))))
michael@0 1664 break;
michael@0 1665 if (NS_FAILED(rv = parent->GetNativePath(_retval)))
michael@0 1666 break;
michael@0 1667 self = parent;
michael@0 1668 } else {
michael@0 1669 _retval = target;
michael@0 1670 }
michael@0 1671
michael@0 1672 const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
michael@0 1673
michael@0 1674 // Any failure in testing the current target we'll just interpret
michael@0 1675 // as having reached our destiny.
michael@0 1676 if (LSTAT(flatRetval.get(), &symStat) == -1)
michael@0 1677 break;
michael@0 1678
michael@0 1679 // And of course we're done if it isn't a symlink.
michael@0 1680 if (!S_ISLNK(symStat.st_mode))
michael@0 1681 break;
michael@0 1682
michael@0 1683 int32_t newSize = (int32_t)symStat.st_size;
michael@0 1684 if (newSize > size) {
michael@0 1685 char *newTarget = (char *)nsMemory::Realloc(target, newSize + 1);
michael@0 1686 if (!newTarget) {
michael@0 1687 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 1688 break;
michael@0 1689 }
michael@0 1690 target = newTarget;
michael@0 1691 size = newSize;
michael@0 1692 }
michael@0 1693
michael@0 1694 int32_t linkLen = readlink(flatRetval.get(), target, size);
michael@0 1695 if (linkLen == -1) {
michael@0 1696 rv = NSRESULT_FOR_ERRNO();
michael@0 1697 break;
michael@0 1698 }
michael@0 1699 target[linkLen] = '\0';
michael@0 1700 }
michael@0 1701
michael@0 1702 nsMemory::Free(target);
michael@0 1703
michael@0 1704 if (NS_FAILED(rv))
michael@0 1705 _retval.Truncate();
michael@0 1706 return rv;
michael@0 1707 }
michael@0 1708
michael@0 1709 NS_IMETHODIMP
michael@0 1710 nsLocalFile::GetFollowLinks(bool *aFollowLinks)
michael@0 1711 {
michael@0 1712 *aFollowLinks = true;
michael@0 1713 return NS_OK;
michael@0 1714 }
michael@0 1715
michael@0 1716 NS_IMETHODIMP
michael@0 1717 nsLocalFile::SetFollowLinks(bool aFollowLinks)
michael@0 1718 {
michael@0 1719 return NS_OK;
michael@0 1720 }
michael@0 1721
michael@0 1722 NS_IMETHODIMP
michael@0 1723 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
michael@0 1724 {
michael@0 1725 nsRefPtr<nsDirEnumeratorUnix> dir = new nsDirEnumeratorUnix();
michael@0 1726
michael@0 1727 nsresult rv = dir->Init(this, false);
michael@0 1728 if (NS_FAILED(rv)) {
michael@0 1729 *entries = nullptr;
michael@0 1730 } else {
michael@0 1731 dir.forget(entries);
michael@0 1732 }
michael@0 1733
michael@0 1734 return rv;
michael@0 1735 }
michael@0 1736
michael@0 1737 NS_IMETHODIMP
michael@0 1738 nsLocalFile::Load(PRLibrary **_retval)
michael@0 1739 {
michael@0 1740 CHECK_mPath();
michael@0 1741 if (NS_WARN_IF(!_retval))
michael@0 1742 return NS_ERROR_INVALID_ARG;
michael@0 1743
michael@0 1744 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 1745 nsTraceRefcnt::SetActivityIsLegal(false);
michael@0 1746 #endif
michael@0 1747
michael@0 1748 *_retval = PR_LoadLibrary(mPath.get());
michael@0 1749
michael@0 1750 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 1751 nsTraceRefcnt::SetActivityIsLegal(true);
michael@0 1752 #endif
michael@0 1753
michael@0 1754 if (!*_retval)
michael@0 1755 return NS_ERROR_FAILURE;
michael@0 1756 return NS_OK;
michael@0 1757 }
michael@0 1758
michael@0 1759 NS_IMETHODIMP
michael@0 1760 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
michael@0 1761 {
michael@0 1762 return GetNativePath(aPersistentDescriptor);
michael@0 1763 }
michael@0 1764
michael@0 1765 NS_IMETHODIMP
michael@0 1766 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
michael@0 1767 {
michael@0 1768 #ifdef MOZ_WIDGET_COCOA
michael@0 1769 if (aPersistentDescriptor.IsEmpty())
michael@0 1770 return NS_ERROR_INVALID_ARG;
michael@0 1771
michael@0 1772 // Support pathnames as user-supplied descriptors if they begin with '/'
michael@0 1773 // or '~'. These characters do not collide with the base64 set used for
michael@0 1774 // encoding alias records.
michael@0 1775 char first = aPersistentDescriptor.First();
michael@0 1776 if (first == '/' || first == '~')
michael@0 1777 return InitWithNativePath(aPersistentDescriptor);
michael@0 1778
michael@0 1779 uint32_t dataSize = aPersistentDescriptor.Length();
michael@0 1780 char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nullptr);
michael@0 1781 if (!decodedData) {
michael@0 1782 NS_ERROR("SetPersistentDescriptor was given bad data");
michael@0 1783 return NS_ERROR_FAILURE;
michael@0 1784 }
michael@0 1785
michael@0 1786 // Cast to an alias record and resolve.
michael@0 1787 AliasRecord aliasHeader = *(AliasPtr)decodedData;
michael@0 1788 int32_t aliasSize = ::GetAliasSizeFromPtr(&aliasHeader);
michael@0 1789 if (aliasSize > ((int32_t)dataSize * 3) / 4) { // be paranoid about having too few data
michael@0 1790 PR_Free(decodedData);
michael@0 1791 return NS_ERROR_FAILURE;
michael@0 1792 }
michael@0 1793
michael@0 1794 nsresult rv = NS_OK;
michael@0 1795
michael@0 1796 // Move the now-decoded data into the Handle.
michael@0 1797 // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h
michael@0 1798 Handle newHandle = nullptr;
michael@0 1799 if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr)
michael@0 1800 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 1801 PR_Free(decodedData);
michael@0 1802 if (NS_FAILED(rv))
michael@0 1803 return rv;
michael@0 1804
michael@0 1805 Boolean changed;
michael@0 1806 FSRef resolvedFSRef;
michael@0 1807 OSErr err = ::FSResolveAlias(nullptr, (AliasHandle)newHandle, &resolvedFSRef, &changed);
michael@0 1808
michael@0 1809 rv = MacErrorMapper(err);
michael@0 1810 DisposeHandle(newHandle);
michael@0 1811 if (NS_FAILED(rv))
michael@0 1812 return rv;
michael@0 1813
michael@0 1814 return InitWithFSRef(&resolvedFSRef);
michael@0 1815 #else
michael@0 1816 return InitWithNativePath(aPersistentDescriptor);
michael@0 1817 #endif
michael@0 1818 }
michael@0 1819
michael@0 1820 NS_IMETHODIMP
michael@0 1821 nsLocalFile::Reveal()
michael@0 1822 {
michael@0 1823 #ifdef MOZ_WIDGET_GTK
michael@0 1824 nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
michael@0 1825 nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
michael@0 1826 if (!giovfs && !gnomevfs)
michael@0 1827 return NS_ERROR_FAILURE;
michael@0 1828
michael@0 1829 bool isDirectory;
michael@0 1830 if (NS_FAILED(IsDirectory(&isDirectory)))
michael@0 1831 return NS_ERROR_FAILURE;
michael@0 1832
michael@0 1833 if (isDirectory) {
michael@0 1834 if (giovfs)
michael@0 1835 return giovfs->ShowURIForInput(mPath);
michael@0 1836 else
michael@0 1837 /* Fallback to GnomeVFS */
michael@0 1838 return gnomevfs->ShowURIForInput(mPath);
michael@0 1839 } else if (giovfs && NS_SUCCEEDED(giovfs->OrgFreedesktopFileManager1ShowItems(mPath))) {
michael@0 1840 return NS_OK;
michael@0 1841 } else {
michael@0 1842 nsCOMPtr<nsIFile> parentDir;
michael@0 1843 nsAutoCString dirPath;
michael@0 1844 if (NS_FAILED(GetParent(getter_AddRefs(parentDir))))
michael@0 1845 return NS_ERROR_FAILURE;
michael@0 1846 if (NS_FAILED(parentDir->GetNativePath(dirPath)))
michael@0 1847 return NS_ERROR_FAILURE;
michael@0 1848
michael@0 1849 if (giovfs)
michael@0 1850 return giovfs->ShowURIForInput(dirPath);
michael@0 1851 else
michael@0 1852 return gnomevfs->ShowURIForInput(dirPath);
michael@0 1853 }
michael@0 1854 #elif defined(MOZ_WIDGET_COCOA)
michael@0 1855 CFURLRef url;
michael@0 1856 if (NS_SUCCEEDED(GetCFURL(&url))) {
michael@0 1857 nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
michael@0 1858 ::CFRelease(url);
michael@0 1859 return rv;
michael@0 1860 }
michael@0 1861 return NS_ERROR_FAILURE;
michael@0 1862 #else
michael@0 1863 return NS_ERROR_FAILURE;
michael@0 1864 #endif
michael@0 1865 }
michael@0 1866
michael@0 1867 NS_IMETHODIMP
michael@0 1868 nsLocalFile::Launch()
michael@0 1869 {
michael@0 1870 #ifdef MOZ_WIDGET_GTK
michael@0 1871 nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
michael@0 1872 nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
michael@0 1873 if (giovfs) {
michael@0 1874 return giovfs->ShowURIForInput(mPath);
michael@0 1875 } else if (gnomevfs) {
michael@0 1876 /* GnomeVFS fallback */
michael@0 1877 return gnomevfs->ShowURIForInput(mPath);
michael@0 1878 }
michael@0 1879
michael@0 1880 return NS_ERROR_FAILURE;
michael@0 1881 #elif defined(MOZ_ENABLE_CONTENTACTION)
michael@0 1882 QUrl uri = QUrl::fromLocalFile(QString::fromUtf8(mPath.get()));
michael@0 1883 ContentAction::Action action =
michael@0 1884 ContentAction::Action::defaultActionForFile(uri);
michael@0 1885
michael@0 1886 if (action.isValid()) {
michael@0 1887 action.trigger();
michael@0 1888 return NS_OK;
michael@0 1889 }
michael@0 1890
michael@0 1891 return NS_ERROR_FAILURE;
michael@0 1892 #elif defined(MOZ_WIDGET_ANDROID)
michael@0 1893 // Try to get a mimetype, if this fails just use the file uri alone
michael@0 1894 nsresult rv;
michael@0 1895 nsAutoCString type;
michael@0 1896 nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1", &rv));
michael@0 1897 if (NS_SUCCEEDED(rv))
michael@0 1898 rv = mimeService->GetTypeFromFile(this, type);
michael@0 1899
michael@0 1900 nsAutoCString fileUri = NS_LITERAL_CSTRING("file://") + mPath;
michael@0 1901 return mozilla::widget::android::GeckoAppShell::OpenUriExternal(NS_ConvertUTF8toUTF16(fileUri),
michael@0 1902 NS_ConvertUTF8toUTF16(type)) ? NS_OK : NS_ERROR_FAILURE;
michael@0 1903 #elif defined(MOZ_WIDGET_COCOA)
michael@0 1904 CFURLRef url;
michael@0 1905 if (NS_SUCCEEDED(GetCFURL(&url))) {
michael@0 1906 nsresult rv = CocoaFileUtils::OpenURL(url);
michael@0 1907 ::CFRelease(url);
michael@0 1908 return rv;
michael@0 1909 }
michael@0 1910 return NS_ERROR_FAILURE;
michael@0 1911 #else
michael@0 1912 return NS_ERROR_FAILURE;
michael@0 1913 #endif
michael@0 1914 }
michael@0 1915
michael@0 1916 nsresult
michael@0 1917 NS_NewNativeLocalFile(const nsACString &path, bool followSymlinks, nsIFile **result)
michael@0 1918 {
michael@0 1919 nsRefPtr<nsLocalFile> file = new nsLocalFile();
michael@0 1920
michael@0 1921 file->SetFollowLinks(followSymlinks);
michael@0 1922
michael@0 1923 if (!path.IsEmpty()) {
michael@0 1924 nsresult rv = file->InitWithNativePath(path);
michael@0 1925 if (NS_FAILED(rv)) {
michael@0 1926 return rv;
michael@0 1927 }
michael@0 1928 }
michael@0 1929 file.forget(result);
michael@0 1930 return NS_OK;
michael@0 1931 }
michael@0 1932
michael@0 1933 //-----------------------------------------------------------------------------
michael@0 1934 // unicode support
michael@0 1935 //-----------------------------------------------------------------------------
michael@0 1936
michael@0 1937 #define SET_UCS(func, ucsArg) \
michael@0 1938 { \
michael@0 1939 nsAutoCString buf; \
michael@0 1940 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
michael@0 1941 if (NS_FAILED(rv)) \
michael@0 1942 return rv; \
michael@0 1943 return (func)(buf); \
michael@0 1944 }
michael@0 1945
michael@0 1946 #define GET_UCS(func, ucsArg) \
michael@0 1947 { \
michael@0 1948 nsAutoCString buf; \
michael@0 1949 nsresult rv = (func)(buf); \
michael@0 1950 if (NS_FAILED(rv)) return rv; \
michael@0 1951 return NS_CopyNativeToUnicode(buf, ucsArg); \
michael@0 1952 }
michael@0 1953
michael@0 1954 #define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
michael@0 1955 { \
michael@0 1956 nsAutoCString buf; \
michael@0 1957 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
michael@0 1958 if (NS_FAILED(rv)) \
michael@0 1959 return rv; \
michael@0 1960 return (func)(opaqueArg, buf); \
michael@0 1961 }
michael@0 1962
michael@0 1963 // Unicode interface Wrapper
michael@0 1964 nsresult
michael@0 1965 nsLocalFile::InitWithPath(const nsAString &filePath)
michael@0 1966 {
michael@0 1967 SET_UCS(InitWithNativePath, filePath);
michael@0 1968 }
michael@0 1969 nsresult
michael@0 1970 nsLocalFile::Append(const nsAString &node)
michael@0 1971 {
michael@0 1972 SET_UCS(AppendNative, node);
michael@0 1973 }
michael@0 1974 nsresult
michael@0 1975 nsLocalFile::AppendRelativePath(const nsAString &node)
michael@0 1976 {
michael@0 1977 SET_UCS(AppendRelativeNativePath, node);
michael@0 1978 }
michael@0 1979 nsresult
michael@0 1980 nsLocalFile::GetLeafName(nsAString &aLeafName)
michael@0 1981 {
michael@0 1982 GET_UCS(GetNativeLeafName, aLeafName);
michael@0 1983 }
michael@0 1984 nsresult
michael@0 1985 nsLocalFile::SetLeafName(const nsAString &aLeafName)
michael@0 1986 {
michael@0 1987 SET_UCS(SetNativeLeafName, aLeafName);
michael@0 1988 }
michael@0 1989 nsresult
michael@0 1990 nsLocalFile::GetPath(nsAString &_retval)
michael@0 1991 {
michael@0 1992 return NS_CopyNativeToUnicode(mPath, _retval);
michael@0 1993 }
michael@0 1994 nsresult
michael@0 1995 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
michael@0 1996 {
michael@0 1997 SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
michael@0 1998 }
michael@0 1999 nsresult
michael@0 2000 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
michael@0 2001 {
michael@0 2002 SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
michael@0 2003 }
michael@0 2004 nsresult
michael@0 2005 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
michael@0 2006 {
michael@0 2007 SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
michael@0 2008 }
michael@0 2009
michael@0 2010 NS_IMETHODIMP
michael@0 2011 nsLocalFile::RenameTo(nsIFile *newParentDir, const nsAString &newName)
michael@0 2012 {
michael@0 2013 nsresult rv;
michael@0 2014
michael@0 2015 // check to make sure that this has been initialized properly
michael@0 2016 CHECK_mPath();
michael@0 2017
michael@0 2018 // check to make sure that we have a new parent
michael@0 2019 nsAutoCString newPathName;
michael@0 2020 nsAutoCString newNativeName;
michael@0 2021 rv = NS_CopyUnicodeToNative(newName, newNativeName);
michael@0 2022 if (NS_FAILED(rv)) {
michael@0 2023 return rv;
michael@0 2024 }
michael@0 2025 rv = GetNativeTargetPathName(newParentDir, newNativeName, newPathName);
michael@0 2026 if (NS_FAILED(rv)) {
michael@0 2027 return rv;
michael@0 2028 }
michael@0 2029
michael@0 2030 // try for atomic rename
michael@0 2031 if (rename(mPath.get(), newPathName.get()) < 0) {
michael@0 2032 #ifdef VMS
michael@0 2033 if (errno == EXDEV || errno == ENXIO) {
michael@0 2034 #else
michael@0 2035 if (errno == EXDEV) {
michael@0 2036 #endif
michael@0 2037 rv = NS_ERROR_FILE_ACCESS_DENIED;
michael@0 2038 } else {
michael@0 2039 rv = NSRESULT_FOR_ERRNO();
michael@0 2040 }
michael@0 2041 }
michael@0 2042
michael@0 2043 return rv;
michael@0 2044 }
michael@0 2045
michael@0 2046 nsresult
michael@0 2047 nsLocalFile::GetTarget(nsAString &_retval)
michael@0 2048 {
michael@0 2049 GET_UCS(GetNativeTarget, _retval);
michael@0 2050 }
michael@0 2051
michael@0 2052 // nsIHashable
michael@0 2053
michael@0 2054 NS_IMETHODIMP
michael@0 2055 nsLocalFile::Equals(nsIHashable* aOther, bool *aResult)
michael@0 2056 {
michael@0 2057 nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
michael@0 2058 if (!otherFile) {
michael@0 2059 *aResult = false;
michael@0 2060 return NS_OK;
michael@0 2061 }
michael@0 2062
michael@0 2063 return Equals(otherFile, aResult);
michael@0 2064 }
michael@0 2065
michael@0 2066 NS_IMETHODIMP
michael@0 2067 nsLocalFile::GetHashCode(uint32_t *aResult)
michael@0 2068 {
michael@0 2069 *aResult = HashString(mPath);
michael@0 2070 return NS_OK;
michael@0 2071 }
michael@0 2072
michael@0 2073 nsresult
michael@0 2074 NS_NewLocalFile(const nsAString &path, bool followLinks, nsIFile* *result)
michael@0 2075 {
michael@0 2076 nsAutoCString buf;
michael@0 2077 nsresult rv = NS_CopyUnicodeToNative(path, buf);
michael@0 2078 if (NS_FAILED(rv))
michael@0 2079 return rv;
michael@0 2080 return NS_NewNativeLocalFile(buf, followLinks, result);
michael@0 2081 }
michael@0 2082
michael@0 2083 //-----------------------------------------------------------------------------
michael@0 2084 // global init/shutdown
michael@0 2085 //-----------------------------------------------------------------------------
michael@0 2086
michael@0 2087 void
michael@0 2088 nsLocalFile::GlobalInit()
michael@0 2089 {
michael@0 2090 }
michael@0 2091
michael@0 2092 void
michael@0 2093 nsLocalFile::GlobalShutdown()
michael@0 2094 {
michael@0 2095 }
michael@0 2096
michael@0 2097 // nsILocalFileMac
michael@0 2098
michael@0 2099 #ifdef MOZ_WIDGET_COCOA
michael@0 2100
michael@0 2101 static nsresult MacErrorMapper(OSErr inErr)
michael@0 2102 {
michael@0 2103 nsresult outErr;
michael@0 2104
michael@0 2105 switch (inErr)
michael@0 2106 {
michael@0 2107 case noErr:
michael@0 2108 outErr = NS_OK;
michael@0 2109 break;
michael@0 2110
michael@0 2111 case fnfErr:
michael@0 2112 case afpObjectNotFound:
michael@0 2113 case afpDirNotFound:
michael@0 2114 outErr = NS_ERROR_FILE_NOT_FOUND;
michael@0 2115 break;
michael@0 2116
michael@0 2117 case dupFNErr:
michael@0 2118 case afpObjectExists:
michael@0 2119 outErr = NS_ERROR_FILE_ALREADY_EXISTS;
michael@0 2120 break;
michael@0 2121
michael@0 2122 case dskFulErr:
michael@0 2123 case afpDiskFull:
michael@0 2124 outErr = NS_ERROR_FILE_DISK_FULL;
michael@0 2125 break;
michael@0 2126
michael@0 2127 case fLckdErr:
michael@0 2128 case afpVolLocked:
michael@0 2129 outErr = NS_ERROR_FILE_IS_LOCKED;
michael@0 2130 break;
michael@0 2131
michael@0 2132 case afpAccessDenied:
michael@0 2133 outErr = NS_ERROR_FILE_ACCESS_DENIED;
michael@0 2134 break;
michael@0 2135
michael@0 2136 case afpDirNotEmpty:
michael@0 2137 outErr = NS_ERROR_FILE_DIR_NOT_EMPTY;
michael@0 2138 break;
michael@0 2139
michael@0 2140 // Can't find good map for some
michael@0 2141 case bdNamErr:
michael@0 2142 outErr = NS_ERROR_FAILURE;
michael@0 2143 break;
michael@0 2144
michael@0 2145 default:
michael@0 2146 outErr = NS_ERROR_FAILURE;
michael@0 2147 break;
michael@0 2148 }
michael@0 2149
michael@0 2150 return outErr;
michael@0 2151 }
michael@0 2152
michael@0 2153 static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr)
michael@0 2154 {
michael@0 2155 // first see if the conversion would succeed and find the length of the result
michael@0 2156 CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef);
michael@0 2157 CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen),
michael@0 2158 kCFStringEncodingUTF8, 0, false,
michael@0 2159 nullptr, 0, &usedBufLen);
michael@0 2160 if (charsConverted == inStrLen) {
michael@0 2161 // all characters converted, do the actual conversion
michael@0 2162 aOutStr.SetLength(usedBufLen);
michael@0 2163 if (aOutStr.Length() != (unsigned int)usedBufLen)
michael@0 2164 return NS_ERROR_OUT_OF_MEMORY;
michael@0 2165 UInt8 *buffer = (UInt8*)aOutStr.BeginWriting();
michael@0 2166 ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8,
michael@0 2167 0, false, buffer, usedBufLen, &usedBufLen);
michael@0 2168 return NS_OK;
michael@0 2169 }
michael@0 2170
michael@0 2171 return NS_ERROR_FAILURE;
michael@0 2172 }
michael@0 2173
michael@0 2174 NS_IMETHODIMP
michael@0 2175 nsLocalFile::InitWithCFURL(CFURLRef aCFURL)
michael@0 2176 {
michael@0 2177 UInt8 path[PATH_MAX];
michael@0 2178 if (::CFURLGetFileSystemRepresentation(aCFURL, false, path, PATH_MAX)) {
michael@0 2179 nsDependentCString nativePath((char*)path);
michael@0 2180 return InitWithNativePath(nativePath);
michael@0 2181 }
michael@0 2182
michael@0 2183 return NS_ERROR_FAILURE;
michael@0 2184 }
michael@0 2185
michael@0 2186 NS_IMETHODIMP
michael@0 2187 nsLocalFile::InitWithFSRef(const FSRef *aFSRef)
michael@0 2188 {
michael@0 2189 if (NS_WARN_IF(!aFSRef))
michael@0 2190 return NS_ERROR_INVALID_ARG;
michael@0 2191
michael@0 2192 CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef);
michael@0 2193 if (newURLRef) {
michael@0 2194 nsresult rv = InitWithCFURL(newURLRef);
michael@0 2195 ::CFRelease(newURLRef);
michael@0 2196 return rv;
michael@0 2197 }
michael@0 2198
michael@0 2199 return NS_ERROR_FAILURE;
michael@0 2200 }
michael@0 2201
michael@0 2202 NS_IMETHODIMP
michael@0 2203 nsLocalFile::GetCFURL(CFURLRef *_retval)
michael@0 2204 {
michael@0 2205 CHECK_mPath();
michael@0 2206
michael@0 2207 bool isDir;
michael@0 2208 IsDirectory(&isDir);
michael@0 2209 *_retval = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
michael@0 2210 (UInt8*)mPath.get(),
michael@0 2211 mPath.Length(),
michael@0 2212 isDir);
michael@0 2213
michael@0 2214 return (*_retval ? NS_OK : NS_ERROR_FAILURE);
michael@0 2215 }
michael@0 2216
michael@0 2217 NS_IMETHODIMP
michael@0 2218 nsLocalFile::GetFSRef(FSRef *_retval)
michael@0 2219 {
michael@0 2220 if (NS_WARN_IF(!_retval))
michael@0 2221 return NS_ERROR_INVALID_ARG;
michael@0 2222
michael@0 2223 nsresult rv = NS_ERROR_FAILURE;
michael@0 2224
michael@0 2225 CFURLRef url = nullptr;
michael@0 2226 if (NS_SUCCEEDED(GetCFURL(&url))) {
michael@0 2227 if (::CFURLGetFSRef(url, _retval)) {
michael@0 2228 rv = NS_OK;
michael@0 2229 }
michael@0 2230 ::CFRelease(url);
michael@0 2231 }
michael@0 2232
michael@0 2233 return rv;
michael@0 2234 }
michael@0 2235
michael@0 2236 NS_IMETHODIMP
michael@0 2237 nsLocalFile::GetFSSpec(FSSpec *_retval)
michael@0 2238 {
michael@0 2239 if (NS_WARN_IF(!_retval))
michael@0 2240 return NS_ERROR_INVALID_ARG;
michael@0 2241
michael@0 2242 FSRef fsRef;
michael@0 2243 nsresult rv = GetFSRef(&fsRef);
michael@0 2244 if (NS_SUCCEEDED(rv)) {
michael@0 2245 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, nullptr, nullptr, _retval, nullptr);
michael@0 2246 return MacErrorMapper(err);
michael@0 2247 }
michael@0 2248
michael@0 2249 return rv;
michael@0 2250 }
michael@0 2251
michael@0 2252 NS_IMETHODIMP
michael@0 2253 nsLocalFile::GetFileSizeWithResFork(int64_t *aFileSizeWithResFork)
michael@0 2254 {
michael@0 2255 if (NS_WARN_IF(!aFileSizeWithResFork))
michael@0 2256 return NS_ERROR_INVALID_ARG;
michael@0 2257
michael@0 2258 FSRef fsRef;
michael@0 2259 nsresult rv = GetFSRef(&fsRef);
michael@0 2260 if (NS_FAILED(rv))
michael@0 2261 return rv;
michael@0 2262
michael@0 2263 FSCatalogInfo catalogInfo;
michael@0 2264 OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes,
michael@0 2265 &catalogInfo, nullptr, nullptr, nullptr);
michael@0 2266 if (err != noErr)
michael@0 2267 return MacErrorMapper(err);
michael@0 2268
michael@0 2269 *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize;
michael@0 2270 return NS_OK;
michael@0 2271 }
michael@0 2272
michael@0 2273 NS_IMETHODIMP
michael@0 2274 nsLocalFile::GetFileType(OSType *aFileType)
michael@0 2275 {
michael@0 2276 CFURLRef url;
michael@0 2277 if (NS_SUCCEEDED(GetCFURL(&url))) {
michael@0 2278 nsresult rv = CocoaFileUtils::GetFileTypeCode(url, aFileType);
michael@0 2279 ::CFRelease(url);
michael@0 2280 return rv;
michael@0 2281 }
michael@0 2282 return NS_ERROR_FAILURE;
michael@0 2283 }
michael@0 2284
michael@0 2285 NS_IMETHODIMP
michael@0 2286 nsLocalFile::SetFileType(OSType aFileType)
michael@0 2287 {
michael@0 2288 CFURLRef url;
michael@0 2289 if (NS_SUCCEEDED(GetCFURL(&url))) {
michael@0 2290 nsresult rv = CocoaFileUtils::SetFileTypeCode(url, aFileType);
michael@0 2291 ::CFRelease(url);
michael@0 2292 return rv;
michael@0 2293 }
michael@0 2294 return NS_ERROR_FAILURE;
michael@0 2295 }
michael@0 2296
michael@0 2297 NS_IMETHODIMP
michael@0 2298 nsLocalFile::GetFileCreator(OSType *aFileCreator)
michael@0 2299 {
michael@0 2300 CFURLRef url;
michael@0 2301 if (NS_SUCCEEDED(GetCFURL(&url))) {
michael@0 2302 nsresult rv = CocoaFileUtils::GetFileCreatorCode(url, aFileCreator);
michael@0 2303 ::CFRelease(url);
michael@0 2304 return rv;
michael@0 2305 }
michael@0 2306 return NS_ERROR_FAILURE;
michael@0 2307 }
michael@0 2308
michael@0 2309 NS_IMETHODIMP
michael@0 2310 nsLocalFile::SetFileCreator(OSType aFileCreator)
michael@0 2311 {
michael@0 2312 CFURLRef url;
michael@0 2313 if (NS_SUCCEEDED(GetCFURL(&url))) {
michael@0 2314 nsresult rv = CocoaFileUtils::SetFileCreatorCode(url, aFileCreator);
michael@0 2315 ::CFRelease(url);
michael@0 2316 return rv;
michael@0 2317 }
michael@0 2318 return NS_ERROR_FAILURE;
michael@0 2319 }
michael@0 2320
michael@0 2321 NS_IMETHODIMP
michael@0 2322 nsLocalFile::LaunchWithDoc(nsIFile *aDocToLoad, bool aLaunchInBackground)
michael@0 2323 {
michael@0 2324 bool isExecutable;
michael@0 2325 nsresult rv = IsExecutable(&isExecutable);
michael@0 2326 if (NS_FAILED(rv))
michael@0 2327 return rv;
michael@0 2328 if (!isExecutable)
michael@0 2329 return NS_ERROR_FILE_EXECUTION_FAILED;
michael@0 2330
michael@0 2331 FSRef appFSRef, docFSRef;
michael@0 2332 rv = GetFSRef(&appFSRef);
michael@0 2333 if (NS_FAILED(rv))
michael@0 2334 return rv;
michael@0 2335
michael@0 2336 if (aDocToLoad) {
michael@0 2337 nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad);
michael@0 2338 rv = macDoc->GetFSRef(&docFSRef);
michael@0 2339 if (NS_FAILED(rv))
michael@0 2340 return rv;
michael@0 2341 }
michael@0 2342
michael@0 2343 LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
michael@0 2344 LSLaunchFSRefSpec thelaunchSpec;
michael@0 2345
michael@0 2346 if (aLaunchInBackground)
michael@0 2347 theLaunchFlags |= kLSLaunchDontSwitch;
michael@0 2348 memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
michael@0 2349
michael@0 2350 thelaunchSpec.appRef = &appFSRef;
michael@0 2351 if (aDocToLoad) {
michael@0 2352 thelaunchSpec.numDocs = 1;
michael@0 2353 thelaunchSpec.itemRefs = &docFSRef;
michael@0 2354 }
michael@0 2355 thelaunchSpec.launchFlags = theLaunchFlags;
michael@0 2356
michael@0 2357 OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
michael@0 2358 if (err != noErr)
michael@0 2359 return MacErrorMapper(err);
michael@0 2360
michael@0 2361 return NS_OK;
michael@0 2362 }
michael@0 2363
michael@0 2364 NS_IMETHODIMP
michael@0 2365 nsLocalFile::OpenDocWithApp(nsIFile *aAppToOpenWith, bool aLaunchInBackground)
michael@0 2366 {
michael@0 2367 FSRef docFSRef;
michael@0 2368 nsresult rv = GetFSRef(&docFSRef);
michael@0 2369 if (NS_FAILED(rv))
michael@0 2370 return rv;
michael@0 2371
michael@0 2372 if (!aAppToOpenWith) {
michael@0 2373 OSErr err = ::LSOpenFSRef(&docFSRef, nullptr);
michael@0 2374 return MacErrorMapper(err);
michael@0 2375 }
michael@0 2376
michael@0 2377 nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv);
michael@0 2378 if (!appFileMac)
michael@0 2379 return rv;
michael@0 2380
michael@0 2381 bool isExecutable;
michael@0 2382 rv = appFileMac->IsExecutable(&isExecutable);
michael@0 2383 if (NS_FAILED(rv))
michael@0 2384 return rv;
michael@0 2385 if (!isExecutable)
michael@0 2386 return NS_ERROR_FILE_EXECUTION_FAILED;
michael@0 2387
michael@0 2388 FSRef appFSRef;
michael@0 2389 rv = appFileMac->GetFSRef(&appFSRef);
michael@0 2390 if (NS_FAILED(rv))
michael@0 2391 return rv;
michael@0 2392
michael@0 2393 LSLaunchFlags theLaunchFlags = kLSLaunchDefaults;
michael@0 2394 LSLaunchFSRefSpec thelaunchSpec;
michael@0 2395
michael@0 2396 if (aLaunchInBackground)
michael@0 2397 theLaunchFlags |= kLSLaunchDontSwitch;
michael@0 2398 memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec));
michael@0 2399
michael@0 2400 thelaunchSpec.appRef = &appFSRef;
michael@0 2401 thelaunchSpec.numDocs = 1;
michael@0 2402 thelaunchSpec.itemRefs = &docFSRef;
michael@0 2403 thelaunchSpec.launchFlags = theLaunchFlags;
michael@0 2404
michael@0 2405 OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, nullptr);
michael@0 2406 if (err != noErr)
michael@0 2407 return MacErrorMapper(err);
michael@0 2408
michael@0 2409 return NS_OK;
michael@0 2410 }
michael@0 2411
michael@0 2412 NS_IMETHODIMP
michael@0 2413 nsLocalFile::IsPackage(bool *_retval)
michael@0 2414 {
michael@0 2415 if (NS_WARN_IF(!_retval))
michael@0 2416 return NS_ERROR_INVALID_ARG;
michael@0 2417 *_retval = false;
michael@0 2418
michael@0 2419 CFURLRef url;
michael@0 2420 nsresult rv = GetCFURL(&url);
michael@0 2421 if (NS_FAILED(rv))
michael@0 2422 return rv;
michael@0 2423
michael@0 2424 LSItemInfoRecord info;
michael@0 2425 OSStatus status = ::LSCopyItemInfoForURL(url, kLSRequestBasicFlagsOnly, &info);
michael@0 2426
michael@0 2427 ::CFRelease(url);
michael@0 2428
michael@0 2429 if (status != noErr) {
michael@0 2430 return NS_ERROR_FAILURE;
michael@0 2431 }
michael@0 2432
michael@0 2433 *_retval = !!(info.flags & kLSItemInfoIsPackage);
michael@0 2434
michael@0 2435 return NS_OK;
michael@0 2436 }
michael@0 2437
michael@0 2438 NS_IMETHODIMP
michael@0 2439 nsLocalFile::GetBundleDisplayName(nsAString& outBundleName)
michael@0 2440 {
michael@0 2441 bool isPackage = false;
michael@0 2442 nsresult rv = IsPackage(&isPackage);
michael@0 2443 if (NS_FAILED(rv) || !isPackage)
michael@0 2444 return NS_ERROR_FAILURE;
michael@0 2445
michael@0 2446 nsAutoString name;
michael@0 2447 rv = GetLeafName(name);
michael@0 2448 if (NS_FAILED(rv))
michael@0 2449 return rv;
michael@0 2450
michael@0 2451 int32_t length = name.Length();
michael@0 2452 if (Substring(name, length - 4, length).EqualsLiteral(".app")) {
michael@0 2453 // 4 characters in ".app"
michael@0 2454 outBundleName = Substring(name, 0, length - 4);
michael@0 2455 }
michael@0 2456 else {
michael@0 2457 outBundleName = name;
michael@0 2458 }
michael@0 2459
michael@0 2460 return NS_OK;
michael@0 2461 }
michael@0 2462
michael@0 2463 NS_IMETHODIMP
michael@0 2464 nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier)
michael@0 2465 {
michael@0 2466 nsresult rv = NS_ERROR_FAILURE;
michael@0 2467
michael@0 2468 CFURLRef urlRef;
michael@0 2469 if (NS_SUCCEEDED(GetCFURL(&urlRef))) {
michael@0 2470 CFBundleRef bundle = ::CFBundleCreate(nullptr, urlRef);
michael@0 2471 if (bundle) {
michael@0 2472 CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle);
michael@0 2473 if (bundleIdentifier)
michael@0 2474 rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier);
michael@0 2475 ::CFRelease(bundle);
michael@0 2476 }
michael@0 2477 ::CFRelease(urlRef);
michael@0 2478 }
michael@0 2479
michael@0 2480 return rv;
michael@0 2481 }
michael@0 2482
michael@0 2483 NS_IMETHODIMP
michael@0 2484 nsLocalFile::GetBundleContentsLastModifiedTime(int64_t *aLastModTime)
michael@0 2485 {
michael@0 2486 CHECK_mPath();
michael@0 2487 if (NS_WARN_IF(!aLastModTime))
michael@0 2488 return NS_ERROR_INVALID_ARG;
michael@0 2489
michael@0 2490 bool isPackage = false;
michael@0 2491 nsresult rv = IsPackage(&isPackage);
michael@0 2492 if (NS_FAILED(rv) || !isPackage) {
michael@0 2493 return GetLastModifiedTime(aLastModTime);
michael@0 2494 }
michael@0 2495
michael@0 2496 nsAutoCString infoPlistPath(mPath);
michael@0 2497 infoPlistPath.AppendLiteral("/Contents/Info.plist");
michael@0 2498 PRFileInfo64 info;
michael@0 2499 if (PR_GetFileInfo64(infoPlistPath.get(), &info) != PR_SUCCESS) {
michael@0 2500 return GetLastModifiedTime(aLastModTime);
michael@0 2501 }
michael@0 2502 int64_t modTime = int64_t(info.modifyTime);
michael@0 2503 if (modTime == 0) {
michael@0 2504 *aLastModTime = 0;
michael@0 2505 } else {
michael@0 2506 *aLastModTime = modTime / int64_t(PR_USEC_PER_MSEC);
michael@0 2507 }
michael@0 2508
michael@0 2509 return NS_OK;
michael@0 2510 }
michael@0 2511
michael@0 2512 NS_IMETHODIMP nsLocalFile::InitWithFile(nsIFile *aFile)
michael@0 2513 {
michael@0 2514 if (NS_WARN_IF(!aFile))
michael@0 2515 return NS_ERROR_INVALID_ARG;
michael@0 2516
michael@0 2517 nsAutoCString nativePath;
michael@0 2518 nsresult rv = aFile->GetNativePath(nativePath);
michael@0 2519 if (NS_FAILED(rv))
michael@0 2520 return rv;
michael@0 2521
michael@0 2522 return InitWithNativePath(nativePath);
michael@0 2523 }
michael@0 2524
michael@0 2525 nsresult
michael@0 2526 NS_NewLocalFileWithFSRef(const FSRef* aFSRef, bool aFollowLinks, nsILocalFileMac** result)
michael@0 2527 {
michael@0 2528 nsRefPtr<nsLocalFile> file = new nsLocalFile();
michael@0 2529
michael@0 2530 file->SetFollowLinks(aFollowLinks);
michael@0 2531
michael@0 2532 nsresult rv = file->InitWithFSRef(aFSRef);
michael@0 2533 if (NS_FAILED(rv)) {
michael@0 2534 return rv;
michael@0 2535 }
michael@0 2536 file.forget(result);
michael@0 2537 return NS_OK;
michael@0 2538 }
michael@0 2539
michael@0 2540 nsresult
michael@0 2541 NS_NewLocalFileWithCFURL(const CFURLRef aURL, bool aFollowLinks, nsILocalFileMac** result)
michael@0 2542 {
michael@0 2543 nsRefPtr<nsLocalFile> file = new nsLocalFile();
michael@0 2544
michael@0 2545 file->SetFollowLinks(aFollowLinks);
michael@0 2546
michael@0 2547 nsresult rv = file->InitWithCFURL(aURL);
michael@0 2548 if (NS_FAILED(rv)) {
michael@0 2549 return rv;
michael@0 2550 }
michael@0 2551 file.forget(result);
michael@0 2552 return NS_OK;
michael@0 2553 }
michael@0 2554
michael@0 2555 #endif

mercurial