Browse Source

Add a way to copy a file in archive into filesystem.

Cheng Zhao 10 years ago
parent
commit
c95a93ef1c

+ 2 - 0
atom.gyp

@@ -196,6 +196,8 @@
       'atom/common/asar/archive.h',
       'atom/common/asar/archive_factory.cc',
       'atom/common/asar/archive_factory.h',
+      'atom/common/asar/scoped_temporary_file.cc',
+      'atom/common/asar/scoped_temporary_file.h',
       'atom/common/common_message_generator.cc',
       'atom/common/common_message_generator.h',
       'atom/common/crash_reporter/crash_reporter.cc',

+ 11 - 1
atom/common/api/atom_api_asar.cc

@@ -67,13 +67,23 @@ class Archive : public mate::Wrappable {
     return mate::ConvertToV8(isolate, files);
   }
 
+  // Copy the file out into a temporary file and returns the new path.
+  v8::Handle<v8::Value> CopyFileOut(v8::Isolate* isolate,
+                                    const base::FilePath& path) {
+    base::FilePath new_path;
+    if (!archive_->CopyFileOut(path, &new_path))
+      return v8::False(isolate);
+    return mate::ConvertToV8(isolate, new_path);
+  }
+
   // mate::Wrappable:
   mate::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) {
     return mate::ObjectTemplateBuilder(isolate)
         .SetValue("path", archive_->path())
         .SetMethod("getFileInfo", &Archive::GetFileInfo)
         .SetMethod("stat", &Archive::Stat)
-        .SetMethod("readdir", &Archive::Readdir);
+        .SetMethod("readdir", &Archive::Readdir)
+        .SetMethod("copyFileOut", &Archive::CopyFileOut);
   }
 
  private:

+ 21 - 0
atom/common/asar/archive.cc

@@ -7,10 +7,12 @@
 #include <string>
 #include <vector>
 
+#include "atom/common/asar/scoped_temporary_file.h"
 #include "base/files/file.h"
 #include "base/logging.h"
 #include "base/pickle.h"
 #include "base/json/json_string_value_serializer.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 
 namespace asar {
@@ -181,4 +183,23 @@ bool Archive::Readdir(const base::FilePath& path,
   return true;
 }
 
+bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
+  if (ContainsKey(external_files_, path)) {
+    *out = external_files_[path]->path();
+    return true;
+  }
+
+  FileInfo info;
+  if (!GetFileInfo(path, &info))
+    return false;
+
+  scoped_refptr<ScopedTemporaryFile> temp_file(new ScopedTemporaryFile);
+  if (!temp_file->InitFromFile(path_, info.offset, info.size))
+    return false;
+
+  external_files_[path] = temp_file;
+  *out = temp_file->path();
+  return true;
+}
+
 }  // namespace asar

+ 12 - 0
atom/common/asar/archive.h

@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "base/containers/hash_tables.h"
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
@@ -17,6 +18,10 @@ class DictionaryValue;
 
 namespace asar {
 
+class ScopedTemporaryFile;
+
+// This class represents an asar package, and provides methods to read
+// information from it.
 class Archive : public base::RefCounted<Archive> {
  public:
   struct FileInfo {
@@ -46,6 +51,9 @@ class Archive : public base::RefCounted<Archive> {
   // Fs.readdir(path).
   bool Readdir(const base::FilePath& path, std::vector<base::FilePath>* files);
 
+  // Copy the file into a temporary file, and return the new path.
+  bool CopyFileOut(const base::FilePath& path, base::FilePath* out);
+
   base::FilePath path() const { return path_; }
   base::DictionaryValue* header() const { return header_.get(); }
 
@@ -57,6 +65,10 @@ class Archive : public base::RefCounted<Archive> {
   uint32 header_size_;
   scoped_ptr<base::DictionaryValue> header_;
 
+  // Cached external temporary files.
+  base::hash_map<base::FilePath,  // NOLINT
+                 scoped_refptr<ScopedTemporaryFile> > external_files_;
+
   DISALLOW_COPY_AND_ASSIGN(Archive);
 };
 

+ 52 - 0
atom/common/asar/scoped_temporary_file.cc

@@ -0,0 +1,52 @@
+// Copyright (c) 2014 GitHub, Inc. All rights reserved.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "atom/common/asar/scoped_temporary_file.h"
+
+#include "base/file_util.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace asar {
+
+ScopedTemporaryFile::ScopedTemporaryFile() {
+}
+
+ScopedTemporaryFile::~ScopedTemporaryFile() {
+  if (!path_.empty()) {
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+    base::DeleteFile(path_, false);
+  }
+}
+
+bool ScopedTemporaryFile::Init() {
+  if (!path_.empty())
+    return true;
+
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+  return base::CreateTemporaryFile(&path_);
+}
+
+bool ScopedTemporaryFile::InitFromFile(const base::FilePath& path,
+                                       uint64 offset, uint64 size) {
+  if (!Init())
+    return false;
+
+  base::File src(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!src.IsValid())
+    return false;
+
+  std::vector<char> buf(size);
+  int len = src.Read(offset, buf.data(), buf.size());
+  if (len != static_cast<int>(size))
+    return false;
+
+  base::File dest(path_, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+  if (!dest.IsValid())
+    return false;
+
+  return dest.WriteAtCurrentPos(buf.data(), buf.size()) ==
+      static_cast<int>(size);
+}
+
+}  // namespace asar

+ 40 - 0
atom/common/asar/scoped_temporary_file.h

@@ -0,0 +1,40 @@
+// Copyright (c) 2014 GitHub, Inc. All rights reserved.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_
+#define ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+
+namespace asar {
+
+// An object representing a temporary file that should be cleaned up when this
+// object goes out of scope.  Note that since deletion occurs during the
+// destructor, no further error handling is possible if the directory fails to
+// be deleted.  As a result, deletion is not guaranteed by this class.
+class ScopedTemporaryFile : public base::RefCounted<ScopedTemporaryFile> {
+ public:
+  ScopedTemporaryFile();
+
+  // Init an empty temporary file.
+  bool Init();
+
+  // Init an temporary file and fill it with content of |path|.
+  bool InitFromFile(const base::FilePath& path, uint64 offset, uint64 size);
+
+  base::FilePath path() const { return path_; }
+
+ private:
+  friend class base::RefCounted<ScopedTemporaryFile>;
+  virtual ~ScopedTemporaryFile();
+
+  base::FilePath path_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile);
+};
+
+}  // namespace asar
+
+#endif  // ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_