Home | History | Annotate | Download | only in payload_consumer
      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 <sys/ioctl.h>
     22 #include <sys/stat.h>
     23 #include <sys/types.h>
     24 
     25 #include <memory>
     26 #include <string>
     27 
     28 #include <base/files/file_path.h>
     29 #include <base/strings/string_number_conversions.h>
     30 #include <base/strings/string_util.h>
     31 #include <base/strings/stringprintf.h>
     32 
     33 #include "update_engine/common/subprocess.h"
     34 #include "update_engine/common/utils.h"
     35 
     36 using std::string;
     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