VirtualBox

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

Last change on this file since 101985 was 101985, checked in by vboxsync, 15 months ago

libs/xpcom: Cleanup nsDebugImpl.{cpp,h}, remove unused code and move a method to the single caller, bugref:10545

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

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