• 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 <stdlib.h>
18 #include <string.h>
19 #include <dirent.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/mman.h>
27 #include <sys/mount.h>
28 #include <sys/param.h>
29 
30 #include <linux/kdev_t.h>
31 
32 #include <cutils/properties.h>
33 
34 #include <diskconfig/diskconfig.h>
35 
36 #include <private/android_filesystem_config.h>
37 
38 #define LOG_TAG "Vold"
39 
40 #include <cutils/log.h>
41 
42 #include "Volume.h"
43 #include "VolumeManager.h"
44 #include "ResponseCode.h"
45 #include "Fat.h"
46 #include "Process.h"
47 #include "cryptfs.h"
48 
49 extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
50 extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
51 
52 
53 /*
54  * Secure directory - stuff that only root can see
55  */
56 const char *Volume::SECDIR            = "/mnt/secure";
57 
58 /*
59  * Secure staging directory - where media is mounted for preparation
60  */
61 const char *Volume::SEC_STGDIR        = "/mnt/secure/staging";
62 
63 /*
64  * Path to the directory on the media which contains publicly accessable
65  * asec imagefiles. This path will be obscured before the mount is
66  * exposed to non priviledged users.
67  */
68 const char *Volume::SEC_STG_SECIMGDIR = "/mnt/secure/staging/.android_secure";
69 
70 /*
71  * Path to external storage where *only* root can access ASEC image files
72  */
73 const char *Volume::SEC_ASECDIR_EXT   = "/mnt/secure/asec";
74 
75 /*
76  * Path to internal storage where *only* root can access ASEC image files
77  */
78 const char *Volume::SEC_ASECDIR_INT   = "/data/app-asec";
79 /*
80  * Path to where secure containers are mounted
81  */
82 const char *Volume::ASECDIR           = "/mnt/asec";
83 
84 /*
85  * Path to where OBBs are mounted
86  */
87 const char *Volume::LOOPDIR           = "/mnt/obb";
88 
stateToStr(int state)89 static const char *stateToStr(int state) {
90     if (state == Volume::State_Init)
91         return "Initializing";
92     else if (state == Volume::State_NoMedia)
93         return "No-Media";
94     else if (state == Volume::State_Idle)
95         return "Idle-Unmounted";
96     else if (state == Volume::State_Pending)
97         return "Pending";
98     else if (state == Volume::State_Mounted)
99         return "Mounted";
100     else if (state == Volume::State_Unmounting)
101         return "Unmounting";
102     else if (state == Volume::State_Checking)
103         return "Checking";
104     else if (state == Volume::State_Formatting)
105         return "Formatting";
106     else if (state == Volume::State_Shared)
107         return "Shared-Unmounted";
108     else if (state == Volume::State_SharedMnt)
109         return "Shared-Mounted";
110     else
111         return "Unknown-Error";
112 }
113 
Volume(VolumeManager * vm,const char * label,const char * mount_point)114 Volume::Volume(VolumeManager *vm, const char *label, const char *mount_point) {
115     mVm = vm;
116     mDebug = false;
117     mLabel = strdup(label);
118     mMountpoint = strdup(mount_point);
119     mState = Volume::State_Init;
120     mCurrentlyMountedKdev = -1;
121     mPartIdx = -1;
122     mRetryMount = false;
123 }
124 
~Volume()125 Volume::~Volume() {
126     free(mLabel);
127     free(mMountpoint);
128 }
129 
protectFromAutorunStupidity()130 void Volume::protectFromAutorunStupidity() {
131     char filename[255];
132 
133     snprintf(filename, sizeof(filename), "%s/autorun.inf", SEC_STGDIR);
134     if (!access(filename, F_OK)) {
135         SLOGW("Volume contains an autorun.inf! - removing");
136         /*
137          * Ensure the filename is all lower-case so
138          * the process killer can find the inode.
139          * Probably being paranoid here but meh.
140          */
141         rename(filename, filename);
142         Process::killProcessesWithOpenFiles(filename, 2);
143         if (unlink(filename)) {
144             SLOGE("Failed to remove %s (%s)", filename, strerror(errno));
145         }
146     }
147 }
148 
setDebug(bool enable)149 void Volume::setDebug(bool enable) {
150     mDebug = enable;
151 }
152 
getDiskDevice()153 dev_t Volume::getDiskDevice() {
154     return MKDEV(0, 0);
155 };
156 
getShareDevice()157 dev_t Volume::getShareDevice() {
158     return getDiskDevice();
159 }
160 
handleVolumeShared()161 void Volume::handleVolumeShared() {
162 }
163 
handleVolumeUnshared()164 void Volume::handleVolumeUnshared() {
165 }
166 
handleBlockEvent(NetlinkEvent * evt)167 int Volume::handleBlockEvent(NetlinkEvent *evt) {
168     errno = ENOSYS;
169     return -1;
170 }
171 
setState(int state)172 void Volume::setState(int state) {
173     char msg[255];
174     int oldState = mState;
175 
176     if (oldState == state) {
177         SLOGW("Duplicate state (%d)\n", state);
178         return;
179     }
180 
181     if ((oldState == Volume::State_Pending) && (state != Volume::State_Idle)) {
182         mRetryMount = false;
183     }
184 
185     mState = state;
186 
187     SLOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
188          oldState, stateToStr(oldState), mState, stateToStr(mState));
189     snprintf(msg, sizeof(msg),
190              "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
191              getMountpoint(), oldState, stateToStr(oldState), mState,
192              stateToStr(mState));
193 
194     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
195                                          msg, false);
196 }
197 
createDeviceNode(const char * path,int major,int minor)198 int Volume::createDeviceNode(const char *path, int major, int minor) {
199     mode_t mode = 0660 | S_IFBLK;
200     dev_t dev = (major << 8) | minor;
201     if (mknod(path, mode, dev) < 0) {
202         if (errno != EEXIST) {
203             return -1;
204         }
205     }
206     return 0;
207 }
208 
formatVol()209 int Volume::formatVol() {
210 
211     if (getState() == Volume::State_NoMedia) {
212         errno = ENODEV;
213         return -1;
214     } else if (getState() != Volume::State_Idle) {
215         errno = EBUSY;
216         return -1;
217     }
218 
219     if (isMountpointMounted(getMountpoint())) {
220         SLOGW("Volume is idle but appears to be mounted - fixing");
221         setState(Volume::State_Mounted);
222         // mCurrentlyMountedKdev = XXX
223         errno = EBUSY;
224         return -1;
225     }
226 
227     bool formatEntireDevice = (mPartIdx == -1);
228     char devicePath[255];
229     dev_t diskNode = getDiskDevice();
230     dev_t partNode = MKDEV(MAJOR(diskNode), (formatEntireDevice ? 1 : mPartIdx));
231 
232     setState(Volume::State_Formatting);
233 
234     int ret = -1;
235     // Only initialize the MBR if we are formatting the entire device
236     if (formatEntireDevice) {
237         sprintf(devicePath, "/dev/block/vold/%d:%d",
238                 MAJOR(diskNode), MINOR(diskNode));
239 
240         if (initializeMbr(devicePath)) {
241             SLOGE("Failed to initialize MBR (%s)", strerror(errno));
242             goto err;
243         }
244     }
245 
246     sprintf(devicePath, "/dev/block/vold/%d:%d",
247             MAJOR(partNode), MINOR(partNode));
248 
249     if (mDebug) {
250         SLOGI("Formatting volume %s (%s)", getLabel(), devicePath);
251     }
252 
253     if (Fat::format(devicePath, 0)) {
254         SLOGE("Failed to format (%s)", strerror(errno));
255         goto err;
256     }
257 
258     ret = 0;
259 
260 err:
261     setState(Volume::State_Idle);
262     return ret;
263 }
264 
isMountpointMounted(const char * path)265 bool Volume::isMountpointMounted(const char *path) {
266     char device[256];
267     char mount_path[256];
268     char rest[256];
269     FILE *fp;
270     char line[1024];
271 
272     if (!(fp = fopen("/proc/mounts", "r"))) {
273         SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
274         return false;
275     }
276 
277     while(fgets(line, sizeof(line), fp)) {
278         line[strlen(line)-1] = '\0';
279         sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
280         if (!strcmp(mount_path, path)) {
281             fclose(fp);
282             return true;
283         }
284 
285     }
286 
287     fclose(fp);
288     return false;
289 }
290 
mountVol()291 int Volume::mountVol() {
292     dev_t deviceNodes[4];
293     int n, i, rc = 0;
294     char errmsg[255];
295     const char* externalStorage = getenv("EXTERNAL_STORAGE");
296     bool primaryStorage = externalStorage && !strcmp(getMountpoint(), externalStorage);
297     char decrypt_state[PROPERTY_VALUE_MAX];
298     char crypto_state[PROPERTY_VALUE_MAX];
299     char encrypt_progress[PROPERTY_VALUE_MAX];
300     int flags;
301 
302     property_get("vold.decrypt", decrypt_state, "");
303     property_get("vold.encrypt_progress", encrypt_progress, "");
304 
305     /* Don't try to mount the volumes if we have not yet entered the disk password
306      * or are in the process of encrypting.
307      */
308     if ((getState() == Volume::State_NoMedia) ||
309         ((!strcmp(decrypt_state, "1") || encrypt_progress[0]) && primaryStorage)) {
310         snprintf(errmsg, sizeof(errmsg),
311                  "Volume %s %s mount failed - no media",
312                  getLabel(), getMountpoint());
313         mVm->getBroadcaster()->sendBroadcast(
314                                          ResponseCode::VolumeMountFailedNoMedia,
315                                          errmsg, false);
316         errno = ENODEV;
317         return -1;
318     } else if (getState() != Volume::State_Idle) {
319         errno = EBUSY;
320         if (getState() == Volume::State_Pending) {
321             mRetryMount = true;
322         }
323         return -1;
324     }
325 
326     if (isMountpointMounted(getMountpoint())) {
327         SLOGW("Volume is idle but appears to be mounted - fixing");
328         setState(Volume::State_Mounted);
329         // mCurrentlyMountedKdev = XXX
330         return 0;
331     }
332 
333     n = getDeviceNodes((dev_t *) &deviceNodes, 4);
334     if (!n) {
335         SLOGE("Failed to get device nodes (%s)\n", strerror(errno));
336         return -1;
337     }
338 
339     /* If we're running encrypted, and the volume is marked as encryptable and nonremovable,
340      * and vold is asking to mount the primaryStorage device, then we need to decrypt
341      * that partition, and update the volume object to point to it's new decrypted
342      * block device
343      */
344     property_get("ro.crypto.state", crypto_state, "");
345     flags = getFlags();
346     if (primaryStorage &&
347         ((flags & (VOL_NONREMOVABLE | VOL_ENCRYPTABLE))==(VOL_NONREMOVABLE | VOL_ENCRYPTABLE)) &&
348         !strcmp(crypto_state, "encrypted") && !isDecrypted()) {
349        char new_sys_path[MAXPATHLEN];
350        char nodepath[256];
351        int new_major, new_minor;
352 
353        if (n != 1) {
354            /* We only expect one device node returned when mounting encryptable volumes */
355            SLOGE("Too many device nodes returned when mounting %d\n", getMountpoint());
356            return -1;
357        }
358 
359        if (cryptfs_setup_volume(getLabel(), MAJOR(deviceNodes[0]), MINOR(deviceNodes[0]),
360                                 new_sys_path, sizeof(new_sys_path),
361                                 &new_major, &new_minor)) {
362            SLOGE("Cannot setup encryption mapping for %d\n", getMountpoint());
363            return -1;
364        }
365        /* We now have the new sysfs path for the decrypted block device, and the
366         * majore and minor numbers for it.  So, create the device, update the
367         * path to the new sysfs path, and continue.
368         */
369         snprintf(nodepath,
370                  sizeof(nodepath), "/dev/block/vold/%d:%d",
371                  new_major, new_minor);
372         if (createDeviceNode(nodepath, new_major, new_minor)) {
373             SLOGE("Error making device node '%s' (%s)", nodepath,
374                                                        strerror(errno));
375         }
376 
377         // Todo: Either create sys filename from nodepath, or pass in bogus path so
378         //       vold ignores state changes on this internal device.
379         updateDeviceInfo(nodepath, new_major, new_minor);
380 
381         /* Get the device nodes again, because they just changed */
382         n = getDeviceNodes((dev_t *) &deviceNodes, 4);
383         if (!n) {
384             SLOGE("Failed to get device nodes (%s)\n", strerror(errno));
385             return -1;
386         }
387     }
388 
389     for (i = 0; i < n; i++) {
390         char devicePath[255];
391 
392         sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
393                 MINOR(deviceNodes[i]));
394 
395         SLOGI("%s being considered for volume %s\n", devicePath, getLabel());
396 
397         errno = 0;
398         setState(Volume::State_Checking);
399 
400         if (Fat::check(devicePath)) {
401             if (errno == ENODATA) {
402                 SLOGW("%s does not contain a FAT filesystem\n", devicePath);
403                 continue;
404             }
405             errno = EIO;
406             /* Badness - abort the mount */
407             SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
408             setState(Volume::State_Idle);
409             return -1;
410         }
411 
412         /*
413          * Mount the device on our internal staging mountpoint so we can
414          * muck with it before exposing it to non priviledged users.
415          */
416         errno = 0;
417         int gid;
418 
419         if (primaryStorage) {
420             // Special case the primary SD card.
421             // For this we grant write access to the SDCARD_RW group.
422             gid = AID_SDCARD_RW;
423         } else {
424             // For secondary external storage we keep things locked up.
425             gid = AID_MEDIA_RW;
426         }
427         if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false, false,
428                 AID_SYSTEM, gid, 0702, true)) {
429             SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
430             continue;
431         }
432 
433         SLOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint());
434 
435         protectFromAutorunStupidity();
436 
437         // only create android_secure on primary storage
438         if (primaryStorage && createBindMounts()) {
439             SLOGE("Failed to create bindmounts (%s)", strerror(errno));
440             umount("/mnt/secure/staging");
441             setState(Volume::State_Idle);
442             return -1;
443         }
444 
445         /*
446          * Now that the bindmount trickery is done, atomically move the
447          * whole subtree to expose it to non priviledged users.
448          */
449         if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) {
450             SLOGE("Failed to move mount (%s)", strerror(errno));
451             umount("/mnt/secure/staging");
452             setState(Volume::State_Idle);
453             return -1;
454         }
455         setState(Volume::State_Mounted);
456         mCurrentlyMountedKdev = deviceNodes[i];
457         return 0;
458     }
459 
460     SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
461     setState(Volume::State_Idle);
462 
463     return -1;
464 }
465 
createBindMounts()466 int Volume::createBindMounts() {
467     unsigned long flags;
468 
469     /*
470      * Rename old /android_secure -> /.android_secure
471      */
472     if (!access("/mnt/secure/staging/android_secure", R_OK | X_OK) &&
473          access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {
474         if (rename("/mnt/secure/staging/android_secure", SEC_STG_SECIMGDIR)) {
475             SLOGE("Failed to rename legacy asec dir (%s)", strerror(errno));
476         }
477     }
478 
479     /*
480      * Ensure that /android_secure exists and is a directory
481      */
482     if (access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {
483         if (errno == ENOENT) {
484             if (mkdir(SEC_STG_SECIMGDIR, 0777)) {
485                 SLOGE("Failed to create %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
486                 return -1;
487             }
488         } else {
489             SLOGE("Failed to access %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
490             return -1;
491         }
492     } else {
493         struct stat sbuf;
494 
495         if (stat(SEC_STG_SECIMGDIR, &sbuf)) {
496             SLOGE("Failed to stat %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
497             return -1;
498         }
499         if (!S_ISDIR(sbuf.st_mode)) {
500             SLOGE("%s is not a directory", SEC_STG_SECIMGDIR);
501             errno = ENOTDIR;
502             return -1;
503         }
504     }
505 
506     /*
507      * Bind mount /mnt/secure/staging/android_secure -> /mnt/secure/asec so we'll
508      * have a root only accessable mountpoint for it.
509      */
510     if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR_EXT, "", MS_BIND, NULL)) {
511         SLOGE("Failed to bind mount points %s -> %s (%s)",
512                 SEC_STG_SECIMGDIR, SEC_ASECDIR_EXT, strerror(errno));
513         return -1;
514     }
515 
516     /*
517      * Mount a read-only, zero-sized tmpfs  on <mountpoint>/android_secure to
518      * obscure the underlying directory from everybody - sneaky eh? ;)
519      */
520     if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=000,uid=0,gid=0")) {
521         SLOGE("Failed to obscure %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
522         umount("/mnt/asec_secure");
523         return -1;
524     }
525 
526     return 0;
527 }
528 
doMoveMount(const char * src,const char * dst,bool force)529 int Volume::doMoveMount(const char *src, const char *dst, bool force) {
530     unsigned int flags = MS_MOVE;
531     int retries = 5;
532 
533     while(retries--) {
534         if (!mount(src, dst, "", flags, NULL)) {
535             if (mDebug) {
536                 SLOGD("Moved mount %s -> %s sucessfully", src, dst);
537             }
538             return 0;
539         } else if (errno != EBUSY) {
540             SLOGE("Failed to move mount %s -> %s (%s)", src, dst, strerror(errno));
541             return -1;
542         }
543         int action = 0;
544 
545         if (force) {
546             if (retries == 1) {
547                 action = 2; // SIGKILL
548             } else if (retries == 2) {
549                 action = 1; // SIGHUP
550             }
551         }
552         SLOGW("Failed to move %s -> %s (%s, retries %d, action %d)",
553                 src, dst, strerror(errno), retries, action);
554         Process::killProcessesWithOpenFiles(src, action);
555         usleep(1000*250);
556     }
557 
558     errno = EBUSY;
559     SLOGE("Giving up on move %s -> %s (%s)", src, dst, strerror(errno));
560     return -1;
561 }
562 
doUnmount(const char * path,bool force)563 int Volume::doUnmount(const char *path, bool force) {
564     int retries = 10;
565 
566     if (mDebug) {
567         SLOGD("Unmounting {%s}, force = %d", path, force);
568     }
569 
570     while (retries--) {
571         if (!umount(path) || errno == EINVAL || errno == ENOENT) {
572             SLOGI("%s sucessfully unmounted", path);
573             return 0;
574         }
575 
576         int action = 0;
577 
578         if (force) {
579             if (retries == 1) {
580                 action = 2; // SIGKILL
581             } else if (retries == 2) {
582                 action = 1; // SIGHUP
583             }
584         }
585 
586         SLOGW("Failed to unmount %s (%s, retries %d, action %d)",
587                 path, strerror(errno), retries, action);
588 
589         Process::killProcessesWithOpenFiles(path, action);
590         usleep(1000*1000);
591     }
592     errno = EBUSY;
593     SLOGE("Giving up on unmount %s (%s)", path, strerror(errno));
594     return -1;
595 }
596 
unmountVol(bool force,bool revert)597 int Volume::unmountVol(bool force, bool revert) {
598     int i, rc;
599 
600     if (getState() != Volume::State_Mounted) {
601         SLOGE("Volume %s unmount request when not mounted", getLabel());
602         errno = EINVAL;
603         return UNMOUNT_NOT_MOUNTED_ERR;
604     }
605 
606     setState(Volume::State_Unmounting);
607     usleep(1000 * 1000); // Give the framework some time to react
608 
609     /*
610      * Remove the bindmount we were using to keep a reference to
611      * the previously obscured directory.
612      */
613     if (doUnmount(Volume::SEC_ASECDIR_EXT, force)) {
614         SLOGE("Failed to remove bindmount on %s (%s)", SEC_ASECDIR_EXT, strerror(errno));
615         goto fail_remount_tmpfs;
616     }
617 
618     /*
619      * Unmount the tmpfs which was obscuring the asec image directory
620      * from non root users
621      */
622     char secure_dir[PATH_MAX];
623     snprintf(secure_dir, PATH_MAX, "%s/.android_secure", getMountpoint());
624     if (doUnmount(secure_dir, force)) {
625         SLOGE("Failed to unmount tmpfs on %s (%s)", secure_dir, strerror(errno));
626         goto fail_republish;
627     }
628 
629     /*
630      * Finally, unmount the actual block device from the staging dir
631      */
632     if (doUnmount(getMountpoint(), force)) {
633         SLOGE("Failed to unmount %s (%s)", SEC_STGDIR, strerror(errno));
634         goto fail_recreate_bindmount;
635     }
636 
637     SLOGI("%s unmounted sucessfully", getMountpoint());
638 
639     /* If this is an encrypted volume, and we've been asked to undo
640      * the crypto mapping, then revert the dm-crypt mapping, and revert
641      * the device info to the original values.
642      */
643     if (revert && isDecrypted()) {
644         cryptfs_revert_volume(getLabel());
645         revertDeviceInfo();
646         SLOGI("Encrypted volume %s reverted successfully", getMountpoint());
647     }
648 
649     setState(Volume::State_Idle);
650     mCurrentlyMountedKdev = -1;
651     return 0;
652 
653     /*
654      * Failure handling - try to restore everything back the way it was
655      */
656 fail_recreate_bindmount:
657     if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR_EXT, "", MS_BIND, NULL)) {
658         SLOGE("Failed to restore bindmount after failure! - Storage will appear offline!");
659         goto out_nomedia;
660     }
661 fail_remount_tmpfs:
662     if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=0,uid=0,gid=0")) {
663         SLOGE("Failed to restore tmpfs after failure! - Storage will appear offline!");
664         goto out_nomedia;
665     }
666 fail_republish:
667     if (doMoveMount(SEC_STGDIR, getMountpoint(), force)) {
668         SLOGE("Failed to republish mount after failure! - Storage will appear offline!");
669         goto out_nomedia;
670     }
671 
672     setState(Volume::State_Mounted);
673     return -1;
674 
675 out_nomedia:
676     setState(Volume::State_NoMedia);
677     return -1;
678 }
initializeMbr(const char * deviceNode)679 int Volume::initializeMbr(const char *deviceNode) {
680     struct disk_info dinfo;
681 
682     memset(&dinfo, 0, sizeof(dinfo));
683 
684     if (!(dinfo.part_lst = (struct part_info *) malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
685         SLOGE("Failed to malloc prt_lst");
686         return -1;
687     }
688 
689     memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));
690     dinfo.device = strdup(deviceNode);
691     dinfo.scheme = PART_SCHEME_MBR;
692     dinfo.sect_size = 512;
693     dinfo.skip_lba = 2048;
694     dinfo.num_lba = 0;
695     dinfo.num_parts = 1;
696 
697     struct part_info *pinfo = &dinfo.part_lst[0];
698 
699     pinfo->name = strdup("android_sdcard");
700     pinfo->flags |= PART_ACTIVE_FLAG;
701     pinfo->type = PC_PART_TYPE_FAT32;
702     pinfo->len_kb = -1;
703 
704     int rc = apply_disk_config(&dinfo, 0);
705 
706     if (rc) {
707         SLOGE("Failed to apply disk configuration (%d)", rc);
708         goto out;
709     }
710 
711  out:
712     free(pinfo->name);
713     free(dinfo.device);
714     free(dinfo.part_lst);
715 
716     return rc;
717 }
718