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