VolumeManager.cpp revision d9a4e358614a0c5f60cc76c0636ee4bb02004a32
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    mBlockDevices = new BlockDeviceCollection();
55    mVolumes = new VolumeCollection();
56    mActiveContainers = new AsecIdCollection();
57    mBroadcaster = NULL;
58    mUsbMassStorageConnected = false;
59}
60
61VolumeManager::~VolumeManager() {
62    delete mBlockDevices;
63    delete mVolumes;
64    delete mActiveContainers;
65}
66
67char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) {
68    MD5_CTX ctx;
69    unsigned char sig[16];
70
71    if (len < 33) {
72        LOGE("Target hash buffer size < 33 bytes (%d)", len);
73        errno = ESPIPE;
74        return NULL;
75    }
76    MD5_Init(&ctx);
77    MD5_Update(&ctx, id, strlen(id));
78    MD5_Final(sig, &ctx);
79
80    memset(buffer, 0, len);
81
82    for (int i = 0; i < 16; i++) {
83        char tmp[3];
84        snprintf(tmp, 3, "%.02x", sig[i]);
85        strcat(buffer, tmp);
86    }
87
88    return buffer;
89}
90
91void VolumeManager::setDebug(bool enable) {
92    mDebug = enable;
93    VolumeCollection::iterator it;
94    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
95        (*it)->setDebug(enable);
96    }
97}
98
99int VolumeManager::start() {
100    return 0;
101}
102
103int VolumeManager::stop() {
104    return 0;
105}
106
107int VolumeManager::addVolume(Volume *v) {
108    mVolumes->push_back(v);
109    return 0;
110}
111
112void VolumeManager::notifyUmsConnected(bool connected) {
113    char msg[255];
114
115    if (connected) {
116        mUsbMassStorageConnected = true;
117    } else {
118        mUsbMassStorageConnected = false;
119    }
120    snprintf(msg, sizeof(msg), "Share method ums now %s",
121             (connected ? "available" : "unavailable"));
122
123    getBroadcaster()->sendBroadcast(ResponseCode::ShareAvailabilityChange,
124                                    msg, false);
125}
126
127void VolumeManager::handleSwitchEvent(NetlinkEvent *evt) {
128    const char *devpath = evt->findParam("DEVPATH");
129    const char *name = evt->findParam("SWITCH_NAME");
130    const char *state = evt->findParam("SWITCH_STATE");
131
132    if (!name || !state) {
133        LOGW("Switch %s event missing name/state info", devpath);
134        return;
135    }
136
137    if (!strcmp(name, "usb_mass_storage")) {
138
139        if (!strcmp(state, "online"))  {
140            notifyUmsConnected(true);
141        } else {
142            notifyUmsConnected(false);
143        }
144    } else {
145        LOGW("Ignoring unknown switch '%s'", name);
146    }
147}
148
149void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
150    const char *devpath = evt->findParam("DEVPATH");
151
152    /* Lookup a volume to handle this device */
153    VolumeCollection::iterator it;
154    bool hit = false;
155    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
156        if (!(*it)->handleBlockEvent(evt)) {
157#ifdef NETLINK_DEBUG
158            LOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
159#endif
160            hit = true;
161            break;
162        }
163    }
164
165    if (!hit) {
166#ifdef NETLINK_DEBUG
167        LOGW("No volumes handled block event for '%s'", devpath);
168#endif
169    }
170}
171
172int VolumeManager::listVolumes(SocketClient *cli) {
173    VolumeCollection::iterator i;
174
175    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
176        char *buffer;
177        asprintf(&buffer, "%s %s %d",
178                 (*i)->getLabel(), (*i)->getMountpoint(),
179                 (*i)->getState());
180        cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
181        free(buffer);
182    }
183    cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
184    return 0;
185}
186
187int VolumeManager::formatVolume(const char *label) {
188    Volume *v = lookupVolume(label);
189
190    if (!v) {
191        errno = ENOENT;
192        return -1;
193    }
194
195    return v->formatVol();
196}
197
198int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
199
200    snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id);
201    return 0;
202}
203
204int VolumeManager::createAsec(const char *id, unsigned int numSectors,
205                              const char *fstype, const char *key, int ownerUid) {
206    struct asec_superblock sb;
207    memset(&sb, 0, sizeof(sb));
208
209    sb.magic = ASEC_SB_MAGIC;
210    sb.ver = ASEC_SB_VER;
211
212    if (numSectors < ((1024*1024)/512)) {
213        LOGE("Invalid container size specified (%d sectors)", numSectors);
214        errno = EINVAL;
215        return -1;
216    }
217
218    if (lookupVolume(id)) {
219        LOGE("ASEC id '%s' currently exists", id);
220        errno = EADDRINUSE;
221        return -1;
222    }
223
224    char asecFileName[255];
225    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
226
227    if (!access(asecFileName, F_OK)) {
228        LOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
229             asecFileName, strerror(errno));
230        errno = EADDRINUSE;
231        return -1;
232    }
233
234    /*
235     * Add some headroom
236     */
237    unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2;
238    unsigned numImgSectors = numSectors + fatSize + 2;
239
240    if (numImgSectors % 63) {
241        numImgSectors += (63 - (numImgSectors % 63));
242    }
243
244    // Add +1 for our superblock which is at the end
245    if (Loop::createImageFile(asecFileName, numImgSectors + 1)) {
246        LOGE("ASEC image file creation failed (%s)", strerror(errno));
247        return -1;
248    }
249
250    char idHash[33];
251    if (!asecHash(id, idHash, sizeof(idHash))) {
252        LOGE("Hash of '%s' failed (%s)", id, strerror(errno));
253        unlink(asecFileName);
254        return -1;
255    }
256
257    char loopDevice[255];
258    if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
259        LOGE("ASEC loop device creation failed (%s)", strerror(errno));
260        unlink(asecFileName);
261        return -1;
262    }
263
264    char dmDevice[255];
265    bool cleanupDm = false;
266
267    if (strcmp(key, "none")) {
268        // XXX: This is all we support for now
269        sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH;
270        if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice,
271                             sizeof(dmDevice))) {
272            LOGE("ASEC device mapping failed (%s)", strerror(errno));
273            Loop::destroyByDevice(loopDevice);
274            unlink(asecFileName);
275            return -1;
276        }
277        cleanupDm = true;
278    } else {
279        sb.c_cipher = ASEC_SB_C_CIPHER_NONE;
280        strcpy(dmDevice, loopDevice);
281    }
282
283    /*
284     * Drop down the superblock at the end of the file
285     */
286
287    int sbfd = open(loopDevice, O_RDWR);
288    if (sbfd < 0) {
289        LOGE("Failed to open new DM device for superblock write (%s)", strerror(errno));
290        if (cleanupDm) {
291            Devmapper::destroy(idHash);
292        }
293        Loop::destroyByDevice(loopDevice);
294        unlink(asecFileName);
295        return -1;
296    }
297
298    if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) {
299        close(sbfd);
300        LOGE("Failed to lseek for superblock (%s)", strerror(errno));
301        if (cleanupDm) {
302            Devmapper::destroy(idHash);
303        }
304        Loop::destroyByDevice(loopDevice);
305        unlink(asecFileName);
306        return -1;
307    }
308
309    if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) {
310        close(sbfd);
311        LOGE("Failed to write superblock (%s)", strerror(errno));
312        if (cleanupDm) {
313            Devmapper::destroy(idHash);
314        }
315        Loop::destroyByDevice(loopDevice);
316        unlink(asecFileName);
317        return -1;
318    }
319    close(sbfd);
320
321    if (strcmp(fstype, "none")) {
322        if (strcmp(fstype, "fat")) {
323            LOGW("Unknown fstype '%s' specified for container", fstype);
324        }
325
326        if (Fat::format(dmDevice, numImgSectors)) {
327            LOGE("ASEC FAT format failed (%s)", strerror(errno));
328            if (cleanupDm) {
329                Devmapper::destroy(idHash);
330            }
331            Loop::destroyByDevice(loopDevice);
332            unlink(asecFileName);
333            return -1;
334        }
335        char mountPoint[255];
336
337        snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
338        if (mkdir(mountPoint, 0777)) {
339            if (errno != EEXIST) {
340                LOGE("Mountpoint creation failed (%s)", strerror(errno));
341                if (cleanupDm) {
342                    Devmapper::destroy(idHash);
343                }
344                Loop::destroyByDevice(loopDevice);
345                unlink(asecFileName);
346                return -1;
347            }
348        }
349
350        if (Fat::doMount(dmDevice, mountPoint, false, false, ownerUid,
351                         0, 0000, false)) {
352            LOGE("ASEC FAT mount failed (%s)", strerror(errno));
353            if (cleanupDm) {
354                Devmapper::destroy(idHash);
355            }
356            Loop::destroyByDevice(loopDevice);
357            unlink(asecFileName);
358            return -1;
359        }
360    } else {
361        LOGI("Created raw secure container %s (no filesystem)", id);
362    }
363
364    mActiveContainers->push_back(strdup(id));
365    return 0;
366}
367
368int VolumeManager::finalizeAsec(const char *id) {
369    char asecFileName[255];
370    char loopDevice[255];
371    char mountPoint[255];
372
373    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
374
375    char idHash[33];
376    if (!asecHash(id, idHash, sizeof(idHash))) {
377        LOGE("Hash of '%s' failed (%s)", id, strerror(errno));
378        return -1;
379    }
380
381    if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
382        LOGE("Unable to finalize %s (%s)", id, strerror(errno));
383        return -1;
384    }
385
386    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
387    // XXX:
388    if (Fat::doMount(loopDevice, mountPoint, true, true, 0, 0, 0227, false)) {
389        LOGE("ASEC finalize mount failed (%s)", strerror(errno));
390        return -1;
391    }
392
393    if (mDebug) {
394        LOGD("ASEC %s finalized", id);
395    }
396    return 0;
397}
398
399int VolumeManager::renameAsec(const char *id1, const char *id2) {
400    char *asecFilename1;
401    char *asecFilename2;
402    char mountPoint[255];
403
404    asprintf(&asecFilename1, "%s/%s.asec", Volume::SEC_ASECDIR, id1);
405    asprintf(&asecFilename2, "%s/%s.asec", Volume::SEC_ASECDIR, id2);
406
407    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1);
408    if (isMountpointMounted(mountPoint)) {
409        LOGW("Rename attempt when src mounted");
410        errno = EBUSY;
411        goto out_err;
412    }
413
414    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2);
415    if (isMountpointMounted(mountPoint)) {
416        LOGW("Rename attempt when dst mounted");
417        errno = EBUSY;
418        goto out_err;
419    }
420
421    if (!access(asecFilename2, F_OK)) {
422        LOGE("Rename attempt when dst exists");
423        errno = EADDRINUSE;
424        goto out_err;
425    }
426
427    if (rename(asecFilename1, asecFilename2)) {
428        LOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno));
429        goto out_err;
430    }
431
432    free(asecFilename1);
433    free(asecFilename2);
434    return 0;
435
436out_err:
437    free(asecFilename1);
438    free(asecFilename2);
439    return -1;
440}
441
442#define ASEC_UNMOUNT_RETRIES 5
443int VolumeManager::unmountAsec(const char *id, bool force) {
444    char asecFileName[255];
445    char mountPoint[255];
446
447    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
448    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
449
450    char idHash[33];
451    if (!asecHash(id, idHash, sizeof(idHash))) {
452        LOGE("Hash of '%s' failed (%s)", id, strerror(errno));
453        return -1;
454    }
455
456    if (!isMountpointMounted(mountPoint)) {
457        LOGE("Unmount request for ASEC %s when not mounted", id);
458        errno = EINVAL;
459        return -1;
460    }
461
462    int i, rc;
463    for (i = 1; i <= ASEC_UNMOUNT_RETRIES; i++) {
464        rc = umount(mountPoint);
465        if (!rc) {
466            break;
467        }
468        if (rc && (errno == EINVAL || errno == ENOENT)) {
469            LOGI("Secure container %s unmounted OK", id);
470            rc = 0;
471            break;
472        }
473        LOGW("ASEC %s unmount attempt %d failed (%s)",
474              id, i, strerror(errno));
475
476        int action = 0; // default is to just complain
477
478        if (force) {
479            if (i > (ASEC_UNMOUNT_RETRIES - 2))
480                action = 2; // SIGKILL
481            else if (i > (ASEC_UNMOUNT_RETRIES - 3))
482                action = 1; // SIGHUP
483        }
484
485        Process::killProcessesWithOpenFiles(mountPoint, action);
486        usleep(1000 * 1000);
487    }
488
489    if (rc) {
490        errno = EBUSY;
491        LOGE("Failed to unmount container %s (%s)", id, strerror(errno));
492        return -1;
493    }
494
495    int retries = 10;
496
497    while(retries--) {
498        if (!rmdir(mountPoint)) {
499            break;
500        }
501
502        LOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno));
503        usleep(1000 * 1000);
504    }
505
506    if (!retries) {
507        LOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno));
508    }
509
510    if (Devmapper::destroy(idHash) && errno != ENXIO) {
511        LOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
512    }
513
514    char loopDevice[255];
515    if (!Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
516        Loop::destroyByDevice(loopDevice);
517    } else {
518        LOGW("Failed to find loop device for {%s} (%s)", asecFileName, strerror(errno));
519    }
520
521    AsecIdCollection::iterator it;
522    for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) {
523        if (!strcmp(*it, id)) {
524            free(*it);
525            mActiveContainers->erase(it);
526            break;
527        }
528    }
529    if (it == mActiveContainers->end()) {
530        LOGW("mActiveContainers is inconsistent!");
531    }
532    return 0;
533}
534
535int VolumeManager::destroyAsec(const char *id, bool force) {
536    char asecFileName[255];
537    char mountPoint[255];
538
539    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
540    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
541
542    if (isMountpointMounted(mountPoint)) {
543        if (mDebug) {
544            LOGD("Unmounting container before destroy");
545        }
546        if (unmountAsec(id, force)) {
547            LOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
548            return -1;
549        }
550    }
551
552    if (unlink(asecFileName)) {
553        LOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
554        return -1;
555    }
556
557    if (mDebug) {
558        LOGD("ASEC %s destroyed", id);
559    }
560    return 0;
561}
562
563int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
564    char asecFileName[255];
565    char mountPoint[255];
566
567    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
568    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
569
570    if (isMountpointMounted(mountPoint)) {
571        LOGE("ASEC %s already mounted", id);
572        errno = EBUSY;
573        return -1;
574    }
575
576    char idHash[33];
577    if (!asecHash(id, idHash, sizeof(idHash))) {
578        LOGE("Hash of '%s' failed (%s)", id, strerror(errno));
579        return -1;
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
934    fclose(fp);
935    return false;
936}
937
938