VolumeManager.cpp revision eb13a90bb96b329d8e24a6c3d4720ae88451d301
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 <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <sys/mount.h>
25
26#include <linux/kdev_t.h>
27
28#define LOG_TAG "Vold"
29
30#include <cutils/log.h>
31
32#include <sysutils/NetlinkEvent.h>
33
34#include "VolumeManager.h"
35#include "DirectVolume.h"
36#include "ResponseCode.h"
37#include "Loop.h"
38#include "Fat.h"
39
40VolumeManager *VolumeManager::sInstance = NULL;
41
42VolumeManager *VolumeManager::Instance() {
43    if (!sInstance)
44        sInstance = new VolumeManager();
45    return sInstance;
46}
47
48VolumeManager::VolumeManager() {
49    mBlockDevices = new BlockDeviceCollection();
50    mVolumes = new VolumeCollection();
51    mBroadcaster = NULL;
52    mUsbMassStorageConnected = false;
53}
54
55VolumeManager::~VolumeManager() {
56    delete mBlockDevices;
57}
58
59int VolumeManager::start() {
60    return 0;
61}
62
63int VolumeManager::stop() {
64    return 0;
65}
66
67int VolumeManager::addVolume(Volume *v) {
68    mVolumes->push_back(v);
69    return 0;
70}
71
72void VolumeManager::notifyUmsConnected(bool connected) {
73    char msg[255];
74
75    if (connected) {
76        mUsbMassStorageConnected = true;
77    } else {
78        mUsbMassStorageConnected = false;
79    }
80    snprintf(msg, sizeof(msg), "Share method ums now %s",
81             (connected ? "available" : "unavailable"));
82
83    getBroadcaster()->sendBroadcast(ResponseCode::ShareAvailabilityChange,
84                                    msg, false);
85}
86
87void VolumeManager::handleSwitchEvent(NetlinkEvent *evt) {
88    const char *devpath = evt->findParam("DEVPATH");
89    const char *name = evt->findParam("SWITCH_NAME");
90    const char *state = evt->findParam("SWITCH_STATE");
91
92    if (!name || !state) {
93        LOGW("Switch %s event missing name/state info", devpath);
94        return;
95    }
96
97    if (!strcmp(name, "usb_mass_storage")) {
98
99        if (!strcmp(state, "online"))  {
100            notifyUmsConnected(true);
101        } else {
102            notifyUmsConnected(false);
103        }
104    } else {
105        LOGW("Ignoring unknown switch '%s'", name);
106    }
107}
108
109void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
110    const char *devpath = evt->findParam("DEVPATH");
111
112    /* Lookup a volume to handle this device */
113    VolumeCollection::iterator it;
114    bool hit = false;
115    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
116        if (!(*it)->handleBlockEvent(evt)) {
117#ifdef NETLINK_DEBUG
118            LOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
119#endif
120            hit = true;
121            break;
122        }
123    }
124
125    if (!hit) {
126#ifdef NETLINK_DEBUG
127        LOGW("No volumes handled block event for '%s'", devpath);
128#endif
129    }
130}
131
132int VolumeManager::listVolumes(SocketClient *cli) {
133    VolumeCollection::iterator i;
134
135    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
136        char *buffer;
137        asprintf(&buffer, "%s %s %d",
138                 (*i)->getLabel(), (*i)->getMountpoint(),
139                 (*i)->getState());
140        cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
141        free(buffer);
142    }
143    cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
144    return 0;
145}
146
147int VolumeManager::formatVolume(const char *label) {
148    Volume *v = lookupVolume(label);
149
150    if (!v) {
151        errno = ENOENT;
152        return -1;
153    }
154
155    return v->formatVol();
156}
157
158int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
159    char mountPoint[255];
160
161    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
162
163    if (!isMountpointMounted(mountPoint)) {
164        errno = ENOENT;
165        return -1;
166    }
167    snprintf(buffer, maxlen, "/asec/%s", id);
168    return 0;
169}
170
171int VolumeManager::createAsec(const char *id, int sizeMb,
172                              const char *fstype, const char *key, int ownerUid) {
173
174    mkdir("/sdcard/android_secure", 0777);
175
176    if (lookupVolume(id)) {
177        LOGE("ASEC volume '%s' currently exists", id);
178        errno = EADDRINUSE;
179        return -1;
180    }
181
182    char asecFileName[255];
183    snprintf(asecFileName, sizeof(asecFileName),
184             "/sdcard/android_secure/%s.asec", id);
185
186    if (!access(asecFileName, F_OK)) {
187        LOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
188             asecFileName, strerror(errno));
189        errno = EADDRINUSE;
190        return -1;
191    }
192
193    if (Loop::createImageFile(asecFileName, sizeMb)) {
194        LOGE("ASEC image file creation failed (%s)", strerror(errno));
195        return -1;
196    }
197
198    char loopDevice[255];
199    if (Loop::getNextAvailable(loopDevice, sizeof(loopDevice))) {
200        unlink(asecFileName);
201        return -1;
202    }
203
204    if (Loop::create(loopDevice, asecFileName)) {
205        LOGE("ASEC loop device creation failed (%s)", strerror(errno));
206        unlink(asecFileName);
207        return -1;
208    }
209
210    /* XXX: Start devmapper */
211
212    if (Fat::format(loopDevice)) {
213        LOGE("ASEC FAT format failed (%s)", strerror(errno));
214        Loop::destroyByDevice(loopDevice);
215        unlink(asecFileName);
216        return -1;
217    }
218
219    char mountPoint[255];
220
221    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
222    if (mkdir(mountPoint, 0777)) {
223        if (errno != EEXIST) {
224            LOGE("Mountpoint creation failed (%s)", strerror(errno));
225            Loop::destroyByDevice(loopDevice);
226            unlink(asecFileName);
227            return -1;
228        }
229    }
230
231    if (Fat::doMount(loopDevice, mountPoint, false, false, ownerUid,
232                     0, 0007, false)) {
233        LOGE("ASEC FAT mount failed (%s)", strerror(errno));
234        Loop::destroyByDevice(loopDevice);
235        unlink(asecFileName);
236        return -1;
237    }
238
239    return 0;
240}
241
242int VolumeManager::finalizeAsec(const char *id) {
243    char asecFileName[255];
244    char loopDevice[255];
245    char mountPoint[255];
246
247    snprintf(asecFileName, sizeof(asecFileName),
248             "/sdcard/android_secure/%s.asec", id);
249
250    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
251        LOGE("Unable to finalize %s (%s)", id, strerror(errno));
252        return -1;
253    }
254
255    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
256    // XXX:
257    if (Fat::doMount(loopDevice, mountPoint, true, true, 0, 0, 0227, false)) {
258        LOGE("ASEC finalize mount failed (%s)", strerror(errno));
259        return -1;
260    }
261
262    LOGD("ASEC %s finalized", id);
263    return 0;
264}
265
266int VolumeManager::destroyAsec(const char *id) {
267    char asecFileName[255];
268    char mountPoint[255];
269
270    snprintf(asecFileName, sizeof(asecFileName),
271             "/sdcard/android_secure/%s.asec", id);
272    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
273
274    if (isMountpointMounted(mountPoint)) {
275        int i, rc;
276        for (i = 0; i < 10; i++) {
277            rc = umount(mountPoint);
278            if (!rc) {
279                break;
280            }
281            if (rc && (errno == EINVAL || errno == ENOENT)) {
282                rc = 0;
283                break;
284            }
285            LOGW("ASEC %s unmount attempt %d failed (%s)",
286                  id, i +1, strerror(errno));
287            usleep(1000 * 250);
288        }
289        if (rc) {
290            LOGE("Failed to unmount ASEC %s for destroy", id);
291            return -1;
292        }
293    }
294
295    char loopDevice[255];
296    if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
297        Loop::destroyByDevice(loopDevice);
298    }
299
300    unlink(asecFileName);
301
302    LOGD("ASEC %s destroyed", id);
303    return 0;
304}
305
306int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
307    char asecFileName[255];
308    char mountPoint[255];
309
310    snprintf(asecFileName, sizeof(asecFileName),
311             "/sdcard/android_secure/%s.asec", id);
312    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
313
314    if (isMountpointMounted(mountPoint)) {
315        LOGE("ASEC %s already mounted", id);
316        errno = EBUSY;
317        return -1;
318    }
319
320    char loopDevice[255];
321    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
322        if (Loop::getNextAvailable(loopDevice, sizeof(loopDevice))) {
323            LOGE("Unable to find loop device for ASEC mount");
324            return -1;
325        }
326
327        if (Loop::create(loopDevice, asecFileName)) {
328            LOGE("ASEC loop device creation failed (%s)", strerror(errno));
329            return -1;
330        }
331    }
332
333    if (mkdir(mountPoint, 0777)) {
334        LOGE("Mountpoint creation failed (%s)", strerror(errno));
335        return -1;
336    }
337
338    if (Fat::doMount(loopDevice, mountPoint, true, false, ownerUid, 0,
339                     0227, false)) {
340        LOGE("ASEC mount failed (%s)", strerror(errno));
341        return -1;
342    }
343
344    LOGD("ASEC %s mounted", id);
345    return 0;
346}
347
348int VolumeManager::mountVolume(const char *label) {
349    Volume *v = lookupVolume(label);
350
351    if (!v) {
352        errno = ENOENT;
353        return -1;
354    }
355
356    return v->mountVol();
357}
358
359int VolumeManager::shareAvailable(const char *method, bool *avail) {
360
361    if (strcmp(method, "ums")) {
362        errno = ENOSYS;
363        return -1;
364    }
365
366    if (mUsbMassStorageConnected)
367        *avail = true;
368    else
369        *avail = false;
370    return 0;
371}
372
373int VolumeManager::simulate(const char *cmd, const char *arg) {
374
375    if (!strcmp(cmd, "ums")) {
376        if (!strcmp(arg, "connect")) {
377            notifyUmsConnected(true);
378        } else if (!strcmp(arg, "disconnect")) {
379            notifyUmsConnected(false);
380        } else {
381            errno = EINVAL;
382            return -1;
383        }
384    } else {
385        errno = EINVAL;
386        return -1;
387    }
388    return 0;
389}
390
391int VolumeManager::shareVolume(const char *label, const char *method) {
392    Volume *v = lookupVolume(label);
393
394    if (!v) {
395        errno = ENOENT;
396        return -1;
397    }
398
399    /*
400     * Eventually, we'll want to support additional share back-ends,
401     * some of which may work while the media is mounted. For now,
402     * we just support UMS
403     */
404    if (strcmp(method, "ums")) {
405        errno = ENOSYS;
406        return -1;
407    }
408
409    if (v->getState() == Volume::State_NoMedia) {
410        errno = ENODEV;
411        return -1;
412    }
413
414    if (v->getState() != Volume::State_Idle) {
415        // You need to unmount manually befoe sharing
416        errno = EBUSY;
417        return -1;
418    }
419
420    dev_t d = v->getDiskDevice();
421    if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
422        // This volume does not support raw disk access
423        errno = EINVAL;
424        return -1;
425    }
426
427    int fd;
428    char nodepath[255];
429    snprintf(nodepath,
430             sizeof(nodepath), "/dev/block/vold/%d:%d",
431             MAJOR(d), MINOR(d));
432
433    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file",
434                   O_WRONLY)) < 0) {
435        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
436        return -1;
437    }
438
439    if (write(fd, nodepath, strlen(nodepath)) < 0) {
440        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
441        close(fd);
442        return -1;
443    }
444
445    close(fd);
446    v->handleVolumeShared();
447    return 0;
448}
449
450int VolumeManager::unshareVolume(const char *label, const char *method) {
451    Volume *v = lookupVolume(label);
452
453    if (!v) {
454        errno = ENOENT;
455        return -1;
456    }
457
458    if (strcmp(method, "ums")) {
459        errno = ENOSYS;
460        return -1;
461    }
462
463    if (v->getState() != Volume::State_Shared) {
464        errno = EINVAL;
465        return -1;
466    }
467
468    dev_t d = v->getDiskDevice();
469
470    int fd;
471    char nodepath[255];
472    snprintf(nodepath,
473             sizeof(nodepath), "/dev/block/vold/%d:%d",
474             MAJOR(d), MINOR(d));
475
476    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) {
477        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
478        return -1;
479    }
480
481    char ch = 0;
482    if (write(fd, &ch, 1) < 0) {
483        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
484        close(fd);
485        return -1;
486    }
487
488    close(fd);
489    v->handleVolumeUnshared();
490    return 0;
491}
492
493int VolumeManager::unmountVolume(const char *label) {
494    Volume *v = lookupVolume(label);
495
496    if (!v) {
497        errno = ENOENT;
498        return -1;
499    }
500
501    if (v->getState() == Volume::State_NoMedia) {
502        errno = ENODEV;
503        return -1;
504    }
505
506    if (v->getState() != Volume::State_Mounted) {
507        LOGW("Attempt to unmount volume which isn't mounted (%d)\n",
508             v->getState());
509        errno = EBUSY;
510        return -1;
511    }
512
513    return v->unmountVol();
514}
515
516/*
517 * Looks up a volume by it's label or mount-point
518 */
519Volume *VolumeManager::lookupVolume(const char *label) {
520    VolumeCollection::iterator i;
521
522    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
523        if (label[0] == '/') {
524            if (!strcmp(label, (*i)->getMountpoint()))
525                return (*i);
526        } else {
527            if (!strcmp(label, (*i)->getLabel()))
528                return (*i);
529        }
530    }
531    return NULL;
532}
533
534bool VolumeManager::isMountpointMounted(const char *mp)
535{
536    char device[256];
537    char mount_path[256];
538    char rest[256];
539    FILE *fp;
540    char line[1024];
541
542    if (!(fp = fopen("/proc/mounts", "r"))) {
543        LOGE("Error opening /proc/mounts (%s)", strerror(errno));
544        return false;
545    }
546
547    while(fgets(line, sizeof(line), fp)) {
548        line[strlen(line)-1] = '\0';
549        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
550        if (!strcmp(mount_path, mp)) {
551            fclose(fp);
552            return true;
553        }
554
555    }
556
557    fclose(fp);
558    return false;
559}
560
561