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