tar nandroid and /data/media support.

Change-Id: I9405e701887fc83c422c63c1dbf5ff087fff880d
diff --git a/Android.mk b/Android.mk
index 1d5a056..d12850c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,7 +26,7 @@
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
-RECOVERY_VERSION := ClockworkMod Recovery v3.2.0.1
+RECOVERY_VERSION := ClockworkMod Recovery v3.2.0.2
 LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)"
 RECOVERY_API_VERSION := 2
 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
@@ -58,7 +58,7 @@
 
 LOCAL_STATIC_LIBRARIES += libedify libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
 
-LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils 
+LOCAL_STATIC_LIBRARIES += libcrecovery libflashutils libmtdutils libmmcutils libbmlutils 
 
 ifeq ($(BOARD_USES_BML_OVER_MTD),true)
 LOCAL_STATIC_LIBRARIES += libbml_over_mtd
@@ -129,6 +129,7 @@
 
 include $(commands_recovery_local_path)/bmlutils/Android.mk
 include $(commands_recovery_local_path)/flashutils/Android.mk
+include $(commands_recovery_local_path)/libcrecovery/Android.mk
 include $(commands_recovery_local_path)/minui/Android.mk
 include $(commands_recovery_local_path)/minzip/Android.mk
 include $(commands_recovery_local_path)/mtdutils/Android.mk
diff --git a/extendedcommands.c b/extendedcommands.c
index cb44cb1..5543e96 100644
--- a/extendedcommands.c
+++ b/extendedcommands.c
@@ -466,10 +466,16 @@
     }
 
     static char tmp[PATH_MAX];
-    sprintf(tmp, "rm -rf %s/*", path);
-    __system(tmp);
-    sprintf(tmp, "rm -rf %s/.*", path);
-    __system(tmp);
+    if (strcmp(path, "/data") == 0) {
+        sprintf(tmp, "cd /data ; for f in $(ls -a | grep -v ^media$); do rm -rf $f; done");
+        __system(tmp);
+    }
+    else {
+        sprintf(tmp, "rm -rf %s/*", path);
+        __system(tmp);
+        sprintf(tmp, "rm -rf %s/.*", path);
+        __system(tmp);
+    }
 
     ensure_path_unmounted(path);
     return 0;
diff --git a/flashutils/Android.mk b/flashutils/Android.mk
index d503ce7..d513275 100644
--- a/flashutils/Android.mk
+++ b/flashutils/Android.mk
@@ -8,7 +8,7 @@
 LOCAL_MODULE := libflashutils
 LOCAL_MODULE_TAGS := eng
 LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES := libmmcutils libmtdutils libbmlutils
+LOCAL_STATIC_LIBRARIES := libmmcutils libmtdutils libbmlutils libcrecovery
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/flashutils/flashutils.c b/flashutils/flashutils.c
index 27f0c66..958476c 100644
--- a/flashutils/flashutils.c
+++ b/flashutils/flashutils.c
@@ -29,47 +29,6 @@
     return device_flash_type() == MMC ? "ext3" : "yaffs2";
 }
 
-// This was pulled from bionic: The default system command always looks
-// for shell in /system/bin/sh. This is bad.
-#define _PATH_BSHELL "/sbin/sh"
-
-extern char **environ;
-int
-__system(const char *command)
-{
-  pid_t pid;
-    sig_t intsave, quitsave;
-    sigset_t mask, omask;
-    int pstat;
-    char *argp[] = {"sh", "-c", NULL, NULL};
-
-    if (!command)        /* just checking... */
-        return(1);
-
-    argp[2] = (char *)command;
-
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGCHLD);
-    sigprocmask(SIG_BLOCK, &mask, &omask);
-    switch (pid = vfork()) {
-    case -1:            /* error */
-        sigprocmask(SIG_SETMASK, &omask, NULL);
-        return(-1);
-    case 0:                /* child */
-        sigprocmask(SIG_SETMASK, &omask, NULL);
-        execve(_PATH_BSHELL, argp, environ);
-    _exit(127);
-  }
-
-    intsave = (sig_t)  bsd_signal(SIGINT, SIG_IGN);
-    quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN);
-    pid = waitpid(pid, (int *)&pstat, 0);
-    sigprocmask(SIG_SETMASK, &omask, NULL);
-    (void)bsd_signal(SIGINT, intsave);
-    (void)bsd_signal(SIGQUIT, quitsave);
-    return (pid == -1 ? -1 : pstat);
-}
-
 int get_flash_type(const char* partitionType) {
     int type = UNSUPPORTED;
     if (strcmp(partitionType, "mtd") == 0)
diff --git a/flashutils/flashutils.h b/flashutils/flashutils.h
index dd59537..d112a31 100644
--- a/flashutils/flashutils.h
+++ b/flashutils/flashutils.h
@@ -15,8 +15,6 @@
 int is_mtd_device();
 char* get_default_filesystem();
 
-int __system(const char *command);
-
 extern int cmd_mtd_restore_raw_partition(const char *partition, const char *filename);
 extern int cmd_mtd_backup_raw_partition(const char *partition, const char *filename);
 extern int cmd_mtd_erase_raw_partition(const char *partition);
diff --git a/libcrecovery/Android.mk b/libcrecovery/Android.mk
new file mode 100644
index 0000000..3d28d97
--- /dev/null
+++ b/libcrecovery/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+ifeq ($(TARGET_ARCH),arm)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := system.c popen.c
+LOCAL_MODULE := libcrecovery
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_STATIC_LIBRARY)
+
+endif
+endif
diff --git a/libcrecovery/common.h b/libcrecovery/common.h
new file mode 100644
index 0000000..bf83dd2
--- /dev/null
+++ b/libcrecovery/common.h
@@ -0,0 +1,8 @@
+#ifndef LIBCRECOVERY_COMMON_H
+#define LIBCRECOVERY_COMMON_H
+
+int __system(const char *command);
+FILE * __popen(const char *program, const char *type);
+int __pclose(FILE *iop);
+
+#endif
\ No newline at end of file
diff --git a/libcrecovery/defines.h b/libcrecovery/defines.h
new file mode 100644
index 0000000..d94ad2d
--- /dev/null
+++ b/libcrecovery/defines.h
@@ -0,0 +1,2 @@
+#undef _PATH_BSHELL
+#define _PATH_BSHELL "/sbin/sh"
diff --git a/libcrecovery/popen.c b/libcrecovery/popen.c
new file mode 100644
index 0000000..73d3c74
--- /dev/null
+++ b/libcrecovery/popen.c
@@ -0,0 +1,169 @@
+/*	$OpenBSD: popen.c,v 1.17 2005/08/08 08:05:34 espie Exp $ */
+/*
+ * Copyright (c) 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software written by Ken Arnold and
+ * published in UNIX Review, Vol. 6, No. 8.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <paths.h>
+
+#include "defines.h"
+
+static struct pid {
+	struct pid *next;
+	FILE *fp;
+	pid_t pid;
+} *pidlist;
+
+FILE *
+__popen(const char *program, const char *type)
+{
+	struct pid * volatile cur;
+	FILE *iop;
+	int pdes[2];
+	pid_t pid;
+
+	if ((*type != 'r' && *type != 'w') || type[1] != '\0') {
+		errno = EINVAL;
+		return (NULL);
+	}
+
+	if ((cur = malloc(sizeof(struct pid))) == NULL)
+		return (NULL);
+
+	if (pipe(pdes) < 0) {
+		free(cur);
+		return (NULL);
+	}
+
+	switch (pid = vfork()) {
+	case -1:			/* Error. */
+		(void)close(pdes[0]);
+		(void)close(pdes[1]);
+		free(cur);
+		return (NULL);
+		/* NOTREACHED */
+	case 0:				/* Child. */
+	    {
+		struct pid *pcur;
+		/*
+		 * because vfork() instead of fork(), must leak FILE *,
+		 * but luckily we are terminally headed for an execl()
+		 */
+		for (pcur = pidlist; pcur; pcur = pcur->next)
+			close(fileno(pcur->fp));
+
+		if (*type == 'r') {
+			int tpdes1 = pdes[1];
+
+			(void) close(pdes[0]);
+			/*
+			 * We must NOT modify pdes, due to the
+			 * semantics of vfork.
+			 */
+			if (tpdes1 != STDOUT_FILENO) {
+				(void)dup2(tpdes1, STDOUT_FILENO);
+				(void)close(tpdes1);
+				tpdes1 = STDOUT_FILENO;
+			}
+		} else {
+			(void)close(pdes[1]);
+			if (pdes[0] != STDIN_FILENO) {
+				(void)dup2(pdes[0], STDIN_FILENO);
+				(void)close(pdes[0]);
+			}
+		}
+		execl(_PATH_BSHELL, "sh", "-c", program, (char *)NULL);
+		_exit(127);
+		/* NOTREACHED */
+	    }
+	}
+
+	/* Parent; assume fdopen can't fail. */
+	if (*type == 'r') {
+		iop = fdopen(pdes[0], type);
+		(void)close(pdes[1]);
+	} else {
+		iop = fdopen(pdes[1], type);
+		(void)close(pdes[0]);
+	}
+
+	/* Link into list of file descriptors. */
+	cur->fp = iop;
+	cur->pid =  pid;
+	cur->next = pidlist;
+	pidlist = cur;
+
+	return (iop);
+}
+
+/*
+ * pclose --
+ *	Pclose returns -1 if stream is not associated with a `popened' command,
+ *	if already `pclosed', or waitpid returns an error.
+ */
+int
+__pclose(FILE *iop)
+{
+	struct pid *cur, *last;
+	int pstat;
+	pid_t pid;
+
+	/* Find the appropriate file pointer. */
+	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
+		if (cur->fp == iop)
+			break;
+
+	if (cur == NULL)
+		return (-1);
+
+	(void)fclose(iop);
+
+	do {
+		pid = waitpid(cur->pid, &pstat, 0);
+	} while (pid == -1 && errno == EINTR);
+
+	/* Remove the entry from the linked list. */
+	if (last == NULL)
+		pidlist = cur->next;
+	else
+		last->next = cur->next;
+	free(cur);
+
+	return (pid == -1 ? -1 : pstat);
+}
diff --git a/libcrecovery/system.c b/libcrecovery/system.c
new file mode 100644
index 0000000..6d78ae9
--- /dev/null
+++ b/libcrecovery/system.c
@@ -0,0 +1,76 @@
+/*	$OpenBSD: system.c,v 1.8 2005/08/08 08:05:37 espie Exp $ */
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <paths.h>
+#include <sys/wait.h>
+
+#include "defines.h"
+
+extern char **environ;
+
+int
+__system(const char *command)
+{
+  pid_t pid;
+	sig_t intsave, quitsave;
+	sigset_t mask, omask;
+	int pstat;
+	char *argp[] = {"sh", "-c", NULL, NULL};
+
+	if (!command)		/* just checking... */
+		return(1);
+
+	argp[2] = (char *)command;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGCHLD);
+	sigprocmask(SIG_BLOCK, &mask, &omask);
+	switch (pid = vfork()) {
+	case -1:			/* error */
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+		return(-1);
+	case 0:				/* child */
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+		execve(_PATH_BSHELL, argp, environ);
+    _exit(127);
+  }
+
+	intsave = (sig_t)  bsd_signal(SIGINT, SIG_IGN);
+	quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN);
+	pid = waitpid(pid, (int *)&pstat, 0);
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+	(void)bsd_signal(SIGINT, intsave);
+	(void)bsd_signal(SIGQUIT, quitsave);
+	return (pid == -1 ? -1 : pstat);
+}
diff --git a/mounts.c b/mounts.c
index c90fc8a..e8b69e8 100644
--- a/mounts.c
+++ b/mounts.c
@@ -23,13 +23,6 @@
 
 #include "mounts.h"
 
-struct MountedVolume {
-    const char *device;
-    const char *mount_point;
-    const char *filesystem;
-    const char *flags;
-};
-
 typedef struct {
     MountedVolume *volumes;
     int volumes_allocd;
diff --git a/mounts.h b/mounts.h
index 30b2927..ed7fb5f 100644
--- a/mounts.h
+++ b/mounts.h
@@ -17,7 +17,12 @@
 #ifndef MTDUTILS_MOUNTS_H_
 #define MTDUTILS_MOUNTS_H_
 
-typedef struct MountedVolume MountedVolume;
+typedef struct {
+ const char *device;
+ const char *mount_point;
+ const char *filesystem;
+ const char *flags;
+} MountedVolume;
 
 int scan_mounted_volumes(void);
 
diff --git a/nandroid.c b/nandroid.c
index 54d90b0..8cee3be 100644
--- a/nandroid.c
+++ b/nandroid.c
@@ -37,10 +37,11 @@
 
 #include "extendedcommands.h"
 #include "nandroid.h"
+#include "mounts.h"
+
 #include "flashutils/flashutils.h"
 #include <libgen.h>
 
-
 void nandroid_generate_timestamp_path(const char* backup_path)
 {
     time_t t = time(NULL);
@@ -57,25 +58,31 @@
     }
 }
 
-int print_and_error(const char* message) {
+static int print_and_error(const char* message) {
     ui_print("%s", message);
     return 1;
 }
 
-int yaffs_files_total = 0;
-int yaffs_files_count = 0;
-void yaffs_callback(const char* filename)
+static int yaffs_files_total = 0;
+static int yaffs_files_count = 0;
+static void yaffs_callback(const char* filename)
 {
-    char* justfile = basename(filename);
-    if (strlen(justfile) < 30)
-        ui_print("%s", justfile);
+    if (filename == NULL)
+        return;
+    const char* justfile = basename(filename);
+    char tmp[PATH_MAX];
+    strcpy(tmp, justfile);
+    if (tmp[strlen(tmp) - 1] == '\n')
+        tmp[strlen(tmp) - 1] = NULL;
+    if (strlen(tmp) < 30)
+        ui_print("%s", tmp);
     yaffs_files_count++;
     if (yaffs_files_total != 0)
         ui_set_progress((float)yaffs_files_count / (float)yaffs_files_total);
     ui_reset_text_col();
 }
 
-void compute_directory_stats(const char* directory)
+static void compute_directory_stats(const char* directory)
 {
     char tmp[PATH_MAX];
     sprintf(tmp, "find %s | wc -l > /tmp/dircount", directory);
@@ -90,12 +97,62 @@
     ui_show_progress(1, 0);
 }
 
+typedef void (*file_event_callback)(const char* filename);
+typedef int (*nandroid_backup_handler)(const char* backup_path, const char* backup_file_image, const file_event_callback callback);
+
+static int mkyaffs2image_wrapper(const char* backup_path, const char* backup_file_image, const file_event_callback callback) {
+    return mkyaffs2image(backup_path, backup_file_image, 0, callback);
+}
+
+static int tar_compress_wrapper(const char* backup_path, const char* backup_file_image, const file_event_callback callback) {
+    char tmp[PATH_MAX];
+    if (strcmp(backup_path, "/data") == 0 && volume_for_path("/sdcard") == NULL)
+      sprintf(tmp, "cd $(dirname %s) ; tar cvf %s --exclude 'media' $(basename %s) ; exit $?", backup_path, backup_file_image, backup_path);
+    else
+      sprintf(tmp, "cd $(dirname %s) ; tar cvf %s $(basename %s) ; exit $?", backup_path, backup_file_image, backup_path);
+
+    FILE *fp = __popen(tmp, "r");
+    if (fp == NULL) {
+        ui_print("Unable to execute tar.\n");
+        return -1;
+    }
+
+    while (fgets(tmp, PATH_MAX, fp) != NULL) {
+        tmp[PATH_MAX - 1] = NULL;
+        if (NULL != callback)
+            yaffs_callback(tmp);
+    }
+
+    return __pclose(fp);
+}
+
+static nandroid_backup_handler get_backup_handler(const char *backup_path) {
+    Volume *v = volume_for_path(backup_path);
+    if (v == NULL) {
+        ui_print("Unable to find volume.\n");
+        return NULL;
+    }
+    scan_mounted_volumes();
+    MountedVolume *mv = find_mounted_volume_by_mount_point(v->mount_point);
+    if (mv == NULL) {
+        ui_print("Unable to find mounted volume: %s\n", v->mount_point);
+        return NULL;
+    }
+
+    if (strcmp("yaffs2", mv->filesystem) == 0) {
+        return mkyaffs2image_wrapper;
+    }
+
+    return tar_compress_wrapper;
+}
+
+
 int nandroid_backup_partition_extended(const char* backup_path, const char* mount_point, int umount_when_finished) {
     int ret = 0;
     char* name = basename(mount_point);
 
     struct stat file_info;
-    mkyaffs2image_callback callback = NULL;
+    file_event_callback callback = NULL;
     if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) {
         callback = yaffs_callback;
     }
@@ -108,12 +165,17 @@
     compute_directory_stats(mount_point);
     char tmp[PATH_MAX];
     sprintf(tmp, "%s/%s.img", backup_path, name);
-    ret = mkyaffs2image(mount_point, tmp, 0, callback);
+    nandroid_backup_handler backup_handler = get_backup_handler(mount_point);
+    if (backup_handler == NULL) {
+        ui_print("Error finding an appropriate backup handler.\n");
+        return -2;
+    }
+    ret = backup_handler(mount_point, tmp, callback);
     if (umount_when_finished) {
         ensure_path_unmounted(mount_point);
     }
     if (0 != ret) {
-        ui_print("Error while making a yaffs2 image of %s!\n", mount_point);
+        ui_print("Error while making a backup image of %s!\n", mount_point);
         return ret;
     }
     return 0;
@@ -245,6 +307,51 @@
     __system(tmp);
 }
 
+typedef int (*nandroid_restore_handler)(const char* backup_file_image, const char* backup_path, file_event_callback callback);
+
+static int unyaffs_wrapper(const char* backup_file_image, const char* backup_path, file_event_callback callback) {
+    return unyaffs(backup_file_image, backup_path, callback);
+}
+
+static int tar_extract_wrapper(const char* backup_file_image, const char* backup_path, file_event_callback callback) {
+    char tmp[PATH_MAX];
+    sprintf(tmp, "cd $(dirname %s) ; tar xvf %s ; exit $?", backup_path, backup_file_image);
+
+    char path[PATH_MAX];
+    FILE *fp = __popen(tmp, "r");
+    if (fp == NULL) {
+        ui_print("Unable to execute tar.\n");
+        return -1;
+    }
+
+    while (fgets(path, PATH_MAX, fp) != NULL) {
+        if (NULL != callback)
+            callback(path);
+    }
+
+    return __pclose(fp);
+}
+
+static nandroid_restore_handler get_restore_handler(const char *backup_path) {
+    Volume *v = volume_for_path(backup_path);
+    if (v == NULL) {
+        ui_print("Unable to find volume.\n");
+        return NULL;
+    }
+    scan_mounted_volumes();
+    MountedVolume *mv = find_mounted_volume_by_mount_point(v->mount_point);
+    if (mv == NULL) {
+        ui_print("Unable to find mounted volume: %s\n", v->mount_point);
+        return NULL;
+    }
+
+    if (strcmp("yaffs2", mv->filesystem) == 0) {
+        return unyaffs_wrapper;
+    }
+
+    return tar_extract_wrapper;
+}
+
 int nandroid_restore_partition_extended(const char* backup_path, const char* mount_point, int umount_when_finished) {
     int ret = 0;
     char* name = basename(mount_point);
@@ -259,7 +366,7 @@
 
     ensure_directory(mount_point);
 
-    unyaffs_callback callback = NULL;
+    file_event_callback callback = NULL;
     if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) {
         callback = yaffs_callback;
     }
@@ -281,7 +388,12 @@
         return ret;
     }
     
-    if (0 != (ret = unyaffs(tmp, mount_point, callback))) {
+    nandroid_restore_handler restore_handler = get_restore_handler(mount_point);
+    if (restore_handler == NULL) {
+        ui_print("Error finding an appropriate restore handler.\n");
+        return -2;
+    }
+    if (0 != (ret = restore_handler(tmp, mount_point, callback))) {
         ui_print("Error while restoring %s!\n", mount_point);
         return ret;
     }
diff --git a/roots.c b/roots.c
index 4ecc4dd..49b7729 100644
--- a/roots.c
+++ b/roots.c
@@ -167,6 +167,16 @@
 int ensure_path_mounted(const char* path) {
     Volume* v = volume_for_path(path);
     if (v == NULL) {
+        // no /sdcard? let's assume /data/media
+        if (strstr(path, "/sdcard") == path) {
+            LOGW("using /data/media, no /sdcard found.\n");
+            int ret;
+            if (0 != (ret = ensure_path_mounted("/data")))
+                return ret;
+            rmdir("/sdcard");
+            symlink("/data/media", "/sdcard");
+            return 0;
+        }
         LOGE("unknown volume for path [%s]\n", path);
         return -1;
     }
@@ -227,8 +237,17 @@
 }
 
 int ensure_path_unmounted(const char* path) {
+    // if we are using /data/media, do not ever unmount volumes /data or /sdcard
+    if (volume_for_path("/sdcard") == NULL && (strstr(path, "/sdcard") == path || strstr(path, "/data") == path)) {
+        return 0;
+    }
+
     Volume* v = volume_for_path(path);
     if (v == NULL) {
+        // no /sdcard? let's assume /data/media
+        if (strstr(path, "/sdcard") == path) {
+            return ensure_path_unmounted("/data");
+        }
         LOGE("unknown volume for path [%s]\n", path);
         return -1;
     }