blob: e747473787cb85593e826fd4cc2e7e873e20d6e8 [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
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 <errno.h>
18#include <stdlib.h>
19#include <sys/mount.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <unistd.h>
Doug Zongkerd4208f92010-09-20 12:16:13 -070023#include <ctype.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080024
Dees_Troy51a0e822012-09-05 15:24:24 -040025extern "C" {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080026#include "mtdutils/mtdutils.h"
27#include "mtdutils/mounts.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040028}
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080029#include "roots.h"
30#include "common.h"
Doug Zongkerd4208f92010-09-20 12:16:13 -070031#include "make_ext4fs.h"
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080032
Doug Zongkerd4208f92010-09-20 12:16:13 -070033static int num_volumes = 0;
34static Volume* device_volumes = NULL;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080035
Kenny Root41dda822012-03-30 20:48:34 -070036extern struct selabel_handle *sehandle;
Stephen Smalley779701d2012-02-09 14:13:23 -050037
Doug Zongker2810ced2011-02-17 15:55:21 -080038static int parse_options(char* options, Volume* volume) {
39 char* option;
Doug Zongker28ce47c2011-10-28 10:33:05 -070040 while ((option = strtok(options, ","))) {
Doug Zongker2810ced2011-02-17 15:55:21 -080041 options = NULL;
42
Dees_Troy51127312012-09-08 13:08:49 -040043 if (strncmp(option, "flags=", 6) == 0) continue;
44 if (strncmp(option, "length=", 7) == 0) {
Doug Zongker2810ced2011-02-17 15:55:21 -080045 volume->length = strtoll(option+7, NULL, 10);
46 } else {
47 LOGE("bad option \"%s\"\n", option);
48 return -1;
49 }
50 }
51 return 0;
52}
53
Doug Zongkerd4208f92010-09-20 12:16:13 -070054void load_volume_table() {
55 int alloc = 2;
Doug Zongker28ce47c2011-10-28 10:33:05 -070056 device_volumes = (Volume*)malloc(alloc * sizeof(Volume));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080057
Doug Zongkerc18eeb82010-09-21 16:49:26 -070058 // Insert an entry for /tmp, which is the ramdisk and is always mounted.
59 device_volumes[0].mount_point = "/tmp";
60 device_volumes[0].fs_type = "ramdisk";
61 device_volumes[0].device = NULL;
62 device_volumes[0].device2 = NULL;
Doug Zongker2810ced2011-02-17 15:55:21 -080063 device_volumes[0].length = 0;
Doug Zongkerc18eeb82010-09-21 16:49:26 -070064 num_volumes = 1;
65
Doug Zongkerd4208f92010-09-20 12:16:13 -070066 FILE* fstab = fopen("/etc/recovery.fstab", "r");
67 if (fstab == NULL) {
68 LOGE("failed to open /etc/recovery.fstab (%s)\n", strerror(errno));
69 return;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080070 }
Doug Zongkerd4208f92010-09-20 12:16:13 -070071
72 char buffer[1024];
73 int i;
74 while (fgets(buffer, sizeof(buffer)-1, fstab)) {
75 for (i = 0; buffer[i] && isspace(buffer[i]); ++i);
76 if (buffer[i] == '\0' || buffer[i] == '#') continue;
77
78 char* original = strdup(buffer);
79
80 char* mount_point = strtok(buffer+i, " \t\n");
81 char* fs_type = strtok(NULL, " \t\n");
82 char* device = strtok(NULL, " \t\n");
83 // lines may optionally have a second device, to use if
84 // mounting the first one fails.
Doug Zongker2810ced2011-02-17 15:55:21 -080085 char* options = NULL;
Doug Zongkerd4208f92010-09-20 12:16:13 -070086 char* device2 = strtok(NULL, " \t\n");
Doug Zongker2810ced2011-02-17 15:55:21 -080087 if (device2) {
88 if (device2[0] == '/') {
89 options = strtok(NULL, " \t\n");
90 } else {
91 options = device2;
92 device2 = NULL;
93 }
94 }
Doug Zongkerd4208f92010-09-20 12:16:13 -070095
96 if (mount_point && fs_type && device) {
97 while (num_volumes >= alloc) {
98 alloc *= 2;
Doug Zongker28ce47c2011-10-28 10:33:05 -070099 device_volumes = (Volume*)realloc(device_volumes, alloc*sizeof(Volume));
Doug Zongkerd4208f92010-09-20 12:16:13 -0700100 }
101 device_volumes[num_volumes].mount_point = strdup(mount_point);
102 device_volumes[num_volumes].fs_type = strdup(fs_type);
103 device_volumes[num_volumes].device = strdup(device);
104 device_volumes[num_volumes].device2 =
105 device2 ? strdup(device2) : NULL;
Doug Zongker2810ced2011-02-17 15:55:21 -0800106
107 device_volumes[num_volumes].length = 0;
108 if (parse_options(options, device_volumes + num_volumes) != 0) {
109 LOGE("skipping malformed recovery.fstab line: %s\n", original);
110 } else {
111 ++num_volumes;
112 }
Doug Zongkerd4208f92010-09-20 12:16:13 -0700113 } else {
114 LOGE("skipping malformed recovery.fstab line: %s\n", original);
115 }
116 free(original);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800117 }
Doug Zongkerd4208f92010-09-20 12:16:13 -0700118
119 fclose(fstab);
120
121 printf("recovery filesystem table\n");
122 printf("=========================\n");
123 for (i = 0; i < num_volumes; ++i) {
124 Volume* v = &device_volumes[i];
Doug Zongker2810ced2011-02-17 15:55:21 -0800125 printf(" %d %s %s %s %s %lld\n", i, v->mount_point, v->fs_type,
126 v->device, v->device2, v->length);
Doug Zongkerd4208f92010-09-20 12:16:13 -0700127 }
128 printf("\n");
129}
130
131Volume* volume_for_path(const char* path) {
132 int i;
133 for (i = 0; i < num_volumes; ++i) {
134 Volume* v = device_volumes+i;
135 int len = strlen(v->mount_point);
136 if (strncmp(path, v->mount_point, len) == 0 &&
137 (path[len] == '\0' || path[len] == '/')) {
138 return v;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800139 }
140 }
141 return NULL;
142}
143
Doug Zongkerd4208f92010-09-20 12:16:13 -0700144int ensure_path_mounted(const char* path) {
145 Volume* v = volume_for_path(path);
146 if (v == NULL) {
147 LOGE("unknown volume for path [%s]\n", path);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800148 return -1;
149 }
Doug Zongkerc18eeb82010-09-21 16:49:26 -0700150 if (strcmp(v->fs_type, "ramdisk") == 0) {
151 // the ramdisk is always mounted.
152 return 0;
153 }
Doug Zongkerd4208f92010-09-20 12:16:13 -0700154
155 int result;
156 result = scan_mounted_volumes();
157 if (result < 0) {
158 LOGE("failed to scan mounted volumes\n");
159 return -1;
Doug Zongker23ceeea2010-07-08 17:27:55 -0700160 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800161
Doug Zongkerd4208f92010-09-20 12:16:13 -0700162 const MountedVolume* mv =
163 find_mounted_volume_by_mount_point(v->mount_point);
164 if (mv) {
165 // volume is already mounted
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800166 return 0;
167 }
Doug Zongkerd4208f92010-09-20 12:16:13 -0700168
169 mkdir(v->mount_point, 0755); // in case it doesn't already exist
170
171 if (strcmp(v->fs_type, "yaffs2") == 0) {
172 // mount an MTD partition as a YAFFS2 filesystem.
173 mtd_scan_partitions();
174 const MtdPartition* partition;
175 partition = mtd_find_partition_by_name(v->device);
176 if (partition == NULL) {
177 LOGE("failed to find \"%s\" partition to mount at \"%s\"\n",
178 v->device, v->mount_point);
179 return -1;
180 }
181 return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0);
182 } else if (strcmp(v->fs_type, "ext4") == 0 ||
183 strcmp(v->fs_type, "vfat") == 0) {
184 result = mount(v->device, v->mount_point, v->fs_type,
Doug Zongker469243e2011-04-12 09:28:10 -0700185 MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
Doug Zongkerd4208f92010-09-20 12:16:13 -0700186 if (result == 0) return 0;
187
188 if (v->device2) {
189 LOGW("failed to mount %s (%s); trying %s\n",
190 v->device, strerror(errno), v->device2);
191 result = mount(v->device2, v->mount_point, v->fs_type,
Doug Zongker469243e2011-04-12 09:28:10 -0700192 MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
Doug Zongkerd4208f92010-09-20 12:16:13 -0700193 if (result == 0) return 0;
194 }
195
196 LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));
197 return -1;
198 }
199
200 LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800201 return -1;
202}
203
Doug Zongkerd4208f92010-09-20 12:16:13 -0700204int ensure_path_unmounted(const char* path) {
205 Volume* v = volume_for_path(path);
206 if (v == NULL) {
207 LOGE("unknown volume for path [%s]\n", path);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800208 return -1;
209 }
Doug Zongkerc18eeb82010-09-21 16:49:26 -0700210 if (strcmp(v->fs_type, "ramdisk") == 0) {
211 // the ramdisk is always mounted; you can't unmount it.
212 return -1;
213 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800214
Doug Zongkerd4208f92010-09-20 12:16:13 -0700215 int result;
216 result = scan_mounted_volumes();
217 if (result < 0) {
218 LOGE("failed to scan mounted volumes\n");
219 return -1;
220 }
221
222 const MountedVolume* mv =
223 find_mounted_volume_by_mount_point(v->mount_point);
224 if (mv == NULL) {
225 // volume is already unmounted
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800226 return 0;
227 }
228
Doug Zongkerd4208f92010-09-20 12:16:13 -0700229 return unmount_mounted_volume(mv);
230}
231
232int format_volume(const char* volume) {
233 Volume* v = volume_for_path(volume);
234 if (v == NULL) {
235 LOGE("unknown volume \"%s\"\n", volume);
236 return -1;
237 }
Doug Zongkerc18eeb82010-09-21 16:49:26 -0700238 if (strcmp(v->fs_type, "ramdisk") == 0) {
239 // you can't format the ramdisk.
240 LOGE("can't format_volume \"%s\"", volume);
241 return -1;
242 }
Doug Zongkerd4208f92010-09-20 12:16:13 -0700243 if (strcmp(v->mount_point, volume) != 0) {
244 LOGE("can't give path \"%s\" to format_volume\n", volume);
245 return -1;
246 }
247
248 if (ensure_path_unmounted(volume) != 0) {
249 LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
250 return -1;
251 }
252
253 if (strcmp(v->fs_type, "yaffs2") == 0 || strcmp(v->fs_type, "mtd") == 0) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800254 mtd_scan_partitions();
Doug Zongkerd4208f92010-09-20 12:16:13 -0700255 const MtdPartition* partition = mtd_find_partition_by_name(v->device);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800256 if (partition == NULL) {
Doug Zongkerd4208f92010-09-20 12:16:13 -0700257 LOGE("format_volume: no MTD partition \"%s\"\n", v->device);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800258 return -1;
259 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800260
Doug Zongkerd4208f92010-09-20 12:16:13 -0700261 MtdWriteContext *write = mtd_write_partition(partition);
262 if (write == NULL) {
263 LOGW("format_volume: can't open MTD \"%s\"\n", v->device);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800264 return -1;
Doug Zongkerd4208f92010-09-20 12:16:13 -0700265 } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
266 LOGW("format_volume: can't erase MTD \"%s\"\n", v->device);
267 mtd_write_close(write);
268 return -1;
269 } else if (mtd_write_close(write)) {
270 LOGW("format_volume: can't close MTD \"%s\"\n", v->device);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800271 return -1;
272 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800273 return 0;
274 }
275
Doug Zongkerd4208f92010-09-20 12:16:13 -0700276 if (strcmp(v->fs_type, "ext4") == 0) {
Stephen Smalley779701d2012-02-09 14:13:23 -0500277 int result = make_ext4fs(v->device, v->length, volume, sehandle);
Doug Zongkerd4208f92010-09-20 12:16:13 -0700278 if (result != 0) {
279 LOGE("format_volume: make_extf4fs failed on %s\n", v->device);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800280 return -1;
281 }
Doug Zongkerd4208f92010-09-20 12:16:13 -0700282 return 0;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800283 }
Doug Zongkerd4208f92010-09-20 12:16:13 -0700284
285 LOGE("format_volume: fs_type \"%s\" unsupported\n", v->fs_type);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800286 return -1;
287}