| // Copyright 2014 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "syzygy/ar/ar_transform.h" |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/files/file_util.h" |
| #include "base/memory/scoped_vector.h" |
| #include "base/strings/stringprintf.h" |
| #include "syzygy/ar/ar_reader.h" |
| #include "syzygy/ar/ar_writer.h" |
| |
| namespace ar { |
| |
| namespace { |
| |
| // Helper struct to delete a file when this object goes out of scope. |
| struct FileDeleter { |
| explicit FileDeleter(const base::FilePath& path) : path_(path) { |
| } |
| |
| ~FileDeleter() { |
| if (!base::DeleteFile(path_, false)) |
| LOG(WARNING) << "Unable to delete file: " << path_.value(); |
| } |
| |
| const base::FilePath& path_; |
| }; |
| |
| } // namespace |
| |
| bool ArTransform::Transform() { |
| DCHECK(!input_archive_.empty()); |
| DCHECK(!output_archive_.empty()); |
| DCHECK(!callback_.is_null()); |
| |
| ArReader reader; |
| if (!reader.Init(input_archive_)) |
| return false; |
| LOG(INFO) << "Read " << reader.symbols().size() << " symbols."; |
| |
| // This collection of buffers must outlive the ArWriter below. |
| ScopedVector<DataBuffer> buffers; |
| |
| // Iterate over the files in the archive. |
| ArWriter writer; |
| for (size_t i = 0; i < reader.offsets().size(); ++i) { |
| // Extract the next file. |
| ParsedArFileHeader header; |
| std::unique_ptr<DataBuffer> buffer(new DataBuffer()); |
| if (!reader.ExtractNext(&header, buffer.get())) |
| return false; |
| |
| LOG(INFO) << "Processing file " << (i + 1) << " of " |
| << reader.offsets().size() << ": " << header.name; |
| |
| // Apply the transform to this file. |
| bool remove = false; |
| if (!callback_.Run(&header, buffer.get(), &remove)) |
| return false; |
| |
| if (remove) |
| continue; |
| |
| // Add the transformed file to the output archive. |
| if (!writer.AddFile(header.name, header.timestamp, header.mode, |
| buffer.get())) { |
| return false; |
| } |
| |
| // Save the buffer so we keep it around until the writer has finished. |
| buffers.push_back(buffer.release()); |
| } |
| |
| if (!writer.Write(output_archive_)) |
| return false; |
| LOG(INFO) << "Wrote " << writer.symbols().size() << " symbols."; |
| |
| return true; |
| } |
| |
| OnDiskArTransformAdapter::OnDiskArTransformAdapter( |
| TransformFileOnDiskCallback inner_callback) |
| : inner_callback_(inner_callback), |
| outer_callback_(base::Bind(&OnDiskArTransformAdapter::Transform, |
| base::Unretained(this))), |
| index_(0) { |
| } |
| |
| OnDiskArTransformAdapter::~OnDiskArTransformAdapter() { |
| if (!base::DeleteFile(temp_dir_, true)) { |
| LOG(WARNING) << "Unable to delete temporary directory: " |
| << temp_dir_.value(); |
| } |
| } |
| |
| bool OnDiskArTransformAdapter::Transform(ParsedArFileHeader* header, |
| DataBuffer* contents, |
| bool* remove) { |
| if (temp_dir_.empty()) { |
| if (!base::CreateNewTempDirectory(L"OnDiskArTransformAdapter", |
| &temp_dir_)) { |
| LOG(ERROR) << "Unable to create temporary directory."; |
| return false; |
| } |
| } |
| |
| // Create input and output file names. |
| base::FilePath input_path = temp_dir_.Append( |
| base::StringPrintf(L"input-%04d.obj", index_)); |
| base::FilePath output_path = temp_dir_.Append( |
| base::StringPrintf(L"output-%04d.obj", index_)); |
| ++index_; |
| |
| // Set up deleters for these files. |
| FileDeleter input_deleter(input_path); |
| FileDeleter output_deleter(output_path); |
| |
| if (base::WriteFile(input_path, |
| reinterpret_cast<const char*>(contents->data()), |
| contents->size()) != |
| static_cast<int>(contents->size())) { |
| LOG(ERROR) << "Unable to write file: " << input_path.value(); |
| return false; |
| } |
| |
| // Delegate to the wrapped callback. |
| if (!inner_callback_.Run(input_path, output_path, header, remove)) |
| return false; |
| |
| // If the file is being removed we don't need to bother reading it. |
| if (*remove) |
| return true; |
| |
| // GetFileSize and ReadFile will both fail in this case, but we can provide |
| // a more meaningful error message by first doing this check. |
| if (!base::PathExists(output_path)) { |
| LOG(ERROR) << "File does not exist: " << output_path.value(); |
| return false; |
| } |
| |
| // Read the transformed file from disk. |
| int64_t size = 0; |
| if (!base::GetFileSize(output_path, &size)) { |
| LOG(ERROR) << "Unable to read size of file: " << output_path.value(); |
| return false; |
| } |
| contents->resize(size); |
| if (base::ReadFile(output_path, |
| reinterpret_cast<char*>(contents->data()), |
| contents->size()) != |
| static_cast<int>(contents->size())) { |
| LOG(ERROR) << "Unable to read file: " << output_path.value(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace ar |