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