Bug 207973: Directory listing errors when a bad soft link (symlink) is present. r=joshmoz, sr=bsmedberg
authorPeter Annema <jag@tty.nl>
Sat, 07 Mar 2009 18:59:40 -0800
changeset 25828 0288a48bd3ab0a3822cf40b11360c90f1a32bbac
parent 25827 b0df372b91923fb3de39effa2fc9fbb4a065f719
child 25829 e444379b923ae86c84c49e18dab33b98560cf794
push idunknown
push userunknown
push dateunknown
reviewersjoshmoz, bsmedberg
bugs207973
milestone1.9.2a1pre
Bug 207973: Directory listing errors when a bad soft link (symlink) is present. r=joshmoz, sr=bsmedberg CFURLGetFSRef doesn't handle dangling symlinks well. Check if that's what we're dealing with, and use an alternative approach to create an FSRef.
xpcom/io/nsLocalFileOSX.mm
--- a/xpcom/io/nsLocalFileOSX.mm
+++ b/xpcom/io/nsLocalFileOSX.mm
@@ -39,16 +39,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsLocalFile.h"
 #include "nsDirectoryServiceDefs.h"
 
 #include "nsObjCExceptions.h"
+#include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsISimpleEnumerator.h"
 #include "nsITimelineService.h"
 #include "nsVoidArray.h"
 #include "nsTArray.h"
 
@@ -186,17 +187,17 @@ class nsDirEnumerator : public nsISimple
             
               if (err == noErr || err == errFSNoMoreItems) {
                 mArrayCnt = actualCnt;
                 mArrayIndex = 0;
               }
             }
 
             if (mArrayIndex < mArrayCnt) {
-              nsLocalFile *newFile = new nsLocalFile;
+              nsRefPtr<nsLocalFile> newFile = new nsLocalFile;
               if (!newFile)
                 return NS_ERROR_OUT_OF_MEMORY;
               FSRef fsRef = mFSRefsArray[mArrayIndex];
               if (NS_FAILED(newFile->InitWithFSRef(&fsRef)))
                 return NS_ERROR_FAILURE;
               mArrayIndex++;
               mNext = newFile;
             } 
@@ -1296,33 +1297,31 @@ NS_IMETHODIMP nsLocalFile::GetParent(nsI
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   NS_ENSURE_ARG_POINTER(aParent);
   *aParent = nsnull;
 
   // Check we are correctly initialized.
   CHECK_mBaseRef();
 
-  nsLocalFile *newFile = nsnull;
-
   // If it can be determined without error that a file does not
   // have a parent, return nsnull for the parent and NS_OK as the result.
   // See bug 133617.
   nsresult rv = NS_OK;
   CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef);
   if (parentURLRef) {
     // If the parent path is longer than file's path then 
     // CFURLCreateCopyDeletingLastPathComponent must have simply added
     // two dots at the end - in this case indicate that there is no parent.
     // See bug 332389.
     CFStringRef path = ::CFURLGetString(mBaseRef);
     CFStringRef newPath = ::CFURLGetString(parentURLRef);
     if (::CFStringGetLength(newPath) < ::CFStringGetLength(path)) {
       rv = NS_ERROR_FAILURE;
-      newFile = new nsLocalFile;
+      nsRefPtr<nsLocalFile> newFile = new nsLocalFile;
       if (newFile) {
         rv = newFile->InitWithCFURL(parentURLRef);
         if (NS_SUCCEEDED(rv)) {
           NS_ADDREF(*aParent = newFile);
           rv = NS_OK;
         }
       }
     }
@@ -2137,19 +2136,55 @@ nsresult nsLocalFile::GetFSRefInternal(F
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef;
   NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER);
   if (::CFURLGetFSRef(whichURLRef, &aFSRef))
     return NS_OK;
 
-  // We have to make an assumption about why CFURLGetFSRef failed as it only
-  // returns a bool. This is the most likely reason for failure.
-  return NS_ERROR_FILE_NOT_FOUND;
+  // See if we are a symlink, and construct an FSRef for that
+  nsCAutoString path;
+  nsresult rv = GetNativePath(path); // XXXjag inline and use whichURLRef?
+  if (NS_FAILED(rv))
+    return rv;
+
+  struct stat symStat;
+  if (lstat(path.get(), &symStat) < 0)
+    return NSRESULT_FOR_ERRNO();
+
+  rv = NS_ERROR_FILE_NOT_FOUND; // best guess if all else fails
+
+  if (S_ISLNK(symStat.st_mode)) {
+    CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, whichURLRef);
+    if (parentURLRef) {
+      FSRef parentFSRef;
+      if (::CFURLGetFSRef(parentURLRef, &parentFSRef)) {
+        CFStringRef leafRef = ::CFURLCopyLastPathComponent(whichURLRef);
+        if (leafRef) {
+          CFRange range = { 0, ::CFStringGetLength(leafRef) };
+          if (range.length > 0) {
+            UniChar* leaf = new UniChar[range.length];
+            if (leaf) {
+              ::CFStringGetCharacters(leafRef, range, leaf);
+              if (::FSMakeFSRefUnicode(&parentFSRef, range.length, leaf,
+                                       kTextEncodingUnknown, &aFSRef) == noErr)
+                rv = NS_OK;
+
+              delete[] leaf;
+            }
+          }
+          ::CFRelease(leafRef);
+        }
+      }
+      ::CFRelease(parentURLRef);
+    }
+  }
+
+  return rv;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 nsresult nsLocalFile::GetPathInternal(nsACString& path)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;