Browse Source

fix: allow paths to asar archives to contain the .asar extension in directories (#20342) (#20401)

Milan Burda 5 years ago
parent
commit
8536f62826

+ 1 - 18
lib/common/asar.js

@@ -40,8 +40,6 @@
     return newArchive
   }
 
-  const ASAR_EXTENSION = '.asar'
-
   // Separate asar package's path from full path.
   const splitPath = archivePathOrBuffer => {
     // Shortcut for disabled asar.
@@ -54,22 +52,7 @@
     }
     if (typeof archivePath !== 'string') return { isAsar: false }
 
-    if (archivePath.endsWith(ASAR_EXTENSION)) {
-      return { isAsar: true, asarPath: archivePath, filePath: '' }
-    }
-
-    archivePath = path.normalize(archivePath)
-    const index = archivePath.lastIndexOf(`${ASAR_EXTENSION}${path.sep}`)
-    if (index === -1) return { isAsar: false }
-
-    // E.g. for "//some/path/to/archive.asar/then/internal.file"...
-    return {
-      isAsar: true,
-      // "//some/path/to/archive.asar"
-      asarPath: archivePath.substr(0, index + ASAR_EXTENSION.length),
-      // "then/internal.file" (with a path separator excluded)
-      filePath: archivePath.substr(index + ASAR_EXTENSION.length + 1)
-    }
+    return asar.splitPath(path.normalize(archivePath))
   }
 
   // Convert asar archive's Stats object to fs's Stats object.

+ 16 - 0
shell/common/api/atom_api_asar.cc

@@ -11,6 +11,7 @@
 #include "native_mate/object_template_builder.h"
 #include "native_mate/wrappable.h"
 #include "shell/common/asar/archive.h"
+#include "shell/common/asar/asar_util.h"
 #include "shell/common/native_mate_converters/callback.h"
 #include "shell/common/native_mate_converters/file_path_converter.h"
 #include "shell/common/node_includes.h"
@@ -127,12 +128,27 @@ void InitAsarSupport(v8::Isolate* isolate, v8::Local<v8::Value> require) {
       &asar_init_params, &asar_init_args, nullptr);
 }
 
+v8::Local<v8::Value> SplitPath(v8::Isolate* isolate,
+                               const base::FilePath& path) {
+  mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
+  base::FilePath asar_path, file_path;
+  if (asar::GetAsarArchivePath(path, &asar_path, &file_path, true)) {
+    dict.Set("isAsar", true);
+    dict.Set("asarPath", asar_path);
+    dict.Set("filePath", file_path);
+  } else {
+    dict.Set("isAsar", false);
+  }
+  return dict.GetHandle();
+}
+
 void Initialize(v8::Local<v8::Object> exports,
                 v8::Local<v8::Value> unused,
                 v8::Local<v8::Context> context,
                 void* priv) {
   mate::Dictionary dict(context->GetIsolate(), exports);
   dict.SetMethod("createArchive", &Archive::Create);
+  dict.SetMethod("splitPath", &SplitPath);
   dict.SetMethod("initAsarSupport", &InitAsarSupport);
 }
 

+ 17 - 3
shell/common/asar/asar_util.cc

@@ -12,6 +12,7 @@
 #include "base/lazy_instance.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_local.h"
+#include "base/threading/thread_restrictions.h"
 #include "shell/common/asar/archive.h"
 
 namespace asar {
@@ -25,6 +26,17 @@ base::LazyInstance<base::ThreadLocalPointer<ArchiveMap>>::Leaky
 
 const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar");
 
+std::map<base::FilePath, bool> g_is_directory_cache;
+
+bool IsDirectoryCached(const base::FilePath& path) {
+  auto it = g_is_directory_cache.find(path);
+  if (it != g_is_directory_cache.end()) {
+    return it->second;
+  }
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+  return g_is_directory_cache[path] = base::DirectoryExists(path);
+}
+
 }  // namespace
 
 std::shared_ptr<Archive> GetOrCreateAsarArchive(const base::FilePath& path) {
@@ -47,11 +59,12 @@ void ClearArchives() {
 
 bool GetAsarArchivePath(const base::FilePath& full_path,
                         base::FilePath* asar_path,
-                        base::FilePath* relative_path) {
+                        base::FilePath* relative_path,
+                        bool allow_root) {
   base::FilePath iter = full_path;
   while (true) {
     base::FilePath dirname = iter.DirName();
-    if (iter.MatchesExtension(kAsarExtension))
+    if (iter.MatchesExtension(kAsarExtension) && !IsDirectoryCached(iter))
       break;
     else if (iter == dirname)
       return false;
@@ -59,7 +72,8 @@ bool GetAsarArchivePath(const base::FilePath& full_path,
   }
 
   base::FilePath tail;
-  if (!iter.AppendRelativePath(full_path, &tail))
+  if (!((allow_root && iter == full_path) ||
+        iter.AppendRelativePath(full_path, &tail)))
     return false;
 
   *asar_path = iter;

+ 2 - 1
shell/common/asar/asar_util.h

@@ -25,7 +25,8 @@ void ClearArchives();
 // Separates the path to Archive out.
 bool GetAsarArchivePath(const base::FilePath& full_path,
                         base::FilePath* asar_path,
-                        base::FilePath* relative_path);
+                        base::FilePath* relative_path,
+                        bool allow_root = false);
 
 // Same with base::ReadFileToString but supports asar Archive.
 bool ReadFileToString(const base::FilePath& path, std::string* contents);

+ 2 - 2
spec-main/api-protocol-spec.ts

@@ -195,7 +195,7 @@ describe('protocol module', () => {
   })
 
   describe('protocol.registerFileProtocol', () => {
-    const filePath = path.join(fixturesPath, 'asar', 'a.asar', 'file1')
+    const filePath = path.join(fixturesPath, 'test.asar', 'a.asar', 'file1')
     const fileContent = fs.readFileSync(filePath)
     const normalPath = path.join(fixturesPath, 'pages', 'a.html')
     const normalContent = fs.readFileSync(normalPath)
@@ -248,7 +248,7 @@ describe('protocol module', () => {
     })
 
     it('fails when sending unexist-file', async () => {
-      const fakeFilePath = path.join(fixturesPath, 'asar', 'a.asar', 'not-exist')
+      const fakeFilePath = path.join(fixturesPath, 'test.asar', 'a.asar', 'not-exist')
       await registerFileProtocol(protocolName, (request, callback) => callback(fakeFilePath))
       await expect(ajax(protocolName + '://fake-host')).to.be.eventually.rejectedWith(Error, '404')
     })

File diff suppressed because it is too large
+ 133 - 127
spec/asar-spec.js


+ 1 - 1
spec/fixtures/module/no-asar.js

@@ -1,7 +1,7 @@
 const fs = require('fs')
 const path = require('path')
 
-const stats = fs.statSync(path.join(__dirname, '..', 'asar', 'a.asar'))
+const stats = fs.statSync(path.join(__dirname, '..', 'test.asar', 'a.asar'))
 
 const details = {
   isFile: stats.isFile(),

+ 1 - 1
spec/fixtures/no-proprietary-codecs.js

@@ -25,7 +25,7 @@ app.once('ready', () => {
     app.exit(1)
   })
 
-  window.loadFile(path.resolve(__dirname, 'asar', 'video.asar', 'index.html'))
+  window.loadFile(path.resolve(__dirname, 'test.asar', 'video.asar', 'index.html'))
 
   ipcMain.on('asar-video', (event, message, error) => {
     if (message === 'ended') {

+ 0 - 0
spec/fixtures/asar/a.asar → spec/fixtures/test.asar/a.asar


+ 0 - 0
spec/fixtures/asar/echo.asar → spec/fixtures/test.asar/echo.asar


+ 0 - 0
spec/fixtures/asar/empty.asar → spec/fixtures/test.asar/empty.asar


+ 0 - 0
spec/fixtures/asar/file → spec/fixtures/test.asar/file


+ 0 - 0
spec/fixtures/asar/logo.asar → spec/fixtures/test.asar/logo.asar


+ 0 - 0
spec/fixtures/asar/script.asar → spec/fixtures/test.asar/script.asar


+ 0 - 0
spec/fixtures/asar/unpack.asar → spec/fixtures/test.asar/unpack.asar


+ 0 - 0
spec/fixtures/asar/unpack.asar.unpacked/a.txt → spec/fixtures/test.asar/unpack.asar.unpacked/a.txt


+ 0 - 0
spec/fixtures/asar/unpack.asar.unpacked/atom.png → spec/fixtures/test.asar/unpack.asar.unpacked/atom.png


+ 0 - 0
spec/fixtures/asar/video.asar → spec/fixtures/test.asar/video.asar


+ 0 - 0
spec/fixtures/asar/web.asar → spec/fixtures/test.asar/web.asar


Some files were not shown because too many files changed in this diff