blob: 934cdb3f66cee53a2a9c69213ded5d4110ae518f [file] [log] [blame]
// 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