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