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