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