blob: 5d940cbbbe1fb7edcfe0fbb1b308bf35dc1396da [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
Alex Deymo39910dc2015-11-09 17:04:30 -080017#include "update_engine/payload_consumer/mtd_file_descriptor.h"
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080018
19#include <fcntl.h>
20#include <mtd/ubi-user.h>
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080021#include <sys/ioctl.h>
22#include <sys/stat.h>
23#include <sys/types.h>
Amin Hassanicd7edbe2017-09-18 17:05:02 -070024
25#include <memory>
26#include <string>
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080027
28#include <base/files/file_path.h>
29#include <base/strings/string_number_conversions.h>
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080030#include <base/strings/string_util.h>
31#include <base/strings/stringprintf.h>
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080032
Alex Deymoe67a8bb2015-11-12 14:58:58 -080033#include "update_engine/common/subprocess.h"
Alex Deymo39910dc2015-11-09 17:04:30 -080034#include "update_engine/common/utils.h"
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080035
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080036using std::string;
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080037
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
44using chromeos_update_engine::UbiVolumeInfo;
45using chromeos_update_engine::utils::ReadFile;
46
47// Return a UbiVolumeInfo pointer if |path| is a UBI volume. Otherwise, return
48// a null unique pointer.
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080049std::unique_ptr<UbiVolumeInfo> GetUbiVolumeInfo(const string& path) {
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080050 base::FilePath device_node(path);
51 base::FilePath ubi_name(device_node.BaseName());
52
Alex Deymo39910dc2015-11-09 17:04:30 -080053 string sysfs_node(kSysfsClassUbi);
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080054 sysfs_node.append(ubi_name.MaybeAsASCII());
55
56 std::unique_ptr<UbiVolumeInfo> ret;
57
58 // Obtain volume info from sysfs.
Alex Deymo39910dc2015-11-09 17:04:30 -080059 string s_reserved_ebs;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080060 if (!ReadFile(sysfs_node + kReservedEbs, &s_reserved_ebs)) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080061 LOG(ERROR) << "Cannot read " << sysfs_node + kReservedEbs;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080062 return ret;
63 }
Alex Deymo39910dc2015-11-09 17:04:30 -080064 string s_eb_size;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080065 if (!ReadFile(sysfs_node + kUsableEbSize, &s_eb_size)) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080066 LOG(ERROR) << "Cannot read " << sysfs_node + kUsableEbSize;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080067 return ret;
68 }
69
Amin Hassani008c4582019-01-13 16:22:47 -080070 base::TrimWhitespaceASCII(
71 s_reserved_ebs, base::TRIM_TRAILING, &s_reserved_ebs);
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080072 base::TrimWhitespaceASCII(s_eb_size, base::TRIM_TRAILING, &s_eb_size);
73
74 uint64_t reserved_ebs, eb_size;
75 if (!base::StringToUint64(s_reserved_ebs, &reserved_ebs)) {
76 LOG(ERROR) << "Cannot parse reserved_ebs: " << s_reserved_ebs;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080077 return ret;
78 }
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080079 if (!base::StringToUint64(s_eb_size, &eb_size)) {
80 LOG(ERROR) << "Cannot parse usable_eb_size: " << s_eb_size;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080081 return ret;
82 }
83
84 ret.reset(new UbiVolumeInfo);
Nam T. Nguyena78b28c2015-03-06 22:30:12 -080085 ret->reserved_ebs = reserved_ebs;
86 ret->eraseblock_size = eb_size;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -080087 return ret;
88}
89
90} // namespace
91
92namespace chromeos_update_engine {
93
94MtdFileDescriptor::MtdFileDescriptor()
95 : read_ctx_(nullptr, &mtd_read_close),
96 write_ctx_(nullptr, &mtd_write_close) {}
97
98bool MtdFileDescriptor::IsMtd(const char* path) {
99 uint64_t size;
100 return mtd_node_info(path, &size, nullptr, nullptr) == 0;
101}
102
103bool MtdFileDescriptor::Open(const char* path, int flags, mode_t mode) {
104 // This File Descriptor does not support read and write.
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800105 TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
106 // But we need to open the underlying file descriptor in O_RDWR mode because
107 // during write, we need to read back to verify the write actually sticks or
108 // we have to skip the block. That job is done by mtdutils library.
109 if ((flags & O_ACCMODE) == O_WRONLY) {
110 flags &= ~O_ACCMODE;
111 flags |= O_RDWR;
112 }
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800113 TEST_AND_RETURN_FALSE(
114 EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
115
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800116 if ((flags & O_ACCMODE) == O_RDWR) {
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800117 write_ctx_.reset(mtd_write_descriptor(fd_, path));
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800118 nr_written_ = 0;
119 } else {
120 read_ctx_.reset(mtd_read_descriptor(fd_, path));
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800121 }
122
123 if (!read_ctx_ && !write_ctx_) {
124 Close();
125 return false;
126 }
127
128 return true;
129}
130
131bool MtdFileDescriptor::Open(const char* path, int flags) {
132 mode_t cur = umask(022);
133 umask(cur);
134 return Open(path, flags, 0777 & ~cur);
135}
136
137ssize_t MtdFileDescriptor::Read(void* buf, size_t count) {
138 CHECK(read_ctx_);
139 return mtd_read_data(read_ctx_.get(), static_cast<char*>(buf), count);
140}
141
142ssize_t MtdFileDescriptor::Write(const void* buf, size_t count) {
143 CHECK(write_ctx_);
Amin Hassani008c4582019-01-13 16:22:47 -0800144 ssize_t result =
145 mtd_write_data(write_ctx_.get(), static_cast<const char*>(buf), count);
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800146 if (result > 0) {
147 nr_written_ += result;
148 }
149 return result;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800150}
151
152off64_t MtdFileDescriptor::Seek(off64_t offset, int whence) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800153 if (write_ctx_) {
154 // Ignore seek in write mode.
155 return nr_written_;
156 }
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800157 return EintrSafeFileDescriptor::Seek(offset, whence);
158}
159
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800160bool MtdFileDescriptor::Close() {
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800161 read_ctx_.reset();
162 write_ctx_.reset();
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800163 return EintrSafeFileDescriptor::Close();
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800164}
165
166bool UbiFileDescriptor::IsUbi(const char* path) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800167 base::FilePath device_node(path);
168 base::FilePath ubi_name(device_node.BaseName());
Amin Hassani008c4582019-01-13 16:22:47 -0800169 TEST_AND_RETURN_FALSE(base::StartsWith(
170 ubi_name.MaybeAsASCII(), "ubi", base::CompareCase::SENSITIVE));
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800171
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800172 return static_cast<bool>(GetUbiVolumeInfo(path));
173}
174
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800175bool UbiFileDescriptor::Open(const char* path, int flags, mode_t mode) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800176 std::unique_ptr<UbiVolumeInfo> info = GetUbiVolumeInfo(path);
177 if (!info) {
178 return false;
179 }
180
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800181 // This File Descriptor does not support read and write.
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800182 TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800183 TEST_AND_RETURN_FALSE(
184 EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
185
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800186 usable_eb_blocks_ = info->reserved_ebs;
187 eraseblock_size_ = info->eraseblock_size;
188 volume_size_ = usable_eb_blocks_ * eraseblock_size_;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800189
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800190 if ((flags & O_ACCMODE) == O_WRONLY) {
191 // It's best to use volume update ioctl so that UBI layer will mark the
192 // volume as being updated, and only clear that mark if the update is
193 // successful. We will need to pad to the whole volume size at close.
194 uint64_t vsize = volume_size_;
195 if (ioctl(fd_, UBI_IOCVOLUP, &vsize) != 0) {
196 PLOG(ERROR) << "Cannot issue volume update ioctl";
197 EintrSafeFileDescriptor::Close();
198 return false;
199 }
200 mode_ = kWriteOnly;
201 nr_written_ = 0;
202 } else {
203 mode_ = kReadOnly;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800204 }
205
206 return true;
207}
208
209bool UbiFileDescriptor::Open(const char* path, int flags) {
210 mode_t cur = umask(022);
211 umask(cur);
212 return Open(path, flags, 0777 & ~cur);
213}
214
215ssize_t UbiFileDescriptor::Read(void* buf, size_t count) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800216 CHECK(mode_ == kReadOnly);
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800217 return EintrSafeFileDescriptor::Read(buf, count);
218}
219
220ssize_t UbiFileDescriptor::Write(const void* buf, size_t count) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800221 CHECK(mode_ == kWriteOnly);
222 ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, count);
223 if (nr_chunk >= 0) {
224 nr_written_ += nr_chunk;
225 }
226 return nr_chunk;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800227}
228
229off64_t UbiFileDescriptor::Seek(off64_t offset, int whence) {
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800230 if (mode_ == kWriteOnly) {
231 // Ignore seek in write mode.
232 return nr_written_;
233 }
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800234 return EintrSafeFileDescriptor::Seek(offset, whence);
235}
236
Nam T. Nguyena78b28c2015-03-06 22:30:12 -0800237bool UbiFileDescriptor::Close() {
238 bool pad_ok = true;
239 if (IsOpen() && mode_ == kWriteOnly) {
240 char buf[1024];
241 memset(buf, 0xFF, sizeof(buf));
242 while (nr_written_ < volume_size_) {
243 // We have written less than the whole volume. In order for us to clear
244 // the update marker, we need to fill the rest. It is recommended to fill
245 // UBI writes with 0xFF.
246 uint64_t to_write = volume_size_ - nr_written_;
247 if (to_write > sizeof(buf)) {
248 to_write = sizeof(buf);
249 }
250 ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, to_write);
251 if (nr_chunk < 0) {
252 LOG(ERROR) << "Cannot 0xFF-pad before closing.";
253 // There is an error, but we can't really do any meaningful thing here.
254 pad_ok = false;
255 break;
256 }
257 nr_written_ += nr_chunk;
258 }
259 }
260 return EintrSafeFileDescriptor::Close() && pad_ok;
Nam T. Nguyenf1d582e2014-12-08 15:07:17 -0800261}
262
263} // namespace chromeos_update_engine