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