VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/io/nsLocalFileUnix.cpp@ 103140

最後變更 在這個檔案從103140是 102204,由 vboxsync 提交於 16 月 前

libs/xpcom/xpcom: Get rid of last PR_GetErrorText() usages which are not relevant anyway, bguref:10545

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 45.8 KB
 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
17 *
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
22 *
23 * Contributor(s):
24 * Mike Shaver <shaver@mozilla.org>
25 * Christopher Blizzard <blizzard@mozilla.org>
26 * Jason Eager <jce2@po.cwru.edu>
27 * Stuart Parmenter <pavlov@netscape.com>
28 * Brendan Eich <brendan@mozilla.org>
29 * Pete Collins <petejc@mozdev.org>
30 * Paul Ashford <arougthopher@lizardland.net>
31 *
32 * Alternatively, the contents of this file may be used under the terms of
33 * either of the GNU General Public License Version 2 or later (the "GPL"),
34 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 * in which case the provisions of the GPL or the LGPL are applicable instead
36 * of those above. If you wish to allow use of your version of this file only
37 * under the terms of either the GPL or the LGPL, and not to allow others to
38 * use your version of this file under the terms of the MPL, indicate your
39 * decision by deleting the provisions above and replace them with the notice
40 * and other provisions required by the GPL or the LGPL. If you do not delete
41 * the provisions above, a recipient may use your version of this file under
42 * the terms of any one of the MPL, the GPL or the LGPL.
43 *
44 * ***** END LICENSE BLOCK ***** */
45
46/**
47 * Implementation of nsIFile for ``Unixy'' systems.
48 */
49
50// We're going to need some autoconf loving, I can just tell.
51#include <sys/types.h>
52#include <sys/stat.h>
53#include <unistd.h>
54#include <fcntl.h>
55#include <errno.h>
56#include <utime.h>
57#include <dirent.h>
58#include <ctype.h>
59#include <locale.h>
60
61#include "nsDirectoryServiceDefs.h"
62#include "nsCRT.h"
63#include "nsCOMPtr.h"
64#include "nsMemory.h"
65#include "nsIFile.h"
66#include "nsString.h"
67#include "nsReadableUtils.h"
68#include "nsLocalFile.h"
69#include "nsIComponentManager.h"
70#include "nsXPIDLString.h"
71#include "nsISimpleEnumerator.h"
72
73#include "nsNativeCharsetUtils.h"
74
75#include <iprt/err.h>
76#include <iprt/dir.h>
77#include <iprt/file.h>
78#include <iprt/path.h>
79#include <iprt/time.h>
80
81#define FILE_STRCMP strcmp
82#define FILE_STRNCMP strncmp
83
84#define VALIDATE_STAT_CACHE() \
85 PR_BEGIN_MACRO \
86 if (!mHaveCachedStat) { \
87 FillStatCache(); \
88 if (!mHaveCachedStat) \
89 return NSRESULT_FOR_ERRNO(); \
90 } \
91 PR_END_MACRO
92
93#define CHECK_mPath() \
94 PR_BEGIN_MACRO \
95 if (mPath.IsEmpty()) \
96 return NS_ERROR_NOT_INITIALIZED; \
97 PR_END_MACRO
98
99NS_COM nsresult
100NS_ErrorAccordingToIPRT(int vrc)
101{
102 switch (vrc) {
103 case VERR_NO_MEMORY: return NS_ERROR_OUT_OF_MEMORY;
104 case VERR_FILE_NOT_FOUND: return NS_ERROR_FILE_NOT_FOUND;
105 case VERR_PATH_NOT_FOUND: return NS_ERROR_FILE_NOT_FOUND;
106 case VERR_NOT_A_DIRECTORY: return NS_ERROR_FILE_NOT_DIRECTORY;
107 case VERR_IS_A_DIRECTORY: return NS_ERROR_FILE_IS_DIRECTORY;
108 case VERR_ALREADY_EXISTS: return NS_ERROR_FILE_ALREADY_EXISTS;
109 case VERR_FILE_LOCK_VIOLATION: return NS_ERROR_FILE_IS_LOCKED;
110 case VERR_FILE_TOO_BIG: return NS_ERROR_FILE_TOO_BIG;
111 case VERR_FILENAME_TOO_LONG: return NS_ERROR_FILE_NAME_TOO_LONG;
112 case VERR_DIR_NOT_EMPTY: return NS_ERROR_FILE_DIR_NOT_EMPTY;
113 case VERR_ACCESS_DENIED: return NS_ERROR_FILE_ACCESS_DENIED;
114 case VERR_WRITE_PROTECT: return NS_ERROR_FILE_READ_ONLY;
115 case VERR_DISK_FULL: return NS_ERROR_FILE_NO_DEVICE_SPACE;
116
117 /** @todo: No IPRT equivalent right now (but shouldn't be important). */
118 //case PR_WOULD_BLOCK_ERROR: return NS_BASE_STREAM_WOULD_BLOCK;
119 default: return NS_ERROR_FAILURE;
120 }
121}
122
123
124/* directory enumerator */
125class NS_COM
126nsDirEnumeratorUnix : public nsISimpleEnumerator
127{
128 public:
129 nsDirEnumeratorUnix();
130
131 // nsISupports interface
132 NS_DECL_ISUPPORTS
133
134 // nsISimpleEnumerator interface
135 NS_DECL_NSISIMPLEENUMERATOR
136
137 NS_IMETHOD Init(nsLocalFile *parent, PRBool ignored);
138
139 private:
140 ~nsDirEnumeratorUnix();
141
142 protected:
143 NS_IMETHOD GetNextEntry();
144
145 DIR *mDir;
146 struct dirent *mEntry;
147 nsCString mParentPath;
148};
149
150nsDirEnumeratorUnix::nsDirEnumeratorUnix() :
151 mDir(nsnull),
152 mEntry(nsnull)
153{
154}
155
156nsDirEnumeratorUnix::~nsDirEnumeratorUnix()
157{
158 if (mDir)
159 closedir(mDir);
160}
161
162NS_IMPL_ISUPPORTS1(nsDirEnumeratorUnix, nsISimpleEnumerator)
163
164NS_IMETHODIMP
165nsDirEnumeratorUnix::Init(nsLocalFile *parent, PRBool resolveSymlinks /*ignored*/)
166{
167 nsCAutoString dirPath;
168 if (NS_FAILED(parent->GetNativePath(dirPath)) ||
169 dirPath.IsEmpty()) {
170 return NS_ERROR_FILE_INVALID_PATH;
171 }
172
173 if (NS_FAILED(parent->GetNativePath(mParentPath)))
174 return NS_ERROR_FAILURE;
175
176 mDir = opendir(dirPath.get());
177 if (!mDir)
178 return NSRESULT_FOR_ERRNO();
179 return GetNextEntry();
180}
181
182NS_IMETHODIMP
183nsDirEnumeratorUnix::HasMoreElements(PRBool *result)
184{
185 *result = mDir && mEntry;
186 return NS_OK;
187}
188
189NS_IMETHODIMP
190nsDirEnumeratorUnix::GetNext(nsISupports **_retval)
191{
192 nsresult rv;
193 if (!mDir || !mEntry) {
194 *_retval = nsnull;
195 return NS_OK;
196 }
197
198 nsLocalFile* file = new nsLocalFile();
199 if (!file)
200 return NS_ERROR_OUT_OF_MEMORY;
201
202 if (NS_FAILED(rv = file->InitWithNativePath(mParentPath)) ||
203 NS_FAILED(rv = file->AppendNative(nsDependentCString(mEntry->d_name)))) {
204 return rv;
205 }
206 *_retval = NS_STATIC_CAST(nsISupports *, file);
207 NS_ADDREF(*_retval);
208 return GetNextEntry();
209}
210
211NS_IMETHODIMP
212nsDirEnumeratorUnix::GetNextEntry()
213{
214 do {
215 errno = 0;
216 mEntry = readdir(mDir);
217
218 // end of dir or error
219 if (!mEntry)
220 return NSRESULT_FOR_ERRNO();
221
222 // keep going past "." and ".."
223 } while (mEntry->d_name[0] == '.' &&
224 (mEntry->d_name[1] == '\0' || // .\0
225 (mEntry->d_name[1] == '.' &&
226 mEntry->d_name[2] == '\0'))); // ..\0
227 return NS_OK;
228}
229
230nsLocalFile::nsLocalFile() :
231 mHaveCachedStat(PR_FALSE)
232{
233}
234
235nsLocalFile::nsLocalFile(const nsLocalFile& other)
236 : mCachedStat(other.mCachedStat)
237 , mPath(other.mPath)
238 , mHaveCachedStat(other.mHaveCachedStat)
239{
240}
241
242NS_IMPL_THREADSAFE_ISUPPORTS2(nsLocalFile,
243 nsIFile,
244 nsILocalFile)
245
246nsresult
247nsLocalFile::nsLocalFileConstructor(nsISupports *outer,
248 const nsIID &aIID,
249 void **aInstancePtr)
250{
251 NS_ENSURE_ARG_POINTER(aInstancePtr);
252 NS_ENSURE_NO_AGGREGATION(outer);
253
254 *aInstancePtr = nsnull;
255
256 nsCOMPtr<nsIFile> inst = new nsLocalFile();
257 if (!inst)
258 return NS_ERROR_OUT_OF_MEMORY;
259 return inst->QueryInterface(aIID, aInstancePtr);
260}
261
262nsresult
263nsLocalFile::FillStatCache() {
264 if (stat(mPath.get(), &mCachedStat) == -1) {
265 // try lstat it may be a symlink
266 if (lstat(mPath.get(), &mCachedStat) == -1) {
267 return NSRESULT_FOR_ERRNO();
268 }
269 }
270 mHaveCachedStat = PR_TRUE;
271 return NS_OK;
272}
273
274NS_IMETHODIMP
275nsLocalFile::Clone(nsIFile **file)
276{
277 // Just copy-construct ourselves
278 *file = new nsLocalFile(*this);
279 if (!*file)
280 return NS_ERROR_OUT_OF_MEMORY;
281
282 NS_ADDREF(*file);
283
284 return NS_OK;
285}
286
287NS_IMETHODIMP
288nsLocalFile::InitWithNativePath(const nsACString &filePath)
289{
290 if (Substring(filePath, 0, 2).EqualsLiteral("~/")) {
291 nsCOMPtr<nsIFile> homeDir;
292 nsCAutoString homePath;
293 if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_HOME_DIR,
294 getter_AddRefs(homeDir))) ||
295 NS_FAILED(homeDir->GetNativePath(homePath))) {
296 return NS_ERROR_FAILURE;
297 }
298
299 mPath = homePath + Substring(filePath, 1, filePath.Length() - 1);
300 } else if (filePath.IsEmpty() || filePath.First() != '/') {
301 NS_ERROR("Relative paths not allowed");
302 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
303 } else {
304 mPath = filePath;
305 }
306
307 // trim off trailing slashes
308 ssize_t len = mPath.Length();
309 while ((len > 1) && (mPath[len - 1] == '/'))
310 --len;
311 mPath.SetLength(len);
312
313 InvalidateCache();
314 return NS_OK;
315}
316
317NS_IMETHODIMP
318nsLocalFile::CreateAllAncestors(PRUint32 permissions)
319{
320 // <jband> I promise to play nice
321 char *buffer = mPath.BeginWriting(),
322 *slashp = buffer;
323
324#ifdef DEBUG_NSIFILE
325 fprintf(stderr, "nsIFile: before: %s\n", buffer);
326#endif
327
328 while ((slashp = strchr(slashp + 1, '/'))) {
329 /*
330 * Sequences of '/' are equivalent to a single '/'.
331 */
332 if (slashp[1] == '/')
333 continue;
334
335 /*
336 * If the path has a trailing slash, don't make the last component,
337 * because we'll get EEXIST in Create when we try to build the final
338 * component again, and it's easier to condition the logic here than
339 * there.
340 */
341 if (slashp[1] == '\0')
342 break;
343
344 /* Temporarily NUL-terminate here */
345 *slashp = '\0';
346#ifdef DEBUG_NSIFILE
347 fprintf(stderr, "nsIFile: mkdir(\"%s\")\n", buffer);
348#endif
349 int mkdir_result = mkdir(buffer, permissions);
350 int mkdir_errno = errno;
351 if (mkdir_result == -1) {
352 /*
353 * Always set |errno| to EEXIST if the dir already exists
354 * (we have to do this here since the errno value is not consistent
355 * in all cases - various reasons like different platform,
356 * automounter-controlled dir, etc. can affect it (see bug 125489
357 * for details)).
358 */
359 if (access(buffer, F_OK) == 0) {
360 mkdir_errno = EEXIST;
361 }
362 }
363
364 /* Put the / back before we (maybe) return */
365 *slashp = '/';
366
367 /*
368 * We could get EEXIST for an existing file -- not directory --
369 * with the name of one of our ancestors, but that's OK: we'll get
370 * ENOTDIR when we try to make the next component in the path,
371 * either here on back in Create, and error out appropriately.
372 */
373 if (mkdir_result == -1 && mkdir_errno != EEXIST)
374 return nsresultForIprt(RTErrConvertFromErrno(mkdir_errno));
375 }
376
377#ifdef DEBUG_NSIFILE
378 fprintf(stderr, "nsIFile: after: %s\n", buffer);
379#endif
380
381 return NS_OK;
382}
383
384static int
385do_create(const char *path, uint32_t flags, mode_t mode, RTFILE *_retval)
386{
387 return RTFileOpen(_retval, path, flags | (mode << RTFILE_O_CREATE_MODE_SHIFT));
388}
389
390static int
391do_mkdir(const char *path, uint32_t flags, mode_t mode, RTFILE *_retval)
392{
393 *_retval = NULL;
394 return RTDirCreate(path, mode, 0 /* fCreate*/);
395}
396
397nsresult
398nsLocalFile::CreateAndKeepOpen(PRUint32 type, uint32_t flags,
399 PRUint32 permissions, RTFILE *_retval)
400{
401 if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
402 return NS_ERROR_FILE_UNKNOWN_TYPE;
403
404 int (*createFunc)(const char *, uint32_t, mode_t, RTFILE *) =
405 (type == NORMAL_FILE_TYPE) ? do_create : do_mkdir;
406
407 int vrc = createFunc(mPath.get(), flags, permissions, _retval);
408 if (vrc == VERR_PATH_NOT_FOUND || vrc == VERR_FILE_NOT_FOUND || vrc == VERR_NOT_FOUND) {
409 /*
410 * If we failed because of missing ancestor components, try to create
411 * them and then retry the original creation.
412 *
413 * Ancestor directories get the same permissions as the file we're
414 * creating, with the X bit set for each of (user,group,other) with
415 * an R bit in the original permissions. If you want to do anything
416 * fancy like setgid or sticky bits, do it by hand.
417 */
418 int dirperm = permissions;
419 if (permissions & S_IRUSR)
420 dirperm |= S_IXUSR;
421 if (permissions & S_IRGRP)
422 dirperm |= S_IXGRP;
423 if (permissions & S_IROTH)
424 dirperm |= S_IXOTH;
425
426#ifdef DEBUG_NSIFILE
427 fprintf(stderr, "nsIFile: perm = %o, dirperm = %o\n", permissions,
428 dirperm);
429#endif
430
431 if (NS_FAILED(CreateAllAncestors(dirperm)))
432 return NS_ERROR_FAILURE;
433
434#ifdef DEBUG_NSIFILE
435 fprintf(stderr, "nsIFile: Create(\"%s\") again\n", mPath.get());
436#endif
437 vrc = createFunc(mPath.get(), flags, permissions, _retval);
438 }
439
440 return NSRESULT_FOR_IPRT(vrc);
441}
442
443NS_IMETHODIMP
444nsLocalFile::Create(PRUint32 type, PRUint32 permissions)
445{
446 RTFILE junk = NULL;
447 nsresult rv = CreateAndKeepOpen(type,
448 RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_TRUNCATE |
449 RTFILE_O_DENY_ALL,
450 permissions,
451 &junk);
452 if (junk)
453 RTFileClose(junk);
454 return rv;
455}
456
457NS_IMETHODIMP
458nsLocalFile::AppendNative(const nsACString &fragment)
459{
460 if (fragment.IsEmpty())
461 return NS_OK;
462
463 // only one component of path can be appended
464 nsACString::const_iterator begin, end;
465 if (FindCharInReadable('/', fragment.BeginReading(begin),
466 fragment.EndReading(end)))
467 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
468
469 return AppendRelativeNativePath(fragment);
470}
471
472NS_IMETHODIMP
473nsLocalFile::AppendRelativeNativePath(const nsACString &fragment)
474{
475 if (fragment.IsEmpty())
476 return NS_OK;
477
478 // No leading '/'
479 if (fragment.First() == '/')
480 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
481
482 if (mPath.EqualsLiteral("/"))
483 mPath.Append(fragment);
484 else
485 mPath.Append(NS_LITERAL_CSTRING("/") + fragment);
486
487 InvalidateCache();
488 return NS_OK;
489}
490
491NS_IMETHODIMP
492nsLocalFile::Normalize()
493{
494 char resolved_path[PATH_MAX] = "";
495 char *resolved_path_ptr = nsnull;
496
497#ifdef XP_BEOS
498 BEntry be_e(mPath.get(), true);
499 BPath be_p;
500 status_t err;
501 if ((err = be_e.GetPath(&be_p)) == B_OK) {
502 resolved_path_ptr = (char *)be_p.Path();
503 PL_strncpyz(resolved_path, resolved_path_ptr, PATH_MAX - 1);
504 }
505#else
506 resolved_path_ptr = realpath(mPath.get(), resolved_path);
507#endif
508 // if there is an error, the return is null.
509 if (!resolved_path_ptr)
510 return NSRESULT_FOR_ERRNO();
511
512 mPath = resolved_path;
513 return NS_OK;
514}
515
516void
517nsLocalFile::LocateNativeLeafName(nsACString::const_iterator &begin,
518 nsACString::const_iterator &end)
519{
520 // XXX perhaps we should cache this??
521
522 mPath.BeginReading(begin);
523 mPath.EndReading(end);
524
525 nsACString::const_iterator it = end;
526 nsACString::const_iterator stop = begin;
527 --stop;
528 while (--it != stop) {
529 if (*it == '/') {
530 begin = ++it;
531 return;
532 }
533 }
534 // else, the entire path is the leaf name (which means this
535 // isn't an absolute path... unexpected??)
536}
537
538NS_IMETHODIMP
539nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
540{
541 nsACString::const_iterator begin, end;
542 LocateNativeLeafName(begin, end);
543 aLeafName = Substring(begin, end);
544 return NS_OK;
545}
546
547NS_IMETHODIMP
548nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
549{
550 nsACString::const_iterator begin, end;
551 LocateNativeLeafName(begin, end);
552 mPath.Replace(begin.get() - mPath.get(), Distance(begin, end), aLeafName);
553 InvalidateCache();
554 return NS_OK;
555}
556
557NS_IMETHODIMP
558nsLocalFile::GetNativePath(nsACString &_retval)
559{
560 _retval = mPath;
561 return NS_OK;
562}
563
564nsresult
565nsLocalFile::GetNativeTargetPathName(nsIFile *newParent,
566 const nsACString &newName,
567 nsACString &_retval)
568{
569 nsresult rv;
570 nsCOMPtr<nsIFile> oldParent;
571
572 if (!newParent) {
573 if (NS_FAILED(rv = GetParent(getter_AddRefs(oldParent))))
574 return rv;
575 newParent = oldParent.get();
576 } else {
577 // check to see if our target directory exists
578 PRBool targetExists;
579 if (NS_FAILED(rv = newParent->Exists(&targetExists)))
580 return rv;
581
582 if (!targetExists) {
583 // XXX create the new directory with some permissions
584 rv = newParent->Create(DIRECTORY_TYPE, 0700);
585 if (NS_FAILED(rv))
586 return rv;
587 } else {
588 // make sure that the target is actually a directory
589 PRBool targetIsDirectory;
590 if (NS_FAILED(rv = newParent->IsDirectory(&targetIsDirectory)))
591 return rv;
592 if (!targetIsDirectory)
593 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
594 }
595 }
596
597 nsACString::const_iterator nameBegin, nameEnd;
598 if (!newName.IsEmpty()) {
599 newName.BeginReading(nameBegin);
600 newName.EndReading(nameEnd);
601 }
602 else
603 LocateNativeLeafName(nameBegin, nameEnd);
604
605 nsCAutoString dirName;
606 if (NS_FAILED(rv = newParent->GetNativePath(dirName)))
607 return rv;
608
609 _retval = dirName
610 + NS_LITERAL_CSTRING("/")
611 + Substring(nameBegin, nameEnd);
612 return NS_OK;
613}
614
615nsresult
616nsLocalFile::CopyDirectoryTo(nsIFile *newParent)
617{
618 nsresult rv;
619 /*
620 * dirCheck is used for various boolean test results such as from Equals,
621 * Exists, isDir, etc.
622 */
623 PRBool dirCheck, isSymlink;
624 PRUint32 oldPerms;
625
626 if (NS_FAILED((rv = IsDirectory(&dirCheck))))
627 return rv;
628 if (!dirCheck)
629 return CopyToNative(newParent, EmptyCString());
630
631 if (NS_FAILED(rv = Equals(newParent, &dirCheck)))
632 return rv;
633 if (dirCheck) {
634 // can't copy dir to itself
635 return NS_ERROR_INVALID_ARG;
636 }
637
638 if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
639 return rv;
640 // get the dirs old permissions
641 if (NS_FAILED(rv = GetPermissions(&oldPerms)))
642 return rv;
643 if (!dirCheck) {
644 if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
645 return rv;
646 } else { // dir exists lets try to use leaf
647 nsCAutoString leafName;
648 if (NS_FAILED(rv = GetNativeLeafName(leafName)))
649 return rv;
650 if (NS_FAILED(rv = newParent->AppendNative(leafName)))
651 return rv;
652 if (NS_FAILED(rv = newParent->Exists(&dirCheck)))
653 return rv;
654 if (dirCheck)
655 return NS_ERROR_FILE_ALREADY_EXISTS; // dest exists
656 if (NS_FAILED(rv = newParent->Create(DIRECTORY_TYPE, oldPerms)))
657 return rv;
658 }
659
660 nsCOMPtr<nsISimpleEnumerator> dirIterator;
661 if (NS_FAILED(rv = GetDirectoryEntries(getter_AddRefs(dirIterator))))
662 return rv;
663
664 PRBool hasMore = PR_FALSE;
665 while (dirIterator->HasMoreElements(&hasMore), hasMore) {
666 nsCOMPtr<nsIFile> entry;
667 rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(entry));
668 if (NS_FAILED(rv))
669 continue;
670 if (NS_FAILED(rv = entry->IsSymlink(&isSymlink)))
671 return rv;
672 if (NS_FAILED(rv = entry->IsDirectory(&dirCheck)))
673 return rv;
674 if (dirCheck && !isSymlink) {
675 nsCOMPtr<nsIFile> destClone;
676 rv = newParent->Clone(getter_AddRefs(destClone));
677 if (NS_SUCCEEDED(rv)) {
678 nsCOMPtr<nsILocalFile> newDir(do_QueryInterface(destClone));
679 if (NS_FAILED(rv = entry->CopyToNative(newDir, EmptyCString()))) {
680#ifdef DEBUG
681 nsresult rv2;
682 nsCAutoString pathName;
683 if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
684 return rv2;
685 printf("Operation not supported: %s\n", pathName.get());
686#endif
687 if (rv == NS_ERROR_OUT_OF_MEMORY)
688 return rv;
689 continue;
690 }
691 }
692 } else {
693 if (NS_FAILED(rv = entry->CopyToNative(newParent, EmptyCString()))) {
694#ifdef DEBUG
695 nsresult rv2;
696 nsCAutoString pathName;
697 if (NS_FAILED(rv2 = entry->GetNativePath(pathName)))
698 return rv2;
699 printf("Operation not supported: %s\n", pathName.get());
700#endif
701 if (rv == NS_ERROR_OUT_OF_MEMORY)
702 return rv;
703 continue;
704 }
705 }
706 }
707 return NS_OK;
708}
709
710NS_IMETHODIMP
711nsLocalFile::CopyToNative(nsIFile *newParent, const nsACString &newName)
712{
713 nsresult rv;
714 // check to make sure that this has been initialized properly
715 CHECK_mPath();
716
717 // we copy the parent here so 'newParent' remains immutable
718 nsCOMPtr <nsIFile> workParent;
719 if (newParent) {
720 if (NS_FAILED(rv = newParent->Clone(getter_AddRefs(workParent))))
721 return rv;
722 } else {
723 if (NS_FAILED(rv = GetParent(getter_AddRefs(workParent))))
724 return rv;
725 }
726
727 // check to see if we are a directory or if we are a file
728 PRBool isDirectory;
729 if (NS_FAILED(rv = IsDirectory(&isDirectory)))
730 return rv;
731
732 nsCAutoString newPathName;
733 if (isDirectory) {
734 if (!newName.IsEmpty()) {
735 if (NS_FAILED(rv = workParent->AppendNative(newName)))
736 return rv;
737 } else {
738 if (NS_FAILED(rv = GetNativeLeafName(newPathName)))
739 return rv;
740 if (NS_FAILED(rv = workParent->AppendNative(newPathName)))
741 return rv;
742 }
743 if (NS_FAILED(rv = CopyDirectoryTo(workParent)))
744 return rv;
745 } else {
746 rv = GetNativeTargetPathName(workParent, newName, newPathName);
747 if (NS_FAILED(rv))
748 return rv;
749
750#ifdef DEBUG_blizzard
751 printf("nsLocalFile::CopyTo() %s -> %s\n", mPath.get(), newPathName.get());
752#endif
753
754 // actually create the file.
755 nsLocalFile *newFile = new nsLocalFile();
756 if (!newFile)
757 return NS_ERROR_OUT_OF_MEMORY;
758
759 nsCOMPtr<nsILocalFile> fileRef(newFile); // release on exit
760
761 rv = newFile->InitWithNativePath(newPathName);
762 if (NS_FAILED(rv))
763 return rv;
764
765 // get the old permissions
766 PRUint32 myPerms;
767 GetPermissions(&myPerms);
768
769 // Create the new file with the old file's permissions, even if write
770 // permission is missing. We can't create with write permission and
771 // then change back to myPerm on all filesystems (FAT on Linux, e.g.).
772 // But we can write to a read-only file on all Unix filesystems if we
773 // open it successfully for writing.
774
775 RTFILE newFD;
776 rv = newFile->CreateAndKeepOpen(NORMAL_FILE_TYPE,
777 RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_NONE,
778 myPerms,
779 &newFD);
780 if (NS_FAILED(rv))
781 return rv;
782
783 // open the old file, too
784 PRBool specialFile;
785 if (NS_FAILED(rv = IsSpecial(&specialFile))) {
786 RTFileClose(newFD);
787 return rv;
788 }
789 if (specialFile) {
790#ifdef DEBUG
791 printf("Operation not supported: %s\n", mPath.get());
792#endif
793 // make sure to clean up properly
794 RTFileClose(newFD);
795 return NS_OK;
796 }
797
798 RTFILE oldFD = NIL_RTFILE;
799 int vrc = RTFileOpen(&oldFD, mPath.get(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
800 if (RT_FAILURE(vrc))
801 rv = NS_ErrorAccordingToIPRT(vrc);
802 if (NS_FAILED(rv)) {
803 // make sure to clean up properly
804 RTFileClose(newFD);
805 return rv;
806 }
807
808 vrc = RTFileCopyByHandles(oldFD, newFD);
809
810 // close the files
811 RTFileClose(newFD);
812 RTFileClose(oldFD);
813
814 // check for read (or write) error after cleaning up
815 if (RT_FAILURE(vrc))
816 return NS_ERROR_OUT_OF_MEMORY;
817 }
818 return rv;
819}
820
821NS_IMETHODIMP
822nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParent, const nsACString &newName)
823{
824 return CopyToNative(newParent, newName);
825}
826
827NS_IMETHODIMP
828nsLocalFile::MoveToNative(nsIFile *newParent, const nsACString &newName)
829{
830 nsresult rv;
831
832 // check to make sure that this has been initialized properly
833 CHECK_mPath();
834
835 // check to make sure that we have a new parent
836 nsCAutoString newPathName;
837 rv = GetNativeTargetPathName(newParent, newName, newPathName);
838 if (NS_FAILED(rv))
839 return rv;
840
841 // try for atomic rename, falling back to copy/delete
842 if (rename(mPath.get(), newPathName.get()) < 0) {
843#ifdef VMS
844 if (errno == EXDEV || errno == ENXIO) {
845#else
846 if (errno == EXDEV) {
847#endif
848 rv = CopyToNative(newParent, newName);
849 if (NS_SUCCEEDED(rv))
850 rv = Remove(PR_TRUE);
851 } else {
852 rv = NSRESULT_FOR_ERRNO();
853 }
854 }
855 return rv;
856}
857
858NS_IMETHODIMP
859nsLocalFile::Remove(PRBool recursive)
860{
861 CHECK_mPath();
862
863 VALIDATE_STAT_CACHE();
864 PRBool isSymLink, isDir;
865
866 nsresult rv = IsSymlink(&isSymLink);
867 if (NS_FAILED(rv))
868 return rv;
869
870 if (!recursive && isSymLink)
871 return NSRESULT_FOR_IPRT(RTErrConvertFromErrno(unlink(mPath.get())));
872
873 isDir = S_ISDIR(mCachedStat.st_mode);
874 InvalidateCache();
875 if (isDir) {
876 if (recursive) {
877 nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
878 if (!dir)
879 return NS_ERROR_OUT_OF_MEMORY;
880
881 nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
882
883 rv = dir->Init(this, PR_FALSE);
884 if (NS_FAILED(rv))
885 return rv;
886
887 PRBool more;
888 while (dir->HasMoreElements(&more), more) {
889 nsCOMPtr<nsISupports> item;
890 rv = dir->GetNext(getter_AddRefs(item));
891 if (NS_FAILED(rv))
892 return NS_ERROR_FAILURE;
893
894 nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
895 if (NS_FAILED(rv))
896 return NS_ERROR_FAILURE;
897 if (NS_FAILED(rv = file->Remove(recursive)))
898 return rv;
899 }
900 }
901
902 if (rmdir(mPath.get()) == -1)
903 return NSRESULT_FOR_ERRNO();
904 } else {
905 if (unlink(mPath.get()) == -1)
906 return NSRESULT_FOR_ERRNO();
907 }
908
909 return NS_OK;
910}
911
912NS_IMETHODIMP
913nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModTime)
914{
915 CHECK_mPath();
916 NS_ENSURE_ARG(aLastModTime);
917
918 RTFSOBJINFO info;
919 int vrc = RTPathQueryInfo(mPath.get(), &info, RTFSOBJATTRADD_NOTHING);
920 if (RT_FAILURE(vrc))
921 return NSRESULT_FOR_IPRT(vrc);
922
923 // microseconds -> milliseconds
924 *aLastModTime = RTTimeSpecGetMilli(&info.ModificationTime);
925 return NS_OK;
926}
927
928NS_IMETHODIMP
929nsLocalFile::SetLastModifiedTime(PRInt64 aLastModTime)
930{
931 CHECK_mPath();
932
933 int result;
934 if (! LL_IS_ZERO(aLastModTime)) {
935 VALIDATE_STAT_CACHE();
936 struct utimbuf ut;
937 ut.actime = mCachedStat.st_atime;
938
939 // convert milliseconds to seconds since the unix epoch
940 double dTime;
941 LL_L2D(dTime, aLastModTime);
942 ut.modtime = (time_t) (dTime / PR_MSEC_PER_SEC);
943 result = utime(mPath.get(), &ut);
944 } else {
945 result = utime(mPath.get(), nsnull);
946 }
947 InvalidateCache();
948 return NSRESULT_FOR_IPRT(RTErrConvertFromErrno(result));
949}
950
951NS_IMETHODIMP
952nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModTimeOfLink)
953{
954 CHECK_mPath();
955 NS_ENSURE_ARG(aLastModTimeOfLink);
956
957 struct stat sbuf;
958 if (lstat(mPath.get(), &sbuf) == -1)
959 return NSRESULT_FOR_ERRNO();
960 LL_I2L(*aLastModTimeOfLink, (PRInt32)sbuf.st_mtime);
961
962 // lstat returns st_mtime in seconds
963 PRInt64 msecPerSec;
964 LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
965 LL_MUL(*aLastModTimeOfLink, *aLastModTimeOfLink, msecPerSec);
966
967 return NS_OK;
968}
969
970/*
971 * utime(2) may or may not dereference symlinks, joy.
972 */
973NS_IMETHODIMP
974nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModTimeOfLink)
975{
976 return SetLastModifiedTime(aLastModTimeOfLink);
977}
978
979/*
980 * Only send back permissions bits: maybe we want to send back the whole
981 * mode_t to permit checks against other file types?
982 */
983
984#define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
985
986NS_IMETHODIMP
987nsLocalFile::GetPermissions(PRUint32 *aPermissions)
988{
989 NS_ENSURE_ARG(aPermissions);
990 VALIDATE_STAT_CACHE();
991 *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
992 return NS_OK;
993}
994
995NS_IMETHODIMP
996nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
997{
998 CHECK_mPath();
999 NS_ENSURE_ARG(aPermissionsOfLink);
1000
1001 struct stat sbuf;
1002 if (lstat(mPath.get(), &sbuf) == -1)
1003 return NSRESULT_FOR_ERRNO();
1004 *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
1005 return NS_OK;
1006}
1007
1008NS_IMETHODIMP
1009nsLocalFile::SetPermissions(PRUint32 aPermissions)
1010{
1011 CHECK_mPath();
1012
1013 InvalidateCache();
1014
1015 /*
1016 * Race condition here: we should use fchmod instead, there's no way to
1017 * guarantee the name still refers to the same file.
1018 */
1019 if (chmod(mPath.get(), aPermissions) < 0)
1020 return NSRESULT_FOR_ERRNO();
1021 return NS_OK;
1022}
1023
1024NS_IMETHODIMP
1025nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
1026{
1027 return SetPermissions(aPermissions);
1028}
1029
1030NS_IMETHODIMP
1031nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1032{
1033 NS_ENSURE_ARG_POINTER(aFileSize);
1034 *aFileSize = LL_ZERO;
1035 VALIDATE_STAT_CACHE();
1036
1037#if defined(VMS)
1038 /* Only two record formats can report correct file content size */
1039 if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
1040 (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
1041 return NS_ERROR_FAILURE;
1042 }
1043#endif
1044
1045 /* XXX autoconf for and use stat64 if available */
1046 if (!S_ISDIR(mCachedStat.st_mode)) {
1047 LL_UI2L(*aFileSize, (PRUint32)mCachedStat.st_size);
1048 }
1049 return NS_OK;
1050}
1051
1052NS_IMETHODIMP
1053nsLocalFile::SetFileSize(PRInt64 aFileSize)
1054{
1055 CHECK_mPath();
1056
1057 PRInt32 size;
1058 LL_L2I(size, aFileSize);
1059 /* XXX truncate64? */
1060 InvalidateCache();
1061 if (truncate(mPath.get(), (off_t)size) == -1)
1062 return NSRESULT_FOR_ERRNO();
1063 return NS_OK;
1064}
1065
1066NS_IMETHODIMP
1067nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1068{
1069 CHECK_mPath();
1070 NS_ENSURE_ARG(aFileSize);
1071
1072 struct stat sbuf;
1073 if (lstat(mPath.get(), &sbuf) == -1)
1074 return NSRESULT_FOR_ERRNO();
1075 /* XXX autoconf for and use lstat64 if available */
1076 LL_UI2L(*aFileSize, (PRUint32)sbuf.st_size);
1077 return NS_OK;
1078}
1079
1080NS_IMETHODIMP
1081nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1082{
1083 NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
1084
1085 // These systems have the operations necessary to check disk space.
1086
1087#if defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_STATVFS_H)
1088
1089 // check to make sure that mPath is properly initialized
1090 CHECK_mPath();
1091
1092 struct STATFS fs_buf;
1093
1094 /*
1095 * Members of the STATFS struct that you should know about:
1096 * f_bsize = block size on disk.
1097 * f_bavail = number of free blocks available to a non-superuser.
1098 * f_bfree = number of total free blocks in file system.
1099 */
1100
1101 if (STATFS(mPath.get(), &fs_buf) < 0) {
1102 // The call to STATFS failed.
1103#ifdef DEBUG
1104 printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
1105#endif
1106 return NS_ERROR_FAILURE;
1107 }
1108#ifdef DEBUG_DISK_SPACE
1109 printf("DiskSpaceAvailable: %d bytes\n",
1110 fs_buf.f_bsize * (fs_buf.f_bavail - 1));
1111#endif
1112
1113 /*
1114 * The number of bytes free == The number of free blocks available to
1115 * a non-superuser, minus one as a fudge factor, multiplied by the size
1116 * of the aforementioned blocks.
1117 */
1118 PRInt64 bsize, bavail;
1119
1120 LL_I2L(bsize, fs_buf.f_bsize);
1121 LL_I2L(bavail, fs_buf.f_bavail - 1);
1122 LL_MUL(*aDiskSpaceAvailable, bsize, bavail);
1123 return NS_OK;
1124
1125#else
1126 /*
1127 * This platform doesn't have statfs or statvfs. I'm sure that there's
1128 * a way to check for free disk space on platforms that don't have statfs
1129 * (I'm SURE they have df, for example).
1130 *
1131 * Until we figure out how to do that, lets be honest and say that this
1132 * command isn't implemented properly for these platforms yet.
1133 */
1134#ifdef DEBUG
1135 printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
1136#endif
1137 return NS_ERROR_NOT_IMPLEMENTED;
1138
1139#endif /* HAVE_SYS_STATFS_H or HAVE_SYS_STATVFS_H */
1140
1141}
1142
1143NS_IMETHODIMP
1144nsLocalFile::GetParent(nsIFile **aParent)
1145{
1146 CHECK_mPath();
1147 NS_ENSURE_ARG_POINTER(aParent);
1148 *aParent = nsnull;
1149
1150 // if '/' we are at the top of the volume, return null
1151 if (mPath.Equals("/"))
1152 return NS_OK;
1153
1154 // <brendan, after jband> I promise to play nice
1155 char *buffer = mPath.BeginWriting(),
1156 *slashp = buffer;
1157
1158 // find the last significant slash in buffer
1159 slashp = strrchr(buffer, '/');
1160 NS_ASSERTION(slashp, "non-canonical mPath?");
1161 if (!slashp)
1162 return NS_ERROR_FILE_INVALID_PATH;
1163
1164 // for the case where we are at '/'
1165 if (slashp == buffer)
1166 slashp++;
1167
1168 // temporarily terminate buffer at the last significant slash
1169 char c = *slashp;
1170 *slashp = '\0';
1171
1172 nsCOMPtr<nsILocalFile> localFile;
1173 nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), PR_TRUE,
1174 getter_AddRefs(localFile));
1175
1176 // make buffer whole again
1177 *slashp = c;
1178
1179 if (NS_SUCCEEDED(rv) && localFile)
1180 rv = CallQueryInterface(localFile, aParent);
1181 return rv;
1182}
1183
1184/*
1185 * The results of Exists, isWritable and isReadable are not cached.
1186 */
1187
1188NS_IMETHODIMP
1189nsLocalFile::Exists(PRBool *_retval)
1190{
1191 CHECK_mPath();
1192 NS_ENSURE_ARG_POINTER(_retval);
1193
1194 *_retval = (access(mPath.get(), F_OK) == 0);
1195 return NS_OK;
1196}
1197
1198#ifdef XP_BEOS
1199// access() is buggy in BeOS POSIX implementation, at least for BFS, using stat() instead
1200NS_IMETHODIMP
1201nsLocalFile::IsWritable(PRBool *_retval)
1202{
1203 CHECK_mPath();
1204 NS_ENSURE_ARG_POINTER(_retval);
1205 struct stat buf;
1206 *_retval = (stat(mPath.get(), &buf) == 0);
1207 *_retval = *_retval && (buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH ));
1208 if (*_retval || errno == EACCES)
1209 return NS_OK;
1210 return NSRESULT_FOR_ERRNO();
1211}
1212
1213NS_IMETHODIMP
1214nsLocalFile::IsReadable(PRBool *_retval)
1215{
1216 CHECK_mPath();
1217 NS_ENSURE_ARG_POINTER(_retval);
1218 struct stat buf;
1219 *_retval = (stat(mPath.get(), &buf) == 0);
1220 *_retval = *_retval && (buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH ));
1221 if (*_retval || errno == EACCES)
1222 return NS_OK;
1223 return NSRESULT_FOR_ERRNO();
1224}
1225
1226NS_IMETHODIMP
1227nsLocalFile::IsExecutable(PRBool *_retval)
1228{
1229 CHECK_mPath();
1230 NS_ENSURE_ARG_POINTER(_retval);
1231 struct stat buf;
1232 *_retval = (stat(mPath.get(), &buf) == 0);
1233 *_retval = *_retval && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
1234 if (*_retval || errno == EACCES)
1235 return NS_OK;
1236 return NSRESULT_FOR_ERRNO();
1237}
1238#else
1239NS_IMETHODIMP
1240nsLocalFile::IsWritable(PRBool *_retval)
1241{
1242 CHECK_mPath();
1243 NS_ENSURE_ARG_POINTER(_retval);
1244
1245 *_retval = (access(mPath.get(), W_OK) == 0);
1246 if (*_retval || errno == EACCES)
1247 return NS_OK;
1248 return NSRESULT_FOR_ERRNO();
1249}
1250
1251NS_IMETHODIMP
1252nsLocalFile::IsReadable(PRBool *_retval)
1253{
1254 CHECK_mPath();
1255 NS_ENSURE_ARG_POINTER(_retval);
1256
1257 *_retval = (access(mPath.get(), R_OK) == 0);
1258 if (*_retval || errno == EACCES)
1259 return NS_OK;
1260 return NSRESULT_FOR_ERRNO();
1261}
1262
1263NS_IMETHODIMP
1264nsLocalFile::IsExecutable(PRBool *_retval)
1265{
1266 CHECK_mPath();
1267 NS_ENSURE_ARG_POINTER(_retval);
1268
1269 *_retval = (access(mPath.get(), X_OK) == 0);
1270 if (*_retval || errno == EACCES)
1271 return NS_OK;
1272 return NSRESULT_FOR_ERRNO();
1273}
1274#endif
1275NS_IMETHODIMP
1276nsLocalFile::IsDirectory(PRBool *_retval)
1277{
1278 NS_ENSURE_ARG_POINTER(_retval);
1279 *_retval = PR_FALSE;
1280 VALIDATE_STAT_CACHE();
1281 *_retval = S_ISDIR(mCachedStat.st_mode);
1282 return NS_OK;
1283}
1284
1285NS_IMETHODIMP
1286nsLocalFile::IsFile(PRBool *_retval)
1287{
1288 NS_ENSURE_ARG_POINTER(_retval);
1289 *_retval = PR_FALSE;
1290 VALIDATE_STAT_CACHE();
1291 *_retval = S_ISREG(mCachedStat.st_mode);
1292 return NS_OK;
1293}
1294
1295NS_IMETHODIMP
1296nsLocalFile::IsHidden(PRBool *_retval)
1297{
1298 NS_ENSURE_ARG_POINTER(_retval);
1299 nsACString::const_iterator begin, end;
1300 LocateNativeLeafName(begin, end);
1301 *_retval = (*begin == '.');
1302 return NS_OK;
1303}
1304
1305NS_IMETHODIMP
1306nsLocalFile::IsSymlink(PRBool *_retval)
1307{
1308 NS_ENSURE_ARG_POINTER(_retval);
1309 CHECK_mPath();
1310
1311 struct stat symStat;
1312 lstat(mPath.get(), &symStat);
1313 *_retval=S_ISLNK(symStat.st_mode);
1314 return NS_OK;
1315}
1316
1317NS_IMETHODIMP
1318nsLocalFile::IsSpecial(PRBool *_retval)
1319{
1320 NS_ENSURE_ARG_POINTER(_retval);
1321 VALIDATE_STAT_CACHE();
1322 *_retval = S_ISCHR(mCachedStat.st_mode) ||
1323 S_ISBLK(mCachedStat.st_mode) ||
1324#ifdef S_ISSOCK
1325 S_ISSOCK(mCachedStat.st_mode) ||
1326#endif
1327 S_ISFIFO(mCachedStat.st_mode);
1328
1329 return NS_OK;
1330}
1331
1332NS_IMETHODIMP
1333nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
1334{
1335 NS_ENSURE_ARG(inFile);
1336 NS_ENSURE_ARG_POINTER(_retval);
1337 *_retval = PR_FALSE;
1338
1339 nsresult rv;
1340 nsCAutoString inPath;
1341
1342 if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1343 return rv;
1344
1345 *_retval = !FILE_STRCMP(inPath.get(), mPath.get());
1346 return NS_OK;
1347}
1348
1349NS_IMETHODIMP
1350nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
1351{
1352 CHECK_mPath();
1353 NS_ENSURE_ARG(inFile);
1354 NS_ENSURE_ARG_POINTER(_retval);
1355
1356 nsCAutoString inPath;
1357 nsresult rv;
1358
1359 if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1360 return rv;
1361
1362 *_retval = PR_FALSE;
1363
1364 ssize_t len = mPath.Length();
1365 if (FILE_STRNCMP(mPath.get(), inPath.get(), len) == 0) {
1366 // Now make sure that the |inFile|'s path has a separator at len,
1367 // which implies that it has more components after len.
1368 if (inPath[len] == '/')
1369 *_retval = PR_TRUE;
1370 }
1371
1372 return NS_OK;
1373}
1374
1375NS_IMETHODIMP
1376nsLocalFile::GetNativeTarget(nsACString &_retval)
1377{
1378 CHECK_mPath();
1379 _retval.Truncate();
1380
1381 struct stat symStat;
1382 lstat(mPath.get(), &symStat);
1383 if (!S_ISLNK(symStat.st_mode))
1384 return NS_ERROR_FILE_INVALID_PATH;
1385
1386 PRInt64 targetSize64;
1387 if (NS_FAILED(GetFileSizeOfLink(&targetSize64)))
1388 return NS_ERROR_FAILURE;
1389
1390 PRInt32 size;
1391 LL_L2I(size, targetSize64);
1392 char *target = (char *)nsMemory::Alloc(size + 1);
1393 if (!target)
1394 return NS_ERROR_OUT_OF_MEMORY;
1395
1396 if (readlink(mPath.get(), target, (size_t)size) < 0) {
1397 nsMemory::Free(target);
1398 return NSRESULT_FOR_ERRNO();
1399 }
1400 target[size] = '\0';
1401
1402 nsresult rv;
1403 PRBool isSymlink;
1404 nsCOMPtr<nsIFile> self(this);
1405 nsCOMPtr<nsIFile> parent;
1406 while (NS_SUCCEEDED(rv = self->GetParent(getter_AddRefs(parent)))) {
1407 NS_ASSERTION(parent != nsnull, "no parent?!");
1408
1409 if (target[0] != '/') {
1410 nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(parent, &rv));
1411 if (NS_FAILED(rv))
1412 break;
1413 if (NS_FAILED(rv = localFile->AppendRelativeNativePath(nsDependentCString(target))))
1414 break;
1415 if (NS_FAILED(rv = localFile->GetNativePath(_retval)))
1416 break;
1417 if (NS_FAILED(rv = parent->IsSymlink(&isSymlink)))
1418 break;
1419 self = parent;
1420 } else {
1421 nsCOMPtr<nsILocalFile> localFile;
1422 rv = NS_NewNativeLocalFile(nsDependentCString(target), PR_TRUE,
1423 getter_AddRefs(localFile));
1424 if (NS_FAILED(rv))
1425 break;
1426 if (NS_FAILED(rv = localFile->IsSymlink(&isSymlink)))
1427 break;
1428 _retval = target; // XXX can we avoid this buffer copy?
1429 self = do_QueryInterface(localFile);
1430 }
1431 if (NS_FAILED(rv) || !isSymlink)
1432 break;
1433
1434 const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
1435
1436 // strip off any and all trailing '/'
1437 PRInt32 len = strlen(target);
1438 while (target[len-1] == '/' && len > 1)
1439 target[--len] = '\0';
1440 if (lstat(flatRetval.get(), &symStat) < 0) {
1441 rv = NSRESULT_FOR_ERRNO();
1442 break;
1443 }
1444 if (!S_ISLNK(symStat.st_mode)) {
1445 rv = NS_ERROR_FILE_INVALID_PATH;
1446 break;
1447 }
1448 size = symStat.st_size;
1449 if (readlink(flatRetval.get(), target, size) < 0) {
1450 rv = NSRESULT_FOR_ERRNO();
1451 break;
1452 }
1453 target[size] = '\0';
1454
1455 _retval.Truncate();
1456 }
1457
1458 nsMemory::Free(target);
1459
1460 if (NS_FAILED(rv))
1461 _retval.Truncate();
1462 return rv;
1463}
1464
1465/* attribute PRBool followLinks; */
1466NS_IMETHODIMP
1467nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
1468{
1469 *aFollowLinks = PR_TRUE;
1470 return NS_OK;
1471}
1472
1473NS_IMETHODIMP
1474nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
1475{
1476 return NS_OK;
1477}
1478
1479NS_IMETHODIMP
1480nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
1481{
1482 nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
1483 if (!dir)
1484 return NS_ERROR_OUT_OF_MEMORY;
1485
1486 NS_ADDREF(dir);
1487 nsresult rv = dir->Init(this, PR_FALSE);
1488 if (NS_FAILED(rv)) {
1489 *entries = nsnull;
1490 NS_RELEASE(dir);
1491 } else {
1492 *entries = dir; // transfer reference
1493 }
1494
1495 return rv;
1496}
1497
1498NS_IMETHODIMP
1499nsLocalFile::Load(RTLDRMOD *phMod)
1500{
1501 CHECK_mPath();
1502 NS_ENSURE_ARG_POINTER(phMod);
1503
1504 RTLDRMOD hMod = NIL_RTLDRMOD;
1505 int vrc = RTLdrLoad(mPath.get(), &hMod);
1506 if (RT_FAILURE(vrc))
1507 return NS_ERROR_FAILURE;
1508
1509 *phMod = hMod;
1510 return NS_OK;
1511}
1512
1513NS_IMETHODIMP
1514nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
1515{
1516 return GetNativePath(aPersistentDescriptor);
1517}
1518
1519NS_IMETHODIMP
1520nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
1521{
1522 return InitWithNativePath(aPersistentDescriptor);
1523}
1524
1525#ifdef XP_BEOS
1526NS_IMETHODIMP
1527nsLocalFile::Reveal()
1528{
1529 BPath bPath(mPath.get());
1530 bPath.GetParent(&bPath);
1531 entry_ref ref;
1532 get_ref_for_path(bPath.Path(),&ref);
1533 BMessage message(B_REFS_RECEIVED);
1534 message.AddRef("refs",&ref);
1535 BMessenger messenger("application/x-vnd.Be-TRAK");
1536 messenger.SendMessage(&message);
1537 return NS_OK;
1538}
1539
1540NS_IMETHODIMP
1541nsLocalFile::Launch()
1542{
1543 entry_ref ref;
1544 get_ref_for_path (mPath.get(), &ref);
1545 be_roster->Launch (&ref);
1546
1547 return NS_OK;
1548}
1549#else
1550NS_IMETHODIMP
1551nsLocalFile::Reveal()
1552{
1553 return NS_ERROR_FAILURE;
1554}
1555
1556NS_IMETHODIMP
1557nsLocalFile::Launch()
1558{
1559 return NS_ERROR_FAILURE;
1560}
1561#endif
1562
1563nsresult
1564NS_NewNativeLocalFile(const nsACString &path, PRBool followSymlinks, nsILocalFile **result)
1565{
1566 nsLocalFile *file = new nsLocalFile();
1567 if (!file)
1568 return NS_ERROR_OUT_OF_MEMORY;
1569 NS_ADDREF(file);
1570
1571 if (!path.IsEmpty()) {
1572 nsresult rv = file->InitWithNativePath(path);
1573 if (NS_FAILED(rv)) {
1574 NS_RELEASE(file);
1575 return rv;
1576 }
1577 }
1578 *result = file;
1579 return NS_OK;
1580}
1581
1582//-----------------------------------------------------------------------------
1583// unicode support
1584//-----------------------------------------------------------------------------
1585
1586#define SET_UCS(func, ucsArg) \
1587 { \
1588 nsCAutoString buf; \
1589 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1590 if (NS_FAILED(rv)) \
1591 return rv; \
1592 return (func)(buf); \
1593 }
1594
1595#define GET_UCS(func, ucsArg) \
1596 { \
1597 nsCAutoString buf; \
1598 nsresult rv = (func)(buf); \
1599 if (NS_FAILED(rv)) return rv; \
1600 return NS_CopyNativeToUnicode(buf, ucsArg); \
1601 }
1602
1603#define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
1604 { \
1605 nsCAutoString buf; \
1606 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1607 if (NS_FAILED(rv)) \
1608 return rv; \
1609 return (func)(opaqueArg, buf); \
1610 }
1611
1612// Unicode interface Wrapper
1613nsresult
1614nsLocalFile::InitWithPath(const nsAString &filePath)
1615{
1616 SET_UCS(InitWithNativePath, filePath);
1617}
1618nsresult
1619nsLocalFile::Append(const nsAString &node)
1620{
1621 SET_UCS(AppendNative, node);
1622}
1623nsresult
1624nsLocalFile::AppendRelativePath(const nsAString &node)
1625{
1626 SET_UCS(AppendRelativeNativePath, node);
1627}
1628nsresult
1629nsLocalFile::GetLeafName(nsAString &aLeafName)
1630{
1631 GET_UCS(GetNativeLeafName, aLeafName);
1632}
1633nsresult
1634nsLocalFile::SetLeafName(const nsAString &aLeafName)
1635{
1636 SET_UCS(SetNativeLeafName, aLeafName);
1637}
1638nsresult
1639nsLocalFile::GetPath(nsAString &_retval)
1640{
1641 return NS_CopyNativeToUnicode(mPath, _retval);
1642}
1643nsresult
1644nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
1645{
1646 SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
1647}
1648nsresult
1649nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
1650{
1651 SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
1652}
1653nsresult
1654nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
1655{
1656 SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
1657}
1658nsresult
1659nsLocalFile::GetTarget(nsAString &_retval)
1660{
1661 GET_UCS(GetNativeTarget, _retval);
1662}
1663nsresult
1664NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
1665{
1666 nsCAutoString buf;
1667 nsresult rv = NS_CopyUnicodeToNative(path, buf);
1668 if (NS_FAILED(rv))
1669 return rv;
1670 return NS_NewNativeLocalFile(buf, followLinks, result);
1671}
1672
1673//-----------------------------------------------------------------------------
1674// global init/shutdown
1675//-----------------------------------------------------------------------------
1676
1677void
1678nsLocalFile::GlobalInit()
1679{
1680}
1681
1682void
1683nsLocalFile::GlobalShutdown()
1684{
1685}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette