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