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/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