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