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