blob: 762bdf3820be516e1a84d99c03ed5e7ef31fa3f7 [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>
23
24#include "mtdutils/mtdutils.h"
25#include "mtdutils/mounts.h"
Doug Zongker49c73a72010-06-29 17:36:28 -070026
27#ifdef USE_EXT4
28#include "make_ext4fs.h"
29#endif
30
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080031#include "minzip/Zip.h"
32#include "roots.h"
33#include "common.h"
34
35typedef struct {
36 const char *name;
37 const char *device;
38 const char *device2; // If the first one doesn't work (may be NULL)
39 const char *partition_name;
40 const char *mount_point;
41 const char *filesystem;
42} RootInfo;
43
44/* Canonical pointers.
45xxx may just want to use enums
46 */
47static const char g_mtd_device[] = "@\0g_mtd_device";
48static const char g_raw[] = "@\0g_raw";
49static const char g_package_file[] = "@\0g_package_file";
Doug Zongker23ceeea2010-07-08 17:27:55 -070050static const char g_ramdisk[] = "@\0g_ramdisk";
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080051
52static RootInfo g_roots[] = {
53 { "BOOT:", g_mtd_device, NULL, "boot", NULL, g_raw },
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080054 { "MISC:", g_mtd_device, NULL, "misc", NULL, g_raw },
55 { "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file },
56 { "RECOVERY:", g_mtd_device, NULL, "recovery", "/", g_raw },
57 { "SDCARD:", "/dev/block/mmcblk0p1", "/dev/block/mmcblk0", NULL, "/sdcard", "vfat" },
58 { "SYSTEM:", g_mtd_device, NULL, "system", "/system", "yaffs2" },
Doug Zongkerb128f542009-06-18 15:07:14 -070059 { "MBM:", g_mtd_device, NULL, "mbm", NULL, g_raw },
Doug Zongker23ceeea2010-07-08 17:27:55 -070060 { "TMP:", NULL, NULL, NULL, "/tmp", g_ramdisk },
Doug Zongker49c73a72010-06-29 17:36:28 -070061
62#ifdef USE_EXT4
63 { "CACHE:", "/dev/block/platform/sdhci-tegra.3/by-name/cache", NULL, NULL,
64 "/cache", "ext4" },
65 { "DATA:", "/dev/block/platform/sdhci-tegra.3/by-name/userdata", NULL, NULL,
66 "/data", "ext4" },
67#else
68 { "CACHE:", g_mtd_device, NULL, "cache", "/cache", "yaffs2" },
69 { "DATA:", g_mtd_device, NULL, "userdata", "/data", "yaffs2" },
70#endif
71
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080072};
73#define NUM_ROOTS (sizeof(g_roots) / sizeof(g_roots[0]))
74
75// TODO: for SDCARD:, try /dev/block/mmcblk0 if mmcblk0p1 fails
76
77static const RootInfo *
78get_root_info_for_path(const char *root_path)
79{
80 const char *c;
81
82 /* Find the first colon.
83 */
84 c = root_path;
85 while (*c != '\0' && *c != ':') {
86 c++;
87 }
88 if (*c == '\0') {
89 return NULL;
90 }
91 size_t len = c - root_path + 1;
92 size_t i;
93 for (i = 0; i < NUM_ROOTS; i++) {
94 RootInfo *info = &g_roots[i];
95 if (strncmp(info->name, root_path, len) == 0) {
96 return info;
97 }
98 }
99 return NULL;
100}
101
102static const ZipArchive *g_package = NULL;
103static char *g_package_path = NULL;
104
105int
106register_package_root(const ZipArchive *package, const char *package_path)
107{
108 if (package != NULL) {
109 package_path = strdup(package_path);
110 if (package_path == NULL) {
111 return -1;
112 }
113 g_package_path = (char *)package_path;
114 } else {
115 free(g_package_path);
116 g_package_path = NULL;
117 }
118 g_package = package;
119 return 0;
120}
121
122int
123is_package_root_path(const char *root_path)
124{
125 const RootInfo *info = get_root_info_for_path(root_path);
126 return info != NULL && info->filesystem == g_package_file;
127}
128
129const char *
130translate_package_root_path(const char *root_path,
131 char *out_buf, size_t out_buf_len, const ZipArchive **out_package)
132{
133 const RootInfo *info = get_root_info_for_path(root_path);
134 if (info == NULL || info->filesystem != g_package_file) {
135 return NULL;
136 }
137
138 /* Strip the package root off of the path.
139 */
140 size_t root_len = strlen(info->name);
141 root_path += root_len;
142 size_t root_path_len = strlen(root_path);
143
144 if (out_buf_len < root_path_len + 1) {
145 return NULL;
146 }
147 strcpy(out_buf, root_path);
148 *out_package = g_package;
149 return out_buf;
150}
151
152/* Takes a string like "SYSTEM:lib" and turns it into a string
153 * like "/system/lib". The translated path is put in out_buf,
154 * and out_buf is returned if the translation succeeded.
155 */
156const char *
157translate_root_path(const char *root_path, char *out_buf, size_t out_buf_len)
158{
159 if (out_buf_len < 1) {
160 return NULL;
161 }
162
163 const RootInfo *info = get_root_info_for_path(root_path);
164 if (info == NULL || info->mount_point == NULL) {
165 return NULL;
166 }
167
168 /* Find the relative part of the non-root part of the path.
169 */
170 root_path += strlen(info->name); // strip off the "root:"
171 while (*root_path != '\0' && *root_path == '/') {
172 root_path++;
173 }
174
175 size_t mp_len = strlen(info->mount_point);
176 size_t rp_len = strlen(root_path);
177 if (mp_len + 1 + rp_len + 1 > out_buf_len) {
178 return NULL;
179 }
180
181 /* Glue the mount point to the relative part of the path.
182 */
183 memcpy(out_buf, info->mount_point, mp_len);
184 if (out_buf[mp_len - 1] != '/') out_buf[mp_len++] = '/';
185
186 memcpy(out_buf + mp_len, root_path, rp_len);
187 out_buf[mp_len + rp_len] = '\0';
188
189 return out_buf;
190}
191
192static int
193internal_root_mounted(const RootInfo *info)
194{
195 if (info->mount_point == NULL) {
196 return -1;
197 }
Doug Zongker23ceeea2010-07-08 17:27:55 -0700198 if (info->filesystem == g_ramdisk) {
199 return 0;
200 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800201
202 /* See if this root is already mounted.
203 */
204 int ret = scan_mounted_volumes();
205 if (ret < 0) {
206 return ret;
207 }
208 const MountedVolume *volume;
209 volume = find_mounted_volume_by_mount_point(info->mount_point);
210 if (volume != NULL) {
211 /* It's already mounted.
212 */
213 return 0;
214 }
215 return -1;
216}
217
218int
219is_root_path_mounted(const char *root_path)
220{
221 const RootInfo *info = get_root_info_for_path(root_path);
222 if (info == NULL) {
223 return -1;
224 }
225 return internal_root_mounted(info) >= 0;
226}
227
228int
229ensure_root_path_mounted(const char *root_path)
230{
231 const RootInfo *info = get_root_info_for_path(root_path);
232 if (info == NULL) {
233 return -1;
234 }
235
236 int ret = internal_root_mounted(info);
237 if (ret >= 0) {
238 /* It's already mounted.
239 */
240 return 0;
241 }
242
243 /* It's not mounted.
244 */
245 if (info->device == g_mtd_device) {
246 if (info->partition_name == NULL) {
247 return -1;
248 }
249//TODO: make the mtd stuff scan once when it needs to
250 mtd_scan_partitions();
251 const MtdPartition *partition;
252 partition = mtd_find_partition_by_name(info->partition_name);
253 if (partition == NULL) {
254 return -1;
255 }
256 return mtd_mount_partition(partition, info->mount_point,
257 info->filesystem, 0);
258 }
259
260 if (info->device == NULL || info->mount_point == NULL ||
261 info->filesystem == NULL ||
262 info->filesystem == g_raw ||
263 info->filesystem == g_package_file) {
264 return -1;
265 }
266
267 mkdir(info->mount_point, 0755); // in case it doesn't already exist
268 if (mount(info->device, info->mount_point, info->filesystem,
Doug Zongker49c73a72010-06-29 17:36:28 -0700269 MS_NOATIME | MS_NODEV | MS_NODIRATIME, "")) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800270 if (info->device2 == NULL) {
271 LOGE("Can't mount %s\n(%s)\n", info->device, strerror(errno));
272 return -1;
273 } else if (mount(info->device2, info->mount_point, info->filesystem,
274 MS_NOATIME | MS_NODEV | MS_NODIRATIME, "")) {
275 LOGE("Can't mount %s (or %s)\n(%s)\n",
276 info->device, info->device2, strerror(errno));
277 return -1;
278 }
279 }
280 return 0;
281}
282
283int
284ensure_root_path_unmounted(const char *root_path)
285{
286 const RootInfo *info = get_root_info_for_path(root_path);
287 if (info == NULL) {
288 return -1;
289 }
290 if (info->mount_point == NULL) {
291 /* This root can't be mounted, so by definition it isn't.
292 */
293 return 0;
294 }
295//xxx if TMP: (or similar) just return error
296
297 /* See if this root is already mounted.
298 */
299 int ret = scan_mounted_volumes();
300 if (ret < 0) {
301 return ret;
302 }
303 const MountedVolume *volume;
304 volume = find_mounted_volume_by_mount_point(info->mount_point);
305 if (volume == NULL) {
306 /* It's not mounted.
307 */
308 return 0;
309 }
310
311 return unmount_mounted_volume(volume);
312}
313
314const MtdPartition *
315get_root_mtd_partition(const char *root_path)
316{
317 const RootInfo *info = get_root_info_for_path(root_path);
318 if (info == NULL || info->device != g_mtd_device ||
319 info->partition_name == NULL)
320 {
321 return NULL;
322 }
323 mtd_scan_partitions();
324 return mtd_find_partition_by_name(info->partition_name);
325}
326
327int
328format_root_device(const char *root)
329{
330 /* Be a little safer here; require that "root" is just
331 * a device with no relative path after it.
332 */
333 const char *c = root;
334 while (*c != '\0' && *c != ':') {
335 c++;
336 }
337 if (c[0] != ':' || c[1] != '\0') {
338 LOGW("format_root_device: bad root name \"%s\"\n", root);
339 return -1;
340 }
341
342 const RootInfo *info = get_root_info_for_path(root);
343 if (info == NULL || info->device == NULL) {
344 LOGW("format_root_device: can't resolve \"%s\"\n", root);
345 return -1;
346 }
347 if (info->mount_point != NULL) {
348 /* Don't try to format a mounted device.
349 */
350 int ret = ensure_root_path_unmounted(root);
351 if (ret < 0) {
352 LOGW("format_root_device: can't unmount \"%s\"\n", root);
353 return ret;
354 }
355 }
356
357 /* Format the device.
358 */
359 if (info->device == g_mtd_device) {
360 mtd_scan_partitions();
361 const MtdPartition *partition;
362 partition = mtd_find_partition_by_name(info->partition_name);
363 if (partition == NULL) {
364 LOGW("format_root_device: can't find mtd partition \"%s\"\n",
365 info->partition_name);
366 return -1;
367 }
368 if (info->filesystem == g_raw || !strcmp(info->filesystem, "yaffs2")) {
369 MtdWriteContext *write = mtd_write_partition(partition);
370 if (write == NULL) {
371 LOGW("format_root_device: can't open \"%s\"\n", root);
372 return -1;
373 } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
374 LOGW("format_root_device: can't erase \"%s\"\n", root);
375 mtd_write_close(write);
376 return -1;
377 } else if (mtd_write_close(write)) {
378 LOGW("format_root_device: can't close \"%s\"\n", root);
379 return -1;
380 } else {
381 return 0;
382 }
383 }
384 }
Doug Zongker49c73a72010-06-29 17:36:28 -0700385
386#ifdef USE_EXT4
387 if (strcmp(info->filesystem, "ext4") == 0) {
388 reset_ext4fs_info();
389 int result = make_ext4fs(info->device, NULL, NULL, 0, 0);
390 if (result != 0) {
391 LOGW("make_ext4fs failed: %d\n", result);
392 return -1;
393 }
394 return 0;
395 }
396#endif
397
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800398//TODO: handle other device types (sdcard, etc.)
Doug Zongker49c73a72010-06-29 17:36:28 -0700399
400 LOGW("format_root_device: unknown device \"%s\"\n", root);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800401 return -1;
402}