From c95a93ef1cc4cfc7450d7c71b1e141499e295e0d Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 25 Sep 2014 16:56:50 +0800 Subject: [PATCH] Add a way to copy a file in archive into filesystem. --- atom.gyp | 2 + atom/common/api/atom_api_asar.cc | 12 +++++- atom/common/asar/archive.cc | 21 +++++++++ atom/common/asar/archive.h | 12 ++++++ atom/common/asar/scoped_temporary_file.cc | 52 +++++++++++++++++++++++ atom/common/asar/scoped_temporary_file.h | 40 +++++++++++++++++ 6 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 atom/common/asar/scoped_temporary_file.cc create mode 100644 atom/common/asar/scoped_temporary_file.h diff --git a/atom.gyp b/atom.gyp index 300b577fb95e..0e059c5c3364 100644 --- a/atom.gyp +++ b/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', diff --git a/atom/common/api/atom_api_asar.cc b/atom/common/api/atom_api_asar.cc index 0a4e3ef46f62..7f4521631822 100644 --- a/atom/common/api/atom_api_asar.cc +++ b/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 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: diff --git a/atom/common/asar/archive.cc b/atom/common/asar/archive.cc index 01e0b4d94dab..899eb6991053 100644 --- a/atom/common/asar/archive.cc +++ b/atom/common/asar/archive.cc @@ -7,10 +7,12 @@ #include #include +#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 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 diff --git a/atom/common/asar/archive.h b/atom/common/asar/archive.h index 37962e80536f..4c44442b51f9 100644 --- a/atom/common/asar/archive.h +++ b/atom/common/asar/archive.h @@ -7,6 +7,7 @@ #include +#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 { public: struct FileInfo { @@ -46,6 +51,9 @@ class Archive : public base::RefCounted { // Fs.readdir(path). bool Readdir(const base::FilePath& path, std::vector* 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 { uint32 header_size_; scoped_ptr header_; + // Cached external temporary files. + base::hash_map > external_files_; + DISALLOW_COPY_AND_ASSIGN(Archive); }; diff --git a/atom/common/asar/scoped_temporary_file.cc b/atom/common/asar/scoped_temporary_file.cc new file mode 100644 index 000000000000..e5de2861930b --- /dev/null +++ b/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 buf(size); + int len = src.Read(offset, buf.data(), buf.size()); + if (len != static_cast(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(size); +} + +} // namespace asar diff --git a/atom/common/asar/scoped_temporary_file.h b/atom/common/asar/scoped_temporary_file.h new file mode 100644 index 000000000000..190eecf4d621 --- /dev/null +++ b/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 { + 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; + virtual ~ScopedTemporaryFile(); + + base::FilePath path_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile); +}; + +} // namespace asar + +#endif // ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_