1 // Copyright (C) 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "writer_base.h"
16 
17 #include <fcntl.h>
18 #include <linux/fs.h>
19 #include <sys/ioctl.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 
23 #include <android-base/logging.h>
24 #include "snapshot_reader.h"
25 
26 // The info messages here are spammy, but as useful for update_engine. Disable
27 // them when running on the host.
28 #ifdef __ANDROID__
29 #define LOG_INFO LOG(INFO)
30 #else
31 #define LOG_INFO LOG(VERBOSE)
32 #endif
33 
34 namespace android {
35 namespace snapshot {
36 
37 using android::base::borrowed_fd;
38 using android::base::unique_fd;
39 
40 namespace {
GetFdPath(borrowed_fd fd)41 std::string GetFdPath(borrowed_fd fd) {
42     const auto fd_path = "/proc/self/fd/" + std::to_string(fd.get());
43     std::string file_path(512, '\0');
44     const auto err = readlink(fd_path.c_str(), file_path.data(), file_path.size());
45     if (err <= 0) {
46         PLOG(ERROR) << "Failed to determine path for fd " << fd.get();
47         file_path.clear();
48     } else {
49         file_path.resize(err);
50     }
51     return file_path;
52 }
53 }  // namespace
54 
CowWriterBase(const CowOptions & options,unique_fd && fd)55 CowWriterBase::CowWriterBase(const CowOptions& options, unique_fd&& fd)
56     : options_(options), fd_(std::move(fd)) {}
57 
InitFd()58 bool CowWriterBase::InitFd() {
59     if (fd_.get() < 0) {
60         fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
61         if (fd_ < 0) {
62             PLOG(ERROR) << "open /dev/null failed";
63             return false;
64         }
65         is_dev_null_ = true;
66         return true;
67     }
68 
69     struct stat stat {};
70     if (fstat(fd_.get(), &stat) < 0) {
71         PLOG(ERROR) << "fstat failed";
72         return false;
73     }
74     const auto file_path = GetFdPath(fd_);
75     is_block_device_ = S_ISBLK(stat.st_mode);
76     if (is_block_device_) {
77         uint64_t size_in_bytes = 0;
78         if (ioctl(fd_.get(), BLKGETSIZE64, &size_in_bytes)) {
79             PLOG(ERROR) << "Failed to get total size for: " << fd_.get();
80             return false;
81         }
82         cow_image_size_ = size_in_bytes;
83         LOG_INFO << "COW image " << file_path << " has size " << size_in_bytes;
84     } else {
85         LOG_INFO << "COW image " << file_path
86                  << " is not a block device, assuming unlimited space.";
87     }
88     return true;
89 }
90 
AddCopy(uint64_t new_block,uint64_t old_block,uint64_t num_blocks)91 bool CowWriterBase::AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
92     CHECK(num_blocks != 0);
93 
94     for (size_t i = 0; i < num_blocks; i++) {
95         if (!ValidateNewBlock(new_block + i)) {
96             return false;
97         }
98     }
99 
100     return EmitCopy(new_block, old_block, num_blocks);
101 }
102 
AddRawBlocks(uint64_t new_block_start,const void * data,size_t size)103 bool CowWriterBase::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
104     if (size % options_.block_size != 0) {
105         LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
106                    << options_.block_size;
107         return false;
108     }
109 
110     uint64_t num_blocks = size / options_.block_size;
111     uint64_t last_block = new_block_start + num_blocks - 1;
112     if (!ValidateNewBlock(last_block)) {
113         return false;
114     }
115     return EmitRawBlocks(new_block_start, data, size);
116 }
117 
AddXorBlocks(uint32_t new_block_start,const void * data,size_t size,uint32_t old_block,uint16_t offset)118 bool CowWriterBase::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
119                                  uint32_t old_block, uint16_t offset) {
120     if (size % options_.block_size != 0) {
121         LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
122                    << options_.block_size;
123         return false;
124     }
125 
126     uint64_t num_blocks = size / options_.block_size;
127     uint64_t last_block = new_block_start + num_blocks - 1;
128     if (!ValidateNewBlock(last_block)) {
129         return false;
130     }
131     if (offset >= options_.block_size) {
132         LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
133                    << options_.block_size;
134     }
135     return EmitXorBlocks(new_block_start, data, size, old_block, offset);
136 }
137 
AddZeroBlocks(uint64_t new_block_start,uint64_t num_blocks)138 bool CowWriterBase::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
139     uint64_t last_block = new_block_start + num_blocks - 1;
140     if (!ValidateNewBlock(last_block)) {
141         return false;
142     }
143     return EmitZeroBlocks(new_block_start, num_blocks);
144 }
145 
AddLabel(uint64_t label)146 bool CowWriterBase::AddLabel(uint64_t label) {
147     return EmitLabel(label);
148 }
149 
AddSequenceData(size_t num_ops,const uint32_t * data)150 bool CowWriterBase::AddSequenceData(size_t num_ops, const uint32_t* data) {
151     return EmitSequenceData(num_ops, data);
152 }
153 
ValidateNewBlock(uint64_t new_block)154 bool CowWriterBase::ValidateNewBlock(uint64_t new_block) {
155     if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
156         LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
157                    << options_.max_blocks.value();
158         return false;
159     }
160     return true;
161 }
162 
OpenReader()163 std::unique_ptr<ICowReader> CowWriterBase::OpenReader() {
164     unique_fd cow_fd(fcntl(fd_.get(), F_DUPFD | F_DUPFD_CLOEXEC, 0));
165     if (cow_fd < 0) {
166         PLOG(ERROR) << "CowWriterV2::OpenReander: dup COW device";
167         return nullptr;
168     }
169 
170     auto cow = std::make_unique<CowReader>();
171     if (!cow->Parse(std::move(cow_fd))) {
172         LOG(ERROR) << "CowWriterV2::OpenReader: unable to read COW";
173         return nullptr;
174     }
175     return cow;
176 }
177 
OpenFileDescriptor(const std::optional<std::string> & source_device)178 std::unique_ptr<chromeos_update_engine::FileDescriptor> CowWriterBase::OpenFileDescriptor(
179         const std::optional<std::string>& source_device) {
180     auto reader = OpenReader();
181     if (!reader) {
182         return nullptr;
183     }
184 
185     std::optional<uint64_t> block_dev_size;
186     if (options_.max_blocks) {
187         block_dev_size = {*options_.max_blocks * options_.block_size};
188     }
189 
190     return std::make_unique<CompressedSnapshotReader>(std::move(reader), source_device,
191                                                       block_dev_size);
192 }
193 
Sync()194 bool CowWriterBase::Sync() {
195     if (is_dev_null_) {
196         return true;
197     }
198     if (fsync(fd_.get()) < 0) {
199         PLOG(ERROR) << "fsync failed";
200         return false;
201     }
202     return true;
203 }
204 
205 }  // namespace snapshot
206 }  // namespace android
207