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