blob: 80a8bf18666a7aada5ca66d9ae7fab7a4f8cc1ab [file] [log] [blame]
San Mehatf1b736b2009-10-10 17:22:08 -07001/*
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
San Mehat49e2bce2009-10-12 16:29:01 -070017#include <stdlib.h>
San Mehatf1b736b2009-10-10 17:22:08 -070018#include <string.h>
San Mehat49e2bce2009-10-12 16:29:01 -070019#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>
San Mehata2677e42009-12-13 10:40:18 -080027#include <sys/mount.h>
28
29#include <linux/kdev_t.h>
30
31#include <cutils/properties.h>
32
San Mehat2a5b8ce2010-03-10 12:48:57 -080033#include <diskconfig/diskconfig.h>
San Mehatf1b736b2009-10-10 17:22:08 -070034
35#define LOG_TAG "Vold"
36
37#include <cutils/log.h>
38
39#include "Volume.h"
San Mehata2677e42009-12-13 10:40:18 -080040#include "VolumeManager.h"
41#include "ResponseCode.h"
San Mehatbf041852010-01-04 10:09:16 -080042#include "Fat.h"
San Mehat586536c2010-02-16 17:12:00 -080043#include "Process.h"
San Mehatf1b736b2009-10-10 17:22:08 -070044
San Mehata2677e42009-12-13 10:40:18 -080045extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
46extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
San Mehat49e2bce2009-10-12 16:29:01 -070047
San Mehat3bb60202010-02-19 18:14:36 -080048
49/*
50 * Secure directory - stuff that only root can see
51 */
52const char *Volume::SECDIR = "/mnt/secure";
53
54/*
55 * Secure staging directory - where media is mounted for preparation
56 */
57const char *Volume::SEC_STGDIR = "/mnt/secure/staging";
58
59/*
60 * Path to the directory on the media which contains publicly accessable
61 * asec imagefiles. This path will be obscured before the mount is
62 * exposed to non priviledged users.
63 */
San Mehat52c2ccb2010-02-23 18:26:13 -080064const char *Volume::SEC_STG_SECIMGDIR = "/mnt/secure/staging/.android_secure";
San Mehat3bb60202010-02-19 18:14:36 -080065
66/*
67 * Path to where *only* root can access asec imagefiles
68 */
69const char *Volume::SEC_ASECDIR = "/mnt/secure/asec";
70
71/*
72 * Path to where secure containers are mounted
73 */
74const char *Volume::ASECDIR = "/mnt/asec";
75
San Mehata2677e42009-12-13 10:40:18 -080076static const char *stateToStr(int state) {
77 if (state == Volume::State_Init)
78 return "Initializing";
79 else if (state == Volume::State_NoMedia)
80 return "No-Media";
81 else if (state == Volume::State_Idle)
82 return "Idle-Unmounted";
83 else if (state == Volume::State_Pending)
84 return "Pending";
85 else if (state == Volume::State_Mounted)
86 return "Mounted";
87 else if (state == Volume::State_Unmounting)
88 return "Unmounting";
89 else if (state == Volume::State_Checking)
90 return "Checking";
91 else if (state == Volume::State_Formatting)
92 return "Formatting";
93 else if (state == Volume::State_Shared)
94 return "Shared-Unmounted";
95 else if (state == Volume::State_SharedMnt)
96 return "Shared-Mounted";
97 else
98 return "Unknown-Error";
99}
100
101Volume::Volume(VolumeManager *vm, const char *label, const char *mount_point) {
102 mVm = vm;
San Mehatd9a4e352010-03-12 13:32:47 -0800103 mDebug = false;
San Mehatf1b736b2009-10-10 17:22:08 -0700104 mLabel = strdup(label);
105 mMountpoint = strdup(mount_point);
106 mState = Volume::State_Init;
San Mehata2677e42009-12-13 10:40:18 -0800107 mCurrentlyMountedKdev = -1;
San Mehatf1b736b2009-10-10 17:22:08 -0700108}
109
110Volume::~Volume() {
111 free(mLabel);
112 free(mMountpoint);
113}
114
San Mehatd9a4e352010-03-12 13:32:47 -0800115void Volume::setDebug(bool enable) {
116 mDebug = enable;
117}
118
San Mehata2677e42009-12-13 10:40:18 -0800119dev_t Volume::getDiskDevice() {
120 return MKDEV(0, 0);
121};
122
123void Volume::handleVolumeShared() {
124}
125
126void Volume::handleVolumeUnshared() {
127}
128
San Mehatfd7f5872009-10-12 11:32:47 -0700129int Volume::handleBlockEvent(NetlinkEvent *evt) {
San Mehatf1b736b2009-10-10 17:22:08 -0700130 errno = ENOSYS;
131 return -1;
132}
133
134void Volume::setState(int state) {
San Mehata2677e42009-12-13 10:40:18 -0800135 char msg[255];
136 int oldState = mState;
137
138 if (oldState == state) {
139 LOGW("Duplicate state (%d)\n", state);
140 return;
141 }
142
San Mehatf1b736b2009-10-10 17:22:08 -0700143 mState = state;
San Mehata2677e42009-12-13 10:40:18 -0800144
145 LOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
146 oldState, stateToStr(oldState), mState, stateToStr(mState));
147 snprintf(msg, sizeof(msg),
148 "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
149 getMountpoint(), oldState, stateToStr(oldState), mState,
150 stateToStr(mState));
151
152 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
153 msg, false);
San Mehatf1b736b2009-10-10 17:22:08 -0700154}
San Mehat49e2bce2009-10-12 16:29:01 -0700155
San Mehatdd9b8e92009-10-21 11:06:52 -0700156int Volume::createDeviceNode(const char *path, int major, int minor) {
157 mode_t mode = 0660 | S_IFBLK;
158 dev_t dev = (major << 8) | minor;
159 if (mknod(path, mode, dev) < 0) {
160 if (errno != EEXIST) {
161 return -1;
162 }
163 }
164 return 0;
165}
166
San Mehata2677e42009-12-13 10:40:18 -0800167int Volume::formatVol() {
San Mehat49e2bce2009-10-12 16:29:01 -0700168
San Mehata2677e42009-12-13 10:40:18 -0800169 if (getState() == Volume::State_NoMedia) {
170 errno = ENODEV;
171 return -1;
172 } else if (getState() != Volume::State_Idle) {
173 errno = EBUSY;
San Mehat49e2bce2009-10-12 16:29:01 -0700174 return -1;
175 }
176
San Mehata2677e42009-12-13 10:40:18 -0800177 if (isMountpointMounted(getMountpoint())) {
178 LOGW("Volume is idle but appears to be mounted - fixing");
179 setState(Volume::State_Mounted);
180 // mCurrentlyMountedKdev = XXX
181 errno = EBUSY;
San Mehat49e2bce2009-10-12 16:29:01 -0700182 return -1;
183 }
184
San Mehata2677e42009-12-13 10:40:18 -0800185 char devicePath[255];
186 dev_t diskNode = getDiskDevice();
187 dev_t partNode = MKDEV(MAJOR(diskNode), 1); // XXX: Hmmm
188
189 sprintf(devicePath, "/dev/block/vold/%d:%d",
190 MAJOR(diskNode), MINOR(diskNode));
191
San Mehatd9a4e352010-03-12 13:32:47 -0800192 if (mDebug) {
193 LOGI("Formatting volume %s (%s)", getLabel(), devicePath);
194 }
San Mehat2a5b8ce2010-03-10 12:48:57 -0800195 setState(Volume::State_Formatting);
San Mehata2677e42009-12-13 10:40:18 -0800196
197 if (initializeMbr(devicePath)) {
198 LOGE("Failed to initialize MBR (%s)", strerror(errno));
199 goto err;
San Mehat49e2bce2009-10-12 16:29:01 -0700200 }
201
San Mehata2677e42009-12-13 10:40:18 -0800202 sprintf(devicePath, "/dev/block/vold/%d:%d",
203 MAJOR(partNode), MINOR(partNode));
San Mehatdd9b8e92009-10-21 11:06:52 -0700204
San Mehatfcf24fe2010-03-03 12:37:32 -0800205 if (Fat::format(devicePath, 0)) {
San Mehata2677e42009-12-13 10:40:18 -0800206 LOGE("Failed to format (%s)", strerror(errno));
207 goto err;
208 }
209
San Mehat2a5b8ce2010-03-10 12:48:57 -0800210 setState(Volume::State_Idle);
San Mehat49e2bce2009-10-12 16:29:01 -0700211 return 0;
San Mehata2677e42009-12-13 10:40:18 -0800212err:
213 return -1;
214}
215
216bool Volume::isMountpointMounted(const char *path) {
217 char device[256];
218 char mount_path[256];
219 char rest[256];
220 FILE *fp;
221 char line[1024];
222
223 if (!(fp = fopen("/proc/mounts", "r"))) {
224 LOGE("Error opening /proc/mounts (%s)", strerror(errno));
225 return false;
226 }
227
228 while(fgets(line, sizeof(line), fp)) {
229 line[strlen(line)-1] = '\0';
230 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
231 if (!strcmp(mount_path, path)) {
232 fclose(fp);
233 return true;
234 }
235
236 }
237
238 fclose(fp);
239 return false;
240}
241
242int Volume::mountVol() {
243 dev_t deviceNodes[4];
244 int n, i, rc = 0;
245 char errmsg[255];
246
247 if (getState() == Volume::State_NoMedia) {
248 snprintf(errmsg, sizeof(errmsg),
249 "Volume %s %s mount failed - no media",
250 getLabel(), getMountpoint());
251 mVm->getBroadcaster()->sendBroadcast(
252 ResponseCode::VolumeMountFailedNoMedia,
253 errmsg, false);
254 errno = ENODEV;
255 return -1;
256 } else if (getState() != Volume::State_Idle) {
257 errno = EBUSY;
258 return -1;
259 }
260
261 if (isMountpointMounted(getMountpoint())) {
262 LOGW("Volume is idle but appears to be mounted - fixing");
263 setState(Volume::State_Mounted);
264 // mCurrentlyMountedKdev = XXX
265 return 0;
266 }
267
268 n = getDeviceNodes((dev_t *) &deviceNodes, 4);
269 if (!n) {
270 LOGE("Failed to get device nodes (%s)\n", strerror(errno));
271 return -1;
272 }
273
274 for (i = 0; i < n; i++) {
275 char devicePath[255];
276
277 sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
278 MINOR(deviceNodes[i]));
279
280 LOGI("%s being considered for volume %s\n", devicePath, getLabel());
281
282 errno = 0;
San Mehatbf041852010-01-04 10:09:16 -0800283 setState(Volume::State_Checking);
284
San Mehat3bb60202010-02-19 18:14:36 -0800285 if (Fat::check(devicePath)) {
San Mehata2677e42009-12-13 10:40:18 -0800286 if (errno == ENODATA) {
287 LOGW("%s does not contain a FAT filesystem\n", devicePath);
288 continue;
San Mehata2677e42009-12-13 10:40:18 -0800289 }
San Mehateba65e92010-01-29 05:15:16 -0800290 errno = EIO;
291 /* Badness - abort the mount */
292 LOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
293 setState(Volume::State_Idle);
294 return -1;
San Mehata2677e42009-12-13 10:40:18 -0800295 }
296
San Mehat3bb60202010-02-19 18:14:36 -0800297 /*
298 * Mount the device on our internal staging mountpoint so we can
299 * muck with it before exposing it to non priviledged users.
300 */
San Mehata2677e42009-12-13 10:40:18 -0800301 errno = 0;
San Mehat3bb60202010-02-19 18:14:36 -0800302 if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false, 1000, 1015, 0702, true)) {
303 LOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
304 continue;
San Mehata2677e42009-12-13 10:40:18 -0800305 }
306
San Mehat3bb60202010-02-19 18:14:36 -0800307 LOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint());
308
309 if (createBindMounts()) {
310 LOGE("Failed to create bindmounts (%s)", strerror(errno));
311 umount("/mnt/secure/staging");
312 setState(Volume::State_Idle);
313 return -1;
314 }
315
316 /*
317 * Now that the bindmount trickery is done, atomically move the
318 * whole subtree to expose it to non priviledged users.
319 */
320 if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) {
321 LOGE("Failed to move mount (%s)", strerror(errno));
322 umount("/mnt/secure/staging");
323 setState(Volume::State_Idle);
324 return -1;
325 }
326 setState(Volume::State_Mounted);
327 mCurrentlyMountedKdev = deviceNodes[i];
328 return 0;
San Mehata2677e42009-12-13 10:40:18 -0800329 }
330
San Mehata2677e42009-12-13 10:40:18 -0800331 LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
332 setState(Volume::State_Idle);
333
San Mehateba65e92010-01-29 05:15:16 -0800334 return -1;
San Mehata2677e42009-12-13 10:40:18 -0800335}
336
San Mehat3bb60202010-02-19 18:14:36 -0800337int Volume::createBindMounts() {
338 unsigned long flags;
339
340 /*
San Mehat52c2ccb2010-02-23 18:26:13 -0800341 * Rename old /android_secure -> /.android_secure
342 */
343 if (!access("/mnt/secure/staging/android_secure", R_OK | X_OK) &&
344 access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {
345 if (rename("/mnt/secure/staging/android_secure", SEC_STG_SECIMGDIR)) {
346 LOGE("Failed to rename legacy asec dir (%s)", strerror(errno));
347 }
348 }
349
350 /*
San Mehat3bb60202010-02-19 18:14:36 -0800351 * Ensure that /android_secure exists and is a directory
352 */
353 if (access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {
354 if (errno == ENOENT) {
355 if (mkdir(SEC_STG_SECIMGDIR, 0777)) {
356 LOGE("Failed to create %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
357 return -1;
358 }
359 } else {
360 LOGE("Failed to access %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
361 return -1;
362 }
363 } else {
364 struct stat sbuf;
365
366 if (stat(SEC_STG_SECIMGDIR, &sbuf)) {
367 LOGE("Failed to stat %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
368 return -1;
369 }
370 if (!S_ISDIR(sbuf.st_mode)) {
371 LOGE("%s is not a directory", SEC_STG_SECIMGDIR);
372 errno = ENOTDIR;
373 return -1;
374 }
375 }
376
377 /*
378 * Bind mount /mnt/secure/staging/android_secure -> /mnt/secure/asec so we'll
379 * have a root only accessable mountpoint for it.
380 */
381 if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR, "", MS_BIND, NULL)) {
382 LOGE("Failed to bind mount points %s -> %s (%s)",
383 SEC_STG_SECIMGDIR, SEC_ASECDIR, strerror(errno));
384 return -1;
385 }
386
387 /*
388 * Mount a read-only, zero-sized tmpfs on <mountpoint>/android_secure to
389 * obscure the underlying directory from everybody - sneaky eh? ;)
390 */
391 if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=000,uid=0,gid=0")) {
392 LOGE("Failed to obscure %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
393 umount("/mnt/asec_secure");
394 return -1;
395 }
396
397 return 0;
398}
399
400int Volume::doMoveMount(const char *src, const char *dst, bool force) {
401 unsigned int flags = MS_MOVE;
402 int retries = 5;
403
404 while(retries--) {
405 if (!mount(src, dst, "", flags, NULL)) {
San Mehatd9a4e352010-03-12 13:32:47 -0800406 if (mDebug) {
407 LOGD("Moved mount %s -> %s sucessfully", src, dst);
408 }
San Mehat3bb60202010-02-19 18:14:36 -0800409 return 0;
410 } else if (errno != EBUSY) {
411 LOGE("Failed to move mount %s -> %s (%s)", src, dst, strerror(errno));
412 return -1;
413 }
414 int action = 0;
415
416 if (force) {
417 if (retries == 1) {
418 action = 2; // SIGKILL
419 } else if (retries == 2) {
420 action = 1; // SIGHUP
421 }
422 }
423 LOGW("Failed to move %s -> %s (%s, retries %d, action %d)",
424 src, dst, strerror(errno), retries, action);
425 Process::killProcessesWithOpenFiles(src, action);
426 usleep(1000*250);
427 }
428
429 errno = EBUSY;
430 LOGE("Giving up on move %s -> %s (%s)", src, dst, strerror(errno));
431 return -1;
432}
433
434int Volume::doUnmount(const char *path, bool force) {
435 int retries = 10;
436
San Mehatd9a4e352010-03-12 13:32:47 -0800437 if (mDebug) {
438 LOGD("Unmounting {%s}, force = %d", path, force);
439 }
440
San Mehat3bb60202010-02-19 18:14:36 -0800441 while (retries--) {
442 if (!umount(path) || errno == EINVAL || errno == ENOENT) {
443 LOGI("%s sucessfully unmounted", path);
444 return 0;
445 }
446
447 int action = 0;
448
449 if (force) {
450 if (retries == 1) {
451 action = 2; // SIGKILL
452 } else if (retries == 2) {
453 action = 1; // SIGHUP
454 }
455 }
456
457 LOGW("Failed to unmount %s (%s, retries %d, action %d)",
458 path, strerror(errno), retries, action);
459
460 Process::killProcessesWithOpenFiles(path, action);
461 usleep(1000*1000);
462 }
463 errno = EBUSY;
464 LOGE("Giving up on unmount %s (%s)", path, strerror(errno));
465 return -1;
466}
467
San Mehat4ba89482010-02-18 09:00:18 -0800468int Volume::unmountVol(bool force) {
San Mehata2677e42009-12-13 10:40:18 -0800469 int i, rc;
470
471 if (getState() != Volume::State_Mounted) {
472 LOGE("Volume %s unmount request when not mounted", getLabel());
473 errno = EINVAL;
474 return -1;
475 }
476
477 setState(Volume::State_Unmounting);
San Mehat4ba89482010-02-18 09:00:18 -0800478 usleep(1000 * 1000); // Give the framework some time to react
San Mehata2677e42009-12-13 10:40:18 -0800479
San Mehat3bb60202010-02-19 18:14:36 -0800480 /*
481 * First move the mountpoint back to our internal staging point
482 * so nobody else can muck with it while we work.
483 */
484 if (doMoveMount(getMountpoint(), SEC_STGDIR, force)) {
485 LOGE("Failed to move mount %s => %s (%s)", getMountpoint(), SEC_STGDIR, strerror(errno));
486 setState(Volume::State_Mounted);
487 return -1;
San Mehata2677e42009-12-13 10:40:18 -0800488 }
489
San Mehat3bb60202010-02-19 18:14:36 -0800490 /*
491 * Unmount the tmpfs which was obscuring the asec image directory
492 * from non root users
493 */
494
495 if (doUnmount(Volume::SEC_STG_SECIMGDIR, force)) {
496 LOGE("Failed to unmount tmpfs on %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));
497 goto fail_republish;
San Mehata2677e42009-12-13 10:40:18 -0800498 }
499
San Mehat3bb60202010-02-19 18:14:36 -0800500 /*
501 * Remove the bindmount we were using to keep a reference to
502 * the previously obscured directory.
503 */
504
505 if (doUnmount(Volume::SEC_ASECDIR, force)) {
506 LOGE("Failed to remove bindmount on %s (%s)", SEC_ASECDIR, strerror(errno));
507 goto fail_remount_tmpfs;
508 }
509
510 /*
511 * Finally, unmount the actual block device from the staging dir
512 */
513 if (doUnmount(Volume::SEC_STGDIR, force)) {
514 LOGE("Failed to unmount %s (%s)", SEC_STGDIR, strerror(errno));
515 goto fail_recreate_bindmount;
516 }
517
518 LOGI("%s unmounted sucessfully", getMountpoint());
519
520 setState(Volume::State_Idle);
521 mCurrentlyMountedKdev = -1;
522 return 0;
523
524 /*
525 * Failure handling - try to restore everything back the way it was
526 */
527fail_recreate_bindmount:
528 if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR, "", MS_BIND, NULL)) {
529 LOGE("Failed to restore bindmount after failure! - Storage will appear offline!");
530 goto out_nomedia;
531 }
532fail_remount_tmpfs:
533 if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=0,uid=0,gid=0")) {
534 LOGE("Failed to restore tmpfs after failure! - Storage will appear offline!");
535 goto out_nomedia;
536 }
537fail_republish:
538 if (doMoveMount(SEC_STGDIR, getMountpoint(), force)) {
539 LOGE("Failed to republish mount after failure! - Storage will appear offline!");
540 goto out_nomedia;
541 }
542
San Mehata2677e42009-12-13 10:40:18 -0800543 setState(Volume::State_Mounted);
544 return -1;
San Mehat3bb60202010-02-19 18:14:36 -0800545
546out_nomedia:
547 setState(Volume::State_NoMedia);
548 return -1;
San Mehata2677e42009-12-13 10:40:18 -0800549}
550
551int Volume::initializeMbr(const char *deviceNode) {
San Mehat2a5b8ce2010-03-10 12:48:57 -0800552 struct disk_info dinfo;
San Mehata2677e42009-12-13 10:40:18 -0800553
San Mehat2a5b8ce2010-03-10 12:48:57 -0800554 memset(&dinfo, 0, sizeof(dinfo));
555
556 if (!(dinfo.part_lst = (struct part_info *) malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
557 LOGE("Failed to malloc prt_lst");
San Mehata2677e42009-12-13 10:40:18 -0800558 return -1;
559 }
560
San Mehat2a5b8ce2010-03-10 12:48:57 -0800561 memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));
562 dinfo.device = strdup(deviceNode);
563 dinfo.scheme = PART_SCHEME_MBR;
564 dinfo.sect_size = 512;
565 dinfo.skip_lba = 2048;
566 dinfo.num_lba = 0;
567 dinfo.num_parts = 1;
568
569 struct part_info *pinfo = &dinfo.part_lst[0];
570
571 pinfo->name = strdup("android_sdcard");
572 pinfo->flags |= PART_ACTIVE_FLAG;
573 pinfo->type = PC_PART_TYPE_FAT32;
574 pinfo->len_kb = -1;
575
576 int rc = apply_disk_config(&dinfo, 0);
577
578 if (rc) {
579 LOGE("Failed to apply disk configuration (%d)", rc);
580 goto out;
San Mehata2677e42009-12-13 10:40:18 -0800581 }
582
San Mehat2a5b8ce2010-03-10 12:48:57 -0800583 out:
584 free(pinfo->name);
585 free(dinfo.device);
586 free(dinfo.part_lst);
San Mehata2677e42009-12-13 10:40:18 -0800587
San Mehat2a5b8ce2010-03-10 12:48:57 -0800588 return rc;
San Mehata2677e42009-12-13 10:40:18 -0800589}