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