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