VolumeManager.cpp revision 55013f7131ffe094e1c7d929cfc32b3b25096a9b
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 (Fat::format(dmDevice)) {
223        LOGE("ASEC FAT format failed (%s)", strerror(errno));
224        if (cleanupDm) {
225            Devmapper::destroy(id);
226        }
227        Loop::destroyByDevice(loopDevice);
228        unlink(asecFileName);
229        return -1;
230    }
231
232    char mountPoint[255];
233
234    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
235    if (mkdir(mountPoint, 0777)) {
236        if (errno != EEXIST) {
237            LOGE("Mountpoint creation failed (%s)", strerror(errno));
238            if (cleanupDm) {
239                Devmapper::destroy(id);
240            }
241            Loop::destroyByDevice(loopDevice);
242            unlink(asecFileName);
243            return -1;
244        }
245    }
246
247    if (Fat::doMount(dmDevice, mountPoint, false, false, ownerUid,
248                     0, 0000, false)) {
249//                     0, 0007, false)) {
250        LOGE("ASEC FAT mount failed (%s)", strerror(errno));
251        if (cleanupDm) {
252            Devmapper::destroy(id);
253        }
254        Loop::destroyByDevice(loopDevice);
255        unlink(asecFileName);
256        return -1;
257    }
258
259    mActiveContainers->push_back(strdup(id));
260    return 0;
261}
262
263int VolumeManager::finalizeAsec(const char *id) {
264    char asecFileName[255];
265    char loopDevice[255];
266    char mountPoint[255];
267
268    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
269
270    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
271        LOGE("Unable to finalize %s (%s)", id, strerror(errno));
272        return -1;
273    }
274
275    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
276    // XXX:
277    if (Fat::doMount(loopDevice, mountPoint, true, true, 0, 0, 0227, false)) {
278        LOGE("ASEC finalize mount failed (%s)", strerror(errno));
279        return -1;
280    }
281
282    LOGD("ASEC %s finalized", id);
283    return 0;
284}
285
286int VolumeManager::renameAsec(const char *id1, const char *id2) {
287    char *asecFilename1;
288    char *asecFilename2;
289    char mountPoint[255];
290
291    asprintf(&asecFilename1, "%s/%s.asec", Volume::SEC_ASECDIR, id1);
292    asprintf(&asecFilename2, "%s/%s.asec", Volume::SEC_ASECDIR, id2);
293
294    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1);
295    if (isMountpointMounted(mountPoint)) {
296        LOGW("Rename attempt when src mounted");
297        errno = EBUSY;
298        goto out_err;
299    }
300
301    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2);
302    if (isMountpointMounted(mountPoint)) {
303        LOGW("Rename attempt when dst mounted");
304        errno = EBUSY;
305        goto out_err;
306    }
307
308    if (!access(asecFilename2, F_OK)) {
309        LOGE("Rename attempt when dst exists");
310        errno = EADDRINUSE;
311        goto out_err;
312    }
313
314    if (rename(asecFilename1, asecFilename2)) {
315        LOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno));
316        goto out_err;
317    }
318
319    free(asecFilename1);
320    free(asecFilename2);
321    return 0;
322
323out_err:
324    free(asecFilename1);
325    free(asecFilename2);
326    return -1;
327}
328
329#define ASEC_UNMOUNT_RETRIES 5
330int VolumeManager::unmountAsec(const char *id, bool force) {
331    char asecFileName[255];
332    char mountPoint[255];
333
334    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
335    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
336
337    if (!isMountpointMounted(mountPoint)) {
338        LOGE("Unmount request for ASEC %s when not mounted", id);
339        errno = EINVAL;
340        return -1;
341    }
342
343    int i, rc;
344    for (i = 1; i <= ASEC_UNMOUNT_RETRIES; i++) {
345        rc = umount(mountPoint);
346        if (!rc) {
347            break;
348        }
349        if (rc && (errno == EINVAL || errno == ENOENT)) {
350            LOGI("Secure container %s unmounted OK", id);
351            rc = 0;
352            break;
353        }
354        LOGW("ASEC %s unmount attempt %d failed (%s)",
355              id, i, strerror(errno));
356
357        int action = 0; // default is to just complain
358
359        if (force) {
360            if (i > (ASEC_UNMOUNT_RETRIES - 2))
361                action = 2; // SIGKILL
362            else if (i > (ASEC_UNMOUNT_RETRIES - 3))
363                action = 1; // SIGHUP
364        }
365
366        Process::killProcessesWithOpenFiles(mountPoint, action);
367        usleep(1000 * 1000);
368    }
369
370    if (rc) {
371        errno = EBUSY;
372        LOGE("Failed to unmount container %s (%s)", id, strerror(errno));
373        return -1;
374    }
375
376    int retries = 10;
377
378    while(retries--) {
379        if (!rmdir(mountPoint)) {
380            break;
381        }
382
383        LOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno));
384        usleep(1000 * 1000);
385    }
386
387    if (!retries) {
388        LOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno));
389    }
390
391    if (Devmapper::destroy(id) && errno != ENXIO) {
392        LOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
393    }
394
395    char loopDevice[255];
396    if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
397        Loop::destroyByDevice(loopDevice);
398    }
399
400    AsecIdCollection::iterator it;
401    for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) {
402        if (!strcmp(*it, id)) {
403            free(*it);
404            mActiveContainers->erase(it);
405            break;
406        }
407    }
408    if (it == mActiveContainers->end()) {
409        LOGW("mActiveContainers is inconsistent!");
410    }
411    return 0;
412}
413
414int VolumeManager::destroyAsec(const char *id, bool force) {
415    char asecFileName[255];
416    char mountPoint[255];
417
418    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
419    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
420
421    if (isMountpointMounted(mountPoint)) {
422        LOGD("Unmounting container before destroy");
423        if (unmountAsec(id, force)) {
424            LOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
425            return -1;
426        }
427    }
428
429    if (unlink(asecFileName)) {
430        LOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
431        return -1;
432    }
433
434    LOGD("ASEC %s destroyed", id);
435    return 0;
436}
437
438int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
439    char asecFileName[255];
440    char mountPoint[255];
441
442    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
443    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
444
445    if (isMountpointMounted(mountPoint)) {
446        LOGE("ASEC %s already mounted", id);
447        errno = EBUSY;
448        return -1;
449    }
450
451    char loopDevice[255];
452    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
453        if (Loop::create(asecFileName, loopDevice, sizeof(loopDevice))) {
454            LOGE("ASEC loop device creation failed (%s)", strerror(errno));
455            return -1;
456        }
457        LOGD("New loop device created at %s", loopDevice);
458    } else {
459        LOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
460    }
461
462    char dmDevice[255];
463    bool cleanupDm = false;
464    if (strcmp(key, "none")) {
465        if (Devmapper::lookupActive(id, dmDevice, sizeof(dmDevice))) {
466            unsigned int nr_sec = 0;
467            int fd;
468
469            if ((fd = open(loopDevice, O_RDWR)) < 0) {
470                LOGE("Failed to open loopdevice (%s)", strerror(errno));
471                Loop::destroyByDevice(loopDevice);
472                return -1;
473            }
474
475            if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
476                LOGE("Failed to get loop size (%s)", strerror(errno));
477                Loop::destroyByDevice(loopDevice);
478                close(fd);
479                return -1;
480            }
481            close(fd);
482            if (Devmapper::create(id, loopDevice, key, nr_sec,
483                                  dmDevice, sizeof(dmDevice))) {
484                LOGE("ASEC device mapping failed (%s)", strerror(errno));
485                Loop::destroyByDevice(loopDevice);
486                return -1;
487            }
488            LOGD("New devmapper instance created at %s", dmDevice);
489        } else {
490            LOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
491        }
492        cleanupDm = true;
493    } else {
494        strcpy(dmDevice, loopDevice);
495    }
496
497    if (mkdir(mountPoint, 0777)) {
498        if (errno != EEXIST) {
499            LOGE("Mountpoint creation failed (%s)", strerror(errno));
500            if (cleanupDm) {
501                Devmapper::destroy(id);
502            }
503            Loop::destroyByDevice(loopDevice);
504            return -1;
505        }
506    }
507
508    if (Fat::doMount(dmDevice, mountPoint, true, false, ownerUid, 0,
509                     0222, false)) {
510//                     0227, false)) {
511        LOGE("ASEC mount failed (%s)", strerror(errno));
512        if (cleanupDm) {
513            Devmapper::destroy(id);
514        }
515        Loop::destroyByDevice(loopDevice);
516        return -1;
517    }
518
519    mActiveContainers->push_back(strdup(id));
520    LOGD("ASEC %s mounted", id);
521    return 0;
522}
523
524int VolumeManager::mountVolume(const char *label) {
525    Volume *v = lookupVolume(label);
526
527    if (!v) {
528        errno = ENOENT;
529        return -1;
530    }
531
532    return v->mountVol();
533}
534
535int VolumeManager::shareAvailable(const char *method, bool *avail) {
536
537    if (strcmp(method, "ums")) {
538        errno = ENOSYS;
539        return -1;
540    }
541
542    if (mUsbMassStorageConnected)
543        *avail = true;
544    else
545        *avail = false;
546    return 0;
547}
548
549int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
550    Volume *v = lookupVolume(label);
551
552    if (!v) {
553        errno = ENOENT;
554        return -1;
555    }
556
557    if (strcmp(method, "ums")) {
558        errno = ENOSYS;
559        return -1;
560    }
561
562    if (v->getState() != Volume::State_Shared) {
563        *enabled = false;
564    } else {
565        *enabled = true;
566    }
567    return 0;
568}
569
570int VolumeManager::simulate(const char *cmd, const char *arg) {
571
572    if (!strcmp(cmd, "ums")) {
573        if (!strcmp(arg, "connect")) {
574            notifyUmsConnected(true);
575        } else if (!strcmp(arg, "disconnect")) {
576            notifyUmsConnected(false);
577        } else {
578            errno = EINVAL;
579            return -1;
580        }
581    } else {
582        errno = EINVAL;
583        return -1;
584    }
585    return 0;
586}
587
588int VolumeManager::shareVolume(const char *label, const char *method) {
589    Volume *v = lookupVolume(label);
590
591    if (!v) {
592        errno = ENOENT;
593        return -1;
594    }
595
596    /*
597     * Eventually, we'll want to support additional share back-ends,
598     * some of which may work while the media is mounted. For now,
599     * we just support UMS
600     */
601    if (strcmp(method, "ums")) {
602        errno = ENOSYS;
603        return -1;
604    }
605
606    if (v->getState() == Volume::State_NoMedia) {
607        errno = ENODEV;
608        return -1;
609    }
610
611    if (v->getState() != Volume::State_Idle) {
612        // You need to unmount manually befoe sharing
613        errno = EBUSY;
614        return -1;
615    }
616
617    dev_t d = v->getDiskDevice();
618    if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
619        // This volume does not support raw disk access
620        errno = EINVAL;
621        return -1;
622    }
623
624    int fd;
625    char nodepath[255];
626    snprintf(nodepath,
627             sizeof(nodepath), "/dev/block/vold/%d:%d",
628             MAJOR(d), MINOR(d));
629
630    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file",
631                   O_WRONLY)) < 0) {
632        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
633        return -1;
634    }
635
636    if (write(fd, nodepath, strlen(nodepath)) < 0) {
637        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
638        close(fd);
639        return -1;
640    }
641
642    close(fd);
643    v->handleVolumeShared();
644    return 0;
645}
646
647int VolumeManager::unshareVolume(const char *label, const char *method) {
648    Volume *v = lookupVolume(label);
649
650    if (!v) {
651        errno = ENOENT;
652        return -1;
653    }
654
655    if (strcmp(method, "ums")) {
656        errno = ENOSYS;
657        return -1;
658    }
659
660    if (v->getState() != Volume::State_Shared) {
661        errno = EINVAL;
662        return -1;
663    }
664
665    dev_t d = v->getDiskDevice();
666
667    int fd;
668    char nodepath[255];
669    snprintf(nodepath,
670             sizeof(nodepath), "/dev/block/vold/%d:%d",
671             MAJOR(d), MINOR(d));
672
673    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) {
674        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
675        return -1;
676    }
677
678    char ch = 0;
679    if (write(fd, &ch, 1) < 0) {
680        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
681        close(fd);
682        return -1;
683    }
684
685    close(fd);
686    v->handleVolumeUnshared();
687    return 0;
688}
689
690int VolumeManager::unmountVolume(const char *label, bool force) {
691    Volume *v = lookupVolume(label);
692
693    if (!v) {
694        errno = ENOENT;
695        return -1;
696    }
697
698    if (v->getState() == Volume::State_NoMedia) {
699        errno = ENODEV;
700        return -1;
701    }
702
703    if (v->getState() != Volume::State_Mounted) {
704        LOGW("Attempt to unmount volume which isn't mounted (%d)\n",
705             v->getState());
706        errno = EBUSY;
707        return -1;
708    }
709
710    while(mActiveContainers->size()) {
711        AsecIdCollection::iterator it = mActiveContainers->begin();
712        LOGI("Unmounting ASEC %s (dependant on %s)", *it, v->getMountpoint());
713        if (unmountAsec(*it, force)) {
714            LOGE("Failed to unmount ASEC %s (%s)", *it, strerror(errno));
715            return -1;
716        }
717    }
718
719    return v->unmountVol(force);
720}
721
722/*
723 * Looks up a volume by it's label or mount-point
724 */
725Volume *VolumeManager::lookupVolume(const char *label) {
726    VolumeCollection::iterator i;
727
728    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
729        if (label[0] == '/') {
730            if (!strcmp(label, (*i)->getMountpoint()))
731                return (*i);
732        } else {
733            if (!strcmp(label, (*i)->getLabel()))
734                return (*i);
735        }
736    }
737    return NULL;
738}
739
740bool VolumeManager::isMountpointMounted(const char *mp)
741{
742    char device[256];
743    char mount_path[256];
744    char rest[256];
745    FILE *fp;
746    char line[1024];
747
748    if (!(fp = fopen("/proc/mounts", "r"))) {
749        LOGE("Error opening /proc/mounts (%s)", strerror(errno));
750        return false;
751    }
752
753    while(fgets(line, sizeof(line), fp)) {
754        line[strlen(line)-1] = '\0';
755        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
756        if (!strcmp(mount_path, mp)) {
757            fclose(fp);
758            return true;
759        }
760
761    }
762
763    fclose(fp);
764    return false;
765}
766
767