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