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