blob: 7ab2afa0a41ea8bb1a46c44b145766bdd43b6d2a [file] [log] [blame]
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -08001// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/mtd_file_descriptor.h"
6
7#include <fcntl.h>
8#include <mtd/ubi-user.h>
9#include <string>
10#include <sys/ioctl.h>
11#include <sys/stat.h>
12#include <sys/types.h>
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080013#include <vector>
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080014
15#include <base/files/file_path.h>
16#include <base/strings/string_number_conversions.h>
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080017#include <base/strings/string_util.h>
18#include <base/strings/stringprintf.h>
19#include <update_engine/subprocess.h>
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080020
21#include "update_engine/utils.h"
22
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080023using std::string;
24using std::vector;
25
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080026namespace {
27
28static const char kSysfsClassUbi[] = "/sys/class/ubi/";
29static const char kUsableEbSize[] = "/usable_eb_size";
30static const char kReservedEbs[] = "/reserved_ebs";
31
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080032using chromeos_update_engine::Subprocess;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080033using chromeos_update_engine::UbiVolumeInfo;
34using chromeos_update_engine::utils::ReadFile;
35
36// Return a UbiVolumeInfo pointer if |path| is a UBI volume. Otherwise, return
37// a null unique pointer.
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080038std::unique_ptr<UbiVolumeInfo> GetUbiVolumeInfo(const string& path) {
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080039 base::FilePath device_node(path);
40 base::FilePath ubi_name(device_node.BaseName());
41
42 std::string sysfs_node(kSysfsClassUbi);
43 sysfs_node.append(ubi_name.MaybeAsASCII());
44
45 std::unique_ptr<UbiVolumeInfo> ret;
46
47 // Obtain volume info from sysfs.
48 std::string s_reserved_ebs;
49 if (!ReadFile(sysfs_node + kReservedEbs, &s_reserved_ebs)) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080050 LOG(ERROR) << "Cannot read " << sysfs_node + kReservedEbs;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080051 return ret;
52 }
53 std::string s_eb_size;
54 if (!ReadFile(sysfs_node + kUsableEbSize, &s_eb_size)) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080055 LOG(ERROR) << "Cannot read " << sysfs_node + kUsableEbSize;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080056 return ret;
57 }
58
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080059 base::TrimWhitespaceASCII(s_reserved_ebs,
60 base::TRIM_TRAILING,
61 &s_reserved_ebs);
62 base::TrimWhitespaceASCII(s_eb_size, base::TRIM_TRAILING, &s_eb_size);
63
64 uint64_t reserved_ebs, eb_size;
65 if (!base::StringToUint64(s_reserved_ebs, &reserved_ebs)) {
66 LOG(ERROR) << "Cannot parse reserved_ebs: " << s_reserved_ebs;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080067 return ret;
68 }
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080069 if (!base::StringToUint64(s_eb_size, &eb_size)) {
70 LOG(ERROR) << "Cannot parse usable_eb_size: " << s_eb_size;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080071 return ret;
72 }
73
74 ret.reset(new UbiVolumeInfo);
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080075 ret->reserved_ebs = reserved_ebs;
76 ret->eraseblock_size = eb_size;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080077 return ret;
78}
79
80} // namespace
81
82namespace chromeos_update_engine {
83
84MtdFileDescriptor::MtdFileDescriptor()
85 : read_ctx_(nullptr, &mtd_read_close),
86 write_ctx_(nullptr, &mtd_write_close) {}
87
88bool MtdFileDescriptor::IsMtd(const char* path) {
89 uint64_t size;
90 return mtd_node_info(path, &size, nullptr, nullptr) == 0;
91}
92
93bool MtdFileDescriptor::Open(const char* path, int flags, mode_t mode) {
94 // This File Descriptor does not support read and write.
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080095 TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
96 // But we need to open the underlying file descriptor in O_RDWR mode because
97 // during write, we need to read back to verify the write actually sticks or
98 // we have to skip the block. That job is done by mtdutils library.
99 if ((flags & O_ACCMODE) == O_WRONLY) {
100 flags &= ~O_ACCMODE;
101 flags |= O_RDWR;
102 }
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800103 TEST_AND_RETURN_FALSE(
104 EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
105
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800106 if ((flags & O_ACCMODE) == O_RDWR) {
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800107 write_ctx_.reset(mtd_write_descriptor(fd_, path));
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800108 nr_written_ = 0;
109 } else {
110 read_ctx_.reset(mtd_read_descriptor(fd_, path));
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800111 }
112
113 if (!read_ctx_ && !write_ctx_) {
114 Close();
115 return false;
116 }
117
118 return true;
119}
120
121bool MtdFileDescriptor::Open(const char* path, int flags) {
122 mode_t cur = umask(022);
123 umask(cur);
124 return Open(path, flags, 0777 & ~cur);
125}
126
127ssize_t MtdFileDescriptor::Read(void* buf, size_t count) {
128 CHECK(read_ctx_);
129 return mtd_read_data(read_ctx_.get(), static_cast<char*>(buf), count);
130}
131
132ssize_t MtdFileDescriptor::Write(const void* buf, size_t count) {
133 CHECK(write_ctx_);
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800134 ssize_t result = mtd_write_data(write_ctx_.get(),
135 static_cast<const char*>(buf),
136 count);
137 if (result > 0) {
138 nr_written_ += result;
139 }
140 return result;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800141}
142
143off64_t MtdFileDescriptor::Seek(off64_t offset, int whence) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800144 if (write_ctx_) {
145 // Ignore seek in write mode.
146 return nr_written_;
147 }
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800148 return EintrSafeFileDescriptor::Seek(offset, whence);
149}
150
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800151bool MtdFileDescriptor::Close() {
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800152 read_ctx_.reset();
153 write_ctx_.reset();
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800154 return EintrSafeFileDescriptor::Close();
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800155}
156
157bool UbiFileDescriptor::IsUbi(const char* path) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800158 base::FilePath device_node(path);
159 base::FilePath ubi_name(device_node.BaseName());
Alex Vakulenko6a9d3492015-06-15 12:53:22 -0700160 TEST_AND_RETURN_FALSE(
161 base::StartsWithASCII(ubi_name.MaybeAsASCII(), "ubi", true));
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800162
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800163 return static_cast<bool>(GetUbiVolumeInfo(path));
164}
165
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800166bool UbiFileDescriptor::Open(const char* path, int flags, mode_t mode) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800167 std::unique_ptr<UbiVolumeInfo> info = GetUbiVolumeInfo(path);
168 if (!info) {
169 return false;
170 }
171
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800172 // This File Descriptor does not support read and write.
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800173 TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800174 TEST_AND_RETURN_FALSE(
175 EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
176
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800177 usable_eb_blocks_ = info->reserved_ebs;
178 eraseblock_size_ = info->eraseblock_size;
179 volume_size_ = usable_eb_blocks_ * eraseblock_size_;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800180
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800181 if ((flags & O_ACCMODE) == O_WRONLY) {
182 // It's best to use volume update ioctl so that UBI layer will mark the
183 // volume as being updated, and only clear that mark if the update is
184 // successful. We will need to pad to the whole volume size at close.
185 uint64_t vsize = volume_size_;
186 if (ioctl(fd_, UBI_IOCVOLUP, &vsize) != 0) {
187 PLOG(ERROR) << "Cannot issue volume update ioctl";
188 EintrSafeFileDescriptor::Close();
189 return false;
190 }
191 mode_ = kWriteOnly;
192 nr_written_ = 0;
193 } else {
194 mode_ = kReadOnly;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800195 }
196
197 return true;
198}
199
200bool UbiFileDescriptor::Open(const char* path, int flags) {
201 mode_t cur = umask(022);
202 umask(cur);
203 return Open(path, flags, 0777 & ~cur);
204}
205
206ssize_t UbiFileDescriptor::Read(void* buf, size_t count) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800207 CHECK(mode_ == kReadOnly);
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800208 return EintrSafeFileDescriptor::Read(buf, count);
209}
210
211ssize_t UbiFileDescriptor::Write(const void* buf, size_t count) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800212 CHECK(mode_ == kWriteOnly);
213 ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, count);
214 if (nr_chunk >= 0) {
215 nr_written_ += nr_chunk;
216 }
217 return nr_chunk;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800218}
219
220off64_t UbiFileDescriptor::Seek(off64_t offset, int whence) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800221 if (mode_ == kWriteOnly) {
222 // Ignore seek in write mode.
223 return nr_written_;
224 }
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800225 return EintrSafeFileDescriptor::Seek(offset, whence);
226}
227
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800228bool UbiFileDescriptor::Close() {
229 bool pad_ok = true;
230 if (IsOpen() && mode_ == kWriteOnly) {
231 char buf[1024];
232 memset(buf, 0xFF, sizeof(buf));
233 while (nr_written_ < volume_size_) {
234 // We have written less than the whole volume. In order for us to clear
235 // the update marker, we need to fill the rest. It is recommended to fill
236 // UBI writes with 0xFF.
237 uint64_t to_write = volume_size_ - nr_written_;
238 if (to_write > sizeof(buf)) {
239 to_write = sizeof(buf);
240 }
241 ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, to_write);
242 if (nr_chunk < 0) {
243 LOG(ERROR) << "Cannot 0xFF-pad before closing.";
244 // There is an error, but we can't really do any meaningful thing here.
245 pad_ok = false;
246 break;
247 }
248 nr_written_ += nr_chunk;
249 }
250 }
251 return EintrSafeFileDescriptor::Close() && pad_ok;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800252}
253
254} // namespace chromeos_update_engine