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