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