Volume.cpp revision d9a4e358614a0c5f60cc76c0636ee4bb02004a32
1/*
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
17#include <stdlib.h>
18#include <string.h>
19#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>
27#include <sys/mount.h>
28
29#include <linux/kdev_t.h>
30
31#include <cutils/properties.h>
32
33#include <diskconfig/diskconfig.h>
34
35#define LOG_TAG "Vold"
36
37#include <cutils/log.h>
38
39#include "Volume.h"
40#include "VolumeManager.h"
41#include "ResponseCode.h"
42#include "Fat.h"
43#include "Process.h"
44
45extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
46extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
47
48
49/*
50 * Secure directory - stuff that only root can see
51 */
52const char *Volume::SECDIR            = "/mnt/secure";
53
54/*
55 * Secure staging directory - where media is mounted for preparation
56 */
57const char *Volume::SEC_STGDIR        = "/mnt/secure/staging";
58
59/*
60 * Path to the directory on the media which contains publicly accessable
61 * asec imagefiles. This path will be obscured before the mount is
62 * exposed to non priviledged users.
63 */
64const char *Volume::SEC_STG_SECIMGDIR = "/mnt/secure/staging/.android_secure";
65
66/*
67 * Path to where *only* root can access asec imagefiles
68 */
69const char *Volume::SEC_ASECDIR       = "/mnt/secure/asec";
70
71/*
72 * Path to where secure containers are mounted
73 */
74const char *Volume::ASECDIR           = "/mnt/asec";
75
76static const char *stateToStr(int state) {
77    if (state == Volume::State_Init)
78        return "Initializing";
79    else if (state == Volume::State_NoMedia)
80        return "No-Media";
81    else if (state == Volume::State_Idle)
82        return "Idle-Unmounted";
83    else if (state == Volume::State_Pending)
84        return "Pending";
85    else if (state == Volume::State_Mounted)
86        return "Mounted";
87    else if (state == Volume::State_Unmounting)
88        return "Unmounting";
89    else if (state == Volume::State_Checking)
90        return "Checking";
91    else if (state == Volume::State_Formatting)
92        return "Formatting";
93    else if (state == Volume::State_Shared)
94        return "Shared-Unmounted";
95    else if (state == Volume::State_SharedMnt)
96        return "Shared-Mounted";
97    else
98        return "Unknown-Error";
99}
100
101Volume::Volume(VolumeManager *vm, const char *label, const char *mount_point) {
102    mVm = vm;
103    mDebug = false;
104    mLabel = strdup(label);
105    mMountpoint = strdup(mount_point);
106    mState = Volume::State_Init;
107    mCurrentlyMountedKdev = -1;
108}
109
110Volume::~Volume() {
111    free(mLabel);
112    free(mMountpoint);
113}
114
115void Volume::setDebug(bool enable) {
116    mDebug = enable;
117}
118
119dev_t Volume::getDiskDevice() {
120    return MKDEV(0, 0);
121};
122
123void Volume::handleVolumeShared() {
124}
125
126void Volume::handleVolumeUnshared() {
127}
128
129int Volume::handleBlockEvent(NetlinkEvent *evt) {
130    errno = ENOSYS;
131    return -1;
132}
133
134void Volume::setState(int state) {
135    char msg[255];
136    int oldState = mState;
137
138    if (oldState == state) {
139        LOGW("Duplicate state (%d)\n", state);
140        return;
141    }
142
143    mState = state;
144
145    LOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
146         oldState, stateToStr(oldState), mState, stateToStr(mState));
147    snprintf(msg, sizeof(msg),
148             "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
149             getMountpoint(), oldState, stateToStr(oldState), mState,
150             stateToStr(mState));
151
152    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
153                                         msg, false);
154}
155
156int Volume::createDeviceNode(const char *path, int major, int minor) {
157    mode_t mode = 0660 | S_IFBLK;
158    dev_t dev = (major << 8) | minor;
159    if (mknod(path, mode, dev) < 0) {
160        if (errno != EEXIST) {
161            return -1;
162        }
163    }
164    return 0;
165}
166
167int Volume::formatVol() {
168
169    if (getState() == Volume::State_NoMedia) {
170        errno = ENODEV;
171        return -1;
172    } else if (getState() != Volume::State_Idle) {
173        errno = EBUSY;
174        return -1;
175    }
176
177    if (isMountpointMounted(getMountpoint())) {
178        LOGW("Volume is idle but appears to be mounted - fixing");
179        setState(Volume::State_Mounted);
180        // mCurrentlyMountedKdev = XXX
181        errno = EBUSY;
182        return -1;
183    }
184
185    char devicePath[255];
186    dev_t diskNode = getDiskDevice();
187    dev_t partNode = MKDEV(MAJOR(diskNode), 1); // XXX: Hmmm
188
189    sprintf(devicePath, "/dev/block/vold/%d:%d",
190            MAJOR(diskNode), MINOR(diskNode));
191
192    if (mDebug) {
193        LOGI("Formatting volume %s (%s)", getLabel(), devicePath);
194    }
195    setState(Volume::State_Formatting);
196
197    if (initializeMbr(devicePath)) {
198        LOGE("Failed to initialize MBR (%s)", strerror(errno));
199        goto err;
200    }
201
202    sprintf(devicePath, "/dev/block/vold/%d:%d",
203            MAJOR(partNode), MINOR(partNode));
204
205    if (Fat::format(devicePath, 0)) {
206        LOGE("Failed to format (%s)", strerror(errno));
207        goto err;
208    }
209
210    setState(Volume::State_Idle);
211    return 0;
212err:
213    return -1;
214}
215
216bool Volume::isMountpointMounted(const char *path) {
217    char device[256];
218    char mount_path[256];
219    char rest[256];
220    FILE *fp;
221    char line[1024];
222
223    if (!(fp = fopen("/proc/mounts", "r"))) {
224        LOGE("Error opening /proc/mounts (%s)", strerror(errno));
225        return false;
226    }
227
228    while(fgets(line, sizeof(line), fp)) {
229        line[strlen(line)-1] = '\0';
230        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
231        if (!strcmp(mount_path, path)) {
232            fclose(fp);
233            return true;
234        }
235
236    }
237
238    fclose(fp);
239    return false;
240}
241
242int Volume::mountVol() {
243    dev_t deviceNodes[4];
244    int n, i, rc = 0;
245    char errmsg[255];
246
247    if (getState() == Volume::State_NoMedia) {
248        snprintf(errmsg, sizeof(errmsg),
249                 "Volume %s %s mount failed - no media",
250                 getLabel(), getMountpoint());
251        mVm->getBroadcaster()->sendBroadcast(
252                                         ResponseCode::VolumeMountFailedNoMedia,
253                                         errmsg, false);
254        errno = ENODEV;
255        return -1;
256    } else if (getState() != Volume::State_Idle) {
257        errno = EBUSY;
258        return -1;
259    }
260
261    if (isMountpointMounted(getMountpoint())) {
262        LOGW("Volume is idle but appears to be mounted - fixing");
263        setState(Volume::State_Mounted);
264        // mCurrentlyMountedKdev = XXX
265        return 0;
266    }
267
268    n = getDeviceNodes((dev_t *) &deviceNodes, 4);
269    if (!n) {
270        LOGE("Failed to get device nodes (%s)\n", strerror(errno));
271        return -1;
272    }
273
274    for (i = 0; i < n; i++) {
275        char devicePath[255];
276
277        sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
278                MINOR(deviceNodes[i]));
279
280        LOGI("%s being considered for volume %s\n", devicePath, getLabel());
281
282        errno = 0;
283        setState(Volume::State_Checking);
284
285        if (Fat::check(devicePath)) {
286            if (errno == ENODATA) {
287                LOGW("%s does not contain a FAT filesystem\n", devicePath);
288                continue;
289            }
290            errno = EIO;
291            /* Badness - abort the mount */
292            LOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
293            setState(Volume::State_Idle);
294            return -1;
295        }
296
297        /*
298         * Mount the device on our internal staging mountpoint so we can
299         * muck with it before exposing it to non priviledged users.
300         */
301        errno = 0;
302        if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false, 1000, 1015, 0702, true)) {
303            LOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
304            continue;
305        }
306
307        LOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint());
308
309        if (createBindMounts()) {
310            LOGE("Failed to create bindmounts (%s)", strerror(errno));
311            umount("/mnt/secure/staging");
312            setState(Volume::State_Idle);
313            return -1;
314        }
315
316        /*
317         * Now that the bindmount trickery is done, atomically move the
318         * whole subtree to expose it to non priviledged users.
319         */
320        if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) {
321            LOGE("Failed to move mount (%s)", strerror(errno));
322            umount("/mnt/secure/staging");
323            setState(Volume::State_Idle);
324            return -1;
325        }
326        setState(Volume::State_Mounted);
327        mCurrentlyMountedKdev = deviceNodes[i];
328        return 0;
329    }
330
331    LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
332    setState(Volume::State_Idle);
333
334    return -1;
335}
336
337int Volume::createBindMounts() {
338    unsigned long flags;
339
340    /*
341     * Rename old /android_secure -> /.android_secure
342     */
343    if (!access("/mnt/secure/staging/android_secure", R_OK | X_OK) &&
344         access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {
345        if (rename("/mnt/secure/staging/android_secure", SEC_STG_SECIMGDIR)) {
346            LOGE("Failed to rename legacy asec dir (%s)", strerror(errno));
347        }
348    }
349
350    /*
351     * Ensure that /android_secure exists and is a directory
352     */
353    if (access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {
354        if (errno == ENOENT) {
355            if (mkdir(SEC_STG_SECIMGDIR, 0777)) {
356                LOGE("Failed to create %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
357                return -1;
358            }
359        } else {
360            LOGE("Failed to access %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
361            return -1;
362        }
363    } else {
364        struct stat sbuf;
365
366        if (stat(SEC_STG_SECIMGDIR, &sbuf)) {
367            LOGE("Failed to stat %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
368            return -1;
369        }
370        if (!S_ISDIR(sbuf.st_mode)) {
371            LOGE("%s is not a directory", SEC_STG_SECIMGDIR);
372            errno = ENOTDIR;
373            return -1;
374        }
375    }
376
377    /*
378     * Bind mount /mnt/secure/staging/android_secure -> /mnt/secure/asec so we'll
379     * have a root only accessable mountpoint for it.
380     */
381    if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR, "", MS_BIND, NULL)) {
382        LOGE("Failed to bind mount points %s -> %s (%s)",
383                SEC_STG_SECIMGDIR, SEC_ASECDIR, strerror(errno));
384        return -1;
385    }
386
387    /*
388     * Mount a read-only, zero-sized tmpfs  on <mountpoint>/android_secure to
389     * obscure the underlying directory from everybody - sneaky eh? ;)
390     */
391    if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=000,uid=0,gid=0")) {
392        LOGE("Failed to obscure %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
393        umount("/mnt/asec_secure");
394        return -1;
395    }
396
397    return 0;
398}
399
400int Volume::doMoveMount(const char *src, const char *dst, bool force) {
401    unsigned int flags = MS_MOVE;
402    int retries = 5;
403
404    while(retries--) {
405        if (!mount(src, dst, "", flags, NULL)) {
406            if (mDebug) {
407                LOGD("Moved mount %s -> %s sucessfully", src, dst);
408            }
409            return 0;
410        } else if (errno != EBUSY) {
411            LOGE("Failed to move mount %s -> %s (%s)", src, dst, strerror(errno));
412            return -1;
413        }
414        int action = 0;
415
416        if (force) {
417            if (retries == 1) {
418                action = 2; // SIGKILL
419            } else if (retries == 2) {
420                action = 1; // SIGHUP
421            }
422        }
423        LOGW("Failed to move %s -> %s (%s, retries %d, action %d)",
424                src, dst, strerror(errno), retries, action);
425        Process::killProcessesWithOpenFiles(src, action);
426        usleep(1000*250);
427    }
428
429    errno = EBUSY;
430    LOGE("Giving up on move %s -> %s (%s)", src, dst, strerror(errno));
431    return -1;
432}
433
434int Volume::doUnmount(const char *path, bool force) {
435    int retries = 10;
436
437    if (mDebug) {
438        LOGD("Unmounting {%s}, force = %d", path, force);
439    }
440
441    while (retries--) {
442        if (!umount(path) || errno == EINVAL || errno == ENOENT) {
443            LOGI("%s sucessfully unmounted", path);
444            return 0;
445        }
446
447        int action = 0;
448
449        if (force) {
450            if (retries == 1) {
451                action = 2; // SIGKILL
452            } else if (retries == 2) {
453                action = 1; // SIGHUP
454            }
455        }
456
457        LOGW("Failed to unmount %s (%s, retries %d, action %d)",
458                path, strerror(errno), retries, action);
459
460        Process::killProcessesWithOpenFiles(path, action);
461        usleep(1000*1000);
462    }
463    errno = EBUSY;
464    LOGE("Giving up on unmount %s (%s)", path, strerror(errno));
465    return -1;
466}
467
468int Volume::unmountVol(bool force) {
469    int i, rc;
470
471    if (getState() != Volume::State_Mounted) {
472        LOGE("Volume %s unmount request when not mounted", getLabel());
473        errno = EINVAL;
474        return -1;
475    }
476
477    setState(Volume::State_Unmounting);
478    usleep(1000 * 1000); // Give the framework some time to react
479
480    /*
481     * First move the mountpoint back to our internal staging point
482     * so nobody else can muck with it while we work.
483     */
484    if (doMoveMount(getMountpoint(), SEC_STGDIR, force)) {
485        LOGE("Failed to move mount %s => %s (%s)", getMountpoint(), SEC_STGDIR, strerror(errno));
486        setState(Volume::State_Mounted);
487        return -1;
488    }
489
490    /*
491     * Unmount the tmpfs which was obscuring the asec image directory
492     * from non root users
493     */
494
495    if (doUnmount(Volume::SEC_STG_SECIMGDIR, force)) {
496        LOGE("Failed to unmount tmpfs on %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
497        goto fail_republish;
498    }
499
500    /*
501     * Remove the bindmount we were using to keep a reference to
502     * the previously obscured directory.
503     */
504
505    if (doUnmount(Volume::SEC_ASECDIR, force)) {
506        LOGE("Failed to remove bindmount on %s (%s)", SEC_ASECDIR, strerror(errno));
507        goto fail_remount_tmpfs;
508    }
509
510    /*
511     * Finally, unmount the actual block device from the staging dir
512     */
513    if (doUnmount(Volume::SEC_STGDIR, force)) {
514        LOGE("Failed to unmount %s (%s)", SEC_STGDIR, strerror(errno));
515        goto fail_recreate_bindmount;
516    }
517
518    LOGI("%s unmounted sucessfully", getMountpoint());
519
520    setState(Volume::State_Idle);
521    mCurrentlyMountedKdev = -1;
522    return 0;
523
524    /*
525     * Failure handling - try to restore everything back the way it was
526     */
527fail_recreate_bindmount:
528    if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR, "", MS_BIND, NULL)) {
529        LOGE("Failed to restore bindmount after failure! - Storage will appear offline!");
530        goto out_nomedia;
531    }
532fail_remount_tmpfs:
533    if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=0,uid=0,gid=0")) {
534        LOGE("Failed to restore tmpfs after failure! - Storage will appear offline!");
535        goto out_nomedia;
536    }
537fail_republish:
538    if (doMoveMount(SEC_STGDIR, getMountpoint(), force)) {
539        LOGE("Failed to republish mount after failure! - Storage will appear offline!");
540        goto out_nomedia;
541    }
542
543    setState(Volume::State_Mounted);
544    return -1;
545
546out_nomedia:
547    setState(Volume::State_NoMedia);
548    return -1;
549}
550
551int Volume::initializeMbr(const char *deviceNode) {
552    struct disk_info dinfo;
553
554    memset(&dinfo, 0, sizeof(dinfo));
555
556    if (!(dinfo.part_lst = (struct part_info *) malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
557        LOGE("Failed to malloc prt_lst");
558        return -1;
559    }
560
561    memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));
562    dinfo.device = strdup(deviceNode);
563    dinfo.scheme = PART_SCHEME_MBR;
564    dinfo.sect_size = 512;
565    dinfo.skip_lba = 2048;
566    dinfo.num_lba = 0;
567    dinfo.num_parts = 1;
568
569    struct part_info *pinfo = &dinfo.part_lst[0];
570
571    pinfo->name = strdup("android_sdcard");
572    pinfo->flags |= PART_ACTIVE_FLAG;
573    pinfo->type = PC_PART_TYPE_FAT32;
574    pinfo->len_kb = -1;
575
576    int rc = apply_disk_config(&dinfo, 0);
577
578    if (rc) {
579        LOGE("Failed to apply disk configuration (%d)", rc);
580        goto out;
581    }
582
583 out:
584    free(pinfo->name);
585    free(dinfo.device);
586    free(dinfo.part_lst);
587
588    return rc;
589}
590