VolumeManager.cpp revision 2dfe297ec47559dbe2297a72bea71cf515c03797
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(strdup(id));
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 = EINVAL;
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        if (!strcmp(*it, id)) {
628            free(*it);
629            mActiveContainers->erase(it);
630            break;
631        }
632    }
633    if (it == mActiveContainers->end()) {
634        SLOGW("mActiveContainers is inconsistent!");
635    }
636    return 0;
637}
638
639int VolumeManager::destroyAsec(const char *id, bool force) {
640    char asecFileName[255];
641    char mountPoint[255];
642
643    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
644    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
645
646    if (isMountpointMounted(mountPoint)) {
647        if (mDebug) {
648            SLOGD("Unmounting container before destroy");
649        }
650        if (unmountAsec(id, force)) {
651            SLOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
652            return -1;
653        }
654    }
655
656    if (unlink(asecFileName)) {
657        SLOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
658        return -1;
659    }
660
661    if (mDebug) {
662        SLOGD("ASEC %s destroyed", id);
663    }
664    return 0;
665}
666
667int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
668    char asecFileName[255];
669    char mountPoint[255];
670
671    snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id);
672    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
673
674    if (isMountpointMounted(mountPoint)) {
675        SLOGE("ASEC %s already mounted", id);
676        errno = EBUSY;
677        return -1;
678    }
679
680    char idHash[33];
681    if (!asecHash(id, idHash, sizeof(idHash))) {
682        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
683        return -1;
684    }
685
686    char loopDevice[255];
687    if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
688        if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
689            SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
690            return -1;
691        }
692        if (mDebug) {
693            SLOGD("New loop device created at %s", loopDevice);
694        }
695    } else {
696        if (mDebug) {
697            SLOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
698        }
699    }
700
701    char dmDevice[255];
702    bool cleanupDm = false;
703    int fd;
704    unsigned int nr_sec = 0;
705
706    if ((fd = open(loopDevice, O_RDWR)) < 0) {
707        SLOGE("Failed to open loopdevice (%s)", strerror(errno));
708        Loop::destroyByDevice(loopDevice);
709        return -1;
710    }
711
712    if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
713        SLOGE("Failed to get loop size (%s)", strerror(errno));
714        Loop::destroyByDevice(loopDevice);
715        close(fd);
716        return -1;
717    }
718
719    /*
720     * Validate superblock
721     */
722    struct asec_superblock sb;
723    memset(&sb, 0, sizeof(sb));
724    if (lseek(fd, ((nr_sec-1) * 512), SEEK_SET) < 0) {
725        SLOGE("lseek failed (%s)", strerror(errno));
726        close(fd);
727        Loop::destroyByDevice(loopDevice);
728        return -1;
729    }
730    if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) {
731        SLOGE("superblock read failed (%s)", strerror(errno));
732        close(fd);
733        Loop::destroyByDevice(loopDevice);
734        return -1;
735    }
736
737    close(fd);
738
739    if (mDebug) {
740        SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
741    }
742    if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
743        SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
744        Loop::destroyByDevice(loopDevice);
745        errno = EMEDIUMTYPE;
746        return -1;
747    }
748    nr_sec--; // We don't want the devmapping to extend onto our superblock
749
750    if (strcmp(key, "none")) {
751        if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
752            if (Devmapper::create(idHash, loopDevice, key, nr_sec,
753                                  dmDevice, sizeof(dmDevice))) {
754                SLOGE("ASEC device mapping failed (%s)", strerror(errno));
755                Loop::destroyByDevice(loopDevice);
756                return -1;
757            }
758            if (mDebug) {
759                SLOGD("New devmapper instance created at %s", dmDevice);
760            }
761        } else {
762            if (mDebug) {
763                SLOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
764            }
765        }
766        cleanupDm = true;
767    } else {
768        strcpy(dmDevice, loopDevice);
769    }
770
771    if (mkdir(mountPoint, 0777)) {
772        if (errno != EEXIST) {
773            SLOGE("Mountpoint creation failed (%s)", strerror(errno));
774            if (cleanupDm) {
775                Devmapper::destroy(idHash);
776            }
777            Loop::destroyByDevice(loopDevice);
778            return -1;
779        }
780    }
781
782    if (Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0,
783                     0222, false)) {
784//                     0227, false)) {
785        SLOGE("ASEC mount failed (%s)", strerror(errno));
786        if (cleanupDm) {
787            Devmapper::destroy(idHash);
788        }
789        Loop::destroyByDevice(loopDevice);
790        return -1;
791    }
792
793    mActiveContainers->push_back(strdup(id));
794    if (mDebug) {
795        SLOGD("ASEC %s mounted", id);
796    }
797    return 0;
798}
799
800/**
801 * Mounts an image file <code>img</code>.
802 */
803int VolumeManager::mountObb(const char *img, const char *key, int ownerUid) {
804    char mountPoint[255];
805
806    char idHash[33];
807    if (!asecHash(img, idHash, sizeof(idHash))) {
808        SLOGE("Hash of '%s' failed (%s)", img, strerror(errno));
809        return -1;
810    }
811
812    snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
813
814    if (isMountpointMounted(mountPoint)) {
815        SLOGE("Image %s already mounted", img);
816        errno = EBUSY;
817        return -1;
818    }
819
820    char loopDevice[255];
821    if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
822        if (Loop::create(idHash, img, loopDevice, sizeof(loopDevice))) {
823            SLOGE("Image loop device creation failed (%s)", strerror(errno));
824            return -1;
825        }
826        if (mDebug) {
827            SLOGD("New loop device created at %s", loopDevice);
828        }
829    } else {
830        if (mDebug) {
831            SLOGD("Found active loopback for %s at %s", img, loopDevice);
832        }
833    }
834
835    char dmDevice[255];
836    bool cleanupDm = false;
837    int fd;
838    unsigned int nr_sec = 0;
839
840    if ((fd = open(loopDevice, O_RDWR)) < 0) {
841        SLOGE("Failed to open loopdevice (%s)", strerror(errno));
842        Loop::destroyByDevice(loopDevice);
843        return -1;
844    }
845
846    if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
847        SLOGE("Failed to get loop size (%s)", strerror(errno));
848        Loop::destroyByDevice(loopDevice);
849        close(fd);
850        return -1;
851    }
852
853    close(fd);
854
855    if (strcmp(key, "none")) {
856        if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
857            if (Devmapper::create(idHash, loopDevice, key, nr_sec,
858                                  dmDevice, sizeof(dmDevice))) {
859                SLOGE("ASEC device mapping failed (%s)", strerror(errno));
860                Loop::destroyByDevice(loopDevice);
861                return -1;
862            }
863            if (mDebug) {
864                SLOGD("New devmapper instance created at %s", dmDevice);
865            }
866        } else {
867            if (mDebug) {
868                SLOGD("Found active devmapper for %s at %s", img, dmDevice);
869            }
870        }
871        cleanupDm = true;
872    } else {
873        strcpy(dmDevice, loopDevice);
874    }
875
876    if (mkdir(mountPoint, 0755)) {
877        if (errno != EEXIST) {
878            SLOGE("Mountpoint creation failed (%s)", strerror(errno));
879            if (cleanupDm) {
880                Devmapper::destroy(idHash);
881            }
882            Loop::destroyByDevice(loopDevice);
883            return -1;
884        }
885    }
886
887    if (Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0,
888                     0227, false)) {
889        SLOGE("Image mount failed (%s)", strerror(errno));
890        if (cleanupDm) {
891            Devmapper::destroy(idHash);
892        }
893        Loop::destroyByDevice(loopDevice);
894        return -1;
895    }
896
897    mActiveContainers->push_back(strdup(img));
898    if (mDebug) {
899        SLOGD("Image %s mounted", img);
900    }
901    return 0;
902}
903
904int VolumeManager::mountVolume(const char *label) {
905    Volume *v = lookupVolume(label);
906
907    if (!v) {
908        errno = ENOENT;
909        return -1;
910    }
911
912    return v->mountVol();
913}
914
915int VolumeManager::listMountedObbs(SocketClient* cli) {
916    char device[256];
917    char mount_path[256];
918    char rest[256];
919    FILE *fp;
920    char line[1024];
921
922    if (!(fp = fopen("/proc/mounts", "r"))) {
923        SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
924        return -1;
925    }
926
927    // Create a string to compare against that has a trailing slash
928    int loopDirLen = sizeof(Volume::LOOPDIR);
929    char loopDir[loopDirLen + 2];
930    strcpy(loopDir, Volume::LOOPDIR);
931    loopDir[loopDirLen++] = '/';
932    loopDir[loopDirLen] = '\0';
933
934    while(fgets(line, sizeof(line), fp)) {
935        line[strlen(line)-1] = '\0';
936
937        /*
938         * Should look like:
939         * /dev/block/loop0 /mnt/obb/fc99df1323fd36424f864dcb76b76d65 ...
940         */
941        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
942
943        if (!strncmp(mount_path, loopDir, loopDirLen)) {
944            int fd = open(device, O_RDONLY);
945            if (fd >= 0) {
946                struct loop_info64 li;
947                if (ioctl(fd, LOOP_GET_STATUS64, &li) >= 0) {
948                    cli->sendMsg(ResponseCode::AsecListResult,
949                            (const char*) li.lo_file_name, false);
950                }
951                close(fd);
952            }
953        }
954    }
955
956    fclose(fp);
957    return 0;
958}
959
960int VolumeManager::shareAvailable(const char *method, bool *avail) {
961
962    if (strcmp(method, "ums")) {
963        errno = ENOSYS;
964        return -1;
965    }
966
967    *avail = massStorageAvailable();
968    return 0;
969}
970
971int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
972    Volume *v = lookupVolume(label);
973
974    if (!v) {
975        errno = ENOENT;
976        return -1;
977    }
978
979    if (strcmp(method, "ums")) {
980        errno = ENOSYS;
981        return -1;
982    }
983
984    if (v->getState() != Volume::State_Shared) {
985        *enabled = false;
986    } else {
987        *enabled = true;
988    }
989    return 0;
990}
991
992int VolumeManager::simulate(const char *cmd, const char *arg) {
993
994    if (!strcmp(cmd, "ums")) {
995        if (!strcmp(arg, "connect")) {
996            notifyUmsAvailable(true);
997        } else if (!strcmp(arg, "disconnect")) {
998            notifyUmsAvailable(false);
999        } else {
1000            errno = EINVAL;
1001            return -1;
1002        }
1003    } else {
1004        errno = EINVAL;
1005        return -1;
1006    }
1007    return 0;
1008}
1009
1010int VolumeManager::shareVolume(const char *label, const char *method) {
1011    Volume *v = lookupVolume(label);
1012
1013    if (!v) {
1014        errno = ENOENT;
1015        return -1;
1016    }
1017
1018    /*
1019     * Eventually, we'll want to support additional share back-ends,
1020     * some of which may work while the media is mounted. For now,
1021     * we just support UMS
1022     */
1023    if (strcmp(method, "ums")) {
1024        errno = ENOSYS;
1025        return -1;
1026    }
1027
1028    if (v->getState() == Volume::State_NoMedia) {
1029        errno = ENODEV;
1030        return -1;
1031    }
1032
1033    if (v->getState() != Volume::State_Idle) {
1034        // You need to unmount manually befoe sharing
1035        errno = EBUSY;
1036        return -1;
1037    }
1038
1039    dev_t d = v->getShareDevice();
1040    if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
1041        // This volume does not support raw disk access
1042        errno = EINVAL;
1043        return -1;
1044    }
1045
1046    int fd;
1047    char nodepath[255];
1048    snprintf(nodepath,
1049             sizeof(nodepath), "/dev/block/vold/%d:%d",
1050             MAJOR(d), MINOR(d));
1051
1052    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file",
1053                   O_WRONLY)) < 0) {
1054        SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
1055        return -1;
1056    }
1057
1058    if (write(fd, nodepath, strlen(nodepath)) < 0) {
1059        SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
1060        close(fd);
1061        return -1;
1062    }
1063
1064    close(fd);
1065    v->handleVolumeShared();
1066    return 0;
1067}
1068
1069int VolumeManager::unshareVolume(const char *label, const char *method) {
1070    Volume *v = lookupVolume(label);
1071
1072    if (!v) {
1073        errno = ENOENT;
1074        return -1;
1075    }
1076
1077    if (strcmp(method, "ums")) {
1078        errno = ENOSYS;
1079        return -1;
1080    }
1081
1082    if (v->getState() != Volume::State_Shared) {
1083        errno = EINVAL;
1084        return -1;
1085    }
1086
1087    int fd;
1088    if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) {
1089        SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
1090        return -1;
1091    }
1092
1093    char ch = 0;
1094    if (write(fd, &ch, 1) < 0) {
1095        SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
1096        close(fd);
1097        return -1;
1098    }
1099
1100    close(fd);
1101    v->handleVolumeUnshared();
1102    return 0;
1103}
1104
1105int VolumeManager::unmountVolume(const char *label, bool force) {
1106    Volume *v = lookupVolume(label);
1107
1108    if (!v) {
1109        errno = ENOENT;
1110        return -1;
1111    }
1112
1113    if (v->getState() == Volume::State_NoMedia) {
1114        errno = ENODEV;
1115        return -1;
1116    }
1117
1118    if (v->getState() != Volume::State_Mounted) {
1119        SLOGW("Attempt to unmount volume which isn't mounted (%d)\n",
1120             v->getState());
1121        errno = EBUSY;
1122        return -1;
1123    }
1124
1125    cleanupAsec(v, force);
1126
1127    return v->unmountVol(force);
1128}
1129
1130/*
1131 * Looks up a volume by it's label or mount-point
1132 */
1133Volume *VolumeManager::lookupVolume(const char *label) {
1134    VolumeCollection::iterator i;
1135
1136    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1137        if (label[0] == '/') {
1138            if (!strcmp(label, (*i)->getMountpoint()))
1139                return (*i);
1140        } else {
1141            if (!strcmp(label, (*i)->getLabel()))
1142                return (*i);
1143        }
1144    }
1145    return NULL;
1146}
1147
1148bool VolumeManager::isMountpointMounted(const char *mp)
1149{
1150    char device[256];
1151    char mount_path[256];
1152    char rest[256];
1153    FILE *fp;
1154    char line[1024];
1155
1156    if (!(fp = fopen("/proc/mounts", "r"))) {
1157        SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
1158        return false;
1159    }
1160
1161    while(fgets(line, sizeof(line), fp)) {
1162        line[strlen(line)-1] = '\0';
1163        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
1164        if (!strcmp(mount_path, mp)) {
1165            fclose(fp);
1166            return true;
1167        }
1168    }
1169
1170    fclose(fp);
1171    return false;
1172}
1173
1174int VolumeManager::cleanupAsec(Volume *v, bool force) {
1175    while(mActiveContainers->size()) {
1176        AsecIdCollection::iterator it = mActiveContainers->begin();
1177        SLOGI("Unmounting ASEC %s (dependant on %s)", *it, v->getMountpoint());
1178        if (unmountAsec(*it, force)) {
1179            SLOGE("Failed to unmount ASEC %s (%s)", *it, strerror(errno));
1180            return -1;
1181        }
1182    }
1183    return 0;
1184}
1185
1186