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