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