VolumeManager.cpp revision fff0b47998a722d57eea6a07350bbd7a6032b3cc
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        LOGE("Mountpoint creation failed (%s)", strerror(errno));
224        Loop::destroyByDevice(loopDevice);
225        unlink(asecFileName);
226        return -1;
227    }
228
229    if (Fat::doMount(loopDevice, mountPoint, false, false, ownerUid,
230                     0, 0007, false)) {
231        LOGE("ASEC FAT mount failed (%s)", strerror(errno));
232        Loop::destroyByDevice(loopDevice);
233        unlink(asecFileName);
234        return -1;
235    }
236
237    return 0;
238}
239
240int VolumeManager::finalizeAsec(const char *id) {
241    char asecFileName[255];
242    char loopDevice[255];
243    char mountPoint[255];
244
245    snprintf(asecFileName, sizeof(asecFileName),
246             "/sdcard/android_secure/%s.asec", id);
247
248    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
249        LOGE("Unable to finalize %s (%s)", id, strerror(errno));
250        return -1;
251    }
252
253    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
254    // XXX:
255    if (Fat::doMount(loopDevice, mountPoint, true, true, 0, 0, 0227, false)) {
256        LOGE("ASEC finalize mount failed (%s)", strerror(errno));
257        return -1;
258    }
259
260    LOGD("ASEC %s finalized", id);
261    return 0;
262}
263
264int VolumeManager::destroyAsec(const char *id) {
265    char asecFileName[255];
266    char mountPoint[255];
267
268    snprintf(asecFileName, sizeof(asecFileName),
269             "/sdcard/android_secure/%s.asec", id);
270    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
271
272    if (isMountpointMounted(mountPoint)) {
273        int i, rc;
274        for (i = 0; i < 10; i++) {
275            rc = umount(mountPoint);
276            if (!rc) {
277                break;
278            }
279            if (rc && (errno == EINVAL || errno == ENOENT)) {
280                rc = 0;
281                break;
282            }
283            LOGW("ASEC %s unmount attempt %d failed (%s)",
284                  id, i +1, strerror(errno));
285            usleep(1000 * 250);
286        }
287        if (rc) {
288            LOGE("Failed to unmount ASEC %s for destroy", id);
289            return -1;
290        }
291    }
292
293    char loopDevice[255];
294    if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
295        Loop::destroyByDevice(loopDevice);
296    }
297
298    unlink(asecFileName);
299
300    LOGD("ASEC %s destroyed", id);
301    return 0;
302}
303
304int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
305    char asecFileName[255];
306    char mountPoint[255];
307
308    snprintf(asecFileName, sizeof(asecFileName),
309             "/sdcard/android_secure/%s.asec", id);
310    snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
311
312    if (isMountpointMounted(mountPoint)) {
313        LOGE("ASEC %s already mounted", id);
314        errno = EBUSY;
315        return -1;
316    }
317
318    char loopDevice[255];
319    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
320        if (Loop::getNextAvailable(loopDevice, sizeof(loopDevice))) {
321            LOGE("Unable to find loop device for ASEC mount");
322            return -1;
323        }
324
325        if (Loop::create(loopDevice, asecFileName)) {
326            LOGE("ASEC loop device creation failed (%s)", strerror(errno));
327            return -1;
328        }
329    }
330
331    if (mkdir(mountPoint, 0777)) {
332        LOGE("Mountpoint creation failed (%s)", strerror(errno));
333        return -1;
334    }
335
336    if (Fat::doMount(loopDevice, mountPoint, true, false, ownerUid, 0,
337                     0227, false)) {
338        LOGE("ASEC mount failed (%s)", strerror(errno));
339        return -1;
340    }
341
342    LOGD("ASEC %s mounted", id);
343    return 0;
344}
345
346int VolumeManager::mountVolume(const char *label) {
347    Volume *v = lookupVolume(label);
348
349    if (!v) {
350        errno = ENOENT;
351        return -1;
352    }
353
354    return v->mountVol();
355}
356
357int VolumeManager::shareAvailable(const char *method, bool *avail) {
358
359    if (strcmp(method, "ums")) {
360        errno = ENOSYS;
361        return -1;
362    }
363
364    if (mUsbMassStorageConnected)
365        *avail = true;
366    else
367        *avail = false;
368    return 0;
369}
370
371int VolumeManager::simulate(const char *cmd, const char *arg) {
372
373    if (!strcmp(cmd, "ums")) {
374        if (!strcmp(arg, "connect")) {
375            notifyUmsConnected(true);
376        } else if (!strcmp(arg, "disconnect")) {
377            notifyUmsConnected(false);
378        } else {
379            errno = EINVAL;
380            return -1;
381        }
382    } else {
383        errno = EINVAL;
384        return -1;
385    }
386    return 0;
387}
388
389int VolumeManager::shareVolume(const char *label, const char *method) {
390    Volume *v = lookupVolume(label);
391
392    if (!v) {
393        errno = ENOENT;
394        return -1;
395    }
396
397    /*
398     * Eventually, we'll want to support additional share back-ends,
399     * some of which may work while the media is mounted. For now,
400     * we just support UMS
401     */
402    if (strcmp(method, "ums")) {
403        errno = ENOSYS;
404        return -1;
405    }
406
407    if (v->getState() == Volume::State_NoMedia) {
408        errno = ENODEV;
409        return -1;
410    }
411
412    if (v->getState() != Volume::State_Idle) {
413        // You need to unmount manually befoe sharing
414        errno = EBUSY;
415        return -1;
416    }
417
418    dev_t d = v->getDiskDevice();
419    if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
420        // This volume does not support raw disk access
421        errno = EINVAL;
422        return -1;
423    }
424
425    int fd;
426    char nodepath[255];
427    snprintf(nodepath,
428             sizeof(nodepath), "/dev/block/vold/%d:%d",
429             MAJOR(d), MINOR(d));
430
431    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file",
432                   O_WRONLY)) < 0) {
433        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
434        return -1;
435    }
436
437    if (write(fd, nodepath, strlen(nodepath)) < 0) {
438        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
439        close(fd);
440        return -1;
441    }
442
443    close(fd);
444    v->handleVolumeShared();
445    return 0;
446}
447
448int VolumeManager::unshareVolume(const char *label, const char *method) {
449    Volume *v = lookupVolume(label);
450
451    if (!v) {
452        errno = ENOENT;
453        return -1;
454    }
455
456    if (strcmp(method, "ums")) {
457        errno = ENOSYS;
458        return -1;
459    }
460
461    if (v->getState() != Volume::State_Shared) {
462        errno = EINVAL;
463        return -1;
464    }
465
466    dev_t d = v->getDiskDevice();
467
468    int fd;
469    char nodepath[255];
470    snprintf(nodepath,
471             sizeof(nodepath), "/dev/block/vold/%d:%d",
472             MAJOR(d), MINOR(d));
473
474    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) {
475        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
476        return -1;
477    }
478
479    char ch = 0;
480    if (write(fd, &ch, 1) < 0) {
481        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
482        close(fd);
483        return -1;
484    }
485
486    close(fd);
487    v->handleVolumeUnshared();
488    return 0;
489}
490
491int VolumeManager::unmountVolume(const char *label) {
492    Volume *v = lookupVolume(label);
493
494    if (!v) {
495        errno = ENOENT;
496        return -1;
497    }
498
499    if (v->getState() == Volume::State_NoMedia) {
500        errno = ENODEV;
501        return -1;
502    }
503
504    if (v->getState() != Volume::State_Mounted) {
505        LOGW("Attempt to unmount volume which isn't mounted (%d)\n",
506             v->getState());
507        errno = EBUSY;
508        return -1;
509    }
510
511    return v->unmountVol();
512}
513
514/*
515 * Looks up a volume by it's label or mount-point
516 */
517Volume *VolumeManager::lookupVolume(const char *label) {
518    VolumeCollection::iterator i;
519
520    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
521        if (label[0] == '/') {
522            if (!strcmp(label, (*i)->getMountpoint()))
523                return (*i);
524        } else {
525            if (!strcmp(label, (*i)->getLabel()))
526                return (*i);
527        }
528    }
529    return NULL;
530}
531
532bool VolumeManager::isMountpointMounted(const char *mp)
533{
534    char device[256];
535    char mount_path[256];
536    char rest[256];
537    FILE *fp;
538    char line[1024];
539
540    if (!(fp = fopen("/proc/mounts", "r"))) {
541        LOGE("Error opening /proc/mounts (%s)", strerror(errno));
542        return false;
543    }
544
545    while(fgets(line, sizeof(line), fp)) {
546        line[strlen(line)-1] = '\0';
547        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
548        if (!strcmp(mount_path, mp)) {
549            fclose(fp);
550            return true;
551        }
552
553    }
554
555    fclose(fp);
556    return false;
557}
558
559