blob: 474ea1df1f43990f749d594af430441c7214cc7d [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
Koushik Dutta1bf4f692010-07-14 18:37:33 -070024#include <limits.h>
25
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080026#include "mtdutils/mtdutils.h"
27#include "mtdutils/mounts.h"
28#include "minzip/Zip.h"
29#include "roots.h"
30#include "common.h"
31
Koushik Dutta2f73e582010-04-18 16:00:21 -070032#include "extendedcommands.h"
33
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080034/* Canonical pointers.
35xxx may just want to use enums
36 */
37static const char g_mtd_device[] = "@\0g_mtd_device";
38static const char g_raw[] = "@\0g_raw";
39static const char g_package_file[] = "@\0g_package_file";
40
41static RootInfo g_roots[] = {
Koushik Duttad4060c32010-07-22 20:14:44 -070042 { "BOOT:", g_mtd_device, NULL, "boot", NULL, g_raw, NULL },
43 { "CACHE:", CACHE_DEVICE, NULL, "cache", "/cache", CACHE_FILESYSTEM, CACHE_FILESYSTEM_OPTIONS },
44 { "DATA:", DATA_DEVICE, NULL, "userdata", "/data", DATA_FILESYSTEM, DATA_FILESYSTEM_OPTIONS },
Koushik Dutta8b5e1852010-06-14 22:04:22 -070045#ifdef HAS_DATADATA
Koushik Duttad4060c32010-07-22 20:14:44 -070046 { "DATADATA:", DATADATA_DEVICE, NULL, "datadata", "/datadata", DATADATA_FILESYSTEM, DATADATA_FILESYSTEM_OPTIONS },
Koushik Dutta8b5e1852010-06-14 22:04:22 -070047#endif
Koushik Duttad4060c32010-07-22 20:14:44 -070048 { "MISC:", g_mtd_device, NULL, "misc", NULL, g_raw, NULL },
49 { "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file, NULL },
50 { "RECOVERY:", g_mtd_device, NULL, "recovery", "/", g_raw, NULL },
51 { "SDCARD:", SDCARD_DEVICE_PRIMARY, SDCARD_DEVICE_SECONDARY, NULL, "/sdcard", "vfat", NULL },
52 { "SDEXT:", SDEXT_DEVICE, NULL, NULL, "/sd-ext", SDEXT_FILESYSTEM, NULL },
53 { "SYSTEM:", SYSTEM_DEVICE, NULL, "system", "/system", SYSTEM_FILESYSTEM, SYSTEM_FILESYSTEM_OPTIONS },
54 { "MBM:", g_mtd_device, NULL, "mbm", NULL, g_raw, NULL },
55 { "TMP:", NULL, NULL, NULL, "/tmp", NULL, NULL },
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080056};
57#define NUM_ROOTS (sizeof(g_roots) / sizeof(g_roots[0]))
58
59// TODO: for SDCARD:, try /dev/block/mmcblk0 if mmcblk0p1 fails
60
Koushik Dutta14239d22010-06-14 15:02:48 -070061const RootInfo *
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080062get_root_info_for_path(const char *root_path)
63{
64 const char *c;
65
66 /* Find the first colon.
67 */
68 c = root_path;
69 while (*c != '\0' && *c != ':') {
70 c++;
71 }
72 if (*c == '\0') {
73 return NULL;
74 }
75 size_t len = c - root_path + 1;
76 size_t i;
77 for (i = 0; i < NUM_ROOTS; i++) {
78 RootInfo *info = &g_roots[i];
79 if (strncmp(info->name, root_path, len) == 0) {
80 return info;
81 }
82 }
83 return NULL;
84}
85
86static const ZipArchive *g_package = NULL;
87static char *g_package_path = NULL;
88
89int
90register_package_root(const ZipArchive *package, const char *package_path)
91{
92 if (package != NULL) {
93 package_path = strdup(package_path);
94 if (package_path == NULL) {
95 return -1;
96 }
97 g_package_path = (char *)package_path;
98 } else {
99 free(g_package_path);
100 g_package_path = NULL;
101 }
102 g_package = package;
103 return 0;
104}
105
106int
107is_package_root_path(const char *root_path)
108{
109 const RootInfo *info = get_root_info_for_path(root_path);
110 return info != NULL && info->filesystem == g_package_file;
111}
112
113const char *
114translate_package_root_path(const char *root_path,
115 char *out_buf, size_t out_buf_len, const ZipArchive **out_package)
116{
117 const RootInfo *info = get_root_info_for_path(root_path);
118 if (info == NULL || info->filesystem != g_package_file) {
119 return NULL;
120 }
121
122 /* Strip the package root off of the path.
123 */
124 size_t root_len = strlen(info->name);
125 root_path += root_len;
126 size_t root_path_len = strlen(root_path);
127
128 if (out_buf_len < root_path_len + 1) {
129 return NULL;
130 }
131 strcpy(out_buf, root_path);
132 *out_package = g_package;
133 return out_buf;
134}
135
136/* Takes a string like "SYSTEM:lib" and turns it into a string
137 * like "/system/lib". The translated path is put in out_buf,
138 * and out_buf is returned if the translation succeeded.
139 */
140const char *
141translate_root_path(const char *root_path, char *out_buf, size_t out_buf_len)
142{
143 if (out_buf_len < 1) {
144 return NULL;
145 }
146
147 const RootInfo *info = get_root_info_for_path(root_path);
148 if (info == NULL || info->mount_point == NULL) {
149 return NULL;
150 }
151
152 /* Find the relative part of the non-root part of the path.
153 */
154 root_path += strlen(info->name); // strip off the "root:"
155 while (*root_path != '\0' && *root_path == '/') {
156 root_path++;
157 }
158
159 size_t mp_len = strlen(info->mount_point);
160 size_t rp_len = strlen(root_path);
161 if (mp_len + 1 + rp_len + 1 > out_buf_len) {
162 return NULL;
163 }
164
165 /* Glue the mount point to the relative part of the path.
166 */
167 memcpy(out_buf, info->mount_point, mp_len);
168 if (out_buf[mp_len - 1] != '/') out_buf[mp_len++] = '/';
169
170 memcpy(out_buf + mp_len, root_path, rp_len);
171 out_buf[mp_len + rp_len] = '\0';
172
173 return out_buf;
174}
175
176static int
177internal_root_mounted(const RootInfo *info)
178{
179 if (info->mount_point == NULL) {
180 return -1;
181 }
182//xxx if TMP: (or similar) just say "yes"
183
184 /* See if this root is already mounted.
185 */
186 int ret = scan_mounted_volumes();
187 if (ret < 0) {
188 return ret;
189 }
190 const MountedVolume *volume;
191 volume = find_mounted_volume_by_mount_point(info->mount_point);
192 if (volume != NULL) {
193 /* It's already mounted.
194 */
195 return 0;
196 }
197 return -1;
198}
199
200int
201is_root_path_mounted(const char *root_path)
202{
203 const RootInfo *info = get_root_info_for_path(root_path);
204 if (info == NULL) {
205 return -1;
206 }
207 return internal_root_mounted(info) >= 0;
208}
209
Koushik Duttad4060c32010-07-22 20:14:44 -0700210static int mount_internal(const char* device, const char* mount_point, const char* filesystem, const char* filesystem_options)
Koushik Dutta1bf4f692010-07-14 18:37:33 -0700211{
Koushik Duttad4060c32010-07-22 20:14:44 -0700212 if (strcmp(filesystem, "auto") != 0 && filesystem_options == NULL) {
Koushik Dutta1bf4f692010-07-14 18:37:33 -0700213 return mount(device, mount_point, filesystem, MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
214 }
215 else {
216 char mount_cmd[PATH_MAX];
Koushik Duttad4060c32010-07-22 20:14:44 -0700217 const char* options = filesystem_options == NULL ? "noatime,nodiratime,nodev" : filesystem_options;
218 sprintf(mount_cmd, "mount -t %s -o%s %s %s", filesystem, options, device, mount_point);
Koushik Dutta1bf4f692010-07-14 18:37:33 -0700219 return __system(mount_cmd);
220 }
221}
222
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800223int
224ensure_root_path_mounted(const char *root_path)
225{
226 const RootInfo *info = get_root_info_for_path(root_path);
227 if (info == NULL) {
228 return -1;
229 }
230
231 int ret = internal_root_mounted(info);
232 if (ret >= 0) {
233 /* It's already mounted.
234 */
235 return 0;
236 }
237
238 /* It's not mounted.
239 */
240 if (info->device == g_mtd_device) {
241 if (info->partition_name == NULL) {
242 return -1;
243 }
244//TODO: make the mtd stuff scan once when it needs to
245 mtd_scan_partitions();
246 const MtdPartition *partition;
247 partition = mtd_find_partition_by_name(info->partition_name);
248 if (partition == NULL) {
249 return -1;
250 }
251 return mtd_mount_partition(partition, info->mount_point,
252 info->filesystem, 0);
253 }
254
255 if (info->device == NULL || info->mount_point == NULL ||
256 info->filesystem == NULL ||
257 info->filesystem == g_raw ||
258 info->filesystem == g_package_file) {
259 return -1;
260 }
261
262 mkdir(info->mount_point, 0755); // in case it doesn't already exist
Koushik Duttad4060c32010-07-22 20:14:44 -0700263 if (mount_internal(info->device, info->mount_point, info->filesystem, info->filesystem_options)) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800264 if (info->device2 == NULL) {
265 LOGE("Can't mount %s\n(%s)\n", info->device, strerror(errno));
266 return -1;
267 } else if (mount(info->device2, info->mount_point, info->filesystem,
268 MS_NOATIME | MS_NODEV | MS_NODIRATIME, "")) {
269 LOGE("Can't mount %s (or %s)\n(%s)\n",
270 info->device, info->device2, strerror(errno));
271 return -1;
272 }
273 }
274 return 0;
275}
276
277int
278ensure_root_path_unmounted(const char *root_path)
279{
280 const RootInfo *info = get_root_info_for_path(root_path);
281 if (info == NULL) {
282 return -1;
283 }
284 if (info->mount_point == NULL) {
285 /* This root can't be mounted, so by definition it isn't.
286 */
287 return 0;
288 }
289//xxx if TMP: (or similar) just return error
290
291 /* See if this root is already mounted.
292 */
293 int ret = scan_mounted_volumes();
294 if (ret < 0) {
295 return ret;
296 }
297 const MountedVolume *volume;
298 volume = find_mounted_volume_by_mount_point(info->mount_point);
299 if (volume == NULL) {
300 /* It's not mounted.
301 */
302 return 0;
303 }
304
305 return unmount_mounted_volume(volume);
306}
307
308const MtdPartition *
309get_root_mtd_partition(const char *root_path)
310{
311 const RootInfo *info = get_root_info_for_path(root_path);
312 if (info == NULL || info->device != g_mtd_device ||
313 info->partition_name == NULL)
314 {
Koushik Duttad4060c32010-07-22 20:14:44 -0700315#ifdef BOARD_HAS_MTD_CACHE
316 if (strcmp(root_path, "CACHE:") != 0)
317 return NULL;
318#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800319 return NULL;
Koushik Duttad4060c32010-07-22 20:14:44 -0700320#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800321 }
322 mtd_scan_partitions();
323 return mtd_find_partition_by_name(info->partition_name);
324}
325
326int
327format_root_device(const char *root)
328{
329 /* Be a little safer here; require that "root" is just
330 * a device with no relative path after it.
331 */
332 const char *c = root;
333 while (*c != '\0' && *c != ':') {
334 c++;
335 }
Koushik Dutta062d6b02010-07-03 13:54:21 -0700336 /*
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800337 if (c[0] != ':' || c[1] != '\0') {
338 LOGW("format_root_device: bad root name \"%s\"\n", root);
339 return -1;
340 }
Koushik Dutta062d6b02010-07-03 13:54:21 -0700341 */
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800342
343 const RootInfo *info = get_root_info_for_path(root);
344 if (info == NULL || info->device == NULL) {
345 LOGW("format_root_device: can't resolve \"%s\"\n", root);
346 return -1;
347 }
Koushik Dutta852bb422010-07-24 11:18:00 -0700348 if (info->mount_point != NULL && info->device == g_mtd_device) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800349 /* Don't try to format a mounted device.
350 */
351 int ret = ensure_root_path_unmounted(root);
352 if (ret < 0) {
353 LOGW("format_root_device: can't unmount \"%s\"\n", root);
354 return ret;
355 }
356 }
357
358 /* Format the device.
359 */
360 if (info->device == g_mtd_device) {
361 mtd_scan_partitions();
362 const MtdPartition *partition;
363 partition = mtd_find_partition_by_name(info->partition_name);
364 if (partition == NULL) {
365 LOGW("format_root_device: can't find mtd partition \"%s\"\n",
366 info->partition_name);
367 return -1;
368 }
369 if (info->filesystem == g_raw || !strcmp(info->filesystem, "yaffs2")) {
370 MtdWriteContext *write = mtd_write_partition(partition);
371 if (write == NULL) {
372 LOGW("format_root_device: can't open \"%s\"\n", root);
373 return -1;
374 } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
375 LOGW("format_root_device: can't erase \"%s\"\n", root);
376 mtd_write_close(write);
377 return -1;
378 } else if (mtd_write_close(write)) {
379 LOGW("format_root_device: can't close \"%s\"\n", root);
380 return -1;
381 } else {
382 return 0;
383 }
384 }
385 }
Koushik Duttad4060c32010-07-22 20:14:44 -0700386
Koushik Dutta2f73e582010-04-18 16:00:21 -0700387 return format_non_mtd_device(root);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800388}