VolumeManager.cpp revision fcf24fe62f98c5d44431aa575555569c2c7a29b0
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#include "Asec.h"
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
166    snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id);
167    return 0;
168}
169
170int VolumeManager::createAsec(const char *id, unsigned int numSectors,
171                              const char *fstype, const char *key, int ownerUid) {
172    struct asec_superblock sb;
173    memset(&sb, 0, sizeof(sb));
174
175    sb.magic = ASEC_SB_MAGIC;
176    sb.ver = ASEC_SB_VER;
177
178    if (numSectors < ((1024*1024)/512)) {
179        LOGE("Invalid container size specified (%d sectors)", numSectors);
180        errno = EINVAL;
181        return -1;
182    }
183
184    if (lookupVolume(id)) {
185        LOGE("ASEC id '%s' currently exists", id);
186        errno = EADDRINUSE;
187        return -1;
188    }
189
190    char asecFileName[255];
191    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
192
193    if (!access(asecFileName, F_OK)) {
194        LOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
195             asecFileName, strerror(errno));
196        errno = EADDRINUSE;
197        return -1;
198    }
199
200    /*
201     * Add some headroom
202     */
203    unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2;
204    unsigned numImgSectors = numSectors + fatSize + 2;
205
206    if (numImgSectors % 63) {
207        numImgSectors += (63 - (numImgSectors % 63));
208    }
209
210    // Add +1 for our superblock which is at the end
211    if (Loop::createImageFile(asecFileName, numImgSectors + 1)) {
212        LOGE("ASEC image file creation failed (%s)", strerror(errno));
213        return -1;
214    }
215
216    char loopDevice[255];
217    if (Loop::create(asecFileName, loopDevice, sizeof(loopDevice))) {
218        LOGE("ASEC loop device creation failed (%s)", strerror(errno));
219        unlink(asecFileName);
220        return -1;
221    }
222
223    char dmDevice[255];
224    bool cleanupDm = false;
225
226    if (strcmp(key, "none")) {
227        // XXX: This is all we support for now
228        sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH;
229        if (Devmapper::create(id, loopDevice, key, numImgSectors, dmDevice,
230                             sizeof(dmDevice))) {
231            LOGE("ASEC device mapping failed (%s)", strerror(errno));
232            Loop::destroyByDevice(loopDevice);
233            unlink(asecFileName);
234            return -1;
235        }
236        cleanupDm = true;
237    } else {
238        sb.c_cipher = ASEC_SB_C_CIPHER_NONE;
239        strcpy(dmDevice, loopDevice);
240    }
241
242    /*
243     * Drop down the superblock at the end of the file
244     */
245
246    int sbfd = open(loopDevice, O_RDWR);
247    if (sbfd < 0) {
248        LOGE("Failed to open new DM device for superblock write (%s)", strerror(errno));
249        if (cleanupDm) {
250            Devmapper::destroy(id);
251        }
252        Loop::destroyByDevice(loopDevice);
253        unlink(asecFileName);
254        return -1;
255    }
256
257    if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) {
258        close(sbfd);
259        LOGE("Failed to lseek for superblock (%s)", strerror(errno));
260        if (cleanupDm) {
261            Devmapper::destroy(id);
262        }
263        Loop::destroyByDevice(loopDevice);
264        unlink(asecFileName);
265        return -1;
266    }
267
268    if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) {
269        close(sbfd);
270        LOGE("Failed to write superblock (%s)", strerror(errno));
271        if (cleanupDm) {
272            Devmapper::destroy(id);
273        }
274        Loop::destroyByDevice(loopDevice);
275        unlink(asecFileName);
276        return -1;
277    }
278    close(sbfd);
279
280    if (strcmp(fstype, "none")) {
281        if (strcmp(fstype, "fat")) {
282            LOGW("Unknown fstype '%s' specified for container", fstype);
283        }
284
285        if (Fat::format(dmDevice, numImgSectors)) {
286            LOGE("ASEC FAT format failed (%s)", strerror(errno));
287            if (cleanupDm) {
288                Devmapper::destroy(id);
289            }
290            Loop::destroyByDevice(loopDevice);
291            unlink(asecFileName);
292            return -1;
293        }
294        char mountPoint[255];
295
296        snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
297        if (mkdir(mountPoint, 0777)) {
298            if (errno != EEXIST) {
299                LOGE("Mountpoint creation failed (%s)", strerror(errno));
300                if (cleanupDm) {
301                    Devmapper::destroy(id);
302                }
303                Loop::destroyByDevice(loopDevice);
304                unlink(asecFileName);
305                return -1;
306            }
307        }
308
309        if (Fat::doMount(dmDevice, mountPoint, false, false, ownerUid,
310                         0, 0000, false)) {
311            LOGE("ASEC FAT mount failed (%s)", strerror(errno));
312            if (cleanupDm) {
313                Devmapper::destroy(id);
314            }
315            Loop::destroyByDevice(loopDevice);
316            unlink(asecFileName);
317            return -1;
318        }
319    } else {
320        LOGI("Created raw secure container %s (no filesystem)", id);
321    }
322
323    mActiveContainers->push_back(strdup(id));
324    return 0;
325}
326
327int VolumeManager::finalizeAsec(const char *id) {
328    char asecFileName[255];
329    char loopDevice[255];
330    char mountPoint[255];
331
332    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
333
334    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
335        LOGE("Unable to finalize %s (%s)", id, strerror(errno));
336        return -1;
337    }
338
339    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
340    // XXX:
341    if (Fat::doMount(loopDevice, mountPoint, true, true, 0, 0, 0227, false)) {
342        LOGE("ASEC finalize mount failed (%s)", strerror(errno));
343        return -1;
344    }
345
346    LOGD("ASEC %s finalized", id);
347    return 0;
348}
349
350int VolumeManager::renameAsec(const char *id1, const char *id2) {
351    char *asecFilename1;
352    char *asecFilename2;
353    char mountPoint[255];
354
355    asprintf(&asecFilename1, "%s/%s.asec", Volume::SEC_ASECDIR, id1);
356    asprintf(&asecFilename2, "%s/%s.asec", Volume::SEC_ASECDIR, id2);
357
358    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1);
359    if (isMountpointMounted(mountPoint)) {
360        LOGW("Rename attempt when src mounted");
361        errno = EBUSY;
362        goto out_err;
363    }
364
365    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2);
366    if (isMountpointMounted(mountPoint)) {
367        LOGW("Rename attempt when dst mounted");
368        errno = EBUSY;
369        goto out_err;
370    }
371
372    if (!access(asecFilename2, F_OK)) {
373        LOGE("Rename attempt when dst exists");
374        errno = EADDRINUSE;
375        goto out_err;
376    }
377
378    if (rename(asecFilename1, asecFilename2)) {
379        LOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno));
380        goto out_err;
381    }
382
383    free(asecFilename1);
384    free(asecFilename2);
385    return 0;
386
387out_err:
388    free(asecFilename1);
389    free(asecFilename2);
390    return -1;
391}
392
393#define ASEC_UNMOUNT_RETRIES 5
394int VolumeManager::unmountAsec(const char *id, bool force) {
395    char asecFileName[255];
396    char mountPoint[255];
397
398    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
399    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
400
401    if (!isMountpointMounted(mountPoint)) {
402        LOGE("Unmount request for ASEC %s when not mounted", id);
403        errno = EINVAL;
404        return -1;
405    }
406
407    int i, rc;
408    for (i = 1; i <= ASEC_UNMOUNT_RETRIES; i++) {
409        rc = umount(mountPoint);
410        if (!rc) {
411            break;
412        }
413        if (rc && (errno == EINVAL || errno == ENOENT)) {
414            LOGI("Secure container %s unmounted OK", id);
415            rc = 0;
416            break;
417        }
418        LOGW("ASEC %s unmount attempt %d failed (%s)",
419              id, i, strerror(errno));
420
421        int action = 0; // default is to just complain
422
423        if (force) {
424            if (i > (ASEC_UNMOUNT_RETRIES - 2))
425                action = 2; // SIGKILL
426            else if (i > (ASEC_UNMOUNT_RETRIES - 3))
427                action = 1; // SIGHUP
428        }
429
430        Process::killProcessesWithOpenFiles(mountPoint, action);
431        usleep(1000 * 1000);
432    }
433
434    if (rc) {
435        errno = EBUSY;
436        LOGE("Failed to unmount container %s (%s)", id, strerror(errno));
437        return -1;
438    }
439
440    int retries = 10;
441
442    while(retries--) {
443        if (!rmdir(mountPoint)) {
444            break;
445        }
446
447        LOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno));
448        usleep(1000 * 1000);
449    }
450
451    if (!retries) {
452        LOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno));
453    }
454
455    if (Devmapper::destroy(id) && errno != ENXIO) {
456        LOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
457    }
458
459    char loopDevice[255];
460    if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
461        Loop::destroyByDevice(loopDevice);
462    }
463
464    AsecIdCollection::iterator it;
465    for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) {
466        if (!strcmp(*it, id)) {
467            free(*it);
468            mActiveContainers->erase(it);
469            break;
470        }
471    }
472    if (it == mActiveContainers->end()) {
473        LOGW("mActiveContainers is inconsistent!");
474    }
475    return 0;
476}
477
478int VolumeManager::destroyAsec(const char *id, bool force) {
479    char asecFileName[255];
480    char mountPoint[255];
481
482    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
483    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
484
485    if (isMountpointMounted(mountPoint)) {
486        LOGD("Unmounting container before destroy");
487        if (unmountAsec(id, force)) {
488            LOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
489            return -1;
490        }
491    }
492
493    if (unlink(asecFileName)) {
494        LOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
495        return -1;
496    }
497
498    LOGD("ASEC %s destroyed", id);
499    return 0;
500}
501
502int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
503    char asecFileName[255];
504    char mountPoint[255];
505
506    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
507    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
508
509    if (isMountpointMounted(mountPoint)) {
510        LOGE("ASEC %s already mounted", id);
511        errno = EBUSY;
512        return -1;
513    }
514
515    char loopDevice[255];
516    if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
517        if (Loop::create(asecFileName, loopDevice, sizeof(loopDevice))) {
518            LOGE("ASEC loop device creation failed (%s)", strerror(errno));
519            return -1;
520        }
521        LOGD("New loop device created at %s", loopDevice);
522    } else {
523        LOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
524    }
525
526    char dmDevice[255];
527    bool cleanupDm = false;
528    int fd;
529    unsigned int nr_sec = 0;
530
531    if ((fd = open(loopDevice, O_RDWR)) < 0) {
532        LOGE("Failed to open loopdevice (%s)", strerror(errno));
533        Loop::destroyByDevice(loopDevice);
534        return -1;
535    }
536
537    if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
538        LOGE("Failed to get loop size (%s)", strerror(errno));
539        Loop::destroyByDevice(loopDevice);
540        close(fd);
541        return -1;
542    }
543
544    /*
545     * Validate superblock
546     */
547    struct asec_superblock sb;
548    memset(&sb, 0, sizeof(sb));
549    if (lseek(fd, ((nr_sec-1) * 512), SEEK_SET) < 0) {
550        LOGE("lseek failed (%s)", strerror(errno));
551        close(fd);
552        Loop::destroyByDevice(loopDevice);
553        return -1;
554    }
555    if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) {
556        LOGE("superblock read failed (%s)", strerror(errno));
557        close(fd);
558        Loop::destroyByDevice(loopDevice);
559        return -1;
560    }
561
562    close(fd);
563
564    LOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
565    if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
566        LOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
567        Loop::destroyByDevice(loopDevice);
568        errno = EMEDIUMTYPE;
569        return -1;
570    }
571    nr_sec--; // We don't want the devmapping to extend onto our superblock
572
573    if (strcmp(key, "none")) {
574        if (Devmapper::lookupActive(id, dmDevice, sizeof(dmDevice))) {
575            if (Devmapper::create(id, loopDevice, key, nr_sec,
576                                  dmDevice, sizeof(dmDevice))) {
577                LOGE("ASEC device mapping failed (%s)", strerror(errno));
578                Loop::destroyByDevice(loopDevice);
579                return -1;
580            }
581            LOGD("New devmapper instance created at %s", dmDevice);
582        } else {
583            LOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
584        }
585        cleanupDm = true;
586    } else {
587        strcpy(dmDevice, loopDevice);
588    }
589
590    if (mkdir(mountPoint, 0777)) {
591        if (errno != EEXIST) {
592            LOGE("Mountpoint creation failed (%s)", strerror(errno));
593            if (cleanupDm) {
594                Devmapper::destroy(id);
595            }
596            Loop::destroyByDevice(loopDevice);
597            return -1;
598        }
599    }
600
601    if (Fat::doMount(dmDevice, mountPoint, true, false, ownerUid, 0,
602                     0222, false)) {
603//                     0227, false)) {
604        LOGE("ASEC mount failed (%s)", strerror(errno));
605        if (cleanupDm) {
606            Devmapper::destroy(id);
607        }
608        Loop::destroyByDevice(loopDevice);
609        return -1;
610    }
611
612    mActiveContainers->push_back(strdup(id));
613    LOGD("ASEC %s mounted", id);
614    return 0;
615}
616
617int VolumeManager::mountVolume(const char *label) {
618    Volume *v = lookupVolume(label);
619
620    if (!v) {
621        errno = ENOENT;
622        return -1;
623    }
624
625    return v->mountVol();
626}
627
628int VolumeManager::shareAvailable(const char *method, bool *avail) {
629
630    if (strcmp(method, "ums")) {
631        errno = ENOSYS;
632        return -1;
633    }
634
635    if (mUsbMassStorageConnected)
636        *avail = true;
637    else
638        *avail = false;
639    return 0;
640}
641
642int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
643    Volume *v = lookupVolume(label);
644
645    if (!v) {
646        errno = ENOENT;
647        return -1;
648    }
649
650    if (strcmp(method, "ums")) {
651        errno = ENOSYS;
652        return -1;
653    }
654
655    if (v->getState() != Volume::State_Shared) {
656        *enabled = false;
657    } else {
658        *enabled = true;
659    }
660    return 0;
661}
662
663int VolumeManager::simulate(const char *cmd, const char *arg) {
664
665    if (!strcmp(cmd, "ums")) {
666        if (!strcmp(arg, "connect")) {
667            notifyUmsConnected(true);
668        } else if (!strcmp(arg, "disconnect")) {
669            notifyUmsConnected(false);
670        } else {
671            errno = EINVAL;
672            return -1;
673        }
674    } else {
675        errno = EINVAL;
676        return -1;
677    }
678    return 0;
679}
680
681int VolumeManager::shareVolume(const char *label, const char *method) {
682    Volume *v = lookupVolume(label);
683
684    if (!v) {
685        errno = ENOENT;
686        return -1;
687    }
688
689    /*
690     * Eventually, we'll want to support additional share back-ends,
691     * some of which may work while the media is mounted. For now,
692     * we just support UMS
693     */
694    if (strcmp(method, "ums")) {
695        errno = ENOSYS;
696        return -1;
697    }
698
699    if (v->getState() == Volume::State_NoMedia) {
700        errno = ENODEV;
701        return -1;
702    }
703
704    if (v->getState() != Volume::State_Idle) {
705        // You need to unmount manually befoe sharing
706        errno = EBUSY;
707        return -1;
708    }
709
710    dev_t d = v->getDiskDevice();
711    if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
712        // This volume does not support raw disk access
713        errno = EINVAL;
714        return -1;
715    }
716
717    int fd;
718    char nodepath[255];
719    snprintf(nodepath,
720             sizeof(nodepath), "/dev/block/vold/%d:%d",
721             MAJOR(d), MINOR(d));
722
723    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file",
724                   O_WRONLY)) < 0) {
725        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
726        return -1;
727    }
728
729    if (write(fd, nodepath, strlen(nodepath)) < 0) {
730        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
731        close(fd);
732        return -1;
733    }
734
735    close(fd);
736    v->handleVolumeShared();
737    return 0;
738}
739
740int VolumeManager::unshareVolume(const char *label, const char *method) {
741    Volume *v = lookupVolume(label);
742
743    if (!v) {
744        errno = ENOENT;
745        return -1;
746    }
747
748    if (strcmp(method, "ums")) {
749        errno = ENOSYS;
750        return -1;
751    }
752
753    if (v->getState() != Volume::State_Shared) {
754        errno = EINVAL;
755        return -1;
756    }
757
758    dev_t d = v->getDiskDevice();
759
760    int fd;
761    char nodepath[255];
762    snprintf(nodepath,
763             sizeof(nodepath), "/dev/block/vold/%d:%d",
764             MAJOR(d), MINOR(d));
765
766    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) {
767        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
768        return -1;
769    }
770
771    char ch = 0;
772    if (write(fd, &ch, 1) < 0) {
773        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
774        close(fd);
775        return -1;
776    }
777
778    close(fd);
779    v->handleVolumeUnshared();
780    return 0;
781}
782
783int VolumeManager::unmountVolume(const char *label, bool force) {
784    Volume *v = lookupVolume(label);
785
786    if (!v) {
787        errno = ENOENT;
788        return -1;
789    }
790
791    if (v->getState() == Volume::State_NoMedia) {
792        errno = ENODEV;
793        return -1;
794    }
795
796    if (v->getState() != Volume::State_Mounted) {
797        LOGW("Attempt to unmount volume which isn't mounted (%d)\n",
798             v->getState());
799        errno = EBUSY;
800        return -1;
801    }
802
803    while(mActiveContainers->size()) {
804        AsecIdCollection::iterator it = mActiveContainers->begin();
805        LOGI("Unmounting ASEC %s (dependant on %s)", *it, v->getMountpoint());
806        if (unmountAsec(*it, force)) {
807            LOGE("Failed to unmount ASEC %s (%s)", *it, strerror(errno));
808            return -1;
809        }
810    }
811
812    return v->unmountVol(force);
813}
814
815/*
816 * Looks up a volume by it's label or mount-point
817 */
818Volume *VolumeManager::lookupVolume(const char *label) {
819    VolumeCollection::iterator i;
820
821    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
822        if (label[0] == '/') {
823            if (!strcmp(label, (*i)->getMountpoint()))
824                return (*i);
825        } else {
826            if (!strcmp(label, (*i)->getLabel()))
827                return (*i);
828        }
829    }
830    return NULL;
831}
832
833bool VolumeManager::isMountpointMounted(const char *mp)
834{
835    char device[256];
836    char mount_path[256];
837    char rest[256];
838    FILE *fp;
839    char line[1024];
840
841    if (!(fp = fopen("/proc/mounts", "r"))) {
842        LOGE("Error opening /proc/mounts (%s)", strerror(errno));
843        return false;
844    }
845
846    while(fgets(line, sizeof(line), fp)) {
847        line[strlen(line)-1] = '\0';
848        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
849        if (!strcmp(mount_path, mp)) {
850            fclose(fp);
851            return true;
852        }
853
854    }
855
856    fclose(fp);
857    return false;
858}
859
860