blob: 7f79d01a7b3cbfa2c2e6a1a840c542271d969455 [file] [log] [blame]
// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "image_burner_impl.h"
#include <cstring>
#include <ctype.h>
#include <base/basictypes.h>
#include <base/logging.h>
namespace imageburn {
const int kBurningBlockSize = 4 * 1024; // 4 KiB
const char* kFilePathPrefix = "/dev/sd";
BurnerImpl::BurnerImpl(FileSystemWriter* writer,
FileSystemReader* reader,
SignalSender* signal_sender,
RootPathGetter* root_path_getter)
: writer_(writer),
reader_(reader),
root_path_getter_(root_path_getter),
signal_sender_(signal_sender),
data_block_size_(kBurningBlockSize) {
}
ErrorCode BurnerImpl::BurnImage(const char* from_path, const char* to_path) {
ErrorCode err = IMAGEBURN_OK;
if (!writer_ || !reader_ || !signal_sender_ || !root_path_getter_)
err = IMAGEBURN_ERROR_BURNER_NOT_INITIALIZED;
if (!err)
ValidateTargetPath(to_path, &err);
if (!err)
ValidateSourcePath(from_path, &err);
if (!err)
DoBurn(from_path, to_path, &err);
// TODO(tbarzic) send error specific error message.
signal_sender_->SendFinishedSignal(to_path, !err,
(!err ? "" : "Error during burn"));
return err;
}
void BurnerImpl::InitSignalSender(SignalSender* signal_sender) {
signal_sender_ = signal_sender;
}
bool BurnerImpl::ValidateTargetPath(const char* path, ErrorCode* error) {
if (!path) {
*error = IMAGEBURN_ERROR_NULL_TARGET_PATH;
LOG(ERROR) << "Target path set to NULL.";
return false;
}
int file_prefix_len = strlen(kFilePathPrefix);
if (strncmp(path, kFilePathPrefix, file_prefix_len) != 0 ||
strlen(path) == file_prefix_len) {
*error = IMAGEBURN_ERROR_INVALID_TARGET_PATH;
LOG(ERROR) << "Target path is not valid file path.";
return false;
}
int i = file_prefix_len;
while (path[i]) {
if (!islower(path[i])) {
*error = IMAGEBURN_ERROR_INVALID_TARGET_PATH;
LOG(ERROR) << "Target path is not valid file path.";
return false;
}
i++;
}
std::string root_path;
// This will return roots file path, so we can compare target path, which
// should also be devices file path, to it.
bool got_root_path = root_path_getter_->GetRootPath(&root_path);
if (!got_root_path ||
strncmp(root_path.c_str(), path, root_path.length()) == 0) {
*error = IMAGEBURN_ERROR_TARGET_PATH_ON_ROOT;
LOG(ERROR) << "Target path is on root device.";
return false;
}
return true;
}
bool BurnerImpl::ValidateSourcePath(const char* path, ErrorCode* error) {
if (!path) {
*error = IMAGEBURN_ERROR_NULL_SOURCE_PATH;
LOG(ERROR) << "Source path set to NULL.";
return false;
}
return true;
}
bool BurnerImpl::DoBurn(const char* from_path, const char* to_path,
ErrorCode* error) {
LOG(INFO) << "Burning " << from_path << " to " << to_path;
*error = IMAGEBURN_OK;
bool success = reader_->Open(from_path);
if (success) {
if (!(success = writer_->Open(to_path)))
*error = IMAGEBURN_ERROR_CANNOT_OPEN_TARGET;
} else {
*error = IMAGEBURN_ERROR_CANNOT_OPEN_SOURCE;
}
if (success) {
scoped_ptr<char> buffer(new char[data_block_size_]);
int64 total_burnt = 0;
int64 image_size = reader_->GetSize();
int len = 0;
while ((len = reader_->Read(buffer.get(), data_block_size_)) > 0) {
if (writer_->Write(buffer.get(), len) == len) {
total_burnt += static_cast<int64>(len);
signal_sender_->SendProgressSignal(total_burnt, image_size, to_path);
} else {
success = false;
*error = IMAGEBURN_ERROR_FAILED_WRITING_TO_TARGET;
break;
}
}
if (len < 0) {
success = false;
*error = IMAGEBURN_ERROR_FAILED_READING_SOURCE;
}
}
if (!writer_->Close() && success) {
// We set this only when there is no previous error.
success = false;
*error = IMAGEBURN_ERROR_CANNOT_CLOSE_TARGET;
}
if (!reader_->Close() && success) {
// We set this only when there is no previous error.
success = false;
*error = IMAGEBURN_ERROR_CANNOT_CLOSE_SOURCE;
}
return success;
}
} // namespace imageburn.