blob: b9f030bbc86502c546d8350f1963eb7e94faf6d9 [file] [log] [blame]
San Mehatf1b736b2009-10-10 17:22:08 -07001/*
2 * Copyright (C) 2008 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
San Mehat49e2bce2009-10-12 16:29:01 -070017#include <stdlib.h>
San Mehatf1b736b2009-10-10 17:22:08 -070018#include <string.h>
San Mehat49e2bce2009-10-12 16:29:01 -070019#include <dirent.h>
20#include <errno.h>
21#include <fcntl.h>
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <sys/mman.h>
San Mehata2677e42009-12-13 10:40:18 -080027#include <sys/mount.h>
28
29#include <linux/kdev_t.h>
30
31#include <cutils/properties.h>
32
33#include "diskmbr.h"
San Mehatf1b736b2009-10-10 17:22:08 -070034
35#define LOG_TAG "Vold"
36
37#include <cutils/log.h>
38
39#include "Volume.h"
San Mehata2677e42009-12-13 10:40:18 -080040#include "VolumeManager.h"
41#include "ResponseCode.h"
San Mehatf1b736b2009-10-10 17:22:08 -070042
San Mehat49e2bce2009-10-12 16:29:01 -070043extern "C" int logwrap(int argc, const char **argv, int background);
San Mehata2677e42009-12-13 10:40:18 -080044extern "C" int mount(const char *, const char *, const char *, unsigned long, const void *);
45extern "C" void KillProcessesWithOpenFiles(const char *, int, int, int);
46extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
47extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
San Mehat49e2bce2009-10-12 16:29:01 -070048
49static char FSCK_MSDOS_PATH[] = "/system/bin/fsck_msdos";
San Mehata2677e42009-12-13 10:40:18 -080050static char MKDOSFS_PATH[] = "/system/bin/newfs_msdos";
San Mehat49e2bce2009-10-12 16:29:01 -070051
San Mehata2677e42009-12-13 10:40:18 -080052static const char *stateToStr(int state) {
53 if (state == Volume::State_Init)
54 return "Initializing";
55 else if (state == Volume::State_NoMedia)
56 return "No-Media";
57 else if (state == Volume::State_Idle)
58 return "Idle-Unmounted";
59 else if (state == Volume::State_Pending)
60 return "Pending";
61 else if (state == Volume::State_Mounted)
62 return "Mounted";
63 else if (state == Volume::State_Unmounting)
64 return "Unmounting";
65 else if (state == Volume::State_Checking)
66 return "Checking";
67 else if (state == Volume::State_Formatting)
68 return "Formatting";
69 else if (state == Volume::State_Shared)
70 return "Shared-Unmounted";
71 else if (state == Volume::State_SharedMnt)
72 return "Shared-Mounted";
73 else
74 return "Unknown-Error";
75}
76
77Volume::Volume(VolumeManager *vm, const char *label, const char *mount_point) {
78 mVm = vm;
San Mehatf1b736b2009-10-10 17:22:08 -070079 mLabel = strdup(label);
80 mMountpoint = strdup(mount_point);
81 mState = Volume::State_Init;
San Mehata2677e42009-12-13 10:40:18 -080082 mCurrentlyMountedKdev = -1;
San Mehatf1b736b2009-10-10 17:22:08 -070083}
84
85Volume::~Volume() {
86 free(mLabel);
87 free(mMountpoint);
88}
89
San Mehata2677e42009-12-13 10:40:18 -080090dev_t Volume::getDiskDevice() {
91 return MKDEV(0, 0);
92};
93
94void Volume::handleVolumeShared() {
95}
96
97void Volume::handleVolumeUnshared() {
98}
99
San Mehatfd7f5872009-10-12 11:32:47 -0700100int Volume::handleBlockEvent(NetlinkEvent *evt) {
San Mehatf1b736b2009-10-10 17:22:08 -0700101 errno = ENOSYS;
102 return -1;
103}
104
105void Volume::setState(int state) {
San Mehata2677e42009-12-13 10:40:18 -0800106 char msg[255];
107 int oldState = mState;
108
109 if (oldState == state) {
110 LOGW("Duplicate state (%d)\n", state);
111 return;
112 }
113
San Mehatf1b736b2009-10-10 17:22:08 -0700114 mState = state;
San Mehata2677e42009-12-13 10:40:18 -0800115
116 LOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
117 oldState, stateToStr(oldState), mState, stateToStr(mState));
118 snprintf(msg, sizeof(msg),
119 "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
120 getMountpoint(), oldState, stateToStr(oldState), mState,
121 stateToStr(mState));
122
123 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
124 msg, false);
San Mehatf1b736b2009-10-10 17:22:08 -0700125}
San Mehat49e2bce2009-10-12 16:29:01 -0700126
San Mehatdd9b8e92009-10-21 11:06:52 -0700127int Volume::createDeviceNode(const char *path, int major, int minor) {
128 mode_t mode = 0660 | S_IFBLK;
129 dev_t dev = (major << 8) | minor;
130 if (mknod(path, mode, dev) < 0) {
131 if (errno != EEXIST) {
132 return -1;
133 }
134 }
135 return 0;
136}
137
San Mehata2677e42009-12-13 10:40:18 -0800138int Volume::formatVol() {
San Mehat49e2bce2009-10-12 16:29:01 -0700139
San Mehata2677e42009-12-13 10:40:18 -0800140 if (getState() == Volume::State_NoMedia) {
141 errno = ENODEV;
142 return -1;
143 } else if (getState() != Volume::State_Idle) {
144 errno = EBUSY;
San Mehat49e2bce2009-10-12 16:29:01 -0700145 return -1;
146 }
147
San Mehata2677e42009-12-13 10:40:18 -0800148 if (isMountpointMounted(getMountpoint())) {
149 LOGW("Volume is idle but appears to be mounted - fixing");
150 setState(Volume::State_Mounted);
151 // mCurrentlyMountedKdev = XXX
152 errno = EBUSY;
San Mehat49e2bce2009-10-12 16:29:01 -0700153 return -1;
154 }
155
San Mehata2677e42009-12-13 10:40:18 -0800156 char devicePath[255];
157 dev_t diskNode = getDiskDevice();
158 dev_t partNode = MKDEV(MAJOR(diskNode), 1); // XXX: Hmmm
159
160 sprintf(devicePath, "/dev/block/vold/%d:%d",
161 MAJOR(diskNode), MINOR(diskNode));
162
163 LOGI("Volume %s (%s) MBR being initialized", getLabel(), devicePath);
164
165 if (initializeMbr(devicePath)) {
166 LOGE("Failed to initialize MBR (%s)", strerror(errno));
167 goto err;
San Mehat49e2bce2009-10-12 16:29:01 -0700168 }
169
San Mehata2677e42009-12-13 10:40:18 -0800170 sprintf(devicePath, "/dev/block/vold/%d:%d",
171 MAJOR(partNode), MINOR(partNode));
San Mehatdd9b8e92009-10-21 11:06:52 -0700172
San Mehata2677e42009-12-13 10:40:18 -0800173 LOGI("Volume %s (%s) being formatted", getLabel(), devicePath);
174
175 if (doFormatVfat(devicePath)) {
176 LOGE("Failed to format (%s)", strerror(errno));
177 goto err;
178 }
179
180 LOGI("Volume %s (%s) formatted sucessfully", getLabel(), devicePath);
San Mehat49e2bce2009-10-12 16:29:01 -0700181 return 0;
San Mehata2677e42009-12-13 10:40:18 -0800182err:
183 return -1;
184}
185
186bool Volume::isMountpointMounted(const char *path) {
187 char device[256];
188 char mount_path[256];
189 char rest[256];
190 FILE *fp;
191 char line[1024];
192
193 if (!(fp = fopen("/proc/mounts", "r"))) {
194 LOGE("Error opening /proc/mounts (%s)", strerror(errno));
195 return false;
196 }
197
198 while(fgets(line, sizeof(line), fp)) {
199 line[strlen(line)-1] = '\0';
200 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
201 if (!strcmp(mount_path, path)) {
202 fclose(fp);
203 return true;
204 }
205
206 }
207
208 fclose(fp);
209 return false;
210}
211
212int Volume::mountVol() {
213 dev_t deviceNodes[4];
214 int n, i, rc = 0;
215 char errmsg[255];
216
217 if (getState() == Volume::State_NoMedia) {
218 snprintf(errmsg, sizeof(errmsg),
219 "Volume %s %s mount failed - no media",
220 getLabel(), getMountpoint());
221 mVm->getBroadcaster()->sendBroadcast(
222 ResponseCode::VolumeMountFailedNoMedia,
223 errmsg, false);
224 errno = ENODEV;
225 return -1;
226 } else if (getState() != Volume::State_Idle) {
227 errno = EBUSY;
228 return -1;
229 }
230
231 if (isMountpointMounted(getMountpoint())) {
232 LOGW("Volume is idle but appears to be mounted - fixing");
233 setState(Volume::State_Mounted);
234 // mCurrentlyMountedKdev = XXX
235 return 0;
236 }
237
238 n = getDeviceNodes((dev_t *) &deviceNodes, 4);
239 if (!n) {
240 LOGE("Failed to get device nodes (%s)\n", strerror(errno));
241 return -1;
242 }
243
244 for (i = 0; i < n; i++) {
245 char devicePath[255];
246
247 sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
248 MINOR(deviceNodes[i]));
249
250 LOGI("%s being considered for volume %s\n", devicePath, getLabel());
251
252 errno = 0;
253 if ((rc = checkFilesystem(devicePath))) {
254 if (errno == ENODATA) {
255 LOGW("%s does not contain a FAT filesystem\n", devicePath);
256 continue;
257 } else {
258 /* Badness - abort the mount */
259 LOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
260 snprintf(errmsg, sizeof(errmsg),
261 "Volume %s %s mount failed - filesystem check failed",
262 getLabel(), getMountpoint());
263 mVm->getBroadcaster()->sendBroadcast(
264 ResponseCode::VolumeMountFailedDamaged,
265 errmsg, false);
266 setState(Volume::State_Idle);
267 goto out;
268 }
269 }
270
271 LOGI("%s checks out - attempting to mount\n", devicePath);
272 errno = 0;
273 if (!(rc = doMountVfat(devicePath, getMountpoint()))) {
274 LOGI("%s sucessfully mounted for volume %s\n", devicePath,
275 getLabel());
276 setState(Volume::State_Mounted);
277 mCurrentlyMountedKdev = deviceNodes[i];
278 goto out;
279 }
280
281 LOGW("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
282 }
283
284 // XXX: Doesn't handle multiple partitions properly
285 if (errno == ENODATA) {
286 snprintf(errmsg, sizeof(errmsg),
287 "Volume %s %s mount failed - no supported file-systems",
288 getLabel(), getMountpoint());
289 mVm->getBroadcaster()->sendBroadcast(
290 ResponseCode::VolumeMountFailedBlank,
291 errmsg, false);
292 }
293
294
295 LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
296 setState(Volume::State_Idle);
297
298out:
299 return rc;
300}
301
302
303int Volume::doMountVfat(const char *deviceNode, const char *mountPoint)
304{
305 int rc;
306 unsigned long flags;
307
308 flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
309
310 /*
311 * Note: This is a temporary hack. If the sampling profiler is enabled,
312 * we make the SD card world-writable so any process can write snapshots.
313 *
314 * TODO: Remove this code once we have a drop box in system_server.
315 */
316 char value[PROPERTY_VALUE_MAX];
317 property_get("persist.sampling_profiler", value, "");
318 if (value[0] == '1') {
319 LOGW("The SD card is world-writable because the"
320 " 'persist.sampling_profiler' system property is set to '1'.");
321 rc = mount(deviceNode, mountPoint, (const char *) "vfat", (unsigned long) flags,
322 (const void *) "utf8,uid=1000,gid=1015,fmask=000,dmask=000,shortname=mixed");
323 } else {
324 /*
325 * The mount masks restrict access so that:
326 * 1. The 'system' user cannot access the SD card at all -
327 * (protects system_server from grabbing file references)
328 * 2. Group users can RWX
329 * 3. Others can only RX
330 */
331 rc = mount(deviceNode, mountPoint, "vfat", flags,
332 "utf8,uid=1000,gid=1015,fmask=702,dmask=702,shortname=mixed");
333 }
334
335 if (rc && errno == EROFS) {
336 LOGE("%s appears to be a read only filesystem - retrying mount RO",
337 deviceNode);
338 flags |= MS_RDONLY;
339 rc = mount(deviceNode, mountPoint, "vfat", flags,
340 "utf8,uid=1000,gid=1015,fmask=702,dmask=702,shortname=mixed");
341 }
342
343 if (rc == 0) {
344 char *lost_path;
345 asprintf(&lost_path, "%s/LOST.DIR", mountPoint);
346 if (access(lost_path, F_OK)) {
347 /*
348 * Create a LOST.DIR in the root so we have somewhere to put
349 * lost cluster chains (fsck_msdos doesn't currently do this)
350 */
351 if (mkdir(lost_path, 0755)) {
352 LOGE("Unable to create LOST.DIR (%s)", strerror(errno));
353 }
354 }
355 free(lost_path);
356 }
357
358 return rc;
San Mehat49e2bce2009-10-12 16:29:01 -0700359}
360
361int Volume::checkFilesystem(const char *nodepath) {
362
363 bool rw = true;
364 if (access(FSCK_MSDOS_PATH, X_OK)) {
365 LOGW("Skipping fs checks\n");
366 return 0;
367 }
368
369 setState(Volume::State_Checking);
370 int pass = 1;
371 int rc = 0;
372 do {
373 const char *args[5];
374 args[0] = FSCK_MSDOS_PATH;
375 args[1] = "-p";
376 args[2] = "-f";
377 args[3] = nodepath;
378 args[4] = NULL;
379
380 rc = logwrap(4, args, 1);
381
382 switch(rc) {
383 case 0:
384 LOGI("Filesystem check completed OK");
385 return 0;
386
387 case 2:
388 LOGE("Filesystem check failed (not a FAT filesystem)");
389 errno = ENODATA;
390 return -1;
391
392 case 4:
393 if (pass++ <= 3) {
394 LOGW("Filesystem modified - rechecking (pass %d)",
395 pass);
396 continue;
397 }
398 LOGE("Failing check after too many rechecks");
399 errno = EIO;
400 return -1;
401
402 default:
403 LOGE("Filesystem check failed (unknown exit code %d)", rc);
404 errno = EIO;
405 return -1;
406 }
407 } while (0);
408
409 return 0;
410}
411
San Mehata2677e42009-12-13 10:40:18 -0800412int Volume::unmountVol() {
413 int i, rc;
414
415 if (getState() != Volume::State_Mounted) {
416 LOGE("Volume %s unmount request when not mounted", getLabel());
417 errno = EINVAL;
418 return -1;
419 }
420
421 setState(Volume::State_Unmounting);
422 for (i = 0; i < 10; i++) {
423 rc = umount(getMountpoint());
424 if (!rc)
425 break;
426
427 if (rc && (errno == EINVAL || errno == ENOENT)) {
428 rc = 0;
429 break;
430 }
431
432 LOGW("Volume %s unmount attempt %d failed (%s)",
433 getLabel(), i + 1, strerror(errno));
434
435 if (i < 5) {
436 usleep(1000 * 250);
437 } else {
438 KillProcessesWithOpenFiles(getMountpoint(),
439 (i < 7 ? 0 : 1),
440 NULL, 0);
441 usleep(1000 * 250);
442 }
443 }
444
445 if (!rc) {
446 LOGI("Volume %s unmounted sucessfully", getLabel());
447 setState(Volume::State_Idle);
448 mCurrentlyMountedKdev = -1;
449 return 0;
450 }
451
452 LOGE("Volume %s failed to unmount (%s)\n", getLabel(), strerror(errno));
453 setState(Volume::State_Mounted);
454 return -1;
455}
456
457int Volume::initializeMbr(const char *deviceNode) {
458 int fd, rc;
459 unsigned char block[512];
460 struct dos_partition part;
461 unsigned int nr_sec;
462
463 if ((fd = open(deviceNode, O_RDWR)) < 0) {
464 LOGE("Error opening disk file (%s)", strerror(errno));
465 return -1;
466 }
467
468 if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
469 LOGE("Unable to get device size (%s)", strerror(errno));
470 close(fd);
471 return -1;
472 }
473
474 memset(&part, 0, sizeof(part));
475 part.dp_flag = 0x80;
476 part.dp_typ = 0xc;
477 part.dp_start = ((1024 * 64) / 512) + 1;
478 part.dp_size = nr_sec - part.dp_start;
479
480 memset(block, 0, sizeof(block));
481 block[0x1fe] = 0x55;
482 block[0x1ff] = 0xaa;
483
484 dos_partition_enc(block + DOSPARTOFF, &part);
485
486 if (write(fd, block, sizeof(block)) < 0) {
487 LOGE("Error writing MBR (%s)", strerror(errno));
488 close(fd);
489 return -1;
490 }
491
492 if (ioctl(fd, BLKRRPART, NULL) < 0) {
493 LOGE("Error re-reading partition table (%s)", strerror(errno));
494 close(fd);
495 return -1;
496 }
497 close(fd);
498 return 0;
499}
500
501int Volume::doFormatVfat(const char *deviceNode) {
502 unsigned int nr_sec;
503 int fd;
504
505 if ((fd = open(deviceNode, O_RDWR)) < 0) {
506 LOGE("Error opening disk file (%s)", strerror(errno));
507 return -1;
508 }
509
510 if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
511 LOGE("Unable to get device size (%s)", strerror(errno));
512 close(fd);
513 return -1;
514 }
515 close(fd);
516
517 const char *args[7];
518 int rc;
519 args[0] = MKDOSFS_PATH;
520 args[1] = "-F";
521 if ((nr_sec * 512) <= ((unsigned int) (1024*1024*1024) * 2))
522 args[2] = "16";
523 else
524 args[2] = "32";
525
526 args[3] = "-O";
527 args[4] = "android";
528 args[5] = deviceNode;
529 args[6] = NULL;
530 rc = logwrap(7, args, 1);
531
532 if (rc == 0) {
533 LOGI("Filesystem formatted OK");
534 return 0;
535 } else {
536 LOGE("Format failed (unknown exit code %d)", rc);
537 errno = EIO;
538 return -1;
539 }
San Mehat49e2bce2009-10-12 16:29:01 -0700540 return 0;
541}