Bug 1274959 - Support symlinks in Directory API - part 3 - no loops with symlink in Directory.getFiles(), r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Sat, 23 Jul 2016 10:35:26 +0200
changeset 306447 e28c3a6965243e23829f5543c06e2888793c4318
parent 306446 c2462f5e5e38b4fc69103bab2756a08a5e914c43
child 306448 0353590d4e05a2d4c87cf5c9c95cbe59d13b1919
push id30484
push usercbook@mozilla.com
push dateMon, 25 Jul 2016 13:51:04 +0000
treeherdermozilla-central@e23f2ec25e96 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1274959
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1274959 - Support symlinks in Directory API - part 3 - no loops with symlink in Directory.getFiles(), r=smaug
dom/filesystem/GetFilesHelper.cpp
dom/filesystem/GetFilesHelper.h
--- a/dom/filesystem/GetFilesHelper.cpp
+++ b/dom/filesystem/GetFilesHelper.cpp
@@ -264,18 +264,23 @@ GetFilesHelperBase::ExploreDirectory(con
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aFile);
 
   // We check if this operation has to be terminated at each recursion.
   if (IsCanceled()) {
     return NS_OK;
   }
 
+  nsresult rv = AddExploredDirectory(aFile);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   nsCOMPtr<nsISimpleEnumerator> entries;
-  nsresult rv = aFile->GetDirectoryEntries(getter_AddRefs(entries));
+  rv = aFile->GetDirectoryEntries(getter_AddRefs(entries));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   for (;;) {
     bool hasMore = false;
     if (NS_WARN_IF(NS_FAILED(entries->HasMoreElements(&hasMore))) || !hasMore) {
       break;
@@ -287,26 +292,31 @@ GetFilesHelperBase::ExploreDirectory(con
     }
 
     nsCOMPtr<nsIFile> currFile = do_QueryInterface(supp);
     MOZ_ASSERT(currFile);
 
     bool isLink, isSpecial, isFile, isDir;
     if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) ||
                    NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
-        isLink || isSpecial) {
+        isSpecial) {
       continue;
     }
 
     if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) ||
                    NS_FAILED(currFile->IsDirectory(&isDir))) ||
         !(isFile || isDir)) {
       continue;
     }
 
+    // We don't want to explore loops of links.
+    if (isDir && isLink && !ShouldFollowSymLink(currFile)) {
+      continue;
+    }
+
     // The new domPath
     nsAutoString domPath;
     domPath.Assign(aDOMPath);
     if (!aDOMPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) {
       domPath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
     }
 
     nsAutoString leafName;
@@ -339,16 +349,79 @@ GetFilesHelperBase::ExploreDirectory(con
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
+nsresult
+GetFilesHelperBase::AddExploredDirectory(nsIFile* aDir)
+{
+  nsresult rv;
+
+#ifdef DEBUG
+  bool isDir;
+  rv = aDir->IsDirectory(&isDir);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(isDir, "Why are we here?");
+#endif
+
+  bool isLink;
+  rv = aDir->IsSymlink(&isLink);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoCString path;
+
+  if (!isLink) {
+    nsAutoString path16;
+    rv = aDir->GetPath(path16);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    path = NS_ConvertUTF16toUTF8(path16);
+  } else {
+    rv = aDir->GetNativeTarget(path);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  mExploredDirectories.PutEntry(path);
+  return NS_OK;
+}
+
+bool
+GetFilesHelperBase::ShouldFollowSymLink(nsIFile* aDir)
+{
+#ifdef DEBUG
+  bool isLink, isDir;
+  if (NS_WARN_IF(NS_FAILED(aDir->IsSymlink(&isLink)) ||
+                 NS_FAILED(aDir->IsDirectory(&isDir)))) {
+    return false;
+  }
+
+  MOZ_ASSERT(isLink && isDir, "Why are we here?");
+#endif
+
+  nsAutoCString targetPath;
+  if (NS_WARN_IF(NS_FAILED(aDir->GetNativeTarget(targetPath)))) {
+    return false;
+  }
+
+  return !mExploredDirectories.Contains(targetPath);
+}
+
 void
 GetFilesHelper::ResolveOrRejectPromise(Promise* aPromise)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mListingCompleted);
   MOZ_ASSERT(aPromise);
 
   // Error propagation.
--- a/dom/filesystem/GetFilesHelper.h
+++ b/dom/filesystem/GetFilesHelper.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_GetFilesHelper_h
 #define mozilla_dom_GetFilesHelper_h
 
 #include "mozilla/Mutex.h"
 #include "mozilla/RefPtr.h"
 #include "nsCycleCollectionTraversalCallback.h"
 #include "nsTArray.h"
+#include "nsTHashtable.h"
 
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 class ContentParent;
@@ -49,25 +50,32 @@ protected:
   IsCanceled()
   {
     return false;
   }
 
   nsresult
   ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile);
 
+  nsresult
+  AddExploredDirectory(nsIFile* aDirectory);
+
+  bool
+  ShouldFollowSymLink(nsIFile* aDirectory);
+
   bool mRecursiveFlag;
 
   // We populate this array in the I/O thread with the paths of the Files that
   // we want to send as result to the promise objects.
   struct FileData {
     nsString mDomPath;
     nsString mRealPath;
   };
   FallibleTArray<FileData> mTargetPathArray;
+  nsTHashtable<nsCStringHashKey> mExploredDirectories;
 };
 
 // Retrieving the list of files can be very time/IO consuming. We use this
 // helper class to do it just once.
 class GetFilesHelper : public Runnable
                      , public GetFilesHelperBase
 {
   friend class GetFilesHelperParent;