VirtualBox

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

Last change on this file since 101803 was 101803, checked in by vboxsync, 13 months ago

libs/xpcom: Get rid of code used only if MOZ_TIMELINE is defined which we never do, bugref:10545

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