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