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