• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <fts.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/mount.h>
27 #include <dirent.h>
28 
29 #include <linux/kdev_t.h>
30 
31 #define LOG_TAG "Vold"
32 
33 #include <openssl/md5.h>
34 
35 #include <cutils/log.h>
36 
37 #include <sysutils/NetlinkEvent.h>
38 
39 #include <private/android_filesystem_config.h>
40 
41 #include "VolumeManager.h"
42 #include "DirectVolume.h"
43 #include "ResponseCode.h"
44 #include "Loop.h"
45 #include "Ext4.h"
46 #include "Fat.h"
47 #include "Devmapper.h"
48 #include "Process.h"
49 #include "Asec.h"
50 #include "cryptfs.h"
51 
52 #define MASS_STORAGE_FILE_PATH  "/sys/class/android_usb/android0/f_mass_storage/lun/file"
53 
54 VolumeManager *VolumeManager::sInstance = NULL;
55 
Instance()56 VolumeManager *VolumeManager::Instance() {
57     if (!sInstance)
58         sInstance = new VolumeManager();
59     return sInstance;
60 }
61 
VolumeManager()62 VolumeManager::VolumeManager() {
63     mDebug = false;
64     mVolumes = new VolumeCollection();
65     mActiveContainers = new AsecIdCollection();
66     mBroadcaster = NULL;
67     mUmsSharingCount = 0;
68     mSavedDirtyRatio = -1;
69     // set dirty ratio to 0 when UMS is active
70     mUmsDirtyRatio = 0;
71     mVolManagerDisabled = 0;
72 }
73 
~VolumeManager()74 VolumeManager::~VolumeManager() {
75     delete mVolumes;
76     delete mActiveContainers;
77 }
78 
asecHash(const char * id,char * buffer,size_t len)79 char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) {
80     static const char* digits = "0123456789abcdef";
81 
82     unsigned char sig[MD5_DIGEST_LENGTH];
83 
84     if (buffer == NULL) {
85         SLOGE("Destination buffer is NULL");
86         errno = ESPIPE;
87         return NULL;
88     } else if (id == NULL) {
89         SLOGE("Source buffer is NULL");
90         errno = ESPIPE;
91         return NULL;
92     } else if (len < MD5_ASCII_LENGTH_PLUS_NULL) {
93         SLOGE("Target hash buffer size < %d bytes (%d)",
94                 MD5_ASCII_LENGTH_PLUS_NULL, len);
95         errno = ESPIPE;
96         return NULL;
97     }
98 
99     MD5(reinterpret_cast<const unsigned char*>(id), strlen(id), sig);
100 
101     char *p = buffer;
102     for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
103         *p++ = digits[sig[i] >> 4];
104         *p++ = digits[sig[i] & 0x0F];
105     }
106     *p = '\0';
107 
108     return buffer;
109 }
110 
setDebug(bool enable)111 void VolumeManager::setDebug(bool enable) {
112     mDebug = enable;
113     VolumeCollection::iterator it;
114     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
115         (*it)->setDebug(enable);
116     }
117 }
118 
start()119 int VolumeManager::start() {
120     return 0;
121 }
122 
stop()123 int VolumeManager::stop() {
124     return 0;
125 }
126 
addVolume(Volume * v)127 int VolumeManager::addVolume(Volume *v) {
128     mVolumes->push_back(v);
129     return 0;
130 }
131 
handleBlockEvent(NetlinkEvent * evt)132 void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
133     const char *devpath = evt->findParam("DEVPATH");
134 
135     /* Lookup a volume to handle this device */
136     VolumeCollection::iterator it;
137     bool hit = false;
138     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
139         if (!(*it)->handleBlockEvent(evt)) {
140 #ifdef NETLINK_DEBUG
141             SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
142 #endif
143             hit = true;
144             break;
145         }
146     }
147 
148     if (!hit) {
149 #ifdef NETLINK_DEBUG
150         SLOGW("No volumes handled block event for '%s'", devpath);
151 #endif
152     }
153 }
154 
listVolumes(SocketClient * cli)155 int VolumeManager::listVolumes(SocketClient *cli) {
156     VolumeCollection::iterator i;
157 
158     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
159         char *buffer;
160         asprintf(&buffer, "%s %s %d",
161                  (*i)->getLabel(), (*i)->getMountpoint(),
162                  (*i)->getState());
163         cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
164         free(buffer);
165     }
166     cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
167     return 0;
168 }
169 
formatVolume(const char * label)170 int VolumeManager::formatVolume(const char *label) {
171     Volume *v = lookupVolume(label);
172 
173     if (!v) {
174         errno = ENOENT;
175         return -1;
176     }
177 
178     if (mVolManagerDisabled) {
179         errno = EBUSY;
180         return -1;
181     }
182 
183     return v->formatVol();
184 }
185 
getObbMountPath(const char * sourceFile,char * mountPath,int mountPathLen)186 int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) {
187     char idHash[33];
188     if (!asecHash(sourceFile, idHash, sizeof(idHash))) {
189         SLOGE("Hash of '%s' failed (%s)", sourceFile, strerror(errno));
190         return -1;
191     }
192 
193     memset(mountPath, 0, mountPathLen);
194     int written = snprintf(mountPath, mountPathLen, "%s/%s", Volume::LOOPDIR, idHash);
195     if ((written < 0) || (written >= mountPathLen)) {
196         errno = EINVAL;
197         return -1;
198     }
199 
200     if (access(mountPath, F_OK)) {
201         errno = ENOENT;
202         return -1;
203     }
204 
205     return 0;
206 }
207 
getAsecMountPath(const char * id,char * buffer,int maxlen)208 int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
209     char asecFileName[255];
210 
211     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
212         SLOGE("Couldn't find ASEC %s", id);
213         return -1;
214     }
215 
216     memset(buffer, 0, maxlen);
217     if (access(asecFileName, F_OK)) {
218         errno = ENOENT;
219         return -1;
220     }
221 
222     int written = snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id);
223     if ((written < 0) || (written >= maxlen)) {
224         SLOGE("getAsecMountPath failed for %s: couldn't construct path in buffer", id);
225         errno = EINVAL;
226         return -1;
227     }
228 
229     return 0;
230 }
231 
getAsecFilesystemPath(const char * id,char * buffer,int maxlen)232 int VolumeManager::getAsecFilesystemPath(const char *id, char *buffer, int maxlen) {
233     char asecFileName[255];
234 
235     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
236         SLOGE("Couldn't find ASEC %s", id);
237         return -1;
238     }
239 
240     memset(buffer, 0, maxlen);
241     if (access(asecFileName, F_OK)) {
242         errno = ENOENT;
243         return -1;
244     }
245 
246     int written = snprintf(buffer, maxlen, "%s", asecFileName);
247     if ((written < 0) || (written >= maxlen)) {
248         errno = EINVAL;
249         return -1;
250     }
251 
252     return 0;
253 }
254 
createAsec(const char * id,unsigned int numSectors,const char * fstype,const char * key,const int ownerUid,bool isExternal)255 int VolumeManager::createAsec(const char *id, unsigned int numSectors, const char *fstype,
256         const char *key, const int ownerUid, bool isExternal) {
257     struct asec_superblock sb;
258     memset(&sb, 0, sizeof(sb));
259 
260     const bool wantFilesystem = strcmp(fstype, "none");
261     bool usingExt4 = false;
262     if (wantFilesystem) {
263         usingExt4 = !strcmp(fstype, "ext4");
264         if (usingExt4) {
265             sb.c_opts |= ASEC_SB_C_OPTS_EXT4;
266         } else if (strcmp(fstype, "fat")) {
267             SLOGE("Invalid filesystem type %s", fstype);
268             errno = EINVAL;
269             return -1;
270         }
271     }
272 
273     sb.magic = ASEC_SB_MAGIC;
274     sb.ver = ASEC_SB_VER;
275 
276     if (numSectors < ((1024*1024)/512)) {
277         SLOGE("Invalid container size specified (%d sectors)", numSectors);
278         errno = EINVAL;
279         return -1;
280     }
281 
282     if (lookupVolume(id)) {
283         SLOGE("ASEC id '%s' currently exists", id);
284         errno = EADDRINUSE;
285         return -1;
286     }
287 
288     char asecFileName[255];
289 
290     if (!findAsec(id, asecFileName, sizeof(asecFileName))) {
291         SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
292                 asecFileName, strerror(errno));
293         errno = EADDRINUSE;
294         return -1;
295     }
296 
297     const char *asecDir = isExternal ? Volume::SEC_ASECDIR_EXT : Volume::SEC_ASECDIR_INT;
298 
299     int written = snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", asecDir, id);
300     if ((written < 0) || (size_t(written) >= sizeof(asecFileName))) {
301         errno = EINVAL;
302         return -1;
303     }
304 
305     if (!access(asecFileName, F_OK)) {
306         SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
307                 asecFileName, strerror(errno));
308         errno = EADDRINUSE;
309         return -1;
310     }
311 
312     /*
313      * Add some headroom
314      */
315     unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2;
316     unsigned numImgSectors = numSectors + fatSize + 2;
317 
318     if (numImgSectors % 63) {
319         numImgSectors += (63 - (numImgSectors % 63));
320     }
321 
322     // Add +1 for our superblock which is at the end
323     if (Loop::createImageFile(asecFileName, numImgSectors + 1)) {
324         SLOGE("ASEC image file creation failed (%s)", strerror(errno));
325         return -1;
326     }
327 
328     char idHash[33];
329     if (!asecHash(id, idHash, sizeof(idHash))) {
330         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
331         unlink(asecFileName);
332         return -1;
333     }
334 
335     char loopDevice[255];
336     if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
337         SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
338         unlink(asecFileName);
339         return -1;
340     }
341 
342     char dmDevice[255];
343     bool cleanupDm = false;
344 
345     if (strcmp(key, "none")) {
346         // XXX: This is all we support for now
347         sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH;
348         if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice,
349                              sizeof(dmDevice))) {
350             SLOGE("ASEC device mapping failed (%s)", strerror(errno));
351             Loop::destroyByDevice(loopDevice);
352             unlink(asecFileName);
353             return -1;
354         }
355         cleanupDm = true;
356     } else {
357         sb.c_cipher = ASEC_SB_C_CIPHER_NONE;
358         strcpy(dmDevice, loopDevice);
359     }
360 
361     /*
362      * Drop down the superblock at the end of the file
363      */
364 
365     int sbfd = open(loopDevice, O_RDWR);
366     if (sbfd < 0) {
367         SLOGE("Failed to open new DM device for superblock write (%s)", strerror(errno));
368         if (cleanupDm) {
369             Devmapper::destroy(idHash);
370         }
371         Loop::destroyByDevice(loopDevice);
372         unlink(asecFileName);
373         return -1;
374     }
375 
376     if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) {
377         close(sbfd);
378         SLOGE("Failed to lseek for superblock (%s)", strerror(errno));
379         if (cleanupDm) {
380             Devmapper::destroy(idHash);
381         }
382         Loop::destroyByDevice(loopDevice);
383         unlink(asecFileName);
384         return -1;
385     }
386 
387     if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) {
388         close(sbfd);
389         SLOGE("Failed to write superblock (%s)", strerror(errno));
390         if (cleanupDm) {
391             Devmapper::destroy(idHash);
392         }
393         Loop::destroyByDevice(loopDevice);
394         unlink(asecFileName);
395         return -1;
396     }
397     close(sbfd);
398 
399     if (wantFilesystem) {
400         int formatStatus;
401         char mountPoint[255];
402 
403         int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
404         if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
405             SLOGE("ASEC fs format failed: couldn't construct mountPoint");
406             if (cleanupDm) {
407                 Devmapper::destroy(idHash);
408             }
409             Loop::destroyByDevice(loopDevice);
410             unlink(asecFileName);
411             return -1;
412         }
413 
414         if (usingExt4) {
415             formatStatus = Ext4::format(dmDevice, mountPoint);
416         } else {
417             formatStatus = Fat::format(dmDevice, numImgSectors);
418         }
419 
420         if (formatStatus < 0) {
421             SLOGE("ASEC fs format failed (%s)", strerror(errno));
422             if (cleanupDm) {
423                 Devmapper::destroy(idHash);
424             }
425             Loop::destroyByDevice(loopDevice);
426             unlink(asecFileName);
427             return -1;
428         }
429 
430         if (mkdir(mountPoint, 0000)) {
431             if (errno != EEXIST) {
432                 SLOGE("Mountpoint creation failed (%s)", strerror(errno));
433                 if (cleanupDm) {
434                     Devmapper::destroy(idHash);
435                 }
436                 Loop::destroyByDevice(loopDevice);
437                 unlink(asecFileName);
438                 return -1;
439             }
440         }
441 
442         int mountStatus;
443         if (usingExt4) {
444             mountStatus = Ext4::doMount(dmDevice, mountPoint, false, false, false);
445         } else {
446             mountStatus = Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid, 0, 0000,
447                     false);
448         }
449 
450         if (mountStatus) {
451             SLOGE("ASEC FAT mount failed (%s)", strerror(errno));
452             if (cleanupDm) {
453                 Devmapper::destroy(idHash);
454             }
455             Loop::destroyByDevice(loopDevice);
456             unlink(asecFileName);
457             return -1;
458         }
459 
460         if (usingExt4) {
461             int dirfd = open(mountPoint, O_DIRECTORY);
462             if (dirfd >= 0) {
463                 if (fchown(dirfd, ownerUid, AID_SYSTEM)
464                         || fchmod(dirfd, S_IRUSR | S_IWUSR | S_IXUSR | S_ISGID | S_IRGRP | S_IXGRP)) {
465                     SLOGI("Cannot chown/chmod new ASEC mount point %s", mountPoint);
466                 }
467                 close(dirfd);
468             }
469         }
470     } else {
471         SLOGI("Created raw secure container %s (no filesystem)", id);
472     }
473 
474     mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
475     return 0;
476 }
477 
finalizeAsec(const char * id)478 int VolumeManager::finalizeAsec(const char *id) {
479     char asecFileName[255];
480     char loopDevice[255];
481     char mountPoint[255];
482 
483     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
484         SLOGE("Couldn't find ASEC %s", id);
485         return -1;
486     }
487 
488     char idHash[33];
489     if (!asecHash(id, idHash, sizeof(idHash))) {
490         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
491         return -1;
492     }
493 
494     if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
495         SLOGE("Unable to finalize %s (%s)", id, strerror(errno));
496         return -1;
497     }
498 
499     unsigned int nr_sec = 0;
500     struct asec_superblock sb;
501 
502     if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
503         return -1;
504     }
505 
506     int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
507     if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
508         SLOGE("ASEC finalize failed: couldn't construct mountPoint");
509         return -1;
510     }
511 
512     int result = 0;
513     if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
514         result = Ext4::doMount(loopDevice, mountPoint, true, true, true);
515     } else {
516         result = Fat::doMount(loopDevice, mountPoint, true, true, true, 0, 0, 0227, false);
517     }
518 
519     if (result) {
520         SLOGE("ASEC finalize mount failed (%s)", strerror(errno));
521         return -1;
522     }
523 
524     if (mDebug) {
525         SLOGD("ASEC %s finalized", id);
526     }
527     return 0;
528 }
529 
fixupAsecPermissions(const char * id,gid_t gid,const char * filename)530 int VolumeManager::fixupAsecPermissions(const char *id, gid_t gid, const char* filename) {
531     char asecFileName[255];
532     char loopDevice[255];
533     char mountPoint[255];
534 
535     if (gid < AID_APP) {
536         SLOGE("Group ID is not in application range");
537         return -1;
538     }
539 
540     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
541         SLOGE("Couldn't find ASEC %s", id);
542         return -1;
543     }
544 
545     char idHash[33];
546     if (!asecHash(id, idHash, sizeof(idHash))) {
547         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
548         return -1;
549     }
550 
551     if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
552         SLOGE("Unable fix permissions during lookup on %s (%s)", id, strerror(errno));
553         return -1;
554     }
555 
556     unsigned int nr_sec = 0;
557     struct asec_superblock sb;
558 
559     if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
560         return -1;
561     }
562 
563     int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
564     if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
565         SLOGE("Unable remount to fix permissions for %s: couldn't construct mountpoint", id);
566         return -1;
567     }
568 
569     int result = 0;
570     if ((sb.c_opts & ASEC_SB_C_OPTS_EXT4) == 0) {
571         return 0;
572     }
573 
574     int ret = Ext4::doMount(loopDevice, mountPoint,
575             false /* read-only */,
576             true  /* remount */,
577             false /* executable */);
578     if (ret) {
579         SLOGE("Unable remount to fix permissions for %s (%s)", id, strerror(errno));
580         return -1;
581     }
582 
583     char *paths[] = { mountPoint, NULL };
584 
585     FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL);
586     if (fts) {
587         // Traverse the entire hierarchy and chown to system UID.
588         for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
589             // We don't care about the lost+found directory.
590             if (!strcmp(ftsent->fts_name, "lost+found")) {
591                 continue;
592             }
593 
594             /*
595              * There can only be one file marked as private right now.
596              * This should be more robust, but it satisfies the requirements
597              * we have for right now.
598              */
599             const bool privateFile = !strcmp(ftsent->fts_name, filename);
600 
601             int fd = open(ftsent->fts_accpath, O_NOFOLLOW);
602             if (fd < 0) {
603                 SLOGE("Couldn't open file %s: %s", ftsent->fts_accpath, strerror(errno));
604                 result = -1;
605                 continue;
606             }
607 
608             result |= fchown(fd, AID_SYSTEM, privateFile? gid : AID_SYSTEM);
609 
610             if (ftsent->fts_info & FTS_D) {
611                 result |= fchmod(fd, 0755);
612             } else if (ftsent->fts_info & FTS_F) {
613                 result |= fchmod(fd, privateFile ? 0640 : 0644);
614             }
615             close(fd);
616         }
617         fts_close(fts);
618 
619         // Finally make the directory readable by everyone.
620         int dirfd = open(mountPoint, O_DIRECTORY);
621         if (dirfd < 0 || fchmod(dirfd, 0755)) {
622             SLOGE("Couldn't change owner of existing directory %s: %s", mountPoint, strerror(errno));
623             result |= -1;
624         }
625         close(dirfd);
626     } else {
627         result |= -1;
628     }
629 
630     result |= Ext4::doMount(loopDevice, mountPoint,
631             true /* read-only */,
632             true /* remount */,
633             true /* execute */);
634 
635     if (result) {
636         SLOGE("ASEC fix permissions failed (%s)", strerror(errno));
637         return -1;
638     }
639 
640     if (mDebug) {
641         SLOGD("ASEC %s permissions fixed", id);
642     }
643     return 0;
644 }
645 
renameAsec(const char * id1,const char * id2)646 int VolumeManager::renameAsec(const char *id1, const char *id2) {
647     char asecFilename1[255];
648     char *asecFilename2;
649     char mountPoint[255];
650 
651     const char *dir;
652 
653     if (findAsec(id1, asecFilename1, sizeof(asecFilename1), &dir)) {
654         SLOGE("Couldn't find ASEC %s", id1);
655         return -1;
656     }
657 
658     asprintf(&asecFilename2, "%s/%s.asec", dir, id2);
659 
660     int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1);
661     if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
662         SLOGE("Rename failed: couldn't construct mountpoint");
663         goto out_err;
664     }
665 
666     if (isMountpointMounted(mountPoint)) {
667         SLOGW("Rename attempt when src mounted");
668         errno = EBUSY;
669         goto out_err;
670     }
671 
672     written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2);
673     if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
674         SLOGE("Rename failed: couldn't construct mountpoint2");
675         goto out_err;
676     }
677 
678     if (isMountpointMounted(mountPoint)) {
679         SLOGW("Rename attempt when dst mounted");
680         errno = EBUSY;
681         goto out_err;
682     }
683 
684     if (!access(asecFilename2, F_OK)) {
685         SLOGE("Rename attempt when dst exists");
686         errno = EADDRINUSE;
687         goto out_err;
688     }
689 
690     if (rename(asecFilename1, asecFilename2)) {
691         SLOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno));
692         goto out_err;
693     }
694 
695     free(asecFilename2);
696     return 0;
697 
698 out_err:
699     free(asecFilename2);
700     return -1;
701 }
702 
703 #define UNMOUNT_RETRIES 5
704 #define UNMOUNT_SLEEP_BETWEEN_RETRY_MS (1000 * 1000)
unmountAsec(const char * id,bool force)705 int VolumeManager::unmountAsec(const char *id, bool force) {
706     char asecFileName[255];
707     char mountPoint[255];
708 
709     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
710         SLOGE("Couldn't find ASEC %s", id);
711         return -1;
712     }
713 
714     int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
715     if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
716         SLOGE("ASEC unmount failed for %s: couldn't construct mountpoint", id);
717         return -1;
718     }
719 
720     char idHash[33];
721     if (!asecHash(id, idHash, sizeof(idHash))) {
722         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
723         return -1;
724     }
725 
726     return unmountLoopImage(id, idHash, asecFileName, mountPoint, force);
727 }
728 
unmountObb(const char * fileName,bool force)729 int VolumeManager::unmountObb(const char *fileName, bool force) {
730     char mountPoint[255];
731 
732     char idHash[33];
733     if (!asecHash(fileName, idHash, sizeof(idHash))) {
734         SLOGE("Hash of '%s' failed (%s)", fileName, strerror(errno));
735         return -1;
736     }
737 
738     int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
739     if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
740         SLOGE("OBB unmount failed for %s: couldn't construct mountpoint", fileName);
741         return -1;
742     }
743 
744     return unmountLoopImage(fileName, idHash, fileName, mountPoint, force);
745 }
746 
unmountLoopImage(const char * id,const char * idHash,const char * fileName,const char * mountPoint,bool force)747 int VolumeManager::unmountLoopImage(const char *id, const char *idHash,
748         const char *fileName, const char *mountPoint, bool force) {
749     if (!isMountpointMounted(mountPoint)) {
750         SLOGE("Unmount request for %s when not mounted", id);
751         errno = ENOENT;
752         return -1;
753     }
754 
755     int i, rc;
756     for (i = 1; i <= UNMOUNT_RETRIES; i++) {
757         rc = umount(mountPoint);
758         if (!rc) {
759             break;
760         }
761         if (rc && (errno == EINVAL || errno == ENOENT)) {
762             SLOGI("Container %s unmounted OK", id);
763             rc = 0;
764             break;
765         }
766         SLOGW("%s unmount attempt %d failed (%s)",
767               id, i, strerror(errno));
768 
769         int action = 0; // default is to just complain
770 
771         if (force) {
772             if (i > (UNMOUNT_RETRIES - 2))
773                 action = 2; // SIGKILL
774             else if (i > (UNMOUNT_RETRIES - 3))
775                 action = 1; // SIGHUP
776         }
777 
778         Process::killProcessesWithOpenFiles(mountPoint, action);
779         usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
780     }
781 
782     if (rc) {
783         errno = EBUSY;
784         SLOGE("Failed to unmount container %s (%s)", id, strerror(errno));
785         return -1;
786     }
787 
788     int retries = 10;
789 
790     while(retries--) {
791         if (!rmdir(mountPoint)) {
792             break;
793         }
794 
795         SLOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno));
796         usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
797     }
798 
799     if (!retries) {
800         SLOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno));
801     }
802 
803     if (Devmapper::destroy(idHash) && errno != ENXIO) {
804         SLOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
805     }
806 
807     char loopDevice[255];
808     if (!Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
809         Loop::destroyByDevice(loopDevice);
810     } else {
811         SLOGW("Failed to find loop device for {%s} (%s)", fileName, strerror(errno));
812     }
813 
814     AsecIdCollection::iterator it;
815     for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) {
816         ContainerData* cd = *it;
817         if (!strcmp(cd->id, id)) {
818             free(*it);
819             mActiveContainers->erase(it);
820             break;
821         }
822     }
823     if (it == mActiveContainers->end()) {
824         SLOGW("mActiveContainers is inconsistent!");
825     }
826     return 0;
827 }
828 
destroyAsec(const char * id,bool force)829 int VolumeManager::destroyAsec(const char *id, bool force) {
830     char asecFileName[255];
831     char mountPoint[255];
832 
833     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
834         SLOGE("Couldn't find ASEC %s", id);
835         return -1;
836     }
837 
838     int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
839     if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
840         SLOGE("ASEC destroy failed for %s: couldn't construct mountpoint", id);
841         return -1;
842     }
843 
844     if (isMountpointMounted(mountPoint)) {
845         if (mDebug) {
846             SLOGD("Unmounting container before destroy");
847         }
848         if (unmountAsec(id, force)) {
849             SLOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
850             return -1;
851         }
852     }
853 
854     if (unlink(asecFileName)) {
855         SLOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
856         return -1;
857     }
858 
859     if (mDebug) {
860         SLOGD("ASEC %s destroyed", id);
861     }
862     return 0;
863 }
864 
isAsecInDirectory(const char * dir,const char * asecName) const865 bool VolumeManager::isAsecInDirectory(const char *dir, const char *asecName) const {
866     int dirfd = open(dir, O_DIRECTORY);
867     if (dirfd < 0) {
868         SLOGE("Couldn't open internal ASEC dir (%s)", strerror(errno));
869         return -1;
870     }
871 
872     bool ret = false;
873 
874     if (!faccessat(dirfd, asecName, F_OK, AT_SYMLINK_NOFOLLOW)) {
875         ret = true;
876     }
877 
878     close(dirfd);
879 
880     return ret;
881 }
882 
findAsec(const char * id,char * asecPath,size_t asecPathLen,const char ** directory) const883 int VolumeManager::findAsec(const char *id, char *asecPath, size_t asecPathLen,
884         const char **directory) const {
885     int dirfd, fd;
886     const int idLen = strlen(id);
887     char *asecName;
888 
889     if (asprintf(&asecName, "%s.asec", id) < 0) {
890         SLOGE("Couldn't allocate string to write ASEC name");
891         return -1;
892     }
893 
894     const char *dir;
895     if (isAsecInDirectory(Volume::SEC_ASECDIR_INT, asecName)) {
896         dir = Volume::SEC_ASECDIR_INT;
897     } else if (isAsecInDirectory(Volume::SEC_ASECDIR_EXT, asecName)) {
898         dir = Volume::SEC_ASECDIR_EXT;
899     } else {
900         free(asecName);
901         return -1;
902     }
903 
904     if (directory != NULL) {
905         *directory = dir;
906     }
907 
908     if (asecPath != NULL) {
909         int written = snprintf(asecPath, asecPathLen, "%s/%s", dir, asecName);
910         if ((written < 0) || (size_t(written) >= asecPathLen)) {
911             SLOGE("findAsec failed for %s: couldn't construct ASEC path", id);
912             free(asecName);
913             return -1;
914         }
915     }
916 
917     free(asecName);
918     return 0;
919 }
920 
mountAsec(const char * id,const char * key,int ownerUid)921 int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
922     char asecFileName[255];
923     char mountPoint[255];
924 
925     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
926         SLOGE("Couldn't find ASEC %s", id);
927         return -1;
928     }
929 
930     int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
931     if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
932         SLOGE("ASEC mount failed: couldn't construct mountpoint", id);
933         return -1;
934     }
935 
936     if (isMountpointMounted(mountPoint)) {
937         SLOGE("ASEC %s already mounted", id);
938         errno = EBUSY;
939         return -1;
940     }
941 
942     char idHash[33];
943     if (!asecHash(id, idHash, sizeof(idHash))) {
944         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
945         return -1;
946     }
947 
948     char loopDevice[255];
949     if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
950         if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
951             SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
952             return -1;
953         }
954         if (mDebug) {
955             SLOGD("New loop device created at %s", loopDevice);
956         }
957     } else {
958         if (mDebug) {
959             SLOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
960         }
961     }
962 
963     char dmDevice[255];
964     bool cleanupDm = false;
965     int fd;
966     unsigned int nr_sec = 0;
967     struct asec_superblock sb;
968 
969     if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
970         return -1;
971     }
972 
973     if (mDebug) {
974         SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
975     }
976     if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
977         SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
978         Loop::destroyByDevice(loopDevice);
979         errno = EMEDIUMTYPE;
980         return -1;
981     }
982     nr_sec--; // We don't want the devmapping to extend onto our superblock
983 
984     if (strcmp(key, "none")) {
985         if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
986             if (Devmapper::create(idHash, loopDevice, key, nr_sec,
987                                   dmDevice, sizeof(dmDevice))) {
988                 SLOGE("ASEC device mapping failed (%s)", strerror(errno));
989                 Loop::destroyByDevice(loopDevice);
990                 return -1;
991             }
992             if (mDebug) {
993                 SLOGD("New devmapper instance created at %s", dmDevice);
994             }
995         } else {
996             if (mDebug) {
997                 SLOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
998             }
999         }
1000         cleanupDm = true;
1001     } else {
1002         strcpy(dmDevice, loopDevice);
1003     }
1004 
1005     if (mkdir(mountPoint, 0000)) {
1006         if (errno != EEXIST) {
1007             SLOGE("Mountpoint creation failed (%s)", strerror(errno));
1008             if (cleanupDm) {
1009                 Devmapper::destroy(idHash);
1010             }
1011             Loop::destroyByDevice(loopDevice);
1012             return -1;
1013         }
1014     }
1015 
1016     /*
1017      * The device mapper node needs to be created. Sometimes it takes a
1018      * while. Wait for up to 1 second. We could also inspect incoming uevents,
1019      * but that would take more effort.
1020      */
1021     int tries = 25;
1022     while (tries--) {
1023         if (!access(dmDevice, F_OK) || errno != ENOENT) {
1024             break;
1025         }
1026         usleep(40 * 1000);
1027     }
1028 
1029     int result;
1030     if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
1031         result = Ext4::doMount(dmDevice, mountPoint, true, false, true);
1032     } else {
1033         result = Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0, 0222, false);
1034     }
1035 
1036     if (result) {
1037         SLOGE("ASEC mount failed (%s)", strerror(errno));
1038         if (cleanupDm) {
1039             Devmapper::destroy(idHash);
1040         }
1041         Loop::destroyByDevice(loopDevice);
1042         return -1;
1043     }
1044 
1045     mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
1046     if (mDebug) {
1047         SLOGD("ASEC %s mounted", id);
1048     }
1049     return 0;
1050 }
1051 
getVolumeForFile(const char * fileName)1052 Volume* VolumeManager::getVolumeForFile(const char *fileName) {
1053     VolumeCollection::iterator i;
1054 
1055     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1056         const char* mountPoint = (*i)->getMountpoint();
1057         if (!strncmp(fileName, mountPoint, strlen(mountPoint))) {
1058             return *i;
1059         }
1060     }
1061 
1062     return NULL;
1063 }
1064 
1065 /**
1066  * Mounts an image file <code>img</code>.
1067  */
mountObb(const char * img,const char * key,int ownerGid)1068 int VolumeManager::mountObb(const char *img, const char *key, int ownerGid) {
1069     char mountPoint[255];
1070 
1071     char idHash[33];
1072     if (!asecHash(img, idHash, sizeof(idHash))) {
1073         SLOGE("Hash of '%s' failed (%s)", img, strerror(errno));
1074         return -1;
1075     }
1076 
1077     int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
1078     if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
1079         SLOGE("OBB mount failed: couldn't construct mountpoint", img);
1080         return -1;
1081     }
1082 
1083     if (isMountpointMounted(mountPoint)) {
1084         SLOGE("Image %s already mounted", img);
1085         errno = EBUSY;
1086         return -1;
1087     }
1088 
1089     char loopDevice[255];
1090     if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
1091         if (Loop::create(idHash, img, loopDevice, sizeof(loopDevice))) {
1092             SLOGE("Image loop device creation failed (%s)", strerror(errno));
1093             return -1;
1094         }
1095         if (mDebug) {
1096             SLOGD("New loop device created at %s", loopDevice);
1097         }
1098     } else {
1099         if (mDebug) {
1100             SLOGD("Found active loopback for %s at %s", img, loopDevice);
1101         }
1102     }
1103 
1104     char dmDevice[255];
1105     bool cleanupDm = false;
1106     int fd;
1107     unsigned int nr_sec = 0;
1108 
1109     if ((fd = open(loopDevice, O_RDWR)) < 0) {
1110         SLOGE("Failed to open loopdevice (%s)", strerror(errno));
1111         Loop::destroyByDevice(loopDevice);
1112         return -1;
1113     }
1114 
1115     if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
1116         SLOGE("Failed to get loop size (%s)", strerror(errno));
1117         Loop::destroyByDevice(loopDevice);
1118         close(fd);
1119         return -1;
1120     }
1121 
1122     close(fd);
1123 
1124     if (strcmp(key, "none")) {
1125         if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
1126             if (Devmapper::create(idHash, loopDevice, key, nr_sec,
1127                                   dmDevice, sizeof(dmDevice))) {
1128                 SLOGE("ASEC device mapping failed (%s)", strerror(errno));
1129                 Loop::destroyByDevice(loopDevice);
1130                 return -1;
1131             }
1132             if (mDebug) {
1133                 SLOGD("New devmapper instance created at %s", dmDevice);
1134             }
1135         } else {
1136             if (mDebug) {
1137                 SLOGD("Found active devmapper for %s at %s", img, dmDevice);
1138             }
1139         }
1140         cleanupDm = true;
1141     } else {
1142         strcpy(dmDevice, loopDevice);
1143     }
1144 
1145     if (mkdir(mountPoint, 0755)) {
1146         if (errno != EEXIST) {
1147             SLOGE("Mountpoint creation failed (%s)", strerror(errno));
1148             if (cleanupDm) {
1149                 Devmapper::destroy(idHash);
1150             }
1151             Loop::destroyByDevice(loopDevice);
1152             return -1;
1153         }
1154     }
1155 
1156     if (Fat::doMount(dmDevice, mountPoint, true, false, true, 0, ownerGid,
1157                      0227, false)) {
1158         SLOGE("Image mount failed (%s)", strerror(errno));
1159         if (cleanupDm) {
1160             Devmapper::destroy(idHash);
1161         }
1162         Loop::destroyByDevice(loopDevice);
1163         return -1;
1164     }
1165 
1166     mActiveContainers->push_back(new ContainerData(strdup(img), OBB));
1167     if (mDebug) {
1168         SLOGD("Image %s mounted", img);
1169     }
1170     return 0;
1171 }
1172 
mountVolume(const char * label)1173 int VolumeManager::mountVolume(const char *label) {
1174     Volume *v = lookupVolume(label);
1175 
1176     if (!v) {
1177         errno = ENOENT;
1178         return -1;
1179     }
1180 
1181     return v->mountVol();
1182 }
1183 
listMountedObbs(SocketClient * cli)1184 int VolumeManager::listMountedObbs(SocketClient* cli) {
1185     char device[256];
1186     char mount_path[256];
1187     char rest[256];
1188     FILE *fp;
1189     char line[1024];
1190 
1191     if (!(fp = fopen("/proc/mounts", "r"))) {
1192         SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
1193         return -1;
1194     }
1195 
1196     // Create a string to compare against that has a trailing slash
1197     int loopDirLen = strlen(Volume::LOOPDIR);
1198     char loopDir[loopDirLen + 2];
1199     strcpy(loopDir, Volume::LOOPDIR);
1200     loopDir[loopDirLen++] = '/';
1201     loopDir[loopDirLen] = '\0';
1202 
1203     while(fgets(line, sizeof(line), fp)) {
1204         line[strlen(line)-1] = '\0';
1205 
1206         /*
1207          * Should look like:
1208          * /dev/block/loop0 /mnt/obb/fc99df1323fd36424f864dcb76b76d65 ...
1209          */
1210         sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
1211 
1212         if (!strncmp(mount_path, loopDir, loopDirLen)) {
1213             int fd = open(device, O_RDONLY);
1214             if (fd >= 0) {
1215                 struct loop_info64 li;
1216                 if (ioctl(fd, LOOP_GET_STATUS64, &li) >= 0) {
1217                     cli->sendMsg(ResponseCode::AsecListResult,
1218                             (const char*) li.lo_file_name, false);
1219                 }
1220                 close(fd);
1221             }
1222         }
1223     }
1224 
1225     fclose(fp);
1226     return 0;
1227 }
1228 
shareEnabled(const char * label,const char * method,bool * enabled)1229 int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
1230     Volume *v = lookupVolume(label);
1231 
1232     if (!v) {
1233         errno = ENOENT;
1234         return -1;
1235     }
1236 
1237     if (strcmp(method, "ums")) {
1238         errno = ENOSYS;
1239         return -1;
1240     }
1241 
1242     if (v->getState() != Volume::State_Shared) {
1243         *enabled = false;
1244     } else {
1245         *enabled = true;
1246     }
1247     return 0;
1248 }
1249 
shareVolume(const char * label,const char * method)1250 int VolumeManager::shareVolume(const char *label, const char *method) {
1251     Volume *v = lookupVolume(label);
1252 
1253     if (!v) {
1254         errno = ENOENT;
1255         return -1;
1256     }
1257 
1258     /*
1259      * Eventually, we'll want to support additional share back-ends,
1260      * some of which may work while the media is mounted. For now,
1261      * we just support UMS
1262      */
1263     if (strcmp(method, "ums")) {
1264         errno = ENOSYS;
1265         return -1;
1266     }
1267 
1268     if (v->getState() == Volume::State_NoMedia) {
1269         errno = ENODEV;
1270         return -1;
1271     }
1272 
1273     if (v->getState() != Volume::State_Idle) {
1274         // You need to unmount manually befoe sharing
1275         errno = EBUSY;
1276         return -1;
1277     }
1278 
1279     if (mVolManagerDisabled) {
1280         errno = EBUSY;
1281         return -1;
1282     }
1283 
1284     dev_t d = v->getShareDevice();
1285     if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
1286         // This volume does not support raw disk access
1287         errno = EINVAL;
1288         return -1;
1289     }
1290 
1291     int fd;
1292     char nodepath[255];
1293     int written = snprintf(nodepath,
1294              sizeof(nodepath), "/dev/block/vold/%d:%d",
1295              MAJOR(d), MINOR(d));
1296 
1297     if ((written < 0) || (size_t(written) >= sizeof(nodepath))) {
1298         SLOGE("shareVolume failed: couldn't construct nodepath");
1299         return -1;
1300     }
1301 
1302     if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
1303         SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
1304         return -1;
1305     }
1306 
1307     if (write(fd, nodepath, strlen(nodepath)) < 0) {
1308         SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
1309         close(fd);
1310         return -1;
1311     }
1312 
1313     close(fd);
1314     v->handleVolumeShared();
1315     if (mUmsSharingCount++ == 0) {
1316         FILE* fp;
1317         mSavedDirtyRatio = -1; // in case we fail
1318         if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
1319             char line[16];
1320             if (fgets(line, sizeof(line), fp) && sscanf(line, "%d", &mSavedDirtyRatio)) {
1321                 fprintf(fp, "%d\n", mUmsDirtyRatio);
1322             } else {
1323                 SLOGE("Failed to read dirty_ratio (%s)", strerror(errno));
1324             }
1325             fclose(fp);
1326         } else {
1327             SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
1328         }
1329     }
1330     return 0;
1331 }
1332 
unshareVolume(const char * label,const char * method)1333 int VolumeManager::unshareVolume(const char *label, const char *method) {
1334     Volume *v = lookupVolume(label);
1335 
1336     if (!v) {
1337         errno = ENOENT;
1338         return -1;
1339     }
1340 
1341     if (strcmp(method, "ums")) {
1342         errno = ENOSYS;
1343         return -1;
1344     }
1345 
1346     if (v->getState() != Volume::State_Shared) {
1347         errno = EINVAL;
1348         return -1;
1349     }
1350 
1351     int fd;
1352     if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
1353         SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
1354         return -1;
1355     }
1356 
1357     char ch = 0;
1358     if (write(fd, &ch, 1) < 0) {
1359         SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
1360         close(fd);
1361         return -1;
1362     }
1363 
1364     close(fd);
1365     v->handleVolumeUnshared();
1366     if (--mUmsSharingCount == 0 && mSavedDirtyRatio != -1) {
1367         FILE* fp;
1368         if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
1369             fprintf(fp, "%d\n", mSavedDirtyRatio);
1370             fclose(fp);
1371         } else {
1372             SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
1373         }
1374         mSavedDirtyRatio = -1;
1375     }
1376     return 0;
1377 }
1378 
vold_disableVol(const char * label)1379 extern "C" int vold_disableVol(const char *label) {
1380     VolumeManager *vm = VolumeManager::Instance();
1381     vm->disableVolumeManager();
1382     vm->unshareVolume(label, "ums");
1383     return vm->unmountVolume(label, true, false);
1384 }
1385 
vold_getNumDirectVolumes(void)1386 extern "C" int vold_getNumDirectVolumes(void) {
1387     VolumeManager *vm = VolumeManager::Instance();
1388     return vm->getNumDirectVolumes();
1389 }
1390 
getNumDirectVolumes(void)1391 int VolumeManager::getNumDirectVolumes(void) {
1392     VolumeCollection::iterator i;
1393     int n=0;
1394 
1395     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1396         if ((*i)->getShareDevice() != (dev_t)0) {
1397             n++;
1398         }
1399     }
1400     return n;
1401 }
1402 
vold_getDirectVolumeList(struct volume_info * vol_list)1403 extern "C" int vold_getDirectVolumeList(struct volume_info *vol_list) {
1404     VolumeManager *vm = VolumeManager::Instance();
1405     return vm->getDirectVolumeList(vol_list);
1406 }
1407 
getDirectVolumeList(struct volume_info * vol_list)1408 int VolumeManager::getDirectVolumeList(struct volume_info *vol_list) {
1409     VolumeCollection::iterator i;
1410     int n=0;
1411     dev_t d;
1412 
1413     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1414         if ((d=(*i)->getShareDevice()) != (dev_t)0) {
1415             (*i)->getVolInfo(&vol_list[n]);
1416             snprintf(vol_list[n].blk_dev, sizeof(vol_list[n].blk_dev),
1417                      "/dev/block/vold/%d:%d",MAJOR(d), MINOR(d));
1418             n++;
1419         }
1420     }
1421 
1422     return 0;
1423 }
1424 
unmountVolume(const char * label,bool force,bool revert)1425 int VolumeManager::unmountVolume(const char *label, bool force, bool revert) {
1426     Volume *v = lookupVolume(label);
1427 
1428     if (!v) {
1429         errno = ENOENT;
1430         return -1;
1431     }
1432 
1433     if (v->getState() == Volume::State_NoMedia) {
1434         errno = ENODEV;
1435         return -1;
1436     }
1437 
1438     if (v->getState() != Volume::State_Mounted) {
1439         SLOGW("Attempt to unmount volume which isn't mounted (%d)\n",
1440              v->getState());
1441         errno = EBUSY;
1442         return UNMOUNT_NOT_MOUNTED_ERR;
1443     }
1444 
1445     cleanupAsec(v, force);
1446 
1447     return v->unmountVol(force, revert);
1448 }
1449 
vold_unmountAllAsecs(void)1450 extern "C" int vold_unmountAllAsecs(void) {
1451     int rc;
1452 
1453     VolumeManager *vm = VolumeManager::Instance();
1454     rc = vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_EXT);
1455     if (vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_INT)) {
1456         rc = -1;
1457     }
1458     return rc;
1459 }
1460 
1461 #define ID_BUF_LEN 256
1462 #define ASEC_SUFFIX ".asec"
1463 #define ASEC_SUFFIX_LEN (sizeof(ASEC_SUFFIX) - 1)
unmountAllAsecsInDir(const char * directory)1464 int VolumeManager::unmountAllAsecsInDir(const char *directory) {
1465     DIR *d = opendir(directory);
1466     int rc = 0;
1467 
1468     if (!d) {
1469         SLOGE("Could not open asec dir %s", directory);
1470         return -1;
1471     }
1472 
1473     size_t dirent_len = offsetof(struct dirent, d_name) +
1474             fpathconf(dirfd(d), _PC_NAME_MAX) + 1;
1475 
1476     struct dirent *dent = (struct dirent *) malloc(dirent_len);
1477     if (dent == NULL) {
1478         SLOGE("Failed to allocate memory for asec dir");
1479         return -1;
1480     }
1481 
1482     struct dirent *result;
1483     while (!readdir_r(d, dent, &result) && result != NULL) {
1484         if (dent->d_name[0] == '.')
1485             continue;
1486         if (dent->d_type != DT_REG)
1487             continue;
1488         size_t name_len = strlen(dent->d_name);
1489         if (name_len > 5 && name_len < (ID_BUF_LEN + ASEC_SUFFIX_LEN - 1) &&
1490                 !strcmp(&dent->d_name[name_len - 5], ASEC_SUFFIX)) {
1491             char id[ID_BUF_LEN];
1492             strlcpy(id, dent->d_name, name_len - 4);
1493             if (unmountAsec(id, true)) {
1494                 /* Register the error, but try to unmount more asecs */
1495                 rc = -1;
1496             }
1497         }
1498     }
1499     closedir(d);
1500 
1501     free(dent);
1502 
1503     return rc;
1504 }
1505 
1506 /*
1507  * Looks up a volume by it's label or mount-point
1508  */
lookupVolume(const char * label)1509 Volume *VolumeManager::lookupVolume(const char *label) {
1510     VolumeCollection::iterator i;
1511 
1512     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1513         if (label[0] == '/') {
1514             if (!strcmp(label, (*i)->getMountpoint()))
1515                 return (*i);
1516         } else {
1517             if (!strcmp(label, (*i)->getLabel()))
1518                 return (*i);
1519         }
1520     }
1521     return NULL;
1522 }
1523 
isMountpointMounted(const char * mp)1524 bool VolumeManager::isMountpointMounted(const char *mp)
1525 {
1526     char device[256];
1527     char mount_path[256];
1528     char rest[256];
1529     FILE *fp;
1530     char line[1024];
1531 
1532     if (!(fp = fopen("/proc/mounts", "r"))) {
1533         SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
1534         return false;
1535     }
1536 
1537     while(fgets(line, sizeof(line), fp)) {
1538         line[strlen(line)-1] = '\0';
1539         sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
1540         if (!strcmp(mount_path, mp)) {
1541             fclose(fp);
1542             return true;
1543         }
1544     }
1545 
1546     fclose(fp);
1547     return false;
1548 }
1549 
cleanupAsec(Volume * v,bool force)1550 int VolumeManager::cleanupAsec(Volume *v, bool force) {
1551     int rc = unmountAllAsecsInDir(Volume::SEC_ASECDIR_EXT);
1552 
1553     AsecIdCollection toUnmount;
1554     // Find the remaining OBB files that are on external storage.
1555     for (AsecIdCollection::iterator it = mActiveContainers->begin(); it != mActiveContainers->end();
1556             ++it) {
1557         ContainerData* cd = *it;
1558 
1559         if (cd->type == ASEC) {
1560             // nothing
1561         } else if (cd->type == OBB) {
1562             if (v == getVolumeForFile(cd->id)) {
1563                 toUnmount.push_back(cd);
1564             }
1565         } else {
1566             SLOGE("Unknown container type %d!", cd->type);
1567         }
1568     }
1569 
1570     for (AsecIdCollection::iterator it = toUnmount.begin(); it != toUnmount.end(); ++it) {
1571         ContainerData *cd = *it;
1572         SLOGI("Unmounting ASEC %s (dependant on %s)", cd->id, v->getMountpoint());
1573         if (unmountObb(cd->id, force)) {
1574             SLOGE("Failed to unmount OBB %s (%s)", cd->id, strerror(errno));
1575             rc = -1;
1576         }
1577     }
1578 
1579     return rc;
1580 
1581 }
1582 
1583