blob: ee6052f554c9394a3f5b39922fcbe2ae565aac08 [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/image_burner_utils.h"
#include <memory>
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/memory/free_deleter.h>
#include <base/strings/stringprintf.h>
#include <rootdev/rootdev.h>
namespace imageburn {
namespace {
const int kFsyncRatio = 1024;
bool RealPath(const char* path, std::string* real_path) {
std::unique_ptr<char, base::FreeDeleter> result(realpath(path, nullptr));
if (!result) {
PLOG(ERROR) << "Couldn't get real path of " << path;
return false;
}
*real_path = result.get();
return true;
}
} // namespace
BurnWriter::BurnWriter()
: fstat_callback_(base::BindRepeating(&base::File::Fstat)) {}
bool BurnWriter::Open(const char* path) {
if (file_.IsValid())
return false;
file_.Initialize(base::FilePath(path),
base::File::FLAG_WRITE | base::File::FLAG_OPEN);
if (!file_.IsValid()) {
PLOG(ERROR) << "Couldn't open target path " << path;
return false;
}
base::stat_wrapper_t st = {};
if (fstat_callback_.Run(file_.GetPlatformFile(), &st) != 0) {
PLOG(ERROR) << "Unable to stat file for path " << path;
Close();
return false;
}
if (!S_ISBLK(st.st_mode)) {
PLOG(ERROR) << "Attempt to write to non-block device " << path;
Close();
return false;
}
LOG(INFO) << path << " opened";
return true;
}
bool BurnWriter::Close() {
if (!file_.IsValid())
return false;
file_.Close();
return true;
}
int BurnWriter::Write(const char* data_block, int data_size) {
const int written = file_.WriteAtCurrentPos(data_block, data_size);
if (written != data_size) {
PLOG(ERROR) << "Error writing to target file";
return written;
}
if (!writes_count_ && !file_.Flush()) {
PLOG(ERROR) << "Error flushing target file.";
return -1;
}
writes_count_++;
if (writes_count_ == kFsyncRatio)
writes_count_ = 0;
return written;
}
BurnReader::BurnReader() {}
bool BurnReader::Open(const char* path) {
if (file_.IsValid())
return false;
file_.Initialize(base::FilePath(path),
base::File::FLAG_READ | base::File::FLAG_OPEN);
if (!file_.IsValid()) {
PLOG(ERROR) << "Couldn't open source path " << path;
return false;
}
// Obtains the real path of the file associated with the opened file
// descriptor by resolving the /proc/self/fd/<descriptor> symlink. Compares
// |path| against the real path determined by /proc/self/fd/<descriptor> to
// make sure |path| specifies the real path and avoids a potential TOCTOU
// race between the underlying realpath() and open() call.
int fd = file_.GetPlatformFile();
std::string fd_path;
if (!RealPath(base::StringPrintf("/proc/self/fd/%d", fd).c_str(), &fd_path)) {
file_.Close();
return false;
}
if (fd_path != path) {
LOG(ERROR) << path << " isn't a fully resolved path";
file_.Close();
return false;
}
LOG(INFO) << path << " opened";
return true;
}
bool BurnReader::Close() {
if (!file_.IsValid())
return false;
file_.Close();
return true;
}
int BurnReader::Read(char* data_block, int data_size) {
const int read = file_.ReadAtCurrentPos(data_block, data_size);
if (read < 0)
PLOG(ERROR) << "Error reading from source file";
return read;
}
int64_t BurnReader::GetSize() {
if (!file_.IsValid())
return -1;
return file_.GetLength();
}
bool BurnPathGetter::GetRealPath(const char* path, std::string* real_path) {
return RealPath(path, real_path);
}
bool BurnPathGetter::GetRootPath(std::string* path) {
char root_path[PATH_MAX];
if (rootdev(root_path, sizeof(root_path), true, true)) {
// Coult not get root path.
return false;
}
*path = root_path;
return true;
}
} // namespace imageburn