update_engine: Add MtdFileDescriptor and UbiFileDescriptor
We send a pure file descriptor to ExtentWriter. This CL changes that to
use FileDescriptor. At the same time, the CL adds two other
FileDescriptor classes MtdFileDescriptor and UbiFileDescriptor to work
with raw NAND and UBI devices. Both of these classes support either read
only or sequential write, but not both at the same time. Seek operation
is possible in read only mode.
These classes are conditionally included if USE_mtd is not '0'.
BUG=chromium:426742
TEST=unittest
TEST=USE=mtd emerge update_engine, make sure there is MtdFileDescriptor
in /usr/sbin/update_engine
TEST=emerge --unmerge android_mtdutils; USE=-mtd emerge update_engine
make sure there is no UbiFileDescriptor in that same file
Change-Id: If3ba43677d93dc4f3cea037f19866c8b546b2cae
Reviewed-on: https://chromium-review.googlesource.com/229004
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Nam Nguyen <namnguyen@chromium.org>
Tested-by: Nam Nguyen <namnguyen@chromium.org>
diff --git a/bzip_extent_writer.cc b/bzip_extent_writer.cc
index 008e64a..7cde7ba 100644
--- a/bzip_extent_writer.cc
+++ b/bzip_extent_writer.cc
@@ -12,7 +12,7 @@
const vector<char>::size_type kOutputBufferLength = 16 * 1024;
}
-bool BzipExtentWriter::Init(int fd,
+bool BzipExtentWriter::Init(FileDescriptorPtr fd,
const vector<Extent>& extents,
uint32_t block_size) {
// Init bzip2 stream
diff --git a/bzip_extent_writer.h b/bzip_extent_writer.h
index 9726253..0187c0b 100644
--- a/bzip_extent_writer.h
+++ b/bzip_extent_writer.h
@@ -23,7 +23,9 @@
}
~BzipExtentWriter() {}
- bool Init(int fd, const std::vector<Extent>& extents, uint32_t block_size);
+ bool Init(FileDescriptorPtr fd,
+ const std::vector<Extent>& extents,
+ uint32_t block_size);
bool Write(const void* bytes, size_t count);
bool EndImpl();
diff --git a/bzip_extent_writer_unittest.cc b/bzip_extent_writer_unittest.cc
index 4cf971b..67611c8 100644
--- a/bzip_extent_writer_unittest.cc
+++ b/bzip_extent_writer_unittest.cc
@@ -4,6 +4,7 @@
#include "update_engine/bzip_extent_writer.h"
+#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -29,19 +30,19 @@
protected:
void SetUp() override {
memcpy(path_, kPathTemplate, sizeof(kPathTemplate));
- fd_ = mkstemp(path_);
- ASSERT_GE(fd_, 0);
+ fd_.reset(new EintrSafeFileDescriptor);
+ int fd = mkstemp(path_);
+ ASSERT_TRUE(fd_->Open(path_, O_RDWR, 0600));
+ close(fd);
}
void TearDown() override {
- close(fd_);
+ fd_->Close();
unlink(path_);
}
- int fd() { return fd_; }
- const char* path() { return path_; }
void WriteAlignedExtents(size_t chunk_size, size_t first_chunk_size);
void TestZeroPad(bool aligned_size);
- private:
- int fd_;
+
+ FileDescriptorPtr fd_;
char path_[sizeof(kPathTemplate)];
};
@@ -63,15 +64,14 @@
DirectExtentWriter direct_writer;
BzipExtentWriter bzip_writer(&direct_writer);
- EXPECT_TRUE(bzip_writer.Init(fd(), extents, kBlockSize));
+ EXPECT_TRUE(bzip_writer.Init(fd_, extents, kBlockSize));
EXPECT_TRUE(bzip_writer.Write(test, sizeof(test)));
EXPECT_TRUE(bzip_writer.End());
- char buf[sizeof(test_uncompressed) + 1];
- memset(buf, 0, sizeof(buf));
- ssize_t bytes_read = pread(fd(), buf, sizeof(buf) - 1, 0);
- EXPECT_EQ(strlen(test_uncompressed), bytes_read);
- EXPECT_EQ(string(buf), string(test_uncompressed));
+ vector<char> buf;
+ EXPECT_TRUE(utils::ReadFile(path_, &buf));
+ EXPECT_EQ(strlen(test_uncompressed), buf.size());
+ EXPECT_EQ(string(buf.data(), buf.size()), string(test_uncompressed));
}
TEST_F(BzipExtentWriterTest, ChunkedTest) {
@@ -104,7 +104,7 @@
DirectExtentWriter direct_writer;
BzipExtentWriter bzip_writer(&direct_writer);
- EXPECT_TRUE(bzip_writer.Init(fd(), extents, kBlockSize));
+ EXPECT_TRUE(bzip_writer.Init(fd_, extents, kBlockSize));
vector<char> original_compressed_data = compressed_data;
for (vector<char>::size_type i = 0; i < compressed_data.size();
@@ -117,10 +117,9 @@
// Check that the const input has not been clobbered.
test_utils::ExpectVectorsEq(original_compressed_data, compressed_data);
- vector<char> output(kDecompressedLength + 1);
- ssize_t bytes_read = pread(fd(), &output[0], output.size(), 0);
- EXPECT_EQ(kDecompressedLength, bytes_read);
- output.resize(kDecompressedLength);
+ vector<char> output;
+ EXPECT_TRUE(utils::ReadFile(path_, &output));
+ EXPECT_EQ(kDecompressedLength, output.size());
test_utils::ExpectVectorsEq(decompressed_data, output);
unlink(decompressed_path.c_str());
diff --git a/delta_performer.cc b/delta_performer.cc
index 2cd83fd..280913c 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -24,6 +24,9 @@
#include "update_engine/extent_ranges.h"
#include "update_engine/extent_writer.h"
#include "update_engine/hardware_interface.h"
+#if USE_MTD
+#include "update_engine/mtd_file_descriptor.h"
+#endif
#include "update_engine/payload_constants.h"
#include "update_engine/payload_state_interface.h"
#include "update_engine/payload_verifier.h"
@@ -56,24 +59,35 @@
namespace {
const int kUpdateStateOperationInvalid = -1;
const int kMaxResumedUpdateFailures = 10;
-// Opens path for read/write, put the fd into *fd. On success returns true
-// and sets *err to 0. On failure, returns false and sets *err to errno.
-bool OpenFile(const char* path, int* fd, int* err) {
- if (*fd != -1) {
- LOG(ERROR) << "Can't open(" << path << "), *fd != -1 (it's " << *fd << ")";
- *err = EINVAL;
- return false;
+
+FileDescriptorPtr CreateFileDescriptor(const char* path) {
+ FileDescriptorPtr ret;
+#if USE_MTD
+ if (UbiFileDescriptor::IsUbi(path)) {
+ ret.reset(new UbiFileDescriptor);
+ } else if (MtdFileDescriptor::IsMtd(path)) {
+ ret.reset(new MtdFileDescriptor);
+ } else
+#endif
+ {
+ ret.reset(new EintrSafeFileDescriptor);
}
- *fd = open(path, O_RDWR, 000);
- if (*fd < 0) {
- *err = errno;
- PLOG(ERROR) << "Unable to open file " << path;
- return false;
- }
- *err = 0;
- return true;
+ return ret;
}
+// Opens path for read/write. On success returns an open FileDescriptor
+// and sets *err to 0. On failure, sets *err to errno and returns nullptr.
+FileDescriptorPtr OpenFile(const char* path, int* err) {
+ FileDescriptorPtr fd = CreateFileDescriptor(path);
+ // TODO(namnguyen): If we're working with MTD or UBI, DO NOT use O_RDWR.
+ if (!fd->Open(path, O_RDWR, 000)) {
+ *err = errno;
+ PLOG(ERROR) << "Unable to open file " << path;
+ return nullptr;
+ }
+ *err = 0;
+ return fd;
+}
} // namespace
@@ -220,31 +234,32 @@
int DeltaPerformer::Open(const char* path, int flags, mode_t mode) {
int err;
- if (OpenFile(path, &fd_, &err))
+ fd_ = OpenFile(path, &err);
+ if (fd_)
path_ = path;
return -err;
}
bool DeltaPerformer::OpenKernel(const char* kernel_path) {
int err;
- bool success = OpenFile(kernel_path, &kernel_fd_, &err);
- if (success)
+ kernel_fd_ = OpenFile(kernel_path, &err);
+ if (kernel_fd_)
kernel_path_ = kernel_path;
- return success;
+ return static_cast<bool>(kernel_fd_);
}
int DeltaPerformer::Close() {
int err = 0;
- if (close(kernel_fd_) == -1) {
+ if (!kernel_fd_->Close()) {
err = errno;
PLOG(ERROR) << "Unable to close kernel fd:";
}
- if (close(fd_) == -1) {
+ if (!fd_->Close()) {
err = errno;
PLOG(ERROR) << "Unable to close rootfs fd:";
}
LOG_IF(ERROR, !hash_calculator_.Finalize()) << "Unable to finalize the hash.";
- fd_ = -2; // Set to invalid so that calls to Open() will fail.
+ fd_.reset(); // Set to invalid so that calls to Open() will fail.
path_ = "";
if (!buffer_.empty()) {
LOG(INFO) << "Discarding " << buffer_.size() << " unused downloaded bytes";
@@ -612,7 +627,7 @@
extents.push_back(operation.dst_extents(i));
}
- int fd = is_kernel_partition ? kernel_fd_ : fd_;
+ FileDescriptorPtr fd = is_kernel_partition ? kernel_fd_ : fd_;
TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_));
TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length()));
@@ -642,7 +657,7 @@
DCHECK_EQ(blocks_to_write, blocks_to_read);
vector<char> buf(blocks_to_write * block_size_);
- int fd = is_kernel_partition ? kernel_fd_ : fd_;
+ FileDescriptorPtr fd = is_kernel_partition ? kernel_fd_ : fd_;
// Read in bytes.
ssize_t bytes_read = 0;
@@ -758,9 +773,6 @@
// file is written out.
DiscardBuffer(true);
- int fd = is_kernel_partition ? kernel_fd_ : fd_;
- const string path = base::StringPrintf("/proc/self/fd/%d", fd);
-
// If this is a non-idempotent operation, request a delayed exit and clear the
// update state in case the operation gets interrupted. Do this as late as
// possible.
@@ -770,6 +782,7 @@
}
vector<string> cmd;
+ const string& path = is_kernel_partition ? kernel_path_ : path_;
cmd.push_back(kBspatchPath);
cmd.push_back(path);
cmd.push_back(path);
@@ -794,6 +807,7 @@
const uint64_t begin_byte =
end_byte - (block_size_ - operation.dst_length() % block_size_);
vector<char> zeros(end_byte - begin_byte);
+ FileDescriptorPtr fd = is_kernel_partition ? kernel_fd_ : fd_;
TEST_AND_RETURN_FALSE(
utils::PWriteAll(fd, &zeros[0], end_byte - begin_byte, begin_byte));
}
diff --git a/delta_performer.h b/delta_performer.h
index 256495f..15659e7 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -14,6 +14,7 @@
#include <google/protobuf/repeated_field.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
+#include "update_engine/file_descriptor.h"
#include "update_engine/file_writer.h"
#include "update_engine/install_plan.h"
#include "update_engine/omaha_hash_calculator.h"
@@ -61,8 +62,8 @@
: prefs_(prefs),
system_state_(system_state),
install_plan_(install_plan),
- fd_(-1),
- kernel_fd_(-1),
+ fd_(nullptr),
+ kernel_fd_(nullptr),
manifest_parsed_(false),
manifest_valid_(false),
metadata_size_(0),
@@ -296,10 +297,10 @@
InstallPlan* install_plan_;
// File descriptor of open device.
- int fd_;
+ FileDescriptorPtr fd_;
// File descriptor of the kernel device
- int kernel_fd_;
+ FileDescriptorPtr kernel_fd_;
std::string path_; // Path that fd_ refers to.
std::string kernel_path_; // Path that kernel_fd_ refers to.
diff --git a/extent_writer.cc b/extent_writer.cc
index 0cdb797..336311d 100644
--- a/extent_writer.cc
+++ b/extent_writer.cc
@@ -5,6 +5,7 @@
#include "update_engine/extent_writer.h"
#include <errno.h>
+#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
@@ -36,7 +37,7 @@
const off64_t offset =
extents_[next_extent_index_].start_block() * block_size_ +
extent_bytes_written_;
- TEST_AND_RETURN_FALSE_ERRNO(lseek64(fd_, offset, SEEK_SET) !=
+ TEST_AND_RETURN_FALSE_ERRNO(fd_->Seek(offset, SEEK_SET) !=
static_cast<off64_t>(-1));
TEST_AND_RETURN_FALSE(
utils::WriteAll(fd_, c_bytes + bytes_written, bytes_to_write));
diff --git a/extent_writer.h b/extent_writer.h
index 883507a..0ea39fc 100644
--- a/extent_writer.h
+++ b/extent_writer.h
@@ -9,6 +9,7 @@
#include <base/logging.h>
+#include "update_engine/file_descriptor.h"
#include "update_engine/update_metadata.pb.h"
#include "update_engine/utils.h"
@@ -25,7 +26,7 @@
}
// Returns true on success.
- virtual bool Init(int fd,
+ virtual bool Init(FileDescriptorPtr fd,
const std::vector<Extent>& extents,
uint32_t block_size) = 0;
@@ -49,13 +50,15 @@
class DirectExtentWriter : public ExtentWriter {
public:
DirectExtentWriter()
- : fd_(-1),
+ : fd_(nullptr),
block_size_(0),
extent_bytes_written_(0),
next_extent_index_(0) {}
~DirectExtentWriter() {}
- bool Init(int fd, const std::vector<Extent>& extents, uint32_t block_size) {
+ bool Init(FileDescriptorPtr fd,
+ const std::vector<Extent>& extents,
+ uint32_t block_size) {
fd_ = fd;
block_size_ = block_size;
extents_ = extents;
@@ -67,7 +70,7 @@
}
private:
- int fd_;
+ FileDescriptorPtr fd_;
size_t block_size_;
// Bytes written into next_extent_index_ thus far
@@ -90,7 +93,9 @@
bytes_written_mod_block_size_(0) {}
~ZeroPadExtentWriter() {}
- bool Init(int fd, const std::vector<Extent>& extents, uint32_t block_size) {
+ bool Init(FileDescriptorPtr fd,
+ const std::vector<Extent>& extents,
+ uint32_t block_size) {
block_size_ = block_size;
return underlying_extent_writer_->Init(fd, extents, block_size);
}
diff --git a/extent_writer_unittest.cc b/extent_writer_unittest.cc
index 32730b0..0a9020c 100644
--- a/extent_writer_unittest.cc
+++ b/extent_writer_unittest.cc
@@ -4,6 +4,7 @@
#include "update_engine/extent_writer.h"
+#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -36,15 +37,15 @@
protected:
void SetUp() override {
memcpy(path_, kPathTemplate, sizeof(kPathTemplate));
- fd_ = mkstemp(path_);
- ASSERT_GE(fd_, 0);
+ fd_.reset(new EintrSafeFileDescriptor);
+ int fd = mkstemp(path_);
+ ASSERT_TRUE(fd_->Open(path_, O_RDWR, 0600));
+ close(fd);
}
void TearDown() override {
- close(fd_);
+ fd_->Close();
unlink(path_);
}
- int fd() { return fd_; }
- const char* path() { return path_; }
// Writes data to an extent writer in 'chunk_size' chunks with
// the first chunk of size first_chunk_size. It calculates what the
@@ -52,8 +53,8 @@
// wrote the file correctly.
void WriteAlignedExtents(size_t chunk_size, size_t first_chunk_size);
void TestZeroPad(bool aligned_size);
- private:
- int fd_;
+
+ FileDescriptorPtr fd_;
char path_[sizeof(kPathTemplate)];
};
@@ -67,14 +68,14 @@
const string bytes = "1234";
DirectExtentWriter direct_writer;
- EXPECT_TRUE(direct_writer.Init(fd(), extents, kBlockSize));
+ EXPECT_TRUE(direct_writer.Init(fd_, extents, kBlockSize));
EXPECT_TRUE(direct_writer.Write(bytes.data(), bytes.size()));
EXPECT_TRUE(direct_writer.End());
- EXPECT_EQ(kBlockSize + bytes.size(), utils::FileSize(fd()));
+ EXPECT_EQ(kBlockSize + bytes.size(), utils::FileSize(path_));
vector<char> result_file;
- EXPECT_TRUE(utils::ReadFile(path(), &result_file));
+ EXPECT_TRUE(utils::ReadFile(path_, &result_file));
vector<char> expected_file(kBlockSize);
expected_file.insert(expected_file.end(),
@@ -90,7 +91,7 @@
extents.push_back(extent);
DirectExtentWriter direct_writer;
- EXPECT_TRUE(direct_writer.Init(fd(), extents, kBlockSize));
+ EXPECT_TRUE(direct_writer.Init(fd_, extents, kBlockSize));
EXPECT_TRUE(direct_writer.Write(nullptr, 0));
EXPECT_TRUE(direct_writer.End());
}
@@ -125,7 +126,7 @@
test_utils::FillWithData(&data);
DirectExtentWriter direct_writer;
- EXPECT_TRUE(direct_writer.Init(fd(), extents, kBlockSize));
+ EXPECT_TRUE(direct_writer.Init(fd_, extents, kBlockSize));
size_t bytes_written = 0;
while (bytes_written < data.size()) {
@@ -138,10 +139,10 @@
}
EXPECT_TRUE(direct_writer.End());
- EXPECT_EQ(data.size(), utils::FileSize(fd()));
+ EXPECT_EQ(data.size(), utils::FileSize(path_));
vector<char> result_file;
- EXPECT_TRUE(utils::ReadFile(path(), &result_file));
+ EXPECT_TRUE(utils::ReadFile(path_, &result_file));
vector<char> expected_file;
expected_file.insert(expected_file.end(),
@@ -178,19 +179,19 @@
DirectExtentWriter direct_writer;
ZeroPadExtentWriter zero_pad_writer(&direct_writer);
- EXPECT_TRUE(zero_pad_writer.Init(fd(), extents, kBlockSize));
+ EXPECT_TRUE(zero_pad_writer.Init(fd_, extents, kBlockSize));
size_t bytes_to_write = data.size();
const size_t missing_bytes = (aligned_size ? 0 : 9);
bytes_to_write -= missing_bytes;
- lseek64(fd(), kBlockSize - missing_bytes, SEEK_SET);
- EXPECT_EQ(3, write(fd(), "xxx", 3));
+ fd_->Seek(kBlockSize - missing_bytes, SEEK_SET);
+ EXPECT_EQ(3, fd_->Write("xxx", 3));
ASSERT_TRUE(zero_pad_writer.Write(&data[0], bytes_to_write));
EXPECT_TRUE(zero_pad_writer.End());
- EXPECT_EQ(data.size(), utils::FileSize(fd()));
+ EXPECT_EQ(data.size(), utils::FileSize(path_));
vector<char> result_file;
- EXPECT_TRUE(utils::ReadFile(path(), &result_file));
+ EXPECT_TRUE(utils::ReadFile(path_, &result_file));
vector<char> expected_file;
expected_file.insert(expected_file.end(),
@@ -224,7 +225,7 @@
test_utils::FillWithData(&data);
DirectExtentWriter direct_writer;
- EXPECT_TRUE(direct_writer.Init(fd(), extents, kBlockSize));
+ EXPECT_TRUE(direct_writer.Init(fd_, extents, kBlockSize));
size_t bytes_written = 0;
while (bytes_written < (block_count * kBlockSize)) {
@@ -236,10 +237,10 @@
EXPECT_TRUE(direct_writer.End());
// check file size, then data inside
- ASSERT_EQ(2 * kBlockSize, utils::FileSize(path()));
+ ASSERT_EQ(2 * kBlockSize, utils::FileSize(path_));
vector<char> resultant_data;
- EXPECT_TRUE(utils::ReadFile(path(), &resultant_data));
+ EXPECT_TRUE(utils::ReadFile(path_, &resultant_data));
// Create expected data
vector<char> expected_data(on_disk_count * kBlockSize);
diff --git a/file_descriptor.cc b/file_descriptor.cc
index f6b55bb..5fd18d0 100644
--- a/file_descriptor.cc
+++ b/file_descriptor.cc
@@ -46,6 +46,11 @@
return written;
}
+off64_t EintrSafeFileDescriptor::Seek(off64_t offset, int whence) {
+ CHECK_GE(fd_, 0);
+ return lseek64(fd_, offset, whence);
+}
+
bool EintrSafeFileDescriptor::Close() {
CHECK_GE(fd_, 0);
if (IGNORE_EINTR(close(fd_)))
diff --git a/file_descriptor.h b/file_descriptor.h
index d86d4ca..78647de 100644
--- a/file_descriptor.h
+++ b/file_descriptor.h
@@ -6,12 +6,11 @@
#define UPDATE_ENGINE_FILE_DESCRIPTOR_H_
#include <errno.h>
+#include <memory>
#include <sys/types.h>
#include <base/logging.h>
-#include "update_engine/utils.h"
-
// Abstraction for managing opening, reading, writing and closing of file
// descriptors. This includes an abstract class and one standard implementation
// based on POSIX system calls.
@@ -37,6 +36,9 @@
namespace chromeos_update_engine {
+class FileDescriptor;
+using FileDescriptorPtr = std::shared_ptr<FileDescriptor>;
+
// An abstract class defining the file descriptor API.
class FileDescriptor {
public:
@@ -59,6 +61,11 @@
// no bytes were written. Specific implementations may set errno accordingly.
virtual ssize_t Write(const void* buf, size_t count) = 0;
+ // Seeks to an offset. Returns the resulting offset location as measured in
+ // bytes from the beginning. On error, return -1. Specific implementations
+ // may set errno accordingly.
+ virtual off64_t Seek(off64_t offset, int whence) = 0;
+
// Closes a file descriptor. The descriptor must be open prior to this call.
// Returns true on success, false otherwise. Specific implementations may set
// errno accordingly.
@@ -88,6 +95,7 @@
bool Open(const char* path, int flags) override;
ssize_t Read(void* buf, size_t count) override;
ssize_t Write(const void* buf, size_t count) override;
+ off64_t Seek(off64_t offset, int whence) override;
bool Close() override;
void Reset() override;
bool IsSettingErrno() override {
@@ -97,7 +105,7 @@
return (fd_ >= 0);
}
- private:
+ protected:
int fd_;
};
diff --git a/mtd_file_descriptor.cc b/mtd_file_descriptor.cc
new file mode 100644
index 0000000..1b33d05
--- /dev/null
+++ b/mtd_file_descriptor.cc
@@ -0,0 +1,184 @@
+// Copyright 2014 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 "update_engine/mtd_file_descriptor.h"
+
+#include <fcntl.h>
+#include <mtd/ubi-user.h>
+#include <string>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <base/files/file_path.h>
+#include <base/strings/string_number_conversions.h>
+
+#include "update_engine/utils.h"
+
+namespace {
+
+static const char kSysfsClassUbi[] = "/sys/class/ubi/";
+static const char kUsableEbSize[] = "/usable_eb_size";
+static const char kReservedEbs[] = "/reserved_ebs";
+
+using chromeos_update_engine::UbiVolumeInfo;
+using chromeos_update_engine::utils::ReadFile;
+
+// Return a UbiVolumeInfo pointer if |path| is a UBI volume. Otherwise, return
+// a null unique pointer.
+std::unique_ptr<UbiVolumeInfo> GetUbiVolumeInfo(const char* path) {
+ base::FilePath device_node(path);
+ base::FilePath ubi_name(device_node.BaseName());
+
+ std::string sysfs_node(kSysfsClassUbi);
+ sysfs_node.append(ubi_name.MaybeAsASCII());
+
+ std::unique_ptr<UbiVolumeInfo> ret;
+
+ // Obtain volume info from sysfs.
+ std::string s_reserved_ebs;
+ if (!ReadFile(sysfs_node + kReservedEbs, &s_reserved_ebs)) {
+ return ret;
+ }
+ std::string s_eb_size;
+ if (!ReadFile(sysfs_node + kUsableEbSize, &s_eb_size)) {
+ return ret;
+ }
+
+ size_t reserved_ebs, eb_size;
+ if (!base::StringToSizeT(s_reserved_ebs, &reserved_ebs)) {
+ return ret;
+ }
+ if (!base::StringToSizeT(s_eb_size, &eb_size)) {
+ return ret;
+ }
+
+ ret.reset(new UbiVolumeInfo);
+ ret->size = reserved_ebs * eb_size;
+ return ret;
+}
+
+} // namespace
+
+namespace chromeos_update_engine {
+
+MtdFileDescriptor::MtdFileDescriptor()
+ : read_ctx_(nullptr, &mtd_read_close),
+ write_ctx_(nullptr, &mtd_write_close) {}
+
+bool MtdFileDescriptor::IsMtd(const char* path) {
+ uint64_t size;
+ return mtd_node_info(path, &size, nullptr, nullptr) == 0;
+}
+
+bool MtdFileDescriptor::Open(const char* path, int flags, mode_t mode) {
+ // This File Descriptor does not support read and write.
+ TEST_AND_RETURN_FALSE((flags & O_RDWR) != O_RDWR);
+ TEST_AND_RETURN_FALSE(
+ EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
+
+ if (flags & O_RDONLY) {
+ read_ctx_.reset(mtd_read_descriptor(fd_, path));
+ } else if (flags & O_WRONLY) {
+ write_ctx_.reset(mtd_write_descriptor(fd_, path));
+ }
+
+ if (!read_ctx_ && !write_ctx_) {
+ Close();
+ return false;
+ }
+
+ return true;
+}
+
+bool MtdFileDescriptor::Open(const char* path, int flags) {
+ mode_t cur = umask(022);
+ umask(cur);
+ return Open(path, flags, 0777 & ~cur);
+}
+
+ssize_t MtdFileDescriptor::Read(void* buf, size_t count) {
+ CHECK(read_ctx_);
+ return mtd_read_data(read_ctx_.get(), static_cast<char*>(buf), count);
+}
+
+ssize_t MtdFileDescriptor::Write(const void* buf, size_t count) {
+ CHECK(write_ctx_);
+ return mtd_write_data(write_ctx_.get(), static_cast<const char*>(buf), count);
+}
+
+off64_t MtdFileDescriptor::Seek(off64_t offset, int whence) {
+ CHECK(read_ctx_);
+ return EintrSafeFileDescriptor::Seek(offset, whence);
+}
+
+void MtdFileDescriptor::Reset() {
+ EintrSafeFileDescriptor::Reset();
+ read_ctx_.reset();
+ write_ctx_.reset();
+}
+
+bool UbiFileDescriptor::IsUbi(const char* path) {
+ return static_cast<bool>(GetUbiVolumeInfo(path));
+}
+
+std::unique_ptr<UbiVolumeInfo> UbiFileDescriptor::CreateWriteContext(
+ const char* path) {
+ std::unique_ptr<UbiVolumeInfo> info = GetUbiVolumeInfo(path);
+ uint64_t volume_size;
+ if (info && (ioctl(fd_, UBI_IOCVOLUP, &volume_size) != 0 ||
+ volume_size != info->size)) {
+ info.reset();
+ }
+ return info;
+}
+
+bool UbiFileDescriptor::Open(const char* path, int flags, mode_t mode) {
+ // This File Descriptor does not support read and write.
+ TEST_AND_RETURN_FALSE((flags & O_RDWR) != O_RDWR);
+ TEST_AND_RETURN_FALSE(
+ EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
+
+ if (flags & O_RDONLY) {
+ read_ctx_ = GetUbiVolumeInfo(path);
+ } else if (flags & O_WRONLY) {
+ write_ctx_ = CreateWriteContext(path);
+ }
+
+ if (!read_ctx_ && !write_ctx_) {
+ Close();
+ return false;
+ }
+
+ return true;
+}
+
+bool UbiFileDescriptor::Open(const char* path, int flags) {
+ mode_t cur = umask(022);
+ umask(cur);
+ return Open(path, flags, 0777 & ~cur);
+}
+
+ssize_t UbiFileDescriptor::Read(void* buf, size_t count) {
+ CHECK(read_ctx_);
+ return EintrSafeFileDescriptor::Read(buf, count);
+}
+
+ssize_t UbiFileDescriptor::Write(const void* buf, size_t count) {
+ CHECK(write_ctx_);
+ return EintrSafeFileDescriptor::Write(buf, count);
+}
+
+off64_t UbiFileDescriptor::Seek(off64_t offset, int whence) {
+ CHECK(read_ctx_);
+ return EintrSafeFileDescriptor::Seek(offset, whence);
+}
+
+void UbiFileDescriptor::Reset() {
+ EintrSafeFileDescriptor::Reset();
+ read_ctx_.reset();
+ write_ctx_.reset();
+}
+
+} // namespace chromeos_update_engine
diff --git a/mtd_file_descriptor.h b/mtd_file_descriptor.h
new file mode 100644
index 0000000..4694016
--- /dev/null
+++ b/mtd_file_descriptor.h
@@ -0,0 +1,71 @@
+// Copyright 2014 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.
+
+#ifndef UPDATE_ENGINE_MTD_FILE_DESCRIPTOR_H_
+#define UPDATE_ENGINE_MTD_FILE_DESCRIPTOR_H_
+
+// This module defines file descriptors that deal with NAND media. We are
+// concerned with raw NAND access (as MTD device), and through UBI layer.
+
+#include <mtdutils.h>
+
+#include "update_engine/file_descriptor.h"
+
+namespace chromeos_update_engine {
+
+// A class defining the file descriptor API for raw MTD device. This file
+// descriptor supports either random read, or sequential write but not both at
+// once.
+class MtdFileDescriptor : public EintrSafeFileDescriptor {
+ public:
+ MtdFileDescriptor();
+
+ static bool IsMtd(const char* path);
+
+ bool Open(const char* path, int flags, mode_t mode) override;
+ bool Open(const char* path, int flags) override;
+ ssize_t Read(void* buf, size_t count) override;
+ ssize_t Write(const void* buf, size_t count) override;
+ off64_t Seek(off64_t offset, int whence) override;
+ void Reset() override;
+
+ private:
+ std::unique_ptr<MtdReadContext, decltype(&mtd_read_close)> read_ctx_;
+ std::unique_ptr<MtdWriteContext, decltype(&mtd_write_close)> write_ctx_;
+};
+
+// TODO(namnguyen) This is a placeholder struct. This struct, and the
+// UbiFileDescriptor class below will need finalized later.
+struct UbiVolumeInfo {
+ uint64_t size;
+};
+
+// A file descriptor to update a UBI volume, similar to MtdFileDescriptor.
+// Once the file descriptor is opened for write, the volume is marked as being
+// updated. The volume will not be usable until an update is completed. See
+// UBI_IOCVOLUP ioctl operation.
+// TODO(namnguyen) Again, this needs fleshed out when we have better library to
+// interact with UBI volumes. I would expect this class to be very similar to
+// MtdFileDescriptor, with two different contexts to bridge C-C++ divide.
+class UbiFileDescriptor : public EintrSafeFileDescriptor {
+ public:
+ static bool IsUbi(const char* path);
+
+ bool Open(const char* path, int flags, mode_t mode) override;
+ bool Open(const char* path, int flags) override;
+ ssize_t Read(void* buf, size_t count) override;
+ ssize_t Write(const void* buf, size_t count) override;
+ off64_t Seek(off64_t offset, int whence) override;
+ void Reset() override;
+
+ private:
+ std::unique_ptr<UbiVolumeInfo> CreateWriteContext(const char* path);
+
+ std::unique_ptr<UbiVolumeInfo> read_ctx_;
+ std::unique_ptr<UbiVolumeInfo> write_ctx_;
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_MTD_FILE_DESCRIPTOR_H_
diff --git a/update_engine.gyp b/update_engine.gyp
index f8d44bf..4212c5b 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -31,6 +31,7 @@
'_FILE_OFFSET_BITS=64',
'_POSIX_C_SOURCE=199309L',
'USE_HWID_OVERRIDE=<(USE_hwid_override)',
+ 'USE_MTD=<(USE_mtd)',
'USE_POWER_MANAGEMENT=<(USE_power_management)',
],
},
@@ -190,6 +191,18 @@
'update_manager/update_manager.cc',
'utils.cc',
],
+ 'conditions': [
+ ['USE_mtd == 1', {
+ 'sources': [
+ 'mtd_file_descriptor.cc',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-lmtdutils',
+ ],
+ },
+ }],
+ ],
},
# update_engine daemon.
{
diff --git a/utils.cc b/utils.cc
index b84eda0..9a596a7 100644
--- a/utils.cc
+++ b/utils.cc
@@ -40,6 +40,7 @@
#include "update_engine/clock_interface.h"
#include "update_engine/constants.h"
+#include "update_engine/file_descriptor.h"
#include "update_engine/file_writer.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/prefs_interface.h"
@@ -162,6 +163,25 @@
return true;
}
+bool WriteAll(FileDescriptorPtr fd, const void* buf, size_t count) {
+ const char* c_buf = static_cast<const char*>(buf);
+ ssize_t bytes_written = 0;
+ while (bytes_written < static_cast<ssize_t>(count)) {
+ ssize_t rc = fd->Write(c_buf + bytes_written, count - bytes_written);
+ TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
+ bytes_written += rc;
+ }
+ return true;
+}
+
+bool PWriteAll(FileDescriptorPtr fd,
+ const void* buf,
+ size_t count,
+ off_t offset) {
+ TEST_AND_RETURN_FALSE_ERRNO(fd->Seek(offset, SEEK_SET));
+ return WriteAll(fd, buf, count);
+}
+
bool PReadAll(int fd, void* buf, size_t count, off_t offset,
ssize_t* out_bytes_read) {
char* c_buf = static_cast<char*>(buf);
@@ -179,6 +199,23 @@
return true;
}
+bool PReadAll(FileDescriptorPtr fd, void* buf, size_t count, off_t offset,
+ ssize_t* out_bytes_read) {
+ TEST_AND_RETURN_FALSE_ERRNO(fd->Seek(offset, SEEK_SET));
+ char* c_buf = static_cast<char*>(buf);
+ ssize_t bytes_read = 0;
+ while (bytes_read < static_cast<ssize_t>(count)) {
+ ssize_t rc = fd->Read(c_buf + bytes_read, count - bytes_read);
+ TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
+ if (rc == 0) {
+ break;
+ }
+ bytes_read += rc;
+ }
+ *out_bytes_read = bytes_read;
+ return true;
+}
+
// Append |nbytes| of content from |buf| to the vector pointed to by either
// |vec_p| or |str_p|.
static void AppendBytes(const char* buf, size_t nbytes,
diff --git a/utils.h b/utils.h
index 50e5864..0fe373a 100644
--- a/utils.h
+++ b/utils.h
@@ -25,6 +25,7 @@
#include "update_engine/action_processor.h"
#include "update_engine/connection_manager.h"
#include "update_engine/constants.h"
+#include "update_engine/file_descriptor.h"
#include "update_engine/metrics.h"
namespace chromeos_update_engine {
@@ -69,11 +70,20 @@
bool WriteAll(int fd, const void* buf, size_t count);
bool PWriteAll(int fd, const void* buf, size_t count, off_t offset);
+bool WriteAll(FileDescriptorPtr fd, const void* buf, size_t count);
+bool PWriteAll(FileDescriptorPtr fd,
+ const void* buf,
+ size_t count,
+ off_t offset);
+
// Calls pread() repeatedly until count bytes are read, or EOF is reached.
// Returns number of bytes read in *bytes_read. Returns true on success.
bool PReadAll(int fd, void* buf, size_t count, off_t offset,
ssize_t* out_bytes_read);
+bool PReadAll(FileDescriptorPtr fd, void* buf, size_t count, off_t offset,
+ ssize_t* out_bytes_read);
+
// Opens |path| for reading and appends its entire content to the container
// pointed to by |out_p|. Returns true upon successfully reading all of the
// file's content, false otherwise, in which case the state of the output