1 // 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 // 16 17 #include "update_engine/payload_consumer/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> 25 #include <vector> 26 27 #include <base/files/file_path.h> 28 #include <base/strings/string_number_conversions.h> 29 #include <base/strings/string_util.h> 30 #include <base/strings/stringprintf.h> 31 32 #include "update_engine/common/subprocess.h" 33 #include "update_engine/common/utils.h" 34 35 using std::string; 36 using std::vector; 37 38 namespace { 39 40 static const char kSysfsClassUbi[] = "/sys/class/ubi/"; 41 static const char kUsableEbSize[] = "/usable_eb_size"; 42 static const char kReservedEbs[] = "/reserved_ebs"; 43 44 using chromeos_update_engine::UbiVolumeInfo; 45 using chromeos_update_engine::utils::ReadFile; 46 47 // Return a UbiVolumeInfo pointer if |path| is a UBI volume. Otherwise, return 48 // a null unique pointer. 49 std::unique_ptr<UbiVolumeInfo> GetUbiVolumeInfo(const string& path) { 50 base::FilePath device_node(path); 51 base::FilePath ubi_name(device_node.BaseName()); 52 53 string sysfs_node(kSysfsClassUbi); 54 sysfs_node.append(ubi_name.MaybeAsASCII()); 55 56 std::unique_ptr<UbiVolumeInfo> ret; 57 58 // Obtain volume info from sysfs. 59 string s_reserved_ebs; 60 if (!ReadFile(sysfs_node + kReservedEbs, &s_reserved_ebs)) { 61 LOG(ERROR) << "Cannot read " << sysfs_node + kReservedEbs; 62 return ret; 63 } 64 string s_eb_size; 65 if (!ReadFile(sysfs_node + kUsableEbSize, &s_eb_size)) { 66 LOG(ERROR) << "Cannot read " << sysfs_node + kUsableEbSize; 67 return ret; 68 } 69 70 base::TrimWhitespaceASCII(s_reserved_ebs, 71 base::TRIM_TRAILING, 72 &s_reserved_ebs); 73 base::TrimWhitespaceASCII(s_eb_size, base::TRIM_TRAILING, &s_eb_size); 74 75 uint64_t reserved_ebs, eb_size; 76 if (!base::StringToUint64(s_reserved_ebs, &reserved_ebs)) { 77 LOG(ERROR) << "Cannot parse reserved_ebs: " << s_reserved_ebs; 78 return ret; 79 } 80 if (!base::StringToUint64(s_eb_size, &eb_size)) { 81 LOG(ERROR) << "Cannot parse usable_eb_size: " << s_eb_size; 82 return ret; 83 } 84 85 ret.reset(new UbiVolumeInfo); 86 ret->reserved_ebs = reserved_ebs; 87 ret->eraseblock_size = eb_size; 88 return ret; 89 } 90 91 } // namespace 92 93 namespace chromeos_update_engine { 94 95 MtdFileDescriptor::MtdFileDescriptor() 96 : read_ctx_(nullptr, &mtd_read_close), 97 write_ctx_(nullptr, &mtd_write_close) {} 98 99 bool MtdFileDescriptor::IsMtd(const char* path) { 100 uint64_t size; 101 return mtd_node_info(path, &size, nullptr, nullptr) == 0; 102 } 103 104 bool MtdFileDescriptor::Open(const char* path, int flags, mode_t mode) { 105 // This File Descriptor does not support read and write. 106 TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR); 107 // But we need to open the underlying file descriptor in O_RDWR mode because 108 // during write, we need to read back to verify the write actually sticks or 109 // we have to skip the block. That job is done by mtdutils library. 110 if ((flags & O_ACCMODE) == O_WRONLY) { 111 flags &= ~O_ACCMODE; 112 flags |= O_RDWR; 113 } 114 TEST_AND_RETURN_FALSE( 115 EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode)); 116 117 if ((flags & O_ACCMODE) == O_RDWR) { 118 write_ctx_.reset(mtd_write_descriptor(fd_, path)); 119 nr_written_ = 0; 120 } else { 121 read_ctx_.reset(mtd_read_descriptor(fd_, path)); 122 } 123 124 if (!read_ctx_ && !write_ctx_) { 125 Close(); 126 return false; 127 } 128 129 return true; 130 } 131 132 bool MtdFileDescriptor::Open(const char* path, int flags) { 133 mode_t cur = umask(022); 134 umask(cur); 135 return Open(path, flags, 0777 & ~cur); 136 } 137 138 ssize_t MtdFileDescriptor::Read(void* buf, size_t count) { 139 CHECK(read_ctx_); 140 return mtd_read_data(read_ctx_.get(), static_cast<char*>(buf), count); 141 } 142 143 ssize_t MtdFileDescriptor::Write(const void* buf, size_t count) { 144 CHECK(write_ctx_); 145 ssize_t result = mtd_write_data(write_ctx_.get(), 146 static_cast<const char*>(buf), 147 count); 148 if (result > 0) { 149 nr_written_ += result; 150 } 151 return result; 152 } 153 154 off64_t MtdFileDescriptor::Seek(off64_t offset, int whence) { 155 if (write_ctx_) { 156 // Ignore seek in write mode. 157 return nr_written_; 158 } 159 return EintrSafeFileDescriptor::Seek(offset, whence); 160 } 161 162 bool MtdFileDescriptor::Close() { 163 read_ctx_.reset(); 164 write_ctx_.reset(); 165 return EintrSafeFileDescriptor::Close(); 166 } 167 168 bool UbiFileDescriptor::IsUbi(const char* path) { 169 base::FilePath device_node(path); 170 base::FilePath ubi_name(device_node.BaseName()); 171 TEST_AND_RETURN_FALSE(base::StartsWith(ubi_name.MaybeAsASCII(), "ubi", 172 base::CompareCase::SENSITIVE)); 173 174 return static_cast<bool>(GetUbiVolumeInfo(path)); 175 } 176 177 bool UbiFileDescriptor::Open(const char* path, int flags, mode_t mode) { 178 std::unique_ptr<UbiVolumeInfo> info = GetUbiVolumeInfo(path); 179 if (!info) { 180 return false; 181 } 182 183 // This File Descriptor does not support read and write. 184 TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR); 185 TEST_AND_RETURN_FALSE( 186 EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode)); 187 188 usable_eb_blocks_ = info->reserved_ebs; 189 eraseblock_size_ = info->eraseblock_size; 190 volume_size_ = usable_eb_blocks_ * eraseblock_size_; 191 192 if ((flags & O_ACCMODE) == O_WRONLY) { 193 // It's best to use volume update ioctl so that UBI layer will mark the 194 // volume as being updated, and only clear that mark if the update is 195 // successful. We will need to pad to the whole volume size at close. 196 uint64_t vsize = volume_size_; 197 if (ioctl(fd_, UBI_IOCVOLUP, &vsize) != 0) { 198 PLOG(ERROR) << "Cannot issue volume update ioctl"; 199 EintrSafeFileDescriptor::Close(); 200 return false; 201 } 202 mode_ = kWriteOnly; 203 nr_written_ = 0; 204 } else { 205 mode_ = kReadOnly; 206 } 207 208 return true; 209 } 210 211 bool UbiFileDescriptor::Open(const char* path, int flags) { 212 mode_t cur = umask(022); 213 umask(cur); 214 return Open(path, flags, 0777 & ~cur); 215 } 216 217 ssize_t UbiFileDescriptor::Read(void* buf, size_t count) { 218 CHECK(mode_ == kReadOnly); 219 return EintrSafeFileDescriptor::Read(buf, count); 220 } 221 222 ssize_t UbiFileDescriptor::Write(const void* buf, size_t count) { 223 CHECK(mode_ == kWriteOnly); 224 ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, count); 225 if (nr_chunk >= 0) { 226 nr_written_ += nr_chunk; 227 } 228 return nr_chunk; 229 } 230 231 off64_t UbiFileDescriptor::Seek(off64_t offset, int whence) { 232 if (mode_ == kWriteOnly) { 233 // Ignore seek in write mode. 234 return nr_written_; 235 } 236 return EintrSafeFileDescriptor::Seek(offset, whence); 237 } 238 239 bool UbiFileDescriptor::Close() { 240 bool pad_ok = true; 241 if (IsOpen() && mode_ == kWriteOnly) { 242 char buf[1024]; 243 memset(buf, 0xFF, sizeof(buf)); 244 while (nr_written_ < volume_size_) { 245 // We have written less than the whole volume. In order for us to clear 246 // the update marker, we need to fill the rest. It is recommended to fill 247 // UBI writes with 0xFF. 248 uint64_t to_write = volume_size_ - nr_written_; 249 if (to_write > sizeof(buf)) { 250 to_write = sizeof(buf); 251 } 252 ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, to_write); 253 if (nr_chunk < 0) { 254 LOG(ERROR) << "Cannot 0xFF-pad before closing."; 255 // There is an error, but we can't really do any meaningful thing here. 256 pad_ok = false; 257 break; 258 } 259 nr_written_ += nr_chunk; 260 } 261 } 262 return EintrSafeFileDescriptor::Close() && pad_ok; 263 } 264 265 } // namespace chromeos_update_engine 266