VirtualBox

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

Last change on this file since 106945 was 104490, checked in by vboxsync, 7 months ago

src/libs/xpcom/io: Some smaller cleanups, fix a possible double expansion of a statement with side effects passed to a macro, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.8 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 <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 {
872 int vrc = RTErrConvertFromErrno(unlink(mPath.get()));
873 return NSRESULT_FOR_IPRT(vrc);
874 }
875
876 isDir = S_ISDIR(mCachedStat.st_mode);
877 InvalidateCache();
878 if (isDir) {
879 if (recursive) {
880 nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
881 if (!dir)
882 return NS_ERROR_OUT_OF_MEMORY;
883
884 nsCOMPtr<nsISimpleEnumerator> dirRef(dir); // release on exit
885
886 rv = dir->Init(this, PR_FALSE);
887 if (NS_FAILED(rv))
888 return rv;
889
890 PRBool more;
891 while (dir->HasMoreElements(&more), more) {
892 nsCOMPtr<nsISupports> item;
893 rv = dir->GetNext(getter_AddRefs(item));
894 if (NS_FAILED(rv))
895 return NS_ERROR_FAILURE;
896
897 nsCOMPtr<nsIFile> file = do_QueryInterface(item, &rv);
898 if (NS_FAILED(rv))
899 return NS_ERROR_FAILURE;
900 if (NS_FAILED(rv = file->Remove(recursive)))
901 return rv;
902 }
903 }
904
905 if (rmdir(mPath.get()) == -1)
906 return NSRESULT_FOR_ERRNO();
907 } else {
908 if (unlink(mPath.get()) == -1)
909 return NSRESULT_FOR_ERRNO();
910 }
911
912 return NS_OK;
913}
914
915NS_IMETHODIMP
916nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModTime)
917{
918 CHECK_mPath();
919 NS_ENSURE_ARG(aLastModTime);
920
921 RTFSOBJINFO info;
922 int vrc = RTPathQueryInfo(mPath.get(), &info, RTFSOBJATTRADD_NOTHING);
923 if (RT_FAILURE(vrc))
924 return NSRESULT_FOR_IPRT(vrc);
925
926 // microseconds -> milliseconds
927 *aLastModTime = RTTimeSpecGetMilli(&info.ModificationTime);
928 return NS_OK;
929}
930
931NS_IMETHODIMP
932nsLocalFile::SetLastModifiedTime(PRInt64 aLastModTime)
933{
934 CHECK_mPath();
935
936 int result;
937 if (! LL_IS_ZERO(aLastModTime)) {
938 VALIDATE_STAT_CACHE();
939 struct utimbuf ut;
940 ut.actime = mCachedStat.st_atime;
941
942 // convert milliseconds to seconds since the unix epoch
943 double dTime;
944 LL_L2D(dTime, aLastModTime);
945 ut.modtime = (time_t) (dTime / PR_MSEC_PER_SEC);
946 result = utime(mPath.get(), &ut);
947 } else {
948 result = utime(mPath.get(), nsnull);
949 }
950 InvalidateCache();
951 int vrc = RTErrConvertFromErrno(result);
952 return NSRESULT_FOR_IPRT(vrc);
953}
954
955NS_IMETHODIMP
956nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModTimeOfLink)
957{
958 CHECK_mPath();
959 NS_ENSURE_ARG(aLastModTimeOfLink);
960
961 struct stat sbuf;
962 if (lstat(mPath.get(), &sbuf) == -1)
963 return NSRESULT_FOR_ERRNO();
964 LL_I2L(*aLastModTimeOfLink, (PRInt32)sbuf.st_mtime);
965
966 // lstat returns st_mtime in seconds
967 PRInt64 msecPerSec;
968 LL_I2L(msecPerSec, PR_MSEC_PER_SEC);
969 LL_MUL(*aLastModTimeOfLink, *aLastModTimeOfLink, msecPerSec);
970
971 return NS_OK;
972}
973
974/*
975 * utime(2) may or may not dereference symlinks, joy.
976 */
977NS_IMETHODIMP
978nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModTimeOfLink)
979{
980 return SetLastModifiedTime(aLastModTimeOfLink);
981}
982
983/*
984 * Only send back permissions bits: maybe we want to send back the whole
985 * mode_t to permit checks against other file types?
986 */
987
988#define NORMALIZE_PERMS(mode) ((mode)& (S_IRWXU | S_IRWXG | S_IRWXO))
989
990NS_IMETHODIMP
991nsLocalFile::GetPermissions(PRUint32 *aPermissions)
992{
993 NS_ENSURE_ARG(aPermissions);
994 VALIDATE_STAT_CACHE();
995 *aPermissions = NORMALIZE_PERMS(mCachedStat.st_mode);
996 return NS_OK;
997}
998
999NS_IMETHODIMP
1000nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
1001{
1002 CHECK_mPath();
1003 NS_ENSURE_ARG(aPermissionsOfLink);
1004
1005 struct stat sbuf;
1006 if (lstat(mPath.get(), &sbuf) == -1)
1007 return NSRESULT_FOR_ERRNO();
1008 *aPermissionsOfLink = NORMALIZE_PERMS(sbuf.st_mode);
1009 return NS_OK;
1010}
1011
1012NS_IMETHODIMP
1013nsLocalFile::SetPermissions(PRUint32 aPermissions)
1014{
1015 CHECK_mPath();
1016
1017 InvalidateCache();
1018
1019 /*
1020 * Race condition here: we should use fchmod instead, there's no way to
1021 * guarantee the name still refers to the same file.
1022 */
1023 if (chmod(mPath.get(), aPermissions) < 0)
1024 return NSRESULT_FOR_ERRNO();
1025 return NS_OK;
1026}
1027
1028NS_IMETHODIMP
1029nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
1030{
1031 return SetPermissions(aPermissions);
1032}
1033
1034NS_IMETHODIMP
1035nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1036{
1037 NS_ENSURE_ARG_POINTER(aFileSize);
1038 *aFileSize = LL_ZERO;
1039 VALIDATE_STAT_CACHE();
1040
1041#if defined(VMS)
1042 /* Only two record formats can report correct file content size */
1043 if ((mCachedStat.st_fab_rfm != FAB$C_STMLF) &&
1044 (mCachedStat.st_fab_rfm != FAB$C_STMCR)) {
1045 return NS_ERROR_FAILURE;
1046 }
1047#endif
1048
1049 /* XXX autoconf for and use stat64 if available */
1050 if (!S_ISDIR(mCachedStat.st_mode)) {
1051 LL_UI2L(*aFileSize, (PRUint32)mCachedStat.st_size);
1052 }
1053 return NS_OK;
1054}
1055
1056NS_IMETHODIMP
1057nsLocalFile::SetFileSize(PRInt64 aFileSize)
1058{
1059 CHECK_mPath();
1060
1061 PRInt32 size;
1062 LL_L2I(size, aFileSize);
1063 /* XXX truncate64? */
1064 InvalidateCache();
1065 if (truncate(mPath.get(), (off_t)size) == -1)
1066 return NSRESULT_FOR_ERRNO();
1067 return NS_OK;
1068}
1069
1070NS_IMETHODIMP
1071nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1072{
1073 CHECK_mPath();
1074 NS_ENSURE_ARG(aFileSize);
1075
1076 struct stat sbuf;
1077 if (lstat(mPath.get(), &sbuf) == -1)
1078 return NSRESULT_FOR_ERRNO();
1079 /* XXX autoconf for and use lstat64 if available */
1080 LL_UI2L(*aFileSize, (PRUint32)sbuf.st_size);
1081 return NS_OK;
1082}
1083
1084NS_IMETHODIMP
1085nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1086{
1087 NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable);
1088
1089 // These systems have the operations necessary to check disk space.
1090
1091#if defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_STATVFS_H)
1092
1093 // check to make sure that mPath is properly initialized
1094 CHECK_mPath();
1095
1096 struct STATFS fs_buf;
1097
1098 /*
1099 * Members of the STATFS struct that you should know about:
1100 * f_bsize = block size on disk.
1101 * f_bavail = number of free blocks available to a non-superuser.
1102 * f_bfree = number of total free blocks in file system.
1103 */
1104
1105 if (STATFS(mPath.get(), &fs_buf) < 0) {
1106 // The call to STATFS failed.
1107#ifdef DEBUG
1108 printf("ERROR: GetDiskSpaceAvailable: STATFS call FAILED. \n");
1109#endif
1110 return NS_ERROR_FAILURE;
1111 }
1112#ifdef DEBUG_DISK_SPACE
1113 printf("DiskSpaceAvailable: %d bytes\n",
1114 fs_buf.f_bsize * (fs_buf.f_bavail - 1));
1115#endif
1116
1117 /*
1118 * The number of bytes free == The number of free blocks available to
1119 * a non-superuser, minus one as a fudge factor, multiplied by the size
1120 * of the aforementioned blocks.
1121 */
1122 PRInt64 bsize, bavail;
1123
1124 LL_I2L(bsize, fs_buf.f_bsize);
1125 LL_I2L(bavail, fs_buf.f_bavail - 1);
1126 LL_MUL(*aDiskSpaceAvailable, bsize, bavail);
1127 return NS_OK;
1128
1129#else
1130 /*
1131 * This platform doesn't have statfs or statvfs. I'm sure that there's
1132 * a way to check for free disk space on platforms that don't have statfs
1133 * (I'm SURE they have df, for example).
1134 *
1135 * Until we figure out how to do that, lets be honest and say that this
1136 * command isn't implemented properly for these platforms yet.
1137 */
1138#ifdef DEBUG
1139 printf("ERROR: GetDiskSpaceAvailable: Not implemented for plaforms without statfs.\n");
1140#endif
1141 return NS_ERROR_NOT_IMPLEMENTED;
1142
1143#endif /* HAVE_SYS_STATFS_H or HAVE_SYS_STATVFS_H */
1144
1145}
1146
1147NS_IMETHODIMP
1148nsLocalFile::GetParent(nsIFile **aParent)
1149{
1150 CHECK_mPath();
1151 NS_ENSURE_ARG_POINTER(aParent);
1152 *aParent = nsnull;
1153
1154 // if '/' we are at the top of the volume, return null
1155 if (mPath.Equals("/"))
1156 return NS_OK;
1157
1158 // <brendan, after jband> I promise to play nice
1159 char *buffer = mPath.BeginWriting();
1160
1161 // find the last significant slash in buffer
1162 char *slashp = strrchr(buffer, '/');
1163 NS_ASSERTION(slashp, "non-canonical mPath?");
1164 if (!slashp)
1165 return NS_ERROR_FILE_INVALID_PATH;
1166
1167 // for the case where we are at '/'
1168 if (slashp == buffer)
1169 slashp++;
1170
1171 // temporarily terminate buffer at the last significant slash
1172 char c = *slashp;
1173 *slashp = '\0';
1174
1175 nsCOMPtr<nsILocalFile> localFile;
1176 nsresult rv = NS_NewNativeLocalFile(nsDependentCString(buffer), PR_TRUE,
1177 getter_AddRefs(localFile));
1178
1179 // make buffer whole again
1180 *slashp = c;
1181
1182 if (NS_SUCCEEDED(rv) && localFile)
1183 rv = CallQueryInterface(localFile, aParent);
1184 return rv;
1185}
1186
1187/*
1188 * The results of Exists, isWritable and isReadable are not cached.
1189 */
1190
1191NS_IMETHODIMP
1192nsLocalFile::Exists(PRBool *_retval)
1193{
1194 CHECK_mPath();
1195 NS_ENSURE_ARG_POINTER(_retval);
1196
1197 *_retval = (access(mPath.get(), F_OK) == 0);
1198 return NS_OK;
1199}
1200
1201#ifdef XP_BEOS
1202// access() is buggy in BeOS POSIX implementation, at least for BFS, using stat() instead
1203NS_IMETHODIMP
1204nsLocalFile::IsWritable(PRBool *_retval)
1205{
1206 CHECK_mPath();
1207 NS_ENSURE_ARG_POINTER(_retval);
1208 struct stat buf;
1209 *_retval = (stat(mPath.get(), &buf) == 0);
1210 *_retval = *_retval && (buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH ));
1211 if (*_retval || errno == EACCES)
1212 return NS_OK;
1213 return NSRESULT_FOR_ERRNO();
1214}
1215
1216NS_IMETHODIMP
1217nsLocalFile::IsReadable(PRBool *_retval)
1218{
1219 CHECK_mPath();
1220 NS_ENSURE_ARG_POINTER(_retval);
1221 struct stat buf;
1222 *_retval = (stat(mPath.get(), &buf) == 0);
1223 *_retval = *_retval && (buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH ));
1224 if (*_retval || errno == EACCES)
1225 return NS_OK;
1226 return NSRESULT_FOR_ERRNO();
1227}
1228
1229NS_IMETHODIMP
1230nsLocalFile::IsExecutable(PRBool *_retval)
1231{
1232 CHECK_mPath();
1233 NS_ENSURE_ARG_POINTER(_retval);
1234 struct stat buf;
1235 *_retval = (stat(mPath.get(), &buf) == 0);
1236 *_retval = *_retval && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH ));
1237 if (*_retval || errno == EACCES)
1238 return NS_OK;
1239 return NSRESULT_FOR_ERRNO();
1240}
1241#else
1242NS_IMETHODIMP
1243nsLocalFile::IsWritable(PRBool *_retval)
1244{
1245 CHECK_mPath();
1246 NS_ENSURE_ARG_POINTER(_retval);
1247
1248 *_retval = (access(mPath.get(), W_OK) == 0);
1249 if (*_retval || errno == EACCES)
1250 return NS_OK;
1251 return NSRESULT_FOR_ERRNO();
1252}
1253
1254NS_IMETHODIMP
1255nsLocalFile::IsReadable(PRBool *_retval)
1256{
1257 CHECK_mPath();
1258 NS_ENSURE_ARG_POINTER(_retval);
1259
1260 *_retval = (access(mPath.get(), R_OK) == 0);
1261 if (*_retval || errno == EACCES)
1262 return NS_OK;
1263 return NSRESULT_FOR_ERRNO();
1264}
1265
1266NS_IMETHODIMP
1267nsLocalFile::IsExecutable(PRBool *_retval)
1268{
1269 CHECK_mPath();
1270 NS_ENSURE_ARG_POINTER(_retval);
1271
1272 *_retval = (access(mPath.get(), X_OK) == 0);
1273 if (*_retval || errno == EACCES)
1274 return NS_OK;
1275 return NSRESULT_FOR_ERRNO();
1276}
1277#endif
1278NS_IMETHODIMP
1279nsLocalFile::IsDirectory(PRBool *_retval)
1280{
1281 NS_ENSURE_ARG_POINTER(_retval);
1282 *_retval = PR_FALSE;
1283 VALIDATE_STAT_CACHE();
1284 *_retval = S_ISDIR(mCachedStat.st_mode);
1285 return NS_OK;
1286}
1287
1288NS_IMETHODIMP
1289nsLocalFile::IsFile(PRBool *_retval)
1290{
1291 NS_ENSURE_ARG_POINTER(_retval);
1292 *_retval = PR_FALSE;
1293 VALIDATE_STAT_CACHE();
1294 *_retval = S_ISREG(mCachedStat.st_mode);
1295 return NS_OK;
1296}
1297
1298NS_IMETHODIMP
1299nsLocalFile::IsHidden(PRBool *_retval)
1300{
1301 NS_ENSURE_ARG_POINTER(_retval);
1302 nsACString::const_iterator begin, end;
1303 LocateNativeLeafName(begin, end);
1304 *_retval = (*begin == '.');
1305 return NS_OK;
1306}
1307
1308NS_IMETHODIMP
1309nsLocalFile::IsSymlink(PRBool *_retval)
1310{
1311 NS_ENSURE_ARG_POINTER(_retval);
1312 CHECK_mPath();
1313
1314 struct stat symStat;
1315 lstat(mPath.get(), &symStat);
1316 *_retval=S_ISLNK(symStat.st_mode);
1317 return NS_OK;
1318}
1319
1320NS_IMETHODIMP
1321nsLocalFile::IsSpecial(PRBool *_retval)
1322{
1323 NS_ENSURE_ARG_POINTER(_retval);
1324 VALIDATE_STAT_CACHE();
1325 *_retval = S_ISCHR(mCachedStat.st_mode) ||
1326 S_ISBLK(mCachedStat.st_mode) ||
1327#ifdef S_ISSOCK
1328 S_ISSOCK(mCachedStat.st_mode) ||
1329#endif
1330 S_ISFIFO(mCachedStat.st_mode);
1331
1332 return NS_OK;
1333}
1334
1335NS_IMETHODIMP
1336nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
1337{
1338 NS_ENSURE_ARG(inFile);
1339 NS_ENSURE_ARG_POINTER(_retval);
1340 *_retval = PR_FALSE;
1341
1342 nsresult rv;
1343 nsCAutoString inPath;
1344
1345 if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1346 return rv;
1347
1348 *_retval = !FILE_STRCMP(inPath.get(), mPath.get());
1349 return NS_OK;
1350}
1351
1352NS_IMETHODIMP
1353nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
1354{
1355 CHECK_mPath();
1356 NS_ENSURE_ARG(inFile);
1357 NS_ENSURE_ARG_POINTER(_retval);
1358
1359 nsCAutoString inPath;
1360 nsresult rv;
1361
1362 if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
1363 return rv;
1364
1365 *_retval = PR_FALSE;
1366
1367 ssize_t len = mPath.Length();
1368 if (FILE_STRNCMP(mPath.get(), inPath.get(), len) == 0) {
1369 // Now make sure that the |inFile|'s path has a separator at len,
1370 // which implies that it has more components after len.
1371 if (inPath[len] == '/')
1372 *_retval = PR_TRUE;
1373 }
1374
1375 return NS_OK;
1376}
1377
1378NS_IMETHODIMP
1379nsLocalFile::GetNativeTarget(nsACString &_retval)
1380{
1381 CHECK_mPath();
1382 _retval.Truncate();
1383
1384 struct stat symStat;
1385 lstat(mPath.get(), &symStat);
1386 if (!S_ISLNK(symStat.st_mode))
1387 return NS_ERROR_FILE_INVALID_PATH;
1388
1389 PRInt64 targetSize64;
1390 if (NS_FAILED(GetFileSizeOfLink(&targetSize64)))
1391 return NS_ERROR_FAILURE;
1392
1393 PRInt32 size;
1394 LL_L2I(size, targetSize64);
1395 char *target = (char *)nsMemory::Alloc(size + 1);
1396 if (!target)
1397 return NS_ERROR_OUT_OF_MEMORY;
1398
1399 if (readlink(mPath.get(), target, (size_t)size) < 0) {
1400 nsMemory::Free(target);
1401 return NSRESULT_FOR_ERRNO();
1402 }
1403 target[size] = '\0';
1404
1405 nsresult rv;
1406 PRBool isSymlink;
1407 nsCOMPtr<nsIFile> self(this);
1408 nsCOMPtr<nsIFile> parent;
1409 while (NS_SUCCEEDED(rv = self->GetParent(getter_AddRefs(parent)))) {
1410 NS_ASSERTION(parent != nsnull, "no parent?!");
1411
1412 if (target[0] != '/') {
1413 nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(parent, &rv));
1414 if (NS_FAILED(rv))
1415 break;
1416 if (NS_FAILED(rv = localFile->AppendRelativeNativePath(nsDependentCString(target))))
1417 break;
1418 if (NS_FAILED(rv = localFile->GetNativePath(_retval)))
1419 break;
1420 if (NS_FAILED(rv = parent->IsSymlink(&isSymlink)))
1421 break;
1422 self = parent;
1423 } else {
1424 nsCOMPtr<nsILocalFile> localFile;
1425 rv = NS_NewNativeLocalFile(nsDependentCString(target), PR_TRUE,
1426 getter_AddRefs(localFile));
1427 if (NS_FAILED(rv))
1428 break;
1429 if (NS_FAILED(rv = localFile->IsSymlink(&isSymlink)))
1430 break;
1431 _retval = target; // XXX can we avoid this buffer copy?
1432 self = do_QueryInterface(localFile);
1433 }
1434 if (NS_FAILED(rv) || !isSymlink)
1435 break;
1436
1437 const nsPromiseFlatCString &flatRetval = PromiseFlatCString(_retval);
1438
1439 // strip off any and all trailing '/'
1440 PRInt32 len = strlen(target);
1441 while (target[len-1] == '/' && len > 1)
1442 target[--len] = '\0';
1443 if (lstat(flatRetval.get(), &symStat) < 0) {
1444 rv = NSRESULT_FOR_ERRNO();
1445 break;
1446 }
1447 if (!S_ISLNK(symStat.st_mode)) {
1448 rv = NS_ERROR_FILE_INVALID_PATH;
1449 break;
1450 }
1451 size = symStat.st_size;
1452 if (readlink(flatRetval.get(), target, size) < 0) {
1453 rv = NSRESULT_FOR_ERRNO();
1454 break;
1455 }
1456 target[size] = '\0';
1457
1458 _retval.Truncate();
1459 }
1460
1461 nsMemory::Free(target);
1462
1463 if (NS_FAILED(rv))
1464 _retval.Truncate();
1465 return rv;
1466}
1467
1468/* attribute PRBool followLinks; */
1469NS_IMETHODIMP
1470nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
1471{
1472 *aFollowLinks = PR_TRUE;
1473 return NS_OK;
1474}
1475
1476NS_IMETHODIMP
1477nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
1478{
1479 return NS_OK;
1480}
1481
1482NS_IMETHODIMP
1483nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **entries)
1484{
1485 nsDirEnumeratorUnix *dir = new nsDirEnumeratorUnix();
1486 if (!dir)
1487 return NS_ERROR_OUT_OF_MEMORY;
1488
1489 NS_ADDREF(dir);
1490 nsresult rv = dir->Init(this, PR_FALSE);
1491 if (NS_FAILED(rv)) {
1492 *entries = nsnull;
1493 NS_RELEASE(dir);
1494 } else {
1495 *entries = dir; // transfer reference
1496 }
1497
1498 return rv;
1499}
1500
1501NS_IMETHODIMP
1502nsLocalFile::Load(RTLDRMOD *phMod)
1503{
1504 CHECK_mPath();
1505 NS_ENSURE_ARG_POINTER(phMod);
1506
1507 RTLDRMOD hMod = NIL_RTLDRMOD;
1508 int vrc = RTLdrLoad(mPath.get(), &hMod);
1509 if (RT_FAILURE(vrc))
1510 return NS_ERROR_FAILURE;
1511
1512 *phMod = hMod;
1513 return NS_OK;
1514}
1515
1516NS_IMETHODIMP
1517nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
1518{
1519 return GetNativePath(aPersistentDescriptor);
1520}
1521
1522NS_IMETHODIMP
1523nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
1524{
1525 return InitWithNativePath(aPersistentDescriptor);
1526}
1527
1528#ifdef XP_BEOS
1529NS_IMETHODIMP
1530nsLocalFile::Reveal()
1531{
1532 BPath bPath(mPath.get());
1533 bPath.GetParent(&bPath);
1534 entry_ref ref;
1535 get_ref_for_path(bPath.Path(),&ref);
1536 BMessage message(B_REFS_RECEIVED);
1537 message.AddRef("refs",&ref);
1538 BMessenger messenger("application/x-vnd.Be-TRAK");
1539 messenger.SendMessage(&message);
1540 return NS_OK;
1541}
1542
1543NS_IMETHODIMP
1544nsLocalFile::Launch()
1545{
1546 entry_ref ref;
1547 get_ref_for_path (mPath.get(), &ref);
1548 be_roster->Launch (&ref);
1549
1550 return NS_OK;
1551}
1552#else
1553NS_IMETHODIMP
1554nsLocalFile::Reveal()
1555{
1556 return NS_ERROR_FAILURE;
1557}
1558
1559NS_IMETHODIMP
1560nsLocalFile::Launch()
1561{
1562 return NS_ERROR_FAILURE;
1563}
1564#endif
1565
1566nsresult
1567NS_NewNativeLocalFile(const nsACString &path, PRBool followSymlinks, nsILocalFile **result)
1568{
1569 nsLocalFile *file = new nsLocalFile();
1570 if (!file)
1571 return NS_ERROR_OUT_OF_MEMORY;
1572 NS_ADDREF(file);
1573
1574 if (!path.IsEmpty()) {
1575 nsresult rv = file->InitWithNativePath(path);
1576 if (NS_FAILED(rv)) {
1577 NS_RELEASE(file);
1578 return rv;
1579 }
1580 }
1581 *result = file;
1582 return NS_OK;
1583}
1584
1585//-----------------------------------------------------------------------------
1586// unicode support
1587//-----------------------------------------------------------------------------
1588
1589#define SET_UCS(func, ucsArg) \
1590 { \
1591 nsCAutoString buf; \
1592 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1593 if (NS_FAILED(rv)) \
1594 return rv; \
1595 return (func)(buf); \
1596 }
1597
1598#define GET_UCS(func, ucsArg) \
1599 { \
1600 nsCAutoString buf; \
1601 nsresult rv = (func)(buf); \
1602 if (NS_FAILED(rv)) return rv; \
1603 return NS_CopyNativeToUnicode(buf, ucsArg); \
1604 }
1605
1606#define SET_UCS_2ARGS_2(func, opaqueArg, ucsArg) \
1607 { \
1608 nsCAutoString buf; \
1609 nsresult rv = NS_CopyUnicodeToNative(ucsArg, buf); \
1610 if (NS_FAILED(rv)) \
1611 return rv; \
1612 return (func)(opaqueArg, buf); \
1613 }
1614
1615// Unicode interface Wrapper
1616nsresult
1617nsLocalFile::InitWithPath(const nsAString &filePath)
1618{
1619 SET_UCS(InitWithNativePath, filePath);
1620}
1621nsresult
1622nsLocalFile::Append(const nsAString &node)
1623{
1624 SET_UCS(AppendNative, node);
1625}
1626nsresult
1627nsLocalFile::AppendRelativePath(const nsAString &node)
1628{
1629 SET_UCS(AppendRelativeNativePath, node);
1630}
1631nsresult
1632nsLocalFile::GetLeafName(nsAString &aLeafName)
1633{
1634 GET_UCS(GetNativeLeafName, aLeafName);
1635}
1636nsresult
1637nsLocalFile::SetLeafName(const nsAString &aLeafName)
1638{
1639 SET_UCS(SetNativeLeafName, aLeafName);
1640}
1641nsresult
1642nsLocalFile::GetPath(nsAString &_retval)
1643{
1644 return NS_CopyNativeToUnicode(mPath, _retval);
1645}
1646nsresult
1647nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
1648{
1649 SET_UCS_2ARGS_2(CopyToNative , newParentDir, newName);
1650}
1651nsresult
1652nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
1653{
1654 SET_UCS_2ARGS_2(CopyToFollowingLinksNative , newParentDir, newName);
1655}
1656nsresult
1657nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
1658{
1659 SET_UCS_2ARGS_2(MoveToNative, newParentDir, newName);
1660}
1661nsresult
1662nsLocalFile::GetTarget(nsAString &_retval)
1663{
1664 GET_UCS(GetNativeTarget, _retval);
1665}
1666nsresult
1667NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
1668{
1669 nsCAutoString buf;
1670 nsresult rv = NS_CopyUnicodeToNative(path, buf);
1671 if (NS_FAILED(rv))
1672 return rv;
1673 return NS_NewNativeLocalFile(buf, followLinks, result);
1674}
1675
1676//-----------------------------------------------------------------------------
1677// global init/shutdown
1678//-----------------------------------------------------------------------------
1679
1680void
1681nsLocalFile::GlobalInit()
1682{
1683}
1684
1685void
1686nsLocalFile::GlobalShutdown()
1687{
1688}
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