blob: c4024e99e7aac8fd007f8dfd89261d4e641da70c [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
2// Copyright (C) 2014 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080016
17#include "update_engine/mtd_file_descriptor.h"
18
19#include <fcntl.h>
20#include <mtd/ubi-user.h>
21#include <string>
22#include <sys/ioctl.h>
23#include <sys/stat.h>
24#include <sys/types.h>
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080025#include <vector>
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080026
27#include <base/files/file_path.h>
28#include <base/strings/string_number_conversions.h>
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080029#include <base/strings/string_util.h>
30#include <base/strings/stringprintf.h>
31#include <update_engine/subprocess.h>
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080032
33#include "update_engine/utils.h"
34
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080035using std::string;
36using std::vector;
37
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080038namespace {
39
40static const char kSysfsClassUbi[] = "/sys/class/ubi/";
41static const char kUsableEbSize[] = "/usable_eb_size";
42static const char kReservedEbs[] = "/reserved_ebs";
43
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080044using chromeos_update_engine::Subprocess;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080045using chromeos_update_engine::UbiVolumeInfo;
46using chromeos_update_engine::utils::ReadFile;
47
48// Return a UbiVolumeInfo pointer if |path| is a UBI volume. Otherwise, return
49// a null unique pointer.
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080050std::unique_ptr<UbiVolumeInfo> GetUbiVolumeInfo(const string& path) {
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080051 base::FilePath device_node(path);
52 base::FilePath ubi_name(device_node.BaseName());
53
54 std::string sysfs_node(kSysfsClassUbi);
55 sysfs_node.append(ubi_name.MaybeAsASCII());
56
57 std::unique_ptr<UbiVolumeInfo> ret;
58
59 // Obtain volume info from sysfs.
60 std::string s_reserved_ebs;
61 if (!ReadFile(sysfs_node + kReservedEbs, &s_reserved_ebs)) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080062 LOG(ERROR) << "Cannot read " << sysfs_node + kReservedEbs;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080063 return ret;
64 }
65 std::string s_eb_size;
66 if (!ReadFile(sysfs_node + kUsableEbSize, &s_eb_size)) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080067 LOG(ERROR) << "Cannot read " << sysfs_node + kUsableEbSize;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080068 return ret;
69 }
70
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080071 base::TrimWhitespaceASCII(s_reserved_ebs,
72 base::TRIM_TRAILING,
73 &s_reserved_ebs);
74 base::TrimWhitespaceASCII(s_eb_size, base::TRIM_TRAILING, &s_eb_size);
75
76 uint64_t reserved_ebs, eb_size;
77 if (!base::StringToUint64(s_reserved_ebs, &reserved_ebs)) {
78 LOG(ERROR) << "Cannot parse reserved_ebs: " << s_reserved_ebs;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080079 return ret;
80 }
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080081 if (!base::StringToUint64(s_eb_size, &eb_size)) {
82 LOG(ERROR) << "Cannot parse usable_eb_size: " << s_eb_size;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080083 return ret;
84 }
85
86 ret.reset(new UbiVolumeInfo);
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080087 ret->reserved_ebs = reserved_ebs;
88 ret->eraseblock_size = eb_size;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080089 return ret;
90}
91
92} // namespace
93
94namespace chromeos_update_engine {
95
96MtdFileDescriptor::MtdFileDescriptor()
97 : read_ctx_(nullptr, &mtd_read_close),
98 write_ctx_(nullptr, &mtd_write_close) {}
99
100bool MtdFileDescriptor::IsMtd(const char* path) {
101 uint64_t size;
102 return mtd_node_info(path, &size, nullptr, nullptr) == 0;
103}
104
105bool MtdFileDescriptor::Open(const char* path, int flags, mode_t mode) {
106 // This File Descriptor does not support read and write.
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800107 TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
108 // But we need to open the underlying file descriptor in O_RDWR mode because
109 // during write, we need to read back to verify the write actually sticks or
110 // we have to skip the block. That job is done by mtdutils library.
111 if ((flags & O_ACCMODE) == O_WRONLY) {
112 flags &= ~O_ACCMODE;
113 flags |= O_RDWR;
114 }
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800115 TEST_AND_RETURN_FALSE(
116 EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
117
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800118 if ((flags & O_ACCMODE) == O_RDWR) {
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800119 write_ctx_.reset(mtd_write_descriptor(fd_, path));
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800120 nr_written_ = 0;
121 } else {
122 read_ctx_.reset(mtd_read_descriptor(fd_, path));
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800123 }
124
125 if (!read_ctx_ && !write_ctx_) {
126 Close();
127 return false;
128 }
129
130 return true;
131}
132
133bool MtdFileDescriptor::Open(const char* path, int flags) {
134 mode_t cur = umask(022);
135 umask(cur);
136 return Open(path, flags, 0777 & ~cur);
137}
138
139ssize_t MtdFileDescriptor::Read(void* buf, size_t count) {
140 CHECK(read_ctx_);
141 return mtd_read_data(read_ctx_.get(), static_cast<char*>(buf), count);
142}
143
144ssize_t MtdFileDescriptor::Write(const void* buf, size_t count) {
145 CHECK(write_ctx_);
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800146 ssize_t result = mtd_write_data(write_ctx_.get(),
147 static_cast<const char*>(buf),
148 count);
149 if (result > 0) {
150 nr_written_ += result;
151 }
152 return result;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800153}
154
155off64_t MtdFileDescriptor::Seek(off64_t offset, int whence) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800156 if (write_ctx_) {
157 // Ignore seek in write mode.
158 return nr_written_;
159 }
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800160 return EintrSafeFileDescriptor::Seek(offset, whence);
161}
162
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800163bool MtdFileDescriptor::Close() {
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800164 read_ctx_.reset();
165 write_ctx_.reset();
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800166 return EintrSafeFileDescriptor::Close();
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800167}
168
169bool UbiFileDescriptor::IsUbi(const char* path) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800170 base::FilePath device_node(path);
171 base::FilePath ubi_name(device_node.BaseName());
Alex Vakulenko6a9d3492015-06-15 12:53:22 -0700172 TEST_AND_RETURN_FALSE(
173 base::StartsWithASCII(ubi_name.MaybeAsASCII(), "ubi", true));
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800174
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800175 return static_cast<bool>(GetUbiVolumeInfo(path));
176}
177
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800178bool UbiFileDescriptor::Open(const char* path, int flags, mode_t mode) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800179 std::unique_ptr<UbiVolumeInfo> info = GetUbiVolumeInfo(path);
180 if (!info) {
181 return false;
182 }
183
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800184 // This File Descriptor does not support read and write.
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800185 TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800186 TEST_AND_RETURN_FALSE(
187 EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
188
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800189 usable_eb_blocks_ = info->reserved_ebs;
190 eraseblock_size_ = info->eraseblock_size;
191 volume_size_ = usable_eb_blocks_ * eraseblock_size_;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800192
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800193 if ((flags & O_ACCMODE) == O_WRONLY) {
194 // It's best to use volume update ioctl so that UBI layer will mark the
195 // volume as being updated, and only clear that mark if the update is
196 // successful. We will need to pad to the whole volume size at close.
197 uint64_t vsize = volume_size_;
198 if (ioctl(fd_, UBI_IOCVOLUP, &vsize) != 0) {
199 PLOG(ERROR) << "Cannot issue volume update ioctl";
200 EintrSafeFileDescriptor::Close();
201 return false;
202 }
203 mode_ = kWriteOnly;
204 nr_written_ = 0;
205 } else {
206 mode_ = kReadOnly;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800207 }
208
209 return true;
210}
211
212bool UbiFileDescriptor::Open(const char* path, int flags) {
213 mode_t cur = umask(022);
214 umask(cur);
215 return Open(path, flags, 0777 & ~cur);
216}
217
218ssize_t UbiFileDescriptor::Read(void* buf, size_t count) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800219 CHECK(mode_ == kReadOnly);
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800220 return EintrSafeFileDescriptor::Read(buf, count);
221}
222
223ssize_t UbiFileDescriptor::Write(const void* buf, size_t count) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800224 CHECK(mode_ == kWriteOnly);
225 ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, count);
226 if (nr_chunk >= 0) {
227 nr_written_ += nr_chunk;
228 }
229 return nr_chunk;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800230}
231
232off64_t UbiFileDescriptor::Seek(off64_t offset, int whence) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800233 if (mode_ == kWriteOnly) {
234 // Ignore seek in write mode.
235 return nr_written_;
236 }
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800237 return EintrSafeFileDescriptor::Seek(offset, whence);
238}
239
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800240bool UbiFileDescriptor::Close() {
241 bool pad_ok = true;
242 if (IsOpen() && mode_ == kWriteOnly) {
243 char buf[1024];
244 memset(buf, 0xFF, sizeof(buf));
245 while (nr_written_ < volume_size_) {
246 // We have written less than the whole volume. In order for us to clear
247 // the update marker, we need to fill the rest. It is recommended to fill
248 // UBI writes with 0xFF.
249 uint64_t to_write = volume_size_ - nr_written_;
250 if (to_write > sizeof(buf)) {
251 to_write = sizeof(buf);
252 }
253 ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, to_write);
254 if (nr_chunk < 0) {
255 LOG(ERROR) << "Cannot 0xFF-pad before closing.";
256 // There is an error, but we can't really do any meaningful thing here.
257 pad_ok = false;
258 break;
259 }
260 nr_written_ += nr_chunk;
261 }
262 }
263 return EintrSafeFileDescriptor::Close() && pad_ok;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800264}
265
266} // namespace chromeos_update_engine