Coverage Report

Created: 2018-09-25 14:53

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