1 /*
2  * Copyright (C) 2007 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 "roots.h"
18 
19 #include <ctype.h>
20 #include <fcntl.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/mount.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 
30 #include <algorithm>
31 #include <string>
32 #include <vector>
33 
34 #include <android-base/logging.h>
35 #include <android-base/properties.h>
36 #include <android-base/stringprintf.h>
37 #include <android-base/unique_fd.h>
38 #include <cryptfs.h>
39 #include <ext4_utils/wipe.h>
40 #include <fs_mgr.h>
41 
42 #include "mounts.h"
43 
44 static struct fstab* fstab = nullptr;
45 
46 extern struct selabel_handle* sehandle;
47 
load_volume_table()48 void load_volume_table() {
49   fstab = fs_mgr_read_fstab_default();
50   if (!fstab) {
51     LOG(ERROR) << "Failed to read default fstab";
52     return;
53   }
54 
55   int ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk");
56   if (ret == -1) {
57     LOG(ERROR) << "Failed to add /tmp entry to fstab";
58     fs_mgr_free_fstab(fstab);
59     fstab = nullptr;
60     return;
61   }
62 
63   printf("recovery filesystem table\n");
64   printf("=========================\n");
65   for (int i = 0; i < fstab->num_entries; ++i) {
66     const Volume* v = &fstab->recs[i];
67     printf("  %d %s %s %s %lld\n", i, v->mount_point, v->fs_type, v->blk_device, v->length);
68   }
69   printf("\n");
70 }
71 
volume_for_mount_point(const std::string & mount_point)72 Volume* volume_for_mount_point(const std::string& mount_point) {
73   return fs_mgr_get_entry_for_mount_point(fstab, mount_point);
74 }
75 
76 // Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match
77 // only, so it attempts the prefixes recursively (e.g. "/cache/recovery/last_log",
78 // "/cache/recovery", "/cache", "/" for a given path of "/cache/recovery/last_log") and returns the
79 // first match or nullptr.
volume_for_path(const char * path)80 static Volume* volume_for_path(const char* path) {
81   if (path == nullptr || path[0] == '\0') return nullptr;
82   std::string str(path);
83   while (true) {
84     Volume* result = fs_mgr_get_entry_for_mount_point(fstab, str);
85     if (result != nullptr || str == "/") {
86       return result;
87     }
88     size_t slash = str.find_last_of('/');
89     if (slash == std::string::npos) return nullptr;
90     if (slash == 0) {
91       str = "/";
92     } else {
93       str = str.substr(0, slash);
94     }
95   }
96   return nullptr;
97 }
98 
99 // Mount the volume specified by path at the given mount_point.
ensure_path_mounted_at(const char * path,const char * mount_point)100 int ensure_path_mounted_at(const char* path, const char* mount_point) {
101   Volume* v = volume_for_path(path);
102   if (v == nullptr) {
103     LOG(ERROR) << "unknown volume for path [" << path << "]";
104     return -1;
105   }
106   if (strcmp(v->fs_type, "ramdisk") == 0) {
107     // The ramdisk is always mounted.
108     return 0;
109   }
110 
111   if (!scan_mounted_volumes()) {
112     LOG(ERROR) << "Failed to scan mounted volumes";
113     return -1;
114   }
115 
116   if (!mount_point) {
117     mount_point = v->mount_point;
118   }
119 
120   const MountedVolume* mv = find_mounted_volume_by_mount_point(mount_point);
121   if (mv != nullptr) {
122     // Volume is already mounted.
123     return 0;
124   }
125 
126   mkdir(mount_point, 0755);  // in case it doesn't already exist
127 
128   if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "squashfs") == 0 ||
129       strcmp(v->fs_type, "vfat") == 0) {
130     int result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options);
131     if (result == -1 && fs_mgr_is_formattable(v)) {
132       PLOG(ERROR) << "Failed to mount " << mount_point << "; formatting";
133       bool crypt_footer = fs_mgr_is_encryptable(v) && !strcmp(v->key_loc, "footer");
134       if (fs_mgr_do_format(v, crypt_footer) == 0) {
135         result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options);
136       } else {
137         PLOG(ERROR) << "Failed to format " << mount_point;
138         return -1;
139       }
140     }
141 
142     if (result == -1) {
143       PLOG(ERROR) << "Failed to mount " << mount_point;
144       return -1;
145     }
146     return 0;
147   }
148 
149   LOG(ERROR) << "unknown fs_type \"" << v->fs_type << "\" for " << mount_point;
150   return -1;
151 }
152 
ensure_path_mounted(const char * path)153 int ensure_path_mounted(const char* path) {
154   // Mount at the default mount point.
155   return ensure_path_mounted_at(path, nullptr);
156 }
157 
ensure_path_unmounted(const char * path)158 int ensure_path_unmounted(const char* path) {
159   const Volume* v = volume_for_path(path);
160   if (v == nullptr) {
161     LOG(ERROR) << "unknown volume for path [" << path << "]";
162     return -1;
163   }
164   if (strcmp(v->fs_type, "ramdisk") == 0) {
165     // The ramdisk is always mounted; you can't unmount it.
166     return -1;
167   }
168 
169   if (!scan_mounted_volumes()) {
170     LOG(ERROR) << "Failed to scan mounted volumes";
171     return -1;
172   }
173 
174   MountedVolume* mv = find_mounted_volume_by_mount_point(v->mount_point);
175   if (mv == nullptr) {
176     // Volume is already unmounted.
177     return 0;
178   }
179 
180   return unmount_mounted_volume(mv);
181 }
182 
exec_cmd(const std::vector<std::string> & args)183 static int exec_cmd(const std::vector<std::string>& args) {
184   CHECK_NE(static_cast<size_t>(0), args.size());
185 
186   std::vector<char*> argv(args.size());
187   std::transform(args.cbegin(), args.cend(), argv.begin(),
188                  [](const std::string& arg) { return const_cast<char*>(arg.c_str()); });
189   argv.push_back(nullptr);
190 
191   pid_t child;
192   if ((child = fork()) == 0) {
193     execv(argv[0], argv.data());
194     _exit(EXIT_FAILURE);
195   }
196 
197   int status;
198   waitpid(child, &status, 0);
199   if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
200     LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status);
201   }
202   return WEXITSTATUS(status);
203 }
204 
get_file_size(int fd,uint64_t reserve_len)205 static int64_t get_file_size(int fd, uint64_t reserve_len) {
206   struct stat buf;
207   int ret = fstat(fd, &buf);
208   if (ret) return 0;
209 
210   int64_t computed_size;
211   if (S_ISREG(buf.st_mode)) {
212     computed_size = buf.st_size - reserve_len;
213   } else if (S_ISBLK(buf.st_mode)) {
214     uint64_t block_device_size = get_block_device_size(fd);
215     if (block_device_size < reserve_len ||
216         block_device_size > std::numeric_limits<int64_t>::max()) {
217       computed_size = 0;
218     } else {
219       computed_size = block_device_size - reserve_len;
220     }
221   } else {
222     computed_size = 0;
223   }
224 
225   return computed_size;
226 }
227 
format_volume(const char * volume,const char * directory)228 int format_volume(const char* volume, const char* directory) {
229   const Volume* v = volume_for_path(volume);
230   if (v == nullptr) {
231     LOG(ERROR) << "unknown volume \"" << volume << "\"";
232     return -1;
233   }
234   if (strcmp(v->fs_type, "ramdisk") == 0) {
235     LOG(ERROR) << "can't format_volume \"" << volume << "\"";
236     return -1;
237   }
238   if (strcmp(v->mount_point, volume) != 0) {
239     LOG(ERROR) << "can't give path \"" << volume << "\" to format_volume";
240     return -1;
241   }
242   if (ensure_path_unmounted(volume) != 0) {
243     LOG(ERROR) << "format_volume: Failed to unmount \"" << v->mount_point << "\"";
244     return -1;
245   }
246   if (strcmp(v->fs_type, "ext4") != 0 && strcmp(v->fs_type, "f2fs") != 0) {
247     LOG(ERROR) << "format_volume: fs_type \"" << v->fs_type << "\" unsupported";
248     return -1;
249   }
250 
251   // If there's a key_loc that looks like a path, it should be a block device for storing encryption
252   // metadata. Wipe it too.
253   if (v->key_loc != nullptr && v->key_loc[0] == '/') {
254     LOG(INFO) << "Wiping " << v->key_loc;
255     int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644);
256     if (fd == -1) {
257       PLOG(ERROR) << "format_volume: Failed to open " << v->key_loc;
258       return -1;
259     }
260     wipe_block_device(fd, get_file_size(fd));
261     close(fd);
262   }
263 
264   int64_t length = 0;
265   if (v->length > 0) {
266     length = v->length;
267   } else if (v->length < 0 ||
268              (v->key_loc != nullptr && strcmp(v->key_loc, "footer") == 0)) {
269     android::base::unique_fd fd(open(v->blk_device, O_RDONLY));
270     if (fd == -1) {
271       PLOG(ERROR) << "format_volume: failed to open " << v->blk_device;
272       return -1;
273     }
274     length =
275         get_file_size(fd.get(), v->length ? -v->length : CRYPT_FOOTER_OFFSET);
276     if (length <= 0) {
277       LOG(ERROR) << "get_file_size: invalid size " << length << " for "
278                  << v->blk_device;
279       return -1;
280     }
281   }
282 
283   if (strcmp(v->fs_type, "ext4") == 0) {
284     static constexpr int kBlockSize = 4096;
285     std::vector<std::string> mke2fs_args = {
286       "/sbin/mke2fs_static", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize),
287     };
288 
289     int raid_stride = v->logical_blk_size / kBlockSize;
290     int raid_stripe_width = v->erase_blk_size / kBlockSize;
291     // stride should be the max of 8KB and logical block size
292     if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) {
293       raid_stride = 8192 / kBlockSize;
294     }
295     if (v->erase_blk_size != 0 && v->logical_blk_size != 0) {
296       mke2fs_args.push_back("-E");
297       mke2fs_args.push_back(
298           android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, raid_stripe_width));
299     }
300     mke2fs_args.push_back(v->blk_device);
301     if (length != 0) {
302       mke2fs_args.push_back(std::to_string(length / kBlockSize));
303     }
304 
305     int result = exec_cmd(mke2fs_args);
306     if (result == 0 && directory != nullptr) {
307       std::vector<std::string> e2fsdroid_args = {
308         "/sbin/e2fsdroid_static",
309         "-e",
310         "-f",
311         directory,
312         "-a",
313         volume,
314         v->blk_device,
315       };
316       result = exec_cmd(e2fsdroid_args);
317     }
318 
319     if (result != 0) {
320       PLOG(ERROR) << "format_volume: Failed to make ext4 on " << v->blk_device;
321       return -1;
322     }
323     return 0;
324   }
325 
326   // Has to be f2fs because we checked earlier.
327   static constexpr int kSectorSize = 4096;
328   std::string cmd("/sbin/mkfs.f2fs");
329   // clang-format off
330   std::vector<std::string> make_f2fs_cmd = {
331     cmd,
332     "-d1",
333     "-f",
334     "-O", "encrypt",
335     "-O", "quota",
336     "-O", "verity",
337     "-w", std::to_string(kSectorSize),
338     v->blk_device,
339   };
340   // clang-format on
341   if (length >= kSectorSize) {
342     make_f2fs_cmd.push_back(std::to_string(length / kSectorSize));
343   }
344 
345   int result = exec_cmd(make_f2fs_cmd);
346   if (result == 0 && directory != nullptr) {
347     cmd = "/sbin/sload.f2fs";
348     // clang-format off
349     std::vector<std::string> sload_f2fs_cmd = {
350       cmd,
351       "-f", directory,
352       "-t", volume,
353       v->blk_device,
354     };
355     // clang-format on
356     result = exec_cmd(sload_f2fs_cmd);
357   }
358   if (result != 0) {
359     PLOG(ERROR) << "format_volume: Failed " << cmd << " on " << v->blk_device;
360     return -1;
361   }
362   return 0;
363 }
364 
format_volume(const char * volume)365 int format_volume(const char* volume) {
366   return format_volume(volume, nullptr);
367 }
368 
setup_install_mounts()369 int setup_install_mounts() {
370   if (fstab == nullptr) {
371     LOG(ERROR) << "can't set up install mounts: no fstab loaded";
372     return -1;
373   }
374   for (int i = 0; i < fstab->num_entries; ++i) {
375     const Volume* v = fstab->recs + i;
376 
377     // We don't want to do anything with "/".
378     if (strcmp(v->mount_point, "/") == 0) {
379       continue;
380     }
381 
382     if (strcmp(v->mount_point, "/tmp") == 0 || strcmp(v->mount_point, "/cache") == 0) {
383       if (ensure_path_mounted(v->mount_point) != 0) {
384         LOG(ERROR) << "Failed to mount " << v->mount_point;
385         return -1;
386       }
387     } else {
388       if (ensure_path_unmounted(v->mount_point) != 0) {
389         LOG(ERROR) << "Failed to unmount " << v->mount_point;
390         return -1;
391       }
392     }
393   }
394   return 0;
395 }
396