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