Merge in lollipop and attempt to fix merge conflicts

This will probably not compile and may need additional work.
For tracking purposes so we know what might still need looking at
as none of this has been compiled and tested, here is a list of
the merge conflicts that I attempted to fix before pushing this
set of changes:

git pull aosp lollipop-release
remote: Finding sources: 100% (992/992)
remote: Total 992 (delta 473), reused 992 (delta 473)
Receiving objects: 100% (992/992), 1.51 MiB | 516.00 KiB/s, done.
Resolving deltas: 100% (473/473), completed with 42 local objects.
From https://android.googlesource.com/platform/bootable/recovery
 * branch            lollipop-release -> FETCH_HEAD
 * [new branch]      lollipop-release -> aosp/lollipop-release
Auto-merging verifier_test.cpp
CONFLICT (content): Merge conflict in verifier_test.cpp
Auto-merging verifier.h
CONFLICT (content): Merge conflict in verifier.h
Auto-merging verifier.cpp
CONFLICT (content): Merge conflict in verifier.cpp
Auto-merging updater/updater.c
Auto-merging updater/install.c
CONFLICT (content): Merge conflict in updater/install.c
Auto-merging updater/Android.mk
CONFLICT (content): Merge conflict in updater/Android.mk
Auto-merging uncrypt/Android.mk
CONFLICT (content): Merge conflict in uncrypt/Android.mk
Auto-merging ui.cpp
CONFLICT (content): Merge conflict in ui.cpp
Auto-merging screen_ui.cpp
Auto-merging roots.cpp
CONFLICT (content): Merge conflict in roots.cpp
CONFLICT (rename/delete): res-hdpi/images/progress_fill.png deleted
in HEAD and renamed in cddb68b5eafbeba696d5276bda1f1a9f70bbde42.
Version cddb68b5eafbeba696d5276bda1f1a9f70bbde42 of
res-hdpi/images/progress_fill.png left in tree.
CONFLICT (rename/delete): res-hdpi/images/progress_empty.png deleted
in HEAD and renamed in cddb68b5eafbeba696d5276bda1f1a9f70bbde42.
Version cddb68b5eafbeba696d5276bda1f1a9f70bbde42 of
res-hdpi/images/progress_empty.png left in tree.
CONFLICT (rename/delete): res-hdpi/images/icon_error.png deleted
in HEAD and renamed in cddb68b5eafbeba696d5276bda1f1a9f70bbde42.
Version cddb68b5eafbeba696d5276bda1f1a9f70bbde42 of
res-hdpi/images/icon_error.png left in tree.
Auto-merging recovery.cpp
CONFLICT (content): Merge conflict in recovery.cpp
Auto-merging minui/resources.c
CONFLICT (content): Merge conflict in minui/resources.c
Auto-merging minui/minui.h
CONFLICT (content): Merge conflict in minui/minui.h
Auto-merging minui/graphics.c
CONFLICT (content): Merge conflict in minui/graphics.c
Auto-merging minui/Android.mk
CONFLICT (content): Merge conflict in minui/Android.mk
Removing minelf/Retouch.h
Removing minelf/Retouch.c
Auto-merging minadbd/usb_linux_client.c
CONFLICT (content): Merge conflict in minadbd/usb_linux_client.c
Auto-merging minadbd/adb.h
CONFLICT (content): Merge conflict in minadbd/adb.h
Auto-merging minadbd/adb.c
CONFLICT (content): Merge conflict in minadbd/adb.c
Auto-merging minadbd/Android.mk
CONFLICT (content): Merge conflict in minadbd/Android.mk
Removing make-overlay.py
Auto-merging install.h
CONFLICT (content): Merge conflict in install.h
Auto-merging etc/init.rc
CONFLICT (content): Merge conflict in etc/init.rc
Auto-merging bootloader.h
Auto-merging applypatch/applypatch.c
Auto-merging applypatch/Android.mk
CONFLICT (content): Merge conflict in applypatch/Android.mk
Auto-merging adb_install.cpp
CONFLICT (content): Merge conflict in adb_install.cpp
Auto-merging Android.mk
CONFLICT (content): Merge conflict in Android.mk
Automatic merge failed; fix conflicts and then commit the result.

Change-Id: I3e0e03e48ad8550912111c7a5c9a140ed0267e2c
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e03babb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.*.swp
+*~
+tags
diff --git a/Android.mk b/Android.mk
index 1a91f00..835bb71 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,18 +14,43 @@
 
 LOCAL_PATH := $(call my-dir)
 
+ifdef project-path-for
+    ifeq ($(LOCAL_PATH),$(call project-path-for,recovery))
+        PROJECT_PATH_AGREES := true
+    endif
+else
+    ifeq ($(LOCAL_PATH),bootable/recovery)
+        PROJECT_PATH_AGREES := true
+    endif
+endif
+
+ifeq ($(PROJECT_PATH_AGREES),true)
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := fuse_sideload.c
+TARGET_RECOVERY_GUI := true
 
-LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
-LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+LOCAL_SRC_FILES := \
+    twrp.cpp \
+    fixPermissions.cpp \
+    twrpTar.cpp \
+	twrpDU.cpp \
+    twrpDigest.cpp \
+    find_file.cpp \
+    infomanager.cpp
 
-LOCAL_MODULE := libfusesideload
+LOCAL_SRC_FILES += \
+    data.cpp \
+    partition.cpp \
+    partitionmanager.cpp \
+    twinstall.cpp \
+    twrp-functions.cpp \
+    openrecoveryscript.cpp \
+    tarWrite.c
 
-LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt
-include $(BUILD_STATIC_LIBRARY)
+ifneq ($(TARGET_RECOVERY_REBOOT_SRC),)
+  LOCAL_SRC_FILES += $(TARGET_RECOVERY_REBOOT_SRC)
+endif
 
 include $(CLEAR_VARS)
 
@@ -43,40 +68,78 @@
 
 LOCAL_MODULE := recovery
 
-LOCAL_FORCE_STATIC_EXECUTABLE := true
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
 
-ifeq ($(HOST_OS),linux)
-LOCAL_REQUIRED_MODULES := mkfs.f2fs
-endif
+#ifeq ($(HOST_OS),linux)
+#LOCAL_REQUIRED_MODULES := mkfs.f2fs
+#endif
 
 RECOVERY_API_VERSION := 3
 RECOVERY_FSTAB_VERSION := 2
 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
 LOCAL_CFLAGS += -Wno-unused-parameter
 
-LOCAL_STATIC_LIBRARIES := \
-    libext4_utils_static \
-    libsparse_static \
-    libminzip \
-    libz \
-    libmtdutils \
-    libmincrypt \
-    libminadbd \
-    libfusesideload \
-    libminui \
-    libpng \
-    libfs_mgr \
-    libcutils \
-    liblog \
-    libselinux \
-    libstdc++ \
-    libm \
-    libc
+#LOCAL_STATIC_LIBRARIES := \
+#    libext4_utils_static \
+#    libsparse_static \
+#    libminzip \
+#    libz \
+#    libmtdutils \
+#    libmincrypt \
+#    libminadbd \
+#    libminui \
+#    libpixelflinger_static \
+#    libpng \
+#    libfs_mgr \
+#    libcutils \
+#    liblog \
+#    libselinux \
+#    libstdc++ \
+#    libm \
+#    libc
 
+LOCAL_C_INCLUDES += bionic external/stlport/stlport
+
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_STATIC_LIBRARIES += libcrecovery libguitwrp
+LOCAL_SHARED_LIBRARIES += libz libc libstlport libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libaosprecovery libcorkscrew
+LOCAL_SHARED_LIBRARIES += libgccdemangle
+
+ifneq ($(wildcard system/core/libsparse/Android.mk),)
+LOCAL_SHARED_LIBRARIES += libsparse
+endif
+
+ifeq ($(TW_OEM_BUILD),true)
+    LOCAL_CFLAGS += -DTW_OEM_BUILD
+    BOARD_HAS_NO_REAL_SDCARD := true
+    TW_USE_TOOLBOX := true
+    TW_EXCLUDE_SUPERSU := true
+    TW_EXCLUDE_MTP := true
+endif
 ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
     LOCAL_CFLAGS += -DUSE_EXT4
-    LOCAL_C_INCLUDES += system/extras/ext4_utils system/vold
-    LOCAL_STATIC_LIBRARIES += libext4_utils_static libz
+    LOCAL_C_INCLUDES += system/extras/ext4_utils
+    LOCAL_SHARED_LIBRARIES += libext4_utils
+endif
+ifneq ($(wildcard external/libselinux/Android.mk),)
+    TWHAVE_SELINUX := true
+endif
+ifeq ($(TWHAVE_SELINUX), true)
+  #LOCAL_C_INCLUDES += external/libselinux/include
+  #LOCAL_STATIC_LIBRARIES += libselinux
+  #LOCAL_CFLAGS += -DHAVE_SELINUX -g
+endif # HAVE_SELINUX
+ifeq ($(TWHAVE_SELINUX), true)
+    LOCAL_C_INCLUDES += external/libselinux/include
+    LOCAL_SHARED_LIBRARIES += libselinux
+    LOCAL_CFLAGS += -DHAVE_SELINUX -g
+    ifneq ($(TARGET_USERIMAGES_USE_EXT4), true)
+        LOCAL_CFLAGS += -DUSE_EXT4
+        LOCAL_C_INCLUDES += system/extras/ext4_utils
+        LOCAL_SHARED_LIBRARIES += libext4_utils
+    endif
 endif
 
 # This binary is in the recovery ramdisk, which is otherwise a copy of root.
@@ -85,17 +148,288 @@
 # TODO: Build the ramdisk image in a more principled way.
 LOCAL_MODULE_TAGS := eng
 
-ifeq ($(TARGET_RECOVERY_UI_LIB),)
+#ifeq ($(TARGET_RECOVERY_UI_LIB),)
   LOCAL_SRC_FILES += default_device.cpp
-else
-  LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
-endif
+#else
+#  LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
+#endif
 
 LOCAL_C_INCLUDES += system/extras/ext4_utils
-LOCAL_C_INCLUDES += external/openssl/include
+
+#TWRP Build Flags
+ifeq ($(TW_EXCLUDE_MTP),)
+    LOCAL_SHARED_LIBRARIES += libtwrpmtp
+    LOCAL_CFLAGS += -DTW_HAS_MTP
+endif
+ifneq ($(TW_NO_SCREEN_TIMEOUT),)
+    LOCAL_CFLAGS += -DTW_NO_SCREEN_TIMEOUT
+endif
+ifeq ($(BOARD_HAS_NO_REAL_SDCARD), true)
+    LOCAL_CFLAGS += -DBOARD_HAS_NO_REAL_SDCARD
+endif
+ifneq ($(SP1_NAME),)
+	LOCAL_CFLAGS += -DSP1_NAME=$(SP1_NAME) -DSP1_BACKUP_METHOD=$(SP1_BACKUP_METHOD) -DSP1_MOUNTABLE=$(SP1_MOUNTABLE)
+endif
+ifneq ($(SP1_DISPLAY_NAME),)
+	LOCAL_CFLAGS += -DSP1_DISPLAY_NAME=$(SP1_DISPLAY_NAME)
+endif
+ifneq ($(SP2_NAME),)
+	LOCAL_CFLAGS += -DSP2_NAME=$(SP2_NAME) -DSP2_BACKUP_METHOD=$(SP2_BACKUP_METHOD) -DSP2_MOUNTABLE=$(SP2_MOUNTABLE)
+endif
+ifneq ($(SP2_DISPLAY_NAME),)
+	LOCAL_CFLAGS += -DSP2_DISPLAY_NAME=$(SP2_DISPLAY_NAME)
+endif
+ifneq ($(SP3_NAME),)
+	LOCAL_CFLAGS += -DSP3_NAME=$(SP3_NAME) -DSP3_BACKUP_METHOD=$(SP3_BACKUP_METHOD) -DSP3_MOUNTABLE=$(SP3_MOUNTABLE)
+endif
+ifneq ($(SP3_DISPLAY_NAME),)
+	LOCAL_CFLAGS += -DSP3_DISPLAY_NAME=$(SP3_DISPLAY_NAME)
+endif
+ifneq ($(RECOVERY_SDCARD_ON_DATA),)
+	LOCAL_CFLAGS += -DRECOVERY_SDCARD_ON_DATA
+endif
+ifneq ($(TW_INCLUDE_DUMLOCK),)
+	LOCAL_CFLAGS += -DTW_INCLUDE_DUMLOCK
+endif
+ifneq ($(TW_INTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_PATH=$(TW_INTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_INTERNAL_STORAGE_MOUNT_POINT),)
+	LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_MOUNT_POINT=$(TW_INTERNAL_STORAGE_MOUNT_POINT)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_PATH=$(TW_EXTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_MOUNT_POINT),)
+	LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_MOUNT_POINT=$(TW_EXTERNAL_STORAGE_MOUNT_POINT)
+endif
+ifeq ($(TW_HAS_NO_RECOVERY_PARTITION), true)
+    LOCAL_CFLAGS += -DTW_HAS_NO_RECOVERY_PARTITION
+endif
+ifeq ($(TW_HAS_NO_BOOT_PARTITION), true)
+    LOCAL_CFLAGS += -DTW_HAS_NO_BOOT_PARTITION
+endif
+ifeq ($(TW_NO_REBOOT_BOOTLOADER), true)
+    LOCAL_CFLAGS += -DTW_NO_REBOOT_BOOTLOADER
+endif
+ifeq ($(TW_NO_REBOOT_RECOVERY), true)
+    LOCAL_CFLAGS += -DTW_NO_REBOOT_RECOVERY
+endif
+ifeq ($(TW_NO_BATT_PERCENT), true)
+    LOCAL_CFLAGS += -DTW_NO_BATT_PERCENT
+endif
+ifeq ($(TW_NO_CPU_TEMP), true)
+    LOCAL_CFLAGS += -DTW_NO_CPU_TEMP
+endif
+ifneq ($(TW_CUSTOM_POWER_BUTTON),)
+	LOCAL_CFLAGS += -DTW_CUSTOM_POWER_BUTTON=$(TW_CUSTOM_POWER_BUTTON)
+endif
+ifeq ($(TW_ALWAYS_RMRF), true)
+    LOCAL_CFLAGS += -DTW_ALWAYS_RMRF
+endif
+ifeq ($(TW_NEVER_UNMOUNT_SYSTEM), true)
+    LOCAL_CFLAGS += -DTW_NEVER_UNMOUNT_SYSTEM
+endif
+ifeq ($(TW_NO_USB_STORAGE), true)
+    LOCAL_CFLAGS += -DTW_NO_USB_STORAGE
+endif
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_INJECTTWRP
+endif
+ifeq ($(TW_INCLUDE_BLOBPACK), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_BLOBPACK
+endif
+ifeq ($(TW_DEFAULT_EXTERNAL_STORAGE), true)
+    LOCAL_CFLAGS += -DTW_DEFAULT_EXTERNAL_STORAGE
+endif
+ifneq ($(TARGET_USE_CUSTOM_LUN_FILE_PATH),)
+    LOCAL_CFLAGS += -DCUSTOM_LUN_FILE=\"$(TARGET_USE_CUSTOM_LUN_FILE_PATH)\"
+endif
+ifneq ($(BOARD_UMS_LUNFILE),)
+    LOCAL_CFLAGS += -DCUSTOM_LUN_FILE=\"$(BOARD_UMS_LUNFILE)\"
+endif
+#ifeq ($(TW_FLASH_FROM_STORAGE), true) Making this the default behavior
+    LOCAL_CFLAGS += -DTW_FLASH_FROM_STORAGE
+#endif
+ifeq ($(TW_HAS_DOWNLOAD_MODE), true)
+    LOCAL_CFLAGS += -DTW_HAS_DOWNLOAD_MODE
+endif
+ifeq ($(TW_NO_SCREEN_BLANK), true)
+    LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK
+endif
+ifeq ($(TW_SDEXT_NO_EXT4), true)
+    LOCAL_CFLAGS += -DTW_SDEXT_NO_EXT4
+endif
+ifeq ($(TW_FORCE_CPUINFO_FOR_DEVICE_ID), true)
+    LOCAL_CFLAGS += -DTW_FORCE_CPUINFO_FOR_DEVICE_ID
+endif
+ifeq ($(TW_NO_EXFAT_FUSE), true)
+    LOCAL_CFLAGS += -DTW_NO_EXFAT_FUSE
+endif
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO
+    LOCAL_CFLAGS += -DCRYPTO_FS_TYPE=\"$(TW_CRYPTO_FS_TYPE)\"
+    LOCAL_CFLAGS += -DCRYPTO_REAL_BLKDEV=\"$(TW_CRYPTO_REAL_BLKDEV)\"
+    LOCAL_CFLAGS += -DCRYPTO_MNT_POINT=\"$(TW_CRYPTO_MNT_POINT)\"
+    LOCAL_CFLAGS += -DCRYPTO_FS_OPTIONS=\"$(TW_CRYPTO_FS_OPTIONS)\"
+    LOCAL_CFLAGS += -DCRYPTO_FS_FLAGS=\"$(TW_CRYPTO_FS_FLAGS)\"
+    LOCAL_CFLAGS += -DCRYPTO_KEY_LOC=\"$(TW_CRYPTO_KEY_LOC)\"
+ifeq ($(TW_INCLUDE_CRYPTO_SAMSUNG), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO_SAMSUNG=\"$(TW_INCLUDE_CRYPTO_SAMSUNG)\"
+    ifdef TW_CRYPTO_SD_REAL_BLKDEV
+        LOCAL_CFLAGS += -DCRYPTO_SD_REAL_BLKDEV=\"$(TW_CRYPTO_SD_REAL_BLKDEV)\"
+        LOCAL_CFLAGS += -DCRYPTO_SD_FS_TYPE=\"$(TW_CRYPTO_SD_FS_TYPE)\"
+    endif
+    #LOCAL_LDFLAGS += -L$(TARGET_OUT_SHARED_LIBRARIES) -lsec_km
+    LOCAL_LDFLAGS += -ldl
+    LOCAL_STATIC_LIBRARIES += libcrypt_samsung
+endif
+    LOCAL_SHARED_LIBRARIES += libcryptfsics
+    #LOCAL_SRC_FILES += crypto/ics/cryptfs.c
+    #LOCAL_C_INCLUDES += system/extras/ext4_utils external/openssl/include
+endif
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO
+    LOCAL_CFLAGS += -DTW_INCLUDE_JB_CRYPTO
+    LOCAL_SHARED_LIBRARIES += libcryptfsjb
+    #LOCAL_SRC_FILES += crypto/jb/cryptfs.c
+    #LOCAL_C_INCLUDES += system/extras/ext4_utils external/openssl/include
+endif
+ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true)
+    LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
+endif
+ifneq ($(TW_BRIGHTNESS_PATH),)
+	LOCAL_CFLAGS += -DTW_BRIGHTNESS_PATH=$(TW_BRIGHTNESS_PATH)
+endif
+ifneq ($(TW_SECONDARY_BRIGHTNESS_PATH),)
+	LOCAL_CFLAGS += -DTW_SECONDARY_BRIGHTNESS_PATH=$(TW_SECONDARY_BRIGHTNESS_PATH)
+endif
+ifneq ($(TW_MAX_BRIGHTNESS),)
+	LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=$(TW_MAX_BRIGHTNESS)
+endif
+ifneq ($(TW_CUSTOM_BATTERY_PATH),)
+	LOCAL_CFLAGS += -DTW_CUSTOM_BATTERY_PATH=$(TW_CUSTOM_BATTERY_PATH)
+endif
+ifneq ($(TW_CUSTOM_CPU_TEMP_PATH),)
+	LOCAL_CFLAGS += -DTW_CUSTOM_CPU_TEMP_PATH=$(TW_CUSTOM_CPU_TEMP_PATH)
+endif
+ifneq ($(TW_NO_CPU_TEMP),)
+	LOCAL_CFLAGS += -DTW_NO_CPU_TEMP=$(TW_NO_CPU_TEMP)
+endif
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+    LOCAL_SHARED_LIBRARIES += libopenaes
+else
+    LOCAL_CFLAGS += -DTW_EXCLUDE_ENCRYPTED_BACKUPS
+endif
+ifeq ($(TARGET_RECOVERY_QCOM_RTC_FIX),)
+  ifeq ($(TARGET_CPU_VARIANT),krait)
+    LOCAL_CFLAGS += -DQCOM_RTC_FIX
+  endif
+else ifeq ($(TARGET_RECOVERY_QCOM_RTC_FIX),true)
+    LOCAL_CFLAGS += -DQCOM_RTC_FIX
+endif
+ifneq ($(TW_NO_LEGACY_PROPS),)
+	LOCAL_CFLAGS += -DTW_NO_LEGACY_PROPS
+endif
+ifneq ($(wildcard bionic/libc/include/sys/capability.h),)
+    LOCAL_CFLAGS += -DHAVE_CAPABILITIES
+endif
+
+LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+    busybox_symlinks \
+    dosfsck \
+    dosfslabel \
+    dump_image \
+    erase_image \
+    flash_image \
+    fix_permissions.sh \
+    fsck_msdos_symlink \
+    mkdosfs \
+    mke2fs.conf \
+    mkexfatfs \
+    pigz \
+    teamwin \
+    toolbox_symlinks \
+    twrp \
+    unpigz_symlink \
+    updater
+
+ifeq ($(BOARD_HAS_NO_REAL_SDCARD),)
+    LOCAL_ADDITIONAL_DEPENDENCIES += parted
+endif
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += openaes ../openaes/LICENSE
+endif
+ifeq ($(TW_INCLUDE_DUMLOCK), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += \
+        htcdumlock htcdumlocksys flash_imagesys dump_imagesys libbmlutils.so \
+        libflashutils.so libmmcutils.so libmtdutils.so HTCDumlock.apk
+endif
+ifneq ($(TW_EXCLUDE_SUPERSU), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += \
+        su install-recovery.sh 99SuperSUDaemon Superuser.apk
+endif
+ifneq ($(TW_NO_EXFAT_FUSE), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += exfat-fuse
+endif
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += cryptfs cryptsettings
+endif
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += getfooter
+endif
+ifeq ($(TW_INCLUDE_FB2PNG), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += fb2png
+endif
+ifneq ($(TW_OEM_BUILD),true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += orscmd
+endif
+ifeq ($(BOARD_USES_BML_OVER_MTD),true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += bml_over_mtd
+endif
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += injecttwrp
+endif
+# Allow devices to specify device-specific recovery dependencies
+ifneq ($(TARGET_RECOVERY_DEVICE_MODULES),)
+    LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_RECOVERY_DEVICE_MODULES)
+endif
 
 include $(BUILD_EXECUTABLE)
 
+ifneq ($(TW_USE_TOOLBOX), true)
+include $(CLEAR_VARS)
+# Create busybox symlinks... gzip and gunzip are excluded because those need to link to pigz instead
+BUSYBOX_LINKS := $(shell cat external/busybox/busybox-full.links)
+exclude := tune2fs mke2fs mkdosfs gzip gunzip
+
+# If busybox does not have restorecon, assume it does not have SELinux support.
+# Then, let toolbox provide 'ls' so -Z is available to list SELinux contexts.
+ifeq ($(TWHAVE_SELINUX), true)
+	ifeq ($(filter restorecon, $(notdir $(BUSYBOX_LINKS))),)
+		exclude += ls
+	endif
+endif
+
+RECOVERY_BUSYBOX_TOOLS := $(filter-out $(exclude), $(notdir $(BUSYBOX_LINKS)))
+RECOVERY_BUSYBOX_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/, $(RECOVERY_BUSYBOX_TOOLS))
+$(RECOVERY_BUSYBOX_SYMLINKS): BUSYBOX_BINARY := busybox
+$(RECOVERY_BUSYBOX_SYMLINKS): $(LOCAL_INSTALLED_MODULE)
+	@echo "Symlink: $@ -> $(BUSYBOX_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(BUSYBOX_BINARY) $@
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := busybox_symlinks
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(RECOVERY_BUSYBOX_SYMLINKS)
+include $(BUILD_PHONY_PACKAGE)
+RECOVERY_BUSYBOX_SYMLINKS :=
+endif # !TW_USE_TOOLBOX
+
 # All the APIs for testing
 include $(CLEAR_VARS)
 LOCAL_MODULE := libverifier
@@ -105,18 +439,33 @@
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
+LOCAL_SRC_FILES := fuse_sideload.c
+
+LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+
+LOCAL_MODULE := libfusesideload
+
+LOCAL_SHARED_LIBRARIES := libcutils libc libmincrypttwrp
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
 LOCAL_MODULE := verifier_test
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_TAGS := tests
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes
+
 LOCAL_CFLAGS += -DNO_RECOVERY_MOUNT
 LOCAL_CFLAGS += -Wno-unused-parameter
+
 LOCAL_SRC_FILES := \
     verifier_test.cpp \
     asn1_decoder.cpp \
     verifier.cpp \
     ui.cpp
 LOCAL_STATIC_LIBRARIES := \
-    libmincrypt \
+    libmincrypttwrp \
     libminui \
     libminzip \
     libcutils \
@@ -124,8 +473,24 @@
     libc
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
 
+LOCAL_MODULE := libaosprecovery
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes
+LOCAL_SRC_FILES = adb_install.cpp bootloader.cpp verifier.cpp mtdutils/mtdutils.c legacy_property_service.c
+LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils libfusesideload
+LOCAL_STATIC_LIBRARIES += libmincrypttwrp
+
+ifneq ($(BOARD_RECOVERY_BLDRMSG_OFFSET),)
+    LOCAL_CFLAGS += -DBOARD_RECOVERY_BLDRMSG_OFFSET=$(BOARD_RECOVERY_BLDRMSG_OFFSET)
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+commands_recovery_local_path := $(LOCAL_PATH)
 include $(LOCAL_PATH)/minui/Android.mk \
+    $(LOCAL_PATH)/minadbd/Android.mk \
     $(LOCAL_PATH)/minzip/Android.mk \
     $(LOCAL_PATH)/minadbd/Android.mk \
     $(LOCAL_PATH)/mtdutils/Android.mk \
@@ -135,3 +500,69 @@
     $(LOCAL_PATH)/uncrypt/Android.mk \
     $(LOCAL_PATH)/updater/Android.mk \
     $(LOCAL_PATH)/applypatch/Android.mk
+
+#includes for TWRP
+include $(commands_recovery_local_path)/injecttwrp/Android.mk \
+    $(commands_recovery_local_path)/htcdumlock/Android.mk \
+    $(commands_recovery_local_path)/gui/Android.mk \
+    $(commands_recovery_local_path)/mmcutils/Android.mk \
+    $(commands_recovery_local_path)/bmlutils/Android.mk \
+    $(commands_recovery_local_path)/prebuilt/Android.mk \
+    $(commands_recovery_local_path)/mtdutils/Android.mk \
+    $(commands_recovery_local_path)/flashutils/Android.mk \
+    $(commands_recovery_local_path)/pigz/Android.mk \
+    $(commands_recovery_local_path)/dosfstools/Android.mk \
+    $(commands_recovery_local_path)/libtar/Android.mk \
+    $(commands_recovery_local_path)/crypto/cryptsettings/Android.mk \
+    $(commands_recovery_local_path)/crypto/cryptfs/Android.mk \
+    $(commands_recovery_local_path)/libcrecovery/Android.mk \
+    $(commands_recovery_local_path)/libblkid/Android.mk \
+    $(commands_recovery_local_path)/minuitwrp/Android.mk \
+    $(commands_recovery_local_path)/openaes/Android.mk \
+    $(commands_recovery_local_path)/toolbox/Android.mk \
+    $(commands_recovery_local_path)/libmincrypt/Android.mk \
+    $(commands_recovery_local_path)/twrpTarMain/Android.mk \
+    $(commands_recovery_local_path)/mtp/Android.mk
+
+ifeq ($(TW_INCLUDE_CRYPTO_SAMSUNG), true)
+    include $(commands_recovery_local_path)/crypto/libcrypt_samsung/Android.mk
+endif
+
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+    include $(commands_recovery_local_path)/crypto/jb/Android.mk
+    include $(commands_recovery_local_path)/crypto/fs_mgr/Android.mk
+    include $(commands_recovery_local_path)/crypto/logwrapper/Android.mk
+    include $(commands_recovery_local_path)/crypto/scrypt/Android.mk
+    include $(commands_recovery_local_path)/crypto/crypttools/Android.mk
+endif
+ifeq ($(TWHAVE_SELINUX), true)
+    include $(commands_recovery_local_path)/minzip/Android.mk
+else
+    include $(commands_recovery_local_path)/minzipold/Android.mk
+endif
+ifeq ($(BUILD_ID), GINGERBREAD)
+    TW_NO_EXFAT := true
+endif
+ifneq ($(TW_NO_EXFAT), true)
+    include $(commands_recovery_local_path)/exfat/mkfs/Android.mk \
+            $(commands_recovery_local_path)/fuse/Android.mk \
+            $(commands_recovery_local_path)/exfat/libexfat/Android.mk
+endif
+ifneq ($(TW_NO_EXFAT_FUSE), true)
+    include $(commands_recovery_local_path)/exfat/exfat-fuse/Android.mk
+endif
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    include $(commands_recovery_local_path)/crypto/ics/Android.mk
+endif
+ifneq ($(TW_OEM_BUILD),true)
+    include $(commands_recovery_local_path)/orscmd/Android.mk
+endif
+
+# FB2PNG
+ifeq ($(TW_INCLUDE_FB2PNG), true)
+    include $(commands_recovery_local_path)/fb2png/Android.mk
+endif
+
+commands_recovery_local_path :=
+
+endif
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b3c4529
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+**Team Win Recovery Project (TWRP)**
+
+The goal of this branch is to rebase TWRP onto AOSP while maintaining as much of the original AOSP code as possible. This goal should allow us to apply updates to the AOSP code going forward with little to no extra work.  With this goal in mind, we will carefully consider any changes needed to the AOSP code before allowing them.  In most cases, instead of changing the AOSP code, we'll create our own functions instead.  The only changes that should be made to AOSP code should be those affecting startup of the recovery and some of the make files.
+
+If there are changes that need to be merged from AOSP, we will pull the change directly from AOSP instead of creating a new patch in order to prevent merge conflicts with AOSP.
+
+This branch is under final testing and will be used shortly for public builds, but has not officially been released.
+
+You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide").
+
+[More information about the project.](http://www.teamw.in/project/twrp2 "More Information")
+
+If you have code changes to submit those should be pushed to our gerrit instance.  A guide can be found [here](http://teamw.in/twrp2-gerrit "Gerrit Guide").
diff --git a/adb_install.cpp b/adb_install.cpp
index be3b9a0..e10cb4a 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -24,11 +24,10 @@
 #include <sys/stat.h>
 #include <signal.h>
 #include <fcntl.h>
+#include <stdio.h>
 
 #include "ui.h"
 #include "cutils/properties.h"
-#include "install.h"
-#include "common.h"
 #include "adb_install.h"
 extern "C" {
 #include "minadbd/fuse_adb_provider.h"
@@ -41,14 +40,24 @@
 set_usb_driver(bool enabled) {
     int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY);
     if (fd < 0) {
+/* These error messages show when built in older Android branches (e.g. Gingerbread)
+   It's not a critical error so we're disabling the error messages.
         ui->Print("failed to open driver control: %s\n", strerror(errno));
+*/
+		printf("failed to open driver control: %s\n", strerror(errno));
         return;
     }
     if (write(fd, enabled ? "1" : "0", 1) < 0) {
+/*
         ui->Print("failed to set driver control: %s\n", strerror(errno));
+*/
+		printf("failed to set driver control: %s\n", strerror(errno));
     }
     if (close(fd) < 0) {
+/*
         ui->Print("failed to close driver control: %s\n", strerror(errno));
+*/
+		printf("failed to close driver control: %s\n", strerror(errno));
     }
 }
 
@@ -64,7 +73,7 @@
     char value[PROPERTY_VALUE_MAX+1];
     int len = property_get("ro.debuggable", value, NULL);
     if (len == 1 && value[0] == '1') {
-        ui->Print("Restarting adbd...\n");
+        printf("Restarting adbd...\n");
         set_usb_driver(true);
         property_set("ctl.start", "adbd");
     }
@@ -75,21 +84,24 @@
 #define ADB_INSTALL_TIMEOUT 300
 
 int
-apply_from_adb(RecoveryUI* ui_, int* wipe_cache, const char* install_file) {
-    ui = ui_;
+apply_from_adb(const char* install_file) {
 
     stop_adbd();
     set_usb_driver(true);
-
+/*
     ui->Print("\n\nNow send the package you want to apply\n"
               "to the device with \"adb sideload <filename>\"...\n");
-
+*/
     pid_t child;
     if ((child = fork()) == 0) {
-        execl("/sbin/recovery", "recovery", "--adbd", NULL);
+        execl("/sbin/recovery", "recovery", "--adbd", install_file, NULL);
         _exit(-1);
     }
 
+	char child_prop[PROPERTY_VALUE_MAX];
+	sprintf(child_prop, "%i", child);
+	property_set("tw_child_pid", child_prop);
+
     // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host
     // connects and starts serving a package.  Poll for its
     // appearance.  (Note that inotify doesn't work with FUSE.)
@@ -133,14 +145,21 @@
 
     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
         if (WEXITSTATUS(status) == 3) {
-            ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
+            printf("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
         } else if (!WIFSIGNALED(status)) {
-            ui->Print("\n(adbd status %d)\n", WEXITSTATUS(status));
+            printf("status %d\n", WEXITSTATUS(status));
         }
     }
-
     set_usb_driver(false);
     maybe_restart_adbd();
 
-    return result;
+    if (stat(install_file, &st) != 0) {
+        if (errno == ENOENT) {
+            printf("No package received.\n");
+        } else {
+            printf("Error reading package:\n  %s\n", strerror(errno));
+        }
+        return -1;
+    }
+	return 0;
 }
diff --git a/adb_install.h b/adb_install.h
index a18b712..a7cb336 100644
--- a/adb_install.h
+++ b/adb_install.h
@@ -17,8 +17,8 @@
 #ifndef _ADB_INSTALL_H
 #define _ADB_INSTALL_H
 
-class RecoveryUI;
+//class RecoveryUI;
 
-int apply_from_adb(RecoveryUI* h, int* wipe_cache, const char* install_file);
+int apply_from_adb(const char* install_file);
 
 #endif
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index 4984093..2cce81f 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -15,11 +15,22 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
 LOCAL_SRC_FILES := applypatch.c bspatch.c freecache.c imgpatch.c utils.c
 LOCAL_MODULE := libapplypatch
 LOCAL_MODULE_TAGS := eng
-LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery
-LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypt libbz libz
+LOCAL_C_INCLUDES += \
+    external/bzip2 \
+    external/zlib \
+    $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypttwrp libbz libz
 
 include $(BUILD_STATIC_LIBRARY)
 
@@ -27,8 +38,9 @@
 
 LOCAL_SRC_FILES := main.c
 LOCAL_MODULE := applypatch
-LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES += $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypttwrp libbz
 LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
 
 include $(BUILD_EXECUTABLE)
@@ -38,9 +50,9 @@
 LOCAL_SRC_FILES := main.c
 LOCAL_MODULE := applypatch_static
 LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_TAGS := eng
-LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_MODULE_TAGS := optional eng
+LOCAL_C_INCLUDES += $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypttwrp libbz
 LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
 
 include $(BUILD_EXECUTABLE)
@@ -52,5 +64,6 @@
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_C_INCLUDES += external/zlib external/bzip2
 LOCAL_STATIC_LIBRARIES += libz libbz
+LOCAL_MODULE_TAGS := eng
 
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index 2c86e09..73195d9 100644
--- a/applypatch/applypatch.c
+++ b/applypatch/applypatch.c
@@ -28,6 +28,7 @@
 
 #include "mincrypt/sha.h"
 #include "applypatch.h"
+#include "bmlutils/bmlutils.h"
 #include "mtdutils/mtdutils.h"
 #include "edify/expr.h"
 
@@ -55,7 +56,8 @@
     // A special 'filename' beginning with "MTD:" or "EMMC:" means to
     // load the contents of a partition.
     if (strncmp(filename, "MTD:", 4) == 0 ||
-        strncmp(filename, "EMMC:", 5) == 0) {
+        strncmp(filename, "EMMC:", 5) == 0 ||
+        strncmp(filename, "BML:", 4) == 0) {
         return LoadPartitionContents(filename, file);
     }
 
@@ -131,6 +133,8 @@
         type = MTD;
     } else if (strcmp(magic, "EMMC") == 0) {
         type = EMMC;
+    } else if (strcmp(magic, "BML") == 0) {
+        type = EMMC;
     } else {
         printf("LoadPartitionContents called with bad filename (%s)\n",
                filename);
@@ -138,6 +142,14 @@
     }
     const char* partition = strtok(NULL, ":");
 
+    if (strcmp(magic, "BML") == 0) {
+        if (strcmp(partition, "boot") == 0) {
+            partition = BOARD_BML_BOOT;
+        } else if (strcmp(partition, "recovery") == 0) {
+            partition = BOARD_BML_RECOVERY;
+        }
+    }
+
     int i;
     int colons = 0;
     for (i = 0; filename[i] != '\0'; ++i) {
@@ -358,12 +370,31 @@
         type = MTD;
     } else if (strcmp(magic, "EMMC") == 0) {
         type = EMMC;
+    } else if (strcmp(magic, "BML") == 0) {
+        type = EMMC;
     } else {
         printf("WriteToPartition called with bad target (%s)\n", target);
         return -1;
     }
     const char* partition = strtok(NULL, ":");
 
+    if (strcmp(magic, "BML") == 0) {
+        if (strcmp(partition, "boot") == 0) {
+            partition = BOARD_BML_BOOT;
+        } else if (strcmp(partition, "recovery") == 0) {
+            partition = BOARD_BML_RECOVERY;
+        }
+
+        int bmlpartition = open(partition, O_RDWR | O_LARGEFILE);
+        if (bmlpartition < 0)
+            return -1;
+        if (ioctl(bmlpartition, BML_UNLOCK_ALL, 0)) {
+            printf("failed to unlock BML partition: (%s)\n", partition);
+            return -1;
+        }
+        close(bmlpartition);
+    }
+
     if (partition == NULL) {
         printf("bad partition target name \"%s\"\n", target);
         return -1;
@@ -835,7 +866,8 @@
         // file?
 
         if (strncmp(target_filename, "MTD:", 4) == 0 ||
-            strncmp(target_filename, "EMMC:", 5) == 0) {
+            strncmp(target_filename, "EMMC:", 5) == 0 ||
+            strncmp(target_filename, "BML:", 4) == 0) {
             // If the target is a partition, we're actually going to
             // write the output to /tmp and then copy it to the
             // partition.  statfs() always returns 0 blocks free for
@@ -877,7 +909,8 @@
                 // location.
 
                 if (strncmp(source_filename, "MTD:", 4) == 0 ||
-                    strncmp(source_filename, "EMMC:", 5) == 0) {
+                    strncmp(source_filename, "EMMC:", 5) == 0 ||
+                    strncmp(source_filename, "BML:", 4) == 0) {
                     // It's impossible to free space on the target filesystem by
                     // deleting the source if the source is a partition.  If
                     // we're ever in a state where we need to do this, fail.
@@ -922,7 +955,8 @@
         output = -1;
         outname = NULL;
         if (strncmp(target_filename, "MTD:", 4) == 0 ||
-            strncmp(target_filename, "EMMC:", 5) == 0) {
+            strncmp(target_filename, "EMMC:", 5) == 0 ||
+            strncmp(target_filename, "BML:", 4) == 0) {
             // We store the decoded output in memory.
             msi.buffer = malloc(target_size);
             if (msi.buffer == NULL) {
diff --git a/bmlutils/Android.mk b/bmlutils/Android.mk
new file mode 100644
index 0000000..7c6676b
--- /dev/null
+++ b/bmlutils/Android.mk
@@ -0,0 +1,35 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+LOCAL_STATIC_LIBRARIES := libcrecovery
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)/libcrecovery
+
+LOCAL_SRC_FILES := bmlutils.c
+LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_STATIC_LIBRARY)
+
+#Added for building TWRP dynamic:
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+LOCAL_SRC_FILES := bmlutils.c
+LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_SHARED_LIBRARY)
diff --git a/bmlutils/bmlutils.c b/bmlutils/bmlutils.c
new file mode 100644
index 0000000..d59475e
--- /dev/null
+++ b/bmlutils/bmlutils.c
@@ -0,0 +1,241 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <sys/limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <bmlutils.h>
+
+#undef _PATH_BSHELL
+#define _PATH_BSHELL "/sbin/sh"
+
+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);
+}
+
+static struct pid {
+	struct pid *next;
+	FILE *fp;
+	pid_t pid;
+} *pidlist;
+
+
+static int restore_internal(const char* bml, const char* filename)
+{
+    char buf[4096];
+    int dstfd, srcfd, bytes_read, bytes_written, total_read = 0;
+    if (filename == NULL)
+        srcfd = 0;
+    else {
+        srcfd = open(filename, O_RDONLY | O_LARGEFILE);
+        if (srcfd < 0)
+            return 2;
+    }
+    dstfd = open(bml, O_RDWR | O_LARGEFILE);
+    if (dstfd < 0)
+        return 3;
+    if (ioctl(dstfd, BML_UNLOCK_ALL, 0))
+        return 4;
+    do {
+        total_read += bytes_read = read(srcfd, buf, 4096);
+        if (!bytes_read)
+            break;
+        if (bytes_read < 4096)
+            memset(&buf[bytes_read], 0, 4096 - bytes_read);
+        if (write(dstfd, buf, 4096) < 4096)
+            return 5;
+    } while(bytes_read == 4096);
+    
+    close(dstfd);
+    close(srcfd);
+    
+    return 0;
+}
+
+int cmd_bml_restore_raw_partition(const char *partition, const char *filename)
+{
+    if (strcmp(partition, "boot") != 0 && strcmp(partition, "recovery") != 0 && strcmp(partition, "recoveryonly") != 0 && partition[0] != '/')
+        return 6;
+
+    int ret = -1;
+    if (strcmp(partition, "recoveryonly") != 0) {
+        // always restore boot, regardless of whether recovery or boot is flashed.
+        // this is because boot and recovery are the same on some samsung phones.
+        // unless of course, recoveryonly is explictly chosen (bml8)
+        ret = restore_internal(BOARD_BML_BOOT, filename);
+        if (ret != 0)
+            return ret;
+    }
+
+    if (strcmp(partition, "recovery") == 0 || strcmp(partition, "recoveryonly") == 0)
+        ret = restore_internal(BOARD_BML_RECOVERY, filename);
+
+    // support explicitly provided device paths
+    if (partition[0] == '/')
+        ret = restore_internal(partition, filename);
+    return ret;
+}
+
+int cmd_bml_backup_raw_partition(const char *partition, const char *out_file)
+{
+    char* bml;
+    if (strcmp("boot", partition) == 0)
+        bml = BOARD_BML_BOOT;
+    else if (strcmp("recovery", partition) == 0)
+        bml = BOARD_BML_RECOVERY;
+    else if (partition[0] == '/') {
+        // support explicitly provided device paths
+        bml = partition;
+    }
+    else {
+        printf("Invalid partition.\n");
+        return -1;
+    }
+
+    int ch;
+    FILE *in;
+    FILE *out;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    char *in_file = bml;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    out = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR2;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    if (sz % 512)
+    {
+        while ( ( ch = fgetc ( in ) ) != EOF )
+            fputc ( ch, out );
+    }
+    else
+    {
+        for (i=0; i< (sz/512); i++)
+        {
+            if ((fread(buf, 512, 1, in)) != 1)
+                goto ERROR1;
+            if ((fwrite(buf, 512, 1, out)) != 1)
+                goto ERROR1;
+        }
+    }
+
+    fsync(out);
+    ret = 0;
+ERROR1:
+    fclose ( out );
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+}
+
+int cmd_bml_erase_raw_partition(const char *partition)
+{
+    // TODO: implement raw wipe
+    return 0;
+}
+
+int cmd_bml_erase_partition(const char *partition, const char *filesystem)
+{
+    return -1;
+}
+
+int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
+{
+    return -1;
+}
+
+int cmd_bml_get_partition_device(const char *partition, char *device)
+{
+    return -1;
+}
+
+int format_rfs_device (const char *device, const char *path) {
+    const char *fatsize = "32";
+    const char *sectorsize = "1";
+
+    if (strcmp(path, "/datadata") == 0 || strcmp(path, "/cache") == 0) {
+        fatsize = "16";
+    }
+
+    // Just in case /data sector size needs to be altered
+    else if (strcmp(path, "/data") == 0 ) {
+        sectorsize = "1";
+    } 
+
+    // dump 10KB of zeros to partition before format due to fat.format bug
+    char cmd[PATH_MAX];
+
+    sprintf(cmd, "/sbin/dd if=/dev/zero of=%s bs=4096 count=10", device);
+    if(__system(cmd)) {
+        printf("failure while zeroing rfs partition.\n");
+        return -1;
+    }
+
+    // Run fat.format
+    sprintf(cmd, "/sbin/fat.format -F %s -S 4096 -s %s %s", fatsize, sectorsize, device);
+    if(__system(cmd)) {
+        printf("failure while running fat.format\n");
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/bmlutils/bmlutils.h b/bmlutils/bmlutils.h
new file mode 100644
index 0000000..1e85f08
--- /dev/null
+++ b/bmlutils/bmlutils.h
@@ -0,0 +1,16 @@
+#ifndef BMLUTILS_H_
+#define BMLUTILS_H_
+
+int format_rfs_device (const char *device, const char *path);
+
+#define BML_UNLOCK_ALL				0x8A29		///< unlock all partition RO -> RW
+
+#ifndef BOARD_BML_BOOT
+#define BOARD_BML_BOOT              "/dev/block/bml7"
+#endif
+
+#ifndef BOARD_BML_RECOVERY
+#define BOARD_BML_RECOVERY          "/dev/block/bml8"
+#endif
+
+#endif // BMLUTILS_H_
diff --git a/bootloader.cpp b/bootloader.cpp
index 600d238..3884c28 100644
--- a/bootloader.cpp
+++ b/bootloader.cpp
@@ -14,10 +14,14 @@
  * limitations under the License.
  */
 
+/*
 #include <fs_mgr.h>
+*/
 #include "bootloader.h"
 #include "common.h"
+extern "C" {
 #include "mtdutils/mtdutils.h"
+}
 #include "roots.h"
 
 #include <errno.h>
@@ -26,38 +30,42 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+static char device_type = 'e'; // e for emmc or m for mtd, default is emmc
+static char device_name[256];
+
+/*
 static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v);
 static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v);
 static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v);
 static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v);
-
+*/
 int get_bootloader_message(struct bootloader_message *out) {
-    Volume* v = volume_for_path("/misc");
-    if (v == NULL) {
+    //volume_for_path("/misc");
+    if (device_name[0] == 0) {
       LOGE("Cannot load volume /misc!\n");
       return -1;
     }
-    if (strcmp(v->fs_type, "mtd") == 0) {
-        return get_bootloader_message_mtd(out, v);
-    } else if (strcmp(v->fs_type, "emmc") == 0) {
-        return get_bootloader_message_block(out, v);
+    if (device_type == 'm') {
+        return get_bootloader_message_mtd_name(out);
+    } else if (device_type == 'e') {
+        return get_bootloader_message_block_name(out);
     }
-    LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
+    LOGE("unknown misc partition fs_type \"%c\"\n", device_type);
     return -1;
 }
 
 int set_bootloader_message(const struct bootloader_message *in) {
-    Volume* v = volume_for_path("/misc");
-    if (v == NULL) {
+    //volume_for_path("/misc");
+    if (device_name[0] == 0) {
       LOGE("Cannot load volume /misc!\n");
       return -1;
     }
-    if (strcmp(v->fs_type, "mtd") == 0) {
-        return set_bootloader_message_mtd(in, v);
-    } else if (strcmp(v->fs_type, "emmc") == 0) {
-        return set_bootloader_message_block(in, v);
+    if (device_type == 'm') {
+        return set_bootloader_message_mtd_name(in, device_name);
+    } else if (device_type == 'e') {
+        return set_bootloader_message_block_name(in, device_name);
     }
-    LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
+    LOGE("unknown misc partition type \"%c\"\n", device_type);
     return -1;
 }
 
@@ -67,7 +75,7 @@
 
 static const int MISC_PAGES = 3;         // number of pages to save
 static const int MISC_COMMAND_PAGE = 1;  // bootloader command is this page
-
+/*
 static int get_bootloader_message_mtd(struct bootloader_message *out,
                                       const Volume* v) {
     size_t write_size;
@@ -137,7 +145,89 @@
     LOGI("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : "");
     return 0;
 }
+*/
 
+void set_device_type(char new_type) {
+	device_type = new_type;
+}
+
+void set_device_name(const char* new_name) {
+	if (strlen(new_name) >= sizeof(device_name)) {
+		LOGE("New device name of '%s' is too large for bootloader.cpp\n", new_name);
+	} else {
+		strcpy(device_name, new_name);
+	}
+}
+
+int get_bootloader_message_mtd_name(struct bootloader_message *out) {
+    size_t write_size;
+    mtd_scan_partitions();
+    const MtdPartition *part = mtd_find_partition_by_name(device_name);
+    if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
+        LOGE("Can't find %s\n", device_name);
+        return -1;
+    }
+
+    MtdReadContext *read = mtd_read_partition(part);
+    if (read == NULL) {
+        LOGE("Can't open %s\n(%s)\n", device_name, strerror(errno));
+        return -1;
+    }
+
+    const ssize_t size = write_size * MISC_PAGES;
+    char data[size];
+    ssize_t r = mtd_read_data(read, data, size);
+    if (r != size) LOGE("Can't read %s\n(%s)\n", device_name, strerror(errno));
+    mtd_read_close(read);
+    if (r != size) return -1;
+
+    memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out));
+    return 0;
+}
+
+int set_bootloader_message_mtd_name(const struct bootloader_message *in,
+                                      const char* mtd_name) {
+    size_t write_size;
+    mtd_scan_partitions();
+    const MtdPartition *part = mtd_find_partition_by_name(mtd_name);
+    if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
+        printf("Can't find %s\n", mtd_name);
+        return -1;
+    }
+
+    MtdReadContext *read = mtd_read_partition(part);
+    if (read == NULL) {
+        printf("Can't open %s\n(%s)\n", mtd_name, strerror(errno));
+        return -1;
+    }
+
+    ssize_t size = write_size * MISC_PAGES;
+    char data[size];
+    ssize_t r = mtd_read_data(read, data, size);
+    if (r != size) printf("Can't read %s\n(%s)\n", mtd_name, strerror(errno));
+    mtd_read_close(read);
+    if (r != size) return -1;
+
+    memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in));
+
+    MtdWriteContext *write = mtd_write_partition(part);
+    if (write == NULL) {
+        printf("Can't open %s\n(%s)\n", mtd_name, strerror(errno));
+        return -1;
+    }
+    if (mtd_write_data(write, data, size) != size) {
+        printf("Can't write %s\n(%s)\n", mtd_name, strerror(errno));
+        mtd_write_close(write);
+        return -1;
+    }
+    if (mtd_write_close(write)) {
+        printf("Can't finish %s\n(%s)\n", mtd_name, strerror(errno));
+        return -1;
+    }
+
+    printf("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : "");
+    return 0;
+}
 
 // ------------------------------------
 // for misc partitions on block devices
@@ -159,7 +249,7 @@
         printf("failed to stat %s\n", fn);
     }
 }
-
+/*
 static int get_bootloader_message_block(struct bootloader_message *out,
                                         const Volume* v) {
     wait_for_device(v->blk_device);
@@ -168,6 +258,9 @@
         LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
         return -1;
     }
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+    fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_SET);
+#endif
     struct bootloader_message temp;
     int count = fread(&temp, sizeof(temp), 1, f);
     if (count != 1) {
@@ -185,11 +278,14 @@
 static int set_bootloader_message_block(const struct bootloader_message *in,
                                         const Volume* v) {
     wait_for_device(v->blk_device);
-    FILE* f = fopen(v->blk_device, "wb");
+    FILE* f = fopen(v->blk_device, "rb+");
     if (f == NULL) {
         LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
         return -1;
     }
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+    fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_SET);
+#endif
     int count = fwrite(in, sizeof(*in), 1, f);
     if (count != 1) {
         LOGE("Failed writing %s\n(%s)\n", v->blk_device, strerror(errno));
@@ -201,3 +297,123 @@
     }
     return 0;
 }
+*/
+
+int get_bootloader_message_block_name(struct bootloader_message *out) {
+    wait_for_device(device_name);
+    FILE* f = fopen(device_name, "rb");
+    if (f == NULL) {
+        LOGE("Can't open %s\n(%s)\n", device_name, strerror(errno));
+        return -1;
+    }
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+    fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_SET);
+#endif
+    struct bootloader_message temp;
+    int count = fread(&temp, sizeof(temp), 1, f);
+    if (count != 1) {
+        LOGE("Failed reading %s\n(%s)\n", device_name, strerror(errno));
+        return -1;
+    }
+    if (fclose(f) != 0) {
+        LOGE("Failed closing %s\n(%s)\n", device_name, strerror(errno));
+        return -1;
+    }
+    memcpy(out, &temp, sizeof(temp));
+    return 0;
+}
+
+int set_bootloader_message_block_name(const struct bootloader_message *in,
+                                        const char* block_name) {
+    wait_for_device(block_name);
+    FILE* f = fopen(block_name, "rb+");
+    if (f == NULL) {
+        printf("Can't open %s\n(%s)\n", block_name, strerror(errno));
+        return -1;
+    }
+#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET
+    fseek(f, BOARD_RECOVERY_BLDRMSG_OFFSET, SEEK_SET);
+#endif
+    int count = fwrite(in, sizeof(*in), 1, f);
+    if (count != 1) {
+        printf("Failed writing %s\n(%s)\n", block_name, strerror(errno));
+        return -1;
+    }
+    if (fclose(f) != 0) {
+        printf("Failed closing %s\n(%s)\n", block_name, strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+static const char *COMMAND_FILE = "/cache/recovery/command";
+static const int MAX_ARG_LENGTH = 4096;
+static const int MAX_ARGS = 100;
+
+// command line args come from, in decreasing precedence:
+//   - the actual command line
+//   - the bootloader control block (one per line, after "recovery")
+//   - the contents of COMMAND_FILE (one per line)
+void
+get_args(int *argc, char ***argv) {
+    struct bootloader_message boot;
+    memset(&boot, 0, sizeof(boot));
+    get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure
+
+    if (boot.command[0] != 0 && boot.command[0] != 255) {
+        LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
+    }
+
+    if (boot.status[0] != 0 && boot.status[0] != 255) {
+        LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
+    }
+
+    // --- if arguments weren't supplied, look in the bootloader control block
+    if (*argc <= 1) {
+        boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
+        const char *arg = strtok(boot.recovery, "\n");
+        if (arg != NULL && !strcmp(arg, "recovery")) {
+            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
+            (*argv)[0] = strdup(arg);
+            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
+                if ((arg = strtok(NULL, "\n")) == NULL) break;
+                (*argv)[*argc] = strdup(arg);
+            }
+            LOGI("Got arguments from boot message\n");
+        } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
+            LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
+        }
+    }
+
+    // --- if that doesn't work, try the command file
+    if (*argc <= 1) {
+        FILE *fp = fopen(COMMAND_FILE, "r");
+        if (fp != NULL) {
+            char *argv0 = (*argv)[0];
+            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
+            (*argv)[0] = argv0;  // use the same program name
+
+            char buf[MAX_ARG_LENGTH];
+            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
+                if (!fgets(buf, sizeof(buf), fp)) break;
+                (*argv)[*argc] = strdup(strtok(buf, "\r\n"));  // Strip newline.
+            }
+
+            fflush(fp);
+		    if (ferror(fp)) LOGE("Error in %s\n(%s)\n", COMMAND_FILE, strerror(errno));
+		    fclose(fp);
+            LOGI("Got arguments from %s\n", COMMAND_FILE);
+        }
+    }
+
+    // --> write the arguments we have back into the bootloader control block
+    // always boot into recovery after this (until finish_recovery() is called)
+    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+    strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+    int i;
+    for (i = 1; i < *argc; ++i) {
+        strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
+        strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+    }
+    set_bootloader_message(&boot);
+}
diff --git a/bootloader.h b/bootloader.h
index c2895dd..15e0a5c 100644
--- a/bootloader.h
+++ b/bootloader.h
@@ -61,8 +61,20 @@
 /* Read and write the bootloader command from the "misc" partition.
  * These return zero on success.
  */
+/*
 int get_bootloader_message(struct bootloader_message *out);
 int set_bootloader_message(const struct bootloader_message *in);
+*/
+
+void set_device_type(char new_type);
+void set_device_name(const char* new_name);
+
+int get_bootloader_message_mtd_name(struct bootloader_message *out);
+int set_bootloader_message_mtd_name(const struct bootloader_message *in, const char* mtd_name);
+int get_bootloader_message_block_name(struct bootloader_message *out);
+int set_bootloader_message_block_name(const struct bootloader_message *in, const char* block_name);
+
+void get_args(int *argc, char ***argv);
 
 #ifdef __cplusplus
 }
diff --git a/common.h b/common.h
index 768f499..18b0304 100644
--- a/common.h
+++ b/common.h
@@ -24,7 +24,13 @@
 extern "C" {
 #endif
 
-#define LOGE(...) ui_print("E:" __VA_ARGS__)
+static long tmplog_offset = 0;
+
+#define ui_print(...) printf(__VA_ARGS__)
+#define ui_print_overwrite(...) printf(__VA_ARGS__)
+
+// TODO: restore ui_print for LOGE
+#define LOGE(...) printf("E:" __VA_ARGS__)
 #define LOGW(...) fprintf(stdout, "W:" __VA_ARGS__)
 #define LOGI(...) fprintf(stdout, "I:" __VA_ARGS__)
 
@@ -44,7 +50,7 @@
 // fopen a file, mounting volumes and making parent dirs as necessary.
 FILE* fopen_path(const char *path, const char *mode);
 
-void ui_print(const char* format, ...);
+//void ui_print(const char* format, ...);
 
 #ifdef __cplusplus
 }
diff --git a/crypto/cryptfs/Android.mk b/crypto/cryptfs/Android.mk
new file mode 100644
index 0000000..f0388c2
--- /dev/null
+++ b/crypto/cryptfs/Android.mk
@@ -0,0 +1,54 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+LOCAL_SRC_FILES:= \
+	cryptfs.c
+
+LOCAL_CFLAGS:= -g -c -W -I../fs_mgr/include
+LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO
+LOCAL_CFLAGS += -DCRYPTO_FS_TYPE=\"$(TW_CRYPTO_FS_TYPE)\"
+LOCAL_CFLAGS += -DCRYPTO_REAL_BLKDEV=\"$(TW_CRYPTO_REAL_BLKDEV)\"
+LOCAL_CFLAGS += -DCRYPTO_MNT_POINT=\"$(TW_CRYPTO_MNT_POINT)\"
+LOCAL_CFLAGS += -DCRYPTO_FS_OPTIONS=\"$(TW_CRYPTO_FS_OPTIONS)\"
+LOCAL_CFLAGS += -DCRYPTO_FS_FLAGS=\"$(TW_CRYPTO_FS_FLAGS)\"
+LOCAL_CFLAGS += -DCRYPTO_KEY_LOC=\"$(TW_CRYPTO_KEY_LOC)\"
+ifdef TW_CRYPTO_SD_REAL_BLKDEV
+    LOCAL_CFLAGS += -DCRYPTO_SD_REAL_BLKDEV=\"$(TW_CRYPTO_SD_REAL_BLKDEV)\"
+    LOCAL_CFLAGS += -DCRYPTO_SD_FS_TYPE=\"$(TW_CRYPTO_SD_FS_TYPE)\"
+endif
+ifneq ($(TW_INTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_PATH=$(TW_INTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_INTERNAL_STORAGE_MOUNT_POINT),)
+	LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_MOUNT_POINT=$(TW_INTERNAL_STORAGE_MOUNT_POINT)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_PATH=$(TW_EXTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_MOUNT_POINT),)
+	LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_MOUNT_POINT=$(TW_EXTERNAL_STORAGE_MOUNT_POINT)
+endif
+
+LOCAL_C_INCLUDES += system/extras/ext4_utils external/openssl/include
+LOCAL_MODULE:=cryptfs
+LOCAL_MODULE_TAGS:= eng
+LOCAL_SHARED_LIBRARIES += libc libcutils
+LOCAL_SHARED_LIBRARIES += libcrypto
+
+
+#LOCAL_LDFLAGS += -L$(TARGET_OUT_SHARED_LIBRARIES) -lsec_km -lsec_ecryptfs -ldl
+LOCAL_LDFLAGS += -ldl
+
+LOCAL_STATIC_LIBRARIES += libmtdutils
+LOCAL_STATIC_LIBRARIES += libminzip libunz
+LOCAL_STATIC_LIBRARIES += libpixelflinger_static libpng libmincrypttwrp
+LOCAL_SHARED_LIBRARIES += libz libc libstlport libcutils libstdc++ libext4_utils
+LOCAL_STATIC_LIBRARIES += libcrypt_samsung
+
+
+LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
+#LOCAL_STATIC_LIBRARIES += libfs_mgrtwrp
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+include $(BUILD_EXECUTABLE)
+endif
diff --git a/crypto/cryptfs/cryptfs.c b/crypto/cryptfs/cryptfs.c
new file mode 100644
index 0000000..59e7add
--- /dev/null
+++ b/crypto/cryptfs/cryptfs.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 a3955269 all rights reversed, no rights reserved.
+ */
+
+#define TW_INCLUDE_CRYPTO_SAMSUNG
+#include "../ics/cryptfs.c"
+
+int dm_remove_device(const char *name)
+{
+    int r;
+    r = delete_crypto_blk_dev(name);
+    if(!r)
+        printf("crypto block device '%s' deleted.\n", name);
+    else
+        printf("deleting crypto block device '%s' failed. [%d - %s]\n", name, r, strerror(errno));
+    return r;
+}
+
+int ecryptfs_test(const char *pw)
+{
+   char pwbuf[256];
+   int r;
+
+   strcpy(pwbuf, pw);
+   // 0: building options without file encryption filtering.
+   // 1: building options with media files filtering.
+   // 2: building options with all new files filtering.
+   r = mount_ecryptfs_drive(pwbuf, "/emmc", "/emmc", 0);
+   printf("mount_ecryptfs_drive: %d\n", r);
+   r = mount("/dev/block/mmcblk1", "/emmc", "vfat", MS_RDONLY, "");
+   printf("mount: %d\n", r);
+
+   r = umount("/emmc");///dev/block/mmcblk1");
+   printf("umount: %d\n", r);
+
+   //r = unmount_ecryptfs_drive("/emmc");
+   //printf("unmount_ecryptfs_drive: %d\n", r);
+
+   return r;
+}
+
+int main(int argc, char* argv[])
+{
+    if(argc < 2)
+    {
+        printf("no args!\n");
+        return 1;
+    }
+
+    property_set("ro.crypto.state", "encrypted");
+
+    property_set("ro.crypto.fs_type", CRYPTO_FS_TYPE);
+    property_set("ro.crypto.fs_real_blkdev", CRYPTO_REAL_BLKDEV);
+    property_set("ro.crypto.fs_mnt_point", CRYPTO_MNT_POINT);
+    property_set("ro.crypto.fs_options", CRYPTO_FS_OPTIONS);
+    property_set("ro.crypto.fs_flags", CRYPTO_FS_FLAGS);
+    property_set("ro.crypto.keyfile.userdata", CRYPTO_KEY_LOC);
+
+#ifdef CRYPTO_SD_FS_TYPE
+    property_set("ro.crypto.sd_fs_type", CRYPTO_SD_FS_TYPE);
+    property_set("ro.crypto.sd_fs_real_blkdev", CRYPTO_SD_REAL_BLKDEV);
+    property_set("ro.crypto.sd_fs_mnt_point", EXPAND(TW_INTERNAL_STORAGE_PATH));
+#endif
+
+    property_set("rw.km_fips_status", "ready");
+
+    delete_crypto_blk_dev("userdata");
+    delete_crypto_blk_dev("sdcard");
+    delete_crypto_blk_dev("emmc");
+
+    cryptfs_check_passwd(argv[1]);
+
+    return 0;
+};
diff --git a/crypto/cryptsettings/Android.mk b/crypto/cryptsettings/Android.mk
new file mode 100644
index 0000000..3a57048
--- /dev/null
+++ b/crypto/cryptsettings/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+LOCAL_SRC_FILES:= \
+	cryptsettings.c
+LOCAL_CFLAGS:= -g -c -W
+LOCAL_MODULE:=cryptsettings
+LOCAL_MODULE_TAGS:= eng
+LOCAL_SHARED_LIBRARIES += libc libcutils
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+LOCAL_CFLAGS += -DTW_INCLUDE_JB_CRYPTO
+LOCAL_STATIC_LIBRARIES += libfs_mgrtwrp
+endif
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+include $(BUILD_EXECUTABLE)
+endif
diff --git a/crypto/cryptsettings/cryptsettings.c b/crypto/cryptsettings/cryptsettings.c
new file mode 100644
index 0000000..4fa2b93
--- /dev/null
+++ b/crypto/cryptsettings/cryptsettings.c
@@ -0,0 +1,55 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef TW_INCLUDE_JB_CRYPTO
+#include "../crypto/fs_mgr/include/fs_mgr.h"
+#endif
+
+#include "cutils/properties.h"
+
+#ifndef PROPERTY_VALUE_MAX
+#define PROPERTY_VALUE_MAX 255
+#endif
+#ifndef FSTAB_PREFIX
+#define FSTAB_PREFIX "/fstab."
+#endif
+
+int main(void)
+{
+	char prop[PROPERTY_VALUE_MAX];
+	char key_loc[PROPERTY_VALUE_MAX];
+	char blk_dev[PROPERTY_VALUE_MAX];
+	char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+
+	printf("This tool will gather the build flags needed for decryption support for TWRP.\n");
+	printf("This tool comes with no warranties whatsoever.\n");
+	printf("http://teamw.in\n\n");
+	property_get("ro.crypto.state", prop, "encrypted");
+	if (strcmp(prop, "encrypted") != 0)
+		printf("Your device is not encrypted, continuing anyway.\n\nTW_INCLUDE_CRYPTO := true\n");
+	property_get("ro.crypto.fs_type", prop, "ERROR");
+	printf("TW_CRYPTO_FS_TYPE := \"%s\"\n", prop);
+	property_get("ro.crypto.fs_real_blkdev", prop, "ERROR");
+	printf("TW_CRYPTO_REAL_BLKDEV := \"%s\"\n", prop);
+	property_get("ro.crypto.fs_mnt_point", prop, "ERROR");
+	printf("TW_CRYPTO_MNT_POINT := \"%s\"\n", prop);
+	property_get("ro.crypto.fs_options", prop, "ERROR");
+	printf("TW_CRYPTO_FS_OPTIONS := \"%s\"\n", prop);
+	property_get("ro.crypto.fs_flags", prop, "ERROR");
+	printf("TW_CRYPTO_FS_FLAGS := \"%s\"\n", prop);
+	property_get("ro.crypto.keyfile.userdata", prop, "footer");
+	printf("TW_CRYPTO_KEY_LOC := \"%s\"\n", prop);
+#ifdef TW_INCLUDE_JB_CRYPTO
+	printf("\n*** NEW FOR JELLY BEAN:\n");
+	strcpy(fstab_filename, FSTAB_PREFIX);
+	property_get("ro.hardware", fstab_filename + sizeof(FSTAB_PREFIX) - 1, "");
+	fs_mgr_get_crypt_info(fstab_filename, key_loc, blk_dev, sizeof(key_loc));
+	printf("fstab file location: '%s'\n\nTW_INCLUDE_JB_CRYPTO := true\n", fstab_filename);
+#endif
+
+	return 0;
+}
diff --git a/crypto/crypttools/Android.mk b/crypto/crypttools/Android.mk
new file mode 100644
index 0000000..fc62583
--- /dev/null
+++ b/crypto/crypttools/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+LOCAL_SRC_FILES:= \
+	getfooter.c
+LOCAL_CFLAGS:= -g -c -W
+LOCAL_MODULE:=getfooter
+LOCAL_MODULE_TAGS:= eng
+LOCAL_STATIC_LIBRARIES += libfs_mgrtwrp libc libcutils
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)/crypto/jb/
+include $(BUILD_EXECUTABLE)
+endif
\ No newline at end of file
diff --git a/crypto/crypttools/getfooter.c b/crypto/crypttools/getfooter.c
new file mode 100644
index 0000000..aa7f88e
--- /dev/null
+++ b/crypto/crypttools/getfooter.c
@@ -0,0 +1,219 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/dm-ioctl.h>
+#include <sys/mount.h>
+#include "../fs_mgr/include/fs_mgr.h"
+#include "cryptfs.h"
+
+#include "cutils/properties.h"
+
+#ifndef PROPERTY_VALUE_MAX
+#define PROPERTY_VALUE_MAX 255
+#endif
+#ifndef FSTAB_PREFIX
+#define FSTAB_PREFIX "/fstab."
+#endif
+#ifndef KEY_IN_FOOTER
+#define KEY_IN_FOOTER "footer"
+#endif
+
+struct fstab *fstab;
+
+static unsigned int get_blkdev_size(int fd)
+{
+  unsigned int nr_sec;
+
+  if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+    nr_sec = 0;
+  }
+
+  return nr_sec;
+}
+
+int get_crypt_ftr_info(char **metadata_fname, off64_t *off)
+{
+  static int cached_data = 0;
+  static off64_t cached_off = 0;
+  static char cached_metadata_fname[PROPERTY_VALUE_MAX] = "";
+  int fd;
+  char key_loc[PROPERTY_VALUE_MAX];
+  char real_blkdev[PROPERTY_VALUE_MAX];
+  unsigned int nr_sec;
+  int rc = -1;
+
+    fs_mgr_get_crypt_info(fstab, key_loc, real_blkdev, sizeof(key_loc));
+
+    if (!strcmp(key_loc, KEY_IN_FOOTER)) {
+      if ( (fd = open(real_blkdev, O_RDWR)) < 0) {
+        printf("Cannot open real block device %s\n", real_blkdev);
+        return -1;
+      }
+
+      if ((nr_sec = get_blkdev_size(fd))) {
+        /* If it's an encrypted Android partition, the last 16 Kbytes contain the
+         * encryption info footer and key, and plenty of bytes to spare for future
+         * growth.
+         */
+        strlcpy(cached_metadata_fname, real_blkdev, sizeof(cached_metadata_fname));
+        cached_off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
+        cached_data = 1;
+      } else {
+        printf("Cannot get size of block device %s\n", real_blkdev);
+      }
+      close(fd);
+    } else {
+      strlcpy(cached_metadata_fname, key_loc, sizeof(cached_metadata_fname));
+      cached_off = 0;
+      cached_data = 1;
+    }
+
+  if (cached_data) {
+    if (metadata_fname) {
+        *metadata_fname = cached_metadata_fname;
+    }
+    if (off) {
+        *off = cached_off;
+    }
+    rc = 0;
+  }
+
+  return rc;
+}
+
+int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr)
+{
+  int fd;
+  unsigned int nr_sec, cnt;
+  off64_t starting_off;
+  int rc = -1;
+  char *fname = NULL;
+  struct stat statbuf;
+
+  if (get_crypt_ftr_info(&fname, &starting_off)) {
+    printf("Unable to get crypt_ftr_info\n");
+    return -1;
+  }
+  if (fname[0] != '/') {
+    printf("Unexpected value for crypto key location '%s'\n", fname);
+    //return -1;
+  }
+  if ( (fd = open(fname, O_RDWR)) < 0) {
+    printf("Cannot open footer file %s for get\n", fname);
+    return -1;
+  }
+
+  /* Make sure it's 16 Kbytes in length */
+  fstat(fd, &statbuf);
+  if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000)) {
+    printf("footer file %s is not the expected size!\n", fname);
+	close(fd);
+    return -1;
+  }
+
+  /* Seek to the start of the crypt footer */
+  if (lseek64(fd, starting_off, SEEK_SET) == -1) {
+    printf("Cannot seek to real block device footer\n");
+	close(fd);
+    return -1;
+  }
+
+  if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) {
+    printf("Cannot read real block device footer\n");
+	close(fd);
+    return -1;
+  }
+  close(fd);
+  return 0;
+}
+
+int main(void)
+{
+	char key_loc[PROPERTY_VALUE_MAX];
+	char blk_dev[PROPERTY_VALUE_MAX];
+	char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+	struct stat st;
+	struct crypt_mnt_ftr crypt_ftr;
+	int fdout;
+
+	printf("This tool comes with no warranties whatsoever.\n");
+	printf("http://teamw.in\n\n");
+	strcpy(fstab_filename, FSTAB_PREFIX);
+	property_get("ro.hardware", fstab_filename + sizeof(FSTAB_PREFIX) - 1, "");
+
+	if (stat(fstab_filename, &st) != 0) {
+		printf("Cannot locate fstab file '%s'\n", fstab_filename);
+		return -1;
+	}
+
+	fstab = fs_mgr_read_fstab(fstab_filename);
+	if (!fstab) {
+		printf("failed to open %s\n", fstab_filename);
+		return -1;
+	}
+
+	fs_mgr_get_crypt_info(fstab, key_loc, blk_dev, sizeof(blk_dev));
+
+	if (get_crypt_ftr_and_key(&crypt_ftr)) {
+		printf("Error getting crypt footer and key\n");
+		return -1;
+	}
+
+	if ( (fdout = open("/footerfile", O_WRONLY | O_CREAT, 0644)) < 0) {
+		printf("Cannot open output file /footerfile\n");
+		return -1;
+	}
+	if (write(fdout, (void*) &crypt_ftr, sizeof(struct crypt_mnt_ftr)) != sizeof(struct crypt_mnt_ftr)) {
+		printf("Failed to write footer.\n");
+	}
+	close(fdout);
+
+	if (!strcmp(key_loc, KEY_IN_FOOTER)) {
+		unsigned int nr_sec, cnt;
+		off64_t off = 0;
+		char buffer[CRYPT_FOOTER_OFFSET];
+		int fd;
+
+		printf("\n\nDumping footer from '%s'...\n", blk_dev);
+		if ( (fd = open(blk_dev, O_RDONLY)) < 0) {
+			printf("Cannot open real block device %s\n", blk_dev);
+			return -1;
+		}
+
+		if ((nr_sec = get_blkdev_size(fd))) {
+			off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
+		} else {
+			printf("Cannot get size of block device %s\n", blk_dev);
+			close(fd);
+			return -1;
+		}
+		printf("Size is %llu, offset is %llu\n", ((off64_t)nr_sec * 512), off);
+		if (lseek64(fd, off, SEEK_SET) == -1) {
+			printf("Cannot seek to real block device footer\n");
+			close(fd);
+			return -1;
+		}
+
+		if ( (cnt = read(fd, buffer, sizeof(buffer))) != sizeof(buffer)) {
+			printf("Cannot read real block device footer\n");
+			close(fd);
+			return -1;
+		}
+		close(fd);
+		if ( (fdout = open("/footerdump", O_WRONLY | O_CREAT, 0644)) < 0) {
+			printf("Cannot open output file /footerdump\n");
+			return -1;
+		}
+		if (write(fdout, buffer, sizeof(buffer)) != sizeof(buffer)) {
+			printf("Failed to write footer.\n");
+		}
+		close(fdout);
+	}
+
+	return 0;
+}
diff --git a/crypto/fs_mgr/Android.mk b/crypto/fs_mgr/Android.mk
new file mode 100644
index 0000000..8dd9d4c
--- /dev/null
+++ b/crypto/fs_mgr/Android.mk
@@ -0,0 +1,38 @@
+# Copyright 2011 The Android Open Source Project
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+LOCAL_MODULE:= libfs_mgrtwrp
+LOCAL_SHARED_LIBRARIES := libext4_utils
+LOCAL_STATIC_LIBRARIES := liblogwraptwrp libmincrypttwrp
+LOCAL_C_INCLUDES += \
+    system/extras/ext4_utils \
+    $(commands_recovery_local_path)/libmincrypt/includes
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= fs_mgr_main.c
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+LOCAL_MODULE:= fs_mgrtwrp
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+LOCAL_STATIC_LIBRARIES := libfs_mgrtwrp liblogwraptwrp libcutils liblog libc libmincrypttwrp libext4_utils_static
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/fs_mgr/fs_mgr.c b/crypto/fs_mgr/fs_mgr.c
new file mode 100644
index 0000000..3aa9e60
--- /dev/null
+++ b/crypto/fs_mgr/fs_mgr.c
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <libgen.h>
+#include <time.h>
+//#include <sys/swap.h>
+/* XXX These need to be obtained from kernel headers. See b/9336527 */
+#define SWAP_FLAG_PREFER        0x8000
+#define SWAP_FLAG_PRIO_MASK     0x7fff
+#define SWAP_FLAG_PRIO_SHIFT    0
+#define SWAP_FLAG_DISCARD       0x10000
+
+#include <linux/loop.h>
+#include <private/android_filesystem_config.h>
+#include <cutils/partition_utils.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+
+#include "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+#include "mincrypt/sha256.h"
+
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_verity.h"
+
+#define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
+#define KEY_IN_FOOTER  "footer"
+
+#define E2FSCK_BIN      "/system/bin/e2fsck"
+#define MKSWAP_BIN      "/system/bin/mkswap"
+
+#define FSCK_LOG_FILE   "/dev/fscklogs/log"
+
+#define ZRAM_CONF_DEV   "/sys/block/zram0/disksize"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+struct flag_list {
+    const char *name;
+    unsigned flag;
+};
+
+static struct flag_list mount_flags[] = {
+    { "noatime",    MS_NOATIME },
+    { "noexec",     MS_NOEXEC },
+    { "nosuid",     MS_NOSUID },
+    { "nodev",      MS_NODEV },
+    { "nodiratime", MS_NODIRATIME },
+    { "ro",         MS_RDONLY },
+    { "rw",         0 },
+    { "remount",    MS_REMOUNT },
+    { "bind",       MS_BIND },
+    { "rec",        MS_REC },
+    { "unbindable", MS_UNBINDABLE },
+    { "private",    MS_PRIVATE },
+    { "slave",      MS_SLAVE },
+    { "shared",     MS_SHARED },
+    { "defaults",   0 },
+    { 0,            0 },
+};
+
+static struct flag_list fs_mgr_flags[] = {
+    { "wait",        MF_WAIT },
+    { "check",       MF_CHECK },
+    { "encryptable=",MF_CRYPT },
+    { "nonremovable",MF_NONREMOVABLE },
+    { "voldmanaged=",MF_VOLDMANAGED},
+    { "length=",     MF_LENGTH },
+    { "recoveryonly",MF_RECOVERYONLY },
+    { "swapprio=",   MF_SWAPPRIO },
+    { "zramsize=",   MF_ZRAMSIZE },
+    { "verify",      MF_VERIFY },
+    { "noemulatedsd", MF_NOEMULATEDSD },
+    { "defaults",    0 },
+    { 0,             0 },
+};
+
+struct fs_mgr_flag_values {
+    char *key_loc;
+    long long part_length;
+    char *label;
+    int partnum;
+    int swap_prio;
+    unsigned int zram_size;
+};
+
+/*
+ * gettime() - returns the time in seconds of the system's monotonic clock or
+ * zero on error.
+ */
+static time_t gettime(void)
+{
+    struct timespec ts;
+    int ret;
+
+    ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+    if (ret < 0) {
+        ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
+        return 0;
+    }
+
+    return ts.tv_sec;
+}
+
+static int wait_for_file(const char *filename, int timeout)
+{
+    struct stat info;
+    time_t timeout_time = gettime() + timeout;
+    int ret = -1;
+
+    while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
+        usleep(10000);
+
+    return ret;
+}
+
+static int parse_flags(char *flags, struct flag_list *fl,
+                       struct fs_mgr_flag_values *flag_vals,
+                       char *fs_options, int fs_options_len)
+{
+    int f = 0;
+    int i;
+    char *p;
+    char *savep;
+
+    /* initialize flag values.  If we find a relevant flag, we'll
+     * update the value */
+    if (flag_vals) {
+        memset(flag_vals, 0, sizeof(*flag_vals));
+        flag_vals->partnum = -1;
+        flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
+    }
+
+    /* initialize fs_options to the null string */
+    if (fs_options && (fs_options_len > 0)) {
+        fs_options[0] = '\0';
+    }
+
+    p = strtok_r(flags, ",", &savep);
+    while (p) {
+        /* Look for the flag "p" in the flag list "fl"
+         * If not found, the loop exits with fl[i].name being null.
+         */
+        for (i = 0; fl[i].name; i++) {
+            if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
+                f |= fl[i].flag;
+                if ((fl[i].flag == MF_CRYPT) && flag_vals) {
+                    /* The encryptable flag is followed by an = and the
+                     * location of the keys.  Get it and return it.
+                     */
+                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+                } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
+                    /* The length flag is followed by an = and the
+                     * size of the partition.  Get it and return it.
+                     */
+                    flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
+                    /* The voldmanaged flag is followed by an = and the
+                     * label, a colon and the partition number or the
+                     * word "auto", e.g.
+                     *   voldmanaged=sdcard:3
+                     * Get and return them.
+                     */
+                    char *label_start;
+                    char *label_end;
+                    char *part_start;
+
+                    label_start = strchr(p, '=') + 1;
+                    label_end = strchr(p, ':');
+                    if (label_end) {
+                        flag_vals->label = strndup(label_start,
+                                                   (int) (label_end - label_start));
+                        part_start = strchr(p, ':') + 1;
+                        if (!strcmp(part_start, "auto")) {
+                            flag_vals->partnum = -1;
+                        } else {
+                            flag_vals->partnum = strtol(part_start, NULL, 0);
+                        }
+                    } else {
+                        ERROR("Warning: voldmanaged= flag malformed\n");
+                    }
+                } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
+                    flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
+                    flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0);
+                }
+                break;
+            }
+        }
+
+        if (!fl[i].name) {
+            if (fs_options) {
+                /* It's not a known flag, so it must be a filesystem specific
+                 * option.  Add it to fs_options if it was passed in.
+                 */
+                strlcat(fs_options, p, fs_options_len);
+                strlcat(fs_options, ",", fs_options_len);
+            } else {
+                /* fs_options was not passed in, so if the flag is unknown
+                 * it's an error.
+                 */
+                ERROR("Warning: unknown flag %s\n", p);
+            }
+        }
+        p = strtok_r(NULL, ",", &savep);
+    }
+
+out:
+    if (fs_options && fs_options[0]) {
+        /* remove the last trailing comma from the list of options */
+        fs_options[strlen(fs_options) - 1] = '\0';
+    }
+
+    return f;
+}
+
+/* Read a line of text till the next newline character.
+ * If no newline is found before the buffer is full, continue reading till a new line is seen,
+ * then return an empty buffer.  This effectively ignores lines that are too long.
+ * On EOF, return null.
+ */
+static char *fs_getline(char *buf, int size, FILE *file)
+{
+    int cnt = 0;
+    int eof = 0;
+    int eol = 0;
+    int c;
+
+    if (size < 1) {
+        return NULL;
+    }
+
+    while (cnt < (size - 1)) {
+        c = getc(file);
+        if (c == EOF) {
+            eof = 1;
+            break;
+        }
+
+        *(buf + cnt) = c;
+        cnt++;
+
+        if (c == '\n') {
+            eol = 1;
+            break;
+        }
+    }
+
+    /* Null terminate what we've read */
+    *(buf + cnt) = '\0';
+
+    if (eof) {
+        if (cnt) {
+            return buf;
+        } else {
+            return NULL;
+        }
+    } else if (eol) {
+        return buf;
+    } else {
+        /* The line is too long.  Read till a newline or EOF.
+         * If EOF, return null, if newline, return an empty buffer.
+         */
+        while(1) {
+            c = getc(file);
+            if (c == EOF) {
+                return NULL;
+            } else if (c == '\n') {
+                *buf = '\0';
+                return buf;
+            }
+        }
+    }
+}
+
+struct fstab *fs_mgr_read_fstab(const char *fstab_path)
+{
+    FILE *fstab_file;
+    int cnt, entries;
+    int len;
+    char line[256];
+    const char *delim = " \t";
+    char *save_ptr, *p;
+    struct fstab *fstab;
+    struct fstab_rec *recs;
+    struct fs_mgr_flag_values flag_vals;
+#define FS_OPTIONS_LEN 1024
+    char tmp_fs_options[FS_OPTIONS_LEN];
+
+    fstab_file = fopen(fstab_path, "r");
+    if (!fstab_file) {
+        ERROR("Cannot open file %s\n", fstab_path);
+        return 0;
+    }
+
+    entries = 0;
+    while (fs_getline(line, sizeof(line), fstab_file)) {
+        /* if the last character is a newline, shorten the string by 1 byte */
+        len = strlen(line);
+        if (line[len - 1] == '\n') {
+            line[len - 1] = '\0';
+        }
+        /* Skip any leading whitespace */
+        p = line;
+        while (isspace(*p)) {
+            p++;
+        }
+        /* ignore comments or empty lines */
+        if (*p == '#' || *p == '\0')
+            continue;
+        entries++;
+    }
+
+    if (!entries) {
+        ERROR("No entries found in fstab\n");
+        return 0;
+    }
+
+    /* Allocate and init the fstab structure */
+    fstab = calloc(1, sizeof(struct fstab));
+    fstab->num_entries = entries;
+    fstab->fstab_filename = strdup(fstab_path);
+    fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
+
+    fseek(fstab_file, 0, SEEK_SET);
+
+    cnt = 0;
+    while (fs_getline(line, sizeof(line), fstab_file)) {
+        /* if the last character is a newline, shorten the string by 1 byte */
+        len = strlen(line);
+        if (line[len - 1] == '\n') {
+            line[len - 1] = '\0';
+        }
+
+        /* Skip any leading whitespace */
+        p = line;
+        while (isspace(*p)) {
+            p++;
+        }
+        /* ignore comments or empty lines */
+        if (*p == '#' || *p == '\0')
+            continue;
+
+        /* If a non-comment entry is greater than the size we allocated, give an
+         * error and quit.  This can happen in the unlikely case the file changes
+         * between the two reads.
+         */
+        if (cnt >= entries) {
+            ERROR("Tried to process more entries than counted\n");
+            break;
+        }
+
+        if (!(p = strtok_r(line, delim, &save_ptr))) {
+            ERROR("Error parsing mount source\n");
+            return 0;
+        }
+        fstab->recs[cnt].blk_device = strdup(p);
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            ERROR("Error parsing mount_point\n");
+            return 0;
+        }
+        fstab->recs[cnt].mount_point = strdup(p);
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            ERROR("Error parsing fs_type\n");
+            return 0;
+        }
+        fstab->recs[cnt].fs_type = strdup(p);
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            ERROR("Error parsing mount_flags\n");
+            return 0;
+        }
+        tmp_fs_options[0] = '\0';
+        fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
+                                       tmp_fs_options, FS_OPTIONS_LEN);
+
+        /* fs_options are optional */
+        if (tmp_fs_options[0]) {
+            fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
+        } else {
+            fstab->recs[cnt].fs_options = NULL;
+        }
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            ERROR("Error parsing fs_mgr_options\n");
+            return 0;
+        }
+        fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
+                                                    &flag_vals, NULL, 0);
+        fstab->recs[cnt].key_loc = flag_vals.key_loc;
+        fstab->recs[cnt].length = flag_vals.part_length;
+        fstab->recs[cnt].label = flag_vals.label;
+        fstab->recs[cnt].partnum = flag_vals.partnum;
+        fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
+        fstab->recs[cnt].zram_size = flag_vals.zram_size;
+        cnt++;
+    }
+    fclose(fstab_file);
+
+    return fstab;
+}
+
+void fs_mgr_free_fstab(struct fstab *fstab)
+{
+    int i;
+
+    if (!fstab) {
+        return;
+    }
+
+    for (i = 0; i < fstab->num_entries; i++) {
+        /* Free the pointers return by strdup(3) */
+        free(fstab->recs[i].blk_device);
+        free(fstab->recs[i].mount_point);
+        free(fstab->recs[i].fs_type);
+        free(fstab->recs[i].fs_options);
+        free(fstab->recs[i].key_loc);
+        free(fstab->recs[i].label);
+        i++;
+    }
+
+    /* Free the fstab_recs array created by calloc(3) */
+    free(fstab->recs);
+
+    /* Free the fstab filename */
+    free(fstab->fstab_filename);
+
+    /* Free fstab */
+    free(fstab);
+}
+
+static void check_fs(char *blk_device, char *fs_type, char *target)
+{
+    int status;
+    int ret;
+    long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
+    char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro";
+    char *e2fsck_argv[] = {
+        E2FSCK_BIN,
+        "-y",
+        blk_device
+    };
+
+    /* Check for the types of filesystems we know how to check */
+    if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
+        /*
+         * First try to mount and unmount the filesystem.  We do this because
+         * the kernel is more efficient than e2fsck in running the journal and
+         * processing orphaned inodes, and on at least one device with a
+         * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes
+         * to do what the kernel does in about a second.
+         *
+         * After mounting and unmounting the filesystem, run e2fsck, and if an
+         * error is recorded in the filesystem superblock, e2fsck will do a full
+         * check.  Otherwise, it does nothing.  If the kernel cannot mount the
+         * filesytsem due to an error, e2fsck is still run to do a full check
+         * fix the filesystem.
+         */
+        ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
+        if (!ret) {
+            umount(target);
+        }
+
+        INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
+
+        ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
+                                      &status, true, LOG_KLOG | LOG_FILE,
+                                      true, FSCK_LOG_FILE);
+
+        if (ret < 0) {
+            /* No need to check for error in fork, we can't really handle it now */
+            ERROR("Failed trying to run %s\n", E2FSCK_BIN);
+        }
+    }
+
+    return;
+}
+
+static void remove_trailing_slashes(char *n)
+{
+    int len;
+
+    len = strlen(n) - 1;
+    while ((*(n + len) == '/') && len) {
+      *(n + len) = '\0';
+      len--;
+    }
+}
+
+/*
+ * Mark the given block device as read-only, using the BLKROSET ioctl.
+ * Return 0 on success, and -1 on error.
+ */
+static void fs_set_blk_ro(const char *blockdev)
+{
+    int fd;
+    int ON = 1;
+
+    fd = open(blockdev, O_RDONLY);
+    if (fd < 0) {
+        // should never happen
+        return;
+    }
+
+    ioctl(fd, BLKROSET, &ON);
+    close(fd);
+}
+
+/*
+ * __mount(): wrapper around the mount() system call which also
+ * sets the underlying block device to read-only if the mount is read-only.
+ * See "man 2 mount" for return values.
+ */
+static int __mount(const char *source, const char *target,
+                   const char *filesystemtype, unsigned long mountflags,
+                   const void *data)
+{
+    int ret = mount(source, target, filesystemtype, mountflags, data);
+
+    if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
+        fs_set_blk_ro(source);
+    }
+
+    return ret;
+}
+
+static int fs_match(char *in1, char *in2)
+{
+    char *n1;
+    char *n2;
+    int ret;
+
+    n1 = strdup(in1);
+    n2 = strdup(in2);
+
+    remove_trailing_slashes(n1);
+    remove_trailing_slashes(n2);
+
+    ret = !strcmp(n1, n2);
+
+    free(n1);
+    free(n2);
+
+    return ret;
+}
+
+int fs_mgr_mount_all(struct fstab *fstab)
+{
+    int i = 0;
+    int encrypted = 0;
+    int ret = -1;
+    int mret;
+
+    if (!fstab) {
+        return ret;
+    }
+
+    for (i = 0; i < fstab->num_entries; i++) {
+        /* Don't mount entries that are managed by vold */
+        if (fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) {
+            continue;
+        }
+
+        /* Skip swap and raw partition entries such as boot, recovery, etc */
+        if (!strcmp(fstab->recs[i].fs_type, "swap") ||
+            !strcmp(fstab->recs[i].fs_type, "emmc") ||
+            !strcmp(fstab->recs[i].fs_type, "mtd")) {
+            continue;
+        }
+
+        if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
+            wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+        }
+
+        if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+            check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                     fstab->recs[i].mount_point);
+        }
+
+        if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) {
+            if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) {
+                ERROR("Could not set up verified partition, skipping!");
+                continue;
+            }
+        }
+
+        mret = __mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
+                     fstab->recs[i].fs_type, fstab->recs[i].flags,
+                     fstab->recs[i].fs_options);
+
+        if (!mret) {
+            /* Success!  Go get the next one */
+            continue;
+        }
+
+        /* mount(2) returned an error, check if it's encrypted and deal with it */
+        if ((fstab->recs[i].fs_mgr_flags & MF_CRYPT) &&
+            !partition_wiped(fstab->recs[i].blk_device)) {
+            /* Need to mount a tmpfs at this mountpoint for now, and set
+             * properties that vold will query later for decrypting
+             */
+            if (mount("tmpfs", fstab->recs[i].mount_point, "tmpfs",
+                  MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS) < 0) {
+                ERROR("Cannot mount tmpfs filesystem for encrypted fs at %s\n",
+                        fstab->recs[i].mount_point);
+                goto out;
+            }
+            encrypted = 1;
+        } else {
+            ERROR("Cannot mount filesystem on %s at %s\n",
+                    fstab->recs[i].blk_device, fstab->recs[i].mount_point);
+            goto out;
+        }
+    }
+
+    if (encrypted) {
+        ret = 1;
+    } else {
+        ret = 0;
+    }
+
+out:
+    return ret;
+}
+
+/* If tmp_mount_point is non-null, mount the filesystem there.  This is for the
+ * tmp mount we do to check the user password
+ */
+int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
+                    char *tmp_mount_point)
+{
+    int i = 0;
+    int ret = -1;
+    char *m;
+
+    if (!fstab) {
+        return ret;
+    }
+
+    for (i = 0; i < fstab->num_entries; i++) {
+        if (!fs_match(fstab->recs[i].mount_point, n_name)) {
+            continue;
+        }
+
+        /* We found our match */
+        /* If this swap or a raw partition, report an error */
+        if (!strcmp(fstab->recs[i].fs_type, "swap") ||
+            !strcmp(fstab->recs[i].fs_type, "emmc") ||
+            !strcmp(fstab->recs[i].fs_type, "mtd")) {
+            ERROR("Cannot mount filesystem of type %s on %s\n",
+                  fstab->recs[i].fs_type, n_blk_device);
+            goto out;
+        }
+
+        /* First check the filesystem if requested */
+        if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
+            wait_for_file(n_blk_device, WAIT_TIMEOUT);
+        }
+
+        if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+            check_fs(n_blk_device, fstab->recs[i].fs_type,
+                     fstab->recs[i].mount_point);
+        }
+
+        if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) {
+            if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) {
+                ERROR("Could not set up verified partition, skipping!");
+                continue;
+            }
+        }
+
+        /* Now mount it where requested */
+        if (tmp_mount_point) {
+            m = tmp_mount_point;
+        } else {
+            m = fstab->recs[i].mount_point;
+        }
+        if (__mount(n_blk_device, m, fstab->recs[i].fs_type,
+                    fstab->recs[i].flags, fstab->recs[i].fs_options)) {
+            ERROR("Cannot mount filesystem on %s at %s\n",
+                    n_blk_device, m);
+            goto out;
+        } else {
+            ret = 0;
+            goto out;
+        }
+    }
+
+    /* We didn't find a match, say so and return an error */
+    ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point);
+
+out:
+    return ret;
+}
+
+/*
+ * mount a tmpfs filesystem at the given point.
+ * return 0 on success, non-zero on failure.
+ */
+int fs_mgr_do_tmpfs_mount(char *n_name)
+{
+    int ret;
+
+    ret = mount("tmpfs", n_name, "tmpfs",
+                MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS);
+    if (ret < 0) {
+        ERROR("Cannot mount tmpfs filesystem at %s\n", n_name);
+        return -1;
+    }
+
+    /* Success */
+    return 0;
+}
+
+int fs_mgr_unmount_all(struct fstab *fstab)
+{
+    int i = 0;
+    int ret = 0;
+
+    if (!fstab) {
+        return -1;
+    }
+
+    while (fstab->recs[i].blk_device) {
+        if (umount(fstab->recs[i].mount_point)) {
+            ERROR("Cannot unmount filesystem at %s\n", fstab->recs[i].mount_point);
+            ret = -1;
+        }
+        i++;
+    }
+
+    return ret;
+}
+
+/* This must be called after mount_all, because the mkswap command needs to be
+ * available.
+ */
+int fs_mgr_swapon_all(struct fstab *fstab)
+{
+    int i = 0;
+    int flags = 0;
+    int err = 0;
+    int ret = 0;
+    int status;
+    char *mkswap_argv[2] = {
+        MKSWAP_BIN,
+        NULL
+    };
+
+    if (!fstab) {
+        return -1;
+    }
+
+    for (i = 0; i < fstab->num_entries; i++) {
+        /* Skip non-swap entries */
+        if (strcmp(fstab->recs[i].fs_type, "swap")) {
+            continue;
+        }
+
+        if (fstab->recs[i].zram_size > 0) {
+            /* A zram_size was specified, so we need to configure the
+             * device.  There is no point in having multiple zram devices
+             * on a system (all the memory comes from the same pool) so
+             * we can assume the device number is 0.
+             */
+            FILE *zram_fp;
+
+            zram_fp = fopen(ZRAM_CONF_DEV, "r+");
+            if (zram_fp == NULL) {
+                ERROR("Unable to open zram conf device " ZRAM_CONF_DEV);
+                ret = -1;
+                continue;
+            }
+            fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size);
+            fclose(zram_fp);
+        }
+
+        if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
+            wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+        }
+
+        /* Initialize the swap area */
+        mkswap_argv[1] = fstab->recs[i].blk_device;
+        err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv,
+                                      &status, true, LOG_KLOG, false, NULL);
+        if (err) {
+            ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device);
+            ret = -1;
+            continue;
+        }
+
+        /* If -1, then no priority was specified in fstab, so don't set
+         * SWAP_FLAG_PREFER or encode the priority */
+        if (fstab->recs[i].swap_prio >= 0) {
+            flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) &
+                    SWAP_FLAG_PRIO_MASK;
+            flags |= SWAP_FLAG_PREFER;
+        } else {
+            flags = 0;
+        }
+        // requires sys/swap.h which is not available in older trees
+        // this entire function does not appear to be used for decrypt
+        err = -1; //swapon(fstab->recs[i].blk_device, flags);
+        if (err) {
+            ERROR("swapon failed for %s\n", fstab->recs[i].blk_device);
+            ret = -1;
+        }
+    }
+
+    return ret;
+}
+
+/*
+ * key_loc must be at least PROPERTY_VALUE_MAX bytes long
+ *
+ * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
+ */
+int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size)
+{
+    int i = 0;
+
+    if (!fstab) {
+        return -1;
+    }
+    /* Initialize return values to null strings */
+    if (key_loc) {
+        *key_loc = '\0';
+    }
+    if (real_blk_device) {
+        *real_blk_device = '\0';
+    }
+
+    /* Look for the encryptable partition to find the data */
+    for (i = 0; i < fstab->num_entries; i++) {
+        /* Don't deal with vold managed enryptable partitions here */
+        if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
+            continue;
+        }
+        if (!(fstab->recs[i].fs_mgr_flags & MF_CRYPT)) {
+            continue;
+        }
+
+        /* We found a match */
+        if (key_loc) {
+            strlcpy(key_loc, fstab->recs[i].key_loc, size);
+        }
+        if (real_blk_device) {
+            strlcpy(real_blk_device, fstab->recs[i].blk_device, size);
+        }
+        break;
+    }
+
+    return 0;
+}
+
+/* Add an entry to the fstab, and return 0 on success or -1 on error */
+int fs_mgr_add_entry(struct fstab *fstab,
+                     const char *mount_point, const char *fs_type,
+                     const char *blk_device, long long length)
+{
+    struct fstab_rec *new_fstab_recs;
+    int n = fstab->num_entries;
+
+    new_fstab_recs = (struct fstab_rec *)
+                     realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
+
+    if (!new_fstab_recs) {
+        return -1;
+    }
+
+    /* A new entry was added, so initialize it */
+     memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
+     new_fstab_recs[n].mount_point = strdup(mount_point);
+     new_fstab_recs[n].fs_type = strdup(fs_type);
+     new_fstab_recs[n].blk_device = strdup(blk_device);
+     new_fstab_recs[n].length = 0;
+
+     /* Update the fstab struct */
+     fstab->recs = new_fstab_recs;
+     fstab->num_entries++;
+
+     return 0;
+}
+
+struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
+{
+    int i;
+
+    if (!fstab) {
+        return NULL;
+    }
+
+    for (i = 0; i < fstab->num_entries; i++) {
+        int len = strlen(fstab->recs[i].mount_point);
+        if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
+            (path[len] == '\0' || path[len] == '/')) {
+            return &fstab->recs[i];
+        }
+    }
+
+    return NULL;
+}
+
+int fs_mgr_is_voldmanaged(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_VOLDMANAGED;
+}
+
+int fs_mgr_is_nonremovable(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_NONREMOVABLE;
+}
+
+int fs_mgr_is_encryptable(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_CRYPT;
+}
+
+int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
+}
diff --git a/crypto/fs_mgr/fs_mgr_main.c b/crypto/fs_mgr/fs_mgr_main.c
new file mode 100644
index 0000000..4bde4a1
--- /dev/null
+++ b/crypto/fs_mgr/fs_mgr_main.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include "fs_mgr_priv.h"
+
+char *me = "";
+
+static void usage(void)
+{
+    ERROR("%s: usage: %s <-a | -n mnt_point blk_dev | -u> <fstab_file>\n", me, me);
+    exit(1);
+}
+
+/* Parse the command line.  If an error is encountered, print an error message
+ * and exit the program, do not return to the caller.
+ * Return the number of argv[] entries consumed.
+ */
+static void parse_options(int argc, char *argv[], int *a_flag, int *u_flag, int *n_flag,
+                     char **n_name, char **n_blk_dev)
+{
+    me = basename(strdup(argv[0]));
+
+    if (argc <= 1) {
+        usage();
+    }
+
+    if (!strcmp(argv[1], "-a")) {
+        if (argc != 3) {
+            usage();
+        }
+        *a_flag = 1;
+    }
+    if (!strcmp(argv[1], "-n")) {
+        if (argc != 5) {
+            usage();
+        }
+        *n_flag = 1;
+        *n_name = argv[2];
+        *n_blk_dev = argv[3];
+    }
+    if (!strcmp(argv[1], "-u")) {
+        if (argc != 3) {
+            usage();
+        }
+        *u_flag = 1;
+    }
+
+    /* If no flag is specified, it's an error */
+    if (!(*a_flag | *n_flag | *u_flag)) {
+        usage();
+    }
+
+    /* If more than one flag is specified, it's an error */
+    if ((*a_flag + *n_flag + *u_flag) > 1) {
+        usage();
+    }
+
+    return;
+}
+
+int main(int argc, char *argv[])
+{
+    int a_flag=0;
+    int u_flag=0;
+    int n_flag=0;
+    char *n_name;
+    char *n_blk_dev;
+    char *fstab_file;
+    struct fstab *fstab;
+
+    klog_init();
+    klog_set_level(6);
+
+    parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev);
+
+    /* The name of the fstab file is last, after the option */
+    fstab_file = argv[argc - 1];
+
+    fstab = fs_mgr_read_fstab(fstab_file);
+
+    if (a_flag) {
+        return fs_mgr_mount_all(fstab);
+    } else if (n_flag) {
+        return fs_mgr_do_mount(fstab, n_name, n_blk_dev, 0);
+    } else if (u_flag) {
+        return fs_mgr_unmount_all(fstab);
+    } else {
+        ERROR("%s: Internal error, unknown option\n", me);
+        exit(1);
+    }
+
+    fs_mgr_free_fstab(fstab);
+
+    /* Should not get here */
+    exit(1);
+}
+
diff --git a/crypto/fs_mgr/fs_mgr_priv.h b/crypto/fs_mgr/fs_mgr_priv.h
new file mode 100644
index 0000000..59ffd78
--- /dev/null
+++ b/crypto/fs_mgr/fs_mgr_priv.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_MGR_PRIV_H
+#define __CORE_FS_MGR_PRIV_H
+
+#include <cutils/klog.h>
+#include <fs_mgr.h>
+
+#define INFO(x...)    KLOG_INFO("fs_mgr", x)
+#define ERROR(x...)   KLOG_ERROR("fs_mgr", x)
+
+#define CRYPTO_TMPFS_OPTIONS "size=128m,mode=0771,uid=1000,gid=1000"
+
+#define WAIT_TIMEOUT 20
+
+/* fstab has the following format:
+ *
+ * Any line starting with a # is a comment and ignored
+ *
+ * Any blank line is ignored
+ *
+ * All other lines must be in this format:
+ *   <source>  <mount_point> <fs_type> <mount_flags> <fs_options> <fs_mgr_options>
+ *
+ *   <mount_flags> is a comma separated list of flags that can be passed to the
+ *                 mount command.  The list includes noatime, nosuid, nodev, nodiratime,
+ *                 ro, rw, remount, defaults.
+ *
+ *   <fs_options> is a comma separated list of options accepted by the filesystem being
+ *                mounted.  It is passed directly to mount without being parsed
+ *
+ *   <fs_mgr_options> is a comma separated list of flags that control the operation of
+ *                     the fs_mgr program.  The list includes "wait", which will wait till
+ *                     the <source> file exists, and "check", which requests that the fs_mgr 
+ *                     run an fscheck program on the <source> before mounting the filesystem.
+ *                     If check is specifed on a read-only filesystem, it is ignored.
+ *                     Also, "encryptable" means that filesystem can be encrypted.
+ *                     The "encryptable" flag _MUST_ be followed by a = and a string which
+ *                     is the location of the encryption keys.  It can either be a path
+ *                     to a file or partition which contains the keys, or the word "footer"
+ *                     which means the keys are in the last 16 Kbytes of the partition
+ *                     containing the filesystem.
+ *
+ * When the fs_mgr is requested to mount all filesystems, it will first mount all the
+ * filesystems that do _NOT_ specify check (including filesystems that are read-only and
+ * specify check, because check is ignored in that case) and then it will check and mount
+ * filesystem marked with check.
+ *
+ */
+
+#define MF_WAIT         0x1
+#define MF_CHECK        0x2
+#define MF_CRYPT        0x4
+#define MF_NONREMOVABLE 0x8
+#define MF_VOLDMANAGED  0x10
+#define MF_LENGTH       0x20
+#define MF_RECOVERYONLY 0x40
+#define MF_SWAPPRIO     0x80
+#define MF_ZRAMSIZE     0x100
+#define MF_VERIFY       0x200
+/*
+ * There is no emulated sdcard daemon running on /data/media on this device,
+ * so treat the physical SD card as the only external storage device,
+ * a la the Nexus One.
+ */
+#define MF_NOEMULATEDSD 0x400
+
+#define DM_BUF_SIZE 4096
+
+#endif /* __CORE_FS_MGR_PRIV_H */
+
diff --git a/crypto/fs_mgr/fs_mgr_priv_verity.h b/crypto/fs_mgr/fs_mgr_priv_verity.h
new file mode 100644
index 0000000..6193784
--- /dev/null
+++ b/crypto/fs_mgr/fs_mgr_priv_verity.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+int fs_mgr_setup_verity(struct fstab_rec *fstab);
\ No newline at end of file
diff --git a/crypto/fs_mgr/fs_mgr_verity.c b/crypto/fs_mgr/fs_mgr_verity.c
new file mode 100644
index 0000000..969eab2
--- /dev/null
+++ b/crypto/fs_mgr/fs_mgr_verity.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <libgen.h>
+#include <time.h>
+
+#include <private/android_filesystem_config.h>
+#include <logwrap/logwrap.h>
+
+#include "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+#include "mincrypt/sha256.h"
+
+#include "ext4_utils.h"
+#include "ext4.h"
+
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_verity.h"
+
+#define VERITY_METADATA_SIZE 32768
+#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
+#define VERITY_TABLE_RSA_KEY "/verity_key"
+
+extern struct fs_info info;
+
+static RSAPublicKey *load_key(char *path)
+{
+    FILE *f;
+    RSAPublicKey *key;
+
+    key = malloc(sizeof(RSAPublicKey));
+    if (!key) {
+        ERROR("Can't malloc key\n");
+        return NULL;
+    }
+
+    f = fopen(path, "r");
+    if (!f) {
+        ERROR("Can't open '%s'\n", path);
+        free(key);
+        return NULL;
+    }
+
+    if (!fread(key, sizeof(*key), 1, f)) {
+        ERROR("Could not read key!");
+        fclose(f);
+        free(key);
+        return NULL;
+    }
+
+    if (key->len != RSANUMWORDS) {
+        ERROR("Invalid key length %d\n", key->len);
+        fclose(f);
+        free(key);
+        return NULL;
+    }
+
+    fclose(f);
+    return key;
+}
+
+static int verify_table(char *signature, char *table, int table_length)
+{
+    int fd;
+    RSAPublicKey *key;
+    uint8_t hash_buf[SHA_DIGEST_SIZE];
+    int retval = -1;
+
+    // Hash the table
+    SHA_hash((uint8_t*)table, table_length, hash_buf);
+
+    // Now get the public key from the keyfile
+    key = load_key(VERITY_TABLE_RSA_KEY);
+    if (!key) {
+        ERROR("Couldn't load verity keys");
+        goto out;
+    }
+
+    // verify the result
+    if (!RSA_verify(key,
+                    (uint8_t*) signature,
+                    RSANUMBYTES,
+                    (uint8_t*) hash_buf,
+                    SHA_DIGEST_SIZE)) {
+        ERROR("Couldn't verify table.");
+        goto out;
+    }
+
+    retval = 0;
+
+out:
+    free(key);
+    return retval;
+}
+
+static int get_target_device_size(char *blk_device, uint64_t *device_size)
+{
+    int data_device;
+    struct ext4_super_block sb;
+
+    data_device = open(blk_device, O_RDONLY);
+    if (data_device < 0) {
+        ERROR("Error opening block device (%s)", strerror(errno));
+        return -1;
+    }
+
+    if (lseek64(data_device, 1024, SEEK_SET) < 0) {
+        ERROR("Error seeking to superblock");
+        close(data_device);
+        return -1;
+    }
+
+    if (read(data_device, &sb, sizeof(sb)) != sizeof(sb)) {
+        ERROR("Error reading superblock");
+        close(data_device);
+        return -1;
+    }
+
+    ext4_parse_sb(&sb);
+    *device_size = info.len;
+
+    close(data_device);
+    return 0;
+}
+
+static int read_verity_metadata(char *block_device, char **signature, char **table)
+{
+    unsigned magic_number;
+    unsigned table_length;
+    uint64_t device_length;
+    int protocol_version;
+    FILE *device;
+    int retval = -1;
+
+    device = fopen(block_device, "r");
+    if (!device) {
+        ERROR("Could not open block device %s (%s).\n", block_device, strerror(errno));
+        goto out;
+    }
+
+    // find the start of the verity metadata
+    if (get_target_device_size(block_device, &device_length) < 0) {
+        ERROR("Could not get target device size.\n");
+        goto out;
+    }
+    if (fseek(device, device_length, SEEK_SET) < 0) {
+        ERROR("Could not seek to start of verity metadata block.\n");
+        goto out;
+    }
+
+    // check the magic number
+    if (!fread(&magic_number, sizeof(int), 1, device)) {
+        ERROR("Couldn't read magic number!\n");
+        goto out;
+    }
+    if (magic_number != VERITY_METADATA_MAGIC_NUMBER) {
+        ERROR("Couldn't find verity metadata at offset %llu!\n", device_length);
+        goto out;
+    }
+
+    // check the protocol version
+    if (!fread(&protocol_version, sizeof(int), 1, device)) {
+        ERROR("Couldn't read verity metadata protocol version!\n");
+        goto out;
+    }
+    if (protocol_version != 0) {
+        ERROR("Got unknown verity metadata protocol version %d!\n", protocol_version);
+        goto out;
+    }
+
+    // get the signature
+    *signature = (char*) malloc(RSANUMBYTES * sizeof(char));
+    if (!*signature) {
+        ERROR("Couldn't allocate memory for signature!\n");
+        goto out;
+    }
+    if (!fread(*signature, RSANUMBYTES, 1, device)) {
+        ERROR("Couldn't read signature from verity metadata!\n");
+        free(*signature);
+        goto out;
+    }
+
+    // get the size of the table
+    if (!fread(&table_length, sizeof(int), 1, device)) {
+        ERROR("Couldn't get the size of the verity table from metadata!\n");
+        free(*signature);
+        goto out;
+    }
+
+    // get the table + null terminator
+    table_length += 1;
+    *table = malloc(table_length);
+    if(!*table) {
+        ERROR("Couldn't allocate memory for verity table!\n");
+        goto out;
+    }
+    if (!fgets(*table, table_length, device)) {
+        ERROR("Couldn't read the verity table from metadata!\n");
+        free(*table);
+        free(*signature);
+        goto out;
+    }
+
+    retval = 0;
+
+out:
+    if (device)
+        fclose(device);
+    return retval;
+}
+
+static void verity_ioctl_init(struct dm_ioctl *io, char *name, unsigned flags)
+{
+    memset(io, 0, DM_BUF_SIZE);
+    io->data_size = DM_BUF_SIZE;
+    io->data_start = sizeof(struct dm_ioctl);
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+    io->flags = flags | DM_READONLY_FLAG;
+    if (name) {
+        strlcpy(io->name, name, sizeof(io->name));
+    }
+}
+
+static int create_verity_device(struct dm_ioctl *io, char *name, int fd)
+{
+    verity_ioctl_init(io, name, 1);
+    if (ioctl(fd, DM_DEV_CREATE, io)) {
+        ERROR("Error creating device mapping (%s)", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+static int get_verity_device_name(struct dm_ioctl *io, char *name, int fd, char **dev_name)
+{
+    verity_ioctl_init(io, name, 0);
+    if (ioctl(fd, DM_DEV_STATUS, io)) {
+        ERROR("Error fetching verity device number (%s)", strerror(errno));
+        return -1;
+    }
+    int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+    if (asprintf(dev_name, "/dev/block/dm-%u", dev_num) < 0) {
+        ERROR("Error getting verity block device name (%s)", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+static int load_verity_table(struct dm_ioctl *io, char *name, char *blockdev, int fd, char *table)
+{
+    char *verity_params;
+    char *buffer = (char*) io;
+    uint64_t device_size = 0;
+
+    if (get_target_device_size(blockdev, &device_size) < 0) {
+        return -1;
+    }
+
+    verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG);
+
+    struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+    // set tgt arguments here
+    io->target_count = 1;
+    tgt->status=0;
+    tgt->sector_start=0;
+    tgt->length=device_size/512;
+    strcpy(tgt->target_type, "verity");
+
+    // build the verity params here
+    verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+    if (sprintf(verity_params, "%s", table) < 0) {
+        return -1;
+    }
+
+    // set next target boundary
+    verity_params += strlen(verity_params) + 1;
+    verity_params = (char*) (((unsigned long)verity_params + 7) & ~8);
+    tgt->next = verity_params - buffer;
+
+    // send the ioctl to load the verity table
+    if (ioctl(fd, DM_TABLE_LOAD, io)) {
+        ERROR("Error loading verity table (%s)", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int resume_verity_table(struct dm_ioctl *io, char *name, int fd)
+{
+    verity_ioctl_init(io, name, 0);
+    if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+        ERROR("Error activating verity device (%s)", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+static int test_access(char *device) {
+    int tries = 25;
+    while (tries--) {
+        if (!access(device, F_OK) || errno != ENOENT) {
+            return 0;
+        }
+        usleep(40 * 1000);
+    }
+    return -1;
+}
+
+int fs_mgr_setup_verity(struct fstab_rec *fstab) {
+
+    int retval = -1;
+
+    char *verity_blk_name;
+    char *verity_table;
+    char *verity_table_signature;
+
+    char buffer[DM_BUF_SIZE];
+    struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+    char *mount_point = basename(fstab->mount_point);
+
+    // set the dm_ioctl flags
+    io->flags |= 1;
+    io->target_count = 1;
+
+    // get the device mapper fd
+    int fd;
+    if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        ERROR("Error opening device mapper (%s)", strerror(errno));
+        return retval;
+    }
+
+    // create the device
+    if (create_verity_device(io, mount_point, fd) < 0) {
+        ERROR("Couldn't create verity device!");
+        goto out;
+    }
+
+    // get the name of the device file
+    if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) {
+        ERROR("Couldn't get verity device number!");
+        goto out;
+    }
+
+    // read the verity block at the end of the block device
+    if (read_verity_metadata(fstab->blk_device,
+                                    &verity_table_signature,
+                                    &verity_table) < 0) {
+        goto out;
+    }
+
+    // verify the signature on the table
+    if (verify_table(verity_table_signature,
+                            verity_table,
+                            strlen(verity_table)) < 0) {
+        goto out;
+    }
+
+    // load the verity mapping table
+    if (load_verity_table(io, mount_point, fstab->blk_device, fd, verity_table) < 0) {
+        goto out;
+    }
+
+    // activate the device
+    if (resume_verity_table(io, mount_point, fd) < 0) {
+        goto out;
+    }
+
+    // assign the new verity block device as the block device
+    free(fstab->blk_device);
+    fstab->blk_device = verity_blk_name;
+
+    // make sure we've set everything up properly
+    if (test_access(fstab->blk_device) < 0) {
+        goto out;
+    }
+
+    retval = 0;
+
+out:
+    close(fd);
+    return retval;
+}
diff --git a/crypto/fs_mgr/include/fs_mgr.h b/crypto/fs_mgr/include/fs_mgr.h
new file mode 100644
index 0000000..0f90c32
--- /dev/null
+++ b/crypto/fs_mgr/include/fs_mgr.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CORE_FS_MGR_H
+#define __CORE_FS_MGR_H
+
+#include <stdint.h>
+#include <linux/dm-ioctl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct fstab {
+    int num_entries;
+    struct fstab_rec *recs;
+    char *fstab_filename;
+};
+
+struct fstab_rec {
+    char *blk_device;
+    char *mount_point;
+    char *fs_type;
+    unsigned long flags;
+    char *fs_options;
+    int fs_mgr_flags;
+    char *key_loc;
+    char *verity_loc;
+    long long length;
+    char *label;
+    int partnum;
+    int swap_prio;
+    unsigned int zram_size;
+};
+
+struct fstab *fs_mgr_read_fstab(const char *fstab_path);
+void fs_mgr_free_fstab(struct fstab *fstab);
+int fs_mgr_mount_all(struct fstab *fstab);
+int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
+                    char *tmp_mount_point);
+int fs_mgr_do_tmpfs_mount(char *n_name);
+int fs_mgr_unmount_all(struct fstab *fstab);
+int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc,
+                          char *real_blk_device, int size);
+int fs_mgr_add_entry(struct fstab *fstab,
+                     const char *mount_point, const char *fs_type,
+                     const char *blk_device, long long length);
+struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path);
+int fs_mgr_is_voldmanaged(struct fstab_rec *fstab);
+int fs_mgr_is_nonremovable(struct fstab_rec *fstab);
+int fs_mgr_is_encryptable(struct fstab_rec *fstab);
+int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab);
+int fs_mgr_swapon_all(struct fstab *fstab);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CORE_FS_MGR_H */
+
diff --git a/crypto/ics/Android.mk b/crypto/ics/Android.mk
new file mode 100644
index 0000000..5616c19
--- /dev/null
+++ b/crypto/ics/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcryptfsics
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_CFLAGS :=
+LOCAL_CFLAGS += -DCRYPTO_FS_TYPE=\"$(TW_CRYPTO_FS_TYPE)\"
+ifeq ($(TW_INCLUDE_CRYPTO_SAMSUNG), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO_SAMSUNG=\"$(TW_INCLUDE_CRYPTO_SAMSUNG)\"
+    LOCAL_LDFLAGS += -ldl
+    LOCAL_STATIC_LIBRARIES += libcrypt_samsung
+endif
+ifneq ($(TW_INTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_PATH=$(TW_INTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_PATH),)
+	LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_PATH=$(TW_EXTERNAL_STORAGE_PATH)
+endif
+LOCAL_SRC_FILES = cryptfs.c
+LOCAL_C_INCLUDES += system/extras/ext4_utils external/openssl/include
+LOCAL_SHARED_LIBRARIES += libc liblog libcutils libcrypto
+
+include $(BUILD_SHARED_LIBRARY)
+endif
diff --git a/crypto/ics/cryptfs.c b/crypto/ics/cryptfs.c
new file mode 100644
index 0000000..4f3d5d0
--- /dev/null
+++ b/crypto/ics/cryptfs.c
@@ -0,0 +1,729 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* TO DO:
+ *   1.  Perhaps keep several copies of the encrypted key, in case something
+ *       goes horribly wrong?
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/dm-ioctl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <errno.h>
+#include <cutils/android_reboot.h>
+#include <ext4.h>
+#include <linux/kdev_t.h>
+#include "cryptfs.h"
+#define LOG_TAG "Cryptfs"
+#include "cutils/log.h"
+#include "cutils/properties.h"
+#include "hardware_legacy/power.h"
+//#include "VolumeManager.h"
+
+#define DM_CRYPT_BUF_SIZE 4096
+#define DATA_MNT_POINT "/data"
+
+#define HASH_COUNT 2000
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+#define KEY_LEN_BYTES_SAMSUNG (sizeof(edk_t))
+#endif
+#define KEY_LEN_BYTES 16
+#define IV_LEN_BYTES 16
+
+#define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
+#define KEY_IN_FOOTER  "footer"
+
+#define EXT4_FS 1
+#define FAT_FS 2
+
+#ifndef EXPAND
+#define STRINGIFY(x) #x
+#define EXPAND(x) STRINGIFY(x)
+#endif
+
+char *me = "cryptfs";
+
+static char *saved_data_blkdev;
+static char *saved_mount_point;
+static int  master_key_saved = 0;
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+static int  using_samsung_encryption = 0;
+//static edk_t saved_master_key;
+static unsigned char saved_master_key[KEY_LEN_BYTES_SAMSUNG];
+edk_payload_t edk_payload;
+#else
+static unsigned char saved_master_key[KEY_LEN_BYTES];
+#endif
+
+int cryptfs_setup_volume(const char *label, const char *real_blkdev, char *crypto_blkdev);
+
+
+static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags)
+{
+    memset(io, 0, dataSize);
+    io->data_size = dataSize;
+    io->data_start = sizeof(struct dm_ioctl);
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+    io->flags = flags;
+    if (name) {
+        strncpy(io->name, name, sizeof(io->name));
+    }
+}
+
+static unsigned int get_blkdev_size(int fd)
+{
+  unsigned int nr_sec;
+
+  if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+    nr_sec = 0;
+  }
+
+  return nr_sec;
+}
+
+/* key or salt can be NULL, in which case just skip writing that value.  Useful to
+ * update the failed mount count but not change the key.
+ */
+static int put_crypt_ftr_and_key(char *real_blk_name, struct crypt_mnt_ftr *crypt_ftr,
+                                  unsigned char *key, unsigned char *salt)
+{
+  // we don't need to update it...
+  return 0;
+}
+
+static int get_crypt_ftr_and_key(char *real_blk_name, struct crypt_mnt_ftr *crypt_ftr,
+                                  unsigned char *key, unsigned char *salt)
+{
+  int fd;
+  unsigned int nr_sec, cnt;
+  off64_t off;
+  int rc = -1;
+  char key_loc[PROPERTY_VALUE_MAX];
+  char *fname;
+  struct stat statbuf;
+
+  property_get(KEY_LOC_PROP, key_loc, KEY_IN_FOOTER);
+
+  if (!strcmp(key_loc, KEY_IN_FOOTER)) {
+    fname = real_blk_name;
+    if ( (fd = open(fname, O_RDONLY)) < 0) {
+      printf("Cannot open real block device %s\n", fname);
+      return -1;
+    }
+
+    if ( (nr_sec = get_blkdev_size(fd)) == 0) {
+      SLOGE("Cannot get size of block device %s\n", fname);
+      goto errout;
+    }
+
+    /* If it's an encrypted Android partition, the last 16 Kbytes contain the
+     * encryption info footer and key, and plenty of bytes to spare for future
+     * growth.
+     */
+    off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
+
+    if (lseek64(fd, off, SEEK_SET) == -1) {
+      printf("Cannot seek to real block device footer\n");
+      goto errout;
+    }
+  } else if (key_loc[0] == '/') {
+    fname = key_loc;
+    if ( (fd = open(fname, O_RDONLY)) < 0) {
+      printf("Cannot open footer file %s\n", fname);
+      return -1;
+    }
+
+    /* Make sure it's 16 Kbytes in length */
+    fstat(fd, &statbuf);
+    if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+        && statbuf.st_size != 0x8000
+#endif
+        )) {
+      printf("footer file %s is not the expected size!\n", fname);
+      goto errout;
+    }
+  } else {
+    printf("Unexpected value for" KEY_LOC_PROP "\n");
+    return -1;;
+  }
+
+  if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) {
+    printf("Cannot read real block device footer\n");
+    goto errout;
+  }
+
+  if (crypt_ftr->magic != CRYPT_MNT_MAGIC) {
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+	if (crypt_ftr->magic != CRYPT_MNT_MAGIC_SAMSUNG) {
+		printf("Bad magic for real block device %s\n", fname);
+		goto errout;
+	} else {
+		printf("Using Samsung encryption.\n");
+		using_samsung_encryption = 1;
+        if ( (cnt = read(fd, &edk_payload, sizeof(edk_payload_t))) != sizeof(edk_payload_t)) {
+            printf("Cannot read EDK payload from real block device footer\n");
+            goto errout;
+        }
+        if (lseek64(fd, sizeof(__le32), SEEK_CUR) == -1) {
+            printf("Cannot seek past unknown data from real block device footer\n");
+            goto errout;
+        }
+        memcpy(key, &edk_payload, sizeof(edk_payload_t));
+	}
+#else
+    printf("Bad magic for real block device %s\n", fname);
+    goto errout;
+#endif
+  }
+
+  if (crypt_ftr->major_version != 1) {
+    printf("Cannot understand major version %d real block device footer\n",
+          crypt_ftr->major_version);
+    goto errout;
+  }
+
+  if (crypt_ftr->minor_version != 0) {
+    printf("Warning: crypto footer minor version %d, expected 0, continuing...\n",
+          crypt_ftr->minor_version);
+  }
+
+  if (crypt_ftr->ftr_size > sizeof(struct crypt_mnt_ftr)) {
+    /* the footer size is bigger than we expected.
+     * Skip to it's stated end so we can read the key.
+     */
+    if (lseek64(fd, crypt_ftr->ftr_size - sizeof(struct crypt_mnt_ftr),  SEEK_CUR) == -1) {
+      printf("Cannot seek to start of key\n");
+      goto errout;
+    }
+  }
+
+  if (crypt_ftr->keysize > sizeof(saved_master_key)) {
+    printf("Keysize of %d bits not supported for real block device %s\n",
+          crypt_ftr->keysize * 8, fname);
+    goto errout;
+  }
+
+  if ( (cnt = read(fd, key, crypt_ftr->keysize)) != crypt_ftr->keysize) {
+    printf("Cannot read key for real block device %s\n", fname);
+    goto errout;
+  }
+
+  if (lseek64(fd, KEY_TO_SALT_PADDING, SEEK_CUR) == -1) {
+    printf("Cannot seek to real block device salt\n");
+    goto errout;
+  }
+
+  if ( (cnt = read(fd, salt, SALT_LEN)) != SALT_LEN) {
+    printf("Cannot read salt for real block device %s\n", fname);
+    goto errout;
+  }
+
+  /* Success! */
+  rc = 0;
+
+errout:
+  close(fd);
+  return rc;
+}
+
+/* Convert a binary key of specified length into an ascii hex string equivalent,
+ * without the leading 0x and with null termination
+ */
+void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize,
+                              char *master_key_ascii)
+{
+  unsigned int i, a;
+  unsigned char nibble;
+
+  for (i=0, a=0; i<keysize; i++, a+=2) {
+    /* For each byte, write out two ascii hex digits */
+    nibble = (master_key[i] >> 4) & 0xf;
+    master_key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30);
+
+    nibble = master_key[i] & 0xf;
+    master_key_ascii[a+1] = nibble + (nibble > 9 ? 0x37 : 0x30);
+  }
+
+  /* Add the null termination */
+  master_key_ascii[a] = '\0';
+
+}
+
+static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key,
+                                    const char *real_blk_name, char *crypto_blk_name, const char *name)
+{
+  char buffer[DM_CRYPT_BUF_SIZE];
+  char master_key_ascii[129]; /* Large enough to hold 512 bit key and null */
+  char *crypt_params;
+  struct dm_ioctl *io;
+  struct dm_target_spec *tgt;
+  unsigned int minor;
+  int fd;
+  int retval = -1;
+
+  if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) {
+    printf("Cannot open device-mapper\n");
+    goto errout;
+  }
+
+  io = (struct dm_ioctl *) buffer;
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  if (ioctl(fd, DM_DEV_CREATE, io)) {
+    printf("Cannot create dm-crypt device\n");
+    goto errout;
+  }
+
+  /* Get the device status, in particular, the name of it's device file */
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  if (ioctl(fd, DM_DEV_STATUS, io)) {
+    printf("Cannot retrieve dm-crypt device status\n");
+    goto errout;
+  }
+  minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+  snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor);
+
+  /* Load the mapping table for this device */
+  tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+  ioctl_init(io, 4096, name, 0);
+  io->target_count = 1;
+  tgt->status = 0;
+  tgt->sector_start = 0;
+  tgt->length = crypt_ftr->fs_size;
+  strcpy(tgt->target_type, "crypt");
+
+  crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+  convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+  sprintf(crypt_params, "%s %s 0 %s 0", crypt_ftr->crypto_type_name,
+          master_key_ascii, real_blk_name);
+  //printf("cryptsetup params: '%s'\n", crypt_params);
+  crypt_params += strlen(crypt_params) + 1;
+  crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */
+  tgt->next = crypt_params - buffer;
+
+  if (ioctl(fd, DM_TABLE_LOAD, io)) {
+      printf("Cannot load dm-crypt mapping table.\n");
+      goto errout;
+  }
+
+  /* Resume this device to activate it */
+  ioctl_init(io, 4096, name, 0);
+
+  if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+    printf("Cannot resume the dm-crypt device\n");
+    goto errout;
+  }
+
+  /* We made it here with no errors.  Woot! */
+  retval = 0;
+
+errout:
+  close(fd);   /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+  return retval;
+}
+
+static int delete_crypto_blk_dev(const char *name)
+{
+  int fd;
+  char buffer[DM_CRYPT_BUF_SIZE];
+  struct dm_ioctl *io;
+  int retval = -1;
+
+  if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) {
+    printf("Cannot open device-mapper\n");
+    goto errout;
+  }
+
+  io = (struct dm_ioctl *) buffer;
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  if (ioctl(fd, DM_DEV_REMOVE, io)) {
+    printf("Cannot remove dm-crypt device\n");
+    goto errout;
+  }
+
+  /* We made it here with no errors.  Woot! */
+  retval = 0;
+
+errout:
+  close(fd);    /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+  return retval;
+
+}
+
+static void pbkdf2(char *passwd, unsigned char *salt, unsigned char *ikey)
+{
+    /* Turn the password into a key and IV that can decrypt the master key */
+    PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, SALT_LEN,
+                           HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey);
+}
+
+static int decrypt_master_key(char *passwd, unsigned char *salt,
+                              unsigned char *encrypted_master_key,
+                              unsigned char *decrypted_master_key)
+{
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+    if (using_samsung_encryption) {
+		property_set("rw.km_fips_status", "ready");
+		return decrypt_EDK((dek_t*)decrypted_master_key, (edk_payload_t*)encrypted_master_key, passwd);
+	}
+#endif
+
+  unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
+  EVP_CIPHER_CTX d_ctx;
+  int decrypted_len, final_len;
+
+  /* Turn the password into a key and IV that can decrypt the master key */
+  pbkdf2(passwd, salt, ikey);
+
+  /* Initialize the decryption engine */
+  if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
+    return -1;
+  }
+  EVP_CIPHER_CTX_set_padding(&d_ctx, 0); /* Turn off padding as our data is block aligned */
+  /* Decrypt the master key */
+  if (! EVP_DecryptUpdate(&d_ctx, decrypted_master_key, &decrypted_len,
+                            encrypted_master_key, KEY_LEN_BYTES)) {
+    return -1;
+  }
+  if (! EVP_DecryptFinal(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) {
+    return -1;
+  }
+
+  if (decrypted_len + final_len != KEY_LEN_BYTES) {
+    return -1;
+  } else {
+    return 0;
+  }
+}
+
+static int get_orig_mount_parms(
+        const char *mount_point, char *fs_type, char *real_blkdev,
+        unsigned long *mnt_flags, char *fs_options)
+{
+  char mount_point2[PROPERTY_VALUE_MAX];
+  char fs_flags[PROPERTY_VALUE_MAX];
+
+  property_get("ro.crypto.fs_type", fs_type, "");
+  property_get("ro.crypto.fs_real_blkdev", real_blkdev, "");
+  property_get("ro.crypto.fs_mnt_point", mount_point2, "");
+  property_get("ro.crypto.fs_options", fs_options, "");
+  property_get("ro.crypto.fs_flags", fs_flags, "");
+  *mnt_flags = strtol(fs_flags, 0, 0);
+
+  if (strcmp(mount_point, mount_point2)) {
+    /* Consistency check.  These should match. If not, something odd happened. */
+    return -1;
+  }
+
+  return 0;
+}
+
+static int get_orig_mount_parms_sd(
+        const char *mount_point, char *fs_type, char *real_blkdev)
+{
+    char mount_point2[PROPERTY_VALUE_MAX];
+
+    property_get("ro.crypto.sd_fs_type", fs_type, "");
+    property_get("ro.crypto.sd_fs_real_blkdev", real_blkdev, "");
+    property_get("ro.crypto.sd_fs_mnt_point", mount_point2, "");
+
+    if (strcmp(mount_point, mount_point2)) {
+        /* Consistency check.  These should match. If not, something odd happened. */
+        return -1;
+    }
+
+    return 0;
+}
+
+static int test_mount_encrypted_fs(
+        char *passwd, char *mount_point, char *label, char *crypto_blkdev)
+{
+  struct crypt_mnt_ftr crypt_ftr;
+  /* Allocate enough space for a 256 bit key, but we may use less */
+  unsigned char encrypted_master_key[256], decrypted_master_key[32];
+  unsigned char salt[SALT_LEN];
+  char real_blkdev[MAXPATHLEN];
+  char fs_type[PROPERTY_VALUE_MAX];
+  char fs_options[PROPERTY_VALUE_MAX];
+  char tmp_mount_point[MAXPATHLEN];
+  unsigned long mnt_flags;
+  unsigned int orig_failed_decrypt_count;
+  char encrypted_state[PROPERTY_VALUE_MAX];
+  int rc;
+
+  property_get("ro.crypto.state", encrypted_state, "");
+  if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
+    printf("encrypted fs already validated or not running with encryption, aborting %s\n", encrypted_state);
+    return -1;
+  }
+
+  if (get_orig_mount_parms(mount_point, fs_type, real_blkdev, &mnt_flags, fs_options)) {
+    printf("Error reading original mount parms for mount point %s\n", mount_point);
+    return -1;
+  }
+
+  if (get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt)) {
+    printf("Error getting crypt footer and key\n");
+    return -1;
+  }
+
+  //printf("crypt_ftr->fs_size = %lld\n", crypt_ftr.fs_size);
+  orig_failed_decrypt_count = crypt_ftr.failed_decrypt_count;
+
+  if (! (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
+    decrypt_master_key(passwd, salt, encrypted_master_key, decrypted_master_key);
+  }
+
+  if (create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev,
+                    crypto_blkdev, label)) {
+    printf("Error creating decrypted block device\n");
+    return -1;
+  }
+
+  /* If init detects an encrypted filesystme, it writes a file for each such
+   * encrypted fs into the tmpfs /data filesystem, and then the framework finds those
+   * files and passes that data to me */
+  /* Create a tmp mount point to try mounting the decryptd fs
+   * Since we're here, the mount_point should be a tmpfs filesystem, so make
+   * a directory in it to test mount the decrypted filesystem.
+   */
+  sprintf(tmp_mount_point, "%s/tmp_mnt", mount_point);
+  mkdir(tmp_mount_point, 0755);
+  if ( mount(crypto_blkdev, tmp_mount_point, fs_type, MS_RDONLY, "") ) {
+    printf("Error temp mounting decrypted block device\n");
+    delete_crypto_blk_dev(label);
+    crypt_ftr.failed_decrypt_count++;
+  } else {
+    /* Success, so just umount and we'll mount it properly when we restart
+     * the framework.
+     */
+    umount(tmp_mount_point);
+    crypt_ftr.failed_decrypt_count  = 0;
+  }
+
+  rmdir(tmp_mount_point);
+
+  if (crypt_ftr.failed_decrypt_count) {
+    /* We failed to mount the device, so return an error */
+    rc = crypt_ftr.failed_decrypt_count;
+
+  } else {
+    /* Woot!  Success!  Save the name of the crypto block device
+     * so we can mount it when restarting the framework.
+     */
+    property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+
+    /* Also save a the master key so we can reencrypted the key
+     * the key when we want to change the password on it.
+     */
+    memcpy(saved_master_key, decrypted_master_key, sizeof(saved_master_key));
+    saved_data_blkdev = strdup(real_blkdev);
+    saved_mount_point = strdup(mount_point);
+    master_key_saved = 1;
+    rc = 0;
+  }
+
+  return rc;
+}
+
+static int test_mount_encrypted_fs_sd(
+        const char *passwd, const char *mount_point, const char *label)
+{
+    char real_blkdev[MAXPATHLEN];
+    char crypto_blkdev[MAXPATHLEN];
+    char tmp_mount_point[MAXPATHLEN];
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    char fs_type[PROPERTY_VALUE_MAX];
+    int rc;
+
+    property_get("ro.crypto.state", encrypted_state, "");
+    if ( !master_key_saved || strcmp(encrypted_state, "encrypted") ) {
+        printf("encrypted fs not yet validated or not running with encryption, aborting\n");
+        return -1;
+    }
+
+    if (get_orig_mount_parms_sd(mount_point, fs_type, real_blkdev)) {
+        printf("Error reading original mount parms for mount point %s\n", mount_point);
+        return -1;
+    }
+
+    rc = cryptfs_setup_volume(label, real_blkdev, crypto_blkdev);
+    if(rc){
+        printf("Error setting up cryptfs volume %s\n", real_blkdev);
+        return -1;
+    }
+
+    sprintf(tmp_mount_point, "%s/tmp_mnt", mount_point);
+    mkdir(tmp_mount_point, 0755);
+    if ( mount(crypto_blkdev, tmp_mount_point, fs_type, MS_RDONLY, "") ) {
+        printf("Error temp mounting decrypted block device\n");
+        delete_crypto_blk_dev(label);
+    } else {
+        /* Success, so just umount and we'll mount it properly when we restart
+        * the framework.
+        */
+        umount(tmp_mount_point);
+
+        property_set("ro.crypto.sd_fs_crypto_blkdev", crypto_blkdev);
+    }
+
+    rmdir(tmp_mount_point);
+
+    return rc;
+}
+
+/*
+ * Called by vold when it's asked to mount an encrypted, nonremovable volume.
+ * Setup a dm-crypt mapping, use the saved master key from
+ * setting up the /data mapping, and return the new device path.
+ */
+int cryptfs_setup_volume(const char *label, const char *real_blkdev, char *crypto_blkdev)
+{
+    struct crypt_mnt_ftr sd_crypt_ftr;
+    unsigned char key[256], salt[32];
+    struct stat statbuf;
+    int nr_sec, fd, rc;
+
+    /* Just want the footer, but gotta get it all */
+    get_crypt_ftr_and_key(saved_data_blkdev, &sd_crypt_ftr, key, salt);
+
+    /* Update the fs_size field to be the size of the volume */
+    fd = open(real_blkdev, O_RDONLY);
+    nr_sec = get_blkdev_size(fd);
+    close(fd);
+    if (nr_sec == 0) {
+        SLOGE("Cannot get size of volume %s\n", real_blkdev);
+        return -1;
+    }
+
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+    if(using_samsung_encryption) {
+        if(!access("/efs/essiv", R_OK)){
+            strcpy(sd_crypt_ftr.crypto_type_name, "aes-cbc-plain:sha1");
+        }
+        else if(!access("/efs/cryptprop_essiv", R_OK)){
+            strcpy(sd_crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256");
+        }
+    }
+#endif
+
+    sd_crypt_ftr.fs_size = nr_sec;
+    rc = create_crypto_blk_dev(
+            &sd_crypt_ftr, saved_master_key, real_blkdev, crypto_blkdev, label);
+
+    stat(crypto_blkdev, &statbuf);
+
+    return rc;
+}
+
+int cryptfs_crypto_complete(void)
+{
+  return -1;
+}
+
+int cryptfs_check_footer(void)
+{
+    int rc = -1;
+    char fs_type[PROPERTY_VALUE_MAX];
+    char real_blkdev[MAXPATHLEN];
+    char fs_options[PROPERTY_VALUE_MAX];
+    unsigned long mnt_flags;
+    struct crypt_mnt_ftr crypt_ftr;
+    /* Allocate enough space for a 256 bit key, but we may use less */
+    unsigned char encrypted_master_key[256];
+    unsigned char salt[SALT_LEN];
+
+    if (get_orig_mount_parms(DATA_MNT_POINT, fs_type, real_blkdev, &mnt_flags, fs_options)) {
+        printf("Error reading original mount parms for mount point %s\n", DATA_MNT_POINT);
+        return rc;
+    }
+
+    rc = get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt);
+
+    return rc;
+}
+
+int cryptfs_check_passwd(const char *passwd)
+{
+    char pwbuf[256];
+    char crypto_blkdev_data[MAXPATHLEN];
+    int rc = -1;
+
+    strcpy(pwbuf, passwd);
+    rc = test_mount_encrypted_fs(pwbuf, DATA_MNT_POINT, "userdata", crypto_blkdev_data);
+
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+    if(using_samsung_encryption) {
+
+        int rc2 = 1;
+#ifndef RECOVERY_SDCARD_ON_DATA
+#ifdef TW_INTERNAL_STORAGE_PATH
+		// internal storage for non data/media devices
+        if(!rc) {
+            strcpy(pwbuf, passwd);
+            rc2 = test_mount_encrypted_fs_sd(
+                    pwbuf, EXPAND(TW_INTERNAL_STORAGE_PATH),
+                    EXPAND(TW_INTERNAL_STORAGE_MOUNT_POINT));
+        }
+#endif
+#endif
+#ifdef TW_EXTERNAL_STORAGE_PATH
+		printf("Temp mounting /data\n");
+		// mount data so mount_ecryptfs_drive can access edk in /data/system/
+		rc2 = mount(crypto_blkdev_data, DATA_MNT_POINT, CRYPTO_FS_TYPE, MS_RDONLY, "");
+        // external sd
+		char decrypt_external[256], external_blkdev[256];
+		property_get("ro.crypto.external_encrypted", decrypt_external, "0");
+		// Mount the external storage as ecryptfs so that ecryptfs can act as a pass-through
+		if (!rc2 && strcmp(decrypt_external, "1") == 0) {
+			printf("Mounting external with ecryptfs...\n");
+            strcpy(pwbuf, passwd);
+            rc2 = mount_ecryptfs_drive(
+                    pwbuf, EXPAND(TW_EXTERNAL_STORAGE_PATH),
+                    EXPAND(TW_EXTERNAL_STORAGE_PATH), 0);
+			if (rc2 == 0)
+				property_set("ro.crypto.external_use_ecryptfs", "1");
+			else
+				property_set("ro.crypto.external_use_ecryptfs", "0");
+        } else {
+			printf("Unable to mount external storage with ecryptfs.\n");
+			umount(EXPAND(TW_EXTERNAL_STORAGE_PATH));
+		}
+        umount(DATA_MNT_POINT);
+    }
+#endif // #ifdef TW_EXTERNAL_STORAGE_PATH
+#endif // #ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+    return rc;
+}
diff --git a/crypto/ics/cryptfs.h b/crypto/ics/cryptfs.h
new file mode 100644
index 0000000..8c80376
--- /dev/null
+++ b/crypto/ics/cryptfs.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This structure starts 16,384 bytes before the end of a hardware
+ * partition that is encrypted.
+ * Immediately following this structure is the encrypted key.
+ * The keysize field tells how long the key is, in bytes.
+ * Then there is 32 bytes of padding,
+ * Finally there is the salt used with the user password.
+ * The salt is fixed at 16 bytes long.
+ * Obviously, the filesystem does not include the last 16 kbytes
+ * of the partition.
+ */
+
+#ifndef __CRYPTFS_H__
+#define __CRYPTFS_H__
+
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+#include "../libcrypt_samsung/include/libcrypt_samsung.h"
+#endif
+
+#define CRYPT_FOOTER_OFFSET 0x4000
+
+#define MAX_CRYPTO_TYPE_NAME_LEN 64
+
+#define SALT_LEN 16
+#define KEY_TO_SALT_PADDING 32
+
+/* definitions of flags in the structure below */
+#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
+#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Set when starting encryption,
+                                          * clear when done before rebooting */
+
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+#define CRYPT_MNT_MAGIC_SAMSUNG 0xD0B5B1C5
+#endif
+#define CRYPT_MNT_MAGIC 0xD0B5B1C4
+
+#define __le32 unsigned int
+#define __le16 unsigned short int 
+
+#pragma pack(1)
+
+struct crypt_mnt_ftr {
+  __le32 magic;		/* See above */
+  __le16 major_version;
+  __le16 minor_version;
+  __le32 ftr_size; 	/* in bytes, not including key following */
+  __le32 flags;		/* See above */
+  __le32 keysize;	/* in bytes */
+  __le32 spare1;	/* ignored */
+  __le64 fs_size;	/* Size of the encrypted fs, in 512 byte sectors */
+  __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
+				  mount, set to 0 on successful mount */
+  char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
+							       needed to decrypt this
+							       partition, null terminated */
+};
+
+#pragma pack()
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  int cryptfs_check_footer(void);
+  int cryptfs_check_passwd(const char *pw);
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __CRYPTFS_H__
+
diff --git a/crypto/jb/Android.mk b/crypto/jb/Android.mk
new file mode 100644
index 0000000..90321d6
--- /dev/null
+++ b/crypto/jb/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcryptfsjb
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_CFLAGS :=
+LOCAL_SRC_FILES = cryptfs.c
+LOCAL_C_INCLUDES += \
+    system/extras/ext4_utils \
+    external/openssl/include \
+    $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+LOCAL_SHARED_LIBRARIES += libc liblog libcutils libcrypto libext4_utils
+LOCAL_STATIC_LIBRARIES += libfs_mgrtwrp libscrypttwrp_static liblogwraptwrp libmincrypttwrp
+
+include $(BUILD_SHARED_LIBRARY)
+endif
diff --git a/crypto/jb/cryptfs.c b/crypto/jb/cryptfs.c
new file mode 100644
index 0000000..f9c0d74
--- /dev/null
+++ b/crypto/jb/cryptfs.c
@@ -0,0 +1,1735 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* TO DO:
+ *   1.  Perhaps keep several copies of the encrypted key, in case something
+ *       goes horribly wrong?
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/dm-ioctl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <errno.h>
+#include <ext4.h>
+#include <linux/kdev_t.h>
+#include <fs_mgr.h>
+#include "cryptfs.h"
+#define LOG_TAG "Cryptfs"
+#include "cutils/log.h"
+#include "cutils/properties.h"
+#include "cutils/android_reboot.h"
+#include "hardware_legacy/power.h"
+/*#include <logwrap/logwrap.h>
+#include "VolumeManager.h"
+#include "VoldUtil.h"*/
+#include "crypto_scrypt.h"
+
+#define DM_CRYPT_BUF_SIZE 4096
+#define DATA_MNT_POINT "/data"
+
+#define HASH_COUNT 2000
+#define KEY_LEN_BYTES 16
+#define IV_LEN_BYTES 16
+
+#define KEY_IN_FOOTER  "footer"
+
+#define EXT4_FS 1
+#define FAT_FS 2
+
+#define TABLE_LOAD_RETRIES 10
+
+char *me = "cryptfs";
+
+static unsigned char saved_master_key[KEY_LEN_BYTES];
+static char *saved_mount_point;
+static int  master_key_saved = 0;
+static struct crypt_persist_data *persist_data = NULL;
+
+struct fstab *fstab;
+
+static void cryptfs_reboot(int recovery)
+{
+    /*if (recovery) {
+        property_set(ANDROID_RB_PROPERTY, "reboot,recovery");
+    } else {
+        property_set(ANDROID_RB_PROPERTY, "reboot");
+    }
+    sleep(20);*/
+
+    /* Shouldn't get here, reboot should happen before sleep times out */
+    return;
+}
+
+static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags)
+{
+    memset(io, 0, dataSize);
+    io->data_size = dataSize;
+    io->data_start = sizeof(struct dm_ioctl);
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+    io->flags = flags;
+    if (name) {
+        strncpy(io->name, name, sizeof(io->name));
+    }
+}
+
+/**
+ * Gets the default device scrypt parameters for key derivation time tuning.
+ * The parameters should lead to about one second derivation time for the
+ * given device.
+ */
+static void get_device_scrypt_params(struct crypt_mnt_ftr *ftr) {
+    const int default_params[] = SCRYPT_DEFAULTS;
+    int params[] = SCRYPT_DEFAULTS;
+    char paramstr[PROPERTY_VALUE_MAX];
+    char *token;
+    char *saveptr;
+    int i;
+
+    property_get(SCRYPT_PROP, paramstr, "");
+    if (paramstr[0] != '\0') {
+        /*
+         * The token we're looking for should be three integers separated by
+         * colons (e.g., "12:8:1"). Scan the property to make sure it matches.
+         */
+        for (i = 0, token = strtok_r(paramstr, ":", &saveptr);
+                token != NULL && i < 3;
+                i++, token = strtok_r(NULL, ":", &saveptr)) {
+            char *endptr;
+            params[i] = strtol(token, &endptr, 10);
+
+            /*
+             * Check that there was a valid number and it's 8-bit. If not,
+             * break out and the end check will take the default values.
+             */
+            if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
+                break;
+            }
+        }
+
+        /*
+         * If there were not enough tokens or a token was malformed (not an
+         * integer), it will end up here and the default parameters can be
+         * taken.
+         */
+        if ((i != 3) || (token != NULL)) {
+            printf("bad scrypt parameters '%s' should be like '12:8:1'; using defaults", paramstr);
+            memcpy(params, default_params, sizeof(params));
+        }
+    }
+
+    ftr->N_factor = params[0];
+    ftr->r_factor = params[1];
+    ftr->p_factor = params[2];
+}
+
+static unsigned int get_fs_size(char *dev)
+{
+    int fd, block_size;
+    struct ext4_super_block sb;
+    off64_t len;
+
+    if ((fd = open(dev, O_RDONLY)) < 0) {
+        printf("Cannot open device to get filesystem size ");
+        return 0;
+    }
+
+    if (lseek64(fd, 1024, SEEK_SET) < 0) {
+        printf("Cannot seek to superblock");
+        return 0;
+    }
+
+    if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) {
+        printf("Cannot read superblock");
+        return 0;
+    }
+
+    close(fd);
+
+    block_size = 1024 << sb.s_log_block_size;
+    /* compute length in bytes */
+    len = ( ((off64_t)sb.s_blocks_count_hi << 32) + sb.s_blocks_count_lo) * block_size;
+
+    /* return length in sectors */
+    return (unsigned int) (len / 512);
+}
+
+static unsigned int get_blkdev_size(int fd)
+{
+  unsigned int nr_sec;
+
+  if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+    nr_sec = 0;
+  }
+
+  return nr_sec;
+}
+
+static int get_crypt_ftr_info(char **metadata_fname, off64_t *off)
+{
+  static int cached_data = 0;
+  static off64_t cached_off = 0;
+  static char cached_metadata_fname[PROPERTY_VALUE_MAX] = "";
+  int fd;
+  char key_loc[PROPERTY_VALUE_MAX];
+  char real_blkdev[PROPERTY_VALUE_MAX];
+  unsigned int nr_sec;
+  int rc = -1;
+
+  if (!cached_data) {
+    fs_mgr_get_crypt_info(fstab, key_loc, real_blkdev, sizeof(key_loc));
+
+    if (!strcmp(key_loc, KEY_IN_FOOTER)) {
+      if ( (fd = open(real_blkdev, O_RDWR)) < 0) {
+        printf("Cannot open real block device %s\n", real_blkdev);
+        return -1;
+      }
+
+      if ((nr_sec = get_blkdev_size(fd))) {
+        /* If it's an encrypted Android partition, the last 16 Kbytes contain the
+         * encryption info footer and key, and plenty of bytes to spare for future
+         * growth.
+         */
+        strlcpy(cached_metadata_fname, real_blkdev, sizeof(cached_metadata_fname));
+        cached_off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
+        cached_data = 1;
+      } else {
+        printf("Cannot get size of block device %s\n", real_blkdev);
+      }
+      close(fd);
+    } else {
+      strlcpy(cached_metadata_fname, key_loc, sizeof(cached_metadata_fname));
+      cached_off = 0;
+      cached_data = 1;
+    }
+  }
+
+  if (cached_data) {
+    if (metadata_fname) {
+        *metadata_fname = cached_metadata_fname;
+    }
+    if (off) {
+        *off = cached_off;
+    }
+    rc = 0;
+  }
+
+  return rc;
+}
+
+/* key or salt can be NULL, in which case just skip writing that value.  Useful to
+ * update the failed mount count but not change the key.
+ */
+static int put_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr)
+{
+  int fd;
+  unsigned int nr_sec, cnt;
+  /* starting_off is set to the SEEK_SET offset
+   * where the crypto structure starts
+   */
+  off64_t starting_off;
+  int rc = -1;
+  char *fname = NULL;
+  struct stat statbuf;
+
+  if (get_crypt_ftr_info(&fname, &starting_off)) {
+    printf("Unable to get crypt_ftr_info\n");
+    return -1;
+  }
+  if (fname[0] != '/') {
+    printf("Unexpected value for crypto key location\n");
+    return -1;
+  }
+  if ( (fd = open(fname, O_RDWR | O_CREAT, 0600)) < 0) {
+    printf("Cannot open footer file %s for put\n", fname);
+    return -1;
+  }
+
+  /* Seek to the start of the crypt footer */
+  if (lseek64(fd, starting_off, SEEK_SET) == -1) {
+    printf("Cannot seek to real block device footer\n");
+    goto errout;
+  }
+
+  if ((cnt = write(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) {
+    printf("Cannot write real block device footer\n");
+    goto errout;
+  }
+
+  fstat(fd, &statbuf);
+  /* If the keys are kept on a raw block device, do not try to truncate it. */
+  if (S_ISREG(statbuf.st_mode)) {
+    if (ftruncate(fd, 0x4000)) {
+      printf("Cannot set footer file size\n", fname);
+      goto errout;
+    }
+  }
+
+  /* Success! */
+  rc = 0;
+
+errout:
+  close(fd);
+  return rc;
+
+}
+
+static inline int unix_read(int  fd, void*  buff, int  len)
+{
+    return TEMP_FAILURE_RETRY(read(fd, buff, len));
+}
+
+static inline int unix_write(int  fd, const void*  buff, int  len)
+{
+    return TEMP_FAILURE_RETRY(write(fd, buff, len));
+}
+
+static void init_empty_persist_data(struct crypt_persist_data *pdata, int len)
+{
+    memset(pdata, 0, len);
+    pdata->persist_magic = PERSIST_DATA_MAGIC;
+    pdata->persist_valid_entries = 0;
+}
+
+/* A routine to update the passed in crypt_ftr to the lastest version.
+ * fd is open read/write on the device that holds the crypto footer and persistent
+ * data, crypt_ftr is a pointer to the struct to be updated, and offset is the
+ * absolute offset to the start of the crypt_mnt_ftr on the passed in fd.
+ */
+static void upgrade_crypt_ftr(int fd, struct crypt_mnt_ftr *crypt_ftr, off64_t offset)
+{
+    int orig_major = crypt_ftr->major_version;
+    int orig_minor = crypt_ftr->minor_version;
+    return; // in recovery we don't want to upgrade
+    if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version == 0)) {
+        struct crypt_persist_data *pdata;
+        off64_t pdata_offset = offset + CRYPT_FOOTER_TO_PERSIST_OFFSET;
+
+        printf("upgrading crypto footer to 1.1");
+
+        pdata = malloc(CRYPT_PERSIST_DATA_SIZE);
+        if (pdata == NULL) {
+            printf("Cannot allocate persisent data\n");
+            return;
+        }
+        memset(pdata, 0, CRYPT_PERSIST_DATA_SIZE);
+
+        /* Need to initialize the persistent data area */
+        if (lseek64(fd, pdata_offset, SEEK_SET) == -1) {
+            printf("Cannot seek to persisent data offset\n");
+            return;
+        }
+        /* Write all zeros to the first copy, making it invalid */
+        unix_write(fd, pdata, CRYPT_PERSIST_DATA_SIZE);
+
+        /* Write a valid but empty structure to the second copy */
+        init_empty_persist_data(pdata, CRYPT_PERSIST_DATA_SIZE);
+        unix_write(fd, pdata, CRYPT_PERSIST_DATA_SIZE);
+
+        /* Update the footer */
+        crypt_ftr->persist_data_size = CRYPT_PERSIST_DATA_SIZE;
+        crypt_ftr->persist_data_offset[0] = pdata_offset;
+        crypt_ftr->persist_data_offset[1] = pdata_offset + CRYPT_PERSIST_DATA_SIZE;
+        crypt_ftr->minor_version = 1;
+    }
+
+    if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version)) {
+        printf("upgrading crypto footer to 1.2");
+        crypt_ftr->kdf_type = KDF_PBKDF2;
+        get_device_scrypt_params(crypt_ftr);
+        crypt_ftr->minor_version = 2;
+    }
+
+    if ((orig_major != crypt_ftr->major_version) || (orig_minor != crypt_ftr->minor_version)) {
+        if (lseek64(fd, offset, SEEK_SET) == -1) {
+            printf("Cannot seek to crypt footer\n");
+            return;
+        }
+        unix_write(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr));
+    }
+}
+
+
+static int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr)
+{
+  int fd;
+  unsigned int nr_sec, cnt;
+  off64_t starting_off;
+  int rc = -1;
+  char *fname = NULL;
+  struct stat statbuf;
+
+  if (get_crypt_ftr_info(&fname, &starting_off)) {
+    printf("Unable to get crypt_ftr_info\n");
+    return -1;
+  }
+  if (fname[0] != '/') {
+    printf("Unexpected value for crypto key location\n");
+    return -1;
+  }
+  if ( (fd = open(fname, O_RDWR)) < 0) {
+    printf("Cannot open footer file %s for get\n", fname);
+    return -1;
+  }
+
+  /* Make sure it's 16 Kbytes in length */
+  fstat(fd, &statbuf);
+  if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000)) {
+    printf("footer file %s is not the expected size!\n", fname);
+    goto errout;
+  }
+
+  /* Seek to the start of the crypt footer */
+  if (lseek64(fd, starting_off, SEEK_SET) == -1) {
+    printf("Cannot seek to real block device footer\n");
+    goto errout;
+  }
+
+  if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) {
+    printf("Cannot read real block device footer\n");
+    goto errout;
+  }
+
+  if (crypt_ftr->magic != CRYPT_MNT_MAGIC) {
+    printf("Bad magic for real block device %s\n", fname);
+    goto errout;
+  }
+
+  if (crypt_ftr->major_version != CURRENT_MAJOR_VERSION) {
+    printf("Cannot understand major version %d real block device footer; expected %d\n",
+          crypt_ftr->major_version, CURRENT_MAJOR_VERSION);
+    goto errout;
+  }
+
+  if (crypt_ftr->minor_version > CURRENT_MINOR_VERSION) {
+    printf("Warning: crypto footer minor version %d, expected <= %d, continuing...\n",
+          crypt_ftr->minor_version, CURRENT_MINOR_VERSION);
+  }
+
+  /* If this is a verion 1.0 crypt_ftr, make it a 1.1 crypt footer, and update the
+   * copy on disk before returning.
+   */
+  /*if (crypt_ftr->minor_version < CURRENT_MINOR_VERSION) {
+    upgrade_crypt_ftr(fd, crypt_ftr, starting_off);
+  }*/
+
+  /* Success! */
+  rc = 0;
+
+errout:
+  close(fd);
+  return rc;
+}
+
+static int validate_persistent_data_storage(struct crypt_mnt_ftr *crypt_ftr)
+{
+    if (crypt_ftr->persist_data_offset[0] + crypt_ftr->persist_data_size >
+        crypt_ftr->persist_data_offset[1]) {
+        printf("Crypt_ftr persist data regions overlap");
+        return -1;
+    }
+
+    if (crypt_ftr->persist_data_offset[0] >= crypt_ftr->persist_data_offset[1]) {
+        printf("Crypt_ftr persist data region 0 starts after region 1");
+        return -1;
+    }
+
+    if (((crypt_ftr->persist_data_offset[1] + crypt_ftr->persist_data_size) -
+        (crypt_ftr->persist_data_offset[0] - CRYPT_FOOTER_TO_PERSIST_OFFSET)) >
+        CRYPT_FOOTER_OFFSET) {
+        printf("Persistent data extends past crypto footer");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int load_persistent_data(void)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    struct crypt_persist_data *pdata = NULL;
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    char *fname;
+    int found = 0;
+    int fd;
+    int ret;
+    int i;
+
+    if (persist_data) {
+        /* Nothing to do, we've already loaded or initialized it */
+        return 0;
+    }
+
+
+    /* If not encrypted, just allocate an empty table and initialize it */
+    property_get("ro.crypto.state", encrypted_state, "");
+    if (strcmp(encrypted_state, "encrypted") ) {
+        pdata = malloc(CRYPT_PERSIST_DATA_SIZE);
+        if (pdata) {
+            init_empty_persist_data(pdata, CRYPT_PERSIST_DATA_SIZE);
+            persist_data = pdata;
+            return 0;
+        }
+        return -1;
+    }
+
+    if(get_crypt_ftr_and_key(&crypt_ftr)) {
+        return -1;
+    }
+
+    if ((crypt_ftr.major_version != 1) || (crypt_ftr.minor_version != 1)) {
+        printf("Crypt_ftr version doesn't support persistent data");
+        return -1;
+    }
+
+    if (get_crypt_ftr_info(&fname, NULL)) {
+        return -1;
+    }
+
+    ret = validate_persistent_data_storage(&crypt_ftr);
+    if (ret) {
+        return -1;
+    }
+
+    fd = open(fname, O_RDONLY);
+    if (fd < 0) {
+        printf("Cannot open %s metadata file", fname);
+        return -1;
+    }
+
+    if (persist_data == NULL) {
+        pdata = malloc(crypt_ftr.persist_data_size);
+        if (pdata == NULL) {
+            printf("Cannot allocate memory for persistent data");
+            goto err;
+        }
+    }
+
+    for (i = 0; i < 2; i++) {
+        if (lseek64(fd, crypt_ftr.persist_data_offset[i], SEEK_SET) < 0) {
+            printf("Cannot seek to read persistent data on %s", fname);
+            goto err2;
+        }
+        if (unix_read(fd, pdata, crypt_ftr.persist_data_size) < 0){
+            printf("Error reading persistent data on iteration %d", i);
+            goto err2;
+        }
+        if (pdata->persist_magic == PERSIST_DATA_MAGIC) {
+            found = 1;
+            break;
+        }
+    }
+
+    if (!found) {
+        printf("Could not find valid persistent data, creating");
+        init_empty_persist_data(pdata, crypt_ftr.persist_data_size);
+    }
+
+    /* Success */
+    persist_data = pdata;
+    close(fd);
+    return 0;
+
+err2:
+    free(pdata);
+
+err:
+    close(fd);
+    return -1;
+}
+
+static int save_persistent_data(void)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    struct crypt_persist_data *pdata;
+    char *fname;
+    off64_t write_offset;
+    off64_t erase_offset;
+    int found = 0;
+    int fd;
+    int ret;
+
+    if (persist_data == NULL) {
+        printf("No persistent data to save");
+        return -1;
+    }
+
+    if(get_crypt_ftr_and_key(&crypt_ftr)) {
+        return -1;
+    }
+
+    if ((crypt_ftr.major_version != 1) || (crypt_ftr.minor_version != 1)) {
+        printf("Crypt_ftr version doesn't support persistent data");
+        return -1;
+    }
+
+    ret = validate_persistent_data_storage(&crypt_ftr);
+    if (ret) {
+        return -1;
+    }
+
+    if (get_crypt_ftr_info(&fname, NULL)) {
+        return -1;
+    }
+
+    fd = open(fname, O_RDWR);
+    if (fd < 0) {
+        printf("Cannot open %s metadata file", fname);
+        return -1;
+    }
+
+    pdata = malloc(crypt_ftr.persist_data_size);
+    if (pdata == NULL) {
+        printf("Cannot allocate persistant data");
+        goto err;
+    }
+
+    if (lseek64(fd, crypt_ftr.persist_data_offset[0], SEEK_SET) < 0) {
+        printf("Cannot seek to read persistent data on %s", fname);
+        goto err2;
+    }
+
+    if (unix_read(fd, pdata, crypt_ftr.persist_data_size) < 0) {
+            printf("Error reading persistent data before save");
+            goto err2;
+    }
+
+    if (pdata->persist_magic == PERSIST_DATA_MAGIC) {
+        /* The first copy is the curent valid copy, so write to
+         * the second copy and erase this one */
+       write_offset = crypt_ftr.persist_data_offset[1];
+       erase_offset = crypt_ftr.persist_data_offset[0];
+    } else {
+        /* The second copy must be the valid copy, so write to
+         * the first copy, and erase the second */
+       write_offset = crypt_ftr.persist_data_offset[0];
+       erase_offset = crypt_ftr.persist_data_offset[1];
+    }
+
+    /* Write the new copy first, if successful, then erase the old copy */
+    if (lseek(fd, write_offset, SEEK_SET) < 0) {
+        printf("Cannot seek to write persistent data");
+        goto err2;
+    }
+    if (unix_write(fd, persist_data, crypt_ftr.persist_data_size) ==
+        (int) crypt_ftr.persist_data_size) {
+        if (lseek(fd, erase_offset, SEEK_SET) < 0) {
+            printf("Cannot seek to erase previous persistent data");
+            goto err2;
+        }
+        fsync(fd);
+        memset(pdata, 0, crypt_ftr.persist_data_size);
+        if (unix_write(fd, pdata, crypt_ftr.persist_data_size) !=
+            (int) crypt_ftr.persist_data_size) {
+            printf("Cannot write to erase previous persistent data");
+            goto err2;
+        }
+        fsync(fd);
+    } else {
+        printf("Cannot write to save persistent data");
+        goto err2;
+    }
+
+    /* Success */
+    free(pdata);
+    close(fd);
+    return 0;
+
+err2:
+    free(pdata);
+err:
+    close(fd);
+    return -1;
+}
+
+/* Convert a binary key of specified length into an ascii hex string equivalent,
+ * without the leading 0x and with null termination
+ */
+void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize,
+                              char *master_key_ascii)
+{
+  unsigned int i, a;
+  unsigned char nibble;
+
+  for (i=0, a=0; i<keysize; i++, a+=2) {
+    /* For each byte, write out two ascii hex digits */
+    nibble = (master_key[i] >> 4) & 0xf;
+    master_key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30);
+
+    nibble = master_key[i] & 0xf;
+    master_key_ascii[a+1] = nibble + (nibble > 9 ? 0x37 : 0x30);
+  }
+
+  /* Add the null termination */
+  master_key_ascii[a] = '\0';
+
+}
+
+static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key,
+                                     char *real_blk_name, const char *name, int fd,
+                                     char *extra_params)
+{
+  char buffer[DM_CRYPT_BUF_SIZE];
+  struct dm_ioctl *io;
+  struct dm_target_spec *tgt;
+  char *crypt_params;
+  char master_key_ascii[129]; /* Large enough to hold 512 bit key and null */
+  int i;
+
+  io = (struct dm_ioctl *) buffer;
+
+  /* Load the mapping table for this device */
+  tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  io->target_count = 1;
+  tgt->status = 0;
+  tgt->sector_start = 0;
+  tgt->length = crypt_ftr->fs_size;
+  strcpy(tgt->target_type, "crypt");
+
+  crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+  convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+  sprintf(crypt_params, "%s %s 0 %s 0 %s", crypt_ftr->crypto_type_name,
+          master_key_ascii, real_blk_name, extra_params);
+  crypt_params += strlen(crypt_params) + 1;
+  crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */
+  tgt->next = crypt_params - buffer;
+
+  for (i = 0; i < TABLE_LOAD_RETRIES; i++) {
+    if (! ioctl(fd, DM_TABLE_LOAD, io)) {
+      break;
+    }
+    usleep(500000);
+  }
+
+  if (i == TABLE_LOAD_RETRIES) {
+    /* We failed to load the table, return an error */
+    return -1;
+  } else {
+    return i + 1;
+  }
+}
+
+
+static int get_dm_crypt_version(int fd, const char *name,  int *version)
+{
+    char buffer[DM_CRYPT_BUF_SIZE];
+    struct dm_ioctl *io;
+    struct dm_target_versions *v;
+    int i;
+
+    io = (struct dm_ioctl *) buffer;
+
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+    if (ioctl(fd, DM_LIST_VERSIONS, io)) {
+        return -1;
+    }
+
+    /* Iterate over the returned versions, looking for name of "crypt".
+     * When found, get and return the version.
+     */
+    v = (struct dm_target_versions *) &buffer[sizeof(struct dm_ioctl)];
+    while (v->next) {
+        if (! strcmp(v->name, "crypt")) {
+            /* We found the crypt driver, return the version, and get out */
+            version[0] = v->version[0];
+            version[1] = v->version[1];
+            version[2] = v->version[2];
+            return 0;
+        }
+        v = (struct dm_target_versions *)(((char *)v) + v->next);
+    }
+
+    return -1;
+}
+
+static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key,
+                                    char *real_blk_name, char *crypto_blk_name, const char *name)
+{
+  char buffer[DM_CRYPT_BUF_SIZE];
+  char master_key_ascii[129]; /* Large enough to hold 512 bit key and null */
+  char *crypt_params;
+  struct dm_ioctl *io;
+  struct dm_target_spec *tgt;
+  unsigned int minor;
+  int fd;
+  int i;
+  int retval = -1;
+  int version[3];
+  char *extra_params;
+  int load_count;
+
+  if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) {
+    printf("Cannot open device-mapper\n");
+    goto errout;
+  }
+
+  io = (struct dm_ioctl *) buffer;
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  if (ioctl(fd, DM_DEV_CREATE, io)) {
+    printf("Cannot create dm-crypt device\n");
+    goto errout;
+  }
+
+  /* Get the device status, in particular, the name of it's device file */
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  if (ioctl(fd, DM_DEV_STATUS, io)) {
+    printf("Cannot retrieve dm-crypt device status\n");
+    goto errout;
+  }
+  minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+  snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor);
+
+  extra_params = "";
+  if (! get_dm_crypt_version(fd, name, version)) {
+      /* Support for allow_discards was added in version 1.11.0 */
+      if ((version[0] >= 2) ||
+          ((version[0] == 1) && (version[1] >= 11))) {
+          extra_params = "1 allow_discards";
+          printf("Enabling support for allow_discards in dmcrypt.\n");
+      }
+  }
+
+  load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name,
+                                         fd, extra_params);
+  if (load_count < 0) {
+      printf("Cannot load dm-crypt mapping table.\n");
+      goto errout;
+  } else if (load_count > 1) {
+      printf("Took %d tries to load dmcrypt table.\n", load_count);
+  }
+
+  /* Resume this device to activate it */
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+  if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+    printf("Cannot resume the dm-crypt device\n");
+    goto errout;
+  }
+
+  /* We made it here with no errors.  Woot! */
+  retval = 0;
+
+errout:
+  close(fd);   /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+  return retval;
+}
+
+static int delete_crypto_blk_dev(char *name)
+{
+  int fd;
+  char buffer[DM_CRYPT_BUF_SIZE];
+  struct dm_ioctl *io;
+  int retval = -1;
+
+  if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) {
+    printf("Cannot open device-mapper\n");
+    goto errout;
+  }
+
+  io = (struct dm_ioctl *) buffer;
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  if (ioctl(fd, DM_DEV_REMOVE, io)) {
+    printf("Cannot remove dm-crypt device\n");
+    goto errout;
+  }
+
+  /* We made it here with no errors.  Woot! */
+  retval = 0;
+
+errout:
+  close(fd);    /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+  return retval;
+
+}
+
+static void pbkdf2(char *passwd, unsigned char *salt, unsigned char *ikey, void *params) {
+    /* Turn the password into a key and IV that can decrypt the master key */
+    PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, SALT_LEN,
+                           HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey);
+}
+
+static void scrypt(char *passwd, unsigned char *salt, unsigned char *ikey, void *params) {
+    struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+    int N = 1 << ftr->N_factor;
+    int r = 1 << ftr->r_factor;
+    int p = 1 << ftr->p_factor;
+
+    /* Turn the password into a key and IV that can decrypt the master key */
+    crypto_scrypt((unsigned char *) passwd, strlen(passwd), salt, SALT_LEN, N, r, p, ikey,
+            KEY_LEN_BYTES + IV_LEN_BYTES);
+}
+
+static int encrypt_master_key(char *passwd, unsigned char *salt,
+                              unsigned char *decrypted_master_key,
+                              unsigned char *encrypted_master_key,
+                              struct crypt_mnt_ftr *crypt_ftr)
+{
+    unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
+    EVP_CIPHER_CTX e_ctx;
+    int encrypted_len, final_len;
+
+    /* Turn the password into a key and IV that can decrypt the master key */
+    get_device_scrypt_params(crypt_ftr);
+    scrypt(passwd, salt, ikey, crypt_ftr);
+
+    /* Initialize the decryption engine */
+    if (! EVP_EncryptInit(&e_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
+        printf("EVP_EncryptInit failed\n");
+        return -1;
+    }
+    EVP_CIPHER_CTX_set_padding(&e_ctx, 0); /* Turn off padding as our data is block aligned */
+
+    /* Encrypt the master key */
+    if (! EVP_EncryptUpdate(&e_ctx, encrypted_master_key, &encrypted_len,
+                              decrypted_master_key, KEY_LEN_BYTES)) {
+        printf("EVP_EncryptUpdate failed\n");
+        return -1;
+    }
+    if (! EVP_EncryptFinal(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) {
+        printf("EVP_EncryptFinal failed\n");
+        return -1;
+    }
+
+    if (encrypted_len + final_len != KEY_LEN_BYTES) {
+        printf("EVP_Encryption length check failed with %d, %d bytes\n", encrypted_len, final_len);
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+static int decrypt_master_key(char *passwd, unsigned char *salt,
+                              unsigned char *encrypted_master_key,
+                              unsigned char *decrypted_master_key,
+                              kdf_func kdf, void *kdf_params)
+{
+  unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
+  EVP_CIPHER_CTX d_ctx;
+  int decrypted_len, final_len;
+
+  /* Turn the password into a key and IV that can decrypt the master key */
+  kdf(passwd, salt, ikey, kdf_params);
+
+  /* Initialize the decryption engine */
+  if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
+    return -1;
+  }
+  EVP_CIPHER_CTX_set_padding(&d_ctx, 0); /* Turn off padding as our data is block aligned */
+  /* Decrypt the master key */
+  if (! EVP_DecryptUpdate(&d_ctx, decrypted_master_key, &decrypted_len,
+                            encrypted_master_key, KEY_LEN_BYTES)) {
+    return -1;
+  }
+  if (! EVP_DecryptFinal(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) {
+    return -1;
+  }
+
+  if (decrypted_len + final_len != KEY_LEN_BYTES) {
+    return -1;
+  } else {
+    return 0;
+  }
+}
+
+static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
+{
+    if (ftr->kdf_type == KDF_SCRYPT) {
+        *kdf = scrypt;
+        *kdf_params = ftr;
+    } else {
+        *kdf = pbkdf2;
+        *kdf_params = NULL;
+    }
+}
+
+static int decrypt_master_key_and_upgrade(char *passwd, unsigned char *decrypted_master_key,
+        struct crypt_mnt_ftr *crypt_ftr)
+{
+    kdf_func kdf;
+    void *kdf_params;
+    int ret;
+
+    get_kdf_func(crypt_ftr, &kdf, &kdf_params);
+    ret = decrypt_master_key(passwd, crypt_ftr->salt, crypt_ftr->master_key, decrypted_master_key, kdf,
+            kdf_params);
+    if (ret != 0) {
+        printf("failure decrypting master key");
+        return ret;
+    }
+
+    /*
+     * Upgrade if we're not using the latest KDF.
+     */
+    /*if (crypt_ftr->kdf_type != KDF_SCRYPT) {
+        crypt_ftr->kdf_type = KDF_SCRYPT;
+        encrypt_master_key(passwd, crypt_ftr->salt, decrypted_master_key, crypt_ftr->master_key,
+                crypt_ftr);
+        put_crypt_ftr_and_key(crypt_ftr);
+    }*/
+
+    return ret;
+}
+
+static int create_encrypted_random_key(char *passwd, unsigned char *master_key, unsigned char *salt,
+        struct crypt_mnt_ftr *crypt_ftr) {
+    int fd;
+    unsigned char key_buf[KEY_LEN_BYTES];
+    EVP_CIPHER_CTX e_ctx;
+    int encrypted_len, final_len;
+
+    /* Get some random bits for a key */
+    fd = open("/dev/urandom", O_RDONLY);
+    read(fd, key_buf, sizeof(key_buf));
+    read(fd, salt, SALT_LEN);
+    close(fd);
+
+    /* Now encrypt it with the password */
+    return encrypt_master_key(passwd, salt, key_buf, master_key, crypt_ftr);
+}
+
+static int wait_and_unmount(char *mountpoint)
+{
+    int i, rc;
+#define WAIT_UNMOUNT_COUNT 20
+
+    /*  Now umount the tmpfs filesystem */
+    for (i=0; i<WAIT_UNMOUNT_COUNT; i++) {
+        if (umount(mountpoint)) {
+            if (errno == EINVAL) {
+                /* EINVAL is returned if the directory is not a mountpoint,
+                 * i.e. there is no filesystem mounted there.  So just get out.
+                 */
+                break;
+            }
+            sleep(1);
+            i++;
+        } else {
+          break;
+        }
+    }
+
+    if (i < WAIT_UNMOUNT_COUNT) {
+      printf("unmounting %s succeeded\n", mountpoint);
+      rc = 0;
+    } else {
+      printf("unmounting %s failed\n", mountpoint);
+      rc = -1;
+    }
+
+    return rc;
+}
+
+#define DATA_PREP_TIMEOUT 200
+static int prep_data_fs(void)
+{
+    int i;
+
+    /* Do the prep of the /data filesystem */
+    property_set("vold.post_fs_data_done", "0");
+    property_set("vold.decrypt", "trigger_post_fs_data");
+    printf("Just triggered post_fs_data\n");
+
+    /* Wait a max of 50 seconds, hopefully it takes much less */
+    for (i=0; i<DATA_PREP_TIMEOUT; i++) {
+        char p[PROPERTY_VALUE_MAX];
+
+        property_get("vold.post_fs_data_done", p, "0");
+        if (*p == '1') {
+            break;
+        } else {
+            usleep(250000);
+        }
+    }
+    if (i == DATA_PREP_TIMEOUT) {
+        /* Ugh, we failed to prep /data in time.  Bail. */
+        printf("post_fs_data timed out!\n");
+        return -1;
+    } else {
+        printf("post_fs_data done\n");
+        return 0;
+    }
+}
+
+int cryptfs_restart(void)
+{
+    char fs_type[32];
+    char real_blkdev[MAXPATHLEN];
+    char crypto_blkdev[MAXPATHLEN];
+    char fs_options[256];
+    unsigned long mnt_flags;
+    struct stat statbuf;
+    int rc = -1, i;
+    static int restart_successful = 0;
+
+    /* Validate that it's OK to call this routine */
+    if (! master_key_saved) {
+        printf("Encrypted filesystem not validated, aborting");
+        return -1;
+    }
+
+    if (restart_successful) {
+        printf("System already restarted with encrypted disk, aborting");
+        return -1;
+    }
+
+    /* Here is where we shut down the framework.  The init scripts
+     * start all services in one of three classes: core, main or late_start.
+     * On boot, we start core and main.  Now, we stop main, but not core,
+     * as core includes vold and a few other really important things that
+     * we need to keep running.  Once main has stopped, we should be able
+     * to umount the tmpfs /data, then mount the encrypted /data.
+     * We then restart the class main, and also the class late_start.
+     * At the moment, I've only put a few things in late_start that I know
+     * are not needed to bring up the framework, and that also cause problems
+     * with unmounting the tmpfs /data, but I hope to add add more services
+     * to the late_start class as we optimize this to decrease the delay
+     * till the user is asked for the password to the filesystem.
+     */
+
+    /* The init files are setup to stop the class main when vold.decrypt is
+     * set to trigger_reset_main.
+     */
+    property_set("vold.decrypt", "trigger_reset_main");
+    printf("Just asked init to shut down class main\n");
+
+    /* Ugh, shutting down the framework is not synchronous, so until it
+     * can be fixed, this horrible hack will wait a moment for it all to
+     * shut down before proceeding.  Without it, some devices cannot
+     * restart the graphics services.
+     */
+    sleep(2);
+
+    /* Now that the framework is shutdown, we should be able to umount()
+     * the tmpfs filesystem, and mount the real one.
+     */
+
+    property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, "");
+    if (strlen(crypto_blkdev) == 0) {
+        printf("fs_crypto_blkdev not set\n");
+        return -1;
+    }
+
+    if (! (rc = wait_and_unmount(DATA_MNT_POINT)) ) {
+        /* If that succeeded, then mount the decrypted filesystem */
+        fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, 0);
+
+        property_set("vold.decrypt", "trigger_load_persist_props");
+        /* Create necessary paths on /data */
+        if (prep_data_fs()) {
+            return -1;
+        }
+
+        /* startup service classes main and late_start */
+        property_set("vold.decrypt", "trigger_restart_framework");
+        printf("Just triggered restart_framework\n");
+
+        /* Give it a few moments to get started */
+        sleep(1);
+    }
+
+    if (rc == 0) {
+        restart_successful = 1;
+    }
+
+    return rc;
+}
+
+static int do_crypto_complete(char *mount_point)
+{
+  struct crypt_mnt_ftr crypt_ftr;
+  char encrypted_state[PROPERTY_VALUE_MAX];
+  char key_loc[PROPERTY_VALUE_MAX];
+
+  property_get("ro.crypto.state", encrypted_state, "");
+  if (strcmp(encrypted_state, "encrypted") ) {
+    printf("not running with encryption, aborting");
+    return 1;
+  }
+
+  if (get_crypt_ftr_and_key(&crypt_ftr)) {
+    fs_mgr_get_crypt_info(fstab, key_loc, 0, sizeof(key_loc));
+
+    /*
+     * Only report this error if key_loc is a file and it exists.
+     * If the device was never encrypted, and /data is not mountable for
+     * some reason, returning 1 should prevent the UI from presenting the
+     * a "enter password" screen, or worse, a "press button to wipe the
+     * device" screen.
+     */
+    if ((key_loc[0] == '/') && (access("key_loc", F_OK) == -1)) {
+      printf("master key file does not exist, aborting");
+      return 1;
+    } else {
+      printf("Error getting crypt footer and key\n");
+      return -1;
+    }
+  }
+
+  if (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS) {
+    printf("Encryption process didn't finish successfully\n");
+    return -2;  /* -2 is the clue to the UI that there is no usable data on the disk,
+                 * and give the user an option to wipe the disk */
+  }
+
+  /* We passed the test! We shall diminish, and return to the west */
+  return 0;
+}
+
+static int test_mount_encrypted_fs(char *passwd, char *mount_point, char *label)
+{
+  struct crypt_mnt_ftr crypt_ftr;
+  /* Allocate enough space for a 256 bit key, but we may use less */
+  unsigned char decrypted_master_key[32];
+  char crypto_blkdev[MAXPATHLEN];
+  char real_blkdev[MAXPATHLEN];
+  char tmp_mount_point[64];
+  unsigned int orig_failed_decrypt_count;
+  char encrypted_state[PROPERTY_VALUE_MAX];
+  int rc;
+  kdf_func kdf;
+  void *kdf_params;
+
+  property_get("ro.crypto.state", encrypted_state, "");
+  if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
+    printf("encrypted fs already validated or not running with encryption, aborting");
+    return -1;
+  }
+
+  fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));
+
+  if (get_crypt_ftr_and_key(&crypt_ftr)) {
+    printf("Error getting crypt footer and key\n");
+    return -1;
+  }
+
+  printf("crypt_ftr->fs_size = %lld\n", crypt_ftr.fs_size);
+  orig_failed_decrypt_count = crypt_ftr.failed_decrypt_count;
+
+  if (! (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
+    decrypt_master_key_and_upgrade(passwd, decrypted_master_key, &crypt_ftr);
+  }
+
+  if (create_crypto_blk_dev(&crypt_ftr, decrypted_master_key,
+                               real_blkdev, crypto_blkdev, label)) {
+    printf("Error creating decrypted block device\n");
+    return -1;
+  }
+
+  /* If init detects an encrypted filesystem, it writes a file for each such
+   * encrypted fs into the tmpfs /data filesystem, and then the framework finds those
+   * files and passes that data to me */
+  /* Create a tmp mount point to try mounting the decryptd fs
+   * Since we're here, the mount_point should be a tmpfs filesystem, so make
+   * a directory in it to test mount the decrypted filesystem.
+   */
+  sprintf(tmp_mount_point, "%s/tmp_mnt", mount_point);
+  mkdir(tmp_mount_point, 0755);
+  if (fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, tmp_mount_point)) {
+    printf("Error temp mounting decrypted block device\n");
+    delete_crypto_blk_dev(label);
+    crypt_ftr.failed_decrypt_count++;
+  } else {
+    /* Success, so just umount and we'll mount it properly when we restart
+     * the framework.
+     */
+    umount(tmp_mount_point);
+    crypt_ftr.failed_decrypt_count  = 0;
+  }
+
+  if (orig_failed_decrypt_count != crypt_ftr.failed_decrypt_count) {
+    put_crypt_ftr_and_key(&crypt_ftr);
+  }
+
+  if (crypt_ftr.failed_decrypt_count) {
+    /* We failed to mount the device, so return an error */
+    rc = crypt_ftr.failed_decrypt_count;
+
+  } else {
+    /* Woot!  Success!  Save the name of the crypto block device
+     * so we can mount it when restarting the framework.
+     */
+    property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+
+    /* Also save a the master key so we can reencrypted the key
+     * the key when we want to change the password on it.
+     */
+    memcpy(saved_master_key, decrypted_master_key, KEY_LEN_BYTES);
+    saved_mount_point = strdup(mount_point);
+    master_key_saved = 1;
+    rc = 0;
+  }
+
+  return rc;
+}
+
+/* Called by vold when it wants to undo the crypto mapping of a volume it
+ * manages.  This is usually in response to a factory reset, when we want
+ * to undo the crypto mapping so the volume is formatted in the clear.
+ */
+int cryptfs_revert_volume(const char *label)
+{
+    return delete_crypto_blk_dev((char *)label);
+}
+
+/*
+ * Called by vold when it's asked to mount an encrypted, nonremovable volume.
+ * Setup a dm-crypt mapping, use the saved master key from
+ * setting up the /data mapping, and return the new device path.
+ */
+int cryptfs_setup_volume(const char *label, int major, int minor,
+                         char *crypto_sys_path, unsigned int max_path,
+                         int *new_major, int *new_minor)
+{
+    char real_blkdev[MAXPATHLEN], crypto_blkdev[MAXPATHLEN];
+    struct crypt_mnt_ftr sd_crypt_ftr;
+    struct stat statbuf;
+    int nr_sec, fd;
+
+    sprintf(real_blkdev, "/dev/block/vold/%d:%d", major, minor);
+
+    get_crypt_ftr_and_key(&sd_crypt_ftr);
+
+    /* Update the fs_size field to be the size of the volume */
+    fd = open(real_blkdev, O_RDONLY);
+    nr_sec = get_blkdev_size(fd);
+    close(fd);
+    if (nr_sec == 0) {
+        printf("Cannot get size of volume %s\n", real_blkdev);
+        return -1;
+    }
+
+    sd_crypt_ftr.fs_size = nr_sec;
+    create_crypto_blk_dev(&sd_crypt_ftr, saved_master_key, real_blkdev, 
+                          crypto_blkdev, label);
+
+    stat(crypto_blkdev, &statbuf);
+    *new_major = MAJOR(statbuf.st_rdev);
+    *new_minor = MINOR(statbuf.st_rdev);
+
+    /* Create path to sys entry for this block device */
+    snprintf(crypto_sys_path, max_path, "/devices/virtual/block/%s", strrchr(crypto_blkdev, '/')+1);
+
+    return 0;
+}
+
+int cryptfs_crypto_complete(void)
+{
+  return do_crypto_complete("/data");
+}
+
+#define FSTAB_PREFIX "/fstab."
+
+int cryptfs_check_footer(void)
+{
+    int rc = -1;
+    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+    char propbuf[PROPERTY_VALUE_MAX];
+    struct crypt_mnt_ftr crypt_ftr;
+
+    property_get("ro.hardware", propbuf, "");
+    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
+
+    fstab = fs_mgr_read_fstab(fstab_filename);
+    if (!fstab) {
+        printf("failed to open %s\n", fstab_filename);
+        return -1;
+    }
+
+    rc = get_crypt_ftr_and_key(&crypt_ftr);
+
+    return rc;
+}
+
+int cryptfs_check_passwd(char *passwd)
+{
+    int rc = -1;
+    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
+    char propbuf[PROPERTY_VALUE_MAX];
+
+    property_get("ro.hardware", propbuf, "");
+    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
+
+    fstab = fs_mgr_read_fstab(fstab_filename);
+    if (!fstab) {
+        printf("failed to open %s\n", fstab_filename);
+        return -1;
+    }
+
+    rc = test_mount_encrypted_fs(passwd, DATA_MNT_POINT, "userdata");
+
+    return rc;
+}
+
+int cryptfs_verify_passwd(char *passwd)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    /* Allocate enough space for a 256 bit key, but we may use less */
+    unsigned char decrypted_master_key[32];
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    int rc;
+
+    property_get("ro.crypto.state", encrypted_state, "");
+    if (strcmp(encrypted_state, "encrypted") ) {
+        printf("device not encrypted, aborting");
+        return -2;
+    }
+
+    if (!master_key_saved) {
+        printf("encrypted fs not yet mounted, aborting");
+        return -1;
+    }
+
+    if (!saved_mount_point) {
+        printf("encrypted fs failed to save mount point, aborting");
+        return -1;
+    }
+
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+        printf("Error getting crypt footer and key\n");
+        return -1;
+    }
+
+    if (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) {
+        /* If the device has no password, then just say the password is valid */
+        rc = 0;
+    } else {
+        decrypt_master_key_and_upgrade(passwd, decrypted_master_key, &crypt_ftr);
+        if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
+            /* They match, the password is correct */
+            rc = 0;
+        } else {
+            /* If incorrect, sleep for a bit to prevent dictionary attacks */
+            sleep(1);
+            rc = 1;
+        }
+    }
+
+    return rc;
+}
+
+/* Initialize a crypt_mnt_ftr structure.  The keysize is
+ * defaulted to 16 bytes, and the filesystem size to 0.
+ * Presumably, at a minimum, the caller will update the
+ * filesystem size and crypto_type_name after calling this function.
+ */
+static void cryptfs_init_crypt_mnt_ftr(struct crypt_mnt_ftr *ftr)
+{
+    off64_t off;
+
+    memset(ftr, 0, sizeof(struct crypt_mnt_ftr));
+    ftr->magic = CRYPT_MNT_MAGIC;
+    ftr->major_version = CURRENT_MAJOR_VERSION;
+    ftr->minor_version = CURRENT_MINOR_VERSION;
+    ftr->ftr_size = sizeof(struct crypt_mnt_ftr);
+    ftr->keysize = KEY_LEN_BYTES;
+
+    ftr->kdf_type = KDF_SCRYPT;
+    get_device_scrypt_params(ftr);
+
+    ftr->persist_data_size = CRYPT_PERSIST_DATA_SIZE;
+    if (get_crypt_ftr_info(NULL, &off) == 0) {
+        ftr->persist_data_offset[0] = off + CRYPT_FOOTER_TO_PERSIST_OFFSET;
+        ftr->persist_data_offset[1] = off + CRYPT_FOOTER_TO_PERSIST_OFFSET +
+                                    ftr->persist_data_size;
+    }
+}
+
+static int cryptfs_enable_wipe(char *crypto_blkdev, off64_t size, int type)
+{
+    return -1;
+}
+
+#define CRYPT_INPLACE_BUFSIZE 4096
+#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / 512)
+static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_t size,
+                                  off64_t *size_already_done, off64_t tot_size)
+{
+    int realfd, cryptofd;
+    char *buf[CRYPT_INPLACE_BUFSIZE];
+    int rc = -1;
+    off64_t numblocks, i, remainder;
+    off64_t one_pct, cur_pct, new_pct;
+    off64_t blocks_already_done, tot_numblocks;
+
+    if ( (realfd = open(real_blkdev, O_RDONLY)) < 0) { 
+        printf("Error opening real_blkdev %s for inplace encrypt\n", real_blkdev);
+        return -1;
+    }
+
+    if ( (cryptofd = open(crypto_blkdev, O_WRONLY)) < 0) { 
+        printf("Error opening crypto_blkdev %s for inplace encrypt\n", crypto_blkdev);
+        close(realfd);
+        return -1;
+    }
+
+    /* This is pretty much a simple loop of reading 4K, and writing 4K.
+     * The size passed in is the number of 512 byte sectors in the filesystem.
+     * So compute the number of whole 4K blocks we should read/write,
+     * and the remainder.
+     */
+    numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+    remainder = size % CRYPT_SECTORS_PER_BUFSIZE;
+    tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+    blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+    printf("Encrypting filesystem in place...");
+
+    one_pct = tot_numblocks / 100;
+    cur_pct = 0;
+    /* process the majority of the filesystem in blocks */
+    for (i=0; i<numblocks; i++) {
+        new_pct = (i + blocks_already_done) / one_pct;
+        if (new_pct > cur_pct) {
+            char buf[8];
+
+            cur_pct = new_pct;
+            snprintf(buf, sizeof(buf), "%lld", cur_pct);
+            property_set("vold.encrypt_progress", buf);
+        }
+        if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
+            printf("Error reading real_blkdev %s for inplace encrypt\n", crypto_blkdev);
+            goto errout;
+        }
+        if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
+            printf("Error writing crypto_blkdev %s for inplace encrypt\n", crypto_blkdev);
+            goto errout;
+        }
+    }
+
+    /* Do any remaining sectors */
+    for (i=0; i<remainder; i++) {
+        if (unix_read(realfd, buf, 512) <= 0) {
+            printf("Error reading rival sectors from real_blkdev %s for inplace encrypt\n", crypto_blkdev);
+            goto errout;
+        }
+        if (unix_write(cryptofd, buf, 512) <= 0) {
+            printf("Error writing final sectors to crypto_blkdev %s for inplace encrypt\n", crypto_blkdev);
+            goto errout;
+        }
+    }
+
+    *size_already_done += size;
+    rc = 0;
+
+errout:
+    close(realfd);
+    close(cryptofd);
+
+    return rc;
+}
+
+#define CRYPTO_ENABLE_WIPE 1
+#define CRYPTO_ENABLE_INPLACE 2
+
+#define FRAMEWORK_BOOT_WAIT 60
+
+static inline int should_encrypt(struct volume_info *volume)
+{
+    return (volume->flags & (VOL_ENCRYPTABLE | VOL_NONREMOVABLE)) == 
+            (VOL_ENCRYPTABLE | VOL_NONREMOVABLE);
+}
+
+int cryptfs_enable(char *howarg, char *passwd)
+{
+    return -1;
+}
+
+int cryptfs_changepw(char *newpw)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    unsigned char decrypted_master_key[KEY_LEN_BYTES];
+
+    /* This is only allowed after we've successfully decrypted the master key */
+    if (! master_key_saved) {
+        printf("Key not saved, aborting");
+        return -1;
+    }
+
+    /* get key */
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+      printf("Error getting crypt footer and key");
+      return -1;
+    }
+
+    encrypt_master_key(newpw, crypt_ftr.salt, saved_master_key, crypt_ftr.master_key, &crypt_ftr);
+
+    /* save the key */
+    put_crypt_ftr_and_key(&crypt_ftr);
+
+    return 0;
+}
+
+static int persist_get_key(char *fieldname, char *value)
+{
+    unsigned int i;
+
+    if (persist_data == NULL) {
+        return -1;
+    }
+    for (i = 0; i < persist_data->persist_valid_entries; i++) {
+        if (!strncmp(persist_data->persist_entry[i].key, fieldname, PROPERTY_KEY_MAX)) {
+            /* We found it! */
+            strlcpy(value, persist_data->persist_entry[i].val, PROPERTY_VALUE_MAX);
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+static int persist_set_key(char *fieldname, char *value, int encrypted)
+{
+    unsigned int i;
+    unsigned int num;
+    struct crypt_mnt_ftr crypt_ftr;
+    unsigned int max_persistent_entries;
+    unsigned int dsize;
+
+    if (persist_data == NULL) {
+        return -1;
+    }
+
+    /* If encrypted, use the values from the crypt_ftr, otherwise
+     * use the values for the current spec.
+     */
+    if (encrypted) {
+        if(get_crypt_ftr_and_key(&crypt_ftr)) {
+            return -1;
+        }
+        dsize = crypt_ftr.persist_data_size;
+    } else {
+        dsize = CRYPT_PERSIST_DATA_SIZE;
+    }
+    max_persistent_entries = (dsize - sizeof(struct crypt_persist_data)) /
+                             sizeof(struct crypt_persist_entry);
+
+    num = persist_data->persist_valid_entries;
+
+    for (i = 0; i < num; i++) {
+        if (!strncmp(persist_data->persist_entry[i].key, fieldname, PROPERTY_KEY_MAX)) {
+            /* We found an existing entry, update it! */
+            memset(persist_data->persist_entry[i].val, 0, PROPERTY_VALUE_MAX);
+            strlcpy(persist_data->persist_entry[i].val, value, PROPERTY_VALUE_MAX);
+            return 0;
+        }
+    }
+
+    /* We didn't find it, add it to the end, if there is room */
+    if (persist_data->persist_valid_entries < max_persistent_entries) {
+        memset(&persist_data->persist_entry[num], 0, sizeof(struct crypt_persist_entry));
+        strlcpy(persist_data->persist_entry[num].key, fieldname, PROPERTY_KEY_MAX);
+        strlcpy(persist_data->persist_entry[num].val, value, PROPERTY_VALUE_MAX);
+        persist_data->persist_valid_entries++;
+        return 0;
+    }
+
+    return -1;
+}
+
+/* Return the value of the specified field. */
+int cryptfs_getfield(char *fieldname, char *value, int len)
+{
+    char temp_value[PROPERTY_VALUE_MAX];
+    char real_blkdev[MAXPATHLEN];
+    /* 0 is success, 1 is not encrypted,
+     * -1 is value not set, -2 is any other error
+     */
+    int rc = -2;
+
+    if (persist_data == NULL) {
+        load_persistent_data();
+        if (persist_data == NULL) {
+            printf("Getfield error, cannot load persistent data");
+            goto out;
+        }
+    }
+
+    if (!persist_get_key(fieldname, temp_value)) {
+        /* We found it, copy it to the caller's buffer and return */
+        strlcpy(value, temp_value, len);
+        rc = 0;
+    } else {
+        /* Sadness, it's not there.  Return the error */
+        rc = -1;
+    }
+
+out:
+    return rc;
+}
+
+/* Set the value of the specified field. */
+int cryptfs_setfield(char *fieldname, char *value)
+{
+    struct crypt_persist_data stored_pdata;
+    struct crypt_persist_data *pdata_p;
+    struct crypt_mnt_ftr crypt_ftr;
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    /* 0 is success, -1 is an error */
+    int rc = -1;
+    int encrypted = 0;
+
+    if (persist_data == NULL) {
+        load_persistent_data();
+        if (persist_data == NULL) {
+            printf("Setfield error, cannot load persistent data");
+            goto out;
+        }
+    }
+
+    property_get("ro.crypto.state", encrypted_state, "");
+    if (!strcmp(encrypted_state, "encrypted") ) {
+        encrypted = 1;
+    }
+
+    if (persist_set_key(fieldname, value, encrypted)) {
+        goto out;
+    }
+
+    /* If we are running encrypted, save the persistent data now */
+    if (encrypted) {
+        if (save_persistent_data()) {
+            printf("Setfield error, cannot save persistent data");
+            goto out;
+        }
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
diff --git a/crypto/jb/cryptfs.h b/crypto/jb/cryptfs.h
new file mode 100644
index 0000000..d815814
--- /dev/null
+++ b/crypto/jb/cryptfs.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This structure starts 16,384 bytes before the end of a hardware
+ * partition that is encrypted, or in a separate partition.  It's location
+ * is specified by a property set in init.<device>.rc.
+ * The structure allocates 48 bytes for a key, but the real key size is
+ * specified in the struct.  Currently, the code is hardcoded to use 128
+ * bit keys.
+ * The fields after salt are only valid in rev 1.1 and later stuctures.
+ * Obviously, the filesystem does not include the last 16 kbytes
+ * of the partition if the crypt_mnt_ftr lives at the end of the
+ * partition.
+ */
+
+#include <cutils/properties.h>
+
+/* The current cryptfs version */
+#define CURRENT_MAJOR_VERSION 1
+#define CURRENT_MINOR_VERSION 2
+
+#define CRYPT_FOOTER_OFFSET 0x4000
+#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
+#define CRYPT_PERSIST_DATA_SIZE 0x1000
+
+#define MAX_CRYPTO_TYPE_NAME_LEN 64
+
+#define MAX_KEY_LEN 48
+#define SALT_LEN 16
+
+/* definitions of flags in the structure below */
+#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
+#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Set when starting encryption,
+                                          * clear when done before rebooting */
+
+#define CRYPT_MNT_MAGIC 0xD0B5B1C4
+#define PERSIST_DATA_MAGIC 0xE950CD44
+
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS { 15, 3, 1 }
+
+/* Key Derivation Function algorithms */
+#define KDF_PBKDF2 1
+#define KDF_SCRYPT 2
+
+#define __le32 unsigned int
+#define __le16 unsigned short int
+#define __le8  unsigned char
+
+struct crypt_mnt_ftr {
+  __le32 magic;		/* See above */
+  __le16 major_version;
+  __le16 minor_version;
+  __le32 ftr_size; 	/* in bytes, not including key following */
+  __le32 flags;		/* See above */
+  __le32 keysize;	/* in bytes */
+  __le32 spare1;	/* ignored */
+  __le64 fs_size;	/* Size of the encrypted fs, in 512 byte sectors */
+  __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
+				  mount, set to 0 on successful mount */
+  unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
+							       needed to decrypt this
+							       partition, null terminated */
+  __le32 spare2;        /* ignored */
+  unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */
+  unsigned char salt[SALT_LEN];   /* The salt used for this encryption */
+  __le64 persist_data_offset[2];  /* Absolute offset to both copies of crypt_persist_data
+                                   * on device with that info, either the footer of the
+                                   * real_blkdevice or the metadata partition. */
+
+  __le32 persist_data_size;       /* The number of bytes allocated to each copy of the
+                                   * persistent data table*/
+
+  __le8  kdf_type; /* The key derivation function used. */
+
+  /* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
+  __le8  N_factor; /* (1 << N) */
+  __le8  r_factor; /* (1 << r) */
+  __le8  p_factor; /* (1 << p) */
+};
+
+/* Persistant data that should be available before decryption.
+ * Things like airplane mode, locale and timezone are kept
+ * here and can be retrieved by the CryptKeeper UI to properly
+ * configure the phone before asking for the password
+ * This is only valid if the major and minor version above
+ * is set to 1.1 or higher.
+ *
+ * This is a 4K structure.  There are 2 copies, and the code alternates
+ * writing one and then clearing the previous one.  The reading
+ * code reads the first valid copy it finds, based on the magic number.
+ * The absolute offset to the first of the two copies is kept in rev 1.1
+ * and higher crypt_mnt_ftr structures.
+ */
+struct crypt_persist_entry {
+  char key[PROPERTY_KEY_MAX];
+  char val[PROPERTY_VALUE_MAX];
+};
+
+/* Should be exactly 4K in size */
+struct crypt_persist_data {
+  __le32 persist_magic;
+  __le32 persist_valid_entries;
+  __le32 persist_spare[30];
+  struct crypt_persist_entry persist_entry[0];
+};
+
+struct volume_info {
+   unsigned int size;
+   unsigned int flags;
+   struct crypt_mnt_ftr crypt_ftr;
+   char mnt_point[256];
+   char blk_dev[256];
+   char crypto_blkdev[256];
+   char label[256];
+};
+#define VOL_NONREMOVABLE   0x1
+#define VOL_ENCRYPTABLE    0x2
+#define VOL_PRIMARY        0x4
+#define VOL_PROVIDES_ASEC  0x8
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  typedef void (*kdf_func)(char *passwd, unsigned char *salt, unsigned char *ikey, void *params);
+
+  int cryptfs_crypto_complete(void);
+  int cryptfs_check_footer(void);
+  int cryptfs_check_passwd(char *pw);
+  int cryptfs_verify_passwd(char *newpw);
+  int cryptfs_restart(void);
+  int cryptfs_enable(char *flag, char *passwd);
+  int cryptfs_changepw(char *newpw);
+  int cryptfs_setup_volume(const char *label, int major, int minor,
+                           char *crypto_dev_path, unsigned int max_pathlen,
+                           int *new_major, int *new_minor);
+  int cryptfs_revert_volume(const char *label);
+  int cryptfs_getfield(char *fieldname, char *value, int len);
+  int cryptfs_setfield(char *fieldname, char *value);
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/crypto/libcrypt_samsung/Android.mk b/crypto/libcrypt_samsung/Android.mk
new file mode 100644
index 0000000..6e0e869
--- /dev/null
+++ b/crypto/libcrypt_samsung/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcrypt_samsung
+LOCAL_SRC_FILES := $(LOCAL_MODULE).c
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_STATIC_LIBRARY)
+
+endif
diff --git a/crypto/libcrypt_samsung/include/libcrypt_samsung.h b/crypto/libcrypt_samsung/include/libcrypt_samsung.h
new file mode 100644
index 0000000..2fb6f2c
--- /dev/null
+++ b/crypto/libcrypt_samsung/include/libcrypt_samsung.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2013 a3955269 all rights reversed, no rights reserved.
+ */
+
+#ifndef __LIBCRYPT_SAMSUNG_H__
+#define __LIBCRYPT_SAMSUNG_H__
+
+//////////////////////////////////////////////////////////////////////////////
+// Name                           Address  Ordinal
+// ----                           -------  -------
+// SECKM_AES_set_encrypt_key      000010D8
+// SECKM_AES_set_decrypt_key      00001464
+// SECKM_AES_encrypt              00001600
+// SECKM_AES_decrypt              00001A10
+// SECKM_aes_selftest             00001D94
+// verify_EDK                     00001F7C
+// encrypt_dek                    00001FC8
+// decrypt_EDK                    000020D4
+// change_EDK                     0000218C
+// generate_dek_salt              000022A4
+// create_EDK                     000023A0
+// free_DEK                       000024DC
+// alloc_DEK                      000024F4
+// SECKM_HMAC_SHA256              00002500
+// SECKM_HMAC_SHA256_selftest     00002690
+// pbkdf                          000026FC
+// pbkdf_selftest                 00002898
+// _SECKM_PRNG_get16              00002958
+// SECKM_PRNG_get16               00002C48
+// _SECKM_PRNG_init               00002C54
+// SECKM_PRNG_selftest            00002F38
+// SECKM_PRNG_set_seed            00002FF0
+// SECKM_PRNG_init                00002FF8
+// SECKM_SHA256_Transform         00003004
+// SECKM_SHA256_Final             000031D8
+// SECKM_SHA256_Update            00003330
+// SECKM_SHA256_Init              000033FC
+// SECKM_SHA2_selftest            00003430
+// integrity_check                00003488
+// update_system_property         00003580
+// setsec_km_fips_status          00003630
+// _all_checks                    00003684
+// get_fips_status                000036D4
+
+
+// EDK Payload is defined as:
+//    Encrypted DEK – EDK itself
+//    HMAC of EDK (32 bytes ???)
+//    Salt         16 bytes
+
+#define EDK_MAGIC   0x1001e4b1
+
+#pragma pack(1)
+
+typedef struct {
+    unsigned int magic;     // EDK_MAGIC
+    unsigned int flags;     // 2
+    unsigned int zeros[6];
+} dek_t;
+
+typedef struct {
+    unsigned char data[32];
+} edk_t;
+
+
+// size 0x70 -> 112
+typedef struct {
+    dek_t dek;
+    edk_t edk;
+    unsigned char hmac[32];
+    unsigned char salt[16];
+} edk_payload_t;
+
+#pragma pack()
+
+//////////////////////////////////////////////////////////////////////////////
+
+int decrypt_EDK(
+        dek_t *dek, const edk_payload_t *edk, /*const*/ char *passwd);
+
+typedef int (*decrypt_EDK_t)(
+        dek_t *dek, const edk_payload_t *edk, /*const*/ char *passwd);
+
+
+int verify_EDK(const edk_payload_t *edk, const char *passwd);
+//change_EDK()
+//create_EDK()
+
+// internally just mallocs 32 bytes
+dek_t *alloc_DEK();
+void free_DEK(dek_t *dek);
+//encrypt_dek()
+//generate_dek_salt()
+
+//pbkdf(_buf_, "passwordPASSWORDpassword", 0x18, "saltSALTsaltSALTsaltSALTsaltSALTsalt", 0x24, 0x1000, 0x140);
+int pbkdf(
+        void *buf, void *pw, int pwlen, void *salt, int saltlen, int hashcnt,
+        int keylen);
+
+// getprop("rw.km_fips_status")
+// "ready, undefined, error_selftest, error_integrity"
+int get_fips_status();
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// libsec_ecryptfs.so (internally uses libkeyutils.so)
+//
+// Name                   Address  Ordinal
+// ----                   -------  -------
+// unmount_ecryptfs_drive 00000A78
+// mount_ecryptfs_drive   00000B48
+// fips_read_edk          00000E44
+// fips_save_edk          00000EA4
+// fips_create_edk        00000F20
+// fips_change_password   00001018
+// fips_delete_edk        00001124
+//
+
+// might depend on /data beeing mounted for reading /data/system/edk_p_sd
+//
+// filter
+// 0: building options without file encryption filtering.
+// 1: building options with media files filtering.
+// 2: building options with all new files filtering.
+
+int mount_ecryptfs_drive(
+        const char *passwd, const char *source, const char *target, int filter);
+
+typedef int (*mount_ecryptfs_drive_t)(
+        const char *passwd, const char *source, const char *target, int filter);
+
+// calls 2 times umount2(source, MNT_EXPIRE)
+int unmount_ecryptfs_drive(
+        const char *source);
+
+typedef int (*unmount_ecryptfs_drive_t)(
+        const char *source);
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif // #ifndef __LIBCRYPT_SAMSUNG_H__
+
+//////////////////////////////////////////////////////////////////////////////
+
diff --git a/crypto/libcrypt_samsung/libcrypt_samsung.c b/crypto/libcrypt_samsung/libcrypt_samsung.c
new file mode 100644
index 0000000..cd3a178
--- /dev/null
+++ b/crypto/libcrypt_samsung/libcrypt_samsung.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013 a3955269 all rights reversed, no rights reserved.
+ */
+
+//////////////////////////////////////////////////////////////////////////////
+
+#include <string.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include "include/libcrypt_samsung.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+int decrypt_EDK(
+        dek_t *dek, const edk_payload_t *edk, /*const*/ char *passwd)
+{
+    void *lib = dlopen("libsec_km.so", RTLD_LAZY);
+
+    if(!lib)
+        return -100;
+
+    int r = -101;
+    decrypt_EDK_t sym = (decrypt_EDK_t)dlsym(lib, "decrypt_EDK");
+    if(sym)
+        r = sym(dek, edk, passwd);
+
+    dlclose(lib);
+
+    return r;
+}
+
+int mount_ecryptfs_drive(
+        const char *passwd, const char *source, const char *target, int filter)
+{
+    void *lib = dlopen("libsec_ecryptfs.so", RTLD_LAZY);
+    if(!lib)
+        return -100;
+
+    int r = -101;
+    mount_ecryptfs_drive_t sym = (mount_ecryptfs_drive_t)dlsym(lib, "mount_ecryptfs_drive");
+    if(sym)
+        r = sym(passwd, source, target, filter);
+
+    dlclose(lib);
+
+    return r;
+}
+
+int unmount_ecryptfs_drive(
+        const char *source)
+{
+    void *lib = dlopen("libsec_ecryptfs.so", RTLD_LAZY);
+    if(!lib)
+        return -100;
+
+    int r = -101;
+    unmount_ecryptfs_drive_t sym = (unmount_ecryptfs_drive_t)dlsym(lib, "unmount_ecryptfs_drive");
+    if(sym)
+        r = sym(source);
+
+    dlclose(lib);
+
+    return r;
+}
\ No newline at end of file
diff --git a/crypto/logwrapper/Android.mk b/crypto/logwrapper/Android.mk
new file mode 100644
index 0000000..01b6193
--- /dev/null
+++ b/crypto/logwrapper/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# ========================================================
+# Static library
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := liblogwraptwrp
+LOCAL_SRC_FILES := logwrap.c
+LOCAL_SHARED_LIBRARIES := libcutils liblog
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+# ========================================================
+# Shared library
+# ========================================================
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := liblogwrap
+#LOCAL_SHARED_LIBRARIES := libcutils liblog
+#LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap
+#LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+#LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+#include $(BUILD_SHARED_LIBRARY)
+
+# ========================================================
+# Executable
+# ========================================================
+#include $(CLEAR_VARS)
+#LOCAL_SRC_FILES:= logwrapper.c
+#LOCAL_MODULE := logwrapper
+#LOCAL_STATIC_LIBRARIES := liblog liblogwrap libcutils
+#include $(BUILD_EXECUTABLE)
diff --git a/crypto/logwrapper/NOTICE b/crypto/logwrapper/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/crypto/logwrapper/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/crypto/logwrapper/include/logwrap/logwrap.h b/crypto/logwrapper/include/logwrap/logwrap.h
new file mode 100644
index 0000000..4307a30
--- /dev/null
+++ b/crypto/logwrapper/include/logwrap/logwrap.h
@@ -0,0 +1,87 @@
+/* system/core/include/logwrap/logwrap.h
+ *
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LIBS_LOGWRAP_H
+#define __LIBS_LOGWRAP_H
+
+#include <stdbool.h>
+
+__BEGIN_DECLS
+
+/*
+ * Run a command while logging its stdout and stderr
+ *
+ * WARNING: while this function is running it will clear all SIGCHLD handlers
+ * if you rely on SIGCHLD in the caller there is a chance zombies will be
+ * created if you're not calling waitpid after calling this. This function will
+ * log a warning when it clears SIGCHLD for processes other than the child it
+ * created.
+ *
+ * Arguments:
+ *   argc:   the number of elements in argv
+ *   argv:   an array of strings containing the command to be executed and its
+ *           arguments as separate strings. argv does not need to be
+ *           NULL-terminated
+ *   status: the equivalent child status as populated by wait(status). This
+ *           value is only valid when logwrap successfully completes. If NULL
+ *           the return value of the child will be the function's return value.
+ *   ignore_int_quit: set to true if you want to completely ignore SIGINT and
+ *           SIGQUIT while logwrap is running. This may force the end-user to
+ *           send a signal twice to signal the caller (once for the child, and
+ *           once for the caller)
+ *   log_target: Specify where to log the output of the child, either LOG_NONE,
+ *           LOG_ALOG (for the Android system log), LOG_KLOG (for the kernel
+ *           log), or LOG_FILE (and you need to specify a pathname in the
+ *           file_path argument, otherwise pass NULL).  These are bit fields,
+ *           and can be OR'ed together to log to multiple places.
+ *   abbreviated: If true, capture up to the first 100 lines and last 4K of
+ *           output from the child.  The abbreviated output is not dumped to
+ *           the specified log until the child has exited.
+ *   file_path: if log_target has the LOG_FILE bit set, then this parameter
+ *           must be set to the pathname of the file to log to.
+ *
+ * Return value:
+ *   0 when logwrap successfully run the child process and captured its status
+ *   -1 when an internal error occurred
+ *   -ECHILD if status is NULL and the child didn't exit properly
+ *   the return value of the child if it exited properly and status is NULL
+ *
+ */
+
+/* Values for the log_target parameter android_fork_execvp_ext() */
+#define LOG_NONE        0
+#define LOG_ALOG        1
+#define LOG_KLOG        2
+#define LOG_FILE        4
+
+int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
+        int log_target, bool abbreviated, char *file_path);
+
+/* Similar to above, except abbreviated logging is not available, and if logwrap
+ * is true, logging is to the Android system log, and if false, there is no
+ * logging.
+ */
+static inline int android_fork_execvp(int argc, char* argv[], int *status,
+                                     bool ignore_int_quit, bool logwrap)
+{
+    return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
+                                   (logwrap ? LOG_ALOG : LOG_NONE), false, NULL);
+}
+
+__END_DECLS
+
+#endif /* __LIBS_LOGWRAP_H */
diff --git a/crypto/logwrapper/logwrap.c b/crypto/logwrapper/logwrap.c
new file mode 100644
index 0000000..4ca1db4
--- /dev/null
+++ b/crypto/logwrapper/logwrap.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#include <logwrap/logwrap.h>
+#include "private/android_filesystem_config.h"
+#include "cutils/log.h"
+#include <cutils/klog.h>
+
+#define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#define ERROR(fmt, args...)                                                   \
+do {                                                                          \
+    fprintf(stderr, fmt, ## args);                                            \
+    ALOG(LOG_ERROR, "logwrapper", fmt, ## args);                              \
+} while(0)
+
+#define FATAL_CHILD(fmt, args...)                                             \
+do {                                                                          \
+    ERROR(fmt, ## args);                                                      \
+    _exit(-1);                                                                \
+} while(0)
+
+#define MAX_KLOG_TAG 16
+
+/* This is a simple buffer that holds up to the first beginning_buf->buf_size
+ * bytes of output from a command.
+ */
+#define BEGINNING_BUF_SIZE 0x1000
+struct beginning_buf {
+    char *buf;
+    size_t alloc_len;
+    /* buf_size is the usable space, which is one less than the allocated size */
+    size_t buf_size;
+    size_t used_len;
+};
+
+/* This is a circular buf that holds up to the last ending_buf->buf_size bytes
+ * of output from a command after the first beginning_buf->buf_size bytes
+ * (which are held in beginning_buf above).
+ */
+#define ENDING_BUF_SIZE 0x1000
+struct ending_buf {
+    char *buf;
+    ssize_t alloc_len;
+    /* buf_size is the usable space, which is one less than the allocated size */
+    ssize_t buf_size;
+    ssize_t used_len;
+    /* read and write offsets into the circular buffer */
+    int read;
+    int write;
+};
+
+ /* A structure to hold all the abbreviated buf data */
+struct abbr_buf {
+    struct beginning_buf b_buf;
+    struct ending_buf e_buf;
+    int beginning_buf_full;
+};
+
+/* Collect all the various bits of info needed for logging in one place. */
+struct log_info {
+    int log_target;
+    char klog_fmt[MAX_KLOG_TAG * 2];
+    char *btag;
+    bool abbreviated;
+    FILE *fp;
+    struct abbr_buf a_buf;
+};
+
+/* Forware declaration */
+static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen);
+
+/* Return 0 on success, and 1 when full */
+static int add_line_to_linear_buf(struct beginning_buf *b_buf,
+                                   char *line, ssize_t line_len)
+{
+    size_t new_len;
+    char *new_buf;
+    int full = 0;
+
+    if ((line_len + b_buf->used_len) > b_buf->buf_size) {
+        full = 1;
+    } else {
+        /* Add to the end of the buf */
+        memcpy(b_buf->buf + b_buf->used_len, line, line_len);
+        b_buf->used_len += line_len;
+    }
+
+    return full;
+}
+
+static void add_line_to_circular_buf(struct ending_buf *e_buf,
+                                     char *line, ssize_t line_len)
+{
+    ssize_t free_len;
+    ssize_t needed_space;
+    char *new_buf;
+    int cnt;
+
+    if (e_buf->buf == NULL) {
+        return;
+    }
+
+   if (line_len > e_buf->buf_size) {
+       return;
+   }
+
+    free_len = e_buf->buf_size - e_buf->used_len;
+
+    if (line_len > free_len) {
+        /* remove oldest entries at read, and move read to make
+         * room for the new string */
+        needed_space = line_len - free_len;
+        e_buf->read = (e_buf->read + needed_space) % e_buf->buf_size;
+        e_buf->used_len -= needed_space;
+    }
+
+    /* Copy the line into the circular buffer, dealing with possible
+     * wraparound.
+     */
+    cnt = MIN(line_len, e_buf->buf_size - e_buf->write);
+    memcpy(e_buf->buf + e_buf->write, line, cnt);
+    if (cnt < line_len) {
+        memcpy(e_buf->buf, line + cnt, line_len - cnt);
+    }
+    e_buf->used_len += line_len;
+    e_buf->write = (e_buf->write + line_len) % e_buf->buf_size;
+}
+
+/* Log directly to the specified log */
+static void do_log_line(struct log_info *log_info, char *line) {
+    if (log_info->log_target & LOG_KLOG) {
+        klog_write(6, log_info->klog_fmt, line);
+    }
+    if (log_info->log_target & LOG_ALOG) {
+        ALOG(LOG_INFO, log_info->btag, "%s", line);
+    }
+    if (log_info->log_target & LOG_FILE) {
+        fprintf(log_info->fp, "%s\n", line);
+    }
+}
+
+/* Log to either the abbreviated buf, or directly to the specified log
+ * via do_log_line() above.
+ */
+static void log_line(struct log_info *log_info, char *line, int len) {
+    if (log_info->abbreviated) {
+        add_line_to_abbr_buf(&log_info->a_buf, line, len);
+    } else {
+        do_log_line(log_info, line);
+    }
+}
+
+/*
+ * The kernel will take a maximum of 1024 bytes in any single write to
+ * the kernel logging device file, so find and print each line one at
+ * a time.  The allocated size for buf should be at least 1 byte larger
+ * than buf_size (the usable size of the buffer) to make sure there is
+ * room to temporarily stuff a null byte to terminate a line for logging.
+ */
+static void print_buf_lines(struct log_info *log_info, char *buf, int buf_size)
+{
+    char *line_start;
+    char c;
+    int line_len;
+    int i;
+
+    line_start = buf;
+    for (i = 0; i < buf_size; i++) {
+        if (*(buf + i) == '\n') {
+            /* Found a line ending, print the line and compute new line_start */
+            /* Save the next char and replace with \0 */
+            c = *(buf + i + 1);
+            *(buf + i + 1) = '\0';
+            do_log_line(log_info, line_start);
+            /* Restore the saved char */
+            *(buf + i + 1) = c;
+            line_start = buf + i + 1;
+        } else if (*(buf + i) == '\0') {
+            /* The end of the buffer, print the last bit */
+            do_log_line(log_info, line_start);
+            break;
+        }
+    }
+    /* If the buffer was completely full, and didn't end with a newline, just
+     * ignore the partial last line.
+     */
+}
+
+static void init_abbr_buf(struct abbr_buf *a_buf) {
+    char *new_buf;
+
+    memset(a_buf, 0, sizeof(struct abbr_buf));
+    new_buf = malloc(BEGINNING_BUF_SIZE);
+    if (new_buf) {
+        a_buf->b_buf.buf = new_buf;
+        a_buf->b_buf.alloc_len = BEGINNING_BUF_SIZE;
+        a_buf->b_buf.buf_size = BEGINNING_BUF_SIZE - 1;
+    }
+    new_buf = malloc(ENDING_BUF_SIZE);
+    if (new_buf) {
+        a_buf->e_buf.buf = new_buf;
+        a_buf->e_buf.alloc_len = ENDING_BUF_SIZE;
+        a_buf->e_buf.buf_size = ENDING_BUF_SIZE - 1;
+    }
+}
+
+static void free_abbr_buf(struct abbr_buf *a_buf) {
+    free(a_buf->b_buf.buf);
+    free(a_buf->e_buf.buf);
+}
+
+static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen) {
+    if (!a_buf->beginning_buf_full) {
+        a_buf->beginning_buf_full =
+            add_line_to_linear_buf(&a_buf->b_buf, linebuf, linelen);
+    }
+    if (a_buf->beginning_buf_full) {
+        add_line_to_circular_buf(&a_buf->e_buf, linebuf, linelen);
+    }
+}
+
+static void print_abbr_buf(struct log_info *log_info) {
+    struct abbr_buf *a_buf = &log_info->a_buf;
+
+    /* Add the abbreviated output to the kernel log */
+    if (a_buf->b_buf.alloc_len) {
+        print_buf_lines(log_info, a_buf->b_buf.buf, a_buf->b_buf.used_len);
+    }
+
+    /* Print an ellipsis to indicate that the buffer has wrapped or
+     * is full, and some data was not logged.
+     */
+    if (a_buf->e_buf.used_len == a_buf->e_buf.buf_size) {
+        do_log_line(log_info, "...\n");
+    }
+
+    if (a_buf->e_buf.used_len == 0) {
+        return;
+    }
+
+    /* Simplest way to print the circular buffer is allocate a second buf
+     * of the same size, and memcpy it so it's a simple linear buffer,
+     * and then cal print_buf_lines on it */
+    if (a_buf->e_buf.read < a_buf->e_buf.write) {
+        /* no wrap around, just print it */
+        print_buf_lines(log_info, a_buf->e_buf.buf + a_buf->e_buf.read,
+                        a_buf->e_buf.used_len);
+    } else {
+        /* The circular buffer will always have at least 1 byte unused,
+         * so by allocating alloc_len here we will have at least
+         * 1 byte of space available as required by print_buf_lines().
+         */
+        char * nbuf = malloc(a_buf->e_buf.alloc_len);
+        if (!nbuf) {
+            return;
+        }
+        int first_chunk_len = a_buf->e_buf.buf_size - a_buf->e_buf.read;
+        memcpy(nbuf, a_buf->e_buf.buf + a_buf->e_buf.read, first_chunk_len);
+        /* copy second chunk */
+        memcpy(nbuf + first_chunk_len, a_buf->e_buf.buf, a_buf->e_buf.write);
+        print_buf_lines(log_info, nbuf, first_chunk_len + a_buf->e_buf.write);
+        free(nbuf);
+    }
+}
+
+static int parent(const char *tag, int parent_read, pid_t pid,
+        int *chld_sts, int log_target, bool abbreviated, char *file_path) {
+    int status = 0;
+    char buffer[4096];
+    struct pollfd poll_fds[] = {
+        [0] = {
+            .fd = parent_read,
+            .events = POLLIN,
+        },
+    };
+    int rc = 0;
+    int fd;
+
+    struct log_info log_info;
+
+    int a = 0;  // start index of unprocessed data
+    int b = 0;  // end index of unprocessed data
+    int sz;
+    bool found_child = false;
+    char tmpbuf[256];
+
+    log_info.btag = basename(tag);
+    if (!log_info.btag) {
+        log_info.btag = (char*) tag;
+    }
+
+    if (abbreviated && (log_target == LOG_NONE)) {
+        abbreviated = 0;
+    }
+    if (abbreviated) {
+        init_abbr_buf(&log_info.a_buf);
+    }
+
+    if (log_target & LOG_KLOG) {
+        snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt),
+                 "<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag);
+    }
+
+    if ((log_target & LOG_FILE) && !file_path) {
+        /* No file_path specified, clear the LOG_FILE bit */
+        log_target &= ~LOG_FILE;
+    }
+
+    if (log_target & LOG_FILE) {
+        fd = open(file_path, O_WRONLY | O_CREAT, 0664);
+        if (fd < 0) {
+            ERROR("Cannot log to file %s\n", file_path);
+            log_target &= ~LOG_FILE;
+        } else {
+            lseek(fd, 0, SEEK_END);
+            log_info.fp = fdopen(fd, "a");
+        }
+    }
+
+    log_info.log_target = log_target;
+    log_info.abbreviated = abbreviated;
+
+    while (!found_child) {
+        if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) {
+            ERROR("poll failed\n");
+            rc = -1;
+            goto err_poll;
+        }
+
+        if (poll_fds[0].revents & POLLIN) {
+            sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b);
+
+            sz += b;
+            // Log one line at a time
+            for (b = 0; b < sz; b++) {
+                if (buffer[b] == '\r') {
+                    if (abbreviated) {
+                        /* The abbreviated logging code uses newline as
+                         * the line separator.  Lucikly, the pty layer
+                         * helpfully cooks the output of the command
+                         * being run and inserts a CR before NL.  So
+                         * I just change it to NL here when doing
+                         * abbreviated logging.
+                         */
+                        buffer[b] = '\n';
+                    } else {
+                        buffer[b] = '\0';
+                    }
+                } else if (buffer[b] == '\n') {
+                    buffer[b] = '\0';
+                    log_line(&log_info, &buffer[a], b - a);
+                    a = b + 1;
+                }
+            }
+
+            if (a == 0 && b == sizeof(buffer) - 1) {
+                // buffer is full, flush
+                buffer[b] = '\0';
+                log_line(&log_info, &buffer[a], b - a);
+                b = 0;
+            } else if (a != b) {
+                // Keep left-overs
+                b -= a;
+                memmove(buffer, &buffer[a], b);
+                a = 0;
+            } else {
+                a = 0;
+                b = 0;
+            }
+        }
+
+        if (poll_fds[0].revents & POLLHUP) {
+            int ret;
+
+            ret = waitpid(pid, &status, WNOHANG);
+            if (ret < 0) {
+                rc = errno;
+                ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
+                goto err_waitpid;
+            }
+            if (ret > 0) {
+                found_child = true;
+            }
+        }
+    }
+
+    if (chld_sts != NULL) {
+        *chld_sts = status;
+    } else {
+      if (WIFEXITED(status))
+        rc = WEXITSTATUS(status);
+      else
+        rc = -ECHILD;
+    }
+
+    // Flush remaining data
+    if (a != b) {
+      buffer[b] = '\0';
+      log_line(&log_info, &buffer[a], b - a);
+    }
+
+    /* All the output has been processed, time to dump the abbreviated output */
+    if (abbreviated) {
+        print_abbr_buf(&log_info);
+    }
+
+    if (WIFEXITED(status)) {
+      if (WEXITSTATUS(status)) {
+        snprintf(tmpbuf, sizeof(tmpbuf),
+                 "%s terminated by exit(%d)\n", log_info.btag, WEXITSTATUS(status));
+        do_log_line(&log_info, tmpbuf);
+      }
+    } else {
+      if (WIFSIGNALED(status)) {
+        snprintf(tmpbuf, sizeof(tmpbuf),
+                       "%s terminated by signal %d\n", log_info.btag, WTERMSIG(status));
+        do_log_line(&log_info, tmpbuf);
+      } else if (WIFSTOPPED(status)) {
+        snprintf(tmpbuf, sizeof(tmpbuf),
+                       "%s stopped by signal %d\n", log_info.btag, WSTOPSIG(status));
+        do_log_line(&log_info, tmpbuf);
+      }
+    }
+
+err_waitpid:
+err_poll:
+    if (log_target & LOG_FILE) {
+        fclose(log_info.fp); /* Also closes underlying fd */
+    }
+    if (abbreviated) {
+        free_abbr_buf(&log_info.a_buf);
+    }
+    return rc;
+}
+
+static void child(int argc, char* argv[]) {
+    // create null terminated argv_child array
+    char* argv_child[argc + 1];
+    memcpy(argv_child, argv, argc * sizeof(char *));
+    argv_child[argc] = NULL;
+
+    if (execvp(argv_child[0], argv_child)) {
+        FATAL_CHILD("executing %s failed: %s\n", argv_child[0],
+                strerror(errno));
+    }
+}
+
+int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
+        int log_target, bool abbreviated, char *file_path) {
+    pid_t pid;
+    int parent_ptty;
+    int child_ptty;
+    char *child_devname = NULL;
+    struct sigaction intact;
+    struct sigaction quitact;
+    sigset_t blockset;
+    sigset_t oldset;
+    int rc = 0;
+
+    rc = pthread_mutex_lock(&fd_mutex);
+    if (rc) {
+        ERROR("failed to lock signal_fd mutex\n");
+        goto err_lock;
+    }
+
+    /* Use ptty instead of socketpair so that STDOUT is not buffered */
+    parent_ptty = open("/dev/ptmx", O_RDWR);
+    if (parent_ptty < 0) {
+        ERROR("Cannot create parent ptty\n");
+        rc = -1;
+        goto err_open;
+    }
+
+    if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
+            ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
+        ERROR("Problem with /dev/ptmx\n");
+        rc = -1;
+        goto err_ptty;
+    }
+
+    child_ptty = open(child_devname, O_RDWR);
+    if (child_ptty < 0) {
+        ERROR("Cannot open child_ptty\n");
+        rc = -1;
+        goto err_child_ptty;
+    }
+
+    sigemptyset(&blockset);
+    sigaddset(&blockset, SIGINT);
+    sigaddset(&blockset, SIGQUIT);
+    pthread_sigmask(SIG_BLOCK, &blockset, &oldset);
+
+    pid = fork();
+    if (pid < 0) {
+        close(child_ptty);
+        ERROR("Failed to fork\n");
+        rc = -1;
+        goto err_fork;
+    } else if (pid == 0) {
+        pthread_mutex_unlock(&fd_mutex);
+        pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+        close(parent_ptty);
+
+        // redirect stdout and stderr
+        dup2(child_ptty, 1);
+        dup2(child_ptty, 2);
+        close(child_ptty);
+
+        child(argc, argv);
+    } else {
+        close(child_ptty);
+        if (ignore_int_quit) {
+            struct sigaction ignact;
+
+            memset(&ignact, 0, sizeof(ignact));
+            ignact.sa_handler = SIG_IGN;
+            sigaction(SIGINT, &ignact, &intact);
+            sigaction(SIGQUIT, &ignact, &quitact);
+        }
+
+        rc = parent(argv[0], parent_ptty, pid, status, log_target,
+                    abbreviated, file_path);
+    }
+
+    if (ignore_int_quit) {
+        sigaction(SIGINT, &intact, NULL);
+        sigaction(SIGQUIT, &quitact, NULL);
+    }
+err_fork:
+    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+err_child_ptty:
+err_ptty:
+    close(parent_ptty);
+err_open:
+    pthread_mutex_unlock(&fd_mutex);
+err_lock:
+    return rc;
+}
diff --git a/crypto/logwrapper/logwrapper.c b/crypto/logwrapper/logwrapper.c
new file mode 100644
index 0000000..d0d8d14
--- /dev/null
+++ b/crypto/logwrapper/logwrapper.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <logwrap/logwrap.h>
+#include <cutils/klog.h>
+
+#include "cutils/log.h"
+
+void fatal(const char *msg) {
+    fprintf(stderr, "%s", msg);
+    ALOG(LOG_ERROR, "logwrapper", "%s", msg);
+    exit(-1);
+}
+
+void usage() {
+    fatal(
+        "Usage: logwrapper [-a] [-d] [-k] BINARY [ARGS ...]\n"
+        "\n"
+        "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
+        "the Android logging system. Tag is set to BINARY, priority is\n"
+        "always LOG_INFO.\n"
+        "\n"
+        "-a: Causes logwrapper to do abbreviated logging.\n"
+        "    This logs up to the first 4K and last 4K of the command\n"
+        "    being run, and logs the output when the command exits\n"
+        "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n"
+        "    fault address is set to the status of wait()\n"
+        "-k: Causes logwrapper to log to the kernel log instead of\n"
+        "    the Android system log\n");
+}
+
+int main(int argc, char* argv[]) {
+    int seg_fault_on_exit = 0;
+    int log_target = LOG_ALOG;
+    bool abbreviated = false;
+    int ch;
+    int status = 0xAAAA;
+    int rc;
+
+    while ((ch = getopt(argc, argv, "adk")) != -1) {
+        switch (ch) {
+            case 'a':
+                abbreviated = true;
+                break;
+            case 'd':
+                seg_fault_on_exit = 1;
+                break;
+            case 'k':
+                log_target = LOG_KLOG;
+                klog_set_level(6);
+                break;
+            case '?':
+            default:
+              usage();
+        }
+    }
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1) {
+        usage();
+    }
+
+    rc = android_fork_execvp_ext(argc, &argv[0], &status, true,
+                                 log_target, abbreviated, NULL);
+    if (!rc) {
+        if (WIFEXITED(status))
+            rc = WEXITSTATUS(status);
+        else
+            rc = -ECHILD;
+    }
+
+    if (seg_fault_on_exit) {
+        *(int *)status = 0;  // causes SIGSEGV with fault_address = status
+    }
+
+    return rc;
+}
diff --git a/crypto/scrypt/Android.mk b/crypto/scrypt/Android.mk
new file mode 100644
index 0000000..7fa96d0
--- /dev/null
+++ b/crypto/scrypt/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH := $(call my-dir)
+
+# Enable to be able to use ALOG* with #include "cutils/log.h"
+#log_c_includes += system/core/include
+#log_shared_libraries := liblog
+
+# These makefiles are here instead of being Android.mk files in the
+# respective crypto, ssl, and apps directories so
+# that import_openssl.sh import won't remove them.
+include $(LOCAL_PATH)/build-config.mk
+include $(LOCAL_PATH)/Scrypt.mk
+
+include $(call all-named-subdir-makefiles,tests)
diff --git a/crypto/scrypt/MODULE_LICENSE_BSD_LIKE b/crypto/scrypt/MODULE_LICENSE_BSD_LIKE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/crypto/scrypt/MODULE_LICENSE_BSD_LIKE
diff --git a/crypto/scrypt/NOTICE b/crypto/scrypt/NOTICE
new file mode 100644
index 0000000..b0b9311
--- /dev/null
+++ b/crypto/scrypt/NOTICE
@@ -0,0 +1,36 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+
+/*
+ * version 20110505
+ * D. J. Bernstein
+ * Public domain.
+ *
+ * Based on crypto_core/salsa208/armneon/core.c from SUPERCOP 20130419
+ */
diff --git a/crypto/scrypt/Scrypt-config.mk b/crypto/scrypt/Scrypt-config.mk
new file mode 100644
index 0000000..c0b00d8
--- /dev/null
+++ b/crypto/scrypt/Scrypt-config.mk
@@ -0,0 +1,105 @@
+# Auto-generated - DO NOT EDIT!
+# To regenerate, edit scrypt.config, then run:
+#     ./import_scrypt.sh import /path/to/scrypt-1.1.6.tar.gz
+#
+# Before including this file, the local Android.mk must define the following
+# variables:
+#
+#    local_c_flags
+#    local_c_includes
+#    local_additional_dependencies
+#
+# This script will define the following variables:
+#
+#    target_c_flags
+#    target_c_includes
+#    target_src_files
+#
+#    host_c_flags
+#    host_c_includes
+#    host_src_files
+#
+
+# Ensure these are empty.
+unknown_arch_c_flags :=
+unknown_arch_src_files :=
+unknown_arch_exclude_files :=
+
+
+common_c_flags :=
+
+common_src_files := \
+  lib/crypto/crypto_scrypt-ref.c \
+
+common_c_includes := \
+  lib/crypto \
+  lib/util \
+
+arm_c_flags :=
+
+arm_src_files :=
+
+arm_exclude_files :=
+
+arm_neon_c_flags :=
+
+arm_neon_src_files := \
+  lib/crypto/crypto_scrypt-neon.c \
+
+arm_neon_exclude_files := \
+  lib/crypto/crypto_scrypt-ref.c \
+
+x86_c_flags :=
+
+x86_src_files := \
+  lib/crypto/crypto_scrypt-sse.c \
+
+x86_exclude_files := \
+  lib/crypto/crypto_scrypt-ref.c \
+
+x86_64_c_flags :=
+
+x86_64_src_files := \
+  lib/crypto/crypto_scrypt-sse.c \
+
+x86_64_exclude_files := \
+  lib/crypto/crypto_scrypt-ref.c \
+
+mips_c_flags :=
+
+mips_src_files :=
+
+mips_exclude_files :=
+
+target_arch := $(TARGET_ARCH)
+ifeq ($(target_arch)-$(TARGET_HAS_BIGENDIAN),mips-true)
+target_arch := unknown_arch
+endif
+
+target_c_flags    := $(common_c_flags) $($(target_arch)_c_flags) $(local_c_flags)
+target_c_includes := $(addprefix $(commands_recovery_local_path)/crypto/scrypt/,$(common_c_includes)) $(local_c_includes)
+target_src_files  := $(common_src_files) $($(target_arch)_src_files)
+target_src_files  := $(filter-out $($(target_arch)_exclude_files), $(target_src_files))
+
+# Hacks for ARM NEON support
+ifeq ($(target_arch),arm)
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+target_c_flags   += $(arm_neon_c_flags)
+target_src_files += $(arm_neon_src_files)
+target_src_files := $(filter-out $(arm_neon_exclude_files), $(target_src_files))
+endif
+endif
+
+ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
+host_arch := x86
+else
+host_arch := unknown_arch
+endif
+
+host_c_flags    := $(common_c_flags) $($(host_arch)_c_flags) $(local_c_flags)
+host_c_includes := $(addprefix $(commands_recovery_local_path)/crypto/scrypt/,$(common_c_includes)) $(local_c_includes)
+host_src_files  := $(common_src_files) $($(host_arch)_src_files)
+host_src_files  := $(filter-out $($(host_arch)_exclude_files), $(host_src_files))
+
+local_additional_dependencies += $(LOCAL_PATH)/Scrypt-config.mk
+
diff --git a/crypto/scrypt/Scrypt.mk b/crypto/scrypt/Scrypt.mk
new file mode 100644
index 0000000..45af97f
--- /dev/null
+++ b/crypto/scrypt/Scrypt.mk
@@ -0,0 +1,46 @@
+local_c_flags := -DUSE_OPENSSL_PBKDF2
+
+local_c_includes := $(log_c_includes) external/openssl/include
+
+local_additional_dependencies := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/Scrypt.mk
+
+include $(LOCAL_PATH)/Scrypt-config.mk
+
+#######################################
+# target static library
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/android-config.mk
+
+LOCAL_SHARED_LIBRARIES := $(log_shared_libraries)
+
+# If we're building an unbundled build, don't try to use clang since it's not
+# in the NDK yet. This can be removed when a clang version that is fast enough
+# in the NDK.
+ifeq (,$(TARGET_BUILD_APPS))
+LOCAL_CLANG := true
+else
+LOCAL_SDK_VERSION := 9
+endif
+
+LOCAL_SRC_FILES += $(target_src_files)
+LOCAL_CFLAGS += $(target_c_flags)
+LOCAL_C_INCLUDES += $(target_c_includes)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= libscrypttwrp_static
+LOCAL_ADDITIONAL_DEPENDENCIES := $(local_additional_dependencies)
+include $(BUILD_STATIC_LIBRARY)
+
+########################################
+# host static library
+
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/android-config.mk
+LOCAL_SHARED_LIBRARIES := $(log_shared_libraries)
+LOCAL_SRC_FILES += $(host_src_files)
+LOCAL_CFLAGS += $(host_c_flags)
+LOCAL_C_INCLUDES += $(host_c_includes)
+LOCAL_LDLIBS += -ldl
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= libscrypttwrp_static
+LOCAL_ADDITIONAL_DEPENDENCIES := $(local_additional_dependencies)
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/crypto/scrypt/android-config.mk b/crypto/scrypt/android-config.mk
new file mode 100644
index 0000000..326e113
--- /dev/null
+++ b/crypto/scrypt/android-config.mk
@@ -0,0 +1,16 @@
+#
+# These flags represent the build-time configuration of scrypt for Android
+#
+# The value of $(scrypt_cflags) was pruned from the Makefile generated
+# by running ./configure from import_scrypt.sh.
+#
+# This script performs minor but required patching for the Android build.
+#
+
+LOCAL_CFLAGS += $(scrypt_cflags)
+
+# Add in flags to let config.h be read properly
+LOCAL_CFLAGS += "-DHAVE_CONFIG_H"
+
+# Add clang here when it works on host
+# LOCAL_CLANG := true
diff --git a/crypto/scrypt/build-config.mk b/crypto/scrypt/build-config.mk
new file mode 100644
index 0000000..3d2ab91
--- /dev/null
+++ b/crypto/scrypt/build-config.mk
@@ -0,0 +1,6 @@
+# Auto-generated - DO NOT EDIT!
+# To regenerate, edit scrypt.config, then run:
+#     ./import_scrypt.sh import /path/to/scrypt-1.1.6.tar.gz
+#
+scrypt_cflags := \
+
diff --git a/crypto/scrypt/config.h b/crypto/scrypt/config.h
new file mode 100644
index 0000000..3514f39
--- /dev/null
+++ b/crypto/scrypt/config.h
@@ -0,0 +1,99 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+
+/* Define to 1 if you have the declaration of `be64enc', and to 0 if you
+   don't. */
+#define HAVE_DECL_BE64ENC 0
+
+/* Define to 1 if you have the <err.h> header file. */
+#define HAVE_ERR_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+#define HAVE_LIBRT 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `posix_memalign' function. */
+#define HAVE_POSIX_MEMALIGN 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if the system has the type `struct sysinfo'. */
+#define HAVE_STRUCT_SYSINFO 1
+
+/* Define to 1 if `mem_unit' is member of `struct sysinfo'. */
+#define HAVE_STRUCT_SYSINFO_MEM_UNIT 1
+
+/* Define to 1 if `totalram' is member of `struct sysinfo'. */
+#define HAVE_STRUCT_SYSINFO_TOTALRAM 1
+
+/* Define to 1 if the OS has a hw.usermem sysctl */
+/* #undef HAVE_SYSCTL_HW_USERMEM */
+
+/* Define to 1 if you have the `sysinfo' function. */
+#define HAVE_SYSINFO 1
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+/* #undef HAVE_SYS_ENDIAN_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/sysinfo.h> header file. */
+#define HAVE_SYS_SYSINFO_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Name of package */
+#define PACKAGE "scrypt"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "scrypt"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "scrypt 1.1.6"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "scrypt"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.1.6"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "1.1.6"
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
diff --git a/crypto/scrypt/import_scrypt.sh b/crypto/scrypt/import_scrypt.sh
new file mode 100755
index 0000000..324eae6
--- /dev/null
+++ b/crypto/scrypt/import_scrypt.sh
@@ -0,0 +1,493 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# This script imports new versions of scrypt (http://www.tarsnap.com/scrypt/) into the
+# Android source tree.  To run, (1) fetch the appropriate tarball from the scrypt repository,
+# (2) check the gpg/pgp signature, and then (3) run:
+#   ./import_scrypt.sh import scrypt-*.tar.gz
+#
+# IMPORTANT: See README.android for additional details.
+
+# turn on exit on error as well as a warning when it happens
+set -e
+set -x
+trap  "echo WARNING: Exiting on non-zero subprocess exit code" ERR;
+
+# Ensure consistent sorting order / tool output.
+export LANG=C
+export LC_ALL=C
+
+export DIRNAME=$(dirname $0)
+
+function die() {
+  declare -r message=$1
+
+  echo $message
+  exit 1
+}
+
+function usage() {
+  declare -r message=$1
+
+  if [ ! "$message" = "" ]; then
+    echo $message
+  fi
+  echo "Usage:"
+  echo "  ./import_scrypt.sh import </path/to/scrypt-*.tar.gz>"
+  echo "  ./import_scrypt.sh regenerate <patch/*.patch>"
+  echo "  ./import_scrypt.sh generate <patch/*.patch> </path/to/scrypt-*.tar.gz>"
+  exit 1
+}
+
+function main() {
+  if [ ! -d patches ]; then
+    die "scrypt patch directory patches/ not found"
+  fi
+
+  if [ ! -f scrypt.version ]; then
+    die "scrypt.version not found"
+  fi
+
+  source $DIRNAME/scrypt.version
+  if [ "$SCRYPT_VERSION" == "" ]; then
+    die "Invalid scrypt.version; see README.android for more information"
+  fi
+
+  SCRYPT_DIR=scrypt-$SCRYPT_VERSION
+  SCRYPT_DIR_ORIG=$SCRYPT_DIR.orig
+
+  if [ ! -f scrypt.config ]; then
+    die "scrypt.config not found"
+  fi
+
+  source $DIRNAME/scrypt.config
+  if [ "$CONFIGURE_ARGS" == "" -o "$UNNEEDED_SOURCES" == "" -o "$NEEDED_SOURCES" == "" ]; then
+    die "Invalid scrypt.config; see README.android for more information"
+  fi
+
+  declare -r command=$1
+  shift || usage "No command specified. Try import, regenerate, or generate."
+  if [ "$command" = "import" ]; then
+    declare -r tar=$1
+    shift || usage "No tar file specified."
+    import $tar
+  elif [ "$command" = "regenerate" ]; then
+    declare -r patch=$1
+    shift || usage "No patch file specified."
+    [ -d $SCRYPT_DIR ] || usage "$SCRYPT_DIR not found, did you mean to use generate?"
+    [ -d $SCRYPT_DIR_ORIG_ORIG ] || usage "$SCRYPT_DIR_ORIG not found, did you mean to use generate?"
+    regenerate $patch
+  elif [ "$command" = "generate" ]; then
+    declare -r patch=$1
+    shift || usage "No patch file specified."
+    declare -r tar=$1
+    shift || usage "No tar file specified."
+    generate $patch $tar
+  else
+    usage "Unknown command specified $command. Try import, regenerate, or generate."
+  fi
+}
+
+# Compute the name of an assembly source file generated by one of the
+# gen_asm_xxxx() functions below. The logic is the following:
+# - if "$2" is not empty, output it directly
+# - otherwise, change the file extension of $1 from .pl to .S and output
+#   it.
+# Usage: default_asm_file "$1" "$2"
+#     or default_asm_file "$@"
+#
+# $1: generator path (perl script)
+# $2: optional output file name.
+function default_asm_file () {
+  if [ "$2" ]; then
+    echo "$2"
+  else
+    echo "${1%%.pl}.S"
+  fi
+}
+
+# Generate an ARM assembly file.
+# $1: generator (perl script)
+# $2: [optional] output file name
+function gen_asm_arm () {
+  local OUT
+  OUT=$(default_asm_file "$@")
+  perl "$1" > "$OUT"
+}
+
+function gen_asm_mips () {
+  local OUT
+  OUT=$(default_asm_file "$@")
+  # The perl scripts expect to run the target compiler as $CC to determine
+  # the endianess of the target. Setting CC to true is a hack that forces the scripts
+  # to generate little endian output
+  CC=true perl "$1" o32 > "$OUT"
+}
+
+function gen_asm_x86 () {
+  local OUT
+  OUT=$(default_asm_file "$@")
+  perl "$1" elf -fPIC > "$OUT"
+}
+
+function gen_asm_x86_64 () {
+  local OUT
+  OUT=$(default_asm_file "$@")
+  perl "$1" elf "$OUT" > "$OUT"
+}
+
+
+# Filter all items in a list that match a given pattern.
+# $1: space-separated list
+# $2: egrep pattern.
+# Out: items in $1 that match $2
+function filter_by_egrep() {
+  declare -r pattern=$1
+  shift
+  echo "$@" | tr ' ' '\n' | grep -e "$pattern" | tr '\n' ' '
+}
+
+# Sort and remove duplicates in a space-separated list
+# $1: space-separated list
+# Out: new space-separated list
+function uniq_sort () {
+  echo "$@" | tr ' ' '\n' | sort -u | tr '\n' ' '
+}
+
+function print_autogenerated_header() {
+  echo "# Auto-generated - DO NOT EDIT!"
+  echo "# To regenerate, edit scrypt.config, then run:"
+  echo "#     ./import_scrypt.sh import /path/to/scrypt-$SCRYPT_VERSION.tar.gz"
+  echo "#"
+}
+
+function generate_build_config_mk() {
+  ./configure $CONFIGURE_ARGS
+  #rm -f apps/CA.pl.bak crypto/scryptconf.h.bak
+
+  declare -r tmpfile=$(mktemp)
+  (grep -e -D Makefile | grep -v CONFIGURE_ARGS= | grep -v OPTIONS=) > $tmpfile
+
+  declare -r cflags=$(filter_by_egrep "^-D" $(grep -e "^CFLAG=" $tmpfile))
+  declare -r depflags=$(filter_by_egrep "^-D" $(grep -e "^DEPFLAG=" $tmpfile))
+  rm -f $tmpfile
+
+  echo "Generating $(basename $1)"
+  (
+    print_autogenerated_header
+
+    echo "scrypt_cflags := \\"
+    for cflag in $cflags $depflags; do
+      echo "  $cflag \\"
+    done
+    echo ""
+  ) > $1
+}
+
+# Return the value of a computed variable name.
+# E.g.:
+#   FOO=foo
+#   BAR=bar
+#   echo $(var_value FOO_$BAR)   -> prints the value of ${FOO_bar}
+# $1: Variable name
+# Out: variable value
+var_value() {
+  # Note: don't use 'echo' here, because it's sensitive to values
+  #       that begin with an underscore (e.g. "-n")
+  eval printf \"%s\\n\" \$$1
+}
+
+# Same as var_value, but returns sorted output without duplicates.
+# $1: Variable name
+# Out: variable value (if space-separated list, sorted with no duplicates)
+var_sorted_value() {
+  uniq_sort $(var_value $1)
+}
+
+# Print the definition of a given variable in a GNU Make build file.
+# $1: Variable name (e.g. common_src_files)
+# $2+: Variable value (e.g. list of sources)
+print_vardef_in_mk() {
+  declare -r varname=$1
+  shift
+  if [ -z "$1" ]; then
+    echo "$varname :="
+  else
+    echo "$varname := \\"
+    for src; do
+      echo "  $src \\"
+    done
+  fi
+  echo ""
+}
+
+# Same as print_vardef_in_mk, but print a CFLAGS definition from
+# a list of compiler defines.
+# $1: Variable name (e.g. common_c_flags)
+# $2: List of defines (e.g. SCRYPT_NO_DONKEYS ...)
+print_defines_in_mk() {
+  declare -r varname=$1
+  shift
+  if [ -z "$1" ]; then
+    echo "$varname :="
+  else
+    echo "$varname := \\"
+    for def; do
+    echo "  -D$def \\"
+    done
+  fi
+  echo ""
+}
+
+# Generate a configuration file like Scrypt-config.mk
+# This uses variable definitions from scrypt.config to build a config
+# file that can compute the list of target- and host-specific sources /
+# compiler flags for a given component.
+#
+# $1: Target file name.  (e.g. Scrypt-config.mk)
+function generate_config_mk() {
+  declare -r output="$1"
+  declare -r all_archs="arm arm_neon x86 x86_64 mips"
+
+  echo "Generating $(basename $output)"
+  (
+    print_autogenerated_header
+    echo \
+"# Before including this file, the local Android.mk must define the following
+# variables:
+#
+#    local_c_flags
+#    local_c_includes
+#    local_additional_dependencies
+#
+# This script will define the following variables:
+#
+#    target_c_flags
+#    target_c_includes
+#    target_src_files
+#
+#    host_c_flags
+#    host_c_includes
+#    host_src_files
+#
+
+# Ensure these are empty.
+unknown_arch_c_flags :=
+unknown_arch_src_files :=
+unknown_arch_exclude_files :=
+
+"
+    common_defines=$(var_sorted_value SCRYPT_DEFINES)
+    print_defines_in_mk common_c_flags $common_defines
+
+    common_sources=$(var_sorted_value SCRYPT_SOURCES)
+    print_vardef_in_mk common_src_files $common_sources
+
+    common_includes=$(var_sorted_value SCRYPT_INCLUDES)
+    print_vardef_in_mk common_c_includes $common_includes
+
+    for arch in $all_archs; do
+      arch_defines=$(var_sorted_value SCRYPT_DEFINES_${arch})
+      print_defines_in_mk ${arch}_c_flags $arch_defines
+
+      arch_sources=$(var_sorted_value SCRYPT_SOURCES_${arch})
+      print_vardef_in_mk ${arch}_src_files $arch_sources
+
+      arch_exclude_sources=$(var_sorted_value SCRYPT_SOURCES_EXCLUDES_${arch})
+      print_vardef_in_mk ${arch}_exclude_files $arch_exclude_sources
+
+    done
+
+    echo "\
+target_arch := \$(TARGET_ARCH)
+ifeq (\$(target_arch)-\$(TARGET_HAS_BIGENDIAN),mips-true)
+target_arch := unknown_arch
+endif
+
+target_c_flags    := \$(common_c_flags) \$(\$(target_arch)_c_flags) \$(local_c_flags)
+target_c_includes := \$(addprefix external/scrypt/,\$(common_c_includes)) \$(local_c_includes)
+target_src_files  := \$(common_src_files) \$(\$(target_arch)_src_files)
+target_src_files  := \$(filter-out \$(\$(target_arch)_exclude_files), \$(target_src_files))
+
+# Hacks for ARM NEON support
+ifeq (\$(target_arch),arm)
+ifeq (\$(ARCH_ARM_HAVE_NEON),true)
+target_c_flags   += \$(arm_neon_c_flags)
+target_src_files += \$(arm_neon_src_files)
+target_src_files := \$(filter-out \$(arm_neon_exclude_files), \$(target_src_files))
+endif
+endif
+
+ifeq (\$(HOST_OS)-\$(HOST_ARCH),linux-x86)
+host_arch := x86
+else
+host_arch := unknown_arch
+endif
+
+host_c_flags    := \$(common_c_flags) \$(\$(host_arch)_c_flags) \$(local_c_flags)
+host_c_includes := \$(addprefix external/scrypt/,\$(common_c_includes)) \$(local_c_includes)
+host_src_files  := \$(common_src_files) \$(\$(host_arch)_src_files)
+host_src_files  := \$(filter-out \$(\$(host_arch)_exclude_files), \$(host_src_files))
+
+local_additional_dependencies += \$(LOCAL_PATH)/$(basename $output)
+"
+
+  ) > "$output"
+}
+
+function import() {
+  declare -r SCRYPT_SOURCE=$1
+
+  untar $SCRYPT_SOURCE readonly
+  applypatches $SCRYPT_DIR
+
+  cd $SCRYPT_DIR
+
+  generate_build_config_mk ../build-config.mk
+
+  touch ../MODULE_LICENSE_BSD_LIKE
+
+  cd ..
+
+  generate_config_mk Scrypt-config.mk
+
+  # Prune unnecessary sources
+  prune
+
+  NEEDED_SOURCES="$NEEDED_SOURCES"
+  for i in $NEEDED_SOURCES; do
+    echo "Updating $i"
+    rm -r $i
+    mv $SCRYPT_DIR/$i .
+  done
+
+  cleantar
+}
+
+function regenerate() {
+  declare -r patch=$1
+
+  generatepatch $patch
+}
+
+function generate() {
+  declare -r patch=$1
+  declare -r SCRYPT_SOURCE=$2
+
+  untar $SCRYPT_SOURCE
+  applypatches $SCRYPT_DIR_ORIG $patch
+  prune
+
+  for i in $NEEDED_SOURCES; do
+    echo "Restoring $i"
+    rm -r $SCRYPT_DIR/$i
+    cp -rf $i $SCRYPT_DIR/$i
+  done
+
+  generatepatch $patch
+  cleantar
+}
+
+# Find all files in a sub-directory that are encoded in ISO-8859
+# $1: Directory.
+# Out: list of files in $1 that are encoded as ISO-8859.
+function find_iso8859_files() {
+  find $1 -type f -print0 | xargs -0 file | fgrep "ISO-8859" | cut -d: -f1
+}
+
+# Convert all ISO-8859 files in a given subdirectory to UTF-8
+# $1: Directory name
+function convert_iso8859_to_utf8() {
+  declare -r iso_files=$(find_iso8859_files "$1")
+  for iso_file in $iso_files; do
+    iconv --from-code iso-8859-1 --to-code utf-8 $iso_file > $iso_file.tmp
+    rm -f $iso_file
+    mv $iso_file.tmp $iso_file
+  done
+}
+
+function untar() {
+  declare -r SCRYPT_SOURCE=$1
+  declare -r readonly=$2
+
+  # Remove old source
+  cleantar
+
+  # Process new source
+  tar -zxf $SCRYPT_SOURCE
+  convert_iso8859_to_utf8 $SCRYPT_DIR
+  cp -rfP $SCRYPT_DIR $SCRYPT_DIR_ORIG
+  if [ ! -z $readonly ]; then
+    find $SCRYPT_DIR_ORIG -type f -print0 | xargs -0 chmod a-w
+  fi
+}
+
+function prune() {
+  echo "Removing $UNNEEDED_SOURCES"
+  (cd $SCRYPT_DIR_ORIG && rm -rf $UNNEEDED_SOURCES)
+  (cd $SCRYPT_DIR      && rm -r  $UNNEEDED_SOURCES)
+}
+
+function cleantar() {
+  rm -rf $SCRYPT_DIR_ORIG
+  rm -rf $SCRYPT_DIR
+}
+
+function applypatches () {
+  declare -r dir=$1
+  declare -r skip_patch=$2
+
+  cd $dir
+
+  # Apply appropriate patches
+  for i in $SCRYPT_PATCHES; do
+    if [ ! "$skip_patch" = "patches/$i" ]; then
+      echo "Applying patch $i"
+      patch -p1 --merge < ../patches/$i || die "Could not apply patches/$i. Fix source and run: $0 regenerate patches/$i"
+    else
+      echo "Skiping patch $i"
+    fi
+
+  done
+
+  # Cleanup patch output
+  find . \( -type f -o -type l \) -name "*.orig" -print0 | xargs -0 rm -f
+
+  cd ..
+}
+
+function generatepatch() {
+  declare -r patch=$1
+
+  # Cleanup stray files before generating patch
+  find $SCRYPT_DIR -type f -name "*.orig" -print0 | xargs -0 rm -f
+  find $SCRYPT_DIR -type f -name "*~" -print0 | xargs -0 rm -f
+
+  declare -r variable_name=SCRYPT_PATCHES_`basename $patch .patch | sed s/-/_/`_SOURCES
+  # http://tldp.org/LDP/abs/html/ivr.html
+  eval declare -r sources=\$$variable_name
+  rm -f $patch
+  touch $patch
+  for i in $sources; do
+    LC_ALL=C TZ=UTC0 diff -aup $SCRYPT_DIR_ORIG/$i $SCRYPT_DIR/$i >> $patch && die "ERROR: No diff for patch $path in file $i"
+  done
+  echo "Generated patch $patch"
+  echo "NOTE To make sure there are not unwanted changes from conflicting patches, be sure to review the generated patch."
+}
+
+main $@
diff --git a/crypto/scrypt/lib/README b/crypto/scrypt/lib/README
new file mode 100644
index 0000000..3bb211e
--- /dev/null
+++ b/crypto/scrypt/lib/README
@@ -0,0 +1,6 @@
+The source code under this directory is taken from the client for the
+Tarsnap online backup system (and released under the 2-clause BSD license
+with permission of the author); keeping this code in sync with the Tarsnap
+code is highly desirable and explains why there is some functionality
+included here which is not actually used by the scrypt file encryption
+utility.
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h b/crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h
new file mode 100644
index 0000000..a3b1019
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h
@@ -0,0 +1,120 @@
+/*
+ * version 20110505
+ * D. J. Bernstein
+ * Public domain.
+ *
+ * Based on crypto_core/salsa208/armneon/core.c from SUPERCOP 20130419
+ */
+
+#define ROUNDS 8
+static void
+salsa20_8_intrinsic(void * input)
+{
+  int i;
+
+  const uint32x4_t abab = {-1,0,-1,0};
+
+  /*
+   * This is modified since we only have one argument. Usually you'd rearrange
+   * the constant, key, and input bytes, but we just have one linear array to
+   * rearrange which is a bit easier.
+   */
+
+  /*
+   * Change the input to be diagonals as if it's a 4x4 matrix of 32-bit values.
+   */
+  uint32x4_t x0x5x10x15;
+  uint32x4_t x12x1x6x11;
+  uint32x4_t x8x13x2x7;
+  uint32x4_t x4x9x14x3;
+
+  uint32x4_t x0x1x10x11;
+  uint32x4_t x12x13x6x7;
+  uint32x4_t x8x9x2x3;
+  uint32x4_t x4x5x14x15;
+
+  uint32x4_t x0x1x2x3;
+  uint32x4_t x4x5x6x7;
+  uint32x4_t x8x9x10x11;
+  uint32x4_t x12x13x14x15;
+
+  x0x1x2x3 = vld1q_u8((uint8_t *) input);
+  x4x5x6x7 = vld1q_u8(16 + (uint8_t *) input);
+  x8x9x10x11 = vld1q_u8(32 + (uint8_t *) input);
+  x12x13x14x15 = vld1q_u8(48 + (uint8_t *) input);
+
+  x0x1x10x11 = vcombine_u32(vget_low_u32(x0x1x2x3), vget_high_u32(x8x9x10x11));
+  x4x5x14x15 = vcombine_u32(vget_low_u32(x4x5x6x7), vget_high_u32(x12x13x14x15));
+  x8x9x2x3 = vcombine_u32(vget_low_u32(x8x9x10x11), vget_high_u32(x0x1x2x3));
+  x12x13x6x7 = vcombine_u32(vget_low_u32(x12x13x14x15), vget_high_u32(x4x5x6x7));
+
+  x0x5x10x15 = vbslq_u32(abab,x0x1x10x11,x4x5x14x15);
+  x8x13x2x7 = vbslq_u32(abab,x8x9x2x3,x12x13x6x7);
+  x4x9x14x3 = vbslq_u32(abab,x4x5x14x15,x8x9x2x3);
+  x12x1x6x11 = vbslq_u32(abab,x12x13x6x7,x0x1x10x11);
+
+  uint32x4_t start0 = x0x5x10x15;
+  uint32x4_t start1 = x12x1x6x11;
+  uint32x4_t start3 = x4x9x14x3;
+  uint32x4_t start2 = x8x13x2x7;
+
+  /* From here on this should be the same as the SUPERCOP version. */
+
+  uint32x4_t diag0 = start0;
+  uint32x4_t diag1 = start1;
+  uint32x4_t diag2 = start2;
+  uint32x4_t diag3 = start3;
+
+  uint32x4_t a0;
+  uint32x4_t a1;
+  uint32x4_t a2;
+  uint32x4_t a3;
+
+  for (i = ROUNDS;i > 0;i -= 2) {
+    a0 = diag1 + diag0;
+    diag3 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25);
+    a1 = diag0 + diag3;
+    diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23);
+    a2 = diag3 + diag2;
+    diag1 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19);
+    a3 = diag2 + diag1;
+    diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14);
+
+    diag3 = vextq_u32(diag3,diag3,3);
+    diag2 = vextq_u32(diag2,diag2,2);
+    diag1 = vextq_u32(diag1,diag1,1);
+
+    a0 = diag3 + diag0;
+    diag1 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25);
+    a1 = diag0 + diag1;
+    diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23);
+    a2 = diag1 + diag2;
+    diag3 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19);
+    a3 = diag2 + diag3;
+    diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14);
+
+    diag1 = vextq_u32(diag1,diag1,3);
+    diag2 = vextq_u32(diag2,diag2,2);
+    diag3 = vextq_u32(diag3,diag3,1);
+  }
+
+  x0x5x10x15 = diag0 + start0;
+  x12x1x6x11 = diag1 + start1;
+  x8x13x2x7 = diag2 + start2;
+  x4x9x14x3 = diag3 + start3;
+
+  x0x1x10x11 = vbslq_u32(abab,x0x5x10x15,x12x1x6x11);
+  x12x13x6x7 = vbslq_u32(abab,x12x1x6x11,x8x13x2x7);
+  x8x9x2x3 = vbslq_u32(abab,x8x13x2x7,x4x9x14x3);
+  x4x5x14x15 = vbslq_u32(abab,x4x9x14x3,x0x5x10x15);
+
+  x0x1x2x3 = vcombine_u32(vget_low_u32(x0x1x10x11),vget_high_u32(x8x9x2x3));
+  x4x5x6x7 = vcombine_u32(vget_low_u32(x4x5x14x15),vget_high_u32(x12x13x6x7));
+  x8x9x10x11 = vcombine_u32(vget_low_u32(x8x9x2x3),vget_high_u32(x0x1x10x11));
+  x12x13x14x15 = vcombine_u32(vget_low_u32(x12x13x6x7),vget_high_u32(x4x5x14x15));
+
+  vst1q_u8((uint8_t *) input,(uint8x16_t) x0x1x2x3);
+  vst1q_u8(16 + (uint8_t *) input,(uint8x16_t) x4x5x6x7);
+  vst1q_u8(32 + (uint8_t *) input,(uint8x16_t) x8x9x10x11);
+  vst1q_u8(48 + (uint8_t *) input,(uint8x16_t) x12x13x14x15);
+}
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-neon.c b/crypto/scrypt/lib/crypto/crypto_scrypt-neon.c
new file mode 100644
index 0000000..a3bf052
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt-neon.c
@@ -0,0 +1,305 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#include "scrypt_platform.h"
+
+#include <machine/cpu-features.h>
+#include <arm_neon.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_OPENSSL_PBKDF2
+#include <openssl/evp.h>
+#else
+#include "sha256.h"
+#endif
+#include "sysendian.h"
+
+#include "crypto_scrypt.h"
+
+#include "crypto_scrypt-neon-salsa208.h"
+
+static void blkcpy(void *, void *, size_t);
+static void blkxor(void *, void *, size_t);
+void crypto_core_salsa208_armneon2(void *);
+static void blockmix_salsa8(uint8x16_t *, uint8x16_t *, uint8x16_t *, size_t);
+static uint64_t integerify(void *, size_t);
+static void smix(uint8_t *, size_t, uint64_t, void *, void *);
+
+static void
+blkcpy(void * dest, void * src, size_t len)
+{
+	uint8x16_t * D = dest;
+	uint8x16_t * S = src;
+	size_t L = len / 16;
+	size_t i;
+
+	for (i = 0; i < L; i++)
+		D[i] = S[i];
+}
+
+static void
+blkxor(void * dest, void * src, size_t len)
+{
+	uint8x16_t * D = dest;
+	uint8x16_t * S = src;
+	size_t L = len / 16;
+	size_t i;
+
+	for (i = 0; i < L; i++)
+		D[i] = veorq_u8(D[i], S[i]);
+}
+
+/**
+ * blockmix_salsa8(B, Y, r):
+ * Compute B = BlockMix_{salsa20/8, r}(B).  The input B must be 128r bytes in
+ * length; the temporary space Y must also be the same size.
+ */
+static void
+blockmix_salsa8(uint8x16_t * Bin, uint8x16_t * Bout, uint8x16_t * X, size_t r)
+{
+	size_t i;
+
+	/* 1: X <-- B_{2r - 1} */
+	blkcpy(X, &Bin[8 * r - 4], 64);
+
+	/* 2: for i = 0 to 2r - 1 do */
+	for (i = 0; i < r; i++) {
+		/* 3: X <-- H(X \xor B_i) */
+		blkxor(X, &Bin[i * 8], 64);
+                salsa20_8_intrinsic((void *) X);
+
+		/* 4: Y_i <-- X */
+		/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+		blkcpy(&Bout[i * 4], X, 64);
+
+		/* 3: X <-- H(X \xor B_i) */
+		blkxor(X, &Bin[i * 8 + 4], 64);
+                salsa20_8_intrinsic((void *) X);
+
+		/* 4: Y_i <-- X */
+		/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+		blkcpy(&Bout[(r + i) * 4], X, 64);
+	}
+}
+
+/**
+ * integerify(B, r):
+ * Return the result of parsing B_{2r-1} as a little-endian integer.
+ */
+static uint64_t
+integerify(void * B, size_t r)
+{
+	uint8_t * X = (void*)((uintptr_t)(B) + (2 * r - 1) * 64);
+
+	return (le64dec(X));
+}
+
+/**
+ * smix(B, r, N, V, XY):
+ * Compute B = SMix_r(B, N).  The input B must be 128r bytes in length; the
+ * temporary storage V must be 128rN bytes in length; the temporary storage
+ * XY must be 256r bytes in length.  The value N must be a power of 2.
+ */
+static void
+smix(uint8_t * B, size_t r, uint64_t N, void * V, void * XY)
+{
+	uint8x16_t * X = XY;
+	uint8x16_t * Y = (void *)((uintptr_t)(XY) + 128 * r);
+        uint8x16_t * Z = (void *)((uintptr_t)(XY) + 256 * r);
+        uint32_t * X32 = (void *)X;
+	uint64_t i, j;
+        size_t k;
+
+	/* 1: X <-- B */
+	blkcpy(X, B, 128 * r);
+
+	/* 2: for i = 0 to N - 1 do */
+	for (i = 0; i < N; i += 2) {
+		/* 3: V_i <-- X */
+		blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r);
+
+		/* 4: X <-- H(X) */
+		blockmix_salsa8(X, Y, Z, r);
+
+		/* 3: V_i <-- X */
+		blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r),
+		    Y, 128 * r);
+
+		/* 4: X <-- H(X) */
+		blockmix_salsa8(Y, X, Z, r);
+	}
+
+	/* 6: for i = 0 to N - 1 do */
+	for (i = 0; i < N; i += 2) {
+		/* 7: j <-- Integerify(X) mod N */
+		j = integerify(X, r) & (N - 1);
+
+		/* 8: X <-- H(X \xor V_j) */
+		blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
+		blockmix_salsa8(X, Y, Z, r);
+
+		/* 7: j <-- Integerify(X) mod N */
+		j = integerify(Y, r) & (N - 1);
+
+		/* 8: X <-- H(X \xor V_j) */
+		blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
+		blockmix_salsa8(Y, X, Z, r);
+	}
+
+	/* 10: B' <-- X */
+	blkcpy(B, X, 128 * r);
+}
+
+/**
+ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen) and write the result into buf.  The parameters r, p, and buflen
+ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32.  The parameter N
+ * must be a power of 2.
+ *
+ * Return 0 on success; or -1 on error.
+ */
+int
+crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+    const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,
+    uint8_t * buf, size_t buflen)
+{
+	void * B0, * V0, * XY0;
+	uint8_t * B;
+	uint32_t * V;
+	uint32_t * XY;
+	uint32_t i;
+
+	/* Sanity-check parameters. */
+#if SIZE_MAX > UINT32_MAX
+	if (buflen > (((uint64_t)(1) << 32) - 1) * 32) {
+		errno = EFBIG;
+		goto err0;
+	}
+#endif
+	if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) {
+		errno = EFBIG;
+		goto err0;
+	}
+	if (((N & (N - 1)) != 0) || (N == 0)) {
+		errno = EINVAL;
+		goto err0;
+	}
+	if ((r > SIZE_MAX / 128 / p) ||
+#if SIZE_MAX / 256 <= UINT32_MAX
+	    (r > SIZE_MAX / 256) ||
+#endif
+	    (N > SIZE_MAX / 128 / r)) {
+		errno = ENOMEM;
+		goto err0;
+	}
+
+	/* Allocate memory. */
+#ifdef HAVE_POSIX_MEMALIGN
+	if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0)
+		goto err0;
+	B = (uint8_t *)(B0);
+	if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0)
+		goto err1;
+	XY = (uint32_t *)(XY0);
+#ifndef MAP_ANON
+	if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0)
+		goto err2;
+	V = (uint32_t *)(V0);
+#endif
+#else
+	if ((B0 = malloc(128 * r * p + 63)) == NULL)
+		goto err0;
+	B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63));
+	if ((XY0 = malloc(256 * r + 64 + 63)) == NULL)
+		goto err1;
+	XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63));
+#ifndef MAP_ANON
+	if ((V0 = malloc(128 * r * N + 63)) == NULL)
+		goto err2;
+	V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63));
+#endif
+#endif
+#ifdef MAP_ANON
+	if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE,
+#ifdef MAP_NOCORE
+	    MAP_ANON | MAP_PRIVATE | MAP_NOCORE,
+#else
+	    MAP_ANON | MAP_PRIVATE,
+#endif
+	    -1, 0)) == MAP_FAILED)
+		goto err2;
+	V = (uint32_t *)(V0);
+#endif
+
+	/* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
+#ifdef USE_OPENSSL_PBKDF2
+	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
+#else
+	PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
+#endif
+
+	/* 2: for i = 0 to p - 1 do */
+	for (i = 0; i < p; i++) {
+		/* 3: B_i <-- MF(B_i, N) */
+		smix(&B[i * 128 * r], r, N, V, XY);
+	}
+
+	/* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
+#ifdef USE_OPENSSL_PBKDF2
+	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
+#else
+	PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
+#endif
+
+	/* Free memory. */
+#ifdef MAP_ANON
+	if (munmap(V0, 128 * r * N))
+		goto err2;
+#else
+	free(V0);
+#endif
+	free(XY0);
+	free(B0);
+
+	/* Success! */
+	return (0);
+
+err2:
+	free(XY0);
+err1:
+	free(B0);
+err0:
+	/* Failure! */
+	return (-1);
+}
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-ref.c b/crypto/scrypt/lib/crypto/crypto_scrypt-ref.c
new file mode 100644
index 0000000..abe23ea
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt-ref.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#include "scrypt_platform.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_OPENSSL_PBKDF2
+#include <openssl/evp.h>
+#else
+#include "sha256.h"
+#endif
+#include "sysendian.h"
+
+#include "crypto_scrypt.h"
+
+static void blkcpy(uint8_t *, uint8_t *, size_t);
+static void blkxor(uint8_t *, uint8_t *, size_t);
+static void salsa20_8(uint8_t[64]);
+static void blockmix_salsa8(uint8_t *, uint8_t *, size_t);
+static uint64_t integerify(uint8_t *, size_t);
+static void smix(uint8_t *, size_t, uint64_t, uint8_t *, uint8_t *);
+
+static void
+blkcpy(uint8_t * dest, uint8_t * src, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		dest[i] = src[i];
+}
+
+static void
+blkxor(uint8_t * dest, uint8_t * src, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		dest[i] ^= src[i];
+}
+
+/**
+ * salsa20_8(B):
+ * Apply the salsa20/8 core to the provided block.
+ */
+static void
+salsa20_8(uint8_t B[64])
+{
+	uint32_t B32[16];
+	uint32_t x[16];
+	size_t i;
+
+	/* Convert little-endian values in. */
+	for (i = 0; i < 16; i++)
+		B32[i] = le32dec(&B[i * 4]);
+
+	/* Compute x = doubleround^4(B32). */
+	for (i = 0; i < 16; i++)
+		x[i] = B32[i];
+	for (i = 0; i < 8; i += 2) {
+#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+		/* Operate on columns. */
+		x[ 4] ^= R(x[ 0]+x[12], 7);  x[ 8] ^= R(x[ 4]+x[ 0], 9);
+		x[12] ^= R(x[ 8]+x[ 4],13);  x[ 0] ^= R(x[12]+x[ 8],18);
+
+		x[ 9] ^= R(x[ 5]+x[ 1], 7);  x[13] ^= R(x[ 9]+x[ 5], 9);
+		x[ 1] ^= R(x[13]+x[ 9],13);  x[ 5] ^= R(x[ 1]+x[13],18);
+
+		x[14] ^= R(x[10]+x[ 6], 7);  x[ 2] ^= R(x[14]+x[10], 9);
+		x[ 6] ^= R(x[ 2]+x[14],13);  x[10] ^= R(x[ 6]+x[ 2],18);
+
+		x[ 3] ^= R(x[15]+x[11], 7);  x[ 7] ^= R(x[ 3]+x[15], 9);
+		x[11] ^= R(x[ 7]+x[ 3],13);  x[15] ^= R(x[11]+x[ 7],18);
+
+		/* Operate on rows. */
+		x[ 1] ^= R(x[ 0]+x[ 3], 7);  x[ 2] ^= R(x[ 1]+x[ 0], 9);
+		x[ 3] ^= R(x[ 2]+x[ 1],13);  x[ 0] ^= R(x[ 3]+x[ 2],18);
+
+		x[ 6] ^= R(x[ 5]+x[ 4], 7);  x[ 7] ^= R(x[ 6]+x[ 5], 9);
+		x[ 4] ^= R(x[ 7]+x[ 6],13);  x[ 5] ^= R(x[ 4]+x[ 7],18);
+
+		x[11] ^= R(x[10]+x[ 9], 7);  x[ 8] ^= R(x[11]+x[10], 9);
+		x[ 9] ^= R(x[ 8]+x[11],13);  x[10] ^= R(x[ 9]+x[ 8],18);
+
+		x[12] ^= R(x[15]+x[14], 7);  x[13] ^= R(x[12]+x[15], 9);
+		x[14] ^= R(x[13]+x[12],13);  x[15] ^= R(x[14]+x[13],18);
+#undef R
+	}
+
+	/* Compute B32 = B32 + x. */
+	for (i = 0; i < 16; i++)
+		B32[i] += x[i];
+
+	/* Convert little-endian values out. */
+	for (i = 0; i < 16; i++)
+		le32enc(&B[4 * i], B32[i]);
+}
+
+/**
+ * blockmix_salsa8(B, Y, r):
+ * Compute B = BlockMix_{salsa20/8, r}(B).  The input B must be 128r bytes in
+ * length; the temporary space Y must also be the same size.
+ */
+static void
+blockmix_salsa8(uint8_t * B, uint8_t * Y, size_t r)
+{
+	uint8_t X[64];
+	size_t i;
+
+	/* 1: X <-- B_{2r - 1} */
+	blkcpy(X, &B[(2 * r - 1) * 64], 64);
+
+	/* 2: for i = 0 to 2r - 1 do */
+	for (i = 0; i < 2 * r; i++) {
+		/* 3: X <-- H(X \xor B_i) */
+		blkxor(X, &B[i * 64], 64);
+		salsa20_8(X);
+
+		/* 4: Y_i <-- X */
+		blkcpy(&Y[i * 64], X, 64);
+	}
+
+	/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+	for (i = 0; i < r; i++)
+		blkcpy(&B[i * 64], &Y[(i * 2) * 64], 64);
+	for (i = 0; i < r; i++)
+		blkcpy(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64);
+}
+
+/**
+ * integerify(B, r):
+ * Return the result of parsing B_{2r-1} as a little-endian integer.
+ */
+static uint64_t
+integerify(uint8_t * B, size_t r)
+{
+	uint8_t * X = &B[(2 * r - 1) * 64];
+
+	return (le64dec(X));
+}
+
+/**
+ * smix(B, r, N, V, XY):
+ * Compute B = SMix_r(B, N).  The input B must be 128r bytes in length; the
+ * temporary storage V must be 128rN bytes in length; the temporary storage
+ * XY must be 256r bytes in length.  The value N must be a power of 2.
+ */
+static void
+smix(uint8_t * B, size_t r, uint64_t N, uint8_t * V, uint8_t * XY)
+{
+	uint8_t * X = XY;
+	uint8_t * Y = &XY[128 * r];
+	uint64_t i;
+	uint64_t j;
+
+	/* 1: X <-- B */
+	blkcpy(X, B, 128 * r);
+
+	/* 2: for i = 0 to N - 1 do */
+	for (i = 0; i < N; i++) {
+		/* 3: V_i <-- X */
+		blkcpy(&V[i * (128 * r)], X, 128 * r);
+
+		/* 4: X <-- H(X) */
+		blockmix_salsa8(X, Y, r);
+	}
+
+	/* 6: for i = 0 to N - 1 do */
+	for (i = 0; i < N; i++) {
+		/* 7: j <-- Integerify(X) mod N */
+		j = integerify(X, r) & (N - 1);
+
+		/* 8: X <-- H(X \xor V_j) */
+		blkxor(X, &V[j * (128 * r)], 128 * r);
+		blockmix_salsa8(X, Y, r);
+	}
+
+	/* 10: B' <-- X */
+	blkcpy(B, X, 128 * r);
+}
+
+/**
+ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen) and write the result into buf.  The parameters r, p, and buflen
+ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32.  The parameter N
+ * must be a power of 2.
+ *
+ * Return 0 on success; or -1 on error.
+ */
+int
+crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+    const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,
+    uint8_t * buf, size_t buflen)
+{
+	uint8_t * B;
+	uint8_t * V;
+	uint8_t * XY;
+	uint32_t i;
+
+	/* Sanity-check parameters. */
+#if SIZE_MAX > UINT32_MAX
+	if (buflen > (((uint64_t)(1) << 32) - 1) * 32) {
+		errno = EFBIG;
+		goto err0;
+	}
+#endif
+	if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) {
+		errno = EFBIG;
+		goto err0;
+	}
+	if (((N & (N - 1)) != 0) || (N == 0)) {
+		errno = EINVAL;
+		goto err0;
+	}
+	if ((r > SIZE_MAX / 128 / p) ||
+#if SIZE_MAX / 256 <= UINT32_MAX
+	    (r > SIZE_MAX / 256) ||
+#endif
+	    (N > SIZE_MAX / 128 / r)) {
+		errno = ENOMEM;
+		goto err0;
+	}
+
+	/* Allocate memory. */
+	if ((B = malloc(128 * r * p)) == NULL)
+		goto err0;
+	if ((XY = malloc(256 * r)) == NULL)
+		goto err1;
+	if ((V = malloc(128 * r * N)) == NULL)
+		goto err2;
+
+	/* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
+#ifdef USE_OPENSSL_PBKDF2
+	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
+#else
+	PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
+#endif
+
+	/* 2: for i = 0 to p - 1 do */
+	for (i = 0; i < p; i++) {
+		/* 3: B_i <-- MF(B_i, N) */
+		smix(&B[i * 128 * r], r, N, V, XY);
+	}
+
+	/* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
+#ifdef USE_OPENSSL_PBKDF2
+	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
+#else
+	PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
+#endif
+
+	/* Free memory. */
+	free(V);
+	free(XY);
+	free(B);
+
+	/* Success! */
+	return (0);
+
+err2:
+	free(XY);
+err1:
+	free(B);
+err0:
+	/* Failure! */
+	return (-1);
+}
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-sse.c b/crypto/scrypt/lib/crypto/crypto_scrypt-sse.c
new file mode 100644
index 0000000..dd18f29
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt-sse.c
@@ -0,0 +1,378 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#include "scrypt_platform.h"
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <emmintrin.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_OPENSSL_PBKDF2
+#include <openssl/evp.h>
+#else
+#include "sha256.h"
+#endif
+#include "sysendian.h"
+
+#include "crypto_scrypt.h"
+
+static void blkcpy(void *, void *, size_t);
+static void blkxor(void *, void *, size_t);
+static void salsa20_8(__m128i *);
+static void blockmix_salsa8(__m128i *, __m128i *, __m128i *, size_t);
+static uint64_t integerify(void *, size_t);
+static void smix(uint8_t *, size_t, uint64_t, void *, void *);
+
+static void
+blkcpy(void * dest, void * src, size_t len)
+{
+	__m128i * D = dest;
+	__m128i * S = src;
+	size_t L = len / 16;
+	size_t i;
+
+	for (i = 0; i < L; i++)
+		D[i] = S[i];
+}
+
+static void
+blkxor(void * dest, void * src, size_t len)
+{
+	__m128i * D = dest;
+	__m128i * S = src;
+	size_t L = len / 16;
+	size_t i;
+
+	for (i = 0; i < L; i++)
+		D[i] = _mm_xor_si128(D[i], S[i]);
+}
+
+/**
+ * salsa20_8(B):
+ * Apply the salsa20/8 core to the provided block.
+ */
+static void
+salsa20_8(__m128i B[4])
+{
+	__m128i X0, X1, X2, X3;
+	__m128i T;
+	size_t i;
+
+	X0 = B[0];
+	X1 = B[1];
+	X2 = B[2];
+	X3 = B[3];
+
+	for (i = 0; i < 8; i += 2) {
+		/* Operate on "columns". */
+		T = _mm_add_epi32(X0, X3);
+		X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7));
+		X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25));
+		T = _mm_add_epi32(X1, X0);
+		X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9));
+		X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23));
+		T = _mm_add_epi32(X2, X1);
+		X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 13));
+		X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19));
+		T = _mm_add_epi32(X3, X2);
+		X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18));
+		X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14));
+
+		/* Rearrange data. */
+		X1 = _mm_shuffle_epi32(X1, 0x93);
+		X2 = _mm_shuffle_epi32(X2, 0x4E);
+		X3 = _mm_shuffle_epi32(X3, 0x39);
+
+		/* Operate on "rows". */
+		T = _mm_add_epi32(X0, X1);
+		X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 7));
+		X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25));
+		T = _mm_add_epi32(X3, X0);
+		X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9));
+		X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23));
+		T = _mm_add_epi32(X2, X3);
+		X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 13));
+		X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19));
+		T = _mm_add_epi32(X1, X2);
+		X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18));
+		X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14));
+
+		/* Rearrange data. */
+		X1 = _mm_shuffle_epi32(X1, 0x39);
+		X2 = _mm_shuffle_epi32(X2, 0x4E);
+		X3 = _mm_shuffle_epi32(X3, 0x93);
+	}
+
+	B[0] = _mm_add_epi32(B[0], X0);
+	B[1] = _mm_add_epi32(B[1], X1);
+	B[2] = _mm_add_epi32(B[2], X2);
+	B[3] = _mm_add_epi32(B[3], X3);
+}
+
+/**
+ * blockmix_salsa8(Bin, Bout, X, r):
+ * Compute Bout = BlockMix_{salsa20/8, r}(Bin).  The input Bin must be 128r
+ * bytes in length; the output Bout must also be the same size.  The
+ * temporary space X must be 64 bytes.
+ */
+static void
+blockmix_salsa8(__m128i * Bin, __m128i * Bout, __m128i * X, size_t r)
+{
+	size_t i;
+
+	/* 1: X <-- B_{2r - 1} */
+	blkcpy(X, &Bin[8 * r - 4], 64);
+
+	/* 2: for i = 0 to 2r - 1 do */
+	for (i = 0; i < r; i++) {
+		/* 3: X <-- H(X \xor B_i) */
+		blkxor(X, &Bin[i * 8], 64);
+		salsa20_8(X);
+
+		/* 4: Y_i <-- X */
+		/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+		blkcpy(&Bout[i * 4], X, 64);
+
+		/* 3: X <-- H(X \xor B_i) */
+		blkxor(X, &Bin[i * 8 + 4], 64);
+		salsa20_8(X);
+
+		/* 4: Y_i <-- X */
+		/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+		blkcpy(&Bout[(r + i) * 4], X, 64);
+	}
+}
+
+/**
+ * integerify(B, r):
+ * Return the result of parsing B_{2r-1} as a little-endian integer.
+ */
+static uint64_t
+integerify(void * B, size_t r)
+{
+	uint32_t * X = (void *)((uintptr_t)(B) + (2 * r - 1) * 64);
+
+	return (((uint64_t)(X[13]) << 32) + X[0]);
+}
+
+/**
+ * smix(B, r, N, V, XY):
+ * Compute B = SMix_r(B, N).  The input B must be 128r bytes in length;
+ * the temporary storage V must be 128rN bytes in length; the temporary
+ * storage XY must be 256r + 64 bytes in length.  The value N must be a
+ * power of 2 greater than 1.  The arrays B, V, and XY must be aligned to a
+ * multiple of 64 bytes.
+ */
+static void
+smix(uint8_t * B, size_t r, uint64_t N, void * V, void * XY)
+{
+	__m128i * X = XY;
+	__m128i * Y = (void *)((uintptr_t)(XY) + 128 * r);
+	__m128i * Z = (void *)((uintptr_t)(XY) + 256 * r);
+	uint32_t * X32 = (void *)X;
+	uint64_t i, j;
+	size_t k;
+
+	/* 1: X <-- B */
+	for (k = 0; k < 2 * r; k++) {
+		for (i = 0; i < 16; i++) {
+			X32[k * 16 + i] =
+			    le32dec(&B[(k * 16 + (i * 5 % 16)) * 4]);
+		}
+	}
+
+	/* 2: for i = 0 to N - 1 do */
+	for (i = 0; i < N; i += 2) {
+		/* 3: V_i <-- X */
+		blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r);
+
+		/* 4: X <-- H(X) */
+		blockmix_salsa8(X, Y, Z, r);
+
+		/* 3: V_i <-- X */
+		blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r),
+		    Y, 128 * r);
+
+		/* 4: X <-- H(X) */
+		blockmix_salsa8(Y, X, Z, r);
+	}
+
+	/* 6: for i = 0 to N - 1 do */
+	for (i = 0; i < N; i += 2) {
+		/* 7: j <-- Integerify(X) mod N */
+		j = integerify(X, r) & (N - 1);
+
+		/* 8: X <-- H(X \xor V_j) */
+		blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
+		blockmix_salsa8(X, Y, Z, r);
+
+		/* 7: j <-- Integerify(X) mod N */
+		j = integerify(Y, r) & (N - 1);
+
+		/* 8: X <-- H(X \xor V_j) */
+		blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
+		blockmix_salsa8(Y, X, Z, r);
+	}
+
+	/* 10: B' <-- X */
+	for (k = 0; k < 2 * r; k++) {
+		for (i = 0; i < 16; i++) {
+			le32enc(&B[(k * 16 + (i * 5 % 16)) * 4],
+			    X32[k * 16 + i]);
+		}
+	}
+}
+
+/**
+ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen) and write the result into buf.  The parameters r, p, and buflen
+ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32.  The parameter N
+ * must be a power of 2 greater than 1.
+ *
+ * Return 0 on success; or -1 on error.
+ */
+int
+crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+    const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,
+    uint8_t * buf, size_t buflen)
+{
+	void * B0, * V0, * XY0;
+	uint8_t * B;
+	uint32_t * V;
+	uint32_t * XY;
+	uint32_t i;
+
+	/* Sanity-check parameters. */
+#if SIZE_MAX > UINT32_MAX
+	if (buflen > (((uint64_t)(1) << 32) - 1) * 32) {
+		errno = EFBIG;
+		goto err0;
+	}
+#endif
+	if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) {
+		errno = EFBIG;
+		goto err0;
+	}
+	if (((N & (N - 1)) != 0) || (N == 0)) {
+		errno = EINVAL;
+		goto err0;
+	}
+	if ((r > SIZE_MAX / 128 / p) ||
+#if SIZE_MAX / 256 <= UINT32_MAX
+	    (r > (SIZE_MAX - 64) / 256) ||
+#endif
+	    (N > SIZE_MAX / 128 / r)) {
+		errno = ENOMEM;
+		goto err0;
+	}
+
+	/* Allocate memory. */
+#ifdef HAVE_POSIX_MEMALIGN
+	if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0)
+		goto err0;
+	B = (uint8_t *)(B0);
+	if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0)
+		goto err1;
+	XY = (uint32_t *)(XY0);
+#ifndef MAP_ANON
+	if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0)
+		goto err2;
+	V = (uint32_t *)(V0);
+#endif
+#else
+	if ((B0 = malloc(128 * r * p + 63)) == NULL)
+		goto err0;
+	B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63));
+	if ((XY0 = malloc(256 * r + 64 + 63)) == NULL)
+		goto err1;
+	XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63));
+#ifndef MAP_ANON
+	if ((V0 = malloc(128 * r * N + 63)) == NULL)
+		goto err2;
+	V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63));
+#endif
+#endif
+#ifdef MAP_ANON
+	if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE,
+#ifdef MAP_NOCORE
+	    MAP_ANON | MAP_PRIVATE | MAP_NOCORE,
+#else
+	    MAP_ANON | MAP_PRIVATE,
+#endif
+	    -1, 0)) == MAP_FAILED)
+		goto err2;
+	V = (uint32_t *)(V0);
+#endif
+
+	/* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
+#ifdef USE_OPENSSL_PBKDF2
+	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
+#else
+	PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
+#endif
+
+	/* 2: for i = 0 to p - 1 do */
+	for (i = 0; i < p; i++) {
+		/* 3: B_i <-- MF(B_i, N) */
+		smix(&B[i * 128 * r], r, N, V, XY);
+	}
+
+	/* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
+#ifdef USE_OPENSSL_PBKDF2
+	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
+#else
+	PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
+#endif
+
+	/* Free memory. */
+#ifdef MAP_ANON
+	if (munmap(V0, 128 * r * N))
+		goto err2;
+#else
+	free(V0);
+#endif
+	free(XY0);
+	free(B0);
+
+	/* Success! */
+	return (0);
+
+err2:
+	free(XY0);
+err1:
+	free(B0);
+err0:
+	/* Failure! */
+	return (-1);
+}
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt.h b/crypto/scrypt/lib/crypto/crypto_scrypt.h
new file mode 100644
index 0000000..f72e1f4
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt.h
@@ -0,0 +1,46 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#ifndef _CRYPTO_SCRYPT_H_
+#define _CRYPTO_SCRYPT_H_
+
+#include <stdint.h>
+
+/**
+ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen) and write the result into buf.  The parameters r, p, and buflen
+ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32.  The parameter N
+ * must be a power of 2 greater than 1.
+ *
+ * Return 0 on success; or -1 on error.
+ */
+int crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t,
+    uint32_t, uint32_t, uint8_t *, size_t);
+
+#endif /* !_CRYPTO_SCRYPT_H_ */
diff --git a/crypto/scrypt/lib/util/sysendian.h b/crypto/scrypt/lib/util/sysendian.h
new file mode 100644
index 0000000..62ef31a
--- /dev/null
+++ b/crypto/scrypt/lib/util/sysendian.h
@@ -0,0 +1,140 @@
+/*-
+ * Copyright 2007-2009 Colin Percival
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#ifndef _SYSENDIAN_H_
+#define _SYSENDIAN_H_
+
+#include "scrypt_platform.h"
+
+/* If we don't have be64enc, the <sys/endian.h> we have isn't usable. */
+#if !HAVE_DECL_BE64ENC
+#undef HAVE_SYS_ENDIAN_H
+#endif
+
+#ifdef HAVE_SYS_ENDIAN_H
+
+#include <sys/endian.h>
+
+#else
+
+#include <stdint.h>
+
+static inline uint32_t
+be32dec(const void *pp)
+{
+	const uint8_t *p = (uint8_t const *)pp;
+
+	return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) +
+	    ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24));
+}
+
+static inline void
+be32enc(void *pp, uint32_t x)
+{
+	uint8_t * p = (uint8_t *)pp;
+
+	p[3] = x & 0xff;
+	p[2] = (x >> 8) & 0xff;
+	p[1] = (x >> 16) & 0xff;
+	p[0] = (x >> 24) & 0xff;
+}
+
+static inline uint64_t
+be64dec(const void *pp)
+{
+	const uint8_t *p = (uint8_t const *)pp;
+
+	return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) +
+	    ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) +
+	    ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) +
+	    ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56));
+}
+
+static inline void
+be64enc(void *pp, uint64_t x)
+{
+	uint8_t * p = (uint8_t *)pp;
+
+	p[7] = x & 0xff;
+	p[6] = (x >> 8) & 0xff;
+	p[5] = (x >> 16) & 0xff;
+	p[4] = (x >> 24) & 0xff;
+	p[3] = (x >> 32) & 0xff;
+	p[2] = (x >> 40) & 0xff;
+	p[1] = (x >> 48) & 0xff;
+	p[0] = (x >> 56) & 0xff;
+}
+
+static inline uint32_t
+le32dec(const void *pp)
+{
+	const uint8_t *p = (uint8_t const *)pp;
+
+	return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) +
+	    ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24));
+}
+
+static inline void
+le32enc(void *pp, uint32_t x)
+{
+	uint8_t * p = (uint8_t *)pp;
+
+	p[0] = x & 0xff;
+	p[1] = (x >> 8) & 0xff;
+	p[2] = (x >> 16) & 0xff;
+	p[3] = (x >> 24) & 0xff;
+}
+
+static inline uint64_t
+le64dec(const void *pp)
+{
+	const uint8_t *p = (uint8_t const *)pp;
+
+	return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) +
+	    ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) +
+	    ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) +
+	    ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56));
+}
+
+static inline void
+le64enc(void *pp, uint64_t x)
+{
+	uint8_t * p = (uint8_t *)pp;
+
+	p[0] = x & 0xff;
+	p[1] = (x >> 8) & 0xff;
+	p[2] = (x >> 16) & 0xff;
+	p[3] = (x >> 24) & 0xff;
+	p[4] = (x >> 32) & 0xff;
+	p[5] = (x >> 40) & 0xff;
+	p[6] = (x >> 48) & 0xff;
+	p[7] = (x >> 56) & 0xff;
+}
+#endif /* !HAVE_SYS_ENDIAN_H */
+
+#endif /* !_SYSENDIAN_H_ */
diff --git a/crypto/scrypt/patches/README b/crypto/scrypt/patches/README
new file mode 100644
index 0000000..353ddbb
--- /dev/null
+++ b/crypto/scrypt/patches/README
@@ -0,0 +1,11 @@
+bionic.patch:
+
+Allows scrypt to compile against bionic.
+
+use_openssl_pbkdf2.patch:
+
+Uses the PBKDF2 function from OpenSSL (it uses accelerated SHA256)
+
+arm-neon.patch:
+
+Adds NEON acceleration for the Salsa20/8 mixing function.
diff --git a/crypto/scrypt/patches/arm-neon.patch b/crypto/scrypt/patches/arm-neon.patch
new file mode 100644
index 0000000..7197f99
--- /dev/null
+++ b/crypto/scrypt/patches/arm-neon.patch
@@ -0,0 +1,437 @@
+diff --git a/lib/crypto/crypto_scrypt-neon-salsa208.h b/lib/crypto/crypto_scrypt-neon-salsa208.h
+new file mode 100644
+index 0000000..a3b1019
+--- /dev/null
++++ b/lib/crypto/crypto_scrypt-neon-salsa208.h
+@@ -0,0 +1,120 @@
++/*
++ * version 20110505
++ * D. J. Bernstein
++ * Public domain.
++ *
++ * Based on crypto_core/salsa208/armneon/core.c from SUPERCOP 20130419
++ */
++
++#define ROUNDS 8
++static void
++salsa20_8_intrinsic(void * input)
++{
++  int i;
++
++  const uint32x4_t abab = {-1,0,-1,0};
++
++  /*
++   * This is modified since we only have one argument. Usually you'd rearrange
++   * the constant, key, and input bytes, but we just have one linear array to
++   * rearrange which is a bit easier.
++   */
++
++  /*
++   * Change the input to be diagonals as if it's a 4x4 matrix of 32-bit values.
++   */
++  uint32x4_t x0x5x10x15;
++  uint32x4_t x12x1x6x11;
++  uint32x4_t x8x13x2x7;
++  uint32x4_t x4x9x14x3;
++
++  uint32x4_t x0x1x10x11;
++  uint32x4_t x12x13x6x7;
++  uint32x4_t x8x9x2x3;
++  uint32x4_t x4x5x14x15;
++
++  uint32x4_t x0x1x2x3;
++  uint32x4_t x4x5x6x7;
++  uint32x4_t x8x9x10x11;
++  uint32x4_t x12x13x14x15;
++
++  x0x1x2x3 = vld1q_u8((uint8_t *) input);
++  x4x5x6x7 = vld1q_u8(16 + (uint8_t *) input);
++  x8x9x10x11 = vld1q_u8(32 + (uint8_t *) input);
++  x12x13x14x15 = vld1q_u8(48 + (uint8_t *) input);
++
++  x0x1x10x11 = vcombine_u32(vget_low_u32(x0x1x2x3), vget_high_u32(x8x9x10x11));
++  x4x5x14x15 = vcombine_u32(vget_low_u32(x4x5x6x7), vget_high_u32(x12x13x14x15));
++  x8x9x2x3 = vcombine_u32(vget_low_u32(x8x9x10x11), vget_high_u32(x0x1x2x3));
++  x12x13x6x7 = vcombine_u32(vget_low_u32(x12x13x14x15), vget_high_u32(x4x5x6x7));
++
++  x0x5x10x15 = vbslq_u32(abab,x0x1x10x11,x4x5x14x15);
++  x8x13x2x7 = vbslq_u32(abab,x8x9x2x3,x12x13x6x7);
++  x4x9x14x3 = vbslq_u32(abab,x4x5x14x15,x8x9x2x3);
++  x12x1x6x11 = vbslq_u32(abab,x12x13x6x7,x0x1x10x11);
++
++  uint32x4_t start0 = x0x5x10x15;
++  uint32x4_t start1 = x12x1x6x11;
++  uint32x4_t start3 = x4x9x14x3;
++  uint32x4_t start2 = x8x13x2x7;
++
++  /* From here on this should be the same as the SUPERCOP version. */
++
++  uint32x4_t diag0 = start0;
++  uint32x4_t diag1 = start1;
++  uint32x4_t diag2 = start2;
++  uint32x4_t diag3 = start3;
++
++  uint32x4_t a0;
++  uint32x4_t a1;
++  uint32x4_t a2;
++  uint32x4_t a3;
++
++  for (i = ROUNDS;i > 0;i -= 2) {
++    a0 = diag1 + diag0;
++    diag3 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25);
++    a1 = diag0 + diag3;
++    diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23);
++    a2 = diag3 + diag2;
++    diag1 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19);
++    a3 = diag2 + diag1;
++    diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14);
++
++    diag3 = vextq_u32(diag3,diag3,3);
++    diag2 = vextq_u32(diag2,diag2,2);
++    diag1 = vextq_u32(diag1,diag1,1);
++
++    a0 = diag3 + diag0;
++    diag1 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25);
++    a1 = diag0 + diag1;
++    diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23);
++    a2 = diag1 + diag2;
++    diag3 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19);
++    a3 = diag2 + diag3;
++    diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14);
++
++    diag1 = vextq_u32(diag1,diag1,3);
++    diag2 = vextq_u32(diag2,diag2,2);
++    diag3 = vextq_u32(diag3,diag3,1);
++  }
++
++  x0x5x10x15 = diag0 + start0;
++  x12x1x6x11 = diag1 + start1;
++  x8x13x2x7 = diag2 + start2;
++  x4x9x14x3 = diag3 + start3;
++
++  x0x1x10x11 = vbslq_u32(abab,x0x5x10x15,x12x1x6x11);
++  x12x13x6x7 = vbslq_u32(abab,x12x1x6x11,x8x13x2x7);
++  x8x9x2x3 = vbslq_u32(abab,x8x13x2x7,x4x9x14x3);
++  x4x5x14x15 = vbslq_u32(abab,x4x9x14x3,x0x5x10x15);
++
++  x0x1x2x3 = vcombine_u32(vget_low_u32(x0x1x10x11),vget_high_u32(x8x9x2x3));
++  x4x5x6x7 = vcombine_u32(vget_low_u32(x4x5x14x15),vget_high_u32(x12x13x6x7));
++  x8x9x10x11 = vcombine_u32(vget_low_u32(x8x9x2x3),vget_high_u32(x0x1x10x11));
++  x12x13x14x15 = vcombine_u32(vget_low_u32(x12x13x6x7),vget_high_u32(x4x5x14x15));
++
++  vst1q_u8((uint8_t *) input,(uint8x16_t) x0x1x2x3);
++  vst1q_u8(16 + (uint8_t *) input,(uint8x16_t) x4x5x6x7);
++  vst1q_u8(32 + (uint8_t *) input,(uint8x16_t) x8x9x10x11);
++  vst1q_u8(48 + (uint8_t *) input,(uint8x16_t) x12x13x14x15);
++}
+diff --git a/lib/crypto/crypto_scrypt-neon.c b/lib/crypto/crypto_scrypt-neon.c
+new file mode 100644
+index 0000000..a3bf052
+--- /dev/null
++++ b/lib/crypto/crypto_scrypt-neon.c
+@@ -0,0 +1,305 @@
++/*-
++ * Copyright 2009 Colin Percival
++ * 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.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
++ *
++ * This file was originally written by Colin Percival as part of the Tarsnap
++ * online backup system.
++ */
++#include "scrypt_platform.h"
++
++#include <machine/cpu-features.h>
++#include <arm_neon.h>
++
++#include <errno.h>
++#include <stdint.h>
++#include <limits.h>
++#include <stdlib.h>
++#include <string.h>
++
++#ifdef USE_OPENSSL_PBKDF2
++#include <openssl/evp.h>
++#else
++#include "sha256.h"
++#endif
++#include "sysendian.h"
++
++#include "crypto_scrypt.h"
++
++#include "crypto_scrypt-neon-salsa208.h"
++
++static void blkcpy(void *, void *, size_t);
++static void blkxor(void *, void *, size_t);
++void crypto_core_salsa208_armneon2(void *);
++static void blockmix_salsa8(uint8x16_t *, uint8x16_t *, uint8x16_t *, size_t);
++static uint64_t integerify(void *, size_t);
++static void smix(uint8_t *, size_t, uint64_t, void *, void *);
++
++static void
++blkcpy(void * dest, void * src, size_t len)
++{
++	uint8x16_t * D = dest;
++	uint8x16_t * S = src;
++	size_t L = len / 16;
++	size_t i;
++
++	for (i = 0; i < L; i++)
++		D[i] = S[i];
++}
++
++static void
++blkxor(void * dest, void * src, size_t len)
++{
++	uint8x16_t * D = dest;
++	uint8x16_t * S = src;
++	size_t L = len / 16;
++	size_t i;
++
++	for (i = 0; i < L; i++)
++		D[i] = veorq_u8(D[i], S[i]);
++}
++
++/**
++ * blockmix_salsa8(B, Y, r):
++ * Compute B = BlockMix_{salsa20/8, r}(B).  The input B must be 128r bytes in
++ * length; the temporary space Y must also be the same size.
++ */
++static void
++blockmix_salsa8(uint8x16_t * Bin, uint8x16_t * Bout, uint8x16_t * X, size_t r)
++{
++	size_t i;
++
++	/* 1: X <-- B_{2r - 1} */
++	blkcpy(X, &Bin[8 * r - 4], 64);
++
++	/* 2: for i = 0 to 2r - 1 do */
++	for (i = 0; i < r; i++) {
++		/* 3: X <-- H(X \xor B_i) */
++		blkxor(X, &Bin[i * 8], 64);
++                salsa20_8_intrinsic((void *) X);
++
++		/* 4: Y_i <-- X */
++		/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
++		blkcpy(&Bout[i * 4], X, 64);
++
++		/* 3: X <-- H(X \xor B_i) */
++		blkxor(X, &Bin[i * 8 + 4], 64);
++                salsa20_8_intrinsic((void *) X);
++
++		/* 4: Y_i <-- X */
++		/* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
++		blkcpy(&Bout[(r + i) * 4], X, 64);
++	}
++}
++
++/**
++ * integerify(B, r):
++ * Return the result of parsing B_{2r-1} as a little-endian integer.
++ */
++static uint64_t
++integerify(void * B, size_t r)
++{
++	uint8_t * X = (void*)((uintptr_t)(B) + (2 * r - 1) * 64);
++
++	return (le64dec(X));
++}
++
++/**
++ * smix(B, r, N, V, XY):
++ * Compute B = SMix_r(B, N).  The input B must be 128r bytes in length; the
++ * temporary storage V must be 128rN bytes in length; the temporary storage
++ * XY must be 256r bytes in length.  The value N must be a power of 2.
++ */
++static void
++smix(uint8_t * B, size_t r, uint64_t N, void * V, void * XY)
++{
++	uint8x16_t * X = XY;
++	uint8x16_t * Y = (void *)((uintptr_t)(XY) + 128 * r);
++        uint8x16_t * Z = (void *)((uintptr_t)(XY) + 256 * r);
++        uint32_t * X32 = (void *)X;
++	uint64_t i, j;
++        size_t k;
++
++	/* 1: X <-- B */
++	blkcpy(X, B, 128 * r);
++
++	/* 2: for i = 0 to N - 1 do */
++	for (i = 0; i < N; i += 2) {
++		/* 3: V_i <-- X */
++		blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r);
++
++		/* 4: X <-- H(X) */
++		blockmix_salsa8(X, Y, Z, r);
++
++		/* 3: V_i <-- X */
++		blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r),
++		    Y, 128 * r);
++
++		/* 4: X <-- H(X) */
++		blockmix_salsa8(Y, X, Z, r);
++	}
++
++	/* 6: for i = 0 to N - 1 do */
++	for (i = 0; i < N; i += 2) {
++		/* 7: j <-- Integerify(X) mod N */
++		j = integerify(X, r) & (N - 1);
++
++		/* 8: X <-- H(X \xor V_j) */
++		blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
++		blockmix_salsa8(X, Y, Z, r);
++
++		/* 7: j <-- Integerify(X) mod N */
++		j = integerify(Y, r) & (N - 1);
++
++		/* 8: X <-- H(X \xor V_j) */
++		blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
++		blockmix_salsa8(Y, X, Z, r);
++	}
++
++	/* 10: B' <-- X */
++	blkcpy(B, X, 128 * r);
++}
++
++/**
++ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
++ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
++ * p, buflen) and write the result into buf.  The parameters r, p, and buflen
++ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32.  The parameter N
++ * must be a power of 2.
++ *
++ * Return 0 on success; or -1 on error.
++ */
++int
++crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
++    const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,
++    uint8_t * buf, size_t buflen)
++{
++	void * B0, * V0, * XY0;
++	uint8_t * B;
++	uint32_t * V;
++	uint32_t * XY;
++	uint32_t i;
++
++	/* Sanity-check parameters. */
++#if SIZE_MAX > UINT32_MAX
++	if (buflen > (((uint64_t)(1) << 32) - 1) * 32) {
++		errno = EFBIG;
++		goto err0;
++	}
++#endif
++	if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) {
++		errno = EFBIG;
++		goto err0;
++	}
++	if (((N & (N - 1)) != 0) || (N == 0)) {
++		errno = EINVAL;
++		goto err0;
++	}
++	if ((r > SIZE_MAX / 128 / p) ||
++#if SIZE_MAX / 256 <= UINT32_MAX
++	    (r > SIZE_MAX / 256) ||
++#endif
++	    (N > SIZE_MAX / 128 / r)) {
++		errno = ENOMEM;
++		goto err0;
++	}
++
++	/* Allocate memory. */
++#ifdef HAVE_POSIX_MEMALIGN
++	if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0)
++		goto err0;
++	B = (uint8_t *)(B0);
++	if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0)
++		goto err1;
++	XY = (uint32_t *)(XY0);
++#ifndef MAP_ANON
++	if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0)
++		goto err2;
++	V = (uint32_t *)(V0);
++#endif
++#else
++	if ((B0 = malloc(128 * r * p + 63)) == NULL)
++		goto err0;
++	B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63));
++	if ((XY0 = malloc(256 * r + 64 + 63)) == NULL)
++		goto err1;
++	XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63));
++#ifndef MAP_ANON
++	if ((V0 = malloc(128 * r * N + 63)) == NULL)
++		goto err2;
++	V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63));
++#endif
++#endif
++#ifdef MAP_ANON
++	if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE,
++#ifdef MAP_NOCORE
++	    MAP_ANON | MAP_PRIVATE | MAP_NOCORE,
++#else
++	    MAP_ANON | MAP_PRIVATE,
++#endif
++	    -1, 0)) == MAP_FAILED)
++		goto err2;
++	V = (uint32_t *)(V0);
++#endif
++
++	/* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
++#ifdef USE_OPENSSL_PBKDF2
++	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
++#else
++	PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
++#endif
++
++	/* 2: for i = 0 to p - 1 do */
++	for (i = 0; i < p; i++) {
++		/* 3: B_i <-- MF(B_i, N) */
++		smix(&B[i * 128 * r], r, N, V, XY);
++	}
++
++	/* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
++#ifdef USE_OPENSSL_PBKDF2
++	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
++#else
++	PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
++#endif
++
++	/* Free memory. */
++#ifdef MAP_ANON
++	if (munmap(V0, 128 * r * N))
++		goto err2;
++#else
++	free(V0);
++#endif
++	free(XY0);
++	free(B0);
++
++	/* Success! */
++	return (0);
++
++err2:
++	free(XY0);
++err1:
++	free(B0);
++err0:
++	/* Failure! */
++	return (-1);
++}
diff --git a/crypto/scrypt/patches/use_openssl_pbkdf2.patch b/crypto/scrypt/patches/use_openssl_pbkdf2.patch
new file mode 100644
index 0000000..0a1328c
--- /dev/null
+++ b/crypto/scrypt/patches/use_openssl_pbkdf2.patch
@@ -0,0 +1,80 @@
+diff --git a/lib/crypto/crypto_scrypt-ref.c b/lib/crypto/crypto_scrypt-ref.c
+index 79a6f8f..60ef2aa 100644
+--- a/lib/crypto/crypto_scrypt-ref.c
++++ b/lib/crypto/crypto_scrypt-ref.c
+@@ -34,7 +34,11 @@
+ #include <stdlib.h>
+ #include <string.h>
+ 
++#ifdef USE_OPENSSL_PBKDF2
++#include <openssl/evp.h>
++#else
+ #include "sha256.h"
++#endif
+ #include "sysendian.h"
+ 
+ #include "crypto_scrypt.h"
+@@ -256,7 +260,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ 		goto err2;
+ 
+ 	/* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
++#ifdef USE_OPENSSL_PBKDF2
++	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
++#else
+ 	PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
++#endif
+ 
+ 	/* 2: for i = 0 to p - 1 do */
+ 	for (i = 0; i < p; i++) {
+@@ -265,7 +273,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ 	}
+ 
+ 	/* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
++#ifdef USE_OPENSSL_PBKDF2
++	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
++#else
+ 	PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
++#endif
+ 
+ 	/* Free memory. */
+ 	free(V);
+diff --git a/lib/crypto/crypto_scrypt-sse.c b/lib/crypto/crypto_scrypt-sse.c
+index 875175e..dd18f29 100644
+--- a/lib/crypto/crypto_scrypt-sse.c
++++ b/lib/crypto/crypto_scrypt-sse.c
+@@ -37,7 +37,11 @@
+ #include <stdlib.h>
+ #include <string.h>
+ 
++#ifdef USE_OPENSSL_PBKDF2
++#include <openssl/evp.h>
++#else
+ #include "sha256.h"
++#endif
+ #include "sysendian.h"
+ 
+ #include "crypto_scrypt.h"
+@@ -332,7 +336,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ #endif
+ 
+ 	/* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
++#ifdef USE_OPENSSL_PBKDF2
++	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
++#else
+ 	PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
++#endif
+ 
+ 	/* 2: for i = 0 to p - 1 do */
+ 	for (i = 0; i < p; i++) {
+@@ -341,7 +349,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ 	}
+ 
+ 	/* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
++#ifdef USE_OPENSSL_PBKDF2
++	PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
++#else
+ 	PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
++#endif
+ 
+ 	/* Free memory. */
+ #ifdef MAP_ANON
diff --git a/crypto/scrypt/scrypt.config b/crypto/scrypt/scrypt.config
new file mode 100644
index 0000000..3ccb4d0
--- /dev/null
+++ b/crypto/scrypt/scrypt.config
@@ -0,0 +1,94 @@
+CONFIGURE_ARGS="\
+ \
+"
+
+# unneeded directories
+UNNEEDED_SOURCES="\
+lib/scryptenc \
+"
+
+# unneeded files
+UNNEEDED_SOURCES+="\
+config.h.in \
+configure \
+FORMAT \
+main.c \
+Makefile.in \
+scrypt.1 \
+lib/crypto/crypto_aesctr.c \
+lib/crypto/crypto_aesctr.h \
+lib/crypto/crypto_scrypt-nosse.c \
+lib/crypto/sha256.c \
+lib/crypto/sha256.h \
+lib/util/memlimit.c \
+lib/util/memlimit.h \
+lib/util/readpass.c \
+lib/util/readpass.h \
+lib/util/warn.c \
+lib/util/warn.h \
+"
+
+NEEDED_SOURCES="\
+config.h \
+lib \
+scrypt_platform.h \
+"
+
+SCRYPT_INCLUDES="\
+lib/crypto \
+lib/util \
+"
+
+SCRYPT_SOURCES="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_SOURCES_arm="\
+"
+
+SCRYPT_SOURCES_EXCLUDES_arm="\
+"
+
+SCRYPT_SOURCES_arm_neon="\
+lib/crypto/crypto_scrypt-neon.c \
+"
+
+SCRYPT_SOURCES_EXCLUDES_arm_neon="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_SOURCES_mips="\
+"
+
+SCRYPT_SOURCES_EXCLUDES_mips="\
+"
+
+SCRYPT_SOURCES_x86="\
+lib/crypto/crypto_scrypt-sse.c \
+"
+
+SCRYPT_SOURCES_EXCLUDES_x86="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_SOURCES_x86_64="\
+lib/crypto/crypto_scrypt-sse.c \
+"
+
+SCRYPT_SOURCES_EXCLUDES_x86_64="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_PATCHES="\
+use_openssl_pbkdf2.patch \
+arm-neon.patch \
+"
+
+SCRYPT_PATCHES_use_openssl_pbkdf2_SOURCES="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_PATCHES_bionic_SOURCES="\
+lib/crypto/crypto_scrypt-neon.c \
+lib/crypto/crypto_scrypt-neon-salsa208.h \
+"
diff --git a/crypto/scrypt/scrypt.version b/crypto/scrypt/scrypt.version
new file mode 100644
index 0000000..155e260
--- /dev/null
+++ b/crypto/scrypt/scrypt.version
@@ -0,0 +1 @@
+SCRYPT_VERSION=1.1.6
diff --git a/crypto/scrypt/scrypt_platform.h b/crypto/scrypt/scrypt_platform.h
new file mode 100644
index 0000000..5cec236
--- /dev/null
+++ b/crypto/scrypt/scrypt_platform.h
@@ -0,0 +1,12 @@
+#ifndef _SCRYPT_PLATFORM_H_
+#define	_SCRYPT_PLATFORM_H_
+
+#if defined(CONFIG_H_FILE)
+#include CONFIG_H_FILE
+#elif defined(HAVE_CONFIG_H)
+#include "config.h"
+#else
+#error Need either CONFIG_H_FILE or HAVE_CONFIG_H defined.
+#endif
+
+#endif /* !_SCRYPT_PLATFORM_H_ */
diff --git a/crypto/scrypt/tests/Android.mk b/crypto/scrypt/tests/Android.mk
new file mode 100644
index 0000000..a4562de
--- /dev/null
+++ b/crypto/scrypt/tests/Android.mk
@@ -0,0 +1,23 @@
+# Build the scrypt unit tests
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    scrypt_test.cpp
+
+LOCAL_C_INCLUDES := \
+    external/gtest/include \
+    external/scrypt/lib/crypto
+
+LOCAL_SHARED_LIBRARIES := \
+    libcrypto
+
+LOCAL_STATIC_LIBRARIES := \
+    libscrypt_static \
+    libgtest \
+    libgtest_main
+
+LOCAL_MODULE := scrypt_test
+
+include $(BUILD_NATIVE_TEST)
diff --git a/crypto/scrypt/tests/scrypt_test.cpp b/crypto/scrypt/tests/scrypt_test.cpp
new file mode 100644
index 0000000..28334d6
--- /dev/null
+++ b/crypto/scrypt/tests/scrypt_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "scrypt_test"
+#include <utils/Log.h>
+#include <utils/UniquePtr.h>
+
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <iostream>
+
+extern "C" {
+#include <crypto_scrypt.h>
+}
+
+namespace android {
+
+typedef struct scrypt_test_setting_t {
+        const char *pw, *salt;
+        uint32_t Nfactor, rfactor, pfactor;
+} scrypt_test_setting;
+
+static const scrypt_test_setting post_settings[] = {
+        {"", "", 16, 1, 1},
+        {"password", "NaCl", 1024, 8, 16},
+        {"pleaseletmein", "SodiumChloride", 16384, 8, 1},
+        {0, 0, 0, 0, 0}
+};
+
+static const uint8_t post_vectors[][64] = {
+        {0x77,0xd6,0x57,0x62,0x38,0x65,0x7b,0x20,0x3b,0x19,0xca,0x42,0xc1,0x8a,0x04,0x97,
+         0xf1,0x6b,0x48,0x44,0xe3,0x07,0x4a,0xe8,0xdf,0xdf,0xfa,0x3f,0xed,0xe2,0x14,0x42,
+         0xfc,0xd0,0x06,0x9d,0xed,0x09,0x48,0xf8,0x32,0x6a,0x75,0x3a,0x0f,0xc8,0x1f,0x17,
+         0xe8,0xd3,0xe0,0xfb,0x2e,0x0d,0x36,0x28,0xcf,0x35,0xe2,0x0c,0x38,0xd1,0x89,0x06},
+        {0xfd,0xba,0xbe,0x1c,0x9d,0x34,0x72,0x00,0x78,0x56,0xe7,0x19,0x0d,0x01,0xe9,0xfe,
+         0x7c,0x6a,0xd7,0xcb,0xc8,0x23,0x78,0x30,0xe7,0x73,0x76,0x63,0x4b,0x37,0x31,0x62,
+         0x2e,0xaf,0x30,0xd9,0x2e,0x22,0xa3,0x88,0x6f,0xf1,0x09,0x27,0x9d,0x98,0x30,0xda,
+         0xc7,0x27,0xaf,0xb9,0x4a,0x83,0xee,0x6d,0x83,0x60,0xcb,0xdf,0xa2,0xcc,0x06,0x40},
+        {0x70,0x23,0xbd,0xcb,0x3a,0xfd,0x73,0x48,0x46,0x1c,0x06,0xcd,0x81,0xfd,0x38,0xeb,
+         0xfd,0xa8,0xfb,0xba,0x90,0x4f,0x8e,0x3e,0xa9,0xb5,0x43,0xf6,0x54,0x5d,0xa1,0xf2,
+         0xd5,0x43,0x29,0x55,0x61,0x3f,0x0f,0xcf,0x62,0xd4,0x97,0x05,0x24,0x2a,0x9a,0xf9,
+         0xe6,0x1e,0x85,0xdc,0x0d,0x65,0x1e,0x40,0xdf,0xcf,0x01,0x7b,0x45,0x57,0x58,0x87},
+};
+
+class ScryptTest : public ::testing::Test {
+};
+
+TEST_F(ScryptTest, TestVectors) {
+    int i;
+
+    for (i = 0; post_settings[i].pw != NULL; i++) {
+        uint8_t output[64];
+
+        scrypt_test_setting_t s = post_settings[i];
+        ASSERT_EQ(0,
+                crypto_scrypt((const uint8_t*) s.pw, strlen(s.pw), (const uint8_t*) s.salt,
+                        strlen(s.salt), s.Nfactor, s.rfactor, s.pfactor, output, sizeof(output)))
+                << "scrypt call should succeed for " << i << "; error=" << strerror(errno);
+        ASSERT_EQ(0, memcmp(post_vectors[i], output, sizeof(output)))
+                << "Should match expected output";
+    }
+}
+
+}
diff --git a/data.cpp b/data.cpp
new file mode 100644
index 0000000..0d05c3d
--- /dev/null
+++ b/data.cpp
@@ -0,0 +1,1151 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <linux/input.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+#include <utility>
+#include <map>
+#include <fstream>
+#include <sstream>
+
+#include "variables.h"
+#include "data.hpp"
+#include "partitions.hpp"
+#include "twrp-functions.hpp"
+#ifndef TW_NO_SCREEN_TIMEOUT
+#include "gui/blanktimer.hpp"
+#endif
+#include "find_file.hpp"
+
+#ifdef TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
+	#include "cutils/properties.h"
+#endif
+
+#ifndef TW_MAX_BRIGHTNESS
+#define TW_MAX_BRIGHTNESS 255
+#endif
+
+extern "C"
+{
+	#include "twcommon.h"
+	#include "data.h"
+	#include "gui/pages.h"
+	#include "minuitwrp/minui.h"
+	void gui_notifyVarChange(const char *name, const char* value);
+}
+
+#define FILE_VERSION 0x00010001
+
+using namespace std;
+
+map<string, DataManager::TStrIntPair>   DataManager::mValues;
+map<string, string>                     DataManager::mConstValues;
+string                                  DataManager::mBackingFile;
+int                                     DataManager::mInitialized = 0;
+
+#ifndef TW_NO_SCREEN_TIMEOUT
+extern blanktimer blankTimer;
+#endif
+extern bool datamedia;
+
+// Device ID functions
+void DataManager::sanitize_device_id(char* device_id) {
+	const char* whitelist ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-._";
+	char str[50];
+	char* c = str;
+
+	strcpy(str, device_id);
+	memset(device_id, 0, sizeof(device_id));
+	while (*c) {
+		if (strchr(whitelist, *c))
+			strncat(device_id, c, 1);
+		c++;
+	}
+	return;
+}
+
+#define CMDLINE_SERIALNO		"androidboot.serialno="
+#define CMDLINE_SERIALNO_LEN	(strlen(CMDLINE_SERIALNO))
+#define CPUINFO_SERIALNO		"Serial"
+#define CPUINFO_SERIALNO_LEN	(strlen(CPUINFO_SERIALNO))
+#define CPUINFO_HARDWARE		"Hardware"
+#define CPUINFO_HARDWARE_LEN	(strlen(CPUINFO_HARDWARE))
+
+void DataManager::get_device_id(void) {
+	FILE *fp;
+	char line[2048];
+	char hardware_id[32], device_id[64];
+	char* token;
+
+	// Assign a blank device_id to start with
+	device_id[0] = 0;
+
+#ifdef TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
+	// Now we'll use product_model_hardwareid as device id
+	char model_id[PROPERTY_VALUE_MAX];
+	property_get("ro.product.model", model_id, "error");
+	if (strcmp(model_id,"error") != 0) {
+		LOGINFO("=> product model: '%s'\n", model_id);
+		// Replace spaces with underscores
+		for(int i = 0; i < strlen(model_id); i++) {
+			if(model_id[i] == ' ')
+			model_id[i] = '_';
+		}
+		strcpy(device_id, model_id);
+		if (hardware_id[0] != 0) {
+			strcat(device_id, "_");
+			strcat(device_id, hardware_id);
+		}
+		sanitize_device_id((char *)device_id);
+		mConstValues.insert(make_pair("device_id", device_id));
+		LOGINFO("=> using device id: '%s'\n", device_id);
+		return;
+	}
+#endif
+
+#ifndef TW_FORCE_CPUINFO_FOR_DEVICE_ID
+	// First, try the cmdline to see if the serial number was supplied
+	fp = fopen("/proc/cmdline", "rt");
+	if (fp != NULL)
+	{
+		// First step, read the line. For cmdline, it's one long line
+		fgets(line, sizeof(line), fp);
+		fclose(fp);
+
+		// Now, let's tokenize the string
+		token = strtok(line, " ");
+
+		// Let's walk through the line, looking for the CMDLINE_SERIALNO token
+		while (token)
+		{
+			// We don't need to verify the length of token, because if it's too short, it will mismatch CMDLINE_SERIALNO at the NULL
+			if (memcmp(token, CMDLINE_SERIALNO, CMDLINE_SERIALNO_LEN) == 0)
+			{
+				// We found the serial number!
+				strcpy(device_id, token + CMDLINE_SERIALNO_LEN);
+				sanitize_device_id((char *)device_id);
+				mConstValues.insert(make_pair("device_id", device_id));
+				return;
+			}
+			token = strtok(NULL, " ");
+		}
+	}
+#endif
+	// Now we'll try cpuinfo for a serial number
+	fp = fopen("/proc/cpuinfo", "rt");
+	if (fp != NULL)
+	{
+		while (fgets(line, sizeof(line), fp) != NULL) { // First step, read the line.
+			if (memcmp(line, CPUINFO_SERIALNO, CPUINFO_SERIALNO_LEN) == 0)  // check the beginning of the line for "Serial"
+			{
+				// We found the serial number!
+				token = line + CPUINFO_SERIALNO_LEN; // skip past "Serial"
+				while ((*token > 0 && *token <= 32 ) || *token == ':') token++; // skip over all spaces and the colon
+				if (*token != 0) {
+					token[30] = 0;
+					if (token[strlen(token)-1] == 10) { // checking for endline chars and dropping them from the end of the string if needed
+						memset(device_id, 0, sizeof(device_id));
+						strncpy(device_id, token, strlen(token) - 1);
+					} else {
+						strcpy(device_id, token);
+					}
+					LOGINFO("=> serial from cpuinfo: '%s'\n", device_id);
+					fclose(fp);
+					sanitize_device_id((char *)device_id);
+					mConstValues.insert(make_pair("device_id", device_id));
+					return;
+				}
+			} else if (memcmp(line, CPUINFO_HARDWARE, CPUINFO_HARDWARE_LEN) == 0) {// We're also going to look for the hardware line in cpuinfo and save it for later in case we don't find the device ID
+				// We found the hardware ID
+				token = line + CPUINFO_HARDWARE_LEN; // skip past "Hardware"
+				while ((*token > 0 && *token <= 32 ) || *token == ':')  token++; // skip over all spaces and the colon
+				if (*token != 0) {
+					token[30] = 0;
+					if (token[strlen(token)-1] == 10) { // checking for endline chars and dropping them from the end of the string if needed
+						memset(hardware_id, 0, sizeof(hardware_id));
+						strncpy(hardware_id, token, strlen(token) - 1);
+					} else {
+						strcpy(hardware_id, token);
+					}
+					LOGINFO("=> hardware id from cpuinfo: '%s'\n", hardware_id);
+				}
+			}
+		}
+		fclose(fp);
+	}
+
+	if (hardware_id[0] != 0) {
+		LOGINFO("\nusing hardware id for device id: '%s'\n", hardware_id);
+		strcpy(device_id, hardware_id);
+		sanitize_device_id((char *)device_id);
+		mConstValues.insert(make_pair("device_id", device_id));
+		return;
+	}
+
+	strcpy(device_id, "serialno");
+	LOGERR("=> device id not found, using '%s'.", device_id);
+	mConstValues.insert(make_pair("device_id", device_id));
+	return;
+}
+
+int DataManager::ResetDefaults()
+{
+	mValues.clear();
+	mConstValues.clear();
+	SetDefaultValues();
+	return 0;
+}
+
+int DataManager::LoadValues(const string filename)
+{
+	string str, dev_id;
+
+	if (!mInitialized)
+		SetDefaultValues();
+
+	GetValue("device_id", dev_id);
+	// Save off the backing file for set operations
+	mBackingFile = filename;
+
+	// Read in the file, if possible
+	FILE* in = fopen(filename.c_str(), "rb");
+	if (!in) {
+		LOGINFO("Settings file '%s' not found.\n", filename.c_str());
+		return 0;
+	} else {
+		LOGINFO("Loading settings from '%s'.\n", filename.c_str());
+	}
+
+	int file_version;
+	if (fread(&file_version, 1, sizeof(int), in) != sizeof(int))	goto error;
+	if (file_version != FILE_VERSION)								goto error;
+
+	while (!feof(in))
+	{
+		string Name;
+		string Value;
+		unsigned short length;
+		char array[512];
+
+		if (fread(&length, 1, sizeof(unsigned short), in) != sizeof(unsigned short))	goto error;
+		if (length >= 512)																goto error;
+		if (fread(array, 1, length, in) != length)										goto error;
+		Name = array;
+
+		if (fread(&length, 1, sizeof(unsigned short), in) != sizeof(unsigned short))	goto error;
+		if (length >= 512)																goto error;
+		if (fread(array, 1, length, in) != length)										goto error;
+		Value = array;
+
+		map<string, TStrIntPair>::iterator pos;
+
+		pos = mValues.find(Name);
+		if (pos != mValues.end())
+		{
+			pos->second.first = Value;
+			pos->second.second = 1;
+		}
+		else
+			mValues.insert(TNameValuePair(Name, TStrIntPair(Value, 1)));
+#ifndef TW_NO_SCREEN_TIMEOUT
+		if (Name == "tw_screen_timeout_secs")
+			blankTimer.setTime(atoi(Value.c_str()));
+#endif
+	}
+error:
+	fclose(in);
+	string current = GetCurrentStoragePath();
+	TWPartition* Part = PartitionManager.Find_Partition_By_Path(current);
+	if(!Part)
+		Part = PartitionManager.Get_Default_Storage_Partition();
+	if (Part && current != Part->Storage_Path && Part->Mount(false)) {
+		LOGINFO("LoadValues setting storage path to '%s'\n", Part->Storage_Path.c_str());
+		SetValue("tw_storage_path", Part->Storage_Path);
+	} else {
+		SetBackupFolder();
+	}
+	return 0;
+}
+
+int DataManager::Flush()
+{
+	return SaveValues();
+}
+
+int DataManager::SaveValues()
+{
+#ifndef TW_OEM_BUILD
+	if (mBackingFile.empty())
+		return -1;
+
+	string mount_path = GetSettingsStoragePath();
+	PartitionManager.Mount_By_Path(mount_path.c_str(), 1);
+
+	FILE* out = fopen(mBackingFile.c_str(), "wb");
+	if (!out)
+		return -1;
+
+	int file_version = FILE_VERSION;
+	fwrite(&file_version, 1, sizeof(int), out);
+
+	map<string, TStrIntPair>::iterator iter;
+	for (iter = mValues.begin(); iter != mValues.end(); ++iter)
+	{
+		// Save only the persisted data
+		if (iter->second.second != 0)
+		{
+			unsigned short length = (unsigned short) iter->first.length() + 1;
+			fwrite(&length, 1, sizeof(unsigned short), out);
+			fwrite(iter->first.c_str(), 1, length, out);
+			length = (unsigned short) iter->second.first.length() + 1;
+			fwrite(&length, 1, sizeof(unsigned short), out);
+			fwrite(iter->second.first.c_str(), 1, length, out);
+		}
+	}
+	fclose(out);
+#endif // ifdef TW_OEM_BUILD
+	return 0;
+}
+
+int DataManager::GetValue(const string varName, string& value)
+{
+	string localStr = varName;
+
+	if (!mInitialized)
+		SetDefaultValues();
+
+	// Strip off leading and trailing '%' if provided
+	if (localStr.length() > 2 && localStr[0] == '%' && localStr[localStr.length()-1] == '%')
+	{
+		localStr.erase(0, 1);
+		localStr.erase(localStr.length() - 1, 1);
+	}
+
+	// Handle magic values
+	if (GetMagicValue(localStr, value) == 0)
+		return 0;
+	map<string, string>::iterator constPos;
+	constPos = mConstValues.find(localStr);
+	if (constPos != mConstValues.end())
+	{
+		value = constPos->second;
+		return 0;
+	}
+
+	map<string, TStrIntPair>::iterator pos;
+	pos = mValues.find(localStr);
+	if (pos == mValues.end())
+		return -1;
+
+	value = pos->second.first;
+	return 0;
+}
+
+int DataManager::GetValue(const string varName, int& value)
+{
+	string data;
+
+	if (GetValue(varName,data) != 0)
+		return -1;
+
+	value = atoi(data.c_str());
+	return 0;
+}
+
+int DataManager::GetValue(const string varName, float& value)
+{
+	string data;
+
+	if (GetValue(varName,data) != 0)
+		return -1;
+
+	value = atof(data.c_str());
+	return 0;
+}
+
+unsigned long long DataManager::GetValue(const string varName, unsigned long long& value)
+{
+	string data;
+
+	if (GetValue(varName,data) != 0)
+		return -1;
+
+	value = strtoull(data.c_str(), NULL, 10);
+	return 0;
+}
+
+// This is a dangerous function. It will create the value if it doesn't exist so it has a valid c_str
+string& DataManager::GetValueRef(const string varName)
+{
+	if (!mInitialized)
+		SetDefaultValues();
+
+	map<string, string>::iterator constPos;
+	constPos = mConstValues.find(varName);
+	if (constPos != mConstValues.end())
+		return constPos->second;
+
+	map<string, TStrIntPair>::iterator pos;
+	pos = mValues.find(varName);
+	if (pos == mValues.end())
+		pos = (mValues.insert(TNameValuePair(varName, TStrIntPair("", 0)))).first;
+
+	return pos->second.first;
+}
+
+// This function will return an empty string if the value doesn't exist
+string DataManager::GetStrValue(const string varName)
+{
+	string retVal;
+
+	GetValue(varName, retVal);
+	return retVal;
+}
+
+// This function will return 0 if the value doesn't exist
+int DataManager::GetIntValue(const string varName)
+{
+	string retVal;
+
+	GetValue(varName, retVal);
+	return atoi(retVal.c_str());
+}
+
+int DataManager::SetValue(const string varName, string value, int persist /* = 0 */)
+{
+	if (!mInitialized)
+		SetDefaultValues();
+
+	// Don't allow empty values or numerical starting values
+	if (varName.empty() || (varName[0] >= '0' && varName[0] <= '9'))
+		return -1;
+
+	map<string, string>::iterator constChk;
+	constChk = mConstValues.find(varName);
+	if (constChk != mConstValues.end())
+		return -1;
+
+	map<string, TStrIntPair>::iterator pos;
+	pos = mValues.find(varName);
+	if (pos == mValues.end())
+		pos = (mValues.insert(TNameValuePair(varName, TStrIntPair(value, persist)))).first;
+	else
+		pos->second.first = value;
+
+	if (pos->second.second != 0)
+		SaveValues();
+
+#ifndef TW_NO_SCREEN_TIMEOUT
+	if (varName == "tw_screen_timeout_secs") {
+		blankTimer.setTime(atoi(value.c_str()));
+	} else
+#endif
+	if (varName == "tw_storage_path") {
+		SetBackupFolder();
+	}
+	gui_notifyVarChange(varName.c_str(), value.c_str());
+	return 0;
+}
+
+int DataManager::SetValue(const string varName, int value, int persist /* = 0 */)
+{
+	ostringstream valStr;
+	valStr << value;
+	if (varName == "tw_use_external_storage") {
+		string str;
+
+		if (GetIntValue(TW_HAS_DUAL_STORAGE) == 1) {
+			if (value == 0) {
+				str = GetStrValue(TW_INTERNAL_PATH);
+			} else {
+				str = GetStrValue(TW_EXTERNAL_PATH);
+			}
+		} else if (GetIntValue(TW_HAS_INTERNAL) == 1)
+			str = GetStrValue(TW_INTERNAL_PATH);
+		else
+			str = GetStrValue(TW_EXTERNAL_PATH);
+
+		SetValue("tw_storage_path", str);
+	}
+	return SetValue(varName, valStr.str(), persist);
+}
+
+int DataManager::SetValue(const string varName, float value, int persist /* = 0 */)
+{
+	ostringstream valStr;
+	valStr << value;
+	return SetValue(varName, valStr.str(), persist);;
+}
+
+int DataManager::SetValue(const string varName, unsigned long long value, int persist /* = 0 */)
+{
+	ostringstream valStr;
+	valStr << value;
+	return SetValue(varName, valStr.str(), persist);
+}
+
+int DataManager::SetProgress(float Fraction) {
+	return SetValue("ui_progress", (float) (Fraction * 100.0));
+}
+
+int DataManager::ShowProgress(float Portion, float Seconds)
+{
+	float Starting_Portion;
+	GetValue("ui_progress_portion", Starting_Portion);
+	if (SetValue("ui_progress_portion", (float)(Portion * 100.0) + Starting_Portion) != 0)
+		return -1;
+	if (SetValue("ui_progress_frames", Seconds * 30) != 0)
+		return -1;
+	return 0;
+}
+
+void DataManager::DumpValues()
+{
+	map<string, TStrIntPair>::iterator iter;
+	gui_print("Data Manager dump - Values with leading X are persisted.\n");
+	for (iter = mValues.begin(); iter != mValues.end(); ++iter)
+		gui_print("%c %s=%s\n", iter->second.second ? 'X' : ' ', iter->first.c_str(), iter->second.first.c_str());
+}
+
+void DataManager::update_tz_environment_variables(void)
+{
+	setenv("TZ", DataManager_GetStrValue(TW_TIME_ZONE_VAR), 1);
+	tzset();
+}
+
+void DataManager::SetBackupFolder()
+{
+	string str = GetCurrentStoragePath();
+	TWPartition* partition = PartitionManager.Find_Partition_By_Path(str);
+	str += "/TWRP/BACKUPS/";
+
+	string dev_id;
+	GetValue("device_id", dev_id);
+
+	str += dev_id;
+	LOGINFO("Backup folder set to '%s'\n", str.c_str());
+	SetValue(TW_BACKUPS_FOLDER_VAR, str, 0);
+	if (partition != NULL) {
+		SetValue("tw_storage_display_name", partition->Storage_Name);
+		char free_space[255];
+		sprintf(free_space, "%llu", partition->Free / 1024 / 1024);
+		SetValue("tw_storage_free_size", free_space);
+		string zip_path, zip_root, storage_path;
+		GetValue(TW_ZIP_LOCATION_VAR, zip_path);
+		if (partition->Has_Data_Media)
+			storage_path = partition->Symlink_Mount_Point;
+		else
+			storage_path = partition->Storage_Path;
+		if (zip_path.size() < storage_path.size()) {
+			SetValue(TW_ZIP_LOCATION_VAR, storage_path);
+		} else {
+			zip_root = TWFunc::Get_Root_Path(zip_path);
+			if (zip_root != storage_path) {
+				LOGINFO("DataManager::SetBackupFolder zip path was %s changing to %s, %s\n", zip_path.c_str(), storage_path.c_str(), zip_root.c_str());
+				SetValue(TW_ZIP_LOCATION_VAR, storage_path);
+			}
+		}
+	} else {
+		if (PartitionManager.Fstab_Processed() != 0)
+			LOGERR("Storage partition '%s' not found\n", str.c_str());
+	}
+}
+
+void DataManager::SetDefaultValues()
+{
+	string str, path;
+
+	get_device_id();
+
+	mInitialized = 1;
+
+	mConstValues.insert(make_pair("true", "1"));
+	mConstValues.insert(make_pair("false", "0"));
+
+	mConstValues.insert(make_pair(TW_VERSION_VAR, TW_VERSION_STR));
+	mValues.insert(make_pair("tw_button_vibrate", make_pair("80", 1)));
+	mValues.insert(make_pair("tw_keyboard_vibrate", make_pair("40", 1)));
+	mValues.insert(make_pair("tw_action_vibrate", make_pair("160", 1)));
+
+	TWPartition *store = PartitionManager.Get_Default_Storage_Partition();
+	if(store)
+		mValues.insert(make_pair("tw_storage_path", make_pair(store->Storage_Path.c_str(), 1)));
+	else
+		mValues.insert(make_pair("tw_storage_path", make_pair("/", 1)));
+
+#ifdef TW_FORCE_CPUINFO_FOR_DEVICE_ID
+	printf("TW_FORCE_CPUINFO_FOR_DEVICE_ID := true\n");
+#endif
+
+#ifdef BOARD_HAS_NO_REAL_SDCARD
+	printf("BOARD_HAS_NO_REAL_SDCARD := true\n");
+	mConstValues.insert(make_pair(TW_ALLOW_PARTITION_SDCARD, "0"));
+#else
+	mConstValues.insert(make_pair(TW_ALLOW_PARTITION_SDCARD, "1"));
+#endif
+
+#ifdef TW_INCLUDE_DUMLOCK
+	printf("TW_INCLUDE_DUMLOCK := true\n");
+	mConstValues.insert(make_pair(TW_SHOW_DUMLOCK, "1"));
+#else
+	mConstValues.insert(make_pair(TW_SHOW_DUMLOCK, "0"));
+#endif
+
+	str = GetCurrentStoragePath();
+	SetValue(TW_ZIP_LOCATION_VAR, str.c_str(), 1);
+	str += "/TWRP/BACKUPS/";
+
+	string dev_id;
+	GetValue("device_id", dev_id);
+
+	str += dev_id;
+	SetValue(TW_BACKUPS_FOLDER_VAR, str, 0);
+
+	mConstValues.insert(make_pair(TW_REBOOT_SYSTEM, "1"));
+#ifdef TW_NO_REBOOT_RECOVERY
+	printf("TW_NO_REBOOT_RECOVERY := true\n");
+	mConstValues.insert(make_pair(TW_REBOOT_RECOVERY, "0"));
+#else
+	mConstValues.insert(make_pair(TW_REBOOT_RECOVERY, "1"));
+#endif
+	mConstValues.insert(make_pair(TW_REBOOT_POWEROFF, "1"));
+#ifdef TW_NO_REBOOT_BOOTLOADER
+	printf("TW_NO_REBOOT_BOOTLOADER := true\n");
+	mConstValues.insert(make_pair(TW_REBOOT_BOOTLOADER, "0"));
+#else
+	mConstValues.insert(make_pair(TW_REBOOT_BOOTLOADER, "1"));
+#endif
+#ifdef RECOVERY_SDCARD_ON_DATA
+	printf("RECOVERY_SDCARD_ON_DATA := true\n");
+	mConstValues.insert(make_pair(TW_HAS_DATA_MEDIA, "1"));
+	mConstValues.insert(make_pair("tw_has_internal", "1"));
+	datamedia = true;
+#else
+	mValues.insert(make_pair(TW_HAS_DATA_MEDIA, make_pair("0", 0)));
+	mValues.insert(make_pair("tw_has_internal", make_pair("0", 0)));
+#endif
+#ifdef TW_NO_BATT_PERCENT
+	printf("TW_NO_BATT_PERCENT := true\n");
+	mConstValues.insert(make_pair(TW_NO_BATTERY_PERCENT, "1"));
+#else
+	mConstValues.insert(make_pair(TW_NO_BATTERY_PERCENT, "0"));
+#endif
+#ifdef TW_NO_CPU_TEMP
+	printf("TW_NO_CPU_TEMP := true\n");
+	mConstValues.insert(make_pair("tw_no_cpu_temp", "1"));
+#else
+	string cpu_temp_file;
+#ifdef TW_CUSTOM_CPU_TEMP_PATH
+	cpu_temp_file = EXPAND(TW_CUSTOM_CPU_TEMP_PATH);
+#else
+	cpu_temp_file = "/sys/class/thermal/thermal_zone0/temp";
+#endif
+	if (TWFunc::Path_Exists(cpu_temp_file)) {
+		mConstValues.insert(make_pair("tw_no_cpu_temp", "0"));
+	} else {
+		LOGINFO("CPU temperature file '%s' not found, disabling CPU temp.\n", cpu_temp_file.c_str());
+		mConstValues.insert(make_pair("tw_no_cpu_temp", "1"));
+	}
+#endif
+#ifdef TW_CUSTOM_POWER_BUTTON
+	printf("TW_POWER_BUTTON := %s\n", EXPAND(TW_CUSTOM_POWER_BUTTON));
+	mConstValues.insert(make_pair(TW_POWER_BUTTON, EXPAND(TW_CUSTOM_POWER_BUTTON)));
+#else
+	mConstValues.insert(make_pair(TW_POWER_BUTTON, "0"));
+#endif
+#ifdef TW_ALWAYS_RMRF
+	printf("TW_ALWAYS_RMRF := true\n");
+	mConstValues.insert(make_pair(TW_RM_RF_VAR, "1"));
+#endif
+#ifdef TW_NEVER_UNMOUNT_SYSTEM
+	printf("TW_NEVER_UNMOUNT_SYSTEM := true\n");
+	mConstValues.insert(make_pair(TW_DONT_UNMOUNT_SYSTEM, "1"));
+#else
+	mConstValues.insert(make_pair(TW_DONT_UNMOUNT_SYSTEM, "0"));
+#endif
+#ifdef TW_NO_USB_STORAGE
+	printf("TW_NO_USB_STORAGE := true\n");
+	mConstValues.insert(make_pair(TW_HAS_USB_STORAGE, "0"));
+#else
+	char lun_file[255];
+	string Lun_File_str = CUSTOM_LUN_FILE;
+	size_t found = Lun_File_str.find("%");
+	if (found != string::npos) {
+		sprintf(lun_file, CUSTOM_LUN_FILE, 0);
+		Lun_File_str = lun_file;
+	}
+	if (!TWFunc::Path_Exists(Lun_File_str)) {
+		LOGINFO("Lun file '%s' does not exist, USB storage mode disabled\n", Lun_File_str.c_str());
+		mConstValues.insert(make_pair(TW_HAS_USB_STORAGE, "0"));
+	} else {
+		LOGINFO("Lun file '%s'\n", Lun_File_str.c_str());
+		mConstValues.insert(make_pair(TW_HAS_USB_STORAGE, "1"));
+	}
+#endif
+#ifdef TW_INCLUDE_INJECTTWRP
+	printf("TW_INCLUDE_INJECTTWRP := true\n");
+	mConstValues.insert(make_pair(TW_HAS_INJECTTWRP, "1"));
+	mValues.insert(make_pair(TW_INJECT_AFTER_ZIP, make_pair("1", 1)));
+#else
+	mConstValues.insert(make_pair(TW_HAS_INJECTTWRP, "0"));
+	mValues.insert(make_pair(TW_INJECT_AFTER_ZIP, make_pair("0", 1)));
+#endif
+#ifdef TW_HAS_DOWNLOAD_MODE
+	printf("TW_HAS_DOWNLOAD_MODE := true\n");
+	mConstValues.insert(make_pair(TW_DOWNLOAD_MODE, "1"));
+#endif
+#ifdef TW_INCLUDE_CRYPTO
+	mConstValues.insert(make_pair(TW_HAS_CRYPTO, "1"));
+	printf("TW_INCLUDE_CRYPTO := true\n");
+#endif
+#ifdef TW_SDEXT_NO_EXT4
+	printf("TW_SDEXT_NO_EXT4 := true\n");
+	mConstValues.insert(make_pair(TW_SDEXT_DISABLE_EXT4, "1"));
+#else
+	mConstValues.insert(make_pair(TW_SDEXT_DISABLE_EXT4, "0"));
+#endif
+
+#ifdef TW_HAS_NO_BOOT_PARTITION
+	mValues.insert(make_pair("tw_backup_list", make_pair("/system;/data;", 1)));
+#else
+	mValues.insert(make_pair("tw_backup_list", make_pair("/system;/data;/boot;", 1)));
+#endif
+	mConstValues.insert(make_pair(TW_MIN_SYSTEM_VAR, TW_MIN_SYSTEM_SIZE));
+	mValues.insert(make_pair(TW_BACKUP_NAME, make_pair("(Auto Generate)", 0)));
+
+	mValues.insert(make_pair(TW_REBOOT_AFTER_FLASH_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_SIGNED_ZIP_VERIFY_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_FORCE_MD5_CHECK_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_COLOR_THEME_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_USE_COMPRESSION_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_SHOW_SPAM_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_TIME_ZONE_VAR, make_pair("CST6CDT", 1)));
+	mValues.insert(make_pair(TW_SORT_FILES_BY_DATE_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_GUI_SORT_ORDER, make_pair("1", 1)));
+	mValues.insert(make_pair(TW_RM_RF_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_SKIP_MD5_CHECK_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_SKIP_MD5_GENERATE_VAR, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_SDEXT_SIZE, make_pair("512", 1)));
+	mValues.insert(make_pair(TW_SWAP_SIZE, make_pair("32", 1)));
+	mValues.insert(make_pair(TW_SDPART_FILE_SYSTEM, make_pair("ext3", 1)));
+	mValues.insert(make_pair(TW_TIME_ZONE_GUISEL, make_pair("CST6;CDT", 1)));
+	mValues.insert(make_pair(TW_TIME_ZONE_GUIOFFSET, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_TIME_ZONE_GUIDST, make_pair("1", 1)));
+	mValues.insert(make_pair(TW_ACTION_BUSY, make_pair("0", 0)));
+	mValues.insert(make_pair("tw_wipe_cache", make_pair("0", 0)));
+	mValues.insert(make_pair("tw_wipe_dalvik", make_pair("0", 0)));
+	if (GetIntValue(TW_HAS_INTERNAL) == 1 && GetIntValue(TW_HAS_DATA_MEDIA) == 1 && GetIntValue(TW_HAS_EXTERNAL) == 0)
+		SetValue(TW_HAS_USB_STORAGE, 0, 0);
+	else
+		SetValue(TW_HAS_USB_STORAGE, 1, 0);
+	mValues.insert(make_pair(TW_ZIP_INDEX, make_pair("0", 0)));
+	mValues.insert(make_pair(TW_ZIP_QUEUE_COUNT, make_pair("0", 0)));
+	mValues.insert(make_pair(TW_FILENAME, make_pair("/sdcard", 0)));
+	mValues.insert(make_pair(TW_SIMULATE_ACTIONS, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_SIMULATE_FAIL, make_pair("0", 1)));
+	mValues.insert(make_pair(TW_IS_ENCRYPTED, make_pair("0", 0)));
+	mValues.insert(make_pair(TW_IS_DECRYPTED, make_pair("0", 0)));
+	mValues.insert(make_pair(TW_CRYPTO_PASSWORD, make_pair("0", 0)));
+	mValues.insert(make_pair(TW_DATA_BLK_DEVICE, make_pair("0", 0)));
+	mValues.insert(make_pair("tw_terminal_state", make_pair("0", 0)));
+	mValues.insert(make_pair("tw_background_thread_running", make_pair("0", 0)));
+	mValues.insert(make_pair(TW_RESTORE_FILE_DATE, make_pair("0", 0)));
+	mValues.insert(make_pair("tw_military_time", make_pair("0", 1)));
+#ifdef TW_NO_SCREEN_TIMEOUT
+	mValues.insert(make_pair("tw_screen_timeout_secs", make_pair("0", 1)));
+	mValues.insert(make_pair("tw_no_screen_timeout", make_pair("1", 1)));
+#else
+	mValues.insert(make_pair("tw_screen_timeout_secs", make_pair("60", 1)));
+	mValues.insert(make_pair("tw_no_screen_timeout", make_pair("0", 1)));
+#endif
+	mValues.insert(make_pair("tw_gui_done", make_pair("0", 0)));
+	mValues.insert(make_pair("tw_encrypt_backup", make_pair("0", 0)));
+#ifdef TW_BRIGHTNESS_PATH
+	string findbright;
+	if (strcmp(EXPAND(TW_BRIGHTNESS_PATH), "/nobrightness") != 0) {
+		findbright = EXPAND(TW_BRIGHTNESS_PATH);
+		LOGINFO("TW_BRIGHTNESS_PATH := %s\n", findbright.c_str());
+		if (!TWFunc::Path_Exists(findbright)) {
+			LOGINFO("Specified brightness file '%s' not found.\n", findbright.c_str());
+			findbright = "";
+		}
+	}
+	if (findbright.empty()) {
+		// Attempt to locate the brightness file
+		findbright = Find_File::Find("brightness", "/sys/class/backlight");
+		if (findbright.empty()) findbright = Find_File::Find("brightness", "/sys/class/leds/lcd-backlight");
+	}
+	if (findbright.empty()) {
+		LOGINFO("Unable to locate brightness file\n");
+		mConstValues.insert(make_pair("tw_has_brightnesss_file", "0"));
+	} else {
+		LOGINFO("Found brightness file at '%s'\n", findbright.c_str());
+		mConstValues.insert(make_pair("tw_has_brightnesss_file", "1"));
+		mConstValues.insert(make_pair("tw_brightness_file", findbright));
+		ostringstream maxVal;
+		maxVal << TW_MAX_BRIGHTNESS;
+		mConstValues.insert(make_pair("tw_brightness_max", maxVal.str()));
+		mValues.insert(make_pair("tw_brightness", make_pair(maxVal.str(), 1)));
+		mValues.insert(make_pair("tw_brightness_pct", make_pair("100", 1)));
+#ifdef TW_SECONDARY_BRIGHTNESS_PATH
+		string secondfindbright = EXPAND(TW_SECONDARY_BRIGHTNESS_PATH);
+		if (secondfindbright != "" && TWFunc::Path_Exists(secondfindbright)) {
+			LOGINFO("Will use a second brightness file at '%s'\n", secondfindbright.c_str());
+			mConstValues.insert(make_pair("tw_secondary_brightness_file", secondfindbright));
+		} else {
+			LOGINFO("Specified secondary brightness file '%s' not found.\n", secondfindbright.c_str());
+		}
+#endif
+		string max_bright = maxVal.str();
+		TWFunc::Set_Brightness(max_bright);
+	}
+#endif
+	mValues.insert(make_pair(TW_MILITARY_TIME, make_pair("0", 1)));
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	mValues.insert(make_pair("tw_include_encrypted_backup", make_pair("1", 0)));
+#else
+	LOGINFO("TW_EXCLUDE_ENCRYPTED_BACKUPS := true\n");
+	mValues.insert(make_pair("tw_include_encrypted_backup", make_pair("0", 0)));
+#endif
+#ifdef TW_HAS_MTP
+	mConstValues.insert(make_pair("tw_has_mtp", "1"));
+	mValues.insert(make_pair("tw_mtp_enabled", make_pair("1", 1)));
+	mValues.insert(make_pair("tw_mtp_debug", make_pair("0", 1)));
+#else
+	LOGINFO("TW_EXCLUDE_MTP := true\n");
+	mConstValues.insert(make_pair("tw_has_mtp", "0"));
+	mConstValues.insert(make_pair("tw_mtp_enabled", "0"));
+#endif
+}
+
+// Magic Values
+int DataManager::GetMagicValue(const string varName, string& value)
+{
+	// Handle special dynamic cases
+	if (varName == "tw_time")
+	{
+		char tmp[32];
+
+		struct tm *current;
+		time_t now;
+		int tw_military_time;
+		now = time(0);
+		current = localtime(&now);
+		GetValue(TW_MILITARY_TIME, tw_military_time);
+		if (current->tm_hour >= 12)
+		{
+			if (tw_military_time == 1)
+				sprintf(tmp, "%d:%02d", current->tm_hour, current->tm_min);
+			else
+				sprintf(tmp, "%d:%02d PM", current->tm_hour == 12 ? 12 : current->tm_hour - 12, current->tm_min);
+		}
+		else
+		{
+			if (tw_military_time == 1)
+				sprintf(tmp, "%d:%02d", current->tm_hour, current->tm_min);
+			else
+				sprintf(tmp, "%d:%02d AM", current->tm_hour == 0 ? 12 : current->tm_hour, current->tm_min);
+		}
+		value = tmp;
+		return 0;
+	}
+	else if (varName == "tw_cpu_temp")
+	{
+	   string cpu_temp_file;
+	   static unsigned long convert_temp = 0;
+	   static time_t cpuSecCheck = 0;
+	   int divisor = 0;
+	   struct timeval curTime;
+	   string results;
+
+	   gettimeofday(&curTime, NULL);
+	   if (curTime.tv_sec > cpuSecCheck)
+	   {
+#ifdef TW_CUSTOM_CPU_TEMP_PATH
+		   cpu_temp_file = EXPAND(TW_CUSTOM_CPU_TEMP_PATH);
+		   if (TWFunc::read_file(cpu_temp_file, results) != 0)
+			return -1;
+#else
+		   cpu_temp_file = "/sys/class/thermal/thermal_zone0/temp";
+		   if (TWFunc::read_file(cpu_temp_file, results) != 0)
+			return -1;
+#endif
+		   convert_temp = strtoul(results.c_str(), NULL, 0) / 1000;
+		   if (convert_temp <= 0)
+			convert_temp = strtoul(results.c_str(), NULL, 0);
+		   if (convert_temp >= 150)
+			convert_temp = strtoul(results.c_str(), NULL, 0) / 10;
+		   cpuSecCheck = curTime.tv_sec + 5;
+	   }
+	   value = TWFunc::to_string(convert_temp);
+	   return 0;
+	}
+	else if (varName == "tw_battery")
+	{
+		char tmp[16];
+		static char charging = ' ';
+		static int lastVal = -1;
+		static time_t nextSecCheck = 0;
+		struct timeval curTime;
+		gettimeofday(&curTime, NULL);
+		if (curTime.tv_sec > nextSecCheck)
+		{
+			char cap_s[4];
+#ifdef TW_CUSTOM_BATTERY_PATH
+			string capacity_file = EXPAND(TW_CUSTOM_BATTERY_PATH);
+			capacity_file += "/capacity";
+			FILE * cap = fopen(capacity_file.c_str(),"rt");
+#else
+			FILE * cap = fopen("/sys/class/power_supply/battery/capacity","rt");
+#endif
+			if (cap){
+				fgets(cap_s, 4, cap);
+				fclose(cap);
+				lastVal = atoi(cap_s);
+				if (lastVal > 100)	lastVal = 101;
+				if (lastVal < 0)	lastVal = 0;
+			}
+#ifdef TW_CUSTOM_BATTERY_PATH
+			string status_file = EXPAND(TW_CUSTOM_BATTERY_PATH);
+			status_file += "/status";
+			cap = fopen(status_file.c_str(),"rt");
+#else
+			cap = fopen("/sys/class/power_supply/battery/status","rt");
+#endif
+			if (cap) {
+				fgets(cap_s, 2, cap);
+				fclose(cap);
+				if (cap_s[0] == 'C')
+					charging = '+';
+				else
+					charging = ' ';
+			}
+			nextSecCheck = curTime.tv_sec + 60;
+		}
+
+		sprintf(tmp, "%i%%%c", lastVal, charging);
+		value = tmp;
+		return 0;
+	}
+	return -1;
+}
+
+void DataManager::Output_Version(void)
+{
+	string Path;
+	char version[255];
+
+	if (!PartitionManager.Mount_By_Path("/cache", false)) {
+		LOGINFO("Unable to mount '%s' to write version number.\n", Path.c_str());
+		return;
+	}
+	if (!TWFunc::Path_Exists("/cache/recovery/.")) {
+		LOGINFO("Recreating /cache/recovery folder.\n");
+		if (mkdir("/cache/recovery", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0) {
+			LOGERR("DataManager::Output_Version -- Unable to make /cache/recovery\n");
+			return;
+		}
+	}
+	Path = "/cache/recovery/.version";
+	if (TWFunc::Path_Exists(Path)) {
+		unlink(Path.c_str());
+	}
+	FILE *fp = fopen(Path.c_str(), "w");
+	if (fp == NULL) {
+		LOGERR("Unable to open '%s'.\n", Path.c_str());
+		return;
+	}
+	strcpy(version, TW_VERSION_STR);
+	fwrite(version, sizeof(version[0]), strlen(version) / sizeof(version[0]), fp);
+	fclose(fp);
+	TWFunc::copy_file("/etc/recovery.fstab", "/cache/recovery/recovery.fstab", 0644);
+	PartitionManager.Output_Storage_Fstab();
+	sync();
+	LOGINFO("Version number saved to '%s'\n", Path.c_str());
+}
+
+void DataManager::ReadSettingsFile(void)
+{
+#ifndef TW_OEM_BUILD
+	// Load up the values for TWRP - Sleep to let the card be ready
+	char mkdir_path[255], settings_file[255];
+	int is_enc, has_dual, use_ext, has_data_media, has_ext;
+
+	GetValue(TW_IS_ENCRYPTED, is_enc);
+	GetValue(TW_HAS_DATA_MEDIA, has_data_media);
+	if (is_enc == 1 && has_data_media == 1) {
+		LOGINFO("Cannot load settings -- encrypted.\n");
+		return;
+	}
+
+	memset(mkdir_path, 0, sizeof(mkdir_path));
+	memset(settings_file, 0, sizeof(settings_file));
+	sprintf(mkdir_path, "%s/TWRP", DataManager_GetSettingsStoragePath());
+	sprintf(settings_file, "%s/.twrps", mkdir_path);
+
+	if (!PartitionManager.Mount_Settings_Storage(false))
+	{
+		usleep(500000);
+		if (!PartitionManager.Mount_Settings_Storage(false))
+			LOGERR("Unable to mount %s when trying to read settings file.\n", settings_file);
+	}
+
+	mkdir(mkdir_path, 0777);
+
+	LOGINFO("Attempt to load settings from settings file...\n");
+	LoadValues(settings_file);
+	Output_Version();
+#endif // ifdef TW_OEM_BUILD
+	PartitionManager.Mount_All_Storage();
+	update_tz_environment_variables();
+#ifdef TW_MAX_BRIGHTNESS
+	if (GetStrValue("tw_brightness_path") != "/nobrightness") {
+		TWFunc::Set_Brightness(GetStrValue("tw_brightness"));
+	}
+#endif
+}
+
+string DataManager::GetCurrentStoragePath(void)
+{
+	return GetStrValue("tw_storage_path");
+}
+
+string& DataManager::CGetCurrentStoragePath()
+{
+	return GetValueRef("tw_storage_path");
+}
+
+string DataManager::GetSettingsStoragePath(void)
+{
+	return GetStrValue("tw_settings_path");
+}
+
+string& DataManager::CGetSettingsStoragePath()
+{
+	return GetValueRef("tw_settings_path");
+}
+
+extern "C" int DataManager_ResetDefaults(void)
+{
+	return DataManager::ResetDefaults();
+}
+
+extern "C" void DataManager_LoadDefaults(void)
+{
+	return DataManager::SetDefaultValues();
+}
+
+extern "C" int DataManager_LoadValues(const char* filename)
+{
+	return DataManager::LoadValues(filename);
+}
+
+extern "C" int DataManager_Flush(void)
+{
+	return DataManager::Flush();
+}
+
+extern "C" int DataManager_GetValue(const char* varName, char* value)
+{
+	int ret;
+	string str;
+
+	ret = DataManager::GetValue(varName, str);
+	if (ret == 0)
+		strcpy(value, str.c_str());
+	return ret;
+}
+
+extern "C" const char* DataManager_GetStrValue(const char* varName)
+{
+	string& str = DataManager::GetValueRef(varName);
+	return str.c_str();
+}
+
+extern "C" const char* DataManager_GetCurrentStoragePath(void)
+{
+	string& str = DataManager::CGetCurrentStoragePath();
+	return str.c_str();
+}
+
+extern "C" const char* DataManager_GetSettingsStoragePath(void)
+{
+	string& str = DataManager::CGetSettingsStoragePath();
+	return str.c_str();
+}
+
+extern "C" int DataManager_GetIntValue(const char* varName)
+{
+	return DataManager::GetIntValue(varName);
+}
+
+extern "C" int DataManager_SetStrValue(const char* varName, char* value)
+{
+	return DataManager::SetValue(varName, value, 0);
+}
+
+extern "C" int DataManager_SetIntValue(const char* varName, int value)
+{
+	return DataManager::SetValue(varName, value, 0);
+}
+
+extern "C" int DataManager_SetFloatValue(const char* varName, float value)
+{
+	return DataManager::SetValue(varName, value, 0);
+}
+
+extern "C" int DataManager_ToggleIntValue(const char* varName)
+{
+	if (DataManager::GetIntValue(varName))
+		return DataManager::SetValue(varName, 0);
+	else
+		return DataManager::SetValue(varName, 1);
+}
+
+extern "C" void DataManager_DumpValues(void)
+{
+	return DataManager::DumpValues();
+}
+
+extern "C" void DataManager_ReadSettingsFile(void)
+{
+	return DataManager::ReadSettingsFile();
+}
+void DataManager::Vibrate(const string varName)
+{
+	int vib_value = 0;
+	GetValue(varName, vib_value);
+	if (vib_value) {
+		vibrate(vib_value);
+	}
+}
diff --git a/data.h b/data.h
new file mode 100644
index 0000000..0ca9f4e
--- /dev/null
+++ b/data.h
@@ -0,0 +1,41 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _DATA_HEADER
+#define _DATA_HEADER
+
+int DataManager_ResetDefaults(void);
+void DataManager_LoadDefaults(void);
+int DataManager_LoadValues(const char* filename);
+int DataManager_Flush(void);
+const char* DataManager_GetStrValue(const char* varName);
+const char* DataManager_GetCurrentStoragePath(void);
+const char* DataManager_GetSettingsStoragePath(void);
+int DataManager_GetIntValue(const char* varName);
+
+int DataManager_SetStrValue(const char* varName, char* value);
+int DataManager_SetIntValue(const char* varName, int value);
+int DataManager_SetFloatValue(const char* varName, float value);
+
+int DataManager_ToggleIntValue(const char* varName);
+
+void DataManager_DumpValues(void);
+void DataManager_ReadSettingsFile(void);
+
+#endif  // _DATA_HEADER
+
diff --git a/data.hpp b/data.hpp
new file mode 100644
index 0000000..df57f23
--- /dev/null
+++ b/data.hpp
@@ -0,0 +1,92 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _DATAMANAGER_HPP_HEADER
+#define _DATAMANAGER_HPP_HEADER
+
+#include <string>
+#include <utility>
+#include <map>
+
+using namespace std;
+
+class DataManager
+{
+public:
+	static int ResetDefaults();
+	static int LoadValues(const string filename);
+	static int Flush();
+
+	// Core get routines
+	static int GetValue(const string varName, string& value);
+	static int GetValue(const string varName, int& value);
+	static int GetValue(const string varName, float& value);
+	static unsigned long long GetValue(const string varName, unsigned long long& value);
+
+	// This is a dangerous function. It will create the value if it doesn't exist so it has a valid c_str
+	static string& GetValueRef(const string varName);
+
+	// Helper functions
+	static string GetStrValue(const string varName);
+	static int GetIntValue(const string varName);
+
+	// Core set routines
+	static int SetValue(const string varName, string value, int persist = 0);
+	static int SetValue(const string varName, int value, int persist = 0);
+	static int SetValue(const string varName, float value, int persist = 0);
+	static int SetValue(const string varName, unsigned long long value, int persist = 0);
+	static int SetProgress(float Fraction);
+	static int ShowProgress(float Portion, float Seconds);
+
+	static void DumpValues();
+	static void update_tz_environment_variables();
+	static void Vibrate(const string varName);
+	static void SetBackupFolder();
+	static void SetDefaultValues();
+	static void Output_Version(void); // Outputs the version to a file in the TWRP folder
+	static void ReadSettingsFile(void);
+
+	static string GetCurrentStoragePath(void);
+	static string& CGetCurrentStoragePath();
+	static string GetSettingsStoragePath(void);
+	static string& CGetSettingsStoragePath();
+
+protected:
+	typedef pair<string, int> TStrIntPair;
+	typedef pair<string, unsigned long long> TStrULLPair;
+	typedef pair<string, TStrIntPair> TNameValuePair;
+	static map<string, TStrIntPair> mValues;
+	static map<string, TStrULLPair> mULLValues;
+	static string mBackingFile;
+	static int mInitialized;
+
+	static map<string, string> mConstValues;
+
+protected:
+	static int SaveValues();
+
+	static int GetMagicValue(string varName, string& value);
+
+private:
+	static void sanitize_device_id(char* device_id);
+	static void get_device_id(void);
+
+};
+
+#endif // _DATAMANAGER_HPP_HEADER
+
diff --git a/digest/md5.c b/digest/md5.c
new file mode 100644
index 0000000..488d16e
--- /dev/null
+++ b/digest/md5.c
@@ -0,0 +1,257 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <string.h>		/* for memcpy() */
+
+#include "md5.h"
+
+#if !defined(WORDS_BIGENDIAN)
+#define byteReverse(buf, len)	/* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32_t t;
+    do {
+	t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(uint32_t *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    memcpy(p, buf, len);
+	    return;
+	}
+	memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[MD5LENGTH], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform.
+     * Use memcpy to avoid aliasing problems.  On most systems,
+     * this will be optimized away to the same code.
+     */
+    memcpy(&ctx->in[14 * sizeof(uint32_t)], &ctx->bits[0], 4);
+    memcpy(&ctx->in[15 * sizeof(uint32_t)], &ctx->bits[1], 4);
+
+    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, MD5LENGTH);
+    memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+    register uint32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#endif
+
diff --git a/digest/md5.h b/digest/md5.h
new file mode 100644
index 0000000..d997e37
--- /dev/null
+++ b/digest/md5.h
@@ -0,0 +1,29 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+#endif
+
+#define MD5LENGTH 16
+
+struct MD5Context {
+	uint32_t buf[4];
+	uint32_t bits[2];
+	unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+	       unsigned len);
+void MD5Final(unsigned char digest[MD5LENGTH], struct MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+#endif /* !MD5_H */
diff --git a/dosfstools/Android.mk b/dosfstools/Android.mk
new file mode 100644
index 0000000..7c8592d
--- /dev/null
+++ b/dosfstools/Android.mk
@@ -0,0 +1,57 @@
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := src/boot.c src/check.c src/common.c \
+	src/fat.c src/file.c src/io.c src/lfn.c src/dosfsck.c
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_SHARED_LIBRARIES := libc
+LOCAL_CFLAGS += -D_USING_BIONIC_
+LOCAL_CFLAGS += -DUSE_ANDROID_RETVALS
+LOCAL_MODULE = dosfsck
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+include $(BUILD_EXECUTABLE)
+
+# build symlink
+SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,fsck_msdos)
+$(SYMLINKS): DOSFSCK_BINARY := $(LOCAL_MODULE)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+	@echo "Symlink: $@ -> $(DOSFSCK_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(DOSFSCK_BINARY) $@
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fsck_msdos_symlink
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(SYMLINKS)
+include $(BUILD_PHONY_PACKAGE)
+SYMLINKS :=
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := src/boot.c src/check.c src/common.c src/fat.c \
+	src/file.c src/io.c src/lfn.c src/dosfslabel.c
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS) \
+	bionic/libc/kernel/common
+LOCAL_SHARED_LIBRARIES := libc
+LOCAL_CFLAGS += -D_USING_BIONIC_
+LOCAL_MODULE = dosfslabel
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := src/mkdosfs.c
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_SHARED_LIBRARIES := libc
+LOCAL_CFLAGS += -D_USING_BIONIC_
+LOCAL_MODULE = mkdosfs
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/dosfstools/COPYING b/dosfstools/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/dosfstools/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/dosfstools/ChangeLog b/dosfstools/ChangeLog
new file mode 100644
index 0000000..b8dc2e0
--- /dev/null
+++ b/dosfstools/ChangeLog
@@ -0,0 +1,760 @@
+commit 2e20319843ea85d77c51a9ce9a9b278662426d98
+Author: Michael Casadevall <mcasadevall@ubuntu.com>
+Date:   Tue Jun 7 19:19:30 2011 +0200
+
+    Correcting miscalculation of sector number in some cases.
+    
+    mkdosfs will incorrectly calculate the number of sectors of a
+    given FAT partition if the number sectors are odd due to
+    count_blocks incorrectly handling the remainder of a division
+    operation. This miscalculation causes the OMAP4 bootloader to
+    fail to boot.
+    
+    This bug can be observed by comparing the total sector size in
+    fdisk expert more to fsck.msdos; this discrepancy only shows up
+    when the number of sectors are odd.
+    
+    See https://bugs.launchpad.net/ubuntu/+source/dosfstools/+bug/794043
+    for more information.
+
+commit 45c3a5d8229ef998962e495f1efe7d2a6cd8a825
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sat Jan 8 23:38:59 2011 +0100
+
+    Re-running Nindent.
+
+commit 37115695884422e6f58ec490d11d460539715f8a
+Author: Sergey Gusarov <laborer2008@gmail.com>
+Date:   Sat Jan 8 23:36:11 2011 +0100
+
+    Fixing compiler warnings related to the mismatch of types "char *" / "unsigned
+    char *".
+    
+    These warnings appear when you compile the project with the option "-Wall", what
+    is done with the current default Makefile.
+
+commit a9055613f0d826021db65c79c2df87ac91e89215
+Author: Jaroslav Skarvada <jskarvad@redhat.com>
+Date:   Thu Jan 6 22:35:00 2011 +0100
+
+    Fixing overflow bug in reclaim_file function, see
+    https://bugzilla.redhat.com/show_bug.cgi?id=660154 for more information.
+    
+    The problem is that alloc_rootdir_entry counts with 10000 files at max, but the
+    filename buffer is only 8 chars long. Due to pattern mask used it results to
+    only 10 files at max (FSCK0-9REC). If there is more than 10 files, it overflows
+    and hangs.
+
+commit 5a2b37f3ef664dbc7850f3d800890d7bb919b3cd
+Author: Sergey Gusarov <laborer2008@gmail.com>
+Date:   Thu Jan 6 22:31:39 2011 +0100
+
+    Fixing conversion specifiers in accordance with the type of expressions.
+
+commit 258e5ebbb24fd6293a86fe22d6bcda8ce1794dcd
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Jan 2 15:41:44 2011 +0100
+
+    Indenting source files.
+
+commit 9c22278dda0f8fc05aa537eb0bcb07e51f0dec6a
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Jan 2 15:39:03 2011 +0100
+
+    Adding Nindent script from syslinux.
+
+commit e6008ff5c15dc2e6d5b33f88a447d1159165c95d
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Dec 24 17:58:29 2010 +0100
+
+    Releasing upstream version 3.0.11.
+
+commit bce60d1b0b739612b63852722d8504986096b40d
+Author: Michael Stapelberg <michael@stapelberg.de>
+Date:   Fri Nov 19 14:09:36 2010 +0100
+
+    Add better error message when the device cannot be opened.
+    
+    This is helpful for SD cards or other removable media which have an enabled
+    write lock -- without the "Permission denied" message, the user has to strace
+    mkdosfs to find out what's going on.
+
+commit ff45bd967e5d8ff7edc496adbad57257d4d5432b
+Author: Jaroslav Skarvada <jskarvad@redhat.com>
+Date:   Fri Oct 8 13:38:16 2010 +0200
+
+    Unalign on s390x, see http://bugzilla.redhat.com/show_bug.cgi?id=624596 for
+    more information.
+
+commit 22874327372f914d2919490326c95f4607f8de74
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Sep 12 09:35:47 2010 +0200
+
+    Releasing upstream version 3.0.10.
+
+commit 8b7c9d94a4571142a77a587138bc26b39f8e2863
+Author: Alexander Korolkov <alexander.korolkov@gmail.com>
+Date:   Sun Sep 12 09:29:12 2010 +0200
+
+    Modify LFN direntries when file is renamed or deleted, see
+    Debian bug #596329.
+
+commit 761b798f3bf2b4a87f2d454f555e18758791c864
+Author: Alexander Korolkov <alexander.korolkov@gmail.com>
+Date:   Sun Sep 12 09:27:07 2010 +0200
+
+    If the test of short filename fails, dosfsck could complain about
+    bad long filename, see Debian bug #596327.
+
+commit 8fa3587a946614cd43d813052e0e31e595e6d63d
+Author: Alexander Korolkov <alexander.korolkov@gmail.com>
+Date:   Sun Sep 12 09:24:47 2010 +0200
+
+    dosfsck: don't complain about bad filenames when short filename
+    contains 7 or more characters with codes 128-255, see Debian
+    bug #596327.
+
+commit 3893857b841096de6a422ef5bed1b2618a7037d5
+Author: Mitch Rybczynski <mrybczynski@miovision.com>
+Date:   Mon Jul 5 14:45:54 2010 +0200
+
+    Adding __arm__ define check for some crosscompile toolchains.
+
+commit 7d03b3cc96b83b67638b463610a29abfd6f51f77
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Mar 14 16:42:32 2010 +0100
+
+    Modernizing dosfslabel manpage.
+
+commit 258049911c5df476fb434e0d87e0ece01b9ba137
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Mar 14 16:33:47 2010 +0100
+
+    Modernizing dosfsck manpage.
+
+commit 50d1d96b9666913a90e16904a63e29925675859c
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Mar 14 16:05:32 2010 +0100
+
+    Fixing spelling error in boot.c.
+
+commit 0e87c7890b598d78c6aa3d2a06b2306980e75a3d
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Jan 31 08:31:32 2010 +0100
+
+    Releasing upstream version 3.0.9.
+
+commit 9415707c2c9ad22b48660593915667dd228722fa
+Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
+Date:   Sun Jan 31 00:11:41 2010 -0500
+
+    Be sure to store the updated reserved_sector count in the boot sector,
+    see Debian bug #567337.
+
+commit 68b3f00471f60a692fe021d65289bbaf2dc990d5
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sat Jan 23 10:16:18 2010 +0100
+
+    Releasing upstream version 3.0.8.
+
+commit 69dbf2e002f0cb3f0781256dec7258b66ffae3b6
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sat Jan 23 10:15:01 2010 +0100
+
+    Removing some cruft in end-comments.
+
+commit eef306657f3152bbf913a8a45c514f11b2dc2494
+Author: Steven J. Magnani <steve@digidescorp.com>
+Date:   Thu Jan 21 16:58:11 2010 +0100
+
+    When compiling a 32-bit version of dosfstools on an x86_64 machine,
+    the resulting applications report strange errors on "large" (> 2 GiB)
+    partitions:
+    
+      Seek to -2118967808:Invalid argument
+    
+      Warning: Filesystem is FAT32 according to fat_length and fat32_length fields,
+      but has only 8613 clusters, less than the required minimum of 65525.
+      This may lead to problems on some systems.
+    
+    This appears to be due to compilation with a 32-bit off_t and lseek() library
+    function.
+    
+    Use lseek64 for positioning, and change some suspect uses of off_t to loff_t.
+
+commit e69f49dd1fe52780071cb3f024d1a8246125915a
+Author: Steven J. Magnani <steve@digidescorp.com>
+Date:   Thu Jan 21 16:56:26 2010 +0100
+
+    If dosfsck is run in read-only mode (-n), exit with code 0
+    if the only issue found is an uninitialized free cluster summary.
+
+commit e52a16d488cf680117e4d476400bdd7915ef2f7a
+Author: Steven J. Magnani <steve@digidescorp.com>
+Date:   Thu Jan 21 16:55:30 2010 +0100
+
+    On x86_64, dosfsck incorrectly claims that a free_cluster summary of
+    0xFFFFFFFF, defined by Microsoft to be "uninitialized," is wrong.
+
+commit 32db02998ed7882df355fa4077009e8d363df3ab
+Author: H. Peter Anvin <hpa@zytor.com>
+Date:   Fri Jan 8 09:16:38 2010 +0100
+
+    mkdosfs: correct alignment of the root directory.
+    
+    Correct the code to align the root directory; it was broken before
+    since bs.dir_entries had already been set at the point of alignment.
+    This patch removes the dual use of bs.dir_entries and root_dir_entries
+    to carry the same information: the information is carried in
+    root_dir_entires exclusively, and then bs.dir_entries is set inside
+    setup_tables() at a late point.
+    
+    The code to align the root directory is also wrapped in
+    if (align_structures); this avoids rounding the number of root
+    directory entries up to a whole sector when used with -a
+    (i.e. preserves the previous behavior.)
+
+commit e462ac31a1d5d235b8a31a9e392e44e2dbc3783c
+Author: H. Peter Anvin <hpa@zytor.com>
+Date:   Wed Jan 6 20:55:36 2010 +0100
+
+    mkdosfs: improve wording in the man page for the -a option.
+    
+    Improve the English language used in the man page for the -a (no
+    align) option to mkdosfs.
+
+commit 680d71d167f30a823f88dd66473fc664cd887ab0
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Wed Jan 6 11:27:25 2010 +0100
+
+    Adding reference to dosfslable in mkdosfs manpage.
+
+commit 60fc9f853c1045e615b34a193738f88021678d30
+Author: H. Peter Anvin <hpa@zytor.com>
+Date:   Wed Jan 6 11:18:55 2010 +0100
+
+    mkdosfs: by default align all structures to cluster boundaries
+    
+    Align all data structures (reserved sectors, FATs, root directory for
+    FAT12/16) to an even multiple of the cluster size. This means that if
+    the partition is aligned, so will all clusters be. This adds
+    significant performance for anything where the physical sector size is
+    larger than the logical sector size, e.g. flash media or large-sector
+    hard disks.
+
+commit 312b05fc47107f695483994375a1f6f429069708
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Thu Dec 24 10:53:36 2009 +0100
+
+    Releasing upstream version 3.0.7.
+
+commit 844307669208608a3464157ddb5e789bd9556f34
+Author: Ben Hutchings <ben@decadent.org.uk>
+Date:   Thu Dec 24 09:55:52 2009 +0100
+
+    Fixing dosfslabel to set volume label in the right place,
+    see Debian bug #559985.
+
+commit 1bae0e2037717d65b3283db9da51ae7686a7a9be
+Author: Lubomir Rintel <lkundrak@v3.sk>
+Date:   Thu Dec 24 09:39:39 2009 +0100
+
+    Fixing out-of bound writes.
+    
+    Firstly, packed attribute is added to the structure so that extension
+    is guarranteed to immediately follow name for the cross-name-extension
+    reads to succeed.
+    
+    Secondly, writes into dir_entry->name that span through the extension as
+    well are split into two, so that FORTIFY_SOURCE's bound checking does
+    not abort dosfsck. There also was an off-by-one error in auto_rename()'s
+    sprintf().
+
+commit eb297071adfca1ed7af85ca111f20ab41db8ac59
+Author: San Mehat <san@google.com>
+Date:   Thu Dec 24 09:31:41 2009 +0100
+
+    Adding custom exit code in dosfsck for the case where the FS is read only.
+
+commit b3864d0939c960d0e0f15e4e3b1d626639b64681
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Oct 4 10:59:33 2009 +0200
+
+    Releasing upstream version 3.0.6.
+
+commit 144f8fcfc3f7982e8f460f8379a753b7a5941783
+Author: Steven J. Magnani <steve@digidescorp.com>
+Date:   Sun Oct 4 10:58:43 2009 +0200
+
+    Attempt to improve clarity of the orphan cluster reclaim code.
+    Minor optimization - remove some unnecessary checking.
+
+commit 343fe6d5e7135efadc498fd91e19ba8da499d0c9
+Author: Steven J. Magnani <steve@digidescorp.com>
+Date:   Sun Oct 4 08:37:19 2009 +0200
+
+    Close hole that permitted clusters to link to (invalid) cluster 1.
+    
+    If an orphan chain that linked to cluster 1 was reclaimed to a file,
+    deletion of the file would result in a filesystem panic.
+
+commit db079a02059d7f7296fbe9f87624623a43816c5f
+Author: Steven J. Magnani <steve@digidescorp.com>
+Date:   Sun Oct 4 08:32:30 2009 +0200
+
+    Fix erroneous report of huge number of clusters in use on big-endian
+    systems when the FSINFO free cluster count is reset.
+
+commit 7d5320b8a374b8da1a16b09b3b9b0713828d6755
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Mon Jul 27 14:26:11 2009 +0200
+
+    Releasing upstream version 3.0.5.
+
+commit e80ede4dd3c2058fe32e29ff82244ecb1c7c5514
+Author: Piotr Kaczuba <pepe@attika.ath.cx>
+Date:   Sun Jul 26 22:21:25 2009 +0200
+
+    Signed/unsigned char mismatch in check.c causes false positives
+    in bad_name() and can result in data loss, see Debian bug #538758.
+
+commit 9e15ddf6d52dd166efcb59f91f16fb9d695c86c5
+Author: Andrew Tridgell <tridge@samba.org>
+Date:   Sun Jul 26 22:12:06 2009 +0200
+
+    Update to new kernel patches that add FAT_NO_83NAME flag.
+    
+    See http://lkml.org/lkml/2009/7/20/425 and
+    http://lkml.org/lkml/2009/7/20/424 for more information.
+
+commit 6c68b94008157c444954d2f90a7f9ec8ffc2ec87
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Tue Jul 21 08:10:52 2009 +0200
+
+    Releasing upstream version 3.0.4.
+
+commit 3ce6422e93f3de746be092e324253a8722917a86
+Author: Andrew Tridgell <tridge@samba.org>
+Date:   Tue Jul 21 07:59:22 2009 +0200
+
+    Modify dosfstools to support the dummy 8.3 short filename values
+    used by Linux systems with the VFAT_FS_DUALNAMES option disabled.
+    
+    See http://lkml.org/lkml/2009/6/26/313 and
+    http://lkml.org/lkml/2009/6/26/314 for more information.
+
+commit 94f8769aeddf0d0d3f0be54361514a464907a4a1
+Author: Paul Rupe <prupe@nc.rr.com>
+Date:   Tue May 19 10:37:52 2009 +0200
+
+    Fixing "Too many files need repair" error during fsck.
+
+commit 89566399e407e54eb14d275770106ad42b3ac87c
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Mon May 18 15:12:04 2009 +0200
+
+    Releasing upstream version 3.0.3.
+
+commit 8147c98a542b714ccab34b57c84ed842bb6b50f2
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Mon May 18 15:10:55 2009 +0200
+
+    Also declaring arm as an unaligned architecture, see Debian bug #502961.
+
+commit 18e27fa5c0f811e7edc10bca771acc7c04b19146
+Author: Steven J. Magnani <steve@digidescorp.com>
+Date:   Mon May 18 15:01:49 2009 +0200
+
+    Adding support for limited-memory embedded systems.
+    
+    This patch reorganizes heap memory usage by dosfsck and mkdosfs
+    to support limited-memory embedded systems - in particular, those
+    based on Xilinx's Microblaze processor. It also adds a few comments.
+
+commit 180b68e652df7bfeb7f336e0247aee8873edea7f
+Author: Mike Frysinger <vapier@gentoo.org>
+Date:   Thu Mar 5 07:03:36 2009 +0100
+
+    Declaring Blackfin as an unaligned architecture.
+
+commit 71ac75353d9158aed663f0a3a9d1a1a67ee4ff4f
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sat Feb 28 09:48:04 2009 +0100
+
+    Releasing upstream version 3.0.2.
+
+commit a75924b8ff629fe7ca5ba9c58ac75f66290242ee
+Author: Hiroaki Ishizawa <hiroaki.ishizawa@gmail.com>
+Date:   Fri Feb 13 10:00:46 2009 +0100
+
+    dosfsck corrupts root directory when fs->nfats is 1.
+
+commit 161a5e1fdd019732e0a304beceaeeb606eb128d6
+Author: Stepan Kasal <skasal@redhat.com>
+Date:   Fri Jan 30 14:56:33 2009 +0100
+
+    src/dosfslabel.c (main): After writing the label, exit code should be 0.
+
+commit 26ffa1fb565c2b3284b846ca2733118808c85cb5
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Jan 30 14:06:01 2009 +0100
+
+    Also installing ChangeLog in install-doc target of Makefile.
+
+commit abad38ee561b02ec47be7e861780bf5fa2a05d0b
+Author: Stepan Kasal <skasal@redhat.com>
+Date:   Fri Jan 30 14:05:12 2009 +0100
+
+    Makefile: Do not clobber time stamps of doc files.
+
+commit 81882d20ec6bd4bf4914d39636cecc8c8e57dd67
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Nov 23 22:45:45 2008 +0100
+
+    Releasing upstream version 3.0.1.
+
+commit 41574812a2586f0b6aa1d4f6e2276e557e9cbbcf
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Nov 23 18:41:01 2008 +0100
+
+    Applying Fedoras dosfstools-vfat-timingfix.diff from Bill Nottingham
+    <notting@redhat.com> to fix vfat timing issue. See
+    https://bugzilla.redhat.com/show_bug.cgi?id=448247 for more information.
+
+commit b80656109cc5cffdefd626c2ec9d45e3cf63a03e
+Author: Ulrich Mueller <ulm@gentoo.org>
+Date:   Tue Oct 7 07:55:37 2008 +0200
+
+    Patch to check for bad number of clusters in dosfsck:
+    
+      * FAT16 filesystems with 65525 clusters or more will be rejected
+        (Before, this was not tested for. Up to 65535 clusters were accepted
+        as good).
+    
+      * For FAT32 filesystems with less than 65525 a warning message will be
+        output.
+    
+    Macro MSDOS_FAT12 is now replaced by FAT12_THRESHOLD to make it
+    consistent with the definition in mkdosfs and to remove the dependency
+    on the kernel version.
+
+commit b9c13d143c420a3ec6e1dcb652cafa407621e9c7
+Author: Dann Frazier <dannf@hp.com>
+Date:   Tue Sep 30 07:25:19 2008 +0200
+
+    Changing some wording to make the indended meaning of "full-disk device"
+    more obvious.
+
+commit 4df18ad463f41ae368c3c51bfb5a033072605663
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Sep 28 11:43:19 2008 +0200
+
+    Releasing upstream version 3.0.0.
+
+commit 17fbf2a6dc9255a6bb832472ae7cda674b55e961
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Sep 28 11:29:01 2008 +0200
+
+    Adding GPL headers to all files.
+
+commit d2039e12c8d53472411c91eb8e7a7c0544e13d6d
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sun Sep 28 10:51:55 2008 +0200
+
+    Adding new GPL license file.
+
+commit e4e457f4b57090ecf0539f48dc682ab9afd14ab8
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 23:31:12 2008 +0200
+
+    Redoing Makefile from scratch.
+
+commit 216568ca3a01ed38962b22c7bf838d15d5a4d98d
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Sat Sep 27 00:17:38 2008 +0200
+
+    Removing whitespaces in all files at EOL and EOF.
+
+commit f59157e06561c525605279145057361afa721042
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 23:48:56 2008 +0200
+
+    Adding Debians dosfslabel.8 manpage from Francois Wendling
+    <frwendling@free.fr>.
+
+commit c1bacab212d2b7f6ea93914976cb60056ff8276d
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 18:36:04 2008 +0200
+
+    Updating version.h includes to new location of version.h file.
+
+commit 1dae9f522062037d3539cadf344e0359c644171f
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 18:19:36 2008 +0200
+
+    Removing old lsm file.
+
+commit d843bec0b987f5582fe048f70e42df18c34d3ae4
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 18:07:47 2008 +0200
+
+    Removing old cvsignore files.
+
+commit 77fddbc03016752286b26913c62b98f86ee63211
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 18:18:39 2008 +0200
+
+    Removing old build file.
+
+commit f3e7168fc9eb6f32a6c85021186d84944cefeba8
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 18:19:16 2008 +0200
+
+    Removing old GPL license files.
+
+commit 68089477036e97911791516ee2167286f26ff819
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 18:21:57 2008 +0200
+
+    Unifying dosfsck and mkdosfs Makefiles in common src/Makefile.
+
+commit 9432a02d6e7c38872d7b1042f1b8be1b24a57427
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 18:04:02 2008 +0200
+
+    Unifying dosfsck and mkdosfs sources in common src directory.
+
+commit 0c179b9ee47174d0f34d9fc796d540012740ac01
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 18:05:27 2008 +0200
+
+    Unifying dosfsck and mkdosfs manpages in common man directory.
+
+commit 2d246c28ba6cfd43be2e44b11283891d922f352b
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 18:12:29 2008 +0200
+
+    Unifying dosfsck and mkdosfs documents in common doc directory.
+
+commit e5b16990515d0214fd103dd8aa22ff6a3cda4b64
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 15:39:51 2008 +0200
+
+    Applying Gentoos dosfstools-2.11-preen.patch from Roy Marples
+    <uberlord@gentoo.org> to alias dosfsck -p to -a:
+    
+      * Map -p to -a for baselayout-2, #177514.
+
+commit 6a1d974251a9f9a142775ace3a8048149abfa90c
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 15:49:43 2008 +0200
+
+    Applying Gentoos dosfstools-2.11-build.patch from Mike Frysinger
+    <vapier@gentoo.org> to improve Makefile:
+    
+      * Respect user settings #157785/#157786 by Diego Petteno.
+
+commit 1ea49f00e61b554dc833d44e1a3617e923be667e
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 15:37:34 2008 +0200
+
+    Applying Gentoos dosfstools-2.11-verify-double-count-fix.patch from
+    Robin H. Johnson <robbat2@gentoo.org> to fix double count of files
+    during verification:
+    
+      * Don't double-count n_files during a verification pass.
+        Bugzilla: http://bugs.gentoo.org/show_bug.cgi?id=99845
+
+commit 2d2f20b2c495fa620c7bb3ec5a0838b08f65ab05
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 15:33:36 2008 +0200
+
+    Applying Gentoos dosfstools-2.11-fat32size.patch from Mike Frysinger
+    <vapier@gentoo.org> to fix generation of filesystems on 256meg devices:
+    
+      * Fix generation of FAT filesystems on devices that are 256meg in size
+        Patch by Ulrich Mueller and accepted upstream
+        http://bugs.gentoo.org/112504
+
+commit a86564a9317b2bf9f7734feacdce794f20af74a7
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 15:22:06 2008 +0200
+
+    Applying Suses dosfstools-2.11-unsupported-sector-size.patch from Petr
+    Gajdos <pgajdos@suse.cz> to add sector size warning:
+    
+      * added warning for creation msdos on filesystem with sector size
+        greater than 4096 [fate#303325]
+
+commit 8171e51f4e02bd9f92bb515ca7896d3cb1b564b5
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 15:18:35 2008 +0200
+
+    Applying Suses dosfstools-2.11-mkdosfs-geo0.diff from Ludwig Nussel
+    <lnussel@suse.de> to fix handling of zero heads and sectors:
+    
+      * the HDIO_GETGEO ioctl works on device mapper devices but returns
+        zero heads and sectors. Therefore let's a) assume dummy values in
+        that case in mkdosfs and b) don't consider such fat file systems as
+        invalid in dosfsck. The Linux kernel accepts them anyways.
+
+commit db887df619f4e995db0ab112334f31464a03fa0e
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 15:15:40 2008 +0200
+
+    Applying Suses dosfstools-2.11-linuxfs.patch from Ruediger Oertel
+    <ro@suse.de> to not include linux/fs.h.
+
+commit 7fe3fa643494b26962d542fac38d988ac60f8c09
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 15:11:50 2008 +0200
+
+    Applying Fedoras dosfstools-2.11-assumeKernel26.patch from Peter Vrabec
+    <pvrabec@redhat.com> to remove linux 2.6 conditionals:
+    
+      * LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) does not work with
+        glibc-kernheaders-2.4-9.1.94
+
+commit 90c1c93c100722a03e48be51b1312fe65c1cb156
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 15:05:00 2008 +0200
+
+    Applying Debians 99-conglomeration.dpatch (no other information
+    available).
+
+commit bb6541bf4735e3a7f1c71b4722c6d03bb4549eea
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:26:41 2008 +0200
+
+    Applying Debians 15-manpage-files.dpatch from Daniel Baumann
+    <daniel@debian.org> to improve dosfsck manpage:
+    
+      * Lists fsckNNNN.rec files in FILES section (Closes: #444596).
+
+commit 49282165866be19d3bc54a3f4bdee2cf3a63ba6c
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:34:42 2008 +0200
+
+    Applying Debians 13-getopt.dpatch from Adonikam Virgo
+    <adonikam@virgonet.org> to fix mkdosfs getopt:
+    
+      * Fixes backup sector getopt (Closes: #232387, #479794).
+
+commit c32e44b85f4eac6f6a94bd620eea4abba257042a
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:34:17 2008 +0200
+
+    Applying Debians 12-zero-slot.dpatch by Karl Tomlinson
+    <karlt@karlt.net> to fix dosfsck zero slot crashes:
+    
+      * Fixes crashes due to zero slot numbers causing a negative offset in
+        the call to copy_lfn_part in lfn_add_slot. On amd64 this results in
+        a SIGSEGV in copy_lfn_part. On x86 the result is heap corruption
+        and thus sometimes a SIGSEGV or double free abort later. (Closes:
+        #152550, #353198, #356377, #401798).
+
+commit 370847af533e628aa9e38710e6d50af09f2b71ba
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:33:54 2008 +0200
+
+    Applying Debians 11-memory-efficiency.dpatch from Eero Tamminen
+    <eero.tamminen@nokia.com> to improve dosfsck memory efficiency:
+    
+      * Improves memory efficiency when checking filesystems.
+
+commit 28da9f286a52acec7df7ad06cb0679e5f828e7f3
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:33:28 2008 +0200
+
+    Applying Debians 10-manpage-synopsis.dpatch from Daniel Baumann
+    <daniel@debian.org> to fix manpage synopsis:
+    
+      * List alternative binary names in manpage synopsis (Closes: #284983).
+
+commit f253073021551c9b58d0f2ac262deb3c1b950b06
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:32:46 2008 +0200
+
+    Applying Debians 09-manpage-fat32.dpatch from Daniel Baumann
+    <daniel@debian.org> to improve mkdosfs manpage:
+    
+      * Don't claim that FAT32 is not choosed automatically (Closes:
+        #414183).
+
+commit f37c07aec3972cfc0db374d544ee3b27c0b4b20b
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:32:23 2008 +0200
+
+    Applying Debians 08-manpage-drop.dpatch from Daniel Baumann
+    <daniel@debian.org> to improve dosfsck manpage:
+    
+      * Don't use confusing word 'drop' when 'delete' is meant (Closes:
+        #134100).
+
+commit 3f970c65586da2f44d2a49b639e89341bbaf1fba
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:31:50 2008 +0200
+
+    Applying Debians 07-manpage-spelling.dpatch from Justin Pryzby
+    <justinpryzby@users.sourceforge.net> to fix mkdosfs manpage typos.
+
+commit 18678ba5f3a10c2a54ffee735651d7a265ae7d54
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:30:31 2008 +0200
+
+    Applying Suses dosfstools-2.11_determine-sector-size.patch from Petr
+    Gajdos <pgajdos@suse.cz> to determine mkdosfs sector size automatically:
+    
+      * determine sector size of device automatically or if -S parameter
+        present, verify, that it's not under physical sector size
+
+commit 29753931b078856d78f473cfb6273e111a26891e
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:30:03 2008 +0200
+
+    Applying Suses dosfstools-2.11-o_excl.patch from Pavol Rusnak
+    <prusnak@suse.cz> to use O_EXCL in mkdosfs:
+    
+      * mkdosfs now opens device with O_EXCL [#238687]
+
+commit 16bb7b09ad9eaf0fe37a732cabcdbdf975b77d3e
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 14:29:36 2008 +0200
+
+    Applying Debians 04-unaligned-memory.dpatch from Khalid Aziz
+    <khalid_aziz@hp.com> to fix dosfsck unaligned memory accesses:
+    
+      * Fix unaligned memory accesses which cause warnings to appear
+        everytime the elilo bootloader script runs. This has led a number
+        of users to believe their install has failed (Closes: #258839).
+
+commit 1298cc8f37eaa27ca542b8b7186ea5a47a63cd7e
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 13:47:40 2008 +0200
+
+    Applying Fedoras dosfstools-2.11-label.patch from Jeremy Katz
+    <katzj@redhat.com> to add dosfslabel (originally by Peter Jones).
+
+commit d23890e1d89770d6d2ba58362c2fc4ebafbde15c
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 13:41:14 2008 +0200
+
+    Applying Fedoras dosfstools-2.11-fortify.patch from Jakub Jelinek
+    <jakub@redhat.com> to make it build with -D_FORTIFY_SOURCE=2:
+    
+      * This violates -D_FORTIFY_SOURCE=2 (which is stricter than C
+        standard), but isn't actually any buffer overflow. But using memcpy
+        is more efficient anyway.
+
+commit 7dbd82000e59246c1c2f8c280c4491259e10a767
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Fri Sep 26 13:40:47 2008 +0200
+
+    Applying Fedoras dosfstools-2.7-argfix.patch (no other information
+    available).
+
+commit 88f3b3139c72ac11cb3dd3f5afa8dbb2198a8de5
+Author: Daniel Baumann <daniel@debian.org>
+Date:   Thu Jun 26 12:45:36 2008 +0200
+
+    Adding upstream version 2.11.
diff --git a/dosfstools/Makefile b/dosfstools/Makefile
new file mode 100644
index 0000000..050b750
--- /dev/null
+++ b/dosfstools/Makefile
@@ -0,0 +1,112 @@
+# Makefile
+#
+# Copyright (C) 2008 Daniel Baumann <daniel@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# On Debian systems, the complete text of the GNU General Public License
+# can be found in /usr/share/common-licenses/GPL-3 file.
+
+DESTDIR =
+PREFIX = /usr/local
+SBINDIR = $(PREFIX)/sbin
+DOCDIR = $(PREFIX)/share/doc
+MANDIR = $(PREFIX)/share/man
+
+#OPTFLAGS = -O2 -fomit-frame-pointer -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+OPTFLAGS = -O2 -fomit-frame-pointer $(shell getconf LFS_CFLAGS)
+#WARNFLAGS = -Wall -pedantic -std=c99
+WARNFLAGS = -Wall
+DEBUGFLAGS = -g
+CFLAGS += $(OPTFLAGS) $(WARNFLAGS) $(DEBUGFLAGS)
+
+VPATH = src
+
+all: build
+
+build: dosfsck dosfslabel mkdosfs
+
+dosfsck: boot.o check.o common.o fat.o file.o io.o lfn.o dosfsck.o
+
+dosfslabel: boot.o check.o common.o fat.o file.o io.o lfn.o dosfslabel.o
+
+mkdosfs: mkdosfs.o
+
+rebuild: distclean build
+
+install: install-bin install-doc install-man
+
+install-bin: build
+	install -d -m 0755 $(DESTDIR)/$(SBINDIR)
+	install -m 0755 dosfsck dosfslabel mkdosfs $(DESTDIR)/$(SBINDIR)
+
+	ln -sf dosfsck $(DESTDIR)/$(SBINDIR)/fsck.msdos
+	ln -sf dosfsck $(DESTDIR)/$(SBINDIR)/fsck.vfat
+	ln -sf mkdosfs $(DESTDIR)/$(SBINDIR)/mkfs.msdos
+	ln -sf mkdosfs $(DESTDIR)/$(SBINDIR)/mkfs.vfat
+
+install-doc:
+	install -d -m 0755 $(DESTDIR)/$(DOCDIR)/dosfstools
+	install -p -m 0644 ChangeLog doc/* $(DESTDIR)/$(DOCDIR)/dosfstools
+
+install-man:
+	install -d -m 0755 $(DESTDIR)/$(MANDIR)/man8
+	install -p -m 0644 man/*.8 $(DESTDIR)/$(MANDIR)/man8
+
+	ln -sf dosfsck.8 $(DESTDIR)/$(MANDIR)/man8/fsck.msdos.8
+	ln -sf dosfsck.8 $(DESTDIR)/$(MANDIR)/man8/fsck.vfat.8
+	ln -sf mkdosfs.8 $(DESTDIR)/$(MANDIR)/man8/mkfs.msdos.8
+	ln -sf mkdosfs.8 $(DESTDIR)/$(MANDIR)/man8/mkfs.vfat.8
+
+uninstall: uninstall-bin uninstall-doc uninstall-man
+
+uninstall-bin:
+	rm -f $(DESTDIR)/$(SBINDIR)/dosfsck
+	rm -f $(DESTDIR)/$(SBINDIR)/dosfslabel
+	rm -f $(DESTDIR)/$(SBINDIR)/mkdosfs
+
+	rm -f $(DESTDIR)/$(SBINDIR)/fsck.msdos
+	rm -f $(DESTDIR)/$(SBINDIR)/fsck.vfat
+	rm -f $(DESTDIR)/$(SBINDIR)/mkfs.msdos
+	rm -f $(DESTDIR)/$(SBINDIR)/mkfs.vfat
+
+	rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(SBINDIR)
+
+uninstall-doc:
+	rm -rf $(DESTDIR)/$(DOCDIR)/dosfstools
+
+	rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(DOCDIR)
+
+uninstall-man:
+	rm -f $(DESTDIR)/$(MANDIR)/man8/dosfsck.8
+	rm -f $(DESTDIR)/$(MANDIR)/man8/dosfslabel.8
+	rm -f $(DESTDIR)/$(MANDIR)/man8/mkdosfs.8
+
+	rm -f $(DESTDIR)/$(MANDIR)/man8/fsck.msdos.8
+	rm -f $(DESTDIR)/$(MANDIR)/man8/fsck.vfat.8
+	rm -f $(DESTDIR)/$(MANDIR)/man8/mkfs.msdos.8
+	rm -f $(DESTDIR)/$(MANDIR)/man8/mkfs.vfat.8
+
+	rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(MANDIR)/man8
+	rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(MANDIR)
+
+reinstall: distclean install
+
+clean:
+	rm -f *.o
+
+distclean: clean
+	rm -f dosfsck dosfslabel mkdosfs
+
+.PHONY: build rebuild install install-bin install-doc install-man uninstall uninstall-bin uninstall-doc uninstall-man reinstall clean distclean
diff --git a/dosfstools/bin/Nindent b/dosfstools/bin/Nindent
new file mode 100755
index 0000000..cf8ecfd
--- /dev/null
+++ b/dosfstools/bin/Nindent
@@ -0,0 +1,18 @@
+#!/bin/sh
+PARAM="-npro -kr -i4 -ts8 -sob -l80 -ss -ncs -cp1"
+RES=`indent --version`
+V1=`echo $RES | cut -d' ' -f3 | cut -d'.' -f1`
+V2=`echo $RES | cut -d' ' -f3 | cut -d'.' -f2`
+V3=`echo $RES | cut -d' ' -f3 | cut -d'.' -f3`
+if [ $V1 -gt 2 ]; then
+  PARAM="$PARAM -il0"
+elif [ $V1 -eq 2 ]; then
+  if [ $V2 -gt 2 ]; then
+    PARAM="$PARAM -il0";
+  elif [ $V2 -eq 2 ]; then
+    if [ $V3 -ge 10 ]; then
+      PARAM="$PARAM -il0"
+    fi
+  fi
+fi
+exec indent $PARAM "$@"
diff --git a/dosfstools/doc/ANNOUNCE.mkdosfs b/dosfstools/doc/ANNOUNCE.mkdosfs
new file mode 100644
index 0000000..1f02ea2
--- /dev/null
+++ b/dosfstools/doc/ANNOUNCE.mkdosfs
@@ -0,0 +1,41 @@
+Announcing the release of mkdosfs version 0.3b (Yggdrasil)
+
+It seems I didn't get the bug completely fixed in 0.3a.  Some
+borderline cases would still allocate too many sectors for the FAT.
+Again, nothing to worry about, just a nitpick -- this one would only
+in certain cases add one sector per FAT.
+
+Announcing the release of mkdosfs version 0.3a (Yggdrasil)
+
+Fixed a bug which would cause too many sectors to be reserved for the
+FAT (filesystem will still work fine, but have slightly less space
+available).
+
+Announcing the release of mkdosfs version 0.3 (Yggdrasil)
+
+This version correctly handles even very large filesystems, and
+properly supports the modern (3.3+) DOS bootsector format, including a
+message printed on boot attempts.
+
+Peter Anvin
+Yggdrasil Computing, Inc.
+hpa@yggdrasil.com
+
+			    --------------
+
+Announcing the release of mkdosfs version 0.2
+
+
+I've just uploaded mkdosfs to sunsite.unc.edu.  It works in a similar way
+to Remy Card's mke2fs, but creates an MS-DOS file system.
+
+The filename is mkdosfs-0.2.tar.gz.
+
+This second release should fix a small bug that could lead to FAT sizes that
+Linux's dosfs would accept but MS-DOS wouldn't.
+
+The archive contains a manual page, binary and source versions.
+
+
+Dave Hudson
+dave@humbug.demon.co.uk
diff --git a/dosfstools/doc/ChangeLog.dosfsck b/dosfstools/doc/ChangeLog.dosfsck
new file mode 100644
index 0000000..f628ac2
--- /dev/null
+++ b/dosfstools/doc/ChangeLog.dosfsck
@@ -0,0 +1,10 @@
+Changes from version 0 to 1
+===========================
+
+  - fixed an off-by-two error in check.c:check_file
+  - fixed marking clusters bad in fat.c:set_fat
+  - fat.c:reclaim_free was also reclaiming bad clusters.
+  - fixed many incorrect byte sex conversions in check.c and fat.c
+  - -t and -w now require -a or -r
+  - added option -d to drop files.
+  - added option -u to try to "undelete" non-directory files.
diff --git a/dosfstools/doc/ChangeLog.dosfstools-2.x b/dosfstools/doc/ChangeLog.dosfstools-2.x
new file mode 100644
index 0000000..7ed9efe
--- /dev/null
+++ b/dosfstools/doc/ChangeLog.dosfstools-2.x
@@ -0,0 +1,161 @@
+version 2.11
+============
+
+ - all: don't use own llseek() anymore, glibc lseek() does everything we need
+ - dosfsck: lfn.c: avoid segfault
+ - dosfsck: check.c, lfn.c: check for orphaned LFN slots
+ - dosfsck: check.c alloc_rootdir_entry(): set owner of newly alloced clusters
+ - dosfsck: dosfsck.h: better use <byteswap.h> for byte swapping
+ - dosfsck: io.c: added code for real DOS
+ - mkdosfs: raised FAT12_THRESHOLD from 4078 to 4085, introduced MIN_CLUST_32
+ - mkdosfs: fix loop device size
+ - mkdosfs: by default, use FAT32 on devices >= 512MB
+ - mkdosfs: fix a memory leak (blank_sector)
+ - mkdosfs: fix parsing of number of blocks on command line, so that numbers
+   >2G can be used
+ - mkdosfs: add 'b' to getopt() string so this option can be used :)
+ - mkdosfs: fix parsing of -i arg (should be unsigned)
+ - mkdosfs: change default permissions of created images (-C) to 0666 & ~umask
+ - mkdosfs: relax geometry check: if HDIO_GETGEO fails, print a warning and
+   default to H=255,S=63
+ - dosfsck: new option -n (no-op): just check non-interactively, but
+   don't write anything to filesystem
+ - A few #include changes to support compilation with linux 2.6
+   headers (thanks to Jim Gifford <jim@jg555.com>)
+ - dosfsck: remove directory entries pointing to start cluster 0, if they're
+   not "." or ".." entries that should actually point to the root dir
+   (pointed out by Thomas Winkler <twinkler@sysgo.de>)
+ - mkdosfs: new option -h to set number of hidden sectors
+   (thanks to Godwin Stewart <gstewart@spamcop.net>)
+ - all: updated my mail address everywhere...
+
+version 2.10
+============
+
+ - dosfsck: various 64-bit fixes and removed some warnings by Michal
+   Cihar <mcihar@suse.cz>
+ - mkdosfs: better error message if called without parameters (also
+   suggested by Michal)
+
+version 2.9
+===========
+
+ - dosfsck: if EOF from stdin, exit with error code
+ - dosfsck: Fix potential for "Internal error: next_cluster on bad cluster".
+ - dosfsck: When clearing long file names, don't overwrite the dir
+   entries with all zeros, but put 0xe5 into the first byte.
+   Otherwise, some OSes stop reading the directory at that point...
+ - dosfsck: in statistics printed by -v, fix 32bit overflow in number
+   of data bytes.
+ - dosfsck: fix an potential overflow in "too many clusters" check
+ - dosfsck: fix 64bit problem in fat.c (Debian bug #152769)
+ - dosfsck: allow FAT size > 32MB.
+ - dosfsck: allow for only one FAT
+ - dosfsck: with -v, also check that last sector of the filesystem can
+   be read (in case a partition is smaller than the fs thinks)
+ - mkdosfs: add note in manpage that creating bootable filesystems is
+   not supported.
+ - mkdosfs: better error message with pointer to -I if target is a
+   full-disk device.
+
+version 2.8
+===========
+
+ - dosfsck: Fixed endless loop whenever a volume label was present.
+
+version 2.7
+===========
+
+ - dosfsck: Don't check volume label for bad characters, everything
+   seems to be allowed there... Also ignore duplicate names where one of
+   them is a volume label.
+
+version 2.6
+===========
+
+ - mkdosfs: Added correct heads definition for 2.88M floppies if not
+   created via loopback.
+ - dosfsck: If boot sector and its backup are different (FAT32), offer
+   to write the backup to sector 0. (tnx to Pavel Roskin for this)
+ - For 64 bit alpha, struct bootsector in dosfsck.h must be defined
+   with __attribute__((packed)).
+ - mkdosfs now actually accepts -R option. (tnx to David Kerrawn)
+ - Fixed typo in dosfsck boot.c (recognition of boot signature in FSINFO)
+ - Various compilation fixes for 2.4 kernel headers and for ia64.
+
+version 2.5
+===========
+
+ - The llseek() implementation for alpha didn't really work; fixed it.
+
+version 2.4
+===========
+
+ - Fix compiling problem on alpha (made a silly typo...)
+
+version 2.3
+===========
+
+ - mkdosfs: Fixed usage message (printed only "bad address").
+ - both: made man pages and usage statements more consistent.
+ - both: fix llseek function for alpha.
+ - dosfsck: fix reading of unaligned fields in boot sector for alpha.
+ - dosfsck: fixed renaming of files (extension wasn't really written).
+
+version 2.2
+===========
+
+ - Added dosfsck/COPYING, putting dosfsck officially under GPL (Werner
+   and I agree that it should be GPL).
+ - mkdosfs: Allow creation of a 16 bit FAT on filesystems that are too
+   small for it if the user explicitly selected FAT16 (but a warning
+   is printed). Formerly, you got the misleading error message "make
+   the fs a bit smaller".
+ - dosfsck: new option -y as synonym for -y; for compability with
+   other fs checkers, which also accept this option.
+ - dosfsck: Now prints messages similar to e2fsck: at start version
+   and feature list; at end number of files (and directories) and
+   number of used/total clusters. This makes the printouts of *fsck at
+   boot time nicer.
+ - dosfsck: -a (auto repair) now turns on -f (salvage files), too. -a
+   should act as non-destructive as possible, so lost clusters should
+   be assigned to files. Otherwise the data in them might be
+   overwritten later.
+ - dosfsck: Don't drop a directory with lots of bad entries in
+   auto-repair mode for the same reason as above.
+ - dosfsck: avoid deleting the whole FAT32 root dir if something is
+   wrong with it (bad start cluster or the like).
+ - general: also create symlinks {mkfs,fsck}.vfat.8 to the respective
+   real man pages.
+
+version 2.1
+===========
+
+ - Fix some forgotten loff_t's for filesystems > 4GB. (Thanks to
+   <ki@kretz.co.at>).
+ - Fix typo in mkdosfs manpage.
+ - Removed inclusion of <linux/loop.h> from mkdosfs.c; it's unnecessary and
+   caused problems in some environments.
+ - Fix condition when to expect . and .. entries in a directory. (Was
+   wrong for non-FAT32 if first entry in root dir was a directory also.)
+ - Also create mkfs.vfat and fsck.vfat symlinks, so that also
+   filesystems listed with type "vfat" in /etc/fstab can be
+   automatically checked.
+
+version 2.0
+===========
+
+ - merge of mkdosfs and dosfstools in one package
+ - new maintainer: Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ - FAT32 support in both mkdosfs and dosfsck
+ - VFAT (long filename) support in dosfsck
+ - Support for Atari variant of MS-DOS filesystem in both tools
+ - Working support for big-endian systems in both tools
+ - Better support for loop devices in mkdosfs: usual floppy sizes are
+   detected and media byte etc. set accordingly; if loop fs has no
+   standard floppy size, use hd params
+   (mainly by Giuliano Procida <gpp10@cus.cam.ac.uk>)
+ - Removed lots of gcc warnings
+ - Fixed some minor calculation bugs in mkdosfs.
+
+For change logs previous to 2.0, see the CHANGES files in the subdirectories.
diff --git a/dosfstools/doc/ChangeLog.mkdosfs b/dosfstools/doc/ChangeLog.mkdosfs
new file mode 100644
index 0000000..e39d9d6
--- /dev/null
+++ b/dosfstools/doc/ChangeLog.mkdosfs
@@ -0,0 +1,18 @@
+28th January 1995		H. Peter Anvin (hpa@yggdrasil.com)
+
+	Better algorithm to select cluster sizes on large filesystems.
+	Added bogus boot sector code that on attempts to boot prints a
+	message (which can be chosen at mkdosfs time) and lets the user
+	press any key and try again.  Corrected support for 1.2 Mb
+	floppies.  mkdosfs now generates the extended bootsector
+	(superblock) format of DOS 3.3+, with support for volume ID's and
+	volume labels (volume labels are also written to the root
+	directory, as they should).
+
+18th February 1994		Dave Hudson (dave@humbug.demon.co.uk)
+
+	Released version 0.2 - clears a bug in the FAT sizing code.
+
+1st September 1993		Dave Hudson (dave@humbug.demon.co.uk)
+
+	Released version 0.1 - ALPHA release of mkdosfs
diff --git a/dosfstools/doc/README.dosfsck b/dosfstools/doc/README.dosfsck
new file mode 100644
index 0000000..7b351aa
--- /dev/null
+++ b/dosfstools/doc/README.dosfsck
@@ -0,0 +1,60 @@
+dosfsck, version 1
+==================
+
+WARNING: This is ALPHA test software. Use at your own risk.
+
+dosfsck is the Linux equivalent of PC/MS-DOS' CHKDSK. It checks the
+consistency of PC/MS-DOS file systems and optionally tries to repair
+them. The tests dosfsck performs are described in the man page.
+
+dosfsck needs header files from dosfs.9 (or later) to compile.
+
+Before using dosfsck to repair a file system that contains data of any
+value, you should verify that dosfsck is able to correct all reported
+errors. (Except fatal errors and those reported as unfixable, of
+course.) In order to do this, run it with the -V option, e.g.
+
+   dosfsck -V /dev/sda1		(automatic check)
+or dosfsck -V -r /dev/sda1	(interactive check and repair)
+
+dosfsck will perform two passes: in the first pass, inconsistencies are
+detected and a list of changes to correct the problems is generated. In
+the second pass, those changes are applied whenever dosfsck reads data
+from disk. Hence no fixable errors should be reported in the second
+pass if the first pass was successful.
+
+Please notify the author if fixable errors are reported in the second
+pass.
+
+After verifying that dosfsck appears to be able to perform the desired
+operations, either confirm that you want the changes to be performed
+(if dosfsck was started with -r) or re-run dosfsck with the -a option
+(if it was started without -r).
+
+Please send bug reports, comments, flames, etc. to
+almesber@nessie.cs.id.ethz.ch  or  almesber@bernina.ethz.ch
+
+- Werner
+
+FAT32 and LFN support
+=====================
+
+I've finally implemented some of the new features of MS-DOS
+filesystems: FAT32 and long filenames.
+
+FAT32 is automatically detected and of course the different FAT
+structure is handled. (Internally many changes were needed, so 32 bit
+variables for all cluster numbers and 64 bit vars for offsets inside
+the filesystem.) New checks for FAT32 are most notably on the backup
+boot sector and the new info sector. Also the possibility that the
+root directory resides in a cluster chain (instead of in a static
+area) on FAT32 is handled.
+
+dosfscheck also knows about VFAT long filenames now. It parses those
+names and uses them in listings etc. when available. There are also
+some checks on the (cruel) structure of how LFNs are stored and some
+attempts to fix problems.
+
+- Roman <roman@hodek.net>
+
+BTW, version 2 isn't ALPHA anymore :-)
diff --git a/dosfstools/doc/README.dosfstools-2.x b/dosfstools/doc/README.dosfstools-2.x
new file mode 100644
index 0000000..5fb00ed
--- /dev/null
+++ b/dosfstools/doc/README.dosfstools-2.x
@@ -0,0 +1,60 @@
+
+Atari format support
+====================
+
+Both mkdosfs and dosfsck now can also handle the Atari variation of
+the MS-DOS filesystem format. The Atari format has some minor
+differences, some caused by the different machine architecture (m68k),
+some being "historic" (Atari didn't change some things that M$
+changed).
+
+Both tools automatically select Atari format if they run on an Atari.
+Additionally the -A switch toggles between Atari and MS-DOS format.
+I.e., on an Atari it selects plain DOS format, on any other machine it
+switches to Atari format.
+
+The differences are in detail:
+
+ - Atari TOS doesn't like cluster sizes != 2, so the usual solution
+   for bigger partitions was to increase the logical sector size. So
+   mkdosfs can handle sector sizes != 512 now, you can also manually
+   select it with the -S option. On filesystems larger than approx. 32
+   MB, the sector size is automatically increased (stead of the
+   cluster size) to make the filesystem fit. mkdosfs will always use 2
+   sectors per cluster (also with the floppy standard configurations),
+   except when directed otherwise on the command line.
+
+ - From the docs, all values between 0xfff8 and 0xffff in the FAT mark
+   an end-of-file. However, DOS usually uses 0xfff8 and Atari 0xffff.
+   This seems to be only an consmetic difference. At least TOS doesn't
+   complain about 0xffff EOF marks. Don't know what DOS thinks of
+   0xfff8 :-) Anyway, both tools use the EOF mark common to the
+   system (DOS/Atari).
+
+ - Something similar of the bad cluster marks: On Atari the FAT values
+   0xfff0 to 0xfff7 are used for this, under DOS only 0xfff7 (the
+   others can be normal cluster numbers, allowing 7 more clusters :-)
+   However, both systems usually mark with 0xfff7. Just dosfsck has to
+   interpret 0xfff0...0xfff7 differently.
+
+ - Some fields in the boot sector are interpreted differently. For
+   example, Atari has a disk serial number (used to aid disk change
+   detection) where DOS stores the system name; the 'hidden' field is
+   32 bit for DOS, but 16 bit for Atari, and there's no 'total_sect'
+   field; the 12/16 bit FAT decision is different: it's not based on
+   the number of clusters, but always FAT12 on floppies and FAT16 on
+   hard disks. mkdosfs nows about these differences and constructs the
+   boot sector accordingly.
+
+ - In dosfsck, the boot sector differences also have to known, to not
+   warn about things that are no error on Atari. In addition, most
+   Atari formatting tools fill the 'tracks' and 'heads' fields with 0
+   for hard disks, because they're meaningless on SCSI disks (Atari
+   has/had no IDE). Due to this, the check that they should be
+   non-zero is switched off.
+
+ - Under Atari TOS, some other characters are illegal in filenames:
+   '<', '>', '|', '"', and ':' are allowed, but all non-ASCII chars
+   (codes >= 128) are forbidden.
+
+- Roman <Roman.Hodek@informatik.uni-erlangen.de>
diff --git a/dosfstools/doc/README.mkdosfs b/dosfstools/doc/README.mkdosfs
new file mode 100644
index 0000000..ae93ada
--- /dev/null
+++ b/dosfstools/doc/README.mkdosfs
@@ -0,0 +1,50 @@
+mkdosfs - Make DOS file system utilty.
+
+
+I wrote this, partially to complement the dosfsck utility written by Werner
+Almesberger (who graciously gave me some pointers when I asked for some
+advice about writing this code), and also to avoid me having to boot DOS
+just to create data partitions (I use Linux to back up DOS :-) ).
+
+The code is really derived from Remy Card's mke2fs utility - I used this as a
+framework, although all of the file system specific stuff was removed and the
+DOS stuff inserted.  I believe originally mke2fs was based on Linus' mkfs
+code, hence the acknowledgements in the source code.
+
+Neither Remy nor Linus have had any involvement with mkdosfs, so if there are
+any bugs they're almost certainly "all my own work".
+
+The code has been available for ftp since 1st September 1993, and I have yet
+to receive any bug reports from users.  I don't know of any bugs, but if you
+do find a bug or have any constructive comments, please mail me!
+
+The only bug I found with version 0.1 was an obscure fault that could lead
+to an invalid (for MS-DOS, not Linux's dos fs) number of sectors used in the
+file allocation table(s).
+
+
+Dave Hudson
+dave@humbug.demon.co.uk
+
+
+FAT32 support
+=============
+
+mkdosfs now can also create filesystems in the new FAT32 format. To do
+this, give mkdosfs a "-F 32" option. FAT32 isn't selected
+automatically (yet), even if very large clusters are needed with
+FAT16. With FAT32 you have two additional options, -R to select the
+number of reserved sectors (usually 32), and -b to select the location
+of the backup boot sector (default 6). Of course such a backup is
+created, as well as the new info sector. On FAT32, the root directory
+is always created as a cluster chain. Sorry, there's no switch to
+generate an old static root dir.
+
+One bigger bug fix besides FAT32 was to reject filesystems that need a
+16 bit FAT to fit all possible clusters, but the bigger FAT needs some
+more sectors, so the total number of clusters drop below the border
+where MS-DOS expects a 12 bit FAT. So such filesystems would be FAT16,
+but interpreted as FAT32 by DOS. The fix is to reduce filesystem size
+a bit.
+
+- Roman <roman@hodek.net>
diff --git a/dosfstools/doc/TODO.dosfstools-2.x b/dosfstools/doc/TODO.dosfstools-2.x
new file mode 100644
index 0000000..dbc2de0
--- /dev/null
+++ b/dosfstools/doc/TODO.dosfstools-2.x
@@ -0,0 +1,14 @@
+                                                    -*- mode: indented-text -*-
+
+ - dosfsck: Better checking of file times: ctime <= mtime <= atime
+
+ - mkdosfs: If /etc/bootsect.dos (or similar) exists, use it as a
+   template for generating boot sectors. This way, you can, e.g., make
+   bootable DOS disks.
+
+   Addendum: Don't know if that's so wise... There are really many
+   variants of DOS/Windows bootcode out in the wild, and the code is
+   proprietary, too.
+
+ - dosfsck: read-only sector test (-t without -a or -r); just print
+   out errors.
diff --git a/dosfstools/man/dosfsck.8 b/dosfstools/man/dosfsck.8
new file mode 100644
index 0000000..d363c68
--- /dev/null
+++ b/dosfstools/man/dosfsck.8
@@ -0,0 +1,112 @@
+.TH DOSFSCK 8 "2010\-01\-31" "3.0.9" "check and repair MS\-DOS filesystems"
+
+.SH NAME
+\fBdosfsck\fR \- check and repair MS\-DOS filesystems
+
+.SH SYNOPSIS
+\fBdosfsck\fR|\fBfsck.msdos\fR|\fBfsck.vfat\fR [\-aAflnrtvVwy] [\-d \fIPATH\fR \-d\ \fI...\fR] [\-u\ \fIPATH\fR \-u \fI...\fR] \fIDEVICE\fR
+
+.SH DESCRIPTION
+\fBdosfsck\fR verifies the consistency of MS\-DOS filesystems and optionally tries to repair them.
+.PP
+The following filesystem problems can be corrected (in this order):
+.IP "*" 4
+FAT contains invalid cluster numbers. Cluster is changed to EOF.
+.IP "*" 4
+File's cluster chain contains a loop. The loop is broken.
+.IP "*" 4
+Bad clusters (read errors). The clusters are marked bad and they are removed from files owning them. This check is optional.
+.IP "*" 4
+Directories with a large number of bad entries (probably corrupt). The directory can be deleted.
+.IP "*" 4
+Files . and .. are non\-directories. They can be deleted or renamed.
+.IP "*" 4
+Directories . and .. in root directory. They are deleted.
+.IP "*" 4
+Bad filenames. They can be renamed.
+.IP "*" 4
+Duplicate directory entries. They can be deleted or renamed.
+.IP "*" 4
+Directories with non\-zero size field. Size is set to zero.
+.IP "*" 4
+Directory . does not point to parent directory. The start pointer is adjusted.
+.IP "*" 4
+Directory .. does not point to parent of parent directory. The start pointer is adjusted.
+.IP "*" 4
+Start cluster number of a file is invalid. The file is truncated.
+.IP "*" 4
+File contains bad or free clusters. The file is truncated.
+.IP "*" 4
+File's cluster chain is longer than indicated by the size fields. The file is truncated.
+.IP "*" 4
+Two or more files share the same cluster(s). All but one of the files are truncated. If the file being truncated is a directory file that has already been read, the filesystem check is restarted after truncation.
+.IP "*" 4
+File's cluster chain is shorter than indicated by the size fields. The file is truncated.
+.IP "*" 4
+Clusters are marked as used but are not owned by a file. They are marked as free.
+.PP
+Additionally, the following problems are detected, but not repaired:
+.IP "*" 4
+Invalid parameters in boot sector.
+.IP "*" 4
+Absence of . and .. entries in non\-root directories
+.PP
+When \fBdosfsck\fR checks a filesystem, it accumulates all changes in memory and performs them only after all checks are complete. This can be disabled with the \fB\-w\fR option.
+
+.SH OPTIONS
+.IP "\fB\-a\fR" 4
+Automatically repair the filesystem. No user intervention is necessary. Whenever there is more than one method to solve a problem, the least destructive approach is used.
+.IP "\fB\-A\fR" 4
+Use Atari variation of the MS\-DOS filesystem. This is default if \fBdosfsck\fR is run on an Atari, then this option turns off Atari format. There are some minor differences in Atari format: Some boot sector fields are interpreted slightly different, and the special FAT entries for end\-of\-file and bad cluster can be different. Under MS\-DOS 0xfff8 is used for EOF and Atari employs 0xffff by default, but both systems recognize all values from 0xfff8...0xffff as end\-of\-file. MS\-DOS uses only 0xfff7 for bad clusters, where on Atari values 0xfff0...0xfff7 are for this purpose (but the standard value is still 0xfff7).
+.IP "\fB\-d\fR" 4
+Delete the specified file. If more that one file with that name exists, the first one is deleted.
+.IP "\fB\-f\fR" 4
+Salvage unused cluster chains to files. By default, unused clusters are added to the free disk space except in auto mode (\fB\-a\fR).
+.IP "\fB\-l\fR" 4
+List path names of files being processed.
+.IP "\fB\-n\fR" 4
+No\-operation mode: non\-interactively check for errors, but don't write
+anything to the filesystem.
+.IP "\fB\-r\fR" 4
+Interactively repair the filesystem. The user is asked for advice whenever
+there is more than one approach to fix an inconsistency.
+.IP "\fB\-t\fR" 4
+Mark unreadable clusters as bad.
+.IP "\fB\-u\fR" 4
+Try to undelete the specified file. \fBdosfsck\fR tries to allocate a chain of contiguous unallocated clusters beginning with the start cluster of the undeleted file.
+.IP "\fB\-v\fR" 4
+Verbose mode. Generates slightly more output.
+.IP "\fB\-V\fR" 4
+Perform a verification pass. The filesystem check is repeated after the first run. The second pass should never report any fixable errors. It may take considerably longer than the first pass, because the first pass may have generated long list of modifications that have to be scanned for each disk read.
+.IP "\fB\-w\fR" 4
+Write changes to disk immediately.
+.IP "\fB\-y\fR" 4
+Same as \fB\-a\fR (automatically repair filesystem) for compatibility with other fsck tools.
+.PP
+\fBNote:\fR If \fB\-a\fR and \fB\-r\fR are absent, the filesystem is only checked, but not repaired.
+
+.SH "EXIT STATUS"
+.IP "0" 4
+No recoverable errors have been detected.
+.IP "1" 4
+Recoverable errors have been detected or \fBdosfsck\fR has discovered an internal inconsistency.
+.IP "2" 4
+Usage error. \fBdosfsck\fR did not access the filesystem.
+
+.SH FILES
+.IP "fsck0000.rec, fsck0001.rec, ..." 4
+When recovering from a corrupted filesystem, \fBdosfsck\fR dumps recovered data into files named 'fsckNNNN.rec' in the top level directory of the filesystem.
+
+.SH BUGS
+Does not create . and .. files where necessary. Does not remove entirely empty directories. Should give more diagnostic messages. Undeleting files should use a more sophisticated algorithm.
+
+.SH SEE ALSO
+\fBdosfslabel\fR(8)
+.br
+\fBmkdosfs\fR(8)
+
+.SH HOMEPAGE
+More information about \fBdosfsck\fR and \fBdosfstools\fR can be found at <\fIhttp://www.daniel\-baumann.ch/software/dosfstools/\fR>.
+
+.SH AUTHORS
+\fBdosfstools\fR were written by Werner Almesberger <\fIwerner.almesberger@lrc.di.epfl.ch\fR>, Roman Hodek <\fIRoman.Hodek@informatik.uni-erlangen.de\fR>, and others. The current maintainer is Daniel Baumann <\fIdaniel@debian.org\fR>.
diff --git a/dosfstools/man/dosfslabel.8 b/dosfstools/man/dosfslabel.8
new file mode 100644
index 0000000..293ff5d
--- /dev/null
+++ b/dosfstools/man/dosfslabel.8
@@ -0,0 +1,29 @@
+.TH DOSFSLABEL 8 "2010\-01\-31" "3.0.9" "set or get MS\-DOS filesystem label"
+
+.SH NAME
+\fBdosfslabel\fR \- set or get MS\-DOS filesystem label
+
+.SH SYNOPSIS
+\fBdosfslabel\fR \fIDEVICE\fR [\fILABEL\fR]
+
+.SH DESCRIPTION
+\fBdosfslabel\fR set or gets a MS\-DOS filesystem label from a given device.
+.PP
+If the label is omitted, then the label name of the specified device is written on the standard output. A label can't be longer than 11 characters.
+
+.SH OPTIONS
+.IP "\fB\-h\fR, \fB\-\-help\fR" 4
+Displays a help message.
+.IP "\fB\-V\fR, \fB\-\-version\fR" 4
+Shows version.
+
+.SH SEE ALSO
+\fBdosfsck\fR(8)
+.br
+\fBmkdosfs\fR(8)
+
+.SH HOMEPAGE
+More information about \fBdosfsck\fR and \fBdosfstools\fR can be found at <\fIhttp://www.daniel\-baumann.ch/software/dosfstools/\fR>.
+
+.SH AUTHORS
+\fBdosfstools\fR were written by Werner Almesberger <\fIwerner.almesberger@lrc.di.epfl.ch\fR>, Roman Hodek <\fIRoman.Hodek@informatik.uni-erlangen.de\fR>, and others. The current maintainer is Daniel Baumann <\fIdaniel@debian.org\fR>.
diff --git a/dosfstools/man/mkdosfs.8 b/dosfstools/man/mkdosfs.8
new file mode 100644
index 0000000..9100c39
--- /dev/null
+++ b/dosfstools/man/mkdosfs.8
@@ -0,0 +1,224 @@
+.\" -*- nroff -*-
+.TH MKDOSFS 8 "5 May 1995" "Version 2.x"
+.SH NAME
+.B mkdosfs
+\- create an MS-DOS file system under Linux
+.SH SYNOPSIS
+.B mkdosfs|mkfs.msdos|mkfs.vfat
+[
+.B \-a
+]
+[
+.B \-A
+]
+[
+.B \-b
+.I sector-of-backup
+]
+[
+.B \-c
+]
+[
+.B \-l
+.I filename
+]
+[
+.B \-C
+]
+[
+.B \-f
+.I number-of-FATs
+]
+[
+.B \-F
+.I FAT-size
+]
+[
+.B \-h
+.I number-of-hidden-sectors
+]
+[
+.B \-i
+.I volume-id
+]
+.RB [ " \-I " ]
+[
+.B \-m
+.I message-file
+]
+[
+.B \-n
+.I volume-name
+]
+[
+.B \-r
+.I root-dir-entries
+]
+[
+.B \-R
+.I number-of-reserved-sectors
+]
+[
+.B \-s
+.I sectors-per-cluster
+]
+[
+.B \-S
+.I logical-sector-size
+]
+[
+.B \-v
+]
+.I device
+[
+.I block-count
+]
+.SH DESCRIPTION
+.B mkdosfs
+is used to create an MS-DOS file system under Linux on a device (usually
+a disk partition).
+.I device
+is the special file corresponding to the device (e.g /dev/hdXX).
+.I block-count
+is the number of blocks on the device.  If omitted,
+.B mkdosfs
+automatically determines the file system size.
+.SH OPTIONS
+.TP
+.B \-a
+Normally, for any filesystem except very small ones, \fBmkdosfs\fP
+will align all the data structures to cluster size, to make sure that
+as long as the partition is properly aligned, so will all the data
+structures in the filesystem.  This option disables alignment; this
+may provide a handful of additional clusters of storage at the expense
+of a significant performance degradation on RAIDs, flash media or
+large-sector hard disks.
+.TP
+.B \-A
+Use Atari variation of the MS-DOS file system. This is default if
+\fBmkdosfs\fP is run on an Atari, then this option turns off Atari
+format. There are some differences when using Atari format: If not
+directed otherwise by the user, \fBmkdosfs\fP will always use 2
+sectors per cluster, since GEMDOS doesn't like other values very much.
+It will also obey the maximum number of sectors GEMDOS can handle.
+Larger file systems are managed by raising the logical sector size.
+Under Atari format, an Atari-compatible serial number for the
+file system is generated, and a 12 bit FAT is used only for file systems
+that have one of the usual floppy sizes (720k, 1.2M, 1.44M, 2.88M), a
+16 bit FAT otherwise. This can be overridden with the \fB\-F\fP
+option. Some PC-specific boot sector fields aren't written, and a boot
+message (option \fB\-m\fP) is ignored.
+.TP
+.BI \-b " sector-of-backup "
+Selects the location of the backup boot sector for FAT32. Default
+depends on number of reserved sectors, but usually is sector 6. The
+backup must be within the range of reserved sectors.
+.TP
+.B \-c
+Check the device for bad blocks before creating the file system.
+.TP
+.B \-C
+Create the file given as \fIdevice\fP on the command line, and write
+the to-be-created file system to it. This can be used to create the
+new file system in a file instead of on a real device, and to avoid
+using \fBdd\fP in advance to create a file of appropriate size. With
+this option, the \fIblock-count\fP must be given, because otherwise
+the intended size of the file system wouldn't be known. The file
+created is a sparse file, which actually only contains the meta-data
+areas (boot sector, FATs, and root directory). The data portions won't
+be stored on the disk, but the file nevertheless will have the
+correct size. The resulting file can be copied later to a floppy disk
+or other device, or mounted through a loop device.
+.TP
+.BI \-f " number-of-FATs"
+Specify the number of file allocation tables in the file system.  The
+default is 2.  Currently the Linux MS-DOS file system does not support
+more than 2 FATs.
+.TP
+.BI \-F " FAT-size"
+Specifies the type of file allocation tables used (12, 16 or 32 bit).
+If nothing is specified, \fBmkdosfs\fR will automatically select
+between 12, 16 and 32 bit, whatever fits better for the file system size.
+.TP
+.BI \-h " number-of-hidden-sectors "
+Select the number of hidden sectors in the volume. Apparently some
+digital cameras get indigestion if you feed them a CF card without
+such hidden sectors, this option allows you to satisfy them. Assumes
+\'0\' if no value is given on the command line.
+.TP
+.I \-i " volume-id"
+Sets the volume ID of the newly created file system;
+.I volume-id
+is a 32-bit hexadecimal number (for example, 2e24ec82).  The default
+is a number which depends on the file system creation time.
+.TP
+.B \-I
+It is typical for fixed disk devices to be partitioned so, by default, you are
+not permitted to create a filesystem across the entire device.
+.B mkdosfs
+will complain and tell you that it refuses to work.  This is different
+when using MO disks.  One doesn't always need partitions on MO disks.
+The file system can go directly to the whole disk.  Under other OSes
+this is known as the 'superfloppy' format.
+
+This switch will force
+.B mkdosfs
+to work properly.
+.TP
+.BI \-l " filename"
+Read the bad blocks list from
+.IR filename .
+.TP
+.BI \-m " message-file"
+Sets the message the user receives on attempts to boot this file system
+without having properly installed an operating system.  The message
+file must not exceed 418 bytes once line feeds have been converted to
+carriage return-line feed combinations, and tabs have been expanded.
+If the filename is a hyphen (-), the text is taken from standard input.
+.TP
+.BI \-n " volume-name"
+Sets the volume name (label) of the file system.  The volume name can
+be up to 11 characters long.  The default is no label.
+.TP
+.BI \-r " root-dir-entries"
+Select the number of entries available in the root directory.  The
+default is 112 or 224 for floppies and 512 for hard disks.
+.TP
+.BI \-R " number-of-reserved-sectors "
+Select the number of reserved sectors. With FAT32 format at least 2
+reserved sectors are needed, the default is 32. Otherwise the default
+is 1 (only the boot sector).
+.TP
+.BI \-s " sectors-per-cluster"
+Specify the number of disk sectors per cluster.  Must be a power of 2,
+i.e. 1, 2, 4, 8, ... 128.
+.TP
+.BI \-S " logical-sector-size"
+Specify the number of bytes per logical sector.  Must be a power of 2
+and greater than or equal to 512, i.e. 512, 1024, 2048, 4096, 8192,
+16384, or 32768.
+.TP
+.B \-v
+Verbose execution.
+.SH BUGS
+.B mkdosfs
+can not create boot-able file systems. This isn't as easy as you might
+think at first glance for various reasons and has been discussed a lot
+already.
+.B mkdosfs
+simply will not support it ;)
+.SH AUTHOR
+Dave Hudson - <dave@humbug.demon.co.uk>; modified by Peter Anvin
+<hpa@yggdrasil.com>. Fixes and additions by Roman Hodek
+<roman@hodek.net> for Debian/GNU Linux.
+.SH ACKNOWLEDGMENTS
+.B mkdosfs
+is based on code from
+.BR mke2fs
+(written by Remy Card - <card@masi.ibp.fr>) which is itself based on
+.BR mkfs
+(written by Linus Torvalds - <torvalds@cs.helsinki.fi>).
+.SH SEE ALSO
+.BR dosfsck (8),
+.BR dosfslabel (8),
+.BR mkfs (8)
diff --git a/dosfstools/src/boot.c b/dosfstools/src/boot.c
new file mode 100644
index 0000000..bbaee04
--- /dev/null
+++ b/dosfstools/src/boot.c
@@ -0,0 +1,518 @@
+/* boot.c - Read and analyze ia PC/MS-DOS boot sector
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
+ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "common.h"
+#include "dosfsck.h"
+#include "fat.h"
+#include "io.h"
+#include "boot.h"
+
+#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
+    /* don't divide by zero */
+
+/* cut-over cluster counts for FAT12 and FAT16 */
+#define FAT12_THRESHOLD  4085
+#define FAT16_THRESHOLD 65525
+
+static struct {
+    __u8 media;
+    char *descr;
+} mediabytes[] = {
+    {
+    0xf0, "5.25\" or 3.5\" HD floppy"}, {
+    0xf8, "hard disk"}, {
+    0xf9, "3,5\" 720k floppy 2s/80tr/9sec or "
+	    "5.25\" 1.2M floppy 2s/80tr/15sec"}, {
+    0xfa, "5.25\" 320k floppy 1s/80tr/8sec"}, {
+    0xfb, "3.5\" 640k floppy 2s/80tr/8sec"}, {
+    0xfc, "5.25\" 180k floppy 1s/40tr/9sec"}, {
+    0xfd, "5.25\" 360k floppy 2s/40tr/9sec"}, {
+    0xfe, "5.25\" 160k floppy 1s/40tr/8sec"}, {
+0xff, "5.25\" 320k floppy 2s/40tr/8sec"},};
+
+#if defined __alpha || defined __arm || defined __arm__ || defined __ia64__ || defined __x86_64__ \
+ || defined __ppc64__ || defined __bfin__ || defined __MICROBLAZE__
+/* Unaligned fields must first be copied byte-wise */
+#define GET_UNALIGNED_W(f)			\
+    ({						\
+	unsigned short __v;			\
+	memcpy( &__v, &f, sizeof(__v) );	\
+	CF_LE_W( *(unsigned short *)&__v );	\
+    })
+#else
+#define GET_UNALIGNED_W(f) CF_LE_W( *(unsigned short *)&f )
+#endif
+
+static char *get_media_descr(unsigned char media)
+{
+    int i;
+
+    for (i = 0; i < sizeof(mediabytes) / sizeof(*mediabytes); ++i) {
+	if (mediabytes[i].media == media)
+	    return (mediabytes[i].descr);
+    }
+    return ("undefined");
+}
+
+static void dump_boot(DOS_FS * fs, struct boot_sector *b, unsigned lss)
+{
+    unsigned short sectors;
+
+    printf("Boot sector contents:\n");
+    if (!atari_format) {
+	char id[9];
+	strncpy(id, (const char *)b->system_id, 8);
+	id[8] = 0;
+	printf("System ID \"%s\"\n", id);
+    } else {
+	/* On Atari, a 24 bit serial number is stored at offset 8 of the boot
+	 * sector */
+	printf("Serial number 0x%x\n",
+	       b->system_id[5] | (b->
+				  system_id[6] << 8) | (b->system_id[7] << 16));
+    }
+    printf("Media byte 0x%02x (%s)\n", b->media, get_media_descr(b->media));
+    printf("%10d bytes per logical sector\n", GET_UNALIGNED_W(b->sector_size));
+    printf("%10d bytes per cluster\n", fs->cluster_size);
+    printf("%10d reserved sector%s\n", CF_LE_W(b->reserved),
+	   CF_LE_W(b->reserved) == 1 ? "" : "s");
+    printf("First FAT starts at byte %llu (sector %llu)\n",
+	   (unsigned long long)fs->fat_start,
+	   (unsigned long long)fs->fat_start / lss);
+    printf("%10d FATs, %d bit entries\n", b->fats, fs->fat_bits);
+    printf("%10d bytes per FAT (= %u sectors)\n", fs->fat_size,
+	   fs->fat_size / lss);
+    if (!fs->root_cluster) {
+	printf("Root directory starts at byte %llu (sector %llu)\n",
+	       (unsigned long long)fs->root_start,
+	       (unsigned long long)fs->root_start / lss);
+	printf("%10d root directory entries\n", fs->root_entries);
+    } else {
+	printf("Root directory start at cluster %lu (arbitrary size)\n",
+	       fs->root_cluster);
+    }
+    printf("Data area starts at byte %llu (sector %llu)\n",
+	   (unsigned long long)fs->data_start,
+	   (unsigned long long)fs->data_start / lss);
+    printf("%10lu data clusters (%llu bytes)\n", fs->clusters,
+	   (unsigned long long)fs->clusters * fs->cluster_size);
+    printf("%u sectors/track, %u heads\n", CF_LE_W(b->secs_track),
+	   CF_LE_W(b->heads));
+    printf("%10u hidden sectors\n", atari_format ?
+	   /* On Atari, the hidden field is only 16 bit wide and unused */
+	   (((unsigned char *)&b->hidden)[0] |
+	    ((unsigned char *)&b->hidden)[1] << 8) : CF_LE_L(b->hidden));
+    sectors = GET_UNALIGNED_W(b->sectors);
+    printf("%10u sectors total\n", sectors ? sectors : CF_LE_L(b->total_sect));
+}
+
+static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, int lss)
+{
+    struct boot_sector b2;
+
+    if (!fs->backupboot_start) {
+	printf("There is no backup boot sector.\n");
+	if (CF_LE_W(b->reserved) < 3) {
+	    printf("And there is no space for creating one!\n");
+	    return;
+	}
+	if (interactive)
+	    printf("1) Create one\n2) Do without a backup\n");
+	else
+	    printf("  Auto-creating backup boot block.\n");
+	if (!interactive || get_key("12", "?") == '1') {
+	    int bbs;
+	    /* The usual place for the backup boot sector is sector 6. Choose
+	     * that or the last reserved sector. */
+	    if (CF_LE_W(b->reserved) >= 7 && CF_LE_W(b->info_sector) != 6)
+		bbs = 6;
+	    else {
+		bbs = CF_LE_W(b->reserved) - 1;
+		if (bbs == CF_LE_W(b->info_sector))
+		    --bbs;	/* this is never 0, as we checked reserved >= 3! */
+	    }
+	    fs->backupboot_start = bbs * lss;
+	    b->backup_boot = CT_LE_W(bbs);
+	    fs_write(fs->backupboot_start, sizeof(*b), b);
+	    fs_write((loff_t) offsetof(struct boot_sector, backup_boot),
+		     sizeof(b->backup_boot), &b->backup_boot);
+	    printf("Created backup of boot sector in sector %d\n", bbs);
+	    return;
+	} else
+	    return;
+    }
+
+    fs_read(fs->backupboot_start, sizeof(b2), &b2);
+    if (memcmp(b, &b2, sizeof(b2)) != 0) {
+	/* there are any differences */
+	__u8 *p, *q;
+	int i, pos, first = 1;
+	char buf[20];
+
+	printf("There are differences between boot sector and its backup.\n");
+	printf("Differences: (offset:original/backup)\n  ");
+	pos = 2;
+	for (p = (__u8 *) b, q = (__u8 *) & b2, i = 0; i < sizeof(b2);
+	     ++p, ++q, ++i) {
+	    if (*p != *q) {
+		sprintf(buf, "%s%u:%02x/%02x", first ? "" : ", ",
+			(unsigned)(p - (__u8 *) b), *p, *q);
+		if (pos + strlen(buf) > 78)
+		    printf("\n  "), pos = 2;
+		printf("%s", buf);
+		pos += strlen(buf);
+		first = 0;
+	    }
+	}
+	printf("\n");
+
+	if (interactive)
+	    printf("1) Copy original to backup\n"
+		   "2) Copy backup to original\n" "3) No action\n");
+	else
+	    printf("  Not automatically fixing this.\n");
+	switch (interactive ? get_key("123", "?") : '3') {
+	case '1':
+	    fs_write(fs->backupboot_start, sizeof(*b), b);
+	    break;
+	case '2':
+	    fs_write(0, sizeof(b2), &b2);
+	    break;
+	default:
+	    break;
+	}
+    }
+}
+
+static void init_fsinfo(struct info_sector *i)
+{
+    i->magic = CT_LE_L(0x41615252);
+    i->signature = CT_LE_L(0x61417272);
+    i->free_clusters = CT_LE_L(-1);
+    i->next_cluster = CT_LE_L(2);
+    i->boot_sign = CT_LE_W(0xaa55);
+}
+
+static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, int lss)
+{
+    struct info_sector i;
+
+    if (!b->info_sector) {
+	printf("No FSINFO sector\n");
+	if (interactive)
+	    printf("1) Create one\n2) Do without FSINFO\n");
+	else
+	    printf("  Not automatically creating it.\n");
+	if (interactive && get_key("12", "?") == '1') {
+	    /* search for a free reserved sector (not boot sector and not
+	     * backup boot sector) */
+	    __u32 s;
+	    for (s = 1; s < CF_LE_W(b->reserved); ++s)
+		if (s != CF_LE_W(b->backup_boot))
+		    break;
+	    if (s > 0 && s < CF_LE_W(b->reserved)) {
+		init_fsinfo(&i);
+		fs_write((loff_t) s * lss, sizeof(i), &i);
+		b->info_sector = CT_LE_W(s);
+		fs_write((loff_t) offsetof(struct boot_sector, info_sector),
+			 sizeof(b->info_sector), &b->info_sector);
+		if (fs->backupboot_start)
+		    fs_write(fs->backupboot_start +
+			     offsetof(struct boot_sector, info_sector),
+			     sizeof(b->info_sector), &b->info_sector);
+	    } else {
+		printf("No free reserved sector found -- "
+		       "no space for FSINFO sector!\n");
+		return;
+	    }
+	} else
+	    return;
+    }
+
+    fs->fsinfo_start = CF_LE_W(b->info_sector) * lss;
+    fs_read(fs->fsinfo_start, sizeof(i), &i);
+
+    if (i.magic != CT_LE_L(0x41615252) ||
+	i.signature != CT_LE_L(0x61417272) || i.boot_sign != CT_LE_W(0xaa55)) {
+	printf("FSINFO sector has bad magic number(s):\n");
+	if (i.magic != CT_LE_L(0x41615252))
+	    printf("  Offset %llu: 0x%08x != expected 0x%08x\n",
+		   (unsigned long long)offsetof(struct info_sector, magic),
+		   CF_LE_L(i.magic), 0x41615252);
+	if (i.signature != CT_LE_L(0x61417272))
+	    printf("  Offset %llu: 0x%08x != expected 0x%08x\n",
+		   (unsigned long long)offsetof(struct info_sector, signature),
+		   CF_LE_L(i.signature), 0x61417272);
+	if (i.boot_sign != CT_LE_W(0xaa55))
+	    printf("  Offset %llu: 0x%04x != expected 0x%04x\n",
+		   (unsigned long long)offsetof(struct info_sector, boot_sign),
+		   CF_LE_W(i.boot_sign), 0xaa55);
+	if (interactive)
+	    printf("1) Correct\n2) Don't correct (FSINFO invalid then)\n");
+	else
+	    printf("  Auto-correcting it.\n");
+	if (!interactive || get_key("12", "?") == '1') {
+	    init_fsinfo(&i);
+	    fs_write(fs->fsinfo_start, sizeof(i), &i);
+	} else
+	    fs->fsinfo_start = 0;
+    }
+
+    if (fs->fsinfo_start)
+	fs->free_clusters = CF_LE_L(i.free_clusters);
+}
+
+void read_boot(DOS_FS * fs)
+{
+    struct boot_sector b;
+    unsigned total_sectors;
+    unsigned short logical_sector_size, sectors;
+    unsigned fat_length;
+    loff_t data_size;
+
+    fs_read(0, sizeof(b), &b);
+    logical_sector_size = GET_UNALIGNED_W(b.sector_size);
+    if (!logical_sector_size)
+	die("Logical sector size is zero.");
+
+    /* This was moved up because it's the first thing that will fail */
+    /* if the platform needs special handling of unaligned multibyte accesses */
+    /* but such handling isn't being provided. See GET_UNALIGNED_W() above. */
+    if (logical_sector_size & (SECTOR_SIZE - 1))
+	die("Logical sector size (%d bytes) is not a multiple of the physical "
+	    "sector size.", logical_sector_size);
+
+    fs->cluster_size = b.cluster_size * logical_sector_size;
+    if (!fs->cluster_size)
+	die("Cluster size is zero.");
+    if (b.fats != 2 && b.fats != 1)
+	die("Currently, only 1 or 2 FATs are supported, not %d.\n", b.fats);
+    fs->nfats = b.fats;
+    sectors = GET_UNALIGNED_W(b.sectors);
+    total_sectors = sectors ? sectors : CF_LE_L(b.total_sect);
+    if (verbose)
+	printf("Checking we can access the last sector of the filesystem\n");
+    /* Can't access last odd sector anyway, so round down */
+    fs_test((loff_t) ((total_sectors & ~1) - 1) * (loff_t) logical_sector_size,
+	    logical_sector_size);
+    fat_length = CF_LE_W(b.fat_length) ?
+	CF_LE_W(b.fat_length) : CF_LE_L(b.fat32_length);
+    fs->fat_start = (loff_t) CF_LE_W(b.reserved) * logical_sector_size;
+    fs->root_start = ((loff_t) CF_LE_W(b.reserved) + b.fats * fat_length) *
+	logical_sector_size;
+    fs->root_entries = GET_UNALIGNED_W(b.dir_entries);
+    fs->data_start = fs->root_start + ROUND_TO_MULTIPLE(fs->root_entries <<
+							MSDOS_DIR_BITS,
+							logical_sector_size);
+    data_size = (loff_t) total_sectors *logical_sector_size - fs->data_start;
+    fs->clusters = data_size / fs->cluster_size;
+    fs->root_cluster = 0;	/* indicates standard, pre-FAT32 root dir */
+    fs->fsinfo_start = 0;	/* no FSINFO structure */
+    fs->free_clusters = -1;	/* unknown */
+    if (!b.fat_length && b.fat32_length) {
+	fs->fat_bits = 32;
+	fs->root_cluster = CF_LE_L(b.root_cluster);
+	if (!fs->root_cluster && fs->root_entries)
+	    /* M$ hasn't specified this, but it looks reasonable: If
+	     * root_cluster is 0 but there is a separate root dir
+	     * (root_entries != 0), we handle the root dir the old way. Give a
+	     * warning, but convertig to a root dir in a cluster chain seems
+	     * to complex for now... */
+	    printf("Warning: FAT32 root dir not in cluster chain! "
+		   "Compatibility mode...\n");
+	else if (!fs->root_cluster && !fs->root_entries)
+	    die("No root directory!");
+	else if (fs->root_cluster && fs->root_entries)
+	    printf("Warning: FAT32 root dir is in a cluster chain, but "
+		   "a separate root dir\n"
+		   "  area is defined. Cannot fix this easily.\n");
+	if (fs->clusters < FAT16_THRESHOLD)
+	    printf("Warning: Filesystem is FAT32 according to fat_length "
+		   "and fat32_length fields,\n"
+		   "  but has only %lu clusters, less than the required "
+		   "minimum of %d.\n"
+		   "  This may lead to problems on some systems.\n",
+		   fs->clusters, FAT16_THRESHOLD);
+
+	fs->backupboot_start = CF_LE_W(b.backup_boot) * logical_sector_size;
+	check_backup_boot(fs, &b, logical_sector_size);
+
+	read_fsinfo(fs, &b, logical_sector_size);
+    } else if (!atari_format) {
+	/* On real MS-DOS, a 16 bit FAT is used whenever there would be too
+	 * much clusers otherwise. */
+	fs->fat_bits = (fs->clusters >= FAT12_THRESHOLD) ? 16 : 12;
+	if (fs->clusters >= FAT16_THRESHOLD)
+	    die("Too many clusters (%lu) for FAT16 filesystem.", fs->clusters);
+    } else {
+	/* On Atari, things are more difficult: GEMDOS always uses 12bit FATs
+	 * on floppies, and always 16 bit on harddisks. */
+	fs->fat_bits = 16;	/* assume 16 bit FAT for now */
+	/* If more clusters than fat entries in 16-bit fat, we assume
+	 * it's a real MSDOS FS with 12-bit fat. */
+	if (fs->clusters + 2 > fat_length * logical_sector_size * 8 / 16 ||
+	    /* if it's a floppy disk --> 12bit fat */
+	    device_no == 2 ||
+	    /* if it's a ramdisk or loopback device and has one of the usual
+	     * floppy sizes -> 12bit FAT  */
+	    ((device_no == 1 || device_no == 7) &&
+	     (total_sectors == 720 || total_sectors == 1440 ||
+	      total_sectors == 2880)))
+	    fs->fat_bits = 12;
+    }
+    /* On FAT32, the high 4 bits of a FAT entry are reserved */
+    fs->eff_fat_bits = (fs->fat_bits == 32) ? 28 : fs->fat_bits;
+    fs->fat_size = fat_length * logical_sector_size;
+
+    fs->label = calloc(12, sizeof(__u8));
+    if (fs->fat_bits == 12 || fs->fat_bits == 16) {
+	struct boot_sector_16 *b16 = (struct boot_sector_16 *)&b;
+	if (b16->extended_sig == 0x29)
+	    memmove(fs->label, b16->label, 11);
+	else
+	    fs->label = NULL;
+    } else if (fs->fat_bits == 32) {
+	if (b.extended_sig == 0x29)
+	    memmove(fs->label, &b.label, 11);
+	else
+	    fs->label = NULL;
+    }
+
+    if (fs->clusters >
+	((unsigned long long)fs->fat_size * 8 / fs->fat_bits) - 2)
+	die("File system has %d clusters but only space for %d FAT entries.",
+	    fs->clusters,
+	    ((unsigned long long)fs->fat_size * 8 / fs->fat_bits) - 2);
+    if (!fs->root_entries && !fs->root_cluster)
+	die("Root directory has zero size.");
+    if (fs->root_entries & (MSDOS_DPS - 1))
+	die("Root directory (%d entries) doesn't span an integral number of "
+	    "sectors.", fs->root_entries);
+    if (logical_sector_size & (SECTOR_SIZE - 1))
+	die("Logical sector size (%d bytes) is not a multiple of the physical "
+	    "sector size.", logical_sector_size);
+#if 0				/* linux kernel doesn't check that either */
+    /* ++roman: On Atari, these two fields are often left uninitialized */
+    if (!atari_format && (!b.secs_track || !b.heads))
+	die("Invalid disk format in boot sector.");
+#endif
+    if (verbose)
+	dump_boot(fs, &b, logical_sector_size);
+}
+
+static void write_boot_label(DOS_FS * fs, char *label)
+{
+    struct boot_sector b;
+    struct boot_sector_16 *b16 = (struct boot_sector_16 *)&b;
+
+    fs_read(0, sizeof(b), &b);
+    if (fs->fat_bits == 12 || fs->fat_bits == 16) {
+	if (b16->extended_sig != 0x29) {
+	    b16->extended_sig = 0x29;
+	    b16->serial = 0;
+	    memmove(b16->fs_type, fs->fat_bits == 12 ? "FAT12   " : "FAT16   ",
+		    8);
+	}
+	memmove(b16->label, label, 11);
+    } else if (fs->fat_bits == 32) {
+	if (b.extended_sig != 0x29) {
+	    b.extended_sig = 0x29;
+	    b.serial = 0;
+	    memmove(b.fs_type, "FAT32   ", 8);
+	}
+	memmove(b.label, label, 11);
+    }
+    fs_write(0, sizeof(b), &b);
+    if (fs->fat_bits == 32 && fs->backupboot_start)
+	fs_write(fs->backupboot_start, sizeof(b), &b);
+}
+
+static loff_t find_volume_de(DOS_FS * fs, DIR_ENT * de)
+{
+    unsigned long cluster;
+    loff_t offset;
+    int i;
+
+    if (fs->root_cluster) {
+	for (cluster = fs->root_cluster;
+	     cluster != 0 && cluster != -1;
+	     cluster = next_cluster(fs, cluster)) {
+	    offset = cluster_start(fs, cluster);
+	    for (i = 0; i * sizeof(DIR_ENT) < fs->cluster_size; i++) {
+		fs_read(offset, sizeof(DIR_ENT), de);
+		if (de->attr & ATTR_VOLUME)
+		    return offset;
+		offset += sizeof(DIR_ENT);
+	    }
+	}
+    } else {
+	for (i = 0; i < fs->root_entries; i++) {
+	    offset = fs->root_start + i * sizeof(DIR_ENT);
+	    fs_read(offset, sizeof(DIR_ENT), de);
+	    if (de->attr & ATTR_VOLUME)
+		return offset;
+	}
+    }
+
+    return 0;
+}
+
+static void write_volume_label(DOS_FS * fs, char *label)
+{
+    time_t now = time(NULL);
+    struct tm *mtime = localtime(&now);
+    loff_t offset;
+    DIR_ENT de;
+
+    offset = find_volume_de(fs, &de);
+    if (offset == 0)
+	return;
+
+    memcpy(de.name, label, 11);
+    de.time = CT_LE_W((unsigned short)((mtime->tm_sec >> 1) +
+				       (mtime->tm_min << 5) +
+				       (mtime->tm_hour << 11)));
+    de.date = CT_LE_W((unsigned short)(mtime->tm_mday +
+				       ((mtime->tm_mon + 1) << 5) +
+				       ((mtime->tm_year - 80) << 9)));
+    fs_write(offset, sizeof(DIR_ENT), &de);
+}
+
+void write_label(DOS_FS * fs, char *label)
+{
+    int l = strlen(label);
+
+    while (l < 11)
+	label[l++] = ' ';
+
+    write_boot_label(fs, label);
+    write_volume_label(fs, label);
+}
diff --git a/dosfstools/src/boot.h b/dosfstools/src/boot.h
new file mode 100644
index 0000000..c9edfa3
--- /dev/null
+++ b/dosfstools/src/boot.h
@@ -0,0 +1,30 @@
+/* boot.h - Read and analyze ia PC/MS-DOS boot sector
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+#ifndef _BOOT_H
+#define _BOOT_H
+
+void read_boot(DOS_FS * fs);
+void write_label(DOS_FS * fs, char *label);
+
+/* Reads the boot sector from the currently open device and initializes *FS */
+
+#endif
diff --git a/dosfstools/src/check.c b/dosfstools/src/check.c
new file mode 100644
index 0000000..3f175b0
--- /dev/null
+++ b/dosfstools/src/check.c
@@ -0,0 +1,1051 @@
+/* check.c - Check and repair a PC/MS-DOS file system
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
+ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+
+#include "common.h"
+#include "dosfsck.h"
+#include "io.h"
+#include "fat.h"
+#include "file.h"
+#include "lfn.h"
+#include "check.h"
+
+static DOS_FILE *root;
+
+/* get start field of a dir entry */
+#define FSTART(p,fs) \
+  ((unsigned long)CF_LE_W(p->dir_ent.start) | \
+   (fs->fat_bits == 32 ? CF_LE_W(p->dir_ent.starthi) << 16 : 0))
+
+#define MODIFY(p,i,v)					\
+  do {							\
+    if (p->offset) {					\
+	p->dir_ent.i = v;				\
+	fs_write(p->offset+offsetof(DIR_ENT,i),		\
+		 sizeof(p->dir_ent.i),&p->dir_ent.i);	\
+    }							\
+  } while(0)
+
+#define MODIFY_START(p,v,fs)						\
+  do {									\
+    unsigned long __v = (v);						\
+    if (!p->offset) {							\
+	/* writing to fake entry for FAT32 root dir */			\
+	if (!__v) die("Oops, deleting FAT32 root dir!");		\
+	fs->root_cluster = __v;						\
+	p->dir_ent.start = CT_LE_W(__v&0xffff);				\
+	p->dir_ent.starthi = CT_LE_W(__v>>16);				\
+	__v = CT_LE_L(__v);						\
+	fs_write((loff_t)offsetof(struct boot_sector,root_cluster),	\
+	         sizeof(((struct boot_sector *)0)->root_cluster),	\
+		 &__v);							\
+    }									\
+    else {								\
+	MODIFY(p,start,CT_LE_W((__v)&0xffff));				\
+	if (fs->fat_bits == 32)						\
+	    MODIFY(p,starthi,CT_LE_W((__v)>>16));			\
+    }									\
+  } while(0)
+
+loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern)
+{
+    static int curr_num = 0;
+    loff_t offset;
+
+    if (fs->root_cluster) {
+	DIR_ENT d2;
+	int i = 0, got = 0;
+	unsigned long clu_num, prev = 0;
+	loff_t offset2;
+
+	clu_num = fs->root_cluster;
+	offset = cluster_start(fs, clu_num);
+	while (clu_num > 0 && clu_num != -1) {
+	    fs_read(offset, sizeof(DIR_ENT), &d2);
+	    if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) {
+		got = 1;
+		break;
+	    }
+	    i += sizeof(DIR_ENT);
+	    offset += sizeof(DIR_ENT);
+	    if ((i % fs->cluster_size) == 0) {
+		prev = clu_num;
+		if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
+		    break;
+		offset = cluster_start(fs, clu_num);
+	    }
+	}
+	if (!got) {
+	    /* no free slot, need to extend root dir: alloc next free cluster
+	     * after previous one */
+	    if (!prev)
+		die("Root directory has no cluster allocated!");
+	    for (clu_num = prev + 1; clu_num != prev; clu_num++) {
+		FAT_ENTRY entry;
+
+		if (clu_num >= fs->clusters + 2)
+		    clu_num = 2;
+		get_fat(&entry, fs->fat, clu_num, fs);
+		if (!entry.value)
+		    break;
+	    }
+	    if (clu_num == prev)
+		die("Root directory full and no free cluster");
+	    set_fat(fs, prev, clu_num);
+	    set_fat(fs, clu_num, -1);
+	    set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
+	    /* clear new cluster */
+	    memset(&d2, 0, sizeof(d2));
+	    offset = cluster_start(fs, clu_num);
+	    for (i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT))
+		fs_write(offset + i, sizeof(d2), &d2);
+	}
+	memset(de, 0, sizeof(DIR_ENT));
+	while (1) {
+	    char expanded[12];
+	    sprintf(expanded, pattern, curr_num);
+	    memcpy(de->name + 4, expanded, 4);
+	    memcpy(de->ext, expanded + 4, 3);
+	    clu_num = fs->root_cluster;
+	    i = 0;
+	    offset2 = cluster_start(fs, clu_num);
+	    while (clu_num > 0 && clu_num != -1) {
+		fs_read(offset2, sizeof(DIR_ENT), &d2);
+		if (offset2 != offset &&
+		    !strncmp((const char *)d2.name, (const char *)de->name,
+			     MSDOS_NAME))
+		    break;
+		i += sizeof(DIR_ENT);
+		offset2 += sizeof(DIR_ENT);
+		if ((i % fs->cluster_size) == 0) {
+		    if ((clu_num = next_cluster(fs, clu_num)) == 0 ||
+			clu_num == -1)
+			break;
+		    offset2 = cluster_start(fs, clu_num);
+		}
+	    }
+	    if (clu_num == 0 || clu_num == -1)
+		break;
+	    if (++curr_num >= 10000)
+		die("Unable to create unique name");
+	}
+    } else {
+	DIR_ENT *root;
+	int next_free = 0, scan;
+
+	root = alloc(fs->root_entries * sizeof(DIR_ENT));
+	fs_read(fs->root_start, fs->root_entries * sizeof(DIR_ENT), root);
+
+	while (next_free < fs->root_entries)
+	    if (IS_FREE(root[next_free].name) &&
+		root[next_free].attr != VFAT_LN_ATTR)
+		break;
+	    else
+		next_free++;
+	if (next_free == fs->root_entries)
+	    die("Root directory is full.");
+	offset = fs->root_start + next_free * sizeof(DIR_ENT);
+	memset(de, 0, sizeof(DIR_ENT));
+	while (1) {
+	    sprintf((char *)de->name, pattern, curr_num);
+	    for (scan = 0; scan < fs->root_entries; scan++)
+		if (scan != next_free &&
+		    !strncmp((const char *)root[scan].name,
+			     (const char *)de->name, MSDOS_NAME))
+		    break;
+	    if (scan == fs->root_entries)
+		break;
+	    if (++curr_num >= 10000)
+		die("Unable to create unique name");
+	}
+	free(root);
+    }
+    ++n_files;
+    return offset;
+}
+
+/**
+ * Construct a full path (starting with '/') for the specified dentry,
+ * relative to the partition. All components are "long" names where possible.
+ *
+ * @param[in]   file    Information about dentry (file or directory) of interest
+ *
+ * return       Pointer to static string containing file's full path
+ */
+static char *path_name(DOS_FILE * file)
+{
+    static char path[PATH_MAX * 2];
+
+    if (!file)
+	*path = 0;		/* Reached the root directory */
+    else {
+	if (strlen(path_name(file->parent)) > PATH_MAX)
+	    die("Path name too long.");
+	if (strcmp(path, "/") != 0)
+	    strcat(path, "/");
+
+	/* Append the long name to the path,
+	 * or the short name if there isn't a long one
+	 */
+	strcpy(strrchr(path, 0),
+	       file->lfn ? file->lfn : file_name(file->dir_ent.name));
+    }
+    return path;
+}
+
+static int day_n[] =
+    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 };
+		  /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+time_t date_dos2unix(unsigned short time, unsigned short date)
+{
+    int month, year;
+    time_t secs;
+
+    month = ((date >> 5) & 15) - 1;
+    year = date >> 9;
+    secs =
+	(time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
+	86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 -
+		 ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
+    /* days since 1.1.70 plus 80's leap day */
+    return secs;
+}
+
+static char *file_stat(DOS_FILE * file)
+{
+    static char temp[100];
+    struct tm *tm;
+    char tmp[100];
+    time_t date;
+
+    date =
+	date_dos2unix(CF_LE_W(file->dir_ent.time), CF_LE_W(file->dir_ent.date));
+    tm = localtime(&date);
+    strftime(tmp, 99, "%H:%M:%S %b %d %Y", tm);
+    sprintf(temp, "  Size %u bytes, date %s", CF_LE_L(file->dir_ent.size), tmp);
+    return temp;
+}
+
+static int bad_name(DOS_FILE * file)
+{
+    int i, spc, suspicious = 0;
+    char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:";
+    unsigned char *name = file->dir_ent.name;
+
+    /* Do not complain about (and auto-correct) the extended attribute files
+     * of OS/2. */
+    if (strncmp((const char *)name, "EA DATA  SF", 11) == 0 ||
+	strncmp((const char *)name, "WP ROOT  SF", 11) == 0)
+	return 0;
+
+    /* don't complain about the dummy 11 bytes used by patched Linux
+       kernels */
+    if (file->dir_ent.lcase & FAT_NO_83NAME)
+	return 0;
+
+    for (i = 0; i < 8; i++) {
+	if (name[i] < ' ' || name[i] == 0x7f)
+	    return 1;
+	if (name[i] > 0x7f)
+	    ++suspicious;
+	if (strchr(bad_chars, name[i]))
+	    return 1;
+    }
+
+    for (i = 8; i < 11; i++) {
+	if (name[i] < ' ' || name[i] == 0x7f)
+	    return 1;
+	if (name[i] > 0x7f)
+	    ++suspicious;
+	if (strchr(bad_chars, name[i]))
+	    return 1;
+    }
+
+    spc = 0;
+    for (i = 0; i < 8; i++) {
+	if (name[i] == ' ')
+	    spc = 1;
+	else if (spc)
+	    /* non-space after a space not allowed, space terminates the name
+	     * part */
+	    return 1;
+    }
+
+    spc = 0;
+    for (i = 8; i < 11; i++) {
+	if (name[i] == ' ')
+	    spc = 1;
+	else if (spc)
+	    /* non-space after a space not allowed, space terminates the name
+	     * part */
+	    return 1;
+    }
+
+    /* Under GEMDOS, chars >= 128 are never allowed. */
+    if (atari_format && suspicious)
+	return 1;
+
+    /* Under MS-DOS and Windows, chars >= 128 in short names are valid
+     * (but these characters can be visualised differently depending on
+     * local codepage: CP437, CP866, etc). The chars are all basically ok,
+     * so we shouldn't auto-correct such names. */
+    return 0;
+}
+
+static void lfn_remove(loff_t from, loff_t to)
+{
+    DIR_ENT empty;
+
+    /* New dir entry is zeroed except first byte, which is set to 0xe5.
+     * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
+     * a directory at the first zero entry...
+     */
+    memset(&empty, 0, sizeof(empty));
+    empty.name[0] = DELETED_FLAG;
+
+    for (; from < to; from += sizeof(empty)) {
+	fs_write(from, sizeof(DIR_ENT), &empty);
+    }
+}
+
+static void drop_file(DOS_FS * fs, DOS_FILE * file)
+{
+    unsigned long cluster;
+
+    MODIFY(file, name[0], DELETED_FLAG);
+    if (file->lfn)
+	lfn_remove(file->lfn_offset, file->offset);
+    for (cluster = FSTART(file, fs); cluster > 0 && cluster <
+	 fs->clusters + 2; cluster = next_cluster(fs, cluster))
+	set_owner(fs, cluster, NULL);
+    --n_files;
+}
+
+static void truncate_file(DOS_FS * fs, DOS_FILE * file, unsigned long clusters)
+{
+    int deleting;
+    unsigned long walk, next, prev;
+
+    walk = FSTART(file, fs);
+    prev = 0;
+    if ((deleting = !clusters))
+	MODIFY_START(file, 0, fs);
+    while (walk > 0 && walk != -1) {
+	next = next_cluster(fs, walk);
+	if (deleting)
+	    set_fat(fs, walk, 0);
+	else if ((deleting = !--clusters))
+	    set_fat(fs, walk, -1);
+	prev = walk;
+	walk = next;
+    }
+}
+
+static void auto_rename(DOS_FILE * file)
+{
+    DOS_FILE *first, *walk;
+    unsigned long int number;
+
+    if (!file->offset)
+	return;			/* cannot rename FAT32 root dir */
+    first = file->parent ? file->parent->first : root;
+    number = 0;
+    while (1) {
+	char num[8];
+	sprintf(num, "%07lu", number);
+	memcpy(file->dir_ent.name, "FSCK", 4);
+	memcpy(file->dir_ent.name + 4, num, 4);
+	memcpy(file->dir_ent.ext, num + 4, 3);
+	for (walk = first; walk; walk = walk->next)
+	    if (walk != file
+		&& !strncmp((const char *)walk->dir_ent.name,
+			    (const char *)file->dir_ent.name, MSDOS_NAME))
+		break;
+	if (!walk) {
+	    fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
+	    if (file->lfn)
+		lfn_fix_checksum(file->lfn_offset, file->offset,
+				 (const char *)file->dir_ent.name);
+	    return;
+	}
+	number++;
+	if (number > 9999999) {
+	    die("Too many files need repair.");
+	}
+    }
+    die("Can't generate a unique name.");
+}
+
+static void rename_file(DOS_FILE * file)
+{
+    unsigned char name[46];
+    unsigned char *walk, *here;
+
+    if (!file->offset) {
+	printf("Cannot rename FAT32 root dir\n");
+	return;			/* cannot rename FAT32 root dir */
+    }
+    while (1) {
+	printf("New name: ");
+	fflush(stdout);
+	if (fgets((char *)name, 45, stdin)) {
+	    if ((here = (unsigned char *)strchr((const char *)name, '\n')))
+		*here = 0;
+	    for (walk = (unsigned char *)strrchr((const char *)name, 0);
+		 walk >= name && (*walk == ' ' || *walk == '\t'); walk--) ;
+	    walk[1] = 0;
+	    for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ;
+	    if (file_cvt(walk, file->dir_ent.name)) {
+		fs_write(file->offset, MSDOS_NAME, file->dir_ent.name);
+		if (file->lfn)
+		    lfn_fix_checksum(file->lfn_offset, file->offset,
+				     (const char *)file->dir_ent.name);
+		return;
+	    }
+	}
+    }
+}
+
+static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots)
+{
+    char *name;
+
+    name =
+	strncmp((const char *)file->dir_ent.name, MSDOS_DOT,
+		MSDOS_NAME) ? ".." : ".";
+    if (!(file->dir_ent.attr & ATTR_DIR)) {
+	printf("%s\n  Is a non-directory.\n", path_name(file));
+	if (interactive)
+	    printf("1) Drop it\n2) Auto-rename\n3) Rename\n"
+		   "4) Convert to directory\n");
+	else
+	    printf("  Auto-renaming it.\n");
+	switch (interactive ? get_key("1234", "?") : '2') {
+	case '1':
+	    drop_file(fs, file);
+	    return 1;
+	case '2':
+	    auto_rename(file);
+	    printf("  Renamed to %s\n", file_name(file->dir_ent.name));
+	    return 0;
+	case '3':
+	    rename_file(file);
+	    return 0;
+	case '4':
+	    MODIFY(file, size, CT_LE_L(0));
+	    MODIFY(file, attr, file->dir_ent.attr | ATTR_DIR);
+	    break;
+	}
+    }
+    if (!dots) {
+	printf("Root contains directory \"%s\". Dropping it.\n", name);
+	drop_file(fs, file);
+	return 1;
+    }
+    return 0;
+}
+
+static int check_file(DOS_FS * fs, DOS_FILE * file)
+{
+    DOS_FILE *owner;
+    int restart;
+    unsigned long expect, curr, this, clusters, prev, walk, clusters2;
+
+    if (file->dir_ent.attr & ATTR_DIR) {
+	if (CF_LE_L(file->dir_ent.size)) {
+	    printf("%s\n  Directory has non-zero size. Fixing it.\n",
+		   path_name(file));
+	    MODIFY(file, size, CT_LE_L(0));
+	}
+	if (file->parent
+	    && !strncmp((const char *)file->dir_ent.name, MSDOS_DOT,
+			MSDOS_NAME)) {
+	    expect = FSTART(file->parent, fs);
+	    if (FSTART(file, fs) != expect) {
+		printf("%s\n  Start (%ld) does not point to parent (%ld)\n",
+		       path_name(file), FSTART(file, fs), expect);
+		MODIFY_START(file, expect, fs);
+	    }
+	    return 0;
+	}
+	if (file->parent
+	    && !strncmp((const char *)file->dir_ent.name, MSDOS_DOTDOT,
+			MSDOS_NAME)) {
+	    expect =
+		file->parent->parent ? FSTART(file->parent->parent, fs) : 0;
+	    if (fs->root_cluster && expect == fs->root_cluster)
+		expect = 0;
+	    if (FSTART(file, fs) != expect) {
+		printf("%s\n  Start (%lu) does not point to .. (%lu)\n",
+		       path_name(file), FSTART(file, fs), expect);
+		MODIFY_START(file, expect, fs);
+	    }
+	    return 0;
+	}
+	if (FSTART(file, fs) == 0) {
+	    printf("%s\n Start does point to root directory. Deleting dir. \n",
+		   path_name(file));
+	    MODIFY(file, name[0], DELETED_FLAG);
+	    return 0;
+	}
+    }
+    if (FSTART(file, fs) >= fs->clusters + 2) {
+	printf
+	    ("%s\n  Start cluster beyond limit (%lu > %lu). Truncating file.\n",
+	     path_name(file), FSTART(file, fs), fs->clusters + 1);
+	if (!file->offset)
+	    die("Bad FAT32 root directory! (bad start cluster)\n");
+	MODIFY_START(file, 0, fs);
+    }
+    clusters = prev = 0;
+    for (curr = FSTART(file, fs) ? FSTART(file, fs) :
+	 -1; curr != -1; curr = next_cluster(fs, curr)) {
+	FAT_ENTRY curEntry;
+	get_fat(&curEntry, fs->fat, curr, fs);
+
+	if (!curEntry.value || bad_cluster(fs, curr)) {
+	    printf("%s\n  Contains a %s cluster (%lu). Assuming EOF.\n",
+		   path_name(file), curEntry.value ? "bad" : "free", curr);
+	    if (prev)
+		set_fat(fs, prev, -1);
+	    else if (!file->offset)
+		die("FAT32 root dir starts with a bad cluster!");
+	    else
+		MODIFY_START(file, 0, fs);
+	    break;
+	}
+	if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <=
+	    (unsigned long long)clusters * fs->cluster_size) {
+	    printf
+		("%s\n  File size is %u bytes, cluster chain length is > %llu "
+		 "bytes.\n  Truncating file to %u bytes.\n", path_name(file),
+		 CF_LE_L(file->dir_ent.size),
+		 (unsigned long long)clusters * fs->cluster_size,
+		 CF_LE_L(file->dir_ent.size));
+	    truncate_file(fs, file, clusters);
+	    break;
+	}
+	if ((owner = get_owner(fs, curr))) {
+	    int do_trunc = 0;
+	    printf("%s  and\n", path_name(owner));
+	    printf("%s\n  share clusters.\n", path_name(file));
+	    clusters2 = 0;
+	    for (walk = FSTART(owner, fs); walk > 0 && walk != -1; walk =
+		 next_cluster(fs, walk))
+		if (walk == curr)
+		    break;
+		else
+		    clusters2++;
+	    restart = file->dir_ent.attr & ATTR_DIR;
+	    if (!owner->offset) {
+		printf("  Truncating second to %llu bytes because first "
+		       "is FAT32 root dir.\n",
+		       (unsigned long long)clusters2 * fs->cluster_size);
+		do_trunc = 2;
+	    } else if (!file->offset) {
+		printf("  Truncating first to %llu bytes because second "
+		       "is FAT32 root dir.\n",
+		       (unsigned long long)clusters * fs->cluster_size);
+		do_trunc = 1;
+	    } else if (interactive)
+		printf("1) Truncate first to %llu bytes%s\n"
+		       "2) Truncate second to %llu bytes\n",
+		       (unsigned long long)clusters * fs->cluster_size,
+		       restart ? " and restart" : "",
+		       (unsigned long long)clusters2 * fs->cluster_size);
+	    else
+		printf("  Truncating second to %llu bytes.\n",
+		       (unsigned long long)clusters2 * fs->cluster_size);
+	    if (do_trunc != 2
+		&& (do_trunc == 1
+		    || (interactive && get_key("12", "?") == '1'))) {
+		prev = 0;
+		clusters = 0;
+		for (this = FSTART(owner, fs); this > 0 && this != -1; this =
+		     next_cluster(fs, this)) {
+		    if (this == curr) {
+			if (prev)
+			    set_fat(fs, prev, -1);
+			else
+			    MODIFY_START(owner, 0, fs);
+			MODIFY(owner, size,
+			       CT_LE_L((unsigned long long)clusters *
+				       fs->cluster_size));
+			if (restart)
+			    return 1;
+			while (this > 0 && this != -1) {
+			    set_owner(fs, this, NULL);
+			    this = next_cluster(fs, this);
+			}
+			this = curr;
+			break;
+		    }
+		    clusters++;
+		    prev = this;
+		}
+		if (this != curr)
+		    die("Internal error: didn't find cluster %d in chain"
+			" starting at %d", curr, FSTART(owner, fs));
+	    } else {
+		if (prev)
+		    set_fat(fs, prev, -1);
+		else
+		    MODIFY_START(file, 0, fs);
+		break;
+	    }
+	}
+	set_owner(fs, curr, file);
+	clusters++;
+	prev = curr;
+    }
+    if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) >
+	(unsigned long long)clusters * fs->cluster_size) {
+	printf
+	    ("%s\n  File size is %u bytes, cluster chain length is %llu bytes."
+	     "\n  Truncating file to %llu bytes.\n", path_name(file),
+	     CF_LE_L(file->dir_ent.size),
+	     (unsigned long long)clusters * fs->cluster_size,
+	     (unsigned long long)clusters * fs->cluster_size);
+	MODIFY(file, size,
+	       CT_LE_L((unsigned long long)clusters * fs->cluster_size));
+    }
+    return 0;
+}
+
+static int check_files(DOS_FS * fs, DOS_FILE * start)
+{
+    while (start) {
+	if (check_file(fs, start))
+	    return 1;
+	start = start->next;
+    }
+    return 0;
+}
+
+static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots)
+{
+    DOS_FILE *parent, **walk, **scan;
+    int dot, dotdot, skip, redo;
+    int good, bad;
+
+    if (!*root)
+	return 0;
+    parent = (*root)->parent;
+    good = bad = 0;
+    for (walk = root; *walk; walk = &(*walk)->next)
+	if (bad_name(*walk))
+	    bad++;
+	else
+	    good++;
+    if (*root && parent && good + bad > 4 && bad > good / 2) {
+	printf("%s\n  Has a large number of bad entries. (%d/%d)\n",
+	       path_name(parent), bad, good + bad);
+	if (!dots)
+	    printf("  Not dropping root directory.\n");
+	else if (!interactive)
+	    printf("  Not dropping it in auto-mode.\n");
+	else if (get_key("yn", "Drop directory ? (y/n)") == 'y') {
+	    truncate_file(fs, parent, 0);
+	    MODIFY(parent, name[0], DELETED_FLAG);
+	    /* buglet: deleted directory stays in the list. */
+	    return 1;
+	}
+    }
+    dot = dotdot = redo = 0;
+    walk = root;
+    while (*walk) {
+	if (!strncmp
+	    ((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME)
+	    || !strncmp((const char *)((*walk)->dir_ent.name), MSDOS_DOTDOT,
+			MSDOS_NAME)) {
+	    if (handle_dot(fs, *walk, dots)) {
+		*walk = (*walk)->next;
+		continue;
+	    }
+	    if (!strncmp
+		((const char *)((*walk)->dir_ent.name), MSDOS_DOT, MSDOS_NAME))
+		dot++;
+	    else
+		dotdot++;
+	}
+	if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name(*walk)) {
+	    puts(path_name(*walk));
+	    printf("  Bad short file name (%s).\n",
+		   file_name((*walk)->dir_ent.name));
+	    if (interactive)
+		printf("1) Drop file\n2) Rename file\n3) Auto-rename\n"
+		       "4) Keep it\n");
+	    else
+		printf("  Auto-renaming it.\n");
+	    switch (interactive ? get_key("1234", "?") : '3') {
+	    case '1':
+		drop_file(fs, *walk);
+		walk = &(*walk)->next;
+		continue;
+	    case '2':
+		rename_file(*walk);
+		redo = 1;
+		break;
+	    case '3':
+		auto_rename(*walk);
+		printf("  Renamed to %s\n", file_name((*walk)->dir_ent.name));
+		break;
+	    case '4':
+		break;
+	    }
+	}
+	/* don't check for duplicates of the volume label */
+	if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) {
+	    scan = &(*walk)->next;
+	    skip = 0;
+	    while (*scan && !skip) {
+		if (!((*scan)->dir_ent.attr & ATTR_VOLUME) &&
+		    !memcmp((*walk)->dir_ent.name, (*scan)->dir_ent.name,
+			    MSDOS_NAME)) {
+		    printf("%s\n  Duplicate directory entry.\n  First  %s\n",
+			   path_name(*walk), file_stat(*walk));
+		    printf("  Second %s\n", file_stat(*scan));
+		    if (interactive)
+			printf
+			    ("1) Drop first\n2) Drop second\n3) Rename first\n"
+			     "4) Rename second\n5) Auto-rename first\n"
+			     "6) Auto-rename second\n");
+		    else
+			printf("  Auto-renaming second.\n");
+		    switch (interactive ? get_key("123456", "?") : '6') {
+		    case '1':
+			drop_file(fs, *walk);
+			*walk = (*walk)->next;
+			skip = 1;
+			break;
+		    case '2':
+			drop_file(fs, *scan);
+			*scan = (*scan)->next;
+			continue;
+		    case '3':
+			rename_file(*walk);
+			printf("  Renamed to %s\n", path_name(*walk));
+			redo = 1;
+			break;
+		    case '4':
+			rename_file(*scan);
+			printf("  Renamed to %s\n", path_name(*walk));
+			redo = 1;
+			break;
+		    case '5':
+			auto_rename(*walk);
+			printf("  Renamed to %s\n",
+			       file_name((*walk)->dir_ent.name));
+			break;
+		    case '6':
+			auto_rename(*scan);
+			printf("  Renamed to %s\n",
+			       file_name((*scan)->dir_ent.name));
+			break;
+		    }
+		}
+		scan = &(*scan)->next;
+	    }
+	    if (skip)
+		continue;
+	}
+	if (!redo)
+	    walk = &(*walk)->next;
+	else {
+	    walk = root;
+	    dot = dotdot = redo = 0;
+	}
+    }
+    if (dots && !dot)
+	printf("%s\n  \".\" is missing. Can't fix this yet.\n",
+	       path_name(parent));
+    if (dots && !dotdot)
+	printf("%s\n  \"..\" is missing. Can't fix this yet.\n",
+	       path_name(parent));
+    return 0;
+}
+
+/**
+ * Check a dentry's cluster chain for bad clusters.
+ * If requested, we verify readability and mark unreadable clusters as bad.
+ *
+ * @param[inout]    fs          Information about the filesystem
+ * @param[in]       file        dentry to check
+ * @param[in]       read_test   Nonzero == verify that dentry's clusters can
+ *                              be read
+ */
+static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test)
+{
+    DOS_FILE *owner;
+    unsigned long walk, prev, clusters, next_clu;
+
+    prev = clusters = 0;
+    for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2;
+	 walk = next_clu) {
+	next_clu = next_cluster(fs, walk);
+
+	/* In this stage we are checking only for a loop within our own
+	 * cluster chain.
+	 * Cross-linking of clusters is handled in check_file()
+	 */
+	if ((owner = get_owner(fs, walk))) {
+	    if (owner == file) {
+		printf("%s\n  Circular cluster chain. Truncating to %lu "
+		       "cluster%s.\n", path_name(file), clusters,
+		       clusters == 1 ? "" : "s");
+		if (prev)
+		    set_fat(fs, prev, -1);
+		else if (!file->offset)
+		    die("Bad FAT32 root directory! (bad start cluster)\n");
+		else
+		    MODIFY_START(file, 0, fs);
+	    }
+	    break;
+	}
+	if (bad_cluster(fs, walk))
+	    break;
+	if (read_test) {
+	    if (fs_test(cluster_start(fs, walk), fs->cluster_size)) {
+		prev = walk;
+		clusters++;
+	    } else {
+		printf("%s\n  Cluster %lu (%lu) is unreadable. Skipping it.\n",
+		       path_name(file), clusters, walk);
+		if (prev)
+		    set_fat(fs, prev, next_cluster(fs, walk));
+		else
+		    MODIFY_START(file, next_cluster(fs, walk), fs);
+		set_fat(fs, walk, -2);
+	    }
+	}
+	set_owner(fs, walk, file);
+    }
+    /* Revert ownership (for now) */
+    for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2;
+	 walk = next_cluster(fs, walk))
+	if (bad_cluster(fs, walk))
+	    break;
+	else if (get_owner(fs, walk) == file)
+	    set_owner(fs, walk, NULL);
+	else
+	    break;
+}
+
+static void undelete(DOS_FS * fs, DOS_FILE * file)
+{
+    unsigned long clusters, left, prev, walk;
+
+    clusters = left = (CF_LE_L(file->dir_ent.size) + fs->cluster_size - 1) /
+	fs->cluster_size;
+    prev = 0;
+
+    walk = FSTART(file, fs);
+
+    while (left && (walk >= 2) && (walk < fs->clusters + 2)) {
+
+	FAT_ENTRY curEntry;
+	get_fat(&curEntry, fs->fat, walk, fs);
+
+	if (!curEntry.value)
+	    break;
+
+	left--;
+	if (prev)
+	    set_fat(fs, prev, walk);
+	prev = walk;
+	walk++;
+    }
+    if (prev)
+	set_fat(fs, prev, -1);
+    else
+	MODIFY_START(file, 0, fs);
+    if (left)
+	printf("Warning: Did only undelete %lu of %lu cluster%s.\n",
+	       clusters - left, clusters, clusters == 1 ? "" : "s");
+
+}
+
+static void new_dir(void)
+{
+    lfn_reset();
+}
+
+/**
+ * Create a description for a referenced dentry and insert it in our dentry
+ * tree. Then, go check the dentry's cluster chain for bad clusters and
+ * cluster loops.
+ *
+ * @param[inout]    fs      Information about the filesystem
+ * @param[out]      chain
+ * @param[in]       parent  Information about parent directory of this file
+ *                          NULL == no parent ('file' is root directory)
+ * @param[in]       offset  Partition-relative byte offset of directory entry of interest
+ *                          0 == Root directory
+ * @param           cp
+ */
+static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent,
+		     loff_t offset, FDSC ** cp)
+{
+    DOS_FILE *new;
+    DIR_ENT de;
+    FD_TYPE type;
+
+    if (offset)
+	fs_read(offset, sizeof(DIR_ENT), &de);
+    else {
+	/* Construct a DIR_ENT for the root directory */
+	memcpy(de.name, "           ", MSDOS_NAME);
+	de.attr = ATTR_DIR;
+	de.size = de.time = de.date = 0;
+	de.start = CT_LE_W(fs->root_cluster & 0xffff);
+	de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff);
+    }
+    if ((type = file_type(cp, (char *)de.name)) != fdt_none) {
+	if (type == fdt_undelete && (de.attr & ATTR_DIR))
+	    die("Can't undelete directories.");
+	file_modify(cp, (char *)de.name);
+	fs_write(offset, 1, &de);
+    }
+    if (IS_FREE(de.name)) {
+	lfn_check_orphaned();
+	return;
+    }
+    if (de.attr == VFAT_LN_ATTR) {
+	lfn_add_slot(&de, offset);
+	return;
+    }
+    new = qalloc(&mem_queue, sizeof(DOS_FILE));
+    new->lfn = lfn_get(&de, &new->lfn_offset);
+    new->offset = offset;
+    memcpy(&new->dir_ent, &de, sizeof(de));
+    new->next = new->first = NULL;
+    new->parent = parent;
+    if (type == fdt_undelete)
+	undelete(fs, new);
+    **chain = new;
+    *chain = &new->next;
+    if (list) {
+	printf("Checking file %s", path_name(new));
+	if (new->lfn)
+	    printf(" (%s)", file_name(new->dir_ent.name));	/* (8.3) */
+	printf("\n");
+    }
+    /* Don't include root directory, '.', or '..' in the total file count */
+    if (offset &&
+	strncmp((const char *)de.name, MSDOS_DOT, MSDOS_NAME) != 0 &&
+	strncmp((const char *)de.name, MSDOS_DOTDOT, MSDOS_NAME) != 0)
+	++n_files;
+    test_file(fs, new, test);	/* Bad cluster check */
+}
+
+static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp);
+
+static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp)
+{
+    DOS_FILE **chain;
+    int i;
+    unsigned long clu_num;
+
+    chain = &this->first;
+    i = 0;
+    clu_num = FSTART(this, fs);
+    new_dir();
+    while (clu_num > 0 && clu_num != -1) {
+	add_file(fs, &chain, this,
+		 cluster_start(fs, clu_num) + (i % fs->cluster_size), cp);
+	i += sizeof(DIR_ENT);
+	if (!(i % fs->cluster_size))
+	    if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
+		break;
+    }
+    lfn_check_orphaned();
+    if (check_dir(fs, &this->first, this->offset))
+	return 0;
+    if (check_files(fs, this->first))
+	return 1;
+    return subdirs(fs, this, cp);
+}
+
+/**
+ * Recursively scan subdirectories of the specified parent directory.
+ *
+ * @param[inout]    fs      Information about the filesystem
+ * @param[in]       parent  Identifies the directory to scan
+ * @param[in]       cp
+ *
+ * @return  0   Success
+ * @return  1   Error
+ */
+static int subdirs(DOS_FS * fs, DOS_FILE * parent, FDSC ** cp)
+{
+    DOS_FILE *walk;
+
+    for (walk = parent ? parent->first : root; walk; walk = walk->next)
+	if (walk->dir_ent.attr & ATTR_DIR)
+	    if (strncmp((const char *)walk->dir_ent.name, MSDOS_DOT, MSDOS_NAME)
+		&& strncmp((const char *)walk->dir_ent.name, MSDOS_DOTDOT,
+			   MSDOS_NAME))
+		if (scan_dir(fs, walk, file_cd(cp, (char *)walk->dir_ent.name)))
+		    return 1;
+    return 0;
+}
+
+/**
+ * Scan all directory and file information for errors.
+ *
+ * @param[inout]    fs      Information about the filesystem
+ *
+ * @return  0   Success
+ * @return  1   Error
+ */
+int scan_root(DOS_FS * fs)
+{
+    DOS_FILE **chain;
+    int i;
+
+    root = NULL;
+    chain = &root;
+    new_dir();
+    if (fs->root_cluster) {
+	add_file(fs, &chain, NULL, 0, &fp_root);
+    } else {
+	for (i = 0; i < fs->root_entries; i++)
+	    add_file(fs, &chain, NULL, fs->root_start + i * sizeof(DIR_ENT),
+		     &fp_root);
+    }
+    lfn_check_orphaned();
+    (void)check_dir(fs, &root, 0);
+    if (check_files(fs, root))
+	return 1;
+    return subdirs(fs, NULL, &fp_root);
+}
diff --git a/dosfstools/src/check.h b/dosfstools/src/check.h
new file mode 100644
index 0000000..277c44b
--- /dev/null
+++ b/dosfstools/src/check.h
@@ -0,0 +1,39 @@
+/* check.h - Check and repair a PC/MS-DOS file system
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+#ifndef _CHECK_H
+#define _CHECK_H
+
+loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern);
+
+/* Allocate a free slot in the root directory for a new file. The file name is
+   constructed after 'pattern', which must include a %d type format for printf
+   and expand to exactly 11 characters. The name actually used is written into
+   the 'de' structure, the rest of *de is cleared. The offset returned is to
+   where in the filesystem the entry belongs. */
+
+int scan_root(DOS_FS * fs);
+
+/* Scans the root directory and recurses into all subdirectories. See check.c
+   for all the details. Returns a non-zero integer if the file system has to
+   be checked again. */
+
+#endif
diff --git a/dosfstools/src/common.c b/dosfstools/src/common.c
new file mode 100644
index 0000000..51605a2
--- /dev/null
+++ b/dosfstools/src/common.c
@@ -0,0 +1,118 @@
+/* common.c - Common functions
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
+ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "common.h"
+
+typedef struct _link {
+    void *data;
+    struct _link *next;
+} LINK;
+
+void die(char *msg, ...)
+{
+    va_list args;
+
+    va_start(args, msg);
+    vfprintf(stderr, msg, args);
+    va_end(args);
+    fprintf(stderr, "\n");
+    exit(1);
+}
+
+void pdie(char *msg, ...)
+{
+    va_list args;
+
+    va_start(args, msg);
+    vfprintf(stderr, msg, args);
+    va_end(args);
+    fprintf(stderr, ":%s\n", strerror(errno));
+    exit(1);
+}
+
+void *alloc(int size)
+{
+    void *this;
+
+    if ((this = malloc(size)))
+	return this;
+    pdie("malloc");
+    return NULL;		/* for GCC */
+}
+
+void *qalloc(void **root, int size)
+{
+    LINK *link;
+
+    link = alloc(sizeof(LINK));
+    link->next = *root;
+    *root = link;
+    return link->data = alloc(size);
+}
+
+void qfree(void **root)
+{
+    LINK *this;
+
+    while (*root) {
+	this = (LINK *) * root;
+	*root = this->next;
+	free(this->data);
+	free(this);
+    }
+}
+
+int min(int a, int b)
+{
+    return a < b ? a : b;
+}
+
+char get_key(char *valid, char *prompt)
+{
+    int ch, okay;
+
+    while (1) {
+	if (prompt)
+	    printf("%s ", prompt);
+	fflush(stdout);
+	while (ch = getchar(), ch == ' ' || ch == '\t') ;
+	if (ch == EOF)
+	    exit(1);
+	if (!strchr(valid, okay = ch))
+	    okay = 0;
+	while (ch = getchar(), ch != '\n' && ch != EOF) ;
+	if (ch == EOF)
+	    exit(1);
+	if (okay)
+	    return okay;
+	printf("Invalid input.\n");
+    }
+}
diff --git a/dosfstools/src/common.h b/dosfstools/src/common.h
new file mode 100644
index 0000000..395eabb
--- /dev/null
+++ b/dosfstools/src/common.h
@@ -0,0 +1,57 @@
+/* common.h - Common functions
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+#include <asm/types.h>
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+void die(char *msg, ...) __attribute((noreturn));
+
+/* Displays a prinf-style message and terminates the program. */
+
+void pdie(char *msg, ...) __attribute((noreturn));
+
+/* Like die, but appends an error message according to the state of errno. */
+
+void *alloc(int size);
+
+/* mallocs SIZE bytes and returns a pointer to the data. Terminates the program
+   if malloc fails. */
+
+void *qalloc(void **root, int size);
+
+/* Like alloc, but registers the data area in a list described by ROOT. */
+
+void qfree(void **root);
+
+/* Deallocates all qalloc'ed data areas described by ROOT. */
+
+int min(int a, int b);
+
+/* Returns the smaller integer value of a and b. */
+
+char get_key(char *valid, char *prompt);
+
+/* Displays PROMPT and waits for user input. Only characters in VALID are
+   accepted. Terminates the program on EOF. Returns the character. */
+
+#endif
diff --git a/dosfstools/src/dosfsck.c b/dosfstools/src/dosfsck.c
new file mode 100644
index 0000000..a7a59e1
--- /dev/null
+++ b/dosfstools/src/dosfsck.c
@@ -0,0 +1,224 @@
+/* dosfsck.c - User interface
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
+ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+
+#include "version.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "common.h"
+#include "dosfsck.h"
+#include "io.h"
+#include "boot.h"
+#include "fat.h"
+#include "file.h"
+#include "check.h"
+
+int interactive = 0, rw = 0, list = 0, test = 0, verbose = 0, write_immed = 0;
+int atari_format = 0;
+unsigned n_files = 0;
+void *mem_queue = NULL;
+
+#ifdef USE_ANDROID_RETVALS
+unsigned retandroid = 1;
+#else
+unsigned retandroid = 0;
+#endif
+
+static void usage(char *name)
+{
+    fprintf(stderr, "usage: %s [-aAflrtvVwy] [-d path -d ...] "
+	    "[-u path -u ...]\n%15sdevice\n", name, "");
+    fprintf(stderr, "  -a       automatically repair the file system\n");
+    fprintf(stderr, "  -A       toggle Atari file system format\n");
+    fprintf(stderr, "  -d path  drop that file\n");
+    fprintf(stderr, "  -f       ignored\n");
+    fprintf(stderr, "  -l       list path names\n");
+    fprintf(stderr,
+	    "  -n       no-op, check non-interactively without changing\n");
+    fprintf(stderr, "  -p       same as -a, for compat with other *fsck\n");
+    fprintf(stderr, "  -r       interactively repair the file system\n");
+    fprintf(stderr, "  -t       test for bad clusters\n");
+    fprintf(stderr, "  -u path  try to undelete that (non-directory) file\n");
+    fprintf(stderr, "  -v       verbose mode\n");
+    fprintf(stderr, "  -V       perform a verification pass\n");
+    fprintf(stderr, "  -w       write changes to disk immediately\n");
+    fprintf(stderr, "  -y       same as -a, for compat with other *fsck\n");
+	if (retandroid) {
+		exit(1);
+	} else {
+    	exit(2);
+	}
+}
+
+/*
+ * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant
+ * of MS-DOS filesystem by default.
+ */
+static void check_atari(void)
+{
+#ifdef __mc68000__
+    FILE *f;
+    char line[128], *p;
+
+    if (!(f = fopen("/proc/hardware", "r"))) {
+	perror("/proc/hardware");
+	return;
+    }
+
+    while (fgets(line, sizeof(line), f)) {
+	if (strncmp(line, "Model:", 6) == 0) {
+	    p = line + 6;
+	    p += strspn(p, " \t");
+	    if (strncmp(p, "Atari ", 6) == 0)
+		atari_format = 1;
+	    break;
+	}
+    }
+    fclose(f);
+#endif
+}
+
+int main(int argc, char **argv)
+{
+    DOS_FS fs;
+    int salvage_files, verify, c;
+    unsigned n_files_check = 0, n_files_verify = 0;
+    unsigned long free_clusters;
+
+    memset(&fs, 0, sizeof(fs));
+    rw = salvage_files = verify = 0;
+    interactive = 1;
+    check_atari();
+
+    while ((c = getopt(argc, argv, "Aad:flnprtu:vVwy")) != EOF)
+	switch (c) {
+	case 'A':		/* toggle Atari format */
+	    atari_format = !atari_format;
+	    break;
+	case 'a':
+	case 'p':
+	case 'y':
+	    rw = 1;
+	    interactive = 0;
+	    salvage_files = 1;
+	    break;
+	case 'd':
+	    file_add(optarg, fdt_drop);
+	    break;
+	case 'f':
+	    salvage_files = 1;
+	    break;
+	case 'l':
+	    list = 1;
+	    break;
+	case 'n':
+	    rw = 0;
+	    interactive = 0;
+	    break;
+	case 'r':
+	    rw = 1;
+	    interactive = 1;
+	    break;
+	case 't':
+	    test = 1;
+	    break;
+	case 'u':
+	    file_add(optarg, fdt_undelete);
+	    break;
+	case 'v':
+	    verbose = 1;
+	    printf("dosfsck " VERSION " (" VERSION_DATE ")\n");
+	    break;
+	case 'V':
+	    verify = 1;
+	    break;
+	case 'w':
+	    write_immed = 1;
+	    break;
+	default:
+	    usage(argv[0]);
+	}
+    if ((test || write_immed) && !rw) {
+	fprintf(stderr, "-t and -w require -a or -r\n");
+	if (retandroid) {
+		exit(1);
+	} else {
+		exit(2);
+	}
+    }
+    if (optind != argc - 1)
+	usage(argv[0]);
+
+    printf("dosfsck " VERSION ", " VERSION_DATE ", FAT32, LFN\n");
+    fs_open(argv[optind], rw);
+    read_boot(&fs);
+    if (verify)
+	printf("Starting check/repair pass.\n");
+    while (read_fat(&fs), scan_root(&fs))
+	qfree(&mem_queue);
+    if (test)
+	fix_bad(&fs);
+    if (salvage_files && 0)
+	reclaim_file(&fs);
+    else
+	reclaim_free(&fs);
+    free_clusters = update_free(&fs);
+    file_unused();
+    qfree(&mem_queue);
+    n_files_check = n_files;
+    if (verify) {
+	n_files = 0;
+	printf("Starting verification pass.\n");
+	read_fat(&fs);
+	scan_root(&fs);
+	reclaim_free(&fs);
+	qfree(&mem_queue);
+	n_files_verify = n_files;
+    }
+
+    if (fs_changed()) {
+	if (rw) {
+	    if (interactive)
+		rw = get_key("yn", "Perform changes ? (y/n)") == 'y';
+	    else
+		printf("Performing changes.\n");
+	} else
+	    printf("Leaving file system unchanged.\n");
+    }
+
+    printf("%s: %u files, %lu/%lu clusters\n", argv[optind],
+	   n_files, fs.clusters - free_clusters, fs.clusters);
+
+	if (retandroid) {
+		return fs_close(rw) ? 4 : 0;
+	} else {
+    	return fs_close(rw) ? 1 : 0;
+	}
+}
diff --git a/dosfstools/src/dosfsck.h b/dosfstools/src/dosfsck.h
new file mode 100644
index 0000000..6f53c72
--- /dev/null
+++ b/dosfstools/src/dosfsck.h
@@ -0,0 +1,218 @@
+/* dosfsck.h  -  Common data structures and global variables
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
+ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+
+#ifndef _DOSFSCK_H
+#define _DOSFSCK_H
+
+#include <sys/types.h>
+#define _LINUX_STAT_H		/* hack to avoid inclusion of <linux/stat.h> */
+#define _LINUX_STRING_H_	/* hack to avoid inclusion of <linux/string.h> */
+#define _LINUX_FS_H		/* hack to avoid inclusion of <linux/fs.h> */
+
+#ifdef _USING_BIONIC_
+#include <sys/endian.h>
+#endif
+
+#include <asm/types.h>
+#include <asm/byteorder.h>
+
+#include <linux/msdos_fs.h>
+
+#undef CF_LE_W
+#undef CF_LE_L
+#undef CT_LE_W
+#undef CT_LE_L
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#include <byteswap.h>
+#define CF_LE_W(v) bswap_16(v)
+#define CF_LE_L(v) bswap_32(v)
+#define CT_LE_W(v) CF_LE_W(v)
+#define CT_LE_L(v) CF_LE_L(v)
+#else
+#define CF_LE_W(v) (v)
+#define CF_LE_L(v) (v)
+#define CT_LE_W(v) (v)
+#define CT_LE_L(v) (v)
+#endif /* __BIG_ENDIAN */
+
+#define VFAT_LN_ATTR (ATTR_RO | ATTR_HIDDEN | ATTR_SYS | ATTR_VOLUME)
+
+/* ++roman: Use own definition of boot sector structure -- the kernel headers'
+ * name for it is msdos_boot_sector in 2.0 and fat_boot_sector in 2.1 ... */
+struct boot_sector {
+    __u8 ignored[3];		/* Boot strap short or near jump */
+    __u8 system_id[8];		/* Name - can be used to special case
+				   partition manager volumes */
+    __u8 sector_size[2];	/* bytes per logical sector */
+    __u8 cluster_size;		/* sectors/cluster */
+    __u16 reserved;		/* reserved sectors */
+    __u8 fats;			/* number of FATs */
+    __u8 dir_entries[2];	/* root directory entries */
+    __u8 sectors[2];		/* number of sectors */
+    __u8 media;			/* media code (unused) */
+    __u16 fat_length;		/* sectors/FAT */
+    __u16 secs_track;		/* sectors per track */
+    __u16 heads;		/* number of heads */
+    __u32 hidden;		/* hidden sectors (unused) */
+    __u32 total_sect;		/* number of sectors (if sectors == 0) */
+
+    /* The following fields are only used by FAT32 */
+    __u32 fat32_length;		/* sectors/FAT */
+    __u16 flags;		/* bit 8: fat mirroring, low 4: active fat */
+    __u8 version[2];		/* major, minor filesystem version */
+    __u32 root_cluster;		/* first cluster in root directory */
+    __u16 info_sector;		/* filesystem info sector */
+    __u16 backup_boot;		/* backup boot sector */
+    __u8 reserved2[12];		/* Unused */
+
+    __u8 drive_number;		/* Logical Drive Number */
+    __u8 reserved3;		/* Unused */
+
+    __u8 extended_sig;		/* Extended Signature (0x29) */
+    __u32 serial;		/* Serial number */
+    __u8 label[11];		/* FS label */
+    __u8 fs_type[8];		/* FS Type */
+
+    /* fill up to 512 bytes */
+    __u8 junk[422];
+} __attribute__ ((packed));
+
+struct boot_sector_16 {
+    __u8 ignored[3];		/* Boot strap short or near jump */
+    __u8 system_id[8];		/* Name - can be used to special case
+				   partition manager volumes */
+    __u8 sector_size[2];	/* bytes per logical sector */
+    __u8 cluster_size;		/* sectors/cluster */
+    __u16 reserved;		/* reserved sectors */
+    __u8 fats;			/* number of FATs */
+    __u8 dir_entries[2];	/* root directory entries */
+    __u8 sectors[2];		/* number of sectors */
+    __u8 media;			/* media code (unused) */
+    __u16 fat_length;		/* sectors/FAT */
+    __u16 secs_track;		/* sectors per track */
+    __u16 heads;		/* number of heads */
+    __u32 hidden;		/* hidden sectors (unused) */
+    __u32 total_sect;		/* number of sectors (if sectors == 0) */
+
+    __u8 drive_number;		/* Logical Drive Number */
+    __u8 reserved2;		/* Unused */
+
+    __u8 extended_sig;		/* Extended Signature (0x29) */
+    __u32 serial;		/* Serial number */
+    __u8 label[11];		/* FS label */
+    __u8 fs_type[8];		/* FS Type */
+
+    /* fill up to 512 bytes */
+    __u8 junk[450];
+} __attribute__ ((packed));
+
+struct info_sector {
+    __u32 magic;		/* Magic for info sector ('RRaA') */
+    __u8 junk[0x1dc];
+    __u32 reserved1;		/* Nothing as far as I can tell */
+    __u32 signature;		/* 0x61417272 ('rrAa') */
+    __u32 free_clusters;	/* Free cluster count.  -1 if unknown */
+    __u32 next_cluster;		/* Most recently allocated cluster. */
+    __u32 reserved2[3];
+    __u16 reserved3;
+    __u16 boot_sign;
+};
+
+typedef struct {
+    __u8 name[8], ext[3];	/* name and extension */
+    __u8 attr;			/* attribute bits */
+    __u8 lcase;			/* Case for base and extension */
+    __u8 ctime_ms;		/* Creation time, milliseconds */
+    __u16 ctime;		/* Creation time */
+    __u16 cdate;		/* Creation date */
+    __u16 adate;		/* Last access date */
+    __u16 starthi;		/* High 16 bits of cluster in FAT32 */
+    __u16 time, date, start;	/* time, date and first cluster */
+    __u32 size;			/* file size (in bytes) */
+} __attribute__ ((packed)) DIR_ENT;
+
+typedef struct _dos_file {
+    DIR_ENT dir_ent;
+    char *lfn;
+    loff_t offset;
+    loff_t lfn_offset;
+    struct _dos_file *parent;	/* parent directory */
+    struct _dos_file *next;	/* next entry */
+    struct _dos_file *first;	/* first entry (directory only) */
+} DOS_FILE;
+
+typedef struct {
+    unsigned long value;
+    unsigned long reserved;
+} FAT_ENTRY;
+
+typedef struct {
+    int nfats;
+    loff_t fat_start;
+    unsigned int fat_size;	/* unit is bytes */
+    unsigned int fat_bits;	/* size of a FAT entry */
+    unsigned int eff_fat_bits;	/* # of used bits in a FAT entry */
+    unsigned long root_cluster;	/* 0 for old-style root dir */
+    loff_t root_start;
+    unsigned int root_entries;
+    loff_t data_start;
+    unsigned int cluster_size;
+    unsigned long clusters;
+    loff_t fsinfo_start;	/* 0 if not present */
+    long free_clusters;
+    loff_t backupboot_start;	/* 0 if not present */
+    unsigned char *fat;
+    DOS_FILE **cluster_owner;
+    char *label;
+} DOS_FS;
+
+#ifndef offsetof
+#define offsetof(t,e)	((int)&(((t *)0)->e))
+#endif
+
+extern int interactive, rw, list, verbose, test, write_immed;
+extern int atari_format;
+extern unsigned n_files;
+extern void *mem_queue;
+extern unsigned retandroid;
+
+/* value to use as end-of-file marker */
+#define FAT_EOF(fs)	((atari_format ? 0xfff : 0xff8) | FAT_EXTD(fs))
+#define FAT_IS_EOF(fs,v) ((unsigned long)(v) >= (0xff8|FAT_EXTD(fs)))
+/* value to mark bad clusters */
+#define FAT_BAD(fs)	(0xff7 | FAT_EXTD(fs))
+/* range of values used for bad clusters */
+#define FAT_MIN_BAD(fs)	((atari_format ? 0xff0 : 0xff7) | FAT_EXTD(fs))
+#define FAT_MAX_BAD(fs)	((atari_format ? 0xff7 : 0xff7) | FAT_EXTD(fs))
+#define FAT_IS_BAD(fs,v) ((v) >= FAT_MIN_BAD(fs) && (v) <= FAT_MAX_BAD(fs))
+
+/* return -16 as a number with fs->fat_bits bits */
+#define FAT_EXTD(fs)	(((1 << fs->eff_fat_bits)-1) & ~0xf)
+
+/* marker for files with no 8.3 name */
+#define FAT_NO_83NAME 32
+
+#endif
diff --git a/dosfstools/src/dosfslabel.c b/dosfstools/src/dosfslabel.c
new file mode 100644
index 0000000..5e2d282
--- /dev/null
+++ b/dosfstools/src/dosfslabel.c
@@ -0,0 +1,128 @@
+/* dosfslabel.c - User interface
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+   Copyright (C) 2007 Red Hat, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+#include "version.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#ifdef _USING_BIONIC_
+#include <linux/fs.h>
+#endif
+
+#include "common.h"
+#include "dosfsck.h"
+#include "io.h"
+#include "boot.h"
+#include "fat.h"
+#include "file.h"
+#include "check.h"
+
+int interactive = 0, rw = 0, list = 0, test = 0, verbose = 0, write_immed = 0;
+int atari_format = 0;
+unsigned n_files = 0;
+void *mem_queue = NULL;
+
+static void usage(int error)
+{
+    FILE *f = error ? stderr : stdout;
+    int status = error ? 1 : 0;
+
+    fprintf(f, "usage: dosfslabel device [label]\n");
+    exit(status);
+}
+
+/*
+ * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant
+ * of MS-DOS filesystem by default.
+ */
+static void check_atari(void)
+{
+#ifdef __mc68000__
+    FILE *f;
+    char line[128], *p;
+
+    if (!(f = fopen("/proc/hardware", "r"))) {
+	perror("/proc/hardware");
+	return;
+    }
+
+    while (fgets(line, sizeof(line), f)) {
+	if (strncmp(line, "Model:", 6) == 0) {
+	    p = line + 6;
+	    p += strspn(p, " \t");
+	    if (strncmp(p, "Atari ", 6) == 0)
+		atari_format = 1;
+	    break;
+	}
+    }
+    fclose(f);
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+    DOS_FS fs;
+    rw = 0;
+
+    char *device = NULL;
+    char *label = NULL;
+
+    check_atari();
+
+    if (argc < 2 || argc > 3)
+	usage(1);
+
+    if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+	usage(0);
+    else if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) {
+	printf("dosfslabel " VERSION ", " VERSION_DATE ", FAT32, LFN\n");
+	exit(0);
+    }
+
+    device = argv[1];
+    if (argc == 3) {
+	label = argv[2];
+	if (strlen(label) > 11) {
+	    fprintf(stderr,
+		    "dosfslabel: labels can be no longer than 11 characters\n");
+	    exit(1);
+	}
+	rw = 1;
+    }
+
+    fs_open(device, rw);
+    read_boot(&fs);
+    if (!rw) {
+	fprintf(stdout, "%s\n", fs.label);
+	exit(0);
+    }
+
+    write_label(&fs, label);
+    fs_close(rw);
+    return 0;
+}
diff --git a/dosfstools/src/fat.c b/dosfstools/src/fat.c
new file mode 100644
index 0000000..5a0dfb0
--- /dev/null
+++ b/dosfstools/src/fat.c
@@ -0,0 +1,547 @@
+/* fat.c - Read/write access to the FAT
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
+ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "dosfsck.h"
+#include "io.h"
+#include "check.h"
+#include "fat.h"
+
+/**
+ * Fetch the FAT entry for a specified cluster.
+ *
+ * @param[out]  entry	    Cluster to which cluster of interest is linked
+ * @param[in]	fat	    FAT table for the partition
+ * @param[in]	cluster     Cluster of interest
+ * @param[in]	fs          Information from the FAT boot sectors (bits per FAT entry)
+ */
+void get_fat(FAT_ENTRY * entry, void *fat, unsigned long cluster, DOS_FS * fs)
+{
+    unsigned char *ptr;
+
+    switch (fs->fat_bits) {
+    case 12:
+	ptr = &((unsigned char *)fat)[cluster * 3 / 2];
+	entry->value = 0xfff & (cluster & 1 ? (ptr[0] >> 4) | (ptr[1] << 4) :
+				(ptr[0] | ptr[1] << 8));
+	break;
+    case 16:
+	entry->value = CF_LE_W(((unsigned short *)fat)[cluster]);
+	break;
+    case 32:
+	/* According to M$, the high 4 bits of a FAT32 entry are reserved and
+	 * are not part of the cluster number. So we cut them off. */
+	{
+	    unsigned long e = CF_LE_L(((unsigned int *)fat)[cluster]);
+	    entry->value = e & 0xfffffff;
+	    entry->reserved = e >> 28;
+	}
+	break;
+    default:
+	die("Bad FAT entry size: %d bits.", fs->fat_bits);
+    }
+}
+
+/**
+ * Build a bookkeeping structure from the partition's FAT table.
+ * If the partition has multiple FATs and they don't agree, try to pick a winner,
+ * and queue a command to overwrite the loser.
+ * One error that is fixed here is a cluster that links to something out of range.
+ *
+ * @param[inout]    fs      Information about the filesystem
+ */
+void read_fat(DOS_FS * fs)
+{
+    int eff_size;
+    unsigned long i;
+    void *first, *second = NULL;
+    int first_ok, second_ok;
+    unsigned long total_num_clusters;
+
+    /* Clean up from previous pass */
+    free(fs->fat);
+    free(fs->cluster_owner);
+    fs->fat = NULL;
+    fs->cluster_owner = NULL;
+
+    total_num_clusters = fs->clusters + 2UL;
+    eff_size = (total_num_clusters * fs->fat_bits + 7) / 8ULL;
+    first = alloc(eff_size);
+    fs_read(fs->fat_start, eff_size, first);
+    if (fs->nfats > 1) {
+	second = alloc(eff_size);
+	fs_read(fs->fat_start + fs->fat_size, eff_size, second);
+    }
+    if (second && memcmp(first, second, eff_size) != 0) {
+	FAT_ENTRY first_media, second_media;
+	get_fat(&first_media, first, 0, fs);
+	get_fat(&second_media, second, 0, fs);
+	first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
+	second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
+	if (first_ok && !second_ok) {
+	    printf("FATs differ - using first FAT.\n");
+	    fs_write(fs->fat_start + fs->fat_size, eff_size, first);
+	}
+	if (!first_ok && second_ok) {
+	    printf("FATs differ - using second FAT.\n");
+	    fs_write(fs->fat_start, eff_size, second);
+	    memcpy(first, second, eff_size);
+	}
+	if (first_ok && second_ok) {
+	    if (interactive) {
+		printf("FATs differ but appear to be intact. Use which FAT ?\n"
+		       "1) Use first FAT\n2) Use second FAT\n");
+		if (get_key("12", "?") == '1') {
+		    fs_write(fs->fat_start + fs->fat_size, eff_size, first);
+		} else {
+		    fs_write(fs->fat_start, eff_size, second);
+		    memcpy(first, second, eff_size);
+		}
+	    } else {
+		printf("FATs differ but appear to be intact. Using first "
+		       "FAT.\n");
+		fs_write(fs->fat_start + fs->fat_size, eff_size, first);
+	    }
+	}
+	if (!first_ok && !second_ok) {
+	    printf("Both FATs appear to be corrupt. Giving up.\n");
+	    exit(1);
+	}
+    }
+    if (second) {
+	free(second);
+    }
+    fs->fat = (unsigned char *)first;
+
+    fs->cluster_owner = alloc(total_num_clusters * sizeof(DOS_FILE *));
+    memset(fs->cluster_owner, 0, (total_num_clusters * sizeof(DOS_FILE *)));
+
+    /* Truncate any cluster chains that link to something out of range */
+    for (i = 2; i < fs->clusters + 2; i++) {
+	FAT_ENTRY curEntry;
+	get_fat(&curEntry, fs->fat, i, fs);
+	if (curEntry.value == 1) {
+	    printf("Cluster %ld out of range (1). Setting to EOF.\n", i - 2);
+	    set_fat(fs, i, -1);
+	}
+	if (curEntry.value >= fs->clusters + 2 &&
+	    (curEntry.value < FAT_MIN_BAD(fs))) {
+	    printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n",
+		   i - 2, curEntry.value, fs->clusters + 2 - 1);
+	    set_fat(fs, i, -1);
+	}
+    }
+}
+
+/**
+ * Update the FAT entry for a specified cluster
+ * (i.e., change the cluster it links to).
+ * Queue a command to write out this change.
+ *
+ * @param[in,out]   fs          Information about the filesystem
+ * @param[in]	    cluster     Cluster to change
+ * @param[in]       new	        Cluster to link to
+ *				Special values:
+ *				   0 == free cluster
+ *				  -1 == end-of-chain
+ *				  -2 == bad cluster
+ */
+void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new)
+{
+    unsigned char *data = NULL;
+    int size;
+    loff_t offs;
+
+    if ((long)new == -1)
+	new = FAT_EOF(fs);
+    else if ((long)new == -2)
+	new = FAT_BAD(fs);
+    switch (fs->fat_bits) {
+    case 12:
+	data = fs->fat + cluster * 3 / 2;
+	offs = fs->fat_start + cluster * 3 / 2;
+	if (cluster & 1) {
+	    FAT_ENTRY prevEntry;
+	    get_fat(&prevEntry, fs->fat, cluster - 1, fs);
+	    data[0] = ((new & 0xf) << 4) | (prevEntry.value >> 8);
+	    data[1] = new >> 4;
+	} else {
+	    FAT_ENTRY subseqEntry;
+	    get_fat(&subseqEntry, fs->fat, cluster + 1, fs);
+	    data[0] = new & 0xff;
+	    data[1] = (new >> 8) | (cluster == fs->clusters - 1 ? 0 :
+				    (0xff & subseqEntry.value) << 4);
+	}
+	size = 2;
+	break;
+    case 16:
+	data = fs->fat + cluster * 2;
+	offs = fs->fat_start + cluster * 2;
+	*(unsigned short *)data = CT_LE_W(new);
+	size = 2;
+	break;
+    case 32:
+	{
+	    FAT_ENTRY curEntry;
+	    get_fat(&curEntry, fs->fat, cluster, fs);
+
+	    data = fs->fat + cluster * 4;
+	    offs = fs->fat_start + cluster * 4;
+	    /* According to M$, the high 4 bits of a FAT32 entry are reserved and
+	     * are not part of the cluster number. So we never touch them. */
+	    *(unsigned long *)data = CT_LE_L((new & 0xfffffff) |
+					     (curEntry.reserved << 28));
+	    size = 4;
+	}
+	break;
+    default:
+	die("Bad FAT entry size: %d bits.", fs->fat_bits);
+    }
+    fs_write(offs, size, data);
+    if (fs->nfats > 1) {
+	fs_write(offs + fs->fat_size, size, data);
+    }
+}
+
+int bad_cluster(DOS_FS * fs, unsigned long cluster)
+{
+    FAT_ENTRY curEntry;
+    get_fat(&curEntry, fs->fat, cluster, fs);
+
+    return FAT_IS_BAD(fs, curEntry.value);
+}
+
+/**
+ * Get the cluster to which the specified cluster is linked.
+ * If the linked cluster is marked bad, abort.
+ *
+ * @param[in]   fs          Information about the filesystem
+ * @param[in]	cluster     Cluster to follow
+ *
+ * @return  -1              'cluster' is at the end of the chain
+ * @return  Other values    Next cluster in this chain
+ */
+unsigned long next_cluster(DOS_FS * fs, unsigned long cluster)
+{
+    unsigned long value;
+    FAT_ENTRY curEntry;
+
+    get_fat(&curEntry, fs->fat, cluster, fs);
+
+    value = curEntry.value;
+    if (FAT_IS_BAD(fs, value))
+	die("Internal error: next_cluster on bad cluster");
+    return FAT_IS_EOF(fs, value) ? -1 : value;
+}
+
+loff_t cluster_start(DOS_FS * fs, unsigned long cluster)
+{
+    return fs->data_start + ((loff_t) cluster -
+			     2) * (unsigned long long)fs->cluster_size;
+}
+
+/**
+ * Update internal bookkeeping to show that the specified cluster belongs
+ * to the specified dentry.
+ *
+ * @param[in,out]   fs          Information about the filesystem
+ * @param[in]	    cluster     Cluster being assigned
+ * @param[in]	    owner       Information on dentry that owns this cluster
+ *                              (may be NULL)
+ */
+void set_owner(DOS_FS * fs, unsigned long cluster, DOS_FILE * owner)
+{
+    if (fs->cluster_owner == NULL)
+	die("Internal error: attempt to set owner in non-existent table");
+
+    if (owner && fs->cluster_owner[cluster]
+	&& (fs->cluster_owner[cluster] != owner))
+	die("Internal error: attempt to change file owner");
+    fs->cluster_owner[cluster] = owner;
+}
+
+DOS_FILE *get_owner(DOS_FS * fs, unsigned long cluster)
+{
+    if (fs->cluster_owner == NULL)
+	return NULL;
+    else
+	return fs->cluster_owner[cluster];
+}
+
+void fix_bad(DOS_FS * fs)
+{
+    unsigned long i;
+
+    if (verbose)
+	printf("Checking for bad clusters.\n");
+    for (i = 2; i < fs->clusters + 2; i++) {
+	FAT_ENTRY curEntry;
+	get_fat(&curEntry, fs->fat, i, fs);
+
+	if (!get_owner(fs, i) && !FAT_IS_BAD(fs, curEntry.value))
+	    if (!fs_test(cluster_start(fs, i), fs->cluster_size)) {
+		printf("Cluster %lu is unreadable.\n", i);
+		set_fat(fs, i, -2);
+	    }
+    }
+}
+
+void reclaim_free(DOS_FS * fs)
+{
+    int reclaimed;
+    unsigned long i;
+
+    if (verbose)
+	printf("Checking for unused clusters.\n");
+    reclaimed = 0;
+    for (i = 2; i < fs->clusters + 2; i++) {
+	FAT_ENTRY curEntry;
+	get_fat(&curEntry, fs->fat, i, fs);
+
+	if (!get_owner(fs, i) && curEntry.value &&
+	    !FAT_IS_BAD(fs, curEntry.value)) {
+	    set_fat(fs, i, 0);
+	    reclaimed++;
+	}
+    }
+    if (reclaimed)
+	printf("Reclaimed %d unused cluster%s (%llu bytes).\n", reclaimed,
+	       reclaimed == 1 ? "" : "s",
+	       (unsigned long long)reclaimed * fs->cluster_size);
+}
+
+/**
+ * Assign the specified owner to all orphan chains (except cycles).
+ * Break cross-links between orphan chains.
+ *
+ * @param[in,out]   fs             Information about the filesystem
+ * @param[in]	    owner          dentry to be assigned ownership of orphans
+ * @param[in,out]   num_refs	   For each orphan cluster [index], how many
+ *				   clusters link to it.
+ * @param[in]	    start_cluster  Where to start scanning for orphans
+ */
+static void tag_free(DOS_FS * fs, DOS_FILE * owner, unsigned long *num_refs,
+		     unsigned long start_cluster)
+{
+    int prev;
+    unsigned long i, walk;
+
+    if (start_cluster == 0)
+	start_cluster = 2;
+
+    for (i = start_cluster; i < fs->clusters + 2; i++) {
+	FAT_ENTRY curEntry;
+	get_fat(&curEntry, fs->fat, i, fs);
+
+	/* If the current entry is the head of an un-owned chain... */
+	if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) &&
+	    !get_owner(fs, i) && !num_refs[i]) {
+	    prev = 0;
+	    /* Walk the chain, claiming ownership as we go */
+	    for (walk = i; walk != -1; walk = next_cluster(fs, walk)) {
+		if (!get_owner(fs, walk)) {
+		    set_owner(fs, walk, owner);
+		} else {
+		    /* We've run into cross-links between orphaned chains,
+		     * or a cycle with a tail.
+		     * Terminate this orphan chain (break the link)
+		     */
+		    set_fat(fs, prev, -1);
+
+		    /* This is not necessary because 'walk' is owned and thus
+		     * will never become the head of a chain (the only case
+		     * that would matter during reclaim to files).
+		     * It's easier to decrement than to prove that it's
+		     * unnecessary.
+		     */
+		    num_refs[walk]--;
+		    break;
+		}
+		prev = walk;
+	    }
+	}
+    }
+}
+
+/**
+ * Recover orphan chains to files, handling any cycles or cross-links.
+ *
+ * @param[in,out]   fs             Information about the filesystem
+ */
+void reclaim_file(DOS_FS * fs)
+{
+    DOS_FILE orphan;
+    int reclaimed, files;
+    int changed = 0;
+    unsigned long i, next, walk;
+    unsigned long *num_refs = NULL;	/* Only for orphaned clusters */
+    unsigned long total_num_clusters;
+
+    if (verbose)
+	printf("Reclaiming unconnected clusters.\n");
+
+    total_num_clusters = fs->clusters + 2UL;
+    num_refs = alloc(total_num_clusters * sizeof(unsigned long));
+    memset(num_refs, 0, (total_num_clusters * sizeof(unsigned long)));
+
+    /* Guarantee that all orphan chains (except cycles) end cleanly
+     * with an end-of-chain mark.
+     */
+
+    for (i = 2; i < total_num_clusters; i++) {
+	FAT_ENTRY curEntry;
+	get_fat(&curEntry, fs->fat, i, fs);
+
+	next = curEntry.value;
+	if (!get_owner(fs, i) && next && next < fs->clusters + 2) {
+	    /* Cluster is linked, but not owned (orphan) */
+	    FAT_ENTRY nextEntry;
+	    get_fat(&nextEntry, fs->fat, next, fs);
+
+	    /* Mark it end-of-chain if it links into an owned cluster,
+	     * a free cluster, or a bad cluster.
+	     */
+	    if (get_owner(fs, next) || !nextEntry.value ||
+		FAT_IS_BAD(fs, nextEntry.value))
+		set_fat(fs, i, -1);
+	    else
+		num_refs[next]++;
+	}
+    }
+
+    /* Scan until all the orphans are accounted for,
+     * and all cycles and cross-links are broken
+     */
+    do {
+	tag_free(fs, &orphan, num_refs, changed);
+	changed = 0;
+
+	/* Any unaccounted-for orphans must be part of a cycle */
+	for (i = 2; i < total_num_clusters; i++) {
+	    FAT_ENTRY curEntry;
+	    get_fat(&curEntry, fs->fat, i, fs);
+
+	    if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) &&
+		!get_owner(fs, i)) {
+		if (!num_refs[curEntry.value]--)
+		    die("Internal error: num_refs going below zero");
+		set_fat(fs, i, -1);
+		changed = curEntry.value;
+		printf("Broke cycle at cluster %lu in free chain.\n", i);
+
+		/* If we've created a new chain head,
+		 * tag_free() can claim it
+		 */
+		if (num_refs[curEntry.value] == 0)
+		    break;
+	    }
+	}
+    }
+    while (changed);
+
+    /* Now we can start recovery */
+    files = reclaimed = 0;
+    for (i = 2; i < total_num_clusters; i++)
+	/* If this cluster is the head of an orphan chain... */
+	if (get_owner(fs, i) == &orphan && !num_refs[i]) {
+	    DIR_ENT de;
+	    loff_t offset;
+	    files++;
+	    offset = alloc_rootdir_entry(fs, &de, "FSCK%04d");
+	    de.start = CT_LE_W(i & 0xffff);
+	    if (fs->fat_bits == 32)
+		de.starthi = CT_LE_W(i >> 16);
+	    for (walk = i; walk > 0 && walk != -1;
+		 walk = next_cluster(fs, walk)) {
+		de.size = CT_LE_L(CF_LE_L(de.size) + fs->cluster_size);
+		reclaimed++;
+	    }
+	    fs_write(offset, sizeof(DIR_ENT), &de);
+	}
+    if (reclaimed)
+	printf("Reclaimed %d unused cluster%s (%llu bytes) in %d chain%s.\n",
+	       reclaimed, reclaimed == 1 ? "" : "s",
+	       (unsigned long long)reclaimed * fs->cluster_size, files,
+	       files == 1 ? "" : "s");
+
+    free(num_refs);
+}
+
+unsigned long update_free(DOS_FS * fs)
+{
+    unsigned long i;
+    unsigned long free = 0;
+    int do_set = 0;
+
+    for (i = 2; i < fs->clusters + 2; i++) {
+	FAT_ENTRY curEntry;
+	get_fat(&curEntry, fs->fat, i, fs);
+
+	if (!get_owner(fs, i) && !FAT_IS_BAD(fs, curEntry.value))
+	    ++free;
+    }
+
+    if (!fs->fsinfo_start)
+	return free;
+
+    if (verbose)
+	printf("Checking free cluster summary.\n");
+    if (fs->free_clusters != 0xFFFFFFFF) {
+	if (free != fs->free_clusters) {
+	    printf("Free cluster summary wrong (%ld vs. really %ld)\n",
+		   fs->free_clusters, free);
+	    if (interactive)
+		printf("1) Correct\n2) Don't correct\n");
+	    else
+		printf("  Auto-correcting.\n");
+	    if (!interactive || get_key("12", "?") == '1')
+		do_set = 1;
+	}
+    } else {
+	printf("Free cluster summary uninitialized (should be %ld)\n", free);
+	if (rw) {
+	    if (interactive)
+		printf("1) Set it\n2) Leave it uninitialized\n");
+	    else
+		printf("  Auto-setting.\n");
+	    if (!interactive || get_key("12", "?") == '1')
+		do_set = 1;
+	}
+    }
+
+    if (do_set) {
+	unsigned long le_free = CT_LE_L(free);
+	fs->free_clusters = free;
+	fs_write(fs->fsinfo_start + offsetof(struct info_sector, free_clusters),
+		 sizeof(le_free), &le_free);
+    }
+
+    return free;
+}
diff --git a/dosfstools/src/fat.h b/dosfstools/src/fat.h
new file mode 100644
index 0000000..13ac1b3
--- /dev/null
+++ b/dosfstools/src/fat.h
@@ -0,0 +1,84 @@
+/* fat.h - Read/write access to the FAT
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+#ifndef _FAT_H
+#define _FAT_H
+
+void read_fat(DOS_FS * fs);
+
+/* Loads the FAT of the file system described by FS. Initializes the FAT,
+   replaces broken FATs and rejects invalid cluster entries. */
+
+void get_fat(FAT_ENTRY * entry, void *fat, unsigned long cluster, DOS_FS * fs);
+
+/* Retrieve the FAT entry (next chained cluster) for CLUSTER. */
+
+void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new);
+
+/* Changes the value of the CLUSTERth cluster of the FAT of FS to NEW. Special
+   values of NEW are -1 (EOF, 0xff8 or 0xfff8) and -2 (bad sector, 0xff7 or
+   0xfff7) */
+
+int bad_cluster(DOS_FS * fs, unsigned long cluster);
+
+/* Returns a non-zero integer if the CLUSTERth cluster is marked as bad or zero
+   otherwise. */
+
+unsigned long next_cluster(DOS_FS * fs, unsigned long cluster);
+
+/* Returns the number of the cluster following CLUSTER, or -1 if this is the
+   last cluster of the respective cluster chain. CLUSTER must not be a bad
+   cluster. */
+
+loff_t cluster_start(DOS_FS * fs, unsigned long cluster);
+
+/* Returns the byte offset of CLUSTER, relative to the respective device. */
+
+void set_owner(DOS_FS * fs, unsigned long cluster, DOS_FILE * owner);
+
+/* Sets the owner pointer of the respective cluster to OWNER. If OWNER was NULL
+   before, it can be set to NULL or any non-NULL value. Otherwise, only NULL is
+   accepted as the new value. */
+
+DOS_FILE *get_owner(DOS_FS * fs, unsigned long cluster);
+
+/* Returns the owner of the repective cluster or NULL if the cluster has no
+   owner. */
+
+void fix_bad(DOS_FS * fs);
+
+/* Scans the disk for currently unused bad clusters and marks them as bad. */
+
+void reclaim_free(DOS_FS * fs);
+
+/* Marks all allocated, but unused clusters as free. */
+
+void reclaim_file(DOS_FS * fs);
+
+/* Scans the FAT for chains of allocated, but unused clusters and creates files
+   for them in the root directory. Also tries to fix all inconsistencies (e.g.
+   loops, shared clusters, etc.) in the process. */
+
+unsigned long update_free(DOS_FS * fs);
+
+/* Updates free cluster count in FSINFO sector. */
+
+#endif
diff --git a/dosfstools/src/file.c b/dosfstools/src/file.c
new file mode 100644
index 0000000..a73b73f
--- /dev/null
+++ b/dosfstools/src/file.c
@@ -0,0 +1,282 @@
+/* file.c - Additional file attributes
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
+ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#define _LINUX_STAT_H		/* hack to avoid inclusion of <linux/stat.h> */
+#define _LINUX_STRING_H_	/* hack to avoid inclusion of <linux/string.h> */
+#define _LINUX_FS_H		/* hack to avoid inclusion of <linux/fs.h> */
+
+#include <asm/types.h>
+
+#include <linux/msdos_fs.h>
+
+#include "common.h"
+#include "file.h"
+
+FDSC *fp_root = NULL;
+
+static void put_char(char **p, unsigned char c)
+{
+    if ((c >= ' ' && c < 0x7f) || c >= 0xa0)
+	*(*p)++ = c;
+    else {
+	*(*p)++ = '\\';
+	*(*p)++ = '0' + (c >> 6);
+	*(*p)++ = '0' + ((c >> 3) & 7);
+	*(*p)++ = '0' + (c & 7);
+    }
+}
+
+/**
+ * Construct the "pretty-printed" representation of the name in a short directory entry.
+ *
+ * @param[in]    fixed  Pointer to name[0] of a DIR_ENT
+ *
+ * @return  Pointer to static string containing pretty "8.3" equivalent of the
+ *          name in the directory entry.
+ */
+char *file_name(unsigned char *fixed)
+{
+    static char path[MSDOS_NAME * 4 + 2];
+    char *p;
+    int i, j;
+
+    p = path;
+    for (i = j = 0; i < 8; i++)
+	if (fixed[i] != ' ') {
+	    while (j++ < i)
+		*p++ = ' ';
+	    put_char(&p, fixed[i]);
+	}
+    if (strncmp((const char *)(fixed + 8), "   ", 3)) {
+	*p++ = '.';
+	for (i = j = 0; i < 3; i++)
+	    if (fixed[i + 8] != ' ') {
+		while (j++ < i)
+		    *p++ = ' ';
+		put_char(&p, fixed[i + 8]);
+	    }
+    }
+    *p = 0;
+    return path;
+}
+
+int file_cvt(unsigned char *name, unsigned char *fixed)
+{
+    unsigned char c;
+    int size, ext, cnt;
+
+    size = 8;
+    ext = 0;
+    while (*name) {
+	c = *name;
+	if (c < ' ' || c > 0x7e || strchr("*?<>|\"/", c)) {
+	    printf("Invalid character in name. Use \\ooo for special "
+		   "characters.\n");
+	    return 0;
+	}
+	if (c == '.') {
+	    if (ext) {
+		printf("Duplicate dots in name.\n");
+		return 0;
+	    }
+	    while (size--)
+		*fixed++ = ' ';
+	    size = 3;
+	    ext = 1;
+	    name++;
+	    continue;
+	}
+	if (c == '\\') {
+	    c = 0;
+	    for (cnt = 3; cnt; cnt--) {
+		if (*name < '0' || *name > '7') {
+		    printf("Invalid octal character.\n");
+		    return 0;
+		}
+		c = c * 8 + *name++ - '0';
+	    }
+	    if (cnt < 4) {
+		printf("Expected three octal digits.\n");
+		return 0;
+	    }
+	    name += 3;
+	}
+	if (islower(c))
+	    c = toupper(c);
+	if (size) {
+	    *fixed++ = c;
+	    size--;
+	}
+	name++;
+    }
+    if (*name || size == 8)
+	return 0;
+    if (!ext) {
+	while (size--)
+	    *fixed++ = ' ';
+	size = 3;
+    }
+    while (size--)
+	*fixed++ = ' ';
+    return 1;
+}
+
+void file_add(char *path, FD_TYPE type)
+{
+    FDSC **current, *walk;
+    char name[MSDOS_NAME];
+    char *here;
+
+    current = &fp_root;
+    if (*path != '/')
+	die("%s: Absolute path required.", path);
+    path++;
+    while (1) {
+	if ((here = strchr(path, '/')))
+	    *here = 0;
+	if (!file_cvt((unsigned char *)path, (unsigned char *)name))
+	    exit(2);
+	for (walk = *current; walk; walk = walk->next)
+	    if (!here && (!strncmp(name, walk->name, MSDOS_NAME) || (type ==
+								     fdt_undelete
+								     &&
+								     !strncmp
+								     (name + 1,
+								      walk->name
+								      + 1,
+								      MSDOS_NAME
+								      - 1))))
+		die("Ambiguous name: \"%s\"", path);
+	    else if (here && !strncmp(name, walk->name, MSDOS_NAME))
+		break;
+	if (!walk) {
+	    walk = alloc(sizeof(FDSC));
+	    strncpy(walk->name, name, MSDOS_NAME);
+	    walk->type = here ? fdt_none : type;
+	    walk->first = NULL;
+	    walk->next = *current;
+	    *current = walk;
+	}
+	current = &walk->first;
+	if (!here)
+	    break;
+	*here = '/';
+	path = here + 1;
+    }
+}
+
+FDSC **file_cd(FDSC ** curr, char *fixed)
+{
+    FDSC **walk;
+
+    if (!curr || !*curr)
+	return NULL;
+    for (walk = curr; *walk; walk = &(*walk)->next)
+	if (!strncmp((*walk)->name, fixed, MSDOS_NAME) && (*walk)->first)
+	    return &(*walk)->first;
+    return NULL;
+}
+
+static FDSC **file_find(FDSC ** dir, char *fixed)
+{
+    if (!dir || !*dir)
+	return NULL;
+    if (*(unsigned char *)fixed == DELETED_FLAG) {
+	while (*dir) {
+	    if (!strncmp((*dir)->name + 1, fixed + 1, MSDOS_NAME - 1)
+		&& !(*dir)->first)
+		return dir;
+	    dir = &(*dir)->next;
+	}
+	return NULL;
+    }
+    while (*dir) {
+	if (!strncmp((*dir)->name, fixed, MSDOS_NAME) && !(*dir)->first)
+	    return dir;
+	dir = &(*dir)->next;
+    }
+    return NULL;
+}
+
+/* Returns the attribute of the file FIXED in directory CURR or FDT_NONE if no
+   such file exists or if CURR is NULL. */
+FD_TYPE file_type(FDSC ** curr, char *fixed)
+{
+    FDSC **this;
+
+    if ((this = file_find(curr, fixed)))
+	return (*this)->type;
+    return fdt_none;
+}
+
+void file_modify(FDSC ** curr, char *fixed)
+{
+    FDSC **this, *next;
+
+    if (!(this = file_find(curr, fixed)))
+	die("Internal error: file_find failed");
+    switch ((*this)->type) {
+    case fdt_drop:
+	printf("Dropping %s\n", file_name((unsigned char *)fixed));
+	*(unsigned char *)fixed = DELETED_FLAG;
+	break;
+    case fdt_undelete:
+	*fixed = *(*this)->name;
+	printf("Undeleting %s\n", file_name((unsigned char *)fixed));
+	break;
+    default:
+	die("Internal error: file_modify");
+    }
+    next = (*this)->next;
+    free(*this);
+    *this = next;
+}
+
+static void report_unused(FDSC * this)
+{
+    FDSC *next;
+
+    while (this) {
+	next = this->next;
+	if (this->first)
+	    report_unused(this->first);
+	else if (this->type != fdt_none)
+	    printf("Warning: did not %s file %s\n", this->type == fdt_drop ?
+		   "drop" : "undelete", file_name((unsigned char *)this->name));
+	free(this);
+	this = next;
+    }
+}
+
+void file_unused(void)
+{
+    report_unused(fp_root);
+}
diff --git a/dosfstools/src/file.h b/dosfstools/src/file.h
new file mode 100644
index 0000000..40bd58a
--- /dev/null
+++ b/dosfstools/src/file.h
@@ -0,0 +1,69 @@
+/* file.h - Additional file attributes
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+#ifndef _FILE_H
+#define _FILE_H
+
+typedef enum { fdt_none, fdt_drop, fdt_undelete } FD_TYPE;
+
+typedef struct _fptr {
+    char name[MSDOS_NAME];
+    FD_TYPE type;
+    struct _fptr *first;	/* first entry */
+    struct _fptr *next;		/* next file in directory */
+} FDSC;
+
+extern FDSC *fp_root;
+
+char *file_name(unsigned char *fixed);
+
+/* Returns a pointer to a pretty-printed representation of a fixed MS-DOS file
+   name. */
+
+int file_cvt(unsigned char *name, unsigned char *fixed);
+
+/* Converts a pretty-printed file name to the fixed MS-DOS format. Returns a
+   non-zero integer on success, zero on failure. */
+
+void file_add(char *path, FD_TYPE type);
+
+/* Define special attributes for a path. TYPE can be either FDT_DROP or
+   FDT_UNDELETE. */
+
+FDSC **file_cd(FDSC ** curr, char *fixed);
+
+/* Returns a pointer to the directory descriptor of the subdirectory FIXED of
+   CURR, or NULL if no such subdirectory exists. */
+
+FD_TYPE file_type(FDSC ** curr, char *fixed);
+
+/* Returns the attribute of the file FIXED in directory CURR or FDT_NONE if no
+   such file exists or if CURR is NULL. */
+
+void file_modify(FDSC ** curr, char *fixed);
+
+/* Performs the necessary operation on the entry of CURR that is named FIXED. */
+
+void file_unused(void);
+
+/* Displays warnings for all unused file attributes. */
+
+#endif
diff --git a/dosfstools/src/io.c b/dosfstools/src/io.c
new file mode 100644
index 0000000..a703c2d
--- /dev/null
+++ b/dosfstools/src/io.c
@@ -0,0 +1,230 @@
+/* io.c - Virtual disk input/output
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/*
+ * Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de>
+ *	Fixed nasty bug that caused every file with a name like
+ *	xxxxxxxx.xxx to be treated as bad name that needed to be fixed.
+ */
+
+/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
+ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+
+#define _LARGEFILE64_SOURCE
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fd.h>
+
+#include "dosfsck.h"
+#include "common.h"
+#include "io.h"
+
+typedef struct _change {
+    void *data;
+    loff_t pos;
+    int size;
+    struct _change *next;
+} CHANGE;
+
+static CHANGE *changes, *last;
+static int fd, did_change = 0;
+
+unsigned device_no;
+
+#ifdef __DJGPP__
+#include "volume.h"		/* DOS lowlevel disk access functions */
+loff_t llseek(int fd, loff_t offset, int whence)
+{
+    if ((whence != SEEK_SET) || (fd == 4711))
+	return -1;		/* only those supported */
+    return VolumeSeek(offset);
+}
+
+#define open OpenVolume
+#define close CloseVolume
+#define read(a,b,c) ReadVolume(b,c)
+#define write(a,b,c) WriteVolume(b,c)
+#else
+loff_t llseek(int fd, loff_t offset, int whence)
+{
+    return (loff_t) lseek64(fd, (off64_t) offset, whence);
+}
+#endif
+
+void fs_open(char *path, int rw)
+{
+    struct stat stbuf;
+
+    if ((fd = open(path, rw ? O_RDWR : O_RDONLY)) < 0) {
+	perror("open");
+	exit(6);
+    }
+    changes = last = NULL;
+    did_change = 0;
+
+#ifndef _DJGPP_
+    if (fstat(fd, &stbuf) < 0)
+	pdie("fstat %s", path);
+    device_no = S_ISBLK(stbuf.st_mode) ? (stbuf.st_rdev >> 8) & 0xff : 0;
+#else
+    if (IsWorkingOnImageFile()) {
+	if (fstat(GetVolumeHandle(), &stbuf) < 0)
+	    pdie("fstat image %s", path);
+	device_no = 0;
+    } else {
+	/* return 2 for floppy, 1 for ramdisk, 7 for loopback  */
+	/* used by boot.c in Atari mode: floppy always FAT12,  */
+	/* loopback / ramdisk only FAT12 if usual floppy size, */
+	/* harddisk always FAT16 on Atari... */
+	device_no = (GetVolumeHandle() < 2) ? 2 : 1;
+	/* telling "floppy" for A:/B:, "ramdisk" for the rest */
+    }
+#endif
+}
+
+/**
+ * Read data from the partition, accounting for any pending updates that are
+ * queued for writing.
+ *
+ * @param[in]   pos     Byte offset, relative to the beginning of the partition,
+ *                      at which to read
+ * @param[in]   size    Number of bytes to read
+ * @param[out]  data    Where to put the data read
+ */
+void fs_read(loff_t pos, int size, void *data)
+{
+    CHANGE *walk;
+    int got;
+
+    if (llseek(fd, pos, 0) != pos)
+	pdie("Seek to %lld", pos);
+    if ((got = read(fd, data, size)) < 0)
+	pdie("Read %d bytes at %lld", size, pos);
+    if (got != size)
+	die("Got %d bytes instead of %d at %lld", got, size, pos);
+    for (walk = changes; walk; walk = walk->next) {
+	if (walk->pos < pos + size && walk->pos + walk->size > pos) {
+	    if (walk->pos < pos)
+		memcpy(data, (char *)walk->data + pos - walk->pos, min(size,
+								       walk->size
+								       - pos +
+								       walk->pos));
+	    else
+		memcpy((char *)data + walk->pos - pos, walk->data,
+		       min(walk->size, size + pos - walk->pos));
+	}
+    }
+}
+
+int fs_test(loff_t pos, int size)
+{
+    void *scratch;
+    int okay;
+
+    if (llseek(fd, pos, 0) != pos)
+	pdie("Seek to %lld", pos);
+    scratch = alloc(size);
+    okay = read(fd, scratch, size) == size;
+    free(scratch);
+    return okay;
+}
+
+void fs_write(loff_t pos, int size, void *data)
+{
+    CHANGE *new;
+    int did;
+
+    if (write_immed) {
+	did_change = 1;
+	if (llseek(fd, pos, 0) != pos)
+	    pdie("Seek to %lld", pos);
+	if ((did = write(fd, data, size)) == size)
+	    return;
+	if (did < 0)
+	    pdie("Write %d bytes at %lld", size, pos);
+	die("Wrote %d bytes instead of %d at %lld", did, size, pos);
+    }
+    new = alloc(sizeof(CHANGE));
+    new->pos = pos;
+    memcpy(new->data = alloc(new->size = size), data, size);
+    new->next = NULL;
+    if (last)
+	last->next = new;
+    else
+	changes = new;
+    last = new;
+}
+
+static void fs_flush(void)
+{
+    CHANGE *this;
+    int size;
+
+    while (changes) {
+	this = changes;
+	changes = changes->next;
+	if (llseek(fd, this->pos, 0) != this->pos)
+	    fprintf(stderr,
+		    "Seek to %lld failed: %s\n  Did not write %d bytes.\n",
+		    (long long)this->pos, strerror(errno), this->size);
+	else if ((size = write(fd, this->data, this->size)) < 0)
+	    fprintf(stderr, "Writing %d bytes at %lld failed: %s\n", this->size,
+		    (long long)this->pos, strerror(errno));
+	else if (size != this->size)
+	    fprintf(stderr, "Wrote %d bytes instead of %d bytes at %lld."
+		    "\n", size, this->size, (long long)this->pos);
+	free(this->data);
+	free(this);
+    }
+}
+
+int fs_close(int write)
+{
+    CHANGE *next;
+    int changed;
+
+    changed = ! !changes;
+    if (write)
+	fs_flush();
+    else
+	while (changes) {
+	    next = changes->next;
+	    free(changes->data);
+	    free(changes);
+	    changes = next;
+	}
+    if (close(fd) < 0)
+	pdie("closing file system");
+    return changed || did_change;
+}
+
+int fs_changed(void)
+{
+    return ! !changes || did_change;
+}
diff --git a/dosfstools/src/io.h b/dosfstools/src/io.h
new file mode 100644
index 0000000..2db4ea7
--- /dev/null
+++ b/dosfstools/src/io.h
@@ -0,0 +1,70 @@
+/* io.h - Virtual disk input/output
+
+   Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
+ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+
+#ifndef _IO_H
+#define _IO_H
+
+#include <sys/types.h>		/* for loff_t */
+
+loff_t llseek(int fd, loff_t offset, int whence);
+
+/* lseek() analogue for large offsets. */
+
+void fs_open(char *path, int rw);
+
+/* Opens the file system PATH. If RW is zero, the file system is opened
+   read-only, otherwise, it is opened read-write. */
+
+void fs_read(loff_t pos, int size, void *data);
+
+/* Reads SIZE bytes starting at POS into DATA. Performs all applicable
+   changes. */
+
+int fs_test(loff_t pos, int size);
+
+/* Returns a non-zero integer if SIZE bytes starting at POS can be read without
+   errors. Otherwise, it returns zero. */
+
+void fs_write(loff_t pos, int size, void *data);
+
+/* If write_immed is non-zero, SIZE bytes are written from DATA to the disk,
+   starting at POS. If write_immed is zero, the change is added to a list in
+   memory. */
+
+int fs_close(int write);
+
+/* Closes the file system, performs all pending changes if WRITE is non-zero
+   and removes the list of changes. Returns a non-zero integer if the file
+   system has been changed since the last fs_open, zero otherwise. */
+
+int fs_changed(void);
+
+/* Determines whether the file system has changed. See fs_close. */
+
+extern unsigned device_no;
+
+/* Major number of device (0 if file) and size (in 512 byte sectors) */
+
+#endif
diff --git a/dosfstools/src/lfn.c b/dosfstools/src/lfn.c
new file mode 100644
index 0000000..736491c
--- /dev/null
+++ b/dosfstools/src/lfn.c
@@ -0,0 +1,502 @@
+/* lfn.c - Functions for handling VFAT long filenames
+
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+
+#include "common.h"
+#include "io.h"
+#include "dosfsck.h"
+#include "lfn.h"
+#include "file.h"
+
+typedef struct {
+    __u8 id;			/* sequence number for slot */
+    __u8 name0_4[10];		/* first 5 characters in name */
+    __u8 attr;			/* attribute byte */
+    __u8 reserved;		/* always 0 */
+    __u8 alias_checksum;	/* checksum for 8.3 alias */
+    __u8 name5_10[12];		/* 6 more characters in name */
+    __u16 start;		/* starting cluster number, 0 in long slots */
+    __u8 name11_12[4];		/* last 2 characters in name */
+} LFN_ENT;
+
+#define LFN_ID_START	0x40
+#define LFN_ID_SLOTMASK	0x1f
+
+#define CHARS_PER_LFN	13
+
+/* These modul-global vars represent the state of the LFN parser */
+unsigned char *lfn_unicode = NULL;
+unsigned char lfn_checksum;
+int lfn_slot = -1;
+loff_t *lfn_offsets = NULL;
+int lfn_parts = 0;
+
+static unsigned char fat_uni2esc[64] = {
+    '0', '1', '2', '3', '4', '5', '6', '7',
+    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+    'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+    'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+    'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+    'u', 'v', 'w', 'x', 'y', 'z', '+', '-'
+};
+
+/* This defines which unicode chars are directly convertable to ISO-8859-1 */
+#define UNICODE_CONVERTABLE(cl,ch)	(ch == 0 && (cl < 0x80 || cl >= 0xa0))
+
+/* for maxlen param */
+#define UNTIL_0		INT_MAX
+
+/* Convert name part in 'lfn' from unicode to ASCII */
+#define CNV_THIS_PART(lfn)				\
+    ({							\
+	unsigned char __part_uni[CHARS_PER_LFN*2];		\
+	copy_lfn_part( __part_uni, lfn );		\
+	cnv_unicode( __part_uni, CHARS_PER_LFN, 0 );	\
+    })
+
+/* Convert name parts collected so far (from previous slots) from unicode to
+ * ASCII */
+#define CNV_PARTS_SO_FAR()					\
+	(cnv_unicode( lfn_unicode+(lfn_slot*CHARS_PER_LFN*2),	\
+		      lfn_parts*CHARS_PER_LFN, 0 ))
+
+/* This function converts an unicode string to a normal ASCII string, assuming
+ * ISO-8859-1 charset. Characters not in 8859-1 are converted to the same
+ * escape notation as used by the kernel, i.e. the uuencode-like ":xxx" */
+static char *cnv_unicode(const unsigned char *uni, int maxlen, int use_q)
+{
+    const unsigned char *up;
+    unsigned char *out, *cp;
+    int len, val;
+
+    for (len = 0, up = uni; (up - uni) / 2 < maxlen && (up[0] || up[1]);
+	 up += 2) {
+	if (UNICODE_CONVERTABLE(up[0], up[1]))
+	    ++len;
+	else
+	    len += 4;
+    }
+    cp = out = use_q ? qalloc(&mem_queue, len + 1) : alloc(len + 1);
+
+    for (up = uni; (up - uni) / 2 < maxlen && (up[0] || up[1]); up += 2) {
+	if (UNICODE_CONVERTABLE(up[0], up[1]))
+	    *cp++ = up[0];
+	else {
+	    /* here the same escape notation is used as in the Linux kernel */
+	    *cp++ = ':';
+	    val = (up[1] << 8) + up[0];
+	    cp[2] = fat_uni2esc[val & 0x3f];
+	    val >>= 6;
+	    cp[1] = fat_uni2esc[val & 0x3f];
+	    val >>= 6;
+	    cp[0] = fat_uni2esc[val & 0x3f];
+	    cp += 3;
+	}
+    }
+    *cp = 0;
+
+    return (char *)out;
+}
+
+static void copy_lfn_part(unsigned char *dst, LFN_ENT * lfn)
+{
+    memcpy(dst, lfn->name0_4, 10);
+    memcpy(dst + 10, lfn->name5_10, 12);
+    memcpy(dst + 22, lfn->name11_12, 4);
+}
+
+static void clear_lfn_slots(int start, int end)
+{
+    int i;
+    LFN_ENT empty;
+
+    /* New dir entry is zeroed except first byte, which is set to 0xe5.
+     * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading
+     * a directory at the first zero entry...
+     */
+    memset(&empty, 0, sizeof(empty));
+    empty.id = DELETED_FLAG;
+
+    for (i = start; i <= end; ++i) {
+	fs_write(lfn_offsets[i], sizeof(LFN_ENT), &empty);
+    }
+}
+
+void lfn_fix_checksum(loff_t from, loff_t to, const char *short_name)
+{
+    int i;
+    __u8 sum;
+    for (sum = 0, i = 0; i < 11; i++)
+	sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + short_name[i];
+
+    for (; from < to; from += sizeof(LFN_ENT)) {
+	fs_write(from + offsetof(LFN_ENT, alias_checksum), sizeof(sum), &sum);
+    }
+}
+
+void lfn_reset(void)
+{
+    if (lfn_unicode)
+	free(lfn_unicode);
+    lfn_unicode = NULL;
+    if (lfn_offsets)
+	free(lfn_offsets);
+    lfn_offsets = NULL;
+    lfn_slot = -1;
+}
+
+/* This function is only called with de->attr == VFAT_LN_ATTR. It stores part
+ * of the long name. */
+void lfn_add_slot(DIR_ENT * de, loff_t dir_offset)
+{
+    LFN_ENT *lfn = (LFN_ENT *) de;
+    int slot = lfn->id & LFN_ID_SLOTMASK;
+    unsigned offset;
+
+    if (lfn_slot == 0)
+	lfn_check_orphaned();
+
+    if (de->attr != VFAT_LN_ATTR)
+	die("lfn_add_slot called with non-LFN directory entry");
+
+    if (lfn->id & LFN_ID_START && slot != 0) {
+	if (lfn_slot != -1) {
+	    int can_clear = 0;
+	    /* There is already a LFN "in progess", so it is an error that a
+	     * new start entry is here. */
+	    /* Causes: 1) if slot# == expected: start bit set mysteriously, 2)
+	     *         old LFN overwritten by new one */
+	    /* Fixes: 1) delete previous LFN 2) if slot# == expected and
+	     *        checksum ok: clear start bit */
+	    /* XXX: Should delay that until next LFN known (then can better
+	     * display the name) */
+	    printf("A new long file name starts within an old one.\n");
+	    if (slot == lfn_slot && lfn->alias_checksum == lfn_checksum) {
+		char *part1 = CNV_THIS_PART(lfn);
+		char *part2 = CNV_PARTS_SO_FAR();
+		printf("  It could be that the LFN start bit is wrong here\n"
+		       "  if \"%s\" seems to match \"%s\".\n", part1, part2);
+		free(part1);
+		free(part2);
+		can_clear = 1;
+	    }
+	    if (interactive) {
+		printf("1: Delete previous LFN\n2: Leave it as it is.\n");
+		if (can_clear)
+		    printf("3: Clear start bit and concatenate LFNs\n");
+	    } else
+		printf("  Not auto-correcting this.\n");
+	    if (interactive) {
+		switch (get_key(can_clear ? "123" : "12", "?")) {
+		case '1':
+		    clear_lfn_slots(0, lfn_parts - 1);
+		    lfn_reset();
+		    break;
+		case '2':
+		    break;
+		case '3':
+		    lfn->id &= ~LFN_ID_START;
+		    fs_write(dir_offset + offsetof(LFN_ENT, id),
+			     sizeof(lfn->id), &lfn->id);
+		    break;
+		}
+	    }
+	}
+	lfn_slot = slot;
+	lfn_checksum = lfn->alias_checksum;
+	lfn_unicode = alloc((lfn_slot * CHARS_PER_LFN + 1) * 2);
+	lfn_offsets = alloc(lfn_slot * sizeof(loff_t));
+	lfn_parts = 0;
+    } else if (lfn_slot == -1 && slot != 0) {
+	/* No LFN in progress, but slot found; start bit missing */
+	/* Causes: 1) start bit got lost, 2) Previous slot with start bit got
+	 *         lost */
+	/* Fixes: 1) delete LFN, 2) set start bit */
+	char *part = CNV_THIS_PART(lfn);
+	printf("Long filename fragment \"%s\" found outside a LFN "
+	       "sequence.\n  (Maybe the start bit is missing on the "
+	       "last fragment)\n", part);
+	if (interactive) {
+	    printf("1: Delete fragment\n2: Leave it as it is.\n"
+		   "3: Set start bit\n");
+	} else
+	    printf("  Not auto-correcting this.\n");
+	switch (interactive ? get_key("123", "?") : '2') {
+	case '1':
+	    if (!lfn_offsets)
+		lfn_offsets = alloc(sizeof(loff_t));
+	    lfn_offsets[0] = dir_offset;
+	    clear_lfn_slots(0, 0);
+	    lfn_reset();
+	    return;
+	case '2':
+	    lfn_reset();
+	    return;
+	case '3':
+	    lfn->id |= LFN_ID_START;
+	    fs_write(dir_offset + offsetof(LFN_ENT, id),
+		     sizeof(lfn->id), &lfn->id);
+	    lfn_slot = slot;
+	    lfn_checksum = lfn->alias_checksum;
+	    lfn_unicode = alloc((lfn_slot * CHARS_PER_LFN + 1) * 2);
+	    lfn_offsets = alloc(lfn_slot * sizeof(loff_t));
+	    lfn_parts = 0;
+	    break;
+	}
+    } else if (slot != lfn_slot) {
+	/* wrong sequence number */
+	/* Causes: 1) seq-no destroyed */
+	/* Fixes: 1) delete LFN, 2) fix number (maybe only if following parts
+	 *        are ok?, maybe only if checksum is ok?) (Attention: space
+	 *        for name was allocated before!) */
+	int can_fix = 0;
+	printf("Unexpected long filename sequence number "
+	       "(%d vs. expected %d).\n", slot, lfn_slot);
+	if (lfn->alias_checksum == lfn_checksum && lfn_slot > 0) {
+	    char *part1 = CNV_THIS_PART(lfn);
+	    char *part2 = CNV_PARTS_SO_FAR();
+	    printf("  It could be that just the number is wrong\n"
+		   "  if \"%s\" seems to match \"%s\".\n", part1, part2);
+	    free(part1);
+	    free(part2);
+	    can_fix = 1;
+	}
+	if (interactive) {
+	    printf
+		("1: Delete LFN\n2: Leave it as it is (and ignore LFN so far)\n");
+	    if (can_fix)
+		printf("3: Correct sequence number\n");
+	} else
+	    printf("  Not auto-correcting this.\n");
+	switch (interactive ? get_key(can_fix ? "123" : "12", "?") : '2') {
+	case '1':
+	    if (!lfn_offsets) {
+		lfn_offsets = alloc(sizeof(loff_t));
+		lfn_parts = 0;
+	    }
+	    lfn_offsets[lfn_parts++] = dir_offset;
+	    clear_lfn_slots(0, lfn_parts - 1);
+	    lfn_reset();
+	    return;
+	case '2':
+	    lfn_reset();
+	    return;
+	case '3':
+	    lfn->id = (lfn->id & ~LFN_ID_SLOTMASK) | lfn_slot;
+	    fs_write(dir_offset + offsetof(LFN_ENT, id),
+		     sizeof(lfn->id), &lfn->id);
+	    break;
+	}
+    }
+
+    if (lfn->alias_checksum != lfn_checksum) {
+	/* checksum mismatch */
+	/* Causes: 1) checksum field here destroyed */
+	/* Fixes: 1) delete LFN, 2) fix checksum */
+	printf("Checksum in long filename part wrong "
+	       "(%02x vs. expected %02x).\n",
+	       lfn->alias_checksum, lfn_checksum);
+	if (interactive) {
+	    printf("1: Delete LFN\n2: Leave it as it is.\n"
+		   "3: Correct checksum\n");
+	} else
+	    printf("  Not auto-correcting this.\n");
+	if (interactive) {
+	    switch (get_key("123", "?")) {
+	    case '1':
+		lfn_offsets[lfn_parts++] = dir_offset;
+		clear_lfn_slots(0, lfn_parts - 1);
+		lfn_reset();
+		return;
+	    case '2':
+		break;
+	    case '3':
+		lfn->alias_checksum = lfn_checksum;
+		fs_write(dir_offset + offsetof(LFN_ENT, alias_checksum),
+			 sizeof(lfn->alias_checksum), &lfn->alias_checksum);
+		break;
+	    }
+	}
+    }
+
+    if (lfn_slot != -1) {
+	lfn_slot--;
+	offset = lfn_slot * CHARS_PER_LFN * 2;
+	copy_lfn_part(lfn_unicode + offset, lfn);
+	if (lfn->id & LFN_ID_START)
+	    lfn_unicode[offset + 26] = lfn_unicode[offset + 27] = 0;
+	lfn_offsets[lfn_parts++] = dir_offset;
+    }
+
+    if (lfn->reserved != 0) {
+	printf("Reserved field in VFAT long filename slot is not 0 "
+	       "(but 0x%02x).\n", lfn->reserved);
+	if (interactive)
+	    printf("1: Fix.\n2: Leave it.\n");
+	else
+	    printf("Auto-setting to 0.\n");
+	if (!interactive || get_key("12", "?") == '1') {
+	    lfn->reserved = 0;
+	    fs_write(dir_offset + offsetof(LFN_ENT, reserved),
+		     sizeof(lfn->reserved), &lfn->reserved);
+	}
+    }
+    if (lfn->start != CT_LE_W(0)) {
+	printf("Start cluster field in VFAT long filename slot is not 0 "
+	       "(but 0x%04x).\n", lfn->start);
+	if (interactive)
+	    printf("1: Fix.\n2: Leave it.\n");
+	else
+	    printf("Auto-setting to 0.\n");
+	if (!interactive || get_key("12", "?") == '1') {
+	    lfn->start = CT_LE_W(0);
+	    fs_write(dir_offset + offsetof(LFN_ENT, start),
+		     sizeof(lfn->start), &lfn->start);
+	}
+    }
+}
+
+/* This function is always called when de->attr != VFAT_LN_ATTR is found, to
+ * retrieve the previously constructed LFN. */
+char *lfn_get(DIR_ENT * de, loff_t * lfn_offset)
+{
+    char *lfn;
+    __u8 sum;
+    int i;
+
+    *lfn_offset = 0;
+    if (de->attr == VFAT_LN_ATTR)
+	die("lfn_get called with LFN directory entry");
+
+#if 0
+    if (de->lcase)
+	printf("lcase=%02x\n", de->lcase);
+#endif
+
+    if (lfn_slot == -1)
+	/* no long name for this file */
+	return NULL;
+
+    if (lfn_slot != 0) {
+	/* The long name isn't finished yet. */
+	/* Causes: 1) LFN slot overwritten by non-VFAT aware tool */
+	/* Fixes: 1) delete LFN 2) move overwriting entry to somewhere else
+	 * and let user enter missing part of LFN (hard to do :-()
+	 * 3) renumber entries and truncate name */
+	char *long_name = CNV_PARTS_SO_FAR();
+	char *short_name = file_name(de->name);
+	printf("Unfinished long file name \"%s\".\n"
+	       "  (Start may have been overwritten by %s)\n",
+	       long_name, short_name);
+	free(long_name);
+	if (interactive) {
+	    printf("1: Delete LFN\n2: Leave it as it is.\n"
+		   "3: Fix numbering (truncates long name and attaches "
+		   "it to short name %s)\n", short_name);
+	} else
+	    printf("  Not auto-correcting this.\n");
+	switch (interactive ? get_key("123", "?") : '2') {
+	case '1':
+	    clear_lfn_slots(0, lfn_parts - 1);
+	    lfn_reset();
+	    return NULL;
+	case '2':
+	    lfn_reset();
+	    return NULL;
+	case '3':
+	    for (i = 0; i < lfn_parts; ++i) {
+		__u8 id = (lfn_parts - i) | (i == 0 ? LFN_ID_START : 0);
+		fs_write(lfn_offsets[i] + offsetof(LFN_ENT, id),
+			 sizeof(id), &id);
+	    }
+	    memmove(lfn_unicode, lfn_unicode + lfn_slot * CHARS_PER_LFN * 2,
+		    lfn_parts * CHARS_PER_LFN * 2);
+	    break;
+	}
+    }
+
+    for (sum = 0, i = 0; i < 11; i++)
+	sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + de->name[i];
+    if (sum != lfn_checksum) {
+	/* checksum doesn't match, long name doesn't apply to this alias */
+	/* Causes: 1) alias renamed */
+	/* Fixes: 1) Fix checksum in LFN entries */
+	char *long_name = CNV_PARTS_SO_FAR();
+	char *short_name = file_name(de->name);
+	printf("Wrong checksum for long file name \"%s\".\n"
+	       "  (Short name %s may have changed without updating the long name)\n",
+	       long_name, short_name);
+	free(long_name);
+	if (interactive) {
+	    printf("1: Delete LFN\n2: Leave it as it is.\n"
+		   "3: Fix checksum (attaches to short name %s)\n", short_name);
+	} else
+	    printf("  Not auto-correcting this.\n");
+	if (interactive) {
+	    switch (get_key("123", "?")) {
+	    case '1':
+		clear_lfn_slots(0, lfn_parts - 1);
+		lfn_reset();
+		return NULL;
+	    case '2':
+		lfn_reset();
+		return NULL;
+	    case '3':
+		for (i = 0; i < lfn_parts; ++i) {
+		    fs_write(lfn_offsets[i] + offsetof(LFN_ENT, alias_checksum),
+			     sizeof(sum), &sum);
+		}
+		break;
+	    }
+	}
+    }
+
+    *lfn_offset = lfn_offsets[0];
+    lfn = cnv_unicode(lfn_unicode, UNTIL_0, 1);
+    lfn_reset();
+    return (lfn);
+}
+
+void lfn_check_orphaned(void)
+{
+    char *long_name;
+
+    if (lfn_slot == -1)
+	return;
+
+    long_name = CNV_PARTS_SO_FAR();
+    printf("Orphaned long file name part \"%s\"\n", long_name);
+    if (interactive)
+	printf("1: Delete.\n2: Leave it.\n");
+    else
+	printf("  Auto-deleting.\n");
+    if (!interactive || get_key("12", "?") == '1') {
+	clear_lfn_slots(0, lfn_parts - 1);
+    }
+    lfn_reset();
+}
diff --git a/dosfstools/src/lfn.h b/dosfstools/src/lfn.h
new file mode 100644
index 0000000..2ea44a7
--- /dev/null
+++ b/dosfstools/src/lfn.h
@@ -0,0 +1,38 @@
+/* lfn.h - Functions for handling VFAT long filenames
+
+   Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+#ifndef _LFN_H
+#define _LFN_H
+
+void lfn_reset(void);
+/* Reset the state of the LFN parser. */
+
+void lfn_add_slot(DIR_ENT * de, loff_t dir_offset);
+/* Process a dir slot that is a VFAT LFN entry. */
+
+char *lfn_get(DIR_ENT * de, loff_t * lfn_offset);
+/* Retrieve the long name for the proper dir entry. */
+
+void lfn_check_orphaned(void);
+
+void lfn_fix_checksum(loff_t from, loff_t to, const char *short_name);
+
+#endif
diff --git a/dosfstools/src/mkdosfs.c b/dosfstools/src/mkdosfs.c
new file mode 100644
index 0000000..9873bef
--- /dev/null
+++ b/dosfstools/src/mkdosfs.c
@@ -0,0 +1,1733 @@
+/* mkdosfs.c - utility to create FAT/MS-DOS filesystems
+
+   Copyright (C) 1991 Linus Torvalds <torvalds@klaava.helsinki.fi>
+   Copyright (C) 1992-1993 Remy Card <card@masi.ibp.fr>
+   Copyright (C) 1993-1994 David Hudson <dave@humbug.demon.co.uk>
+   Copyright (C) 1998 H. Peter Anvin <hpa@zytor.com>
+   Copyright (C) 1998-2005 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+/* Description: Utility to allow an MS-DOS filesystem to be created
+   under Linux.  A lot of the basic structure of this program has been
+   borrowed from Remy Card's "mke2fs" code.
+
+   As far as possible the aim here is to make the "mkdosfs" command
+   look almost identical to the other Linux filesystem make utilties,
+   eg bad blocks are still specified as blocks, not sectors, but when
+   it comes down to it, DOS is tied to the idea of a sector (512 bytes
+   as a rule), and not the block.  For example the boot block does not
+   occupy a full cluster.
+
+   Fixes/additions May 1998 by Roman Hodek
+   <Roman.Hodek@informatik.uni-erlangen.de>:
+   - Atari format support
+   - New options -A, -S, -C
+   - Support for filesystems > 2GB
+   - FAT32 support */
+
+/* Include the header files */
+
+#include "version.h"
+
+#include <fcntl.h>
+#include <linux/hdreg.h>
+#if defined(_USING_BIONIC_)
+#include <linux/fs.h>
+#else
+#include <sys/mount.h>
+#endif
+#include <linux/fd.h>
+#include <endian.h>
+#include <mntent.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include <asm/types.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+
+#include <asm/byteorder.h>
+#ifdef __le16_to_cpu
+/* ++roman: 2.1 kernel headers define these function, they're probably more
+ * efficient then coding the swaps machine-independently. */
+#define CF_LE_W	__le16_to_cpu
+#define CF_LE_L	__le32_to_cpu
+#define CT_LE_W	__cpu_to_le16
+#define CT_LE_L	__cpu_to_le32
+#else
+#define CF_LE_W(v) ((((v) & 0xff) << 8) | (((v) >> 8) & 0xff))
+#define CF_LE_L(v) (((unsigned)(v)>>24) | (((unsigned)(v)>>8)&0xff00) | \
+               (((unsigned)(v)<<8)&0xff0000) | ((unsigned)(v)<<24))
+#define CT_LE_W(v) CF_LE_W(v)
+#define CT_LE_L(v) CF_LE_L(v)
+#endif /* defined(__le16_to_cpu) */
+
+#else
+
+#define CF_LE_W(v) (v)
+#define CF_LE_L(v) (v)
+#define CT_LE_W(v) (v)
+#define CT_LE_L(v) (v)
+
+#endif /* __BIG_ENDIAN */
+
+/* In earlier versions, an own llseek() was used, but glibc lseek() is
+ * sufficient (or even better :) for 64 bit offsets in the meantime */
+#define llseek lseek64
+
+/* Constant definitions */
+
+#define TRUE 1			/* Boolean constants */
+#define FALSE 0
+
+#define TEST_BUFFER_BLOCKS 16
+#define HARD_SECTOR_SIZE   512
+#define SECTORS_PER_BLOCK ( BLOCK_SIZE / HARD_SECTOR_SIZE )
+
+/* Macro definitions */
+
+/* Report a failure message and return a failure error code */
+
+#define die( str ) fatal_error( "%s: " str "\n" )
+
+/* Mark a cluster in the FAT as bad */
+
+#define mark_sector_bad( sector ) mark_FAT_sector( sector, FAT_BAD )
+
+/* Compute ceil(a/b) */
+
+inline int cdiv(int a, int b)
+{
+    return (a + b - 1) / b;
+}
+
+/* MS-DOS filesystem structures -- I included them here instead of
+   including linux/msdos_fs.h since that doesn't include some fields we
+   need */
+
+#define ATTR_RO      1		/* read-only */
+#define ATTR_HIDDEN  2		/* hidden */
+#define ATTR_SYS     4		/* system */
+#define ATTR_VOLUME  8		/* volume label */
+#define ATTR_DIR     16		/* directory */
+#define ATTR_ARCH    32		/* archived */
+
+#define ATTR_NONE    0		/* no attribute bits */
+#define ATTR_UNUSED  (ATTR_VOLUME | ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN)
+	/* attribute bits that are copied "as is" */
+
+/* FAT values */
+#define FAT_EOF      (atari_format ? 0x0fffffff : 0x0ffffff8)
+#define FAT_BAD      0x0ffffff7
+
+#define MSDOS_EXT_SIGN 0x29	/* extended boot sector signature */
+#define MSDOS_FAT12_SIGN "FAT12   "	/* FAT12 filesystem signature */
+#define MSDOS_FAT16_SIGN "FAT16   "	/* FAT16 filesystem signature */
+#define MSDOS_FAT32_SIGN "FAT32   "	/* FAT32 filesystem signature */
+
+#define BOOT_SIGN 0xAA55	/* Boot sector magic number */
+
+#define MAX_CLUST_12	((1 << 12) - 16)
+#define MAX_CLUST_16	((1 << 16) - 16)
+#define MIN_CLUST_32    65529
+/* M$ says the high 4 bits of a FAT32 FAT entry are reserved and don't belong
+ * to the cluster number. So the max. cluster# is based on 2^28 */
+#define MAX_CLUST_32	((1 << 28) - 16)
+
+#define FAT12_THRESHOLD	4085
+
+#define OLDGEMDOS_MAX_SECTORS	32765
+#define GEMDOS_MAX_SECTORS	65531
+#define GEMDOS_MAX_SECTOR_SIZE	(16*1024)
+
+#define BOOTCODE_SIZE		448
+#define BOOTCODE_FAT32_SIZE	420
+
+/* __attribute__ ((packed)) is used on all structures to make gcc ignore any
+ * alignments */
+
+struct msdos_volume_info {
+    __u8 drive_number;		/* BIOS drive number */
+    __u8 RESERVED;		/* Unused */
+    __u8 ext_boot_sign;		/* 0x29 if fields below exist (DOS 3.3+) */
+    __u8 volume_id[4];		/* Volume ID number */
+    __u8 volume_label[11];	/* Volume label */
+    __u8 fs_type[8];		/* Typically FAT12 or FAT16 */
+} __attribute__ ((packed));
+
+struct msdos_boot_sector {
+    __u8 boot_jump[3];		/* Boot strap short or near jump */
+    __u8 system_id[8];		/* Name - can be used to special case
+				   partition manager volumes */
+    __u8 sector_size[2];	/* bytes per logical sector */
+    __u8 cluster_size;		/* sectors/cluster */
+    __u16 reserved;		/* reserved sectors */
+    __u8 fats;			/* number of FATs */
+    __u8 dir_entries[2];	/* root directory entries */
+    __u8 sectors[2];		/* number of sectors */
+    __u8 media;			/* media code (unused) */
+    __u16 fat_length;		/* sectors/FAT */
+    __u16 secs_track;		/* sectors per track */
+    __u16 heads;		/* number of heads */
+    __u32 hidden;		/* hidden sectors (unused) */
+    __u32 total_sect;		/* number of sectors (if sectors == 0) */
+    union {
+	struct {
+	    struct msdos_volume_info vi;
+	    __u8 boot_code[BOOTCODE_SIZE];
+	} __attribute__ ((packed)) _oldfat;
+	struct {
+	    __u32 fat32_length;	/* sectors/FAT */
+	    __u16 flags;	/* bit 8: fat mirroring, low 4: active fat */
+	    __u8 version[2];	/* major, minor filesystem version */
+	    __u32 root_cluster;	/* first cluster in root directory */
+	    __u16 info_sector;	/* filesystem info sector */
+	    __u16 backup_boot;	/* backup boot sector */
+	    __u16 reserved2[6];	/* Unused */
+	    struct msdos_volume_info vi;
+	    __u8 boot_code[BOOTCODE_FAT32_SIZE];
+	} __attribute__ ((packed)) _fat32;
+    } __attribute__ ((packed)) fstype;
+    __u16 boot_sign;
+} __attribute__ ((packed));
+#define fat32	fstype._fat32
+#define oldfat	fstype._oldfat
+
+struct fat32_fsinfo {
+    __u32 reserved1;		/* Nothing as far as I can tell */
+    __u32 signature;		/* 0x61417272L */
+    __u32 free_clusters;	/* Free cluster count.  -1 if unknown */
+    __u32 next_cluster;		/* Most recently allocated cluster.
+				 * Unused under Linux. */
+    __u32 reserved2[4];
+};
+
+struct msdos_dir_entry {
+    char name[8], ext[3];	/* name and extension */
+    __u8 attr;			/* attribute bits */
+    __u8 lcase;			/* Case for base and extension */
+    __u8 ctime_ms;		/* Creation time, milliseconds */
+    __u16 ctime;		/* Creation time */
+    __u16 cdate;		/* Creation date */
+    __u16 adate;		/* Last access date */
+    __u16 starthi;		/* high 16 bits of first cl. (FAT32) */
+    __u16 time, date, start;	/* time, date and first cluster */
+    __u32 size;			/* file size (in bytes) */
+} __attribute__ ((packed));
+
+/* The "boot code" we put into the filesystem... it writes a message and
+   tells the user to try again */
+
+char dummy_boot_jump[3] = { 0xeb, 0x3c, 0x90 };
+
+char dummy_boot_jump_m68k[2] = { 0x60, 0x1c };
+
+#define MSG_OFFSET_OFFSET 3
+char dummy_boot_code[BOOTCODE_SIZE] = "\x0e"	/* push cs */
+    "\x1f"			/* pop ds */
+    "\xbe\x5b\x7c"		/* mov si, offset message_txt */
+    /* write_msg: */
+    "\xac"			/* lodsb */
+    "\x22\xc0"			/* and al, al */
+    "\x74\x0b"			/* jz key_press */
+    "\x56"			/* push si */
+    "\xb4\x0e"			/* mov ah, 0eh */
+    "\xbb\x07\x00"		/* mov bx, 0007h */
+    "\xcd\x10"			/* int 10h */
+    "\x5e"			/* pop si */
+    "\xeb\xf0"			/* jmp write_msg */
+    /* key_press: */
+    "\x32\xe4"			/* xor ah, ah */
+    "\xcd\x16"			/* int 16h */
+    "\xcd\x19"			/* int 19h */
+    "\xeb\xfe"			/* foo: jmp foo */
+    /* message_txt: */
+    "This is not a bootable disk.  Please insert a bootable floppy and\r\n"
+    "press any key to try again ... \r\n";
+
+#define MESSAGE_OFFSET 29	/* Offset of message in above code */
+
+/* Global variables - the root of all evil :-) - see these and weep! */
+
+static char *program_name = "mkdosfs";	/* Name of the program */
+static char *device_name = NULL;	/* Name of the device on which to create the filesystem */
+static int atari_format = 0;	/* Use Atari variation of MS-DOS FS format */
+static int check = FALSE;	/* Default to no readablity checking */
+static int verbose = 0;		/* Default to verbose mode off */
+static long volume_id;		/* Volume ID number */
+static time_t create_time;	/* Creation time */
+static struct timeval create_timeval;	/* Creation time */
+static char volume_name[] = "           ";	/* Volume name */
+static unsigned long long blocks;	/* Number of blocks in filesystem */
+static int sector_size = 512;	/* Size of a logical sector */
+static int sector_size_set = 0;	/* User selected sector size */
+static int backup_boot = 0;	/* Sector# of backup boot sector */
+static int reserved_sectors = 0;	/* Number of reserved sectors */
+static int badblocks = 0;	/* Number of bad blocks in the filesystem */
+static int nr_fats = 2;		/* Default number of FATs to produce */
+static int size_fat = 0;	/* Size in bits of FAT entries */
+static int size_fat_by_user = 0;	/* 1 if FAT size user selected */
+static int dev = -1;		/* FS block device file handle */
+static int ignore_full_disk = 0;	/* Ignore warning about 'full' disk devices */
+static off_t currently_testing = 0;	/* Block currently being tested (if autodetect bad blocks) */
+static struct msdos_boot_sector bs;	/* Boot sector data */
+static int start_data_sector;	/* Sector number for the start of the data area */
+static int start_data_block;	/* Block number for the start of the data area */
+static unsigned char *fat;	/* File allocation table */
+static unsigned alloced_fat_length;	/* # of FAT sectors we can keep in memory */
+static unsigned char *info_sector;	/* FAT32 info sector */
+static struct msdos_dir_entry *root_dir;	/* Root directory */
+static int size_root_dir;	/* Size of the root directory in bytes */
+static int sectors_per_cluster = 0;	/* Number of sectors per disk cluster */
+static int root_dir_entries = 0;	/* Number of root directory entries */
+static char *blank_sector;	/* Blank sector - all zeros */
+static int hidden_sectors = 0;	/* Number of hidden sectors */
+static int malloc_entire_fat = FALSE;	/* Whether we should malloc() the entire FAT or not */
+static int align_structures = TRUE;	/* Whether to enforce alignment */
+static int orphaned_sectors = 0;	/* Sectors that exist in the last block of filesystem */
+
+/* Function prototype definitions */
+
+static void fatal_error(const char *fmt_string) __attribute__ ((noreturn));
+static void mark_FAT_cluster(int cluster, unsigned int value);
+static void mark_FAT_sector(int sector, unsigned int value);
+static long do_check(char *buffer, int try, off_t current_block);
+static void alarm_intr(int alnum);
+static void check_blocks(void);
+static void get_list_blocks(char *filename);
+static int valid_offset(int fd, loff_t offset);
+static unsigned long long count_blocks(char *filename, int *remainder);
+static void check_mount(char *device_name);
+static void establish_params(int device_num, int size);
+static void setup_tables(void);
+static void write_tables(void);
+
+/* The function implementations */
+
+/* Handle the reporting of fatal errors.  Volatile to let gcc know that this doesn't return */
+
+static void fatal_error(const char *fmt_string)
+{
+    fprintf(stderr, fmt_string, program_name, device_name);
+    exit(1);			/* The error exit code is 1! */
+}
+
+/* Mark the specified cluster as having a particular value */
+
+static void mark_FAT_cluster(int cluster, unsigned int value)
+{
+    switch (size_fat) {
+    case 12:
+	value &= 0x0fff;
+	if (((cluster * 3) & 0x1) == 0) {
+	    fat[3 * cluster / 2] = (unsigned char)(value & 0x00ff);
+	    fat[(3 * cluster / 2) + 1] =
+		(unsigned char)((fat[(3 * cluster / 2) + 1] & 0x00f0)
+				| ((value & 0x0f00) >> 8));
+	} else {
+	    fat[3 * cluster / 2] =
+		(unsigned char)((fat[3 * cluster / 2] & 0x000f) |
+				((value & 0x000f) << 4));
+	    fat[(3 * cluster / 2) + 1] = (unsigned char)((value & 0x0ff0) >> 4);
+	}
+	break;
+
+    case 16:
+	value &= 0xffff;
+	fat[2 * cluster] = (unsigned char)(value & 0x00ff);
+	fat[(2 * cluster) + 1] = (unsigned char)(value >> 8);
+	break;
+
+    case 32:
+	value &= 0xfffffff;
+	fat[4 * cluster] = (unsigned char)(value & 0x000000ff);
+	fat[(4 * cluster) + 1] = (unsigned char)((value & 0x0000ff00) >> 8);
+	fat[(4 * cluster) + 2] = (unsigned char)((value & 0x00ff0000) >> 16);
+	fat[(4 * cluster) + 3] = (unsigned char)((value & 0xff000000) >> 24);
+	break;
+
+    default:
+	die("Bad FAT size (not 12, 16, or 32)");
+    }
+}
+
+/* Mark a specified sector as having a particular value in it's FAT entry */
+
+static void mark_FAT_sector(int sector, unsigned int value)
+{
+    int cluster;
+
+    cluster = (sector - start_data_sector) / (int)(bs.cluster_size) /
+	(sector_size / HARD_SECTOR_SIZE);
+    if (cluster < 0)
+	die("Invalid cluster number in mark_FAT_sector: probably bug!");
+
+    mark_FAT_cluster(cluster, value);
+}
+
+/* Perform a test on a block.  Return the number of blocks that could be read successfully */
+
+static long do_check(char *buffer, int try, off_t current_block)
+{
+    long got;
+
+    if (llseek(dev, current_block * BLOCK_SIZE, SEEK_SET)	/* Seek to the correct location */
+	!=current_block * BLOCK_SIZE)
+	die("seek failed during testing for blocks");
+
+    got = read(dev, buffer, try * BLOCK_SIZE);	/* Try reading! */
+    if (got < 0)
+	got = 0;
+
+    if (got & (BLOCK_SIZE - 1))
+	printf("Unexpected values in do_check: probably bugs\n");
+    got /= BLOCK_SIZE;
+
+    return got;
+}
+
+/* Alarm clock handler - display the status of the quest for bad blocks!  Then retrigger the alarm for five senconds
+   later (so we can come here again) */
+
+static void alarm_intr(int alnum)
+{
+    if (currently_testing >= blocks)
+	return;
+
+    signal(SIGALRM, alarm_intr);
+    alarm(5);
+    if (!currently_testing)
+	return;
+
+    printf("%lld... ", (unsigned long long)currently_testing);
+    fflush(stdout);
+}
+
+static void check_blocks(void)
+{
+    int try, got;
+    int i;
+    static char blkbuf[BLOCK_SIZE * TEST_BUFFER_BLOCKS];
+
+    if (verbose) {
+	printf("Searching for bad blocks ");
+	fflush(stdout);
+    }
+    currently_testing = 0;
+    if (verbose) {
+	signal(SIGALRM, alarm_intr);
+	alarm(5);
+    }
+    try = TEST_BUFFER_BLOCKS;
+    while (currently_testing < blocks) {
+	if (currently_testing + try > blocks)
+	    try = blocks - currently_testing;
+	got = do_check(blkbuf, try, currently_testing);
+	currently_testing += got;
+	if (got == try) {
+	    try = TEST_BUFFER_BLOCKS;
+	    continue;
+	} else
+	    try = 1;
+	if (currently_testing < start_data_block)
+	    die("bad blocks before data-area: cannot make fs");
+
+	for (i = 0; i < SECTORS_PER_BLOCK; i++)	/* Mark all of the sectors in the block as bad */
+	    mark_sector_bad(currently_testing * SECTORS_PER_BLOCK + i);
+	badblocks++;
+	currently_testing++;
+    }
+
+    if (verbose)
+	printf("\n");
+
+    if (badblocks)
+	printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : "");
+}
+
+static void get_list_blocks(char *filename)
+{
+    int i;
+    FILE *listfile;
+    unsigned long blockno;
+
+    listfile = fopen(filename, "r");
+    if (listfile == (FILE *) NULL)
+	die("Can't open file of bad blocks");
+
+    while (!feof(listfile)) {
+	fscanf(listfile, "%ld\n", &blockno);
+	for (i = 0; i < SECTORS_PER_BLOCK; i++)	/* Mark all of the sectors in the block as bad */
+	    mark_sector_bad(blockno * SECTORS_PER_BLOCK + i);
+	badblocks++;
+    }
+    fclose(listfile);
+
+    if (badblocks)
+	printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : "");
+}
+
+/* Given a file descriptor and an offset, check whether the offset is a valid offset for the file - return FALSE if it
+   isn't valid or TRUE if it is */
+
+static int valid_offset(int fd, loff_t offset)
+{
+    char ch;
+
+    if (llseek(fd, offset, SEEK_SET) < 0)
+	return FALSE;
+    if (read(fd, &ch, 1) < 1)
+	return FALSE;
+    return TRUE;
+}
+
+/* Given a filename, look to see how many blocks of BLOCK_SIZE are present, returning the answer */
+
+static unsigned long long count_blocks(char *filename, int *remainder)
+
+{
+    loff_t high, low;
+    int fd;
+
+    if ((fd = open(filename, O_RDONLY)) < 0) {
+	perror(filename);
+	exit(1);
+    }
+
+    /* first try SEEK_END, which should work on most devices nowadays */
+    if ((low = llseek(fd, 0, SEEK_END)) <= 0) {
+	low = 0;
+	for (high = 1; valid_offset(fd, high); high *= 2)
+	    low = high;
+	while (low < high - 1) {
+	    const loff_t mid = (low + high) / 2;
+	    if (valid_offset(fd, mid))
+		low = mid;
+	    else
+		high = mid;
+	}
+	++low;
+    }
+
+    close(fd);
+    *remainder = (low%BLOCK_SIZE)/sector_size;
+    return(low / BLOCK_SIZE);
+}
+
+/* Check to see if the specified device is currently mounted - abort if it is */
+
+static void check_mount(char *device_name)
+{
+#if ! defined(_USING_BIONIC_)
+    FILE *f;
+    struct mntent *mnt;
+
+    if ((f = setmntent(MOUNTED, "r")) == NULL)
+	return;
+    while ((mnt = getmntent(f)) != NULL)
+	if (strcmp(device_name, mnt->mnt_fsname) == 0)
+	    die("%s contains a mounted file system.");
+    endmntent(f);
+#endif
+}
+
+/* Establish the geometry and media parameters for the device */
+
+static void establish_params(int device_num, int size)
+{
+    long loop_size;
+    struct hd_geometry geometry;
+    struct floppy_struct param;
+    int def_root_dir_entries = 512;
+
+    if ((0 == device_num) || ((device_num & 0xff00) == 0x0200))
+	/* file image or floppy disk */
+    {
+	if (0 == device_num) {
+	    param.size = size / 512;
+	    switch (param.size) {
+	    case 720:
+		param.sect = 9;
+		param.head = 2;
+		break;
+	    case 1440:
+		param.sect = 9;
+		param.head = 2;
+		break;
+	    case 2400:
+		param.sect = 15;
+		param.head = 2;
+		break;
+	    case 2880:
+		param.sect = 18;
+		param.head = 2;
+		break;
+	    case 5760:
+		param.sect = 36;
+		param.head = 2;
+		break;
+	    default:
+		/* fake values */
+		param.sect = 32;
+		param.head = 64;
+		break;
+	    }
+
+	} else {		/* is a floppy diskette */
+
+	    if (ioctl(dev, FDGETPRM, &param))	/*  Can we get the diskette geometry? */
+		die("unable to get diskette geometry for '%s'");
+	}
+	bs.secs_track = CT_LE_W(param.sect);	/*  Set up the geometry information */
+	bs.heads = CT_LE_W(param.head);
+	switch (param.size) {	/*  Set up the media descriptor byte */
+	case 720:		/* 5.25", 2, 9, 40 - 360K */
+	    bs.media = (char)0xfd;
+	    bs.cluster_size = (char)2;
+	    def_root_dir_entries = 112;
+	    break;
+
+	case 1440:		/* 3.5", 2, 9, 80 - 720K */
+	    bs.media = (char)0xf9;
+	    bs.cluster_size = (char)2;
+	    def_root_dir_entries = 112;
+	    break;
+
+	case 2400:		/* 5.25", 2, 15, 80 - 1200K */
+	    bs.media = (char)0xf9;
+	    bs.cluster_size = (char)(atari_format ? 2 : 1);
+	    def_root_dir_entries = 224;
+	    break;
+
+	case 5760:		/* 3.5", 2, 36, 80 - 2880K */
+	    bs.media = (char)0xf0;
+	    bs.cluster_size = (char)2;
+	    def_root_dir_entries = 224;
+	    break;
+
+	case 2880:		/* 3.5", 2, 18, 80 - 1440K */
+floppy_default:
+	    bs.media = (char)0xf0;
+	    bs.cluster_size = (char)(atari_format ? 2 : 1);
+	    def_root_dir_entries = 224;
+	    break;
+
+	default:		/* Anything else */
+	    if (0 == device_num)
+		goto def_hd_params;
+	    else
+		goto floppy_default;
+	}
+    } else if ((device_num & 0xff00) == 0x0700) {	/* This is a loop device */
+	if (ioctl(dev, BLKGETSIZE, &loop_size))
+	    die("unable to get loop device size");
+
+	switch (loop_size) {	/* Assuming the loop device -> floppy later */
+	case 720:		/* 5.25", 2, 9, 40 - 360K */
+	    bs.secs_track = CF_LE_W(9);
+	    bs.heads = CF_LE_W(2);
+	    bs.media = (char)0xfd;
+	    bs.cluster_size = (char)2;
+	    def_root_dir_entries = 112;
+	    break;
+
+	case 1440:		/* 3.5", 2, 9, 80 - 720K */
+	    bs.secs_track = CF_LE_W(9);
+	    bs.heads = CF_LE_W(2);
+	    bs.media = (char)0xf9;
+	    bs.cluster_size = (char)2;
+	    def_root_dir_entries = 112;
+	    break;
+
+	case 2400:		/* 5.25", 2, 15, 80 - 1200K */
+	    bs.secs_track = CF_LE_W(15);
+	    bs.heads = CF_LE_W(2);
+	    bs.media = (char)0xf9;
+	    bs.cluster_size = (char)(atari_format ? 2 : 1);
+	    def_root_dir_entries = 224;
+	    break;
+
+	case 5760:		/* 3.5", 2, 36, 80 - 2880K */
+	    bs.secs_track = CF_LE_W(36);
+	    bs.heads = CF_LE_W(2);
+	    bs.media = (char)0xf0;
+	    bs.cluster_size = (char)2;
+	    bs.dir_entries[0] = (char)224;
+	    bs.dir_entries[1] = (char)0;
+	    break;
+
+	case 2880:		/* 3.5", 2, 18, 80 - 1440K */
+	    bs.secs_track = CF_LE_W(18);
+	    bs.heads = CF_LE_W(2);
+	    bs.media = (char)0xf0;
+	    bs.cluster_size = (char)(atari_format ? 2 : 1);
+	    def_root_dir_entries = 224;
+	    break;
+
+	default:		/* Anything else: default hd setup */
+	    printf("Loop device does not match a floppy size, using "
+		   "default hd params\n");
+	    bs.secs_track = CT_LE_W(32);	/* these are fake values... */
+	    bs.heads = CT_LE_W(64);
+	    goto def_hd_params;
+	}
+    } else
+	/* Must be a hard disk then! */
+    {
+	/* Can we get the drive geometry? (Note I'm not too sure about */
+	/* whether to use HDIO_GETGEO or HDIO_REQ) */
+	if (ioctl(dev, HDIO_GETGEO, &geometry) || geometry.sectors == 0
+	    || geometry.heads == 0) {
+	    printf("unable to get drive geometry, using default 255/63\n");
+	    bs.secs_track = CT_LE_W(63);
+	    bs.heads = CT_LE_W(255);
+	} else {
+	    bs.secs_track = CT_LE_W(geometry.sectors);	/* Set up the geometry information */
+	    bs.heads = CT_LE_W(geometry.heads);
+	}
+def_hd_params:
+	bs.media = (char)0xf8;	/* Set up the media descriptor for a hard drive */
+	if (!size_fat && blocks * SECTORS_PER_BLOCK > 1064960) {
+	    if (verbose)
+		printf("Auto-selecting FAT32 for large filesystem\n");
+	    size_fat = 32;
+	}
+	if (size_fat == 32) {
+	    /* For FAT32, try to do the same as M$'s format command
+	     * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20):
+	     * fs size <= 260M: 0.5k clusters
+	     * fs size <=   8G: 4k clusters
+	     * fs size <=  16G: 8k clusters
+	     * fs size >   16G: 16k clusters
+	     */
+	    unsigned long sz_mb =
+		(blocks + (1 << (20 - BLOCK_SIZE_BITS)) - 1) >> (20 -
+								 BLOCK_SIZE_BITS);
+	    bs.cluster_size =
+		sz_mb > 16 * 1024 ? 32 : sz_mb > 8 * 1024 ? 16 : sz_mb >
+		260 ? 8 : 1;
+	} else {
+	    /* FAT12 and FAT16: start at 4 sectors per cluster */
+	    bs.cluster_size = (char)4;
+	}
+    }
+
+    if (!root_dir_entries)
+	root_dir_entries = def_root_dir_entries;
+}
+
+/*
+ * If alignment is enabled, round the first argument up to the second; the
+ * latter must be a power of two.
+ */
+static unsigned int align_object(unsigned int sectors, unsigned int clustsize)
+{
+    if (align_structures)
+	return (sectors + clustsize - 1) & ~(clustsize - 1);
+    else
+	return sectors;
+}
+
+/* Create the filesystem data tables */
+
+static void setup_tables(void)
+{
+    unsigned num_sectors;
+    unsigned cluster_count = 0, fat_length;
+    struct tm *ctime;
+    struct msdos_volume_info *vi =
+	(size_fat == 32 ? &bs.fat32.vi : &bs.oldfat.vi);
+
+    if (atari_format)
+	/* On Atari, the first few bytes of the boot sector are assigned
+	 * differently: The jump code is only 2 bytes (and m68k machine code
+	 * :-), then 6 bytes filler (ignored), then 3 byte serial number. */
+	memcpy(bs.system_id - 1, "mkdosf", 6);
+    else
+	strcpy((char *)bs.system_id, "mkdosfs");
+    if (sectors_per_cluster)
+	bs.cluster_size = (char)sectors_per_cluster;
+    if (size_fat == 32) {
+	/* Under FAT32, the root dir is in a cluster chain, and this is
+	 * signalled by bs.dir_entries being 0. */
+	root_dir_entries = 0;
+    }
+
+    if (atari_format) {
+	bs.system_id[5] = (unsigned char)(volume_id & 0x000000ff);
+	bs.system_id[6] = (unsigned char)((volume_id & 0x0000ff00) >> 8);
+	bs.system_id[7] = (unsigned char)((volume_id & 0x00ff0000) >> 16);
+    } else {
+	vi->volume_id[0] = (unsigned char)(volume_id & 0x000000ff);
+	vi->volume_id[1] = (unsigned char)((volume_id & 0x0000ff00) >> 8);
+	vi->volume_id[2] = (unsigned char)((volume_id & 0x00ff0000) >> 16);
+	vi->volume_id[3] = (unsigned char)(volume_id >> 24);
+    }
+
+    if (!atari_format) {
+	memcpy(vi->volume_label, volume_name, 11);
+
+	memcpy(bs.boot_jump, dummy_boot_jump, 3);
+	/* Patch in the correct offset to the boot code */
+	bs.boot_jump[1] = ((size_fat == 32 ?
+			    (char *)&bs.fat32.boot_code :
+			    (char *)&bs.oldfat.boot_code) - (char *)&bs) - 2;
+
+	if (size_fat == 32) {
+	    int offset = (char *)&bs.fat32.boot_code -
+		(char *)&bs + MESSAGE_OFFSET + 0x7c00;
+	    if (dummy_boot_code[BOOTCODE_FAT32_SIZE - 1])
+		printf("Warning: message too long; truncated\n");
+	    dummy_boot_code[BOOTCODE_FAT32_SIZE - 1] = 0;
+	    memcpy(bs.fat32.boot_code, dummy_boot_code, BOOTCODE_FAT32_SIZE);
+	    bs.fat32.boot_code[MSG_OFFSET_OFFSET] = offset & 0xff;
+	    bs.fat32.boot_code[MSG_OFFSET_OFFSET + 1] = offset >> 8;
+	} else {
+	    memcpy(bs.oldfat.boot_code, dummy_boot_code, BOOTCODE_SIZE);
+	}
+	bs.boot_sign = CT_LE_W(BOOT_SIGN);
+    } else {
+	memcpy(bs.boot_jump, dummy_boot_jump_m68k, 2);
+    }
+    if (verbose >= 2)
+	printf("Boot jump code is %02x %02x\n",
+	       bs.boot_jump[0], bs.boot_jump[1]);
+
+    if (!reserved_sectors)
+	reserved_sectors = (size_fat == 32) ? 32 : 1;
+    else {
+	if (size_fat == 32 && reserved_sectors < 2)
+	    die("On FAT32 at least 2 reserved sectors are needed.");
+    }
+    bs.reserved = CT_LE_W(reserved_sectors);
+    if (verbose >= 2)
+	printf("Using %d reserved sectors\n", reserved_sectors);
+    bs.fats = (char)nr_fats;
+    if (!atari_format || size_fat == 32)
+	bs.hidden = CT_LE_L(hidden_sectors);
+    else {
+	/* In Atari format, hidden is a 16 bit field */
+	__u16 hidden = CT_LE_W(hidden_sectors);
+	if (hidden_sectors & ~0xffff)
+	    die("#hidden doesn't fit in 16bit field of Atari format\n");
+	memcpy(&bs.hidden, &hidden, 2);
+    }
+
+    num_sectors = (long long)(blocks *BLOCK_SIZE / sector_size)+orphaned_sectors;
+
+    if (!atari_format) {
+	unsigned fatdata1216;	/* Sectors for FATs + data area (FAT12/16) */
+	unsigned fatdata32;	/* Sectors for FATs + data area (FAT32) */
+	unsigned fatlength12, fatlength16, fatlength32;
+	unsigned maxclust12, maxclust16, maxclust32;
+	unsigned clust12, clust16, clust32;
+	int maxclustsize;
+	unsigned root_dir_sectors = cdiv(root_dir_entries * 32, sector_size);
+
+	/*
+	 * If the filesystem is 8192 sectors or less (4 MB with 512-byte
+	 * sectors, i.e. floppy size), don't align the data structures.
+	 */
+	if (num_sectors <= 8192) {
+	    if (align_structures && verbose >= 2)
+		printf("Disabling alignment due to tiny filesystem\n");
+
+	    align_structures = FALSE;
+	}
+
+	if (sectors_per_cluster)
+	    bs.cluster_size = maxclustsize = sectors_per_cluster;
+	else
+	    /* An initial guess for bs.cluster_size should already be set */
+	    maxclustsize = 128;
+
+	do {
+	    fatdata32 = num_sectors
+		- align_object(reserved_sectors, bs.cluster_size);
+	    fatdata1216 = fatdata32
+		- align_object(root_dir_sectors, bs.cluster_size);
+
+	    if (verbose >= 2)
+		printf("Trying with %d sectors/cluster:\n", bs.cluster_size);
+
+	    /* The factor 2 below avoids cut-off errors for nr_fats == 1.
+	     * The "nr_fats*3" is for the reserved first two FAT entries */
+	    clust12 = 2 * ((long long)fatdata1216 * sector_size + nr_fats * 3) /
+		(2 * (int)bs.cluster_size * sector_size + nr_fats * 3);
+	    fatlength12 = cdiv(((clust12 + 2) * 3 + 1) >> 1, sector_size);
+	    fatlength12 = align_object(fatlength12, bs.cluster_size);
+	    /* Need to recalculate number of clusters, since the unused parts of the
+	     * FATS and data area together could make up space for an additional,
+	     * not really present cluster. */
+	    clust12 = (fatdata1216 - nr_fats * fatlength12) / bs.cluster_size;
+	    maxclust12 = (fatlength12 * 2 * sector_size) / 3;
+	    if (maxclust12 > MAX_CLUST_12)
+		maxclust12 = MAX_CLUST_12;
+	    if (verbose >= 2)
+		printf("FAT12: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n",
+		       clust12, fatlength12, maxclust12, MAX_CLUST_12);
+	    if (clust12 > maxclust12 - 2) {
+		clust12 = 0;
+		if (verbose >= 2)
+		    printf("FAT12: too much clusters\n");
+	    }
+
+	    clust16 = ((long long)fatdata1216 * sector_size + nr_fats * 4) /
+		((int)bs.cluster_size * sector_size + nr_fats * 2);
+	    fatlength16 = cdiv((clust16 + 2) * 2, sector_size);
+	    fatlength16 = align_object(fatlength16, bs.cluster_size);
+	    /* Need to recalculate number of clusters, since the unused parts of the
+	     * FATS and data area together could make up space for an additional,
+	     * not really present cluster. */
+	    clust16 = (fatdata1216 - nr_fats * fatlength16) / bs.cluster_size;
+	    maxclust16 = (fatlength16 * sector_size) / 2;
+	    if (maxclust16 > MAX_CLUST_16)
+		maxclust16 = MAX_CLUST_16;
+	    if (verbose >= 2)
+		printf("FAT16: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n",
+		       clust16, fatlength16, maxclust16, MAX_CLUST_16);
+	    if (clust16 > maxclust16 - 2) {
+		if (verbose >= 2)
+		    printf("FAT16: too much clusters\n");
+		clust16 = 0;
+	    }
+	    /* The < 4078 avoids that the filesystem will be misdetected as having a
+	     * 12 bit FAT. */
+	    if (clust16 < FAT12_THRESHOLD
+		&& !(size_fat_by_user && size_fat == 16)) {
+		if (verbose >= 2)
+		    printf(clust16 < FAT12_THRESHOLD ?
+			   "FAT16: would be misdetected as FAT12\n" :
+			   "FAT16: too much clusters\n");
+		clust16 = 0;
+	    }
+
+	    clust32 = ((long long)fatdata32 * sector_size + nr_fats * 8) /
+		((int)bs.cluster_size * sector_size + nr_fats * 4);
+	    fatlength32 = cdiv((clust32 + 2) * 4, sector_size);
+	    fatlength32 = align_object(fatlength32, bs.cluster_size);
+	    /* Need to recalculate number of clusters, since the unused parts of the
+	     * FATS and data area together could make up space for an additional,
+	     * not really present cluster. */
+	    clust32 = (fatdata32 - nr_fats * fatlength32) / bs.cluster_size;
+	    maxclust32 = (fatlength32 * sector_size) / 4;
+	    if (maxclust32 > MAX_CLUST_32)
+		maxclust32 = MAX_CLUST_32;
+	    if (clust32 && clust32 < MIN_CLUST_32
+		&& !(size_fat_by_user && size_fat == 32)) {
+		clust32 = 0;
+		if (verbose >= 2)
+		    printf("FAT32: not enough clusters (%d)\n", MIN_CLUST_32);
+	    }
+	    if (verbose >= 2)
+		printf("FAT32: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n",
+		       clust32, fatlength32, maxclust32, MAX_CLUST_32);
+	    if (clust32 > maxclust32) {
+		clust32 = 0;
+		if (verbose >= 2)
+		    printf("FAT32: too much clusters\n");
+	    }
+
+	    if ((clust12 && (size_fat == 0 || size_fat == 12)) ||
+		(clust16 && (size_fat == 0 || size_fat == 16)) ||
+		(clust32 && size_fat == 32))
+		break;
+
+	    bs.cluster_size <<= 1;
+	} while (bs.cluster_size && bs.cluster_size <= maxclustsize);
+
+	/* Use the optimal FAT size if not specified;
+	 * FAT32 is (not yet) choosen automatically */
+	if (!size_fat) {
+	    size_fat = (clust16 > clust12) ? 16 : 12;
+	    if (verbose >= 2)
+		printf("Choosing %d bits for FAT\n", size_fat);
+	}
+
+	switch (size_fat) {
+	case 12:
+	    cluster_count = clust12;
+	    fat_length = fatlength12;
+	    bs.fat_length = CT_LE_W(fatlength12);
+	    memcpy(vi->fs_type, MSDOS_FAT12_SIGN, 8);
+	    break;
+
+	case 16:
+	    if (clust16 < FAT12_THRESHOLD) {
+		if (size_fat_by_user) {
+		    fprintf(stderr, "WARNING: Not enough clusters for a "
+			    "16 bit FAT! The filesystem will be\n"
+			    "misinterpreted as having a 12 bit FAT without "
+			    "mount option \"fat=16\".\n");
+		} else {
+		    fprintf(stderr, "This filesystem has an unfortunate size. "
+			    "A 12 bit FAT cannot provide\n"
+			    "enough clusters, but a 16 bit FAT takes up a little "
+			    "bit more space so that\n"
+			    "the total number of clusters becomes less than the "
+			    "threshold value for\n"
+			    "distinction between 12 and 16 bit FATs.\n");
+		    die("Make the file system a bit smaller manually.");
+		}
+	    }
+	    cluster_count = clust16;
+	    fat_length = fatlength16;
+	    bs.fat_length = CT_LE_W(fatlength16);
+	    memcpy(vi->fs_type, MSDOS_FAT16_SIGN, 8);
+	    break;
+
+	case 32:
+	    if (clust32 < MIN_CLUST_32)
+		fprintf(stderr,
+			"WARNING: Not enough clusters for a 32 bit FAT!\n");
+	    cluster_count = clust32;
+	    fat_length = fatlength32;
+	    bs.fat_length = CT_LE_W(0);
+	    bs.fat32.fat32_length = CT_LE_L(fatlength32);
+	    memcpy(vi->fs_type, MSDOS_FAT32_SIGN, 8);
+	    root_dir_entries = 0;
+	    break;
+
+	default:
+	    die("FAT not 12, 16 or 32 bits");
+	}
+
+	/* Adjust the reserved number of sectors for alignment */
+	reserved_sectors = align_object(reserved_sectors, bs.cluster_size);
+	bs.reserved = CT_LE_W(reserved_sectors);
+
+	/* Adjust the number of root directory entries to help enforce alignment */
+	if (align_structures) {
+	    root_dir_entries = align_object(root_dir_sectors, bs.cluster_size)
+		* (sector_size >> 5);
+	}
+    } else {
+	unsigned clusters, maxclust, fatdata;
+
+	/* GEMDOS always uses a 12 bit FAT on floppies, and always a 16 bit FAT on
+	 * hard disks. So use 12 bit if the size of the file system suggests that
+	 * this fs is for a floppy disk, if the user hasn't explicitly requested a
+	 * size.
+	 */
+	if (!size_fat)
+	    size_fat = (num_sectors == 1440 || num_sectors == 2400 ||
+			num_sectors == 2880 || num_sectors == 5760) ? 12 : 16;
+	if (verbose >= 2)
+	    printf("Choosing %d bits for FAT\n", size_fat);
+
+	/* Atari format: cluster size should be 2, except explicitly requested by
+	 * the user, since GEMDOS doesn't like other cluster sizes very much.
+	 * Instead, tune the sector size for the FS to fit.
+	 */
+	bs.cluster_size = sectors_per_cluster ? sectors_per_cluster : 2;
+	if (!sector_size_set) {
+	    while (num_sectors > GEMDOS_MAX_SECTORS) {
+		num_sectors >>= 1;
+		sector_size <<= 1;
+	    }
+	}
+	if (verbose >= 2)
+	    printf("Sector size must be %d to have less than %d log. sectors\n",
+		   sector_size, GEMDOS_MAX_SECTORS);
+
+	/* Check if there are enough FAT indices for how much clusters we have */
+	do {
+	    fatdata = num_sectors - cdiv(root_dir_entries * 32, sector_size) -
+		reserved_sectors;
+	    /* The factor 2 below avoids cut-off errors for nr_fats == 1 and
+	     * size_fat == 12
+	     * The "2*nr_fats*size_fat/8" is for the reserved first two FAT entries
+	     */
+	    clusters =
+		(2 *
+		 ((long long)fatdata * sector_size -
+		  2 * nr_fats * size_fat / 8)) / (2 * ((int)bs.cluster_size *
+						       sector_size +
+						       nr_fats * size_fat / 8));
+	    fat_length = cdiv((clusters + 2) * size_fat / 8, sector_size);
+	    /* Need to recalculate number of clusters, since the unused parts of the
+	     * FATS and data area together could make up space for an additional,
+	     * not really present cluster. */
+	    clusters = (fatdata - nr_fats * fat_length) / bs.cluster_size;
+	    maxclust = (fat_length * sector_size * 8) / size_fat;
+	    if (verbose >= 2)
+		printf("ss=%d: #clu=%d, fat_len=%d, maxclu=%d\n",
+		       sector_size, clusters, fat_length, maxclust);
+
+	    /* last 10 cluster numbers are special (except FAT32: 4 high bits rsvd);
+	     * first two numbers are reserved */
+	    if (maxclust <=
+		(size_fat == 32 ? MAX_CLUST_32 : (1 << size_fat) - 0x10)
+		&& clusters <= maxclust - 2)
+		break;
+	    if (verbose >= 2)
+		printf(clusters > maxclust - 2 ?
+		       "Too many clusters\n" : "FAT too big\n");
+
+	    /* need to increment sector_size once more to  */
+	    if (sector_size_set)
+		die("With this sector size, the maximum number of FAT entries "
+		    "would be exceeded.");
+	    num_sectors >>= 1;
+	    sector_size <<= 1;
+	} while (sector_size <= GEMDOS_MAX_SECTOR_SIZE);
+
+	if (sector_size > GEMDOS_MAX_SECTOR_SIZE)
+	    die("Would need a sector size > 16k, which GEMDOS can't work with");
+
+	cluster_count = clusters;
+	if (size_fat != 32)
+	    bs.fat_length = CT_LE_W(fat_length);
+	else {
+	    bs.fat_length = 0;
+	    bs.fat32.fat32_length = CT_LE_L(fat_length);
+	}
+    }
+
+    bs.sector_size[0] = (char)(sector_size & 0x00ff);
+    bs.sector_size[1] = (char)((sector_size & 0xff00) >> 8);
+
+    bs.dir_entries[0] = (char)(root_dir_entries & 0x00ff);
+    bs.dir_entries[1] = (char)((root_dir_entries & 0xff00) >> 8);
+
+    if (size_fat == 32) {
+	/* set up additional FAT32 fields */
+	bs.fat32.flags = CT_LE_W(0);
+	bs.fat32.version[0] = 0;
+	bs.fat32.version[1] = 0;
+	bs.fat32.root_cluster = CT_LE_L(2);
+	bs.fat32.info_sector = CT_LE_W(1);
+	if (!backup_boot)
+	    backup_boot = (reserved_sectors >= 7) ? 6 :
+		(reserved_sectors >= 2) ? reserved_sectors - 1 : 0;
+	else {
+	    if (backup_boot == 1)
+		die("Backup boot sector must be after sector 1");
+	    else if (backup_boot >= reserved_sectors)
+		die("Backup boot sector must be a reserved sector");
+	}
+	if (verbose >= 2)
+	    printf("Using sector %d as backup boot sector (0 = none)\n",
+		   backup_boot);
+	bs.fat32.backup_boot = CT_LE_W(backup_boot);
+	memset(&bs.fat32.reserved2, 0, sizeof(bs.fat32.reserved2));
+    }
+
+    if (atari_format) {
+	/* Just some consistency checks */
+	if (num_sectors >= GEMDOS_MAX_SECTORS)
+	    die("GEMDOS can't handle more than 65531 sectors");
+	else if (num_sectors >= OLDGEMDOS_MAX_SECTORS)
+	    printf("Warning: More than 32765 sector need TOS 1.04 "
+		   "or higher.\n");
+    }
+    if (num_sectors >= 65536) {
+	bs.sectors[0] = (char)0;
+	bs.sectors[1] = (char)0;
+	bs.total_sect = CT_LE_L(num_sectors);
+    } else {
+	bs.sectors[0] = (char)(num_sectors & 0x00ff);
+	bs.sectors[1] = (char)((num_sectors & 0xff00) >> 8);
+	if (!atari_format)
+	    bs.total_sect = CT_LE_L(0);
+    }
+
+    if (!atari_format)
+	vi->ext_boot_sign = MSDOS_EXT_SIGN;
+
+    if (!cluster_count) {
+	if (sectors_per_cluster)	/* If yes, die if we'd spec'd sectors per cluster */
+	    die("Too many clusters for file system - try more sectors per cluster");
+	else
+	    die("Attempting to create a too large file system");
+    }
+
+    /* The two following vars are in hard sectors, i.e. 512 byte sectors! */
+    start_data_sector = (reserved_sectors + nr_fats * fat_length) *
+	(sector_size / HARD_SECTOR_SIZE);
+    start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) /
+	SECTORS_PER_BLOCK;
+
+    if (blocks < start_data_block + 32)	/* Arbitrary undersize file system! */
+	die("Too few blocks for viable file system");
+
+    if (verbose) {
+	printf("%s has %d head%s and %d sector%s per track,\n",
+	       device_name, CF_LE_W(bs.heads),
+	       (CF_LE_W(bs.heads) != 1) ? "s" : "", CF_LE_W(bs.secs_track),
+	       (CF_LE_W(bs.secs_track) != 1) ? "s" : "");
+	printf("logical sector size is %d,\n", sector_size);
+	printf("using 0x%02x media descriptor, with %d sectors;\n",
+	       (int)(bs.media), num_sectors);
+	printf("file system has %d %d-bit FAT%s and %d sector%s per cluster.\n",
+	       (int)(bs.fats), size_fat, (bs.fats != 1) ? "s" : "",
+	       (int)(bs.cluster_size), (bs.cluster_size != 1) ? "s" : "");
+	printf("FAT size is %d sector%s, and provides %d cluster%s.\n",
+	       fat_length, (fat_length != 1) ? "s" : "",
+	       cluster_count, (cluster_count != 1) ? "s" : "");
+	printf("There %s %u reserved sector%s.\n",
+	       (reserved_sectors != 1) ? "are" : "is",
+	       reserved_sectors, (reserved_sectors != 1) ? "s" : "");
+
+	if (size_fat != 32) {
+	    unsigned root_dir_entries =
+		bs.dir_entries[0] + ((bs.dir_entries[1]) * 256);
+	    unsigned root_dir_sectors =
+		cdiv(root_dir_entries * 32, sector_size);
+	    printf("Root directory contains %u slots and uses %u sectors.\n",
+		   root_dir_entries, root_dir_sectors);
+	}
+	printf("Volume ID is %08lx, ", volume_id &
+	       (atari_format ? 0x00ffffff : 0xffffffff));
+	if (strcmp(volume_name, "           "))
+	    printf("volume label %s.\n", volume_name);
+	else
+	    printf("no volume label.\n");
+    }
+
+    /* Make the file allocation tables! */
+
+    if (malloc_entire_fat)
+	alloced_fat_length = fat_length;
+    else
+	alloced_fat_length = 1;
+
+    if ((fat =
+	 (unsigned char *)malloc(alloced_fat_length * sector_size)) == NULL)
+	die("unable to allocate space for FAT image in memory");
+
+    memset(fat, 0, alloced_fat_length * sector_size);
+
+    mark_FAT_cluster(0, 0xffffffff);	/* Initial fat entries */
+    mark_FAT_cluster(1, 0xffffffff);
+    fat[0] = (unsigned char)bs.media;	/* Put media type in first byte! */
+    if (size_fat == 32) {
+	/* Mark cluster 2 as EOF (used for root dir) */
+	mark_FAT_cluster(2, FAT_EOF);
+    }
+
+    /* Make the root directory entries */
+
+    size_root_dir = (size_fat == 32) ?
+	bs.cluster_size * sector_size :
+	(((int)bs.dir_entries[1] * 256 + (int)bs.dir_entries[0]) *
+	 sizeof(struct msdos_dir_entry));
+    if ((root_dir = (struct msdos_dir_entry *)malloc(size_root_dir)) == NULL) {
+	free(fat);		/* Tidy up before we die! */
+	die("unable to allocate space for root directory in memory");
+    }
+
+    memset(root_dir, 0, size_root_dir);
+    if (memcmp(volume_name, "           ", 11)) {
+	struct msdos_dir_entry *de = &root_dir[0];
+	memcpy(de->name, volume_name, 8);
+	memcpy(de->ext, volume_name + 8, 3);
+	de->attr = ATTR_VOLUME;
+	ctime = localtime(&create_time);
+	de->time = CT_LE_W((unsigned short)((ctime->tm_sec >> 1) +
+					    (ctime->tm_min << 5) +
+					    (ctime->tm_hour << 11)));
+	de->date =
+	    CT_LE_W((unsigned short)(ctime->tm_mday +
+				     ((ctime->tm_mon + 1) << 5) +
+				     ((ctime->tm_year - 80) << 9)));
+	de->ctime_ms = 0;
+	de->ctime = de->time;
+	de->cdate = de->date;
+	de->adate = de->date;
+	de->starthi = CT_LE_W(0);
+	de->start = CT_LE_W(0);
+	de->size = CT_LE_L(0);
+    }
+
+    if (size_fat == 32) {
+	/* For FAT32, create an info sector */
+	struct fat32_fsinfo *info;
+
+	if (!(info_sector = malloc(sector_size)))
+	    die("Out of memory");
+	memset(info_sector, 0, sector_size);
+	/* fsinfo structure is at offset 0x1e0 in info sector by observation */
+	info = (struct fat32_fsinfo *)(info_sector + 0x1e0);
+
+	/* Info sector magic */
+	info_sector[0] = 'R';
+	info_sector[1] = 'R';
+	info_sector[2] = 'a';
+	info_sector[3] = 'A';
+
+	/* Magic for fsinfo structure */
+	info->signature = CT_LE_L(0x61417272);
+	/* We've allocated cluster 2 for the root dir. */
+	info->free_clusters = CT_LE_L(cluster_count - 1);
+	info->next_cluster = CT_LE_L(2);
+
+	/* Info sector also must have boot sign */
+	*(__u16 *) (info_sector + 0x1fe) = CT_LE_W(BOOT_SIGN);
+    }
+
+    if (!(blank_sector = malloc(sector_size)))
+	die("Out of memory");
+    memset(blank_sector, 0, sector_size);
+}
+
+/* Write the new filesystem's data tables to wherever they're going to end up! */
+
+#define error(str)				\
+  do {						\
+    free (fat);					\
+    if (info_sector) free (info_sector);	\
+    free (root_dir);				\
+    die (str);					\
+  } while(0)
+
+#define seekto(pos,errstr)						\
+  do {									\
+    loff_t __pos = (pos);						\
+    if (llseek (dev, __pos, SEEK_SET) != __pos)				\
+	error ("seek to " errstr " failed whilst writing tables");	\
+  } while(0)
+
+#define writebuf(buf,size,errstr)			\
+  do {							\
+    int __size = (size);				\
+    if (write (dev, buf, __size) != __size)		\
+	error ("failed whilst writing " errstr);	\
+  } while(0)
+
+static void write_tables(void)
+{
+    int x;
+    int fat_length;
+
+    fat_length = (size_fat == 32) ?
+	CF_LE_L(bs.fat32.fat32_length) : CF_LE_W(bs.fat_length);
+
+    seekto(0, "start of device");
+    /* clear all reserved sectors */
+    for (x = 0; x < reserved_sectors; ++x)
+	writebuf(blank_sector, sector_size, "reserved sector");
+    /* seek back to sector 0 and write the boot sector */
+    seekto(0, "boot sector");
+    writebuf((char *)&bs, sizeof(struct msdos_boot_sector), "boot sector");
+    /* on FAT32, write the info sector and backup boot sector */
+    if (size_fat == 32) {
+	seekto(CF_LE_W(bs.fat32.info_sector) * sector_size, "info sector");
+	writebuf(info_sector, 512, "info sector");
+	if (backup_boot != 0) {
+	    seekto(backup_boot * sector_size, "backup boot sector");
+	    writebuf((char *)&bs, sizeof(struct msdos_boot_sector),
+		     "backup boot sector");
+	}
+    }
+    /* seek to start of FATS and write them all */
+    seekto(reserved_sectors * sector_size, "first FAT");
+    for (x = 1; x <= nr_fats; x++) {
+	int y;
+	int blank_fat_length = fat_length - alloced_fat_length;
+	writebuf(fat, alloced_fat_length * sector_size, "FAT");
+	for (y = 0; y < blank_fat_length; y++)
+	    writebuf(blank_sector, sector_size, "FAT");
+    }
+    /* Write the root directory directly after the last FAT. This is the root
+     * dir area on FAT12/16, and the first cluster on FAT32. */
+    writebuf((char *)root_dir, size_root_dir, "root directory");
+
+    if (blank_sector)
+	free(blank_sector);
+    if (info_sector)
+	free(info_sector);
+    free(root_dir);		/* Free up the root directory space from setup_tables */
+    free(fat);			/* Free up the fat table space reserved during setup_tables */
+}
+
+/* Report the command usage and return a failure error code */
+
+void usage(void)
+{
+    fatal_error("\
+Usage: mkdosfs [-a][-A][-c][-C][-v][-I][-l bad-block-file][-b backup-boot-sector]\n\
+       [-m boot-msg-file][-n volume-name][-i volume-id]\n\
+       [-s sectors-per-cluster][-S logical-sector-size][-f number-of-FATs]\n\
+       [-h hidden-sectors][-F fat-size][-r root-dir-entries][-R reserved-sectors]\n\
+       /dev/name [blocks]\n");
+}
+
+/*
+ * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant
+ * of MS-DOS filesystem by default.
+ */
+static void check_atari(void)
+{
+#ifdef __mc68000__
+    FILE *f;
+    char line[128], *p;
+
+    if (!(f = fopen("/proc/hardware", "r"))) {
+	perror("/proc/hardware");
+	return;
+    }
+
+    while (fgets(line, sizeof(line), f)) {
+	if (strncmp(line, "Model:", 6) == 0) {
+	    p = line + 6;
+	    p += strspn(p, " \t");
+	    if (strncmp(p, "Atari ", 6) == 0)
+		atari_format = 1;
+	    break;
+	}
+    }
+    fclose(f);
+#endif
+}
+
+/* The "main" entry point into the utility - we pick up the options and attempt to process them in some sort of sensible
+   way.  In the event that some/all of the options are invalid we need to tell the user so that something can be done! */
+
+int main(int argc, char **argv)
+{
+    int c;
+    char *tmp;
+    char *listfile = NULL;
+    FILE *msgfile;
+    struct stat statbuf;
+    int i = 0, pos, ch;
+    int create = 0;
+    unsigned long long cblocks = 0;
+    int min_sector_size;
+
+    if (argc && *argv) {	/* What's the program name? */
+	char *p;
+	program_name = *argv;
+	if ((p = strrchr(program_name, '/')))
+	    program_name = p + 1;
+    }
+
+    gettimeofday(&create_timeval, NULL);
+    create_time = create_timeval.tv_sec;
+    volume_id = (u_int32_t) ((create_timeval.tv_sec << 20) | create_timeval.tv_usec);	/* Default volume ID = creation time, fudged for more uniqueness */
+    check_atari();
+
+    printf("%s " VERSION " (" VERSION_DATE ")\n", program_name);
+
+    while ((c = getopt(argc, argv, "aAb:cCf:F:Ii:l:m:n:r:R:s:S:h:v")) != EOF)
+	/* Scan the command line for options */
+	switch (c) {
+	case 'A':		/* toggle Atari format */
+	    atari_format = !atari_format;
+	    break;
+
+	case 'a':		/* a : skip alignment */
+	    align_structures = FALSE;
+	    break;
+
+	case 'b':		/* b : location of backup boot sector */
+	    backup_boot = (int)strtol(optarg, &tmp, 0);
+	    if (*tmp || backup_boot < 2 || backup_boot > 0xffff) {
+		printf("Bad location for backup boot sector : %s\n", optarg);
+		usage();
+	    }
+	    break;
+
+	case 'c':		/* c : Check FS as we build it */
+	    check = TRUE;
+	    malloc_entire_fat = TRUE;	/* Need to be able to mark clusters bad */
+	    break;
+
+	case 'C':		/* C : Create a new file */
+	    create = TRUE;
+	    break;
+
+	case 'f':		/* f : Choose number of FATs */
+	    nr_fats = (int)strtol(optarg, &tmp, 0);
+	    if (*tmp || nr_fats < 1 || nr_fats > 4) {
+		printf("Bad number of FATs : %s\n", optarg);
+		usage();
+	    }
+	    break;
+
+	case 'F':		/* F : Choose FAT size */
+	    size_fat = (int)strtol(optarg, &tmp, 0);
+	    if (*tmp || (size_fat != 12 && size_fat != 16 && size_fat != 32)) {
+		printf("Bad FAT type : %s\n", optarg);
+		usage();
+	    }
+	    size_fat_by_user = 1;
+	    break;
+
+	case 'h':		/* h : number of hidden sectors */
+	    hidden_sectors = (int)strtol(optarg, &tmp, 0);
+	    if (*tmp || hidden_sectors < 0) {
+		printf("Bad number of hidden sectors : %s\n", optarg);
+		usage();
+	    }
+	    break;
+
+	case 'I':
+	    ignore_full_disk = 1;
+	    break;
+
+	case 'i':		/* i : specify volume ID */
+	    volume_id = strtoul(optarg, &tmp, 16);
+	    if (*tmp) {
+		printf("Volume ID must be a hexadecimal number\n");
+		usage();
+	    }
+	    break;
+
+	case 'l':		/* l : Bad block filename */
+	    listfile = optarg;
+	    malloc_entire_fat = TRUE;	/* Need to be able to mark clusters bad */
+	    break;
+
+	case 'm':		/* m : Set boot message */
+	    if (strcmp(optarg, "-")) {
+		msgfile = fopen(optarg, "r");
+		if (!msgfile)
+		    perror(optarg);
+	    } else
+		msgfile = stdin;
+
+	    if (msgfile) {
+		/* The boot code ends at offset 448 and needs a null terminator */
+		i = MESSAGE_OFFSET;
+		pos = 0;	/* We are at beginning of line */
+		do {
+		    ch = getc(msgfile);
+		    switch (ch) {
+		    case '\r':	/* Ignore CRs */
+		    case '\0':	/* and nulls */
+			break;
+
+		    case '\n':	/* LF -> CR+LF if necessary */
+			if (pos) {	/* If not at beginning of line */
+			    dummy_boot_code[i++] = '\r';
+			    pos = 0;
+			}
+			dummy_boot_code[i++] = '\n';
+			break;
+
+		    case '\t':	/* Expand tabs */
+			do {
+			    dummy_boot_code[i++] = ' ';
+			    pos++;
+			}
+			while (pos % 8 && i < BOOTCODE_SIZE - 1);
+			break;
+
+		    case EOF:
+			dummy_boot_code[i++] = '\0';	/* Null terminator */
+			break;
+
+		    default:
+			dummy_boot_code[i++] = ch;	/* Store character */
+			pos++;	/* Advance position */
+			break;
+		    }
+		}
+		while (ch != EOF && i < BOOTCODE_SIZE - 1);
+
+		/* Fill up with zeros */
+		while (i < BOOTCODE_SIZE - 1)
+		    dummy_boot_code[i++] = '\0';
+		dummy_boot_code[BOOTCODE_SIZE - 1] = '\0';	/* Just in case */
+
+		if (ch != EOF)
+		    printf("Warning: message too long; truncated\n");
+
+		if (msgfile != stdin)
+		    fclose(msgfile);
+	    }
+	    break;
+
+	case 'n':		/* n : Volume name */
+	    sprintf(volume_name, "%-11.11s", optarg);
+	    break;
+
+	case 'r':		/* r : Root directory entries */
+	    root_dir_entries = (int)strtol(optarg, &tmp, 0);
+	    if (*tmp || root_dir_entries < 16 || root_dir_entries > 32768) {
+		printf("Bad number of root directory entries : %s\n", optarg);
+		usage();
+	    }
+	    break;
+
+	case 'R':		/* R : number of reserved sectors */
+	    reserved_sectors = (int)strtol(optarg, &tmp, 0);
+	    if (*tmp || reserved_sectors < 1 || reserved_sectors > 0xffff) {
+		printf("Bad number of reserved sectors : %s\n", optarg);
+		usage();
+	    }
+	    break;
+
+	case 's':		/* s : Sectors per cluster */
+	    sectors_per_cluster = (int)strtol(optarg, &tmp, 0);
+	    if (*tmp || (sectors_per_cluster != 1 && sectors_per_cluster != 2
+			 && sectors_per_cluster != 4 && sectors_per_cluster != 8
+			 && sectors_per_cluster != 16
+			 && sectors_per_cluster != 32
+			 && sectors_per_cluster != 64
+			 && sectors_per_cluster != 128)) {
+		printf("Bad number of sectors per cluster : %s\n", optarg);
+		usage();
+	    }
+	    break;
+
+	case 'S':		/* S : Sector size */
+	    sector_size = (int)strtol(optarg, &tmp, 0);
+	    if (*tmp || (sector_size != 512 && sector_size != 1024 &&
+			 sector_size != 2048 && sector_size != 4096 &&
+			 sector_size != 8192 && sector_size != 16384 &&
+			 sector_size != 32768)) {
+		printf("Bad logical sector size : %s\n", optarg);
+		usage();
+	    }
+	    sector_size_set = 1;
+	    break;
+
+	case 'v':		/* v : Verbose execution */
+	    ++verbose;
+	    break;
+
+	default:
+	    printf("Unknown option: %c\n", c);
+	    usage();
+	}
+    if (optind < argc) {
+	device_name = argv[optind];	/* Determine the number of blocks in the FS */
+
+	if (!device_name) {
+	    printf("No device specified.\n");
+	    usage();
+	}
+
+	if (!create)
+	    cblocks = count_blocks(device_name, &orphaned_sectors);	/*  Have a look and see! */
+    }
+    if (optind == argc - 2) {	/*  Either check the user specified number */
+	blocks = strtoull(argv[optind + 1], &tmp, 0);
+	if (!create && blocks != cblocks) {
+	    fprintf(stderr, "Warning: block count mismatch: ");
+	    fprintf(stderr, "found %llu but assuming %llu.\n", cblocks, blocks);
+	}
+    } else if (optind == argc - 1) {	/*  Or use value found */
+	if (create)
+	    die("Need intended size with -C.");
+	blocks = cblocks;
+	tmp = "";
+    } else {
+	fprintf(stderr, "No device specified!\n");
+	usage();
+    }
+    if (*tmp) {
+	printf("Bad block count : %s\n", argv[optind + 1]);
+	usage();
+    }
+
+    if (check && listfile)	/* Auto and specified bad block handling are mutually */
+	die("-c and -l are incompatible");	/* exclusive of each other! */
+
+    if (!create) {
+	check_mount(device_name);	/* Is the device already mounted? */
+	dev = open(device_name, O_EXCL | O_RDWR);	/* Is it a suitable device to build the FS on? */
+	if (dev < 0) {
+	    fprintf(stderr, "%s: unable to open %s: %s\n", program_name,
+		    device_name, strerror(errno));
+	    exit(1);		/* The error exit code is 1! */
+	}
+    } else {
+	loff_t offset = blocks * BLOCK_SIZE - 1;
+	char null = 0;
+	/* create the file */
+	dev = open(device_name, O_EXCL | O_RDWR | O_CREAT | O_TRUNC, 0666);
+	if (dev < 0)
+	    die("unable to create %s");
+	/* seek to the intended end-1, and write one byte. this creates a
+	 * sparse-as-possible file of appropriate size. */
+	if (llseek(dev, offset, SEEK_SET) != offset)
+	    die("seek failed");
+	if (write(dev, &null, 1) < 0)
+	    die("write failed");
+	if (llseek(dev, 0, SEEK_SET) != 0)
+	    die("seek failed");
+    }
+
+    if (fstat(dev, &statbuf) < 0)
+	die("unable to stat %s");
+    if (!S_ISBLK(statbuf.st_mode)) {
+	statbuf.st_rdev = 0;
+	check = 0;
+    } else
+	/*
+	 * Ignore any 'full' fixed disk devices, if -I is not given.
+	 * On a MO-disk one doesn't need partitions.  The filesytem can go
+	 * directly to the whole disk.  Under other OSes this is known as
+	 * the 'superfloppy' format.  As I don't know how to find out if
+	 * this is a MO disk I introduce a -I (ignore) switch.  -Joey
+	 */
+	if (!ignore_full_disk && ((statbuf.st_rdev & 0xff3f) == 0x0300 ||	/* hda, hdb */
+				  (statbuf.st_rdev & 0xff0f) == 0x0800 ||	/* sd */
+				  (statbuf.st_rdev & 0xff3f) == 0x0d00 ||	/* xd */
+				  (statbuf.st_rdev & 0xff3f) == 0x1600)	/* hdc, hdd */
+	)
+	die("Device partition expected, not making filesystem on entire device '%s' (use -I to override)");
+
+    if (sector_size_set) {
+	if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0)
+	    if (sector_size < min_sector_size) {
+		sector_size = min_sector_size;
+		fprintf(stderr,
+			"Warning: sector size was set to %d (minimal for this device)\n",
+			sector_size);
+	    }
+    } else {
+	if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0) {
+	    sector_size = min_sector_size;
+	    sector_size_set = 1;
+	}
+    }
+
+    if (sector_size > 4096)
+	fprintf(stderr,
+		"Warning: sector size is set to %d > 4096, such filesystem will not propably mount\n",
+		sector_size);
+
+    establish_params(statbuf.st_rdev, statbuf.st_size);
+    /* Establish the media parameters */
+
+    setup_tables();		/* Establish the file system tables */
+
+    if (check)			/* Determine any bad block locations and mark them */
+	check_blocks();
+    else if (listfile)
+	get_list_blocks(listfile);
+
+    write_tables();		/* Write the file system tables away! */
+
+    exit(0);			/* Terminate with no errors! */
+}
diff --git a/dosfstools/src/version.h b/dosfstools/src/version.h
new file mode 100644
index 0000000..6379103
--- /dev/null
+++ b/dosfstools/src/version.h
@@ -0,0 +1,28 @@
+/* version.h
+
+   Copyright (C) 1998-2005 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+   On Debian systems, the complete text of the GNU General Public License
+   can be found in /usr/share/common-licenses/GPL-3 file.
+*/
+
+#ifndef _version_h
+#define _version_h
+
+#define VERSION "3.0.12"
+#define VERSION_DATE "29 Oct 2011"
+
+#endif
diff --git a/etc/init.rc b/etc/init.rc
index 1b402e2..67b6a6f 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -13,13 +13,13 @@
 
 on init
     export PATH /sbin:/system/bin
+    export LD_LIBRARY_PATH .:/sbin
     export ANDROID_ROOT /system
     export ANDROID_DATA /data
     export EXTERNAL_STORAGE /sdcard
 
-    symlink /system/etc /etc
-
-    mkdir /sdcard
+    mkdir /boot
+    mkdir /recovery
     mkdir /system
     mkdir /data
     mkdir /cache
@@ -38,7 +38,7 @@
 
     write /sys/class/android_usb/android0/enable 0
     write /sys/class/android_usb/android0/idVendor 18D1
-    write /sys/class/android_usb/android0/idProduct D001
+    write /sys/class/android_usb/android0/idProduct D002
     write /sys/class/android_usb/android0/f_ffs/aliases adb
     write /sys/class/android_usb/android0/functions adb
     write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer}
@@ -75,6 +75,35 @@
 on property:sys.powerctl=*
    powerctl ${sys.powerctl}
 
+on property:sys.storage.ums_enabled=1
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/functions adb,mass_storage
+    write /sys/class/android_usb/android0/enable 1
+
+on property:sys.storage.ums_enabled=0
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/functions adb
+    write /sys/class/android_usb/android0/enable ${service.adb.root}
+
+on property:sys.usb.config=none
+    stop adbd
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/bDeviceClass 0
+
+on property:sys.usb.config=mtp,adb
+    stop adbd
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/functions mtp,adb
+    write /sys/class/android_usb/android0/enable 1
+    start adbd
+
+on property:sys.usb.config=adb
+    stop adbd
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/functions adb
+    write /sys/class/android_usb/android0/enable ${service.adb.root}
+    start adbd
+
 service ueventd /sbin/ueventd
     critical
     seclabel u:r:ueventd:s0
@@ -93,8 +122,9 @@
 
 # Always start adbd on userdebug and eng builds
 on property:ro.debuggable=1
-    write /sys/class/android_usb/android0/enable 1
-    start adbd
+    #write /sys/class/android_usb/android0/enable 1
+    #start adbd
+    setprop service.adb.root 1
 
 # Restart adbd so it can run as root
 on property:service.adb.root=1
diff --git a/exfat/COPYING b/exfat/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/exfat/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/exfat/ChangeLog b/exfat/ChangeLog
new file mode 100644
index 0000000..e24b2d6
--- /dev/null
+++ b/exfat/ChangeLog
@@ -0,0 +1,97 @@
+1.0.0 (2013-01-19)
+
+* Fixed crash when renaming a file within a single directory and a new name
+differs only in case.
+* Fixed clusters allocation: a cluster beyond valid clusters range could be
+allocated.
+* Fixed crash when a volume is unmounted while some files are open.
+* SConscript now respects AR and RANLIB environment variables.
+* Improved error handling.
+
+Linux:
+
+* Enabled big_writes. This improves write speed (larger block size means less
+switches between kernel- and user-space).
+* Do BLKROGET ioctl to make sure the device is not read-only: after
+"blockdev --setro" kernel still allows to open the device in read-write mode
+but fails writes.
+
+OS X:
+
+* Fixed OS X 10.8 support.
+* Switched to 64-bit inode numbers (now Mac OS X 10.5 or later is required).
+* Switched from unmaintained MacFUSE to OSXFUSE (http://osxfuse.github.com).
+* Fixed device size detection. Now mkfs works.
+* Workarounded some utilities failures due to missing chmod() support.
+* Disabled (senseless) permission checks made by FUSE.
+
+0.9.8 (2012-08-09)
+
+* The mkfs utility can now create huge file systems (up to several exabytes).
+* Fixed handling of characters beyond Basic Multilingual Plane.
+* Echo messages to syslog only if stderr is not connected to a terminal.
+
+0.9.7 (2012-03-08)
+
+* Out-of-the-box FreeBSD support (via ublio library).
+* Fixed "missing EOD entry" error (could happen while reading directory that
+consists of several clusters).
+* Fixed interpretation of minutes field in files timestamps (minutes could be
+displayed incorrectly).
+* Fixed mtime seconds field initialization for newly created file (mtime could
+be 1 sec less than creation time).
+* SConscript now respects CC, CCFLAGS and LDFLAGS environment variables.
+
+0.9.6 (2012-01-14)
+
+* Fixed write performance regression introduced in 0.9.4.
+* Mount in read-only mode if the device is write-protected.
+* Set ctime to mtime to ensure we don't break programs that rely on ctime
+(e.g. rsync considered that all files are outdated) [Eldad Zack].
+* Indicate that FS in not clean when it was not cleanly unmounted.
+* Utilities are now compatible with GNU/Hurd.
+* Fixed several memory leaks that could occur on error handling paths.
+* Improved handling of corrupted file systems.
+
+0.9.5 (2011-05-15)
+
+* Fixed erasing of the root directory cluster when creating a new FS with
+mkexfatfs. This bug could cause mkexfatfs to produce invalid FS.
+* Utilities are not linked with libfuse anymore.
+* Ensure that the path being opened is either a device or a regular file.
+
+0.9.4 (2011-03-05)
+
+* Introduced exfat-utils: dumpexfat, exfatfsck, mkexfatfs, exfatlabel.
+* Fixed "Invalid argument" error while mounting a volume from a disk with sector size greater than 512 bytes.
+* Wait for all data to be flushed to disk on unmount.
+* Kernel cache is no longer flushed on open. This can slightly improve read performance by avoiding extra read requests from kernel to user-space.
+* Allow to unmount volumes as user (fusermount -u) if they were mounted from the very same user [Tino Lange].
+* Errors and warnings are now duplicated to syslog.
+
+0.9.3 (2010-09-25)
+
+* Directories now can shrink.
+* Improved timestamps resolution from 2 sec to 1 sec.
+* Fixed timestamps displaying under Mac OS X when compiled for i386 or ppc.
+* Fixed FS size displaying for non-GNU systems.
+
+0.9.2 (2010-07-24)
+
+* Fixed a bug which could cause the whole directory to become unreadable after renaming a file in it.
+* Support for Solaris and various *BSD [Albert Lee].
+* Improved error handling on corrupted volumes.
+* Improved allowed file name characters filter.
+* Added man page.
+
+0.9.1 (2010-06-12)
+
+* Implemented automounting (util-linux-ng 2.18 or later is required).
+* Fixed mounting when cluster bitmap is larger than expected.
+* Fixed crash on statfs() when root directory contains error.
+* Fixed bugs specific to big-endian machines.
+* Other bugfixes.
+
+0.9.0 (2010-03-21)
+
+* Initial release.
diff --git a/exfat/dump/dumpexfat.8 b/exfat/dump/dumpexfat.8
new file mode 100644
index 0000000..7fea065
--- /dev/null
+++ b/exfat/dump/dumpexfat.8
@@ -0,0 +1,46 @@
+.\" Copyright (C) 2011  Andrew Nayenko
+.\"
+.TH DUMPEXFAT 8 "February 2011"
+.SH NAME
+.B dumpexfat
+\- dump exFAT file system
+.SH SYNOPSIS
+.B exfatlabel
+[
+.B \-s
+]
+[
+.B \-u
+]
+[
+.B \-V
+]
+.I device
+
+.SH DESCRIPTION
+.B dumpexfat
+dumps details about exFAT file system including low-level info. All sizes are
+in bytes.
+
+.SH OPTIONS
+Command line options available:
+.TP
+.B \-s
+Dump only info from super block. May be useful for heavily corrupted file
+systems.
+.TP
+.B \-u
+Dump ranges of used sectors starting from 0 and separated with spaces. May be
+useful for backup tools.
+.TP
+.BI \-V
+Print version and copyright.
+
+.SH EXIT CODES
+Zero is returned on success. Any other code means an error.
+
+.SH AUTHOR
+Andrew Nayenko
+
+.SH SEE ALSO
+.BR mkexfatfs (8)
diff --git a/exfat/dump/main.c b/exfat/dump/main.c
new file mode 100644
index 0000000..7ef571d
--- /dev/null
+++ b/exfat/dump/main.c
@@ -0,0 +1,184 @@
+/*
+	main.c (08.11.10)
+	Prints detailed information about exFAT volume.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <exfat.h>
+
+static void print_generic_info(const struct exfat_super_block* sb)
+{
+	printf("Volume serial number      0x%08x\n",
+			le32_to_cpu(sb->volume_serial));
+	printf("FS version                       %hhu.%hhu\n",
+			sb->version.major, sb->version.minor);
+	printf("Sector size               %10u\n",
+			SECTOR_SIZE(*sb));
+	printf("Cluster size              %10u\n",
+			CLUSTER_SIZE(*sb));
+}
+
+static void print_sector_info(const struct exfat_super_block* sb)
+{
+	printf("Sectors count             %10"PRIu64"\n",
+			le64_to_cpu(sb->sector_count));
+}
+
+static void print_cluster_info(const struct exfat_super_block* sb)
+{
+	printf("Clusters count            %10u\n",
+			le32_to_cpu(sb->cluster_count));
+}
+
+static void print_other_info(const struct exfat_super_block* sb)
+{
+	printf("First sector              %10"PRIu64"\n",
+			le64_to_cpu(sb->sector_start));
+	printf("FAT first sector          %10u\n",
+			le32_to_cpu(sb->fat_sector_start));
+	printf("FAT sectors count         %10u\n",
+			le32_to_cpu(sb->fat_sector_count));
+	printf("First cluster sector      %10u\n",
+			le32_to_cpu(sb->cluster_sector_start));
+	printf("Root directory cluster    %10u\n",
+			le32_to_cpu(sb->rootdir_cluster));
+	printf("Volume state                  0x%04hx\n",
+			le16_to_cpu(sb->volume_state));
+	printf("FATs count                %10hhu\n",
+			sb->fat_count);
+	printf("Drive number                    0x%02hhx\n",
+			sb->drive_no);
+	printf("Allocated space           %9hhu%%\n",
+			sb->allocated_percent);
+}
+
+static int dump_sb(const char* spec)
+{
+	struct exfat_dev* dev;
+	struct exfat_super_block sb;
+
+	dev = exfat_open(spec, EXFAT_MODE_RO);
+	if (dev == NULL)
+		return 1;
+
+	if (exfat_read(dev, &sb, sizeof(struct exfat_super_block)) < 0)
+	{
+		exfat_close(dev);
+		exfat_error("failed to read from `%s'", spec);
+		return 1;
+	}
+	if (memcmp(sb.oem_name, "EXFAT   ", sizeof(sb.oem_name)) != 0)
+	{
+		exfat_close(dev);
+		exfat_error("exFAT file system is not found on `%s'", spec);
+		return 1;
+	}
+
+	print_generic_info(&sb);
+	print_sector_info(&sb);
+	print_cluster_info(&sb);
+	print_other_info(&sb);
+
+	exfat_close(dev);
+	return 0;
+}
+
+static void dump_sectors(struct exfat* ef)
+{
+	off_t a = 0, b = 0;
+
+	printf("Used sectors ");
+	while (exfat_find_used_sectors(ef, &a, &b) == 0)
+		printf(" %"PRIu64"-%"PRIu64, a, b);
+	puts("");
+}
+
+static int dump_full(const char* spec, bool used_sectors)
+{
+	struct exfat ef;
+	uint32_t free_clusters;
+	uint64_t free_sectors;
+
+	if (exfat_mount(&ef, spec, "ro") != 0)
+		return 1;
+
+	free_clusters = exfat_count_free_clusters(&ef);
+	free_sectors = (uint64_t) free_clusters << ef.sb->spc_bits;
+
+	printf("Volume label         %15s\n", exfat_get_label(&ef));
+	print_generic_info(ef.sb);
+	print_sector_info(ef.sb);
+	printf("Free sectors              %10"PRIu64"\n", free_sectors);
+	print_cluster_info(ef.sb);
+	printf("Free clusters             %10u\n", free_clusters);
+	print_other_info(ef.sb);
+	if (used_sectors)
+		dump_sectors(&ef);
+
+	exfat_unmount(&ef);
+	return 0;
+}
+
+static void usage(const char* prog)
+{
+	fprintf(stderr, "Usage: %s [-s] [-u] [-V] <device>\n", prog);
+	exit(1);
+}
+
+int main(int argc, char* argv[])
+{
+	int opt;
+	const char* spec = NULL;
+	bool sb_only = false;
+	bool used_sectors = false;
+
+	printf("dumpexfat %u.%u.%u\n",
+			EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
+
+	while ((opt = getopt(argc, argv, "suV")) != -1)
+	{
+		switch (opt)
+		{
+		case 's':
+			sb_only = true;
+			break;
+		case 'u':
+			used_sectors = true;
+			break;
+		case 'V':
+			puts("Copyright (C) 2011-2013  Andrew Nayenko");
+			return 0;
+		default:
+			usage(argv[0]);
+		}
+	}
+	if (argc - optind != 1)
+		usage(argv[0]);
+	spec = argv[optind];
+
+	if (sb_only)
+		return dump_sb(spec);
+
+	return dump_full(spec, used_sectors);
+}
diff --git a/exfat/exfat-fuse/Android.mk b/exfat/exfat-fuse/Android.mk
new file mode 100644
index 0000000..251e087
--- /dev/null
+++ b/exfat/exfat-fuse/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := exfat-fuse
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64
+LOCAL_SRC_FILES = main.c 
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+					$(commands_recovery_local_path)/exfat/libexfat \
+					$(commands_recovery_local_path)/fuse/include
+LOCAL_SHARED_LIBRARIES += libz libc libexfat libdl 
+LOCAL_STATIC_LIBRARIES += libfusetwrp
+
+include $(BUILD_EXECUTABLE)
diff --git a/exfat/exfat-fuse/main.c b/exfat/exfat-fuse/main.c
new file mode 100644
index 0000000..abb60e7
--- /dev/null
+++ b/exfat/exfat-fuse/main.c
@@ -0,0 +1,564 @@
+/*
+	main.c (01.09.09)
+	FUSE-based exFAT implementation. Requires FUSE 2.6 or later.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#define FUSE_USE_VERSION 26
+#include <fuse.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <exfat.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#define exfat_debug(format, ...)
+
+#if !defined(FUSE_VERSION) || (FUSE_VERSION < 26)
+	#error FUSE 2.6 or later is required
+#endif
+
+const char* default_options = "ro_fallback,allow_other,blkdev,big_writes,"
+		"defer_permissions";
+
+struct exfat ef;
+
+static struct exfat_node* get_node(const struct fuse_file_info* fi)
+{
+	return (struct exfat_node*) (size_t) fi->fh;
+}
+
+static void set_node(struct fuse_file_info* fi, struct exfat_node* node)
+{
+	fi->fh = (uint64_t) (size_t) node;
+}
+
+static int fuse_exfat_getattr(const char* path, struct stat* stbuf)
+{
+	struct exfat_node* node;
+	int rc;
+
+	exfat_debug("[%s] %s", __func__, path);
+
+	rc = exfat_lookup(&ef, &node, path);
+	if (rc != 0)
+		return rc;
+
+	exfat_stat(&ef, node, stbuf);
+	exfat_put_node(&ef, node);
+	return 0;
+}
+
+static int fuse_exfat_truncate(const char* path, off64_t size)
+{
+	struct exfat_node* node;
+	int rc;
+
+	exfat_debug("[%s] %s, %"PRId64, __func__, path, size);
+
+	rc = exfat_lookup(&ef, &node, path);
+	if (rc != 0)
+		return rc;
+
+	rc = exfat_truncate(&ef, node, size, true);
+	exfat_put_node(&ef, node);
+	return rc;
+}
+
+static int fuse_exfat_readdir(const char* path, void* buffer,
+		fuse_fill_dir_t filler, off64_t offset, struct fuse_file_info* fi)
+{
+	struct exfat_node* parent;
+	struct exfat_node* node;
+	struct exfat_iterator it;
+	int rc;
+	char name[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+
+	exfat_debug("[%s] %s", __func__, path);
+
+	rc = exfat_lookup(&ef, &parent, path);
+	if (rc != 0)
+		return rc;
+	if (!(parent->flags & EXFAT_ATTRIB_DIR))
+	{
+		exfat_put_node(&ef, parent);
+		exfat_error("`%s' is not a directory (0x%x)", path, parent->flags);
+		return -ENOTDIR;
+	}
+
+	filler(buffer, ".", NULL, 0);
+	filler(buffer, "..", NULL, 0);
+
+	rc = exfat_opendir(&ef, parent, &it);
+	if (rc != 0)
+	{
+		exfat_put_node(&ef, parent);
+		exfat_error("failed to open directory `%s'", path);
+		return rc;
+	}
+	while ((node = exfat_readdir(&ef, &it)))
+	{
+		exfat_get_name(node, name, sizeof(name) - 1);
+		exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
+				name, IS_CONTIGUOUS(*node) ? "contiguous" : "fragmented",
+				node->size, node->start_cluster);
+		filler(buffer, name, NULL, 0);
+		exfat_put_node(&ef, node);
+	}
+	exfat_closedir(&ef, &it);
+	exfat_put_node(&ef, parent);
+	return 0;
+}
+
+static int fuse_exfat_open(const char* path, struct fuse_file_info* fi)
+{
+	struct exfat_node* node;
+	int rc;
+
+	exfat_debug("[%s] %s", __func__, path);
+
+	rc = exfat_lookup(&ef, &node, path);
+	if (rc != 0)
+		return rc;
+	set_node(fi, node);
+	fi->keep_cache = 1;
+	return 0;
+}
+
+static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
+{
+	exfat_debug("[%s] %s", __func__, path);
+	exfat_put_node(&ef, get_node(fi));
+	return 0;
+}
+
+static int fuse_exfat_fsync(const char* path, int datasync,
+		struct fuse_file_info *fi)
+{
+	int rc;
+
+	exfat_debug("[%s] %s", __func__, path);
+	rc = exfat_flush_node(&ef, get_node(fi));
+	if (rc != 0)
+		return rc;
+	rc = exfat_flush(&ef);
+	if (rc != 0)
+		return rc;
+	return exfat_fsync(ef.dev);
+}
+
+static int fuse_exfat_read(const char* path, char* buffer, size_t size,
+		off64_t offset, struct fuse_file_info* fi)
+{
+	ssize_t ret;
+
+	exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
+	ret = exfat_generic_pread(&ef, get_node(fi), buffer, size, offset);
+	if (ret < 0)
+		return -EIO;
+	return ret;
+}
+
+static int fuse_exfat_write(const char* path, const char* buffer, size_t size,
+		off64_t offset, struct fuse_file_info* fi)
+{
+	ssize_t ret;
+
+	exfat_debug("[%s] %s (%zu bytes)", __func__, path, size);
+	ret = exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset);
+	if (ret < 0)
+		return -EIO;
+	return ret;
+}
+
+static int fuse_exfat_unlink(const char* path)
+{
+	struct exfat_node* node;
+	int rc;
+
+	exfat_debug("[%s] %s", __func__, path);
+
+	rc = exfat_lookup(&ef, &node, path);
+	if (rc != 0)
+		return rc;
+
+	rc = exfat_unlink(&ef, node);
+	exfat_put_node(&ef, node);
+	return rc;
+}
+
+static int fuse_exfat_rmdir(const char* path)
+{
+	struct exfat_node* node;
+	int rc;
+
+	exfat_debug("[%s] %s", __func__, path);
+
+	rc = exfat_lookup(&ef, &node, path);
+	if (rc != 0)
+		return rc;
+
+	rc = exfat_rmdir(&ef, node);
+	exfat_put_node(&ef, node);
+	return rc;
+}
+
+static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev)
+{
+	exfat_debug("[%s] %s 0%ho", __func__, path, mode);
+	return exfat_mknod(&ef, path);
+}
+
+static int fuse_exfat_mkdir(const char* path, mode_t mode)
+{
+	exfat_debug("[%s] %s 0%ho", __func__, path, mode);
+	return exfat_mkdir(&ef, path);
+}
+
+static int fuse_exfat_rename(const char* old_path, const char* new_path)
+{
+	exfat_debug("[%s] %s => %s", __func__, old_path, new_path);
+	return exfat_rename(&ef, old_path, new_path);
+}
+
+static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
+{
+	struct exfat_node* node;
+	int rc;
+
+	exfat_debug("[%s] %s", __func__, path);
+
+	rc = exfat_lookup(&ef, &node, path);
+	if (rc != 0)
+		return rc;
+
+	exfat_utimes(node, tv);
+	exfat_put_node(&ef, node);
+	return 0;
+}
+
+static int fuse_exfat_chmod(const char* path, mode_t mode)
+{
+	const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR |
+			S_IRWXU | S_IRWXG | S_IRWXO;
+
+	exfat_debug("[%s] %s 0%ho", __func__, path, mode);
+	if (mode & ~VALID_MODE_MASK)
+		return -EPERM;
+	return 0;
+}
+
+static int fuse_exfat_chown(const char* path, uid_t uid, gid_t gid)
+{
+	exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid);
+	if (uid != ef.uid || gid != ef.gid)
+		return -EPERM;
+	return 0;
+}
+
+static int fuse_exfat_statfs(const char* path, struct statvfs* sfs)
+{
+	exfat_debug("[%s]", __func__);
+
+	sfs->f_bsize = CLUSTER_SIZE(*ef.sb);
+	sfs->f_frsize = CLUSTER_SIZE(*ef.sb);
+	sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits;
+	sfs->f_bavail = exfat_count_free_clusters(&ef);
+	sfs->f_bfree = sfs->f_bavail;
+	sfs->f_namemax = EXFAT_NAME_MAX;
+
+	/*
+	   Below are fake values because in exFAT there is
+	   a) no simple way to count files;
+	   b) no such thing as inode;
+	   So here we assume that inode = cluster.
+	*/
+	sfs->f_files = (sfs->f_blocks - sfs->f_bfree) >> ef.sb->spc_bits;
+	sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits;
+	sfs->f_ffree = sfs->f_bavail;
+
+	return 0;
+}
+
+static void* fuse_exfat_init(struct fuse_conn_info* fci)
+{
+	exfat_debug("[%s]", __func__);
+#ifdef FUSE_CAP_BIG_WRITES
+	fci->want |= FUSE_CAP_BIG_WRITES;
+#endif
+	return NULL;
+}
+
+static void fuse_exfat_destroy(void* unused)
+{
+	exfat_debug("[%s]", __func__);
+	exfat_unmount(&ef);
+}
+
+static void usage(const char* prog)
+{
+	fprintf(stderr, "Usage: %s [-d] [-o options] [-V] <device> <dir>\n", prog);
+	exit(1);
+}
+
+static struct fuse_operations fuse_exfat_ops =
+{
+	.getattr	= fuse_exfat_getattr,
+	.truncate	= fuse_exfat_truncate,
+	.readdir	= fuse_exfat_readdir,
+	.open		= fuse_exfat_open,
+	.release	= fuse_exfat_release,
+	.fsync		= fuse_exfat_fsync,
+	.fsyncdir	= fuse_exfat_fsync,
+	.read		= fuse_exfat_read,
+	.write		= fuse_exfat_write,
+	.unlink		= fuse_exfat_unlink,
+	.rmdir		= fuse_exfat_rmdir,
+	.mknod		= fuse_exfat_mknod,
+	.mkdir		= fuse_exfat_mkdir,
+	.rename		= fuse_exfat_rename,
+	.utimens	= fuse_exfat_utimens,
+	.chmod		= fuse_exfat_chmod,
+	.chown		= fuse_exfat_chown,
+	.statfs		= fuse_exfat_statfs,
+	.init		= fuse_exfat_init,
+	.destroy	= fuse_exfat_destroy,
+};
+
+static char* add_option(char* options, const char* name, const char* value)
+{
+	size_t size;
+
+	if (value)
+		size = strlen(options) + strlen(name) + strlen(value) + 3;
+	else
+		size = strlen(options) + strlen(name) + 2;
+
+	options = realloc(options, size);
+	if (options == NULL)
+	{
+		exfat_error("failed to reallocate options string");
+		return NULL;
+	}
+	strcat(options, ",");
+	strcat(options, name);
+	if (value)
+	{
+		strcat(options, "=");
+		strcat(options, value);
+	}
+	return options;
+}
+
+static char* add_user_option(char* options)
+{
+	struct passwd* pw;
+
+	if (getuid() == 0)
+		return options;
+
+	pw = getpwuid(getuid());
+	if (pw == NULL || pw->pw_name == NULL)
+	{
+		free(options);
+		exfat_error("failed to determine username");
+		return NULL;
+	}
+	return add_option(options, "user", pw->pw_name);
+}
+
+static char* add_blksize_option(char* options, long cluster_size)
+{
+	long page_size = sysconf(_SC_PAGESIZE);
+	char blksize[20];
+
+	if (page_size < 1)
+		page_size = 0x1000;
+
+	snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size));
+	return add_option(options, "blksize", blksize);
+}
+
+static char* add_fuse_options(char* options, const char* spec)
+{
+	options = add_option(options, "fsname", spec);
+	if (options == NULL)
+		return NULL;
+	options = add_user_option(options);
+	if (options == NULL)
+		return NULL;
+	options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb));
+	if (options == NULL)
+		return NULL;
+
+	return options;
+}
+
+int main(int argc, char* argv[])
+{
+	struct fuse_args mount_args = FUSE_ARGS_INIT(0, NULL);
+	struct fuse_args newfs_args = FUSE_ARGS_INIT(0, NULL);
+	const char* spec = NULL;
+	const char* mount_point = NULL;
+	char* mount_options;
+	int debug = 0;
+	struct fuse_chan* fc = NULL;
+	struct fuse* fh = NULL;
+	int opt;
+
+	printf("FUSE exfat %u.%u.%u\n",
+			EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
+
+	mount_options = strdup(default_options);
+	if (mount_options == NULL)
+	{
+		exfat_error("failed to allocate options string");
+		return 1;
+	}
+
+	while ((opt = getopt(argc, argv, "dno:Vv")) != -1)
+	{
+		switch (opt)
+		{
+		case 'd':
+			debug = 1;
+			break;
+		case 'n':
+			break;
+		case 'o':
+			mount_options = add_option(mount_options, optarg, NULL);
+			if (mount_options == NULL)
+				return 1;
+			break;
+		case 'V':
+			free(mount_options);
+			puts("Copyright (C) 2010-2013  Andrew Nayenko");
+			return 0;
+		case 'v':
+			break;
+		default:
+			free(mount_options);
+			usage(argv[0]);
+			break;
+		}
+	}
+	if (argc - optind != 2)
+	{
+		free(mount_options);
+		usage(argv[0]);
+	}
+	spec = argv[optind];
+	mount_point = argv[optind + 1];
+
+	if (exfat_mount(&ef, spec, mount_options) != 0)
+	{
+		free(mount_options);
+		return 1;
+	}
+
+	if (ef.ro == -1) /* read-only fallback was used */
+	{
+		mount_options = add_option(mount_options, "ro", NULL);
+		if (mount_options == NULL)
+		{
+			exfat_unmount(&ef);
+			return 1;
+		}
+	}
+
+	mount_options = add_fuse_options(mount_options, spec);
+	if (mount_options == NULL)
+	{
+		exfat_unmount(&ef);
+		return 1;
+	}
+
+	/* create arguments for fuse_mount() */
+	if (fuse_opt_add_arg(&mount_args, "exfat") != 0 ||
+		fuse_opt_add_arg(&mount_args, "-o") != 0 ||
+		fuse_opt_add_arg(&mount_args, mount_options) != 0)
+	{
+		exfat_unmount(&ef);
+		free(mount_options);
+		return 1;
+	}
+
+	free(mount_options);
+
+	/* create FUSE mount point */
+	fc = fuse_mount(mount_point, &mount_args);
+	fuse_opt_free_args(&mount_args);
+	if (fc == NULL)
+	{
+		exfat_unmount(&ef);
+		return 1;
+	}
+
+	/* create arguments for fuse_new() */
+	if (fuse_opt_add_arg(&newfs_args, "") != 0 ||
+		(debug && fuse_opt_add_arg(&newfs_args, "-d") != 0))
+	{
+		fuse_unmount(mount_point, fc);
+		exfat_unmount(&ef);
+		return 1;
+	}
+
+	/* create new FUSE file system */
+	fh = fuse_new(fc, &newfs_args, &fuse_exfat_ops,
+			sizeof(struct fuse_operations), NULL);
+	fuse_opt_free_args(&newfs_args);
+	if (fh == NULL)
+	{
+		fuse_unmount(mount_point, fc);
+		exfat_unmount(&ef);
+		return 1;
+	}
+
+	/* exit session on HUP, TERM and INT signals and ignore PIPE signal */
+	if (fuse_set_signal_handlers(fuse_get_session(fh)) != 0)
+	{
+		fuse_unmount(mount_point, fc);
+		fuse_destroy(fh);
+		exfat_unmount(&ef);
+		exfat_error("failed to set signal handlers");
+		return 1;
+	}
+
+	/* go to background (unless "-d" option is passed) and run FUSE
+	   main loop */
+	if (fuse_daemonize(debug) == 0)
+	{
+		if (fuse_loop(fh) != 0)
+			exfat_error("FUSE loop failure");
+	}
+	else
+		exfat_error("failed to daemonize");
+
+	fuse_remove_signal_handlers(fuse_get_session(fh));
+	/* note that fuse_unmount() must be called BEFORE fuse_destroy() */
+	fuse_unmount(mount_point, fc);
+	fuse_destroy(fh);
+	return 0;
+}
diff --git a/exfat/exfat-fuse/mount.exfat-fuse.8 b/exfat/exfat-fuse/mount.exfat-fuse.8
new file mode 100644
index 0000000..b7e9d56
--- /dev/null
+++ b/exfat/exfat-fuse/mount.exfat-fuse.8
@@ -0,0 +1,88 @@
+.\" Copyright (C) 2010  Andrew Nayenko
+.\"
+.TH EXFAT-FUSE 8 "July 2010"
+.SH NAME
+mount.exfat-fuse \- mount an exFAT file system
+.SH SYNOPSIS
+.B mount.exfat-fuse
+[
+.B \-d
+]
+[
+.B \-n
+]
+[
+.B \-o
+.I options
+]
+[
+.B \-V
+]
+[
+.B \-v
+]
+.I device dir
+
+.SH DESCRIPTION
+.B mount.exfat-fuse
+is a free exFAT file system implementation with write support. exFAT is a
+simple file system created by Microsoft. It is intended to replace FAT32
+removing some of its limitations. exFAT is a standard FS for SDXC memory
+cards.
+
+.SH COMMAND LINE OPTIONS
+Command line options available:
+.TP
+.BI \-d
+Enable debug logging and do not detach from shell.
+.TP
+.BI \-n
+Ignored.
+.TP
+.BI \-o " options"
+File system specific options. For more details see
+.B FILE SYSTEM OPTIONS
+section below.
+.TP
+.BI \-V
+Print version and copyright.
+.TP
+.BI \-v
+Ignored.
+
+.SH FILE SYSTEM OPTIONS
+.TP
+.BI umask= value
+Set the umask (the bitmask of the permissions that are
+.B not
+present, in octal).
+The default is the umask of the current process.
+.TP
+.BI dmask= value
+Set the umask for directories only.
+.TP
+.BI fmask= value
+Set the umask for files only.
+.TP
+.BI uid= n
+Set the owner for all files and directories.
+The default is the owner of the current process.
+.TP
+.BI gid= n
+Set the group for all files and directories.
+The default is the group of the current process.
+.TP
+.BI ro
+Mount the file system in read only mode.
+.TP
+.BI noatime
+Do not update access time when file is read.
+
+.SH EXIT CODES
+Zero is returned on successful mount. Any other code means an error.
+
+.SH AUTHOR
+Andrew Nayenko
+
+.SH SEE ALSO
+.BR mount (8)
diff --git a/exfat/fsck/exfatfsck.8 b/exfat/fsck/exfatfsck.8
new file mode 100644
index 0000000..40d7c85
--- /dev/null
+++ b/exfat/fsck/exfatfsck.8
@@ -0,0 +1,32 @@
+.\" Copyright (C) 2011  Andrew Nayenko
+.\"
+.TH EXFATFSCK 8 "February 2011"
+.SH NAME
+.B exfatfsck
+\- check an exFAT file system
+.SH SYNOPSIS
+.B exfatfsck
+[
+.B \-V
+]
+.I device
+
+.SH DESCRIPTION
+.B exfatfsck
+checks an exFAT file system for errors. Note that it cannot repair corrupted
+FS, it just reports found errors.
+
+.SH COMMAND LINE OPTIONS
+Command line options available:
+.TP
+.BI \-V
+Print version and copyright.
+
+.SH EXIT CODES
+Zero is returned if errors were not found. Any other code means an error.
+
+.SH AUTHOR
+Andrew Nayenko
+
+.SH SEE ALSO
+.BR fsck (8)
diff --git a/exfat/fsck/main.c b/exfat/fsck/main.c
new file mode 100644
index 0000000..91f7545
--- /dev/null
+++ b/exfat/fsck/main.c
@@ -0,0 +1,177 @@
+/*
+	main.c (02.09.09)
+	exFAT file system checker.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <exfat.h>
+#include <exfatfs.h>
+#include <inttypes.h>
+#include <unistd.h>
+
+#define exfat_debug(format, ...)
+
+uint64_t files_count, directories_count;
+
+static int nodeck(struct exfat* ef, struct exfat_node* node)
+{
+	const cluster_t cluster_size = CLUSTER_SIZE(*ef->sb);
+	cluster_t clusters = (node->size + cluster_size - 1) / cluster_size;
+	cluster_t c = node->start_cluster;
+	int rc = 0;
+
+	while (clusters--)
+	{
+		if (CLUSTER_INVALID(c))
+		{
+			char name[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+
+			exfat_get_name(node, name, sizeof(name) - 1);
+			exfat_error("file `%s' has invalid cluster 0x%x", name, c);
+			rc = 1;
+			break;
+		}
+		if (BMAP_GET(ef->cmap.chunk, c - EXFAT_FIRST_DATA_CLUSTER) == 0)
+		{
+			char name[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+
+			exfat_get_name(node, name, sizeof(name) - 1);
+			exfat_error("cluster 0x%x of file `%s' is not allocated", c, name);
+			rc = 1;
+		}
+		c = exfat_next_cluster(ef, node, c);
+	}
+	return rc;
+}
+
+static void dirck(struct exfat* ef, const char* path)
+{
+	struct exfat_node* parent;
+	struct exfat_node* node;
+	struct exfat_iterator it;
+	int rc;
+	size_t path_length;
+	char* entry_path;
+
+	if (exfat_lookup(ef, &parent, path) != 0)
+		exfat_bug("directory `%s' is not found", path);
+	if (!(parent->flags & EXFAT_ATTRIB_DIR))
+		exfat_bug("`%s' is not a directory (0x%x)", path, parent->flags);
+	if (nodeck(ef, parent) != 0)
+		return;
+
+	path_length = strlen(path);
+	entry_path = malloc(path_length + 1 + UTF8_BYTES(EXFAT_NAME_MAX) + 1);
+	if (entry_path == NULL)
+	{
+		exfat_error("out of memory");
+		return;
+	}
+	strcpy(entry_path, path);
+	strcat(entry_path, "/");
+
+	rc = exfat_opendir(ef, parent, &it);
+	if (rc != 0)
+	{
+		free(entry_path);
+		exfat_put_node(ef, parent);
+		exfat_error("failed to open directory `%s'", path);
+		return;
+	}
+	while ((node = exfat_readdir(ef, &it)))
+	{
+		exfat_get_name(node, entry_path + path_length + 1,
+				UTF8_BYTES(EXFAT_NAME_MAX));
+		exfat_debug("%s: %s, %"PRIu64" bytes, cluster %u", entry_path,
+				IS_CONTIGUOUS(*node) ? "contiguous" : "fragmented",
+				node->size, node->start_cluster);
+		if (node->flags & EXFAT_ATTRIB_DIR)
+		{
+			directories_count++;
+			dirck(ef, entry_path);
+		}
+		else
+		{
+			files_count++;
+			nodeck(ef, node);
+		}
+		exfat_put_node(ef, node);
+	}
+	exfat_closedir(ef, &it);
+	exfat_put_node(ef, parent);
+	free(entry_path);
+}
+
+static void fsck(struct exfat* ef)
+{
+	exfat_print_info(ef->sb, exfat_count_free_clusters(ef));
+	dirck(ef, "");
+}
+
+static void usage(const char* prog)
+{
+	fprintf(stderr, "Usage: %s [-V] <device>\n", prog);
+	exit(1);
+}
+
+int main(int argc, char* argv[])
+{
+	int opt;
+	const char* spec = NULL;
+	struct exfat ef;
+
+	printf("exfatfsck %u.%u.%u\n",
+			EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
+
+	while ((opt = getopt(argc, argv, "V")) != -1)
+	{
+		switch (opt)
+		{
+		case 'V':
+			puts("Copyright (C) 2011-2013  Andrew Nayenko");
+			return 0;
+		default:
+			usage(argv[0]);
+			break;
+		}
+	}
+	if (argc - optind != 1)
+		usage(argv[0]);
+	spec = argv[optind];
+
+	if (exfat_mount(&ef, spec, "ro") != 0)
+		return 1;
+
+	printf("Checking file system on %s.\n", spec);
+	fsck(&ef);
+	exfat_unmount(&ef);
+	printf("Totally %"PRIu64" directories and %"PRIu64" files.\n",
+			directories_count, files_count);
+
+	fputs("File system checking finished. ", stdout);
+	if (exfat_errors != 0)
+	{
+		printf("ERRORS FOUND: %d.\n", exfat_errors);
+		return 1;
+	}
+	puts("No errors found.");
+	return 0;
+}
diff --git a/exfat/label/exfatlabel.8 b/exfat/label/exfatlabel.8
new file mode 100644
index 0000000..dd2ef1c
--- /dev/null
+++ b/exfat/label/exfatlabel.8
@@ -0,0 +1,48 @@
+.\" Copyright (C) 2011  Andrew Nayenko
+.\"
+.TH EXFATLABEL 8 "February 2011"
+.SH NAME
+.B exfatlabel
+\- get or set an exFAT file system label
+.SH SYNOPSIS
+.B exfatlabel
+[
+.B \-V
+]
+.I device
+[
+.I label
+]
+
+.SH DESCRIPTION
+.B exfatlabel
+reads or changes an exFAT file system label (volume name).
+
+If
+.I label
+argument is present,
+.B exfatlabel
+sets the new volume name. Label can be up to 15 characters. This limit is
+shorter if characters beyond Unicode BMP are used because internally label
+is stored in UTF-16.
+
+If
+.I label
+argument is omitted,
+.B exfatlabel
+just prints current volume name.
+
+.SH COMMAND LINE OPTIONS
+Command line options available:
+.TP
+.BI \-V
+Print version and copyright.
+
+.SH EXIT CODES
+Zero is returned on success. Any other code means an error.
+
+.SH AUTHOR
+Andrew Nayenko
+
+.SH SEE ALSO
+.BR mkexfatfs (8)
diff --git a/exfat/label/main.c b/exfat/label/main.c
new file mode 100644
index 0000000..2e5b5ca
--- /dev/null
+++ b/exfat/label/main.c
@@ -0,0 +1,63 @@
+/*
+	main.c (20.01.11)
+	Prints or changes exFAT volume label.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <exfat.h>
+
+int main(int argc, char* argv[])
+{
+	char** pp;
+	struct exfat ef;
+	int rc = 0;
+
+	for (pp = argv + 1; *pp; pp++)
+		if (strcmp(*pp, "-V") == 0)
+		{
+			printf("exfatlabel %u.%u.%u\n", EXFAT_VERSION_MAJOR,
+					EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
+			puts("Copyright (C) 2011-2013  Andrew Nayenko");
+			return 0;
+		}
+
+	if (argc != 2 && argc != 3)
+	{
+		fprintf(stderr, "Usage: %s [-V] <device> [label]\n", argv[0]);
+		return 1;
+	}
+
+	if (argv[2])
+	{
+		if (exfat_mount(&ef, argv[1], "") != 0)
+			return 1;
+		rc = (exfat_set_label(&ef, argv[2]) != 0);
+	}
+	else
+	{
+		if (exfat_mount(&ef, argv[1], "ro") != 0)
+			return 1;
+		puts(exfat_get_label(&ef));
+	}
+
+	exfat_unmount(&ef);
+	return rc;
+}
diff --git a/exfat/libexfat/Android.mk b/exfat/libexfat/Android.mk
new file mode 100644
index 0000000..39a7faa
--- /dev/null
+++ b/exfat/libexfat/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libexfat
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64
+LOCAL_SRC_FILES = cluster.c io.c log.c lookup.c mount.c node.c time.c utf.c utils.c 
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+LOCAL_SHARED_LIBRARIES += libc
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/exfat/libexfat/COPYING b/exfat/libexfat/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/exfat/libexfat/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/exfat/libexfat/byteorder.h b/exfat/libexfat/byteorder.h
new file mode 100644
index 0000000..10746e1
--- /dev/null
+++ b/exfat/libexfat/byteorder.h
@@ -0,0 +1,110 @@
+/*
+	byteorder.h (12.01.10)
+	Endianness stuff. exFAT uses little-endian byte order.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef BYTEORDER_H_INCLUDED
+#define BYTEORDER_H_INCLUDED
+
+#define __GLIBC__
+#include <stdint.h>
+
+#if defined(__GLIBC__)
+
+#include <endian.h>
+#include <byteswap.h>
+
+#elif defined(__APPLE__)
+
+#include <machine/endian.h>
+#include <libkern/OSByteOrder.h>
+#define bswap_16(x) OSSwapInt16(x)
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+
+#elif defined(__FreeBSD__) || defined(__DragonFlyBSD__) || defined(__NetBSD__)
+
+#include <sys/endian.h>
+#define bswap_16(x) bswap16(x)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+
+#elif defined(__OpenBSD__)
+
+#include <machine/endian.h>
+#define bswap_16(x) swap16(x)
+#define bswap_32(x) swap32(x)
+#define bswap_64(x) swap64(x)
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+
+#elif defined(__sun)
+
+#include <sys/byteorder.h>
+#define bswap_16(x) BSWAP_16(x)
+#define bswap_32(x) BSWAP_32(x)
+#define bswap_64(x) BSWAP_64(x)
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#ifdef _LITTLE_ENDIAN
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#else
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif
+
+#else 
+#error No byte order macros available for your platform
+#endif
+
+typedef struct { uint16_t __u16; } le16_t;
+typedef struct { uint32_t __u32; } le32_t;
+typedef struct { uint64_t __u64; } le64_t;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline uint16_t le16_to_cpu(le16_t v) { return v.__u16; }
+static inline uint32_t le32_to_cpu(le32_t v) { return v.__u32; }
+static inline uint64_t le64_to_cpu(le64_t v) { return v.__u64; }
+
+static inline le16_t cpu_to_le16(uint16_t v) { le16_t t = {v}; return t; }
+static inline le32_t cpu_to_le32(uint32_t v) { le32_t t = {v}; return t; }
+static inline le64_t cpu_to_le64(uint64_t v) { le64_t t = {v}; return t; }
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint16_t le16_to_cpu(le16_t v) { return bswap_16(v.__u16); }
+static inline uint32_t le32_to_cpu(le32_t v) { return bswap_32(v.__u32); }
+static inline uint64_t le64_to_cpu(le64_t v) { return bswap_64(v.__u64); }
+
+static inline le16_t cpu_to_le16(uint16_t v)
+	{ le16_t t = {bswap_16(v)}; return t; }
+static inline le32_t cpu_to_le32(uint32_t v)
+	{ le32_t t = {bswap_32(v)}; return t; }
+static inline le64_t cpu_to_le64(uint64_t v)
+	{ le64_t t = {bswap_64(v)}; return t; }
+#else
+#error Wow! You have a PDP machine?!
+#endif
+
+#endif /* ifndef BYTEORDER_H_INCLUDED */
diff --git a/exfat/libexfat/cluster.c b/exfat/libexfat/cluster.c
new file mode 100644
index 0000000..2215636
--- /dev/null
+++ b/exfat/libexfat/cluster.c
@@ -0,0 +1,472 @@
+/*
+	cluster.c (03.09.09)
+	exFAT file system implementation library.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+/*
+ * Sector to absolute offset.
+ */
+static off64_t s2o(const struct exfat* ef, off64_t sector)
+{
+	return sector << ef->sb->sector_bits;
+}
+
+/*
+ * Cluster to sector.
+ */
+static off64_t c2s(const struct exfat* ef, cluster_t cluster)
+{
+	if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+		exfat_bug("invalid cluster number %u", cluster);
+	return le32_to_cpu(ef->sb->cluster_sector_start) +
+		((off64_t) (cluster - EXFAT_FIRST_DATA_CLUSTER) << ef->sb->spc_bits);
+}
+
+/*
+ * Cluster to absolute offset.
+ */
+off64_t exfat_c2o(const struct exfat* ef, cluster_t cluster)
+{
+	return s2o(ef, c2s(ef, cluster));
+}
+
+/*
+ * Sector to cluster.
+ */
+static cluster_t s2c(const struct exfat* ef, off64_t sector)
+{
+	return ((sector - le32_to_cpu(ef->sb->cluster_sector_start)) >>
+			ef->sb->spc_bits) + EXFAT_FIRST_DATA_CLUSTER;
+}
+
+/*
+ * Size in bytes to size in clusters (rounded upwards).
+ */
+static uint32_t bytes2clusters(const struct exfat* ef, uint64_t bytes)
+{
+	uint64_t cluster_size = CLUSTER_SIZE(*ef->sb);
+	return (bytes + cluster_size - 1) / cluster_size;
+}
+
+cluster_t exfat_next_cluster(const struct exfat* ef,
+		const struct exfat_node* node, cluster_t cluster)
+{
+	le32_t next;
+	off64_t fat_offset;
+
+	if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+		exfat_bug("bad cluster 0x%x", cluster);
+
+	if (IS_CONTIGUOUS(*node))
+		return cluster + 1;
+	fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start))
+		+ cluster * sizeof(cluster_t);
+	/* FIXME handle I/O error */
+	if (exfat_pread(ef->dev, &next, sizeof(next), fat_offset) < 0)
+		exfat_bug("failed to read the next cluster after %#x", cluster);
+	return le32_to_cpu(next);
+}
+
+cluster_t exfat_advance_cluster(const struct exfat* ef,
+		struct exfat_node* node, uint32_t count)
+{
+	uint32_t i;
+
+	if (node->fptr_index > count)
+	{
+		node->fptr_index = 0;
+		node->fptr_cluster = node->start_cluster;
+	}
+
+	for (i = node->fptr_index; i < count; i++)
+	{
+		node->fptr_cluster = exfat_next_cluster(ef, node, node->fptr_cluster);
+		if (CLUSTER_INVALID(node->fptr_cluster))
+			break; /* the caller should handle this and print appropriate 
+			          error message */
+	}
+	node->fptr_index = count;
+	return node->fptr_cluster;
+}
+
+static cluster_t find_bit_and_set(bitmap_t* bitmap, size_t start, size_t end)
+{
+	const size_t start_index = start / sizeof(bitmap_t) / 8;
+	const size_t end_index = DIV_ROUND_UP(end, sizeof(bitmap_t) * 8);
+	size_t i;
+	size_t start_bitindex;
+	size_t end_bitindex;
+	size_t c;
+
+	for (i = start_index; i < end_index; i++)
+	{
+		if (bitmap[i] == ~((bitmap_t) 0))
+			continue;
+		start_bitindex = MAX(i * sizeof(bitmap_t) * 8, start);
+		end_bitindex = MIN((i + 1) * sizeof(bitmap_t) * 8, end);
+		for (c = start_bitindex; c < end_bitindex; c++)
+			if (BMAP_GET(bitmap, c) == 0)
+			{
+				BMAP_SET(bitmap, c);
+				return c + EXFAT_FIRST_DATA_CLUSTER;
+			}
+	}
+	return EXFAT_CLUSTER_END;
+}
+
+int exfat_flush(struct exfat* ef)
+{
+	if (ef->cmap.dirty)
+	{
+		if (exfat_pwrite(ef->dev, ef->cmap.chunk,
+				BMAP_SIZE(ef->cmap.chunk_size),
+				exfat_c2o(ef, ef->cmap.start_cluster)) < 0)
+		{
+			exfat_error("failed to write clusters bitmap");
+			return -EIO;
+		}
+		ef->cmap.dirty = false;
+	}
+	return 0;
+}
+
+static bool set_next_cluster(const struct exfat* ef, bool contiguous,
+		cluster_t current, cluster_t next)
+{
+	off64_t fat_offset;
+	le32_t next_le32;
+
+	if (contiguous)
+		return true;
+	fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start))
+		+ current * sizeof(cluster_t);
+	next_le32 = cpu_to_le32(next);
+	if (exfat_pwrite(ef->dev, &next_le32, sizeof(next_le32), fat_offset) < 0)
+	{
+		exfat_error("failed to write the next cluster %#x after %#x", next,
+				current);
+		return false;
+	}
+	return true;
+}
+
+static cluster_t allocate_cluster(struct exfat* ef, cluster_t hint)
+{
+	cluster_t cluster;
+
+	hint -= EXFAT_FIRST_DATA_CLUSTER;
+	if (hint >= ef->cmap.chunk_size)
+		hint = 0;
+
+	cluster = find_bit_and_set(ef->cmap.chunk, hint, ef->cmap.chunk_size);
+	if (cluster == EXFAT_CLUSTER_END)
+		cluster = find_bit_and_set(ef->cmap.chunk, 0, hint);
+	if (cluster == EXFAT_CLUSTER_END)
+	{
+		exfat_error("no free space left");
+		return EXFAT_CLUSTER_END;
+	}
+
+	ef->cmap.dirty = true;
+	return cluster;
+}
+
+static void free_cluster(struct exfat* ef, cluster_t cluster)
+{
+	if (CLUSTER_INVALID(cluster))
+		exfat_bug("freeing invalid cluster 0x%x", cluster);
+	if (cluster - EXFAT_FIRST_DATA_CLUSTER >= ef->cmap.size)
+		exfat_bug("freeing non-existing cluster 0x%x (0x%x)", cluster,
+				ef->cmap.size);
+
+	BMAP_CLR(ef->cmap.chunk, cluster - EXFAT_FIRST_DATA_CLUSTER);
+	ef->cmap.dirty = true;
+}
+
+static bool make_noncontiguous(const struct exfat* ef, cluster_t first,
+		cluster_t last)
+{
+	cluster_t c;
+
+	for (c = first; c < last; c++)
+		if (!set_next_cluster(ef, false, c, c + 1))
+			return false;
+	return true;
+}
+
+static int shrink_file(struct exfat* ef, struct exfat_node* node,
+		uint32_t current, uint32_t difference);
+
+static int grow_file(struct exfat* ef, struct exfat_node* node,
+		uint32_t current, uint32_t difference)
+{
+	cluster_t previous;
+	cluster_t next;
+	uint32_t allocated = 0;
+
+	if (difference == 0)
+		exfat_bug("zero clusters count passed");
+
+	if (node->start_cluster != EXFAT_CLUSTER_FREE)
+	{
+		/* get the last cluster of the file */
+		previous = exfat_advance_cluster(ef, node, current - 1);
+		if (CLUSTER_INVALID(previous))
+		{
+			exfat_error("invalid cluster 0x%x while growing", previous);
+			return -EIO;
+		}
+	}
+	else
+	{
+		if (node->fptr_index != 0)
+			exfat_bug("non-zero pointer index (%u)", node->fptr_index);
+		/* file does not have clusters (i.e. is empty), allocate
+		   the first one for it */
+		previous = allocate_cluster(ef, 0);
+		if (CLUSTER_INVALID(previous))
+			return -ENOSPC;
+		node->fptr_cluster = node->start_cluster = previous;
+		allocated = 1;
+		/* file consists of only one cluster, so it's contiguous */
+		node->flags |= EXFAT_ATTRIB_CONTIGUOUS;
+	}
+
+	while (allocated < difference)
+	{
+		next = allocate_cluster(ef, previous + 1);
+		if (CLUSTER_INVALID(next))
+		{
+			if (allocated != 0)
+				shrink_file(ef, node, current + allocated, allocated);
+			return -ENOSPC;
+		}
+		if (next != previous - 1 && IS_CONTIGUOUS(*node))
+		{
+			/* it's a pity, but we are not able to keep the file contiguous
+			   anymore */
+			if (!make_noncontiguous(ef, node->start_cluster, previous))
+				return -EIO;
+			node->flags &= ~EXFAT_ATTRIB_CONTIGUOUS;
+			node->flags |= EXFAT_ATTRIB_DIRTY;
+		}
+		if (!set_next_cluster(ef, IS_CONTIGUOUS(*node), previous, next))
+			return -EIO;
+		previous = next;
+		allocated++;
+	}
+
+	if (!set_next_cluster(ef, IS_CONTIGUOUS(*node), previous,
+			EXFAT_CLUSTER_END))
+		return -EIO;
+	return 0;
+}
+
+static int shrink_file(struct exfat* ef, struct exfat_node* node,
+		uint32_t current, uint32_t difference)
+{
+	cluster_t previous;
+	cluster_t next;
+
+	if (difference == 0)
+		exfat_bug("zero difference passed");
+	if (node->start_cluster == EXFAT_CLUSTER_FREE)
+		exfat_bug("unable to shrink empty file (%u clusters)", current);
+	if (current < difference)
+		exfat_bug("file underflow (%u < %u)", current, difference);
+
+	/* crop the file */
+	if (current > difference)
+	{
+		cluster_t last = exfat_advance_cluster(ef, node,
+				current - difference - 1);
+		if (CLUSTER_INVALID(last))
+		{
+			exfat_error("invalid cluster 0x%x while shrinking", last);
+			return -EIO;
+		}
+		previous = exfat_next_cluster(ef, node, last);
+		if (!set_next_cluster(ef, IS_CONTIGUOUS(*node), last,
+				EXFAT_CLUSTER_END))
+			return -EIO;
+	}
+	else
+	{
+		previous = node->start_cluster;
+		node->start_cluster = EXFAT_CLUSTER_FREE;
+	}
+	node->fptr_index = 0;
+	node->fptr_cluster = node->start_cluster;
+
+	/* free remaining clusters */
+	while (difference--)
+	{
+		if (CLUSTER_INVALID(previous))
+		{
+			exfat_error("invalid cluster 0x%x while freeing after shrink",
+					previous);
+			return -EIO;
+		}
+		next = exfat_next_cluster(ef, node, previous);
+		if (!set_next_cluster(ef, IS_CONTIGUOUS(*node), previous,
+				EXFAT_CLUSTER_FREE))
+			return -EIO;
+		free_cluster(ef, previous);
+		previous = next;
+	}
+	return 0;
+}
+
+static bool erase_raw(struct exfat* ef, size_t size, off64_t offset)
+{
+	if (exfat_pwrite(ef->dev, ef->zero_cluster, size, offset) < 0)
+	{
+		exfat_error("failed to erase %zu bytes at %"PRId64, size, offset);
+		return false;
+	}
+	return true;
+}
+
+static int erase_range(struct exfat* ef, struct exfat_node* node,
+		uint64_t begin, uint64_t end)
+{
+	uint64_t cluster_boundary;
+	cluster_t cluster;
+
+	if (begin >= end)
+		return 0;
+
+	cluster_boundary = (begin | (CLUSTER_SIZE(*ef->sb) - 1)) + 1;
+	cluster = exfat_advance_cluster(ef, node,
+			begin / CLUSTER_SIZE(*ef->sb));
+	if (CLUSTER_INVALID(cluster))
+	{
+		exfat_error("invalid cluster 0x%x while erasing", cluster);
+		return -EIO;
+	}
+	/* erase from the beginning to the closest cluster boundary */
+	if (!erase_raw(ef, MIN(cluster_boundary, end) - begin,
+			exfat_c2o(ef, cluster) + begin % CLUSTER_SIZE(*ef->sb)))
+		return -EIO;
+	/* erase whole clusters */
+	while (cluster_boundary < end)
+	{
+		cluster = exfat_next_cluster(ef, node, cluster);
+		/* the cluster cannot be invalid because we have just allocated it */
+		if (CLUSTER_INVALID(cluster))
+			exfat_bug("invalid cluster 0x%x after allocation", cluster);
+		if (!erase_raw(ef, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, cluster)))
+			return -EIO;
+		cluster_boundary += CLUSTER_SIZE(*ef->sb);
+	}
+	return 0;
+}
+
+int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,
+		bool erase)
+{
+	uint32_t c1 = bytes2clusters(ef, node->size);
+	uint32_t c2 = bytes2clusters(ef, size);
+	int rc = 0;
+
+	if (node->references == 0 && node->parent)
+		exfat_bug("no references, node changes can be lost");
+
+	if (node->size == size)
+		return 0;
+
+	if (c1 < c2)
+		rc = grow_file(ef, node, c1, c2 - c1);
+	else if (c1 > c2)
+		rc = shrink_file(ef, node, c1, c1 - c2);
+
+	if (rc != 0)
+		return rc;
+
+	if (erase)
+	{
+		rc = erase_range(ef, node, node->size, size);
+		if (rc != 0)
+			return rc;
+	}
+
+	exfat_update_mtime(node);
+	node->size = size;
+	node->flags |= EXFAT_ATTRIB_DIRTY;
+	return 0;
+}
+
+uint32_t exfat_count_free_clusters(const struct exfat* ef)
+{
+	uint32_t free_clusters = 0;
+	uint32_t i;
+
+	for (i = 0; i < ef->cmap.size; i++)
+		if (BMAP_GET(ef->cmap.chunk, i) == 0)
+			free_clusters++;
+	return free_clusters;
+}
+
+static int find_used_clusters(const struct exfat* ef,
+		cluster_t* a, cluster_t* b)
+{
+	const cluster_t end = le32_to_cpu(ef->sb->cluster_count);
+
+	/* find first used cluster */
+	for (*a = *b + 1; *a < end; (*a)++)
+		if (BMAP_GET(ef->cmap.chunk, *a - EXFAT_FIRST_DATA_CLUSTER))
+			break;
+	if (*a >= end)
+		return 1;
+
+	/* find last contiguous used cluster */
+	for (*b = *a; *b < end; (*b)++)
+		if (BMAP_GET(ef->cmap.chunk, *b - EXFAT_FIRST_DATA_CLUSTER) == 0)
+		{
+			(*b)--;
+			break;
+		}
+
+	return 0;
+}
+
+int exfat_find_used_sectors(const struct exfat* ef, off64_t* a, off64_t* b)
+{
+	cluster_t ca, cb;
+
+	if (*a == 0 && *b == 0)
+		ca = cb = EXFAT_FIRST_DATA_CLUSTER - 1;
+	else
+	{
+		ca = s2c(ef, *a);
+		cb = s2c(ef, *b);
+	}
+	if (find_used_clusters(ef, &ca, &cb) != 0)
+		return 1;
+	if (*a != 0 || *b != 0)
+		*a = c2s(ef, ca);
+	*b = c2s(ef, cb) + (CLUSTER_SIZE(*ef->sb) - 1) / SECTOR_SIZE(*ef->sb);
+	return 0;
+}
diff --git a/exfat/libexfat/compiler.h b/exfat/libexfat/compiler.h
new file mode 100644
index 0000000..6edd8ec
--- /dev/null
+++ b/exfat/libexfat/compiler.h
@@ -0,0 +1,62 @@
+/*
+	compiler.h (09.06.13)
+	Compiler-specific definitions. Note that unknown compiler is not a
+	showstopper.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef COMPILER_H_INCLUDED
+#define COMPILER_H_INCLUDED
+
+#if defined(__clang__)
+
+#define PRINTF __attribute__((format(printf, 1, 2)))
+#define NORETURN __attribute__((noreturn))
+#define PACKED __attribute__((packed))
+#if __has_extension(c_static_assert)
+#define USE_C11_STATIC_ASSERT
+#endif
+
+#elif defined(__GNUC__)
+
+#define PRINTF __attribute__((format(printf, 1, 2)))
+#define NORETURN __attribute__((noreturn))
+#define PACKED __attribute__((packed))
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#define USE_C11_STATIC_ASSERT
+#endif
+
+#else
+
+#define PRINTF
+#define NORETURN
+#define PACKED
+
+#endif
+
+#ifdef USE_C11_STATIC_ASSERT
+#define STATIC_ASSERT(cond) _Static_assert(cond, #cond)
+#else
+#define CONCAT2(a, b) a ## b
+#define CONCAT1(a, b) CONCAT2(a, b)
+#define STATIC_ASSERT(cond) \
+	extern void CONCAT1(static_assert, __LINE__)(int x[(cond) ? 1 : -1])
+#endif
+
+#endif /* ifndef COMPILER_H_INCLUDED */
diff --git a/exfat/libexfat/exfat.h b/exfat/libexfat/exfat.h
new file mode 100644
index 0000000..ff58e84
--- /dev/null
+++ b/exfat/libexfat/exfat.h
@@ -0,0 +1,219 @@
+/*
+	exfat.h (29.08.09)
+	Definitions of structures and constants used in exFAT file system
+	implementation.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef EXFAT_H_INCLUDED
+#define EXFAT_H_INCLUDED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "compiler.h"
+#include "exfatfs.h"
+#include "version.h"
+
+#define EXFAT_NAME_MAX 256
+#define EXFAT_ATTRIB_CONTIGUOUS 0x10000
+#define EXFAT_ATTRIB_CACHED     0x20000
+#define EXFAT_ATTRIB_DIRTY      0x40000
+#define EXFAT_ATTRIB_UNLINKED   0x80000
+#define IS_CONTIGUOUS(node) (((node).flags & EXFAT_ATTRIB_CONTIGUOUS) != 0)
+#define SECTOR_SIZE(sb) (1 << (sb).sector_bits)
+#define CLUSTER_SIZE(sb) (SECTOR_SIZE(sb) << (sb).spc_bits)
+#define CLUSTER_INVALID(c) \
+	((c) < EXFAT_FIRST_DATA_CLUSTER || (c) > EXFAT_LAST_DATA_CLUSTER)
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define DIV_ROUND_UP(x, d) (((x) + (d) - 1) / (d))
+#define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d))
+#define UTF8_BYTES(c) ((c) * 6) /* UTF-8 character can occupy up to 6 bytes */
+
+typedef size_t bitmap_t;
+#define BMAP_SIZE(count) (ROUND_UP(count, sizeof(bitmap_t) * 8) / 8)
+#define BMAP_BLOCK(index) ((index) / sizeof(bitmap_t) / 8)
+#define BMAP_MASK(index) ((bitmap_t) 1 << ((index) % (sizeof(bitmap_t) * 8)))
+#define BMAP_GET(bitmap, index) \
+	((bitmap)[BMAP_BLOCK(index)] & BMAP_MASK(index))
+#define BMAP_SET(bitmap, index) \
+	((bitmap)[BMAP_BLOCK(index)] |= BMAP_MASK(index))
+#define BMAP_CLR(bitmap, index) \
+	((bitmap)[BMAP_BLOCK(index)] &= ~BMAP_MASK(index))
+
+struct exfat_node
+{
+	struct exfat_node* parent;
+	struct exfat_node* child;
+	struct exfat_node* next;
+	struct exfat_node* prev;
+
+	int references;
+	uint32_t fptr_index;
+	cluster_t fptr_cluster;
+	cluster_t entry_cluster;
+	off64_t entry_offset;
+	cluster_t start_cluster;
+	int flags;
+	uint64_t size;
+	time_t mtime, atime;
+	le16_t name[EXFAT_NAME_MAX + 1];
+};
+
+enum exfat_mode
+{
+	EXFAT_MODE_RO,
+	EXFAT_MODE_RW,
+	EXFAT_MODE_ANY,
+};
+
+struct exfat_dev;
+
+struct exfat
+{
+	struct exfat_dev* dev;
+	struct exfat_super_block* sb;
+	le16_t* upcase;
+	size_t upcase_chars;
+	struct exfat_node* root;
+	struct
+	{
+		cluster_t start_cluster;
+		uint32_t size;				/* in bits */
+		bitmap_t* chunk;
+		uint32_t chunk_size;		/* in bits */
+		bool dirty;
+	}
+	cmap;
+	char label[UTF8_BYTES(EXFAT_ENAME_MAX) + 1];
+	void* zero_cluster;
+	int dmask, fmask;
+	uid_t uid;
+	gid_t gid;
+	int ro;
+	bool noatime;
+};
+
+/* in-core nodes iterator */
+struct exfat_iterator
+{
+	struct exfat_node* parent;
+	struct exfat_node* current;
+};
+
+struct exfat_human_bytes
+{
+	uint64_t value;
+	const char* unit;
+};
+
+extern int exfat_errors;
+
+void exfat_bug(const char* format, ...) PRINTF NORETURN;
+void exfat_error(const char* format, ...) PRINTF;
+void exfat_warn(const char* format, ...) PRINTF;
+void exfat_debug(const char* format, ...) PRINTF;
+
+struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode);
+int exfat_close(struct exfat_dev* dev);
+int exfat_fsync(struct exfat_dev* dev);
+enum exfat_mode exfat_get_mode(const struct exfat_dev* dev);
+off64_t exfat_get_size(const struct exfat_dev* dev);
+off64_t exfat_seek(struct exfat_dev* dev, off64_t offset, int whence);
+ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size);
+ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size);
+ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
+		off64_t offset);
+ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
+		off64_t offset);
+ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
+		void* buffer, size_t size, off64_t offset);
+ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
+		const void* buffer, size_t size, off64_t offset);
+
+int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
+		struct exfat_iterator* it);
+void exfat_closedir(struct exfat* ef, struct exfat_iterator* it);
+struct exfat_node* exfat_readdir(struct exfat* ef, struct exfat_iterator* it);
+int exfat_lookup(struct exfat* ef, struct exfat_node** node,
+		const char* path);
+int exfat_split(struct exfat* ef, struct exfat_node** parent,
+		struct exfat_node** node, le16_t* name, const char* path);
+
+off64_t exfat_c2o(const struct exfat* ef, cluster_t cluster);
+cluster_t exfat_next_cluster(const struct exfat* ef,
+		const struct exfat_node* node, cluster_t cluster);
+cluster_t exfat_advance_cluster(const struct exfat* ef,
+		struct exfat_node* node, uint32_t count);
+int exfat_flush(struct exfat* ef);
+int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size,
+		bool erase);
+uint32_t exfat_count_free_clusters(const struct exfat* ef);
+int exfat_find_used_sectors(const struct exfat* ef, off64_t* a, off64_t* b);
+
+void exfat_stat(const struct exfat* ef, const struct exfat_node* node,
+		struct stat* stbuf);
+void exfat_get_name(const struct exfat_node* node, char* buffer, size_t n);
+uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry);
+uint16_t exfat_add_checksum(const void* entry, uint16_t sum);
+le16_t exfat_calc_checksum(const struct exfat_entry_meta1* meta1,
+		const struct exfat_entry_meta2* meta2, const le16_t* name);
+uint32_t exfat_vbr_start_checksum(const void* sector, size_t size);
+uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum);
+le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name);
+void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb);
+void exfat_print_info(const struct exfat_super_block* sb,
+		uint32_t free_clusters);
+
+int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
+		size_t insize);
+int utf8_to_utf16(le16_t* output, const char* input, size_t outsize,
+		size_t insize);
+size_t utf16_length(const le16_t* str);
+
+struct exfat_node* exfat_get_node(struct exfat_node* node);
+void exfat_put_node(struct exfat* ef, struct exfat_node* node);
+int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir);
+void exfat_reset_cache(struct exfat* ef);
+int exfat_flush_node(struct exfat* ef, struct exfat_node* node);
+int exfat_unlink(struct exfat* ef, struct exfat_node* node);
+int exfat_rmdir(struct exfat* ef, struct exfat_node* node);
+int exfat_mknod(struct exfat* ef, const char* path);
+int exfat_mkdir(struct exfat* ef, const char* path);
+int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path);
+void exfat_utimes(struct exfat_node* node, const struct timespec tv[2]);
+void exfat_update_atime(struct exfat_node* node);
+void exfat_update_mtime(struct exfat_node* node);
+const char* exfat_get_label(struct exfat* ef);
+int exfat_set_label(struct exfat* ef, const char* label);
+
+int exfat_mount(struct exfat* ef, const char* spec, const char* options);
+void exfat_unmount(struct exfat* ef);
+
+time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec);
+void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
+		uint8_t* centisec);
+void exfat_tzset(void);
+
+#endif /* ifndef EXFAT_H_INCLUDED */
diff --git a/exfat/libexfat/exfatfs.h b/exfat/libexfat/exfatfs.h
new file mode 100644
index 0000000..a436daa
--- /dev/null
+++ b/exfat/libexfat/exfatfs.h
@@ -0,0 +1,173 @@
+/*
+	exfatfs.h (29.08.09)
+	Definitions of structures and constants used in exFAT file system.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef EXFATFS_H_INCLUDED
+#define EXFATFS_H_INCLUDED
+
+#include "byteorder.h"
+
+typedef uint32_t cluster_t;		/* cluster number */
+
+#define EXFAT_FIRST_DATA_CLUSTER 2
+#define EXFAT_LAST_DATA_CLUSTER 0xfffffff6
+
+#define EXFAT_CLUSTER_FREE         0 /* free cluster */
+#define EXFAT_CLUSTER_BAD 0xfffffff7 /* cluster contains bad sector */
+#define EXFAT_CLUSTER_END 0xffffffff /* final cluster of file or directory */
+
+#define EXFAT_STATE_MOUNTED 2
+
+struct exfat_super_block
+{
+	uint8_t jump[3];				/* 0x00 jmp and nop instructions */
+	uint8_t oem_name[8];			/* 0x03 "EXFAT   " */
+	uint8_t	__unused1[53];			/* 0x0B always 0 */
+	le64_t sector_start;			/* 0x40 partition first sector */
+	le64_t sector_count;			/* 0x48 partition sectors count */
+	le32_t fat_sector_start;		/* 0x50 FAT first sector */
+	le32_t fat_sector_count;		/* 0x54 FAT sectors count */
+	le32_t cluster_sector_start;	/* 0x58 first cluster sector */
+	le32_t cluster_count;			/* 0x5C total clusters count */
+	le32_t rootdir_cluster;			/* 0x60 first cluster of the root dir */
+	le32_t volume_serial;			/* 0x64 volume serial number */
+	struct							/* 0x68 FS version */
+	{
+		uint8_t minor;
+		uint8_t major;
+	}
+	version;
+	le16_t volume_state;			/* 0x6A volume state flags */
+	uint8_t sector_bits;			/* 0x6C sector size as (1 << n) */
+	uint8_t spc_bits;				/* 0x6D sectors per cluster as (1 << n) */
+	uint8_t fat_count;				/* 0x6E always 1 */
+	uint8_t drive_no;				/* 0x6F always 0x80 */
+	uint8_t allocated_percent;		/* 0x70 percentage of allocated space */
+	uint8_t __unused2[397];			/* 0x71 always 0 */
+	le16_t boot_signature;			/* the value of 0xAA55 */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_super_block) == 512);
+
+#define EXFAT_ENTRY_VALID     0x80
+#define EXFAT_ENTRY_CONTINUED 0x40
+
+#define EXFAT_ENTRY_BITMAP    (0x01 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_UPCASE    (0x02 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_LABEL     (0x03 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_FILE      (0x05 | EXFAT_ENTRY_VALID)
+#define EXFAT_ENTRY_FILE_INFO (0x00 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED)
+#define EXFAT_ENTRY_FILE_NAME (0x01 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED)
+
+struct exfat_entry					/* common container for all entries */
+{
+	uint8_t type;					/* any of EXFAT_ENTRY_xxx */
+	uint8_t data[31];
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry) == 32);
+
+#define EXFAT_ENAME_MAX 15
+
+struct exfat_entry_bitmap			/* allocated clusters bitmap */
+{
+	uint8_t type;					/* EXFAT_ENTRY_BITMAP */
+	uint8_t __unknown1[19];
+	le32_t start_cluster;
+	le64_t size;					/* in bytes */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_bitmap) == 32);
+
+struct exfat_entry_upcase			/* upper case translation table */
+{
+	uint8_t type;					/* EXFAT_ENTRY_UPCASE */
+	uint8_t __unknown1[3];
+	le32_t checksum;
+	uint8_t __unknown2[12];
+	le32_t start_cluster;
+	le64_t size;					/* in bytes */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_upcase) == 32);
+
+struct exfat_entry_label			/* volume label */
+{
+	uint8_t type;					/* EXFAT_ENTRY_LABEL */
+	uint8_t length;					/* number of characters */
+	le16_t name[EXFAT_ENAME_MAX];	/* in UTF-16LE */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_label) == 32);
+
+#define EXFAT_ATTRIB_RO     0x01
+#define EXFAT_ATTRIB_HIDDEN 0x02
+#define EXFAT_ATTRIB_SYSTEM 0x04
+#define EXFAT_ATTRIB_VOLUME 0x08
+#define EXFAT_ATTRIB_DIR    0x10
+#define EXFAT_ATTRIB_ARCH   0x20
+
+struct exfat_entry_meta1			/* file or directory info (part 1) */
+{
+	uint8_t type;					/* EXFAT_ENTRY_FILE */
+	uint8_t continuations;
+	le16_t checksum;
+	le16_t attrib;					/* combination of EXFAT_ATTRIB_xxx */
+	le16_t __unknown1;
+	le16_t crtime, crdate;			/* creation date and time */
+	le16_t mtime, mdate;			/* latest modification date and time */
+	le16_t atime, adate;			/* latest access date and time */
+	uint8_t crtime_cs;				/* creation time in cs (centiseconds) */
+	uint8_t mtime_cs;				/* latest modification time in cs */
+	uint8_t __unknown2[10];
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_meta1) == 32);
+
+#define EXFAT_FLAG_ALWAYS1		(1u << 0)
+#define EXFAT_FLAG_CONTIGUOUS	(1u << 1)
+
+struct exfat_entry_meta2			/* file or directory info (part 2) */
+{
+	uint8_t type;					/* EXFAT_ENTRY_FILE_INFO */
+	uint8_t flags;					/* combination of EXFAT_FLAG_xxx */
+	uint8_t __unknown1;
+	uint8_t name_length;
+	le16_t name_hash;
+	le16_t __unknown2;
+	le64_t real_size;				/* in bytes, equals to size */
+	uint8_t __unknown3[4];
+	le32_t start_cluster;
+	le64_t size;					/* in bytes, equals to real_size */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_meta2) == 32);
+
+struct exfat_entry_name				/* file or directory name */
+{
+	uint8_t type;					/* EXFAT_ENTRY_FILE_NAME */
+	uint8_t __unknown;
+	le16_t name[EXFAT_ENAME_MAX];	/* in UTF-16LE */
+}
+PACKED;
+STATIC_ASSERT(sizeof(struct exfat_entry_name) == 32);
+
+#endif /* ifndef EXFATFS_H_INCLUDED */
diff --git a/exfat/libexfat/io.c b/exfat/libexfat/io.c
new file mode 100644
index 0000000..306c5dd
--- /dev/null
+++ b/exfat/libexfat/io.c
@@ -0,0 +1,396 @@
+/*
+	io.c (02.09.09)
+	exFAT file system implementation library.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef __APPLE__
+#include <sys/disk.h>
+#endif
+#ifdef USE_UBLIO
+#include <sys/uio.h>
+#include <ublio.h>
+#endif
+
+struct exfat_dev
+{
+	int fd;
+	enum exfat_mode mode;
+	off64_t size; /* in bytes */
+#ifdef USE_UBLIO
+	off64_t pos;
+	ublio_filehandle_t ufh;
+#endif
+};
+
+static int open_ro(const char* spec)
+{
+	return open(spec, O_RDONLY);
+}
+
+static int open_rw(const char* spec)
+{
+	int fd = open(spec, O_RDWR);
+#ifdef __linux__
+	int ro = 0;
+
+	/*
+	   This ioctl is needed because after "blockdev --setro" kernel still
+	   allows to open the device in read-write mode but fails writes.
+	*/
+	if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
+	{
+		close(fd);
+		return -1;
+	}
+#endif
+	return fd;
+}
+
+struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
+{
+	struct exfat_dev* dev;
+	struct stat stbuf;
+#ifdef USE_UBLIO
+	struct ublio_param up;
+#endif
+
+	dev = malloc(sizeof(struct exfat_dev));
+	if (dev == NULL)
+	{
+		exfat_error("failed to allocate memory for device structure");
+		return NULL;
+	}
+
+	switch (mode)
+	{
+	case EXFAT_MODE_RO:
+		dev->fd = open_ro(spec);
+		if (dev->fd == -1)
+		{
+			free(dev);
+			exfat_error("failed to open `%s' in read-only mode", spec);
+			return NULL;
+		}
+		dev->mode = EXFAT_MODE_RO;
+		break;
+	case EXFAT_MODE_RW:
+		dev->fd = open_rw(spec);
+		if (dev->fd == -1)
+		{
+			free(dev);
+			exfat_error("failed to open `%s' in read-write mode", spec);
+			return NULL;
+		}
+		dev->mode = EXFAT_MODE_RW;
+		break;
+	case EXFAT_MODE_ANY:
+		dev->fd = open_rw(spec);
+		if (dev->fd != -1)
+		{
+			dev->mode = EXFAT_MODE_RW;
+			break;
+		}
+		dev->fd = open_ro(spec);
+		if (dev->fd != -1)
+		{
+			dev->mode = EXFAT_MODE_RO;
+			exfat_warn("`%s' is write-protected, mounting read-only", spec);
+			break;
+		}
+		free(dev);
+		exfat_error("failed to open `%s'", spec);
+		return NULL;
+	}
+
+	if (fstat(dev->fd, &stbuf) != 0)
+	{
+		close(dev->fd);
+		free(dev);
+		exfat_error("failed to fstat `%s'", spec);
+		return NULL;
+	}
+	if (!S_ISBLK(stbuf.st_mode) &&
+		!S_ISCHR(stbuf.st_mode) &&
+		!S_ISREG(stbuf.st_mode))
+	{
+		close(dev->fd);
+		free(dev);
+		exfat_error("`%s' is neither a device, nor a regular file", spec);
+		return NULL;
+	}
+
+#ifdef __APPLE__
+	if (!S_ISREG(stbuf.st_mode))
+	{
+		uint32_t block_size = 0;
+		uint64_t blocks = 0;
+
+		if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0)
+		{
+			close(dev->fd);
+			free(dev);
+			exfat_error("failed to get block size");
+			return NULL;
+		}
+		if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0)
+		{
+			close(dev->fd);
+			free(dev);
+			exfat_error("failed to get blocks count");
+			return NULL;
+		}
+		dev->size = blocks * block_size;
+	}
+	else
+#endif
+	{
+		/* works for Linux, FreeBSD, Solaris */
+		dev->size = exfat_seek(dev, 0, SEEK_END);
+		if (dev->size <= 0)
+		{
+			close(dev->fd);
+			free(dev);
+			exfat_error("failed to get size of `%s'", spec);
+			return NULL;
+		}
+		if (exfat_seek(dev, 0, SEEK_SET) == -1)
+		{
+			close(dev->fd);
+			free(dev);
+			exfat_error("failed to seek to the beginning of `%s'", spec);
+			return NULL;
+		}
+	}
+
+#ifdef USE_UBLIO
+	memset(&up, 0, sizeof(struct ublio_param));
+	up.up_blocksize = 256 * 1024;
+	up.up_items = 64;
+	up.up_grace = 32;
+	up.up_priv = &dev->fd;
+
+	dev->pos = 0;
+	dev->ufh = ublio_open(&up);
+	if (dev->ufh == NULL)
+	{
+		close(dev->fd);
+		free(dev);
+		exfat_error("failed to initialize ublio");
+		return NULL;
+	}
+#endif
+
+	return dev;
+}
+
+int exfat_close(struct exfat_dev* dev)
+{
+#ifdef USE_UBLIO
+	if (ublio_close(dev->ufh) != 0)
+		exfat_error("failed to close ublio");
+#endif
+	if (close(dev->fd) != 0)
+	{
+		free(dev);
+		exfat_error("failed to close device");
+		return 1;
+	}
+	free(dev);
+	return 0;
+}
+
+int exfat_fsync(struct exfat_dev* dev)
+{
+#ifdef USE_UBLIO
+	if (ublio_fsync(dev->ufh) != 0)
+#else
+	if (fsync(dev->fd) != 0)
+#endif
+	{
+		exfat_error("fsync failed");
+		return 1;
+	}
+	return 0;
+}
+
+enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
+{
+	return dev->mode;
+}
+
+off64_t exfat_get_size(const struct exfat_dev* dev)
+{
+	return dev->size;
+}
+
+off64_t exfat_seek(struct exfat_dev* dev, off64_t offset, int whence)
+{
+#ifdef USE_UBLIO
+	/* XXX SEEK_CUR will be handled incorrectly */
+	return dev->pos = lseek64(dev->fd, offset, whence);
+#else
+	return lseek64(dev->fd, offset, whence);
+#endif
+}
+
+ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
+{
+#ifdef USE_UBLIO
+	ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
+	if (result >= 0)
+		dev->pos += size;
+	return result;
+#else
+	return read(dev->fd, buffer, size);
+#endif
+}
+
+ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
+{
+#ifdef USE_UBLIO
+	ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
+	if (result >= 0)
+		dev->pos += size;
+	return result;
+#else
+	return write(dev->fd, buffer, size);
+#endif
+}
+
+ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
+		off64_t offset)
+{
+#ifdef USE_UBLIO
+	return ublio_pread(dev->ufh, buffer, size, offset);
+#else
+	return pread64(dev->fd, buffer, size, offset);
+#endif
+}
+
+ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
+		off64_t offset)
+{
+#ifdef USE_UBLIO
+	return ublio_pwrite(dev->ufh, buffer, size, offset);
+#else
+	return pwrite64(dev->fd, buffer, size, offset);
+#endif
+}
+
+ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
+		void* buffer, size_t size, off64_t offset)
+{
+	cluster_t cluster;
+	char* bufp = buffer;
+	off64_t lsize, loffset, remainder;
+
+	if (offset >= node->size)
+		return 0;
+	if (size == 0)
+		return 0;
+
+	cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
+	if (CLUSTER_INVALID(cluster))
+	{
+		exfat_error("invalid cluster 0x%x while reading", cluster);
+		return -1;
+	}
+
+	loffset = offset % CLUSTER_SIZE(*ef->sb);
+	remainder = MIN(size, node->size - offset);
+	while (remainder > 0)
+	{
+		if (CLUSTER_INVALID(cluster))
+		{
+			exfat_error("invalid cluster 0x%x while reading", cluster);
+			return -1;
+		}
+		lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
+		if (exfat_pread(ef->dev, bufp, lsize,
+					exfat_c2o(ef, cluster) + loffset) < 0)
+		{
+			exfat_error("failed to read cluster %#x", cluster);
+			return -1;
+		}
+		bufp += lsize;
+		loffset = 0;
+		remainder -= lsize;
+		cluster = exfat_next_cluster(ef, node, cluster);
+	}
+	if (!ef->ro && !ef->noatime)
+		exfat_update_atime(node);
+	return MIN(size, node->size - offset) - remainder;
+}
+
+ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
+		const void* buffer, size_t size, off64_t offset)
+{
+	cluster_t cluster;
+	const char* bufp = buffer;
+	off64_t lsize, loffset, remainder;
+
+ 	if (offset > node->size)
+ 		if (exfat_truncate(ef, node, offset, true) != 0)
+ 			return -1;
+  	if (offset + size > node->size)
+ 		if (exfat_truncate(ef, node, offset + size, false) != 0)
+ 			return -1;
+	if (size == 0)
+		return 0;
+
+	cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
+	if (CLUSTER_INVALID(cluster))
+	{
+		exfat_error("invalid cluster 0x%x while writing", cluster);
+		return -1;
+	}
+
+	loffset = offset % CLUSTER_SIZE(*ef->sb);
+	remainder = size;
+	while (remainder > 0)
+	{
+		if (CLUSTER_INVALID(cluster))
+		{
+			exfat_error("invalid cluster 0x%x while writing", cluster);
+			return -1;
+		}
+		lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
+		if (exfat_pwrite(ef->dev, bufp, lsize,
+				exfat_c2o(ef, cluster) + loffset) < 0)
+		{
+			exfat_error("failed to write cluster %#x", cluster);
+			return -1;
+		}
+		bufp += lsize;
+		loffset = 0;
+		remainder -= lsize;
+		cluster = exfat_next_cluster(ef, node, cluster);
+	}
+	exfat_update_mtime(node);
+	return size - remainder;
+}
diff --git a/exfat/libexfat/log.c b/exfat/libexfat/log.c
new file mode 100644
index 0000000..7e541aa
--- /dev/null
+++ b/exfat/libexfat/log.c
@@ -0,0 +1,110 @@
+/*
+	log.c (02.09.09)
+	exFAT file system implementation library.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <stdarg.h>
+#include <syslog.h>
+#include <unistd.h>
+
+int exfat_errors;
+
+/*
+ * This message means an internal bug in exFAT implementation.
+ */
+void exfat_bug(const char* format, ...)
+{
+	va_list ap, aq;
+
+	va_start(ap, format);
+	va_copy(aq, ap);
+
+	fflush(stdout);
+	fputs("BUG: ", stderr);
+	vfprintf(stderr, format, ap);
+	va_end(ap);
+	fputs(".\n", stderr);
+
+	if (!isatty(STDERR_FILENO))
+		vsyslog(LOG_CRIT, format, aq);
+	va_end(aq);
+
+	abort();
+}
+
+/*
+ * This message means an error in exFAT file system.
+ */
+void exfat_error(const char* format, ...)
+{
+	va_list ap, aq;
+
+	exfat_errors++;
+	va_start(ap, format);
+	va_copy(aq, ap);
+
+	fflush(stdout);
+	fputs("ERROR: ", stderr);
+	vfprintf(stderr, format, ap);
+	va_end(ap);
+	fputs(".\n", stderr);
+
+	if (!isatty(STDERR_FILENO))
+		vsyslog(LOG_ERR, format, aq);
+	va_end(aq);
+}
+
+/*
+ * This message means that there is something unexpected in exFAT file system
+ * that can be a potential problem.
+ */
+void exfat_warn(const char* format, ...)
+{
+	va_list ap, aq;
+
+	va_start(ap, format);
+	va_copy(aq, ap);
+
+	fflush(stdout);
+	fputs("WARN: ", stderr);
+	vfprintf(stderr, format, ap);
+	va_end(ap);
+	fputs(".\n", stderr);
+
+	if (!isatty(STDERR_FILENO))
+		vsyslog(LOG_WARNING, format, aq);
+	va_end(aq);
+}
+
+/*
+ * Just debug message. Disabled by default.
+ */
+void exfat_debug(const char* format, ...)
+{
+	va_list ap;
+
+	fflush(stdout);
+	fputs("DEBUG: ", stderr);
+	va_start(ap, format);
+	vfprintf(stderr, format, ap);
+	va_end(ap);
+	fputs(".\n", stderr);
+}
diff --git a/exfat/libexfat/lookup.c b/exfat/libexfat/lookup.c
new file mode 100644
index 0000000..00cd533
--- /dev/null
+++ b/exfat/libexfat/lookup.c
@@ -0,0 +1,225 @@
+/*
+	lookup.c (02.09.09)
+	exFAT file system implementation library.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
+		struct exfat_iterator* it)
+{
+	int rc;
+
+	exfat_get_node(dir);
+	it->parent = dir;
+	it->current = NULL;
+	rc = exfat_cache_directory(ef, dir);
+	if (rc != 0)
+		exfat_put_node(ef, dir);
+	return rc;
+}
+
+void exfat_closedir(struct exfat* ef, struct exfat_iterator* it)
+{
+	exfat_put_node(ef, it->parent);
+	it->parent = NULL;
+	it->current = NULL;
+}
+
+struct exfat_node* exfat_readdir(struct exfat* ef, struct exfat_iterator* it)
+{
+	if (it->current == NULL)
+		it->current = it->parent->child;
+	else
+		it->current = it->current->next;
+
+	if (it->current != NULL)
+		return exfat_get_node(it->current);
+	else
+		return NULL;
+}
+
+static int compare_char(struct exfat* ef, uint16_t a, uint16_t b)
+{
+	if (a >= ef->upcase_chars || b >= ef->upcase_chars)
+		return (int) a - (int) b;
+
+	return (int) le16_to_cpu(ef->upcase[a]) - (int) le16_to_cpu(ef->upcase[b]);
+}
+
+static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b)
+{
+	while (le16_to_cpu(*a) && le16_to_cpu(*b))
+	{
+		int rc = compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
+		if (rc != 0)
+			return rc;
+		a++;
+		b++;
+	}
+	return compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
+}
+
+static int lookup_name(struct exfat* ef, struct exfat_node* parent,
+		struct exfat_node** node, const char* name, size_t n)
+{
+	struct exfat_iterator it;
+	le16_t buffer[EXFAT_NAME_MAX + 1];
+	int rc;
+
+	*node = NULL;
+
+	rc = utf8_to_utf16(buffer, name, EXFAT_NAME_MAX, n);
+	if (rc != 0)
+		return rc;
+
+	rc = exfat_opendir(ef, parent, &it);
+	if (rc != 0)
+		return rc;
+	while ((*node = exfat_readdir(ef, &it)))
+	{
+		if (compare_name(ef, buffer, (*node)->name) == 0)
+		{
+			exfat_closedir(ef, &it);
+			return 0;
+		}
+		exfat_put_node(ef, *node);
+	}
+	exfat_closedir(ef, &it);
+	return -ENOENT;
+}
+
+static size_t get_comp(const char* path, const char** comp)
+{
+	const char* end;
+
+	*comp = path + strspn(path, "/");				/* skip leading slashes */
+	end = strchr(*comp, '/');
+	if (end == NULL)
+		return strlen(*comp);
+	else
+		return end - *comp;
+}
+
+int exfat_lookup(struct exfat* ef, struct exfat_node** node,
+		const char* path)
+{
+	struct exfat_node* parent;
+	const char* p;
+	size_t n;
+	int rc;
+
+	/* start from the root directory */
+	parent = *node = exfat_get_node(ef->root);
+	for (p = path; (n = get_comp(p, &p)); p += n)
+	{
+		if (n == 1 && *p == '.')				/* skip "." component */
+			continue;
+		rc = lookup_name(ef, parent, node, p, n);
+		if (rc != 0)
+		{
+			exfat_put_node(ef, parent);
+			return rc;
+		}
+		exfat_put_node(ef, parent);
+		parent = *node;
+	}
+	return 0;
+}
+
+static bool is_last_comp(const char* comp, size_t length)
+{
+	const char* p = comp + length;
+
+	return get_comp(p, &p) == 0;
+}
+
+static bool is_allowed(const char* comp, size_t length)
+{
+	size_t i;
+
+	for (i = 0; i < length; i++)
+		switch (comp[i])
+		{
+		case 0x01 ... 0x1f:
+		case '/':
+		case '\\':
+		case ':':
+		case '*':
+		case '?':
+		case '"':
+		case '<':
+		case '>':
+		case '|':
+			return false;
+		}
+	return true;
+}
+
+int exfat_split(struct exfat* ef, struct exfat_node** parent,
+		struct exfat_node** node, le16_t* name, const char* path)
+{
+	const char* p;
+	size_t n;
+	int rc;
+
+	memset(name, 0, (EXFAT_NAME_MAX + 1) * sizeof(le16_t));
+	*parent = *node = exfat_get_node(ef->root);
+	for (p = path; (n = get_comp(p, &p)); p += n)
+	{
+		if (n == 1 && *p == '.')
+			continue;
+		if (is_last_comp(p, n))
+		{
+			if (!is_allowed(p, n))
+			{
+				/* contains characters that are not allowed */
+				exfat_put_node(ef, *parent);
+				return -ENOENT;
+			}
+			rc = utf8_to_utf16(name, p, EXFAT_NAME_MAX, n);
+			if (rc != 0)
+			{
+				exfat_put_node(ef, *parent);
+				return rc;
+			}
+
+			rc = lookup_name(ef, *parent, node, p, n);
+			if (rc != 0 && rc != -ENOENT)
+			{
+				exfat_put_node(ef, *parent);
+				return rc;
+			}
+			return 0;
+		}
+		rc = lookup_name(ef, *parent, node, p, n);
+		if (rc != 0)
+		{
+			exfat_put_node(ef, *parent);
+			return rc;
+		}
+		exfat_put_node(ef, *parent);
+		*parent = *node;
+	}
+	exfat_bug("impossible");
+}
diff --git a/exfat/libexfat/mount.c b/exfat/libexfat/mount.c
new file mode 100644
index 0000000..928c561
--- /dev/null
+++ b/exfat/libexfat/mount.c
@@ -0,0 +1,338 @@
+/*
+	mount.c (22.10.09)
+	exFAT file system implementation library.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+static uint64_t rootdir_size(const struct exfat* ef)
+{
+	uint64_t clusters = 0;
+	cluster_t rootdir_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
+
+	while (!CLUSTER_INVALID(rootdir_cluster))
+	{
+		clusters++;
+		/* root directory cannot be contiguous because there is no flag
+		   to indicate this */
+		rootdir_cluster = exfat_next_cluster(ef, ef->root, rootdir_cluster);
+	}
+	return clusters * CLUSTER_SIZE(*ef->sb);
+}
+
+static const char* get_option(const char* options, const char* option_name)
+{
+	const char* p;
+	size_t length = strlen(option_name);
+
+	for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
+		if ((p == options || p[-1] == ',') && p[length] == '=')
+			return p + length + 1;
+	return NULL;
+}
+
+static int get_int_option(const char* options, const char* option_name,
+		int base, int default_value)
+{
+	const char* p = get_option(options, option_name);
+
+	if (p == NULL)
+		return default_value;
+	return strtol(p, NULL, base);
+}
+
+static bool match_option(const char* options, const char* option_name)
+{
+	const char* p;
+	size_t length = strlen(option_name);
+
+	for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
+		if ((p == options || p[-1] == ',') &&
+				(p[length] == ',' || p[length] == '\0'))
+			return true;
+	return false;
+}
+
+static void parse_options(struct exfat* ef, const char* options)
+{
+	int sys_umask = umask(0);
+	int opt_umask;
+
+	umask(sys_umask); /* restore umask */
+	opt_umask = get_int_option(options, "umask", 8, sys_umask);
+	ef->dmask = get_int_option(options, "dmask", 8, opt_umask) & 0777;
+	ef->fmask = get_int_option(options, "fmask", 8, opt_umask) & 0777;
+
+	ef->uid = get_int_option(options, "uid", 10, geteuid());
+	ef->gid = get_int_option(options, "gid", 10, getegid());
+
+	ef->noatime = match_option(options, "noatime");
+}
+
+static int verify_vbr_checksum(struct exfat_dev* dev, void* sector,
+		off64_t sector_size)
+{
+	uint32_t vbr_checksum;
+	int i;
+
+	if (exfat_pread(dev, sector, sector_size, 0) < 0)
+	{
+		exfat_error("failed to read boot sector");
+		return 1;
+	}
+	vbr_checksum = exfat_vbr_start_checksum(sector, sector_size);
+	for (i = 1; i < 11; i++)
+	{
+		if (exfat_pread(dev, sector, sector_size, i * sector_size) < 0)
+		{
+			exfat_error("failed to read VBR sector");
+			return 1;
+		}
+		vbr_checksum = exfat_vbr_add_checksum(sector, sector_size,
+				vbr_checksum);
+	}
+	if (exfat_pread(dev, sector, sector_size, i * sector_size) < 0)
+	{
+		exfat_error("failed to read VBR checksum sector");
+		return 1;
+	}
+	for (i = 0; i < sector_size / sizeof(vbr_checksum); i++)
+		if (le32_to_cpu(((const le32_t*) sector)[i]) != vbr_checksum)
+		{
+			exfat_error("invalid VBR checksum 0x%x (expected 0x%x)",
+					le32_to_cpu(((const le32_t*) sector)[i]), vbr_checksum);
+			return 1;
+		}
+	return 0;
+}
+
+static int commit_super_block(const struct exfat* ef)
+{
+	if (exfat_pwrite(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0)
+	{
+		exfat_error("failed to write super block");
+		return 1;
+	}
+	return exfat_fsync(ef->dev);
+}
+
+static int prepare_super_block(const struct exfat* ef)
+{
+	if (le16_to_cpu(ef->sb->volume_state) & EXFAT_STATE_MOUNTED)
+		exfat_warn("volume was not unmounted cleanly");
+
+	if (ef->ro)
+		return 0;
+
+	ef->sb->volume_state = cpu_to_le16(
+			le16_to_cpu(ef->sb->volume_state) | EXFAT_STATE_MOUNTED);
+	return commit_super_block(ef);
+}
+
+int exfat_mount(struct exfat* ef, const char* spec, const char* options)
+{
+	int rc;
+	enum exfat_mode mode;
+
+	exfat_tzset();
+	memset(ef, 0, sizeof(struct exfat));
+
+	parse_options(ef, options);
+
+	if (match_option(options, "ro"))
+		mode = EXFAT_MODE_RO;
+	else if (match_option(options, "ro_fallback"))
+		mode = EXFAT_MODE_ANY;
+	else
+		mode = EXFAT_MODE_RW;
+	ef->dev = exfat_open(spec, mode);
+	if (ef->dev == NULL)
+		return -EIO;
+	if (exfat_get_mode(ef->dev) == EXFAT_MODE_RO)
+	{
+		if (mode == EXFAT_MODE_ANY)
+			ef->ro = -1;
+		else
+			ef->ro = 1;
+	}
+
+	ef->sb = malloc(sizeof(struct exfat_super_block));
+	if (ef->sb == NULL)
+	{
+		exfat_close(ef->dev);
+		exfat_error("failed to allocate memory for the super block");
+		return -ENOMEM;
+	}
+	memset(ef->sb, 0, sizeof(struct exfat_super_block));
+
+	if (exfat_pread(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0)
+	{
+		exfat_close(ef->dev);
+		free(ef->sb);
+		exfat_error("failed to read boot sector");
+		return -EIO;
+	}
+	if (memcmp(ef->sb->oem_name, "EXFAT   ", 8) != 0)
+	{
+		exfat_close(ef->dev);
+		free(ef->sb);
+		exfat_error("exFAT file system is not found");
+		return -EIO;
+	}
+	if (ef->sb->version.major != 1 || ef->sb->version.minor != 0)
+	{
+		exfat_close(ef->dev);
+		exfat_error("unsupported exFAT version: %hhu.%hhu",
+				ef->sb->version.major, ef->sb->version.minor);
+		free(ef->sb);
+		return -EIO;
+	}
+	if (ef->sb->fat_count != 1)
+	{
+		exfat_close(ef->dev);
+		free(ef->sb);
+		exfat_error("unsupported FAT count: %hhu", ef->sb->fat_count);
+		return -EIO;
+	}
+	/* officially exFAT supports cluster size up to 32 MB */
+	if ((int) ef->sb->sector_bits + (int) ef->sb->spc_bits > 25)
+	{
+		exfat_close(ef->dev);
+		free(ef->sb);
+		exfat_error("too big cluster size: 2^%d",
+				(int) ef->sb->sector_bits + (int) ef->sb->spc_bits);
+		return -EIO;
+	}
+
+	ef->zero_cluster = malloc(CLUSTER_SIZE(*ef->sb));
+	if (ef->zero_cluster == NULL)
+	{
+		exfat_close(ef->dev);
+		free(ef->sb);
+		exfat_error("failed to allocate zero sector");
+		return -ENOMEM;
+	}
+	/* use zero_cluster as a temporary buffer for VBR checksum verification */
+	if (verify_vbr_checksum(ef->dev, ef->zero_cluster,
+			SECTOR_SIZE(*ef->sb)) != 0)
+	{
+		free(ef->zero_cluster);
+		exfat_close(ef->dev);
+		free(ef->sb);
+		return -EIO;
+	}
+	memset(ef->zero_cluster, 0, CLUSTER_SIZE(*ef->sb));
+
+	ef->root = malloc(sizeof(struct exfat_node));
+	if (ef->root == NULL)
+	{
+		free(ef->zero_cluster);
+		exfat_close(ef->dev);
+		free(ef->sb);
+		exfat_error("failed to allocate root node");
+		return -ENOMEM;
+	}
+	memset(ef->root, 0, sizeof(struct exfat_node));
+	ef->root->flags = EXFAT_ATTRIB_DIR;
+	ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
+	ef->root->fptr_cluster = ef->root->start_cluster;
+	ef->root->name[0] = cpu_to_le16('\0');
+	ef->root->size = rootdir_size(ef);
+	/* exFAT does not have time attributes for the root directory */
+	ef->root->mtime = 0;
+	ef->root->atime = 0;
+	/* always keep at least 1 reference to the root node */
+	exfat_get_node(ef->root);
+
+	rc = exfat_cache_directory(ef, ef->root);
+	if (rc != 0)
+		goto error;
+	if (ef->upcase == NULL)
+	{
+		exfat_error("upcase table is not found");
+		goto error;
+	}
+	if (ef->cmap.chunk == NULL)
+	{
+		exfat_error("clusters bitmap is not found");
+		goto error;
+	}
+
+	if (prepare_super_block(ef) != 0)
+		goto error;
+
+	return 0;
+
+error:
+	exfat_put_node(ef, ef->root);
+	exfat_reset_cache(ef);
+	free(ef->root);
+	free(ef->zero_cluster);
+	exfat_close(ef->dev);
+	free(ef->sb);
+	return -EIO;
+}
+
+static void finalize_super_block(struct exfat* ef)
+{
+	if (ef->ro)
+		return;
+
+	ef->sb->volume_state = cpu_to_le16(
+			le16_to_cpu(ef->sb->volume_state) & ~EXFAT_STATE_MOUNTED);
+
+	/* Some implementations set the percentage of allocated space to 0xff
+	   on FS creation and never update it. In this case leave it as is. */
+	if (ef->sb->allocated_percent != 0xff)
+	{
+		uint32_t free, total;
+
+		free = exfat_count_free_clusters(ef);
+		total = le32_to_cpu(ef->sb->cluster_count);
+		ef->sb->allocated_percent = ((total - free) * 100 + total / 2) / total;
+	}
+
+	commit_super_block(ef);
+}
+
+void exfat_unmount(struct exfat* ef)
+{
+	exfat_put_node(ef, ef->root);
+	exfat_reset_cache(ef);
+	free(ef->root);
+	ef->root = NULL;
+	finalize_super_block(ef);
+	exfat_close(ef->dev);	/* close descriptor immediately after fsync */
+	ef->dev = NULL;
+	free(ef->zero_cluster);
+	ef->zero_cluster = NULL;
+	free(ef->cmap.chunk);
+	ef->cmap.chunk = NULL;
+	free(ef->sb);
+	ef->sb = NULL;
+	free(ef->upcase);
+	ef->upcase = NULL;
+	ef->upcase_chars = 0;
+}
diff --git a/exfat/libexfat/node.c b/exfat/libexfat/node.c
new file mode 100644
index 0000000..56205fc
--- /dev/null
+++ b/exfat/libexfat/node.c
@@ -0,0 +1,1172 @@
+/*
+	node.c (09.10.09)
+	exFAT file system implementation library.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+/* on-disk nodes iterator */
+struct iterator
+{
+	cluster_t cluster;
+	off64_t offset;
+	int contiguous;
+	char* chunk;
+};
+
+struct exfat_node* exfat_get_node(struct exfat_node* node)
+{
+	/* if we switch to multi-threaded mode we will need atomic
+	   increment here and atomic decrement in exfat_put_node() */
+	node->references++;
+	return node;
+}
+
+void exfat_put_node(struct exfat* ef, struct exfat_node* node)
+{
+	if (--node->references < 0)
+	{
+		char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+		exfat_get_name(node, buffer, sizeof(buffer) - 1);
+		exfat_bug("reference counter of `%s' is below zero", buffer);
+	}
+
+	if (node->references == 0)
+	{
+		/* FIXME handle I/O error */
+		if (exfat_flush_node(ef, node) != 0)
+			exfat_bug("node flush failed");
+		if (node->flags & EXFAT_ATTRIB_UNLINKED)
+		{
+			/* free all clusters and node structure itself */
+			exfat_truncate(ef, node, 0, true);
+			free(node);
+		}
+		/* FIXME handle I/O error */
+		if (exfat_flush(ef) != 0)
+			exfat_bug("flush failed");
+	}
+}
+
+/**
+ * Cluster + offset from the beginning of the directory to absolute offset.
+ */
+static off64_t co2o(struct exfat* ef, cluster_t cluster, off64_t offset)
+{
+	return exfat_c2o(ef, cluster) + offset % CLUSTER_SIZE(*ef->sb);
+}
+
+static int opendir(struct exfat* ef, const struct exfat_node* dir,
+		struct iterator* it)
+{
+	if (!(dir->flags & EXFAT_ATTRIB_DIR))
+		exfat_bug("not a directory");
+	it->cluster = dir->start_cluster;
+	it->offset = 0;
+	it->contiguous = IS_CONTIGUOUS(*dir);
+	it->chunk = malloc(CLUSTER_SIZE(*ef->sb));
+	if (it->chunk == NULL)
+	{
+		exfat_error("out of memory");
+		return -ENOMEM;
+	}
+	if (exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb),
+			exfat_c2o(ef, it->cluster)) < 0)
+	{
+		exfat_error("failed to read directory cluster %#x", it->cluster);
+		return -EIO;
+	}
+	return 0;
+}
+
+static void closedir(struct iterator* it)
+{
+	it->cluster = 0;
+	it->offset = 0;
+	it->contiguous = 0;
+	free(it->chunk);
+	it->chunk = NULL;
+}
+
+static int fetch_next_entry(struct exfat* ef, const struct exfat_node* parent,
+		struct iterator* it)
+{
+	/* move iterator to the next entry in the directory */
+	it->offset += sizeof(struct exfat_entry);
+	/* fetch the next cluster if needed */
+	if ((it->offset & (CLUSTER_SIZE(*ef->sb) - 1)) == 0)
+	{
+		/* reached the end of directory; the caller should check this
+		   condition too */
+		if (it->offset >= parent->size)
+			return 0;
+		it->cluster = exfat_next_cluster(ef, parent, it->cluster);
+		if (CLUSTER_INVALID(it->cluster))
+		{
+			exfat_error("invalid cluster 0x%x while reading directory",
+					it->cluster);
+			return 1;
+		}
+		if (exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb),
+				exfat_c2o(ef, it->cluster)) < 0)
+		{
+			exfat_error("failed to read the next directory cluster %#x",
+					it->cluster);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static struct exfat_node* allocate_node(void)
+{
+	struct exfat_node* node = malloc(sizeof(struct exfat_node));
+	if (node == NULL)
+	{
+		exfat_error("failed to allocate node");
+		return NULL;
+	}
+	memset(node, 0, sizeof(struct exfat_node));
+	return node;
+}
+
+static void init_node_meta1(struct exfat_node* node,
+		const struct exfat_entry_meta1* meta1)
+{
+	node->flags = le16_to_cpu(meta1->attrib);
+	node->mtime = exfat_exfat2unix(meta1->mdate, meta1->mtime,
+			meta1->mtime_cs);
+	/* there is no centiseconds field for atime */
+	node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, 0);
+}
+
+static void init_node_meta2(struct exfat_node* node,
+		const struct exfat_entry_meta2* meta2)
+{
+	node->size = le64_to_cpu(meta2->size);
+	node->start_cluster = le32_to_cpu(meta2->start_cluster);
+	node->fptr_cluster = node->start_cluster;
+	if (meta2->flags & EXFAT_FLAG_CONTIGUOUS)
+		node->flags |= EXFAT_ATTRIB_CONTIGUOUS;
+}
+
+static const struct exfat_entry* get_entry_ptr(const struct exfat* ef,
+		const struct iterator* it)
+{
+	return (const struct exfat_entry*)
+			(it->chunk + it->offset % CLUSTER_SIZE(*ef->sb));
+}
+
+/*
+ * Reads one entry in directory at position pointed by iterator and fills
+ * node structure.
+ */
+static int readdir(struct exfat* ef, const struct exfat_node* parent,
+		struct exfat_node** node, struct iterator* it)
+{
+	int rc = -EIO;
+	const struct exfat_entry* entry;
+	const struct exfat_entry_meta1* meta1;
+	const struct exfat_entry_meta2* meta2;
+	const struct exfat_entry_name* file_name;
+	const struct exfat_entry_upcase* upcase;
+	const struct exfat_entry_bitmap* bitmap;
+	const struct exfat_entry_label* label;
+	uint8_t continuations = 0;
+	le16_t* namep = NULL;
+	uint16_t reference_checksum = 0;
+	uint16_t actual_checksum = 0;
+	uint64_t real_size = 0;
+
+	*node = NULL;
+
+	for (;;)
+	{
+		if (it->offset >= parent->size)
+		{
+			if (continuations != 0)
+			{
+				exfat_error("expected %hhu continuations", continuations);
+				goto error;
+			}
+			return -ENOENT; /* that's OK, means end of directory */
+		}
+
+		entry = get_entry_ptr(ef, it);
+		switch (entry->type)
+		{
+		case EXFAT_ENTRY_FILE:
+			if (continuations != 0)
+			{
+				exfat_error("expected %hhu continuations before new entry",
+						continuations);
+				goto error;
+			}
+			meta1 = (const struct exfat_entry_meta1*) entry;
+			continuations = meta1->continuations;
+			/* each file entry must have at least 2 continuations:
+			   info and name */
+			if (continuations < 2)
+			{
+				exfat_error("too few continuations (%hhu)", continuations);
+				goto error;
+			}
+			if (continuations > 1 +
+					DIV_ROUND_UP(EXFAT_NAME_MAX, EXFAT_ENAME_MAX))
+			{
+				exfat_error("too many continuations (%hhu)", continuations);
+				goto error;
+			}
+			reference_checksum = le16_to_cpu(meta1->checksum);
+			actual_checksum = exfat_start_checksum(meta1);
+			*node = allocate_node();
+			if (*node == NULL)
+			{
+				rc = -ENOMEM;
+				goto error;
+			}
+			/* new node has zero reference counter */
+			(*node)->entry_cluster = it->cluster;
+			(*node)->entry_offset = it->offset;
+			init_node_meta1(*node, meta1);
+			namep = (*node)->name;
+			break;
+
+		case EXFAT_ENTRY_FILE_INFO:
+			if (continuations < 2)
+			{
+				exfat_error("unexpected continuation (%hhu)",
+						continuations);
+				goto error;
+			}
+			meta2 = (const struct exfat_entry_meta2*) entry;
+			if (meta2->flags & ~(EXFAT_FLAG_ALWAYS1 | EXFAT_FLAG_CONTIGUOUS))
+			{
+				exfat_error("unknown flags in meta2 (0x%hhx)", meta2->flags);
+				goto error;
+			}
+			init_node_meta2(*node, meta2);
+			actual_checksum = exfat_add_checksum(entry, actual_checksum);
+			real_size = le64_to_cpu(meta2->real_size);
+			/* empty files must be marked as non-contiguous */
+			if ((*node)->size == 0 && (meta2->flags & EXFAT_FLAG_CONTIGUOUS))
+			{
+				exfat_error("empty file marked as contiguous (0x%hhx)",
+						meta2->flags);
+				goto error;
+			}
+			/* directories must be aligned on at cluster boundary */
+			if (((*node)->flags & EXFAT_ATTRIB_DIR) &&
+				(*node)->size % CLUSTER_SIZE(*ef->sb) != 0)
+			{
+				exfat_error("directory has invalid size %"PRIu64" bytes",
+						(*node)->size);
+				goto error;
+			}
+			--continuations;
+			break;
+
+		case EXFAT_ENTRY_FILE_NAME:
+			if (continuations == 0)
+			{
+				exfat_error("unexpected continuation");
+				goto error;
+			}
+			file_name = (const struct exfat_entry_name*) entry;
+			actual_checksum = exfat_add_checksum(entry, actual_checksum);
+
+			memcpy(namep, file_name->name,
+					MIN(EXFAT_ENAME_MAX,
+						((*node)->name + EXFAT_NAME_MAX - namep)) *
+					sizeof(le16_t));
+			namep += EXFAT_ENAME_MAX;
+			if (--continuations == 0)
+			{
+				/*
+				   There are two fields that contain file size. Maybe they
+				   plan to add compression support in the future and one of
+				   those fields is visible (uncompressed) size and the other
+				   is real (compressed) size. Anyway, currently it looks like
+				   exFAT does not support compression and both fields must be
+				   equal.
+
+				   There is an exception though: pagefile.sys (its real_size
+				   is always 0).
+				*/
+				if (real_size != (*node)->size)
+				{
+					char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+
+					exfat_get_name(*node, buffer, sizeof(buffer) - 1);
+					exfat_error("`%s' real size does not equal to size "
+							"(%"PRIu64" != %"PRIu64")", buffer,
+							real_size, (*node)->size);
+					goto error;
+				}
+				if (actual_checksum != reference_checksum)
+				{
+					char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+
+					exfat_get_name(*node, buffer, sizeof(buffer) - 1);
+					exfat_error("`%s' has invalid checksum (0x%hx != 0x%hx)",
+							buffer, actual_checksum, reference_checksum);
+					goto error;
+				}
+				if (fetch_next_entry(ef, parent, it) != 0)
+					goto error;
+				return 0; /* entry completed */
+			}
+			break;
+
+		case EXFAT_ENTRY_UPCASE:
+			if (ef->upcase != NULL)
+				break;
+			upcase = (const struct exfat_entry_upcase*) entry;
+			if (CLUSTER_INVALID(le32_to_cpu(upcase->start_cluster)))
+			{
+				exfat_error("invalid cluster 0x%x in upcase table",
+						le32_to_cpu(upcase->start_cluster));
+				goto error;
+			}
+			if (le64_to_cpu(upcase->size) == 0 ||
+				le64_to_cpu(upcase->size) > 0xffff * sizeof(uint16_t) ||
+				le64_to_cpu(upcase->size) % sizeof(uint16_t) != 0)
+			{
+				exfat_error("bad upcase table size (%"PRIu64" bytes)",
+						le64_to_cpu(upcase->size));
+				goto error;
+			}
+			ef->upcase = malloc(le64_to_cpu(upcase->size));
+			if (ef->upcase == NULL)
+			{
+				exfat_error("failed to allocate upcase table (%"PRIu64" bytes)",
+						le64_to_cpu(upcase->size));
+				rc = -ENOMEM;
+				goto error;
+			}
+			ef->upcase_chars = le64_to_cpu(upcase->size) / sizeof(le16_t);
+
+			if (exfat_pread(ef->dev, ef->upcase, le64_to_cpu(upcase->size),
+					exfat_c2o(ef, le32_to_cpu(upcase->start_cluster))) < 0)
+			{
+				exfat_error("failed to read upper case table "
+						"(%"PRIu64" bytes starting at cluster %#x)",
+						le64_to_cpu(upcase->size),
+						le32_to_cpu(upcase->start_cluster));
+				goto error;
+			}
+			break;
+
+		case EXFAT_ENTRY_BITMAP:
+			bitmap = (const struct exfat_entry_bitmap*) entry;
+			ef->cmap.start_cluster = le32_to_cpu(bitmap->start_cluster);
+			if (CLUSTER_INVALID(ef->cmap.start_cluster))
+			{
+				exfat_error("invalid cluster 0x%x in clusters bitmap",
+						ef->cmap.start_cluster);
+				goto error;
+			}
+			ef->cmap.size = le32_to_cpu(ef->sb->cluster_count) -
+				EXFAT_FIRST_DATA_CLUSTER;
+			if (le64_to_cpu(bitmap->size) < DIV_ROUND_UP(ef->cmap.size, 8))
+			{
+				exfat_error("invalid clusters bitmap size: %"PRIu64
+						" (expected at least %u)",
+						le64_to_cpu(bitmap->size),
+						DIV_ROUND_UP(ef->cmap.size, 8));
+				goto error;
+			}
+			/* FIXME bitmap can be rather big, up to 512 MB */
+			ef->cmap.chunk_size = ef->cmap.size;
+			ef->cmap.chunk = malloc(BMAP_SIZE(ef->cmap.chunk_size));
+			if (ef->cmap.chunk == NULL)
+			{
+				exfat_error("failed to allocate clusters bitmap chunk "
+						"(%"PRIu64" bytes)", le64_to_cpu(bitmap->size));
+				rc = -ENOMEM;
+				goto error;
+			}
+
+			if (exfat_pread(ef->dev, ef->cmap.chunk,
+					BMAP_SIZE(ef->cmap.chunk_size),
+					exfat_c2o(ef, ef->cmap.start_cluster)) < 0)
+			{
+				exfat_error("failed to read clusters bitmap "
+						"(%"PRIu64" bytes starting at cluster %#x)",
+						le64_to_cpu(bitmap->size), ef->cmap.start_cluster);
+				goto error;
+			}
+			break;
+
+		case EXFAT_ENTRY_LABEL:
+			label = (const struct exfat_entry_label*) entry;
+			if (label->length > EXFAT_ENAME_MAX)
+			{
+				exfat_error("too long label (%hhu chars)", label->length);
+				goto error;
+			}
+			if (utf16_to_utf8(ef->label, label->name,
+						sizeof(ef->label) - 1, EXFAT_ENAME_MAX) != 0)
+				goto error;
+			break;
+
+		default:
+			if (entry->type & EXFAT_ENTRY_VALID)
+			{
+				exfat_error("unknown entry type 0x%hhx", entry->type);
+				goto error;
+			}
+			break;
+		}
+
+		if (fetch_next_entry(ef, parent, it) != 0)
+			goto error;
+	}
+	/* we never reach here */
+
+error:
+	free(*node);
+	*node = NULL;
+	return rc;
+}
+
+int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir)
+{
+	struct iterator it;
+	int rc;
+	struct exfat_node* node;
+	struct exfat_node* current = NULL;
+
+	if (dir->flags & EXFAT_ATTRIB_CACHED)
+		return 0; /* already cached */
+
+	rc = opendir(ef, dir, &it);
+	if (rc != 0)
+		return rc;
+	while ((rc = readdir(ef, dir, &node, &it)) == 0)
+	{
+		node->parent = dir;
+		if (current != NULL)
+		{
+			current->next = node;
+			node->prev = current;
+		}
+		else
+			dir->child = node;
+
+		current = node;
+	}
+	closedir(&it);
+
+	if (rc != -ENOENT)
+	{
+		/* rollback */
+		for (current = dir->child; current; current = node)
+		{
+			node = current->next;
+			free(current);
+		}
+		dir->child = NULL;
+		return rc;
+	}
+
+	dir->flags |= EXFAT_ATTRIB_CACHED;
+	return 0;
+}
+
+static void tree_attach(struct exfat_node* dir, struct exfat_node* node)
+{
+	node->parent = dir;
+	if (dir->child)
+	{
+		dir->child->prev = node;
+		node->next = dir->child;
+	}
+	dir->child = node;
+}
+
+static void tree_detach(struct exfat_node* node)
+{
+	if (node->prev)
+		node->prev->next = node->next;
+	else /* this is the first node in the list */
+		node->parent->child = node->next;
+	if (node->next)
+		node->next->prev = node->prev;
+	node->parent = NULL;
+	node->prev = NULL;
+	node->next = NULL;
+}
+
+static void reset_cache(struct exfat* ef, struct exfat_node* node)
+{
+	while (node->child)
+	{
+		struct exfat_node* p = node->child;
+		reset_cache(ef, p);
+		tree_detach(p);
+		free(p);
+	}
+	node->flags &= ~EXFAT_ATTRIB_CACHED;
+	if (node->references != 0)
+	{
+		char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+		exfat_get_name(node, buffer, sizeof(buffer) - 1);
+		exfat_warn("non-zero reference counter (%d) for `%s'",
+				node->references, buffer);
+	}
+	while (node->references)
+		exfat_put_node(ef, node);
+}
+
+void exfat_reset_cache(struct exfat* ef)
+{
+	reset_cache(ef, ef->root);
+}
+
+static void next_entry(struct exfat* ef, const struct exfat_node* parent,
+		cluster_t* cluster, off64_t* offset)
+{
+	*offset += sizeof(struct exfat_entry);
+	if (*offset % CLUSTER_SIZE(*ef->sb) == 0)
+		/* next cluster cannot be invalid */
+		*cluster = exfat_next_cluster(ef, parent, *cluster);
+}
+
+int exfat_flush_node(struct exfat* ef, struct exfat_node* node)
+{
+	cluster_t cluster;
+	off64_t offset;
+	off64_t meta1_offset, meta2_offset;
+	struct exfat_entry_meta1 meta1;
+	struct exfat_entry_meta2 meta2;
+
+	if (!(node->flags & EXFAT_ATTRIB_DIRTY))
+		return 0; /* no need to flush */
+
+	if (ef->ro)
+		exfat_bug("unable to flush node to read-only FS");
+
+	if (node->parent == NULL)
+		return 0; /* do not flush unlinked node */
+
+	cluster = node->entry_cluster;
+	offset = node->entry_offset;
+	meta1_offset = co2o(ef, cluster, offset);
+	next_entry(ef, node->parent, &cluster, &offset);
+	meta2_offset = co2o(ef, cluster, offset);
+
+	if (exfat_pread(ef->dev, &meta1, sizeof(meta1), meta1_offset) < 0)
+	{
+		exfat_error("failed to read meta1 entry on flush");
+		return -EIO;
+	}
+	if (meta1.type != EXFAT_ENTRY_FILE)
+		exfat_bug("invalid type of meta1: 0x%hhx", meta1.type);
+	meta1.attrib = cpu_to_le16(node->flags);
+	exfat_unix2exfat(node->mtime, &meta1.mdate, &meta1.mtime, &meta1.mtime_cs);
+	exfat_unix2exfat(node->atime, &meta1.adate, &meta1.atime, NULL);
+
+	if (exfat_pread(ef->dev, &meta2, sizeof(meta2), meta2_offset) < 0)
+	{
+		exfat_error("failed to read meta2 entry on flush");
+		return -EIO;
+	}
+	if (meta2.type != EXFAT_ENTRY_FILE_INFO)
+		exfat_bug("invalid type of meta2: 0x%hhx", meta2.type);
+	meta2.size = meta2.real_size = cpu_to_le64(node->size);
+	meta2.start_cluster = cpu_to_le32(node->start_cluster);
+	meta2.flags = EXFAT_FLAG_ALWAYS1;
+	/* empty files must not be marked as contiguous */
+	if (node->size != 0 && IS_CONTIGUOUS(*node))
+		meta2.flags |= EXFAT_FLAG_CONTIGUOUS;
+	/* name hash remains unchanged, no need to recalculate it */
+
+	meta1.checksum = exfat_calc_checksum(&meta1, &meta2, node->name);
+
+	if (exfat_pwrite(ef->dev, &meta1, sizeof(meta1), meta1_offset) < 0)
+	{
+		exfat_error("failed to write meta1 entry on flush");
+		return -EIO;
+	}
+	if (exfat_pwrite(ef->dev, &meta2, sizeof(meta2), meta2_offset) < 0)
+	{
+		exfat_error("failed to write meta2 entry on flush");
+		return -EIO;
+	}
+
+	node->flags &= ~EXFAT_ATTRIB_DIRTY;
+	return 0;
+}
+
+static bool erase_entry(struct exfat* ef, struct exfat_node* node)
+{
+	cluster_t cluster = node->entry_cluster;
+	off64_t offset = node->entry_offset;
+	int name_entries = DIV_ROUND_UP(utf16_length(node->name), EXFAT_ENAME_MAX);
+	uint8_t entry_type;
+
+	entry_type = EXFAT_ENTRY_FILE & ~EXFAT_ENTRY_VALID;
+	if (exfat_pwrite(ef->dev, &entry_type, 1, co2o(ef, cluster, offset)) < 0)
+	{
+		exfat_error("failed to erase meta1 entry");
+		return false;
+	}
+
+	next_entry(ef, node->parent, &cluster, &offset);
+	entry_type = EXFAT_ENTRY_FILE_INFO & ~EXFAT_ENTRY_VALID;
+	if (exfat_pwrite(ef->dev, &entry_type, 1, co2o(ef, cluster, offset)) < 0)
+	{
+		exfat_error("failed to erase meta2 entry");
+		return false;
+	}
+
+	while (name_entries--)
+	{
+		next_entry(ef, node->parent, &cluster, &offset);
+		entry_type = EXFAT_ENTRY_FILE_NAME & ~EXFAT_ENTRY_VALID;
+		if (exfat_pwrite(ef->dev, &entry_type, 1,
+				co2o(ef, cluster, offset)) < 0)
+		{
+			exfat_error("failed to erase name entry");
+			return false;
+		}
+	}
+	return true;
+}
+
+static int shrink_directory(struct exfat* ef, struct exfat_node* dir,
+		off64_t deleted_offset)
+{
+	const struct exfat_node* node;
+	const struct exfat_node* last_node;
+	uint64_t entries = 0;
+	uint64_t new_size;
+	int rc;
+
+	if (!(dir->flags & EXFAT_ATTRIB_DIR))
+		exfat_bug("attempted to shrink a file");
+	if (!(dir->flags & EXFAT_ATTRIB_CACHED))
+		exfat_bug("attempted to shrink uncached directory");
+
+	for (last_node = node = dir->child; node; node = node->next)
+	{
+		if (deleted_offset < node->entry_offset)
+		{
+			/* there are other entries after the removed one, no way to shrink
+			   this directory */
+			return 0;
+		}
+		if (last_node->entry_offset < node->entry_offset)
+			last_node = node;
+	}
+
+	if (last_node)
+	{
+		/* offset of the last entry */
+		entries += last_node->entry_offset / sizeof(struct exfat_entry);
+		/* two subentries with meta info */
+		entries += 2;
+		/* subentries with file name */
+		entries += DIV_ROUND_UP(utf16_length(last_node->name),
+				EXFAT_ENAME_MAX);
+	}
+
+	new_size = DIV_ROUND_UP(entries * sizeof(struct exfat_entry),
+				 CLUSTER_SIZE(*ef->sb)) * CLUSTER_SIZE(*ef->sb);
+	if (new_size == 0) /* directory always has at least 1 cluster */
+		new_size = CLUSTER_SIZE(*ef->sb);
+	if (new_size == dir->size)
+		return 0;
+	rc = exfat_truncate(ef, dir, new_size, true);
+	if (rc != 0)
+		return rc;
+	return 0;
+}
+
+static int delete(struct exfat* ef, struct exfat_node* node)
+{
+	struct exfat_node* parent = node->parent;
+	off64_t deleted_offset = node->entry_offset;
+	int rc;
+
+	exfat_get_node(parent);
+	if (!erase_entry(ef, node))
+	{
+		exfat_put_node(ef, parent);
+		return -EIO;
+	}
+	exfat_update_mtime(parent);
+	tree_detach(node);
+	rc = shrink_directory(ef, parent, deleted_offset);
+	exfat_put_node(ef, parent);
+	/* file clusters will be freed when node reference counter becomes 0 */
+	node->flags |= EXFAT_ATTRIB_UNLINKED;
+	return rc;
+}
+
+int exfat_unlink(struct exfat* ef, struct exfat_node* node)
+{
+	if (node->flags & EXFAT_ATTRIB_DIR)
+		return -EISDIR;
+	return delete(ef, node);
+}
+
+int exfat_rmdir(struct exfat* ef, struct exfat_node* node)
+{
+	if (!(node->flags & EXFAT_ATTRIB_DIR))
+		return -ENOTDIR;
+	/* check that directory is empty */
+	exfat_cache_directory(ef, node);
+	if (node->child)
+		return -ENOTEMPTY;
+	return delete(ef, node);
+}
+
+static int grow_directory(struct exfat* ef, struct exfat_node* dir,
+		uint64_t asize, uint32_t difference)
+{
+	return exfat_truncate(ef, dir,
+			DIV_ROUND_UP(asize + difference, CLUSTER_SIZE(*ef->sb))
+				* CLUSTER_SIZE(*ef->sb), true);
+}
+
+static int find_slot(struct exfat* ef, struct exfat_node* dir,
+		cluster_t* cluster, off64_t* offset, int subentries)
+{
+	struct iterator it;
+	int rc;
+	const struct exfat_entry* entry;
+	int contiguous = 0;
+
+	rc = opendir(ef, dir, &it);
+	if (rc != 0)
+		return rc;
+	for (;;)
+	{
+		if (contiguous == 0)
+		{
+			*cluster = it.cluster;
+			*offset = it.offset;
+		}
+		entry = get_entry_ptr(ef, &it);
+		if (entry->type & EXFAT_ENTRY_VALID)
+			contiguous = 0;
+		else
+			contiguous++;
+		if (contiguous == subentries)
+			break;	/* suitable slot is found */
+		if (it.offset + sizeof(struct exfat_entry) >= dir->size)
+		{
+			rc = grow_directory(ef, dir, dir->size,
+					(subentries - contiguous) * sizeof(struct exfat_entry));
+			if (rc != 0)
+			{
+				closedir(&it);
+				return rc;
+			}
+		}
+		if (fetch_next_entry(ef, dir, &it) != 0)
+		{
+			closedir(&it);
+			return -EIO;
+		}
+	}
+	closedir(&it);
+	return 0;
+}
+
+static int write_entry(struct exfat* ef, struct exfat_node* dir,
+		const le16_t* name, cluster_t cluster, off64_t offset, uint16_t attrib)
+{
+	struct exfat_node* node;
+	struct exfat_entry_meta1 meta1;
+	struct exfat_entry_meta2 meta2;
+	const size_t name_length = utf16_length(name);
+	const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
+	int i;
+
+	node = allocate_node();
+	if (node == NULL)
+		return -ENOMEM;
+	node->entry_cluster = cluster;
+	node->entry_offset = offset;
+	memcpy(node->name, name, name_length * sizeof(le16_t));
+
+	memset(&meta1, 0, sizeof(meta1));
+	meta1.type = EXFAT_ENTRY_FILE;
+	meta1.continuations = 1 + name_entries;
+	meta1.attrib = cpu_to_le16(attrib);
+	exfat_unix2exfat(time(NULL), &meta1.crdate, &meta1.crtime,
+			&meta1.crtime_cs);
+	meta1.adate = meta1.mdate = meta1.crdate;
+	meta1.atime = meta1.mtime = meta1.crtime;
+	meta1.mtime_cs = meta1.crtime_cs; /* there is no atime_cs */
+
+	memset(&meta2, 0, sizeof(meta2));
+	meta2.type = EXFAT_ENTRY_FILE_INFO;
+	meta2.flags = EXFAT_FLAG_ALWAYS1;
+	meta2.name_length = name_length;
+	meta2.name_hash = exfat_calc_name_hash(ef, node->name);
+	meta2.start_cluster = cpu_to_le32(EXFAT_CLUSTER_FREE);
+
+	meta1.checksum = exfat_calc_checksum(&meta1, &meta2, node->name);
+
+	if (exfat_pwrite(ef->dev, &meta1, sizeof(meta1),
+			co2o(ef, cluster, offset)) < 0)
+	{
+		exfat_error("failed to write meta1 entry");
+		return -EIO;
+	}
+	next_entry(ef, dir, &cluster, &offset);
+	if (exfat_pwrite(ef->dev, &meta2, sizeof(meta2),
+			co2o(ef, cluster, offset)) < 0)
+	{
+		exfat_error("failed to write meta2 entry");
+		return -EIO;
+	}
+	for (i = 0; i < name_entries; i++)
+	{
+		struct exfat_entry_name name_entry = {EXFAT_ENTRY_FILE_NAME, 0};
+		memcpy(name_entry.name, node->name + i * EXFAT_ENAME_MAX,
+				MIN(EXFAT_ENAME_MAX, EXFAT_NAME_MAX - i * EXFAT_ENAME_MAX) *
+				sizeof(le16_t));
+		next_entry(ef, dir, &cluster, &offset);
+		if (exfat_pwrite(ef->dev, &name_entry, sizeof(name_entry),
+				co2o(ef, cluster, offset)) < 0)
+		{
+			exfat_error("failed to write name entry");
+			return -EIO;
+		}
+	}
+
+	init_node_meta1(node, &meta1);
+	init_node_meta2(node, &meta2);
+
+	tree_attach(dir, node);
+	exfat_update_mtime(dir);
+	return 0;
+}
+
+static int create(struct exfat* ef, const char* path, uint16_t attrib)
+{
+	struct exfat_node* dir;
+	struct exfat_node* existing;
+	cluster_t cluster = EXFAT_CLUSTER_BAD;
+	off64_t offset = -1;
+	le16_t name[EXFAT_NAME_MAX + 1];
+	int rc;
+
+	rc = exfat_split(ef, &dir, &existing, name, path);
+	if (rc != 0)
+		return rc;
+	if (existing != NULL)
+	{
+		exfat_put_node(ef, existing);
+		exfat_put_node(ef, dir);
+		return -EEXIST;
+	}
+
+	rc = find_slot(ef, dir, &cluster, &offset,
+			2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
+	if (rc != 0)
+	{
+		exfat_put_node(ef, dir);
+		return rc;
+	}
+	rc = write_entry(ef, dir, name, cluster, offset, attrib);
+	exfat_put_node(ef, dir);
+	return rc;
+}
+
+int exfat_mknod(struct exfat* ef, const char* path)
+{
+	return create(ef, path, EXFAT_ATTRIB_ARCH);
+}
+
+int exfat_mkdir(struct exfat* ef, const char* path)
+{
+	int rc;
+	struct exfat_node* node;
+
+	rc = create(ef, path, EXFAT_ATTRIB_ARCH | EXFAT_ATTRIB_DIR);
+	if (rc != 0)
+		return rc;
+	rc = exfat_lookup(ef, &node, path);
+	if (rc != 0)
+		return 0;
+	/* directories always have at least one cluster */
+	rc = exfat_truncate(ef, node, CLUSTER_SIZE(*ef->sb), true);
+	if (rc != 0)
+	{
+		delete(ef, node);
+		exfat_put_node(ef, node);
+		return rc;
+	}
+	exfat_put_node(ef, node);
+	return 0;
+}
+
+static int rename_entry(struct exfat* ef, struct exfat_node* dir,
+		struct exfat_node* node, const le16_t* name, cluster_t new_cluster,
+		off64_t new_offset)
+{
+	struct exfat_entry_meta1 meta1;
+	struct exfat_entry_meta2 meta2;
+	cluster_t old_cluster = node->entry_cluster;
+	off64_t old_offset = node->entry_offset;
+	const size_t name_length = utf16_length(name);
+	const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
+	int i;
+
+	if (exfat_pread(ef->dev, &meta1, sizeof(meta1),
+			co2o(ef, old_cluster, old_offset)) < 0)
+	{
+		exfat_error("failed to read meta1 entry on rename");
+		return -EIO;
+	}
+	next_entry(ef, node->parent, &old_cluster, &old_offset);
+	if (exfat_pread(ef->dev, &meta2, sizeof(meta2),
+			co2o(ef, old_cluster, old_offset)) < 0)
+	{
+		exfat_error("failed to read meta2 entry on rename");
+		return -EIO;
+	}
+	meta1.continuations = 1 + name_entries;
+	meta2.name_hash = exfat_calc_name_hash(ef, name);
+	meta2.name_length = name_length;
+	meta1.checksum = exfat_calc_checksum(&meta1, &meta2, name);
+
+	if (!erase_entry(ef, node))
+		return -EIO;
+
+	node->entry_cluster = new_cluster;
+	node->entry_offset = new_offset;
+
+	if (exfat_pwrite(ef->dev, &meta1, sizeof(meta1),
+			co2o(ef, new_cluster, new_offset)) < 0)
+	{
+		exfat_error("failed to write meta1 entry on rename");
+		return -EIO;
+	}
+	next_entry(ef, dir, &new_cluster, &new_offset);
+	if (exfat_pwrite(ef->dev, &meta2, sizeof(meta2),
+			co2o(ef, new_cluster, new_offset)) < 0)
+	{
+		exfat_error("failed to write meta2 entry on rename");
+		return -EIO;
+	}
+
+	for (i = 0; i < name_entries; i++)
+	{
+		struct exfat_entry_name name_entry = {EXFAT_ENTRY_FILE_NAME, 0};
+		memcpy(name_entry.name, name + i * EXFAT_ENAME_MAX,
+				EXFAT_ENAME_MAX * sizeof(le16_t));
+		next_entry(ef, dir, &new_cluster, &new_offset);
+		if (exfat_pwrite(ef->dev, &name_entry, sizeof(name_entry),
+				co2o(ef, new_cluster, new_offset)) < 0)
+		{
+			exfat_error("failed to write name entry on rename");
+			return -EIO;
+		}
+	}
+
+	memcpy(node->name, name, (EXFAT_NAME_MAX + 1) * sizeof(le16_t));
+	tree_detach(node);
+	tree_attach(dir, node);
+	return 0;
+}
+
+int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path)
+{
+	struct exfat_node* node;
+	struct exfat_node* existing;
+	struct exfat_node* dir;
+	cluster_t cluster = EXFAT_CLUSTER_BAD;
+	off64_t offset = -1;
+	le16_t name[EXFAT_NAME_MAX + 1];
+	int rc;
+
+	rc = exfat_lookup(ef, &node, old_path);
+	if (rc != 0)
+		return rc;
+
+	rc = exfat_split(ef, &dir, &existing, name, new_path);
+	if (rc != 0)
+	{
+		exfat_put_node(ef, node);
+		return rc;
+	}
+
+	/* check that target is not a subdirectory of the source */
+	if (node->flags & EXFAT_ATTRIB_DIR)
+	{
+		struct exfat_node* p;
+
+		for (p = dir; p; p = p->parent)
+			if (node == p)
+			{
+				if (existing != NULL)
+					exfat_put_node(ef, existing);
+				exfat_put_node(ef, dir);
+				exfat_put_node(ef, node);
+				return -EINVAL;
+			}
+	}
+
+	if (existing != NULL)
+	{
+		/* remove target if it's not the same node as source */
+		if (existing != node)
+		{
+			if (existing->flags & EXFAT_ATTRIB_DIR)
+			{
+				if (node->flags & EXFAT_ATTRIB_DIR)
+					rc = exfat_rmdir(ef, existing);
+				else
+					rc = -ENOTDIR;
+			}
+			else
+			{
+				if (!(node->flags & EXFAT_ATTRIB_DIR))
+					rc = exfat_unlink(ef, existing);
+				else
+					rc = -EISDIR;
+			}
+			exfat_put_node(ef, existing);
+			if (rc != 0)
+			{
+				exfat_put_node(ef, dir);
+				exfat_put_node(ef, node);
+				return rc;
+			}
+		}
+		else
+			exfat_put_node(ef, existing);
+	}
+
+	rc = find_slot(ef, dir, &cluster, &offset,
+			2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
+	if (rc != 0)
+	{
+		exfat_put_node(ef, dir);
+		exfat_put_node(ef, node);
+		return rc;
+	}
+	rc = rename_entry(ef, dir, node, name, cluster, offset);
+	exfat_put_node(ef, dir);
+	exfat_put_node(ef, node);
+	return 0;
+}
+
+void exfat_utimes(struct exfat_node* node, const struct timespec tv[2])
+{
+	node->atime = tv[0].tv_sec;
+	node->mtime = tv[1].tv_sec;
+	node->flags |= EXFAT_ATTRIB_DIRTY;
+}
+
+void exfat_update_atime(struct exfat_node* node)
+{
+	node->atime = time(NULL);
+	node->flags |= EXFAT_ATTRIB_DIRTY;
+}
+
+void exfat_update_mtime(struct exfat_node* node)
+{
+	node->mtime = time(NULL);
+	node->flags |= EXFAT_ATTRIB_DIRTY;
+}
+
+const char* exfat_get_label(struct exfat* ef)
+{
+	return ef->label;
+}
+
+static int find_label(struct exfat* ef, cluster_t* cluster, off64_t* offset)
+{
+	struct iterator it;
+	int rc;
+
+	rc = opendir(ef, ef->root, &it);
+	if (rc != 0)
+		return rc;
+
+	for (;;)
+	{
+		if (it.offset >= ef->root->size)
+		{
+			closedir(&it);
+			return -ENOENT;
+		}
+
+		if (get_entry_ptr(ef, &it)->type == EXFAT_ENTRY_LABEL)
+		{
+			*cluster = it.cluster;
+			*offset = it.offset;
+			closedir(&it);
+			return 0;
+		}
+
+		if (fetch_next_entry(ef, ef->root, &it) != 0)
+		{
+			closedir(&it);
+			return -EIO;
+		}
+	}
+}
+
+int exfat_set_label(struct exfat* ef, const char* label)
+{
+	le16_t label_utf16[EXFAT_ENAME_MAX + 1];
+	int rc;
+	cluster_t cluster;
+	off64_t offset;
+	struct exfat_entry_label entry;
+
+	memset(label_utf16, 0, sizeof(label_utf16));
+	rc = utf8_to_utf16(label_utf16, label, EXFAT_ENAME_MAX, strlen(label));
+	if (rc != 0)
+		return rc;
+
+	rc = find_label(ef, &cluster, &offset);
+	if (rc == -ENOENT)
+		rc = find_slot(ef, ef->root, &cluster, &offset, 1);
+	if (rc != 0)
+		return rc;
+
+	entry.type = EXFAT_ENTRY_LABEL;
+	entry.length = utf16_length(label_utf16);
+	memcpy(entry.name, label_utf16, sizeof(entry.name));
+	if (entry.length == 0)
+		entry.type ^= EXFAT_ENTRY_VALID;
+
+	if (exfat_pwrite(ef->dev, &entry, sizeof(struct exfat_entry_label),
+			co2o(ef, cluster, offset)) < 0)
+	{
+		exfat_error("failed to write label entry");
+		return -EIO;
+	}
+	strcpy(ef->label, label);
+	return 0;
+}
diff --git a/exfat/libexfat/platform.h b/exfat/libexfat/platform.h
new file mode 100644
index 0000000..320f757
--- /dev/null
+++ b/exfat/libexfat/platform.h
@@ -0,0 +1,79 @@
+/*
+	platform.h (14.05.13)
+	OS-specific code (libc-specific in fact). Note that systems with the
+	same kernel can use different libc implementations.
+
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PLATFORM_H_INCLUDED
+#define PLATFORM_H_INCLUDED
+
+#if defined(__GLIBC__)
+
+#include <endian.h>
+#include <byteswap.h>
+
+#elif defined(__APPLE__)
+
+#include <machine/endian.h>
+#include <libkern/OSByteOrder.h>
+#define bswap_16(x) OSSwapInt16(x)
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+
+#elif defined(__FreeBSD__) || defined(__DragonFlyBSD__) || defined(__NetBSD__)
+
+#include <sys/endian.h>
+#define bswap_16(x) bswap16(x)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+
+#elif defined(__OpenBSD__)
+
+#include <machine/endian.h>
+#define bswap_16(x) swap16(x)
+#define bswap_32(x) swap32(x)
+#define bswap_64(x) swap64(x)
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+
+#elif defined(__sun)
+
+#include <sys/byteorder.h>
+#define bswap_16(x) BSWAP_16(x)
+#define bswap_32(x) BSWAP_32(x)
+#define bswap_64(x) BSWAP_64(x)
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#ifdef _LITTLE_ENDIAN
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#else
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif
+
+#else 
+#error Unknown platform
+#endif
+
+#endif /* ifndef PLATFORM_H_INCLUDED */
diff --git a/exfat/libexfat/time.c b/exfat/libexfat/time.c
new file mode 100644
index 0000000..43c5fa4
--- /dev/null
+++ b/exfat/libexfat/time.c
@@ -0,0 +1,158 @@
+/*
+	time.c (03.02.12)
+	exFAT file system implementation library.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+
+/* timezone offset from UTC in seconds; positive for western timezones,
+   negative for eastern ones */
+static long exfat_timezone;
+
+#define SEC_IN_MIN 60ll
+#define SEC_IN_HOUR (60 * SEC_IN_MIN)
+#define SEC_IN_DAY (24 * SEC_IN_HOUR)
+#define SEC_IN_YEAR (365 * SEC_IN_DAY) /* not leap year */
+/* Unix epoch started at 0:00:00 UTC 1 January 1970 */
+#define UNIX_EPOCH_YEAR 1970
+/* exFAT epoch started at 0:00:00 UTC 1 January 1980 */
+#define EXFAT_EPOCH_YEAR 1980
+/* number of years from Unix epoch to exFAT epoch */
+#define EPOCH_DIFF_YEAR (EXFAT_EPOCH_YEAR - UNIX_EPOCH_YEAR)
+/* number of days from Unix epoch to exFAT epoch (considering leap days) */
+#define EPOCH_DIFF_DAYS (EPOCH_DIFF_YEAR * 365 + EPOCH_DIFF_YEAR / 4)
+/* number of seconds from Unix epoch to exFAT epoch (considering leap days) */
+#define EPOCH_DIFF_SEC (EPOCH_DIFF_DAYS * SEC_IN_DAY)
+/* number of leap years passed from exFAT epoch to the specified year
+   (excluding the specified year itself) */
+#define LEAP_YEARS(year) ((EXFAT_EPOCH_YEAR + (year) - 1) / 4 \
+		- (EXFAT_EPOCH_YEAR - 1) / 4)
+/* checks whether the specified year is leap */
+#define IS_LEAP_YEAR(year) ((EXFAT_EPOCH_YEAR + (year)) % 4 == 0)
+
+static const time_t days_in_year[] =
+{
+	/* Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec */
+	0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334
+};
+
+time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec)
+{
+	time_t unix_time = EPOCH_DIFF_SEC;
+	uint16_t ndate = le16_to_cpu(date);
+	uint16_t ntime = le16_to_cpu(time);
+
+	uint16_t day    = ndate & 0x1f;      /* 5 bits, 1-31 */
+	uint16_t month  = ndate >> 5 & 0xf;  /* 4 bits, 1-12 */
+	uint16_t year   = ndate >> 9;        /* 7 bits, 1-127 (+1980) */
+
+	uint16_t twosec = ntime & 0x1f;      /* 5 bits, 0-29 (2 sec granularity) */
+	uint16_t min    = ntime >> 5 & 0x3f; /* 6 bits, 0-59 */
+	uint16_t hour   = ntime >> 11;       /* 5 bits, 0-23 */
+
+	if (day == 0 || month == 0 || month > 12)
+	{
+		exfat_error("bad date %u-%02hu-%02hu",
+				year + EXFAT_EPOCH_YEAR, month, day);
+		return 0;
+	}
+	if (hour > 23 || min > 59 || twosec > 29)
+	{
+		exfat_error("bad time %hu:%02hu:%02u",
+				hour, min, twosec * 2);
+		return 0;
+	}
+	if (centisec > 199)
+	{
+		exfat_error("bad centiseconds count %hhu", centisec);
+		return 0;
+	}
+
+	/* every 4th year between 1904 and 2096 is leap */
+	unix_time += year * SEC_IN_YEAR + LEAP_YEARS(year) * SEC_IN_DAY;
+	unix_time += days_in_year[month] * SEC_IN_DAY;
+	/* if it's leap year and February has passed we should add 1 day */
+	if ((EXFAT_EPOCH_YEAR + year) % 4 == 0 && month > 2)
+		unix_time += SEC_IN_DAY;
+	unix_time += (day - 1) * SEC_IN_DAY;
+
+	unix_time += hour * SEC_IN_HOUR;
+	unix_time += min * SEC_IN_MIN;
+	/* exFAT represents time with 2 sec granularity */
+	unix_time += twosec * 2;
+	unix_time += centisec / 100;
+
+	/* exFAT stores timestamps in local time, so we correct it to UTC */
+	unix_time += exfat_timezone;
+
+	return unix_time;
+}
+
+void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
+		uint8_t* centisec)
+{
+	time_t shift = EPOCH_DIFF_SEC + exfat_timezone;
+	uint16_t day, month, year;
+	uint16_t twosec, min, hour;
+	int days;
+	int i;
+
+	/* time before exFAT epoch cannot be represented */
+	if (unix_time < shift)
+		unix_time = shift;
+
+	unix_time -= shift;
+
+	days = unix_time / SEC_IN_DAY;
+	year = (4 * days) / (4 * 365 + 1);
+	days -= year * 365 + LEAP_YEARS(year);
+	month = 0;
+	for (i = 1; i <= 12; i++)
+	{
+		int leap_day = (IS_LEAP_YEAR(year) && i == 2);
+		int leap_sub = (IS_LEAP_YEAR(year) && i >= 3);
+
+		if (i == 12 || days - leap_sub < days_in_year[i + 1] + leap_day)
+		{
+			month = i;
+			days -= days_in_year[i] + leap_sub;
+			break;
+		}
+	}
+	day = days + 1;
+
+	hour = (unix_time % SEC_IN_DAY) / SEC_IN_HOUR;
+	min = (unix_time % SEC_IN_HOUR) / SEC_IN_MIN;
+	twosec = (unix_time % SEC_IN_MIN) / 2;
+
+	*date = cpu_to_le16(day | (month << 5) | (year << 9));
+	*time = cpu_to_le16(twosec | (min << 5) | (hour << 11));
+	if (centisec)
+		*centisec = (unix_time % 2) * 100;
+}
+
+void exfat_tzset(void)
+{
+	time_t now;
+
+	tzset();
+	now = time(NULL);
+	exfat_timezone = mktime(gmtime(&now)) - now;
+}
diff --git a/exfat/libexfat/utf.c b/exfat/libexfat/utf.c
new file mode 100644
index 0000000..3b7e26b
--- /dev/null
+++ b/exfat/libexfat/utf.c
@@ -0,0 +1,231 @@
+/*
+	utf.c (13.09.09)
+	exFAT file system implementation library.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <errno.h>
+
+static char* wchar_to_utf8(char* output, wchar_t wc, size_t outsize)
+{
+	if (wc <= 0x7f)
+	{
+		if (outsize < 1)
+			return NULL;
+		*output++ = (char) wc;
+	}
+	else if (wc <= 0x7ff)
+	{
+		if (outsize < 2)
+			return NULL;
+		*output++ = 0xc0 | (wc >> 6);
+		*output++ = 0x80 | (wc & 0x3f);
+	}
+	else if (wc <= 0xffff)
+	{
+		if (outsize < 3)
+			return NULL;
+		*output++ = 0xe0 | (wc >> 12);
+		*output++ = 0x80 | ((wc >> 6) & 0x3f);
+		*output++ = 0x80 | (wc & 0x3f);
+	}
+	else if (wc <= 0x1fffff)
+	{
+		if (outsize < 4)
+			return NULL;
+		*output++ = 0xf0 | (wc >> 18);
+		*output++ = 0x80 | ((wc >> 12) & 0x3f);
+		*output++ = 0x80 | ((wc >> 6) & 0x3f);
+		*output++ = 0x80 | (wc & 0x3f);
+	}
+	else if (wc <= 0x3ffffff)
+	{
+		if (outsize < 5)
+			return NULL;
+		*output++ = 0xf8 | (wc >> 24);
+		*output++ = 0x80 | ((wc >> 18) & 0x3f);
+		*output++ = 0x80 | ((wc >> 12) & 0x3f);
+		*output++ = 0x80 | ((wc >> 6) & 0x3f);
+		*output++ = 0x80 | (wc & 0x3f);
+	}
+	else if (wc <= 0x7fffffff)
+	{
+		if (outsize < 6)
+			return NULL;
+		*output++ = 0xfc | (wc >> 30);
+		*output++ = 0x80 | ((wc >> 24) & 0x3f);
+		*output++ = 0x80 | ((wc >> 18) & 0x3f);
+		*output++ = 0x80 | ((wc >> 12) & 0x3f);
+		*output++ = 0x80 | ((wc >> 6) & 0x3f);
+		*output++ = 0x80 | (wc & 0x3f);
+	}
+	else
+		return NULL;
+
+	return output;
+}
+
+static const le16_t* utf16_to_wchar(const le16_t* input, wchar_t* wc,
+		size_t insize)
+{
+	if ((le16_to_cpu(input[0]) & 0xfc00) == 0xd800)
+	{
+		if (insize < 2 || (le16_to_cpu(input[1]) & 0xfc00) != 0xdc00)
+			return NULL;
+		*wc = ((wchar_t) (le16_to_cpu(input[0]) & 0x3ff) << 10);
+		*wc |= (le16_to_cpu(input[1]) & 0x3ff);
+		*wc += 0x10000;
+		return input + 2;
+	}
+	else
+	{
+		*wc = le16_to_cpu(*input);
+		return input + 1;
+	}
+}
+
+int utf16_to_utf8(char* output, const le16_t* input, size_t outsize,
+		size_t insize)
+{
+	const le16_t* inp = input;
+	char* outp = output;
+	wchar_t wc;
+
+	while (inp - input < insize && le16_to_cpu(*inp))
+	{
+		inp = utf16_to_wchar(inp, &wc, insize - (inp - input));
+		if (inp == NULL)
+		{
+			exfat_error("illegal UTF-16 sequence");
+			return -EILSEQ;
+		}
+		outp = wchar_to_utf8(outp, wc, outsize - (outp - output));
+		if (outp == NULL)
+		{
+			exfat_error("name is too long");
+			return -ENAMETOOLONG;
+		}
+	}
+	*outp = '\0';
+	return 0;
+}
+
+static const char* utf8_to_wchar(const char* input, wchar_t* wc,
+		size_t insize)
+{
+	if ((input[0] & 0x80) == 0 && insize >= 1)
+	{
+		*wc = (wchar_t) input[0];
+		return input + 1;
+	}
+	if ((input[0] & 0xe0) == 0xc0 && insize >= 2)
+	{
+		*wc = (((wchar_t) input[0] & 0x1f) << 6) |
+		       ((wchar_t) input[1] & 0x3f);
+		return input + 2;
+	}
+	if ((input[0] & 0xf0) == 0xe0 && insize >= 3)
+	{
+		*wc = (((wchar_t) input[0] & 0x0f) << 12) |
+		      (((wchar_t) input[1] & 0x3f) << 6) |
+		       ((wchar_t) input[2] & 0x3f);
+		return input + 3;
+	}
+	if ((input[0] & 0xf8) == 0xf0 && insize >= 4)
+	{
+		*wc = (((wchar_t) input[0] & 0x07) << 18) |
+		      (((wchar_t) input[1] & 0x3f) << 12) |
+		      (((wchar_t) input[2] & 0x3f) << 6) |
+		       ((wchar_t) input[3] & 0x3f);
+		return input + 4;
+	}
+	if ((input[0] & 0xfc) == 0xf8 && insize >= 5)
+	{
+		*wc = (((wchar_t) input[0] & 0x03) << 24) |
+		      (((wchar_t) input[1] & 0x3f) << 18) |
+		      (((wchar_t) input[2] & 0x3f) << 12) |
+		      (((wchar_t) input[3] & 0x3f) << 6) |
+		       ((wchar_t) input[4] & 0x3f);
+		return input + 5;
+	}
+	if ((input[0] & 0xfe) == 0xfc && insize >= 6)
+	{
+		*wc = (((wchar_t) input[0] & 0x01) << 30) |
+		      (((wchar_t) input[1] & 0x3f) << 24) |
+		      (((wchar_t) input[2] & 0x3f) << 18) |
+		      (((wchar_t) input[3] & 0x3f) << 12) |
+		      (((wchar_t) input[4] & 0x3f) << 6) |
+		       ((wchar_t) input[5] & 0x3f);
+		return input + 6;
+	}
+	return NULL;
+}
+
+static le16_t* wchar_to_utf16(le16_t* output, wchar_t wc, size_t outsize)
+{
+	if (wc <= 0xffff) /* if character is from BMP */
+	{
+		if (outsize == 0)
+			return NULL;
+		output[0] = cpu_to_le16(wc);
+		return output + 1;
+	}
+	if (outsize < 2)
+		return NULL;
+	wc -= 0x10000;
+	output[0] = cpu_to_le16(0xd800 | ((wc >> 10) & 0x3ff));
+	output[1] = cpu_to_le16(0xdc00 | (wc & 0x3ff));
+	return output + 2;
+}
+
+int utf8_to_utf16(le16_t* output, const char* input, size_t outsize,
+		size_t insize)
+{
+	const char* inp = input;
+	le16_t* outp = output;
+	wchar_t wc;
+
+	while (inp - input < insize && *inp)
+	{
+		inp = utf8_to_wchar(inp, &wc, insize - (inp - input));
+		if (inp == NULL)
+		{
+			exfat_error("illegal UTF-8 sequence");
+			return -EILSEQ;
+		}
+		outp = wchar_to_utf16(outp, wc, outsize - (outp - output));
+		if (outp == NULL)
+		{
+			exfat_error("name is too long");
+			return -ENAMETOOLONG;
+		}
+	}
+	*outp = cpu_to_le16(0);
+	return 0;
+}
+
+size_t utf16_length(const le16_t* str)
+{
+	size_t i = 0;
+
+	while (le16_to_cpu(str[i]))
+		i++;
+	return i;
+}
diff --git a/exfat/libexfat/utils.c b/exfat/libexfat/utils.c
new file mode 100644
index 0000000..ef87692
--- /dev/null
+++ b/exfat/libexfat/utils.c
@@ -0,0 +1,179 @@
+/*
+	utils.c (04.09.09)
+	exFAT file system implementation library.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "exfat.h"
+#include <string.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+void exfat_stat(const struct exfat* ef, const struct exfat_node* node,
+		struct stat* stbuf)
+{
+	memset(stbuf, 0, sizeof(struct stat));
+	if (node->flags & EXFAT_ATTRIB_DIR)
+		stbuf->st_mode = S_IFDIR | (0777 & ~ef->dmask);
+	else
+		stbuf->st_mode = S_IFREG | (0777 & ~ef->fmask);
+	stbuf->st_nlink = 1;
+	stbuf->st_uid = ef->uid;
+	stbuf->st_gid = ef->gid;
+	stbuf->st_size = node->size;
+	stbuf->st_blocks = DIV_ROUND_UP(node->size, CLUSTER_SIZE(*ef->sb)) *
+		CLUSTER_SIZE(*ef->sb) / 512;
+	stbuf->st_mtime = node->mtime;
+	stbuf->st_atime = node->atime;
+	/* set ctime to mtime to ensure we don't break programs that rely on ctime
+	   (e.g. rsync) */
+	stbuf->st_ctime = node->mtime;
+}
+
+void exfat_get_name(const struct exfat_node* node, char* buffer, size_t n)
+{
+	if (utf16_to_utf8(buffer, node->name, n, EXFAT_NAME_MAX) != 0)
+		exfat_bug("failed to convert name to UTF-8");
+}
+
+uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry)
+{
+	uint16_t sum = 0;
+	int i;
+
+	for (i = 0; i < sizeof(struct exfat_entry); i++)
+		if (i != 2 && i != 3) /* skip checksum field itself */
+			sum = ((sum << 15) | (sum >> 1)) + ((const uint8_t*) entry)[i];
+	return sum;
+}
+
+uint16_t exfat_add_checksum(const void* entry, uint16_t sum)
+{
+	int i;
+
+	for (i = 0; i < sizeof(struct exfat_entry); i++)
+		sum = ((sum << 15) | (sum >> 1)) + ((const uint8_t*) entry)[i];
+	return sum;
+}
+
+le16_t exfat_calc_checksum(const struct exfat_entry_meta1* meta1,
+		const struct exfat_entry_meta2* meta2, const le16_t* name)
+{
+	uint16_t checksum;
+	const int name_entries = DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX);
+	int i;
+
+	checksum = exfat_start_checksum(meta1);
+	checksum = exfat_add_checksum(meta2, checksum);
+	for (i = 0; i < name_entries; i++)
+	{
+		struct exfat_entry_name name_entry = {EXFAT_ENTRY_FILE_NAME, 0};
+		memcpy(name_entry.name, name + i * EXFAT_ENAME_MAX,
+				MIN(EXFAT_ENAME_MAX, EXFAT_NAME_MAX - i * EXFAT_ENAME_MAX) *
+				sizeof(le16_t));
+		checksum = exfat_add_checksum(&name_entry, checksum);
+	}
+	return cpu_to_le16(checksum);
+}
+
+uint32_t exfat_vbr_start_checksum(const void* sector, size_t size)
+{
+	size_t i;
+	uint32_t sum = 0;
+
+	for (i = 0; i < size; i++)
+		/* skip volume_state and allocated_percent fields */
+		if (i != 0x6a && i != 0x6b && i != 0x70)
+			sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
+	return sum;
+}
+
+uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum)
+{
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i];
+	return sum;
+}
+
+le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name)
+{
+	size_t i;
+	size_t length = utf16_length(name);
+	uint16_t hash = 0;
+
+	for (i = 0; i < length; i++)
+	{
+		uint16_t c = le16_to_cpu(name[i]);
+
+		/* convert to upper case */
+		if (c < ef->upcase_chars)
+			c = le16_to_cpu(ef->upcase[c]);
+
+		hash = ((hash << 15) | (hash >> 1)) + (c & 0xff);
+		hash = ((hash << 15) | (hash >> 1)) + (c >> 8);
+	}
+	return cpu_to_le16(hash);
+}
+
+void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb)
+{
+	size_t i;
+	/* 16 EB (minus 1 byte) is the largest size that can be represented by
+	   uint64_t */
+	const char* units[] = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB"};
+	uint64_t divisor = 1;
+	uint64_t temp = 0;
+
+	for (i = 0; ; i++, divisor *= 1024)
+	{
+		temp = (value + divisor / 2) / divisor;
+
+		if (temp == 0)
+			break;
+		if (temp / 1024 * 1024 == temp)
+			continue;
+		if (temp < 10240)
+			break;
+	}
+	hb->value = temp;
+	hb->unit = units[i];
+}
+
+void exfat_print_info(const struct exfat_super_block* sb,
+		uint32_t free_clusters)
+{
+	struct exfat_human_bytes hb;
+	off64_t total_space = le64_to_cpu(sb->sector_count) * SECTOR_SIZE(*sb);
+	off64_t avail_space = (off64_t) free_clusters * CLUSTER_SIZE(*sb);
+
+	printf("File system version           %hhu.%hhu\n",
+			sb->version.major, sb->version.minor);
+	exfat_humanize_bytes(SECTOR_SIZE(*sb), &hb);
+	printf("Sector size          %10"PRIu64" %s\n", hb.value, hb.unit);
+	exfat_humanize_bytes(CLUSTER_SIZE(*sb), &hb);
+	printf("Cluster size         %10"PRIu64" %s\n", hb.value, hb.unit);
+	exfat_humanize_bytes(total_space, &hb);
+	printf("Volume size          %10"PRIu64" %s\n", hb.value, hb.unit);
+	exfat_humanize_bytes(total_space - avail_space, &hb);
+	printf("Used space           %10"PRIu64" %s\n", hb.value, hb.unit);
+	exfat_humanize_bytes(avail_space, &hb);
+	printf("Available space      %10"PRIu64" %s\n", hb.value, hb.unit);
+}
diff --git a/exfat/libexfat/version.h b/exfat/libexfat/version.h
new file mode 100644
index 0000000..062fa04
--- /dev/null
+++ b/exfat/libexfat/version.h
@@ -0,0 +1,30 @@
+/*
+	version.h (12.06.10)
+	Version constants.
+
+	Free exFAT implementation.
+	Copyright (C) 2010-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef VERSION_H_INCLUDED
+#define VERSION_H_INCLUDED
+
+#define EXFAT_VERSION_MAJOR 1
+#define EXFAT_VERSION_MINOR 0
+#define EXFAT_VERSION_PATCH 0
+
+#endif /* ifndef VERSION_H_INCLUDED */
diff --git a/exfat/mkfs/Android.mk b/exfat/mkfs/Android.mk
new file mode 100644
index 0000000..a0c601f
--- /dev/null
+++ b/exfat/mkfs/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := mkexfatfs
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64
+LOCAL_SRC_FILES =  cbm.c fat.c main.c mkexfat.c rootdir.c uct.c uctc.c vbr.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+					$(commands_recovery_local_path)/exfat/libexfat \
+					$(commands_recovery_local_path)/fuse/include
+LOCAL_SHARED_LIBRARIES += libz libc libexfat libdl 
+LOCAL_STATIC_LIBRARIES += libfusetwrp
+
+include $(BUILD_EXECUTABLE)
diff --git a/exfat/mkfs/cbm.c b/exfat/mkfs/cbm.c
new file mode 100644
index 0000000..0110b40
--- /dev/null
+++ b/exfat/mkfs/cbm.c
@@ -0,0 +1,76 @@
+/*
+	cbm.c (09.11.10)
+	Clusters Bitmap creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <limits.h>
+#include "cbm.h"
+#include "fat.h"
+#include "uct.h"
+#include "rootdir.h"
+
+static off64_t cbm_alignment(void)
+{
+	return get_cluster_size();
+}
+
+static off64_t cbm_size(void)
+{
+	return DIV_ROUND_UP(
+			(get_volume_size() - get_position(&cbm)) / get_cluster_size(),
+			CHAR_BIT);
+}
+
+static int cbm_write(struct exfat_dev* dev)
+{
+	uint32_t allocated_clusters =
+			DIV_ROUND_UP(cbm.get_size(), get_cluster_size()) +
+			DIV_ROUND_UP(uct.get_size(), get_cluster_size()) +
+			DIV_ROUND_UP(rootdir.get_size(), get_cluster_size());
+	size_t bitmap_size = DIV_ROUND_UP(allocated_clusters, CHAR_BIT);
+	uint8_t* bitmap = malloc(bitmap_size);
+	size_t i;
+
+	if (bitmap == NULL)
+	{
+		exfat_error("failed to allocate bitmap of %zu bytes", bitmap_size);
+		return 1;
+	}
+
+	for (i = 0; i < bitmap_size * CHAR_BIT; i++)
+		if (i < allocated_clusters)
+			BMAP_SET(bitmap, i);
+		else
+			BMAP_CLR(bitmap, i);
+	if (exfat_write(dev, bitmap, bitmap_size) < 0)
+	{
+		exfat_error("failed to write bitmap of %zu bytes", bitmap_size);
+		return 1;
+	}
+	free(bitmap);
+	return 0;
+}
+
+const struct fs_object cbm =
+{
+	.get_alignment = cbm_alignment,
+	.get_size = cbm_size,
+	.write = cbm_write,
+};
diff --git a/exfat/mkfs/cbm.h b/exfat/mkfs/cbm.h
new file mode 100644
index 0000000..b9d2850
--- /dev/null
+++ b/exfat/mkfs/cbm.h
@@ -0,0 +1,30 @@
+/*
+	cbm.h (09.11.10)
+	Clusters Bitmap creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_CBM_H_INCLUDED
+#define MKFS_CBM_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object cbm;
+
+#endif /* ifndef MKFS_CBM_H_INCLUDED */
diff --git a/exfat/mkfs/fat.c b/exfat/mkfs/fat.c
new file mode 100644
index 0000000..9ed9022
--- /dev/null
+++ b/exfat/mkfs/fat.c
@@ -0,0 +1,88 @@
+/*
+	fat.c (09.11.10)
+	File Allocation Table creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <unistd.h>
+#include "fat.h"
+#include "cbm.h"
+#include "uct.h"
+#include "rootdir.h"
+
+static off64_t fat_alignment(void)
+{
+	return (off64_t) 128 * get_sector_size();
+}
+
+static off64_t fat_size(void)
+{
+	return get_volume_size() / get_cluster_size() * sizeof(cluster_t);
+}
+
+static cluster_t fat_write_entry(struct exfat_dev* dev, cluster_t cluster,
+		cluster_t value)
+{
+	le32_t fat_entry = cpu_to_le32(value);
+	if (exfat_write(dev, &fat_entry, sizeof(fat_entry)) < 0)
+	{
+		exfat_error("failed to write FAT entry 0x%x", value);
+		return 0;
+	}
+	return cluster + 1;
+}
+
+static cluster_t fat_write_entries(struct exfat_dev* dev, cluster_t cluster,
+		uint64_t length)
+{
+	cluster_t end = cluster + DIV_ROUND_UP(length, get_cluster_size());
+
+	while (cluster < end - 1)
+	{
+		cluster = fat_write_entry(dev, cluster, cluster + 1);
+		if (cluster == 0)
+			return 0;
+	}
+	return fat_write_entry(dev, cluster, EXFAT_CLUSTER_END);
+}
+
+static int fat_write(struct exfat_dev* dev)
+{
+	cluster_t c = 0;
+
+	if (!(c = fat_write_entry(dev, c, 0xfffffff8))) /* media type */
+		return 1;
+	if (!(c = fat_write_entry(dev, c, 0xffffffff))) /* some weird constant */
+		return 1;
+	if (!(c = fat_write_entries(dev, c, cbm.get_size())))
+		return 1;
+	if (!(c = fat_write_entries(dev, c, uct.get_size())))
+		return 1;
+	if (!(c = fat_write_entries(dev, c, rootdir.get_size())))
+		return 1;
+
+	return 0;
+}
+
+const struct fs_object fat =
+{
+	.get_alignment = fat_alignment,
+	.get_size = fat_size,
+	.write = fat_write,
+};
diff --git a/exfat/mkfs/fat.h b/exfat/mkfs/fat.h
new file mode 100644
index 0000000..1327f0a
--- /dev/null
+++ b/exfat/mkfs/fat.h
@@ -0,0 +1,30 @@
+/*
+	fat.h (09.11.10)
+	File Allocation Table creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_FAT_H_INCLUDED
+#define MKFS_FAT_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object fat;
+
+#endif /* ifndef MKFS_FAT_H_INCLUDED */
diff --git a/exfat/mkfs/main.c b/exfat/mkfs/main.c
new file mode 100644
index 0000000..3c3c382
--- /dev/null
+++ b/exfat/mkfs/main.c
@@ -0,0 +1,256 @@
+/*
+	main.c (15.08.10)
+	Creates exFAT file system.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <exfat.h>
+#include "mkexfat.h"
+#include "vbr.h"
+#include "fat.h"
+#include "cbm.h"
+#include "uct.h"
+#include "rootdir.h"
+
+const struct fs_object* objects[] =
+{
+	&vbr,
+	&vbr,
+	&fat,
+	/* clusters heap */
+	&cbm,
+	&uct,
+	&rootdir,
+	NULL,
+};
+
+static struct
+{
+	int sector_bits;
+	int spc_bits;
+	off64_t volume_size;
+	le16_t volume_label[EXFAT_ENAME_MAX + 1];
+	uint32_t volume_serial;
+	uint64_t first_sector;
+}
+param;
+
+int get_sector_bits(void)
+{
+	return param.sector_bits;
+}
+
+int get_spc_bits(void)
+{
+	return param.spc_bits;
+}
+
+off64_t get_volume_size(void)
+{
+	return param.volume_size;
+}
+
+const le16_t* get_volume_label(void)
+{
+	return param.volume_label;
+}
+
+uint32_t get_volume_serial(void)
+{
+	return param.volume_serial;
+}
+
+uint64_t get_first_sector(void)
+{
+	return param.first_sector;
+}
+
+int get_sector_size(void)
+{
+	return 1 << get_sector_bits();
+}
+
+int get_cluster_size(void)
+{
+	return get_sector_size() << get_spc_bits();
+}
+
+static int setup_spc_bits(int sector_bits, int user_defined, off64_t volume_size)
+{
+	int i;
+
+	if (user_defined != -1)
+	{
+		off64_t cluster_size = 1 << sector_bits << user_defined;
+		if (volume_size / cluster_size > EXFAT_LAST_DATA_CLUSTER)
+		{
+			struct exfat_human_bytes chb, vhb;
+
+			exfat_humanize_bytes(cluster_size, &chb);
+			exfat_humanize_bytes(volume_size, &vhb);
+			exfat_error("cluster size %"PRIu64" %s is too small for "
+					"%"PRIu64" %s volume, try -s %d",
+					chb.value, chb.unit,
+					vhb.value, vhb.unit,
+					1 << setup_spc_bits(sector_bits, -1, volume_size));
+			return -1;
+		}
+		return user_defined;
+	}
+
+	if (volume_size < 256ull * 1024 * 1024)
+		return MAX(0, 12 - sector_bits);	/* 4 KB */
+	if (volume_size < 32ull * 1024 * 1024 * 1024)
+		return MAX(0, 15 - sector_bits);	/* 32 KB */
+
+	for (i = 17; ; i++)						/* 128 KB or more */
+		if (DIV_ROUND_UP(volume_size, 1 << i) <= EXFAT_LAST_DATA_CLUSTER)
+			return MAX(0, i - sector_bits);
+}
+
+static int setup_volume_label(le16_t label[EXFAT_ENAME_MAX + 1], const char* s)
+{
+	memset(label, 0, (EXFAT_ENAME_MAX + 1) * sizeof(le16_t));
+	if (s == NULL)
+		return 0;
+	return utf8_to_utf16(label, s, EXFAT_ENAME_MAX, strlen(s));
+}
+
+static uint32_t setup_volume_serial(uint32_t user_defined)
+{
+	struct timeval now;
+
+	if (user_defined != 0)
+		return user_defined;
+
+	if (gettimeofday(&now, NULL) != 0)
+	{
+		exfat_error("failed to form volume id");
+		return 0;
+	}
+	return (now.tv_sec << 20) | now.tv_usec;
+}
+
+static int setup(struct exfat_dev* dev, int sector_bits, int spc_bits,
+		const char* volume_label, uint32_t volume_serial,
+		uint64_t first_sector)
+{
+	param.sector_bits = sector_bits;
+	param.first_sector = first_sector;
+	param.volume_size = exfat_get_size(dev);
+
+	param.spc_bits = setup_spc_bits(sector_bits, spc_bits, param.volume_size);
+	if (param.spc_bits == -1)
+		return 1;
+
+	if (setup_volume_label(param.volume_label, volume_label) != 0)
+		return 1;
+
+	param.volume_serial = setup_volume_serial(volume_serial);
+	if (param.volume_serial == 0)
+		return 1;
+
+	return mkfs(dev, param.volume_size);
+}
+
+static int logarithm2(int n)
+{
+	int i;
+
+	for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
+		if ((1 << i) == n)
+			return i;
+	return -1;
+}
+
+static void usage(const char* prog)
+{
+	fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
+			"[-p partition-first-sector] "
+			"[-s sectors-per-cluster] [-V] <device>\n", prog);
+	exit(1);
+}
+
+int main(int argc, char* argv[])
+{
+	const char* spec = NULL;
+	int opt;
+	int spc_bits = -1;
+	const char* volume_label = NULL;
+	uint32_t volume_serial = 0;
+	uint64_t first_sector = 0;
+	struct exfat_dev* dev;
+
+	printf("mkexfatfs %u.%u.%u\n",
+			EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
+
+	while ((opt = getopt(argc, argv, "i:n:p:s:V")) != -1)
+	{
+		switch (opt)
+		{
+		case 'i':
+			volume_serial = strtol(optarg, NULL, 16);
+			break;
+		case 'n':
+			volume_label = optarg;
+			break;
+		case 'p':
+			first_sector = strtoll(optarg, NULL, 10);
+			break;
+		case 's':
+			spc_bits = logarithm2(atoi(optarg));
+			if (spc_bits < 0)
+			{
+				exfat_error("invalid option value: `%s'", optarg);
+				return 1;
+			}
+			break;
+		case 'V':
+			puts("Copyright (C) 2011-2013  Andrew Nayenko");
+			return 0;
+		default:
+			usage(argv[0]);
+			break;
+		}
+	}
+	if (argc - optind != 1)
+		usage(argv[0]);
+	spec = argv[optind];
+
+	dev = exfat_open(spec, EXFAT_MODE_RW);
+	if (dev == NULL)
+		return 1;
+	if (setup(dev, 9, spc_bits, volume_label, volume_serial,
+				first_sector) != 0)
+	{
+		exfat_close(dev);
+		return 1;
+	}
+	if (exfat_close(dev) != 0)
+		return 1;
+	printf("File system created successfully.\n");
+	return 0;
+}
diff --git a/exfat/mkfs/mkexfat.c b/exfat/mkfs/mkexfat.c
new file mode 100644
index 0000000..c5c86d9
--- /dev/null
+++ b/exfat/mkfs/mkexfat.c
@@ -0,0 +1,162 @@
+/*
+	mkexfat.c (22.04.12)
+	FS creation engine.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include "mkexfat.h"
+
+static int check_size(off64_t volume_size)
+{
+	const struct fs_object** pp;
+	off64_t position = 0;
+
+	for (pp = objects; *pp; pp++)
+	{
+		position = ROUND_UP(position, (*pp)->get_alignment());
+		position += (*pp)->get_size();
+	}
+
+	if (position > volume_size)
+	{
+		struct exfat_human_bytes vhb;
+
+		exfat_humanize_bytes(volume_size, &vhb);
+		exfat_error("too small device (%"PRIu64" %s)", vhb.value, vhb.unit);
+		return 1;
+	}
+
+	return 0;
+
+}
+
+static int erase_object(struct exfat_dev* dev, const void* block,
+		size_t block_size, off64_t start, off64_t size)
+{
+	const off64_t block_count = DIV_ROUND_UP(size, block_size);
+	off64_t i;
+
+	if (exfat_seek(dev, start, SEEK_SET) == (off64_t) -1)
+	{
+		exfat_error("seek to 0x%"PRIx64" failed", start);
+		return 1;
+	}
+	for (i = 0; i < size; i += block_size)
+	{
+		if (exfat_write(dev, block, MIN(size - i, block_size)) < 0)
+		{
+			exfat_error("failed to erase block %"PRIu64"/%"PRIu64
+					" at 0x%"PRIx64, i + 1, block_count, start);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int erase(struct exfat_dev* dev)
+{
+	const struct fs_object** pp;
+	off64_t position = 0;
+	const size_t block_size = 1024 * 1024;
+	void* block = malloc(block_size);
+
+	if (block == NULL)
+	{
+		exfat_error("failed to allocate erase block of %zu bytes", block_size);
+		return 1;
+	}
+	memset(block, 0, block_size);
+
+	for (pp = objects; *pp; pp++)
+	{
+		position = ROUND_UP(position, (*pp)->get_alignment());
+		if (erase_object(dev, block, block_size, position,
+				(*pp)->get_size()) != 0)
+		{
+			free(block);
+			return 1;
+		}
+		position += (*pp)->get_size();
+	}
+
+	free(block);
+	return 0;
+}
+
+static int create(struct exfat_dev* dev)
+{
+	const struct fs_object** pp;
+	off64_t position = 0;
+
+	for (pp = objects; *pp; pp++)
+	{
+		position = ROUND_UP(position, (*pp)->get_alignment());
+		if (exfat_seek(dev, position, SEEK_SET) == (off64_t) -1)
+		{
+			exfat_error("seek to 0x%"PRIx64" failed", position);
+			return 1;
+		}
+		if ((*pp)->write(dev) != 0)
+			return 1;
+		position += (*pp)->get_size();
+	}
+	return 0;
+}
+
+int mkfs(struct exfat_dev* dev, off64_t volume_size)
+{
+	if (check_size(volume_size) != 0)
+		return 1;
+
+	fputs("Creating... ", stdout);
+	fflush(stdout);
+	if (erase(dev) != 0)
+		return 1;
+	if (create(dev) != 0)
+		return 1;
+	puts("done.");
+
+	fputs("Flushing... ", stdout);
+	fflush(stdout);
+	if (exfat_fsync(dev) != 0)
+		return 1;
+	puts("done.");
+
+	return 0;
+}
+
+off64_t get_position(const struct fs_object* object)
+{
+	const struct fs_object** pp;
+	off64_t position = 0;
+
+	for (pp = objects; *pp; pp++)
+	{
+		position = ROUND_UP(position, (*pp)->get_alignment());
+		if (*pp == object)
+			return position;
+		position += (*pp)->get_size();
+	}
+	exfat_bug("unknown object");
+}
diff --git a/exfat/mkfs/mkexfat.h b/exfat/mkfs/mkexfat.h
new file mode 100644
index 0000000..d0685af
--- /dev/null
+++ b/exfat/mkfs/mkexfat.h
@@ -0,0 +1,49 @@
+/*
+	mkexfat.h (09.11.10)
+	FS creation engine.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_MKEXFAT_H_INCLUDED
+#define MKFS_MKEXFAT_H_INCLUDED
+
+#include <exfat.h>
+
+struct fs_object
+{
+	off64_t (*get_alignment)(void);
+	off64_t (*get_size)(void);
+	int (*write)(struct exfat_dev* dev);
+};
+
+extern const struct fs_object* objects[];
+
+int get_sector_bits(void);
+int get_spc_bits(void);
+off64_t get_volume_size(void);
+const le16_t* get_volume_label(void);
+uint32_t get_volume_serial(void);
+uint64_t get_first_sector(void);
+int get_sector_size(void);
+int get_cluster_size(void);
+
+int mkfs(struct exfat_dev* dev, off64_t volume_size);
+off64_t get_position(const struct fs_object* object);
+
+#endif /* ifndef MKFS_MKEXFAT_H_INCLUDED */
diff --git a/exfat/mkfs/mkexfatfs.8 b/exfat/mkfs/mkexfatfs.8
new file mode 100644
index 0000000..5f6ba3d
--- /dev/null
+++ b/exfat/mkfs/mkexfatfs.8
@@ -0,0 +1,68 @@
+.\" Copyright (C) 2011  Andrew Nayenko
+.\"
+.TH MKEXFATFS 8 "January 2011"
+.SH NAME
+.B mkexfatfs
+\- create an exFAT file system
+.SH SYNOPSIS
+.B mkexfatfs
+[
+.B \-i
+.I volume-id
+]
+[
+.B \-n
+.I volume-name
+]
+[
+.B \-p
+.I partition-first-sector
+]
+[
+.B \-s
+.I sectors-per-cluster
+]
+[
+.B \-V
+]
+.I device
+
+.SH DESCRIPTION
+.B mkexfatfs
+creates an exFAT file system on a block device.
+.I device
+is a special file corresponding to the device.
+
+.SH OPTIONS
+Command line options available:
+.TP
+.BI \-i " volume-id"
+A 32-bit hexadecimal number. By default a value based on current time is set.
+.TP
+.BI \-n " volume-name"
+Volume name (label), up to 15 characters. By default no label is set.
+.TP
+.BI \-p " partition-first-sector"
+First sector of the partition starting from the beginning of the whole disk.
+exFAT super block has a field for this value but in fact it's optional and
+does not affect anything. Default is 0.
+.TP
+.BI \-s " sectors-per-cluster"
+Number of physical sectors per cluster (cluster is an allocation unit in
+exFAT). Must be a power of 2, i.e. 1, 2, 4, 8, etc. Cluster size can not
+exceed 32 MB. Default cluster sizes are:
+4 KB if volume size is less than 256 MB,
+32 KB if volume size is from 256 MB to 32 GB,
+128 KB if volume size is 32 GB or larger.
+.TP
+.BI \-V
+Print version and copyright.
+
+.SH EXIT CODES
+Zero is returned on successful creation. Any other code means an error.
+
+.SH AUTHOR
+Andrew Nayenko
+
+.SH SEE ALSO
+.BR mkfs (8)
diff --git a/exfat/mkfs/rootdir.c b/exfat/mkfs/rootdir.c
new file mode 100644
index 0000000..49d3643
--- /dev/null
+++ b/exfat/mkfs/rootdir.c
@@ -0,0 +1,102 @@
+/*
+	rootdir.c (09.11.10)
+	Root directory creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <string.h>
+#include "rootdir.h"
+#include "uct.h"
+#include "cbm.h"
+#include "uctc.h"
+
+static off64_t rootdir_alignment(void)
+{
+	return get_cluster_size();
+}
+
+static off64_t rootdir_size(void)
+{
+	return get_cluster_size();
+}
+
+static void init_label_entry(struct exfat_entry_label* label_entry)
+{
+	memset(label_entry, 0, sizeof(struct exfat_entry_label));
+	label_entry->type = EXFAT_ENTRY_LABEL ^ EXFAT_ENTRY_VALID;
+
+	if (utf16_length(get_volume_label()) == 0)
+		return;
+
+	memcpy(label_entry->name, get_volume_label(),
+			EXFAT_ENAME_MAX * sizeof(le16_t));
+	label_entry->length = utf16_length(get_volume_label());
+	label_entry->type |= EXFAT_ENTRY_VALID;
+}
+
+static void init_bitmap_entry(struct exfat_entry_bitmap* bitmap_entry)
+{
+	memset(bitmap_entry, 0, sizeof(struct exfat_entry_bitmap));
+	bitmap_entry->type = EXFAT_ENTRY_BITMAP;
+	bitmap_entry->start_cluster = cpu_to_le32(EXFAT_FIRST_DATA_CLUSTER);
+	bitmap_entry->size = cpu_to_le64(cbm.get_size());
+}
+
+static void init_upcase_entry(struct exfat_entry_upcase* upcase_entry)
+{
+	size_t i;
+	uint32_t sum = 0;
+
+	for (i = 0; i < sizeof(upcase_table); i++)
+		sum = ((sum << 31) | (sum >> 1)) + upcase_table[i];
+
+	memset(upcase_entry, 0, sizeof(struct exfat_entry_upcase));
+	upcase_entry->type = EXFAT_ENTRY_UPCASE;
+	upcase_entry->checksum = cpu_to_le32(sum);
+	upcase_entry->start_cluster = cpu_to_le32(
+			(get_position(&uct) - get_position(&cbm)) / get_cluster_size() +
+			EXFAT_FIRST_DATA_CLUSTER);
+	upcase_entry->size = cpu_to_le64(sizeof(upcase_table));
+}
+
+static int rootdir_write(struct exfat_dev* dev)
+{
+	struct exfat_entry_label label_entry;
+	struct exfat_entry_bitmap bitmap_entry;
+	struct exfat_entry_upcase upcase_entry;
+
+	init_label_entry(&label_entry);
+	init_bitmap_entry(&bitmap_entry);
+	init_upcase_entry(&upcase_entry);
+
+	if (exfat_write(dev, &label_entry, sizeof(struct exfat_entry)) < 0)
+		return 1;
+	if (exfat_write(dev, &bitmap_entry, sizeof(struct exfat_entry)) < 0)
+		return 1;
+	if (exfat_write(dev, &upcase_entry, sizeof(struct exfat_entry)) < 0)
+		return 1;
+	return 0;
+}
+
+const struct fs_object rootdir =
+{
+	.get_alignment = rootdir_alignment,
+	.get_size = rootdir_size,
+	.write = rootdir_write,
+};
diff --git a/exfat/mkfs/rootdir.h b/exfat/mkfs/rootdir.h
new file mode 100644
index 0000000..2d1d345
--- /dev/null
+++ b/exfat/mkfs/rootdir.h
@@ -0,0 +1,30 @@
+/*
+	rootdir.h (09.11.10)
+	Root directory creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_ROOTDIR_H_INCLUDED
+#define MKFS_ROOTDIR_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object rootdir;
+
+#endif /* ifndef MKFS_ROOTDIR_H_INCLUDED */
diff --git a/exfat/mkfs/uct.c b/exfat/mkfs/uct.c
new file mode 100644
index 0000000..5f17323
--- /dev/null
+++ b/exfat/mkfs/uct.c
@@ -0,0 +1,52 @@
+/*
+	uct.c (09.11.10)
+	Upper Case Table creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "uct.h"
+#include "uctc.h"
+
+static off64_t uct_alignment(void)
+{
+	return get_cluster_size();
+}
+
+static off64_t uct_size(void)
+{
+	return sizeof(upcase_table);
+}
+
+static int uct_write(struct exfat_dev* dev)
+{
+	if (exfat_write(dev, upcase_table, sizeof(upcase_table)) < 0)
+	{
+		exfat_error("failed to write upcase table of %zu bytes",
+				sizeof(upcase_table));
+		return 1;
+	}
+	return 0;
+}
+
+const struct fs_object uct =
+{
+	.get_alignment = uct_alignment,
+	.get_size = uct_size,
+	.write = uct_write,
+};
diff --git a/exfat/mkfs/uct.h b/exfat/mkfs/uct.h
new file mode 100644
index 0000000..ef33392
--- /dev/null
+++ b/exfat/mkfs/uct.h
@@ -0,0 +1,30 @@
+/*
+	uct.h (09.11.10)
+	Upper Case Table creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_UCT_H_INCLUDED
+#define MKFS_UCT_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object uct;
+
+#endif /* ifndef MKFS_UCT_H_INCLUDED */
diff --git a/exfat/mkfs/uctc.c b/exfat/mkfs/uctc.c
new file mode 100644
index 0000000..1ee8efe
--- /dev/null
+++ b/exfat/mkfs/uctc.c
@@ -0,0 +1,757 @@
+/*
+	uctc.c (30.04.12)
+	Upper Case Table contents.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "uctc.h"
+
+uint8_t upcase_table[5836] =
+{
+	0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+	0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00,
+	0x08, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00,
+	0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00,
+	0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00,
+	0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
+	0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00,
+	0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1f, 0x00,
+	0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00,
+	0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00,
+	0x28, 0x00, 0x29, 0x00, 0x2a, 0x00, 0x2b, 0x00,
+	0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, 0x2f, 0x00,
+	0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00,
+	0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00,
+	0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00,
+	0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00,
+	0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
+	0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
+	0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
+	0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00,
+	0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+	0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
+	0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00,
+	0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5f, 0x00,
+	0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
+	0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
+	0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
+	0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00,
+	0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+	0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00,
+	0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x7b, 0x00,
+	0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7f, 0x00,
+	0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00,
+	0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00,
+	0x88, 0x00, 0x89, 0x00, 0x8a, 0x00, 0x8b, 0x00,
+	0x8c, 0x00, 0x8d, 0x00, 0x8e, 0x00, 0x8f, 0x00,
+	0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00,
+	0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00,
+	0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00,
+	0x9c, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00,
+	0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00,
+	0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00,
+	0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00,
+	0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00,
+	0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0x00,
+	0xb4, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, 0x00,
+	0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00,
+	0xbc, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbf, 0x00,
+	0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00,
+	0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00,
+	0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00,
+	0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00,
+	0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00,
+	0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00,
+	0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00,
+	0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xdf, 0x00,
+	0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00,
+	0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00,
+	0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00,
+	0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00,
+	0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00,
+	0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xf7, 0x00,
+	0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00,
+	0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0x78, 0x01,
+	0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01,
+	0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01,
+	0x08, 0x01, 0x08, 0x01, 0x0a, 0x01, 0x0a, 0x01,
+	0x0c, 0x01, 0x0c, 0x01, 0x0e, 0x01, 0x0e, 0x01,
+	0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01,
+	0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01,
+	0x18, 0x01, 0x18, 0x01, 0x1a, 0x01, 0x1a, 0x01,
+	0x1c, 0x01, 0x1c, 0x01, 0x1e, 0x01, 0x1e, 0x01,
+	0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01,
+	0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01,
+	0x28, 0x01, 0x28, 0x01, 0x2a, 0x01, 0x2a, 0x01,
+	0x2c, 0x01, 0x2c, 0x01, 0x2e, 0x01, 0x2e, 0x01,
+	0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01,
+	0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01,
+	0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3b, 0x01,
+	0x3b, 0x01, 0x3d, 0x01, 0x3d, 0x01, 0x3f, 0x01,
+	0x3f, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01,
+	0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01,
+	0x47, 0x01, 0x49, 0x01, 0x4a, 0x01, 0x4a, 0x01,
+	0x4c, 0x01, 0x4c, 0x01, 0x4e, 0x01, 0x4e, 0x01,
+	0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01,
+	0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01,
+	0x58, 0x01, 0x58, 0x01, 0x5a, 0x01, 0x5a, 0x01,
+	0x5c, 0x01, 0x5c, 0x01, 0x5e, 0x01, 0x5e, 0x01,
+	0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01,
+	0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01,
+	0x68, 0x01, 0x68, 0x01, 0x6a, 0x01, 0x6a, 0x01,
+	0x6c, 0x01, 0x6c, 0x01, 0x6e, 0x01, 0x6e, 0x01,
+	0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01,
+	0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01,
+	0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7b, 0x01,
+	0x7b, 0x01, 0x7d, 0x01, 0x7d, 0x01, 0x7f, 0x01,
+	0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01,
+	0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01,
+	0x87, 0x01, 0x89, 0x01, 0x8a, 0x01, 0x8b, 0x01,
+	0x8b, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01,
+	0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01,
+	0x94, 0x01, 0xf6, 0x01, 0x96, 0x01, 0x97, 0x01,
+	0x98, 0x01, 0x98, 0x01, 0x3d, 0x02, 0x9b, 0x01,
+	0x9c, 0x01, 0x9d, 0x01, 0x20, 0x02, 0x9f, 0x01,
+	0xa0, 0x01, 0xa0, 0x01, 0xa2, 0x01, 0xa2, 0x01,
+	0xa4, 0x01, 0xa4, 0x01, 0xa6, 0x01, 0xa7, 0x01,
+	0xa7, 0x01, 0xa9, 0x01, 0xaa, 0x01, 0xab, 0x01,
+	0xac, 0x01, 0xac, 0x01, 0xae, 0x01, 0xaf, 0x01,
+	0xaf, 0x01, 0xb1, 0x01, 0xb2, 0x01, 0xb3, 0x01,
+	0xb3, 0x01, 0xb5, 0x01, 0xb5, 0x01, 0xb7, 0x01,
+	0xb8, 0x01, 0xb8, 0x01, 0xba, 0x01, 0xbb, 0x01,
+	0xbc, 0x01, 0xbc, 0x01, 0xbe, 0x01, 0xf7, 0x01,
+	0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01, 0xc3, 0x01,
+	0xc4, 0x01, 0xc5, 0x01, 0xc4, 0x01, 0xc7, 0x01,
+	0xc8, 0x01, 0xc7, 0x01, 0xca, 0x01, 0xcb, 0x01,
+	0xca, 0x01, 0xcd, 0x01, 0xcd, 0x01, 0xcf, 0x01,
+	0xcf, 0x01, 0xd1, 0x01, 0xd1, 0x01, 0xd3, 0x01,
+	0xd3, 0x01, 0xd5, 0x01, 0xd5, 0x01, 0xd7, 0x01,
+	0xd7, 0x01, 0xd9, 0x01, 0xd9, 0x01, 0xdb, 0x01,
+	0xdb, 0x01, 0x8e, 0x01, 0xde, 0x01, 0xde, 0x01,
+	0xe0, 0x01, 0xe0, 0x01, 0xe2, 0x01, 0xe2, 0x01,
+	0xe4, 0x01, 0xe4, 0x01, 0xe6, 0x01, 0xe6, 0x01,
+	0xe8, 0x01, 0xe8, 0x01, 0xea, 0x01, 0xea, 0x01,
+	0xec, 0x01, 0xec, 0x01, 0xee, 0x01, 0xee, 0x01,
+	0xf0, 0x01, 0xf1, 0x01, 0xf2, 0x01, 0xf1, 0x01,
+	0xf4, 0x01, 0xf4, 0x01, 0xf6, 0x01, 0xf7, 0x01,
+	0xf8, 0x01, 0xf8, 0x01, 0xfa, 0x01, 0xfa, 0x01,
+	0xfc, 0x01, 0xfc, 0x01, 0xfe, 0x01, 0xfe, 0x01,
+	0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
+	0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02,
+	0x08, 0x02, 0x08, 0x02, 0x0a, 0x02, 0x0a, 0x02,
+	0x0c, 0x02, 0x0c, 0x02, 0x0e, 0x02, 0x0e, 0x02,
+	0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02,
+	0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02,
+	0x18, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1a, 0x02,
+	0x1c, 0x02, 0x1c, 0x02, 0x1e, 0x02, 0x1e, 0x02,
+	0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02,
+	0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02,
+	0x28, 0x02, 0x28, 0x02, 0x2a, 0x02, 0x2a, 0x02,
+	0x2c, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x2e, 0x02,
+	0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02,
+	0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02,
+	0x38, 0x02, 0x39, 0x02, 0x65, 0x2c, 0x3b, 0x02,
+	0x3b, 0x02, 0x3d, 0x02, 0x66, 0x2c, 0x3f, 0x02,
+	0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02,
+	0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02,
+	0x48, 0x02, 0x48, 0x02, 0x4a, 0x02, 0x4a, 0x02,
+	0x4c, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x4e, 0x02,
+	0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01,
+	0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8a, 0x01,
+	0x58, 0x02, 0x8f, 0x01, 0x5a, 0x02, 0x90, 0x01,
+	0x5c, 0x02, 0x5d, 0x02, 0x5e, 0x02, 0x5f, 0x02,
+	0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01,
+	0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02,
+	0x97, 0x01, 0x96, 0x01, 0x6a, 0x02, 0x62, 0x2c,
+	0x6c, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x9c, 0x01,
+	0x70, 0x02, 0x71, 0x02, 0x9d, 0x01, 0x73, 0x02,
+	0x74, 0x02, 0x9f, 0x01, 0x76, 0x02, 0x77, 0x02,
+	0x78, 0x02, 0x79, 0x02, 0x7a, 0x02, 0x7b, 0x02,
+	0x7c, 0x02, 0x64, 0x2c, 0x7e, 0x02, 0x7f, 0x02,
+	0xa6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xa9, 0x01,
+	0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02,
+	0xae, 0x01, 0x44, 0x02, 0xb1, 0x01, 0xb2, 0x01,
+	0x45, 0x02, 0x8d, 0x02, 0x8e, 0x02, 0x8f, 0x02,
+	0x90, 0x02, 0x91, 0x02, 0xb7, 0x01, 0x93, 0x02,
+	0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02,
+	0x98, 0x02, 0x99, 0x02, 0x9a, 0x02, 0x9b, 0x02,
+	0x9c, 0x02, 0x9d, 0x02, 0x9e, 0x02, 0x9f, 0x02,
+	0xa0, 0x02, 0xa1, 0x02, 0xa2, 0x02, 0xa3, 0x02,
+	0xa4, 0x02, 0xa5, 0x02, 0xa6, 0x02, 0xa7, 0x02,
+	0xa8, 0x02, 0xa9, 0x02, 0xaa, 0x02, 0xab, 0x02,
+	0xac, 0x02, 0xad, 0x02, 0xae, 0x02, 0xaf, 0x02,
+	0xb0, 0x02, 0xb1, 0x02, 0xb2, 0x02, 0xb3, 0x02,
+	0xb4, 0x02, 0xb5, 0x02, 0xb6, 0x02, 0xb7, 0x02,
+	0xb8, 0x02, 0xb9, 0x02, 0xba, 0x02, 0xbb, 0x02,
+	0xbc, 0x02, 0xbd, 0x02, 0xbe, 0x02, 0xbf, 0x02,
+	0xc0, 0x02, 0xc1, 0x02, 0xc2, 0x02, 0xc3, 0x02,
+	0xc4, 0x02, 0xc5, 0x02, 0xc6, 0x02, 0xc7, 0x02,
+	0xc8, 0x02, 0xc9, 0x02, 0xca, 0x02, 0xcb, 0x02,
+	0xcc, 0x02, 0xcd, 0x02, 0xce, 0x02, 0xcf, 0x02,
+	0xd0, 0x02, 0xd1, 0x02, 0xd2, 0x02, 0xd3, 0x02,
+	0xd4, 0x02, 0xd5, 0x02, 0xd6, 0x02, 0xd7, 0x02,
+	0xd8, 0x02, 0xd9, 0x02, 0xda, 0x02, 0xdb, 0x02,
+	0xdc, 0x02, 0xdd, 0x02, 0xde, 0x02, 0xdf, 0x02,
+	0xe0, 0x02, 0xe1, 0x02, 0xe2, 0x02, 0xe3, 0x02,
+	0xe4, 0x02, 0xe5, 0x02, 0xe6, 0x02, 0xe7, 0x02,
+	0xe8, 0x02, 0xe9, 0x02, 0xea, 0x02, 0xeb, 0x02,
+	0xec, 0x02, 0xed, 0x02, 0xee, 0x02, 0xef, 0x02,
+	0xf0, 0x02, 0xf1, 0x02, 0xf2, 0x02, 0xf3, 0x02,
+	0xf4, 0x02, 0xf5, 0x02, 0xf6, 0x02, 0xf7, 0x02,
+	0xf8, 0x02, 0xf9, 0x02, 0xfa, 0x02, 0xfb, 0x02,
+	0xfc, 0x02, 0xfd, 0x02, 0xfe, 0x02, 0xff, 0x02,
+	0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03,
+	0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03,
+	0x08, 0x03, 0x09, 0x03, 0x0a, 0x03, 0x0b, 0x03,
+	0x0c, 0x03, 0x0d, 0x03, 0x0e, 0x03, 0x0f, 0x03,
+	0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03,
+	0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03,
+	0x18, 0x03, 0x19, 0x03, 0x1a, 0x03, 0x1b, 0x03,
+	0x1c, 0x03, 0x1d, 0x03, 0x1e, 0x03, 0x1f, 0x03,
+	0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03,
+	0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03,
+	0x28, 0x03, 0x29, 0x03, 0x2a, 0x03, 0x2b, 0x03,
+	0x2c, 0x03, 0x2d, 0x03, 0x2e, 0x03, 0x2f, 0x03,
+	0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03,
+	0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03,
+	0x38, 0x03, 0x39, 0x03, 0x3a, 0x03, 0x3b, 0x03,
+	0x3c, 0x03, 0x3d, 0x03, 0x3e, 0x03, 0x3f, 0x03,
+	0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03,
+	0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03,
+	0x48, 0x03, 0x49, 0x03, 0x4a, 0x03, 0x4b, 0x03,
+	0x4c, 0x03, 0x4d, 0x03, 0x4e, 0x03, 0x4f, 0x03,
+	0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03,
+	0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03,
+	0x58, 0x03, 0x59, 0x03, 0x5a, 0x03, 0x5b, 0x03,
+	0x5c, 0x03, 0x5d, 0x03, 0x5e, 0x03, 0x5f, 0x03,
+	0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03,
+	0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03,
+	0x68, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x03,
+	0x6c, 0x03, 0x6d, 0x03, 0x6e, 0x03, 0x6f, 0x03,
+	0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03,
+	0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03,
+	0x78, 0x03, 0x79, 0x03, 0x7a, 0x03, 0xfd, 0x03,
+	0xfe, 0x03, 0xff, 0x03, 0x7e, 0x03, 0x7f, 0x03,
+	0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03,
+	0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03,
+	0x88, 0x03, 0x89, 0x03, 0x8a, 0x03, 0x8b, 0x03,
+	0x8c, 0x03, 0x8d, 0x03, 0x8e, 0x03, 0x8f, 0x03,
+	0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
+	0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
+	0x98, 0x03, 0x99, 0x03, 0x9a, 0x03, 0x9b, 0x03,
+	0x9c, 0x03, 0x9d, 0x03, 0x9e, 0x03, 0x9f, 0x03,
+	0xa0, 0x03, 0xa1, 0x03, 0xa2, 0x03, 0xa3, 0x03,
+	0xa4, 0x03, 0xa5, 0x03, 0xa6, 0x03, 0xa7, 0x03,
+	0xa8, 0x03, 0xa9, 0x03, 0xaa, 0x03, 0xab, 0x03,
+	0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8a, 0x03,
+	0xb0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03,
+	0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03,
+	0x98, 0x03, 0x99, 0x03, 0x9a, 0x03, 0x9b, 0x03,
+	0x9c, 0x03, 0x9d, 0x03, 0x9e, 0x03, 0x9f, 0x03,
+	0xa0, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa3, 0x03,
+	0xa4, 0x03, 0xa5, 0x03, 0xa6, 0x03, 0xa7, 0x03,
+	0xa8, 0x03, 0xa9, 0x03, 0xaa, 0x03, 0xab, 0x03,
+	0x8c, 0x03, 0x8e, 0x03, 0x8f, 0x03, 0xcf, 0x03,
+	0xd0, 0x03, 0xd1, 0x03, 0xd2, 0x03, 0xd3, 0x03,
+	0xd4, 0x03, 0xd5, 0x03, 0xd6, 0x03, 0xd7, 0x03,
+	0xd8, 0x03, 0xd8, 0x03, 0xda, 0x03, 0xda, 0x03,
+	0xdc, 0x03, 0xdc, 0x03, 0xde, 0x03, 0xde, 0x03,
+	0xe0, 0x03, 0xe0, 0x03, 0xe2, 0x03, 0xe2, 0x03,
+	0xe4, 0x03, 0xe4, 0x03, 0xe6, 0x03, 0xe6, 0x03,
+	0xe8, 0x03, 0xe8, 0x03, 0xea, 0x03, 0xea, 0x03,
+	0xec, 0x03, 0xec, 0x03, 0xee, 0x03, 0xee, 0x03,
+	0xf0, 0x03, 0xf1, 0x03, 0xf9, 0x03, 0xf3, 0x03,
+	0xf4, 0x03, 0xf5, 0x03, 0xf6, 0x03, 0xf7, 0x03,
+	0xf7, 0x03, 0xf9, 0x03, 0xfa, 0x03, 0xfa, 0x03,
+	0xfc, 0x03, 0xfd, 0x03, 0xfe, 0x03, 0xff, 0x03,
+	0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
+	0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
+	0x08, 0x04, 0x09, 0x04, 0x0a, 0x04, 0x0b, 0x04,
+	0x0c, 0x04, 0x0d, 0x04, 0x0e, 0x04, 0x0f, 0x04,
+	0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
+	0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
+	0x18, 0x04, 0x19, 0x04, 0x1a, 0x04, 0x1b, 0x04,
+	0x1c, 0x04, 0x1d, 0x04, 0x1e, 0x04, 0x1f, 0x04,
+	0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
+	0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
+	0x28, 0x04, 0x29, 0x04, 0x2a, 0x04, 0x2b, 0x04,
+	0x2c, 0x04, 0x2d, 0x04, 0x2e, 0x04, 0x2f, 0x04,
+	0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04,
+	0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04,
+	0x18, 0x04, 0x19, 0x04, 0x1a, 0x04, 0x1b, 0x04,
+	0x1c, 0x04, 0x1d, 0x04, 0x1e, 0x04, 0x1f, 0x04,
+	0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04,
+	0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04,
+	0x28, 0x04, 0x29, 0x04, 0x2a, 0x04, 0x2b, 0x04,
+	0x2c, 0x04, 0x2d, 0x04, 0x2e, 0x04, 0x2f, 0x04,
+	0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04,
+	0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04,
+	0x08, 0x04, 0x09, 0x04, 0x0a, 0x04, 0x0b, 0x04,
+	0x0c, 0x04, 0x0d, 0x04, 0x0e, 0x04, 0x0f, 0x04,
+	0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04,
+	0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04,
+	0x68, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6a, 0x04,
+	0x6c, 0x04, 0x6c, 0x04, 0x6e, 0x04, 0x6e, 0x04,
+	0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04,
+	0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04,
+	0x78, 0x04, 0x78, 0x04, 0x7a, 0x04, 0x7a, 0x04,
+	0x7c, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x7e, 0x04,
+	0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04,
+	0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04,
+	0x88, 0x04, 0x89, 0x04, 0x8a, 0x04, 0x8a, 0x04,
+	0x8c, 0x04, 0x8c, 0x04, 0x8e, 0x04, 0x8e, 0x04,
+	0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04,
+	0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04,
+	0x98, 0x04, 0x98, 0x04, 0x9a, 0x04, 0x9a, 0x04,
+	0x9c, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0x9e, 0x04,
+	0xa0, 0x04, 0xa0, 0x04, 0xa2, 0x04, 0xa2, 0x04,
+	0xa4, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa6, 0x04,
+	0xa8, 0x04, 0xa8, 0x04, 0xaa, 0x04, 0xaa, 0x04,
+	0xac, 0x04, 0xac, 0x04, 0xae, 0x04, 0xae, 0x04,
+	0xb0, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb2, 0x04,
+	0xb4, 0x04, 0xb4, 0x04, 0xb6, 0x04, 0xb6, 0x04,
+	0xb8, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xba, 0x04,
+	0xbc, 0x04, 0xbc, 0x04, 0xbe, 0x04, 0xbe, 0x04,
+	0xc0, 0x04, 0xc1, 0x04, 0xc1, 0x04, 0xc3, 0x04,
+	0xc3, 0x04, 0xc5, 0x04, 0xc5, 0x04, 0xc7, 0x04,
+	0xc7, 0x04, 0xc9, 0x04, 0xc9, 0x04, 0xcb, 0x04,
+	0xcb, 0x04, 0xcd, 0x04, 0xcd, 0x04, 0xc0, 0x04,
+	0xd0, 0x04, 0xd0, 0x04, 0xd2, 0x04, 0xd2, 0x04,
+	0xd4, 0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd6, 0x04,
+	0xd8, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xda, 0x04,
+	0xdc, 0x04, 0xdc, 0x04, 0xde, 0x04, 0xde, 0x04,
+	0xe0, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe2, 0x04,
+	0xe4, 0x04, 0xe4, 0x04, 0xe6, 0x04, 0xe6, 0x04,
+	0xe8, 0x04, 0xe8, 0x04, 0xea, 0x04, 0xea, 0x04,
+	0xec, 0x04, 0xec, 0x04, 0xee, 0x04, 0xee, 0x04,
+	0xf0, 0x04, 0xf0, 0x04, 0xf2, 0x04, 0xf2, 0x04,
+	0xf4, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf6, 0x04,
+	0xf8, 0x04, 0xf8, 0x04, 0xfa, 0x04, 0xfa, 0x04,
+	0xfc, 0x04, 0xfc, 0x04, 0xfe, 0x04, 0xfe, 0x04,
+	0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05,
+	0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05,
+	0x08, 0x05, 0x08, 0x05, 0x0a, 0x05, 0x0a, 0x05,
+	0x0c, 0x05, 0x0c, 0x05, 0x0e, 0x05, 0x0e, 0x05,
+	0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05,
+	0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05,
+	0x18, 0x05, 0x19, 0x05, 0x1a, 0x05, 0x1b, 0x05,
+	0x1c, 0x05, 0x1d, 0x05, 0x1e, 0x05, 0x1f, 0x05,
+	0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05,
+	0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05,
+	0x28, 0x05, 0x29, 0x05, 0x2a, 0x05, 0x2b, 0x05,
+	0x2c, 0x05, 0x2d, 0x05, 0x2e, 0x05, 0x2f, 0x05,
+	0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
+	0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
+	0x38, 0x05, 0x39, 0x05, 0x3a, 0x05, 0x3b, 0x05,
+	0x3c, 0x05, 0x3d, 0x05, 0x3e, 0x05, 0x3f, 0x05,
+	0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
+	0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
+	0x48, 0x05, 0x49, 0x05, 0x4a, 0x05, 0x4b, 0x05,
+	0x4c, 0x05, 0x4d, 0x05, 0x4e, 0x05, 0x4f, 0x05,
+	0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
+	0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05,
+	0x58, 0x05, 0x59, 0x05, 0x5a, 0x05, 0x5b, 0x05,
+	0x5c, 0x05, 0x5d, 0x05, 0x5e, 0x05, 0x5f, 0x05,
+	0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05,
+	0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05,
+	0x38, 0x05, 0x39, 0x05, 0x3a, 0x05, 0x3b, 0x05,
+	0x3c, 0x05, 0x3d, 0x05, 0x3e, 0x05, 0x3f, 0x05,
+	0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05,
+	0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05,
+	0x48, 0x05, 0x49, 0x05, 0x4a, 0x05, 0x4b, 0x05,
+	0x4c, 0x05, 0x4d, 0x05, 0x4e, 0x05, 0x4f, 0x05,
+	0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05,
+	0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xff, 0xff,
+	0xf6, 0x17, 0x63, 0x2c, 0x7e, 0x1d, 0x7f, 0x1d,
+	0x80, 0x1d, 0x81, 0x1d, 0x82, 0x1d, 0x83, 0x1d,
+	0x84, 0x1d, 0x85, 0x1d, 0x86, 0x1d, 0x87, 0x1d,
+	0x88, 0x1d, 0x89, 0x1d, 0x8a, 0x1d, 0x8b, 0x1d,
+	0x8c, 0x1d, 0x8d, 0x1d, 0x8e, 0x1d, 0x8f, 0x1d,
+	0x90, 0x1d, 0x91, 0x1d, 0x92, 0x1d, 0x93, 0x1d,
+	0x94, 0x1d, 0x95, 0x1d, 0x96, 0x1d, 0x97, 0x1d,
+	0x98, 0x1d, 0x99, 0x1d, 0x9a, 0x1d, 0x9b, 0x1d,
+	0x9c, 0x1d, 0x9d, 0x1d, 0x9e, 0x1d, 0x9f, 0x1d,
+	0xa0, 0x1d, 0xa1, 0x1d, 0xa2, 0x1d, 0xa3, 0x1d,
+	0xa4, 0x1d, 0xa5, 0x1d, 0xa6, 0x1d, 0xa7, 0x1d,
+	0xa8, 0x1d, 0xa9, 0x1d, 0xaa, 0x1d, 0xab, 0x1d,
+	0xac, 0x1d, 0xad, 0x1d, 0xae, 0x1d, 0xaf, 0x1d,
+	0xb0, 0x1d, 0xb1, 0x1d, 0xb2, 0x1d, 0xb3, 0x1d,
+	0xb4, 0x1d, 0xb5, 0x1d, 0xb6, 0x1d, 0xb7, 0x1d,
+	0xb8, 0x1d, 0xb9, 0x1d, 0xba, 0x1d, 0xbb, 0x1d,
+	0xbc, 0x1d, 0xbd, 0x1d, 0xbe, 0x1d, 0xbf, 0x1d,
+	0xc0, 0x1d, 0xc1, 0x1d, 0xc2, 0x1d, 0xc3, 0x1d,
+	0xc4, 0x1d, 0xc5, 0x1d, 0xc6, 0x1d, 0xc7, 0x1d,
+	0xc8, 0x1d, 0xc9, 0x1d, 0xca, 0x1d, 0xcb, 0x1d,
+	0xcc, 0x1d, 0xcd, 0x1d, 0xce, 0x1d, 0xcf, 0x1d,
+	0xd0, 0x1d, 0xd1, 0x1d, 0xd2, 0x1d, 0xd3, 0x1d,
+	0xd4, 0x1d, 0xd5, 0x1d, 0xd6, 0x1d, 0xd7, 0x1d,
+	0xd8, 0x1d, 0xd9, 0x1d, 0xda, 0x1d, 0xdb, 0x1d,
+	0xdc, 0x1d, 0xdd, 0x1d, 0xde, 0x1d, 0xdf, 0x1d,
+	0xe0, 0x1d, 0xe1, 0x1d, 0xe2, 0x1d, 0xe3, 0x1d,
+	0xe4, 0x1d, 0xe5, 0x1d, 0xe6, 0x1d, 0xe7, 0x1d,
+	0xe8, 0x1d, 0xe9, 0x1d, 0xea, 0x1d, 0xeb, 0x1d,
+	0xec, 0x1d, 0xed, 0x1d, 0xee, 0x1d, 0xef, 0x1d,
+	0xf0, 0x1d, 0xf1, 0x1d, 0xf2, 0x1d, 0xf3, 0x1d,
+	0xf4, 0x1d, 0xf5, 0x1d, 0xf6, 0x1d, 0xf7, 0x1d,
+	0xf8, 0x1d, 0xf9, 0x1d, 0xfa, 0x1d, 0xfb, 0x1d,
+	0xfc, 0x1d, 0xfd, 0x1d, 0xfe, 0x1d, 0xff, 0x1d,
+	0x00, 0x1e, 0x00, 0x1e, 0x02, 0x1e, 0x02, 0x1e,
+	0x04, 0x1e, 0x04, 0x1e, 0x06, 0x1e, 0x06, 0x1e,
+	0x08, 0x1e, 0x08, 0x1e, 0x0a, 0x1e, 0x0a, 0x1e,
+	0x0c, 0x1e, 0x0c, 0x1e, 0x0e, 0x1e, 0x0e, 0x1e,
+	0x10, 0x1e, 0x10, 0x1e, 0x12, 0x1e, 0x12, 0x1e,
+	0x14, 0x1e, 0x14, 0x1e, 0x16, 0x1e, 0x16, 0x1e,
+	0x18, 0x1e, 0x18, 0x1e, 0x1a, 0x1e, 0x1a, 0x1e,
+	0x1c, 0x1e, 0x1c, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+	0x20, 0x1e, 0x20, 0x1e, 0x22, 0x1e, 0x22, 0x1e,
+	0x24, 0x1e, 0x24, 0x1e, 0x26, 0x1e, 0x26, 0x1e,
+	0x28, 0x1e, 0x28, 0x1e, 0x2a, 0x1e, 0x2a, 0x1e,
+	0x2c, 0x1e, 0x2c, 0x1e, 0x2e, 0x1e, 0x2e, 0x1e,
+	0x30, 0x1e, 0x30, 0x1e, 0x32, 0x1e, 0x32, 0x1e,
+	0x34, 0x1e, 0x34, 0x1e, 0x36, 0x1e, 0x36, 0x1e,
+	0x38, 0x1e, 0x38, 0x1e, 0x3a, 0x1e, 0x3a, 0x1e,
+	0x3c, 0x1e, 0x3c, 0x1e, 0x3e, 0x1e, 0x3e, 0x1e,
+	0x40, 0x1e, 0x40, 0x1e, 0x42, 0x1e, 0x42, 0x1e,
+	0x44, 0x1e, 0x44, 0x1e, 0x46, 0x1e, 0x46, 0x1e,
+	0x48, 0x1e, 0x48, 0x1e, 0x4a, 0x1e, 0x4a, 0x1e,
+	0x4c, 0x1e, 0x4c, 0x1e, 0x4e, 0x1e, 0x4e, 0x1e,
+	0x50, 0x1e, 0x50, 0x1e, 0x52, 0x1e, 0x52, 0x1e,
+	0x54, 0x1e, 0x54, 0x1e, 0x56, 0x1e, 0x56, 0x1e,
+	0x58, 0x1e, 0x58, 0x1e, 0x5a, 0x1e, 0x5a, 0x1e,
+	0x5c, 0x1e, 0x5c, 0x1e, 0x5e, 0x1e, 0x5e, 0x1e,
+	0x60, 0x1e, 0x60, 0x1e, 0x62, 0x1e, 0x62, 0x1e,
+	0x64, 0x1e, 0x64, 0x1e, 0x66, 0x1e, 0x66, 0x1e,
+	0x68, 0x1e, 0x68, 0x1e, 0x6a, 0x1e, 0x6a, 0x1e,
+	0x6c, 0x1e, 0x6c, 0x1e, 0x6e, 0x1e, 0x6e, 0x1e,
+	0x70, 0x1e, 0x70, 0x1e, 0x72, 0x1e, 0x72, 0x1e,
+	0x74, 0x1e, 0x74, 0x1e, 0x76, 0x1e, 0x76, 0x1e,
+	0x78, 0x1e, 0x78, 0x1e, 0x7a, 0x1e, 0x7a, 0x1e,
+	0x7c, 0x1e, 0x7c, 0x1e, 0x7e, 0x1e, 0x7e, 0x1e,
+	0x80, 0x1e, 0x80, 0x1e, 0x82, 0x1e, 0x82, 0x1e,
+	0x84, 0x1e, 0x84, 0x1e, 0x86, 0x1e, 0x86, 0x1e,
+	0x88, 0x1e, 0x88, 0x1e, 0x8a, 0x1e, 0x8a, 0x1e,
+	0x8c, 0x1e, 0x8c, 0x1e, 0x8e, 0x1e, 0x8e, 0x1e,
+	0x90, 0x1e, 0x90, 0x1e, 0x92, 0x1e, 0x92, 0x1e,
+	0x94, 0x1e, 0x94, 0x1e, 0x96, 0x1e, 0x97, 0x1e,
+	0x98, 0x1e, 0x99, 0x1e, 0x9a, 0x1e, 0x9b, 0x1e,
+	0x9c, 0x1e, 0x9d, 0x1e, 0x9e, 0x1e, 0x9f, 0x1e,
+	0xa0, 0x1e, 0xa0, 0x1e, 0xa2, 0x1e, 0xa2, 0x1e,
+	0xa4, 0x1e, 0xa4, 0x1e, 0xa6, 0x1e, 0xa6, 0x1e,
+	0xa8, 0x1e, 0xa8, 0x1e, 0xaa, 0x1e, 0xaa, 0x1e,
+	0xac, 0x1e, 0xac, 0x1e, 0xae, 0x1e, 0xae, 0x1e,
+	0xb0, 0x1e, 0xb0, 0x1e, 0xb2, 0x1e, 0xb2, 0x1e,
+	0xb4, 0x1e, 0xb4, 0x1e, 0xb6, 0x1e, 0xb6, 0x1e,
+	0xb8, 0x1e, 0xb8, 0x1e, 0xba, 0x1e, 0xba, 0x1e,
+	0xbc, 0x1e, 0xbc, 0x1e, 0xbe, 0x1e, 0xbe, 0x1e,
+	0xc0, 0x1e, 0xc0, 0x1e, 0xc2, 0x1e, 0xc2, 0x1e,
+	0xc4, 0x1e, 0xc4, 0x1e, 0xc6, 0x1e, 0xc6, 0x1e,
+	0xc8, 0x1e, 0xc8, 0x1e, 0xca, 0x1e, 0xca, 0x1e,
+	0xcc, 0x1e, 0xcc, 0x1e, 0xce, 0x1e, 0xce, 0x1e,
+	0xd0, 0x1e, 0xd0, 0x1e, 0xd2, 0x1e, 0xd2, 0x1e,
+	0xd4, 0x1e, 0xd4, 0x1e, 0xd6, 0x1e, 0xd6, 0x1e,
+	0xd8, 0x1e, 0xd8, 0x1e, 0xda, 0x1e, 0xda, 0x1e,
+	0xdc, 0x1e, 0xdc, 0x1e, 0xde, 0x1e, 0xde, 0x1e,
+	0xe0, 0x1e, 0xe0, 0x1e, 0xe2, 0x1e, 0xe2, 0x1e,
+	0xe4, 0x1e, 0xe4, 0x1e, 0xe6, 0x1e, 0xe6, 0x1e,
+	0xe8, 0x1e, 0xe8, 0x1e, 0xea, 0x1e, 0xea, 0x1e,
+	0xec, 0x1e, 0xec, 0x1e, 0xee, 0x1e, 0xee, 0x1e,
+	0xf0, 0x1e, 0xf0, 0x1e, 0xf2, 0x1e, 0xf2, 0x1e,
+	0xf4, 0x1e, 0xf4, 0x1e, 0xf6, 0x1e, 0xf6, 0x1e,
+	0xf8, 0x1e, 0xf8, 0x1e, 0xfa, 0x1e, 0xfb, 0x1e,
+	0xfc, 0x1e, 0xfd, 0x1e, 0xfe, 0x1e, 0xff, 0x1e,
+	0x08, 0x1f, 0x09, 0x1f, 0x0a, 0x1f, 0x0b, 0x1f,
+	0x0c, 0x1f, 0x0d, 0x1f, 0x0e, 0x1f, 0x0f, 0x1f,
+	0x08, 0x1f, 0x09, 0x1f, 0x0a, 0x1f, 0x0b, 0x1f,
+	0x0c, 0x1f, 0x0d, 0x1f, 0x0e, 0x1f, 0x0f, 0x1f,
+	0x18, 0x1f, 0x19, 0x1f, 0x1a, 0x1f, 0x1b, 0x1f,
+	0x1c, 0x1f, 0x1d, 0x1f, 0x16, 0x1f, 0x17, 0x1f,
+	0x18, 0x1f, 0x19, 0x1f, 0x1a, 0x1f, 0x1b, 0x1f,
+	0x1c, 0x1f, 0x1d, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f,
+	0x28, 0x1f, 0x29, 0x1f, 0x2a, 0x1f, 0x2b, 0x1f,
+	0x2c, 0x1f, 0x2d, 0x1f, 0x2e, 0x1f, 0x2f, 0x1f,
+	0x28, 0x1f, 0x29, 0x1f, 0x2a, 0x1f, 0x2b, 0x1f,
+	0x2c, 0x1f, 0x2d, 0x1f, 0x2e, 0x1f, 0x2f, 0x1f,
+	0x38, 0x1f, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x1f,
+	0x3c, 0x1f, 0x3d, 0x1f, 0x3e, 0x1f, 0x3f, 0x1f,
+	0x38, 0x1f, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x1f,
+	0x3c, 0x1f, 0x3d, 0x1f, 0x3e, 0x1f, 0x3f, 0x1f,
+	0x48, 0x1f, 0x49, 0x1f, 0x4a, 0x1f, 0x4b, 0x1f,
+	0x4c, 0x1f, 0x4d, 0x1f, 0x46, 0x1f, 0x47, 0x1f,
+	0x48, 0x1f, 0x49, 0x1f, 0x4a, 0x1f, 0x4b, 0x1f,
+	0x4c, 0x1f, 0x4d, 0x1f, 0x4e, 0x1f, 0x4f, 0x1f,
+	0x50, 0x1f, 0x59, 0x1f, 0x52, 0x1f, 0x5b, 0x1f,
+	0x54, 0x1f, 0x5d, 0x1f, 0x56, 0x1f, 0x5f, 0x1f,
+	0x58, 0x1f, 0x59, 0x1f, 0x5a, 0x1f, 0x5b, 0x1f,
+	0x5c, 0x1f, 0x5d, 0x1f, 0x5e, 0x1f, 0x5f, 0x1f,
+	0x68, 0x1f, 0x69, 0x1f, 0x6a, 0x1f, 0x6b, 0x1f,
+	0x6c, 0x1f, 0x6d, 0x1f, 0x6e, 0x1f, 0x6f, 0x1f,
+	0x68, 0x1f, 0x69, 0x1f, 0x6a, 0x1f, 0x6b, 0x1f,
+	0x6c, 0x1f, 0x6d, 0x1f, 0x6e, 0x1f, 0x6f, 0x1f,
+	0xba, 0x1f, 0xbb, 0x1f, 0xc8, 0x1f, 0xc9, 0x1f,
+	0xca, 0x1f, 0xcb, 0x1f, 0xda, 0x1f, 0xdb, 0x1f,
+	0xf8, 0x1f, 0xf9, 0x1f, 0xea, 0x1f, 0xeb, 0x1f,
+	0xfa, 0x1f, 0xfb, 0x1f, 0x7e, 0x1f, 0x7f, 0x1f,
+	0x88, 0x1f, 0x89, 0x1f, 0x8a, 0x1f, 0x8b, 0x1f,
+	0x8c, 0x1f, 0x8d, 0x1f, 0x8e, 0x1f, 0x8f, 0x1f,
+	0x88, 0x1f, 0x89, 0x1f, 0x8a, 0x1f, 0x8b, 0x1f,
+	0x8c, 0x1f, 0x8d, 0x1f, 0x8e, 0x1f, 0x8f, 0x1f,
+	0x98, 0x1f, 0x99, 0x1f, 0x9a, 0x1f, 0x9b, 0x1f,
+	0x9c, 0x1f, 0x9d, 0x1f, 0x9e, 0x1f, 0x9f, 0x1f,
+	0x98, 0x1f, 0x99, 0x1f, 0x9a, 0x1f, 0x9b, 0x1f,
+	0x9c, 0x1f, 0x9d, 0x1f, 0x9e, 0x1f, 0x9f, 0x1f,
+	0xa8, 0x1f, 0xa9, 0x1f, 0xaa, 0x1f, 0xab, 0x1f,
+	0xac, 0x1f, 0xad, 0x1f, 0xae, 0x1f, 0xaf, 0x1f,
+	0xa8, 0x1f, 0xa9, 0x1f, 0xaa, 0x1f, 0xab, 0x1f,
+	0xac, 0x1f, 0xad, 0x1f, 0xae, 0x1f, 0xaf, 0x1f,
+	0xb8, 0x1f, 0xb9, 0x1f, 0xb2, 0x1f, 0xbc, 0x1f,
+	0xb4, 0x1f, 0xb5, 0x1f, 0xb6, 0x1f, 0xb7, 0x1f,
+	0xb8, 0x1f, 0xb9, 0x1f, 0xba, 0x1f, 0xbb, 0x1f,
+	0xbc, 0x1f, 0xbd, 0x1f, 0xbe, 0x1f, 0xbf, 0x1f,
+	0xc0, 0x1f, 0xc1, 0x1f, 0xc2, 0x1f, 0xc3, 0x1f,
+	0xc4, 0x1f, 0xc5, 0x1f, 0xc6, 0x1f, 0xc7, 0x1f,
+	0xc8, 0x1f, 0xc9, 0x1f, 0xca, 0x1f, 0xcb, 0x1f,
+	0xc3, 0x1f, 0xcd, 0x1f, 0xce, 0x1f, 0xcf, 0x1f,
+	0xd8, 0x1f, 0xd9, 0x1f, 0xd2, 0x1f, 0xd3, 0x1f,
+	0xd4, 0x1f, 0xd5, 0x1f, 0xd6, 0x1f, 0xd7, 0x1f,
+	0xd8, 0x1f, 0xd9, 0x1f, 0xda, 0x1f, 0xdb, 0x1f,
+	0xdc, 0x1f, 0xdd, 0x1f, 0xde, 0x1f, 0xdf, 0x1f,
+	0xe8, 0x1f, 0xe9, 0x1f, 0xe2, 0x1f, 0xe3, 0x1f,
+	0xe4, 0x1f, 0xec, 0x1f, 0xe6, 0x1f, 0xe7, 0x1f,
+	0xe8, 0x1f, 0xe9, 0x1f, 0xea, 0x1f, 0xeb, 0x1f,
+	0xec, 0x1f, 0xed, 0x1f, 0xee, 0x1f, 0xef, 0x1f,
+	0xf0, 0x1f, 0xf1, 0x1f, 0xf2, 0x1f, 0xf3, 0x1f,
+	0xf4, 0x1f, 0xf5, 0x1f, 0xf6, 0x1f, 0xf7, 0x1f,
+	0xf8, 0x1f, 0xf9, 0x1f, 0xfa, 0x1f, 0xfb, 0x1f,
+	0xf3, 0x1f, 0xfd, 0x1f, 0xfe, 0x1f, 0xff, 0x1f,
+	0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20,
+	0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20,
+	0x08, 0x20, 0x09, 0x20, 0x0a, 0x20, 0x0b, 0x20,
+	0x0c, 0x20, 0x0d, 0x20, 0x0e, 0x20, 0x0f, 0x20,
+	0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20,
+	0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20,
+	0x18, 0x20, 0x19, 0x20, 0x1a, 0x20, 0x1b, 0x20,
+	0x1c, 0x20, 0x1d, 0x20, 0x1e, 0x20, 0x1f, 0x20,
+	0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20,
+	0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20,
+	0x28, 0x20, 0x29, 0x20, 0x2a, 0x20, 0x2b, 0x20,
+	0x2c, 0x20, 0x2d, 0x20, 0x2e, 0x20, 0x2f, 0x20,
+	0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20,
+	0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20,
+	0x38, 0x20, 0x39, 0x20, 0x3a, 0x20, 0x3b, 0x20,
+	0x3c, 0x20, 0x3d, 0x20, 0x3e, 0x20, 0x3f, 0x20,
+	0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20,
+	0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20,
+	0x48, 0x20, 0x49, 0x20, 0x4a, 0x20, 0x4b, 0x20,
+	0x4c, 0x20, 0x4d, 0x20, 0x4e, 0x20, 0x4f, 0x20,
+	0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20,
+	0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20,
+	0x58, 0x20, 0x59, 0x20, 0x5a, 0x20, 0x5b, 0x20,
+	0x5c, 0x20, 0x5d, 0x20, 0x5e, 0x20, 0x5f, 0x20,
+	0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20,
+	0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20,
+	0x68, 0x20, 0x69, 0x20, 0x6a, 0x20, 0x6b, 0x20,
+	0x6c, 0x20, 0x6d, 0x20, 0x6e, 0x20, 0x6f, 0x20,
+	0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20,
+	0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20,
+	0x78, 0x20, 0x79, 0x20, 0x7a, 0x20, 0x7b, 0x20,
+	0x7c, 0x20, 0x7d, 0x20, 0x7e, 0x20, 0x7f, 0x20,
+	0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20,
+	0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20,
+	0x88, 0x20, 0x89, 0x20, 0x8a, 0x20, 0x8b, 0x20,
+	0x8c, 0x20, 0x8d, 0x20, 0x8e, 0x20, 0x8f, 0x20,
+	0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20,
+	0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20,
+	0x98, 0x20, 0x99, 0x20, 0x9a, 0x20, 0x9b, 0x20,
+	0x9c, 0x20, 0x9d, 0x20, 0x9e, 0x20, 0x9f, 0x20,
+	0xa0, 0x20, 0xa1, 0x20, 0xa2, 0x20, 0xa3, 0x20,
+	0xa4, 0x20, 0xa5, 0x20, 0xa6, 0x20, 0xa7, 0x20,
+	0xa8, 0x20, 0xa9, 0x20, 0xaa, 0x20, 0xab, 0x20,
+	0xac, 0x20, 0xad, 0x20, 0xae, 0x20, 0xaf, 0x20,
+	0xb0, 0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20,
+	0xb4, 0x20, 0xb5, 0x20, 0xb6, 0x20, 0xb7, 0x20,
+	0xb8, 0x20, 0xb9, 0x20, 0xba, 0x20, 0xbb, 0x20,
+	0xbc, 0x20, 0xbd, 0x20, 0xbe, 0x20, 0xbf, 0x20,
+	0xc0, 0x20, 0xc1, 0x20, 0xc2, 0x20, 0xc3, 0x20,
+	0xc4, 0x20, 0xc5, 0x20, 0xc6, 0x20, 0xc7, 0x20,
+	0xc8, 0x20, 0xc9, 0x20, 0xca, 0x20, 0xcb, 0x20,
+	0xcc, 0x20, 0xcd, 0x20, 0xce, 0x20, 0xcf, 0x20,
+	0xd0, 0x20, 0xd1, 0x20, 0xd2, 0x20, 0xd3, 0x20,
+	0xd4, 0x20, 0xd5, 0x20, 0xd6, 0x20, 0xd7, 0x20,
+	0xd8, 0x20, 0xd9, 0x20, 0xda, 0x20, 0xdb, 0x20,
+	0xdc, 0x20, 0xdd, 0x20, 0xde, 0x20, 0xdf, 0x20,
+	0xe0, 0x20, 0xe1, 0x20, 0xe2, 0x20, 0xe3, 0x20,
+	0xe4, 0x20, 0xe5, 0x20, 0xe6, 0x20, 0xe7, 0x20,
+	0xe8, 0x20, 0xe9, 0x20, 0xea, 0x20, 0xeb, 0x20,
+	0xec, 0x20, 0xed, 0x20, 0xee, 0x20, 0xef, 0x20,
+	0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, 0x20,
+	0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20,
+	0xf8, 0x20, 0xf9, 0x20, 0xfa, 0x20, 0xfb, 0x20,
+	0xfc, 0x20, 0xfd, 0x20, 0xfe, 0x20, 0xff, 0x20,
+	0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21,
+	0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21,
+	0x08, 0x21, 0x09, 0x21, 0x0a, 0x21, 0x0b, 0x21,
+	0x0c, 0x21, 0x0d, 0x21, 0x0e, 0x21, 0x0f, 0x21,
+	0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21,
+	0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21,
+	0x18, 0x21, 0x19, 0x21, 0x1a, 0x21, 0x1b, 0x21,
+	0x1c, 0x21, 0x1d, 0x21, 0x1e, 0x21, 0x1f, 0x21,
+	0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21,
+	0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21,
+	0x28, 0x21, 0x29, 0x21, 0x2a, 0x21, 0x2b, 0x21,
+	0x2c, 0x21, 0x2d, 0x21, 0x2e, 0x21, 0x2f, 0x21,
+	0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21,
+	0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21,
+	0x38, 0x21, 0x39, 0x21, 0x3a, 0x21, 0x3b, 0x21,
+	0x3c, 0x21, 0x3d, 0x21, 0x3e, 0x21, 0x3f, 0x21,
+	0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21,
+	0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21,
+	0x48, 0x21, 0x49, 0x21, 0x4a, 0x21, 0x4b, 0x21,
+	0x4c, 0x21, 0x4d, 0x21, 0x32, 0x21, 0x4f, 0x21,
+	0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21,
+	0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21,
+	0x58, 0x21, 0x59, 0x21, 0x5a, 0x21, 0x5b, 0x21,
+	0x5c, 0x21, 0x5d, 0x21, 0x5e, 0x21, 0x5f, 0x21,
+	0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
+	0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
+	0x68, 0x21, 0x69, 0x21, 0x6a, 0x21, 0x6b, 0x21,
+	0x6c, 0x21, 0x6d, 0x21, 0x6e, 0x21, 0x6f, 0x21,
+	0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21,
+	0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21,
+	0x68, 0x21, 0x69, 0x21, 0x6a, 0x21, 0x6b, 0x21,
+	0x6c, 0x21, 0x6d, 0x21, 0x6e, 0x21, 0x6f, 0x21,
+	0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21,
+	0x83, 0x21, 0xff, 0xff, 0x4b, 0x03, 0xb6, 0x24,
+	0xb7, 0x24, 0xb8, 0x24, 0xb9, 0x24, 0xba, 0x24,
+	0xbb, 0x24, 0xbc, 0x24, 0xbd, 0x24, 0xbe, 0x24,
+	0xbf, 0x24, 0xc0, 0x24, 0xc1, 0x24, 0xc2, 0x24,
+	0xc3, 0x24, 0xc4, 0x24, 0xc5, 0x24, 0xc6, 0x24,
+	0xc7, 0x24, 0xc8, 0x24, 0xc9, 0x24, 0xca, 0x24,
+	0xcb, 0x24, 0xcc, 0x24, 0xcd, 0x24, 0xce, 0x24,
+	0xcf, 0x24, 0xff, 0xff, 0x46, 0x07, 0x00, 0x2c,
+	0x01, 0x2c, 0x02, 0x2c, 0x03, 0x2c, 0x04, 0x2c,
+	0x05, 0x2c, 0x06, 0x2c, 0x07, 0x2c, 0x08, 0x2c,
+	0x09, 0x2c, 0x0a, 0x2c, 0x0b, 0x2c, 0x0c, 0x2c,
+	0x0d, 0x2c, 0x0e, 0x2c, 0x0f, 0x2c, 0x10, 0x2c,
+	0x11, 0x2c, 0x12, 0x2c, 0x13, 0x2c, 0x14, 0x2c,
+	0x15, 0x2c, 0x16, 0x2c, 0x17, 0x2c, 0x18, 0x2c,
+	0x19, 0x2c, 0x1a, 0x2c, 0x1b, 0x2c, 0x1c, 0x2c,
+	0x1d, 0x2c, 0x1e, 0x2c, 0x1f, 0x2c, 0x20, 0x2c,
+	0x21, 0x2c, 0x22, 0x2c, 0x23, 0x2c, 0x24, 0x2c,
+	0x25, 0x2c, 0x26, 0x2c, 0x27, 0x2c, 0x28, 0x2c,
+	0x29, 0x2c, 0x2a, 0x2c, 0x2b, 0x2c, 0x2c, 0x2c,
+	0x2d, 0x2c, 0x2e, 0x2c, 0x5f, 0x2c, 0x60, 0x2c,
+	0x60, 0x2c, 0x62, 0x2c, 0x63, 0x2c, 0x64, 0x2c,
+	0x65, 0x2c, 0x66, 0x2c, 0x67, 0x2c, 0x67, 0x2c,
+	0x69, 0x2c, 0x69, 0x2c, 0x6b, 0x2c, 0x6b, 0x2c,
+	0x6d, 0x2c, 0x6e, 0x2c, 0x6f, 0x2c, 0x70, 0x2c,
+	0x71, 0x2c, 0x72, 0x2c, 0x73, 0x2c, 0x74, 0x2c,
+	0x75, 0x2c, 0x75, 0x2c, 0x77, 0x2c, 0x78, 0x2c,
+	0x79, 0x2c, 0x7a, 0x2c, 0x7b, 0x2c, 0x7c, 0x2c,
+	0x7d, 0x2c, 0x7e, 0x2c, 0x7f, 0x2c, 0x80, 0x2c,
+	0x80, 0x2c, 0x82, 0x2c, 0x82, 0x2c, 0x84, 0x2c,
+	0x84, 0x2c, 0x86, 0x2c, 0x86, 0x2c, 0x88, 0x2c,
+	0x88, 0x2c, 0x8a, 0x2c, 0x8a, 0x2c, 0x8c, 0x2c,
+	0x8c, 0x2c, 0x8e, 0x2c, 0x8e, 0x2c, 0x90, 0x2c,
+	0x90, 0x2c, 0x92, 0x2c, 0x92, 0x2c, 0x94, 0x2c,
+	0x94, 0x2c, 0x96, 0x2c, 0x96, 0x2c, 0x98, 0x2c,
+	0x98, 0x2c, 0x9a, 0x2c, 0x9a, 0x2c, 0x9c, 0x2c,
+	0x9c, 0x2c, 0x9e, 0x2c, 0x9e, 0x2c, 0xa0, 0x2c,
+	0xa0, 0x2c, 0xa2, 0x2c, 0xa2, 0x2c, 0xa4, 0x2c,
+	0xa4, 0x2c, 0xa6, 0x2c, 0xa6, 0x2c, 0xa8, 0x2c,
+	0xa8, 0x2c, 0xaa, 0x2c, 0xaa, 0x2c, 0xac, 0x2c,
+	0xac, 0x2c, 0xae, 0x2c, 0xae, 0x2c, 0xb0, 0x2c,
+	0xb0, 0x2c, 0xb2, 0x2c, 0xb2, 0x2c, 0xb4, 0x2c,
+	0xb4, 0x2c, 0xb6, 0x2c, 0xb6, 0x2c, 0xb8, 0x2c,
+	0xb8, 0x2c, 0xba, 0x2c, 0xba, 0x2c, 0xbc, 0x2c,
+	0xbc, 0x2c, 0xbe, 0x2c, 0xbe, 0x2c, 0xc0, 0x2c,
+	0xc0, 0x2c, 0xc2, 0x2c, 0xc2, 0x2c, 0xc4, 0x2c,
+	0xc4, 0x2c, 0xc6, 0x2c, 0xc6, 0x2c, 0xc8, 0x2c,
+	0xc8, 0x2c, 0xca, 0x2c, 0xca, 0x2c, 0xcc, 0x2c,
+	0xcc, 0x2c, 0xce, 0x2c, 0xce, 0x2c, 0xd0, 0x2c,
+	0xd0, 0x2c, 0xd2, 0x2c, 0xd2, 0x2c, 0xd4, 0x2c,
+	0xd4, 0x2c, 0xd6, 0x2c, 0xd6, 0x2c, 0xd8, 0x2c,
+	0xd8, 0x2c, 0xda, 0x2c, 0xda, 0x2c, 0xdc, 0x2c,
+	0xdc, 0x2c, 0xde, 0x2c, 0xde, 0x2c, 0xe0, 0x2c,
+	0xe0, 0x2c, 0xe2, 0x2c, 0xe2, 0x2c, 0xe4, 0x2c,
+	0xe5, 0x2c, 0xe6, 0x2c, 0xe7, 0x2c, 0xe8, 0x2c,
+	0xe9, 0x2c, 0xea, 0x2c, 0xeb, 0x2c, 0xec, 0x2c,
+	0xed, 0x2c, 0xee, 0x2c, 0xef, 0x2c, 0xf0, 0x2c,
+	0xf1, 0x2c, 0xf2, 0x2c, 0xf3, 0x2c, 0xf4, 0x2c,
+	0xf5, 0x2c, 0xf6, 0x2c, 0xf7, 0x2c, 0xf8, 0x2c,
+	0xf9, 0x2c, 0xfa, 0x2c, 0xfb, 0x2c, 0xfc, 0x2c,
+	0xfd, 0x2c, 0xfe, 0x2c, 0xff, 0x2c, 0xa0, 0x10,
+	0xa1, 0x10, 0xa2, 0x10, 0xa3, 0x10, 0xa4, 0x10,
+	0xa5, 0x10, 0xa6, 0x10, 0xa7, 0x10, 0xa8, 0x10,
+	0xa9, 0x10, 0xaa, 0x10, 0xab, 0x10, 0xac, 0x10,
+	0xad, 0x10, 0xae, 0x10, 0xaf, 0x10, 0xb0, 0x10,
+	0xb1, 0x10, 0xb2, 0x10, 0xb3, 0x10, 0xb4, 0x10,
+	0xb5, 0x10, 0xb6, 0x10, 0xb7, 0x10, 0xb8, 0x10,
+	0xb9, 0x10, 0xba, 0x10, 0xbb, 0x10, 0xbc, 0x10,
+	0xbd, 0x10, 0xbe, 0x10, 0xbf, 0x10, 0xc0, 0x10,
+	0xc1, 0x10, 0xc2, 0x10, 0xc3, 0x10, 0xc4, 0x10,
+	0xc5, 0x10, 0xff, 0xff, 0x1b, 0xd2, 0x21, 0xff,
+	0x22, 0xff, 0x23, 0xff, 0x24, 0xff, 0x25, 0xff,
+	0x26, 0xff, 0x27, 0xff, 0x28, 0xff, 0x29, 0xff,
+	0x2a, 0xff, 0x2b, 0xff, 0x2c, 0xff, 0x2d, 0xff,
+	0x2e, 0xff, 0x2f, 0xff, 0x30, 0xff, 0x31, 0xff,
+	0x32, 0xff, 0x33, 0xff, 0x34, 0xff, 0x35, 0xff,
+	0x36, 0xff, 0x37, 0xff, 0x38, 0xff, 0x39, 0xff,
+	0x3a, 0xff, 0x5b, 0xff, 0x5c, 0xff, 0x5d, 0xff,
+	0x5e, 0xff, 0x5f, 0xff, 0x60, 0xff, 0x61, 0xff,
+	0x62, 0xff, 0x63, 0xff, 0x64, 0xff, 0x65, 0xff,
+	0x66, 0xff, 0x67, 0xff, 0x68, 0xff, 0x69, 0xff,
+	0x6a, 0xff, 0x6b, 0xff, 0x6c, 0xff, 0x6d, 0xff,
+	0x6e, 0xff, 0x6f, 0xff, 0x70, 0xff, 0x71, 0xff,
+	0x72, 0xff, 0x73, 0xff, 0x74, 0xff, 0x75, 0xff,
+	0x76, 0xff, 0x77, 0xff, 0x78, 0xff, 0x79, 0xff,
+	0x7a, 0xff, 0x7b, 0xff, 0x7c, 0xff, 0x7d, 0xff,
+	0x7e, 0xff, 0x7f, 0xff, 0x80, 0xff, 0x81, 0xff,
+	0x82, 0xff, 0x83, 0xff, 0x84, 0xff, 0x85, 0xff,
+	0x86, 0xff, 0x87, 0xff, 0x88, 0xff, 0x89, 0xff,
+	0x8a, 0xff, 0x8b, 0xff, 0x8c, 0xff, 0x8d, 0xff,
+	0x8e, 0xff, 0x8f, 0xff, 0x90, 0xff, 0x91, 0xff,
+	0x92, 0xff, 0x93, 0xff, 0x94, 0xff, 0x95, 0xff,
+	0x96, 0xff, 0x97, 0xff, 0x98, 0xff, 0x99, 0xff,
+	0x9a, 0xff, 0x9b, 0xff, 0x9c, 0xff, 0x9d, 0xff,
+	0x9e, 0xff, 0x9f, 0xff, 0xa0, 0xff, 0xa1, 0xff,
+	0xa2, 0xff, 0xa3, 0xff, 0xa4, 0xff, 0xa5, 0xff,
+	0xa6, 0xff, 0xa7, 0xff, 0xa8, 0xff, 0xa9, 0xff,
+	0xaa, 0xff, 0xab, 0xff, 0xac, 0xff, 0xad, 0xff,
+	0xae, 0xff, 0xaf, 0xff, 0xb0, 0xff, 0xb1, 0xff,
+	0xb2, 0xff, 0xb3, 0xff, 0xb4, 0xff, 0xb5, 0xff,
+	0xb6, 0xff, 0xb7, 0xff, 0xb8, 0xff, 0xb9, 0xff,
+	0xba, 0xff, 0xbb, 0xff, 0xbc, 0xff, 0xbd, 0xff,
+	0xbe, 0xff, 0xbf, 0xff, 0xc0, 0xff, 0xc1, 0xff,
+	0xc2, 0xff, 0xc3, 0xff, 0xc4, 0xff, 0xc5, 0xff,
+	0xc6, 0xff, 0xc7, 0xff, 0xc8, 0xff, 0xc9, 0xff,
+	0xca, 0xff, 0xcb, 0xff, 0xcc, 0xff, 0xcd, 0xff,
+	0xce, 0xff, 0xcf, 0xff, 0xd0, 0xff, 0xd1, 0xff,
+	0xd2, 0xff, 0xd3, 0xff, 0xd4, 0xff, 0xd5, 0xff,
+	0xd6, 0xff, 0xd7, 0xff, 0xd8, 0xff, 0xd9, 0xff,
+	0xda, 0xff, 0xdb, 0xff, 0xdc, 0xff, 0xdd, 0xff,
+	0xde, 0xff, 0xdf, 0xff, 0xe0, 0xff, 0xe1, 0xff,
+	0xe2, 0xff, 0xe3, 0xff, 0xe4, 0xff, 0xe5, 0xff,
+	0xe6, 0xff, 0xe7, 0xff, 0xe8, 0xff, 0xe9, 0xff,
+	0xea, 0xff, 0xeb, 0xff, 0xec, 0xff, 0xed, 0xff,
+	0xee, 0xff, 0xef, 0xff, 0xf0, 0xff, 0xf1, 0xff,
+	0xf2, 0xff, 0xf3, 0xff, 0xf4, 0xff, 0xf5, 0xff,
+	0xf6, 0xff, 0xf7, 0xff, 0xf8, 0xff, 0xf9, 0xff,
+	0xfa, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xfd, 0xff,
+	0xfe, 0xff, 0xff, 0xff
+};
diff --git a/exfat/mkfs/uctc.h b/exfat/mkfs/uctc.h
new file mode 100644
index 0000000..3e7dee0
--- /dev/null
+++ b/exfat/mkfs/uctc.h
@@ -0,0 +1,30 @@
+/*
+	uctc.h (30.10.10)
+	Upper Case Table declaration.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_UCTC_H_INCLUDED
+#define MKFS_UCTC_H_INCLUDED
+
+#include <stdint.h>
+
+extern uint8_t upcase_table[5836];
+
+#endif /* ifndef MKFS_UCTC_H_INCLUDED */
diff --git a/exfat/mkfs/vbr.c b/exfat/mkfs/vbr.c
new file mode 100644
index 0000000..bbf1304
--- /dev/null
+++ b/exfat/mkfs/vbr.c
@@ -0,0 +1,148 @@
+/*
+	vbr.c (09.11.10)
+	Volume Boot Record creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <string.h>
+#include "vbr.h"
+#include "fat.h"
+#include "cbm.h"
+#include "uct.h"
+#include "rootdir.h"
+
+static off64_t vbr_alignment(void)
+{
+	return get_sector_size();
+}
+
+static off64_t vbr_size(void)
+{
+	return 12 * get_sector_size();
+}
+
+static void init_sb(struct exfat_super_block* sb)
+{
+	uint32_t clusters_max;
+	uint32_t fat_sectors;
+
+	clusters_max = get_volume_size() / get_cluster_size();
+	fat_sectors = DIV_ROUND_UP((off64_t) clusters_max * sizeof(cluster_t),
+			get_sector_size());
+
+	memset(sb, 0, sizeof(struct exfat_super_block));
+	sb->jump[0] = 0xeb;
+	sb->jump[1] = 0x76;
+	sb->jump[2] = 0x90;
+	memcpy(sb->oem_name, "EXFAT   ", sizeof(sb->oem_name));
+	sb->sector_start = cpu_to_le64(get_first_sector());
+	sb->sector_count = cpu_to_le64(get_volume_size() / get_sector_size());
+	sb->fat_sector_start = cpu_to_le32(
+			fat.get_alignment() / get_sector_size());
+	sb->fat_sector_count = cpu_to_le32(ROUND_UP(
+			le32_to_cpu(sb->fat_sector_start) + fat_sectors,
+				1 << get_spc_bits()) -
+			le32_to_cpu(sb->fat_sector_start));
+	sb->cluster_sector_start = cpu_to_le32(
+			get_position(&cbm) / get_sector_size());
+	sb->cluster_count = cpu_to_le32(clusters_max -
+			((le32_to_cpu(sb->fat_sector_start) +
+			  le32_to_cpu(sb->fat_sector_count)) >> get_spc_bits()));
+	sb->rootdir_cluster = cpu_to_le32(
+			(get_position(&rootdir) - get_position(&cbm)) / get_cluster_size()
+			+ EXFAT_FIRST_DATA_CLUSTER);
+	sb->volume_serial = cpu_to_le32(get_volume_serial());
+	sb->version.major = 1;
+	sb->version.minor = 0;
+	sb->volume_state = cpu_to_le16(0);
+	sb->sector_bits = get_sector_bits();
+	sb->spc_bits = get_spc_bits();
+	sb->fat_count = 1;
+	sb->drive_no = 0x80;
+	sb->allocated_percent = 0;
+	sb->boot_signature = cpu_to_le16(0xaa55);
+}
+
+static int vbr_write(struct exfat_dev* dev)
+{
+	struct exfat_super_block sb;
+	uint32_t checksum;
+	le32_t* sector = malloc(get_sector_size());
+	size_t i;
+
+	if (sector == NULL)
+	{
+		exfat_error("failed to allocate sector-sized block of memory");
+		return 1;
+	}
+
+	init_sb(&sb);
+	if (exfat_write(dev, &sb, sizeof(struct exfat_super_block)) < 0)
+	{
+		free(sector);
+		exfat_error("failed to write super block sector");
+		return 1;
+	}
+	checksum = exfat_vbr_start_checksum(&sb, sizeof(struct exfat_super_block));
+
+	memset(sector, 0, get_sector_size());
+	sector[get_sector_size() / sizeof(sector[0]) - 1] =
+			cpu_to_le32(0xaa550000);
+	for (i = 0; i < 8; i++)
+	{
+		if (exfat_write(dev, sector, get_sector_size()) < 0)
+		{
+			free(sector);
+			exfat_error("failed to write a sector with boot signature");
+			return 1;
+		}
+		checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum);
+	}
+
+	memset(sector, 0, get_sector_size());
+	for (i = 0; i < 2; i++)
+	{
+		if (exfat_write(dev, sector, get_sector_size()) < 0)
+		{
+			free(sector);
+			exfat_error("failed to write an empty sector");
+			return 1;
+		}
+		checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum);
+	}
+
+	for (i = 0; i < get_sector_size() / sizeof(sector[0]); i++)
+		sector[i] = cpu_to_le32(checksum);
+	if (exfat_write(dev, sector, get_sector_size()) < 0)
+	{
+		free(sector);
+		exfat_error("failed to write checksum sector");
+		return 1;
+	}
+
+	free(sector);
+	return 0;
+}
+
+const struct fs_object vbr =
+{
+	.get_alignment = vbr_alignment,
+	.get_size = vbr_size,
+	.write = vbr_write,
+};
diff --git a/exfat/mkfs/vbr.h b/exfat/mkfs/vbr.h
new file mode 100644
index 0000000..fec88bc
--- /dev/null
+++ b/exfat/mkfs/vbr.h
@@ -0,0 +1,30 @@
+/*
+	vbr.h (09.11.10)
+	Volume Boot Record creation code.
+
+	Free exFAT implementation.
+	Copyright (C) 2011-2013  Andrew Nayenko
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License along
+	with this program; if not, write to the Free Software Foundation, Inc.,
+	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MKFS_VBR_H_INCLUDED
+#define MKFS_VBR_H_INCLUDED
+
+#include "mkexfat.h"
+
+extern const struct fs_object vbr;
+
+#endif /* ifndef MKFS_VBR_H_INCLUDED */
diff --git a/fb2png/.gitignore b/fb2png/.gitignore
new file mode 100644
index 0000000..e32c432
--- /dev/null
+++ b/fb2png/.gitignore
@@ -0,0 +1,5 @@
+*.o
+fb2png
+adb_screenshoot
+libs
+obj
diff --git a/fb2png/Android.mk b/fb2png/Android.mk
new file mode 100644
index 0000000..6d7b25e
--- /dev/null
+++ b/fb2png/Android.mk
@@ -0,0 +1,77 @@
+# Makefile for Android to build fb2png
+#
+# Copyright (C) 2012  Kyan <kyan.ql.he@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+#Ported to CWM source for PhilZ Touch recovery
+#Special thanks to talustus for his help in cross compiling and the Makefile
+#Thanks to McKael @xda for his help in fixing for Nexus 4
+
+LOCAL_PATH:= $(call my-dir)
+
+# We need to build this for both the device (as a shared library)
+# and the host (as a static library for tools to use).
+
+# <-- Build libpng
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libpng
+LOCAL_SRC_FILES := libpng/lib/libpng.a
+
+include $(PREBUILT_STATIC_LIBRARY)
+# -->
+
+
+# <-- Build libfb2png
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libfb2png
+LOCAL_SRC_FILES := \
+    fb2png.c \
+    img_process.c \
+    fb.c
+
+LOCAL_C_INCLUDES +=\
+    external/libpng\
+    external/zlib
+
+LOCAL_CFLAGS += -DANDROID
+LOCAL_STATIC_LIBRARIES := libpng libz
+
+include $(BUILD_STATIC_LIBRARY)
+# -->
+
+
+# <-- Build fb2png bin
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := main.c
+LOCAL_MODULE := fb2png
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_TAGS := eng
+LOCAL_CFLAGS += -DANDROID
+LOCAL_STATIC_LIBRARIES := libfb2png libpng libz libc
+
+LOCAL_C_INCLUDES +=\
+    external/libpng\
+    external/zlib
+
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+
+include $(BUILD_EXECUTABLE)
+# -->
diff --git a/fb2png/AndroidManifest.xml b/fb2png/AndroidManifest.xml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fb2png/AndroidManifest.xml
diff --git a/fb2png/COPYING b/fb2png/COPYING
new file mode 100644
index 0000000..0c5d923
--- /dev/null
+++ b/fb2png/COPYING
@@ -0,0 +1,17 @@
+/*
+ *   Author: Kyan He <kyan.ql.he@gmail.com>
+ *
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
diff --git a/fb2png/Makefile b/fb2png/Makefile
new file mode 100644
index 0000000..5ee843c
--- /dev/null
+++ b/fb2png/Makefile
@@ -0,0 +1,17 @@
+# NDK
+CC := arm-linux-androideabi-gcc
+CFLAGS += -g -static -DANDROID
+LDFLAGS += -lpng -lz -lm
+
+ALL: fb2png adb_screenshoot
+
+fb2png: main.o fb.o img_process.o fb2png.o
+	$(CC) $(CFLAGS) main.o fb.o img_process.o fb2png.o -o fb2png $(LDFLAGS)
+	# $(CC) $(CFLAGS) main.o fb.o img_process.o fb2png.o -o fb2png
+
+adb_screenshoot: adb_screenshoot.o fb.o img_process.o
+	 $(CC) $(CFLAGS) adb_screenshoot.o fb.o img_process.o -o adb_screenshoot $(LDFLAGS)
+
+clean:
+	rm -f *.o
+	rm -f fb2png adb_screenshoot
diff --git a/fb2png/adb_screenshoot.c b/fb2png/adb_screenshoot.c
new file mode 100644
index 0000000..afd54ca
--- /dev/null
+++ b/fb2png/adb_screenshoot.c
@@ -0,0 +1,191 @@
+/*
+ *   -- http://android-fb2png.googlecode.com/svn/trunk/adb_screenshoot.c --
+ *
+ *   Copyright 2011, Kyan He <kyan.ql.he@gmail.com>
+ *
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include "fb.h"
+#include "log.h"
+
+#define DEFAULT_SAVE_PATH "fbdump.png"
+
+/* defined in $T/system/core/adb/framebuffer_service.c */
+#define DDMS_RAWIMAGE_VERSION 1
+struct fbinfo {
+    unsigned int version;
+    unsigned int bpp;
+    unsigned int size;
+    unsigned int width;
+    unsigned int height;
+    unsigned int red_offset;
+    unsigned int red_length;
+    unsigned int blue_offset;
+    unsigned int blue_length;
+    unsigned int green_offset;
+    unsigned int green_length;
+    unsigned int alpha_offset;
+    unsigned int alpha_length;
+} __attribute__((packed));
+
+static int remote_socket(const char *host, int port)
+{
+    struct sockaddr_in sa;
+    struct hostent *hp;
+    int s;
+
+    if(!(hp = gethostbyname(host))){ return -1; }
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sin_port = htons(port);
+    sa.sin_family = hp->h_addrtype;
+    memcpy((void*) &sa.sin_addr, (void*) hp->h_addr, hp->h_length);
+
+    if((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) {
+        return -1;
+    }
+
+    if(connect(s, (struct sockaddr*) &sa, sizeof(sa)) != 0){
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+char *target = "usb";
+static int adb_fd;
+
+/**
+ * Write command through adb protocol.
+ * Return
+ *      Bytes have been wrote.
+ */
+static int adb_write(const char *cmd)
+{
+    char buf[1024];
+    int sz;
+
+    /* Construct command. */
+    sz = sprintf(buf, "%04x%s", strlen(cmd), cmd);
+
+    write(adb_fd, buf, sz);
+
+#if 0
+    D("<< %s", buf);
+#endif
+    return sz;
+}
+
+/**
+ * Read data through adb protocol.
+ * Return
+ *      Bytes have been read.
+ */
+static int adb_read(char *buf, int sz)
+{
+    sz = read(adb_fd, buf, sz);
+    if (sz < 0) {
+        E("Fail to read from adb socket, %s", strerror(errno));
+    }
+    buf[sz] = '\0';
+#if 0
+    D(">> %d", sz);
+#endif
+    return sz;
+}
+
+static int get_fb_from_adb(struct fb *fb)
+{
+    char buf[1024];
+    const struct fbinfo* fbinfo;
+
+    /* Init socket */
+    adb_fd = remote_socket("localhost", 5037);
+    if (adb_fd < 0) {
+        E("Fail to create socket, %s", strerror(errno));
+    }
+
+    adb_write("host:transport-");
+    adb_read(buf, 1024);
+
+    adb_write("framebuffer:");
+    adb_read(buf, 1024);
+
+    /* Parse FB header. */
+    adb_read(buf, sizeof(struct fbinfo));
+    fbinfo = (struct fbinfo*) buf;
+
+    if (fbinfo->version != DDMS_RAWIMAGE_VERSION) {
+        E("unspport adb version");
+    }
+
+    /* Assemble struct fb */
+    memcpy(fb, &fbinfo->bpp, sizeof(struct fbinfo) - 4);
+    fb_dump(fb);
+
+    fb->data = malloc(fb->size);
+    if (!fb->data) return -1;
+
+    /* Read out the whole framebuffer */
+    int bytes_read = 0;
+    while (bytes_read < fb->size) {
+        bytes_read += adb_read(fb->data + bytes_read, fb->size - bytes_read);
+    }
+
+    return 0;
+}
+
+int fb2png(const char* path)
+{
+    struct fb fb;
+
+    if (get_fb_from_adb(&fb)) {
+        D("cannot get framebuffer.");
+        return -1;
+    }
+
+    return fb_save_png(&fb, path);
+}
+
+int main(int argc, char *argv[])
+{
+    char fn[128];
+
+    if (argc == 2) {
+        //if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
+        if (argv[1][0] == '-') {
+            printf(
+                "Usage: fb2png [path/to/output.png]\n"
+                "    The default output path is ./fbdump.png\n"
+                );
+            exit(0);
+        } else {
+            sprintf(fn, "%s", argv[1]);
+        }
+    } else {
+        sprintf(fn, "%s", DEFAULT_SAVE_PATH);
+    }
+
+    return fb2png(fn);
+}
diff --git a/fb2png/ascreenshooter.py b/fb2png/ascreenshooter.py
new file mode 100644
index 0000000..1e99484
--- /dev/null
+++ b/fb2png/ascreenshooter.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+
+import socket
+import sys
+import struct
+import time
+
+# debug
+VERBOSE = True
+
+def D(msg):
+    if VERBOSE: print(msg)
+
+# "struct fbinfo" is defined in $T/system/core/adb/framebuffer_service.c
+def fbinfo_unpack(data):
+    keys = ("version",
+            "bpp",
+            "size",
+            "width",
+            "height",
+            "red_offset",
+            "red_length",
+            "blue_offset",
+            "blue_length",
+            "green_offset",
+            "green_length",
+            "alpha_offset",
+            "alpha_length"
+        )
+    # the data is little-endian
+    values = struct.unpack("<IIIIIIIIIIIII",data)
+
+    D("dump struct fbinfo")
+    i = 0
+    for key in keys:
+        D("%14s: %-12d" % (key, values[i]))
+        i = i + 1
+
+
+
+def save():
+    f = open('dump', 'w')
+    while True:
+        data = s.recv(4096 * 16)
+        if data == "":
+            break
+        f.write(data)
+    f.close()
+
+
+def communicate(cmd=None):
+    if cmd != None:
+        buf = "%04x%s" % (len(cmd), cmd)
+        D("<< " + buf)
+        s.send(buf)
+    data = s.recv(4096)
+
+    D(">> [%s]" % len(data))
+    D(data)
+
+    if data[0:4] == 'FAIL':
+        return False
+    else:
+        return True
+
+
+target = ''
+# use getopt module in future
+for arg in sys.argv:
+    if arg == '-q':
+        VERBOSE = False
+    if target != 'any':
+        # compatiable with "adb -d", redirect commands to usb
+        if arg == '-d':
+            target = 'usb'
+        # compatiable with "adb -e", redirect commands to emulator
+        elif arg == '-e':
+            target = 'local'
+
+if target == '': target ='any'
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+D("connecting")
+try:
+    s.connect(("localhost", 5037))
+except socket.error:
+    print 'Cannot connect to localhost:5037'
+    print socket.error
+    sys.exit(0)
+
+D("connected")
+
+if not communicate("host:transport-%s" % target):
+    sys.exit(1)
+#communicate("host:transport-usb:shell:ls /data")
+communicate("framebuffer:")
+
+data = s.recv(52)
+fbinfo_unpack(data)
+
+t0 = float(time.time())
+save()
+t1 = float(time.time())
+print t1 - t0
+
diff --git a/fb2png/fb.c b/fb2png/fb.c
new file mode 100644
index 0000000..2ed4789
--- /dev/null
+++ b/fb2png/fb.c
@@ -0,0 +1,132 @@
+/*
+ *   -- http://android-fb2png.googlecode.com/svn/trunk/fb.c --
+ *
+ *   Copyright 2011, Kyan He <kyan.ql.he@gmail.com>
+ *
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "log.h"
+#include "fb.h"
+#include "img_process.h"
+
+void fb_dump(const struct fb* fb)
+{
+    D("%12s : %d", "bpp", fb->bpp);
+    D("%12s : %d", "size", fb->size);
+    D("%12s : %d", "width", fb->width);
+    D("%12s : %d", "height", fb->height);
+    D("%12s : %d %d %d %d", "ARGB offset",
+            fb->alpha_offset, fb->red_offset,
+            fb->green_offset, fb->blue_offset);
+    D("%12s : %d %d %d %d", "ARGB length",
+            fb->alpha_length, fb->red_length,
+            fb->green_length, fb->blue_length);
+}
+
+/**
+ * Returns the format of fb.
+ */
+static int fb_get_format(const struct fb *fb)
+{
+    int ao = fb->alpha_offset;
+    int ro = fb->red_offset;
+    int go = fb->green_offset;
+    int bo = fb->blue_offset;
+
+#define FB_FORMAT_UNKNOWN   0
+#define FB_FORMAT_RGB565    1
+#define FB_FORMAT_ARGB8888  2
+#define FB_FORMAT_RGBA8888  3
+#define FB_FORMAT_ABGR8888  4
+#define FB_FORMAT_BGRA8888  5
+
+    /* TODO: use offset */
+    if (fb->bpp == 16)
+        return FB_FORMAT_RGB565;
+
+    /* TODO: validate */
+    if (ao == 0 && ro == 8)
+        return FB_FORMAT_ARGB8888;
+
+    if (ao == 0 && bo == 8)
+        return FB_FORMAT_ABGR8888;
+
+    if (ro == 0)
+        return FB_FORMAT_RGBA8888;
+
+    if (bo == 0)
+        return FB_FORMAT_BGRA8888;
+
+    /* fallback */
+    return FB_FORMAT_UNKNOWN;
+}
+
+int fb_save_png(const struct fb *fb, const char *path)
+{
+    char *rgb_matrix;
+    int ret = -1;
+
+    /* Allocate RGB Matrix. */
+    rgb_matrix = malloc(fb->width * fb->height * 3);
+    if(!rgb_matrix) {
+        free(rgb_matrix);
+        return -1;
+    }
+
+    int fmt = fb_get_format(fb);
+    D("Framebuffer Pixel Format: %d", fmt);
+
+    switch(fmt) {
+        case FB_FORMAT_RGB565:
+            /* emulator use rgb565 */
+            ret = rgb565_to_rgb888(fb->data,
+                    rgb_matrix, fb->width * fb->height);
+            break;
+        case FB_FORMAT_ARGB8888:
+            /* most devices use argb8888 */
+            ret = argb8888_to_rgb888(fb->data,
+                    rgb_matrix, fb->width * fb->height);
+            break;
+        case FB_FORMAT_ABGR8888:
+            ret = abgr8888_to_rgb888(fb->data,
+                    rgb_matrix, fb->width * fb->height);
+            break;
+        case FB_FORMAT_BGRA8888:
+            ret = bgra8888_to_rgb888(fb->data,
+                    rgb_matrix, fb->width * fb->height);
+            break;
+        case FB_FORMAT_RGBA8888:
+            ret = rgba8888_to_rgb888(fb->data,
+                    rgb_matrix, fb->width * fb->height);
+            break;
+        default:
+            D("Unsupported framebuffer type.");
+            break;
+    }
+
+    if (ret != 0)
+        D("Error while processing input image.");
+    else if (0 != (ret = save_png(path, rgb_matrix, fb->width, fb->height)))
+        D("Failed to save in PNG format.");
+
+    free(rgb_matrix);
+    return ret;
+}
diff --git a/fb2png/fb.h b/fb2png/fb.h
new file mode 100644
index 0000000..25922c8
--- /dev/null
+++ b/fb2png/fb.h
@@ -0,0 +1,42 @@
+/*
+ *   -- http://android-fb2png.googlecode.com/svn/trunk/fb.h --
+ *
+ *   Copyright 2011, Kyan He <kyan.ql.he@gmail.com>
+ *
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __FB_H__
+#define __FB_H__
+
+struct fb {
+    unsigned int bpp;
+    unsigned int size;
+    unsigned int width;
+    unsigned int height;
+    unsigned int red_offset;
+    unsigned int red_length;
+    unsigned int blue_offset;
+    unsigned int blue_length;
+    unsigned int green_offset;
+    unsigned int green_length;
+    unsigned int alpha_offset;
+    unsigned int alpha_length;
+    void* data;
+};
+
+int fb_save_png(const struct fb *fb, const char *path);
+
+#endif
diff --git a/fb2png/fb2png-jni.c b/fb2png/fb2png-jni.c
new file mode 100644
index 0000000..d2de33c
--- /dev/null
+++ b/fb2png/fb2png-jni.c
@@ -0,0 +1,30 @@
+/*
+ *   -- http://android-fb2png.googlecode.com/svn/trunk/fb2png-jni.c --
+ *
+ *   Copyright 2011, Kyan He <kyan.ql.he@gmail.com>
+ *
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <jni.h>
+
+#include "fb2png.h"
+
+jint Java_im_kyan_android_graphics_FrameBuffer_captureScreen( JNIEnv *env,
+                                          jobject this,
+                                          jstring path )
+{
+    return fb2png("/data/local/fbdump.png");
+}
diff --git a/fb2png/fb2png.c b/fb2png/fb2png.c
new file mode 100644
index 0000000..a357b7f
--- /dev/null
+++ b/fb2png/fb2png.c
@@ -0,0 +1,121 @@
+/**
+ * fb2png  Save screenshot into .png.
+ *
+ * Copyright (C) 2012  Kyan <kyan.ql.he@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <linux/fb.h>
+#include <errno.h>
+
+#include "log.h"
+#include "fb2png.h"
+#include "fb.h"
+
+/**
+ * Get the {@code struct fb} from device's framebuffer.
+ * Return
+ *      0 for success.
+ */
+int get_device_fb(const char* path, struct fb *fb)
+{
+    int fd;
+    int bytespp;
+    int offset;
+    char *x;
+    struct fb_var_screeninfo vinfo;
+
+    fd = open(path, O_RDONLY);
+    if (fd < 0) return -1;
+
+    if(ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) < 0) {
+        D("ioctl failed, %s\n", strerror(errno));
+        return -1;
+    }
+
+    bytespp = vinfo.bits_per_pixel / 8;
+
+    fb->bpp = vinfo.bits_per_pixel;
+    fb->size = vinfo.xres * vinfo.yres * bytespp;
+    fb->width = vinfo.xres;
+    fb->height = vinfo.yres;
+    fb->red_offset = vinfo.red.offset;
+    fb->red_length = vinfo.red.length;
+    fb->green_offset = vinfo.green.offset;
+    fb->green_length = vinfo.green.length;
+    fb->blue_offset = vinfo.blue.offset;
+    fb->blue_length = vinfo.blue.length;
+    fb->alpha_offset = vinfo.transp.offset;
+    fb->alpha_length = vinfo.transp.length;
+
+#ifdef ANDROID
+    /* HACK: for several of 3d cores a specific alignment
+     * is required so the start of the fb may not be an integer number of lines
+     * from the base.  As a result we are storing the additional offset in
+     * xoffset. This is not the correct usage for xoffset, it should be added
+     * to each line, not just once at the beginning */
+
+    offset = vinfo.xoffset * bytespp;
+
+    /* Android use double-buffer, capture 2nd */
+    offset += vinfo.xres * vinfo.yoffset * bytespp;
+#else
+    offset = 0;
+#endif
+
+    x = malloc(fb->size);
+    if (!x) return -1;
+
+    lseek(fd, offset, SEEK_SET);
+
+    if (read(fd, x ,fb->size) != fb->size) goto oops;
+
+    fb->data = x;
+    close(fd);
+
+    return 0;
+
+oops:
+    close(fd);
+    free(x);
+    return -1;
+}
+
+int fb2png(const char *path)
+{
+    struct fb fb;
+    int ret;
+
+#ifdef ANDROID
+    ret = get_device_fb("/dev/graphics/fb0", &fb);
+#else
+    ret = get_device_fb("/dev/fb0", &fb);
+#endif
+
+    if (ret) {
+        D("Failed to read framebuffer.");
+        return -1;
+    }
+
+    fb_dump(&fb);
+
+    return fb_save_png(&fb, path);
+}
+
diff --git a/fb2png/fb2png.h b/fb2png/fb2png.h
new file mode 100644
index 0000000..c5eea9c
--- /dev/null
+++ b/fb2png/fb2png.h
@@ -0,0 +1,26 @@
+/*
+ *   -- http://android-fb2png.googlecode.com/svn/trunk/fb2png.h --
+ *
+ *   Copyright 2011, Kyan He <kyan.ql.he@gmail.com>
+ *
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __FB2PNG_H__
+#define __FB2PNG_H__
+
+int fb2png(const char *path);
+
+#endif
diff --git a/fb2png/img_process.c b/fb2png/img_process.c
new file mode 100644
index 0000000..eb75e9a
--- /dev/null
+++ b/fb2png/img_process.c
@@ -0,0 +1,254 @@
+/*
+ *   -- http://android-fb2png.googlecode.com/svn/trunk/img_process.c --
+ *
+ *   Copyright 2011, Kyan He <kyan.ql.he@gmail.com>
+ *
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <png.h>
+
+#include "img_process.h"
+#include "log.h"
+
+int rgb565_to_rgb888(const char* src, char* dst, size_t pixel)
+{
+    struct rgb565  *from;
+    struct rgb888  *to;
+
+    from = (struct rgb565 *) src;
+    to = (struct rgb888 *) dst;
+
+    int i = 0;
+    /* traverse pixel of the row */
+    while(i++ < pixel) {
+
+        to->r = from->r;
+        to->g = from->g;
+        to->b = from->b;
+        /* scale */
+        to->r <<= 3;
+        to->g <<= 2;
+        to->b <<= 3;
+
+        to++;
+        from++;
+    }
+
+    return 0;
+}
+
+int argb8888_to_rgb888(const char* src, char* dst, size_t pixel)
+{
+    int i;
+    struct argb8888  *from;
+    struct rgb888  *to;
+
+    from = (struct argb8888 *) src;
+    to = (struct rgb888 *) dst;
+
+    i = 0;
+    /* traverse pixel of the row */
+    while(i++ < pixel) {
+
+        to->r = from->r;
+        to->g = from->g;
+        to->b = from->b;
+
+        to++;
+        from++;
+    }
+
+    return 0;
+}
+
+int abgr8888_to_rgb888(const char* src, char* dst, size_t pixel)
+{
+    int i;
+    struct abgr8888  *from;
+    struct rgb888  *to;
+
+    from = (struct abgr8888 *) src;
+    to = (struct rgb888 *) dst;
+
+    i = 0;
+    /* traverse pixel of the row */
+    while(i++ < pixel) {
+
+        to->r = from->r;
+        to->g = from->g;
+        to->b = from->b;
+
+        to++;
+        from++;
+    }
+
+    return 0;
+}
+
+int bgra8888_to_rgb888(const char* src, char* dst, size_t pixel)
+{
+    int i;
+    struct bgra8888  *from;
+    struct rgb888  *to;
+
+    from = (struct bgra8888 *) src;
+    to = (struct rgb888 *) dst;
+
+    i = 0;
+    /* traverse pixel of the row */
+    while(i++ < pixel) {
+
+        to->r = from->r;
+        to->g = from->g;
+        to->b = from->b;
+
+        to++;
+        from++;
+    }
+
+    return 0;
+}
+
+int rgba8888_to_rgb888(const char* src, char* dst, size_t pixel)
+{
+    int i;
+    struct rgba8888  *from;
+    struct rgb888  *to;
+
+    from = (struct rgba8888 *) src;
+    to = (struct rgb888 *) dst;
+
+    i = 0;
+    /* traverse pixel of the row */
+    while(i++ < pixel) {
+
+        to->r = from->r;
+        to->g = from->g;
+        to->b = from->b;
+
+        to++;
+        from++;
+    }
+
+    return 0;
+}
+
+static void
+stdio_write_func (png_structp png, png_bytep data, png_size_t size)
+{
+    FILE *fp;
+    size_t ret;
+
+    fp = png_get_io_ptr (png);
+    while (size) {
+        ret = fwrite (data, 1, size, fp);
+        size -= ret;
+        data += ret;
+      if (size && ferror (fp))
+         E("write: %m\n");
+   }
+}
+
+static void
+png_simple_output_flush_fn (png_structp png_ptr)
+{
+}
+
+static void
+png_simple_error_callback (png_structp png,
+                       png_const_charp error_msg)
+{
+    E("png error: %s\n", error_msg);
+}
+
+static void
+png_simple_warning_callback (png_structp png,
+                         png_const_charp error_msg)
+{
+    fprintf(stderr, "png warning: %s\n", error_msg);
+}
+
+/* save rgb888 to png format in fp */
+int save_png(const char* path, const char* data, int width, int height)
+{
+    FILE *fp;
+    png_byte **volatile rows;
+    png_struct *png;
+    png_info *info;
+
+    fp = fopen(path, "w");
+    if (!fp) {
+        int errsv = errno;
+        E("Cannot open file %s for writing.\n", path);
+        return errsv;
+    }
+
+    rows = malloc(height * sizeof rows[0]);
+    if (!rows) goto oops;
+
+    int i;
+    for (i = 0; i < height; i++)
+        rows[i] = (png_byte *) data + i * width * 3 /*fb.stride*/;
+
+    png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL,
+                               png_simple_error_callback,
+                               png_simple_warning_callback);
+    if (!png) {
+        E("png_create_write_struct failed\n");
+        goto oops;
+    }
+
+    info = png_create_info_struct (png);
+    if (!info) {
+        E("png_create_info_struct failed\n");
+        png_destroy_write_struct (&png, NULL);
+        goto oops;
+    }
+
+    png_set_write_fn (png, fp, stdio_write_func, png_simple_output_flush_fn);
+    png_set_IHDR (png, info,
+            width,
+            height,
+#define DEPTH 8
+            DEPTH,
+        PNG_COLOR_TYPE_RGB,
+        PNG_INTERLACE_NONE,
+        PNG_COMPRESSION_TYPE_DEFAULT,
+        PNG_FILTER_TYPE_DEFAULT);
+
+    png_color_16 white;
+
+    white.gray = (1 << DEPTH) - 1;
+    white.red = white.blue = white.green = white.gray;
+
+    png_set_bKGD (png, info, &white);
+    png_write_info (png, info);
+
+    png_write_image (png, rows);
+    png_write_end (png, info);
+
+    png_destroy_write_struct (&png, &info);
+
+    fclose(fp);
+    free (rows);
+    return 0;
+
+oops:
+    fclose(fp);
+    free (rows);
+    return -1;
+}
diff --git a/fb2png/img_process.h b/fb2png/img_process.h
new file mode 100644
index 0000000..871cfc9
--- /dev/null
+++ b/fb2png/img_process.h
@@ -0,0 +1,84 @@
+/*
+ *   -- http://android-fb2png.googlecode.com/svn/trunk/img_process.h --
+ *
+ *   Copyright 2011, Kyan He <kyan.ql.he@gmail.com>
+ *
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IMG_PROCESS_H__
+#define __IMG_PROCESS_H__
+
+/**
+ * rgba8888 is found on Desire HD Linux localhost 2.6.35.10-g931a37e #1
+ *      PREEMPT Wed Nov 9 14:04:03 CST 2011 armv7l GNU/Linux
+ */
+
+
+typedef struct rgb888 {
+        char r;
+        char g;
+        char b;
+} rgb888_t;
+
+typedef rgb888_t rgb24_t;
+
+typedef struct argb8888 {
+        char a;
+        char r;
+        char g;
+        char b;
+} argb8888_t;
+
+typedef struct abgr8888 {
+        char a;
+        char b;
+        char g;
+        char r;
+} abgr8888_t;
+
+typedef struct bgra8888 {
+        char b;
+        char g;
+        char r;
+        char a;
+} bgra8888_t;
+
+typedef struct rgba8888 {
+        char r;
+        char g;
+        char b;
+        char a;
+} rgba8888_t;
+
+typedef struct rgb565 {
+        short b:5;
+        short g:6;
+        short r:5;
+} rgb565_t;
+
+int rgb565_to_rgb888(const char* src, char* dst, size_t pixel);
+
+int argb8888_to_rgb888(const char* src, char* dst, size_t pixel);
+
+int abgr8888_to_rgb888(const char* src, char* dst, size_t pixel);
+
+int bgra8888_to_rgb888(const char* src, char* dst, size_t pixel);
+
+int rgba8888_to_rgb888(const char* src, char* dst, size_t pixel);
+
+int save_png(const char* path, const char* data, int width, int height);
+
+#endif
diff --git a/fb2png/jni/Application.mk b/fb2png/jni/Application.mk
new file mode 100644
index 0000000..a720a09
--- /dev/null
+++ b/fb2png/jni/Application.mk
@@ -0,0 +1 @@
+APP_BUILD_SCRIPT := $(call my-dir)/../Android.mk
diff --git a/fb2png/libpng/include/png.h b/fb2png/libpng/include/png.h
new file mode 100644
index 0000000..0ad09eb
--- /dev/null
+++ b/fb2png/libpng/include/png.h
@@ -0,0 +1,3301 @@
+
+/* png.h - header file for PNG reference library
+ *
+ * libpng version 1.6.1 - March 28, 2013
+ * Copyright (c) 1998-2013 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license (See LICENSE, below)
+ *
+ * Authors and maintainers:
+ *   libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
+ *   libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
+ *   libpng versions 0.97, January 1998, through 1.6.1 - March 28, 2013: Glenn
+ *   See also "Contributing Authors", below.
+ *
+ * Note about libpng version numbers:
+ *
+ *   Due to various miscommunications, unforeseen code incompatibilities
+ *   and occasional factors outside the authors' control, version numbering
+ *   on the library has not always been consistent and straightforward.
+ *   The following table summarizes matters since version 0.89c, which was
+ *   the first widely used release:
+ *
+ *    source                 png.h  png.h  shared-lib
+ *    version                string   int  version
+ *    -------                ------ -----  ----------
+ *    0.89c "1.0 beta 3"     0.89      89  1.0.89
+ *    0.90  "1.0 beta 4"     0.90      90  0.90  [should have been 2.0.90]
+ *    0.95  "1.0 beta 5"     0.95      95  0.95  [should have been 2.0.95]
+ *    0.96  "1.0 beta 6"     0.96      96  0.96  [should have been 2.0.96]
+ *    0.97b "1.00.97 beta 7" 1.00.97   97  1.0.1 [should have been 2.0.97]
+ *    0.97c                  0.97      97  2.0.97
+ *    0.98                   0.98      98  2.0.98
+ *    0.99                   0.99      98  2.0.99
+ *    0.99a-m                0.99      99  2.0.99
+ *    1.00                   1.00     100  2.1.0 [100 should be 10000]
+ *    1.0.0      (from here on, the   100  2.1.0 [100 should be 10000]
+ *    1.0.1       png.h string is   10001  2.1.0
+ *    1.0.1a-e    identical to the  10002  from here on, the shared library
+ *    1.0.2       source version)   10002  is 2.V where V is the source code
+ *    1.0.2a-b                      10003  version, except as noted.
+ *    1.0.3                         10003
+ *    1.0.3a-d                      10004
+ *    1.0.4                         10004
+ *    1.0.4a-f                      10005
+ *    1.0.5 (+ 2 patches)           10005
+ *    1.0.5a-d                      10006
+ *    1.0.5e-r                      10100 (not source compatible)
+ *    1.0.5s-v                      10006 (not binary compatible)
+ *    1.0.6 (+ 3 patches)           10006 (still binary incompatible)
+ *    1.0.6d-f                      10007 (still binary incompatible)
+ *    1.0.6g                        10007
+ *    1.0.6h                        10007  10.6h (testing xy.z so-numbering)
+ *    1.0.6i                        10007  10.6i
+ *    1.0.6j                        10007  2.1.0.6j (incompatible with 1.0.0)
+ *    1.0.7beta11-14        DLLNUM  10007  2.1.0.7beta11-14 (binary compatible)
+ *    1.0.7beta15-18           1    10007  2.1.0.7beta15-18 (binary compatible)
+ *    1.0.7rc1-2               1    10007  2.1.0.7rc1-2 (binary compatible)
+ *    1.0.7                    1    10007  (still compatible)
+ *    1.0.8beta1-4             1    10008  2.1.0.8beta1-4
+ *    1.0.8rc1                 1    10008  2.1.0.8rc1
+ *    1.0.8                    1    10008  2.1.0.8
+ *    1.0.9beta1-6             1    10009  2.1.0.9beta1-6
+ *    1.0.9rc1                 1    10009  2.1.0.9rc1
+ *    1.0.9beta7-10            1    10009  2.1.0.9beta7-10
+ *    1.0.9rc2                 1    10009  2.1.0.9rc2
+ *    1.0.9                    1    10009  2.1.0.9
+ *    1.0.10beta1              1    10010  2.1.0.10beta1
+ *    1.0.10rc1                1    10010  2.1.0.10rc1
+ *    1.0.10                   1    10010  2.1.0.10
+ *    1.0.11beta1-3            1    10011  2.1.0.11beta1-3
+ *    1.0.11rc1                1    10011  2.1.0.11rc1
+ *    1.0.11                   1    10011  2.1.0.11
+ *    1.0.12beta1-2            2    10012  2.1.0.12beta1-2
+ *    1.0.12rc1                2    10012  2.1.0.12rc1
+ *    1.0.12                   2    10012  2.1.0.12
+ *    1.1.0a-f                 -    10100  2.1.1.0a-f (branch abandoned)
+ *    1.2.0beta1-2             2    10200  2.1.2.0beta1-2
+ *    1.2.0beta3-5             3    10200  3.1.2.0beta3-5
+ *    1.2.0rc1                 3    10200  3.1.2.0rc1
+ *    1.2.0                    3    10200  3.1.2.0
+ *    1.2.1beta1-4             3    10201  3.1.2.1beta1-4
+ *    1.2.1rc1-2               3    10201  3.1.2.1rc1-2
+ *    1.2.1                    3    10201  3.1.2.1
+ *    1.2.2beta1-6            12    10202  12.so.0.1.2.2beta1-6
+ *    1.0.13beta1             10    10013  10.so.0.1.0.13beta1
+ *    1.0.13rc1               10    10013  10.so.0.1.0.13rc1
+ *    1.2.2rc1                12    10202  12.so.0.1.2.2rc1
+ *    1.0.13                  10    10013  10.so.0.1.0.13
+ *    1.2.2                   12    10202  12.so.0.1.2.2
+ *    1.2.3rc1-6              12    10203  12.so.0.1.2.3rc1-6
+ *    1.2.3                   12    10203  12.so.0.1.2.3
+ *    1.2.4beta1-3            13    10204  12.so.0.1.2.4beta1-3
+ *    1.0.14rc1               13    10014  10.so.0.1.0.14rc1
+ *    1.2.4rc1                13    10204  12.so.0.1.2.4rc1
+ *    1.0.14                  10    10014  10.so.0.1.0.14
+ *    1.2.4                   13    10204  12.so.0.1.2.4
+ *    1.2.5beta1-2            13    10205  12.so.0.1.2.5beta1-2
+ *    1.0.15rc1-3             10    10015  10.so.0.1.0.15rc1-3
+ *    1.2.5rc1-3              13    10205  12.so.0.1.2.5rc1-3
+ *    1.0.15                  10    10015  10.so.0.1.0.15
+ *    1.2.5                   13    10205  12.so.0.1.2.5
+ *    1.2.6beta1-4            13    10206  12.so.0.1.2.6beta1-4
+ *    1.0.16                  10    10016  10.so.0.1.0.16
+ *    1.2.6                   13    10206  12.so.0.1.2.6
+ *    1.2.7beta1-2            13    10207  12.so.0.1.2.7beta1-2
+ *    1.0.17rc1               10    10017  12.so.0.1.0.17rc1
+ *    1.2.7rc1                13    10207  12.so.0.1.2.7rc1
+ *    1.0.17                  10    10017  12.so.0.1.0.17
+ *    1.2.7                   13    10207  12.so.0.1.2.7
+ *    1.2.8beta1-5            13    10208  12.so.0.1.2.8beta1-5
+ *    1.0.18rc1-5             10    10018  12.so.0.1.0.18rc1-5
+ *    1.2.8rc1-5              13    10208  12.so.0.1.2.8rc1-5
+ *    1.0.18                  10    10018  12.so.0.1.0.18
+ *    1.2.8                   13    10208  12.so.0.1.2.8
+ *    1.2.9beta1-3            13    10209  12.so.0.1.2.9beta1-3
+ *    1.2.9beta4-11           13    10209  12.so.0.9[.0]
+ *    1.2.9rc1                13    10209  12.so.0.9[.0]
+ *    1.2.9                   13    10209  12.so.0.9[.0]
+ *    1.2.10beta1-7           13    10210  12.so.0.10[.0]
+ *    1.2.10rc1-2             13    10210  12.so.0.10[.0]
+ *    1.2.10                  13    10210  12.so.0.10[.0]
+ *    1.4.0beta1-5            14    10400  14.so.0.0[.0]
+ *    1.2.11beta1-4           13    10211  12.so.0.11[.0]
+ *    1.4.0beta7-8            14    10400  14.so.0.0[.0]
+ *    1.2.11                  13    10211  12.so.0.11[.0]
+ *    1.2.12                  13    10212  12.so.0.12[.0]
+ *    1.4.0beta9-14           14    10400  14.so.0.0[.0]
+ *    1.2.13                  13    10213  12.so.0.13[.0]
+ *    1.4.0beta15-36          14    10400  14.so.0.0[.0]
+ *    1.4.0beta37-87          14    10400  14.so.14.0[.0]
+ *    1.4.0rc01               14    10400  14.so.14.0[.0]
+ *    1.4.0beta88-109         14    10400  14.so.14.0[.0]
+ *    1.4.0rc02-08            14    10400  14.so.14.0[.0]
+ *    1.4.0                   14    10400  14.so.14.0[.0]
+ *    1.4.1beta01-03          14    10401  14.so.14.1[.0]
+ *    1.4.1rc01               14    10401  14.so.14.1[.0]
+ *    1.4.1beta04-12          14    10401  14.so.14.1[.0]
+ *    1.4.1                   14    10401  14.so.14.1[.0]
+ *    1.4.2                   14    10402  14.so.14.2[.0]
+ *    1.4.3                   14    10403  14.so.14.3[.0]
+ *    1.4.4                   14    10404  14.so.14.4[.0]
+ *    1.5.0beta01-58          15    10500  15.so.15.0[.0]
+ *    1.5.0rc01-07            15    10500  15.so.15.0[.0]
+ *    1.5.0                   15    10500  15.so.15.0[.0]
+ *    1.5.1beta01-11          15    10501  15.so.15.1[.0]
+ *    1.5.1rc01-02            15    10501  15.so.15.1[.0]
+ *    1.5.1                   15    10501  15.so.15.1[.0]
+ *    1.5.2beta01-03          15    10502  15.so.15.2[.0]
+ *    1.5.2rc01-03            15    10502  15.so.15.2[.0]
+ *    1.5.2                   15    10502  15.so.15.2[.0]
+ *    1.5.3beta01-10          15    10503  15.so.15.3[.0]
+ *    1.5.3rc01-02            15    10503  15.so.15.3[.0]
+ *    1.5.3beta11             15    10503  15.so.15.3[.0]
+ *    1.5.3 [omitted]
+ *    1.5.4beta01-08          15    10504  15.so.15.4[.0]
+ *    1.5.4rc01               15    10504  15.so.15.4[.0]
+ *    1.5.4                   15    10504  15.so.15.4[.0]
+ *    1.5.5beta01-08          15    10505  15.so.15.5[.0]
+ *    1.5.5rc01               15    10505  15.so.15.5[.0]
+ *    1.5.5                   15    10505  15.so.15.5[.0]
+ *    1.5.6beta01-07          15    10506  15.so.15.6[.0]
+ *    1.5.6rc01-03            15    10506  15.so.15.6[.0]
+ *    1.5.6                   15    10506  15.so.15.6[.0]
+ *    1.5.7beta01-05          15    10507  15.so.15.7[.0]
+ *    1.5.7rc01-03            15    10507  15.so.15.7[.0]
+ *    1.5.7                   15    10507  15.so.15.7[.0]
+ *    1.6.0beta01-40          16    10600  16.so.16.0[.0]
+ *    1.6.0rc01-08            16    10600  16.so.16.0[.0]
+ *    1.6.0                   16    10600  16.so.16.0[.0]
+ *    1.6.1beta01-10          16    10601  16.so.16.1[.0]
+ *    1.6.1rc01               16    10601  16.so.16.1[.0]
+ *    1.6.1                   16    10601  16.so.16.1[.0]
+ *
+ *   Henceforth the source version will match the shared-library major
+ *   and minor numbers; the shared-library major version number will be
+ *   used for changes in backward compatibility, as it is intended.  The
+ *   PNG_LIBPNG_VER macro, which is not used within libpng but is available
+ *   for applications, is an unsigned integer of the form xyyzz corresponding
+ *   to the source version x.y.z (leading zeros in y and z).  Beta versions
+ *   were given the previous public release number plus a letter, until
+ *   version 1.0.6j; from then on they were given the upcoming public
+ *   release number plus "betaNN" or "rcNN".
+ *
+ *   Binary incompatibility exists only when applications make direct access
+ *   to the info_ptr or png_ptr members through png.h, and the compiled
+ *   application is loaded with a different version of the library.
+ *
+ *   DLLNUM will change each time there are forward or backward changes
+ *   in binary compatibility (e.g., when a new feature is added).
+ *
+ * See libpng-manual.txt or libpng.3 for more information.  The PNG
+ * specification is available as a W3C Recommendation and as an ISO
+ * Specification, <http://www.w3.org/TR/2003/REC-PNG-20031110/
+ */
+
+/*
+ * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+ *
+ * If you modify libpng you may insert additional notices immediately following
+ * this sentence.
+ *
+ * This code is released under the libpng license.
+ *
+ * libpng versions 1.2.6, August 15, 2004, through 1.6.1, March 28, 2013, are
+ * Copyright (c) 2004, 2006-2013 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-1.2.5
+ * with the following individual added to the list of Contributing Authors:
+ *
+ *    Cosmin Truta
+ *
+ * libpng versions 1.0.7, July 1, 2000, through 1.2.5, October 3, 2002, are
+ * Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-1.0.6
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ *    Simon-Pierre Cadieux
+ *    Eric S. Raymond
+ *    Gilles Vollant
+ *
+ * and with the following additions to the disclaimer:
+ *
+ *    There is no warranty against interference with your enjoyment of the
+ *    library or against infringement.  There is no warranty that our
+ *    efforts or the library will fulfill any of your particular purposes
+ *    or needs.  This library is provided with all faults, and the entire
+ *    risk of satisfactory quality, performance, accuracy, and effort is with
+ *    the user.
+ *
+ * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+ * Copyright (c) 1998, 1999, 2000 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-0.96,
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ *    Tom Lane
+ *    Glenn Randers-Pehrson
+ *    Willem van Schaik
+ *
+ * libpng versions 0.89, June 1996, through 0.96, May 1997, are
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Distributed according to the same disclaimer and license as libpng-0.88,
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ *    John Bowler
+ *    Kevin Bracey
+ *    Sam Bushell
+ *    Magnus Holmgren
+ *    Greg Roelofs
+ *    Tom Tanner
+ *
+ * libpng versions 0.5, May 1995, through 0.88, January 1996, are
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ *
+ * For the purposes of this copyright and license, "Contributing Authors"
+ * is defined as the following set of individuals:
+ *
+ *    Andreas Dilger
+ *    Dave Martindale
+ *    Guy Eric Schalnat
+ *    Paul Schmidt
+ *    Tim Wegner
+ *
+ * The PNG Reference Library is supplied "AS IS".  The Contributing Authors
+ * and Group 42, Inc. disclaim all warranties, expressed or implied,
+ * including, without limitation, the warranties of merchantability and of
+ * fitness for any purpose.  The Contributing Authors and Group 42, Inc.
+ * assume no liability for direct, indirect, incidental, special, exemplary,
+ * or consequential damages, which may result from the use of the PNG
+ * Reference Library, even if advised of the possibility of such damage.
+ *
+ * Permission is hereby granted to use, copy, modify, and distribute this
+ * source code, or portions hereof, for any purpose, without fee, subject
+ * to the following restrictions:
+ *
+ *   1. The origin of this source code must not be misrepresented.
+ *
+ *   2. Altered versions must be plainly marked as such and must not
+ *      be misrepresented as being the original source.
+ *
+ *   3. This Copyright notice may not be removed or altered from
+ *      any source or altered source distribution.
+ *
+ * The Contributing Authors and Group 42, Inc. specifically permit, without
+ * fee, and encourage the use of this source code as a component to
+ * supporting the PNG file format in commercial products.  If you use this
+ * source code in a product, acknowledgment is not required but would be
+ * appreciated.
+ */
+
+/*
+ * A "png_get_copyright" function is available, for convenient use in "about"
+ * boxes and the like:
+ *
+ *     printf("%s", png_get_copyright(NULL));
+ *
+ * Also, the PNG logo (in PNG format, of course) is supplied in the
+ * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
+ */
+
+/*
+ * Libpng is OSI Certified Open Source Software.  OSI Certified is a
+ * certification mark of the Open Source Initiative.
+ */
+
+/*
+ * The contributing authors would like to thank all those who helped
+ * with testing, bug fixes, and patience.  This wouldn't have been
+ * possible without all of you.
+ *
+ * Thanks to Frank J. T. Wojcik for helping with the documentation.
+ */
+
+/*
+ * Y2K compliance in libpng:
+ * =========================
+ *
+ *    March 28, 2013
+ *
+ *    Since the PNG Development group is an ad-hoc body, we can't make
+ *    an official declaration.
+ *
+ *    This is your unofficial assurance that libpng from version 0.71 and
+ *    upward through 1.6.1 are Y2K compliant.  It is my belief that
+ *    earlier versions were also Y2K compliant.
+ *
+ *    Libpng only has two year fields.  One is a 2-byte unsigned integer
+ *    that will hold years up to 65535.  The other, which is deprecated,
+ *    holds the date in text format, and will hold years up to 9999.
+ *
+ *    The integer is
+ *        "png_uint_16 year" in png_time_struct.
+ *
+ *    The string is
+ *        "char time_buffer[29]" in png_struct.  This is no longer used
+ *    in libpng-1.6.x and will be removed from libpng-1.7.0.
+ *
+ *    There are seven time-related functions:
+ *        png.c: png_convert_to_rfc_1123_buffer() in png.c
+ *          (formerly png_convert_to_rfc_1123() prior to libpng-1.5.x and
+ *          png_convert_to_rfc_1152() in error prior to libpng-0.98)
+ *        png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c
+ *        png_convert_from_time_t() in pngwrite.c
+ *        png_get_tIME() in pngget.c
+ *        png_handle_tIME() in pngrutil.c, called in pngread.c
+ *        png_set_tIME() in pngset.c
+ *        png_write_tIME() in pngwutil.c, called in pngwrite.c
+ *
+ *    All handle dates properly in a Y2K environment.  The
+ *    png_convert_from_time_t() function calls gmtime() to convert from system
+ *    clock time, which returns (year - 1900), which we properly convert to
+ *    the full 4-digit year.  There is a possibility that libpng applications
+ *    are not passing 4-digit years into the png_convert_to_rfc_1123_buffer()
+ *    function, or that they are incorrectly passing only a 2-digit year
+ *    instead of "year - 1900" into the png_convert_from_struct_tm() function,
+ *    but this is not under our control.  The libpng documentation has always
+ *    stated that it works with 4-digit years, and the APIs have been
+ *    documented as such.
+ *
+ *    The tIME chunk itself is also Y2K compliant.  It uses a 2-byte unsigned
+ *    integer to hold the year, and can hold years as large as 65535.
+ *
+ *    zlib, upon which libpng depends, is also Y2K compliant.  It contains
+ *    no date-related code.
+ *
+ *       Glenn Randers-Pehrson
+ *       libpng maintainer
+ *       PNG Development Group
+ */
+
+#ifndef PNG_H
+#define PNG_H
+
+/* This is not the place to learn how to use libpng. The file libpng-manual.txt
+ * describes how to use libpng, and the file example.c summarizes it
+ * with some code on which to build.  This file is useful for looking
+ * at the actual function definitions and structure components.
+ *
+ * If you just need to read a PNG file and don't want to read the documentation
+ * skip to the end of this file and read the section entitled 'simplified API'.
+ */
+
+/* Version information for png.h - this should match the version in png.c */
+#define PNG_LIBPNG_VER_STRING "1.6.1"
+#define PNG_HEADER_VERSION_STRING \
+     " libpng version 1.6.1 - March 28, 2013\n"
+
+#define PNG_LIBPNG_VER_SONUM   16
+#define PNG_LIBPNG_VER_DLLNUM  16
+
+/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
+#define PNG_LIBPNG_VER_MAJOR   1
+#define PNG_LIBPNG_VER_MINOR   6
+#define PNG_LIBPNG_VER_RELEASE 1
+
+/* This should match the numeric part of the final component of
+ * PNG_LIBPNG_VER_STRING, omitting any leading zero:
+ */
+
+#define PNG_LIBPNG_VER_BUILD  0
+
+/* Release Status */
+#define PNG_LIBPNG_BUILD_ALPHA    1
+#define PNG_LIBPNG_BUILD_BETA     2
+#define PNG_LIBPNG_BUILD_RC       3
+#define PNG_LIBPNG_BUILD_STABLE   4
+#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7
+
+/* Release-Specific Flags */
+#define PNG_LIBPNG_BUILD_PATCH    8 /* Can be OR'ed with
+                                       PNG_LIBPNG_BUILD_STABLE only */
+#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with
+                                       PNG_LIBPNG_BUILD_SPECIAL */
+#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with
+                                       PNG_LIBPNG_BUILD_PRIVATE */
+
+#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE
+
+/* Careful here.  At one time, Guy wanted to use 082, but that would be octal.
+ * We must not include leading zeros.
+ * Versions 0.7 through 1.0.0 were in the range 0 to 100 here (only
+ * version 1.0.0 was mis-numbered 100 instead of 10000).  From
+ * version 1.0.1 it's    xxyyzz, where x=major, y=minor, z=release
+ */
+#define PNG_LIBPNG_VER 10601 /* 1.6.1 */
+
+/* Library configuration: these options cannot be changed after
+ * the library has been built.
+ */
+#ifndef PNGLCONF_H
+    /* If pnglibconf.h is missing, you can
+     * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h
+     */
+#   include "pnglibconf.h"
+#endif
+
+#ifndef PNG_VERSION_INFO_ONLY
+   /* Machine specific configuration. */
+#  include "pngconf.h"
+#endif
+
+/*
+ * Added at libpng-1.2.8
+ *
+ * Ref MSDN: Private as priority over Special
+ * VS_FF_PRIVATEBUILD File *was not* built using standard release
+ * procedures. If this value is given, the StringFileInfo block must
+ * contain a PrivateBuild string.
+ *
+ * VS_FF_SPECIALBUILD File *was* built by the original company using
+ * standard release procedures but is a variation of the standard
+ * file of the same version number. If this value is given, the
+ * StringFileInfo block must contain a SpecialBuild string.
+ */
+
+#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */
+#  define PNG_LIBPNG_BUILD_TYPE \
+       (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE)
+#else
+#  ifdef PNG_LIBPNG_SPECIALBUILD
+#    define PNG_LIBPNG_BUILD_TYPE \
+         (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL)
+#  else
+#    define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE)
+#  endif
+#endif
+
+#ifndef PNG_VERSION_INFO_ONLY
+
+/* Inhibit C++ name-mangling for libpng functions but not for system calls. */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Version information for C files, stored in png.c.  This had better match
+ * the version above.
+ */
+#define png_libpng_ver png_get_header_ver(NULL)
+
+/* This file is arranged in several sections:
+ *
+ * 1. Any configuration options that can be specified by for the application
+ *    code when it is built.  (Build time configuration is in pnglibconf.h)
+ * 2. Type definitions (base types are defined in pngconf.h), structure
+ *    definitions.
+ * 3. Exported library functions.
+ * 4. Simplified API.
+ *
+ * The library source code has additional files (principally pngpriv.h) that
+ * allow configuration of the library.
+ */
+/* Section 1: run time configuration
+ * See pnglibconf.h for build time configuration
+ *
+ * Run time configuration allows the application to choose between
+ * implementations of certain arithmetic APIs.  The default is set
+ * at build time and recorded in pnglibconf.h, but it is safe to
+ * override these (and only these) settings.  Note that this won't
+ * change what the library does, only application code, and the
+ * settings can (and probably should) be made on a per-file basis
+ * by setting the #defines before including png.h
+ *
+ * Use macros to read integers from PNG data or use the exported
+ * functions?
+ *   PNG_USE_READ_MACROS: use the macros (see below)  Note that
+ *     the macros evaluate their argument multiple times.
+ *   PNG_NO_USE_READ_MACROS: call the relevant library function.
+ *
+ * Use the alternative algorithm for compositing alpha samples that
+ * does not use division?
+ *   PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division'
+ *      algorithm.
+ *   PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm.
+ *
+ * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is
+ * false?
+ *   PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error
+ *      APIs to png_warning.
+ * Otherwise the calls are mapped to png_error.
+ */
+
+/* Section 2: type definitions, including structures and compile time
+ * constants.
+ * See pngconf.h for base types that vary by machine/system
+ */
+
+/* This triggers a compiler error in png.c, if png.c and png.h
+ * do not agree upon the version number.
+ */
+typedef char* png_libpng_version_1_6_1;
+
+/* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
+ *
+ * png_struct is the cache of information used while reading or writing a single
+ * PNG file.  One of these is always required, although the simplified API
+ * (below) hides the creation and destruction of it.
+ */
+typedef struct png_struct_def png_struct;
+typedef const png_struct * png_const_structp;
+typedef png_struct * png_structp;
+typedef png_struct * * png_structpp;
+
+/* png_info contains information read from or to be written to a PNG file.  One
+ * or more of these must exist while reading or creating a PNG file.  The
+ * information is not used by libpng during read but is used to control what
+ * gets written when a PNG file is created.  "png_get_" function calls read
+ * information during read and "png_set_" functions calls write information
+ * when creating a PNG.
+ * been moved into a separate header file that is not accessible to
+ * applications.  Read libpng-manual.txt or libpng.3 for more info.
+ */
+typedef struct png_info_def png_info;
+typedef png_info * png_infop;
+typedef const png_info * png_const_infop;
+typedef png_info * * png_infopp;
+
+/* Types with names ending 'p' are pointer types.  The corresponding types with
+ * names ending 'rp' are identical pointer types except that the pointer is
+ * marked 'restrict', which means that it is the only pointer to the object
+ * passed to the function.  Applications should not use the 'restrict' types;
+ * it is always valid to pass 'p' to a pointer with a function argument of the
+ * corresponding 'rp' type.  Different compilers have different rules with
+ * regard to type matching in the presence of 'restrict'.  For backward
+ * compatibility libpng callbacks never have 'restrict' in their parameters and,
+ * consequentially, writing portable application code is extremely difficult if
+ * an attempt is made to use 'restrict'.
+ */
+typedef png_struct * PNG_RESTRICT png_structrp;
+typedef const png_struct * PNG_RESTRICT png_const_structrp;
+typedef png_info * PNG_RESTRICT png_inforp;
+typedef const png_info * PNG_RESTRICT png_const_inforp;
+
+/* Three color definitions.  The order of the red, green, and blue, (and the
+ * exact size) is not important, although the size of the fields need to
+ * be png_byte or png_uint_16 (as defined below).
+ */
+typedef struct png_color_struct
+{
+   png_byte red;
+   png_byte green;
+   png_byte blue;
+} png_color;
+typedef png_color * png_colorp;
+typedef const png_color * png_const_colorp;
+typedef png_color * * png_colorpp;
+
+typedef struct png_color_16_struct
+{
+   png_byte index;    /* used for palette files */
+   png_uint_16 red;   /* for use in red green blue files */
+   png_uint_16 green;
+   png_uint_16 blue;
+   png_uint_16 gray;  /* for use in grayscale files */
+} png_color_16;
+typedef png_color_16 * png_color_16p;
+typedef const png_color_16 * png_const_color_16p;
+typedef png_color_16 * * png_color_16pp;
+
+typedef struct png_color_8_struct
+{
+   png_byte red;   /* for use in red green blue files */
+   png_byte green;
+   png_byte blue;
+   png_byte gray;  /* for use in grayscale files */
+   png_byte alpha; /* for alpha channel files */
+} png_color_8;
+typedef png_color_8 * png_color_8p;
+typedef const png_color_8 * png_const_color_8p;
+typedef png_color_8 * * png_color_8pp;
+
+/*
+ * The following two structures are used for the in-core representation
+ * of sPLT chunks.
+ */
+typedef struct png_sPLT_entry_struct
+{
+   png_uint_16 red;
+   png_uint_16 green;
+   png_uint_16 blue;
+   png_uint_16 alpha;
+   png_uint_16 frequency;
+} png_sPLT_entry;
+typedef png_sPLT_entry * png_sPLT_entryp;
+typedef const png_sPLT_entry * png_const_sPLT_entryp;
+typedef png_sPLT_entry * * png_sPLT_entrypp;
+
+/*  When the depth of the sPLT palette is 8 bits, the color and alpha samples
+ *  occupy the LSB of their respective members, and the MSB of each member
+ *  is zero-filled.  The frequency member always occupies the full 16 bits.
+ */
+
+typedef struct png_sPLT_struct
+{
+   png_charp name;           /* palette name */
+   png_byte depth;           /* depth of palette samples */
+   png_sPLT_entryp entries;  /* palette entries */
+   png_int_32 nentries;      /* number of palette entries */
+} png_sPLT_t;
+typedef png_sPLT_t * png_sPLT_tp;
+typedef const png_sPLT_t * png_const_sPLT_tp;
+typedef png_sPLT_t * * png_sPLT_tpp;
+
+#ifdef PNG_TEXT_SUPPORTED
+/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file,
+ * and whether that contents is compressed or not.  The "key" field
+ * points to a regular zero-terminated C string.  The "text" fields can be a
+ * regular C string, an empty string, or a NULL pointer.
+ * However, the structure returned by png_get_text() will always contain
+ * the "text" field as a regular zero-terminated C string (possibly
+ * empty), never a NULL pointer, so it can be safely used in printf() and
+ * other string-handling functions.  Note that the "itxt_length", "lang", and
+ * "lang_key" members of the structure only exist when the library is built
+ * with iTXt chunk support.  Prior to libpng-1.4.0 the library was built by
+ * default without iTXt support. Also note that when iTXt *is* supported,
+ * the "lang" and "lang_key" fields contain NULL pointers when the
+ * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or
+ * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the
+ * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag"
+ * which is always 0 or 1, or its "compression method" which is always 0.
+ */
+typedef struct png_text_struct
+{
+   int  compression;       /* compression value:
+                             -1: tEXt, none
+                              0: zTXt, deflate
+                              1: iTXt, none
+                              2: iTXt, deflate  */
+   png_charp key;          /* keyword, 1-79 character description of "text" */
+   png_charp text;         /* comment, may be an empty string (ie "")
+                              or a NULL pointer */
+   png_size_t text_length; /* length of the text string */
+   png_size_t itxt_length; /* length of the itxt string */
+   png_charp lang;         /* language code, 0-79 characters
+                              or a NULL pointer */
+   png_charp lang_key;     /* keyword translated UTF-8 string, 0 or more
+                              chars or a NULL pointer */
+} png_text;
+typedef png_text * png_textp;
+typedef const png_text * png_const_textp;
+typedef png_text * * png_textpp;
+#endif
+
+/* Supported compression types for text in PNG files (tEXt, and zTXt).
+ * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */
+#define PNG_TEXT_COMPRESSION_NONE_WR -3
+#define PNG_TEXT_COMPRESSION_zTXt_WR -2
+#define PNG_TEXT_COMPRESSION_NONE    -1
+#define PNG_TEXT_COMPRESSION_zTXt     0
+#define PNG_ITXT_COMPRESSION_NONE     1
+#define PNG_ITXT_COMPRESSION_zTXt     2
+#define PNG_TEXT_COMPRESSION_LAST     3  /* Not a valid value */
+
+/* png_time is a way to hold the time in an machine independent way.
+ * Two conversions are provided, both from time_t and struct tm.  There
+ * is no portable way to convert to either of these structures, as far
+ * as I know.  If you know of a portable way, send it to me.  As a side
+ * note - PNG has always been Year 2000 compliant!
+ */
+typedef struct png_time_struct
+{
+   png_uint_16 year; /* full year, as in, 1995 */
+   png_byte month;   /* month of year, 1 - 12 */
+   png_byte day;     /* day of month, 1 - 31 */
+   png_byte hour;    /* hour of day, 0 - 23 */
+   png_byte minute;  /* minute of hour, 0 - 59 */
+   png_byte second;  /* second of minute, 0 - 60 (for leap seconds) */
+} png_time;
+typedef png_time * png_timep;
+typedef const png_time * png_const_timep;
+typedef png_time * * png_timepp;
+
+#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+/* png_unknown_chunk is a structure to hold queued chunks for which there is
+ * no specific support.  The idea is that we can use this to queue
+ * up private chunks for output even though the library doesn't actually
+ * know about their semantics.
+ *
+ * The data in the structure is set by libpng on read and used on write.
+ */
+typedef struct png_unknown_chunk_t
+{
+    png_byte name[5]; /* Textual chunk name with '\0' terminator */
+    png_byte *data;   /* Data, should not be modified on read! */
+    png_size_t size;
+
+    /* On write 'location' must be set using the flag values listed below.
+     * Notice that on read it is set by libpng however the values stored have
+     * more bits set than are listed below.  Always treat the value as a
+     * bitmask.  On write set only one bit - setting multiple bits may cause the
+     * chunk to be written in multiple places.
+     */
+    png_byte location; /* mode of operation at read time */
+}
+png_unknown_chunk;
+
+typedef png_unknown_chunk * png_unknown_chunkp;
+typedef const png_unknown_chunk * png_const_unknown_chunkp;
+typedef png_unknown_chunk * * png_unknown_chunkpp;
+#endif
+
+/* Flag values for the unknown chunk location byte. */
+#define PNG_HAVE_IHDR  0x01
+#define PNG_HAVE_PLTE  0x02
+#define PNG_AFTER_IDAT 0x08
+
+/* Maximum positive integer used in PNG is (2^31)-1 */
+#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL)
+#define PNG_UINT_32_MAX ((png_uint_32)(-1))
+#define PNG_SIZE_MAX ((png_size_t)(-1))
+
+/* These are constants for fixed point values encoded in the
+ * PNG specification manner (x100000)
+ */
+#define PNG_FP_1    100000
+#define PNG_FP_HALF  50000
+#define PNG_FP_MAX  ((png_fixed_point)0x7fffffffL)
+#define PNG_FP_MIN  (-PNG_FP_MAX)
+
+/* These describe the color_type field in png_info. */
+/* color type masks */
+#define PNG_COLOR_MASK_PALETTE    1
+#define PNG_COLOR_MASK_COLOR      2
+#define PNG_COLOR_MASK_ALPHA      4
+
+/* color types.  Note that not all combinations are legal */
+#define PNG_COLOR_TYPE_GRAY 0
+#define PNG_COLOR_TYPE_PALETTE  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
+#define PNG_COLOR_TYPE_RGB        (PNG_COLOR_MASK_COLOR)
+#define PNG_COLOR_TYPE_RGB_ALPHA  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
+#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA)
+/* aliases */
+#define PNG_COLOR_TYPE_RGBA  PNG_COLOR_TYPE_RGB_ALPHA
+#define PNG_COLOR_TYPE_GA  PNG_COLOR_TYPE_GRAY_ALPHA
+
+/* This is for compression type. PNG 1.0-1.2 only define the single type. */
+#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */
+#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE
+
+/* This is for filter type. PNG 1.0-1.2 only define the single type. */
+#define PNG_FILTER_TYPE_BASE      0 /* Single row per-byte filtering */
+#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */
+#define PNG_FILTER_TYPE_DEFAULT   PNG_FILTER_TYPE_BASE
+
+/* These are for the interlacing type.  These values should NOT be changed. */
+#define PNG_INTERLACE_NONE        0 /* Non-interlaced image */
+#define PNG_INTERLACE_ADAM7       1 /* Adam7 interlacing */
+#define PNG_INTERLACE_LAST        2 /* Not a valid value */
+
+/* These are for the oFFs chunk.  These values should NOT be changed. */
+#define PNG_OFFSET_PIXEL          0 /* Offset in pixels */
+#define PNG_OFFSET_MICROMETER     1 /* Offset in micrometers (1/10^6 meter) */
+#define PNG_OFFSET_LAST           2 /* Not a valid value */
+
+/* These are for the pCAL chunk.  These values should NOT be changed. */
+#define PNG_EQUATION_LINEAR       0 /* Linear transformation */
+#define PNG_EQUATION_BASE_E       1 /* Exponential base e transform */
+#define PNG_EQUATION_ARBITRARY    2 /* Arbitrary base exponential transform */
+#define PNG_EQUATION_HYPERBOLIC   3 /* Hyperbolic sine transformation */
+#define PNG_EQUATION_LAST         4 /* Not a valid value */
+
+/* These are for the sCAL chunk.  These values should NOT be changed. */
+#define PNG_SCALE_UNKNOWN         0 /* unknown unit (image scale) */
+#define PNG_SCALE_METER           1 /* meters per pixel */
+#define PNG_SCALE_RADIAN          2 /* radians per pixel */
+#define PNG_SCALE_LAST            3 /* Not a valid value */
+
+/* These are for the pHYs chunk.  These values should NOT be changed. */
+#define PNG_RESOLUTION_UNKNOWN    0 /* pixels/unknown unit (aspect ratio) */
+#define PNG_RESOLUTION_METER      1 /* pixels/meter */
+#define PNG_RESOLUTION_LAST       2 /* Not a valid value */
+
+/* These are for the sRGB chunk.  These values should NOT be changed. */
+#define PNG_sRGB_INTENT_PERCEPTUAL 0
+#define PNG_sRGB_INTENT_RELATIVE   1
+#define PNG_sRGB_INTENT_SATURATION 2
+#define PNG_sRGB_INTENT_ABSOLUTE   3
+#define PNG_sRGB_INTENT_LAST       4 /* Not a valid value */
+
+/* This is for text chunks */
+#define PNG_KEYWORD_MAX_LENGTH     79
+
+/* Maximum number of entries in PLTE/sPLT/tRNS arrays */
+#define PNG_MAX_PALETTE_LENGTH    256
+
+/* These determine if an ancillary chunk's data has been successfully read
+ * from the PNG header, or if the application has filled in the corresponding
+ * data in the info_struct to be written into the output file.  The values
+ * of the PNG_INFO_<chunk> defines should NOT be changed.
+ */
+#define PNG_INFO_gAMA 0x0001
+#define PNG_INFO_sBIT 0x0002
+#define PNG_INFO_cHRM 0x0004
+#define PNG_INFO_PLTE 0x0008
+#define PNG_INFO_tRNS 0x0010
+#define PNG_INFO_bKGD 0x0020
+#define PNG_INFO_hIST 0x0040
+#define PNG_INFO_pHYs 0x0080
+#define PNG_INFO_oFFs 0x0100
+#define PNG_INFO_tIME 0x0200
+#define PNG_INFO_pCAL 0x0400
+#define PNG_INFO_sRGB 0x0800   /* GR-P, 0.96a */
+#define PNG_INFO_iCCP 0x1000   /* ESR, 1.0.6 */
+#define PNG_INFO_sPLT 0x2000   /* ESR, 1.0.6 */
+#define PNG_INFO_sCAL 0x4000   /* ESR, 1.0.6 */
+#define PNG_INFO_IDAT 0x8000   /* ESR, 1.0.6 */
+
+/* This is used for the transformation routines, as some of them
+ * change these values for the row.  It also should enable using
+ * the routines for other purposes.
+ */
+typedef struct png_row_info_struct
+{
+   png_uint_32 width;    /* width of row */
+   png_size_t rowbytes;  /* number of bytes in row */
+   png_byte color_type;  /* color type of row */
+   png_byte bit_depth;   /* bit depth of row */
+   png_byte channels;    /* number of channels (1, 2, 3, or 4) */
+   png_byte pixel_depth; /* bits per pixel (depth * channels) */
+} png_row_info;
+
+typedef png_row_info * png_row_infop;
+typedef png_row_info * * png_row_infopp;
+
+/* These are the function types for the I/O functions and for the functions
+ * that allow the user to override the default I/O functions with his or her
+ * own.  The png_error_ptr type should match that of user-supplied warning
+ * and error functions, while the png_rw_ptr type should match that of the
+ * user read/write data functions.  Note that the 'write' function must not
+ * modify the buffer it is passed. The 'read' function, on the other hand, is
+ * expected to return the read data in the buffer.
+ */
+typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp));
+typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, png_size_t));
+typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp));
+typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32,
+    int));
+typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32,
+    int));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop));
+typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop));
+
+/* The following callback receives png_uint_32 row_number, int pass for the
+ * png_bytep data of the row.  When transforming an interlaced image the
+ * row number is the row number within the sub-image of the interlace pass, so
+ * the value will increase to the height of the sub-image (not the full image)
+ * then reset to 0 for the next pass.
+ *
+ * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to
+ * find the output pixel (x,y) given an interlaced sub-image pixel
+ * (row,col,pass).  (See below for these macros.)
+ */
+typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep,
+    png_uint_32, int));
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop,
+    png_bytep));
+#endif
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp,
+    png_unknown_chunkp));
+#endif
+#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
+/* not used anywhere */
+/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* This must match the function definition in <setjmp.h>, and the application
+ * must include this before png.h to obtain the definition of jmp_buf.  The
+ * function is required to be PNG_NORETURN, but this is not checked.  If the
+ * function does return the application will crash via an abort() or similar
+ * system level call.
+ *
+ * If you get a warning here while building the library you may need to make
+ * changes to ensure that pnglibconf.h records the calling convention used by
+ * your compiler.  This may be very difficult - try using a different compiler
+ * to build the library!
+ */
+PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef);
+#endif
+
+/* Transform masks for the high-level interface */
+#define PNG_TRANSFORM_IDENTITY       0x0000    /* read and write */
+#define PNG_TRANSFORM_STRIP_16       0x0001    /* read only */
+#define PNG_TRANSFORM_STRIP_ALPHA    0x0002    /* read only */
+#define PNG_TRANSFORM_PACKING        0x0004    /* read and write */
+#define PNG_TRANSFORM_PACKSWAP       0x0008    /* read and write */
+#define PNG_TRANSFORM_EXPAND         0x0010    /* read only */
+#define PNG_TRANSFORM_INVERT_MONO    0x0020    /* read and write */
+#define PNG_TRANSFORM_SHIFT          0x0040    /* read and write */
+#define PNG_TRANSFORM_BGR            0x0080    /* read and write */
+#define PNG_TRANSFORM_SWAP_ALPHA     0x0100    /* read and write */
+#define PNG_TRANSFORM_SWAP_ENDIAN    0x0200    /* read and write */
+#define PNG_TRANSFORM_INVERT_ALPHA   0x0400    /* read and write */
+#define PNG_TRANSFORM_STRIP_FILLER   0x0800    /* write only */
+/* Added to libpng-1.2.34 */
+#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER
+#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */
+/* Added to libpng-1.4.0 */
+#define PNG_TRANSFORM_GRAY_TO_RGB   0x2000      /* read only */
+/* Added to libpng-1.5.4 */
+#define PNG_TRANSFORM_EXPAND_16     0x4000      /* read only */
+#define PNG_TRANSFORM_SCALE_16      0x8000      /* read only */
+
+/* Flags for MNG supported features */
+#define PNG_FLAG_MNG_EMPTY_PLTE     0x01
+#define PNG_FLAG_MNG_FILTER_64      0x04
+#define PNG_ALL_MNG_FEATURES        0x05
+
+/* NOTE: prior to 1.5 these functions had no 'API' style declaration,
+ * this allowed the zlib default functions to be used on Windows
+ * platforms.  In 1.5 the zlib default malloc (which just calls malloc and
+ * ignores the first argument) should be completely compatible with the
+ * following.
+ */
+typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp,
+    png_alloc_size_t));
+typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp));
+
+/* Section 3: exported functions
+ * Here are the function definitions most commonly used.  This is not
+ * the place to find out how to use libpng.  See libpng-manual.txt for the
+ * full explanation, see example.c for the summary.  This just provides
+ * a simple one line description of the use of each function.
+ *
+ * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in
+ * pngconf.h and in the *.dfn files in the scripts directory.
+ *
+ *   PNG_EXPORT(ordinal, type, name, (args));
+ *
+ *       ordinal:    ordinal that is used while building
+ *                   *.def files. The ordinal value is only
+ *                   relevant when preprocessing png.h with
+ *                   the *.dfn files for building symbol table
+ *                   entries, and are removed by pngconf.h.
+ *       type:       return type of the function
+ *       name:       function name
+ *       args:       function arguments, with types
+ *
+ * When we wish to append attributes to a function prototype we use
+ * the PNG_EXPORTA() macro instead.
+ *
+ *   PNG_EXPORTA(ordinal, type, name, (args), attributes);
+ *
+ *       ordinal, type, name, and args: same as in PNG_EXPORT().
+ *       attributes: function attributes
+ */
+
+/* Returns the version number of the library */
+PNG_EXPORT(1, png_uint_32, png_access_version_number, (void));
+
+/* Tell lib we have already handled the first <num_bytes> magic bytes.
+ * Handling more than 8 bytes from the beginning of the file is an error.
+ */
+PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes));
+
+/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a
+ * PNG file.  Returns zero if the supplied bytes match the 8-byte PNG
+ * signature, and non-zero otherwise.  Having num_to_check == 0 or
+ * start > 7 will always fail (ie return non-zero).
+ */
+PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, png_size_t start,
+    png_size_t num_to_check));
+
+/* Simple signature checking function.  This is the same as calling
+ * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n).
+ */
+#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n))
+
+/* Allocate and initialize png_ptr struct for reading, and any other memory. */
+PNG_EXPORTA(4, png_structp, png_create_read_struct,
+    (png_const_charp user_png_ver, png_voidp error_ptr,
+    png_error_ptr error_fn, png_error_ptr warn_fn),
+    PNG_ALLOCATED);
+
+/* Allocate and initialize png_ptr struct for writing, and any other memory */
+PNG_EXPORTA(5, png_structp, png_create_write_struct,
+    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
+    png_error_ptr warn_fn),
+    PNG_ALLOCATED);
+
+PNG_EXPORT(6, png_size_t, png_get_compression_buffer_size,
+    (png_const_structrp png_ptr));
+
+PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr,
+    png_size_t size));
+
+/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp
+ * match up.
+ */
+#ifdef PNG_SETJMP_SUPPORTED
+/* This function returns the jmp_buf built in to *png_ptr.  It must be
+ * supplied with an appropriate 'longjmp' function to use on that jmp_buf
+ * unless the default error function is overridden in which case NULL is
+ * acceptable.  The size of the jmp_buf is checked against the actual size
+ * allocated by the library - the call will return NULL on a mismatch
+ * indicating an ABI mismatch.
+ */
+PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr,
+    png_longjmp_ptr longjmp_fn, size_t jmp_buf_size));
+#  define png_jmpbuf(png_ptr) \
+      (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf))))
+#else
+#  define png_jmpbuf(png_ptr) \
+      (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP)
+#endif
+/* This function should be used by libpng applications in place of
+ * longjmp(png_ptr->jmpbuf, val).  If longjmp_fn() has been set, it
+ * will use it; otherwise it will call PNG_ABORT().  This function was
+ * added in libpng-1.5.0.
+ */
+PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val),
+    PNG_NORETURN);
+
+#ifdef PNG_READ_SUPPORTED
+/* Reset the compression stream */
+PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED);
+#endif
+
+/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */
+#ifdef PNG_USER_MEM_SUPPORTED
+PNG_EXPORTA(11, png_structp, png_create_read_struct_2,
+    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
+    png_error_ptr warn_fn,
+    png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
+    PNG_ALLOCATED);
+PNG_EXPORTA(12, png_structp, png_create_write_struct_2,
+    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
+    png_error_ptr warn_fn,
+    png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
+    PNG_ALLOCATED);
+#endif
+
+/* Write the PNG file signature. */
+PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr));
+
+/* Write a PNG chunk - size, type, (optional) data, CRC. */
+PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep
+    chunk_name, png_const_bytep data, png_size_t length));
+
+/* Write the start of a PNG chunk - length and chunk name. */
+PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr,
+    png_const_bytep chunk_name, png_uint_32 length));
+
+/* Write the data of a PNG chunk started with png_write_chunk_start(). */
+PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr,
+    png_const_bytep data, png_size_t length));
+
+/* Finish a chunk started with png_write_chunk_start() (includes CRC). */
+PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr));
+
+/* Allocate and initialize the info structure */
+PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr),
+    PNG_ALLOCATED);
+
+/* DEPRECATED: this function allowed init structures to be created using the
+ * default allocation method (typically malloc).  Use is deprecated in 1.6.0 and
+ * the API will be removed in the future.
+ */
+PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr,
+    png_size_t png_info_struct_size), PNG_DEPRECATED);
+
+/* Writes all the PNG information before the image. */
+PNG_EXPORT(20, void, png_write_info_before_PLTE,
+    (png_structrp png_ptr, png_const_inforp info_ptr));
+PNG_EXPORT(21, void, png_write_info,
+    (png_structrp png_ptr, png_const_inforp info_ptr));
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the information before the actual image data. */
+PNG_EXPORT(22, void, png_read_info,
+    (png_structrp png_ptr, png_inforp info_ptr));
+#endif
+
+#ifdef PNG_TIME_RFC1123_SUPPORTED
+   /* Convert to a US string format: there is no localization support in this
+    * routine.  The original implementation used a 29 character buffer in
+    * png_struct, this will be removed in future versions.
+    */
+#if PNG_LIBPNG_VER < 10700
+/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */
+PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr,
+    png_const_timep ptime),PNG_DEPRECATED);
+#endif
+PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29],
+    png_const_timep ptime));
+#endif
+
+#ifdef PNG_CONVERT_tIME_SUPPORTED
+/* Convert from a struct tm to png_time */
+PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime,
+    const struct tm * ttime));
+
+/* Convert from time_t to png_time.  Uses gmtime() */
+PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime));
+#endif /* PNG_CONVERT_tIME_SUPPORTED */
+
+#ifdef PNG_READ_EXPAND_SUPPORTED
+/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */
+PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr));
+PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr));
+PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr));
+PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr));
+#endif
+
+#ifdef PNG_READ_EXPAND_16_SUPPORTED
+/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion
+ * of a tRNS chunk if present.
+ */
+PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr));
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* Use blue, green, red order for pixels. */
+PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr));
+#endif
+
+#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
+/* Expand the grayscale to 24-bit RGB if necessary. */
+PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr));
+#endif
+
+#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
+/* Reduce RGB to grayscale. */
+#define PNG_ERROR_ACTION_NONE  1
+#define PNG_ERROR_ACTION_WARN  2
+#define PNG_ERROR_ACTION_ERROR 3
+#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/
+
+PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr,
+    int error_action, double red, double green))
+PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr,
+    int error_action, png_fixed_point red, png_fixed_point green))
+
+PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp
+    png_ptr));
+#endif
+
+#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
+PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth,
+    png_colorp palette));
+#endif
+
+#ifdef PNG_READ_ALPHA_MODE_SUPPORTED
+/* How the alpha channel is interpreted - this affects how the color channels of
+ * a PNG file are returned when an alpha channel, or tRNS chunk in a palette
+ * file, is present.
+ *
+ * This has no effect on the way pixels are written into a PNG output
+ * datastream. The color samples in a PNG datastream are never premultiplied
+ * with the alpha samples.
+ *
+ * The default is to return data according to the PNG specification: the alpha
+ * channel is a linear measure of the contribution of the pixel to the
+ * corresponding composited pixel.  The gamma encoded color channels must be
+ * scaled according to the contribution and to do this it is necessary to undo
+ * the encoding, scale the color values, perform the composition and reencode
+ * the values.  This is the 'PNG' mode.
+ *
+ * The alternative is to 'associate' the alpha with the color information by
+ * storing color channel values that have been scaled by the alpha.  The
+ * advantage is that the color channels can be resampled (the image can be
+ * scaled) in this form.  The disadvantage is that normal practice is to store
+ * linear, not (gamma) encoded, values and this requires 16-bit channels for
+ * still images rather than the 8-bit channels that are just about sufficient if
+ * gamma encoding is used.  In addition all non-transparent pixel values,
+ * including completely opaque ones, must be gamma encoded to produce the final
+ * image.  This is the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' mode (the
+ * latter being the two common names for associated alpha color channels.)
+ *
+ * Since it is not necessary to perform arithmetic on opaque color values so
+ * long as they are not to be resampled and are in the final color space it is
+ * possible to optimize the handling of alpha by storing the opaque pixels in
+ * the PNG format (adjusted for the output color space) while storing partially
+ * opaque pixels in the standard, linear, format.  The accuracy required for
+ * standard alpha composition is relatively low, because the pixels are
+ * isolated, therefore typically the accuracy loss in storing 8-bit linear
+ * values is acceptable.  (This is not true if the alpha channel is used to
+ * simulate transparency over large areas - use 16 bits or the PNG mode in
+ * this case!)  This is the 'OPTIMIZED' mode.  For this mode a pixel is
+ * treated as opaque only if the alpha value is equal to the maximum value.
+ *
+ * The final choice is to gamma encode the alpha channel as well.  This is
+ * broken because, in practice, no implementation that uses this choice
+ * correctly undoes the encoding before handling alpha composition.  Use this
+ * choice only if other serious errors in the software or hardware you use
+ * mandate it; the typical serious error is for dark halos to appear around
+ * opaque areas of the composited PNG image because of arithmetic overflow.
+ *
+ * The API function png_set_alpha_mode specifies which of these choices to use
+ * with an enumerated 'mode' value and the gamma of the required output:
+ */
+#define PNG_ALPHA_PNG           0 /* according to the PNG standard */
+#define PNG_ALPHA_STANDARD      1 /* according to Porter/Duff */
+#define PNG_ALPHA_ASSOCIATED    1 /* as above; this is the normal practice */
+#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */
+#define PNG_ALPHA_OPTIMIZED     2 /* 'PNG' for opaque pixels, else 'STANDARD' */
+#define PNG_ALPHA_BROKEN        3 /* the alpha channel is gamma encoded */
+
+PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode,
+    double output_gamma))
+PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr,
+    int mode, png_fixed_point output_gamma))
+#endif
+
+#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED)
+/* The output_gamma value is a screen gamma in libpng terminology: it expresses
+ * how to decode the output values, not how they are encoded.  The values used
+ * correspond to the normal numbers used to describe the overall gamma of a
+ * computer display system; for example 2.2 for an sRGB conformant system.  The
+ * values are scaled by 100000 in the _fixed version of the API (so 220000 for
+ * sRGB.)
+ *
+ * The inverse of the value is always used to provide a default for the PNG file
+ * encoding if it has no gAMA chunk and if png_set_gamma() has not been called
+ * to override the PNG gamma information.
+ *
+ * When the ALPHA_OPTIMIZED mode is selected the output gamma is used to encode
+ * opaque pixels however pixels with lower alpha values are not encoded,
+ * regardless of the output gamma setting.
+ *
+ * When the standard Porter Duff handling is requested with mode 1 the output
+ * encoding is set to be linear and the output_gamma value is only relevant
+ * as a default for input data that has no gamma information.  The linear output
+ * encoding will be overridden if png_set_gamma() is called - the results may be
+ * highly unexpected!
+ *
+ * The following numbers are derived from the sRGB standard and the research
+ * behind it.  sRGB is defined to be approximated by a PNG gAMA chunk value of
+ * 0.45455 (1/2.2) for PNG.  The value implicitly includes any viewing
+ * correction required to take account of any differences in the color
+ * environment of the original scene and the intended display environment; the
+ * value expresses how to *decode* the image for display, not how the original
+ * data was *encoded*.
+ *
+ * sRGB provides a peg for the PNG standard by defining a viewing environment.
+ * sRGB itself, and earlier TV standards, actually use a more complex transform
+ * (a linear portion then a gamma 2.4 power law) than PNG can express.  (PNG is
+ * limited to simple power laws.)  By saying that an image for direct display on
+ * an sRGB conformant system should be stored with a gAMA chunk value of 45455
+ * (11.3.3.2 and 11.3.3.5 of the ISO PNG specification) the PNG specification
+ * makes it possible to derive values for other display systems and
+ * environments.
+ *
+ * The Mac value is deduced from the sRGB based on an assumption that the actual
+ * extra viewing correction used in early Mac display systems was implemented as
+ * a power 1.45 lookup table.
+ *
+ * Any system where a programmable lookup table is used or where the behavior of
+ * the final display device characteristics can be changed requires system
+ * specific code to obtain the current characteristic.  However this can be
+ * difficult and most PNG gamma correction only requires an approximate value.
+ *
+ * By default, if png_set_alpha_mode() is not called, libpng assumes that all
+ * values are unencoded, linear, values and that the output device also has a
+ * linear characteristic.  This is only very rarely correct - it is invariably
+ * better to call png_set_alpha_mode() with PNG_DEFAULT_sRGB than rely on the
+ * default if you don't know what the right answer is!
+ *
+ * The special value PNG_GAMMA_MAC_18 indicates an older Mac system (pre Mac OS
+ * 10.6) which used a correction table to implement a somewhat lower gamma on an
+ * otherwise sRGB system.
+ *
+ * Both these values are reserved (not simple gamma values) in order to allow
+ * more precise correction internally in the future.
+ *
+ * NOTE: the following values can be passed to either the fixed or floating
+ * point APIs, but the floating point API will also accept floating point
+ * values.
+ */
+#define PNG_DEFAULT_sRGB -1       /* sRGB gamma and color space */
+#define PNG_GAMMA_MAC_18 -2       /* Old Mac '1.8' gamma and color space */
+#define PNG_GAMMA_sRGB   220000   /* Television standards--matches sRGB gamma */
+#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */
+#endif
+
+/* The following are examples of calls to png_set_alpha_mode to achieve the
+ * required overall gamma correction and, where necessary, alpha
+ * premultiplication.
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
+ *    This is the default libpng handling of the alpha channel - it is not
+ *    pre-multiplied into the color components.  In addition the call states
+ *    that the output is for a sRGB system and causes all PNG files without gAMA
+ *    chunks to be assumed to be encoded using sRGB.
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC);
+ *    In this case the output is assumed to be something like an sRGB conformant
+ *    display preceeded by a power-law lookup table of power 1.45.  This is how
+ *    early Mac systems behaved.
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR);
+ *    This is the classic Jim Blinn approach and will work in academic
+ *    environments where everything is done by the book.  It has the shortcoming
+ *    of assuming that input PNG data with no gamma information is linear - this
+ *    is unlikely to be correct unless the PNG files where generated locally.
+ *    Most of the time the output precision will be so low as to show
+ *    significant banding in dark areas of the image.
+ *
+ * png_set_expand_16(pp);
+ * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB);
+ *    This is a somewhat more realistic Jim Blinn inspired approach.  PNG files
+ *    are assumed to have the sRGB encoding if not marked with a gamma value and
+ *    the output is always 16 bits per component.  This permits accurate scaling
+ *    and processing of the data.  If you know that your input PNG files were
+ *    generated locally you might need to replace PNG_DEFAULT_sRGB with the
+ *    correct value for your system.
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB);
+ *    If you just need to composite the PNG image onto an existing background
+ *    and if you control the code that does this you can use the optimization
+ *    setting.  In this case you just copy completely opaque pixels to the
+ *    output.  For pixels that are not completely transparent (you just skip
+ *    those) you do the composition math using png_composite or png_composite_16
+ *    below then encode the resultant 8-bit or 16-bit values to match the output
+ *    encoding.
+ *
+ * Other cases
+ *    If neither the PNG nor the standard linear encoding work for you because
+ *    of the software or hardware you use then you have a big problem.  The PNG
+ *    case will probably result in halos around the image.  The linear encoding
+ *    will probably result in a washed out, too bright, image (it's actually too
+ *    contrasty.)  Try the ALPHA_OPTIMIZED mode above - this will probably
+ *    substantially reduce the halos.  Alternatively try:
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB);
+ *    This option will also reduce the halos, but there will be slight dark
+ *    halos round the opaque parts of the image where the background is light.
+ *    In the OPTIMIZED mode the halos will be light halos where the background
+ *    is dark.  Take your pick - the halos are unavoidable unless you can get
+ *    your hardware/software fixed!  (The OPTIMIZED approach is slightly
+ *    faster.)
+ *
+ * When the default gamma of PNG files doesn't match the output gamma.
+ *    If you have PNG files with no gamma information png_set_alpha_mode allows
+ *    you to provide a default gamma, but it also sets the ouput gamma to the
+ *    matching value.  If you know your PNG files have a gamma that doesn't
+ *    match the output you can take advantage of the fact that
+ *    png_set_alpha_mode always sets the output gamma but only sets the PNG
+ *    default if it is not already set:
+ *
+ * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
+ * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC);
+ *    The first call sets both the default and the output gamma values, the
+ *    second call overrides the output gamma without changing the default.  This
+ *    is easier than achieving the same effect with png_set_gamma.  You must use
+ *    PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will
+ *    fire if more than one call to png_set_alpha_mode and png_set_background is
+ *    made in the same read operation, however multiple calls with PNG_ALPHA_PNG
+ *    are ignored.
+ */
+
+#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
+PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr));
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr));
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr));
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */
+PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler,
+    int flags));
+/* The values of the PNG_FILLER_ defines should NOT be changed */
+#  define PNG_FILLER_BEFORE 0
+#  define PNG_FILLER_AFTER 1
+/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */
+PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr,
+    png_uint_32 filler, int flags));
+#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* Swap bytes in 16-bit depth files. */
+PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */
+PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr));
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
+    defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* Swap packing order of pixels in bytes. */
+PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr));
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+/* Converts files to legal bit depths. */
+PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p
+    true_bits));
+#endif
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+    defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* Have the code handle the interlacing.  Returns the number of passes.
+ * MUST be called before png_read_update_info or png_start_read_image,
+ * otherwise it will not have the desired effect.  Note that it is still
+ * necessary to call png_read_row or png_read_rows png_get_image_height
+ * times for each pass.
+*/
+PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr));
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+/* Invert monochrome files */
+PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr));
+#endif
+
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+/* Handle alpha and tRNS by replacing with a background color.  Prior to
+ * libpng-1.5.4 this API must not be called before the PNG file header has been
+ * read.  Doing so will result in unexpected behavior and possible warnings or
+ * errors if the PNG file contains a bKGD chunk.
+ */
+PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr,
+    png_const_color_16p background_color, int background_gamma_code,
+    int need_expand, double background_gamma))
+PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr,
+    png_const_color_16p background_color, int background_gamma_code,
+    int need_expand, png_fixed_point background_gamma))
+#endif
+#ifdef PNG_READ_BACKGROUND_SUPPORTED
+#  define PNG_BACKGROUND_GAMMA_UNKNOWN 0
+#  define PNG_BACKGROUND_GAMMA_SCREEN  1
+#  define PNG_BACKGROUND_GAMMA_FILE    2
+#  define PNG_BACKGROUND_GAMMA_UNIQUE  3
+#endif
+
+#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
+/* Scale a 16-bit depth file down to 8-bit, accurately. */
+PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr));
+#endif
+
+#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
+#define PNG_READ_16_TO_8 SUPPORTED /* Name prior to 1.5.4 */
+/* Strip the second byte of information from a 16-bit depth file. */
+PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr));
+#endif
+
+#ifdef PNG_READ_QUANTIZE_SUPPORTED
+/* Turn on quantizing, and reduce the palette to the number of colors
+ * available.
+ */
+PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr,
+    png_colorp palette, int num_palette, int maximum_colors,
+    png_const_uint_16p histogram, int full_quantize));
+#endif
+
+#ifdef PNG_READ_GAMMA_SUPPORTED
+/* The threshold on gamma processing is configurable but hard-wired into the
+ * library.  The following is the floating point variant.
+ */
+#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001)
+
+/* Handle gamma correction. Screen_gamma=(display_exponent).
+ * NOTE: this API simply sets the screen and file gamma values. It will
+ * therefore override the value for gamma in a PNG file if it is called after
+ * the file header has been read - use with care  - call before reading the PNG
+ * file for best results!
+ *
+ * These routines accept the same gamma values as png_set_alpha_mode (described
+ * above).  The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either
+ * API (floating point or fixed.)  Notice, however, that the 'file_gamma' value
+ * is the inverse of a 'screen gamma' value.
+ */
+PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr,
+    double screen_gamma, double override_file_gamma))
+PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr,
+    png_fixed_point screen_gamma, png_fixed_point override_file_gamma))
+#endif
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+/* Set how many lines between output flushes - 0 for no flushing */
+PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows));
+/* Flush the current PNG output buffer */
+PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr));
+#endif
+
+/* Optional update palette with requested transformations */
+PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr));
+
+/* Optional call to update the users info structure */
+PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr,
+    png_inforp info_ptr));
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read one or more rows of image data. */
+PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row,
+    png_bytepp display_row, png_uint_32 num_rows));
+#endif
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read a row of data. */
+PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row,
+    png_bytep display_row));
+#endif
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the whole image into memory at once. */
+PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image));
+#endif
+
+/* Write a row of image data */
+PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr,
+    png_const_bytep row));
+
+/* Write a few rows of image data: (*row) is not written; however, the type
+ * is declared as writeable to maintain compatibility with previous versions
+ * of libpng and to allow the 'display_row' array from read_rows to be passed
+ * unchanged to write_rows.
+ */
+PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row,
+    png_uint_32 num_rows));
+
+/* Write the image data */
+PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image));
+
+/* Write the end of the PNG file. */
+PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr,
+    png_inforp info_ptr));
+
+#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
+/* Read the end of the PNG file. */
+PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr));
+#endif
+
+/* Free any memory associated with the png_info_struct */
+PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr,
+    png_infopp info_ptr_ptr));
+
+/* Free any memory associated with the png_struct and the png_info_structs */
+PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr,
+    png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr));
+
+/* Free any memory associated with the png_struct and the png_info_structs */
+PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr,
+    png_infopp info_ptr_ptr));
+
+/* Set the libpng method of handling chunk CRC errors */
+PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
+    int ancil_action));
+
+/* Values for png_set_crc_action() say how to handle CRC errors in
+ * ancillary and critical chunks, and whether to use the data contained
+ * therein.  Note that it is impossible to "discard" data in a critical
+ * chunk.  For versions prior to 0.90, the action was always error/quit,
+ * whereas in version 0.90 and later, the action for CRC errors in ancillary
+ * chunks is warn/discard.  These values should NOT be changed.
+ *
+ *      value                       action:critical     action:ancillary
+ */
+#define PNG_CRC_DEFAULT       0  /* error/quit          warn/discard data */
+#define PNG_CRC_ERROR_QUIT    1  /* error/quit          error/quit        */
+#define PNG_CRC_WARN_DISCARD  2  /* (INVALID)           warn/discard data */
+#define PNG_CRC_WARN_USE      3  /* warn/use data       warn/use data     */
+#define PNG_CRC_QUIET_USE     4  /* quiet/use data      quiet/use data    */
+#define PNG_CRC_NO_CHANGE     5  /* use current value   use current value */
+
+/* These functions give the user control over the scan-line filtering in
+ * libpng and the compression methods used by zlib.  These functions are
+ * mainly useful for testing, as the defaults should work with most users.
+ * Those users who are tight on memory or want faster performance at the
+ * expense of compression can modify them.  See the compression library
+ * header file (zlib.h) for an explination of the compression functions.
+ */
+
+/* Set the filtering method(s) used by libpng.  Currently, the only valid
+ * value for "method" is 0.
+ */
+PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
+    int filters));
+
+/* Flags for png_set_filter() to say which filters to use.  The flags
+ * are chosen so that they don't conflict with real filter types
+ * below, in case they are supplied instead of the #defined constants.
+ * These values should NOT be changed.
+ */
+#define PNG_NO_FILTERS     0x00
+#define PNG_FILTER_NONE    0x08
+#define PNG_FILTER_SUB     0x10
+#define PNG_FILTER_UP      0x20
+#define PNG_FILTER_AVG     0x40
+#define PNG_FILTER_PAETH   0x80
+#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \
+                         PNG_FILTER_AVG | PNG_FILTER_PAETH)
+
+/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now.
+ * These defines should NOT be changed.
+ */
+#define PNG_FILTER_VALUE_NONE  0
+#define PNG_FILTER_VALUE_SUB   1
+#define PNG_FILTER_VALUE_UP    2
+#define PNG_FILTER_VALUE_AVG   3
+#define PNG_FILTER_VALUE_PAETH 4
+#define PNG_FILTER_VALUE_LAST  5
+
+#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */
+/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_
+ * defines, either the default (minimum-sum-of-absolute-differences), or
+ * the experimental method (weighted-minimum-sum-of-absolute-differences).
+ *
+ * Weights are factors >= 1.0, indicating how important it is to keep the
+ * filter type consistent between rows.  Larger numbers mean the current
+ * filter is that many times as likely to be the same as the "num_weights"
+ * previous filters.  This is cumulative for each previous row with a weight.
+ * There needs to be "num_weights" values in "filter_weights", or it can be
+ * NULL if the weights aren't being specified.  Weights have no influence on
+ * the selection of the first row filter.  Well chosen weights can (in theory)
+ * improve the compression for a given image.
+ *
+ * Costs are factors >= 1.0 indicating the relative decoding costs of a
+ * filter type.  Higher costs indicate more decoding expense, and are
+ * therefore less likely to be selected over a filter with lower computational
+ * costs.  There needs to be a value in "filter_costs" for each valid filter
+ * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't
+ * setting the costs.  Costs try to improve the speed of decompression without
+ * unduly increasing the compressed image size.
+ *
+ * A negative weight or cost indicates the default value is to be used, and
+ * values in the range [0.0, 1.0) indicate the value is to remain unchanged.
+ * The default values for both weights and costs are currently 1.0, but may
+ * change if good general weighting/cost heuristics can be found.  If both
+ * the weights and costs are set to 1.0, this degenerates the WEIGHTED method
+ * to the UNWEIGHTED method, but with added encoding time/computation.
+ */
+PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr,
+    int heuristic_method, int num_weights, png_const_doublep filter_weights,
+    png_const_doublep filter_costs))
+PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed,
+    (png_structrp png_ptr, int heuristic_method, int num_weights,
+    png_const_fixed_point_p filter_weights,
+    png_const_fixed_point_p filter_costs))
+#endif /*  PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+/* Heuristic used for row filter selection.  These defines should NOT be
+ * changed.
+ */
+#define PNG_FILTER_HEURISTIC_DEFAULT    0  /* Currently "UNWEIGHTED" */
+#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1  /* Used by libpng < 0.95 */
+#define PNG_FILTER_HEURISTIC_WEIGHTED   2  /* Experimental feature */
+#define PNG_FILTER_HEURISTIC_LAST       3  /* Not a valid value */
+
+#ifdef PNG_WRITE_SUPPORTED
+/* Set the library compression level.  Currently, valid values range from
+ * 0 - 9, corresponding directly to the zlib compression levels 0 - 9
+ * (0 - no compression, 9 - "maximal" compression).  Note that tests have
+ * shown that zlib compression levels 3-6 usually perform as well as level 9
+ * for PNG images, and do considerably fewer caclulations.  In the future,
+ * these values may not correspond directly to the zlib compression levels.
+ */
+PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr,
+    int level));
+
+PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr,
+    int mem_level));
+
+PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr,
+    int strategy));
+
+/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
+ * smaller value of window_bits if it can do so safely.
+ */
+PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr,
+    int window_bits));
+
+PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr,
+    int method));
+#endif
+
+#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+/* Also set zlib parameters for compressing non-IDAT chunks */
+PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr,
+    int level));
+
+PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr,
+    int mem_level));
+
+PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr,
+    int strategy));
+
+/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
+ * smaller value of window_bits if it can do so safely.
+ */
+PNG_EXPORT(225, void, png_set_text_compression_window_bits,
+    (png_structrp png_ptr, int window_bits));
+
+PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr,
+    int method));
+#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */
+
+/* These next functions are called for input/output, memory, and error
+ * handling.  They are in the file pngrio.c, pngwio.c, and pngerror.c,
+ * and call standard C I/O routines such as fread(), fwrite(), and
+ * fprintf().  These functions can be made to use other I/O routines
+ * at run time for those applications that need to handle I/O in a
+ * different manner by calling png_set_???_fn().  See libpng-manual.txt for
+ * more information.
+ */
+
+#ifdef PNG_STDIO_SUPPORTED
+/* Initialize the input/output for the PNG file to the default functions. */
+PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp));
+#endif
+
+/* Replace the (error and abort), and warning functions with user
+ * supplied functions.  If no messages are to be printed you must still
+ * write and use replacement functions. The replacement error_fn should
+ * still do a longjmp to the last setjmp location if you are using this
+ * method of error handling.  If error_fn or warning_fn is NULL, the
+ * default function will be used.
+ */
+
+PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr,
+    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn));
+
+/* Return the user pointer associated with the error functions */
+PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr));
+
+/* Replace the default data output functions with a user supplied one(s).
+ * If buffered output is not used, then output_flush_fn can be set to NULL.
+ * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time
+ * output_flush_fn will be ignored (and thus can be NULL).
+ * It is probably a mistake to use NULL for output_flush_fn if
+ * write_data_fn is not also NULL unless you have built libpng with
+ * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's
+ * default flush function, which uses the standard *FILE structure, will
+ * be used.
+ */
+PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr,
+    png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn));
+
+/* Replace the default data input function with a user supplied one. */
+PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr,
+    png_rw_ptr read_data_fn));
+
+/* Return the user pointer associated with the I/O functions */
+PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr));
+
+PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr,
+    png_read_status_ptr read_row_fn));
+
+PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr,
+    png_write_status_ptr write_row_fn));
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* Replace the default memory allocation functions with user supplied one(s). */
+PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr,
+    png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+/* Return the user pointer associated with the memory functions */
+PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr));
+#endif
+
+#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
+PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr,
+    png_user_transform_ptr read_user_transform_fn));
+#endif
+
+#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
+PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr,
+    png_user_transform_ptr write_user_transform_fn));
+#endif
+
+#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
+PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr,
+    png_voidp user_transform_ptr, int user_transform_depth,
+    int user_transform_channels));
+/* Return the user pointer associated with the user transform functions */
+PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr,
+    (png_const_structrp png_ptr));
+#endif
+
+#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
+/* Return information about the row currently being processed.  Note that these
+ * APIs do not fail but will return unexpected results if called outside a user
+ * transform callback.  Also note that when transforming an interlaced image the
+ * row number is the row number within the sub-image of the interlace pass, so
+ * the value will increase to the height of the sub-image (not the full image)
+ * then reset to 0 for the next pass.
+ *
+ * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to
+ * find the output pixel (x,y) given an interlaced sub-image pixel
+ * (row,col,pass).  (See below for these macros.)
+ */
+PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp));
+PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp));
+#endif
+
+#ifdef PNG_READ_USER_CHUNKS_SUPPORTED
+/* This callback is called only for *unknown* chunks.  If
+ * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known
+ * chunks to be treated as unknown, however in this case the callback must do
+ * any processing required by the chunk (e.g. by calling the appropriate
+ * png_set_ APIs.)
+ *
+ * There is no write support - on write, by default, all the chunks in the
+ * 'unknown' list are written in the specified position.
+ *
+ * The integer return from the callback function is interpreted thus:
+ *
+ * negative: An error occured, png_chunk_error will be called.
+ *     zero: The chunk was not handled, the chunk will be saved. A critical
+ *           chunk will cause an error at this point unless it is to be saved.
+ * positive: The chunk was handled, libpng will ignore/discard it.
+ *
+ * See "INTERACTION WTIH USER CHUNK CALLBACKS" below for important notes about
+ * how this behavior will change in libpng 1.7
+ */
+PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr,
+    png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn));
+#endif
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr));
+#endif
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+/* Sets the function callbacks for the push reader, and a pointer to a
+ * user-defined structure available to the callback functions.
+ */
+PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr,
+    png_voidp progressive_ptr, png_progressive_info_ptr info_fn,
+    png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn));
+
+/* Returns the user pointer associated with the push read functions */
+PNG_EXPORT(91, png_voidp, png_get_progressive_ptr,
+    (png_const_structrp png_ptr));
+
+/* Function to be called when data becomes available */
+PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr,
+    png_inforp info_ptr, png_bytep buffer, png_size_t buffer_size));
+
+/* A function which may be called *only* within png_process_data to stop the
+ * processing of any more data.  The function returns the number of bytes
+ * remaining, excluding any that libpng has cached internally.  A subsequent
+ * call to png_process_data must supply these bytes again.  If the argument
+ * 'save' is set to true the routine will first save all the pending data and
+ * will always return 0.
+ */
+PNG_EXPORT(219, png_size_t, png_process_data_pause, (png_structrp, int save));
+
+/* A function which may be called *only* outside (after) a call to
+ * png_process_data.  It returns the number of bytes of data to skip in the
+ * input.  Normally it will return 0, but if it returns a non-zero value the
+ * application must skip than number of bytes of input data and pass the
+ * following data to the next call to png_process_data.
+ */
+PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp));
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+/* Function that combines rows.  'new_row' is a flag that should come from
+ * the callback and be non-NULL if anything needs to be done; the library
+ * stores its own version of the new data internally and ignores the passed
+ * in value.
+ */
+PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr,
+    png_bytep old_row, png_const_bytep new_row));
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr,
+    png_alloc_size_t size), PNG_ALLOCATED);
+/* Added at libpng version 1.4.0 */
+PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr,
+    png_alloc_size_t size), PNG_ALLOCATED);
+
+/* Added at libpng version 1.2.4 */
+PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr,
+    png_alloc_size_t size), PNG_ALLOCATED);
+
+/* Frees a pointer allocated by png_malloc() */
+PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr));
+
+/* Free data that was allocated internally */
+PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 free_me, int num));
+
+/* Reassign responsibility for freeing existing data, whether allocated
+ * by libpng or by the application; this works on the png_info structure passed
+ * in, it does not change the state for other png_info structures.
+ *
+ * It is unlikely that this function works correctly as of 1.6.0 and using it
+ * may result either in memory leaks or double free of allocated data.
+ */
+PNG_EXPORTA(99, void, png_data_freer, (png_const_structrp png_ptr,
+    png_inforp info_ptr, int freer, png_uint_32 mask), PNG_DEPRECATED);
+
+/* Assignments for png_data_freer */
+#define PNG_DESTROY_WILL_FREE_DATA 1
+#define PNG_SET_WILL_FREE_DATA 1
+#define PNG_USER_WILL_FREE_DATA 2
+/* Flags for png_ptr->free_me and info_ptr->free_me */
+#define PNG_FREE_HIST 0x0008
+#define PNG_FREE_ICCP 0x0010
+#define PNG_FREE_SPLT 0x0020
+#define PNG_FREE_ROWS 0x0040
+#define PNG_FREE_PCAL 0x0080
+#define PNG_FREE_SCAL 0x0100
+#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+#  define PNG_FREE_UNKN 0x0200
+#endif
+/*      PNG_FREE_LIST 0x0400    removed in 1.6.0 because it is ignored */
+#define PNG_FREE_PLTE 0x1000
+#define PNG_FREE_TRNS 0x2000
+#define PNG_FREE_TEXT 0x4000
+#define PNG_FREE_ALL  0x7fff
+#define PNG_FREE_MUL  0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
+
+#ifdef PNG_USER_MEM_SUPPORTED
+PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr,
+    png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED);
+PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr,
+    png_voidp ptr), PNG_DEPRECATED);
+#endif
+
+#ifdef PNG_ERROR_TEXT_SUPPORTED
+/* Fatal error in PNG image of libpng - can't continue */
+PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr,
+    png_const_charp error_message), PNG_NORETURN);
+
+/* The same, but the chunk name is prepended to the error string. */
+PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr,
+    png_const_charp error_message), PNG_NORETURN);
+
+#else
+/* Fatal error in PNG image of libpng - can't continue */
+PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN);
+#endif
+
+#ifdef PNG_WARNINGS_SUPPORTED
+/* Non-fatal error in libpng.  Can continue, but may have a problem. */
+PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr,
+    png_const_charp warning_message));
+
+/* Non-fatal error in libpng, chunk name is prepended to message. */
+PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr,
+    png_const_charp warning_message));
+#endif
+
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+/* Benign error in libpng.  Can continue, but may have a problem.
+ * User can choose whether to handle as a fatal error or as a warning. */
+PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr,
+    png_const_charp warning_message));
+
+#ifdef PNG_READ_SUPPORTED
+/* Same, chunk name is prepended to message (only during read) */
+PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr,
+    png_const_charp warning_message));
+#endif
+
+PNG_EXPORT(109, void, png_set_benign_errors,
+    (png_structrp png_ptr, int allowed));
+#else
+#  ifdef PNG_ALLOW_BENIGN_ERRORS
+#    define png_benign_error png_warning
+#    define png_chunk_benign_error png_chunk_warning
+#  else
+#    define png_benign_error png_error
+#    define png_chunk_benign_error png_chunk_error
+#  endif
+#endif
+
+/* The png_set_<chunk> functions are for storing values in the png_info_struct.
+ * Similarly, the png_get_<chunk> calls are used to read values from the
+ * png_info_struct, either storing the parameters in the passed variables, or
+ * setting pointers into the png_info_struct where the data is stored.  The
+ * png_get_<chunk> functions return a non-zero value if the data was available
+ * in info_ptr, or return zero and do not change any of the parameters if the
+ * data was not available.
+ *
+ * These functions should be used instead of directly accessing png_info
+ * to avoid problems with future changes in the size and internal layout of
+ * png_info_struct.
+ */
+/* Returns "flag" if chunk data is valid in info_ptr. */
+PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, png_uint_32 flag));
+
+/* Returns number of bytes needed to hold a transformed row. */
+PNG_EXPORT(111, png_size_t, png_get_rowbytes, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+/* Returns row_pointers, which is an array of pointers to scanlines that was
+ * returned from png_read_png().
+ */
+PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+/* Set row_pointers, which is an array of pointers to scanlines for use
+ * by png_write_png().
+ */
+PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_bytepp row_pointers));
+#endif
+
+/* Returns number of color channels in image. */
+PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* Returns image width in pixels. */
+PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+/* Returns image height in pixels. */
+PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+/* Returns image bit_depth. */
+PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+/* Returns image color_type. */
+PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+/* Returns image filter_type. */
+PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+/* Returns image interlace_type. */
+PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+/* Returns image compression_type. */
+PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+
+/* Returns image resolution in pixels per meter, from pHYs chunk data. */
+PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+
+/* Returns pixel aspect ratio, computed from pHYs chunk data.  */
+PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+
+/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */
+PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+PNG_EXPORT(128, png_int_32, png_get_x_offset_microns,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+PNG_EXPORT(129, png_int_32, png_get_y_offset_microns,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+
+#endif /* PNG_EASY_ACCESS_SUPPORTED */
+
+#ifdef PNG_READ_SUPPORTED
+/* Returns pointer to signature string read from PNG header */
+PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr));
+#endif
+
+#ifdef PNG_bKGD_SUPPORTED
+PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_color_16p *background));
+#endif
+
+#ifdef PNG_bKGD_SUPPORTED
+PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_color_16p background));
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x,
+    double *red_y, double *green_x, double *green_y, double *blue_x,
+    double *blue_y))
+PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z,
+    double *green_X, double *green_Y, double *green_Z, double *blue_X,
+    double *blue_Y, double *blue_Z))
+PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_fixed_point *int_white_x, png_fixed_point *int_white_y,
+    png_fixed_point *int_red_x, png_fixed_point *int_red_y,
+    png_fixed_point *int_green_x, png_fixed_point *int_green_y,
+    png_fixed_point *int_blue_x, png_fixed_point *int_blue_y))
+PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_fixed_point *int_red_X, png_fixed_point *int_red_Y,
+    png_fixed_point *int_red_Z, png_fixed_point *int_green_X,
+    png_fixed_point *int_green_Y, png_fixed_point *int_green_Z,
+    png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
+    png_fixed_point *int_blue_Z))
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr,
+    png_inforp info_ptr,
+    double white_x, double white_y, double red_x, double red_y, double green_x,
+    double green_y, double blue_x, double blue_y))
+PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr,
+    png_inforp info_ptr, double red_X, double red_Y, double red_Z,
+    double green_X, double green_Y, double green_Z, double blue_X,
+    double blue_Y, double blue_Z))
+PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_fixed_point int_white_x,
+    png_fixed_point int_white_y, png_fixed_point int_red_x,
+    png_fixed_point int_red_y, png_fixed_point int_green_x,
+    png_fixed_point int_green_y, png_fixed_point int_blue_x,
+    png_fixed_point int_blue_y))
+PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y,
+    png_fixed_point int_red_Z, png_fixed_point int_green_X,
+    png_fixed_point int_green_Y, png_fixed_point int_green_Z,
+    png_fixed_point int_blue_X, png_fixed_point int_blue_Y,
+    png_fixed_point int_blue_Z))
+#endif
+
+#ifdef PNG_gAMA_SUPPORTED
+PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, double *file_gamma))
+PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr,
+    png_fixed_point *int_file_gamma))
+#endif
+
+#ifdef PNG_gAMA_SUPPORTED
+PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr,
+    png_inforp info_ptr, double file_gamma))
+PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_fixed_point int_file_gamma))
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_uint_16p *hist));
+#endif
+
+#ifdef PNG_hIST_SUPPORTED
+PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_uint_16p hist));
+#endif
+
+PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height,
+    int *bit_depth, int *color_type, int *interlace_method,
+    int *compression_method, int *filter_method));
+
+PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth,
+    int color_type, int interlace_method, int compression_method,
+    int filter_method));
+
+#ifdef PNG_oFFs_SUPPORTED
+PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr,
+   png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y,
+   int *unit_type));
+#endif
+
+#ifdef PNG_oFFs_SUPPORTED
+PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y,
+    int unit_type));
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_charp *purpose, png_int_32 *X0,
+    png_int_32 *X1, int *type, int *nparams, png_charp *units,
+    png_charpp *params));
+#endif
+
+#ifdef PNG_pCAL_SUPPORTED
+PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1,
+    int type, int nparams, png_const_charp units, png_charpp params));
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
+    int *unit_type));
+#endif
+
+#ifdef PNG_pHYs_SUPPORTED
+PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type));
+#endif
+
+PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr,
+   png_inforp info_ptr, png_colorp *palette, int *num_palette));
+
+PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr,
+    png_inforp info_ptr, png_const_colorp palette, int num_palette));
+
+#ifdef PNG_sBIT_SUPPORTED
+PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_color_8p *sig_bit));
+#endif
+
+#ifdef PNG_sBIT_SUPPORTED
+PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_color_8p sig_bit));
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, int *file_srgb_intent));
+#endif
+
+#ifdef PNG_sRGB_SUPPORTED
+PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr,
+    png_inforp info_ptr, int srgb_intent));
+PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr,
+    png_inforp info_ptr, int srgb_intent));
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_charpp name, int *compression_type,
+    png_bytepp profile, png_uint_32 *proflen));
+#endif
+
+#ifdef PNG_iCCP_SUPPORTED
+PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_charp name, int compression_type,
+    png_const_bytep profile, png_uint_32 proflen));
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_sPLT_tpp entries));
+#endif
+
+#ifdef PNG_sPLT_SUPPORTED
+PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_sPLT_tp entries, int nentries));
+#endif
+
+#ifdef PNG_TEXT_SUPPORTED
+/* png_get_text also returns the number of text chunks in *num_text */
+PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_textp *text_ptr, int *num_text));
+#endif
+
+/* Note while png_set_text() will accept a structure whose text,
+ * language, and  translated keywords are NULL pointers, the structure
+ * returned by png_get_text will always contain regular
+ * zero-terminated C strings.  They might be empty strings but
+ * they will never be NULL pointers.
+ */
+
+#ifdef PNG_TEXT_SUPPORTED
+PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_textp text_ptr, int num_text));
+#endif
+
+#ifdef PNG_tIME_SUPPORTED
+PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_timep *mod_time));
+#endif
+
+#ifdef PNG_tIME_SUPPORTED
+PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_timep mod_time));
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans,
+    png_color_16p *trans_color));
+#endif
+
+#ifdef PNG_tRNS_SUPPORTED
+PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr,
+    png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans,
+    png_const_color_16p trans_color));
+#endif
+
+#ifdef PNG_sCAL_SUPPORTED
+PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, int *unit, double *width, double *height))
+#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \
+   defined(PNG_FLOATING_POINT_SUPPORTED)
+/* NOTE: this API is currently implemented using floating point arithmetic,
+ * consequently it can only be used on systems with floating point support.
+ * In any case the range of values supported by png_fixed_point is small and it
+ * is highly recommended that png_get_sCAL_s be used instead.
+ */
+PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit,
+    png_fixed_point *width, png_fixed_point *height))
+#endif
+PNG_EXPORT(169, png_uint_32, png_get_sCAL_s,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit,
+    png_charpp swidth, png_charpp sheight));
+
+PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr,
+    png_inforp info_ptr, int unit, double width, double height))
+PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr,
+   png_inforp info_ptr, int unit, png_fixed_point width,
+   png_fixed_point height))
+PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr,
+    png_inforp info_ptr, int unit,
+    png_const_charp swidth, png_const_charp sheight));
+#endif /* PNG_sCAL_SUPPORTED */
+
+#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+/* Provide the default handling for all unknown chunks or, optionally, for
+ * specific unknown chunks.
+ *
+ * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was
+ * ignored and the default was used, the per-chunk setting only had an effect on
+ * write.  If you wish to have chunk-specific handling on read in code that must
+ * work on earlier versions you must use a user chunk callback to specify the
+ * desired handling (keep or discard.)
+ *
+ * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below.  The
+ * parameter is interpreted as follows:
+ *
+ * READ:
+ *    PNG_HANDLE_CHUNK_AS_DEFAULT:
+ *       Known chunks: do normal libpng processing, do not keep the chunk (but
+ *          see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
+ *       Unknown chunks: for a specific chunk use the global default, when used
+ *          as the default discard the chunk data.
+ *    PNG_HANDLE_CHUNK_NEVER:
+ *       Discard the chunk data.
+ *    PNG_HANDLE_CHUNK_IF_SAFE:
+ *       Keep the chunk data if the chunk is not critical else raise a chunk
+ *       error.
+ *    PNG_HANDLE_CHUNK_ALWAYS:
+ *       Keep the chunk data.
+ *
+ * If the chunk data is saved it can be retrieved using png_get_unknown_chunks,
+ * below.  Notice that specifying "AS_DEFAULT" as a global default is equivalent
+ * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks
+ * it simply resets the behavior to the libpng default.
+ *
+ * INTERACTION WTIH USER CHUNK CALLBACKS:
+ * The per-chunk handling is always used when there is a png_user_chunk_ptr
+ * callback and the callback returns 0; the chunk is then always stored *unless*
+ * it is critical and the per-chunk setting is other than ALWAYS.  Notice that
+ * the global default is *not* used in this case.  (In effect the per-chunk
+ * value is incremented to at least IF_SAFE.)
+ *
+ * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and
+ * per-chunk defaults will be honored.  If you want to preserve the current
+ * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE
+ * as the default - if you don't do this libpng 1.6 will issue a warning.
+ *
+ * If you want unhandled unknown chunks to be discarded in libpng 1.6 and
+ * earlier simply return '1' (handled).
+ *
+ * PNG_HANDLE_AS_UNKNOWN_SUPPORTED:
+ *    If this is *not* set known chunks will always be handled by libpng and
+ *    will never be stored in the unknown chunk list.  Known chunks listed to
+ *    png_set_keep_unknown_chunks will have no effect.  If it is set then known
+ *    chunks listed with a keep other than AS_DEFAULT will *never* be processed
+ *    by libpng, in addition critical chunks must either be processed by the
+ *    callback or saved.
+ *
+ *    The IHDR and IEND chunks must not be listed.  Because this turns off the
+ *    default handling for chunks that would otherwise be recognized the
+ *    behavior of libpng transformations may well become incorrect!
+ *
+ * WRITE:
+ *    When writing chunks the options only apply to the chunks specified by
+ *    png_set_unknown_chunks (below), libpng will *always* write known chunks
+ *    required by png_set_ calls and will always write the core critical chunks
+ *    (as required for PLTE).
+ *
+ *    Each chunk in the png_set_unknown_chunks list is looked up in the
+ *    png_set_keep_unknown_chunks list to find the keep setting, this is then
+ *    interpreted as follows:
+ *
+ *    PNG_HANDLE_CHUNK_AS_DEFAULT:
+ *       Write safe-to-copy chunks and write other chunks if the global
+ *       default is set to _ALWAYS, otherwise don't write this chunk.
+ *    PNG_HANDLE_CHUNK_NEVER:
+ *       Do not write the chunk.
+ *    PNG_HANDLE_CHUNK_IF_SAFE:
+ *       Write the chunk if it is safe-to-copy, otherwise do not write it.
+ *    PNG_HANDLE_CHUNK_ALWAYS:
+ *       Write the chunk.
+ *
+ * Note that the default behavior is effectively the opposite of the read case -
+ * in read unknown chunks are not stored by default, in write they are written
+ * by default.  Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different
+ * - on write the safe-to-copy bit is checked, on read the critical bit is
+ * checked and on read if the chunk is critical an error will be raised.
+ *
+ * num_chunks:
+ * ===========
+ *    If num_chunks is positive, then the "keep" parameter specifies the manner
+ *    for handling only those chunks appearing in the chunk_list array,
+ *    otherwise the chunk list array is ignored.
+ *
+ *    If num_chunks is 0 the "keep" parameter specifies the default behavior for
+ *    unknown chunks, as described above.
+ *
+ *    If num_chunks is negative, then the "keep" parameter specifies the manner
+ *    for handling all unknown chunks plus all chunks recognized by libpng
+ *    except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to
+ *    be processed by libpng.
+ */
+PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr,
+    int keep, png_const_bytep chunk_list, int num_chunks));
+
+/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned;
+ * the result is therefore true (non-zero) if special handling is required,
+ * false for the default handling.
+ */
+PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr,
+    png_const_bytep chunk_name));
+#endif
+
+#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_const_unknown_chunkp unknowns,
+    int num_unknowns));
+   /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added
+    * unknowns to the location currently stored in the png_struct.  This is
+    * invariably the wrong value on write.  To fix this call the following API
+    * for each chunk in the list with the correct location.  If you know your
+    * code won't be compiled on earlier versions you can rely on
+    * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing
+    * the correct thing.
+    */
+
+PNG_EXPORT(175, void, png_set_unknown_chunk_location,
+    (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location));
+
+PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_unknown_chunkpp entries));
+#endif
+
+/* Png_free_data() will turn off the "valid" flag for anything it frees.
+ * If you need to turn it off for a chunk that your application has freed,
+ * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK);
+ */
+PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr,
+    png_inforp info_ptr, int mask));
+
+#ifdef PNG_INFO_IMAGE_SUPPORTED
+/* The "params" pointer is currently not used and is for future expansion. */
+PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr,
+    int transforms, png_voidp params));
+PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr,
+    int transforms, png_voidp params));
+#endif
+
+PNG_EXPORT(180, png_const_charp, png_get_copyright,
+    (png_const_structrp png_ptr));
+PNG_EXPORT(181, png_const_charp, png_get_header_ver,
+    (png_const_structrp png_ptr));
+PNG_EXPORT(182, png_const_charp, png_get_header_version,
+    (png_const_structrp png_ptr));
+PNG_EXPORT(183, png_const_charp, png_get_libpng_ver,
+    (png_const_structrp png_ptr));
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr,
+    png_uint_32 mng_features_permitted));
+#endif
+
+/* For use in png_set_keep_unknown, added to version 1.2.6 */
+#define PNG_HANDLE_CHUNK_AS_DEFAULT   0
+#define PNG_HANDLE_CHUNK_NEVER        1
+#define PNG_HANDLE_CHUNK_IF_SAFE      2
+#define PNG_HANDLE_CHUNK_ALWAYS       3
+#define PNG_HANDLE_CHUNK_LAST         4
+
+/* Strip the prepended error numbers ("#nnn ") from error and warning
+ * messages before passing them to the error or warning handler.
+ */
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr,
+    png_uint_32 strip_mode));
+#endif
+
+/* Added in libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr,
+    png_uint_32 user_width_max, png_uint_32 user_height_max));
+PNG_EXPORT(187, png_uint_32, png_get_user_width_max,
+    (png_const_structrp png_ptr));
+PNG_EXPORT(188, png_uint_32, png_get_user_height_max,
+    (png_const_structrp png_ptr));
+/* Added in libpng-1.4.0 */
+PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr,
+    png_uint_32 user_chunk_cache_max));
+PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max,
+    (png_const_structrp png_ptr));
+/* Added in libpng-1.4.1 */
+PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr,
+    png_alloc_size_t user_chunk_cache_max));
+PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max,
+    (png_const_structrp png_ptr));
+#endif
+
+#if defined(PNG_INCH_CONVERSIONS_SUPPORTED)
+PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+
+PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+
+PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr));
+
+PNG_FP_EXPORT(196, float, png_get_x_offset_inches,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
+PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+#endif
+
+PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr))
+#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
+PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed,
+    (png_const_structrp png_ptr, png_const_inforp info_ptr))
+#endif
+
+#  ifdef PNG_pHYs_SUPPORTED
+PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr,
+    png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
+    int *unit_type));
+#  endif /* PNG_pHYs_SUPPORTED */
+#endif  /* PNG_INCH_CONVERSIONS_SUPPORTED */
+
+/* Added in libpng-1.4.0 */
+#ifdef PNG_IO_STATE_SUPPORTED
+PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr));
+
+/* Removed from libpng 1.6; use png_get_io_chunk_type. */
+PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr),
+    PNG_DEPRECATED)
+
+PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
+    (png_const_structrp png_ptr));
+
+/* The flags returned by png_get_io_state() are the following: */
+#  define PNG_IO_NONE        0x0000   /* no I/O at this moment */
+#  define PNG_IO_READING     0x0001   /* currently reading */
+#  define PNG_IO_WRITING     0x0002   /* currently writing */
+#  define PNG_IO_SIGNATURE   0x0010   /* currently at the file signature */
+#  define PNG_IO_CHUNK_HDR   0x0020   /* currently at the chunk header */
+#  define PNG_IO_CHUNK_DATA  0x0040   /* currently at the chunk data */
+#  define PNG_IO_CHUNK_CRC   0x0080   /* currently at the chunk crc */
+#  define PNG_IO_MASK_OP     0x000f   /* current operation: reading/writing */
+#  define PNG_IO_MASK_LOC    0x00f0   /* current location: sig/hdr/data/crc */
+#endif /* ?PNG_IO_STATE_SUPPORTED */
+
+/* Interlace support.  The following macros are always defined so that if
+ * libpng interlace handling is turned off the macros may be used to handle
+ * interlaced images within the application.
+ */
+#define PNG_INTERLACE_ADAM7_PASSES 7
+
+/* Two macros to return the first row and first column of the original,
+ * full, image which appears in a given pass.  'pass' is in the range 0
+ * to 6 and the result is in the range 0 to 7.
+ */
+#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7)
+#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7)
+
+/* A macro to return the offset between pixels in the output row for a pair of
+ * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that
+ * follows.  Note that ROW_OFFSET is the offset from one row to the next whereas
+ * COL_OFFSET is from one column to the next, within a row.
+ */
+#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8)
+#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1))
+
+/* Two macros to help evaluate the number of rows or columns in each
+ * pass.  This is expressed as a shift - effectively log2 of the number or
+ * rows or columns in each 8x8 tile of the original image.
+ */
+#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
+#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
+
+/* Hence two macros to determine the number of rows or columns in a given
+ * pass of an image given its height or width.  In fact these macros may
+ * return non-zero even though the sub-image is empty, because the other
+ * dimension may be empty for a small image.
+ */
+#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
+   -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
+#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
+   -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
+
+/* For the reader row callbacks (both progressive and sequential) it is
+ * necessary to find the row in the output image given a row in an interlaced
+ * image, so two more macros:
+ */
+#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \
+   (((y_in)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
+#define PNG_COL_FROM_PASS_COL(x_in, pass) \
+   (((x_in)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
+
+/* Two macros which return a boolean (0 or 1) saying whether the given row
+ * or column is in a particular pass.  These use a common utility macro that
+ * returns a mask for a given pass - the offset 'off' selects the row or
+ * column version.  The mask has the appropriate bit set for each column in
+ * the tile.
+ */
+#define PNG_PASS_MASK(pass,off) ( \
+   ((0x110145AF>>(((7-(off))-(pass))<<2)) & 0xF) | \
+   ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0))
+
+#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
+   ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
+#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
+   ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
+
+#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED
+/* With these routines we avoid an integer divide, which will be slower on
+ * most machines.  However, it does take more operations than the corresponding
+ * divide method, so it may be slower on a few RISC systems.  There are two
+ * shifts (by 8 or 16 bits) and an addition, versus a single integer divide.
+ *
+ * Note that the rounding factors are NOT supposed to be the same!  128 and
+ * 32768 are correct for the NODIV code; 127 and 32767 are correct for the
+ * standard method.
+ *
+ * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ]
+ */
+
+ /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */
+
+#  define png_composite(composite, fg, alpha, bg)         \
+     { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \
+           * (png_uint_16)(alpha)                         \
+           + (png_uint_16)(bg)*(png_uint_16)(255          \
+           - (png_uint_16)(alpha)) + 128);                \
+       (composite) = (png_byte)((temp + (temp >> 8)) >> 8); }
+
+#  define png_composite_16(composite, fg, alpha, bg)       \
+     { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg)  \
+           * (png_uint_32)(alpha)                          \
+           + (png_uint_32)(bg)*(65535                      \
+           - (png_uint_32)(alpha)) + 32768);               \
+       (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); }
+
+#else  /* Standard method using integer division */
+
+#  define png_composite(composite, fg, alpha, bg)                          \
+     (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) +  \
+     (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) +       \
+     127) / 255)
+
+#  define png_composite_16(composite, fg, alpha, bg)                         \
+     (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \
+     (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) +         \
+     32767) / 65535)
+#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */
+
+#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
+PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf));
+PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf));
+PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf));
+#endif
+
+PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr,
+    png_const_bytep buf));
+/* No png_get_int_16 -- may be added if there's a real need for it. */
+
+/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */
+#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i));
+#endif
+#ifdef PNG_SAVE_INT_32_SUPPORTED
+PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i));
+#endif
+
+/* Place a 16-bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
+/* No png_save_int_16 -- may be added if there's a real need for it. */
+#endif
+
+#ifdef PNG_USE_READ_MACROS
+/* Inline macros to do direct reads of bytes from the input buffer.
+ * The png_get_int_32() routine assumes we are using two's complement
+ * format for negative values, which is almost certainly true.
+ */
+#  define PNG_get_uint_32(buf) \
+     (((png_uint_32)(*(buf)) << 24) + \
+      ((png_uint_32)(*((buf) + 1)) << 16) + \
+      ((png_uint_32)(*((buf) + 2)) << 8) + \
+      ((png_uint_32)(*((buf) + 3))))
+
+   /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the
+    * function) incorrectly returned a value of type png_uint_32.
+    */
+#  define PNG_get_uint_16(buf) \
+     ((png_uint_16) \
+      (((unsigned int)(*(buf)) << 8) + \
+       ((unsigned int)(*((buf) + 1)))))
+
+#  define PNG_get_int_32(buf) \
+     ((png_int_32)((*(buf) & 0x80) \
+      ? -((png_int_32)((png_get_uint_32(buf) ^ 0xffffffffL) + 1)) \
+      : (png_int_32)png_get_uint_32(buf)))
+
+   /* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h,
+    * but defining a macro name prefixed with PNG_PREFIX.
+    */
+#  ifndef PNG_PREFIX
+#     define png_get_uint_32(buf) PNG_get_uint_32(buf)
+#     define png_get_uint_16(buf) PNG_get_uint_16(buf)
+#     define png_get_int_32(buf)  PNG_get_int_32(buf)
+#  endif
+#else
+#  ifdef PNG_PREFIX
+      /* No macros; revert to the (redefined) function */
+#     define PNG_get_uint_32 (png_get_uint_32)
+#     define PNG_get_uint_16 (png_get_uint_16)
+#     define PNG_get_int_32  (png_get_int_32)
+#  endif
+#endif
+
+/*******************************************************************************
+ *  SIMPLIFIED API
+ *******************************************************************************
+ *
+ * Please read the documentation in libpng-manual.txt (TODO: write said
+ * documentation) if you don't understand what follows.
+ *
+ * The simplified API hides the details of both libpng and the PNG file format
+ * itself.  It allows PNG files to be read into a very limited number of
+ * in-memory bitmap formats or to be written from the same formats.  If these
+ * formats do not accomodate your needs then you can, and should, use the more
+ * sophisticated APIs above - these support a wide variety of in-memory formats
+ * and a wide variety of sophisticated transformations to those formats as well
+ * as a wide variety of APIs to manipulate ancillary information.
+ *
+ * To read a PNG file using the simplified API:
+ *
+ * 1) Declare a 'png_image' structure (see below) on the stack and set the
+ *    version field to PNG_IMAGE_VERSION.
+ * 2) Call the appropriate png_image_begin_read... function.
+ * 3) Set the png_image 'format' member to the required sample format.
+ * 4) Allocate a buffer for the image and, if required, the color-map.
+ * 5) Call png_image_finish_read to read the image and, if required, the
+ *    color-map into your buffers.
+ *
+ * There are no restrictions on the format of the PNG input itself; all valid
+ * color types, bit depths, and interlace methods are acceptable, and the
+ * input image is transformed as necessary to the requested in-memory format
+ * during the png_image_finish_read() step.  The only caveat is that if you
+ * request a color-mapped image from a PNG that is full-color or makes
+ * complex use of an alpha channel the transformation is extremely lossy and the
+ * result may look terrible.
+ *
+ * To write a PNG file using the simplified API:
+ *
+ * 1) Declare a 'png_image' structure on the stack and memset() it to all zero.
+ * 2) Initialize the members of the structure that describe the image, setting
+ *    the 'format' member to the format of the image samples.
+ * 3) Call the appropriate png_image_write... function with a pointer to the
+ *    image and, if necessary, the color-map to write the PNG data.
+ *
+ * png_image is a structure that describes the in-memory format of an image
+ * when it is being read or defines the in-memory format of an image that you
+ * need to write:
+ */
+#define PNG_IMAGE_VERSION 1
+
+typedef struct png_control *png_controlp;
+typedef struct
+{
+   png_controlp opaque;    /* Initialize to NULL, free with png_image_free */
+   png_uint_32  version;   /* Set to PNG_IMAGE_VERSION */
+   png_uint_32  width;     /* Image width in pixels (columns) */
+   png_uint_32  height;    /* Image height in pixels (rows) */
+   png_uint_32  format;    /* Image format as defined below */
+   png_uint_32  flags;     /* A bit mask containing informational flags */
+   png_uint_32  colormap_entries;
+                           /* Number of entries in the color-map */
+
+   /* In the event of an error or warning the following field will be set to a
+    * non-zero value and the 'message' field will contain a '\0' terminated
+    * string with the libpng error or warning message.  If both warnings and
+    * an error were encountered, only the error is recorded.  If there
+    * are multiple warnings, only the first one is recorded.
+    *
+    * The upper 30 bits of this value are reserved, the low two bits contain
+    * a value as follows:
+    */
+#  define PNG_IMAGE_WARNING 1
+#  define PNG_IMAGE_ERROR 2
+   /*
+    * The result is a two bit code such that a value more than 1 indicates
+    * a failure in the API just called:
+    *
+    *    0 - no warning or error
+    *    1 - warning
+    *    2 - error
+    *    3 - error preceded by warning
+    */
+#  define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1)
+
+   png_uint_32  warning_or_error;
+
+   char         message[64];
+} png_image, *png_imagep;
+
+/* The samples of the image have one to four channels whose components have
+ * original values in the range 0 to 1.0:
+ *
+ * 1: A single gray or luminance channel (G).
+ * 2: A gray/luminance channel and an alpha channel (GA).
+ * 3: Three red, green, blue color channels (RGB).
+ * 4: Three color channels and an alpha channel (RGBA).
+ *
+ * The components are encoded in one of two ways:
+ *
+ * a) As a small integer, value 0..255, contained in a single byte.  For the
+ * alpha channel the original value is simply value/255.  For the color or
+ * luminance channels the value is encoded according to the sRGB specification
+ * and matches the 8-bit format expected by typical display devices.
+ *
+ * The color/gray channels are not scaled (pre-multiplied) by the alpha
+ * channel and are suitable for passing to color management software.
+ *
+ * b) As a value in the range 0..65535, contained in a 2-byte integer.  All
+ * channels can be converted to the original value by dividing by 65535; all
+ * channels are linear.  Color channels use the RGB encoding (RGB end-points) of
+ * the sRGB specification.  This encoding is identified by the
+ * PNG_FORMAT_FLAG_LINEAR flag below.
+ *
+ * When the simplified API needs to convert between sRGB and linear colorspaces,
+ * the actual sRGB transfer curve defined in the sRGB specification (see the
+ * article at http://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2
+ * approximation used elsewhere in libpng.
+ *
+ * When an alpha channel is present it is expected to denote pixel coverage
+ * of the color or luminance channels and is returned as an associated alpha
+ * channel: the color/gray channels are scaled (pre-multiplied) by the alpha
+ * value.
+ *
+ * The samples are either contained directly in the image data, between 1 and 8
+ * bytes per pixel according to the encoding, or are held in a color-map indexed
+ * by bytes in the image data.  In the case of a color-map the color-map entries
+ * are individual samples, encoded as above, and the image data has one byte per
+ * pixel to select the relevant sample from the color-map.
+ */
+
+/* PNG_FORMAT_*
+ *
+ * #defines to be used in png_image::format.  Each #define identifies a
+ * particular layout of sample data and, if present, alpha values.  There are
+ * separate defines for each of the two component encodings.
+ *
+ * A format is built up using single bit flag values.  All combinations are
+ * valid.  Formats can be built up from the flag values or you can use one of
+ * the predefined values below.  When testing formats always use the FORMAT_FLAG
+ * macros to test for individual features - future versions of the library may
+ * add new flags.
+ *
+ * When reading or writing color-mapped images the format should be set to the
+ * format of the entries in the color-map then png_image_{read,write}_colormap
+ * called to read or write the color-map and set the format correctly for the
+ * image data.  Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly!
+ *
+ * NOTE: libpng can be built with particular features disabled, if you see
+ * compiler errors because the definition of one of the following flags has been
+ * compiled out it is because libpng does not have the required support.  It is
+ * possible, however, for the libpng configuration to enable the format on just
+ * read or just write; in that case you may see an error at run time.  You can
+ * guard against this by checking for the definition of the appropriate
+ * "_SUPPORTED" macro, one of:
+ *
+ *    PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED
+ */
+#define PNG_FORMAT_FLAG_ALPHA    0x01U /* format with an alpha channel */
+#define PNG_FORMAT_FLAG_COLOR    0x02U /* color format: otherwise grayscale */
+#define PNG_FORMAT_FLAG_LINEAR   0x04U /* 2 byte channels else 1 byte */
+#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */
+
+#ifdef PNG_FORMAT_BGR_SUPPORTED
+#  define PNG_FORMAT_FLAG_BGR    0x10U /* BGR colors, else order is RGB */
+#endif
+
+#ifdef PNG_FORMAT_AFIRST_SUPPORTED
+#  define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */
+#endif
+
+/* Commonly used formats have predefined macros.
+ *
+ * First the single byte (sRGB) formats:
+ */
+#define PNG_FORMAT_GRAY 0
+#define PNG_FORMAT_GA   PNG_FORMAT_FLAG_ALPHA
+#define PNG_FORMAT_AG   (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST)
+#define PNG_FORMAT_RGB  PNG_FORMAT_FLAG_COLOR
+#define PNG_FORMAT_BGR  (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR)
+#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA)
+#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST)
+#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA)
+#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST)
+
+/* Then the linear 2-byte formats.  When naming these "Y" is used to
+ * indicate a luminance (gray) channel.
+ */
+#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR
+#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA)
+#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR)
+#define PNG_FORMAT_LINEAR_RGB_ALPHA \
+   (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA)
+
+/* With color-mapped formats the image data is one byte for each pixel, the byte
+ * is an index into the color-map which is formatted as above.  To obtain a
+ * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP
+ * to one of the above definitions, or you can use one of the definitions below.
+ */
+#define PNG_FORMAT_RGB_COLORMAP  (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP)
+#define PNG_FORMAT_BGR_COLORMAP  (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP)
+#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP)
+#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP)
+#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP)
+#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP)
+
+/* PNG_IMAGE macros
+ *
+ * These are convenience macros to derive information from a png_image
+ * structure.  The PNG_IMAGE_SAMPLE_ macros return values appropriate to the
+ * actual image sample values - either the entries in the color-map or the
+ * pixels in the image.  The PNG_IMAGE_PIXEL_ macros return corresponding values
+ * for the pixels and will always return 1 for color-mapped formats.  The
+ * remaining macros return information about the rows in the image and the
+ * complete image.
+ *
+ * NOTE: All the macros that take a png_image::format parameter are compile time
+ * constants if the format parameter is, itself, a constant.  Therefore these
+ * macros can be used in array declarations and case labels where required.
+ * Similarly the macros are also pre-processor constants (sizeof is not used) so
+ * they can be used in #if tests.
+ *
+ * First the information about the samples.
+ */
+#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\
+   (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1)
+   /* Return the total number of channels in a given format: 1..4 */
+
+#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\
+   ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1)
+   /* Return the size in bytes of a single component of a pixel or color-map
+    * entry (as appropriate) in the image: 1 or 2.
+    */
+
+#define PNG_IMAGE_SAMPLE_SIZE(fmt)\
+   (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt))
+   /* This is the size of the sample data for one sample.  If the image is
+    * color-mapped it is the size of one color-map entry (and image pixels are
+    * one byte in size), otherwise it is the size of one image pixel.
+    */
+
+#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\
+   (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256)
+   /* The maximum size of the color-map required by the format expressed in a
+    * count of components.  This can be used to compile-time allocate a
+    * color-map:
+    *
+    * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)];
+    *
+    * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)];
+    *
+    * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the
+    * information from one of the png_image_begin_read_ APIs and dynamically
+    * allocate the required memory.
+    */
+
+/* Corresponding information about the pixels */
+#define PNG_IMAGE_PIXEL_(test,fmt)\
+   (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt))
+
+#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\
+   PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt)
+   /* The number of separate channels (components) in a pixel; 1 for a
+    * color-mapped image.
+    */
+
+#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\
+   PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt)
+   /* The size, in bytes, of each component in a pixel; 1 for a color-mapped
+    * image.
+    */
+
+#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt)
+   /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */
+
+/* Information about the whole row, or whole image */
+#define PNG_IMAGE_ROW_STRIDE(image)\
+   (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width)
+   /* Return the total number of components in a single row of the image; this
+    * is the minimum 'row stride', the minimum count of components between each
+    * row.  For a color-mapped image this is the minimum number of bytes in a
+    * row.
+    */
+
+#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\
+   (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride))
+   /* Return the size, in bytes, of an image buffer given a png_image and a row
+    * stride - the number of components to leave space for in each row.
+    */
+
+#define PNG_IMAGE_SIZE(image)\
+   PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image))
+   /* Return the size, in bytes, of the image in memory given just a png_image;
+    * the row stride is the minimum stride required for the image.
+    */
+
+#define PNG_IMAGE_COLORMAP_SIZE(image)\
+   (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries)
+   /* Return the size, in bytes, of the color-map of this image.  If the image
+    * format is not a color-map format this will return a size sufficient for
+    * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if
+    * you don't want to allocate a color-map in this case.
+    */
+
+/* PNG_IMAGE_FLAG_*
+ *
+ * Flags containing additional information about the image are held in the
+ * 'flags' field of png_image.
+ */
+#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01
+   /* This indicates the the RGB values of the in-memory bitmap do not
+    * correspond to the red, green and blue end-points defined by sRGB.
+    */
+
+#define PNG_IMAGE_FLAG_FAST 0x02
+   /* On write emphasise speed over compression; the resultant PNG file will be
+    * larger but will be produced significantly faster, particular for large
+    * images.  Do not use this option for images which will be distributed, only
+    * used it when producing intermediate files that will be read back in
+    * repeatedly.  For a typical 24-bit image the option will double the read
+    * speed at the cost of increasing the image size by 25%, however for many
+    * more compressible images the PNG file can be 10 times larger with only a
+    * slight speed gain.
+    */
+
+#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04
+   /* On read if the image is a 16-bit per component image and there is no gAMA
+    * or sRGB chunk assume that the components are sRGB encoded.  Notice that
+    * images output by the simplified API always have gamma information; setting
+    * this flag only affects the interpretation of 16-bit images from an
+    * external source.  It is recommended that the application expose this flag
+    * to the user; the user can normally easily recognize the difference between
+    * linear and sRGB encoding.  This flag has no effect on write - the data
+    * passed to the write APIs must have the correct encoding (as defined
+    * above.)
+    *
+    * If the flag is not set (the default) input 16-bit per component data is
+    * assumed to be linear.
+    *
+    * NOTE: the flag can only be set after the png_image_begin_read_ call,
+    * because that call initializes the 'flags' field.
+    */
+
+#ifdef PNG_SIMPLIFIED_READ_SUPPORTED
+/* READ APIs
+ * ---------
+ *
+ * The png_image passed to the read APIs must have been initialized by setting
+ * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.)
+ */
+#ifdef PNG_STDIO_SUPPORTED
+PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image,
+   const char *file_name));
+   /* The named file is opened for read and the image header is filled in
+    * from the PNG header in the file.
+    */
+
+PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image,
+   FILE* file));
+   /* The PNG header is read from the stdio FILE object. */
+#endif /* PNG_STDIO_SUPPORTED */
+
+PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image,
+   png_const_voidp memory, png_size_t size));
+   /* The PNG header is read from the given memory buffer. */
+
+PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image,
+   png_const_colorp background, void *buffer, png_int_32 row_stride,
+   void *colormap));
+   /* Finish reading the image into the supplied buffer and clean up the
+    * png_image structure.
+    *
+    * row_stride is the step, in byte or 2-byte units as appropriate,
+    * between adjacent rows.  A positive stride indicates that the top-most row
+    * is first in the buffer - the normal top-down arrangement.  A negative
+    * stride indicates that the bottom-most row is first in the buffer.
+    *
+    * background need only be supplied if an alpha channel must be removed from
+    * a png_byte format and the removal is to be done by compositing on a solid
+    * color; otherwise it may be NULL and any composition will be done directly
+    * onto the buffer.  The value is an sRGB color to use for the background,
+    * for grayscale output the green channel is used.
+    *
+    * background must be supplied when an alpha channel must be removed from a
+    * single byte color-mapped output format, in other words if:
+    *
+    * 1) The original format from png_image_begin_read_from_* had
+    *    PNG_FORMAT_FLAG_ALPHA set.
+    * 2) The format set by the application does not.
+    * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and
+    *    PNG_FORMAT_FLAG_LINEAR *not* set.
+    *
+    * For linear output removing the alpha channel is always done by compositing
+    * on black and background is ignored.
+    *
+    * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set.  It must
+    * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE.
+    * image->colormap_entries will be updated to the actual number of entries
+    * written to the colormap; this may be less than the original value.
+    */
+
+PNG_EXPORT(238, void, png_image_free, (png_imagep image));
+   /* Free any data allocated by libpng in image->opaque, setting the pointer to
+    * NULL.  May be called at any time after the structure is initialized.
+    */
+#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */
+
+#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED
+/* WRITE APIS
+ * ----------
+ * For write you must initialize a png_image structure to describe the image to
+ * be written.  To do this use memset to set the whole structure to 0 then
+ * initialize fields describing your image.
+ *
+ * version: must be set to PNG_IMAGE_VERSION
+ * opaque: must be initialized to NULL
+ * width: image width in pixels
+ * height: image height in rows
+ * format: the format of the data (image and color-map) you wish to write
+ * flags: set to 0 unless one of the defined flags applies; set
+ *    PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB
+ *    values do not correspond to the colors in sRGB.
+ * colormap_entries: set to the number of entries in the color-map (0 to 256)
+ */
+PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image,
+   const char *file, int convert_to_8bit, const void *buffer,
+   png_int_32 row_stride, const void *colormap));
+   /* Write the image to the named file. */
+
+PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
+   int convert_to_8_bit, const void *buffer, png_int_32 row_stride,
+   const void *colormap));
+   /* Write the image to the given (FILE*). */
+
+/* With both write APIs if image is in one of the linear formats with 16-bit
+ * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG
+ * gamma encoded according to the sRGB specification, otherwise a 16-bit linear
+ * encoded PNG file is written.
+ *
+ * With color-mapped data formats the colormap parameter point to a color-map
+ * with at least image->colormap_entries encoded in the specified format.  If
+ * the format is linear the written PNG color-map will be converted to sRGB
+ * regardless of the convert_to_8_bit flag.
+ *
+ * With all APIs row_stride is handled as in the read APIs - it is the spacing
+ * from one row to the next in component sized units (1 or 2 bytes) and if
+ * negative indicates a bottom-up row layout in the buffer.
+ *
+ * Note that the write API does not support interlacing or sub-8-bit pixels.
+ */
+#endif /* PNG_SIMPLIFIED_WRITE_SUPPORTED */
+/*******************************************************************************
+ *  END OF SIMPLIFIED API
+ ******************************************************************************/
+
+#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+PNG_EXPORT(242, void, png_set_check_for_invalid_index,
+    (png_structrp png_ptr, int allowed));
+#  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
+PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr,
+    png_const_infop info_ptr));
+#  endif
+#endif /* CHECK_FOR_INVALID_INDEX */
+
+/*******************************************************************************
+ *  IMPLEMENTATION OPTIONS
+ *******************************************************************************
+ *
+ * Support for arbitrary implementation-specific optimizations.  The API allows
+ * particular options to be turned on or off.  'Option' is the number of the
+ * option and 'onoff' is 0 (off) or non-0 (on).  The value returned is given
+ * by the PNG_OPTION_ defines below.
+ *
+ * HARDWARE: normally hardware capabilites, such as the Intel SSE instructions,
+ *           are detected at run time, however sometimes it may be impossible
+ *           to do this in user mode, in which case it is necessary to discover
+ *           the capabilities in an OS specific way.  Such capabilities are
+ *           listed here when libpng has support for them and must be turned
+ *           ON by the application if present.
+ *
+ * SOFTWARE: sometimes software optimizations actually result in performance
+ *           decrease on some architectures or systems, or with some sets of
+ *           PNG images.  'Software' options allow such optimizations to be
+ *           selected at run time.
+ */
+#ifdef PNG_SET_OPTION_SUPPORTED
+#ifdef PNG_ARM_NEON_API_SUPPORTED
+#  define PNG_ARM_NEON   0 /* HARDWARE: ARM Neon SIMD instructions supported */
+#endif
+#define PNG_OPTION_NEXT  2 /* Next option - numbers must be even */
+
+/* Return values: NOTE: there are four values and 'off' is *not* zero */
+#define PNG_OPTION_UNSET   0 /* Unset - defaults to off */
+#define PNG_OPTION_INVALID 1 /* Option number out of range */
+#define PNG_OPTION_OFF     2
+#define PNG_OPTION_ON      3
+
+PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option,
+   int onoff));
+#endif
+
+/*******************************************************************************
+ *  END OF HARDWARE OPTIONS
+ ******************************************************************************/
+
+/* Maintainer: Put new public prototypes here ^, in libpng.3, and project
+ * defs, scripts/pnglibconf.h, and scripts/pnglibconf.h.prebuilt
+ */
+
+/* The last ordinal number (this is the *last* one already used; the next
+ * one to use is one more than this.)  Maintainer, remember to add an entry to
+ * scripts/symbols.def as well.
+ */
+#ifdef PNG_EXPORT_LAST_ORDINAL
+  PNG_EXPORT_LAST_ORDINAL(244);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PNG_VERSION_INFO_ONLY */
+/* Do not put anything past this line */
+#endif /* PNG_H */
diff --git a/fb2png/libpng/include/pngconf.h b/fb2png/libpng/include/pngconf.h
new file mode 100644
index 0000000..d9dbff0
--- /dev/null
+++ b/fb2png/libpng/include/pngconf.h
@@ -0,0 +1,616 @@
+
+/* pngconf.h - machine configurable file for libpng
+ *
+ * libpng version 1.6.1 - March 28, 2013
+ *
+ * Copyright (c) 1998-2013 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ */
+
+/* Any machine specific code is near the front of this file, so if you
+ * are configuring libpng for a machine, you may want to read the section
+ * starting here down to where it starts to typedef png_color, png_text,
+ * and png_info.
+ */
+
+#ifndef PNGCONF_H
+#define PNGCONF_H
+
+/* To do: Do all of this in scripts/pnglibconf.dfa */
+#ifdef PNG_SAFE_LIMITS_SUPPORTED
+#  ifdef PNG_USER_WIDTH_MAX
+#    undef PNG_USER_WIDTH_MAX
+#    define PNG_USER_WIDTH_MAX 1000000L
+#  endif
+#  ifdef PNG_USER_HEIGHT_MAX
+#    undef PNG_USER_HEIGHT_MAX
+#    define PNG_USER_HEIGHT_MAX 1000000L
+#  endif
+#  ifdef PNG_USER_CHUNK_MALLOC_MAX
+#    undef PNG_USER_CHUNK_MALLOC_MAX
+#    define PNG_USER_CHUNK_MALLOC_MAX 4000000L
+#  endif
+#  ifdef PNG_USER_CHUNK_CACHE_MAX
+#    undef PNG_USER_CHUNK_CACHE_MAX
+#    define PNG_USER_CHUNK_CACHE_MAX 128
+#  endif
+#endif
+
+#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */
+
+/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C
+ * compiler for correct compilation.  The following header files are required by
+ * the standard.  If your compiler doesn't provide these header files, or they
+ * do not match the standard, you will need to provide/improve them.
+ */
+#include <limits.h>
+#include <stddef.h>
+
+/* Library header files.  These header files are all defined by ISOC90; libpng
+ * expects conformant implementations, however, an ISOC90 conformant system need
+ * not provide these header files if the functionality cannot be implemented.
+ * In this case it will be necessary to disable the relevant parts of libpng in
+ * the build of pnglibconf.h.
+ *
+ * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not
+ * include this unnecessary header file.
+ */
+
+#ifdef PNG_STDIO_SUPPORTED
+   /* Required for the definition of FILE: */
+#  include <stdio.h>
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* Required for the definition of jmp_buf and the declaration of longjmp: */
+#  include <setjmp.h>
+#endif
+
+#ifdef PNG_CONVERT_tIME_SUPPORTED
+   /* Required for struct tm: */
+#  include <time.h>
+#endif
+
+#endif /* PNG_BUILDING_SYMBOL_TABLE */
+
+/* Prior to 1.6.0 it was possible to turn off 'const' in declarations using
+ * PNG_NO_CONST; this is no longer supported except for data declarations which
+ * apparently still cause problems in 2011 on some compilers.
+ */
+#define PNG_CONST const /* backward compatibility only */
+
+/* This controls optimization of the reading of 16 and 32 bit values
+ * from PNG files.  It can be set on a per-app-file basis - it
+ * just changes whether a macro is used when the function is called.
+ * The library builder sets the default; if read functions are not
+ * built into the library the macro implementation is forced on.
+ */
+#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED
+#  define PNG_USE_READ_MACROS
+#endif
+#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS)
+#  if PNG_DEFAULT_READ_MACROS
+#    define PNG_USE_READ_MACROS
+#  endif
+#endif
+
+/* COMPILER SPECIFIC OPTIONS.
+ *
+ * These options are provided so that a variety of difficult compilers
+ * can be used.  Some are fixed at build time (e.g. PNG_API_RULE
+ * below) but still have compiler specific implementations, others
+ * may be changed on a per-file basis when compiling against libpng.
+ */
+
+/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect
+ * against legacy (pre ISOC90) compilers that did not understand function
+ * prototypes.  It is not required for modern C compilers.
+ */
+#ifndef PNGARG
+#  define PNGARG(arglist) arglist
+#endif
+
+/* Function calling conventions.
+ * =============================
+ * Normally it is not necessary to specify to the compiler how to call
+ * a function - it just does it - however on x86 systems derived from
+ * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems
+ * and some others) there are multiple ways to call a function and the
+ * default can be changed on the compiler command line.  For this reason
+ * libpng specifies the calling convention of every exported function and
+ * every function called via a user supplied function pointer.  This is
+ * done in this file by defining the following macros:
+ *
+ * PNGAPI    Calling convention for exported functions.
+ * PNGCBAPI  Calling convention for user provided (callback) functions.
+ * PNGCAPI   Calling convention used by the ANSI-C library (required
+ *           for longjmp callbacks and sometimes used internally to
+ *           specify the calling convention for zlib).
+ *
+ * These macros should never be overridden.  If it is necessary to
+ * change calling convention in a private build this can be done
+ * by setting PNG_API_RULE (which defaults to 0) to one of the values
+ * below to select the correct 'API' variants.
+ *
+ * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout.
+ *                This is correct in every known environment.
+ * PNG_API_RULE=1 Use the operating system convention for PNGAPI and
+ *                the 'C' calling convention (from PNGCAPI) for
+ *                callbacks (PNGCBAPI).  This is no longer required
+ *                in any known environment - if it has to be used
+ *                please post an explanation of the problem to the
+ *                libpng mailing list.
+ *
+ * These cases only differ if the operating system does not use the C
+ * calling convention, at present this just means the above cases
+ * (x86 DOS/Windows sytems) and, even then, this does not apply to
+ * Cygwin running on those systems.
+ *
+ * Note that the value must be defined in pnglibconf.h so that what
+ * the application uses to call the library matches the conventions
+ * set when building the library.
+ */
+
+/* Symbol export
+ * =============
+ * When building a shared library it is almost always necessary to tell
+ * the compiler which symbols to export.  The png.h macro 'PNG_EXPORT'
+ * is used to mark the symbols.  On some systems these symbols can be
+ * extracted at link time and need no special processing by the compiler,
+ * on other systems the symbols are flagged by the compiler and just
+ * the declaration requires a special tag applied (unfortunately) in a
+ * compiler dependent way.  Some systems can do either.
+ *
+ * A small number of older systems also require a symbol from a DLL to
+ * be flagged to the program that calls it.  This is a problem because
+ * we do not know in the header file included by application code that
+ * the symbol will come from a shared library, as opposed to a statically
+ * linked one.  For this reason the application must tell us by setting
+ * the magic flag PNG_USE_DLL to turn on the special processing before
+ * it includes png.h.
+ *
+ * Four additional macros are used to make this happen:
+ *
+ * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from
+ *            the build or imported if PNG_USE_DLL is set - compiler
+ *            and system specific.
+ *
+ * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to
+ *                       'type', compiler specific.
+ *
+ * PNG_DLL_EXPORT Set to the magic to use during a libpng build to
+ *                make a symbol exported from the DLL.  Not used in the
+ *                public header files; see pngpriv.h for how it is used
+ *                in the libpng build.
+ *
+ * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come
+ *                from a DLL - used to define PNG_IMPEXP when
+ *                PNG_USE_DLL is set.
+ */
+
+/* System specific discovery.
+ * ==========================
+ * This code is used at build time to find PNG_IMPEXP, the API settings
+ * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL
+ * import processing is possible.  On Windows systems it also sets
+ * compiler-specific macros to the values required to change the calling
+ * conventions of the various functions.
+ */
+#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\
+    defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+  /* Windows system (DOS doesn't support DLLs).  Includes builds under Cygwin or
+   * MinGW on any architecture currently supported by Windows.  Also includes
+   * Watcom builds but these need special treatment because they are not
+   * compatible with GCC or Visual C because of different calling conventions.
+   */
+#  if PNG_API_RULE == 2
+    /* If this line results in an error, either because __watcall is not
+     * understood or because of a redefine just below you cannot use *this*
+     * build of the library with the compiler you are using.  *This* build was
+     * build using Watcom and applications must also be built using Watcom!
+     */
+#    define PNGCAPI __watcall
+#  endif
+
+#  if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800))
+#    define PNGCAPI __cdecl
+#    if PNG_API_RULE == 1
+       /* If this line results in an error __stdcall is not understood and
+        * PNG_API_RULE should not have been set to '1'.
+        */
+#      define PNGAPI __stdcall
+#    endif
+#  else
+    /* An older compiler, or one not detected (erroneously) above,
+     * if necessary override on the command line to get the correct
+     * variants for the compiler.
+     */
+#    ifndef PNGCAPI
+#      define PNGCAPI _cdecl
+#    endif
+#    if PNG_API_RULE == 1 && !defined(PNGAPI)
+#      define PNGAPI _stdcall
+#    endif
+#  endif /* compiler/api */
+  /* NOTE: PNGCBAPI always defaults to PNGCAPI. */
+
+#  if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD)
+#     error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed"
+#  endif
+
+#  if (defined(_MSC_VER) && _MSC_VER < 800) ||\
+      (defined(__BORLANDC__) && __BORLANDC__ < 0x500)
+    /* older Borland and MSC
+     * compilers used '__export' and required this to be after
+     * the type.
+     */
+#    ifndef PNG_EXPORT_TYPE
+#      define PNG_EXPORT_TYPE(type) type PNG_IMPEXP
+#    endif
+#    define PNG_DLL_EXPORT __export
+#  else /* newer compiler */
+#    define PNG_DLL_EXPORT __declspec(dllexport)
+#    ifndef PNG_DLL_IMPORT
+#      define PNG_DLL_IMPORT __declspec(dllimport)
+#    endif
+#  endif /* compiler */
+
+#else /* !Windows */
+#  if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
+#    define PNGAPI _System
+#  else /* !Windows/x86 && !OS/2 */
+    /* Use the defaults, or define PNG*API on the command line (but
+     * this will have to be done for every compile!)
+     */
+#  endif /* other system, !OS/2 */
+#endif /* !Windows/x86 */
+
+/* Now do all the defaulting . */
+#ifndef PNGCAPI
+#  define PNGCAPI
+#endif
+#ifndef PNGCBAPI
+#  define PNGCBAPI PNGCAPI
+#endif
+#ifndef PNGAPI
+#  define PNGAPI PNGCAPI
+#endif
+
+/* PNG_IMPEXP may be set on the compilation system command line or (if not set)
+ * then in an internal header file when building the library, otherwise (when
+ * using the library) it is set here.
+ */
+#ifndef PNG_IMPEXP
+#  if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT)
+     /* This forces use of a DLL, disallowing static linking */
+#    define PNG_IMPEXP PNG_DLL_IMPORT
+#  endif
+
+#  ifndef PNG_IMPEXP
+#    define PNG_IMPEXP
+#  endif
+#endif
+
+/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat
+ * 'attributes' as a storage class - the attributes go at the start of the
+ * function definition, and attributes are always appended regardless of the
+ * compiler.  This considerably simplifies these macros but may cause problems
+ * if any compilers both need function attributes and fail to handle them as
+ * a storage class (this is unlikely.)
+ */
+#ifndef PNG_FUNCTION
+#  define PNG_FUNCTION(type, name, args, attributes) attributes type name args
+#endif
+
+#ifndef PNG_EXPORT_TYPE
+#  define PNG_EXPORT_TYPE(type) PNG_IMPEXP type
+#endif
+
+   /* The ordinal value is only relevant when preprocessing png.h for symbol
+    * table entries, so we discard it here.  See the .dfn files in the
+    * scripts directory.
+    */
+#ifndef PNG_EXPORTA
+
+#  define PNG_EXPORTA(ordinal, type, name, args, attributes)\
+      PNG_FUNCTION(PNG_EXPORT_TYPE(type),(PNGAPI name),PNGARG(args), \
+        extern attributes)
+#endif
+
+/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument,
+ * so make something non-empty to satisfy the requirement:
+ */
+#define PNG_EMPTY /*empty list*/
+
+#define PNG_EXPORT(ordinal, type, name, args)\
+   PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY)
+
+/* Use PNG_REMOVED to comment out a removed interface. */
+#ifndef PNG_REMOVED
+#  define PNG_REMOVED(ordinal, type, name, args, attributes)
+#endif
+
+#ifndef PNG_CALLBACK
+#  define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args)
+#endif
+
+/* Support for compiler specific function attributes.  These are used
+ * so that where compiler support is available incorrect use of API
+ * functions in png.h will generate compiler warnings.
+ *
+ * Added at libpng-1.2.41.
+ */
+
+#ifndef PNG_NO_PEDANTIC_WARNINGS
+#  ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED
+#    define PNG_PEDANTIC_WARNINGS_SUPPORTED
+#  endif
+#endif
+
+#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED
+  /* Support for compiler specific function attributes.  These are used
+   * so that where compiler support is available, incorrect use of API
+   * functions in png.h will generate compiler warnings.  Added at libpng
+   * version 1.2.41.  Disabling these removes the warnings but may also produce
+   * less efficient code.
+   */
+#  if defined(__GNUC__)
+#    ifndef PNG_USE_RESULT
+#      define PNG_USE_RESULT __attribute__((__warn_unused_result__))
+#    endif
+#    ifndef PNG_NORETURN
+#      define PNG_NORETURN   __attribute__((__noreturn__))
+#    endif
+#    if __GNUC__ >= 3
+#      ifndef PNG_ALLOCATED
+#        define PNG_ALLOCATED  __attribute__((__malloc__))
+#      endif
+#      ifndef PNG_DEPRECATED
+#        define PNG_DEPRECATED __attribute__((__deprecated__))
+#      endif
+#      ifndef PNG_PRIVATE
+#        if 0 /* Doesn't work so we use deprecated instead*/
+#          define PNG_PRIVATE \
+            __attribute__((warning("This function is not exported by libpng.")))
+#        else
+#          define PNG_PRIVATE \
+            __attribute__((__deprecated__))
+#        endif
+#      endif
+#      if ((__GNUC__ != 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1))
+#        ifndef PNG_RESTRICT
+#          define PNG_RESTRICT __restrict
+#        endif
+#      endif /*  __GNUC__ == 3.0 */
+#    endif /*  __GNUC__ >= 3 */
+
+#  elif defined(_MSC_VER)  && (_MSC_VER >= 1300)
+#    ifndef PNG_USE_RESULT
+#      define PNG_USE_RESULT /* not supported */
+#    endif
+#    ifndef PNG_NORETURN
+#      define PNG_NORETURN   __declspec(noreturn)
+#    endif
+#    ifndef PNG_ALLOCATED
+#      if (_MSC_VER >= 1400)
+#        define PNG_ALLOCATED __declspec(restrict)
+#      endif
+#    endif
+#    ifndef PNG_DEPRECATED
+#      define PNG_DEPRECATED __declspec(deprecated)
+#    endif
+#    ifndef PNG_PRIVATE
+#      define PNG_PRIVATE __declspec(deprecated)
+#    endif
+#    ifndef PNG_RESTRICT
+#      if (_MSC_VER >= 1400)
+#        define PNG_RESTRICT __restrict
+#      endif
+#    endif
+
+#  elif defined(__WATCOMC__)
+#    ifndef PNG_RESTRICT
+#      define PNG_RESTRICT __restrict
+#    endif
+#  endif /* _MSC_VER */
+#endif /* PNG_PEDANTIC_WARNINGS */
+
+#ifndef PNG_DEPRECATED
+#  define PNG_DEPRECATED  /* Use of this function is deprecated */
+#endif
+#ifndef PNG_USE_RESULT
+#  define PNG_USE_RESULT  /* The result of this function must be checked */
+#endif
+#ifndef PNG_NORETURN
+#  define PNG_NORETURN    /* This function does not return */
+#endif
+#ifndef PNG_ALLOCATED
+#  define PNG_ALLOCATED   /* The result of the function is new memory */
+#endif
+#ifndef PNG_PRIVATE
+#  define PNG_PRIVATE     /* This is a private libpng function */
+#endif
+#ifndef PNG_RESTRICT
+#  define PNG_RESTRICT    /* The C99 "restrict" feature */
+#endif
+#ifndef PNG_FP_EXPORT     /* A floating point API. */
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+#     define PNG_FP_EXPORT(ordinal, type, name, args)\
+         PNG_EXPORT(ordinal, type, name, args);
+#  else                   /* No floating point APIs */
+#     define PNG_FP_EXPORT(ordinal, type, name, args)
+#  endif
+#endif
+#ifndef PNG_FIXED_EXPORT  /* A fixed point API. */
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+#     define PNG_FIXED_EXPORT(ordinal, type, name, args)\
+         PNG_EXPORT(ordinal, type, name, args);
+#  else                   /* No fixed point APIs */
+#     define PNG_FIXED_EXPORT(ordinal, type, name, args)
+#  endif
+#endif
+
+#ifndef PNG_BUILDING_SYMBOL_TABLE
+/* Some typedefs to get us started.  These should be safe on most of the common
+ * platforms.
+ *
+ * png_uint_32 and png_int_32 may, currently, be larger than required to hold a
+ * 32-bit value however this is not normally advisable.
+ *
+ * png_uint_16 and png_int_16 should always be two bytes in size - this is
+ * verified at library build time.
+ *
+ * png_byte must always be one byte in size.
+ *
+ * The checks below use constants from limits.h, as defined by the ISOC90
+ * standard.
+ */
+#if CHAR_BIT == 8 && UCHAR_MAX == 255
+   typedef unsigned char png_byte;
+#else
+#  error "libpng requires 8 bit bytes"
+#endif
+
+#if INT_MIN == -32768 && INT_MAX == 32767
+   typedef int png_int_16;
+#elif SHRT_MIN == -32768 && SHRT_MAX == 32767
+   typedef short png_int_16;
+#else
+#  error "libpng requires a signed 16 bit type"
+#endif
+
+#if UINT_MAX == 65535
+   typedef unsigned int png_uint_16;
+#elif USHRT_MAX == 65535
+   typedef unsigned short png_uint_16;
+#else
+#  error "libpng requires an unsigned 16 bit type"
+#endif
+
+#if INT_MIN < -2147483646 && INT_MAX > 2147483646
+   typedef int png_int_32;
+#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646
+   typedef long int png_int_32;
+#else
+#  error "libpng requires a signed 32 bit (or more) type"
+#endif
+
+#if UINT_MAX > 4294967294
+   typedef unsigned int png_uint_32;
+#elif ULONG_MAX > 4294967294
+   typedef unsigned long int png_uint_32;
+#else
+#  error "libpng requires an unsigned 32 bit (or more) type"
+#endif
+
+/* Prior to 1.6.0 it was possible to disable the use of size_t, 1.6.0, however,
+ * requires an ISOC90 compiler and relies on consistent behavior of sizeof.
+ */
+typedef size_t png_size_t;
+typedef ptrdiff_t png_ptrdiff_t;
+
+/* libpng needs to know the maximum value of 'size_t' and this controls the
+ * definition of png_alloc_size_t, below.  This maximum value of size_t limits
+ * but does not control the maximum allocations the library makes - there is
+ * direct application control of this through png_set_user_limits().
+ */
+#ifndef PNG_SMALL_SIZE_T
+   /* Compiler specific tests for systems where size_t is known to be less than
+    * 32 bits (some of these systems may no longer work because of the lack of
+    * 'far' support; see above.)
+    */
+#  if (defined(__TURBOC__) && !defined(__FLAT__)) ||\
+   (defined(_MSC_VER) && defined(MAXSEG_64K))
+#     define PNG_SMALL_SIZE_T
+#  endif
+#endif
+
+/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, and no
+ * smaller than png_uint_32.  Casts from png_size_t or png_uint_32 to
+ * png_alloc_size_t are not necessary; in fact, it is recommended not to use
+ * them at all so that the compiler can complain when something turns out to be
+ * problematic.
+ *
+ * Casts in the other direction (from png_alloc_size_t to png_size_t or
+ * png_uint_32) should be explicitly applied; however, we do not expect to
+ * encounter practical situations that require such conversions.
+ *
+ * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than
+ * 4294967295 - i.e. less than the maximum value of png_uint_32.
+ */
+#ifdef PNG_SMALL_SIZE_T
+   typedef png_uint_32 png_alloc_size_t;
+#else
+   typedef png_size_t png_alloc_size_t;
+#endif
+
+/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler
+ * implementations of Intel CPU specific support of user-mode segmented address
+ * spaces, where 16-bit pointers address more than 65536 bytes of memory using
+ * separate 'segment' registers.  The implementation requires two different
+ * types of pointer (only one of which includes the segment value.)
+ *
+ * If required this support is available in version 1.2 of libpng and may be
+ * available in versions through 1.5, although the correctness of the code has
+ * not been verified recently.
+ */
+
+/* Typedef for floating-point numbers that are converted to fixed-point with a
+ * multiple of 100,000, e.g., gamma
+ */
+typedef png_int_32 png_fixed_point;
+
+/* Add typedefs for pointers */
+typedef void                  * png_voidp;
+typedef const void            * png_const_voidp;
+typedef png_byte              * png_bytep;
+typedef const png_byte        * png_const_bytep;
+typedef png_uint_32           * png_uint_32p;
+typedef const png_uint_32     * png_const_uint_32p;
+typedef png_int_32            * png_int_32p;
+typedef const png_int_32      * png_const_int_32p;
+typedef png_uint_16           * png_uint_16p;
+typedef const png_uint_16     * png_const_uint_16p;
+typedef png_int_16            * png_int_16p;
+typedef const png_int_16      * png_const_int_16p;
+typedef char                  * png_charp;
+typedef const char            * png_const_charp;
+typedef png_fixed_point       * png_fixed_point_p;
+typedef const png_fixed_point * png_const_fixed_point_p;
+typedef png_size_t            * png_size_tp;
+typedef const png_size_t      * png_const_size_tp;
+
+#ifdef PNG_STDIO_SUPPORTED
+typedef FILE            * png_FILE_p;
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double       * png_doublep;
+typedef const double * png_const_doublep;
+#endif
+
+/* Pointers to pointers; i.e. arrays */
+typedef png_byte        * * png_bytepp;
+typedef png_uint_32     * * png_uint_32pp;
+typedef png_int_32      * * png_int_32pp;
+typedef png_uint_16     * * png_uint_16pp;
+typedef png_int_16      * * png_int_16pp;
+typedef const char      * * png_const_charpp;
+typedef char            * * png_charpp;
+typedef png_fixed_point * * png_fixed_point_pp;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double          * * png_doublepp;
+#endif
+
+/* Pointers to pointers to pointers; i.e., pointer to array */
+typedef char            * * * png_charppp;
+
+#endif /* PNG_BUILDING_SYMBOL_TABLE */
+
+#endif /* PNGCONF_H */
diff --git a/fb2png/libpng/include/pnglibconf.h b/fb2png/libpng/include/pnglibconf.h
new file mode 100644
index 0000000..b9c75e8
--- /dev/null
+++ b/fb2png/libpng/include/pnglibconf.h
@@ -0,0 +1,209 @@
+/* pnglibconf.h - library build configuration */
+
+/* libpng version 1.6.1 - March 28, 2013 */
+
+/* Copyright (c) 1998-2012 Glenn Randers-Pehrson */
+
+/* This code is released under the libpng license. */
+/* For conditions of distribution and use, see the disclaimer */
+/* and license in png.h */
+
+/* pnglibconf.h */
+/* Machine generated file: DO NOT EDIT */
+/* Derived from: scripts/pnglibconf.dfa */
+#ifndef PNGLCONF_H
+#define PNGLCONF_H
+/* options */
+#define PNG_16BIT_SUPPORTED
+#define PNG_ALIGNED_MEMORY_SUPPORTED
+/*#undef PNG_ARM_NEON_API_SUPPORTED*/
+/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/
+/*#undef PNG_ARM_NEON_SUPPORTED*/
+#define PNG_BENIGN_ERRORS_SUPPORTED
+#define PNG_BENIGN_READ_ERRORS_SUPPORTED
+/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/
+#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
+#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#define PNG_COLORSPACE_SUPPORTED
+#define PNG_CONSOLE_IO_SUPPORTED
+#define PNG_CONVERT_tIME_SUPPORTED
+#define PNG_EASY_ACCESS_SUPPORTED
+/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
+#define PNG_ERROR_TEXT_SUPPORTED
+#define PNG_FIXED_POINT_SUPPORTED
+#define PNG_FLOATING_ARITHMETIC_SUPPORTED
+#define PNG_FLOATING_POINT_SUPPORTED
+#define PNG_FORMAT_AFIRST_SUPPORTED
+#define PNG_FORMAT_BGR_SUPPORTED
+#define PNG_GAMMA_SUPPORTED
+#define PNG_GET_PALETTE_MAX_SUPPORTED
+#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#define PNG_INCH_CONVERSIONS_SUPPORTED
+#define PNG_INFO_IMAGE_SUPPORTED
+#define PNG_IO_STATE_SUPPORTED
+#define PNG_MNG_FEATURES_SUPPORTED
+#define PNG_POINTER_INDEXING_SUPPORTED
+#define PNG_PROGRESSIVE_READ_SUPPORTED
+#define PNG_READ_16BIT_SUPPORTED
+#define PNG_READ_ALPHA_MODE_SUPPORTED
+#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_READ_BACKGROUND_SUPPORTED
+#define PNG_READ_BGR_SUPPORTED
+#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
+#define PNG_READ_COMPRESSED_TEXT_SUPPORTED
+#define PNG_READ_EXPAND_16_SUPPORTED
+#define PNG_READ_EXPAND_SUPPORTED
+#define PNG_READ_FILLER_SUPPORTED
+#define PNG_READ_GAMMA_SUPPORTED
+#define PNG_READ_GET_PALETTE_MAX_SUPPORTED
+#define PNG_READ_GRAY_TO_RGB_SUPPORTED
+#define PNG_READ_INTERLACING_SUPPORTED
+#define PNG_READ_INT_FUNCTIONS_SUPPORTED
+#define PNG_READ_INVERT_ALPHA_SUPPORTED
+#define PNG_READ_INVERT_SUPPORTED
+#define PNG_READ_OPT_PLTE_SUPPORTED
+#define PNG_READ_PACKSWAP_SUPPORTED
+#define PNG_READ_PACK_SUPPORTED
+#define PNG_READ_QUANTIZE_SUPPORTED
+#define PNG_READ_RGB_TO_GRAY_SUPPORTED
+#define PNG_READ_SCALE_16_TO_8_SUPPORTED
+#define PNG_READ_SHIFT_SUPPORTED
+#define PNG_READ_STRIP_16_TO_8_SUPPORTED
+#define PNG_READ_STRIP_ALPHA_SUPPORTED
+#define PNG_READ_SUPPORTED
+#define PNG_READ_SWAP_ALPHA_SUPPORTED
+#define PNG_READ_SWAP_SUPPORTED
+#define PNG_READ_TEXT_SUPPORTED
+#define PNG_READ_TRANSFORMS_SUPPORTED
+#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_READ_USER_CHUNKS_SUPPORTED
+#define PNG_READ_USER_TRANSFORM_SUPPORTED
+#define PNG_READ_bKGD_SUPPORTED
+#define PNG_READ_cHRM_SUPPORTED
+#define PNG_READ_gAMA_SUPPORTED
+#define PNG_READ_hIST_SUPPORTED
+#define PNG_READ_iCCP_SUPPORTED
+#define PNG_READ_iTXt_SUPPORTED
+#define PNG_READ_oFFs_SUPPORTED
+#define PNG_READ_pCAL_SUPPORTED
+#define PNG_READ_pHYs_SUPPORTED
+#define PNG_READ_sBIT_SUPPORTED
+#define PNG_READ_sCAL_SUPPORTED
+#define PNG_READ_sPLT_SUPPORTED
+#define PNG_READ_sRGB_SUPPORTED
+#define PNG_READ_tEXt_SUPPORTED
+#define PNG_READ_tIME_SUPPORTED
+#define PNG_READ_tRNS_SUPPORTED
+#define PNG_READ_zTXt_SUPPORTED
+/*#undef PNG_SAFE_LIMITS_SUPPORTED*/
+#define PNG_SAVE_INT_32_SUPPORTED
+#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_SEQUENTIAL_READ_SUPPORTED
+#define PNG_SETJMP_SUPPORTED
+#define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED
+#define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
+/*#undef PNG_SET_OPTION_SUPPORTED*/
+#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_SET_USER_LIMITS_SUPPORTED
+#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
+#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED
+#define PNG_SIMPLIFIED_READ_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_SUPPORTED
+#define PNG_STDIO_SUPPORTED
+#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_TEXT_SUPPORTED
+#define PNG_TIME_RFC1123_SUPPORTED
+#define PNG_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_USER_CHUNKS_SUPPORTED
+#define PNG_USER_LIMITS_SUPPORTED
+#define PNG_USER_MEM_SUPPORTED
+#define PNG_USER_TRANSFORM_INFO_SUPPORTED
+#define PNG_USER_TRANSFORM_PTR_SUPPORTED
+#define PNG_WARNINGS_SUPPORTED
+#define PNG_WRITE_16BIT_SUPPORTED
+#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_WRITE_BGR_SUPPORTED
+#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
+#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+#define PNG_WRITE_FILLER_SUPPORTED
+#define PNG_WRITE_FILTER_SUPPORTED
+#define PNG_WRITE_FLUSH_SUPPORTED
+#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED
+#define PNG_WRITE_INTERLACING_SUPPORTED
+#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
+#define PNG_WRITE_INVERT_SUPPORTED
+#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+#define PNG_WRITE_PACKSWAP_SUPPORTED
+#define PNG_WRITE_PACK_SUPPORTED
+#define PNG_WRITE_SHIFT_SUPPORTED
+#define PNG_WRITE_SUPPORTED
+#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
+#define PNG_WRITE_SWAP_SUPPORTED
+#define PNG_WRITE_TEXT_SUPPORTED
+#define PNG_WRITE_TRANSFORMS_SUPPORTED
+#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
+#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+#define PNG_WRITE_bKGD_SUPPORTED
+#define PNG_WRITE_cHRM_SUPPORTED
+#define PNG_WRITE_gAMA_SUPPORTED
+#define PNG_WRITE_hIST_SUPPORTED
+#define PNG_WRITE_iCCP_SUPPORTED
+#define PNG_WRITE_iTXt_SUPPORTED
+#define PNG_WRITE_oFFs_SUPPORTED
+#define PNG_WRITE_pCAL_SUPPORTED
+#define PNG_WRITE_pHYs_SUPPORTED
+#define PNG_WRITE_sBIT_SUPPORTED
+#define PNG_WRITE_sCAL_SUPPORTED
+#define PNG_WRITE_sPLT_SUPPORTED
+#define PNG_WRITE_sRGB_SUPPORTED
+#define PNG_WRITE_tEXt_SUPPORTED
+#define PNG_WRITE_tIME_SUPPORTED
+#define PNG_WRITE_tRNS_SUPPORTED
+#define PNG_WRITE_zTXt_SUPPORTED
+#define PNG_bKGD_SUPPORTED
+#define PNG_cHRM_SUPPORTED
+#define PNG_gAMA_SUPPORTED
+#define PNG_hIST_SUPPORTED
+#define PNG_iCCP_SUPPORTED
+#define PNG_iTXt_SUPPORTED
+#define PNG_oFFs_SUPPORTED
+#define PNG_pCAL_SUPPORTED
+#define PNG_pHYs_SUPPORTED
+#define PNG_sBIT_SUPPORTED
+#define PNG_sCAL_SUPPORTED
+#define PNG_sPLT_SUPPORTED
+#define PNG_sRGB_SUPPORTED
+#define PNG_tEXt_SUPPORTED
+#define PNG_tIME_SUPPORTED
+#define PNG_tRNS_SUPPORTED
+#define PNG_zTXt_SUPPORTED
+/* end of options */
+/* settings */
+#define PNG_API_RULE 0
+#define PNG_CALLOC_SUPPORTED
+#define PNG_COST_SHIFT 3
+#define PNG_DEFAULT_READ_MACROS 1
+#define PNG_GAMMA_THRESHOLD_FIXED 5000
+#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
+#define PNG_INFLATE_BUF_SIZE 1024
+#define PNG_MAX_GAMMA_8 11
+#define PNG_QUANTIZE_BLUE_BITS 5
+#define PNG_QUANTIZE_GREEN_BITS 5
+#define PNG_QUANTIZE_RED_BITS 5
+#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
+#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
+#define PNG_WEIGHT_SHIFT 8
+#define PNG_ZBUF_SIZE 8192
+#define PNG_Z_DEFAULT_COMPRESSION (-1)
+#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0
+#define PNG_Z_DEFAULT_STRATEGY 1
+#define PNG_sCAL_PRECISION 5
+#define PNG_sRGB_PROFILE_CHECKS 2
+/* end of settings */
+#endif /* PNGLCONF_H */
diff --git a/fb2png/libpng/lib/libpng.a b/fb2png/libpng/lib/libpng.a
new file mode 100644
index 0000000..5b6288c
--- /dev/null
+++ b/fb2png/libpng/lib/libpng.a
Binary files differ
diff --git a/fb2png/log.h b/fb2png/log.h
new file mode 100644
index 0000000..96e90d7
--- /dev/null
+++ b/fb2png/log.h
@@ -0,0 +1,85 @@
+/**
+ * fb2png  Save screenshot into .png.
+ *
+ * Copyright (C) 2012  Kyan <kyan.ql.he@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef __KYAN_LOG_H__
+#define __KYAN_LOG_H__
+
+#include <errno.h>
+
+#ifdef ANDROID_XXX
+
+#ifndef LOG_TAG
+#define LOG_TAG "tag"
+#endif
+
+#include <android/log.h>
+
+#define D LOGD
+#define E LOGE
+
+#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG  , LOG_TAG, __VA_ARGS__)
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO   , LOG_TAG, __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN   , LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , LOG_TAG, __VA_ARGS__)
+
+#else /* ANDROID */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#if DEBUG == 1
+
+#define LOG_FUNCTION_NAME \
+    fprintf(stderr, "\033[0;1;31m__func__: %s\033[0;0m\n", __FUNCTION__);
+
+#else
+
+#define LOG_FUNCTION_NAME
+
+#endif
+
+static void
+D(const char *msg, ...)
+{
+    va_list ap;
+
+    va_start (ap, msg);
+    vfprintf(stdout, msg, ap);
+    fprintf(stdout, "\n");
+    va_end (ap);
+    fflush(stdout);
+}
+
+static void
+E(const char *msg, ...)
+{
+    va_list ap;
+
+    va_start (ap, msg);
+    vfprintf(stderr, msg, ap);
+    fprintf(stderr, ", %s", strerror(errno));
+    fprintf(stderr, "\n");
+    va_end (ap);
+}
+
+#endif /* ANDROID */
+
+#endif
diff --git a/fb2png/main.c b/fb2png/main.c
new file mode 100644
index 0000000..84e08a9
--- /dev/null
+++ b/fb2png/main.c
@@ -0,0 +1,61 @@
+/**
+ * fb2png  Save screenshot into .png.
+ *
+ * Copyright (C) 2012  Kyan <kyan.ql.he@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include "fb2png.h"
+
+#ifdef ANDROID
+    #define DEFAULT_SAVE_PATH "/data/local/fbdump.png"
+#else
+    #define DEFAULT_SAVE_PATH "fbdump.png"
+#endif
+
+int main(int argc, char *argv[])
+{
+    char fn[PATH_MAX];
+    int ret;
+
+    if (argc == 2 && argv[1][0] != '-') {
+        if (strlen(argv[1]) >= PATH_MAX) {
+            printf("Output path is too long!\n");
+            exit(-1);
+        }
+        sprintf(fn, "%s", argv[1]);
+    } else if (argc == 1) {
+        sprintf(fn, "%s", DEFAULT_SAVE_PATH);
+    } else {
+        //if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+        printf(
+            "Android Screenshooter - fb2png\n"
+            "Author: Kyan He <kyan.ql.he@gmail.com>\n"
+            "Modified by  Phil3759 & McKael @xda\n"
+            "Base version 0.0.2 ---> v0.0.5  <2013>\n"
+            "Usage: fb2png [path/to/output.png]\n"
+            "    The default output path is /data/local/fbdump.png\n"
+            );
+        exit(0);
+    }
+
+    if (0 == (ret = fb2png(fn)))
+        printf("Saved image to %s\n", fn);
+
+    exit(ret);
+}
diff --git a/fb2png/run.sh b/fb2png/run.sh
new file mode 100644
index 0000000..e7e0354
--- /dev/null
+++ b/fb2png/run.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# helper script for capture picture on device
+#
+# Kyan He <kyan.ql.he@gmail.com> @ Tue Feb 15 12:42:48 CST 2011
+#
+#
+
+ADB_OPTIONS=
+PNG="/data/local/fbdump.png"
+
+if [ ! "$FB2PNG" = "" ];
+then
+
+adb $ADB_OPTIONS push $FB2PNG /data/local
+adb $ADB_OPTIONS shell chmod 777 /data/local
+adb $ADB_OPTIONS shell /data/local/fb2png
+
+adb $ADB_OPTIONS pull $PNG
+adb $ADB_OPTIONS shell rm $PNG
+else
+    echo "define \$FB2PNG first"
+fi
diff --git a/fb2png/view888 b/fb2png/view888
new file mode 100644
index 0000000..6e10dcd
--- /dev/null
+++ b/fb2png/view888
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+# view argb8888
+#
+# Modified: 
+# Kyan He <kyan.ql.he@gmail.com> @ Tue Feb 15 01:45:54 CST 2011
+#
+# Initial version
+# Kyan He <kyan.ql.he@gmail.com> @ Mon Sep 20 11:45:54 CST 2010
+#
+
+if ! which ffmpeg >/dev/null;
+then
+    echo "no ffmpeg found"
+elif [[ ! $# -eq 2 ]];
+then
+    echo "Usage: `basename $0` <data.argb8888> <width>x<height>"
+else
+    ffmpeg -vcodec rawvideo -f rawvideo -pix_fmt rgb24 -s $2 -i $1 -f image2 -vcodec png $1.png
+fi
+
diff --git a/find_file.cpp b/find_file.cpp
new file mode 100644
index 0000000..8ae0a51
--- /dev/null
+++ b/find_file.cpp
@@ -0,0 +1,90 @@
+/*
+		Copyright 2014 TeamWin
+		This file is part of TWRP/TeamWin Recovery Project.
+
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string>
+#include <vector>
+#include <dirent.h>
+#include "find_file.hpp"
+#include "twrp-functions.hpp"
+#include "twcommon.h"
+
+using namespace std;
+
+string Find_File::Find(const string& file_name, const string& start_path) {
+	return Find_File().Find_Internal(file_name, start_path);
+}
+
+Find_File::Find_File() {
+}
+
+string Find_File::Find_Internal(const string& filename, const string& starting_path) {
+	DIR *d;
+	string new_path, return_path;
+	vector<string> dirs;
+	vector<string> symlinks;
+	unsigned index;
+
+	// Check to see if we have already searched this directory to prevent infinite loops
+	if (std::find(searched_dirs.begin(), searched_dirs.end(), starting_path) != searched_dirs.end()) {
+		return "";
+	}
+	searched_dirs.push_back(starting_path);
+
+	d = opendir(starting_path.c_str());
+	if (d == NULL) {
+		LOGERR("Find_File: Error opening '%s'\n", starting_path.c_str());
+		return "";
+	}
+
+	struct dirent *p;
+	while ((p = readdir(d))) {
+		if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
+			continue;
+		new_path = starting_path + "/";
+		new_path.append(p->d_name);
+		if (p->d_type == DT_DIR) {
+			// Add dir to search list for later
+			dirs.push_back(new_path);
+		} else if (p->d_type == DT_LNK) {
+			// Add symlink to search list for later
+			symlinks.push_back(new_path);
+		} else if (p->d_type == DT_REG && filename == p->d_name) {
+			// We found a match!
+			closedir(d);
+			return new_path;
+		}
+	}
+	closedir(d);
+
+	// Scan real directories first if no match found in this path
+	for (index = 0; index < dirs.size(); index++) {
+		return_path = Find_Internal(filename, dirs.at(index));
+		if (!return_path.empty()) return return_path;
+	}
+	// Scan symlinks after scanning real directories
+	for (index = 0; index < symlinks.size(); index++) {
+		char buf[PATH_MAX];
+		// Resolve symlink to a real path
+		char* ret = realpath(symlinks.at(index).c_str(), buf);
+		if (ret) {
+			return_path = Find_Internal(filename, buf);
+			if (!return_path.empty()) return return_path;
+		}
+	}
+	return "";
+}
diff --git a/find_file.hpp b/find_file.hpp
new file mode 100644
index 0000000..8833e9a
--- /dev/null
+++ b/find_file.hpp
@@ -0,0 +1,37 @@
+/*
+        Copyright 2014 TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef Find_File_HPP
+#define Find_File_HPP
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+class Find_File {
+
+public:
+	static string Find(const string& file_name, const string& start_path);
+private:
+	Find_File();
+	string Find_Internal(const string& filename, const string& starting_path);
+	vector<string> searched_dirs;
+};
+
+#endif
diff --git a/fixPermissions.cpp b/fixPermissions.cpp
new file mode 100644
index 0000000..5d57b47
--- /dev/null
+++ b/fixPermissions.cpp
@@ -0,0 +1,683 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include "gui/rapidxml.hpp"
+#include "fixPermissions.hpp"
+#include "twrp-functions.hpp"
+#include "twcommon.h"
+#ifdef HAVE_SELINUX
+#include "selinux/selinux.h"
+#include "selinux/label.h"
+#include "selinux/android.h"
+#include "selinux/label.h"
+#endif
+
+using namespace std;
+using namespace rapidxml;
+
+#ifdef HAVE_SELINUX
+struct selabel_handle *sehandle;
+struct selinux_opt selinux_options[] = {
+	{ SELABEL_OPT_PATH, "/file_contexts" }
+};
+
+int fixPermissions::restorecon(string entry, struct stat *sb) {
+	char *oldcontext, *newcontext;
+	if (lgetfilecon(entry.c_str(), &oldcontext) < 0) {
+		LOGINFO("Couldn't get selinux context for %s\n", entry.c_str());
+		return -1;
+	}
+	if (selabel_lookup(sehandle, &newcontext, entry.c_str(), sb->st_mode) < 0) {
+		LOGINFO("Couldn't lookup selinux context for %s\n", entry.c_str());
+		return -1;
+	}
+	if (strcmp(oldcontext, newcontext) != 0) {
+		LOGINFO("Relabeling %s from %s to %s\n", entry.c_str(), oldcontext, newcontext);
+		if (lsetfilecon(entry.c_str(), newcontext) < 0) {
+			LOGINFO("Couldn't label %s with %s: %s\n", entry.c_str(), newcontext, strerror(errno));
+		}
+	}
+	freecon(oldcontext);
+	freecon(newcontext);
+	return 0;
+}
+
+int fixPermissions::fixDataDataContexts(void) {
+	string dir = "/data/data/";
+	sehandle = selabel_open(SELABEL_CTX_FILE, selinux_options, 1);
+	if (!sehandle) {
+		LOGINFO("Unable to open /file_contexts\n");
+		return 0;
+	}
+	if (TWFunc::Path_Exists(dir)) {
+		fixContextsRecursively(dir, 0);
+	}
+	selabel_close(sehandle);
+	return 0;
+}
+
+int fixPermissions::fixContextsRecursively(string name, int level) {
+	DIR *d;
+	struct dirent *de;
+	struct stat sb;
+	string path;
+
+	if (!(d = opendir(name.c_str())))
+		return -1;
+	if (!(de = readdir(d)))
+		return -1;
+
+	do {
+		if (de->d_type ==  DT_DIR) {
+			if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+				continue;
+			path = name + "/" + de->d_name;
+			restorecon(path, &sb);
+			fixContextsRecursively(path, level + 1);
+		}
+		else {
+			path = name + "/" + de->d_name;
+			restorecon(path, &sb);
+		}
+	} while (de = readdir(d));
+	closedir(d);
+	return 0;
+}
+
+int fixPermissions::fixDataInternalContexts(void) {
+	DIR *d;
+	struct dirent *de;
+	struct stat sb;
+	string dir, androiddir;
+	sehandle = selabel_open(SELABEL_CTX_FILE, selinux_options, 1);
+	if (!sehandle) {
+		LOGINFO("Unable to open /file_contexts\n");
+		return 0;
+	}
+	if (TWFunc::Path_Exists("/data/media/0"))
+		dir = "/data/media/0";
+	else
+		dir = "/data/media";
+	if (!TWFunc::Path_Exists(dir)) {
+		LOGINFO("fixDataInternalContexts: '%s' does not exist!\n", dir.c_str());
+		return 0;
+	}
+	LOGINFO("Fixing %s contexts\n", dir.c_str());
+	restorecon(dir, &sb);
+	d = opendir(dir.c_str());
+
+	while (( de = readdir(d)) != NULL) {
+		stat(de->d_name, &sb);
+		string f;
+		f = dir + "/" + de->d_name;
+		restorecon(f, &sb);
+	}
+	closedir(d);
+
+	androiddir = dir + "/Android/";
+	if (TWFunc::Path_Exists(androiddir)) {
+		fixContextsRecursively(androiddir, 0);
+	}
+	selabel_close(sehandle);
+	return 0;
+}
+#endif
+
+int fixPermissions::fixPerms(bool enable_debug, bool remove_data_for_missing_apps) {
+	packageFile = "/data/system/packages.xml";
+	debug = enable_debug;
+	remove_data = remove_data_for_missing_apps;
+	multi_user = TWFunc::Path_Exists("/data/user");
+
+	if (!(TWFunc::Path_Exists(packageFile))) {
+		gui_print("Can't check permissions\n");
+		gui_print("after Factory Reset.\n");
+		gui_print("Please boot rom and try\n");
+		gui_print("again after you reboot into\n");
+		gui_print("recovery.\n");
+		return -1;
+	}
+
+	gui_print("Fixing permissions...\nLoading packages...\n");
+	if ((getPackages()) != 0) {
+		return -1;
+	}
+
+	gui_print("Fixing /system/app permissions...\n");
+	if ((fixSystemApps()) != 0) {
+		return -1;
+	}
+
+	gui_print("Fixing /data/app permissions...\n");
+	if ((fixDataApps()) != 0) {
+		return -1;
+	}
+
+	if (multi_user) {
+		DIR *d = opendir("/data/user");
+		string new_path, user_id;
+
+		if (d == NULL) {
+			LOGERR("Error opening '/data/user'\n");
+			return -1;
+		}
+
+		if (d) {
+			struct dirent *p;
+			while ((p = readdir(d))) {
+				if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
+					continue;
+
+				new_path = "/data/user/";
+				new_path.append(p->d_name);
+				user_id = "u";
+				user_id += p->d_name;
+				user_id += "_";
+				if (p->d_type == DT_LNK) {
+					char link[512], realPath[512];
+					strcpy(link, new_path.c_str());
+					memset(realPath, 0, sizeof(realPath));
+					while (readlink(link, realPath, sizeof(realPath)) > 0) {
+						strcpy(link, realPath);
+						memset(realPath, 0, sizeof(realPath));
+					}
+					new_path = link;
+				} else if (p->d_type != DT_DIR) {
+					continue;
+				} else {
+					new_path.append("/");
+					// We're probably going to need to fix permissions on multi user but
+					// it will have to wait for another time. Need to figure out where
+					// the uid and gid is stored for other users.
+					continue;
+				}
+				gui_print("Fixing %s permissions...\n", new_path.c_str());
+				if ((fixDataData(new_path)) != 0) {
+					closedir(d);
+					return -1;
+				}
+			}
+			closedir(d);
+		}
+	} else {
+		gui_print("Fixing /data/data permissions...\n");
+		if ((fixDataData("/data/data/")) != 0) {
+			return -1;
+		}
+	}
+	#ifdef HAVE_SELINUX
+	gui_print("Fixing /data/data/ contexts.\n");
+	fixDataDataContexts();
+	fixDataInternalContexts();
+	#endif
+	gui_print("Done fixing permissions.\n");
+	return 0;
+}
+
+int fixPermissions::pchown(string fn, int puid, int pgid) {
+	LOGINFO("Fixing %s, uid: %d, gid: %d\n", fn.c_str(), puid, pgid);
+	if (chown(fn.c_str(), puid, pgid) != 0) {
+		LOGERR("Unable to chown '%s' %i %i\n", fn.c_str(), puid, pgid);
+		return -1;
+	}
+	return 0;
+}
+
+int fixPermissions::pchmod(string fn, string mode) {
+	long mask = 0;
+	LOGINFO("Fixing %s, mode: %s\n", fn.c_str(), mode.c_str());
+	for ( std::string::size_type n = 0; n < mode.length(); ++n) {
+		if (n == 0) {
+			if (mode[n] == '0')
+				continue;
+			else if (mode[n] == '1')
+				mask = S_ISVTX;
+			else if (mode[n] == '2')
+				mask = S_ISGID;
+		}
+		else if (n == 1) {
+			if (mode[n] == '7') {
+				mask |= S_IRWXU;
+			}
+			if (mode[n] == '6') {
+				mask |= S_IRUSR;
+				mask |= S_IWUSR;
+			}
+			if (mode[n] == '5') {
+				mask |= S_IRUSR;
+				mask |= S_IXUSR;
+			}
+			if (mode[n] == '4')
+				mask |= S_IRUSR;
+			if (mode[n] == '3') {
+				mask |= S_IWUSR;
+				mask |= S_IRUSR;
+			}
+			if (mode[n] == '2')
+				mask |= S_IWUSR;
+			if (mode[n] == '1')
+				mask |= S_IXUSR;
+		}
+		else if (n == 2) {
+			if (mode[n] == '7') {
+				mask |= S_IRWXG;
+			}
+			if (mode[n] == '6') {
+				mask |= S_IRGRP;
+				mask |= S_IWGRP;
+			}
+			if (mode[n] == '5') {
+				mask |= S_IRGRP;
+				mask |= S_IXGRP;
+			}
+			if (mode[n] == '4')
+				mask |= S_IRGRP;
+			if (mode[n] == '3') {
+				mask |= S_IWGRP;
+				mask |= S_IXGRP;
+			}
+			if (mode[n] == '2')
+				mask |= S_IWGRP;
+			if (mode[n] == '1')
+				mask |= S_IXGRP;
+		}
+		else if (n == 3) {
+			if (mode[n] == '7') {
+				mask |= S_IRWXO;
+			}
+			if (mode[n] == '6') {
+				mask |= S_IROTH;
+				mask |= S_IWOTH;
+			}
+			if (mode[n] == '5') {
+				mask |= S_IROTH;
+				mask |= S_IXOTH;
+			}
+			if (mode[n] == '4')
+				mask |= S_IROTH;
+			if (mode[n] == '3') {
+				mask |= S_IWOTH;
+				mask |= S_IXOTH;
+			}
+			if (mode[n] == '2')
+				mask |= S_IWOTH;
+			if (mode[n] == '1')
+				mask |= S_IXOTH;
+		}
+	}
+
+	if (chmod(fn.c_str(), mask) != 0) {
+		LOGERR("Unable to chmod '%s' %l\n", fn.c_str(), mask);
+		return -1;
+	}
+
+	return 0;
+}
+
+int fixPermissions::fixSystemApps() {
+	temp = head;
+	while (temp != NULL) {
+		if (TWFunc::Path_Exists(temp->codePath)) {
+			if (temp->appDir.compare("/system/app") == 0 || temp->appDir.compare("/system/priv-app") == 0) {
+				if (debug)	{
+					LOGINFO("Looking at '%s'\n", temp->codePath.c_str());
+					LOGINFO("Fixing permissions on '%s'\n", temp->pkgName.c_str());
+					LOGINFO("Directory: '%s'\n", temp->appDir.c_str());
+					LOGINFO("Original package owner: %d, group: %d\n", temp->uid, temp->gid);
+				}
+				if (pchown(temp->codePath, 0, 0) != 0)
+					return -1;
+				if (pchmod(temp->codePath, "0644") != 0)
+					return -1;
+			}
+		} else {
+			//Remove data directory since app isn't installed
+			if (remove_data && TWFunc::Path_Exists(temp->dDir) && temp->appDir.size() >= 9 && temp->appDir.substr(0, 9) != "/mnt/asec") {
+				if (debug)
+					LOGINFO("Looking at '%s', removing data dir: '%s', appDir: '%s'", temp->codePath.c_str(), temp->dDir.c_str(), temp->appDir.c_str());
+				if (TWFunc::removeDir(temp->dDir, false) != 0) {
+					LOGINFO("Unable to removeDir '%s'\n", temp->dDir.c_str());
+					return -1;
+				}
+			}
+		}
+		temp = temp->next;
+	}
+	return 0;
+}
+
+int fixPermissions::fixDataApps() {
+	bool fix = false;
+	int new_gid = 0;
+	string perms = "0000";
+
+	temp = head;
+	while (temp != NULL) {
+		if (TWFunc::Path_Exists(temp->codePath)) {
+			if (temp->appDir.compare("/data/app") == 0 || temp->appDir.compare("/sd-ext/app") == 0) {
+				fix = true;
+				new_gid = 1000;
+				perms = "0644";
+			} else if (temp->appDir.compare("/data/app-private") == 0 || temp->appDir.compare("/sd-ext/app-private") == 0) {
+				fix = true;
+				new_gid = temp->gid;
+				perms = "0640";
+			} else
+				fix = false;
+			if (fix) {
+				if (debug) {
+					LOGINFO("Looking at '%s'\n", temp->codePath.c_str());
+					LOGINFO("Fixing permissions on '%s'\n", temp->pkgName.c_str());
+					LOGINFO("Directory: '%s'\n", temp->appDir.c_str());
+					LOGINFO("Original package owner: %d, group: %d\n", temp->uid, temp->gid);
+				}
+				if (pchown(temp->codePath, 1000, new_gid) != 0)
+					return -1;
+				if (pchmod(temp->codePath, perms) != 0)
+					return -1;
+			}
+		} else {
+			//Remove data directory since app isn't installed
+			if (remove_data && TWFunc::Path_Exists(temp->dDir) && temp->appDir.size() >= 9 && temp->appDir.substr(0, 9) != "/mnt/asec") {
+				if (debug)
+					LOGINFO("Looking at '%s', removing data dir: '%s', appDir: '%s'", temp->codePath.c_str(), temp->dDir.c_str(), temp->appDir.c_str());
+				if (TWFunc::removeDir(temp->dDir, false) != 0) {
+					LOGINFO("Unable to removeDir '%s'\n", temp->dDir.c_str());
+					return -1;
+				}
+			}
+		}
+		temp = temp->next;
+	}
+	return 0;
+}
+
+int fixPermissions::fixAllFiles(string directory, int gid, int uid, string file_perms) {
+	vector <string> files;
+	string file;
+
+	files = listAllFiles(directory);
+	for (unsigned i = 0; i < files.size(); ++i) {
+		file = directory + "/";
+		file.append(files.at(i));
+		if (debug)
+			LOGINFO("Looking at file '%s'\n", file.c_str());
+		if (pchmod(file, file_perms) != 0)
+			return -1;
+		if (pchown(file, uid, gid) != 0)
+			return -1;
+	}
+	return 0;
+}
+
+int fixPermissions::fixDataData(string dataDir) {
+	string directory, dir;
+
+	temp = head;
+	while (temp != NULL) {
+		dir = dataDir + temp->dDir;
+		if (TWFunc::Path_Exists(dir)) {
+			vector <string> dataDataDirs = listAllDirectories(dir);
+			for (unsigned n = 0; n < dataDataDirs.size(); ++n) {
+				directory = dir + "/";
+				directory.append(dataDataDirs.at(n));
+				if (debug)
+					LOGINFO("Looking at data directory: '%s'\n", directory.c_str());
+				if (dataDataDirs.at(n) == ".") {
+					if (pchmod(directory, "0755") != 0)
+						return -1;
+					if (pchown(directory.c_str(), temp->uid, temp->gid) != 0)
+						return -1;
+					if (fixAllFiles(directory, temp->uid, temp->gid, "0755") != 0)
+						return -1;
+				}
+				else if (dataDataDirs.at(n) == "..") {
+					if (debug)
+						LOGINFO("Skipping ..\n");
+					continue;
+				}
+				else if (dataDataDirs.at(n) == "lib") {
+					if (pchmod(directory.c_str(), "0755") != 0)
+						return -1;
+					if (pchown(directory.c_str(), 1000, 1000) != 0)
+						return -1;
+					if (fixAllFiles(directory, temp->uid, temp->gid, "0755") != 0)
+						return -1;
+				}
+				else if (dataDataDirs.at(n) == "shared_prefs") {
+					if (pchmod(directory.c_str(), "0771") != 0)
+						return -1;
+					if (pchown(directory.c_str(), temp->uid, temp->gid) != 0)
+						return -1;
+					if (fixAllFiles(directory, temp->uid, temp->gid, "0660") != 0)
+						return -1;
+				}
+				else if (dataDataDirs.at(n) == "databases") {
+					if (pchmod(directory.c_str(), "0771") != 0)
+						return -1;
+					if (pchown(directory.c_str(), temp->uid, temp->gid) != 0)
+						return -1;
+					if (fixAllFiles(directory, temp->uid, temp->gid, "0660") != 0)
+						return -1;
+				}
+				else if (dataDataDirs.at(n) == "cache") {
+					if (pchmod(directory.c_str(), "0771") != 0)
+						return -1;
+					if (pchown(directory.c_str(), temp->uid, temp->gid) != 0)
+						return -1;
+					if (fixAllFiles(directory, temp->uid, temp->gid, "0600") != 0)
+						return -1;
+				}
+				else {
+					if (pchmod(directory.c_str(), "0771") != 0)
+						return -1;
+					if (pchown(directory.c_str(), temp->uid, temp->gid) != 0)
+						return -1;
+					if (fixAllFiles(directory, temp->uid, temp->gid, "0755") != 0)
+						return -1;
+				}
+			}
+		}
+		temp = temp->next;
+	}
+	return 0;
+}
+
+vector <string> fixPermissions::listAllDirectories(string path) {
+	DIR *dir = opendir(path.c_str());
+	vector <string> dirs;
+
+	if (dir == NULL) {
+		LOGERR("Error opening '%s'\n", path.c_str());
+		return dirs;
+	}
+	struct dirent *entry = readdir(dir);
+	while (entry != NULL) {
+		if (entry->d_type == DT_DIR)
+			dirs.push_back(entry->d_name);
+		entry = readdir(dir);
+	}
+	closedir(dir);
+	return dirs;
+}
+
+vector <string> fixPermissions::listAllFiles(string path) {
+	DIR *dir = opendir(path.c_str());
+	vector <string> files;
+
+	if (dir == NULL) {
+		LOGERR("Error opening '%s'\n", path.c_str());
+		return files;
+	}
+	struct dirent *entry = readdir(dir);
+	while (entry != NULL) {
+		if (entry->d_type == DT_REG)
+			files.push_back(entry->d_name);
+		entry = readdir(dir);
+	}
+	closedir(dir);
+	return files;
+}
+
+int fixPermissions::getPackages() {
+	int len = 0;
+	bool skiploop = false;
+	vector <string> skip;
+	string name;
+	head = NULL;
+
+	skip.push_back("/system/framework/framework-res.apk");
+	skip.push_back("/system/framework/com.htc.resources.apk");
+
+	ifstream xmlFile(packageFile.c_str());
+	xmlFile.seekg(0, ios::end);
+	len = (int) xmlFile.tellg();
+	xmlFile.seekg(0, ios::beg);
+	char xmlBuf[len + 1];
+	xmlFile.read(&xmlBuf[0], len);
+	xmlBuf[len] = '\0';
+	xml_document<> pkgDoc;
+	LOGINFO("parsing package, %i...\n", len);
+	pkgDoc.parse<parse_full>(&xmlBuf[0]);
+
+	xml_node<> * pkgNode = pkgDoc.first_node("packages");
+	if (pkgNode == NULL) {
+		LOGERR("No packages found to fix.\n");
+		return -1;
+	}
+	xml_node <> * next = pkgNode->first_node("package");
+	if (next == NULL) {
+		LOGERR("No package found to fix.\n");
+		return -1;
+	}
+
+	//Get packages
+	while (next->first_attribute("name") != NULL) {
+		package* temp = new package;
+		for (unsigned n = 0; n < skip.size(); ++n) {
+			if (skip.at(n).compare(next->first_attribute("codePath")->value()) == 0) {
+				skiploop = true;
+				break;
+			}
+		}
+
+		if (skiploop == true) {
+			if (debug)
+				LOGINFO("Skipping package %s\n", next->first_attribute("codePath")->value());
+			free(temp);
+			next = next->next_sibling();
+			skiploop = false;
+			continue;
+		}
+		name.append((next->first_attribute("name")->value()));
+		temp->pkgName = next->first_attribute("name")->value();
+		if (debug)
+			LOGINFO("Loading pkg: %s\n", next->first_attribute("name")->value());
+		if (next->first_attribute("codePath") == NULL) {
+			LOGINFO("Problem with codePath on %s\n", next->first_attribute("name")->value());
+		} else {
+			temp->codePath = next->first_attribute("codePath")->value();
+			temp->app = basename(next->first_attribute("codePath")->value());
+			temp->appDir = dirname(next->first_attribute("codePath")->value());
+		}
+		temp->dDir = name;
+		if ( next->first_attribute("sharedUserId") != NULL) {
+			temp->uid = atoi(next->first_attribute("sharedUserId")->value());
+			temp->gid = atoi(next->first_attribute("sharedUserId")->value());
+		}
+		else {
+			if (next->first_attribute("userId") == NULL) {
+				LOGINFO("Problem with userID on %s\n", next->first_attribute("name")->value());
+			} else {
+				temp->uid = atoi(next->first_attribute("userId")->value());
+				temp->gid = atoi(next->first_attribute("userId")->value());
+			}
+		}
+		temp->next = head;
+		head = temp;
+		if (next->next_sibling("package") == NULL)
+			break;
+		name.clear();
+		next = next->next_sibling("package");
+	}
+	//Get updated packages
+	next = pkgNode->first_node("updated-package");
+	if (next != NULL) {
+		while (next->first_attribute("name") != NULL) {
+			package* temp = new package;
+			for (unsigned n = 0; n < skip.size(); ++n) {
+				if (skip.at(n).compare(next->first_attribute("codePath")->value()) == 0) {
+					skiploop = true;
+					break;
+				}
+			}
+
+			if (skiploop == true) {
+				if (debug)
+					LOGINFO("Skipping package %s\n", next->first_attribute("codePath")->value());
+				free(temp);
+				next = next->next_sibling();
+				skiploop = false;
+				continue;
+			}
+			name.append((next->first_attribute("name")->value()));
+			temp->pkgName = next->first_attribute("name")->value();
+			if (debug)
+				LOGINFO("Loading pkg: %s\n", next->first_attribute("name")->value());
+			if (next->first_attribute("codePath") == NULL) {
+				LOGINFO("Problem with codePath on %s\n", next->first_attribute("name")->value());
+			} else {
+				temp->codePath = next->first_attribute("codePath")->value();
+				temp->app = basename(next->first_attribute("codePath")->value());
+				temp->appDir = dirname(next->first_attribute("codePath")->value());
+			}
+
+			temp->dDir = name;
+			if ( next->first_attribute("sharedUserId") != NULL) {
+				temp->uid = atoi(next->first_attribute("sharedUserId")->value());
+				temp->gid = atoi(next->first_attribute("sharedUserId")->value());
+			}
+			else {
+				if (next->first_attribute("userId") == NULL) {
+					LOGINFO("Problem with userID on %s\n", next->first_attribute("name")->value());
+				} else {
+					temp->uid = atoi(next->first_attribute("userId")->value());
+					temp->gid = atoi(next->first_attribute("userId")->value());
+				}
+			}
+			temp->next = head;
+			head = temp;
+			if (next->next_sibling("package") == NULL)
+				break;
+			name.clear();
+			next = next->next_sibling("package");
+		}
+	}
+	return 0;
+}
diff --git a/fixPermissions.hpp b/fixPermissions.hpp
new file mode 100644
index 0000000..e57d7bf
--- /dev/null
+++ b/fixPermissions.hpp
@@ -0,0 +1,52 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include "gui/rapidxml.hpp"
+#include "twrp-functions.hpp"
+
+using namespace std;
+
+class fixPermissions {
+	public:
+		int fixPerms(bool enable_debug, bool remove_data_for_missing_apps);
+		int fixDataInternalContexts(void);
+
+	private:
+		int pchown(std::string fn, int puid, int pgid);
+		int pchmod(std::string fn, string mode);
+		vector <string> listAllDirectories(std::string path);
+		vector <string> listAllFiles(std::string path);
+		int getPackages();
+		int fixSystemApps();
+		int fixDataApps();
+		int fixAllFiles(string directory, int gid, int uid, string file_perms);
+		int fixDataData(string dataDir);
+		int restorecon(std::string entry, struct stat *sb);
+		int fixDataDataContexts(void);
+		int fixContextsRecursively(std::string path, int level);
+
+		struct package {
+			string pkgName;
+			string codePath;
+			string appDir;
+			string app;
+			string dDir;
+			int gid;
+			int uid;
+			package *next;
+		};
+		bool debug;
+		bool remove_data;
+		bool multi_user;
+		package* head;
+		package* temp;
+		string packageFile;
+};
diff --git a/flashutils/Android.mk b/flashutils/Android.mk
new file mode 100644
index 0000000..3209cb0
--- /dev/null
+++ b/flashutils/Android.mk
@@ -0,0 +1,129 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := flashutils.c
+LOCAL_MODULE := libflashutils
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES += $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := libmmcutils libmtdutils libbmlutils libcrecovery
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := flash_image.c
+LOCAL_MODULE := libflash_image
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Dmain=flash_image_main
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := dump_image.c
+LOCAL_MODULE := libdump_image
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Dmain=dump_image_main
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := erase_image.c
+LOCAL_MODULE := liberase_image
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -Dmain=erase_image_main
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := dump_image.c
+LOCAL_MODULE := utility_dump_image
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE_STEM := dump_image
+LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := flash_image.c
+LOCAL_MODULE := utility_flash_image
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE_STEM := flash_image
+LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := erase_image.c
+LOCAL_MODULE := utility_erase_image
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE_STEM := erase_image
+LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(BUILD_EXECUTABLE)
+
+#Added for dynamic building for TWRP:
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := flashutils.c
+LOCAL_MODULE := libflashutils
+LOCAL_MODULE_TAGS := eng
+LOCAL_C_INCLUDES += $(commands_recovery_local_path)
+LOCAL_SHARED_LIBRARIES := libc libmmcutils libbmlutils
+LOCAL_STATIC_LIBRARIES := libmtdutils
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := flash_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := flash_image.c
+LOCAL_SHARED_LIBRARIES := libflashutils libmmcutils libbmlutils libcutils libc
+LOCAL_STATIC_LIBRARIES := libmtdutils
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dump_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := dump_image.c
+LOCAL_SHARED_LIBRARIES := libflashutils libmmcutils libbmlutils libcutils libc
+LOCAL_STATIC_LIBRARIES := libmtdutils
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := erase_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := erase_image.c
+LOCAL_SHARED_LIBRARIES := libflashutils libmmcutils libbmlutils libcutils libc
+LOCAL_STATIC_LIBRARIES := libmtdutils
+include $(BUILD_EXECUTABLE)
+
+endif	# !TARGET_SIMULATOR
diff --git a/flashutils/dump_image.c b/flashutils/dump_image.c
new file mode 100644
index 0000000..64c4e1c
--- /dev/null
+++ b/flashutils/dump_image.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "cutils/log.h"
+#include "flashutils.h"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#if 0
+
+#define LOG_TAG "dump_image"
+
+#define BLOCK_SIZE    2048
+#define SPARE_SIZE    (BLOCK_SIZE >> 5)
+
+static int die(const char *msg, ...) {
+    int err = errno;
+    va_list args;
+    va_start(args, msg);
+    char buf[1024];
+    vsnprintf(buf, sizeof(buf), msg, args);
+    va_end(args);
+
+    if (err != 0) {
+        strlcat(buf, ": ", sizeof(buf));
+        strlcat(buf, strerror(err), sizeof(buf));
+    }
+
+    fprintf(stderr, "%s\n", buf);
+    return 1;
+}
+
+/* Read a flash partition and write it to an image file. */
+
+int dump_image(char* partition_name, char* filename, dump_image_callback callback) {
+    MtdReadContext *in;
+    const MtdPartition *partition;
+    char buf[BLOCK_SIZE + SPARE_SIZE];
+    size_t partition_size;
+    size_t read_size;
+    size_t total;
+    int fd;
+    int wrote;
+    int len;
+    
+    if (mtd_scan_partitions() <= 0)
+        return die("error scanning partitions");
+
+    partition = mtd_find_partition_by_name(partition_name);
+    if (partition == NULL)
+        return die("can't find %s partition", partition_name);
+
+    if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
+        return die("can't get info of partition %s", partition_name);
+    }
+
+    if (!strcmp(filename, "-")) {
+        fd = fileno(stdout);
+    } 
+    else {
+        fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+    }
+
+    if (fd < 0)
+        return die("error opening %s", filename);
+
+    in = mtd_read_partition(partition);
+    if (in == NULL) {
+        close(fd);
+        unlink(filename);
+        return die("error opening %s: %s\n", partition_name, strerror(errno));
+    }
+
+    total = 0;
+    while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
+        wrote = write(fd, buf, len);
+        if (wrote != len) {
+            close(fd);
+            unlink(filename);
+            return die("error writing %s", filename);
+        }
+        total += BLOCK_SIZE;
+        if (callback != NULL)
+            callback(total, partition_size);
+    }
+
+    mtd_read_close(in);
+
+    if (close(fd)) {
+        unlink(filename);
+        return die("error closing %s", filename);
+    }
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    ssize_t (*read_func) (MtdReadContext *, char *, size_t);
+    MtdReadContext *in;
+    const MtdPartition *partition;
+    char buf[BLOCK_SIZE + SPARE_SIZE];
+    size_t partition_size;
+    size_t read_size;
+    size_t total;
+    int fd;
+    int wrote;
+    int len;
+
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
+        return 2;
+    }
+
+    return dump_image(argv[1], argv[2], NULL);
+}
+
+#endif
+
+int main(int argc, char **argv)
+{
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
+        return 2;
+    }
+
+    return backup_raw_partition(NULL, argv[1], argv[2]);
+}
diff --git a/flashutils/erase_image.c b/flashutils/erase_image.c
new file mode 100644
index 0000000..b09a424
--- /dev/null
+++ b/flashutils/erase_image.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Portions Copyright (C) 2010 Magnus Eriksson <packetlss@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+
+#include "cutils/log.h"
+#include "flashutils.h"
+
+#if 0
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+
+#define LOG_TAG "erase_image"
+
+static int die(const char *msg, ...) {
+    int err = errno;
+    va_list args;
+    va_start(args, msg);
+    char buf[1024];
+    vsnprintf(buf, sizeof(buf), msg, args);
+    va_end(args);
+
+    if (err != 0) {
+        strlcat(buf, ": ", sizeof(buf));
+        strlcat(buf, strerror(err), sizeof(buf));
+    }
+
+    fprintf(stderr, "%s\n", buf);
+    LOGE("%s\n", buf);
+    return 3;
+}
+
+
+int erase_image(char* partition_name) {
+    MtdWriteContext *out;
+    size_t erased;
+    size_t total_size;
+    size_t erase_size;
+
+    if (mtd_scan_partitions() <= 0) die("error scanning partitions");
+    const MtdPartition *partition = mtd_find_partition_by_name(partition_name);
+    if (partition == NULL) return die("can't find %s partition", partition_name);
+
+    out = mtd_write_partition(partition);
+    if (out == NULL) return die("could not estabilish write context for %s", partition_name);
+
+    // do the actual erase, -1 = full partition erase
+    erased = mtd_erase_blocks(out, -1);
+
+    // erased = bytes erased, if zero, something borked
+    if (!erased) return die("error erasing %s", partition_name);
+
+    return 0;
+}
+
+
+/* Erase a mtd partition */
+
+int main(int argc, char **argv) {
+    if (argc != 2) {
+        fprintf(stderr, "usage: %s <partition>\n", argv[0]);
+        return 2;
+    }
+    
+    return erase_image(argv[1]);
+}
+
+#endif
+
+
+int main(int argc, char **argv)
+{
+    if (argc != 2) {
+        fprintf(stderr, "usage: %s partition\n", argv[0]);
+        return 2;
+    }
+
+    return erase_raw_partition(NULL, argv[1]);
+}
diff --git a/flashutils/flash_image.c b/flashutils/flash_image.c
new file mode 100644
index 0000000..c9f3683
--- /dev/null
+++ b/flashutils/flash_image.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cutils/log.h"
+#include "flashutils.h"
+
+#if 0
+#define LOG_TAG "flash_image"
+
+#define HEADER_SIZE 2048  // size of header to compare for equality
+
+void die(const char *msg, ...) {
+    int err = errno;
+    va_list args;
+    va_start(args, msg);
+    char buf[1024];
+    vsnprintf(buf, sizeof(buf), msg, args);
+    va_end(args);
+
+    if (err != 0) {
+        strlcat(buf, ": ", sizeof(buf));
+        strlcat(buf, strerror(err), sizeof(buf));
+    }
+
+    fprintf(stderr, "%s\n", buf);
+    LOGE("%s\n", buf);
+    exit(1);
+}
+
+/* Read an image file and write it to a flash partition. */
+
+int main(int argc, char **argv) {
+    const MtdPartition *ptn;
+    MtdWriteContext *write;
+    void *data;
+    unsigned sz;
+
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
+        return 2;
+    }
+
+    if (mtd_scan_partitions() <= 0) die("error scanning partitions");
+    const MtdPartition *partition = mtd_find_partition_by_name(argv[1]);
+    if (partition == NULL) die("can't find %s partition", argv[1]);
+
+    // If the first part of the file matches the partition, skip writing
+
+    int fd = open(argv[2], O_RDONLY);
+    if (fd < 0) die("error opening %s", argv[2]);
+
+    char header[HEADER_SIZE];
+    int headerlen = read(fd, header, sizeof(header));
+    if (headerlen <= 0) die("error reading %s header", argv[2]);
+
+    MtdReadContext *in = mtd_read_partition(partition);
+    if (in == NULL) {
+        LOGW("error opening %s: %s\n", argv[1], strerror(errno));
+        // just assume it needs re-writing
+    } else {
+        char check[HEADER_SIZE];
+        int checklen = mtd_read_data(in, check, sizeof(check));
+        if (checklen <= 0) {
+            LOGW("error reading %s: %s\n", argv[1], strerror(errno));
+            // just assume it needs re-writing
+        } else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
+            LOGI("header is the same, not flashing %s\n", argv[1]);
+            return 0;
+        }
+        mtd_read_close(in);
+    }
+
+    // Skip the header (we'll come back to it), write everything else
+    LOGI("flashing %s from %s\n", argv[1], argv[2]);
+
+    MtdWriteContext *out = mtd_write_partition(partition);
+    if (out == NULL) die("error writing %s", argv[1]);
+
+    char buf[HEADER_SIZE];
+    memset(buf, 0, headerlen);
+    int wrote = mtd_write_data(out, buf, headerlen);
+    if (wrote != headerlen) die("error writing %s", argv[1]);
+
+    int len;
+    while ((len = read(fd, buf, sizeof(buf))) > 0) {
+        wrote = mtd_write_data(out, buf, len);
+        if (wrote != len) die("error writing %s", argv[1]);
+    }
+    if (len < 0) die("error reading %s", argv[2]);
+
+    if (mtd_write_close(out)) die("error closing %s", argv[1]);
+
+    // Now come back and write the header last
+
+    out = mtd_write_partition(partition);
+    if (out == NULL) die("error re-opening %s", argv[1]);
+
+    wrote = mtd_write_data(out, header, headerlen);
+    if (wrote != headerlen) die("error re-writing %s", argv[1]);
+
+    // Need to write a complete block, so write the rest of the first block
+    size_t block_size;
+    if (mtd_partition_info(partition, NULL, &block_size, NULL))
+        die("error getting %s block size", argv[1]);
+
+    if (lseek(fd, headerlen, SEEK_SET) != headerlen)
+        die("error rewinding %s", argv[2]);
+
+    int left = block_size - headerlen;
+    while (left < 0) left += block_size;
+    while (left > 0) {
+        len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left);
+        if (len <= 0) die("error reading %s", argv[2]);
+        if (mtd_write_data(out, buf, len) != len)
+            die("error writing %s", argv[1]);
+        left -= len;
+    }
+
+    if (mtd_write_close(out)) die("error closing %s", argv[1]);
+    return 0;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
+        return 2;
+    }
+
+    int ret = restore_raw_partition(NULL, argv[1], argv[2]);
+    if (ret != 0)
+        fprintf(stderr, "failed with error: %d\n", ret);
+    return ret;
+}
diff --git a/flashutils/flashutils.c b/flashutils/flashutils.c
new file mode 100644
index 0000000..de45e87
--- /dev/null
+++ b/flashutils/flashutils.c
@@ -0,0 +1,156 @@
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+
+#include "flashutils/flashutils.h"
+
+#ifndef BOARD_BML_BOOT
+#define BOARD_BML_BOOT              "/dev/block/bml7"
+#endif
+
+#ifndef BOARD_BML_RECOVERY
+#define BOARD_BML_RECOVERY          "/dev/block/bml8"
+#endif
+
+int the_flash_type = UNKNOWN;
+
+int device_flash_type()
+{
+    if (the_flash_type == UNKNOWN) {
+        if (access(BOARD_BML_BOOT, F_OK) == 0) {
+            the_flash_type = BML;
+        } else if (access("/proc/emmc", F_OK) == 0) {
+            the_flash_type = MMC;
+        } else if (access("/proc/mtd", F_OK) == 0) {
+            the_flash_type = MTD;
+        } else {
+            the_flash_type = UNSUPPORTED;
+        }
+    }
+    return the_flash_type;
+}
+
+char* get_default_filesystem()
+{
+    return device_flash_type() == MMC ? "ext3" : "yaffs2";
+}
+
+int get_flash_type(const char* partitionType) {
+    int type = UNSUPPORTED;
+    if (strcmp(partitionType, "mtd") == 0)
+        type = MTD;
+    else if (strcmp(partitionType, "emmc") == 0)
+        type = MMC;
+    else if (strcmp(partitionType, "bml") == 0)
+        type = BML;
+    return type;
+}
+
+static int detect_partition(const char *partitionType, const char *partition)
+{
+    int type = device_flash_type();
+    if (strstr(partition, "/dev/block/mtd") != NULL)
+        type = MTD;
+    else if (strstr(partition, "/dev/block/mmc") != NULL)
+        type = MMC;
+    else if (strstr(partition, "/dev/block/bml") != NULL)
+        type = BML;
+
+    if (partitionType != NULL) {
+        type = get_flash_type(partitionType);
+    }
+
+    return type;
+}
+int restore_raw_partition(const char* partitionType, const char *partition, const char *filename)
+{
+    int type = detect_partition(partitionType, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_restore_raw_partition(partition, filename);
+        case MMC:
+            return cmd_mmc_restore_raw_partition(partition, filename);
+        case BML:
+            return cmd_bml_restore_raw_partition(partition, filename);
+        default:
+            return -1;
+    }
+}
+
+int backup_raw_partition(const char* partitionType, const char *partition, const char *filename)
+{
+    int type = detect_partition(partitionType, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_backup_raw_partition(partition, filename);
+        case MMC:
+            return cmd_mmc_backup_raw_partition(partition, filename);
+        case BML:
+            return cmd_bml_backup_raw_partition(partition, filename);
+        default:
+            printf("unable to detect device type");
+            return -1;
+    }
+}
+
+int erase_raw_partition(const char* partitionType, const char *partition)
+{
+    int type = detect_partition(partitionType, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_erase_raw_partition(partition);
+        case MMC:
+            return cmd_mmc_erase_raw_partition(partition);
+        case BML:
+            return cmd_bml_erase_raw_partition(partition);
+        default:
+            return -1;
+    }
+}
+
+int erase_partition(const char *partition, const char *filesystem)
+{
+    int type = detect_partition(NULL, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_erase_partition(partition, filesystem);
+        case MMC:
+            return cmd_mmc_erase_partition(partition, filesystem);
+        case BML:
+            return cmd_bml_erase_partition(partition, filesystem);
+        default:
+            return -1;
+    }
+}
+
+int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
+{
+    int type = detect_partition(NULL, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_mount_partition(partition, mount_point, filesystem, read_only);
+        case MMC:
+            return cmd_mmc_mount_partition(partition, mount_point, filesystem, read_only);
+        case BML:
+            return cmd_bml_mount_partition(partition, mount_point, filesystem, read_only);
+        default:
+            return -1;
+    }
+}
+
+int get_partition_device(const char *partition, char *device)
+{
+    int type = device_flash_type();
+    switch (type) {
+        case MTD:
+            return cmd_mtd_get_partition_device(partition, device);
+        case MMC:
+            return cmd_mmc_get_partition_device(partition, device);
+        case BML:
+            return cmd_bml_get_partition_device(partition, device);
+        default:
+            return -1;
+    }
+}
diff --git a/flashutils/flashutils.h b/flashutils/flashutils.h
new file mode 100644
index 0000000..d112a31
--- /dev/null
+++ b/flashutils/flashutils.h
@@ -0,0 +1,50 @@
+#ifndef FLASHUTILS_H
+#define FLASHUTILS_H
+
+int restore_raw_partition(const char* partitionType, const char *partition, const char *filename);
+int backup_raw_partition(const char* partitionType, const char *partition, const char *filename);
+int erase_raw_partition(const char* partitionType, const char *partition);
+int erase_partition(const char *partition, const char *filesystem);
+int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
+int get_partition_device(const char *partition, char *device);
+
+#define FLASH_MTD 0
+#define FLASH_MMC 1
+#define FLASH_BML 2
+
+int is_mtd_device();
+char* get_default_filesystem();
+
+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);
+extern int cmd_mtd_erase_partition(const char *partition, const char *filesystem);
+extern int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
+extern int cmd_mtd_get_partition_device(const char *partition, char *device);
+
+extern int cmd_mmc_restore_raw_partition(const char *partition, const char *filename);
+extern int cmd_mmc_backup_raw_partition(const char *partition, const char *filename);
+extern int cmd_mmc_erase_raw_partition(const char *partition);
+extern int cmd_mmc_erase_partition(const char *partition, const char *filesystem);
+extern int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
+extern int cmd_mmc_get_partition_device(const char *partition, char *device);
+
+extern int cmd_bml_restore_raw_partition(const char *partition, const char *filename);
+extern int cmd_bml_backup_raw_partition(const char *partition, const char *filename);
+extern int cmd_bml_erase_raw_partition(const char *partition);
+extern int cmd_bml_erase_partition(const char *partition, const char *filesystem);
+extern int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
+extern int cmd_bml_get_partition_device(const char *partition, char *device);
+
+extern int device_flash_type();
+extern int get_flash_type(const char* fs_type);
+
+enum flash_type {
+    UNSUPPORTED = -1,
+    UNKNOWN = 0,
+    MTD = 1,
+    MMC = 2,
+    BML = 3
+};
+
+#endif
\ No newline at end of file
diff --git a/fuse/Android.mk b/fuse/Android.mk
new file mode 100644
index 0000000..f531426
--- /dev/null
+++ b/fuse/Android.mk
@@ -0,0 +1,70 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	buffer.c \
+	cuse_lowlevel.c \
+	fuse.c \
+	fuse_kern_chan.c \
+	fuse_loop.c \
+	fuse_loop_mt.c \
+	fuse_lowlevel.c \
+	fuse_mt.c fuse_opt.c \
+	fuse_session.c \
+	fuse_signals.c \
+	helper.c \
+	mount.c \
+	mount_util.c \
+	ulockmgr.c
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/include
+
+LOCAL_SHARED_LIBRARIES := \
+	libutils libdl
+
+LOCAL_CFLAGS := \
+	-D_FILE_OFFSET_BITS=64 \
+	-DFUSE_USE_VERSION=26 \
+	-fno-strict-aliasing
+
+LOCAL_MODULE := libfusetwrp
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
+#include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	fusexmp.c
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/include
+
+LOCAL_CFLAGS := -D_FILE_OFFSET_BITS=64
+
+LOCAL_MODULE := fusexmp
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_LIBRARIES := libfusetwrp
+
+LOCAL_SHARED_LIBRARIES := \
+	libutils libdl
+
+include $(BUILD_EXECUTABLE)
diff --git a/fuse/README b/fuse/README
new file mode 100644
index 0000000..3d1f6f0
--- /dev/null
+++ b/fuse/README
@@ -0,0 +1,12 @@
+Libfuse for Android
+
+FUSE[1] is a framework to develop userspace file systems for linux. Since Android is based on linux, it won't be too difficult to port libfuse to Android.
+
+The main problem of building and running libfuse on Android is that the bionic c library lacks support for pthread_cancel(), which is necessary for libfuse multi-thread code. This stackoverflow entry[2] has suggested a solution, which uses SIGUSR1 as an alternative. It seems to work.
+
+Libfuse can be build with Android NDK[3]. If success, you will get libfuse.a and an example program fusexmp. To run the example, the android kernel should be built with FUSE kernel support.
+
+References:
+[1]. http://fuse.sourceforge.net
+[2]. http://stackoverflow.com/questions/4610086/pthread-cancel-alternatives-in-android-ndk
+[3]. http://developer.android.com/sdk/ndk/index.html
diff --git a/fuse/buffer.c b/fuse/buffer.c
new file mode 100644
index 0000000..053e396
--- /dev/null
+++ b/fuse/buffer.c
@@ -0,0 +1,318 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2010  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_lowlevel.h"
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+size_t fuse_buf_size(const struct fuse_bufvec *bufv)
+{
+	size_t i;
+	size_t size = 0;
+
+	for (i = 0; i < bufv->count; i++) {
+		if (bufv->buf[i].size == SIZE_MAX)
+			size = SIZE_MAX;
+		else
+			size += bufv->buf[i].size;
+	}
+
+	return size;
+}
+
+static size_t min_size(size_t s1, size_t s2)
+{
+	return s1 < s2 ? s1 : s2;
+}
+
+static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
+			      const struct fuse_buf *src, size_t src_off,
+			      size_t len)
+{
+	ssize_t res = 0;
+	size_t copied = 0;
+
+	while (len) {
+		if (dst->flags & FUSE_BUF_FD_SEEK) {
+			res = pwrite(dst->fd, src->mem + src_off, len,
+				     dst->pos + dst_off);
+		} else {
+			res = write(dst->fd, src->mem + src_off, len);
+		}
+		if (res == -1) {
+			if (!copied)
+				return -errno;
+			break;
+		}
+		if (res == 0)
+			break;
+
+		copied += res;
+		if (!(dst->flags & FUSE_BUF_FD_RETRY))
+			break;
+
+		src_off += res;
+		dst_off += res;
+		len -= res;
+	}
+
+	return copied;
+}
+
+static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
+			     const struct fuse_buf *src, size_t src_off,
+			     size_t len)
+{
+	ssize_t res = 0;
+	size_t copied = 0;
+
+	while (len) {
+		if (src->flags & FUSE_BUF_FD_SEEK) {
+			res = pread(src->fd, dst->mem + dst_off, len,
+				     src->pos + src_off);
+		} else {
+			res = read(src->fd, dst->mem + dst_off, len);
+		}
+		if (res == -1) {
+			if (!copied)
+				return -errno;
+			break;
+		}
+		if (res == 0)
+			break;
+
+		copied += res;
+		if (!(src->flags & FUSE_BUF_FD_RETRY))
+			break;
+
+		dst_off += res;
+		src_off += res;
+		len -= res;
+	}
+
+	return copied;
+}
+
+static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
+				 const struct fuse_buf *src, size_t src_off,
+				 size_t len)
+{
+	char buf[4096];
+	struct fuse_buf tmp = {
+		.size = sizeof(buf),
+		.flags = 0,
+	};
+	ssize_t res;
+	size_t copied = 0;
+
+	tmp.mem = buf;
+
+	while (len) {
+		size_t this_len = min_size(tmp.size, len);
+		size_t read_len;
+
+		res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
+		if (res < 0) {
+			if (!copied)
+				return res;
+			break;
+		}
+		if (res == 0)
+			break;
+
+		read_len = res;
+		res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
+		if (res < 0) {
+			if (!copied)
+				return res;
+			break;
+		}
+		if (res == 0)
+			break;
+
+		copied += res;
+
+		if (res < this_len)
+			break;
+
+		dst_off += res;
+		src_off += res;
+		len -= res;
+	}
+
+	return copied;
+}
+
+#ifdef HAVE_SPLICE
+static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
+			       const struct fuse_buf *src, size_t src_off,
+			       size_t len, enum fuse_buf_copy_flags flags)
+{
+	int splice_flags = 0;
+	off64_t *srcpos = NULL;
+	off64_t *dstpos = NULL;
+	off64_t srcpos_val;
+	off64_t dstpos_val;
+	ssize_t res;
+	size_t copied = 0;
+
+	if (flags & FUSE_BUF_SPLICE_MOVE)
+		splice_flags |= SPLICE_F_MOVE;
+	if (flags & FUSE_BUF_SPLICE_NONBLOCK)
+		splice_flags |= SPLICE_F_NONBLOCK;
+
+	if (src->flags & FUSE_BUF_FD_SEEK) {
+		srcpos_val = src->pos + src_off;
+		srcpos = &srcpos_val;
+	}
+	if (dst->flags & FUSE_BUF_FD_SEEK) {
+		dstpos_val = dst->pos + dst_off;
+		dstpos = &dstpos_val;
+	}
+
+	while (len) {
+		res = splice(src->fd, srcpos, dst->fd, dstpos, len,
+			     splice_flags);
+		if (res == -1) {
+			if (copied)
+				break;
+
+			if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
+				return -errno;
+
+			/* Maybe splice is not supported for this combination */
+			return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
+						 len);
+		}
+		if (res == 0)
+			break;
+
+		copied += res;
+		if (!(src->flags & FUSE_BUF_FD_RETRY) &&
+		    !(dst->flags & FUSE_BUF_FD_RETRY)) {
+			break;
+		}
+
+		len -= res;
+	}
+
+	return copied;
+}
+#else
+static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
+			       const struct fuse_buf *src, size_t src_off,
+			       size_t len, enum fuse_buf_copy_flags flags)
+{
+	(void) flags;
+
+	return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
+}
+#endif
+
+
+static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
+				 const struct fuse_buf *src, size_t src_off,
+				 size_t len, enum fuse_buf_copy_flags flags)
+{
+	int src_is_fd = src->flags & FUSE_BUF_IS_FD;
+	int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
+
+	if (!src_is_fd && !dst_is_fd) {
+		void *dstmem = dst->mem + dst_off;
+		void *srcmem = src->mem + src_off;
+
+		if (dstmem != srcmem) {
+			if (dstmem + len <= srcmem || srcmem + len <= dstmem)
+				memcpy(dstmem, srcmem, len);
+			else
+				memmove(dstmem, srcmem, len);
+		}
+
+		return len;
+	} else if (!src_is_fd) {
+		return fuse_buf_write(dst, dst_off, src, src_off, len);
+	} else if (!dst_is_fd) {
+		return fuse_buf_read(dst, dst_off, src, src_off, len);
+	} else if (flags & FUSE_BUF_NO_SPLICE) {
+		return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
+	} else {
+		return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
+	}
+}
+
+static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
+{
+	if (bufv->idx < bufv->count)
+		return &bufv->buf[bufv->idx];
+	else
+		return NULL;
+}
+
+static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
+{
+	const struct fuse_buf *buf = fuse_bufvec_current(bufv);
+
+	bufv->off += len;
+	assert(bufv->off <= buf->size);
+	if (bufv->off == buf->size) {
+		assert(bufv->idx < bufv->count);
+		bufv->idx++;
+		if (bufv->idx == bufv->count)
+			return 0;
+		bufv->off = 0;
+	}
+	return 1;
+}
+
+ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
+		      enum fuse_buf_copy_flags flags)
+{
+	size_t copied = 0;
+
+	if (dstv == srcv)
+		return fuse_buf_size(dstv);
+
+	for (;;) {
+		const struct fuse_buf *src = fuse_bufvec_current(srcv);
+		const struct fuse_buf *dst = fuse_bufvec_current(dstv);
+		size_t src_len;
+		size_t dst_len;
+		size_t len;
+		ssize_t res;
+
+		if (src == NULL || dst == NULL)
+			break;
+
+		src_len = src->size - srcv->off;
+		dst_len = dst->size - dstv->off;
+		len = min_size(src_len, dst_len);
+
+		res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
+		if (res < 0) {
+			if (!copied)
+				return res;
+			break;
+		}
+		copied += res;
+
+		if (!fuse_bufvec_advance(srcv, res) ||
+		    !fuse_bufvec_advance(dstv, res))
+			break;
+
+		if (res < len)
+			break;
+	}
+
+	return copied;
+}
diff --git a/fuse/cuse_lowlevel.c b/fuse/cuse_lowlevel.c
new file mode 100644
index 0000000..be49ad4
--- /dev/null
+++ b/fuse/cuse_lowlevel.c
@@ -0,0 +1,371 @@
+/*
+  CUSE: Character device in Userspace
+  Copyright (C) 2008       SUSE Linux Products GmbH
+  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include "cuse_lowlevel.h"
+#include "fuse_kernel.h"
+#include "fuse_i.h"
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+
+struct cuse_data {
+	struct cuse_lowlevel_ops	clop;
+	unsigned			max_read;
+	unsigned			dev_major;
+	unsigned			dev_minor;
+	unsigned			flags;
+	unsigned			dev_info_len;
+	char				dev_info[];
+};
+
+static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
+{
+	return &req->f->cuse_data->clop;
+}
+
+static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
+			  struct fuse_file_info *fi)
+{
+	(void)ino;
+	req_clop(req)->open(req, fi);
+}
+
+static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+			  off64_t off, struct fuse_file_info *fi)
+{
+	(void)ino;
+	req_clop(req)->read(req, size, off, fi);
+}
+
+static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
+			   size_t size, off64_t off, struct fuse_file_info *fi)
+{
+	(void)ino;
+	req_clop(req)->write(req, buf, size, off, fi);
+}
+
+static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
+			   struct fuse_file_info *fi)
+{
+	(void)ino;
+	req_clop(req)->flush(req, fi);
+}
+
+static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
+			     struct fuse_file_info *fi)
+{
+	(void)ino;
+	req_clop(req)->release(req, fi);
+}
+
+static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
+			   struct fuse_file_info *fi)
+{
+	(void)ino;
+	req_clop(req)->fsync(req, datasync, fi);
+}
+
+static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
+		       struct fuse_file_info *fi, unsigned int flags,
+		       const void *in_buf, size_t in_bufsz, size_t out_bufsz)
+{
+	(void)ino;
+	req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
+			     out_bufsz);
+}
+
+static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
+			  struct fuse_file_info *fi, struct fuse_pollhandle *ph)
+{
+	(void)ino;
+	req_clop(req)->poll(req, fi, ph);
+}
+
+static size_t cuse_pack_info(int argc, const char **argv, char *buf)
+{
+	size_t size = 0;
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		size_t len;
+
+		len = strlen(argv[i]) + 1;
+		size += len;
+		if (buf) {
+			memcpy(buf, argv[i], len);
+			buf += len;
+		}
+	}
+
+	return size;
+}
+
+static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
+					const struct cuse_lowlevel_ops *clop)
+{
+	struct cuse_data *cd;
+	size_t dev_info_len;
+
+	dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
+				      NULL);
+
+	if (dev_info_len > CUSE_INIT_INFO_MAX) {
+		fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n",
+			dev_info_len, CUSE_INIT_INFO_MAX);
+		return NULL;
+	}
+
+	cd = calloc(1, sizeof(*cd) + dev_info_len);
+	if (!cd) {
+		fprintf(stderr, "cuse: failed to allocate cuse_data\n");
+		return NULL;
+	}
+
+	memcpy(&cd->clop, clop, sizeof(cd->clop));
+	cd->max_read = 131072;
+	cd->dev_major = ci->dev_major;
+	cd->dev_minor = ci->dev_minor;
+	cd->dev_info_len = dev_info_len;
+	cd->flags = ci->flags;
+	cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
+
+	return cd;
+}
+
+struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
+				       const struct cuse_info *ci,
+				       const struct cuse_lowlevel_ops *clop,
+				       void *userdata)
+{
+	struct fuse_lowlevel_ops lop;
+	struct cuse_data *cd;
+	struct fuse_session *se;
+	struct fuse_ll *ll;
+
+	cd = cuse_prep_data(ci, clop);
+	if (!cd)
+		return NULL;
+
+	memset(&lop, 0, sizeof(lop));
+	lop.init	= clop->init;
+	lop.destroy	= clop->destroy;
+	lop.open	= clop->open		? cuse_fll_open		: NULL;
+	lop.read	= clop->read		? cuse_fll_read		: NULL;
+	lop.write	= clop->write		? cuse_fll_write	: NULL;
+	lop.flush	= clop->flush		? cuse_fll_flush	: NULL;
+	lop.release	= clop->release		? cuse_fll_release	: NULL;
+	lop.fsync	= clop->fsync		? cuse_fll_fsync	: NULL;
+	lop.ioctl	= clop->ioctl		? cuse_fll_ioctl	: NULL;
+	lop.poll	= clop->poll		? cuse_fll_poll		: NULL;
+
+	se = fuse_lowlevel_new_common(args, &lop, sizeof(lop), userdata);
+	if (!se) {
+		free(cd);
+		return NULL;
+	}
+	ll = se->data;
+	ll->cuse_data = cd;
+
+	return se;
+}
+
+static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
+			   char *dev_info, unsigned dev_info_len)
+{
+	struct iovec iov[3];
+
+	iov[1].iov_base = arg;
+	iov[1].iov_len = sizeof(struct cuse_init_out);
+	iov[2].iov_base = dev_info;
+	iov[2].iov_len = dev_info_len;
+
+	return fuse_send_reply_iov_nofree(req, 0, iov, 3);
+}
+
+void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
+	struct cuse_init_out outarg;
+	struct fuse_ll *f = req->f;
+	struct cuse_data *cd = f->cuse_data;
+	size_t bufsize = fuse_chan_bufsize(req->ch);
+	struct cuse_lowlevel_ops *clop = req_clop(req);
+
+	(void) nodeid;
+	if (f->debug) {
+		fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
+		fprintf(stderr, "flags=0x%08x\n", arg->flags);
+	}
+	f->conn.proto_major = arg->major;
+	f->conn.proto_minor = arg->minor;
+	f->conn.capable = 0;
+	f->conn.want = 0;
+
+	if (arg->major < 7) {
+		fprintf(stderr, "cuse: unsupported protocol version: %u.%u\n",
+			arg->major, arg->minor);
+		fuse_reply_err(req, EPROTO);
+		return;
+	}
+
+	if (bufsize < FUSE_MIN_READ_BUFFER) {
+		fprintf(stderr, "cuse: warning: buffer size too small: %zu\n",
+			bufsize);
+		bufsize = FUSE_MIN_READ_BUFFER;
+	}
+
+	bufsize -= 4096;
+	if (bufsize < f->conn.max_write)
+		f->conn.max_write = bufsize;
+
+	f->got_init = 1;
+	if (f->op.init)
+		f->op.init(f->userdata, &f->conn);
+
+	memset(&outarg, 0, sizeof(outarg));
+	outarg.major = FUSE_KERNEL_VERSION;
+	outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+	outarg.flags = cd->flags;
+	outarg.max_read = cd->max_read;
+	outarg.max_write = f->conn.max_write;
+	outarg.dev_major = cd->dev_major;
+	outarg.dev_minor = cd->dev_minor;
+
+	if (f->debug) {
+		fprintf(stderr, "   CUSE_INIT: %u.%u\n",
+			outarg.major, outarg.minor);
+		fprintf(stderr, "   flags=0x%08x\n", outarg.flags);
+		fprintf(stderr, "   max_read=0x%08x\n", outarg.max_read);
+		fprintf(stderr, "   max_write=0x%08x\n", outarg.max_write);
+		fprintf(stderr, "   dev_major=%u\n", outarg.dev_major);
+		fprintf(stderr, "   dev_minor=%u\n", outarg.dev_minor);
+		fprintf(stderr, "   dev_info: %.*s\n", cd->dev_info_len,
+			cd->dev_info);
+	}
+
+	cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
+
+	if (clop->init_done)
+		clop->init_done(f->userdata);
+
+	fuse_free_req(req);
+}
+
+struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
+					 const struct cuse_info *ci,
+					 const struct cuse_lowlevel_ops *clop,
+					 int *multithreaded, void *userdata)
+{
+	const char *devname = "/dev/cuse";
+	static const struct fuse_opt kill_subtype_opts[] = {
+		FUSE_OPT_KEY("subtype=",  FUSE_OPT_KEY_DISCARD),
+		FUSE_OPT_END
+	};
+	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+	struct fuse_session *se;
+	struct fuse_chan *ch;
+	int fd;
+	int foreground;
+	int res;
+
+	res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground);
+	if (res == -1)
+		goto err_args;
+
+	res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
+	if (res == -1)
+		goto err_args;
+
+	/*
+	 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
+	 * would ensue.
+	 */
+	do {
+		fd = open("/dev/null", O_RDWR);
+		if (fd > 2)
+			close(fd);
+	} while (fd >= 0 && fd <= 2);
+
+	se = cuse_lowlevel_new(&args, ci, clop, userdata);
+	fuse_opt_free_args(&args);
+	if (se == NULL)
+		goto err_args;
+
+	fd = open(devname, O_RDWR);
+	if (fd == -1) {
+		if (errno == ENODEV || errno == ENOENT)
+			fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n");
+		else
+			fprintf(stderr, "cuse: failed to open %s: %s\n",
+				devname, strerror(errno));
+		goto err_se;
+	}
+
+	ch = fuse_kern_chan_new(fd);
+	if (!ch) {
+		close(fd);
+		goto err_se;
+	}
+
+	fuse_session_add_chan(se, ch);
+
+	res = fuse_set_signal_handlers(se);
+	if (res == -1)
+		goto err_se;
+
+	res = fuse_daemonize(foreground);
+	if (res == -1)
+		goto err_sig;
+
+	return se;
+
+err_sig:
+	fuse_remove_signal_handlers(se);
+err_se:
+	fuse_session_destroy(se);
+err_args:
+	fuse_opt_free_args(&args);
+	return NULL;
+}
+
+void cuse_lowlevel_teardown(struct fuse_session *se)
+{
+	fuse_remove_signal_handlers(se);
+	fuse_session_destroy(se);
+}
+
+int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
+		       const struct cuse_lowlevel_ops *clop, void *userdata)
+{
+	struct fuse_session *se;
+	int multithreaded;
+	int res;
+
+	se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
+				 userdata);
+	if (se == NULL)
+		return 1;
+
+	if (multithreaded)
+		res = fuse_session_loop_mt(se);
+	else
+		res = fuse_session_loop(se);
+
+	cuse_lowlevel_teardown(se);
+	if (res == -1)
+		return 1;
+
+	return 0;
+}
diff --git a/fuse/fuse.c b/fuse/fuse.c
new file mode 100644
index 0000000..588d445
--- /dev/null
+++ b/fuse/fuse.c
@@ -0,0 +1,4890 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+
+/* For pthread_rwlock_t */
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_lowlevel.h"
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+#include "fuse_common_compat.h"
+#include "fuse_compat.h"
+#include "fuse_kernel.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <signal.h>
+#include <dlfcn.h>
+#include <assert.h>
+#include <poll.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#define FUSE_NODE_SLAB 1
+
+#ifndef MAP_ANONYMOUS
+#undef FUSE_NODE_SLAB
+#endif
+
+#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
+
+#define FUSE_UNKNOWN_INO 0xffffffff
+#define OFFSET_MAX 0x7fffffffffffffffLL
+
+#define NODE_TABLE_MIN_SIZE 8192
+
+struct fuse_config {
+	unsigned int uid;
+	unsigned int gid;
+	unsigned int  umask;
+	double entry_timeout;
+	double negative_timeout;
+	double attr_timeout;
+	double ac_attr_timeout;
+	int ac_attr_timeout_set;
+	int remember;
+	int nopath;
+	int debug;
+	int hard_remove;
+	int use_ino;
+	int readdir_ino;
+	int set_mode;
+	int set_uid;
+	int set_gid;
+	int direct_io;
+	int kernel_cache;
+	int auto_cache;
+	int intr;
+	int intr_signal;
+	int help;
+	char *modules;
+};
+
+struct fuse_fs {
+	struct fuse_operations op;
+	struct fuse_module *m;
+	void *user_data;
+	int compat;
+	int debug;
+};
+
+struct fusemod_so {
+	void *handle;
+	int ctr;
+};
+
+struct lock_queue_element {
+	struct lock_queue_element *next;
+	pthread_cond_t cond;
+	fuse_ino_t nodeid1;
+	const char *name1;
+	char **path1;
+	struct node **wnode1;
+	fuse_ino_t nodeid2;
+	const char *name2;
+	char **path2;
+	struct node **wnode2;
+	int err;
+	bool first_locked : 1;
+	bool second_locked : 1;
+	bool done : 1;
+};
+
+struct node_table {
+	struct node **array;
+	size_t use;
+	size_t size;
+	size_t split;
+};
+
+#define container_of(ptr, type, member) ({                              \
+                        const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+                        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define list_entry(ptr, type, member)           \
+        container_of(ptr, type, member)
+
+struct list_head {
+	struct list_head *next;
+	struct list_head *prev;
+};
+
+struct node_slab {
+	struct list_head list;  /* must be the first member */
+	struct list_head freelist;
+	int used;
+};
+
+struct fuse {
+	struct fuse_session *se;
+	struct node_table name_table;
+	struct node_table id_table;
+	struct list_head lru_table;
+	fuse_ino_t ctr;
+	unsigned int generation;
+	unsigned int hidectr;
+	pthread_mutex_t lock;
+	struct fuse_config conf;
+	int intr_installed;
+	struct fuse_fs *fs;
+	int nullpath_ok;
+	int utime_omit_ok;
+	struct lock_queue_element *lockq;
+	int pagesize;
+	struct list_head partial_slabs;
+	struct list_head full_slabs;
+	pthread_t prune_thread;
+};
+
+struct lock {
+	int type;
+	off64_t start;
+	off64_t end;
+	pid_t pid;
+	uint64_t owner;
+	struct lock *next;
+};
+
+struct node {
+	struct node *name_next;
+	struct node *id_next;
+	fuse_ino_t nodeid;
+	unsigned int generation;
+	int refctr;
+	struct node *parent;
+	char *name;
+	uint64_t nlookup;
+	int open_count;
+	struct timespec stat_updated;
+	struct timespec mtime;
+	off64_t size;
+	struct lock *locks;
+	unsigned int is_hidden : 1;
+	unsigned int cache_valid : 1;
+	int treelock;
+	char inline_name[32];
+};
+
+#define TREELOCK_WRITE -1
+#define TREELOCK_WAIT_OFFSET INT_MIN
+
+struct node_lru {
+	struct node node;
+	struct list_head lru;
+	struct timespec forget_time;
+};
+
+struct fuse_dh {
+	pthread_mutex_t lock;
+	struct fuse *fuse;
+	fuse_req_t req;
+	char *contents;
+	int allocated;
+	unsigned len;
+	unsigned size;
+	unsigned needlen;
+	int filled;
+	uint64_t fh;
+	int error;
+	fuse_ino_t nodeid;
+};
+
+/* old dir handle */
+struct fuse_dirhandle {
+	fuse_fill_dir_t filler;
+	void *buf;
+};
+
+struct fuse_context_i {
+	struct fuse_context ctx;
+	fuse_req_t req;
+};
+
+static pthread_key_t fuse_context_key;
+static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
+static int fuse_context_ref;
+static struct fusemod_so *fuse_current_so;
+static struct fuse_module *fuse_modules;
+
+static int fuse_load_so_name(const char *soname)
+{
+	struct fusemod_so *so;
+
+	so = calloc(1, sizeof(struct fusemod_so));
+	if (!so) {
+		fprintf(stderr, "fuse: memory allocation failed\n");
+		return -1;
+	}
+
+	fuse_current_so = so;
+	so->handle = dlopen(soname, RTLD_NOW);
+	fuse_current_so = NULL;
+	if (!so->handle) {
+		fprintf(stderr, "fuse: %s\n", dlerror());
+		goto err;
+	}
+	if (!so->ctr) {
+		fprintf(stderr, "fuse: %s did not register any modules\n",
+			soname);
+		goto err;
+	}
+	return 0;
+
+err:
+	if (so->handle)
+		dlclose(so->handle);
+	free(so);
+	return -1;
+}
+
+static int fuse_load_so_module(const char *module)
+{
+	int res;
+	char *soname = malloc(strlen(module) + 64);
+	if (!soname) {
+		fprintf(stderr, "fuse: memory allocation failed\n");
+		return -1;
+	}
+	sprintf(soname, "libfusemod_%s.so", module);
+	res = fuse_load_so_name(soname);
+	free(soname);
+	return res;
+}
+
+static struct fuse_module *fuse_find_module(const char *module)
+{
+	struct fuse_module *m;
+	for (m = fuse_modules; m; m = m->next) {
+		if (strcmp(module, m->name) == 0) {
+			m->ctr++;
+			break;
+		}
+	}
+	return m;
+}
+
+static struct fuse_module *fuse_get_module(const char *module)
+{
+	struct fuse_module *m;
+
+	pthread_mutex_lock(&fuse_context_lock);
+	m = fuse_find_module(module);
+	if (!m) {
+		int err = fuse_load_so_module(module);
+		if (!err)
+			m = fuse_find_module(module);
+	}
+	pthread_mutex_unlock(&fuse_context_lock);
+	return m;
+}
+
+static void fuse_put_module(struct fuse_module *m)
+{
+	pthread_mutex_lock(&fuse_context_lock);
+	assert(m->ctr > 0);
+	m->ctr--;
+	if (!m->ctr && m->so) {
+		struct fusemod_so *so = m->so;
+		assert(so->ctr > 0);
+		so->ctr--;
+		if (!so->ctr) {
+			struct fuse_module **mp;
+			for (mp = &fuse_modules; *mp;) {
+				if ((*mp)->so == so)
+					*mp = (*mp)->next;
+				else
+					mp = &(*mp)->next;
+			}
+			dlclose(so->handle);
+			free(so);
+		}
+	}
+	pthread_mutex_unlock(&fuse_context_lock);
+}
+
+static void init_list_head(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+static int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+static void list_add(struct list_head *new, struct list_head *prev,
+		     struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+static inline void list_add_head(struct list_head *new, struct list_head *head)
+{
+	list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	list_add(new, head->prev, head);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+	struct list_head *prev = entry->prev;
+	struct list_head *next = entry->next;
+
+	next->prev = prev;
+	prev->next = next;
+}
+
+static inline int lru_enabled(struct fuse *f)
+{
+	return f->conf.remember > 0;
+}
+
+static struct node_lru *node_lru(struct node *node)
+{
+	return (struct node_lru *) node;
+}
+
+static size_t get_node_size(struct fuse *f)
+{
+	if (lru_enabled(f))
+		return sizeof(struct node_lru);
+	else
+		return sizeof(struct node);
+}
+
+#ifdef FUSE_NODE_SLAB
+static struct node_slab *list_to_slab(struct list_head *head)
+{
+	return (struct node_slab *) head;
+}
+
+static struct node_slab *node_to_slab(struct fuse *f, struct node *node)
+{
+	return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1));
+}
+
+static int alloc_slab(struct fuse *f)
+{
+	void *mem;
+	struct node_slab *slab;
+	char *start;
+	size_t num;
+	size_t i;
+	size_t node_size = get_node_size(f);
+
+	mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE,
+		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+	if (mem == MAP_FAILED)
+		return -1;
+
+	slab = mem;
+	init_list_head(&slab->freelist);
+	slab->used = 0;
+	num = (f->pagesize - sizeof(struct node_slab)) / node_size;
+
+	start = (char *) mem + f->pagesize - num * node_size;
+	for (i = 0; i < num; i++) {
+		struct list_head *n;
+
+		n = (struct list_head *) (start + i * node_size);
+		list_add_tail(n, &slab->freelist);
+	}
+	list_add_tail(&slab->list, &f->partial_slabs);
+
+	return 0;
+}
+
+static struct node *alloc_node(struct fuse *f)
+{
+	struct node_slab *slab;
+	struct list_head *node;
+
+	if (list_empty(&f->partial_slabs)) {
+		int res = alloc_slab(f);
+		if (res != 0)
+			return NULL;
+	}
+	slab = list_to_slab(f->partial_slabs.next);
+	slab->used++;
+	node = slab->freelist.next;
+	list_del(node);
+	if (list_empty(&slab->freelist)) {
+		list_del(&slab->list);
+		list_add_tail(&slab->list, &f->full_slabs);
+	}
+	memset(node, 0, sizeof(struct node));
+
+	return (struct node *) node;
+}
+
+static void free_slab(struct fuse *f, struct node_slab *slab)
+{
+	int res;
+
+	list_del(&slab->list);
+	res = munmap(slab, f->pagesize);
+	if (res == -1)
+		fprintf(stderr, "fuse warning: munmap(%p) failed\n", slab);
+}
+
+static void free_node_mem(struct fuse *f, struct node *node)
+{
+	struct node_slab *slab = node_to_slab(f, node);
+	struct list_head *n = (struct list_head *) node;
+
+	slab->used--;
+	if (slab->used) {
+		if (list_empty(&slab->freelist)) {
+			list_del(&slab->list);
+			list_add_tail(&slab->list, &f->partial_slabs);
+		}
+		list_add_head(n, &slab->freelist);
+	} else {
+		free_slab(f, slab);
+	}
+}
+#else
+static struct node *alloc_node(struct fuse *f)
+{
+	return (struct node *) calloc(1, get_node_size(f));
+}
+
+static void free_node_mem(struct fuse *f, struct node *node)
+{
+	(void) f;
+	free(node);
+}
+#endif
+
+static size_t id_hash(struct fuse *f, fuse_ino_t ino)
+{
+	uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size;
+	uint64_t oldhash = hash % (f->id_table.size / 2);
+
+	if (oldhash >= f->id_table.split)
+		return oldhash;
+	else
+		return hash;
+}
+
+static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
+{
+	size_t hash = id_hash(f, nodeid);
+	struct node *node;
+
+	for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
+		if (node->nodeid == nodeid)
+			return node;
+
+	return NULL;
+}
+
+static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
+{
+	struct node *node = get_node_nocheck(f, nodeid);
+	if (!node) {
+		fprintf(stderr, "fuse internal error: node %llu not found\n",
+			(unsigned long long) nodeid);
+		abort();
+	}
+	return node;
+}
+
+static void curr_time(struct timespec *now);
+static double diff_timespec(const struct timespec *t1,
+			   const struct timespec *t2);
+
+static void remove_node_lru(struct node *node)
+{
+	struct node_lru *lnode = node_lru(node);
+	list_del(&lnode->lru);
+	init_list_head(&lnode->lru);
+}
+
+static void set_forget_time(struct fuse *f, struct node *node)
+{
+	struct node_lru *lnode = node_lru(node);
+
+	list_del(&lnode->lru);
+	list_add_tail(&lnode->lru, &f->lru_table);
+	curr_time(&lnode->forget_time);
+}
+
+static void free_node(struct fuse *f, struct node *node)
+{
+	if (node->name != node->inline_name)
+		free(node->name);
+	free_node_mem(f, node);
+}
+
+static void node_table_reduce(struct node_table *t)
+{
+	size_t newsize = t->size / 2;
+	void *newarray;
+
+	if (newsize < NODE_TABLE_MIN_SIZE)
+		return;
+
+	newarray = realloc(t->array, sizeof(struct node *) * newsize);
+	if (newarray != NULL)
+		t->array = newarray;
+
+	t->size = newsize;
+	t->split = t->size / 2;
+}
+
+static void remerge_id(struct fuse *f)
+{
+	struct node_table *t = &f->id_table;
+	int iter;
+
+	if (t->split == 0)
+		node_table_reduce(t);
+
+	for (iter = 8; t->split > 0 && iter; iter--) {
+		struct node **upper;
+
+		t->split--;
+		upper = &t->array[t->split + t->size / 2];
+		if (*upper) {
+			struct node **nodep;
+
+			for (nodep = &t->array[t->split]; *nodep;
+			     nodep = &(*nodep)->id_next);
+
+			*nodep = *upper;
+			*upper = NULL;
+			break;
+		}
+	}
+}
+
+static void unhash_id(struct fuse *f, struct node *node)
+{
+	struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
+
+	for (; *nodep != NULL; nodep = &(*nodep)->id_next)
+		if (*nodep == node) {
+			*nodep = node->id_next;
+			f->id_table.use--;
+
+			if(f->id_table.use < f->id_table.size / 4)
+				remerge_id(f);
+			return;
+		}
+}
+
+static int node_table_resize(struct node_table *t)
+{
+	size_t newsize = t->size * 2;
+	void *newarray;
+
+	newarray = realloc(t->array, sizeof(struct node *) * newsize);
+	if (newarray == NULL)
+		return -1;
+
+	t->array = newarray;
+	memset(t->array + t->size, 0, t->size * sizeof(struct node *));
+	t->size = newsize;
+	t->split = 0;
+
+	return 0;
+}
+
+static void rehash_id(struct fuse *f)
+{
+	struct node_table *t = &f->id_table;
+	struct node **nodep;
+	struct node **next;
+	size_t hash;
+
+	if (t->split == t->size / 2)
+		return;
+
+	hash = t->split;
+	t->split++;
+	for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
+		struct node *node = *nodep;
+		size_t newhash = id_hash(f, node->nodeid);
+
+		if (newhash != hash) {
+			next = nodep;
+			*nodep = node->id_next;
+			node->id_next = t->array[newhash];
+			t->array[newhash] = node;
+		} else {
+			next = &node->id_next;
+		}
+	}
+	if (t->split == t->size / 2)
+		node_table_resize(t);
+}
+
+static void hash_id(struct fuse *f, struct node *node)
+{
+	size_t hash = id_hash(f, node->nodeid);
+	node->id_next = f->id_table.array[hash];
+	f->id_table.array[hash] = node;
+	f->id_table.use++;
+
+	if (f->id_table.use >= f->id_table.size / 2)
+		rehash_id(f);
+}
+
+static size_t name_hash(struct fuse *f, fuse_ino_t parent,
+			const char *name)
+{
+	uint64_t hash = parent;
+	uint64_t oldhash;
+
+	for (; *name; name++)
+		hash = hash * 31 + (unsigned char) *name;
+
+	hash %= f->name_table.size;
+	oldhash = hash % (f->name_table.size / 2);
+	if (oldhash >= f->name_table.split)
+		return oldhash;
+	else
+		return hash;
+}
+
+static void unref_node(struct fuse *f, struct node *node);
+
+static void remerge_name(struct fuse *f)
+{
+	struct node_table *t = &f->name_table;
+	int iter;
+
+	if (t->split == 0)
+		node_table_reduce(t);
+
+	for (iter = 8; t->split > 0 && iter; iter--) {
+		struct node **upper;
+
+		t->split--;
+		upper = &t->array[t->split + t->size / 2];
+		if (*upper) {
+			struct node **nodep;
+
+			for (nodep = &t->array[t->split]; *nodep;
+			     nodep = &(*nodep)->name_next);
+
+			*nodep = *upper;
+			*upper = NULL;
+			break;
+		}
+	}
+}
+
+static void unhash_name(struct fuse *f, struct node *node)
+{
+	if (node->name) {
+		size_t hash = name_hash(f, node->parent->nodeid, node->name);
+		struct node **nodep = &f->name_table.array[hash];
+
+		for (; *nodep != NULL; nodep = &(*nodep)->name_next)
+			if (*nodep == node) {
+				*nodep = node->name_next;
+				node->name_next = NULL;
+				unref_node(f, node->parent);
+				if (node->name != node->inline_name)
+					free(node->name);
+				node->name = NULL;
+				node->parent = NULL;
+				f->name_table.use--;
+
+				if (f->name_table.use < f->name_table.size / 4)
+					remerge_name(f);
+				return;
+			}
+		fprintf(stderr,
+			"fuse internal error: unable to unhash node: %llu\n",
+			(unsigned long long) node->nodeid);
+		abort();
+	}
+}
+
+static void rehash_name(struct fuse *f)
+{
+	struct node_table *t = &f->name_table;
+	struct node **nodep;
+	struct node **next;
+	size_t hash;
+
+	if (t->split == t->size / 2)
+		return;
+
+	hash = t->split;
+	t->split++;
+	for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
+		struct node *node = *nodep;
+		size_t newhash = name_hash(f, node->parent->nodeid, node->name);
+
+		if (newhash != hash) {
+			next = nodep;
+			*nodep = node->name_next;
+			node->name_next = t->array[newhash];
+			t->array[newhash] = node;
+		} else {
+			next = &node->name_next;
+		}
+	}
+	if (t->split == t->size / 2)
+		node_table_resize(t);
+}
+
+static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
+		     const char *name)
+{
+	size_t hash = name_hash(f, parentid, name);
+	struct node *parent = get_node(f, parentid);
+	if (strlen(name) < sizeof(node->inline_name)) {
+		strcpy(node->inline_name, name);
+		node->name = node->inline_name;
+	} else {
+		node->name = strdup(name);
+		if (node->name == NULL)
+			return -1;
+	}
+
+	parent->refctr ++;
+	node->parent = parent;
+	node->name_next = f->name_table.array[hash];
+	f->name_table.array[hash] = node;
+	f->name_table.use++;
+
+	if (f->name_table.use >= f->name_table.size / 2)
+		rehash_name(f);
+
+	return 0;
+}
+
+static void delete_node(struct fuse *f, struct node *node)
+{
+	if (f->conf.debug)
+		fprintf(stderr, "DELETE: %llu\n",
+			(unsigned long long) node->nodeid);
+
+	assert(node->treelock == 0);
+	unhash_name(f, node);
+	if (lru_enabled(f))
+		remove_node_lru(node);
+	unhash_id(f, node);
+	free_node(f, node);
+}
+
+static void unref_node(struct fuse *f, struct node *node)
+{
+	assert(node->refctr > 0);
+	node->refctr --;
+	if (!node->refctr)
+		delete_node(f, node);
+}
+
+static fuse_ino_t next_id(struct fuse *f)
+{
+	do {
+		f->ctr = (f->ctr + 1) & 0xffffffff;
+		if (!f->ctr)
+			f->generation ++;
+	} while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
+		 get_node_nocheck(f, f->ctr) != NULL);
+	return f->ctr;
+}
+
+static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
+				const char *name)
+{
+	size_t hash = name_hash(f, parent, name);
+	struct node *node;
+
+	for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
+		if (node->parent->nodeid == parent &&
+		    strcmp(node->name, name) == 0)
+			return node;
+
+	return NULL;
+}
+
+static void inc_nlookup(struct node *node)
+{
+	if (!node->nlookup)
+		node->refctr++;
+	node->nlookup++;
+}
+
+static struct node *find_node(struct fuse *f, fuse_ino_t parent,
+			      const char *name)
+{
+	struct node *node;
+
+	pthread_mutex_lock(&f->lock);
+	if (!name)
+		node = get_node(f, parent);
+	else
+		node = lookup_node(f, parent, name);
+	if (node == NULL) {
+		node = alloc_node(f);
+		if (node == NULL)
+			goto out_err;
+
+		node->nodeid = next_id(f);
+		node->generation = f->generation;
+		if (f->conf.remember)
+			inc_nlookup(node);
+
+		if (hash_name(f, node, parent, name) == -1) {
+			free_node(f, node);
+			node = NULL;
+			goto out_err;
+		}
+		hash_id(f, node);
+		if (lru_enabled(f)) {
+			struct node_lru *lnode = node_lru(node);
+			init_list_head(&lnode->lru);
+		}
+	} else if (lru_enabled(f) && node->nlookup == 1) {
+		remove_node_lru(node);
+	}
+	inc_nlookup(node);
+out_err:
+	pthread_mutex_unlock(&f->lock);
+	return node;
+}
+
+static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name)
+{
+	size_t len = strlen(name);
+
+	if (s - len <= *buf) {
+		unsigned pathlen = *bufsize - (s - *buf);
+		unsigned newbufsize = *bufsize;
+		char *newbuf;
+
+		while (newbufsize < pathlen + len + 1) {
+			if (newbufsize >= 0x80000000)
+				newbufsize = 0xffffffff;
+			else
+				newbufsize *= 2;
+		}
+
+		newbuf = realloc(*buf, newbufsize);
+		if (newbuf == NULL)
+			return NULL;
+
+		*buf = newbuf;
+		s = newbuf + newbufsize - pathlen;
+		memmove(s, newbuf + *bufsize - pathlen, pathlen);
+		*bufsize = newbufsize;
+	}
+	s -= len;
+	strncpy(s, name, len);
+	s--;
+	*s = '/';
+
+	return s;
+}
+
+static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
+			struct node *end)
+{
+	struct node *node;
+
+	if (wnode) {
+		assert(wnode->treelock == TREELOCK_WRITE);
+		wnode->treelock = 0;
+	}
+
+	for (node = get_node(f, nodeid);
+	     node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) {
+		assert(node->treelock != 0);
+		assert(node->treelock != TREELOCK_WAIT_OFFSET);
+		assert(node->treelock != TREELOCK_WRITE);
+		node->treelock--;
+		if (node->treelock == TREELOCK_WAIT_OFFSET)
+			node->treelock = 0;
+	}
+}
+
+static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
+			char **path, struct node **wnodep, bool need_lock)
+{
+	unsigned bufsize = 256;
+	char *buf;
+	char *s;
+	struct node *node;
+	struct node *wnode = NULL;
+	int err;
+
+	*path = NULL;
+
+	err = -ENOMEM;
+	buf = malloc(bufsize);
+	if (buf == NULL)
+		goto out_err;
+
+	s = buf + bufsize - 1;
+	*s = '\0';
+
+	if (name != NULL) {
+		s = add_name(&buf, &bufsize, s, name);
+		err = -ENOMEM;
+		if (s == NULL)
+			goto out_free;
+	}
+
+	if (wnodep) {
+		assert(need_lock);
+		wnode = lookup_node(f, nodeid, name);
+		if (wnode) {
+			if (wnode->treelock != 0) {
+				if (wnode->treelock > 0)
+					wnode->treelock += TREELOCK_WAIT_OFFSET;
+				err = -EAGAIN;
+				goto out_free;
+			}
+			wnode->treelock = TREELOCK_WRITE;
+		}
+	}
+
+	for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
+	     node = node->parent) {
+		err = -ENOENT;
+		if (node->name == NULL || node->parent == NULL)
+			goto out_unlock;
+
+		err = -ENOMEM;
+		s = add_name(&buf, &bufsize, s, node->name);
+		if (s == NULL)
+			goto out_unlock;
+
+		if (need_lock) {
+			err = -EAGAIN;
+			if (node->treelock < 0)
+				goto out_unlock;
+
+			node->treelock++;
+		}
+	}
+
+	if (s[0])
+		memmove(buf, s, bufsize - (s - buf));
+	else
+		strcpy(buf, "/");
+
+	*path = buf;
+	if (wnodep)
+		*wnodep = wnode;
+
+	return 0;
+
+ out_unlock:
+	if (need_lock)
+		unlock_path(f, nodeid, wnode, node);
+ out_free:
+	free(buf);
+
+ out_err:
+	return err;
+}
+
+static void queue_element_unlock(struct fuse *f, struct lock_queue_element *qe)
+{
+	struct node *wnode;
+
+	if (qe->first_locked) {
+		wnode = qe->wnode1 ? *qe->wnode1 : NULL;
+		unlock_path(f, qe->nodeid1, wnode, NULL);
+	}
+	if (qe->second_locked) {
+		wnode = qe->wnode2 ? *qe->wnode2 : NULL;
+		unlock_path(f, qe->nodeid2, wnode, NULL);
+	}
+}
+
+static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
+{
+	int err;
+	bool first = (qe == f->lockq);
+
+	if (!qe->path1) {
+		/* Just waiting for it to be unlocked */
+		if (get_node(f, qe->nodeid1)->treelock == 0)
+			pthread_cond_signal(&qe->cond);
+
+		return;
+	}
+
+	if (!qe->first_locked) {
+		err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
+				   qe->wnode1, true);
+		if (!err)
+			qe->first_locked = true;
+		else if (err != -EAGAIN)
+			goto err_unlock;
+	}
+	if (!qe->second_locked && qe->path2) {
+		err = try_get_path(f, qe->nodeid2, qe->name2, qe->path2,
+				   qe->wnode2, true);
+		if (!err)
+			qe->second_locked = true;
+		else if (err != -EAGAIN)
+			goto err_unlock;
+	}
+
+	if (qe->first_locked && (qe->second_locked || !qe->path2)) {
+		err = 0;
+		goto done;
+	}
+
+	/*
+	 * Only let the first element be partially locked otherwise there could
+	 * be a deadlock.
+	 *
+	 * But do allow the first element to be partially locked to prevent
+	 * starvation.
+	 */
+	if (!first)
+		queue_element_unlock(f, qe);
+
+	/* keep trying */
+	return;
+
+err_unlock:
+	queue_element_unlock(f, qe);
+done:
+	qe->err = err;
+	qe->done = true;
+	pthread_cond_signal(&qe->cond);
+}
+
+static void wake_up_queued(struct fuse *f)
+{
+	struct lock_queue_element *qe;
+
+	for (qe = f->lockq; qe != NULL; qe = qe->next)
+		queue_element_wakeup(f, qe);
+}
+
+static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid,
+		       const char *name, bool wr)
+{
+	if (f->conf.debug) {
+		struct node *wnode = NULL;
+
+		if (wr)
+			wnode = lookup_node(f, nodeid, name);
+
+		if (wnode)
+			fprintf(stderr, "%s %li (w)\n",	msg, wnode->nodeid);
+		else
+			fprintf(stderr, "%s %li\n", msg, nodeid);
+	}
+}
+
+static void queue_path(struct fuse *f, struct lock_queue_element *qe)
+{
+	struct lock_queue_element **qp;
+
+	qe->done = false;
+	qe->first_locked = false;
+	qe->second_locked = false;
+	pthread_cond_init(&qe->cond, NULL);
+	qe->next = NULL;
+	for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
+	*qp = qe;
+}
+
+static void dequeue_path(struct fuse *f, struct lock_queue_element *qe)
+{
+	struct lock_queue_element **qp;
+
+	pthread_cond_destroy(&qe->cond);
+	for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next);
+	*qp = qe->next;
+}
+
+static int wait_path(struct fuse *f, struct lock_queue_element *qe)
+{
+	queue_path(f, qe);
+
+	do {
+		pthread_cond_wait(&qe->cond, &f->lock);
+	} while (!qe->done);
+
+	dequeue_path(f, qe);
+
+	return qe->err;
+}
+
+static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name,
+			   char **path, struct node **wnode)
+{
+	int err;
+
+	pthread_mutex_lock(&f->lock);
+	err = try_get_path(f, nodeid, name, path, wnode, true);
+	if (err == -EAGAIN) {
+		struct lock_queue_element qe = {
+			.nodeid1 = nodeid,
+			.name1 = name,
+			.path1 = path,
+			.wnode1 = wnode,
+		};
+		debug_path(f, "QUEUE PATH", nodeid, name, !!wnode);
+		err = wait_path(f, &qe);
+		debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode);
+	}
+	pthread_mutex_unlock(&f->lock);
+
+	return err;
+}
+
+static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
+{
+	return get_path_common(f, nodeid, NULL, path, NULL);
+}
+
+static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
+{
+	int err = 0;
+
+	if (f->conf.nopath) {
+		*path = NULL;
+	} else {
+		err = get_path_common(f, nodeid, NULL, path, NULL);
+		if (err == -ENOENT && f->nullpath_ok)
+			err = 0;
+	}
+
+	return err;
+}
+
+static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
+			 char **path)
+{
+	return get_path_common(f, nodeid, name, path, NULL);
+}
+
+static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
+			   char **path, struct node **wnode)
+{
+	return get_path_common(f, nodeid, name, path, wnode);
+}
+
+static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
+			 fuse_ino_t nodeid2, const char *name2,
+			 char **path1, char **path2,
+			 struct node **wnode1, struct node **wnode2)
+{
+	int err;
+
+	/* FIXME: locking two paths needs deadlock checking */
+	err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
+	if (!err) {
+		err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
+		if (err) {
+			struct node *wn1 = wnode1 ? *wnode1 : NULL;
+
+			unlock_path(f, nodeid1, wn1, NULL);
+			free(*path1);
+		}
+	}
+	return err;
+}
+
+static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
+		     fuse_ino_t nodeid2, const char *name2,
+		     char **path1, char **path2,
+		     struct node **wnode1, struct node **wnode2)
+{
+	int err;
+
+	pthread_mutex_lock(&f->lock);
+	err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
+			    path1, path2, wnode1, wnode2);
+	if (err == -EAGAIN) {
+		struct lock_queue_element qe = {
+			.nodeid1 = nodeid1,
+			.name1 = name1,
+			.path1 = path1,
+			.wnode1 = wnode1,
+			.nodeid2 = nodeid2,
+			.name2 = name2,
+			.path2 = path2,
+			.wnode2 = wnode2,
+		};
+
+		debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1);
+		debug_path(f, "      PATH2", nodeid2, name2, !!wnode2);
+		err = wait_path(f, &qe);
+		debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
+		debug_path(f, "        PATH2", nodeid2, name2, !!wnode2);
+	}
+	pthread_mutex_unlock(&f->lock);
+
+	return err;
+}
+
+static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid,
+			     struct node *wnode, char *path)
+{
+	pthread_mutex_lock(&f->lock);
+	unlock_path(f, nodeid, wnode, NULL);
+	if (f->lockq)
+		wake_up_queued(f);
+	pthread_mutex_unlock(&f->lock);
+	free(path);
+}
+
+static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path)
+{
+	if (path)
+		free_path_wrlock(f, nodeid, NULL, path);
+}
+
+static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2,
+		       struct node *wnode1, struct node *wnode2,
+		       char *path1, char *path2)
+{
+	pthread_mutex_lock(&f->lock);
+	unlock_path(f, nodeid1, wnode1, NULL);
+	unlock_path(f, nodeid2, wnode2, NULL);
+	wake_up_queued(f);
+	pthread_mutex_unlock(&f->lock);
+	free(path1);
+	free(path2);
+}
+
+static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
+{
+	struct node *node;
+	if (nodeid == FUSE_ROOT_ID)
+		return;
+	pthread_mutex_lock(&f->lock);
+	node = get_node(f, nodeid);
+
+	/*
+	 * Node may still be locked due to interrupt idiocy in open,
+	 * create and opendir
+	 */
+	while (node->nlookup == nlookup && node->treelock) {
+		struct lock_queue_element qe = {
+			.nodeid1 = nodeid,
+		};
+
+		debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false);
+		queue_path(f, &qe);
+
+		do {
+			pthread_cond_wait(&qe.cond, &f->lock);
+		} while (node->nlookup == nlookup && node->treelock);
+
+		dequeue_path(f, &qe);
+		debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false);
+	}
+
+	assert(node->nlookup >= nlookup);
+	node->nlookup -= nlookup;
+	if (!node->nlookup) {
+		unref_node(f, node);
+	} else if (lru_enabled(f) && node->nlookup == 1) {
+		set_forget_time(f, node);
+	}
+	pthread_mutex_unlock(&f->lock);
+}
+
+static void unlink_node(struct fuse *f, struct node *node)
+{
+	if (f->conf.remember) {
+		assert(node->nlookup > 1);
+		node->nlookup--;
+	}
+	unhash_name(f, node);
+}
+
+static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
+{
+	struct node *node;
+
+	pthread_mutex_lock(&f->lock);
+	node = lookup_node(f, dir, name);
+	if (node != NULL)
+		unlink_node(f, node);
+	pthread_mutex_unlock(&f->lock);
+}
+
+static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
+		       fuse_ino_t newdir, const char *newname, int hide)
+{
+	struct node *node;
+	struct node *newnode;
+	int err = 0;
+
+	pthread_mutex_lock(&f->lock);
+	node  = lookup_node(f, olddir, oldname);
+	newnode	 = lookup_node(f, newdir, newname);
+	if (node == NULL)
+		goto out;
+
+	if (newnode != NULL) {
+		if (hide) {
+			fprintf(stderr, "fuse: hidden file got created during hiding\n");
+			err = -EBUSY;
+			goto out;
+		}
+		unlink_node(f, newnode);
+	}
+
+	unhash_name(f, node);
+	if (hash_name(f, node, newdir, newname) == -1) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	if (hide)
+		node->is_hidden = 1;
+
+out:
+	pthread_mutex_unlock(&f->lock);
+	return err;
+}
+
+static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
+{
+	if (!f->conf.use_ino)
+		stbuf->st_ino = nodeid;
+	if (f->conf.set_mode)
+		stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
+				 (0777 & ~f->conf.umask);
+	if (f->conf.set_uid)
+		stbuf->st_uid = f->conf.uid;
+	if (f->conf.set_gid)
+		stbuf->st_gid = f->conf.gid;
+}
+
+static struct fuse *req_fuse(fuse_req_t req)
+{
+	return (struct fuse *) fuse_req_userdata(req);
+}
+
+static void fuse_intr_sighandler(int sig)
+{
+	(void) sig;
+	/* Nothing to do */
+}
+
+struct fuse_intr_data {
+	pthread_t id;
+	pthread_cond_t cond;
+	int finished;
+};
+
+static void fuse_interrupt(fuse_req_t req, void *d_)
+{
+	struct fuse_intr_data *d = d_;
+	struct fuse *f = req_fuse(req);
+
+	if (d->id == pthread_self())
+		return;
+
+	pthread_mutex_lock(&f->lock);
+	while (!d->finished) {
+		struct timeval now;
+		struct timespec timeout;
+
+		pthread_kill(d->id, f->conf.intr_signal);
+		gettimeofday(&now, NULL);
+		timeout.tv_sec = now.tv_sec + 1;
+		timeout.tv_nsec = now.tv_usec * 1000;
+		pthread_cond_timedwait(&d->cond, &f->lock, &timeout);
+	}
+	pthread_mutex_unlock(&f->lock);
+}
+
+static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req,
+				     struct fuse_intr_data *d)
+{
+	pthread_mutex_lock(&f->lock);
+	d->finished = 1;
+	pthread_cond_broadcast(&d->cond);
+	pthread_mutex_unlock(&f->lock);
+	fuse_req_interrupt_func(req, NULL, NULL);
+	pthread_cond_destroy(&d->cond);
+}
+
+static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d)
+{
+	d->id = pthread_self();
+	pthread_cond_init(&d->cond, NULL);
+	d->finished = 0;
+	fuse_req_interrupt_func(req, fuse_interrupt, d);
+}
+
+static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req,
+					 struct fuse_intr_data *d)
+{
+	if (f->conf.intr)
+		fuse_do_finish_interrupt(f, req, d);
+}
+
+static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
+					  struct fuse_intr_data *d)
+{
+	if (f->conf.intr)
+		fuse_do_prepare_interrupt(req, d);
+}
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+static int fuse_compat_open(struct fuse_fs *fs, const char *path,
+			    struct fuse_file_info *fi)
+{
+	int err;
+	if (!fs->compat || fs->compat >= 25)
+		err = fs->op.open(path, fi);
+	else if (fs->compat == 22) {
+		struct fuse_file_info_compat tmp;
+		memcpy(&tmp, fi, sizeof(tmp));
+		err = ((struct fuse_operations_compat22 *) &fs->op)->open(path,
+									  &tmp);
+		memcpy(fi, &tmp, sizeof(tmp));
+		fi->fh = tmp.fh;
+	} else
+		err = ((struct fuse_operations_compat2 *) &fs->op)
+			->open(path, fi->flags);
+	return err;
+}
+
+static int fuse_compat_release(struct fuse_fs *fs, const char *path,
+			       struct fuse_file_info *fi)
+{
+	if (!fs->compat || fs->compat >= 22)
+		return fs->op.release(path, fi);
+	else
+		return ((struct fuse_operations_compat2 *) &fs->op)
+			->release(path, fi->flags);
+}
+
+static int fuse_compat_opendir(struct fuse_fs *fs, const char *path,
+			       struct fuse_file_info *fi)
+{
+	if (!fs->compat || fs->compat >= 25)
+		return fs->op.opendir(path, fi);
+	else {
+		int err;
+		struct fuse_file_info_compat tmp;
+		memcpy(&tmp, fi, sizeof(tmp));
+		err = ((struct fuse_operations_compat22 *) &fs->op)
+			->opendir(path, &tmp);
+		memcpy(fi, &tmp, sizeof(tmp));
+		fi->fh = tmp.fh;
+		return err;
+	}
+}
+
+static void convert_statfs_compat(struct fuse_statfs_compat1 *compatbuf,
+				  struct statvfs *stbuf)
+{
+	stbuf->f_bsize	 = compatbuf->block_size;
+	stbuf->f_blocks	 = compatbuf->blocks;
+	stbuf->f_bfree	 = compatbuf->blocks_free;
+	stbuf->f_bavail	 = compatbuf->blocks_free;
+	stbuf->f_files	 = compatbuf->files;
+	stbuf->f_ffree	 = compatbuf->files_free;
+	stbuf->f_namemax = compatbuf->namelen;
+}
+
+static void convert_statfs_old(struct statfs *oldbuf, struct statvfs *stbuf)
+{
+	stbuf->f_bsize	 = oldbuf->f_bsize;
+	stbuf->f_blocks	 = oldbuf->f_blocks;
+	stbuf->f_bfree	 = oldbuf->f_bfree;
+	stbuf->f_bavail	 = oldbuf->f_bavail;
+	stbuf->f_files	 = oldbuf->f_files;
+	stbuf->f_ffree	 = oldbuf->f_ffree;
+	stbuf->f_namemax = oldbuf->f_namelen;
+}
+
+static int fuse_compat_statfs(struct fuse_fs *fs, const char *path,
+			      struct statvfs *buf)
+{
+	int err;
+
+	if (!fs->compat || fs->compat >= 25) {
+		err = fs->op.statfs(fs->compat == 25 ? "/" : path, buf);
+	} else if (fs->compat > 11) {
+		struct statfs oldbuf;
+		err = ((struct fuse_operations_compat22 *) &fs->op)
+			->statfs("/", &oldbuf);
+		if (!err)
+			convert_statfs_old(&oldbuf, buf);
+	} else {
+		struct fuse_statfs_compat1 compatbuf;
+		memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1));
+		err = ((struct fuse_operations_compat1 *) &fs->op)
+			->statfs(&compatbuf);
+		if (!err)
+			convert_statfs_compat(&compatbuf, buf);
+	}
+	return err;
+}
+
+#else /* __FreeBSD__ || __NetBSD__ */
+
+static inline int fuse_compat_open(struct fuse_fs *fs, char *path,
+				   struct fuse_file_info *fi)
+{
+	return fs->op.open(path, fi);
+}
+
+static inline int fuse_compat_release(struct fuse_fs *fs, const char *path,
+				      struct fuse_file_info *fi)
+{
+	return fs->op.release(path, fi);
+}
+
+static inline int fuse_compat_opendir(struct fuse_fs *fs, const char *path,
+				      struct fuse_file_info *fi)
+{
+	return fs->op.opendir(path, fi);
+}
+
+static inline int fuse_compat_statfs(struct fuse_fs *fs, const char *path,
+				     struct statvfs *buf)
+{
+	return fs->op.statfs(fs->compat == 25 ? "/" : path, buf);
+}
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.getattr) {
+		if (fs->debug)
+			fprintf(stderr, "getattr %s\n", path);
+
+		return fs->op.getattr(path, buf);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf,
+		     struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.fgetattr) {
+		if (fs->debug)
+			fprintf(stderr, "fgetattr[%llu] %s\n",
+				(unsigned long long) fi->fh, path);
+
+		return fs->op.fgetattr(path, buf, fi);
+	} else if (path && fs->op.getattr) {
+		if (fs->debug)
+			fprintf(stderr, "getattr %s\n", path);
+
+		return fs->op.getattr(path, buf);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
+		   const char *newpath)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.rename) {
+		if (fs->debug)
+			fprintf(stderr, "rename %s %s\n", oldpath, newpath);
+
+		return fs->op.rename(oldpath, newpath);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.unlink) {
+		if (fs->debug)
+			fprintf(stderr, "unlink %s\n", path);
+
+		return fs->op.unlink(path);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_rmdir(struct fuse_fs *fs, const char *path)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.rmdir) {
+		if (fs->debug)
+			fprintf(stderr, "rmdir %s\n", path);
+
+		return fs->op.rmdir(path);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.symlink) {
+		if (fs->debug)
+			fprintf(stderr, "symlink %s %s\n", linkname, path);
+
+		return fs->op.symlink(linkname, path);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.link) {
+		if (fs->debug)
+			fprintf(stderr, "link %s %s\n", oldpath, newpath);
+
+		return fs->op.link(oldpath, newpath);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_release(struct fuse_fs *fs,	 const char *path,
+		    struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.release) {
+		if (fs->debug)
+			fprintf(stderr, "release%s[%llu] flags: 0x%x\n",
+				fi->flush ? "+flush" : "",
+				(unsigned long long) fi->fh, fi->flags);
+
+		return fuse_compat_release(fs, path, fi);
+	} else {
+		return 0;
+	}
+}
+
+int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
+		    struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.opendir) {
+		int err;
+
+		if (fs->debug)
+			fprintf(stderr, "opendir flags: 0x%x %s\n", fi->flags,
+				path);
+
+		err = fuse_compat_opendir(fs, path, fi);
+
+		if (fs->debug && !err)
+			fprintf(stderr, "   opendir[%lli] flags: 0x%x %s\n",
+				(unsigned long long) fi->fh, fi->flags, path);
+
+		return err;
+	} else {
+		return 0;
+	}
+}
+
+int fuse_fs_open(struct fuse_fs *fs, const char *path,
+		 struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.open) {
+		int err;
+
+		if (fs->debug)
+			fprintf(stderr, "open flags: 0x%x %s\n", fi->flags,
+				path);
+
+		err = fuse_compat_open(fs, path, fi);
+
+		if (fs->debug && !err)
+			fprintf(stderr, "   open[%lli] flags: 0x%x %s\n",
+				(unsigned long long) fi->fh, fi->flags, path);
+
+		return err;
+	} else {
+		return 0;
+	}
+}
+
+static void fuse_free_buf(struct fuse_bufvec *buf)
+{
+	if (buf != NULL) {
+		size_t i;
+
+		for (i = 0; i < buf->count; i++)
+			free(buf->buf[i].mem);
+		free(buf);
+	}
+}
+
+int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
+		     struct fuse_bufvec **bufp, size_t size, off64_t off,
+		     struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.read || fs->op.read_buf) {
+		int res;
+
+		if (fs->debug)
+			fprintf(stderr,
+				"read[%llu] %zu bytes from %llu flags: 0x%x\n",
+				(unsigned long long) fi->fh,
+				size, (unsigned long long) off, fi->flags);
+
+		if (fs->op.read_buf) {
+			res = fs->op.read_buf(path, bufp, size, off, fi);
+		} else {
+			struct fuse_bufvec *buf;
+			void *mem;
+
+			buf = malloc(sizeof(struct fuse_bufvec));
+			if (buf == NULL)
+				return -ENOMEM;
+
+			mem = malloc(size);
+			if (mem == NULL) {
+				free(buf);
+				return -ENOMEM;
+			}
+			*buf = FUSE_BUFVEC_INIT(size);
+			buf->buf[0].mem = mem;
+			*bufp = buf;
+
+			res = fs->op.read(path, mem, size, off, fi);
+			if (res >= 0)
+				buf->buf[0].size = res;
+		}
+
+		if (fs->debug && res >= 0)
+			fprintf(stderr, "   read[%llu] %zu bytes from %llu\n",
+				(unsigned long long) fi->fh,
+				fuse_buf_size(*bufp),
+				(unsigned long long) off);
+		if (res >= 0 && fuse_buf_size(*bufp) > (int) size)
+			fprintf(stderr, "fuse: read too many bytes\n");
+
+		if (res < 0)
+			return res;
+
+		return 0;
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
+		 off64_t off, struct fuse_file_info *fi)
+{
+	int res;
+	struct fuse_bufvec *buf = NULL;
+
+	res = fuse_fs_read_buf(fs, path, &buf, size, off, fi);
+	if (res == 0) {
+		struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
+
+		dst.buf[0].mem = mem;
+		res = fuse_buf_copy(&dst, buf, 0);
+	}
+	fuse_free_buf(buf);
+
+	return res;
+}
+
+int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
+		      struct fuse_bufvec *buf, off64_t off,
+		      struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.write_buf || fs->op.write) {
+		int res;
+		size_t size = fuse_buf_size(buf);
+
+		assert(buf->idx == 0 && buf->off == 0);
+		if (fs->debug)
+			fprintf(stderr,
+				"write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
+				fi->writepage ? "page" : "",
+				(unsigned long long) fi->fh,
+				size,
+				(unsigned long long) off,
+				fi->flags);
+
+		if (fs->op.write_buf) {
+			res = fs->op.write_buf(path, buf, off, fi);
+		} else {
+			void *mem = NULL;
+			struct fuse_buf *flatbuf;
+			struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size);
+
+			if (buf->count == 1 &&
+			    !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
+				flatbuf = &buf->buf[0];
+			} else {
+				res = -ENOMEM;
+				mem = malloc(size);
+				if (mem == NULL)
+					goto out;
+
+				tmp.buf[0].mem = mem;
+				res = fuse_buf_copy(&tmp, buf, 0);
+				if (res <= 0)
+					goto out_free;
+
+				tmp.buf[0].size = res;
+				flatbuf = &tmp.buf[0];
+			}
+
+			res = fs->op.write(path, flatbuf->mem, flatbuf->size,
+					   off, fi);
+out_free:
+			free(mem);
+		}
+out:
+		if (fs->debug && res >= 0)
+			fprintf(stderr, "   write%s[%llu] %u bytes to %llu\n",
+				fi->writepage ? "page" : "",
+				(unsigned long long) fi->fh, res,
+				(unsigned long long) off);
+		if (res > (int) size)
+			fprintf(stderr, "fuse: wrote too many bytes\n");
+
+		return res;
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem,
+		  size_t size, off64_t off, struct fuse_file_info *fi)
+{
+	struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size);
+
+	bufv.buf[0].mem = (void *) mem;
+
+	return fuse_fs_write_buf(fs, path, &bufv, off, fi);
+}
+
+int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
+		  struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.fsync) {
+		if (fs->debug)
+			fprintf(stderr, "fsync[%llu] datasync: %i\n",
+				(unsigned long long) fi->fh, datasync);
+
+		return fs->op.fsync(path, datasync, fi);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
+		     struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.fsyncdir) {
+		if (fs->debug)
+			fprintf(stderr, "fsyncdir[%llu] datasync: %i\n",
+				(unsigned long long) fi->fh, datasync);
+
+		return fs->op.fsyncdir(path, datasync, fi);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_flush(struct fuse_fs *fs, const char *path,
+		  struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.flush) {
+		if (fs->debug)
+			fprintf(stderr, "flush[%llu]\n",
+				(unsigned long long) fi->fh);
+
+		return fs->op.flush(path, fi);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.statfs) {
+		if (fs->debug)
+			fprintf(stderr, "statfs %s\n", path);
+
+		return fuse_compat_statfs(fs, path, buf);
+	} else {
+		buf->f_namemax = 255;
+		buf->f_bsize = 512;
+		return 0;
+	}
+}
+
+int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
+		       struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.releasedir) {
+		if (fs->debug)
+			fprintf(stderr, "releasedir[%llu] flags: 0x%x\n",
+				(unsigned long long) fi->fh, fi->flags);
+
+		return fs->op.releasedir(path, fi);
+	} else {
+		return 0;
+	}
+}
+
+static int fill_dir_old(struct fuse_dirhandle *dh, const char *name, int type,
+			ino_t ino)
+{
+	int res;
+	struct stat stbuf;
+
+	memset(&stbuf, 0, sizeof(stbuf));
+	stbuf.st_mode = type << 12;
+	stbuf.st_ino = ino;
+
+	res = dh->filler(dh->buf, name, &stbuf, 0);
+	return res ? -ENOMEM : 0;
+}
+
+int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
+		    fuse_fill_dir_t filler, off64_t off,
+		    struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.readdir) {
+		if (fs->debug)
+			fprintf(stderr, "readdir[%llu] from %llu\n",
+				(unsigned long long) fi->fh,
+				(unsigned long long) off);
+
+		return fs->op.readdir(path, buf, filler, off, fi);
+	} else if (fs->op.getdir) {
+		struct fuse_dirhandle dh;
+
+		if (fs->debug)
+			fprintf(stderr, "getdir[%llu]\n",
+				(unsigned long long) fi->fh);
+
+		dh.filler = filler;
+		dh.buf = buf;
+		return fs->op.getdir(path, &dh, fill_dir_old);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
+		   struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.create) {
+		int err;
+
+		if (fs->debug)
+			fprintf(stderr,
+				"create flags: 0x%x %s 0%o umask=0%03o\n",
+				fi->flags, path, mode,
+				fuse_get_context()->umask);
+
+		err = fs->op.create(path, mode, fi);
+
+		if (fs->debug && !err)
+			fprintf(stderr, "   create[%llu] flags: 0x%x %s\n",
+				(unsigned long long) fi->fh, fi->flags, path);
+
+		return err;
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_lock(struct fuse_fs *fs, const char *path,
+		 struct fuse_file_info *fi, int cmd, struct flock *lock)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.lock) {
+		if (fs->debug)
+			fprintf(stderr, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
+				(unsigned long long) fi->fh,
+				(cmd == F_GETLK ? "F_GETLK" :
+				 (cmd == F_SETLK ? "F_SETLK" :
+				  (cmd == F_SETLKW ? "F_SETLKW" : "???"))),
+				(lock->l_type == F_RDLCK ? "F_RDLCK" :
+				 (lock->l_type == F_WRLCK ? "F_WRLCK" :
+				  (lock->l_type == F_UNLCK ? "F_UNLCK" :
+				   "???"))),
+				(unsigned long long) lock->l_start,
+				(unsigned long long) lock->l_len,
+				(unsigned long long) lock->l_pid);
+
+		return fs->op.lock(path, fi, cmd, lock);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_flock(struct fuse_fs *fs, const char *path,
+		  struct fuse_file_info *fi, int op)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.flock) {
+		if (fs->debug) {
+			int xop = op & ~LOCK_NB;
+
+			fprintf(stderr, "lock[%llu] %s%s\n",
+				(unsigned long long) fi->fh,
+				xop == LOCK_SH ? "LOCK_SH" :
+				(xop == LOCK_EX ? "LOCK_EX" :
+				 (xop == LOCK_UN ? "LOCK_UN" : "???")),
+				(op & LOCK_NB) ? "|LOCK_NB" : "");
+		}
+		return fs->op.flock(path, fi, op);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.chown) {
+		if (fs->debug)
+			fprintf(stderr, "chown %s %lu %lu\n", path,
+				(unsigned long) uid, (unsigned long) gid);
+
+		return fs->op.chown(path, uid, gid);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off64_t size)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.truncate) {
+		if (fs->debug)
+			fprintf(stderr, "truncate %s %llu\n", path,
+				(unsigned long long) size);
+
+		return fs->op.truncate(path, size);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off64_t size,
+		      struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.ftruncate) {
+		if (fs->debug)
+			fprintf(stderr, "ftruncate[%llu] %llu\n",
+				(unsigned long long) fi->fh,
+				(unsigned long long) size);
+
+		return fs->op.ftruncate(path, size, fi);
+	} else if (path && fs->op.truncate) {
+		if (fs->debug)
+			fprintf(stderr, "truncate %s %llu\n", path,
+				(unsigned long long) size);
+
+		return fs->op.truncate(path, size);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
+		    const struct timespec tv[2])
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.utimens) {
+		if (fs->debug)
+			fprintf(stderr, "utimens %s %li.%09lu %li.%09lu\n",
+				path, tv[0].tv_sec, tv[0].tv_nsec,
+				tv[1].tv_sec, tv[1].tv_nsec);
+
+		return fs->op.utimens(path, tv);
+	} else if(fs->op.utime) {
+		struct utimbuf buf;
+
+		if (fs->debug)
+			fprintf(stderr, "utime %s %li %li\n", path,
+				tv[0].tv_sec, tv[1].tv_sec);
+
+		buf.actime = tv[0].tv_sec;
+		buf.modtime = tv[1].tv_sec;
+		return fs->op.utime(path, &buf);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.access) {
+		if (fs->debug)
+			fprintf(stderr, "access %s 0%o\n", path, mask);
+
+		return fs->op.access(path, mask);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
+		     size_t len)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.readlink) {
+		if (fs->debug)
+			fprintf(stderr, "readlink %s %lu\n", path,
+				(unsigned long) len);
+
+		return fs->op.readlink(path, buf, len);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
+		  dev_t rdev)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.mknod) {
+		if (fs->debug)
+			fprintf(stderr, "mknod %s 0%o 0x%llx umask=0%03o\n",
+				path, mode, (unsigned long long) rdev,
+				fuse_get_context()->umask);
+
+		return fs->op.mknod(path, mode, rdev);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.mkdir) {
+		if (fs->debug)
+			fprintf(stderr, "mkdir %s 0%o umask=0%03o\n",
+				path, mode, fuse_get_context()->umask);
+
+		return fs->op.mkdir(path, mode);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
+		     const char *value, size_t size, int flags)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.setxattr) {
+		if (fs->debug)
+			fprintf(stderr, "setxattr %s %s %lu 0x%x\n",
+				path, name, (unsigned long) size, flags);
+
+		return fs->op.setxattr(path, name, value, size, flags);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
+		     char *value, size_t size)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.getxattr) {
+		if (fs->debug)
+			fprintf(stderr, "getxattr %s %s %lu\n",
+				path, name, (unsigned long) size);
+
+		return fs->op.getxattr(path, name, value, size);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
+		      size_t size)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.listxattr) {
+		if (fs->debug)
+			fprintf(stderr, "listxattr %s %lu\n",
+				path, (unsigned long) size);
+
+		return fs->op.listxattr(path, list, size);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
+		 uint64_t *idx)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.bmap) {
+		if (fs->debug)
+			fprintf(stderr, "bmap %s blocksize: %lu index: %llu\n",
+				path, (unsigned long) blocksize,
+				(unsigned long long) *idx);
+
+		return fs->op.bmap(path, blocksize, idx);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.removexattr) {
+		if (fs->debug)
+			fprintf(stderr, "removexattr %s %s\n", path, name);
+
+		return fs->op.removexattr(path, name);
+	} else {
+		return -ENOSYS;
+	}
+}
+
+int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
+		  struct fuse_file_info *fi, unsigned int flags, void *data)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.ioctl) {
+		if (fs->debug)
+			fprintf(stderr, "ioctl[%llu] 0x%x flags: 0x%x\n",
+				(unsigned long long) fi->fh, cmd, flags);
+
+		return fs->op.ioctl(path, cmd, arg, fi, flags, data);
+	} else
+		return -ENOSYS;
+}
+
+int fuse_fs_poll(struct fuse_fs *fs, const char *path,
+		 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
+		 unsigned *reventsp)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.poll) {
+		int res;
+
+		if (fs->debug)
+			fprintf(stderr, "poll[%llu] ph: %p\n",
+				(unsigned long long) fi->fh, ph);
+
+		res = fs->op.poll(path, fi, ph, reventsp);
+
+		if (fs->debug && !res)
+			fprintf(stderr, "   poll[%llu] revents: 0x%x\n",
+				(unsigned long long) fi->fh, *reventsp);
+
+		return res;
+	} else
+		return -ENOSYS;
+}
+
+int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
+		off64_t offset, off64_t length, struct fuse_file_info *fi)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.fallocate) {
+		if (fs->debug)
+			fprintf(stderr, "fallocate %s mode %x, offset: %llu, length: %llu\n",
+				path,
+				mode,
+				(unsigned long long) offset,
+				(unsigned long long) length);
+
+		return fs->op.fallocate(path, mode, offset, length, fi);
+	} else
+		return -ENOSYS;
+}
+
+static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
+{
+	struct node *node;
+	int isopen = 0;
+	pthread_mutex_lock(&f->lock);
+	node = lookup_node(f, dir, name);
+	if (node && node->open_count > 0)
+		isopen = 1;
+	pthread_mutex_unlock(&f->lock);
+	return isopen;
+}
+
+static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname,
+			 char *newname, size_t bufsize)
+{
+	struct stat buf;
+	struct node *node;
+	struct node *newnode;
+	char *newpath;
+	int res;
+	int failctr = 10;
+
+	do {
+		pthread_mutex_lock(&f->lock);
+		node = lookup_node(f, dir, oldname);
+		if (node == NULL) {
+			pthread_mutex_unlock(&f->lock);
+			return NULL;
+		}
+		do {
+			f->hidectr ++;
+			snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
+				 (unsigned int) node->nodeid, f->hidectr);
+			newnode = lookup_node(f, dir, newname);
+		} while(newnode);
+
+		res = try_get_path(f, dir, newname, &newpath, NULL, false);
+		pthread_mutex_unlock(&f->lock);
+		if (res)
+			break;
+
+		res = fuse_fs_getattr(f->fs, newpath, &buf);
+		if (res == -ENOENT)
+			break;
+		free(newpath);
+		newpath = NULL;
+	} while(res == 0 && --failctr);
+
+	return newpath;
+}
+
+static int hide_node(struct fuse *f, const char *oldpath,
+		     fuse_ino_t dir, const char *oldname)
+{
+	char newname[64];
+	char *newpath;
+	int err = -EBUSY;
+
+	newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
+	if (newpath) {
+		err = fuse_fs_rename(f->fs, oldpath, newpath);
+		if (!err)
+			err = rename_node(f, dir, oldname, dir, newname, 1);
+		free(newpath);
+	}
+	return err;
+}
+
+static int mtime_eq(const struct stat *stbuf, const struct timespec *ts)
+{
+	return stbuf->st_mtime == ts->tv_sec &&
+		ST_MTIM_NSEC(stbuf) == ts->tv_nsec;
+}
+
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC CLOCK_REALTIME
+#endif
+
+static void curr_time(struct timespec *now)
+{
+	static clockid_t clockid = CLOCK_MONOTONIC;
+	int res = clock_gettime(clockid, now);
+	if (res == -1 && errno == EINVAL) {
+		clockid = CLOCK_REALTIME;
+		res = clock_gettime(clockid, now);
+	}
+	if (res == -1) {
+		perror("fuse: clock_gettime");
+		abort();
+	}
+}
+
+static void update_stat(struct node *node, const struct stat *stbuf)
+{
+	if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) ||
+				  stbuf->st_size != node->size))
+		node->cache_valid = 0;
+	node->mtime.tv_sec = stbuf->st_mtime;
+	node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf);
+	node->size = stbuf->st_size;
+	curr_time(&node->stat_updated);
+}
+
+static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
+		       const char *name, const char *path,
+		       struct fuse_entry_param *e, struct fuse_file_info *fi)
+{
+	int res;
+
+	memset(e, 0, sizeof(struct fuse_entry_param));
+	if (fi)
+		res = fuse_fs_fgetattr(f->fs, path, &e->attr, fi);
+	else
+		res = fuse_fs_getattr(f->fs, path, &e->attr);
+	if (res == 0) {
+		struct node *node;
+
+		node = find_node(f, nodeid, name);
+		if (node == NULL)
+			res = -ENOMEM;
+		else {
+			e->ino = node->nodeid;
+			e->generation = node->generation;
+			e->entry_timeout = f->conf.entry_timeout;
+			e->attr_timeout = f->conf.attr_timeout;
+			if (f->conf.auto_cache) {
+				pthread_mutex_lock(&f->lock);
+				update_stat(node, &e->attr);
+				pthread_mutex_unlock(&f->lock);
+			}
+			set_stat(f, e->ino, &e->attr);
+			if (f->conf.debug)
+				fprintf(stderr, "   NODEID: %lu\n",
+					(unsigned long) e->ino);
+		}
+	}
+	return res;
+}
+
+static struct fuse_context_i *fuse_get_context_internal(void)
+{
+	struct fuse_context_i *c;
+
+	c = (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
+	if (c == NULL) {
+		c = (struct fuse_context_i *)
+			calloc(1, sizeof(struct fuse_context_i));
+		if (c == NULL) {
+			/* This is hard to deal with properly, so just
+			   abort.  If memory is so low that the
+			   context cannot be allocated, there's not
+			   much hope for the filesystem anyway */
+			fprintf(stderr, "fuse: failed to allocate thread specific data\n");
+			abort();
+		}
+		pthread_setspecific(fuse_context_key, c);
+	}
+	return c;
+}
+
+static void fuse_freecontext(void *data)
+{
+	free(data);
+}
+
+static int fuse_create_context_key(void)
+{
+	int err = 0;
+	pthread_mutex_lock(&fuse_context_lock);
+	if (!fuse_context_ref) {
+		err = pthread_key_create(&fuse_context_key, fuse_freecontext);
+		if (err) {
+			fprintf(stderr, "fuse: failed to create thread specific key: %s\n",
+				strerror(err));
+			pthread_mutex_unlock(&fuse_context_lock);
+			return -1;
+		}
+	}
+	fuse_context_ref++;
+	pthread_mutex_unlock(&fuse_context_lock);
+	return 0;
+}
+
+static void fuse_delete_context_key(void)
+{
+	pthread_mutex_lock(&fuse_context_lock);
+	fuse_context_ref--;
+	if (!fuse_context_ref) {
+		free(pthread_getspecific(fuse_context_key));
+		pthread_key_delete(fuse_context_key);
+	}
+	pthread_mutex_unlock(&fuse_context_lock);
+}
+
+static struct fuse *req_fuse_prepare(fuse_req_t req)
+{
+	struct fuse_context_i *c = fuse_get_context_internal();
+	const struct fuse_ctx *ctx = fuse_req_ctx(req);
+	c->req = req;
+	c->ctx.fuse = req_fuse(req);
+	c->ctx.uid = ctx->uid;
+	c->ctx.gid = ctx->gid;
+	c->ctx.pid = ctx->pid;
+	c->ctx.umask = ctx->umask;
+	return c->ctx.fuse;
+}
+
+static inline void reply_err(fuse_req_t req, int err)
+{
+	/* fuse_reply_err() uses non-negated errno values */
+	fuse_reply_err(req, -err);
+}
+
+static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
+			int err)
+{
+	if (!err) {
+		struct fuse *f = req_fuse(req);
+		if (fuse_reply_entry(req, e) == -ENOENT) {
+			/* Skip forget for negative result */
+			if  (e->ino != 0)
+				forget_node(f, e->ino, 1);
+		}
+	} else
+		reply_err(req, err);
+}
+
+void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (!fs->op.write_buf)
+		conn->want &= ~FUSE_CAP_SPLICE_READ;
+	if (!fs->op.lock)
+		conn->want &= ~FUSE_CAP_POSIX_LOCKS;
+	if (!fs->op.flock)
+		conn->want &= ~FUSE_CAP_FLOCK_LOCKS;
+	if (fs->op.init)
+		fs->user_data = fs->op.init(conn);
+}
+
+static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
+{
+	struct fuse *f = (struct fuse *) data;
+	struct fuse_context_i *c = fuse_get_context_internal();
+
+	memset(c, 0, sizeof(*c));
+	c->ctx.fuse = f;
+	conn->want |= FUSE_CAP_EXPORT_SUPPORT;
+	fuse_fs_init(f->fs, conn);
+}
+
+void fuse_fs_destroy(struct fuse_fs *fs)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.destroy)
+		fs->op.destroy(fs->user_data);
+	if (fs->m)
+		fuse_put_module(fs->m);
+	free(fs);
+}
+
+static void fuse_lib_destroy(void *data)
+{
+	struct fuse *f = (struct fuse *) data;
+	struct fuse_context_i *c = fuse_get_context_internal();
+
+	memset(c, 0, sizeof(*c));
+	c->ctx.fuse = f;
+	fuse_fs_destroy(f->fs);
+	f->fs = NULL;
+}
+
+static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
+			    const char *name)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_entry_param e;
+	char *path;
+	int err;
+	struct node *dot = NULL;
+
+	if (name[0] == '.') {
+		int len = strlen(name);
+
+		if (len == 1 || (name[1] == '.' && len == 2)) {
+			pthread_mutex_lock(&f->lock);
+			if (len == 1) {
+				if (f->conf.debug)
+					fprintf(stderr, "LOOKUP-DOT\n");
+				dot = get_node_nocheck(f, parent);
+				if (dot == NULL) {
+					pthread_mutex_unlock(&f->lock);
+					reply_entry(req, &e, -ESTALE);
+					return;
+				}
+				dot->refctr++;
+			} else {
+				if (f->conf.debug)
+					fprintf(stderr, "LOOKUP-DOTDOT\n");
+				parent = get_node(f, parent)->parent->nodeid;
+			}
+			pthread_mutex_unlock(&f->lock);
+			name = NULL;
+		}
+	}
+
+	err = get_path_name(f, parent, name, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		if (f->conf.debug)
+			fprintf(stderr, "LOOKUP %s\n", path);
+		fuse_prepare_interrupt(f, req, &d);
+		err = lookup_path(f, parent, name, path, &e, NULL);
+		if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
+			e.ino = 0;
+			e.entry_timeout = f->conf.negative_timeout;
+			err = 0;
+		}
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, parent, path);
+	}
+	if (dot) {
+		pthread_mutex_lock(&f->lock);
+		unref_node(f, dot);
+		pthread_mutex_unlock(&f->lock);
+	}
+	reply_entry(req, &e, err);
+}
+
+static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
+{
+	if (f->conf.debug)
+		fprintf(stderr, "FORGET %llu/%llu\n", (unsigned long long)ino,
+			(unsigned long long) nlookup);
+	forget_node(f, ino, nlookup);
+}
+
+static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino,
+			    unsigned long nlookup)
+{
+	do_forget(req_fuse(req), ino, nlookup);
+	fuse_reply_none(req);
+}
+
+static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
+				  struct fuse_forget_data *forgets)
+{
+	struct fuse *f = req_fuse(req);
+	size_t i;
+
+	for (i = 0; i < count; i++)
+		do_forget(f, forgets[i].ino, forgets[i].nlookup);
+
+	fuse_reply_none(req);
+}
+
+
+static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
+			     struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct stat buf;
+	char *path;
+	int err;
+
+	memset(&buf, 0, sizeof(buf));
+
+	if (fi != NULL && f->fs->op.fgetattr)
+		err = get_path_nullok(f, ino, &path);
+	else
+		err = get_path(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		if (fi)
+			err = fuse_fs_fgetattr(f->fs, path, &buf, fi);
+		else
+			err = fuse_fs_getattr(f->fs, path, &buf);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	if (!err) {
+		struct node *node;
+
+		pthread_mutex_lock(&f->lock);
+		node = get_node(f, ino);
+		if (node->is_hidden && buf.st_nlink > 0)
+			buf.st_nlink--;
+		if (f->conf.auto_cache)
+			update_stat(node, &buf);
+		pthread_mutex_unlock(&f->lock);
+		set_stat(f, ino, &buf);
+		fuse_reply_attr(req, &buf, f->conf.attr_timeout);
+	} else
+		reply_err(req, err);
+}
+
+int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode)
+{
+	fuse_get_context()->private_data = fs->user_data;
+	if (fs->op.chmod)
+		return fs->op.chmod(path, mode);
+	else
+		return -ENOSYS;
+}
+
+static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+			     int valid, struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct stat buf;
+	char *path;
+	int err;
+
+	if (valid == FUSE_SET_ATTR_SIZE && fi != NULL &&
+	    f->fs->op.ftruncate && f->fs->op.fgetattr)
+		err = get_path_nullok(f, ino, &path);
+	else
+		err = get_path(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = 0;
+		if (!err && (valid & FUSE_SET_ATTR_MODE))
+			err = fuse_fs_chmod(f->fs, path, attr->st_mode);
+		if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) {
+			uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
+				attr->st_uid : (uid_t) -1;
+			gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
+				attr->st_gid : (gid_t) -1;
+			err = fuse_fs_chown(f->fs, path, uid, gid);
+		}
+		if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
+			if (fi)
+				err = fuse_fs_ftruncate(f->fs, path,
+							attr->st_size, fi);
+			else
+				err = fuse_fs_truncate(f->fs, path,
+						       attr->st_size);
+		}
+#ifdef HAVE_UTIMENSAT
+		if (!err && f->utime_omit_ok &&
+		    (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
+			struct timespec tv[2];
+
+			tv[0].tv_sec = 0;
+			tv[1].tv_sec = 0;
+			tv[0].tv_nsec = UTIME_OMIT;
+			tv[1].tv_nsec = UTIME_OMIT;
+
+			if (valid & FUSE_SET_ATTR_ATIME_NOW)
+				tv[0].tv_nsec = UTIME_NOW;
+			else if (valid & FUSE_SET_ATTR_ATIME)
+				tv[0] = attr->st_atim;
+
+			if (valid & FUSE_SET_ATTR_MTIME_NOW)
+				tv[1].tv_nsec = UTIME_NOW;
+			else if (valid & FUSE_SET_ATTR_MTIME)
+				tv[1] = attr->st_mtim;
+
+			err = fuse_fs_utimens(f->fs, path, tv);
+		} else
+#endif
+		if (!err &&
+		    (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
+		    (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
+			struct timespec tv[2];
+			tv[0].tv_sec = attr->st_atime;
+			tv[0].tv_nsec = ST_ATIM_NSEC(attr);
+			tv[1].tv_sec = attr->st_mtime;
+			tv[1].tv_nsec = ST_MTIM_NSEC(attr);
+			err = fuse_fs_utimens(f->fs, path, tv);
+		}
+		if (!err) {
+			if (fi)
+				err = fuse_fs_fgetattr(f->fs, path, &buf, fi);
+			else
+				err = fuse_fs_getattr(f->fs, path, &buf);
+		}
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	if (!err) {
+		if (f->conf.auto_cache) {
+			pthread_mutex_lock(&f->lock);
+			update_stat(get_node(f, ino), &buf);
+			pthread_mutex_unlock(&f->lock);
+		}
+		set_stat(f, ino, &buf);
+		fuse_reply_attr(req, &buf, f->conf.attr_timeout);
+	} else
+		reply_err(req, err);
+}
+
+static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char *path;
+	int err;
+
+	err = get_path(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_access(f->fs, path, mask);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	reply_err(req, err);
+}
+
+static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char linkname[PATH_MAX + 1];
+	char *path;
+	int err;
+
+	err = get_path(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname));
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	if (!err) {
+		linkname[PATH_MAX] = '\0';
+		fuse_reply_readlink(req, linkname);
+	} else
+		reply_err(req, err);
+}
+
+static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
+			   mode_t mode, dev_t rdev)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_entry_param e;
+	char *path;
+	int err;
+
+	err = get_path_name(f, parent, name, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		err = -ENOSYS;
+		if (S_ISREG(mode)) {
+			struct fuse_file_info fi;
+
+			memset(&fi, 0, sizeof(fi));
+			fi.flags = O_CREAT | O_EXCL | O_WRONLY;
+			err = fuse_fs_create(f->fs, path, mode, &fi);
+			if (!err) {
+				err = lookup_path(f, parent, name, path, &e,
+						  &fi);
+				fuse_fs_release(f->fs, path, &fi);
+			}
+		}
+		if (err == -ENOSYS) {
+			err = fuse_fs_mknod(f->fs, path, mode, rdev);
+			if (!err)
+				err = lookup_path(f, parent, name, path, &e,
+						  NULL);
+		}
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, parent, path);
+	}
+	reply_entry(req, &e, err);
+}
+
+static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
+			   mode_t mode)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_entry_param e;
+	char *path;
+	int err;
+
+	err = get_path_name(f, parent, name, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_mkdir(f->fs, path, mode);
+		if (!err)
+			err = lookup_path(f, parent, name, path, &e, NULL);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, parent, path);
+	}
+	reply_entry(req, &e, err);
+}
+
+static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
+			    const char *name)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct node *wnode;
+	char *path;
+	int err;
+
+	err = get_path_wrlock(f, parent, name, &path, &wnode);
+	if (!err) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		if (!f->conf.hard_remove && is_open(f, parent, name)) {
+			err = hide_node(f, path, parent, name);
+		} else {
+			err = fuse_fs_unlink(f->fs, path);
+			if (!err)
+				remove_node(f, parent, name);
+		}
+		fuse_finish_interrupt(f, req, &d);
+		free_path_wrlock(f, parent, wnode, path);
+	}
+	reply_err(req, err);
+}
+
+static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct node *wnode;
+	char *path;
+	int err;
+
+	err = get_path_wrlock(f, parent, name, &path, &wnode);
+	if (!err) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_rmdir(f->fs, path);
+		fuse_finish_interrupt(f, req, &d);
+		if (!err)
+			remove_node(f, parent, name);
+		free_path_wrlock(f, parent, wnode, path);
+	}
+	reply_err(req, err);
+}
+
+static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
+			     fuse_ino_t parent, const char *name)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_entry_param e;
+	char *path;
+	int err;
+
+	err = get_path_name(f, parent, name, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_symlink(f->fs, linkname, path);
+		if (!err)
+			err = lookup_path(f, parent, name, path, &e, NULL);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, parent, path);
+	}
+	reply_entry(req, &e, err);
+}
+
+static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
+			    const char *oldname, fuse_ino_t newdir,
+			    const char *newname)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char *oldpath;
+	char *newpath;
+	struct node *wnode1;
+	struct node *wnode2;
+	int err;
+
+	err = get_path2(f, olddir, oldname, newdir, newname,
+			&oldpath, &newpath, &wnode1, &wnode2);
+	if (!err) {
+		struct fuse_intr_data d;
+		err = 0;
+		fuse_prepare_interrupt(f, req, &d);
+		if (!f->conf.hard_remove && is_open(f, newdir, newname))
+			err = hide_node(f, newpath, newdir, newname);
+		if (!err) {
+			err = fuse_fs_rename(f->fs, oldpath, newpath);
+			if (!err)
+				err = rename_node(f, olddir, oldname, newdir,
+						  newname, 0);
+		}
+		fuse_finish_interrupt(f, req, &d);
+		free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
+	}
+	reply_err(req, err);
+}
+
+static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+			  const char *newname)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_entry_param e;
+	char *oldpath;
+	char *newpath;
+	int err;
+
+	err = get_path2(f, ino, NULL, newparent, newname,
+			&oldpath, &newpath, NULL, NULL);
+	if (!err) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_link(f->fs, oldpath, newpath);
+		if (!err)
+			err = lookup_path(f, newparent, newname, newpath,
+					  &e, NULL);
+		fuse_finish_interrupt(f, req, &d);
+		free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath);
+	}
+	reply_entry(req, &e, err);
+}
+
+static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
+			    struct fuse_file_info *fi)
+{
+	struct node *node;
+	int unlink_hidden = 0;
+	const char *compatpath;
+
+	if (path != NULL || f->nullpath_ok || f->conf.nopath)
+		compatpath = path;
+	else
+		compatpath = "-";
+
+	fuse_fs_release(f->fs, compatpath, fi);
+
+	pthread_mutex_lock(&f->lock);
+	node = get_node(f, ino);
+	assert(node->open_count > 0);
+	--node->open_count;
+	if (node->is_hidden && !node->open_count) {
+		unlink_hidden = 1;
+		node->is_hidden = 0;
+	}
+	pthread_mutex_unlock(&f->lock);
+
+	if(unlink_hidden) {
+		if (path) {
+			fuse_fs_unlink(f->fs, path);
+		} else if (f->conf.nopath) {
+			char *unlinkpath;
+
+			if (get_path(f, ino, &unlinkpath) == 0)
+				fuse_fs_unlink(f->fs, unlinkpath);
+
+			free_path(f, ino, unlinkpath);
+		}
+	}
+}
+
+static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
+			    const char *name, mode_t mode,
+			    struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_intr_data d;
+	struct fuse_entry_param e;
+	char *path;
+	int err;
+
+	err = get_path_name(f, parent, name, &path);
+	if (!err) {
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_create(f->fs, path, mode, fi);
+		if (!err) {
+			err = lookup_path(f, parent, name, path, &e, fi);
+			if (err)
+				fuse_fs_release(f->fs, path, fi);
+			else if (!S_ISREG(e.attr.st_mode)) {
+				err = -EIO;
+				fuse_fs_release(f->fs, path, fi);
+				forget_node(f, e.ino, 1);
+			} else {
+				if (f->conf.direct_io)
+					fi->direct_io = 1;
+				if (f->conf.kernel_cache)
+					fi->keep_cache = 1;
+
+			}
+		}
+		fuse_finish_interrupt(f, req, &d);
+	}
+	if (!err) {
+		pthread_mutex_lock(&f->lock);
+		get_node(f, e.ino)->open_count++;
+		pthread_mutex_unlock(&f->lock);
+		if (fuse_reply_create(req, &e, fi) == -ENOENT) {
+			/* The open syscall was interrupted, so it
+			   must be cancelled */
+			fuse_do_release(f, e.ino, path, fi);
+			forget_node(f, e.ino, 1);
+		}
+	} else {
+		reply_err(req, err);
+	}
+
+	free_path(f, parent, path);
+}
+
+static double diff_timespec(const struct timespec *t1,
+			    const struct timespec *t2)
+{
+	return (t1->tv_sec - t2->tv_sec) +
+		((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0;
+}
+
+static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path,
+			    struct fuse_file_info *fi)
+{
+	struct node *node;
+
+	pthread_mutex_lock(&f->lock);
+	node = get_node(f, ino);
+	if (node->cache_valid) {
+		struct timespec now;
+
+		curr_time(&now);
+		if (diff_timespec(&now, &node->stat_updated) >
+		    f->conf.ac_attr_timeout) {
+			struct stat stbuf;
+			int err;
+			pthread_mutex_unlock(&f->lock);
+			err = fuse_fs_fgetattr(f->fs, path, &stbuf, fi);
+			pthread_mutex_lock(&f->lock);
+			if (!err)
+				update_stat(node, &stbuf);
+			else
+				node->cache_valid = 0;
+		}
+	}
+	if (node->cache_valid)
+		fi->keep_cache = 1;
+
+	node->cache_valid = 1;
+	pthread_mutex_unlock(&f->lock);
+}
+
+static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
+			  struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_intr_data d;
+	char *path;
+	int err;
+
+	err = get_path(f, ino, &path);
+	if (!err) {
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_open(f->fs, path, fi);
+		if (!err) {
+			if (f->conf.direct_io)
+				fi->direct_io = 1;
+			if (f->conf.kernel_cache)
+				fi->keep_cache = 1;
+
+			if (f->conf.auto_cache)
+				open_auto_cache(f, ino, path, fi);
+		}
+		fuse_finish_interrupt(f, req, &d);
+	}
+	if (!err) {
+		pthread_mutex_lock(&f->lock);
+		get_node(f, ino)->open_count++;
+		pthread_mutex_unlock(&f->lock);
+		if (fuse_reply_open(req, fi) == -ENOENT) {
+			/* The open syscall was interrupted, so it
+			   must be cancelled */
+			fuse_do_release(f, ino, path, fi);
+		}
+	} else
+		reply_err(req, err);
+
+	free_path(f, ino, path);
+}
+
+static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+			  off64_t off, struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_bufvec *buf = NULL;
+	char *path;
+	int res;
+
+	res = get_path_nullok(f, ino, &path);
+	if (res == 0) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+
+	if (res == 0)
+		fuse_reply_data(req, buf, FUSE_BUF_SPLICE_MOVE);
+	else
+		reply_err(req, res);
+
+	fuse_free_buf(buf);
+}
+
+static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
+			       struct fuse_bufvec *buf, off64_t off,
+			       struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char *path;
+	int res;
+
+	res = get_path_nullok(f, ino, &path);
+	if (res == 0) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		res = fuse_fs_write_buf(f->fs, path, buf, off, fi);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+
+	if (res >= 0)
+		fuse_reply_write(req, res);
+	else
+		reply_err(req, res);
+}
+
+static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
+			   struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char *path;
+	int err;
+
+	err = get_path_nullok(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_fsync(f->fs, path, datasync, fi);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	reply_err(req, err);
+}
+
+static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi,
+				     struct fuse_file_info *fi)
+{
+	struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh;
+	memset(fi, 0, sizeof(struct fuse_file_info));
+	fi->fh = dh->fh;
+	fi->fh_old = dh->fh;
+	return dh;
+}
+
+static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
+			     struct fuse_file_info *llfi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_intr_data d;
+	struct fuse_dh *dh;
+	struct fuse_file_info fi;
+	char *path;
+	int err;
+
+	dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
+	if (dh == NULL) {
+		reply_err(req, -ENOMEM);
+		return;
+	}
+	memset(dh, 0, sizeof(struct fuse_dh));
+	dh->fuse = f;
+	dh->contents = NULL;
+	dh->len = 0;
+	dh->filled = 0;
+	dh->nodeid = ino;
+	fuse_mutex_init(&dh->lock);
+
+	llfi->fh = (uintptr_t) dh;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.flags = llfi->flags;
+
+	err = get_path(f, ino, &path);
+	if (!err) {
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_opendir(f->fs, path, &fi);
+		fuse_finish_interrupt(f, req, &d);
+		dh->fh = fi.fh;
+	}
+	if (!err) {
+		if (fuse_reply_open(req, llfi) == -ENOENT) {
+			/* The opendir syscall was interrupted, so it
+			   must be cancelled */
+			fuse_fs_releasedir(f->fs, path, &fi);
+			pthread_mutex_destroy(&dh->lock);
+			free(dh);
+		}
+	} else {
+		reply_err(req, err);
+		pthread_mutex_destroy(&dh->lock);
+		free(dh);
+	}
+	free_path(f, ino, path);
+}
+
+static int extend_contents(struct fuse_dh *dh, unsigned minsize)
+{
+	if (minsize > dh->size) {
+		char *newptr;
+		unsigned newsize = dh->size;
+		if (!newsize)
+			newsize = 1024;
+		while (newsize < minsize) {
+			if (newsize >= 0x80000000)
+				newsize = 0xffffffff;
+			else
+				newsize *= 2;
+		}
+
+		newptr = (char *) realloc(dh->contents, newsize);
+		if (!newptr) {
+			dh->error = -ENOMEM;
+			return -1;
+		}
+		dh->contents = newptr;
+		dh->size = newsize;
+	}
+	return 0;
+}
+
+static int fill_dir(void *dh_, const char *name, const struct stat *statp,
+		    off64_t off)
+{
+	struct fuse_dh *dh = (struct fuse_dh *) dh_;
+	struct stat stbuf;
+	size_t newlen;
+
+	if (statp)
+		stbuf = *statp;
+	else {
+		memset(&stbuf, 0, sizeof(stbuf));
+		stbuf.st_ino = FUSE_UNKNOWN_INO;
+	}
+
+	if (!dh->fuse->conf.use_ino) {
+		stbuf.st_ino = FUSE_UNKNOWN_INO;
+		if (dh->fuse->conf.readdir_ino) {
+			struct node *node;
+			pthread_mutex_lock(&dh->fuse->lock);
+			node = lookup_node(dh->fuse, dh->nodeid, name);
+			if (node)
+				stbuf.st_ino  = (ino_t) node->nodeid;
+			pthread_mutex_unlock(&dh->fuse->lock);
+		}
+	}
+
+	if (off) {
+		if (extend_contents(dh, dh->needlen) == -1)
+			return 1;
+
+		dh->filled = 0;
+		newlen = dh->len +
+			fuse_add_direntry(dh->req, dh->contents + dh->len,
+					  dh->needlen - dh->len, name,
+					  &stbuf, off);
+		if (newlen > dh->needlen)
+			return 1;
+	} else {
+		newlen = dh->len +
+			fuse_add_direntry(dh->req, NULL, 0, name, NULL, 0);
+		if (extend_contents(dh, newlen) == -1)
+			return 1;
+
+		fuse_add_direntry(dh->req, dh->contents + dh->len,
+				  dh->size - dh->len, name, &stbuf, newlen);
+	}
+	dh->len = newlen;
+	return 0;
+}
+
+static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
+			size_t size, off64_t off, struct fuse_dh *dh,
+			struct fuse_file_info *fi)
+{
+	char *path;
+	int err;
+
+	if (f->fs->op.readdir)
+		err = get_path_nullok(f, ino, &path);
+	else
+		err = get_path(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+
+		dh->len = 0;
+		dh->error = 0;
+		dh->needlen = size;
+		dh->filled = 1;
+		dh->req = req;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_readdir(f->fs, path, dh, fill_dir, off, fi);
+		fuse_finish_interrupt(f, req, &d);
+		dh->req = NULL;
+		if (!err)
+			err = dh->error;
+		if (err)
+			dh->filled = 0;
+		free_path(f, ino, path);
+	}
+	return err;
+}
+
+static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+			     off64_t off, struct fuse_file_info *llfi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_file_info fi;
+	struct fuse_dh *dh = get_dirhandle(llfi, &fi);
+
+	pthread_mutex_lock(&dh->lock);
+	/* According to SUS, directory contents need to be refreshed on
+	   rewinddir() */
+	if (!off)
+		dh->filled = 0;
+
+	if (!dh->filled) {
+		int err = readdir_fill(f, req, ino, size, off, dh, &fi);
+		if (err) {
+			reply_err(req, err);
+			goto out;
+		}
+	}
+	if (dh->filled) {
+		if (off < dh->len) {
+			if (off + size > dh->len)
+				size = dh->len - off;
+		} else
+			size = 0;
+	} else {
+		size = dh->len;
+		off = 0;
+	}
+	fuse_reply_buf(req, dh->contents + off, size);
+out:
+	pthread_mutex_unlock(&dh->lock);
+}
+
+static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
+				struct fuse_file_info *llfi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_intr_data d;
+	struct fuse_file_info fi;
+	struct fuse_dh *dh = get_dirhandle(llfi, &fi);
+	char *path;
+	const char *compatpath;
+
+	get_path_nullok(f, ino, &path);
+	if (path != NULL || f->nullpath_ok || f->conf.nopath)
+		compatpath = path;
+	else
+		compatpath = "-";
+
+	fuse_prepare_interrupt(f, req, &d);
+	fuse_fs_releasedir(f->fs, compatpath, &fi);
+	fuse_finish_interrupt(f, req, &d);
+	free_path(f, ino, path);
+
+	pthread_mutex_lock(&dh->lock);
+	pthread_mutex_unlock(&dh->lock);
+	pthread_mutex_destroy(&dh->lock);
+	free(dh->contents);
+	free(dh);
+	reply_err(req, 0);
+}
+
+static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
+			      struct fuse_file_info *llfi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_file_info fi;
+	char *path;
+	int err;
+
+	get_dirhandle(llfi, &fi);
+
+	err = get_path_nullok(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	reply_err(req, err);
+}
+
+static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct statvfs buf;
+	char *path = NULL;
+	int err = 0;
+
+	memset(&buf, 0, sizeof(buf));
+	if (ino)
+		err = get_path(f, ino, &path);
+
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_statfs(f->fs, path ? path : "/", &buf);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+
+	if (!err)
+		fuse_reply_statfs(req, &buf);
+	else
+		reply_err(req, err);
+}
+
+static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
+			      const char *value, size_t size, int flags)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char *path;
+	int err;
+
+	err = get_path(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_setxattr(f->fs, path, name, value, size, flags);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	reply_err(req, err);
+}
+
+static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
+			   const char *name, char *value, size_t size)
+{
+	int err;
+	char *path;
+
+	err = get_path(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_getxattr(f->fs, path, name, value, size);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	return err;
+}
+
+static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
+			      size_t size)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	int res;
+
+	if (size) {
+		char *value = (char *) malloc(size);
+		if (value == NULL) {
+			reply_err(req, -ENOMEM);
+			return;
+		}
+		res = common_getxattr(f, req, ino, name, value, size);
+		if (res > 0)
+			fuse_reply_buf(req, value, res);
+		else
+			reply_err(req, res);
+		free(value);
+	} else {
+		res = common_getxattr(f, req, ino, name, NULL, 0);
+		if (res >= 0)
+			fuse_reply_xattr(req, res);
+		else
+			reply_err(req, res);
+	}
+}
+
+static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
+			    char *list, size_t size)
+{
+	char *path;
+	int err;
+
+	err = get_path(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_listxattr(f->fs, path, list, size);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	return err;
+}
+
+static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	int res;
+
+	if (size) {
+		char *list = (char *) malloc(size);
+		if (list == NULL) {
+			reply_err(req, -ENOMEM);
+			return;
+		}
+		res = common_listxattr(f, req, ino, list, size);
+		if (res > 0)
+			fuse_reply_buf(req, list, res);
+		else
+			reply_err(req, res);
+		free(list);
+	} else {
+		res = common_listxattr(f, req, ino, NULL, 0);
+		if (res >= 0)
+			fuse_reply_xattr(req, res);
+		else
+			reply_err(req, res);
+	}
+}
+
+static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
+				 const char *name)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char *path;
+	int err;
+
+	err = get_path(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_removexattr(f->fs, path, name);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	reply_err(req, err);
+}
+
+static struct lock *locks_conflict(struct node *node, const struct lock *lock)
+{
+	struct lock *l;
+
+	for (l = node->locks; l; l = l->next)
+		if (l->owner != lock->owner &&
+		    lock->start <= l->end && l->start <= lock->end &&
+		    (l->type == F_WRLCK || lock->type == F_WRLCK))
+			break;
+
+	return l;
+}
+
+static void delete_lock(struct lock **lockp)
+{
+	struct lock *l = *lockp;
+	*lockp = l->next;
+	free(l);
+}
+
+static void insert_lock(struct lock **pos, struct lock *lock)
+{
+	lock->next = *pos;
+	*pos = lock;
+}
+
+static int locks_insert(struct node *node, struct lock *lock)
+{
+	struct lock **lp;
+	struct lock *newl1 = NULL;
+	struct lock *newl2 = NULL;
+
+	if (lock->type != F_UNLCK || lock->start != 0 ||
+	    lock->end != OFFSET_MAX) {
+		newl1 = malloc(sizeof(struct lock));
+		newl2 = malloc(sizeof(struct lock));
+
+		if (!newl1 || !newl2) {
+			free(newl1);
+			free(newl2);
+			return -ENOLCK;
+		}
+	}
+
+	for (lp = &node->locks; *lp;) {
+		struct lock *l = *lp;
+		if (l->owner != lock->owner)
+			goto skip;
+
+		if (lock->type == l->type) {
+			if (l->end < lock->start - 1)
+				goto skip;
+			if (lock->end < l->start - 1)
+				break;
+			if (l->start <= lock->start && lock->end <= l->end)
+				goto out;
+			if (l->start < lock->start)
+				lock->start = l->start;
+			if (lock->end < l->end)
+				lock->end = l->end;
+			goto delete;
+		} else {
+			if (l->end < lock->start)
+				goto skip;
+			if (lock->end < l->start)
+				break;
+			if (lock->start <= l->start && l->end <= lock->end)
+				goto delete;
+			if (l->end <= lock->end) {
+				l->end = lock->start - 1;
+				goto skip;
+			}
+			if (lock->start <= l->start) {
+				l->start = lock->end + 1;
+				break;
+			}
+			*newl2 = *l;
+			newl2->start = lock->end + 1;
+			l->end = lock->start - 1;
+			insert_lock(&l->next, newl2);
+			newl2 = NULL;
+		}
+	skip:
+		lp = &l->next;
+		continue;
+
+	delete:
+		delete_lock(lp);
+	}
+	if (lock->type != F_UNLCK) {
+		*newl1 = *lock;
+		insert_lock(lp, newl1);
+		newl1 = NULL;
+	}
+out:
+	free(newl1);
+	free(newl2);
+	return 0;
+}
+
+static void flock_to_lock(struct flock *flock, struct lock *lock)
+{
+	memset(lock, 0, sizeof(struct lock));
+	lock->type = flock->l_type;
+	lock->start = flock->l_start;
+	lock->end =
+		flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
+	lock->pid = flock->l_pid;
+}
+
+static void lock_to_flock(struct lock *lock, struct flock *flock)
+{
+	flock->l_type = lock->type;
+	flock->l_start = lock->start;
+	flock->l_len =
+		(lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
+	flock->l_pid = lock->pid;
+}
+
+static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
+			     const char *path, struct fuse_file_info *fi)
+{
+	struct fuse_intr_data d;
+	struct flock lock;
+	struct lock l;
+	int err;
+	int errlock;
+
+	fuse_prepare_interrupt(f, req, &d);
+	memset(&lock, 0, sizeof(lock));
+	lock.l_type = F_UNLCK;
+	lock.l_whence = SEEK_SET;
+	err = fuse_fs_flush(f->fs, path, fi);
+	errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock);
+	fuse_finish_interrupt(f, req, &d);
+
+	if (errlock != -ENOSYS) {
+		flock_to_lock(&lock, &l);
+		l.owner = fi->lock_owner;
+		pthread_mutex_lock(&f->lock);
+		locks_insert(get_node(f, ino), &l);
+		pthread_mutex_unlock(&f->lock);
+
+		/* if op.lock() is defined FLUSH is needed regardless
+		   of op.flush() */
+		if (err == -ENOSYS)
+			err = 0;
+	}
+	return err;
+}
+
+static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino,
+			     struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_intr_data d;
+	char *path;
+	int err = 0;
+
+	get_path_nullok(f, ino, &path);
+	if (fi->flush) {
+		err = fuse_flush_common(f, req, ino, path, fi);
+		if (err == -ENOSYS)
+			err = 0;
+	}
+
+	fuse_prepare_interrupt(f, req, &d);
+	fuse_do_release(f, ino, path, fi);
+	fuse_finish_interrupt(f, req, &d);
+	free_path(f, ino, path);
+
+	reply_err(req, err);
+}
+
+static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino,
+			   struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char *path;
+	int err;
+
+	get_path_nullok(f, ino, &path);
+	err = fuse_flush_common(f, req, ino, path, fi);
+	free_path(f, ino, path);
+
+	reply_err(req, err);
+}
+
+static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
+			    struct fuse_file_info *fi, struct flock *lock,
+			    int cmd)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char *path;
+	int err;
+
+	err = get_path_nullok(f, ino, &path);
+	if (!err) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_lock(f->fs, path, fi, cmd, lock);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	return err;
+}
+
+static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino,
+			   struct fuse_file_info *fi, struct flock *lock)
+{
+	int err;
+	struct lock l;
+	struct lock *conflict;
+	struct fuse *f = req_fuse(req);
+
+	flock_to_lock(lock, &l);
+	l.owner = fi->lock_owner;
+	pthread_mutex_lock(&f->lock);
+	conflict = locks_conflict(get_node(f, ino), &l);
+	if (conflict)
+		lock_to_flock(conflict, lock);
+	pthread_mutex_unlock(&f->lock);
+	if (!conflict)
+		err = fuse_lock_common(req, ino, fi, lock, F_GETLK);
+	else
+		err = 0;
+
+	if (!err)
+		fuse_reply_lock(req, lock);
+	else
+		reply_err(req, err);
+}
+
+static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino,
+			   struct fuse_file_info *fi, struct flock *lock,
+			   int sleep)
+{
+	int err = fuse_lock_common(req, ino, fi, lock,
+				   sleep ? F_SETLKW : F_SETLK);
+	if (!err) {
+		struct fuse *f = req_fuse(req);
+		struct lock l;
+		flock_to_lock(lock, &l);
+		l.owner = fi->lock_owner;
+		pthread_mutex_lock(&f->lock);
+		locks_insert(get_node(f, ino), &l);
+		pthread_mutex_unlock(&f->lock);
+	}
+	reply_err(req, err);
+}
+
+static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino,
+			   struct fuse_file_info *fi, int op)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	char *path;
+	int err;
+
+	err = get_path_nullok(f, ino, &path);
+	if (err == 0) {
+		struct fuse_intr_data d;
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_flock(f->fs, path, fi, op);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	reply_err(req, err);
+}
+
+static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
+			  uint64_t idx)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_intr_data d;
+	char *path;
+	int err;
+
+	err = get_path(f, ino, &path);
+	if (!err) {
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	if (!err)
+		fuse_reply_bmap(req, idx);
+	else
+		reply_err(req, err);
+}
+
+static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
+			   struct fuse_file_info *fi, unsigned int flags,
+			   const void *in_buf, size_t in_bufsz,
+			   size_t out_bufsz)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_intr_data d;
+	char *path, *out_buf = NULL;
+	int err;
+
+	err = -EPERM;
+	if (flags & FUSE_IOCTL_UNRESTRICTED)
+		goto err;
+
+	if (out_bufsz) {
+		err = -ENOMEM;
+		out_buf = malloc(out_bufsz);
+		if (!out_buf)
+			goto err;
+	}
+
+	assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
+	if (out_buf)
+		memcpy(out_buf, in_buf, in_bufsz);
+
+	err = get_path_nullok(f, ino, &path);
+	if (err)
+		goto err;
+
+	fuse_prepare_interrupt(f, req, &d);
+
+	err = fuse_fs_ioctl(f->fs, path, cmd, arg, fi, flags,
+			    out_buf ?: (void *)in_buf);
+
+	fuse_finish_interrupt(f, req, &d);
+	free_path(f, ino, path);
+
+	fuse_reply_ioctl(req, err, out_buf, out_bufsz);
+	goto out;
+err:
+	reply_err(req, err);
+out:
+	free(out_buf);
+}
+
+static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
+			  struct fuse_file_info *fi, struct fuse_pollhandle *ph)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_intr_data d;
+	char *path;
+	int err;
+	unsigned revents = 0;
+
+	err = get_path_nullok(f, ino, &path);
+	if (!err) {
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_poll(f->fs, path, fi, ph, &revents);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	if (!err)
+		fuse_reply_poll(req, revents);
+	else
+		reply_err(req, err);
+}
+
+static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
+		off64_t offset, off64_t length, struct fuse_file_info *fi)
+{
+	struct fuse *f = req_fuse_prepare(req);
+	struct fuse_intr_data d;
+	char *path;
+	int err;
+
+	err = get_path_nullok(f, ino, &path);
+	if (!err) {
+		fuse_prepare_interrupt(f, req, &d);
+		err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi);
+		fuse_finish_interrupt(f, req, &d);
+		free_path(f, ino, path);
+	}
+	reply_err(req, err);
+}
+
+static int clean_delay(struct fuse *f)
+{
+	/*
+	 * This is calculating the delay between clean runs.  To
+	 * reduce the number of cleans we are doing them 10 times
+	 * within the remember window.
+	 */
+	int min_sleep = 60;
+	int max_sleep = 3600;
+	int sleep_time = f->conf.remember / 10;
+
+	if (sleep_time > max_sleep)
+		return max_sleep;
+	if (sleep_time < min_sleep)
+		return min_sleep;
+	return sleep_time;
+}
+
+int fuse_clean_cache(struct fuse *f)
+{
+	struct node_lru *lnode;
+	struct list_head *curr, *next;
+	struct node *node;
+	struct timespec now;
+
+	pthread_mutex_lock(&f->lock);
+
+	curr_time(&now);
+
+	for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) {
+		double age;
+
+		next = curr->next;
+		lnode = list_entry(curr, struct node_lru, lru);
+		node = &lnode->node;
+
+		age = diff_timespec(&now, &lnode->forget_time);
+		if (age <= f->conf.remember)
+			break;
+
+		assert(node->nlookup == 1);
+
+		/* Don't forget active directories */
+		if (node->refctr > 1)
+			continue;
+
+		node->nlookup = 0;
+		unhash_name(f, node);
+		unref_node(f, node);
+	}
+	pthread_mutex_unlock(&f->lock);
+
+	return clean_delay(f);
+}
+
+static struct fuse_lowlevel_ops fuse_path_ops = {
+	.init = fuse_lib_init,
+	.destroy = fuse_lib_destroy,
+	.lookup = fuse_lib_lookup,
+	.forget = fuse_lib_forget,
+	.forget_multi = fuse_lib_forget_multi,
+	.getattr = fuse_lib_getattr,
+	.setattr = fuse_lib_setattr,
+	.access = fuse_lib_access,
+	.readlink = fuse_lib_readlink,
+	.mknod = fuse_lib_mknod,
+	.mkdir = fuse_lib_mkdir,
+	.unlink = fuse_lib_unlink,
+	.rmdir = fuse_lib_rmdir,
+	.symlink = fuse_lib_symlink,
+	.rename = fuse_lib_rename,
+	.link = fuse_lib_link,
+	.create = fuse_lib_create,
+	.open = fuse_lib_open,
+	.read = fuse_lib_read,
+	.write_buf = fuse_lib_write_buf,
+	.flush = fuse_lib_flush,
+	.release = fuse_lib_release,
+	.fsync = fuse_lib_fsync,
+	.opendir = fuse_lib_opendir,
+	.readdir = fuse_lib_readdir,
+	.releasedir = fuse_lib_releasedir,
+	.fsyncdir = fuse_lib_fsyncdir,
+	.statfs = fuse_lib_statfs,
+	.setxattr = fuse_lib_setxattr,
+	.getxattr = fuse_lib_getxattr,
+	.listxattr = fuse_lib_listxattr,
+	.removexattr = fuse_lib_removexattr,
+	.getlk = fuse_lib_getlk,
+	.setlk = fuse_lib_setlk,
+	.flock = fuse_lib_flock,
+	.bmap = fuse_lib_bmap,
+	.ioctl = fuse_lib_ioctl,
+	.poll = fuse_lib_poll,
+	.fallocate = fuse_lib_fallocate,
+};
+
+int fuse_notify_poll(struct fuse_pollhandle *ph)
+{
+	return fuse_lowlevel_notify_poll(ph);
+}
+
+static void free_cmd(struct fuse_cmd *cmd)
+{
+	free(cmd->buf);
+	free(cmd);
+}
+
+void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
+{
+	fuse_session_process(f->se, cmd->buf, cmd->buflen, cmd->ch);
+	free_cmd(cmd);
+}
+
+int fuse_exited(struct fuse *f)
+{
+	return fuse_session_exited(f->se);
+}
+
+struct fuse_session *fuse_get_session(struct fuse *f)
+{
+	return f->se;
+}
+
+static struct fuse_cmd *fuse_alloc_cmd(size_t bufsize)
+{
+	struct fuse_cmd *cmd = (struct fuse_cmd *) malloc(sizeof(*cmd));
+	if (cmd == NULL) {
+		fprintf(stderr, "fuse: failed to allocate cmd\n");
+		return NULL;
+	}
+	cmd->buf = (char *) malloc(bufsize);
+	if (cmd->buf == NULL) {
+		fprintf(stderr, "fuse: failed to allocate read buffer\n");
+		free(cmd);
+		return NULL;
+	}
+	return cmd;
+}
+
+struct fuse_cmd *fuse_read_cmd(struct fuse *f)
+{
+	struct fuse_chan *ch = fuse_session_next_chan(f->se, NULL);
+	size_t bufsize = fuse_chan_bufsize(ch);
+	struct fuse_cmd *cmd = fuse_alloc_cmd(bufsize);
+	if (cmd != NULL) {
+		int res = fuse_chan_recv(&ch, cmd->buf, bufsize);
+		if (res <= 0) {
+			free_cmd(cmd);
+			if (res < 0 && res != -EINTR && res != -EAGAIN)
+				fuse_exit(f);
+			return NULL;
+		}
+		cmd->buflen = res;
+		cmd->ch = ch;
+	}
+	return cmd;
+}
+
+static int fuse_session_loop_remember(struct fuse *f)
+{
+	struct fuse_session *se = f->se;
+	int res = 0;
+	struct timespec now;
+	time_t next_clean;
+	struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+	size_t bufsize = fuse_chan_bufsize(ch);
+	char *buf = (char *) malloc(bufsize);
+	struct pollfd fds = {
+		.fd = fuse_chan_fd(ch),
+		.events = POLLIN
+	};
+
+	if (!buf) {
+		fprintf(stderr, "fuse: failed to allocate read buffer\n");
+		return -1;
+	}
+
+	curr_time(&now);
+	next_clean = now.tv_sec;
+	while (!fuse_session_exited(se)) {
+		struct fuse_chan *tmpch = ch;
+		struct fuse_buf fbuf = {
+			.mem = buf,
+			.size = bufsize,
+		};
+		unsigned timeout;
+
+		curr_time(&now);
+		if (now.tv_sec < next_clean)
+			timeout = next_clean - now.tv_sec;
+		else
+			timeout = 0;
+
+		res = poll(&fds, 1, timeout * 1000);
+		if (res == -1) {
+			if (errno == -EINTR)
+				continue;
+			else
+				break;
+		} else if (res > 0) {
+			res = fuse_session_receive_buf(se, &fbuf, &tmpch);
+
+			if (res == -EINTR)
+				continue;
+			if (res <= 0)
+				break;
+
+			fuse_session_process_buf(se, &fbuf, tmpch);
+		} else {
+			timeout = fuse_clean_cache(f);
+			curr_time(&now);
+			next_clean = now.tv_sec + timeout;
+		}
+	}
+
+	free(buf);
+	fuse_session_reset(se);
+	return res < 0 ? -1 : 0;
+}
+
+int fuse_loop(struct fuse *f)
+{
+	if (!f)
+		return -1;
+
+	if (lru_enabled(f))
+		return fuse_session_loop_remember(f);
+
+	return fuse_session_loop(f->se);
+}
+
+int fuse_invalidate(struct fuse *f, const char *path)
+{
+	(void) f;
+	(void) path;
+	return -EINVAL;
+}
+
+void fuse_exit(struct fuse *f)
+{
+	fuse_session_exit(f->se);
+}
+
+struct fuse_context *fuse_get_context(void)
+{
+	return &fuse_get_context_internal()->ctx;
+}
+
+/*
+ * The size of fuse_context got extended, so need to be careful about
+ * incompatibility (i.e. a new binary cannot work with an old
+ * library).
+ */
+struct fuse_context *fuse_get_context_compat22(void);
+struct fuse_context *fuse_get_context_compat22(void)
+{
+	return &fuse_get_context_internal()->ctx;
+}
+FUSE_SYMVER(".symver fuse_get_context_compat22,fuse_get_context@FUSE_2.2");
+
+int fuse_getgroups(int size, gid_t list[])
+{
+	fuse_req_t req = fuse_get_context_internal()->req;
+	return fuse_req_getgroups(req, size, list);
+}
+
+int fuse_interrupted(void)
+{
+	return fuse_req_interrupted(fuse_get_context_internal()->req);
+}
+
+void fuse_set_getcontext_func(struct fuse_context *(*func)(void))
+{
+	(void) func;
+	/* no-op */
+}
+
+enum {
+	KEY_HELP,
+};
+
+#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
+
+static const struct fuse_opt fuse_lib_opts[] = {
+	FUSE_OPT_KEY("-h",		      KEY_HELP),
+	FUSE_OPT_KEY("--help",		      KEY_HELP),
+	FUSE_OPT_KEY("debug",		      FUSE_OPT_KEY_KEEP),
+	FUSE_OPT_KEY("-d",		      FUSE_OPT_KEY_KEEP),
+	FUSE_LIB_OPT("debug",		      debug, 1),
+	FUSE_LIB_OPT("-d",		      debug, 1),
+	FUSE_LIB_OPT("hard_remove",	      hard_remove, 1),
+	FUSE_LIB_OPT("use_ino",		      use_ino, 1),
+	FUSE_LIB_OPT("readdir_ino",	      readdir_ino, 1),
+	FUSE_LIB_OPT("direct_io",	      direct_io, 1),
+	FUSE_LIB_OPT("kernel_cache",	      kernel_cache, 1),
+	FUSE_LIB_OPT("auto_cache",	      auto_cache, 1),
+	FUSE_LIB_OPT("noauto_cache",	      auto_cache, 0),
+	FUSE_LIB_OPT("umask=",		      set_mode, 1),
+	FUSE_LIB_OPT("umask=%o",	      umask, 0),
+	FUSE_LIB_OPT("uid=",		      set_uid, 1),
+	FUSE_LIB_OPT("uid=%d",		      uid, 0),
+	FUSE_LIB_OPT("gid=",		      set_gid, 1),
+	FUSE_LIB_OPT("gid=%d",		      gid, 0),
+	FUSE_LIB_OPT("entry_timeout=%lf",     entry_timeout, 0),
+	FUSE_LIB_OPT("attr_timeout=%lf",      attr_timeout, 0),
+	FUSE_LIB_OPT("ac_attr_timeout=%lf",   ac_attr_timeout, 0),
+	FUSE_LIB_OPT("ac_attr_timeout=",      ac_attr_timeout_set, 1),
+	FUSE_LIB_OPT("negative_timeout=%lf",  negative_timeout, 0),
+	FUSE_LIB_OPT("noforget",              remember, -1),
+	FUSE_LIB_OPT("remember=%u",           remember, 0),
+	FUSE_LIB_OPT("nopath",                nopath, 1),
+	FUSE_LIB_OPT("intr",		      intr, 1),
+	FUSE_LIB_OPT("intr_signal=%d",	      intr_signal, 0),
+	FUSE_LIB_OPT("modules=%s",	      modules, 0),
+	FUSE_OPT_END
+};
+
+static void fuse_lib_help(void)
+{
+	fprintf(stderr,
+"    -o hard_remove         immediate removal (don't hide files)\n"
+"    -o use_ino             let filesystem set inode numbers\n"
+"    -o readdir_ino         try to fill in d_ino in readdir\n"
+"    -o direct_io           use direct I/O\n"
+"    -o kernel_cache        cache files in kernel\n"
+"    -o [no]auto_cache      enable caching based on modification times (off)\n"
+"    -o umask=M             set file permissions (octal)\n"
+"    -o uid=N               set file owner\n"
+"    -o gid=N               set file group\n"
+"    -o entry_timeout=T     cache timeout for names (1.0s)\n"
+"    -o negative_timeout=T  cache timeout for deleted names (0.0s)\n"
+"    -o attr_timeout=T      cache timeout for attributes (1.0s)\n"
+"    -o ac_attr_timeout=T   auto cache timeout for attributes (attr_timeout)\n"
+"    -o noforget            never forget cached inodes\n"
+"    -o remember=T          remember cached inodes for T seconds (0s)\n"
+"    -o intr                allow requests to be interrupted\n"
+"    -o intr_signal=NUM     signal to send on interrupt (%i)\n"
+"    -o modules=M1[:M2...]  names of modules to push onto filesystem stack\n"
+"\n", FUSE_DEFAULT_INTR_SIGNAL);
+}
+
+static void fuse_lib_help_modules(void)
+{
+	struct fuse_module *m;
+	fprintf(stderr, "\nModule options:\n");
+	pthread_mutex_lock(&fuse_context_lock);
+	for (m = fuse_modules; m; m = m->next) {
+		struct fuse_fs *fs = NULL;
+		struct fuse_fs *newfs;
+		struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+		if (fuse_opt_add_arg(&args, "") != -1 &&
+		    fuse_opt_add_arg(&args, "-h") != -1) {
+			fprintf(stderr, "\n[%s]\n", m->name);
+			newfs = m->factory(&args, &fs);
+			assert(newfs == NULL);
+		}
+		fuse_opt_free_args(&args);
+	}
+	pthread_mutex_unlock(&fuse_context_lock);
+}
+
+static int fuse_lib_opt_proc(void *data, const char *arg, int key,
+			     struct fuse_args *outargs)
+{
+	(void) arg; (void) outargs;
+
+	if (key == KEY_HELP) {
+		struct fuse_config *conf = (struct fuse_config *) data;
+		fuse_lib_help();
+		conf->help = 1;
+	}
+
+	return 1;
+}
+
+int fuse_is_lib_option(const char *opt)
+{
+	return fuse_lowlevel_is_lib_option(opt) ||
+		fuse_opt_match(fuse_lib_opts, opt);
+}
+
+static int fuse_init_intr_signal(int signum, int *installed)
+{
+	struct sigaction old_sa;
+
+	if (sigaction(signum, NULL, &old_sa) == -1) {
+		perror("fuse: cannot get old signal handler");
+		return -1;
+	}
+
+	if (old_sa.sa_handler == SIG_DFL) {
+		struct sigaction sa;
+
+		memset(&sa, 0, sizeof(struct sigaction));
+		sa.sa_handler = fuse_intr_sighandler;
+		sigemptyset(&sa.sa_mask);
+
+		if (sigaction(signum, &sa, NULL) == -1) {
+			perror("fuse: cannot set interrupt signal handler");
+			return -1;
+		}
+		*installed = 1;
+	}
+	return 0;
+}
+
+static void fuse_restore_intr_signal(int signum)
+{
+	struct sigaction sa;
+
+	memset(&sa, 0, sizeof(struct sigaction));
+	sa.sa_handler = SIG_DFL;
+	sigaction(signum, &sa, NULL);
+}
+
+
+static int fuse_push_module(struct fuse *f, const char *module,
+			    struct fuse_args *args)
+{
+	struct fuse_fs *fs[2] = { f->fs, NULL };
+	struct fuse_fs *newfs;
+	struct fuse_module *m = fuse_get_module(module);
+
+	if (!m)
+		return -1;
+
+	newfs = m->factory(args, fs);
+	if (!newfs) {
+		fuse_put_module(m);
+		return -1;
+	}
+	newfs->m = m;
+	f->fs = newfs;
+	f->nullpath_ok = newfs->op.flag_nullpath_ok && f->nullpath_ok;
+	f->conf.nopath = newfs->op.flag_nopath && f->conf.nopath;
+	f->utime_omit_ok = newfs->op.flag_utime_omit_ok && f->utime_omit_ok;
+	return 0;
+}
+
+struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
+			    void *user_data)
+{
+	struct fuse_fs *fs;
+
+	if (sizeof(struct fuse_operations) < op_size) {
+		fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
+		op_size = sizeof(struct fuse_operations);
+	}
+
+	fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
+	if (!fs) {
+		fprintf(stderr, "fuse: failed to allocate fuse_fs object\n");
+		return NULL;
+	}
+
+	fs->user_data = user_data;
+	if (op)
+		memcpy(&fs->op, op, op_size);
+	return fs;
+}
+
+static int node_table_init(struct node_table *t)
+{
+	t->size = NODE_TABLE_MIN_SIZE;
+	t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
+	if (t->array == NULL) {
+		fprintf(stderr, "fuse: memory allocation failed\n");
+		return -1;
+	}
+	t->use = 0;
+	t->split = 0;
+
+	return 0;
+}
+
+static void *fuse_prune_nodes(void *fuse)
+{
+	struct fuse *f = fuse;
+	int sleep_time;
+
+	while(1) {
+		sleep_time = fuse_clean_cache(f);
+		sleep(sleep_time);
+	}
+	return NULL;
+}
+
+int fuse_start_cleanup_thread(struct fuse *f)
+{
+	if (lru_enabled(f))
+		return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
+
+	return 0;
+}
+
+void fuse_stop_cleanup_thread(struct fuse *f)
+{
+	if (lru_enabled(f)) {
+		pthread_mutex_lock(&f->lock);
+#ifndef ANDROID
+		pthread_cancel(f->prune_thread);
+#else
+		pthread_kill(f->prune_thread, SIGUSR1);
+#endif
+		pthread_mutex_unlock(&f->lock);
+		pthread_join(f->prune_thread, NULL);
+	}
+}
+
+struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
+			     const struct fuse_operations *op,
+			     size_t op_size, void *user_data, int compat)
+{
+	struct fuse *f;
+	struct node *root;
+	struct fuse_fs *fs;
+	struct fuse_lowlevel_ops llop = fuse_path_ops;
+
+	if (fuse_create_context_key() == -1)
+		goto out;
+
+	f = (struct fuse *) calloc(1, sizeof(struct fuse));
+	if (f == NULL) {
+		fprintf(stderr, "fuse: failed to allocate fuse object\n");
+		goto out_delete_context_key;
+	}
+
+	fs = fuse_fs_new(op, op_size, user_data);
+	if (!fs)
+		goto out_free;
+
+	fs->compat = compat;
+	f->fs = fs;
+	f->nullpath_ok = fs->op.flag_nullpath_ok;
+	f->conf.nopath = fs->op.flag_nopath;
+	f->utime_omit_ok = fs->op.flag_utime_omit_ok;
+
+	/* Oh f**k, this is ugly! */
+	if (!fs->op.lock) {
+		llop.getlk = NULL;
+		llop.setlk = NULL;
+	}
+
+	f->conf.entry_timeout = 1.0;
+	f->conf.attr_timeout = 1.0;
+	f->conf.negative_timeout = 0.0;
+	f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
+
+	f->pagesize = getpagesize();
+	init_list_head(&f->partial_slabs);
+	init_list_head(&f->full_slabs);
+	init_list_head(&f->lru_table);
+
+	if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
+			   fuse_lib_opt_proc) == -1)
+		goto out_free_fs;
+
+	if (f->conf.modules) {
+		char *module;
+		char *next;
+
+		for (module = f->conf.modules; module; module = next) {
+			char *p;
+			for (p = module; *p && *p != ':'; p++);
+			next = *p ? p + 1 : NULL;
+			*p = '\0';
+			if (module[0] &&
+			    fuse_push_module(f, module, args) == -1)
+				goto out_free_fs;
+		}
+	}
+
+	if (!f->conf.ac_attr_timeout_set)
+		f->conf.ac_attr_timeout = f->conf.attr_timeout;
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+	/*
+	 * In FreeBSD, we always use these settings as inode numbers
+	 * are needed to make getcwd(3) work.
+	 */
+	f->conf.readdir_ino = 1;
+#endif
+
+	if (compat && compat <= 25) {
+		if (fuse_sync_compat_args(args) == -1)
+			goto out_free_fs;
+	}
+
+	f->se = fuse_lowlevel_new_common(args, &llop, sizeof(llop), f);
+	if (f->se == NULL) {
+		if (f->conf.help)
+			fuse_lib_help_modules();
+		goto out_free_fs;
+	}
+
+	fuse_session_add_chan(f->se, ch);
+
+	if (f->conf.debug) {
+		fprintf(stderr, "nullpath_ok: %i\n", f->nullpath_ok);
+		fprintf(stderr, "nopath: %i\n", f->conf.nopath);
+		fprintf(stderr, "utime_omit_ok: %i\n", f->utime_omit_ok);
+	}
+
+	/* Trace topmost layer by default */
+	f->fs->debug = f->conf.debug;
+	f->ctr = 0;
+	f->generation = 0;
+	if (node_table_init(&f->name_table) == -1)
+		goto out_free_session;
+
+	if (node_table_init(&f->id_table) == -1)
+		goto out_free_name_table;
+
+	fuse_mutex_init(&f->lock);
+
+	root = alloc_node(f);
+	if (root == NULL) {
+		fprintf(stderr, "fuse: memory allocation failed\n");
+		goto out_free_id_table;
+	}
+
+	strcpy(root->inline_name, "/");
+	root->name = root->inline_name;
+
+	if (f->conf.intr &&
+	    fuse_init_intr_signal(f->conf.intr_signal,
+				  &f->intr_installed) == -1)
+		goto out_free_root;
+
+	root->parent = NULL;
+	root->nodeid = FUSE_ROOT_ID;
+	inc_nlookup(root);
+	hash_id(f, root);
+
+	return f;
+
+out_free_root:
+	free(root);
+out_free_id_table:
+	free(f->id_table.array);
+out_free_name_table:
+	free(f->name_table.array);
+out_free_session:
+	fuse_session_destroy(f->se);
+out_free_fs:
+	/* Horrible compatibility hack to stop the destructor from being
+	   called on the filesystem without init being called first */
+	fs->op.destroy = NULL;
+	fuse_fs_destroy(f->fs);
+	free(f->conf.modules);
+out_free:
+	free(f);
+out_delete_context_key:
+	fuse_delete_context_key();
+out:
+	return NULL;
+}
+
+struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
+		      const struct fuse_operations *op, size_t op_size,
+		      void *user_data)
+{
+	return fuse_new_common(ch, args, op, op_size, user_data, 0);
+}
+
+void fuse_destroy(struct fuse *f)
+{
+	size_t i;
+
+	if (f->conf.intr && f->intr_installed)
+		fuse_restore_intr_signal(f->conf.intr_signal);
+
+	if (f->fs) {
+		struct fuse_context_i *c = fuse_get_context_internal();
+
+		memset(c, 0, sizeof(*c));
+		c->ctx.fuse = f;
+
+		for (i = 0; i < f->id_table.size; i++) {
+			struct node *node;
+
+			for (node = f->id_table.array[i]; node != NULL;
+			     node = node->id_next) {
+				if (node->is_hidden) {
+					char *path;
+					if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
+						fuse_fs_unlink(f->fs, path);
+						free(path);
+					}
+				}
+			}
+		}
+	}
+	for (i = 0; i < f->id_table.size; i++) {
+		struct node *node;
+		struct node *next;
+
+		for (node = f->id_table.array[i]; node != NULL; node = next) {
+			next = node->id_next;
+			free_node(f, node);
+			f->id_table.use--;
+		}
+	}
+	assert(list_empty(&f->partial_slabs));
+	assert(list_empty(&f->full_slabs));
+
+	free(f->id_table.array);
+	free(f->name_table.array);
+	pthread_mutex_destroy(&f->lock);
+	fuse_session_destroy(f->se);
+	free(f->conf.modules);
+	free(f);
+	fuse_delete_context_key();
+}
+
+static struct fuse *fuse_new_common_compat25(int fd, struct fuse_args *args,
+					     const struct fuse_operations *op,
+					     size_t op_size, int compat)
+{
+	struct fuse *f = NULL;
+	struct fuse_chan *ch = fuse_kern_chan_new(fd);
+
+	if (ch)
+		f = fuse_new_common(ch, args, op, op_size, NULL, compat);
+
+	return f;
+}
+
+/* called with fuse_context_lock held or during initialization (before
+   main() has been called) */
+void fuse_register_module(struct fuse_module *mod)
+{
+	mod->ctr = 0;
+	mod->so = fuse_current_so;
+	if (mod->so)
+		mod->so->ctr++;
+	mod->next = fuse_modules;
+	fuse_modules = mod;
+}
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+static struct fuse *fuse_new_common_compat(int fd, const char *opts,
+					   const struct fuse_operations *op,
+					   size_t op_size, int compat)
+{
+	struct fuse *f;
+	struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+
+	if (fuse_opt_add_arg(&args, "") == -1)
+		return NULL;
+	if (opts &&
+	    (fuse_opt_add_arg(&args, "-o") == -1 ||
+	     fuse_opt_add_arg(&args, opts) == -1)) {
+		fuse_opt_free_args(&args);
+		return NULL;
+	}
+	f = fuse_new_common_compat25(fd, &args, op, op_size, compat);
+	fuse_opt_free_args(&args);
+
+	return f;
+}
+
+struct fuse *fuse_new_compat22(int fd, const char *opts,
+			       const struct fuse_operations_compat22 *op,
+			       size_t op_size)
+{
+	return fuse_new_common_compat(fd, opts, (struct fuse_operations *) op,
+				      op_size, 22);
+}
+
+struct fuse *fuse_new_compat2(int fd, const char *opts,
+			      const struct fuse_operations_compat2 *op)
+{
+	return fuse_new_common_compat(fd, opts, (struct fuse_operations *) op,
+				      sizeof(struct fuse_operations_compat2),
+				      21);
+}
+
+struct fuse *fuse_new_compat1(int fd, int flags,
+			      const struct fuse_operations_compat1 *op)
+{
+	const char *opts = NULL;
+	if (flags & FUSE_DEBUG_COMPAT1)
+		opts = "debug";
+	return fuse_new_common_compat(fd, opts, (struct fuse_operations *) op,
+				      sizeof(struct fuse_operations_compat1),
+				      11);
+}
+
+FUSE_SYMVER(".symver fuse_exited,__fuse_exited@");
+FUSE_SYMVER(".symver fuse_process_cmd,__fuse_process_cmd@");
+FUSE_SYMVER(".symver fuse_read_cmd,__fuse_read_cmd@");
+FUSE_SYMVER(".symver fuse_set_getcontext_func,__fuse_set_getcontext_func@");
+FUSE_SYMVER(".symver fuse_new_compat2,fuse_new@");
+FUSE_SYMVER(".symver fuse_new_compat22,fuse_new@FUSE_2.2");
+
+#endif /* __FreeBSD__ || __NetBSD__  */
+
+struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
+			       const struct fuse_operations_compat25 *op,
+			       size_t op_size)
+{
+	return fuse_new_common_compat25(fd, args, (struct fuse_operations *) op,
+					op_size, 25);
+}
+
+FUSE_SYMVER(".symver fuse_new_compat25,fuse_new@FUSE_2.5");
diff --git a/fuse/fuse_i.h b/fuse/fuse_i.h
new file mode 100644
index 0000000..78f1467
--- /dev/null
+++ b/fuse/fuse_i.h
@@ -0,0 +1,128 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#include "fuse.h"
+#include "fuse_lowlevel.h"
+
+struct fuse_chan;
+struct fuse_ll;
+
+struct fuse_session {
+	struct fuse_session_ops op;
+
+	int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf,
+			   struct fuse_chan **chp);
+
+	void (*process_buf)(void *data, const struct fuse_buf *buf,
+			    struct fuse_chan *ch);
+
+	void *data;
+
+	volatile int exited;
+
+	struct fuse_chan *ch;
+};
+
+struct fuse_req {
+	struct fuse_ll *f;
+	uint64_t unique;
+	int ctr;
+	pthread_mutex_t lock;
+	struct fuse_ctx ctx;
+	struct fuse_chan *ch;
+	int interrupted;
+	unsigned int ioctl_64bit : 1;
+	union {
+		struct {
+			uint64_t unique;
+		} i;
+		struct {
+			fuse_interrupt_func_t func;
+			void *data;
+		} ni;
+	} u;
+	struct fuse_req *next;
+	struct fuse_req *prev;
+};
+
+struct fuse_notify_req {
+	uint64_t unique;
+	void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
+		      const void *, const struct fuse_buf *);
+	struct fuse_notify_req *next;
+	struct fuse_notify_req *prev;
+};
+
+struct fuse_ll {
+	int debug;
+	int allow_root;
+	int atomic_o_trunc;
+	int no_remote_posix_lock;
+	int no_remote_flock;
+	int big_writes;
+	int splice_write;
+	int splice_move;
+	int splice_read;
+	int no_splice_write;
+	int no_splice_move;
+	int no_splice_read;
+	struct fuse_lowlevel_ops op;
+	int got_init;
+	struct cuse_data *cuse_data;
+	void *userdata;
+	uid_t owner;
+	struct fuse_conn_info conn;
+	struct fuse_req list;
+	struct fuse_req interrupts;
+	pthread_mutex_t lock;
+	int got_destroy;
+	pthread_key_t pipe_key;
+	int broken_splice_nonblock;
+	uint64_t notify_ctr;
+	struct fuse_notify_req notify_list;
+};
+
+struct fuse_cmd {
+	char *buf;
+	size_t buflen;
+	struct fuse_chan *ch;
+};
+
+struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
+			     const struct fuse_operations *op,
+			     size_t op_size, void *user_data, int compat);
+
+int fuse_sync_compat_args(struct fuse_args *args);
+
+struct fuse_chan *fuse_kern_chan_new(int fd);
+
+struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
+					const struct fuse_lowlevel_ops *op,
+					size_t op_size, void *userdata);
+
+void fuse_kern_unmount_compat22(const char *mountpoint);
+void fuse_kern_unmount(const char *mountpoint, int fd);
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args);
+
+int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
+			       int count);
+void fuse_free_req(fuse_req_t req);
+
+
+struct fuse *fuse_setup_common(int argc, char *argv[],
+			       const struct fuse_operations *op,
+			       size_t op_size,
+			       char **mountpoint,
+			       int *multithreaded,
+			       int *fd,
+			       void *user_data,
+			       int compat);
+
+void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
+
+int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
diff --git a/fuse/fuse_kern_chan.c b/fuse/fuse_kern_chan.c
new file mode 100644
index 0000000..5f77bbf
--- /dev/null
+++ b/fuse/fuse_kern_chan.c
@@ -0,0 +1,95 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#include "fuse_lowlevel.h"
+#include "fuse_kernel.h"
+#include "fuse_i.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+
+static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
+				  size_t size)
+{
+	struct fuse_chan *ch = *chp;
+	int err;
+	ssize_t res;
+	struct fuse_session *se = fuse_chan_session(ch);
+	assert(se != NULL);
+
+restart:
+	res = read(fuse_chan_fd(ch), buf, size);
+	err = errno;
+
+	if (fuse_session_exited(se))
+		return 0;
+	if (res == -1) {
+		/* ENOENT means the operation was interrupted, it's safe
+		   to restart */
+		if (err == ENOENT)
+			goto restart;
+
+		if (err == ENODEV) {
+			fuse_session_exit(se);
+			return 0;
+		}
+		/* Errors occurring during normal operation: EINTR (read
+		   interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
+		   umounted) */
+		if (err != EINTR && err != EAGAIN)
+			perror("fuse: reading device");
+		return -err;
+	}
+	if ((size_t) res < sizeof(struct fuse_in_header)) {
+		fprintf(stderr, "short read on fuse device\n");
+		return -EIO;
+	}
+	return res;
+}
+
+static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
+			       size_t count)
+{
+	if (iov) {
+		ssize_t res = writev(fuse_chan_fd(ch), iov, count);
+		int err = errno;
+
+		if (res == -1) {
+			struct fuse_session *se = fuse_chan_session(ch);
+
+			assert(se != NULL);
+
+			/* ENOENT means the operation was interrupted */
+			if (!fuse_session_exited(se) && err != ENOENT)
+				perror("fuse: writing device");
+			return -err;
+		}
+	}
+	return 0;
+}
+
+static void fuse_kern_chan_destroy(struct fuse_chan *ch)
+{
+	close(fuse_chan_fd(ch));
+}
+
+#define MIN_BUFSIZE 0x21000
+
+struct fuse_chan *fuse_kern_chan_new(int fd)
+{
+	struct fuse_chan_ops op = {
+		.receive = fuse_kern_chan_receive,
+		.send = fuse_kern_chan_send,
+		.destroy = fuse_kern_chan_destroy,
+	};
+	size_t bufsize = getpagesize() + 0x1000;
+	bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize;
+	return fuse_chan_new(&op, fd, bufsize, NULL);
+}
diff --git a/fuse/fuse_loop.c b/fuse/fuse_loop.c
new file mode 100644
index 0000000..b7b4ca4
--- /dev/null
+++ b/fuse/fuse_loop.c
@@ -0,0 +1,46 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#include "fuse_lowlevel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+int fuse_session_loop(struct fuse_session *se)
+{
+	int res = 0;
+	struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+	size_t bufsize = fuse_chan_bufsize(ch);
+	char *buf = (char *) malloc(bufsize);
+	if (!buf) {
+		fprintf(stderr, "fuse: failed to allocate read buffer\n");
+		return -1;
+	}
+
+	while (!fuse_session_exited(se)) {
+		struct fuse_chan *tmpch = ch;
+		struct fuse_buf fbuf = {
+			.mem = buf,
+			.size = bufsize,
+		};
+
+		res = fuse_session_receive_buf(se, &fbuf, &tmpch);
+
+		if (res == -EINTR)
+			continue;
+		if (res <= 0)
+			break;
+
+		fuse_session_process_buf(se, &fbuf, tmpch);
+	}
+
+	free(buf);
+	fuse_session_reset(se);
+	return res < 0 ? -1 : 0;
+}
diff --git a/fuse/fuse_loop_mt.c b/fuse/fuse_loop_mt.c
new file mode 100644
index 0000000..aefc3ec
--- /dev/null
+++ b/fuse/fuse_loop_mt.c
@@ -0,0 +1,271 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include "fuse_lowlevel.h"
+#include "fuse_misc.h"
+#include "fuse_kernel.h"
+#include "fuse_i.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+/* Environment var controlling the thread stack size */
+#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
+
+struct fuse_worker {
+	struct fuse_worker *prev;
+	struct fuse_worker *next;
+	pthread_t thread_id;
+	size_t bufsize;
+	char *buf;
+	struct fuse_mt *mt;
+};
+
+struct fuse_mt {
+	pthread_mutex_t lock;
+	int numworker;
+	int numavail;
+	struct fuse_session *se;
+	struct fuse_chan *prevch;
+	struct fuse_worker main;
+	sem_t finish;
+	int exit;
+	int error;
+};
+
+static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
+{
+	struct fuse_worker *prev = next->prev;
+	w->next = next;
+	w->prev = prev;
+	prev->next = w;
+	next->prev = w;
+}
+
+static void list_del_worker(struct fuse_worker *w)
+{
+	struct fuse_worker *prev = w->prev;
+	struct fuse_worker *next = w->next;
+	prev->next = next;
+	next->prev = prev;
+}
+
+#define PTHREAD_CANCEL_ENABLE 0
+#define PTHREAD_CANCEL_DISABLE 1
+
+static int fuse_loop_start_thread(struct fuse_mt *mt);
+
+static void *fuse_do_work(void *data)
+{
+	struct fuse_worker *w = (struct fuse_worker *) data;
+	struct fuse_mt *mt = w->mt;
+
+	while (!fuse_session_exited(mt->se)) {
+		int isforget = 0;
+		struct fuse_chan *ch = mt->prevch;
+		struct fuse_buf fbuf = {
+			.mem = w->buf,
+			.size = w->bufsize,
+		};
+		int res;
+
+#ifndef ANDROID
+		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+#endif
+		res = fuse_session_receive_buf(mt->se, &fbuf, &ch);
+#ifndef ANDROID
+		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+#endif
+		if (res == -EINTR)
+			continue;
+		if (res <= 0) {
+			if (res < 0) {
+				fuse_session_exit(mt->se);
+				mt->error = -1;
+			}
+			break;
+		}
+
+		pthread_mutex_lock(&mt->lock);
+		if (mt->exit) {
+			pthread_mutex_unlock(&mt->lock);
+			return NULL;
+		}
+
+		/*
+		 * This disgusting hack is needed so that zillions of threads
+		 * are not created on a burst of FORGET messages
+		 */
+		if (!(fbuf.flags & FUSE_BUF_IS_FD)) {
+			struct fuse_in_header *in = fbuf.mem;
+
+			if (in->opcode == FUSE_FORGET ||
+			    in->opcode == FUSE_BATCH_FORGET)
+				isforget = 1;
+		}
+
+		if (!isforget)
+			mt->numavail--;
+		if (mt->numavail == 0)
+			fuse_loop_start_thread(mt);
+		pthread_mutex_unlock(&mt->lock);
+
+		fuse_session_process_buf(mt->se, &fbuf, ch);
+
+		pthread_mutex_lock(&mt->lock);
+		if (!isforget)
+			mt->numavail++;
+		if (mt->numavail > 10) {
+			if (mt->exit) {
+				pthread_mutex_unlock(&mt->lock);
+				return NULL;
+			}
+			list_del_worker(w);
+			mt->numavail--;
+			mt->numworker--;
+			pthread_mutex_unlock(&mt->lock);
+
+			pthread_detach(w->thread_id);
+			free(w->buf);
+			free(w);
+			return NULL;
+		}
+		pthread_mutex_unlock(&mt->lock);
+	}
+
+	sem_post(&mt->finish);
+
+	return NULL;
+}
+
+int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
+{
+	sigset_t oldset;
+	sigset_t newset;
+	int res;
+	pthread_attr_t attr;
+	char *stack_size;
+
+	/* Override default stack size */
+	pthread_attr_init(&attr);
+	stack_size = getenv(ENVNAME_THREAD_STACK);
+	if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
+		fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
+
+	/* Disallow signal reception in worker threads */
+	sigemptyset(&newset);
+	sigaddset(&newset, SIGTERM);
+	sigaddset(&newset, SIGINT);
+	sigaddset(&newset, SIGHUP);
+	sigaddset(&newset, SIGQUIT);
+	pthread_sigmask(SIG_BLOCK, &newset, &oldset);
+	res = pthread_create(thread_id, &attr, func, arg);
+	pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+	pthread_attr_destroy(&attr);
+	if (res != 0) {
+		fprintf(stderr, "fuse: error creating thread: %s\n",
+			strerror(res));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int fuse_loop_start_thread(struct fuse_mt *mt)
+{
+	int res;
+	struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
+	if (!w) {
+		fprintf(stderr, "fuse: failed to allocate worker structure\n");
+		return -1;
+	}
+	memset(w, 0, sizeof(struct fuse_worker));
+	w->bufsize = fuse_chan_bufsize(mt->prevch);
+	w->buf = malloc(w->bufsize);
+	w->mt = mt;
+	if (!w->buf) {
+		fprintf(stderr, "fuse: failed to allocate read buffer\n");
+		free(w);
+		return -1;
+	}
+
+	res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
+	if (res == -1) {
+		free(w->buf);
+		free(w);
+		return -1;
+	}
+	list_add_worker(w, &mt->main);
+	mt->numavail ++;
+	mt->numworker ++;
+
+	return 0;
+}
+
+static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
+{
+	pthread_join(w->thread_id, NULL);
+	pthread_mutex_lock(&mt->lock);
+	list_del_worker(w);
+	pthread_mutex_unlock(&mt->lock);
+	free(w->buf);
+	free(w);
+}
+
+int fuse_session_loop_mt(struct fuse_session *se)
+{
+	int err;
+	struct fuse_mt mt;
+	struct fuse_worker *w;
+
+	memset(&mt, 0, sizeof(struct fuse_mt));
+	mt.se = se;
+	mt.prevch = fuse_session_next_chan(se, NULL);
+	mt.error = 0;
+	mt.numworker = 0;
+	mt.numavail = 0;
+	mt.main.thread_id = pthread_self();
+	mt.main.prev = mt.main.next = &mt.main;
+	sem_init(&mt.finish, 0, 0);
+	fuse_mutex_init(&mt.lock);
+
+	pthread_mutex_lock(&mt.lock);
+	err = fuse_loop_start_thread(&mt);
+	pthread_mutex_unlock(&mt.lock);
+	if (!err) {
+		/* sem_wait() is interruptible */
+		while (!fuse_session_exited(se))
+			sem_wait(&mt.finish);
+
+#ifndef ANDROID
+		for (w = mt.main.next; w != &mt.main; w = w->next)
+			pthread_cancel(w->thread_id);
+#else
+                for (w = mt.main.next; w != &mt.main; w = w->next)
+			pthread_kill(w->thread_id, SIGUSR1);
+#endif
+		mt.exit = 1;
+
+		while (mt.main.next != &mt.main)
+			fuse_join_worker(&mt, mt.main.next);
+
+		err = mt.error;
+	}
+
+	pthread_mutex_destroy(&mt.lock);
+	sem_destroy(&mt.finish);
+	fuse_session_reset(se);
+	return err;
+}
diff --git a/fuse/fuse_lowlevel.c b/fuse/fuse_lowlevel.c
new file mode 100644
index 0000000..103f831
--- /dev/null
+++ b/fuse/fuse_lowlevel.c
@@ -0,0 +1,2967 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_kernel.h"
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+#include "fuse_common_compat.h"
+#include "fuse_lowlevel_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE       1024
+#endif
+#ifndef F_SETPIPE_SZ
+#define F_SETPIPE_SZ	(F_LINUX_SPECIFIC_BASE + 7)
+#endif
+
+
+#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
+#define OFFSET_MAX 0x7fffffffffffffffLL
+
+#define container_of(ptr, type, member) ({				\
+			const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+			(type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct fuse_pollhandle {
+	uint64_t kh;
+	struct fuse_chan *ch;
+	struct fuse_ll *f;
+};
+
+static size_t pagesize;
+
+static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
+{
+	pagesize = getpagesize();
+}
+
+static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
+{
+	attr->ino	= stbuf->st_ino;
+	attr->mode	= stbuf->st_mode;
+	attr->nlink	= stbuf->st_nlink;
+	attr->uid	= stbuf->st_uid;
+	attr->gid	= stbuf->st_gid;
+	attr->rdev	= stbuf->st_rdev;
+	attr->size	= stbuf->st_size;
+	attr->blksize	= stbuf->st_blksize;
+	attr->blocks	= stbuf->st_blocks;
+	attr->atime	= stbuf->st_atime;
+	attr->mtime	= stbuf->st_mtime;
+	attr->ctime	= stbuf->st_ctime;
+	attr->atimensec = ST_ATIM_NSEC(stbuf);
+	attr->mtimensec = ST_MTIM_NSEC(stbuf);
+	attr->ctimensec = ST_CTIM_NSEC(stbuf);
+}
+
+static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
+{
+	stbuf->st_mode	       = attr->mode;
+	stbuf->st_uid	       = attr->uid;
+	stbuf->st_gid	       = attr->gid;
+	stbuf->st_size	       = attr->size;
+	stbuf->st_atime	       = attr->atime;
+	stbuf->st_mtime	       = attr->mtime;
+	ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
+	ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
+}
+
+static	size_t iov_length(const struct iovec *iov, size_t count)
+{
+	size_t seg;
+	size_t ret = 0;
+
+	for (seg = 0; seg < count; seg++)
+		ret += iov[seg].iov_len;
+	return ret;
+}
+
+static void list_init_req(struct fuse_req *req)
+{
+	req->next = req;
+	req->prev = req;
+}
+
+static void list_del_req(struct fuse_req *req)
+{
+	struct fuse_req *prev = req->prev;
+	struct fuse_req *next = req->next;
+	prev->next = next;
+	next->prev = prev;
+}
+
+static void list_add_req(struct fuse_req *req, struct fuse_req *next)
+{
+	struct fuse_req *prev = next->prev;
+	req->next = next;
+	req->prev = prev;
+	prev->next = req;
+	next->prev = req;
+}
+
+static void destroy_req(fuse_req_t req)
+{
+	pthread_mutex_destroy(&req->lock);
+	free(req);
+}
+
+void fuse_free_req(fuse_req_t req)
+{
+	int ctr;
+	struct fuse_ll *f = req->f;
+
+	pthread_mutex_lock(&f->lock);
+	req->u.ni.func = NULL;
+	req->u.ni.data = NULL;
+	list_del_req(req);
+	ctr = --req->ctr;
+	pthread_mutex_unlock(&f->lock);
+	if (!ctr)
+		destroy_req(req);
+}
+
+static struct fuse_req *fuse_ll_alloc_req(struct fuse_ll *f)
+{
+	struct fuse_req *req;
+
+	req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
+	if (req == NULL) {
+		fprintf(stderr, "fuse: failed to allocate request\n");
+	} else {
+		req->f = f;
+		req->ctr = 1;
+		list_init_req(req);
+		fuse_mutex_init(&req->lock);
+	}
+
+	return req;
+}
+
+
+static int fuse_send_msg(struct fuse_ll *f, struct fuse_chan *ch,
+			 struct iovec *iov, int count)
+{
+	struct fuse_out_header *out = iov[0].iov_base;
+
+	out->len = iov_length(iov, count);
+	if (f->debug) {
+		if (out->unique == 0) {
+			fprintf(stderr, "NOTIFY: code=%d length=%u\n",
+				out->error, out->len);
+		} else if (out->error) {
+			fprintf(stderr,
+				"   unique: %llu, error: %i (%s), outsize: %i\n",
+				(unsigned long long) out->unique, out->error,
+				strerror(-out->error), out->len);
+		} else {
+			fprintf(stderr,
+				"   unique: %llu, success, outsize: %i\n",
+				(unsigned long long) out->unique, out->len);
+		}
+	}
+
+	return fuse_chan_send(ch, iov, count);
+}
+
+int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
+			       int count)
+{
+	struct fuse_out_header out;
+
+	if (error <= -1000 || error > 0) {
+		fprintf(stderr, "fuse: bad error value: %i\n",	error);
+		error = -ERANGE;
+	}
+
+	out.unique = req->unique;
+	out.error = error;
+
+	iov[0].iov_base = &out;
+	iov[0].iov_len = sizeof(struct fuse_out_header);
+
+	return fuse_send_msg(req->f, req->ch, iov, count);
+}
+
+static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
+			  int count)
+{
+	int res;
+
+	res = fuse_send_reply_iov_nofree(req, error, iov, count);
+	fuse_free_req(req);
+	return res;
+}
+
+static int send_reply(fuse_req_t req, int error, const void *arg,
+		      size_t argsize)
+{
+	struct iovec iov[2];
+	int count = 1;
+	if (argsize) {
+		iov[1].iov_base = (void *) arg;
+		iov[1].iov_len = argsize;
+		count++;
+	}
+	return send_reply_iov(req, error, iov, count);
+}
+
+int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
+{
+	int res;
+	struct iovec *padded_iov;
+
+	padded_iov = malloc((count + 1) * sizeof(struct iovec));
+	if (padded_iov == NULL)
+		return fuse_reply_err(req, ENOMEM);
+
+	memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
+	count++;
+
+	res = send_reply_iov(req, 0, padded_iov, count);
+	free(padded_iov);
+
+	return res;
+}
+
+size_t fuse_dirent_size(size_t namelen)
+{
+	return FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen);
+}
+
+char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
+		      off64_t off)
+{
+	unsigned namelen = strlen(name);
+	unsigned entlen = FUSE_NAME_OFFSET + namelen;
+	unsigned entsize = fuse_dirent_size(namelen);
+	unsigned padlen = entsize - entlen;
+	struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
+
+	dirent->ino = stbuf->st_ino;
+	dirent->off = off;
+	dirent->namelen = namelen;
+	dirent->type = (stbuf->st_mode & 0170000) >> 12;
+	strncpy(dirent->name, name, namelen);
+	if (padlen)
+		memset(buf + entlen, 0, padlen);
+
+	return buf + entsize;
+}
+
+size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
+			 const char *name, const struct stat *stbuf, off64_t off)
+{
+	size_t entsize;
+
+	(void) req;
+	entsize = fuse_dirent_size(strlen(name));
+	if (entsize <= bufsize && buf)
+		fuse_add_dirent(buf, name, stbuf, off);
+	return entsize;
+}
+
+static void convert_statfs(const struct statvfs *stbuf,
+			   struct fuse_kstatfs *kstatfs)
+{
+	kstatfs->bsize	 = stbuf->f_bsize;
+	kstatfs->frsize	 = stbuf->f_frsize;
+	kstatfs->blocks	 = stbuf->f_blocks;
+	kstatfs->bfree	 = stbuf->f_bfree;
+	kstatfs->bavail	 = stbuf->f_bavail;
+	kstatfs->files	 = stbuf->f_files;
+	kstatfs->ffree	 = stbuf->f_ffree;
+	kstatfs->namelen = stbuf->f_namemax;
+}
+
+static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
+{
+	return send_reply(req, 0, arg, argsize);
+}
+
+int fuse_reply_err(fuse_req_t req, int err)
+{
+	return send_reply(req, -err, NULL, 0);
+}
+
+void fuse_reply_none(fuse_req_t req)
+{
+	if (req->ch)
+		fuse_chan_send(req->ch, NULL, 0);
+	fuse_free_req(req);
+}
+
+static unsigned long calc_timeout_sec(double t)
+{
+	if (t > (double) ULONG_MAX)
+		return ULONG_MAX;
+	else if (t < 0.0)
+		return 0;
+	else
+		return (unsigned long) t;
+}
+
+static unsigned int calc_timeout_nsec(double t)
+{
+	double f = t - (double) calc_timeout_sec(t);
+	if (f < 0.0)
+		return 0;
+	else if (f >= 0.999999999)
+		return 999999999;
+	else
+		return (unsigned int) (f * 1.0e9);
+}
+
+static void fill_entry(struct fuse_entry_out *arg,
+		       const struct fuse_entry_param *e)
+{
+	arg->nodeid = e->ino;
+	arg->generation = e->generation;
+	arg->entry_valid = calc_timeout_sec(e->entry_timeout);
+	arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
+	arg->attr_valid = calc_timeout_sec(e->attr_timeout);
+	arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
+	convert_stat(&e->attr, &arg->attr);
+}
+
+static void fill_open(struct fuse_open_out *arg,
+		      const struct fuse_file_info *f)
+{
+	arg->fh = f->fh;
+	if (f->direct_io)
+		arg->open_flags |= FOPEN_DIRECT_IO;
+	if (f->keep_cache)
+		arg->open_flags |= FOPEN_KEEP_CACHE;
+	if (f->nonseekable)
+		arg->open_flags |= FOPEN_NONSEEKABLE;
+}
+
+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
+{
+	struct fuse_entry_out arg;
+	size_t size = req->f->conn.proto_minor < 9 ?
+		FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
+
+	/* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
+	   negative entry */
+	if (!e->ino && req->f->conn.proto_minor < 4)
+		return fuse_reply_err(req, ENOENT);
+
+	memset(&arg, 0, sizeof(arg));
+	fill_entry(&arg, e);
+	return send_reply_ok(req, &arg, size);
+}
+
+int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
+		      const struct fuse_file_info *f)
+{
+	char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
+	size_t entrysize = req->f->conn.proto_minor < 9 ?
+		FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
+	struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
+	struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
+
+	memset(buf, 0, sizeof(buf));
+	fill_entry(earg, e);
+	fill_open(oarg, f);
+	return send_reply_ok(req, buf,
+			     entrysize + sizeof(struct fuse_open_out));
+}
+
+int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
+		    double attr_timeout)
+{
+	struct fuse_attr_out arg;
+	size_t size = req->f->conn.proto_minor < 9 ?
+		FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
+
+	memset(&arg, 0, sizeof(arg));
+	arg.attr_valid = calc_timeout_sec(attr_timeout);
+	arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
+	convert_stat(attr, &arg.attr);
+
+	return send_reply_ok(req, &arg, size);
+}
+
+int fuse_reply_readlink(fuse_req_t req, const char *linkname)
+{
+	return send_reply_ok(req, linkname, strlen(linkname));
+}
+
+int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
+{
+	struct fuse_open_out arg;
+
+	memset(&arg, 0, sizeof(arg));
+	fill_open(&arg, f);
+	return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_write(fuse_req_t req, size_t count)
+{
+	struct fuse_write_out arg;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.size = count;
+
+	return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
+{
+	return send_reply_ok(req, buf, size);
+}
+
+static int fuse_send_data_iov_fallback(struct fuse_ll *f, struct fuse_chan *ch,
+				       struct iovec *iov, int iov_count,
+				       struct fuse_bufvec *buf,
+				       size_t len)
+{
+	struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
+	void *mbuf;
+	int res;
+
+	/* Optimize common case */
+	if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
+	    !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
+		/* FIXME: also avoid memory copy if there are multiple buffers
+		   but none of them contain an fd */
+
+		iov[iov_count].iov_base = buf->buf[0].mem;
+		iov[iov_count].iov_len = len;
+		iov_count++;
+		return fuse_send_msg(f, ch, iov, iov_count);
+	}
+
+	res = posix_memalign(&mbuf, pagesize, len);
+	if (res != 0)
+		return res;
+
+	mem_buf.buf[0].mem = mbuf;
+	res = fuse_buf_copy(&mem_buf, buf, 0);
+	if (res < 0) {
+		free(mbuf);
+		return -res;
+	}
+	len = res;
+
+	iov[iov_count].iov_base = mbuf;
+	iov[iov_count].iov_len = len;
+	iov_count++;
+	res = fuse_send_msg(f, ch, iov, iov_count);
+	free(mbuf);
+
+	return res;
+}
+
+struct fuse_ll_pipe {
+	size_t size;
+	int can_grow;
+	int pipe[2];
+};
+
+static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
+{
+	close(llp->pipe[0]);
+	close(llp->pipe[1]);
+	free(llp);
+}
+
+#ifdef HAVE_SPLICE
+static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_ll *f)
+{
+	struct fuse_ll_pipe *llp = pthread_getspecific(f->pipe_key);
+	if (llp == NULL) {
+		int res;
+
+		llp = malloc(sizeof(struct fuse_ll_pipe));
+		if (llp == NULL)
+			return NULL;
+
+		res = pipe(llp->pipe);
+		if (res == -1) {
+			free(llp);
+			return NULL;
+		}
+
+		if (fcntl(llp->pipe[0], F_SETFL, O_NONBLOCK) == -1 ||
+		    fcntl(llp->pipe[1], F_SETFL, O_NONBLOCK) == -1) {
+			close(llp->pipe[0]);
+			close(llp->pipe[1]);
+			free(llp);
+			return NULL;
+		}
+
+		/*
+		 *the default size is 16 pages on linux
+		 */
+		llp->size = pagesize * 16;
+		llp->can_grow = 1;
+
+		pthread_setspecific(f->pipe_key, llp);
+	}
+
+	return llp;
+}
+#endif
+
+static void fuse_ll_clear_pipe(struct fuse_ll *f)
+{
+	struct fuse_ll_pipe *llp = pthread_getspecific(f->pipe_key);
+	if (llp) {
+		pthread_setspecific(f->pipe_key, NULL);
+		fuse_ll_pipe_free(llp);
+	}
+}
+
+#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
+static int read_back(int fd, char *buf, size_t len)
+{
+	int res;
+
+	res = read(fd, buf, len);
+	if (res == -1) {
+		fprintf(stderr, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
+		return -EIO;
+	}
+	if (res != len) {
+		fprintf(stderr, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch,
+			       struct iovec *iov, int iov_count,
+			       struct fuse_bufvec *buf, unsigned int flags)
+{
+	int res;
+	size_t len = fuse_buf_size(buf);
+	struct fuse_out_header *out = iov[0].iov_base;
+	struct fuse_ll_pipe *llp;
+	int splice_flags;
+	size_t pipesize;
+	size_t total_fd_size;
+	size_t idx;
+	size_t headerlen;
+	struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
+
+	if (f->broken_splice_nonblock)
+		goto fallback;
+
+	if (flags & FUSE_BUF_NO_SPLICE)
+		goto fallback;
+
+	total_fd_size = 0;
+	for (idx = buf->idx; idx < buf->count; idx++) {
+		if (buf->buf[idx].flags & FUSE_BUF_IS_FD) {
+			total_fd_size = buf->buf[idx].size;
+			if (idx == buf->idx)
+				total_fd_size -= buf->off;
+		}
+	}
+	if (total_fd_size < 2 * pagesize)
+		goto fallback;
+
+	if (f->conn.proto_minor < 14 ||
+	    !(f->conn.want & FUSE_CAP_SPLICE_WRITE))
+		goto fallback;
+
+	llp = fuse_ll_get_pipe(f);
+	if (llp == NULL)
+		goto fallback;
+
+
+	headerlen = iov_length(iov, iov_count);
+
+	out->len = headerlen + len;
+
+	/*
+	 * Heuristic for the required pipe size, does not work if the
+	 * source contains less than page size fragments
+	 */
+	pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
+
+	if (llp->size < pipesize) {
+		if (llp->can_grow) {
+			res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
+			if (res == -1) {
+				llp->can_grow = 0;
+				goto fallback;
+			}
+			llp->size = res;
+		}
+		if (llp->size < pipesize)
+			goto fallback;
+	}
+
+
+	res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
+	if (res == -1)
+		goto fallback;
+
+	if (res != headerlen) {
+		res = -EIO;
+		fprintf(stderr, "fuse: short vmsplice to pipe: %u/%zu\n", res,
+			headerlen);
+		goto clear_pipe;
+	}
+
+	pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
+	pipe_buf.buf[0].fd = llp->pipe[1];
+
+	res = fuse_buf_copy(&pipe_buf, buf,
+			    FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK);
+	if (res < 0) {
+		if (res == -EAGAIN || res == -EINVAL) {
+			/*
+			 * Should only get EAGAIN on kernels with
+			 * broken SPLICE_F_NONBLOCK support (<=
+			 * 2.6.35) where this error or a short read is
+			 * returned even if the pipe itself is not
+			 * full
+			 *
+			 * EINVAL might mean that splice can't handle
+			 * this combination of input and output.
+			 */
+			if (res == -EAGAIN)
+				f->broken_splice_nonblock = 1;
+
+			pthread_setspecific(f->pipe_key, NULL);
+			fuse_ll_pipe_free(llp);
+			goto fallback;
+		}
+		res = -res;
+		goto clear_pipe;
+	}
+
+	if (res != 0 && res < len) {
+		struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
+		void *mbuf;
+		size_t now_len = res;
+		/*
+		 * For regular files a short count is either
+		 *  1) due to EOF, or
+		 *  2) because of broken SPLICE_F_NONBLOCK (see above)
+		 *
+		 * For other inputs it's possible that we overflowed
+		 * the pipe because of small buffer fragments.
+		 */
+
+		res = posix_memalign(&mbuf, pagesize, len);
+		if (res != 0)
+			goto clear_pipe;
+
+		mem_buf.buf[0].mem = mbuf;
+		mem_buf.off = now_len;
+		res = fuse_buf_copy(&mem_buf, buf, 0);
+		if (res > 0) {
+			char *tmpbuf;
+			size_t extra_len = res;
+			/*
+			 * Trickiest case: got more data.  Need to get
+			 * back the data from the pipe and then fall
+			 * back to regular write.
+			 */
+			tmpbuf = malloc(headerlen);
+			if (tmpbuf == NULL) {
+				free(mbuf);
+				res = ENOMEM;
+				goto clear_pipe;
+			}
+			res = read_back(llp->pipe[0], tmpbuf, headerlen);
+			if (res != 0) {
+				free(mbuf);
+				goto clear_pipe;
+			}
+			free(tmpbuf);
+			res = read_back(llp->pipe[0], mbuf, now_len);
+			if (res != 0) {
+				free(mbuf);
+				goto clear_pipe;
+			}
+			len = now_len + extra_len;
+			iov[iov_count].iov_base = mbuf;
+			iov[iov_count].iov_len = len;
+			iov_count++;
+			res = fuse_send_msg(f, ch, iov, iov_count);
+			free(mbuf);
+			return res;
+		}
+		free(mbuf);
+		res = now_len;
+	}
+	len = res;
+	out->len = headerlen + len;
+
+	if (f->debug) {
+		fprintf(stderr,
+			"   unique: %llu, success, outsize: %i (splice)\n",
+			(unsigned long long) out->unique, out->len);
+	}
+
+	splice_flags = 0;
+	if ((flags & FUSE_BUF_SPLICE_MOVE) &&
+	    (f->conn.want & FUSE_CAP_SPLICE_MOVE))
+		splice_flags |= SPLICE_F_MOVE;
+
+	res = splice(llp->pipe[0], NULL,
+		     fuse_chan_fd(ch), NULL, out->len, splice_flags);
+	if (res == -1) {
+		res = -errno;
+		perror("fuse: splice from pipe");
+		goto clear_pipe;
+	}
+	if (res != out->len) {
+		res = -EIO;
+		fprintf(stderr, "fuse: short splice from pipe: %u/%u\n",
+			res, out->len);
+		goto clear_pipe;
+	}
+	return 0;
+
+clear_pipe:
+	fuse_ll_clear_pipe(f);
+	return res;
+
+fallback:
+	return fuse_send_data_iov_fallback(f, ch, iov, iov_count, buf, len);
+}
+#else
+static int fuse_send_data_iov(struct fuse_ll *f, struct fuse_chan *ch,
+			       struct iovec *iov, int iov_count,
+			       struct fuse_bufvec *buf, unsigned int flags)
+{
+	size_t len = fuse_buf_size(buf);
+	(void) flags;
+
+	return fuse_send_data_iov_fallback(f, ch, iov, iov_count, buf, len);
+}
+#endif
+
+int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
+		    enum fuse_buf_copy_flags flags)
+{
+	struct iovec iov[2];
+	struct fuse_out_header out;
+	int res;
+
+	iov[0].iov_base = &out;
+	iov[0].iov_len = sizeof(struct fuse_out_header);
+
+	out.unique = req->unique;
+	out.error = 0;
+
+	res = fuse_send_data_iov(req->f, req->ch, iov, 1, bufv, flags);
+	if (res <= 0) {
+		fuse_free_req(req);
+		return res;
+	} else {
+		return fuse_reply_err(req, res);
+	}
+}
+
+int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
+{
+	struct fuse_statfs_out arg;
+	size_t size = req->f->conn.proto_minor < 4 ?
+		FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
+
+	memset(&arg, 0, sizeof(arg));
+	convert_statfs(stbuf, &arg.st);
+
+	return send_reply_ok(req, &arg, size);
+}
+
+int fuse_reply_xattr(fuse_req_t req, size_t count)
+{
+	struct fuse_getxattr_out arg;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.size = count;
+
+	return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
+{
+	struct fuse_lk_out arg;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.lk.type = lock->l_type;
+	if (lock->l_type != F_UNLCK) {
+		arg.lk.start = lock->l_start;
+		if (lock->l_len == 0)
+			arg.lk.end = OFFSET_MAX;
+		else
+			arg.lk.end = lock->l_start + lock->l_len - 1;
+	}
+	arg.lk.pid = lock->l_pid;
+	return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
+{
+	struct fuse_bmap_out arg;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.block = idx;
+
+	return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
+						      size_t count)
+{
+	struct fuse_ioctl_iovec *fiov;
+	size_t i;
+
+	fiov = malloc(sizeof(fiov[0]) * count);
+	if (!fiov)
+		return NULL;
+
+	for (i = 0; i < count; i++) {
+		fiov[i].base = (uintptr_t) iov[i].iov_base;
+		fiov[i].len = iov[i].iov_len;
+	}
+
+	return fiov;
+}
+
+int fuse_reply_ioctl_retry(fuse_req_t req,
+			   const struct iovec *in_iov, size_t in_count,
+			   const struct iovec *out_iov, size_t out_count)
+{
+	struct fuse_ioctl_out arg;
+	struct fuse_ioctl_iovec *in_fiov = NULL;
+	struct fuse_ioctl_iovec *out_fiov = NULL;
+	struct iovec iov[4];
+	size_t count = 1;
+	int res;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.flags |= FUSE_IOCTL_RETRY;
+	arg.in_iovs = in_count;
+	arg.out_iovs = out_count;
+	iov[count].iov_base = &arg;
+	iov[count].iov_len = sizeof(arg);
+	count++;
+
+	if (req->f->conn.proto_minor < 16) {
+		if (in_count) {
+			iov[count].iov_base = (void *)in_iov;
+			iov[count].iov_len = sizeof(in_iov[0]) * in_count;
+			count++;
+		}
+
+		if (out_count) {
+			iov[count].iov_base = (void *)out_iov;
+			iov[count].iov_len = sizeof(out_iov[0]) * out_count;
+			count++;
+		}
+	} else {
+		/* Can't handle non-compat 64bit ioctls on 32bit */
+		if (sizeof(void *) == 4 && req->ioctl_64bit) {
+			res = fuse_reply_err(req, EINVAL);
+			goto out;
+		}
+
+		if (in_count) {
+			in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
+			if (!in_fiov)
+				goto enomem;
+
+			iov[count].iov_base = (void *)in_fiov;
+			iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
+			count++;
+		}
+		if (out_count) {
+			out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
+			if (!out_fiov)
+				goto enomem;
+
+			iov[count].iov_base = (void *)out_fiov;
+			iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
+			count++;
+		}
+	}
+
+	res = send_reply_iov(req, 0, iov, count);
+out:
+	free(in_fiov);
+	free(out_fiov);
+
+	return res;
+
+enomem:
+	res = fuse_reply_err(req, ENOMEM);
+	goto out;
+}
+
+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
+{
+	struct fuse_ioctl_out arg;
+	struct iovec iov[3];
+	size_t count = 1;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.result = result;
+	iov[count].iov_base = &arg;
+	iov[count].iov_len = sizeof(arg);
+	count++;
+
+	if (size) {
+		iov[count].iov_base = (char *) buf;
+		iov[count].iov_len = size;
+		count++;
+	}
+
+	return send_reply_iov(req, 0, iov, count);
+}
+
+int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
+			 int count)
+{
+	struct iovec *padded_iov;
+	struct fuse_ioctl_out arg;
+	int res;
+
+	padded_iov = malloc((count + 2) * sizeof(struct iovec));
+	if (padded_iov == NULL)
+		return fuse_reply_err(req, ENOMEM);
+
+	memset(&arg, 0, sizeof(arg));
+	arg.result = result;
+	padded_iov[1].iov_base = &arg;
+	padded_iov[1].iov_len = sizeof(arg);
+
+	memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
+
+	res = send_reply_iov(req, 0, padded_iov, count + 2);
+	free(padded_iov);
+
+	return res;
+}
+
+int fuse_reply_poll(fuse_req_t req, unsigned revents)
+{
+	struct fuse_poll_out arg;
+
+	memset(&arg, 0, sizeof(arg));
+	arg.revents = revents;
+
+	return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	char *name = (char *) inarg;
+
+	if (req->f->op.lookup)
+		req->f->op.lookup(req, nodeid, name);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
+
+	if (req->f->op.forget)
+		req->f->op.forget(req, nodeid, arg->nlookup);
+	else
+		fuse_reply_none(req);
+}
+
+static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
+			    const void *inarg)
+{
+	struct fuse_batch_forget_in *arg = (void *) inarg;
+	struct fuse_forget_one *param = (void *) PARAM(arg);
+	unsigned int i;
+
+	(void) nodeid;
+
+	if (req->f->op.forget_multi) {
+		req->f->op.forget_multi(req, arg->count,
+				     (struct fuse_forget_data *) param);
+	} else if (req->f->op.forget) {
+		for (i = 0; i < arg->count; i++) {
+			struct fuse_forget_one *forget = &param[i];
+			struct fuse_req *dummy_req;
+
+			dummy_req = fuse_ll_alloc_req(req->f);
+			if (dummy_req == NULL)
+				break;
+
+			dummy_req->unique = req->unique;
+			dummy_req->ctx = req->ctx;
+			dummy_req->ch = NULL;
+
+			req->f->op.forget(dummy_req, forget->nodeid,
+					  forget->nlookup);
+		}
+		fuse_reply_none(req);
+	} else {
+		fuse_reply_none(req);
+	}
+}
+
+static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_file_info *fip = NULL;
+	struct fuse_file_info fi;
+
+	if (req->f->conn.proto_minor >= 9) {
+		struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
+
+		if (arg->getattr_flags & FUSE_GETATTR_FH) {
+			memset(&fi, 0, sizeof(fi));
+			fi.fh = arg->fh;
+			fi.fh_old = fi.fh;
+			fip = &fi;
+		}
+	}
+
+	if (req->f->op.getattr)
+		req->f->op.getattr(req, nodeid, fip);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
+
+	if (req->f->op.setattr) {
+		struct fuse_file_info *fi = NULL;
+		struct fuse_file_info fi_store;
+		struct stat stbuf;
+		memset(&stbuf, 0, sizeof(stbuf));
+		convert_attr(arg, &stbuf);
+		if (arg->valid & FATTR_FH) {
+			arg->valid &= ~FATTR_FH;
+			memset(&fi_store, 0, sizeof(fi_store));
+			fi = &fi_store;
+			fi->fh = arg->fh;
+			fi->fh_old = fi->fh;
+		}
+		arg->valid &=
+			FUSE_SET_ATTR_MODE	|
+			FUSE_SET_ATTR_UID	|
+			FUSE_SET_ATTR_GID	|
+			FUSE_SET_ATTR_SIZE	|
+			FUSE_SET_ATTR_ATIME	|
+			FUSE_SET_ATTR_MTIME	|
+			FUSE_SET_ATTR_ATIME_NOW	|
+			FUSE_SET_ATTR_MTIME_NOW;
+
+		req->f->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
+	} else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
+
+	if (req->f->op.access)
+		req->f->op.access(req, nodeid, arg->mask);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	(void) inarg;
+
+	if (req->f->op.readlink)
+		req->f->op.readlink(req, nodeid);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
+	char *name = PARAM(arg);
+
+	if (req->f->conn.proto_minor >= 12)
+		req->ctx.umask = arg->umask;
+	else
+		name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
+
+	if (req->f->op.mknod)
+		req->f->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
+
+	if (req->f->conn.proto_minor >= 12)
+		req->ctx.umask = arg->umask;
+
+	if (req->f->op.mkdir)
+		req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	char *name = (char *) inarg;
+
+	if (req->f->op.unlink)
+		req->f->op.unlink(req, nodeid, name);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	char *name = (char *) inarg;
+
+	if (req->f->op.rmdir)
+		req->f->op.rmdir(req, nodeid, name);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	char *name = (char *) inarg;
+	char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
+
+	if (req->f->op.symlink)
+		req->f->op.symlink(req, linkname, nodeid, name);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
+	char *oldname = PARAM(arg);
+	char *newname = oldname + strlen(oldname) + 1;
+
+	if (req->f->op.rename)
+		req->f->op.rename(req, nodeid, oldname, arg->newdir, newname);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
+
+	if (req->f->op.link)
+		req->f->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
+
+	if (req->f->op.create) {
+		struct fuse_file_info fi;
+		char *name = PARAM(arg);
+
+		memset(&fi, 0, sizeof(fi));
+		fi.flags = arg->flags;
+
+		if (req->f->conn.proto_minor >= 12)
+			req->ctx.umask = arg->umask;
+		else
+			name = (char *) inarg + sizeof(struct fuse_open_in);
+
+		req->f->op.create(req, nodeid, name, arg->mode, &fi);
+	} else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.flags = arg->flags;
+
+	if (req->f->op.open)
+		req->f->op.open(req, nodeid, &fi);
+	else
+		fuse_reply_open(req, &fi);
+}
+
+static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
+
+	if (req->f->op.read) {
+		struct fuse_file_info fi;
+
+		memset(&fi, 0, sizeof(fi));
+		fi.fh = arg->fh;
+		fi.fh_old = fi.fh;
+		if (req->f->conn.proto_minor >= 9) {
+			fi.lock_owner = arg->lock_owner;
+			fi.flags = arg->flags;
+		}
+		req->f->op.read(req, nodeid, arg->size, arg->offset, &fi);
+	} else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
+	struct fuse_file_info fi;
+	char *param;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+	fi.writepage = arg->write_flags & 1;
+
+	if (req->f->conn.proto_minor < 9) {
+		param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
+	} else {
+		fi.lock_owner = arg->lock_owner;
+		fi.flags = arg->flags;
+		param = PARAM(arg);
+	}
+
+	if (req->f->op.write)
+		req->f->op.write(req, nodeid, param, arg->size,
+				 arg->offset, &fi);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
+			 const struct fuse_buf *ibuf)
+{
+	struct fuse_ll *f = req->f;
+	struct fuse_bufvec bufv = {
+		.buf[0] = *ibuf,
+		.count = 1,
+	};
+	struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+	fi.writepage = arg->write_flags & 1;
+
+	if (req->f->conn.proto_minor < 9) {
+		bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
+		bufv.buf[0].size -= sizeof(struct fuse_in_header) +
+			FUSE_COMPAT_WRITE_IN_SIZE;
+		assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
+	} else {
+		fi.lock_owner = arg->lock_owner;
+		fi.flags = arg->flags;
+		if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
+			bufv.buf[0].mem = PARAM(arg);
+
+		bufv.buf[0].size -= sizeof(struct fuse_in_header) +
+			sizeof(struct fuse_write_in);
+	}
+	if (bufv.buf[0].size < arg->size) {
+		fprintf(stderr, "fuse: do_write_buf: buffer size too small\n");
+		fuse_reply_err(req, EIO);
+		goto out;
+	}
+	bufv.buf[0].size = arg->size;
+
+	req->f->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
+
+out:
+	/* Need to reset the pipe if ->write_buf() didn't consume all data */
+	if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
+		fuse_ll_clear_pipe(f);
+}
+
+static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+	fi.flush = 1;
+	if (req->f->conn.proto_minor >= 7)
+		fi.lock_owner = arg->lock_owner;
+
+	if (req->f->op.flush)
+		req->f->op.flush(req, nodeid, &fi);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.flags = arg->flags;
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+	if (req->f->conn.proto_minor >= 8) {
+		fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
+		fi.lock_owner = arg->lock_owner;
+	}
+	if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
+		fi.flock_release = 1;
+		fi.lock_owner = arg->lock_owner;
+	}
+
+	if (req->f->op.release)
+		req->f->op.release(req, nodeid, &fi);
+	else
+		fuse_reply_err(req, 0);
+}
+
+static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+
+	if (req->f->op.fsync)
+		req->f->op.fsync(req, nodeid, arg->fsync_flags & 1, &fi);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.flags = arg->flags;
+
+	if (req->f->op.opendir)
+		req->f->op.opendir(req, nodeid, &fi);
+	else
+		fuse_reply_open(req, &fi);
+}
+
+static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+
+	if (req->f->op.readdir)
+		req->f->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.flags = arg->flags;
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+
+	if (req->f->op.releasedir)
+		req->f->op.releasedir(req, nodeid, &fi);
+	else
+		fuse_reply_err(req, 0);
+}
+
+static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+
+	if (req->f->op.fsyncdir)
+		req->f->op.fsyncdir(req, nodeid, arg->fsync_flags & 1, &fi);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	(void) nodeid;
+	(void) inarg;
+
+	if (req->f->op.statfs)
+		req->f->op.statfs(req, nodeid);
+	else {
+		struct statvfs buf = {
+			.f_namemax = 255,
+			.f_bsize = 512,
+		};
+		fuse_reply_statfs(req, &buf);
+	}
+}
+
+static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
+	char *name = PARAM(arg);
+	char *value = name + strlen(name) + 1;
+
+	if (req->f->op.setxattr)
+		req->f->op.setxattr(req, nodeid, name, value, arg->size,
+				    arg->flags);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
+
+	if (req->f->op.getxattr)
+		req->f->op.getxattr(req, nodeid, PARAM(arg), arg->size);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
+
+	if (req->f->op.listxattr)
+		req->f->op.listxattr(req, nodeid, arg->size);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	char *name = (char *) inarg;
+
+	if (req->f->op.removexattr)
+		req->f->op.removexattr(req, nodeid, name);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void convert_fuse_file_lock(struct fuse_file_lock *fl,
+				   struct flock *flock)
+{
+	memset(flock, 0, sizeof(struct flock));
+	flock->l_type = fl->type;
+	flock->l_whence = SEEK_SET;
+	flock->l_start = fl->start;
+	if (fl->end == OFFSET_MAX)
+		flock->l_len = 0;
+	else
+		flock->l_len = fl->end - fl->start + 1;
+	flock->l_pid = fl->pid;
+}
+
+static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
+	struct fuse_file_info fi;
+	struct flock flock;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.lock_owner = arg->owner;
+
+	convert_fuse_file_lock(&arg->lk, &flock);
+	if (req->f->op.getlk)
+		req->f->op.getlk(req, nodeid, &fi, &flock);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
+			    const void *inarg, int sleep)
+{
+	struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
+	struct fuse_file_info fi;
+	struct flock flock;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.lock_owner = arg->owner;
+
+	if (arg->lk_flags & FUSE_LK_FLOCK) {
+		int op = 0;
+
+		switch (arg->lk.type) {
+		case F_RDLCK:
+			op = LOCK_SH;
+			break;
+		case F_WRLCK:
+			op = LOCK_EX;
+			break;
+		case F_UNLCK:
+			op = LOCK_UN;
+			break;
+		}
+		if (!sleep)
+			op |= LOCK_NB;
+
+		if (req->f->op.flock)
+			req->f->op.flock(req, nodeid, &fi, op);
+		else
+			fuse_reply_err(req, ENOSYS);
+	} else {
+		convert_fuse_file_lock(&arg->lk, &flock);
+		if (req->f->op.setlk)
+			req->f->op.setlk(req, nodeid, &fi, &flock, sleep);
+		else
+			fuse_reply_err(req, ENOSYS);
+	}
+}
+
+static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	do_setlk_common(req, nodeid, inarg, 0);
+}
+
+static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	do_setlk_common(req, nodeid, inarg, 1);
+}
+
+static int find_interrupted(struct fuse_ll *f, struct fuse_req *req)
+{
+	struct fuse_req *curr;
+
+	for (curr = f->list.next; curr != &f->list; curr = curr->next) {
+		if (curr->unique == req->u.i.unique) {
+			fuse_interrupt_func_t func;
+			void *data;
+
+			curr->ctr++;
+			pthread_mutex_unlock(&f->lock);
+
+			/* Ugh, ugly locking */
+			pthread_mutex_lock(&curr->lock);
+			pthread_mutex_lock(&f->lock);
+			curr->interrupted = 1;
+			func = curr->u.ni.func;
+			data = curr->u.ni.data;
+			pthread_mutex_unlock(&f->lock);
+			if (func)
+				func(curr, data);
+			pthread_mutex_unlock(&curr->lock);
+
+			pthread_mutex_lock(&f->lock);
+			curr->ctr--;
+			if (!curr->ctr)
+				destroy_req(curr);
+
+			return 1;
+		}
+	}
+	for (curr = f->interrupts.next; curr != &f->interrupts;
+	     curr = curr->next) {
+		if (curr->u.i.unique == req->u.i.unique)
+			return 1;
+	}
+	return 0;
+}
+
+static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
+	struct fuse_ll *f = req->f;
+
+	(void) nodeid;
+	if (f->debug)
+		fprintf(stderr, "INTERRUPT: %llu\n",
+			(unsigned long long) arg->unique);
+
+	req->u.i.unique = arg->unique;
+
+	pthread_mutex_lock(&f->lock);
+	if (find_interrupted(f, req))
+		destroy_req(req);
+	else
+		list_add_req(req, &f->interrupts);
+	pthread_mutex_unlock(&f->lock);
+}
+
+static struct fuse_req *check_interrupt(struct fuse_ll *f, struct fuse_req *req)
+{
+	struct fuse_req *curr;
+
+	for (curr = f->interrupts.next; curr != &f->interrupts;
+	     curr = curr->next) {
+		if (curr->u.i.unique == req->unique) {
+			req->interrupted = 1;
+			list_del_req(curr);
+			free(curr);
+			return NULL;
+		}
+	}
+	curr = f->interrupts.next;
+	if (curr != &f->interrupts) {
+		list_del_req(curr);
+		list_init_req(curr);
+		return curr;
+	} else
+		return NULL;
+}
+
+static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
+
+	if (req->f->op.bmap)
+		req->f->op.bmap(req, nodeid, arg->blocksize, arg->block);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
+	unsigned int flags = arg->flags;
+	void *in_buf = arg->in_size ? PARAM(arg) : NULL;
+	struct fuse_file_info fi;
+
+	if (flags & FUSE_IOCTL_DIR &&
+	    !(req->f->conn.want & FUSE_CAP_IOCTL_DIR)) {
+		fuse_reply_err(req, ENOTTY);
+		return;
+	}
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+
+	if (sizeof(void *) == 4 && req->f->conn.proto_minor >= 16 &&
+	    !(flags & FUSE_IOCTL_32BIT)) {
+		req->ioctl_64bit = 1;
+	}
+
+	if (req->f->op.ioctl)
+		req->f->op.ioctl(req, nodeid, arg->cmd,
+				 (void *)(uintptr_t)arg->arg, &fi, flags,
+				 in_buf, arg->in_size, arg->out_size);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
+{
+	free(ph);
+}
+
+static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+	fi.fh_old = fi.fh;
+
+	if (req->f->op.poll) {
+		struct fuse_pollhandle *ph = NULL;
+
+		if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
+			ph = malloc(sizeof(struct fuse_pollhandle));
+			if (ph == NULL) {
+				fuse_reply_err(req, ENOMEM);
+				return;
+			}
+			ph->kh = arg->kh;
+			ph->ch = req->ch;
+			ph->f = req->f;
+		}
+
+		req->f->op.poll(req, nodeid, &fi, ph);
+	} else {
+		fuse_reply_err(req, ENOSYS);
+	}
+}
+
+static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
+	struct fuse_file_info fi;
+
+	memset(&fi, 0, sizeof(fi));
+	fi.fh = arg->fh;
+
+	if (req->f->op.fallocate)
+		req->f->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
+	else
+		fuse_reply_err(req, ENOSYS);
+}
+
+static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
+	struct fuse_init_out outarg;
+	struct fuse_ll *f = req->f;
+	size_t bufsize = fuse_chan_bufsize(req->ch);
+
+	(void) nodeid;
+	if (f->debug) {
+		fprintf(stderr, "INIT: %u.%u\n", arg->major, arg->minor);
+		if (arg->major == 7 && arg->minor >= 6) {
+			fprintf(stderr, "flags=0x%08x\n", arg->flags);
+			fprintf(stderr, "max_readahead=0x%08x\n",
+				arg->max_readahead);
+		}
+	}
+	f->conn.proto_major = arg->major;
+	f->conn.proto_minor = arg->minor;
+	f->conn.capable = 0;
+	f->conn.want = 0;
+
+	memset(&outarg, 0, sizeof(outarg));
+	outarg.major = FUSE_KERNEL_VERSION;
+	outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+
+	if (arg->major < 7) {
+		fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n",
+			arg->major, arg->minor);
+		fuse_reply_err(req, EPROTO);
+		return;
+	}
+
+	if (arg->major > 7) {
+		/* Wait for a second INIT request with a 7.X version */
+		send_reply_ok(req, &outarg, sizeof(outarg));
+		return;
+	}
+
+	if (arg->minor >= 6) {
+		if (f->conn.async_read)
+			f->conn.async_read = arg->flags & FUSE_ASYNC_READ;
+		if (arg->max_readahead < f->conn.max_readahead)
+			f->conn.max_readahead = arg->max_readahead;
+		if (arg->flags & FUSE_ASYNC_READ)
+			f->conn.capable |= FUSE_CAP_ASYNC_READ;
+		if (arg->flags & FUSE_POSIX_LOCKS)
+			f->conn.capable |= FUSE_CAP_POSIX_LOCKS;
+		if (arg->flags & FUSE_ATOMIC_O_TRUNC)
+			f->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
+		if (arg->flags & FUSE_EXPORT_SUPPORT)
+			f->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
+		if (arg->flags & FUSE_BIG_WRITES)
+			f->conn.capable |= FUSE_CAP_BIG_WRITES;
+		if (arg->flags & FUSE_DONT_MASK)
+			f->conn.capable |= FUSE_CAP_DONT_MASK;
+		if (arg->flags & FUSE_FLOCK_LOCKS)
+			f->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
+	} else {
+		f->conn.async_read = 0;
+		f->conn.max_readahead = 0;
+	}
+
+	if (req->f->conn.proto_minor >= 14) {
+#ifdef HAVE_SPLICE
+#ifdef HAVE_VMSPLICE
+		f->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
+		if (f->splice_write)
+			f->conn.want |= FUSE_CAP_SPLICE_WRITE;
+		if (f->splice_move)
+			f->conn.want |= FUSE_CAP_SPLICE_MOVE;
+#endif
+		f->conn.capable |= FUSE_CAP_SPLICE_READ;
+		if (f->splice_read)
+			f->conn.want |= FUSE_CAP_SPLICE_READ;
+#endif
+	}
+	if (req->f->conn.proto_minor >= 18)
+		f->conn.capable |= FUSE_CAP_IOCTL_DIR;
+
+	if (f->atomic_o_trunc)
+		f->conn.want |= FUSE_CAP_ATOMIC_O_TRUNC;
+	if (f->op.getlk && f->op.setlk && !f->no_remote_posix_lock)
+		f->conn.want |= FUSE_CAP_POSIX_LOCKS;
+	if (f->op.flock && !f->no_remote_flock)
+		f->conn.want |= FUSE_CAP_FLOCK_LOCKS;
+	if (f->big_writes)
+		f->conn.want |= FUSE_CAP_BIG_WRITES;
+
+	if (bufsize < FUSE_MIN_READ_BUFFER) {
+		fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
+			bufsize);
+		bufsize = FUSE_MIN_READ_BUFFER;
+	}
+
+	bufsize -= 4096;
+	if (bufsize < f->conn.max_write)
+		f->conn.max_write = bufsize;
+
+	f->got_init = 1;
+	if (f->op.init)
+		f->op.init(f->userdata, &f->conn);
+
+	if (f->no_splice_read)
+		f->conn.want &= ~FUSE_CAP_SPLICE_READ;
+	if (f->no_splice_write)
+		f->conn.want &= ~FUSE_CAP_SPLICE_WRITE;
+	if (f->no_splice_move)
+		f->conn.want &= ~FUSE_CAP_SPLICE_MOVE;
+
+	if (f->conn.async_read || (f->conn.want & FUSE_CAP_ASYNC_READ))
+		outarg.flags |= FUSE_ASYNC_READ;
+	if (f->conn.want & FUSE_CAP_POSIX_LOCKS)
+		outarg.flags |= FUSE_POSIX_LOCKS;
+	if (f->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
+		outarg.flags |= FUSE_ATOMIC_O_TRUNC;
+	if (f->conn.want & FUSE_CAP_EXPORT_SUPPORT)
+		outarg.flags |= FUSE_EXPORT_SUPPORT;
+	if (f->conn.want & FUSE_CAP_BIG_WRITES)
+		outarg.flags |= FUSE_BIG_WRITES;
+	if (f->conn.want & FUSE_CAP_DONT_MASK)
+		outarg.flags |= FUSE_DONT_MASK;
+	if (f->conn.want & FUSE_CAP_FLOCK_LOCKS)
+		outarg.flags |= FUSE_FLOCK_LOCKS;
+	outarg.max_readahead = f->conn.max_readahead;
+	outarg.max_write = f->conn.max_write;
+	if (f->conn.proto_minor >= 13) {
+		if (f->conn.max_background >= (1 << 16))
+			f->conn.max_background = (1 << 16) - 1;
+		if (f->conn.congestion_threshold > f->conn.max_background)
+			f->conn.congestion_threshold = f->conn.max_background;
+		if (!f->conn.congestion_threshold) {
+			f->conn.congestion_threshold =
+				f->conn.max_background * 3 / 4;
+		}
+
+		outarg.max_background = f->conn.max_background;
+		outarg.congestion_threshold = f->conn.congestion_threshold;
+	}
+
+	if (f->debug) {
+		fprintf(stderr, "   INIT: %u.%u\n", outarg.major, outarg.minor);
+		fprintf(stderr, "   flags=0x%08x\n", outarg.flags);
+		fprintf(stderr, "   max_readahead=0x%08x\n",
+			outarg.max_readahead);
+		fprintf(stderr, "   max_write=0x%08x\n", outarg.max_write);
+		fprintf(stderr, "   max_background=%i\n",
+			outarg.max_background);
+		fprintf(stderr, "   congestion_threshold=%i\n",
+		        outarg.congestion_threshold);
+	}
+
+	send_reply_ok(req, &outarg, arg->minor < 5 ? 8 : sizeof(outarg));
+}
+
+static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+	struct fuse_ll *f = req->f;
+
+	(void) nodeid;
+	(void) inarg;
+
+	f->got_destroy = 1;
+	if (f->op.destroy)
+		f->op.destroy(f->userdata);
+
+	send_reply_ok(req, NULL, 0);
+}
+
+static void list_del_nreq(struct fuse_notify_req *nreq)
+{
+	struct fuse_notify_req *prev = nreq->prev;
+	struct fuse_notify_req *next = nreq->next;
+	prev->next = next;
+	next->prev = prev;
+}
+
+static void list_add_nreq(struct fuse_notify_req *nreq,
+			  struct fuse_notify_req *next)
+{
+	struct fuse_notify_req *prev = next->prev;
+	nreq->next = next;
+	nreq->prev = prev;
+	prev->next = nreq;
+	next->prev = nreq;
+}
+
+static void list_init_nreq(struct fuse_notify_req *nreq)
+{
+	nreq->next = nreq;
+	nreq->prev = nreq;
+}
+
+static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
+			    const void *inarg, const struct fuse_buf *buf)
+{
+	struct fuse_ll *f = req->f;
+	struct fuse_notify_req *nreq;
+	struct fuse_notify_req *head;
+
+	pthread_mutex_lock(&f->lock);
+	head = &f->notify_list;
+	for (nreq = head->next; nreq != head; nreq = nreq->next) {
+		if (nreq->unique == req->unique) {
+			list_del_nreq(nreq);
+			break;
+		}
+	}
+	pthread_mutex_unlock(&f->lock);
+
+	if (nreq != head)
+		nreq->reply(nreq, req, nodeid, inarg, buf);
+}
+
+static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch,
+			   int notify_code, struct iovec *iov, int count)
+{
+	struct fuse_out_header out;
+
+	if (!f->got_init)
+		return -ENOTCONN;
+
+	out.unique = 0;
+	out.error = notify_code;
+	iov[0].iov_base = &out;
+	iov[0].iov_len = sizeof(struct fuse_out_header);
+
+	return fuse_send_msg(f, ch, iov, count);
+}
+
+int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
+{
+	if (ph != NULL) {
+		struct fuse_notify_poll_wakeup_out outarg;
+		struct iovec iov[2];
+
+		outarg.kh = ph->kh;
+
+		iov[1].iov_base = &outarg;
+		iov[1].iov_len = sizeof(outarg);
+
+		return send_notify_iov(ph->f, ph->ch, FUSE_NOTIFY_POLL, iov, 2);
+	} else {
+		return 0;
+	}
+}
+
+int fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch, fuse_ino_t ino,
+                                     off64_t off, off64_t len)
+{
+	struct fuse_notify_inval_inode_out outarg;
+	struct fuse_ll *f;
+	struct iovec iov[2];
+
+	if (!ch)
+		return -EINVAL;
+
+	f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+	if (!f)
+		return -ENODEV;
+
+	outarg.ino = ino;
+	outarg.off = off;
+	outarg.len = len;
+
+	iov[1].iov_base = &outarg;
+	iov[1].iov_len = sizeof(outarg);
+
+	return send_notify_iov(f, ch, FUSE_NOTIFY_INVAL_INODE, iov, 2);
+}
+
+int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
+                                     const char *name, size_t namelen)
+{
+	struct fuse_notify_inval_entry_out outarg;
+	struct fuse_ll *f;
+	struct iovec iov[3];
+
+	if (!ch)
+		return -EINVAL;
+
+	f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+	if (!f)
+		return -ENODEV;
+
+	outarg.parent = parent;
+	outarg.namelen = namelen;
+	outarg.padding = 0;
+
+	iov[1].iov_base = &outarg;
+	iov[1].iov_len = sizeof(outarg);
+	iov[2].iov_base = (void *)name;
+	iov[2].iov_len = namelen + 1;
+
+	return send_notify_iov(f, ch, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
+}
+
+int fuse_lowlevel_notify_delete(struct fuse_chan *ch,
+				fuse_ino_t parent, fuse_ino_t child,
+				const char *name, size_t namelen)
+{
+	struct fuse_notify_delete_out outarg;
+	struct fuse_ll *f;
+	struct iovec iov[3];
+
+	if (!ch)
+		return -EINVAL;
+
+	f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+	if (!f)
+		return -ENODEV;
+
+	if (f->conn.proto_minor < 18)
+		return -ENOSYS;
+
+	outarg.parent = parent;
+	outarg.child = child;
+	outarg.namelen = namelen;
+	outarg.padding = 0;
+
+	iov[1].iov_base = &outarg;
+	iov[1].iov_len = sizeof(outarg);
+	iov[2].iov_base = (void *)name;
+	iov[2].iov_len = namelen + 1;
+
+	return send_notify_iov(f, ch, FUSE_NOTIFY_DELETE, iov, 3);
+}
+
+int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
+			       off64_t offset, struct fuse_bufvec *bufv,
+			       enum fuse_buf_copy_flags flags)
+{
+	struct fuse_out_header out;
+	struct fuse_notify_store_out outarg;
+	struct fuse_ll *f;
+	struct iovec iov[3];
+	size_t size = fuse_buf_size(bufv);
+	int res;
+
+	if (!ch)
+		return -EINVAL;
+
+	f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+	if (!f)
+		return -ENODEV;
+
+	if (f->conn.proto_minor < 15)
+		return -ENOSYS;
+
+	out.unique = 0;
+	out.error = FUSE_NOTIFY_STORE;
+
+	outarg.nodeid = ino;
+	outarg.offset = offset;
+	outarg.size = size;
+
+	iov[0].iov_base = &out;
+	iov[0].iov_len = sizeof(out);
+	iov[1].iov_base = &outarg;
+	iov[1].iov_len = sizeof(outarg);
+
+	res = fuse_send_data_iov(f, ch, iov, 2, bufv, flags);
+	if (res > 0)
+		res = -res;
+
+	return res;
+}
+
+struct fuse_retrieve_req {
+	struct fuse_notify_req nreq;
+	void *cookie;
+};
+
+static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
+				   fuse_req_t req, fuse_ino_t ino,
+				   const void *inarg,
+				   const struct fuse_buf *ibuf)
+{
+	struct fuse_ll *f = req->f;
+	struct fuse_retrieve_req *rreq =
+		container_of(nreq, struct fuse_retrieve_req, nreq);
+	const struct fuse_notify_retrieve_in *arg = inarg;
+	struct fuse_bufvec bufv = {
+		.buf[0] = *ibuf,
+		.count = 1,
+	};
+
+	if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
+		bufv.buf[0].mem = PARAM(arg);
+
+	bufv.buf[0].size -= sizeof(struct fuse_in_header) +
+		sizeof(struct fuse_notify_retrieve_in);
+
+	if (bufv.buf[0].size < arg->size) {
+		fprintf(stderr, "fuse: retrieve reply: buffer size too small\n");
+		fuse_reply_none(req);
+		goto out;
+	}
+	bufv.buf[0].size = arg->size;
+
+	if (req->f->op.retrieve_reply) {
+		req->f->op.retrieve_reply(req, rreq->cookie, ino,
+					  arg->offset, &bufv);
+	} else {
+		fuse_reply_none(req);
+	}
+out:
+	free(rreq);
+	if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
+		fuse_ll_clear_pipe(f);
+}
+
+int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino,
+				  size_t size, off64_t offset, void *cookie)
+{
+	struct fuse_notify_retrieve_out outarg;
+	struct fuse_ll *f;
+	struct iovec iov[2];
+	struct fuse_retrieve_req *rreq;
+	int err;
+
+	if (!ch)
+		return -EINVAL;
+
+	f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+	if (!f)
+		return -ENODEV;
+
+	if (f->conn.proto_minor < 15)
+		return -ENOSYS;
+
+	rreq = malloc(sizeof(*rreq));
+	if (rreq == NULL)
+		return -ENOMEM;
+
+	pthread_mutex_lock(&f->lock);
+	rreq->cookie = cookie;
+	rreq->nreq.unique = f->notify_ctr++;
+	rreq->nreq.reply = fuse_ll_retrieve_reply;
+	list_add_nreq(&rreq->nreq, &f->notify_list);
+	pthread_mutex_unlock(&f->lock);
+
+	outarg.notify_unique = rreq->nreq.unique;
+	outarg.nodeid = ino;
+	outarg.offset = offset;
+	outarg.size = size;
+
+	iov[1].iov_base = &outarg;
+	iov[1].iov_len = sizeof(outarg);
+
+	err = send_notify_iov(f, ch, FUSE_NOTIFY_RETRIEVE, iov, 2);
+	if (err) {
+		pthread_mutex_lock(&f->lock);
+		list_del_nreq(&rreq->nreq);
+		pthread_mutex_unlock(&f->lock);
+		free(rreq);
+	}
+
+	return err;
+}
+
+void *fuse_req_userdata(fuse_req_t req)
+{
+	return req->f->userdata;
+}
+
+const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
+{
+	return &req->ctx;
+}
+
+/*
+ * The size of fuse_ctx got extended, so need to be careful about
+ * incompatibility (i.e. a new binary cannot work with an old
+ * library).
+ */
+const struct fuse_ctx *fuse_req_ctx_compat24(fuse_req_t req);
+const struct fuse_ctx *fuse_req_ctx_compat24(fuse_req_t req)
+{
+	return fuse_req_ctx(req);
+}
+#ifndef __NetBSD__
+FUSE_SYMVER(".symver fuse_req_ctx_compat24,fuse_req_ctx@FUSE_2.4");
+#endif
+
+
+void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
+			     void *data)
+{
+	pthread_mutex_lock(&req->lock);
+	pthread_mutex_lock(&req->f->lock);
+	req->u.ni.func = func;
+	req->u.ni.data = data;
+	pthread_mutex_unlock(&req->f->lock);
+	if (req->interrupted && func)
+		func(req, data);
+	pthread_mutex_unlock(&req->lock);
+}
+
+int fuse_req_interrupted(fuse_req_t req)
+{
+	int interrupted;
+
+	pthread_mutex_lock(&req->f->lock);
+	interrupted = req->interrupted;
+	pthread_mutex_unlock(&req->f->lock);
+
+	return interrupted;
+}
+
+static struct {
+	void (*func)(fuse_req_t, fuse_ino_t, const void *);
+	const char *name;
+} fuse_ll_ops[] = {
+	[FUSE_LOOKUP]	   = { do_lookup,      "LOOKUP"	     },
+	[FUSE_FORGET]	   = { do_forget,      "FORGET"	     },
+	[FUSE_GETATTR]	   = { do_getattr,     "GETATTR"     },
+	[FUSE_SETATTR]	   = { do_setattr,     "SETATTR"     },
+	[FUSE_READLINK]	   = { do_readlink,    "READLINK"    },
+	[FUSE_SYMLINK]	   = { do_symlink,     "SYMLINK"     },
+	[FUSE_MKNOD]	   = { do_mknod,       "MKNOD"	     },
+	[FUSE_MKDIR]	   = { do_mkdir,       "MKDIR"	     },
+	[FUSE_UNLINK]	   = { do_unlink,      "UNLINK"	     },
+	[FUSE_RMDIR]	   = { do_rmdir,       "RMDIR"	     },
+	[FUSE_RENAME]	   = { do_rename,      "RENAME"	     },
+	[FUSE_LINK]	   = { do_link,	       "LINK"	     },
+	[FUSE_OPEN]	   = { do_open,	       "OPEN"	     },
+	[FUSE_READ]	   = { do_read,	       "READ"	     },
+	[FUSE_WRITE]	   = { do_write,       "WRITE"	     },
+	[FUSE_STATFS]	   = { do_statfs,      "STATFS"	     },
+	[FUSE_RELEASE]	   = { do_release,     "RELEASE"     },
+	[FUSE_FSYNC]	   = { do_fsync,       "FSYNC"	     },
+	[FUSE_SETXATTR]	   = { do_setxattr,    "SETXATTR"    },
+	[FUSE_GETXATTR]	   = { do_getxattr,    "GETXATTR"    },
+	[FUSE_LISTXATTR]   = { do_listxattr,   "LISTXATTR"   },
+	[FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
+	[FUSE_FLUSH]	   = { do_flush,       "FLUSH"	     },
+	[FUSE_INIT]	   = { do_init,	       "INIT"	     },
+	[FUSE_OPENDIR]	   = { do_opendir,     "OPENDIR"     },
+	[FUSE_READDIR]	   = { do_readdir,     "READDIR"     },
+	[FUSE_RELEASEDIR]  = { do_releasedir,  "RELEASEDIR"  },
+	[FUSE_FSYNCDIR]	   = { do_fsyncdir,    "FSYNCDIR"    },
+	[FUSE_GETLK]	   = { do_getlk,       "GETLK"	     },
+	[FUSE_SETLK]	   = { do_setlk,       "SETLK"	     },
+	[FUSE_SETLKW]	   = { do_setlkw,      "SETLKW"	     },
+	[FUSE_ACCESS]	   = { do_access,      "ACCESS"	     },
+	[FUSE_CREATE]	   = { do_create,      "CREATE"	     },
+	[FUSE_INTERRUPT]   = { do_interrupt,   "INTERRUPT"   },
+	[FUSE_BMAP]	   = { do_bmap,	       "BMAP"	     },
+	[FUSE_IOCTL]	   = { do_ioctl,       "IOCTL"	     },
+	[FUSE_POLL]	   = { do_poll,        "POLL"	     },
+	[FUSE_FALLOCATE]   = { do_fallocate,   "FALLOCATE"   },
+	[FUSE_DESTROY]	   = { do_destroy,     "DESTROY"     },
+	[FUSE_NOTIFY_REPLY] = { (void *) 1,    "NOTIFY_REPLY" },
+	[FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
+	[CUSE_INIT]	   = { cuse_lowlevel_init, "CUSE_INIT"   },
+};
+
+#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
+
+static const char *opname(enum fuse_opcode opcode)
+{
+	if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
+		return "???";
+	else
+		return fuse_ll_ops[opcode].name;
+}
+
+static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
+				  struct fuse_bufvec *src)
+{
+	int res = fuse_buf_copy(dst, src, 0);
+	if (res < 0) {
+		fprintf(stderr, "fuse: copy from pipe: %s\n", strerror(-res));
+		return res;
+	}
+	if (res < fuse_buf_size(dst)) {
+		fprintf(stderr, "fuse: copy from pipe: short read\n");
+		return -1;
+	}
+	return 0;
+}
+
+static void fuse_ll_process_buf(void *data, const struct fuse_buf *buf,
+				struct fuse_chan *ch)
+{
+	struct fuse_ll *f = (struct fuse_ll *) data;
+	const size_t write_header_size = sizeof(struct fuse_in_header) +
+		sizeof(struct fuse_write_in);
+	struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
+	struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
+	struct fuse_in_header *in;
+	const void *inarg;
+	struct fuse_req *req;
+	void *mbuf = NULL;
+	int err;
+	int res;
+
+	if (buf->flags & FUSE_BUF_IS_FD) {
+		if (buf->size < tmpbuf.buf[0].size)
+			tmpbuf.buf[0].size = buf->size;
+
+		mbuf = malloc(tmpbuf.buf[0].size);
+		if (mbuf == NULL) {
+			fprintf(stderr, "fuse: failed to allocate header\n");
+			goto clear_pipe;
+		}
+		tmpbuf.buf[0].mem = mbuf;
+
+		res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
+		if (res < 0)
+			goto clear_pipe;
+
+		in = mbuf;
+	} else {
+		in = buf->mem;
+	}
+
+	if (f->debug) {
+		fprintf(stderr,
+			"unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %zu, pid: %u\n",
+			(unsigned long long) in->unique,
+			opname((enum fuse_opcode) in->opcode), in->opcode,
+			(unsigned long) in->nodeid, buf->size, in->pid);
+	}
+
+	req = fuse_ll_alloc_req(f);
+	if (req == NULL) {
+		struct fuse_out_header out = {
+			.unique = in->unique,
+			.error = -ENOMEM,
+		};
+		struct iovec iov = {
+			.iov_base = &out,
+			.iov_len = sizeof(struct fuse_out_header),
+		};
+
+		fuse_send_msg(f, ch, &iov, 1);
+		goto clear_pipe;
+	}
+
+	req->unique = in->unique;
+	req->ctx.uid = in->uid;
+	req->ctx.gid = in->gid;
+	req->ctx.pid = in->pid;
+	req->ch = ch;
+
+	err = EIO;
+	if (!f->got_init) {
+		enum fuse_opcode expected;
+
+		expected = f->cuse_data ? CUSE_INIT : FUSE_INIT;
+		if (in->opcode != expected)
+			goto reply_err;
+	} else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
+		goto reply_err;
+
+	err = EACCES;
+	if (f->allow_root && in->uid != f->owner && in->uid != 0 &&
+		 in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
+		 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
+		 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
+		 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
+		 in->opcode != FUSE_NOTIFY_REPLY)
+		goto reply_err;
+
+	err = ENOSYS;
+	if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
+		goto reply_err;
+	if (in->opcode != FUSE_INTERRUPT) {
+		struct fuse_req *intr;
+		pthread_mutex_lock(&f->lock);
+		intr = check_interrupt(f, req);
+		list_add_req(req, &f->list);
+		pthread_mutex_unlock(&f->lock);
+		if (intr)
+			fuse_reply_err(intr, EAGAIN);
+	}
+
+	if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
+	    (in->opcode != FUSE_WRITE || !f->op.write_buf) &&
+	    in->opcode != FUSE_NOTIFY_REPLY) {
+		void *newmbuf;
+
+		err = ENOMEM;
+		newmbuf = realloc(mbuf, buf->size);
+		if (newmbuf == NULL)
+			goto reply_err;
+		mbuf = newmbuf;
+
+		tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
+		tmpbuf.buf[0].mem = mbuf + write_header_size;
+
+		res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
+		err = -res;
+		if (res < 0)
+			goto reply_err;
+
+		in = mbuf;
+	}
+
+	inarg = (void *) &in[1];
+	if (in->opcode == FUSE_WRITE && f->op.write_buf)
+		do_write_buf(req, in->nodeid, inarg, buf);
+	else if (in->opcode == FUSE_NOTIFY_REPLY)
+		do_notify_reply(req, in->nodeid, inarg, buf);
+	else
+		fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
+
+out_free:
+	free(mbuf);
+	return;
+
+reply_err:
+	fuse_reply_err(req, err);
+clear_pipe:
+	if (buf->flags & FUSE_BUF_IS_FD)
+		fuse_ll_clear_pipe(f);
+	goto out_free;
+}
+
+static void fuse_ll_process(void *data, const char *buf, size_t len,
+			    struct fuse_chan *ch)
+{
+	struct fuse_buf fbuf = {
+		.mem = (void *) buf,
+		.size = len,
+	};
+
+	fuse_ll_process_buf(data, &fbuf, ch);
+}
+
+enum {
+	KEY_HELP,
+	KEY_VERSION,
+};
+
+static const struct fuse_opt fuse_ll_opts[] = {
+	{ "debug", offsetof(struct fuse_ll, debug), 1 },
+	{ "-d", offsetof(struct fuse_ll, debug), 1 },
+	{ "allow_root", offsetof(struct fuse_ll, allow_root), 1 },
+	{ "max_write=%u", offsetof(struct fuse_ll, conn.max_write), 0 },
+	{ "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 },
+	{ "max_background=%u", offsetof(struct fuse_ll, conn.max_background), 0 },
+	{ "congestion_threshold=%u",
+	  offsetof(struct fuse_ll, conn.congestion_threshold), 0 },
+	{ "async_read", offsetof(struct fuse_ll, conn.async_read), 1 },
+	{ "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 },
+	{ "atomic_o_trunc", offsetof(struct fuse_ll, atomic_o_trunc), 1},
+	{ "no_remote_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
+	{ "no_remote_lock", offsetof(struct fuse_ll, no_remote_flock), 1},
+	{ "no_remote_flock", offsetof(struct fuse_ll, no_remote_flock), 1},
+	{ "no_remote_posix_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
+	{ "big_writes", offsetof(struct fuse_ll, big_writes), 1},
+	{ "splice_write", offsetof(struct fuse_ll, splice_write), 1},
+	{ "no_splice_write", offsetof(struct fuse_ll, no_splice_write), 1},
+	{ "splice_move", offsetof(struct fuse_ll, splice_move), 1},
+	{ "no_splice_move", offsetof(struct fuse_ll, no_splice_move), 1},
+	{ "splice_read", offsetof(struct fuse_ll, splice_read), 1},
+	{ "no_splice_read", offsetof(struct fuse_ll, no_splice_read), 1},
+	FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
+	FUSE_OPT_KEY("-h", KEY_HELP),
+	FUSE_OPT_KEY("--help", KEY_HELP),
+	FUSE_OPT_KEY("-V", KEY_VERSION),
+	FUSE_OPT_KEY("--version", KEY_VERSION),
+	FUSE_OPT_END
+};
+
+static void fuse_ll_version(void)
+{
+	fprintf(stderr, "using FUSE kernel interface version %i.%i\n",
+		FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
+}
+
+static void fuse_ll_help(void)
+{
+	fprintf(stderr,
+"    -o max_write=N         set maximum size of write requests\n"
+"    -o max_readahead=N     set maximum readahead\n"
+"    -o max_background=N    set number of maximum background requests\n"
+"    -o congestion_threshold=N  set kernel's congestion threshold\n"
+"    -o async_read          perform reads asynchronously (default)\n"
+"    -o sync_read           perform reads synchronously\n"
+"    -o atomic_o_trunc      enable atomic open+truncate support\n"
+"    -o big_writes          enable larger than 4kB writes\n"
+"    -o no_remote_lock      disable remote file locking\n"
+"    -o no_remote_flock     disable remote file locking (BSD)\n"
+"    -o no_remote_posix_lock disable remove file locking (POSIX)\n"
+"    -o [no_]splice_write   use splice to write to the fuse device\n"
+"    -o [no_]splice_move    move data while splicing to the fuse device\n"
+"    -o [no_]splice_read    use splice to read from the fuse device\n"
+);
+}
+
+static int fuse_ll_opt_proc(void *data, const char *arg, int key,
+			    struct fuse_args *outargs)
+{
+	(void) data; (void) outargs;
+
+	switch (key) {
+	case KEY_HELP:
+		fuse_ll_help();
+		break;
+
+	case KEY_VERSION:
+		fuse_ll_version();
+		break;
+
+	default:
+		fprintf(stderr, "fuse: unknown option `%s'\n", arg);
+	}
+
+	return -1;
+}
+
+int fuse_lowlevel_is_lib_option(const char *opt)
+{
+	return fuse_opt_match(fuse_ll_opts, opt);
+}
+
+static void fuse_ll_destroy(void *data)
+{
+	struct fuse_ll *f = (struct fuse_ll *) data;
+	struct fuse_ll_pipe *llp;
+
+	if (f->got_init && !f->got_destroy) {
+		if (f->op.destroy)
+			f->op.destroy(f->userdata);
+	}
+	llp = pthread_getspecific(f->pipe_key);
+	if (llp != NULL)
+		fuse_ll_pipe_free(llp);
+	pthread_key_delete(f->pipe_key);
+	pthread_mutex_destroy(&f->lock);
+	free(f->cuse_data);
+	free(f);
+}
+
+static void fuse_ll_pipe_destructor(void *data)
+{
+	struct fuse_ll_pipe *llp = data;
+	fuse_ll_pipe_free(llp);
+}
+
+#ifdef HAVE_SPLICE
+static int fuse_ll_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+			       struct fuse_chan **chp)
+{
+	struct fuse_chan *ch = *chp;
+	struct fuse_ll *f = fuse_session_data(se);
+	size_t bufsize = buf->size;
+	struct fuse_ll_pipe *llp;
+	struct fuse_buf tmpbuf;
+	int err;
+	int res;
+
+	if (f->conn.proto_minor < 14 || !(f->conn.want & FUSE_CAP_SPLICE_READ))
+		goto fallback;
+
+	llp = fuse_ll_get_pipe(f);
+	if (llp == NULL)
+		goto fallback;
+
+	if (llp->size < bufsize) {
+		if (llp->can_grow) {
+			res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
+			if (res == -1) {
+				llp->can_grow = 0;
+				goto fallback;
+			}
+			llp->size = res;
+		}
+		if (llp->size < bufsize)
+			goto fallback;
+	}
+
+	res = splice(fuse_chan_fd(ch), NULL, llp->pipe[1], NULL, bufsize, 0);
+	err = errno;
+
+	if (fuse_session_exited(se))
+		return 0;
+
+	if (res == -1) {
+		if (err == ENODEV) {
+			fuse_session_exit(se);
+			return 0;
+		}
+		if (err != EINTR && err != EAGAIN)
+			perror("fuse: splice from device");
+		return -err;
+	}
+
+	if (res < sizeof(struct fuse_in_header)) {
+		fprintf(stderr, "short splice from fuse device\n");
+		return -EIO;
+	}
+
+	tmpbuf = (struct fuse_buf) {
+		.size = res,
+		.flags = FUSE_BUF_IS_FD,
+		.fd = llp->pipe[0],
+	};
+
+	/*
+	 * Don't bother with zero copy for small requests.
+	 * fuse_loop_mt() needs to check for FORGET so this more than
+	 * just an optimization.
+	 */
+	if (res < sizeof(struct fuse_in_header) +
+	    sizeof(struct fuse_write_in) + pagesize) {
+		struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
+		struct fuse_bufvec dst = { .buf[0] = *buf, .count = 1 };
+
+		res = fuse_buf_copy(&dst, &src, 0);
+		if (res < 0) {
+			fprintf(stderr, "fuse: copy from pipe: %s\n",
+				strerror(-res));
+			fuse_ll_clear_pipe(f);
+			return res;
+		}
+		if (res < tmpbuf.size) {
+			fprintf(stderr, "fuse: copy from pipe: short read\n");
+			fuse_ll_clear_pipe(f);
+			return -EIO;
+		}
+		buf->size = tmpbuf.size;
+		return buf->size;
+	}
+
+	*buf = tmpbuf;
+
+	return res;
+
+fallback:
+	res = fuse_chan_recv(chp, buf->mem, bufsize);
+	if (res <= 0)
+		return res;
+
+	buf->size = res;
+
+	return res;
+}
+#else
+static int fuse_ll_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+			       struct fuse_chan **chp)
+{
+	(void) se;
+
+	int res = fuse_chan_recv(chp, buf->mem, buf->size);
+	if (res <= 0)
+		return res;
+
+	buf->size = res;
+
+	return res;
+}
+#endif
+
+
+/*
+ * always call fuse_lowlevel_new_common() internally, to work around a
+ * misfeature in the FreeBSD runtime linker, which links the old
+ * version of a symbol to internal references.
+ */
+struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
+					      const struct fuse_lowlevel_ops *op,
+					      size_t op_size, void *userdata)
+{
+	int err;
+	struct fuse_ll *f;
+	struct fuse_session *se;
+	struct fuse_session_ops sop = {
+		.process = fuse_ll_process,
+		.destroy = fuse_ll_destroy,
+	};
+
+	if (sizeof(struct fuse_lowlevel_ops) < op_size) {
+		fprintf(stderr, "fuse: warning: library too old, some operations may not work\n");
+		op_size = sizeof(struct fuse_lowlevel_ops);
+	}
+
+	f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
+	if (f == NULL) {
+		fprintf(stderr, "fuse: failed to allocate fuse object\n");
+		goto out;
+	}
+
+	f->conn.async_read = 1;
+	f->conn.max_write = UINT_MAX;
+	f->conn.max_readahead = UINT_MAX;
+	f->atomic_o_trunc = 0;
+	list_init_req(&f->list);
+	list_init_req(&f->interrupts);
+	list_init_nreq(&f->notify_list);
+	f->notify_ctr = 1;
+	fuse_mutex_init(&f->lock);
+
+	err = pthread_key_create(&f->pipe_key, fuse_ll_pipe_destructor);
+	if (err) {
+		fprintf(stderr, "fuse: failed to create thread specific key: %s\n",
+			strerror(err));
+		goto out_free;
+	}
+
+	if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
+		goto out_key_destroy;
+
+	if (f->debug)
+		fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
+
+	memcpy(&f->op, op, op_size);
+	f->owner = getuid();
+	f->userdata = userdata;
+
+	se = fuse_session_new(&sop, f);
+	if (!se)
+		goto out_key_destroy;
+
+	se->receive_buf = fuse_ll_receive_buf;
+	se->process_buf = fuse_ll_process_buf;
+
+	return se;
+
+out_key_destroy:
+	pthread_key_delete(f->pipe_key);
+out_free:
+	pthread_mutex_destroy(&f->lock);
+	free(f);
+out:
+	return NULL;
+}
+
+
+struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
+				       const struct fuse_lowlevel_ops *op,
+				       size_t op_size, void *userdata)
+{
+	return fuse_lowlevel_new_common(args, op, op_size, userdata);
+}
+
+#ifdef linux
+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
+{
+	char *buf;
+	size_t bufsize = 1024;
+	char path[128];
+	int ret;
+	int fd;
+	unsigned long pid = req->ctx.pid;
+	char *s;
+
+	sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
+
+retry:
+	buf = malloc(bufsize);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	ret = -EIO;
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		goto out_free;
+
+	ret = read(fd, buf, bufsize);
+	close(fd);
+	if (ret == -1) {
+		ret = -EIO;
+		goto out_free;
+	}
+
+	if (ret == bufsize) {
+		free(buf);
+		bufsize *= 4;
+		goto retry;
+	}
+
+	ret = -EIO;
+	s = strstr(buf, "\nGroups:");
+	if (s == NULL)
+		goto out_free;
+
+	s += 8;
+	ret = 0;
+	while (1) {
+		char *end;
+		unsigned long val = strtoul(s, &end, 0);
+		if (end == s)
+			break;
+
+		s = end;
+		if (ret < size)
+			list[ret] = val;
+		ret++;
+	}
+
+out_free:
+	free(buf);
+	return ret;
+}
+#else /* linux */
+/*
+ * This is currently not implemented on other than Linux...
+ */
+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
+{
+	return -ENOSYS;
+}
+#endif
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+static void fill_open_compat(struct fuse_open_out *arg,
+			     const struct fuse_file_info_compat *f)
+{
+	arg->fh = f->fh;
+	if (f->direct_io)
+		arg->open_flags |= FOPEN_DIRECT_IO;
+	if (f->keep_cache)
+		arg->open_flags |= FOPEN_KEEP_CACHE;
+}
+
+static void convert_statfs_compat(const struct statfs *compatbuf,
+				  struct statvfs *buf)
+{
+	buf->f_bsize	= compatbuf->f_bsize;
+	buf->f_blocks	= compatbuf->f_blocks;
+	buf->f_bfree	= compatbuf->f_bfree;
+	buf->f_bavail	= compatbuf->f_bavail;
+	buf->f_files	= compatbuf->f_files;
+	buf->f_ffree	= compatbuf->f_ffree;
+	buf->f_namemax	= compatbuf->f_namelen;
+}
+
+int fuse_reply_open_compat(fuse_req_t req,
+			   const struct fuse_file_info_compat *f)
+{
+	struct fuse_open_out arg;
+
+	memset(&arg, 0, sizeof(arg));
+	fill_open_compat(&arg, f);
+	return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf)
+{
+	struct statvfs newbuf;
+
+	memset(&newbuf, 0, sizeof(newbuf));
+	convert_statfs_compat(stbuf, &newbuf);
+
+	return fuse_reply_statfs(req, &newbuf);
+}
+
+struct fuse_session *fuse_lowlevel_new_compat(const char *opts,
+				const struct fuse_lowlevel_ops_compat *op,
+				size_t op_size, void *userdata)
+{
+	struct fuse_session *se;
+	struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+
+	if (opts &&
+	    (fuse_opt_add_arg(&args, "") == -1 ||
+	     fuse_opt_add_arg(&args, "-o") == -1 ||
+	     fuse_opt_add_arg(&args, opts) == -1)) {
+		fuse_opt_free_args(&args);
+		return NULL;
+	}
+	se = fuse_lowlevel_new(&args, (const struct fuse_lowlevel_ops *) op,
+			       op_size, userdata);
+	fuse_opt_free_args(&args);
+
+	return se;
+}
+
+struct fuse_ll_compat_conf {
+	unsigned max_read;
+	int set_max_read;
+};
+
+static const struct fuse_opt fuse_ll_opts_compat[] = {
+	{ "max_read=", offsetof(struct fuse_ll_compat_conf, set_max_read), 1 },
+	{ "max_read=%u", offsetof(struct fuse_ll_compat_conf, max_read), 0 },
+	FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
+	FUSE_OPT_END
+};
+
+int fuse_sync_compat_args(struct fuse_args *args)
+{
+	struct fuse_ll_compat_conf conf;
+
+	memset(&conf, 0, sizeof(conf));
+	if (fuse_opt_parse(args, &conf, fuse_ll_opts_compat, NULL) == -1)
+		return -1;
+
+	if (fuse_opt_insert_arg(args, 1, "-osync_read"))
+		return -1;
+
+	if (conf.set_max_read) {
+		char tmpbuf[64];
+
+		sprintf(tmpbuf, "-omax_readahead=%u", conf.max_read);
+		if (fuse_opt_insert_arg(args, 1, tmpbuf) == -1)
+			return -1;
+	}
+	return 0;
+}
+
+FUSE_SYMVER(".symver fuse_reply_statfs_compat,fuse_reply_statfs@FUSE_2.4");
+FUSE_SYMVER(".symver fuse_reply_open_compat,fuse_reply_open@FUSE_2.4");
+FUSE_SYMVER(".symver fuse_lowlevel_new_compat,fuse_lowlevel_new@FUSE_2.4");
+
+#else /* __FreeBSD__ || __NetBSD__ */
+
+int fuse_sync_compat_args(struct fuse_args *args)
+{
+	(void) args;
+	return 0;
+}
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
+				const struct fuse_lowlevel_ops_compat25 *op,
+				size_t op_size, void *userdata)
+{
+	if (fuse_sync_compat_args(args) == -1)
+		return NULL;
+
+	return fuse_lowlevel_new_common(args,
+					(const struct fuse_lowlevel_ops *) op,
+					op_size, userdata);
+}
+
+FUSE_SYMVER(".symver fuse_lowlevel_new_compat25,fuse_lowlevel_new@FUSE_2.5");
diff --git a/fuse/fuse_misc.h b/fuse/fuse_misc.h
new file mode 100644
index 0000000..eedf0e0
--- /dev/null
+++ b/fuse/fuse_misc.h
@@ -0,0 +1,57 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#include "config.h"
+#include <pthread.h>
+
+/*
+  Versioned symbols cannot be used in some cases because it
+    - confuse the dynamic linker in uClibc
+    - not supported on MacOSX (in MachO binary format)
+*/
+#if (!defined(__UCLIBC__) && !defined(__APPLE__))
+#define FUSE_SYMVER(x) __asm__(x)
+#else
+#define FUSE_SYMVER(x)
+#endif
+
+#ifndef USE_UCLIBC
+#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL)
+#else
+/* Is this hack still needed? */
+static inline void fuse_mutex_init(pthread_mutex_t *mut)
+{
+	pthread_mutexattr_t attr;
+	pthread_mutexattr_init(&attr);
+	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
+	pthread_mutex_init(mut, &attr);
+	pthread_mutexattr_destroy(&attr);
+}
+#endif
+
+#ifdef HAVE_STRUCT_STAT_ST_ATIM
+/* Linux */
+#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
+#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
+#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
+#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val)
+#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val)
+#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
+/* FreeBSD */
+#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
+#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
+#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
+#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val)
+#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val)
+#else
+#define ST_ATIM_NSEC(stbuf) 0
+#define ST_CTIM_NSEC(stbuf) 0
+#define ST_MTIM_NSEC(stbuf) 0
+#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0)
+#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0)
+#endif
diff --git a/fuse/fuse_mt.c b/fuse/fuse_mt.c
new file mode 100644
index 0000000..f6dbe71
--- /dev/null
+++ b/fuse/fuse_mt.c
@@ -0,0 +1,122 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_lowlevel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+
+struct procdata {
+	struct fuse *f;
+	struct fuse_chan *prevch;
+	struct fuse_session *prevse;
+	fuse_processor_t proc;
+	void *data;
+};
+
+static void mt_session_proc(void *data, const char *buf, size_t len,
+			    struct fuse_chan *ch)
+{
+	struct procdata *pd = (struct procdata *) data;
+	struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
+
+	(void) len;
+	(void) ch;
+	pd->proc(pd->f, cmd, pd->data);
+}
+
+static void mt_session_exit(void *data, int val)
+{
+	struct procdata *pd = (struct procdata *) data;
+	if (val)
+		fuse_session_exit(pd->prevse);
+	else
+		fuse_session_reset(pd->prevse);
+}
+
+static int mt_session_exited(void *data)
+{
+	struct procdata *pd = (struct procdata *) data;
+	return fuse_session_exited(pd->prevse);
+}
+
+static int mt_chan_receive(struct fuse_chan **chp, char *buf, size_t size)
+{
+	struct fuse_cmd *cmd;
+	struct procdata *pd = (struct procdata *) fuse_chan_data(*chp);
+
+	assert(size >= sizeof(cmd));
+
+	cmd = fuse_read_cmd(pd->f);
+	if (cmd == NULL)
+		return 0;
+
+	*(struct fuse_cmd **) buf = cmd;
+
+	return sizeof(cmd);
+}
+
+int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
+{
+	int res;
+	struct procdata pd;
+	struct fuse_session *prevse = fuse_get_session(f);
+	struct fuse_session *se;
+	struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL);
+	struct fuse_chan *ch;
+	struct fuse_session_ops sop = {
+		.exit = mt_session_exit,
+		.exited = mt_session_exited,
+		.process = mt_session_proc,
+	};
+	struct fuse_chan_ops cop = {
+		.receive = mt_chan_receive,
+	};
+
+	pd.f = f;
+	pd.prevch = prevch;
+	pd.prevse = prevse;
+	pd.proc = proc;
+	pd.data = data;
+
+	se = fuse_session_new(&sop, &pd);
+	if (se == NULL)
+		return -1;
+
+	ch = fuse_chan_new(&cop, fuse_chan_fd(prevch),
+			   sizeof(struct fuse_cmd *), &pd);
+	if (ch == NULL) {
+		fuse_session_destroy(se);
+		return -1;
+	}
+	fuse_session_add_chan(se, ch);
+	res = fuse_session_loop_mt(se);
+	fuse_session_destroy(se);
+	return res;
+}
+
+int fuse_loop_mt(struct fuse *f)
+{
+	if (f == NULL)
+		return -1;
+
+	int res = fuse_start_cleanup_thread(f);
+	if (res)
+		return -1;
+
+	res = fuse_session_loop_mt(fuse_get_session(f));
+	fuse_stop_cleanup_thread(f);
+	return res;
+}
+
+FUSE_SYMVER(".symver fuse_loop_mt_proc,__fuse_loop_mt@");
diff --git a/fuse/fuse_opt.c b/fuse/fuse_opt.c
new file mode 100644
index 0000000..a2118ce
--- /dev/null
+++ b/fuse/fuse_opt.c
@@ -0,0 +1,426 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+struct fuse_opt_context {
+	void *data;
+	const struct fuse_opt *opt;
+	fuse_opt_proc_t proc;
+	int argctr;
+	int argc;
+	char **argv;
+	struct fuse_args outargs;
+	char *opts;
+	int nonopt;
+};
+
+void fuse_opt_free_args(struct fuse_args *args)
+{
+	if (args) {
+		if (args->argv && args->allocated) {
+			int i;
+			for (i = 0; i < args->argc; i++)
+				free(args->argv[i]);
+			free(args->argv);
+		}
+		args->argc = 0;
+		args->argv = NULL;
+		args->allocated = 0;
+	}
+}
+
+static int alloc_failed(void)
+{
+	fprintf(stderr, "fuse: memory allocation failed\n");
+	return -1;
+}
+
+int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
+{
+	char **newargv;
+	char *newarg;
+
+	assert(!args->argv || args->allocated);
+
+	newarg = strdup(arg);
+	if (!newarg)
+		return alloc_failed();
+
+	newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
+	if (!newargv) {
+		free(newarg);
+		return alloc_failed();
+	}
+
+	args->argv = newargv;
+	args->allocated = 1;
+	args->argv[args->argc++] = newarg;
+	args->argv[args->argc] = NULL;
+	return 0;
+}
+
+static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
+				      const char *arg)
+{
+	assert(pos <= args->argc);
+	if (fuse_opt_add_arg(args, arg) == -1)
+		return -1;
+
+	if (pos != args->argc - 1) {
+		char *newarg = args->argv[args->argc - 1];
+		memmove(&args->argv[pos + 1], &args->argv[pos],
+			sizeof(char *) * (args->argc - pos - 1));
+		args->argv[pos] = newarg;
+	}
+	return 0;
+}
+
+int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
+{
+	return fuse_opt_insert_arg_common(args, pos, arg);
+}
+
+int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos,
+			       const char *arg);
+int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos, const char *arg)
+{
+	return fuse_opt_insert_arg_common(args, pos, arg);
+}
+
+static int next_arg(struct fuse_opt_context *ctx, const char *opt)
+{
+	if (ctx->argctr + 1 >= ctx->argc) {
+		fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
+		return -1;
+	}
+	ctx->argctr++;
+	return 0;
+}
+
+static int add_arg(struct fuse_opt_context *ctx, const char *arg)
+{
+	return fuse_opt_add_arg(&ctx->outargs, arg);
+}
+
+static int add_opt_common(char **opts, const char *opt, int esc)
+{
+	unsigned oldlen = *opts ? strlen(*opts) : 0;
+	char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
+
+	if (!d)
+		return alloc_failed();
+
+	*opts = d;
+	if (oldlen) {
+		d += oldlen;
+		*d++ = ',';
+	}
+
+	for (; *opt; opt++) {
+		if (esc && (*opt == ',' || *opt == '\\'))
+			*d++ = '\\';
+		*d++ = *opt;
+	}
+	*d = '\0';
+
+	return 0;
+}
+
+int fuse_opt_add_opt(char **opts, const char *opt)
+{
+	return add_opt_common(opts, opt, 0);
+}
+
+int fuse_opt_add_opt_escaped(char **opts, const char *opt)
+{
+	return add_opt_common(opts, opt, 1);
+}
+
+static int add_opt(struct fuse_opt_context *ctx, const char *opt)
+{
+	return add_opt_common(&ctx->opts, opt, 1);
+}
+
+static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
+		     int iso)
+{
+	if (key == FUSE_OPT_KEY_DISCARD)
+		return 0;
+
+	if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
+		int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
+		if (res == -1 || !res)
+			return res;
+	}
+	if (iso)
+		return add_opt(ctx, arg);
+	else
+		return add_arg(ctx, arg);
+}
+
+static int match_template(const char *t, const char *arg, unsigned *sepp)
+{
+	int arglen = strlen(arg);
+	const char *sep = strchr(t, '=');
+	sep = sep ? sep : strchr(t, ' ');
+	if (sep && (!sep[1] || sep[1] == '%')) {
+		int tlen = sep - t;
+		if (sep[0] == '=')
+			tlen ++;
+		if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
+			*sepp = sep - t;
+			return 1;
+		}
+	}
+	if (strcmp(t, arg) == 0) {
+		*sepp = 0;
+		return 1;
+	}
+	return 0;
+}
+
+static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
+				       const char *arg, unsigned *sepp)
+{
+	for (; opt && opt->templ; opt++)
+		if (match_template(opt->templ, arg, sepp))
+			return opt;
+	return NULL;
+}
+
+int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
+{
+	unsigned dummy;
+	return find_opt(opts, opt, &dummy) ? 1 : 0;
+}
+
+static int process_opt_param(void *var, const char *format, const char *param,
+			     const char *arg)
+{
+	assert(format[0] == '%');
+	if (format[1] == 's') {
+		char *copy = strdup(param);
+		if (!copy)
+			return alloc_failed();
+
+		*(char **) var = copy;
+	} else {
+		if (sscanf(param, format, var) != 1) {
+			fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int process_opt(struct fuse_opt_context *ctx,
+		       const struct fuse_opt *opt, unsigned sep,
+		       const char *arg, int iso)
+{
+	if (opt->offset == -1U) {
+		if (call_proc(ctx, arg, opt->value, iso) == -1)
+			return -1;
+	} else {
+		void *var = ctx->data + opt->offset;
+		if (sep && opt->templ[sep + 1]) {
+			const char *param = arg + sep;
+			if (opt->templ[sep] == '=')
+				param ++;
+			if (process_opt_param(var, opt->templ + sep + 1,
+					      param, arg) == -1)
+				return -1;
+		} else
+			*(int *)var = opt->value;
+	}
+	return 0;
+}
+
+static int process_opt_sep_arg(struct fuse_opt_context *ctx,
+			       const struct fuse_opt *opt, unsigned sep,
+			       const char *arg, int iso)
+{
+	int res;
+	char *newarg;
+	char *param;
+
+	if (next_arg(ctx, arg) == -1)
+		return -1;
+
+	param = ctx->argv[ctx->argctr];
+	newarg = malloc(sep + strlen(param) + 1);
+	if (!newarg)
+		return alloc_failed();
+
+	memcpy(newarg, arg, sep);
+	strcpy(newarg + sep, param);
+	res = process_opt(ctx, opt, sep, newarg, iso);
+	free(newarg);
+
+	return res;
+}
+
+static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
+{
+	unsigned sep;
+	const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
+	if (opt) {
+		for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
+			int res;
+			if (sep && opt->templ[sep] == ' ' && !arg[sep])
+				res = process_opt_sep_arg(ctx, opt, sep, arg,
+							  iso);
+			else
+				res = process_opt(ctx, opt, sep, arg, iso);
+			if (res == -1)
+				return -1;
+		}
+		return 0;
+	} else
+		return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
+}
+
+static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
+{
+	char *s = opts;
+	char *d = s;
+	int end = 0;
+
+	while (!end) {
+		if (*s == '\0')
+			end = 1;
+		if (*s == ',' || end) {
+			int res;
+
+			*d = '\0';
+			res = process_gopt(ctx, opts, 1);
+			if (res == -1)
+				return -1;
+			d = opts;
+		} else {
+			if (s[0] == '\\' && s[1] != '\0') {
+				s++;
+				if (s[0] >= '0' && s[0] <= '3' &&
+				    s[1] >= '0' && s[1] <= '7' &&
+				    s[2] >= '0' && s[2] <= '7') {
+					*d++ = (s[0] - '0') * 0100 +
+						(s[1] - '0') * 0010 +
+						(s[2] - '0');
+					s += 2;
+				} else {
+					*d++ = *s;
+				}
+			} else {
+				*d++ = *s;
+			}
+		}
+		s++;
+	}
+
+	return 0;
+}
+
+static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
+{
+	int res;
+	char *copy = strdup(opts);
+
+	if (!copy) {
+		fprintf(stderr, "fuse: memory allocation failed\n");
+		return -1;
+	}
+	res = process_real_option_group(ctx, copy);
+	free(copy);
+	return res;
+}
+
+static int process_one(struct fuse_opt_context *ctx, const char *arg)
+{
+	if (ctx->nonopt || arg[0] != '-')
+		return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
+	else if (arg[1] == 'o') {
+		if (arg[2])
+			return process_option_group(ctx, arg + 2);
+		else {
+			if (next_arg(ctx, arg) == -1)
+				return -1;
+
+			return process_option_group(ctx,
+						    ctx->argv[ctx->argctr]);
+		}
+	} else if (arg[1] == '-' && !arg[2]) {
+		if (add_arg(ctx, arg) == -1)
+			return -1;
+		ctx->nonopt = ctx->outargs.argc;
+		return 0;
+	} else
+		return process_gopt(ctx, arg, 0);
+}
+
+static int opt_parse(struct fuse_opt_context *ctx)
+{
+	if (ctx->argc) {
+		if (add_arg(ctx, ctx->argv[0]) == -1)
+			return -1;
+	}
+
+	for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
+		if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
+			return -1;
+
+	if (ctx->opts) {
+		if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
+		    fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
+			return -1;
+	}
+
+	/* If option separator ("--") is the last argument, remove it */
+	if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
+	    strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
+		free(ctx->outargs.argv[ctx->outargs.argc - 1]);
+		ctx->outargs.argv[--ctx->outargs.argc] = NULL;
+	}
+
+	return 0;
+}
+
+int fuse_opt_parse(struct fuse_args *args, void *data,
+		   const struct fuse_opt opts[], fuse_opt_proc_t proc)
+{
+	int res;
+	struct fuse_opt_context ctx = {
+		.data = data,
+		.opt = opts,
+		.proc = proc,
+	};
+
+	if (!args || !args->argv || !args->argc)
+		return 0;
+
+	ctx.argc = args->argc;
+	ctx.argv = args->argv;
+
+	res = opt_parse(&ctx);
+	if (res != -1) {
+		struct fuse_args tmp = *args;
+		*args = ctx.outargs;
+		ctx.outargs = tmp;
+	}
+	free(ctx.opts);
+	fuse_opt_free_args(&ctx.outargs);
+	return res;
+}
+
+/* This symbol version was mistakenly added to the version script */
+FUSE_SYMVER(".symver fuse_opt_insert_arg_compat,fuse_opt_insert_arg@FUSE_2.5");
diff --git a/fuse/fuse_session.c b/fuse/fuse_session.c
new file mode 100644
index 0000000..c55f250
--- /dev/null
+++ b/fuse/fuse_session.c
@@ -0,0 +1,233 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_common_compat.h"
+#include "fuse_lowlevel_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+struct fuse_chan {
+	struct fuse_chan_ops op;
+
+	struct fuse_session *se;
+
+	int fd;
+
+	size_t bufsize;
+
+	void *data;
+
+	int compat;
+};
+
+struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
+{
+	struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
+	if (se == NULL) {
+		fprintf(stderr, "fuse: failed to allocate session\n");
+		return NULL;
+	}
+
+	memset(se, 0, sizeof(*se));
+	se->op = *op;
+	se->data = data;
+
+	return se;
+}
+
+void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
+{
+	assert(se->ch == NULL);
+	assert(ch->se == NULL);
+	se->ch = ch;
+	ch->se = se;
+}
+
+void fuse_session_remove_chan(struct fuse_chan *ch)
+{
+	struct fuse_session *se = ch->se;
+	if (se) {
+		assert(se->ch == ch);
+		se->ch = NULL;
+		ch->se = NULL;
+	}
+}
+
+struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
+					 struct fuse_chan *ch)
+{
+	assert(ch == NULL || ch == se->ch);
+	if (ch == NULL)
+		return se->ch;
+	else
+		return NULL;
+}
+
+void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
+			  struct fuse_chan *ch)
+{
+	se->op.process(se->data, buf, len, ch);
+}
+
+void fuse_session_process_buf(struct fuse_session *se,
+			      const struct fuse_buf *buf, struct fuse_chan *ch)
+{
+	if (se->process_buf) {
+		se->process_buf(se->data, buf, ch);
+	} else {
+		assert(!(buf->flags & FUSE_BUF_IS_FD));
+		fuse_session_process(se->data, buf->mem, buf->size, ch);
+	}
+}
+
+int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+			     struct fuse_chan **chp)
+{
+	int res;
+
+	if (se->receive_buf) {
+		res = se->receive_buf(se, buf, chp);
+	} else {
+		res = fuse_chan_recv(chp, buf->mem, buf->size);
+		if (res > 0)
+			buf->size = res;
+	}
+
+	return res;
+}
+
+
+void fuse_session_destroy(struct fuse_session *se)
+{
+	if (se->op.destroy)
+		se->op.destroy(se->data);
+	if (se->ch != NULL)
+		fuse_chan_destroy(se->ch);
+	free(se);
+}
+
+void fuse_session_exit(struct fuse_session *se)
+{
+	if (se->op.exit)
+		se->op.exit(se->data, 1);
+	se->exited = 1;
+}
+
+void fuse_session_reset(struct fuse_session *se)
+{
+	if (se->op.exit)
+		se->op.exit(se->data, 0);
+	se->exited = 0;
+}
+
+int fuse_session_exited(struct fuse_session *se)
+{
+	if (se->op.exited)
+		return se->op.exited(se->data);
+	else
+		return se->exited;
+}
+
+void *fuse_session_data(struct fuse_session *se)
+{
+	return se->data;
+}
+
+static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd,
+					      size_t bufsize, void *data,
+					      int compat)
+{
+	struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
+	if (ch == NULL) {
+		fprintf(stderr, "fuse: failed to allocate channel\n");
+		return NULL;
+	}
+
+	memset(ch, 0, sizeof(*ch));
+	ch->op = *op;
+	ch->fd = fd;
+	ch->bufsize = bufsize;
+	ch->data = data;
+	ch->compat = compat;
+
+	return ch;
+}
+
+struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
+				size_t bufsize, void *data)
+{
+	return fuse_chan_new_common(op, fd, bufsize, data, 0);
+}
+
+struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
+					 int fd, size_t bufsize, void *data)
+{
+	return fuse_chan_new_common((struct fuse_chan_ops *) op, fd, bufsize,
+				    data, 24);
+}
+
+int fuse_chan_fd(struct fuse_chan *ch)
+{
+	return ch->fd;
+}
+
+size_t fuse_chan_bufsize(struct fuse_chan *ch)
+{
+	return ch->bufsize;
+}
+
+void *fuse_chan_data(struct fuse_chan *ch)
+{
+	return ch->data;
+}
+
+struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
+{
+	return ch->se;
+}
+
+int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)
+{
+	struct fuse_chan *ch = *chp;
+	if (ch->compat)
+		return ((struct fuse_chan_ops_compat24 *) &ch->op)
+			->receive(ch, buf, size);
+	else
+		return ch->op.receive(chp, buf, size);
+}
+
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
+{
+	int res;
+
+	res = fuse_chan_recv(&ch, buf, size);
+	return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
+}
+
+int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
+{
+	return ch->op.send(ch, iov, count);
+}
+
+void fuse_chan_destroy(struct fuse_chan *ch)
+{
+	fuse_session_remove_chan(ch);
+	if (ch->op.destroy)
+		ch->op.destroy(ch);
+	free(ch);
+}
+
+#ifndef __FreeBSD__
+FUSE_SYMVER(".symver fuse_chan_new_compat24,fuse_chan_new@FUSE_2.4");
+#endif
diff --git a/fuse/fuse_signals.c b/fuse/fuse_signals.c
new file mode 100644
index 0000000..88ac39e
--- /dev/null
+++ b/fuse/fuse_signals.c
@@ -0,0 +1,72 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#include "fuse_lowlevel.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+static struct fuse_session *fuse_instance;
+
+static void exit_handler(int sig)
+{
+	(void) sig;
+	if (fuse_instance)
+		fuse_session_exit(fuse_instance);
+}
+
+static int set_one_signal_handler(int sig, void (*handler)(int))
+{
+	struct sigaction sa;
+	struct sigaction old_sa;
+
+	memset(&sa, 0, sizeof(struct sigaction));
+	sa.sa_handler = handler;
+	sigemptyset(&(sa.sa_mask));
+	sa.sa_flags = 0;
+
+	if (sigaction(sig, NULL, &old_sa) == -1) {
+		perror("fuse: cannot get old signal handler");
+		return -1;
+	}
+
+	if (old_sa.sa_handler == SIG_DFL &&
+	    sigaction(sig, &sa, NULL) == -1) {
+		perror("fuse: cannot set signal handler");
+		return -1;
+	}
+	return 0;
+}
+
+int fuse_set_signal_handlers(struct fuse_session *se)
+{
+	if (set_one_signal_handler(SIGHUP, exit_handler) == -1 ||
+	    set_one_signal_handler(SIGINT, exit_handler) == -1 ||
+	    set_one_signal_handler(SIGTERM, exit_handler) == -1 ||
+	    set_one_signal_handler(SIGPIPE, SIG_IGN) == -1)
+		return -1;
+
+	fuse_instance = se;
+	return 0;
+}
+
+void fuse_remove_signal_handlers(struct fuse_session *se)
+{
+	if (fuse_instance != se)
+		fprintf(stderr,
+			"fuse: fuse_remove_signal_handlers: unknown session\n");
+	else
+		fuse_instance = NULL;
+
+	set_one_signal_handler(SIGHUP, SIG_DFL);
+	set_one_signal_handler(SIGINT, SIG_DFL);
+	set_one_signal_handler(SIGTERM, SIG_DFL);
+	set_one_signal_handler(SIGPIPE, SIG_DFL);
+}
+
diff --git a/fuse/fusexmp.c b/fuse/fusexmp.c
new file mode 100644
index 0000000..d4edbc9
--- /dev/null
+++ b/fuse/fusexmp.c
@@ -0,0 +1,385 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
+
+  gcc -Wall `pkg-config fuse --cflags --libs` fusexmp.c -o fusexmp
+*/
+
+#define FUSE_USE_VERSION 26
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef linux
+/* For pread()/pwrite() */
+#define _XOPEN_SOURCE 500
+#endif
+
+#include <fuse.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/time.h>
+#ifdef HAVE_SETXATTR
+#include <sys/xattr.h>
+#endif
+
+static int xmp_getattr(const char *path, struct stat *stbuf)
+{
+	int res;
+
+	res = lstat(path, stbuf);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_access(const char *path, int mask)
+{
+	int res;
+
+	res = access(path, mask);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_readlink(const char *path, char *buf, size_t size)
+{
+	int res;
+
+	res = readlink(path, buf, size - 1);
+	if (res == -1)
+		return -errno;
+
+	buf[res] = '\0';
+	return 0;
+}
+
+
+static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+		       off64_t offset, struct fuse_file_info *fi)
+{
+	DIR *dp;
+	struct dirent *de;
+
+	(void) offset;
+	(void) fi;
+
+	dp = opendir(path);
+	if (dp == NULL)
+		return -errno;
+
+	while ((de = readdir(dp)) != NULL) {
+		struct stat st;
+		memset(&st, 0, sizeof(st));
+		st.st_ino = de->d_ino;
+		st.st_mode = de->d_type << 12;
+		if (filler(buf, de->d_name, &st, 0))
+			break;
+	}
+
+	closedir(dp);
+	return 0;
+}
+
+static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
+{
+	int res;
+
+	/* On Linux this could just be 'mknod(path, mode, rdev)' but this
+	   is more portable */
+	if (S_ISREG(mode)) {
+		res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode);
+		if (res >= 0)
+			res = close(res);
+	} else if (S_ISFIFO(mode))
+		res = mkfifo(path, mode);
+	else
+		res = mknod(path, mode, rdev);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_mkdir(const char *path, mode_t mode)
+{
+	int res;
+
+	res = mkdir(path, mode);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_unlink(const char *path)
+{
+	int res;
+
+	res = unlink(path);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_rmdir(const char *path)
+{
+	int res;
+
+	res = rmdir(path);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_symlink(const char *from, const char *to)
+{
+	int res;
+
+	res = symlink(from, to);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_rename(const char *from, const char *to)
+{
+	int res;
+
+	res = rename(from, to);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_link(const char *from, const char *to)
+{
+	int res;
+
+	res = link(from, to);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_chmod(const char *path, mode_t mode)
+{
+	int res;
+
+	res = chmod(path, mode);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_chown(const char *path, uid_t uid, gid_t gid)
+{
+	int res;
+
+	res = lchown(path, uid, gid);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_truncate(const char *path, off64_t size)
+{
+	int res;
+
+	res = truncate(path, size);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_utimens(const char *path, const struct timespec ts[2])
+{
+	int res;
+	struct timeval tv[2];
+
+	tv[0].tv_sec = ts[0].tv_sec;
+	tv[0].tv_usec = ts[0].tv_nsec / 1000;
+	tv[1].tv_sec = ts[1].tv_sec;
+	tv[1].tv_usec = ts[1].tv_nsec / 1000;
+
+	res = utimes(path, tv);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_open(const char *path, struct fuse_file_info *fi)
+{
+	int res;
+
+	res = open(path, fi->flags);
+	if (res == -1)
+		return -errno;
+
+	close(res);
+	return 0;
+}
+
+static int xmp_read(const char *path, char *buf, size_t size, off64_t offset,
+		    struct fuse_file_info *fi)
+{
+	int fd;
+	int res;
+
+	(void) fi;
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return -errno;
+
+	res = pread(fd, buf, size, offset);
+	if (res == -1)
+		res = -errno;
+
+	close(fd);
+	return res;
+}
+
+static int xmp_write(const char *path, const char *buf, size_t size,
+		     off64_t offset, struct fuse_file_info *fi)
+{
+	int fd;
+	int res;
+
+	(void) fi;
+	fd = open(path, O_WRONLY);
+	if (fd == -1)
+		return -errno;
+
+	res = pwrite(fd, buf, size, offset);
+	if (res == -1)
+		res = -errno;
+
+	close(fd);
+	return res;
+}
+
+static int xmp_statfs(const char *path, struct statvfs *stbuf)
+{
+	int res;
+
+	//res = statvfs(path, stbuf);
+	if (res == -1)
+		return -errno;
+
+	return 0;
+}
+
+static int xmp_release(const char *path, struct fuse_file_info *fi)
+{
+	/* Just a stub.	 This method is optional and can safely be left
+	   unimplemented */
+
+	(void) path;
+	(void) fi;
+	return 0;
+}
+
+static int xmp_fsync(const char *path, int isdatasync,
+		     struct fuse_file_info *fi)
+{
+	/* Just a stub.	 This method is optional and can safely be left
+	   unimplemented */
+
+	(void) path;
+	(void) isdatasync;
+	(void) fi;
+	return 0;
+}
+
+#ifdef HAVE_SETXATTR
+/* xattr operations are optional and can safely be left unimplemented */
+static int xmp_setxattr(const char *path, const char *name, const char *value,
+			size_t size, int flags)
+{
+	int res = lsetxattr(path, name, value, size, flags);
+	if (res == -1)
+		return -errno;
+	return 0;
+}
+
+static int xmp_getxattr(const char *path, const char *name, char *value,
+			size_t size)
+{
+	int res = lgetxattr(path, name, value, size);
+	if (res == -1)
+		return -errno;
+	return res;
+}
+
+static int xmp_listxattr(const char *path, char *list, size_t size)
+{
+	int res = llistxattr(path, list, size);
+	if (res == -1)
+		return -errno;
+	return res;
+}
+
+static int xmp_removexattr(const char *path, const char *name)
+{
+	int res = lremovexattr(path, name);
+	if (res == -1)
+		return -errno;
+	return 0;
+}
+#endif /* HAVE_SETXATTR */
+
+static struct fuse_operations xmp_oper = {
+	.getattr	= xmp_getattr,
+	.access		= xmp_access,
+	.readlink	= xmp_readlink,
+	.readdir	= xmp_readdir,
+	.mknod		= xmp_mknod,
+	.mkdir		= xmp_mkdir,
+	.symlink	= xmp_symlink,
+	.unlink		= xmp_unlink,
+	.rmdir		= xmp_rmdir,
+	.rename		= xmp_rename,
+	.link		= xmp_link,
+	.chmod		= xmp_chmod,
+	.chown		= xmp_chown,
+	.truncate	= xmp_truncate,
+	.utimens	= xmp_utimens,
+	.open		= xmp_open,
+	.read		= xmp_read,
+	.write		= xmp_write,
+	.statfs		= xmp_statfs,
+	.release	= xmp_release,
+	.fsync		= xmp_fsync,
+#ifdef HAVE_SETXATTR
+	.setxattr	= xmp_setxattr,
+	.getxattr	= xmp_getxattr,
+	.listxattr	= xmp_listxattr,
+	.removexattr	= xmp_removexattr,
+#endif
+};
+
+int main(int argc, char *argv[])
+{
+	umask(0);
+	return fuse_main(argc, argv, &xmp_oper, NULL);
+}
diff --git a/fuse/helper.c b/fuse/helper.c
new file mode 100644
index 0000000..ace19dd
--- /dev/null
+++ b/fuse/helper.c
@@ -0,0 +1,478 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_opt.h"
+#include "fuse_lowlevel.h"
+#include "fuse_common_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/param.h>
+
+enum  {
+	KEY_HELP,
+	KEY_HELP_NOHEADER,
+	KEY_VERSION,
+};
+
+struct helper_opts {
+	int singlethread;
+	int foreground;
+	int nodefault_subtype;
+	char *mountpoint;
+};
+
+#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 }
+
+static const struct fuse_opt fuse_helper_opts[] = {
+	FUSE_HELPER_OPT("-d",		foreground),
+	FUSE_HELPER_OPT("debug",	foreground),
+	FUSE_HELPER_OPT("-f",		foreground),
+	FUSE_HELPER_OPT("-s",		singlethread),
+	FUSE_HELPER_OPT("fsname=",	nodefault_subtype),
+	FUSE_HELPER_OPT("subtype=",	nodefault_subtype),
+
+	FUSE_OPT_KEY("-h",		KEY_HELP),
+	FUSE_OPT_KEY("--help",		KEY_HELP),
+	FUSE_OPT_KEY("-ho",		KEY_HELP_NOHEADER),
+	FUSE_OPT_KEY("-V",		KEY_VERSION),
+	FUSE_OPT_KEY("--version",	KEY_VERSION),
+	FUSE_OPT_KEY("-d",		FUSE_OPT_KEY_KEEP),
+	FUSE_OPT_KEY("debug",		FUSE_OPT_KEY_KEEP),
+	FUSE_OPT_KEY("fsname=",		FUSE_OPT_KEY_KEEP),
+	FUSE_OPT_KEY("subtype=",	FUSE_OPT_KEY_KEEP),
+	FUSE_OPT_END
+};
+
+static void usage(const char *progname)
+{
+	fprintf(stderr,
+		"usage: %s mountpoint [options]\n\n", progname);
+	fprintf(stderr,
+		"general options:\n"
+		"    -o opt,[opt...]        mount options\n"
+		"    -h   --help            print help\n"
+		"    -V   --version         print version\n"
+		"\n");
+}
+
+static void helper_help(void)
+{
+	fprintf(stderr,
+		"FUSE options:\n"
+		"    -d   -o debug          enable debug output (implies -f)\n"
+		"    -f                     foreground operation\n"
+		"    -s                     disable multi-threaded operation\n"
+		"\n"
+		);
+}
+
+static void helper_version(void)
+{
+	fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
+}
+
+static int fuse_helper_opt_proc(void *data, const char *arg, int key,
+				struct fuse_args *outargs)
+{
+	struct helper_opts *hopts = data;
+
+	switch (key) {
+	case KEY_HELP:
+		usage(outargs->argv[0]);
+		/* fall through */
+
+	case KEY_HELP_NOHEADER:
+		helper_help();
+		return fuse_opt_add_arg(outargs, "-h");
+
+	case KEY_VERSION:
+		helper_version();
+		return 1;
+
+	case FUSE_OPT_KEY_NONOPT:
+		if (!hopts->mountpoint) {
+			char mountpoint[PATH_MAX];
+			if (realpath(arg, mountpoint) == NULL) {
+				fprintf(stderr,
+					"fuse: bad mount point `%s': %s\n",
+					arg, strerror(errno));
+				return -1;
+			}
+			return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
+		} else {
+			fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
+			return -1;
+		}
+
+	default:
+		return 1;
+	}
+}
+
+static int add_default_subtype(const char *progname, struct fuse_args *args)
+{
+	int res;
+	char *subtype_opt;
+	const char *basename = strrchr(progname, '/');
+	if (basename == NULL)
+		basename = progname;
+	else if (basename[1] != '\0')
+		basename++;
+
+	subtype_opt = (char *) malloc(strlen(basename) + 64);
+	if (subtype_opt == NULL) {
+		fprintf(stderr, "fuse: memory allocation failed\n");
+		return -1;
+	}
+	sprintf(subtype_opt, "-osubtype=%s", basename);
+	res = fuse_opt_add_arg(args, subtype_opt);
+	free(subtype_opt);
+	return res;
+}
+
+int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint,
+		       int *multithreaded, int *foreground)
+{
+	int res;
+	struct helper_opts hopts;
+
+	memset(&hopts, 0, sizeof(hopts));
+	res = fuse_opt_parse(args, &hopts, fuse_helper_opts,
+			     fuse_helper_opt_proc);
+	if (res == -1)
+		return -1;
+
+	if (!hopts.nodefault_subtype) {
+		res = add_default_subtype(args->argv[0], args);
+		if (res == -1)
+			goto err;
+	}
+	if (mountpoint)
+		*mountpoint = hopts.mountpoint;
+	else
+		free(hopts.mountpoint);
+
+	if (multithreaded)
+		*multithreaded = !hopts.singlethread;
+	if (foreground)
+		*foreground = hopts.foreground;
+	return 0;
+
+err:
+	free(hopts.mountpoint);
+	return -1;
+}
+
+int fuse_daemonize(int foreground)
+{
+	if (!foreground) {
+		int nullfd;
+
+		/*
+		 * demonize current process by forking it and killing the
+		 * parent.  This makes current process as a child of 'init'.
+		 */
+		switch(fork()) {
+		case -1:
+			perror("fuse_daemonize: fork");
+			return -1;
+		case 0:
+			break;
+		default:
+			_exit(0);
+		}
+
+		if (setsid() == -1) {
+			perror("fuse_daemonize: setsid");
+			return -1;
+		}
+
+		(void) chdir("/");
+
+		nullfd = open("/dev/null", O_RDWR, 0);
+		if (nullfd != -1) {
+			(void) dup2(nullfd, 0);
+			(void) dup2(nullfd, 1);
+			(void) dup2(nullfd, 2);
+			if (nullfd > 2)
+				close(nullfd);
+		}
+	}
+	return 0;
+}
+
+static struct fuse_chan *fuse_mount_common(const char *mountpoint,
+					   struct fuse_args *args)
+{
+	struct fuse_chan *ch;
+	int fd;
+
+	/*
+	 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
+	 * would ensue.
+	 */
+	do {
+		fd = open("/dev/null", O_RDWR);
+		if (fd > 2)
+			close(fd);
+	} while (fd >= 0 && fd <= 2);
+
+	fd = fuse_mount_compat25(mountpoint, args);
+	if (fd == -1)
+		return NULL;
+
+	ch = fuse_kern_chan_new(fd);
+	if (!ch)
+		fuse_kern_unmount(mountpoint, fd);
+
+	return ch;
+}
+
+struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args)
+{
+	return fuse_mount_common(mountpoint, args);
+}
+
+static void fuse_unmount_common(const char *mountpoint, struct fuse_chan *ch)
+{
+	int fd = ch ? fuse_chan_fd(ch) : -1;
+	fuse_kern_unmount(mountpoint, fd);
+	if (ch)
+		fuse_chan_destroy(ch);
+}
+
+void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
+{
+	fuse_unmount_common(mountpoint, ch);
+}
+
+struct fuse *fuse_setup_common(int argc, char *argv[],
+			       const struct fuse_operations *op,
+			       size_t op_size,
+			       char **mountpoint,
+			       int *multithreaded,
+			       int *fd,
+			       void *user_data,
+			       int compat)
+{
+	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+	struct fuse_chan *ch;
+	struct fuse *fuse;
+	int foreground;
+	int res;
+
+	res = fuse_parse_cmdline(&args, mountpoint, multithreaded, &foreground);
+	if (res == -1)
+		return NULL;
+
+	ch = fuse_mount_common(*mountpoint, &args);
+	if (!ch) {
+		fuse_opt_free_args(&args);
+		goto err_free;
+	}
+
+	fuse = fuse_new_common(ch, &args, op, op_size, user_data, compat);
+	fuse_opt_free_args(&args);
+	if (fuse == NULL)
+		goto err_unmount;
+
+	res = fuse_daemonize(foreground);
+	if (res == -1)
+		goto err_unmount;
+
+	res = fuse_set_signal_handlers(fuse_get_session(fuse));
+	if (res == -1)
+		goto err_unmount;
+
+	if (fd)
+		*fd = fuse_chan_fd(ch);
+
+	return fuse;
+
+err_unmount:
+	fuse_unmount_common(*mountpoint, ch);
+	if (fuse)
+		fuse_destroy(fuse);
+err_free:
+	free(*mountpoint);
+	return NULL;
+}
+
+struct fuse *fuse_setup(int argc, char *argv[],
+			const struct fuse_operations *op, size_t op_size,
+			char **mountpoint, int *multithreaded, void *user_data)
+{
+	return fuse_setup_common(argc, argv, op, op_size, mountpoint,
+				 multithreaded, NULL, user_data, 0);
+}
+
+static void fuse_teardown_common(struct fuse *fuse, char *mountpoint)
+{
+	struct fuse_session *se = fuse_get_session(fuse);
+	struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+	fuse_remove_signal_handlers(se);
+	fuse_unmount_common(mountpoint, ch);
+	fuse_destroy(fuse);
+	free(mountpoint);
+}
+
+void fuse_teardown(struct fuse *fuse, char *mountpoint)
+{
+	fuse_teardown_common(fuse, mountpoint);
+}
+
+static int fuse_main_common(int argc, char *argv[],
+			    const struct fuse_operations *op, size_t op_size,
+			    void *user_data, int compat)
+{
+	struct fuse *fuse;
+	char *mountpoint;
+	int multithreaded;
+	int res;
+
+	fuse = fuse_setup_common(argc, argv, op, op_size, &mountpoint,
+				 &multithreaded, NULL, user_data, compat);
+	if (fuse == NULL)
+		return 1;
+
+	if (multithreaded)
+		res = fuse_loop_mt(fuse);
+	else
+		res = fuse_loop(fuse);
+
+	fuse_teardown_common(fuse, mountpoint);
+	if (res == -1)
+		return 1;
+
+	return 0;
+}
+
+int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
+		   size_t op_size, void *user_data)
+{
+	return fuse_main_common(argc, argv, op, op_size, user_data, 0);
+}
+
+#undef fuse_main
+int fuse_main(void);
+int fuse_main(void)
+{
+	fprintf(stderr, "fuse_main(): This function does not exist\n");
+	return -1;
+}
+
+int fuse_version(void)
+{
+	return FUSE_VERSION;
+}
+
+#include "fuse_compat.h"
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+struct fuse *fuse_setup_compat22(int argc, char *argv[],
+				 const struct fuse_operations_compat22 *op,
+				 size_t op_size, char **mountpoint,
+				 int *multithreaded, int *fd)
+{
+	return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+				 op_size, mountpoint, multithreaded, fd, NULL,
+				 22);
+}
+
+struct fuse *fuse_setup_compat2(int argc, char *argv[],
+				const struct fuse_operations_compat2 *op,
+				char **mountpoint, int *multithreaded,
+				int *fd)
+{
+	return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+				 sizeof(struct fuse_operations_compat2),
+				 mountpoint, multithreaded, fd, NULL, 21);
+}
+
+int fuse_main_real_compat22(int argc, char *argv[],
+			    const struct fuse_operations_compat22 *op,
+			    size_t op_size)
+{
+	return fuse_main_common(argc, argv, (struct fuse_operations *) op,
+				op_size, NULL, 22);
+}
+
+void fuse_main_compat1(int argc, char *argv[],
+		       const struct fuse_operations_compat1 *op)
+{
+	fuse_main_common(argc, argv, (struct fuse_operations *) op,
+			 sizeof(struct fuse_operations_compat1), NULL, 11);
+}
+
+int fuse_main_compat2(int argc, char *argv[],
+		      const struct fuse_operations_compat2 *op)
+{
+	return fuse_main_common(argc, argv, (struct fuse_operations *) op,
+				sizeof(struct fuse_operations_compat2), NULL,
+				21);
+}
+
+int fuse_mount_compat1(const char *mountpoint, const char *args[])
+{
+	/* just ignore mount args for now */
+	(void) args;
+	return fuse_mount_compat22(mountpoint, NULL);
+}
+
+FUSE_SYMVER(".symver fuse_setup_compat2,__fuse_setup@");
+FUSE_SYMVER(".symver fuse_setup_compat22,fuse_setup@FUSE_2.2");
+FUSE_SYMVER(".symver fuse_teardown,__fuse_teardown@");
+FUSE_SYMVER(".symver fuse_main_compat2,fuse_main@");
+FUSE_SYMVER(".symver fuse_main_real_compat22,fuse_main_real@FUSE_2.2");
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+
+struct fuse *fuse_setup_compat25(int argc, char *argv[],
+				 const struct fuse_operations_compat25 *op,
+				 size_t op_size, char **mountpoint,
+				 int *multithreaded, int *fd)
+{
+	return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+				 op_size, mountpoint, multithreaded, fd, NULL,
+				 25);
+}
+
+int fuse_main_real_compat25(int argc, char *argv[],
+			    const struct fuse_operations_compat25 *op,
+			    size_t op_size)
+{
+	return fuse_main_common(argc, argv, (struct fuse_operations *) op,
+				op_size, NULL, 25);
+}
+
+void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint)
+{
+	(void) fd;
+	fuse_teardown_common(fuse, mountpoint);
+}
+
+int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args)
+{
+	return fuse_kern_mount(mountpoint, args);
+}
+
+FUSE_SYMVER(".symver fuse_setup_compat25,fuse_setup@FUSE_2.5");
+FUSE_SYMVER(".symver fuse_teardown_compat22,fuse_teardown@FUSE_2.2");
+FUSE_SYMVER(".symver fuse_main_real_compat25,fuse_main_real@FUSE_2.5");
+FUSE_SYMVER(".symver fuse_mount_compat25,fuse_mount@FUSE_2.5");
diff --git a/fuse/include/Makefile b/fuse/include/Makefile
new file mode 100644
index 0000000..43fd00a
--- /dev/null
+++ b/fuse/include/Makefile
@@ -0,0 +1,515 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# include/Makefile.  Generated from Makefile.in by configure.
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+
+pkgdatadir = $(datadir)/fuse
+pkgincludedir = $(includedir)/fuse
+pkglibdir = $(libdir)/fuse
+pkglibexecdir = $(libexecdir)/fuse
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = i686-pc-linux-gnu
+host_triplet = i686-pc-linux-gnu
+target_triplet = i686-pc-linux-gnu
+subdir = include
+DIST_COMMON = $(fuseinclude_HEADERS) $(include_HEADERS) \
+	$(noinst_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+	$(srcdir)/config.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(fuseincludedir)" \
+	"$(DESTDIR)$(includedir)"
+HEADERS = $(fuseinclude_HEADERS) $(include_HEADERS) $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /root/fuse-2.8.5/missing --run aclocal-1.11
+AMTAR = ${SHELL} /root/fuse-2.8.5/missing --run tar
+AR = ar
+AUTOCONF = ${SHELL} /root/fuse-2.8.5/missing --run autoconf
+AUTOHEADER = ${SHELL} /root/fuse-2.8.5/missing --run autoheader
+AUTOMAKE = ${SHELL} /root/fuse-2.8.5/missing --run automake-1.11
+AWK = mawk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -Wall -W -Wno-sign-compare -Wstrict-prototypes -Wmissing-declarations -Wwrite-strings -g -O2 -fno-strict-aliasing
+CPP = gcc -E
+CPPFLAGS = 
+CYGPATH_W = echo
+DEFS = -DHAVE_CONFIG_H
+DEPDIR = .deps
+DSYMUTIL = 
+DUMPBIN = 
+ECHO_C = 
+ECHO_N = -n
+ECHO_T = 
+EGREP = /bin/grep -E
+EXEEXT = 
+FGREP = /bin/grep -F
+GREP = /bin/grep
+INIT_D_PATH = /etc/init.d
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
+LD = /usr/bin/ld
+LDFLAGS = 
+LIBICONV = 
+LIBOBJS = 
+LIBS = 
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LIPO = 
+LN_S = ln -s
+LTLIBICONV = 
+LTLIBOBJS = 
+MAKEINFO = ${SHELL} /root/fuse-2.8.5/missing --run makeinfo
+MKDIR_P = /bin/mkdir -p
+MOUNT_FUSE_PATH = /sbin
+NM = /usr/bin/nm -B
+NMEDIT = 
+OBJDUMP = objdump
+OBJEXT = o
+OTOOL = 
+OTOOL64 = 
+PACKAGE = fuse
+PACKAGE_BUGREPORT = 
+PACKAGE_NAME = fuse
+PACKAGE_STRING = fuse 2.8.5
+PACKAGE_TARNAME = fuse
+PACKAGE_VERSION = 2.8.5
+PATH_SEPARATOR = :
+RANLIB = ranlib
+SED = /bin/sed
+SET_MAKE = 
+SHELL = /bin/bash
+STRIP = strip
+UDEV_RULES_PATH = /etc/udev/rules.d
+VERSION = 2.8.5
+abs_builddir = /root/fuse-2.8.5/include
+abs_srcdir = /root/fuse-2.8.5/include
+abs_top_builddir = /root/fuse-2.8.5
+abs_top_srcdir = /root/fuse-2.8.5
+ac_ct_CC = gcc
+ac_ct_DUMPBIN = 
+am__include = include
+am__leading_dot = .
+am__quote = 
+am__tar = ${AMTAR} chof - "$$tardir"
+am__untar = ${AMTAR} xf -
+bindir = ${exec_prefix}/bin
+build = i686-pc-linux-gnu
+build_alias = 
+build_cpu = i686
+build_os = linux-gnu
+build_vendor = pc
+builddir = .
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
+dvidir = ${docdir}
+exec_prefix = ${prefix}
+host = i686-pc-linux-gnu
+host_alias = 
+host_cpu = i686
+host_os = linux-gnu
+host_vendor = pc
+htmldir = ${docdir}
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = ${SHELL} /root/fuse-2.8.5/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+libfuse_libs = -pthread -lrt -ldl  
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+lt_ECHO = echo
+mandir = ${datarootdir}/man
+mkdir_p = /bin/mkdir -p
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+pkgconfigdir = ${libdir}/pkgconfig
+prefix = /usr/local
+program_transform_name = s,x,x,
+psdir = ${docdir}
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+srcdir = .
+subdirs2 = include lib util example
+sysconfdir = ${prefix}/etc
+target = i686-pc-linux-gnu
+target_alias = 
+target_cpu = i686
+target_os = linux-gnu
+target_vendor = pc
+top_build_prefix = ../
+top_builddir = ..
+top_srcdir = ..
+fuseincludedir = $(includedir)/fuse
+fuseinclude_HEADERS = \
+	fuse.h			\
+	fuse_compat.h		\
+	fuse_common.h		\
+	fuse_common_compat.h    \
+	fuse_lowlevel.h		\
+	fuse_lowlevel_compat.h	\
+	fuse_opt.h		\
+	cuse_lowlevel.h
+
+include_HEADERS = old/fuse.h ulockmgr.h
+noinst_HEADERS = fuse_kernel.h
+all: config.h
+	$(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu include/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+	@if test ! -f $@; then \
+	  rm -f stamp-h1; \
+	  $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+	else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+	@rm -f stamp-h1
+	cd $(top_builddir) && $(SHELL) ./config.status include/config.h
+$(srcdir)/config.h.in:  $(am__configure_deps) 
+	($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+	rm -f stamp-h1
+	touch $@
+
+distclean-hdr:
+	-rm -f config.h stamp-h1
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+install-fuseincludeHEADERS: $(fuseinclude_HEADERS)
+	@$(NORMAL_INSTALL)
+	test -z "$(fuseincludedir)" || $(MKDIR_P) "$(DESTDIR)$(fuseincludedir)"
+	@list='$(fuseinclude_HEADERS)'; test -n "$(fuseincludedir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(fuseincludedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(fuseincludedir)" || exit $$?; \
+	done
+
+uninstall-fuseincludeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(fuseinclude_HEADERS)'; test -n "$(fuseincludedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(fuseincludedir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(fuseincludedir)" && rm -f $$files
+install-includeHEADERS: $(include_HEADERS)
+	@$(NORMAL_INSTALL)
+	test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+	done
+
+uninstall-includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(includedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(HEADERS) config.h
+installdirs:
+	for dir in "$(DESTDIR)$(fuseincludedir)" "$(DESTDIR)$(includedir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-fuseincludeHEADERS install-includeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-fuseincludeHEADERS uninstall-includeHEADERS
+
+.MAKE: all install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool ctags distclean distclean-generic distclean-hdr \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-fuseincludeHEADERS install-html \
+	install-html-am install-includeHEADERS install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
+	uninstall-am uninstall-fuseincludeHEADERS \
+	uninstall-includeHEADERS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/fuse/include/Makefile.am b/fuse/include/Makefile.am
new file mode 100644
index 0000000..663e164
--- /dev/null
+++ b/fuse/include/Makefile.am
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in
+
+fuseincludedir=$(includedir)/fuse
+
+fuseinclude_HEADERS = \
+	fuse.h			\
+	fuse_compat.h		\
+	fuse_common.h		\
+	fuse_common_compat.h    \
+	fuse_lowlevel.h		\
+	fuse_lowlevel_compat.h	\
+	fuse_opt.h		\
+	cuse_lowlevel.h
+
+include_HEADERS = old/fuse.h ulockmgr.h
+
+noinst_HEADERS = fuse_kernel.h
diff --git a/fuse/include/Makefile.in b/fuse/include/Makefile.in
new file mode 100644
index 0000000..1ae6d85
--- /dev/null
+++ b/fuse/include/Makefile.in
@@ -0,0 +1,515 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = include
+DIST_COMMON = $(fuseinclude_HEADERS) $(include_HEADERS) \
+	$(noinst_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+	$(srcdir)/config.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(fuseincludedir)" \
+	"$(DESTDIR)$(includedir)"
+HEADERS = $(fuseinclude_HEADERS) $(include_HEADERS) $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INIT_D_PATH = @INIT_D_PATH@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MOUNT_FUSE_PATH = @MOUNT_FUSE_PATH@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+UDEV_RULES_PATH = @UDEV_RULES_PATH@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+libfuse_libs = @libfuse_libs@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs2 = @subdirs2@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+fuseincludedir = $(includedir)/fuse
+fuseinclude_HEADERS = \
+	fuse.h			\
+	fuse_compat.h		\
+	fuse_common.h		\
+	fuse_common_compat.h    \
+	fuse_lowlevel.h		\
+	fuse_lowlevel_compat.h	\
+	fuse_opt.h		\
+	cuse_lowlevel.h
+
+include_HEADERS = old/fuse.h ulockmgr.h
+noinst_HEADERS = fuse_kernel.h
+all: config.h
+	$(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu include/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+	@if test ! -f $@; then \
+	  rm -f stamp-h1; \
+	  $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+	else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+	@rm -f stamp-h1
+	cd $(top_builddir) && $(SHELL) ./config.status include/config.h
+$(srcdir)/config.h.in:  $(am__configure_deps) 
+	($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+	rm -f stamp-h1
+	touch $@
+
+distclean-hdr:
+	-rm -f config.h stamp-h1
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+install-fuseincludeHEADERS: $(fuseinclude_HEADERS)
+	@$(NORMAL_INSTALL)
+	test -z "$(fuseincludedir)" || $(MKDIR_P) "$(DESTDIR)$(fuseincludedir)"
+	@list='$(fuseinclude_HEADERS)'; test -n "$(fuseincludedir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(fuseincludedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(fuseincludedir)" || exit $$?; \
+	done
+
+uninstall-fuseincludeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(fuseinclude_HEADERS)'; test -n "$(fuseincludedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(fuseincludedir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(fuseincludedir)" && rm -f $$files
+install-includeHEADERS: $(include_HEADERS)
+	@$(NORMAL_INSTALL)
+	test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+	done
+
+uninstall-includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(includedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(HEADERS) config.h
+installdirs:
+	for dir in "$(DESTDIR)$(fuseincludedir)" "$(DESTDIR)$(includedir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-fuseincludeHEADERS install-includeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-fuseincludeHEADERS uninstall-includeHEADERS
+
+.MAKE: all install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool ctags distclean distclean-generic distclean-hdr \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-fuseincludeHEADERS install-html \
+	install-html-am install-includeHEADERS install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
+	uninstall-am uninstall-fuseincludeHEADERS \
+	uninstall-includeHEADERS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/fuse/include/config.h b/fuse/include/config.h
new file mode 100644
index 0000000..a0c603a
--- /dev/null
+++ b/fuse/include/config.h
@@ -0,0 +1,87 @@
+/* include/config.h.  Generated from config.h.in by configure.  */
+/* include/config.h.in.  Generated from configure.in by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `fdatasync' function. */
+#define HAVE_FDATASYNC 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define if you have the iconv() function and it works. */
+#define HAVE_ICONV 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `setxattr' function. */
+#define HAVE_SETXATTR 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if `st_atim' is member of `struct stat'. */
+//#define HAVE_STRUCT_STAT_ST_ATIM 0
+
+/* Define to 1 if `st_atimespec' is member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST 
+
+/* Don't update /etc/mtab */
+/* #undef IGNORE_MTAB */
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "fuse"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "fuse"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "fuse 2.8.5"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "fuse"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.8.5"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "2.8.5"
diff --git a/fuse/include/config.h.in b/fuse/include/config.h.in
new file mode 100644
index 0000000..3e7c14c
--- /dev/null
+++ b/fuse/include/config.h.in
@@ -0,0 +1,86 @@
+/* include/config.h.in.  Generated from configure.in by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the `fdatasync' function. */
+#undef HAVE_FDATASYNC
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define if you have the iconv() function and it works. */
+#undef HAVE_ICONV
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `setxattr' function. */
+#undef HAVE_SETXATTR
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if `st_atim' is member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIM
+
+/* Define to 1 if `st_atimespec' is member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIMESPEC
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define as const if the declaration of iconv() needs const. */
+#undef ICONV_CONST
+
+/* Don't update /etc/mtab */
+#undef IGNORE_MTAB
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
diff --git a/fuse/include/cuse_lowlevel.h b/fuse/include/cuse_lowlevel.h
new file mode 100644
index 0000000..d628eac
--- /dev/null
+++ b/fuse/include/cuse_lowlevel.h
@@ -0,0 +1,87 @@
+/*
+  CUSE: Character device in Userspace
+  Copyright (C) 2008-2009  SUSE Linux Products GmbH
+  Copyright (C) 2008-2009  Tejun Heo <tj@kernel.org>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+
+  Read example/cusexmp.c for usages.
+*/
+
+#ifndef _CUSE_LOWLEVEL_H_
+#define _CUSE_LOWLEVEL_H_
+
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 29
+#endif
+
+#include "fuse_lowlevel.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CUSE_UNRESTRICTED_IOCTL		(1 << 0) /* use unrestricted ioctl */
+
+struct fuse_session;
+
+struct cuse_info {
+	unsigned	dev_major;
+	unsigned	dev_minor;
+	unsigned	dev_info_argc;
+	const char	**dev_info_argv;
+	unsigned	flags;
+};
+
+/*
+ * Most ops behave almost identically to the matching fuse_lowlevel
+ * ops except that they don't take @ino.
+ *
+ * init_done	: called after initialization is complete
+ * read/write	: always direct IO, simultaneous operations allowed
+ * ioctl	: might be in unrestricted mode depending on ci->flags
+ */
+struct cuse_lowlevel_ops {
+	void (*init) (void *userdata, struct fuse_conn_info *conn);
+	void (*init_done) (void *userdata);
+	void (*destroy) (void *userdata);
+	void (*open) (fuse_req_t req, struct fuse_file_info *fi);
+	void (*read) (fuse_req_t req, size_t size, off64_t off,
+		      struct fuse_file_info *fi);
+	void (*write) (fuse_req_t req, const char *buf, size_t size, off64_t off,
+		       struct fuse_file_info *fi);
+	void (*flush) (fuse_req_t req, struct fuse_file_info *fi);
+	void (*release) (fuse_req_t req, struct fuse_file_info *fi);
+	void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi);
+	void (*ioctl) (fuse_req_t req, int cmd, void *arg,
+		       struct fuse_file_info *fi, unsigned int flags,
+		       const void *in_buf, size_t in_bufsz, size_t out_bufsz);
+	void (*poll) (fuse_req_t req, struct fuse_file_info *fi,
+		      struct fuse_pollhandle *ph);
+};
+
+struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
+				       const struct cuse_info *ci,
+				       const struct cuse_lowlevel_ops *clop,
+				       void *userdata);
+
+struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
+					 const struct cuse_info *ci,
+					 const struct cuse_lowlevel_ops *clop,
+					 int *multithreaded, void *userdata);
+
+void cuse_lowlevel_teardown(struct fuse_session *se);
+
+int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
+		       const struct cuse_lowlevel_ops *clop, void *userdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CUSE_LOWLEVEL_H_ */
diff --git a/fuse/include/fuse.h b/fuse/include/fuse.h
new file mode 100644
index 0000000..cad816c
--- /dev/null
+++ b/fuse/include/fuse.h
@@ -0,0 +1,1059 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#ifndef _FUSE_H_
+#define _FUSE_H_
+
+/** @file
+ *
+ * This file defines the library interface of FUSE
+ *
+ * IMPORTANT: you should define FUSE_USE_VERSION before including this
+ * header.  To use the newest API define it to 26 (recommended for any
+ * new application), to use the old API define it to 21 (default) 22
+ * or 25, to use the even older 1.X API define it to 11.
+ */
+
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 21
+#endif
+
+#include "fuse_common.h"
+
+#include <fcntl.h>
+#include <time.h>
+#include <utime.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/uio.h>
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ----------------------------------------------------------- *
+ * Basic FUSE API					       *
+ * ----------------------------------------------------------- */
+
+/** Handle for a FUSE filesystem */
+struct fuse;
+
+/** Structure containing a raw command */
+struct fuse_cmd;
+
+/** Function to add an entry in a readdir() operation
+ *
+ * @param buf the buffer passed to the readdir() operation
+ * @param name the file name of the directory entry
+ * @param stat file attributes, can be NULL
+ * @param off offset of the next entry or zero
+ * @return 1 if buffer is full, zero otherwise
+ */
+typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
+				const struct stat *stbuf, off64_t off);
+
+/* Used by deprecated getdir() method */
+typedef struct fuse_dirhandle *fuse_dirh_t;
+typedef int (*fuse_dirfil_t) (fuse_dirh_t h, const char *name, int type,
+			      ino_t ino);
+
+/**
+ * The file system operations:
+ *
+ * Most of these should work very similarly to the well known UNIX
+ * file system operations.  A major exception is that instead of
+ * returning an error in 'errno', the operation should return the
+ * negated error value (-errno) directly.
+ *
+ * All methods are optional, but some are essential for a useful
+ * filesystem (e.g. getattr).  Open, flush, release, fsync, opendir,
+ * releasedir, fsyncdir, access, create, ftruncate, fgetattr, lock,
+ * init and destroy are special purpose methods, without which a full
+ * featured filesystem can still be implemented.
+ *
+ * Almost all operations take a path which can be of any length.
+ *
+ * Changed in fuse 2.8.0 (regardless of API version)
+ * Previously, paths were limited to a length of PATH_MAX.
+ *
+ * See http://fuse.sourceforge.net/wiki/ for more information.  There
+ * is also a snapshot of the relevant wiki pages in the doc/ folder.
+ */
+struct fuse_operations {
+	/** Get file attributes.
+	 *
+	 * Similar to stat().  The 'st_dev' and 'st_blksize' fields are
+	 * ignored.	 The 'st_ino' field is ignored except if the 'use_ino'
+	 * mount option is given.
+	 */
+	int (*getattr) (const char *, struct stat *);
+
+	/** Read the target of a symbolic link
+	 *
+	 * The buffer should be filled with a null terminated string.  The
+	 * buffer size argument includes the space for the terminating
+	 * null character.	If the linkname is too long to fit in the
+	 * buffer, it should be truncated.	The return value should be 0
+	 * for success.
+	 */
+	int (*readlink) (const char *, char *, size_t);
+
+	/* Deprecated, use readdir() instead */
+	int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
+
+	/** Create a file node
+	 *
+	 * This is called for creation of all non-directory, non-symlink
+	 * nodes.  If the filesystem defines a create() method, then for
+	 * regular files that will be called instead.
+	 */
+	int (*mknod) (const char *, mode_t, dev_t);
+
+	/** Create a directory 
+	 *
+	 * Note that the mode argument may not have the type specification
+	 * bits set, i.e. S_ISDIR(mode) can be false.  To obtain the
+	 * correct directory type bits use  mode|S_IFDIR
+	 * */
+	int (*mkdir) (const char *, mode_t);
+
+	/** Remove a file */
+	int (*unlink) (const char *);
+
+	/** Remove a directory */
+	int (*rmdir) (const char *);
+
+	/** Create a symbolic link */
+	int (*symlink) (const char *, const char *);
+
+	/** Rename a file */
+	int (*rename) (const char *, const char *);
+
+	/** Create a hard link to a file */
+	int (*link) (const char *, const char *);
+
+	/** Change the permission bits of a file */
+	int (*chmod) (const char *, mode_t);
+
+	/** Change the owner and group of a file */
+	int (*chown) (const char *, uid_t, gid_t);
+
+	/** Change the size of a file */
+	int (*truncate) (const char *, off64_t);
+
+	/** Change the access and/or modification times of a file
+	 *
+	 * Deprecated, use utimens() instead.
+	 */
+	int (*utime) (const char *, struct utimbuf *);
+
+	/** File open operation
+	 *
+	 * No creation (O_CREAT, O_EXCL) and by default also no
+	 * truncation (O_TRUNC) flags will be passed to open(). If an
+	 * application specifies O_TRUNC, fuse first calls truncate()
+	 * and then open(). Only if 'atomic_o_trunc' has been
+	 * specified and kernel version is 2.6.24 or later, O_TRUNC is
+	 * passed on to open.
+	 *
+	 * Unless the 'default_permissions' mount option is given,
+	 * open should check if the operation is permitted for the
+	 * given flags. Optionally open may also return an arbitrary
+	 * filehandle in the fuse_file_info structure, which will be
+	 * passed to all file operations.
+	 *
+	 * Changed in version 2.2
+	 */
+	int (*open) (const char *, struct fuse_file_info *);
+
+	/** Read data from an open file
+	 *
+	 * Read should return exactly the number of bytes requested except
+	 * on EOF or error, otherwise the rest of the data will be
+	 * substituted with zeroes.	 An exception to this is when the
+	 * 'direct_io' mount option is specified, in which case the return
+	 * value of the read system call will reflect the return value of
+	 * this operation.
+	 *
+	 * Changed in version 2.2
+	 */
+	int (*read) (const char *, char *, size_t, off64_t,
+		     struct fuse_file_info *);
+
+	/** Write data to an open file
+	 *
+	 * Write should return exactly the number of bytes requested
+	 * except on error.	 An exception to this is when the 'direct_io'
+	 * mount option is specified (see read operation).
+	 *
+	 * Changed in version 2.2
+	 */
+	int (*write) (const char *, const char *, size_t, off64_t,
+		      struct fuse_file_info *);
+
+	/** Get file system statistics
+	 *
+	 * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
+	 *
+	 * Replaced 'struct statfs' parameter with 'struct statvfs' in
+	 * version 2.5
+	 */
+	int (*statfs) (const char *, struct statvfs *);
+
+	/** Possibly flush cached data
+	 *
+	 * BIG NOTE: This is not equivalent to fsync().  It's not a
+	 * request to sync dirty data.
+	 *
+	 * Flush is called on each close() of a file descriptor.  So if a
+	 * filesystem wants to return write errors in close() and the file
+	 * has cached dirty data, this is a good place to write back data
+	 * and return any errors.  Since many applications ignore close()
+	 * errors this is not always useful.
+	 *
+	 * NOTE: The flush() method may be called more than once for each
+	 * open().	This happens if more than one file descriptor refers
+	 * to an opened file due to dup(), dup2() or fork() calls.	It is
+	 * not possible to determine if a flush is final, so each flush
+	 * should be treated equally.  Multiple write-flush sequences are
+	 * relatively rare, so this shouldn't be a problem.
+	 *
+	 * Filesystems shouldn't assume that flush will always be called
+	 * after some writes, or that if will be called at all.
+	 *
+	 * Changed in version 2.2
+	 */
+	int (*flush) (const char *, struct fuse_file_info *);
+
+	/** Release an open file
+	 *
+	 * Release is called when there are no more references to an open
+	 * file: all file descriptors are closed and all memory mappings
+	 * are unmapped.
+	 *
+	 * For every open() call there will be exactly one release() call
+	 * with the same flags and file descriptor.	 It is possible to
+	 * have a file opened more than once, in which case only the last
+	 * release will mean, that no more reads/writes will happen on the
+	 * file.  The return value of release is ignored.
+	 *
+	 * Changed in version 2.2
+	 */
+	int (*release) (const char *, struct fuse_file_info *);
+
+	/** Synchronize file contents
+	 *
+	 * If the datasync parameter is non-zero, then only the user data
+	 * should be flushed, not the meta data.
+	 *
+	 * Changed in version 2.2
+	 */
+	int (*fsync) (const char *, int, struct fuse_file_info *);
+
+	/** Set extended attributes */
+	int (*setxattr) (const char *, const char *, const char *, size_t, int);
+
+	/** Get extended attributes */
+	int (*getxattr) (const char *, const char *, char *, size_t);
+
+	/** List extended attributes */
+	int (*listxattr) (const char *, char *, size_t);
+
+	/** Remove extended attributes */
+	int (*removexattr) (const char *, const char *);
+
+	/** Open directory
+	 *
+	 * Unless the 'default_permissions' mount option is given,
+	 * this method should check if opendir is permitted for this
+	 * directory. Optionally opendir may also return an arbitrary
+	 * filehandle in the fuse_file_info structure, which will be
+	 * passed to readdir, closedir and fsyncdir.
+	 *
+	 * Introduced in version 2.3
+	 */
+	int (*opendir) (const char *, struct fuse_file_info *);
+
+	/** Read directory
+	 *
+	 * This supersedes the old getdir() interface.  New applications
+	 * should use this.
+	 *
+	 * The filesystem may choose between two modes of operation:
+	 *
+	 * 1) The readdir implementation ignores the offset parameter, and
+	 * passes zero to the filler function's offset.  The filler
+	 * function will not return '1' (unless an error happens), so the
+	 * whole directory is read in a single readdir operation.  This
+	 * works just like the old getdir() method.
+	 *
+	 * 2) The readdir implementation keeps track of the offsets of the
+	 * directory entries.  It uses the offset parameter and always
+	 * passes non-zero offset to the filler function.  When the buffer
+	 * is full (or an error happens) the filler function will return
+	 * '1'.
+	 *
+	 * Introduced in version 2.3
+	 */
+	int (*readdir) (const char *, void *, fuse_fill_dir_t, off64_t,
+			struct fuse_file_info *);
+
+	/** Release directory
+	 *
+	 * Introduced in version 2.3
+	 */
+	int (*releasedir) (const char *, struct fuse_file_info *);
+
+	/** Synchronize directory contents
+	 *
+	 * If the datasync parameter is non-zero, then only the user data
+	 * should be flushed, not the meta data
+	 *
+	 * Introduced in version 2.3
+	 */
+	int (*fsyncdir) (const char *, int, struct fuse_file_info *);
+
+	/**
+	 * Initialize filesystem
+	 *
+	 * The return value will passed in the private_data field of
+	 * fuse_context to all file operations and as a parameter to the
+	 * destroy() method.
+	 *
+	 * Introduced in version 2.3
+	 * Changed in version 2.6
+	 */
+	void *(*init) (struct fuse_conn_info *conn);
+
+	/**
+	 * Clean up filesystem
+	 *
+	 * Called on filesystem exit.
+	 *
+	 * Introduced in version 2.3
+	 */
+	void (*destroy) (void *);
+
+	/**
+	 * Check file access permissions
+	 *
+	 * This will be called for the access() system call.  If the
+	 * 'default_permissions' mount option is given, this method is not
+	 * called.
+	 *
+	 * This method is not called under Linux kernel versions 2.4.x
+	 *
+	 * Introduced in version 2.5
+	 */
+	int (*access) (const char *, int);
+
+	/**
+	 * Create and open a file
+	 *
+	 * If the file does not exist, first create it with the specified
+	 * mode, and then open it.
+	 *
+	 * If this method is not implemented or under Linux kernel
+	 * versions earlier than 2.6.15, the mknod() and open() methods
+	 * will be called instead.
+	 *
+	 * Introduced in version 2.5
+	 */
+	int (*create) (const char *, mode_t, struct fuse_file_info *);
+
+	/**
+	 * Change the size of an open file
+	 *
+	 * This method is called instead of the truncate() method if the
+	 * truncation was invoked from an ftruncate() system call.
+	 *
+	 * If this method is not implemented or under Linux kernel
+	 * versions earlier than 2.6.15, the truncate() method will be
+	 * called instead.
+	 *
+	 * Introduced in version 2.5
+	 */
+	int (*ftruncate) (const char *, off64_t, struct fuse_file_info *);
+
+	/**
+	 * Get attributes from an open file
+	 *
+	 * This method is called instead of the getattr() method if the
+	 * file information is available.
+	 *
+	 * Currently this is only called after the create() method if that
+	 * is implemented (see above).  Later it may be called for
+	 * invocations of fstat() too.
+	 *
+	 * Introduced in version 2.5
+	 */
+	int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
+
+	/**
+	 * Perform POSIX file locking operation
+	 *
+	 * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
+	 *
+	 * For the meaning of fields in 'struct flock' see the man page
+	 * for fcntl(2).  The l_whence field will always be set to
+	 * SEEK_SET.
+	 *
+	 * For checking lock ownership, the 'fuse_file_info->owner'
+	 * argument must be used.
+	 *
+	 * For F_GETLK operation, the library will first check currently
+	 * held locks, and if a conflicting lock is found it will return
+	 * information without calling this method.	 This ensures, that
+	 * for local locks the l_pid field is correctly filled in.	The
+	 * results may not be accurate in case of race conditions and in
+	 * the presence of hard links, but it's unlikely that an
+	 * application would rely on accurate GETLK results in these
+	 * cases.  If a conflicting lock is not found, this method will be
+	 * called, and the filesystem may fill out l_pid by a meaningful
+	 * value, or it may leave this field zero.
+	 *
+	 * For F_SETLK and F_SETLKW the l_pid field will be set to the pid
+	 * of the process performing the locking operation.
+	 *
+	 * Note: if this method is not implemented, the kernel will still
+	 * allow file locking to work locally.  Hence it is only
+	 * interesting for network filesystems and similar.
+	 *
+	 * Introduced in version 2.6
+	 */
+	int (*lock) (const char *, struct fuse_file_info *, int cmd,
+		     struct flock *);
+
+	/**
+	 * Change the access and modification times of a file with
+	 * nanosecond resolution
+	 *
+	 * This supersedes the old utime() interface.  New applications
+	 * should use this.
+	 *
+	 * See the utimensat(2) man page for details.
+	 *
+	 * Introduced in version 2.6
+	 */
+	int (*utimens) (const char *, const struct timespec tv[2]);
+
+	/**
+	 * Map block index within file to block index within device
+	 *
+	 * Note: This makes sense only for block device backed filesystems
+	 * mounted with the 'blkdev' option
+	 *
+	 * Introduced in version 2.6
+	 */
+	int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
+
+	/**
+	 * Flag indicating that the filesystem can accept a NULL path
+	 * as the first argument for the following operations:
+	 *
+	 * read, write, flush, release, fsync, readdir, releasedir,
+	 * fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
+	 *
+	 * If this flag is set these operations continue to work on
+	 * unlinked files even if "-ohard_remove" option was specified.
+	 */
+	unsigned int flag_nullpath_ok:1;
+
+	/**
+	 * Flag indicating that the path need not be calculated for
+	 * the following operations:
+	 *
+	 * read, write, flush, release, fsync, readdir, releasedir,
+	 * fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
+	 *
+	 * Closely related to flag_nullpath_ok, but if this flag is
+	 * set then the path will not be calculaged even if the file
+	 * wasn't unlinked.  However the path can still be non-NULL if
+	 * it needs to be calculated for some other reason.
+	 */
+	unsigned int flag_nopath:1;
+
+	/**
+	 * Flag indicating that the filesystem accepts special
+	 * UTIME_NOW and UTIME_OMIT values in its utimens operation.
+	 */
+	unsigned int flag_utime_omit_ok:1;
+
+	/**
+	 * Reserved flags, don't set
+	 */
+	unsigned int flag_reserved:29;
+
+	/**
+	 * Ioctl
+	 *
+	 * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
+	 * 64bit environment.  The size and direction of data is
+	 * determined by _IOC_*() decoding of cmd.  For _IOC_NONE,
+	 * data will be NULL, for _IOC_WRITE data is out area, for
+	 * _IOC_READ in area and if both are set in/out area.  In all
+	 * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
+	 *
+	 * Introduced in version 2.8
+	 */
+	int (*ioctl) (const char *, int cmd, void *arg,
+		      struct fuse_file_info *, unsigned int flags, void *data);
+
+	/**
+	 * Poll for IO readiness events
+	 *
+	 * Note: If ph is non-NULL, the client should notify
+	 * when IO readiness events occur by calling
+	 * fuse_notify_poll() with the specified ph.
+	 *
+	 * Regardless of the number of times poll with a non-NULL ph
+	 * is received, single notification is enough to clear all.
+	 * Notifying more times incurs overhead but doesn't harm
+	 * correctness.
+	 *
+	 * The callee is responsible for destroying ph with
+	 * fuse_pollhandle_destroy() when no longer in use.
+	 *
+	 * Introduced in version 2.8
+	 */
+	int (*poll) (const char *, struct fuse_file_info *,
+		     struct fuse_pollhandle *ph, unsigned *reventsp);
+
+	/** Write contents of buffer to an open file
+	 *
+	 * Similar to the write() method, but data is supplied in a
+	 * generic buffer.  Use fuse_buf_copy() to transfer data to
+	 * the destination.
+	 *
+	 * Introduced in version 2.9
+	 */
+	int (*write_buf) (const char *, struct fuse_bufvec *buf, off64_t off,
+			  struct fuse_file_info *);
+
+	/** Store data from an open file in a buffer
+	 *
+	 * Similar to the read() method, but data is stored and
+	 * returned in a generic buffer.
+	 *
+	 * No actual copying of data has to take place, the source
+	 * file descriptor may simply be stored in the buffer for
+	 * later data transfer.
+	 *
+	 * The buffer must be allocated dynamically and stored at the
+	 * location pointed to by bufp.  If the buffer contains memory
+	 * regions, they too must be allocated using malloc().  The
+	 * allocated memory will be freed by the caller.
+	 *
+	 * Introduced in version 2.9
+	 */
+	int (*read_buf) (const char *, struct fuse_bufvec **bufp,
+			 size_t size, off64_t off, struct fuse_file_info *);
+	/**
+	 * Perform BSD file locking operation
+	 *
+	 * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN
+	 *
+	 * Nonblocking requests will be indicated by ORing LOCK_NB to
+	 * the above operations
+	 *
+	 * For more information see the flock(2) manual page.
+	 *
+	 * Additionally fi->owner will be set to a value unique to
+	 * this open file.  This same value will be supplied to
+	 * ->release() when the file is released.
+	 *
+	 * Note: if this method is not implemented, the kernel will still
+	 * allow file locking to work locally.  Hence it is only
+	 * interesting for network filesystems and similar.
+	 *
+	 * Introduced in version 2.9
+	 */
+	int (*flock) (const char *, struct fuse_file_info *, int op);
+
+	/**
+	 * Allocates space for an open file
+	 *
+	 * This function ensures that required space is allocated for specified
+	 * file.  If this function returns success then any subsequent write
+	 * request to specified range is guaranteed not to fail because of lack
+	 * of space on the file system media.
+	 *
+	 * Introduced in version 2.9.1
+	 */
+	int (*fallocate) (const char *, int, off64_t, off64_t,
+			  struct fuse_file_info *);
+};
+
+/** Extra context that may be needed by some filesystems
+ *
+ * The uid, gid and pid fields are not filled in case of a writepage
+ * operation.
+ */
+struct fuse_context {
+	/** Pointer to the fuse object */
+	struct fuse *fuse;
+
+	/** User ID of the calling process */
+	uid_t uid;
+
+	/** Group ID of the calling process */
+	gid_t gid;
+
+	/** Thread ID of the calling process */
+	pid_t pid;
+
+	/** Private filesystem data */
+	void *private_data;
+
+	/** Umask of the calling process (introduced in version 2.8) */
+	mode_t umask;
+};
+
+/**
+ * Main function of FUSE.
+ *
+ * This is for the lazy.  This is all that has to be called from the
+ * main() function.
+ *
+ * This function does the following:
+ *   - parses command line options (-d -s and -h)
+ *   - passes relevant mount options to the fuse_mount()
+ *   - installs signal handlers for INT, HUP, TERM and PIPE
+ *   - registers an exit handler to unmount the filesystem on program exit
+ *   - creates a fuse handle
+ *   - registers the operations
+ *   - calls either the single-threaded or the multi-threaded event loop
+ *
+ * Note: this is currently implemented as a macro.
+ *
+ * @param argc the argument counter passed to the main() function
+ * @param argv the argument vector passed to the main() function
+ * @param op the file system operation
+ * @param user_data user data supplied in the context during the init() method
+ * @return 0 on success, nonzero on failure
+ */
+/*
+  int fuse_main(int argc, char *argv[], const struct fuse_operations *op,
+  void *user_data);
+*/
+#define fuse_main(argc, argv, op, user_data)				\
+	fuse_main_real(argc, argv, op, sizeof(*(op)), user_data)
+
+/* ----------------------------------------------------------- *
+ * More detailed API					       *
+ * ----------------------------------------------------------- */
+
+/**
+ * Create a new FUSE filesystem.
+ *
+ * @param ch the communication channel
+ * @param args argument vector
+ * @param op the filesystem operations
+ * @param op_size the size of the fuse_operations structure
+ * @param user_data user data supplied in the context during the init() method
+ * @return the created FUSE handle
+ */
+struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
+		      const struct fuse_operations *op, size_t op_size,
+		      void *user_data);
+
+/**
+ * Destroy the FUSE handle.
+ *
+ * The communication channel attached to the handle is also destroyed.
+ *
+ * NOTE: This function does not unmount the filesystem.	 If this is
+ * needed, call fuse_unmount() before calling this function.
+ *
+ * @param f the FUSE handle
+ */
+void fuse_destroy(struct fuse *f);
+
+/**
+ * FUSE event loop.
+ *
+ * Requests from the kernel are processed, and the appropriate
+ * operations are called.
+ *
+ * @param f the FUSE handle
+ * @return 0 if no error occurred, -1 otherwise
+ */
+int fuse_loop(struct fuse *f);
+
+/**
+ * Exit from event loop
+ *
+ * @param f the FUSE handle
+ */
+void fuse_exit(struct fuse *f);
+
+/**
+ * FUSE event loop with multiple threads
+ *
+ * Requests from the kernel are processed, and the appropriate
+ * operations are called.  Request are processed in parallel by
+ * distributing them between multiple threads.
+ *
+ * Calling this function requires the pthreads library to be linked to
+ * the application.
+ *
+ * @param f the FUSE handle
+ * @return 0 if no error occurred, -1 otherwise
+ */
+int fuse_loop_mt(struct fuse *f);
+
+/**
+ * Get the current context
+ *
+ * The context is only valid for the duration of a filesystem
+ * operation, and thus must not be stored and used later.
+ *
+ * @return the context
+ */
+struct fuse_context *fuse_get_context(void);
+
+/**
+ * Get the current supplementary group IDs for the current request
+ *
+ * Similar to the getgroups(2) system call, except the return value is
+ * always the total number of group IDs, even if it is larger than the
+ * specified size.
+ *
+ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass
+ * the group list to userspace, hence this function needs to parse
+ * "/proc/$TID/task/$TID/status" to get the group IDs.
+ *
+ * This feature may not be supported on all operating systems.  In
+ * such a case this function will return -ENOSYS.
+ *
+ * @param size size of given array
+ * @param list array of group IDs to be filled in
+ * @return the total number of supplementary group IDs or -errno on failure
+ */
+int fuse_getgroups(int size, gid_t list[]);
+
+/**
+ * Check if the current request has already been interrupted
+ *
+ * @return 1 if the request has been interrupted, 0 otherwise
+ */
+int fuse_interrupted(void);
+
+/**
+ * Obsolete, doesn't do anything
+ *
+ * @return -EINVAL
+ */
+int fuse_invalidate(struct fuse *f, const char *path);
+
+/* Deprecated, don't use */
+int fuse_is_lib_option(const char *opt);
+
+/**
+ * The real main function
+ *
+ * Do not call this directly, use fuse_main()
+ */
+int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
+		   size_t op_size, void *user_data);
+
+/**
+ * Start the cleanup thread when using option "remember".
+ *
+ * This is done automatically by fuse_loop_mt()
+ * @param fuse struct fuse pointer for fuse instance
+ * @return 0 on success and -1 on error
+ */
+int fuse_start_cleanup_thread(struct fuse *fuse);
+
+/**
+ * Stop the cleanup thread when using option "remember".
+ *
+ * This is done automatically by fuse_loop_mt()
+ * @param fuse struct fuse pointer for fuse instance
+ */
+void fuse_stop_cleanup_thread(struct fuse *fuse);
+
+/**
+ * Iterate over cache removing stale entries
+ * use in conjunction with "-oremember"
+ *
+ * NOTE: This is already done for the standard sessions
+ *
+ * @param fuse struct fuse pointer for fuse instance
+ * @return the number of seconds until the next cleanup
+ */
+int fuse_clean_cache(struct fuse *fuse);
+
+/*
+ * Stacking API
+ */
+
+/**
+ * Fuse filesystem object
+ *
+ * This is opaque object represents a filesystem layer
+ */
+struct fuse_fs;
+
+/*
+ * These functions call the relevant filesystem operation, and return
+ * the result.
+ *
+ * If the operation is not defined, they return -ENOSYS, with the
+ * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
+ * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
+ */
+
+int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf);
+int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf,
+		     struct fuse_file_info *fi);
+int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
+		   const char *newpath);
+int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
+int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
+int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
+		    const char *path);
+int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
+int fuse_fs_release(struct fuse_fs *fs,	 const char *path,
+		    struct fuse_file_info *fi);
+int fuse_fs_open(struct fuse_fs *fs, const char *path,
+		 struct fuse_file_info *fi);
+int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
+		 off64_t off, struct fuse_file_info *fi);
+int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
+		     struct fuse_bufvec **bufp, size_t size, off64_t off,
+		     struct fuse_file_info *fi);
+int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
+		  size_t size, off64_t off, struct fuse_file_info *fi);
+int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
+		      struct fuse_bufvec *buf, off64_t off,
+		      struct fuse_file_info *fi);
+int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
+		  struct fuse_file_info *fi);
+int fuse_fs_flush(struct fuse_fs *fs, const char *path,
+		  struct fuse_file_info *fi);
+int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
+int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
+		    struct fuse_file_info *fi);
+int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
+		    fuse_fill_dir_t filler, off64_t off,
+		    struct fuse_file_info *fi);
+int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
+		     struct fuse_file_info *fi);
+int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
+		       struct fuse_file_info *fi);
+int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
+		   struct fuse_file_info *fi);
+int fuse_fs_lock(struct fuse_fs *fs, const char *path,
+		 struct fuse_file_info *fi, int cmd, struct flock *lock);
+int fuse_fs_flock(struct fuse_fs *fs, const char *path,
+		  struct fuse_file_info *fi, int op);
+int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode);
+int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid);
+int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off64_t size);
+int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off64_t size,
+		      struct fuse_file_info *fi);
+int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
+		    const struct timespec tv[2]);
+int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
+int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
+		     size_t len);
+int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
+		  dev_t rdev);
+int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
+int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
+		     const char *value, size_t size, int flags);
+int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
+		     char *value, size_t size);
+int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
+		      size_t size);
+int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
+			const char *name);
+int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
+		 uint64_t *idx);
+int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
+		  struct fuse_file_info *fi, unsigned int flags, void *data);
+int fuse_fs_poll(struct fuse_fs *fs, const char *path,
+		 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
+		 unsigned *reventsp);
+int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
+		 off64_t offset, off64_t length, struct fuse_file_info *fi);
+void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn);
+void fuse_fs_destroy(struct fuse_fs *fs);
+
+int fuse_notify_poll(struct fuse_pollhandle *ph);
+
+/**
+ * Create a new fuse filesystem object
+ *
+ * This is usually called from the factory of a fuse module to create
+ * a new instance of a filesystem.
+ *
+ * @param op the filesystem operations
+ * @param op_size the size of the fuse_operations structure
+ * @param user_data user data supplied in the context during the init() method
+ * @return a new filesystem object
+ */
+struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
+			    void *user_data);
+
+/**
+ * Filesystem module
+ *
+ * Filesystem modules are registered with the FUSE_REGISTER_MODULE()
+ * macro.
+ *
+ * If the "-omodules=modname:..." option is present, filesystem
+ * objects are created and pushed onto the stack with the 'factory'
+ * function.
+ */
+struct fuse_module {
+	/**
+	 * Name of filesystem
+	 */
+	const char *name;
+
+	/**
+	 * Factory for creating filesystem objects
+	 *
+	 * The function may use and remove options from 'args' that belong
+	 * to this module.
+	 *
+	 * For now the 'fs' vector always contains exactly one filesystem.
+	 * This is the filesystem which will be below the newly created
+	 * filesystem in the stack.
+	 *
+	 * @param args the command line arguments
+	 * @param fs NULL terminated filesystem object vector
+	 * @return the new filesystem object
+	 */
+	struct fuse_fs *(*factory)(struct fuse_args *args,
+				   struct fuse_fs *fs[]);
+
+	struct fuse_module *next;
+	struct fusemod_so *so;
+	int ctr;
+};
+
+/**
+ * Register a filesystem module
+ *
+ * This function is used by FUSE_REGISTER_MODULE and there's usually
+ * no need to call it directly
+ */
+void fuse_register_module(struct fuse_module *mod);
+
+/**
+ * Register filesystem module
+ *
+ * For the parameters, see description of the fields in 'struct
+ * fuse_module'
+ */
+#define FUSE_REGISTER_MODULE(name_, factory_)				  \
+	static __attribute__((constructor)) void name_ ## _register(void) \
+	{								  \
+		static struct fuse_module mod =				  \
+			{ #name_, factory_, NULL, NULL, 0 };		  \
+		fuse_register_module(&mod);				  \
+	}
+
+
+/* ----------------------------------------------------------- *
+ * Advanced API for event handling, don't worry about this...  *
+ * ----------------------------------------------------------- */
+
+/* NOTE: the following functions are deprecated, and will be removed
+   from the 3.0 API.  Use the lowlevel session functions instead */
+
+/** Function type used to process commands */
+typedef void (*fuse_processor_t)(struct fuse *, struct fuse_cmd *, void *);
+
+/** This is the part of fuse_main() before the event loop */
+struct fuse *fuse_setup(int argc, char *argv[],
+			const struct fuse_operations *op, size_t op_size,
+			char **mountpoint, int *multithreaded,
+			void *user_data);
+
+/** This is the part of fuse_main() after the event loop */
+void fuse_teardown(struct fuse *fuse, char *mountpoint);
+
+/** Read a single command.  If none are read, return NULL */
+struct fuse_cmd *fuse_read_cmd(struct fuse *f);
+
+/** Process a single command */
+void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd);
+
+/** Multi threaded event loop, which calls the custom command
+    processor function */
+int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data);
+
+/** Return the exited flag, which indicates if fuse_exit() has been
+    called */
+int fuse_exited(struct fuse *f);
+
+/** This function is obsolete and implemented as a no-op */
+void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
+
+/** Get session from fuse object */
+struct fuse_session *fuse_get_session(struct fuse *f);
+
+/* ----------------------------------------------------------- *
+ * Compatibility stuff					       *
+ * ----------------------------------------------------------- */
+
+#if FUSE_USE_VERSION < 26
+#  include "fuse_compat.h"
+#  undef fuse_main
+#  if FUSE_USE_VERSION == 25
+#    define fuse_main(argc, argv, op)				\
+	fuse_main_real_compat25(argc, argv, op, sizeof(*(op)))
+#    define fuse_new fuse_new_compat25
+#    define fuse_setup fuse_setup_compat25
+#    define fuse_teardown fuse_teardown_compat22
+#    define fuse_operations fuse_operations_compat25
+#  elif FUSE_USE_VERSION == 22
+#    define fuse_main(argc, argv, op)				\
+	fuse_main_real_compat22(argc, argv, op, sizeof(*(op)))
+#    define fuse_new fuse_new_compat22
+#    define fuse_setup fuse_setup_compat22
+#    define fuse_teardown fuse_teardown_compat22
+#    define fuse_operations fuse_operations_compat22
+#    define fuse_file_info fuse_file_info_compat
+#  elif FUSE_USE_VERSION == 24
+#    error Compatibility with high-level API version 24 not supported
+#  else
+#    define fuse_dirfil_t fuse_dirfil_t_compat
+#    define __fuse_read_cmd fuse_read_cmd
+#    define __fuse_process_cmd fuse_process_cmd
+#    define __fuse_loop_mt fuse_loop_mt_proc
+#    if FUSE_USE_VERSION == 21
+#      define fuse_operations fuse_operations_compat2
+#      define fuse_main fuse_main_compat2
+#      define fuse_new fuse_new_compat2
+#      define __fuse_setup fuse_setup_compat2
+#      define __fuse_teardown fuse_teardown_compat22
+#      define __fuse_exited fuse_exited
+#      define __fuse_set_getcontext_func fuse_set_getcontext_func
+#    else
+#      define fuse_statfs fuse_statfs_compat1
+#      define fuse_operations fuse_operations_compat1
+#      define fuse_main fuse_main_compat1
+#      define fuse_new fuse_new_compat1
+#      define FUSE_DEBUG FUSE_DEBUG_COMPAT1
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_H_ */
diff --git a/fuse/include/fuse_common.h b/fuse/include/fuse_common.h
new file mode 100644
index 0000000..dab3a56
--- /dev/null
+++ b/fuse/include/fuse_common.h
@@ -0,0 +1,505 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+/** @file */
+
+#if !defined(_FUSE_H_) && !defined(_FUSE_LOWLEVEL_H_)
+#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
+#endif
+
+#ifndef _FUSE_COMMON_H_
+#define _FUSE_COMMON_H_
+
+#include "fuse_opt.h"
+#include <stdint.h>
+#include <sys/types.h>
+
+/** Major version of FUSE library interface */
+#define FUSE_MAJOR_VERSION 2
+
+/** Minor version of FUSE library interface */
+#define FUSE_MINOR_VERSION 9
+
+#define FUSE_MAKE_VERSION(maj, min)  ((maj) * 10 + (min))
+#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
+
+/* This interface uses 64 bit off64_t */
+#if _FILE_OFFSET_BITS != 64
+#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags!
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Information about open files
+ *
+ * Changed in version 2.5
+ */
+struct fuse_file_info {
+	/** Open flags.	 Available in open() and release() */
+	int flags;
+
+	/** Old file handle, don't use */
+	unsigned long fh_old;
+
+	/** In case of a write operation indicates if this was caused by a
+	    writepage */
+	int writepage;
+
+	/** Can be filled in by open, to use direct I/O on this file.
+	    Introduced in version 2.4 */
+	unsigned int direct_io : 1;
+
+	/** Can be filled in by open, to indicate, that cached file data
+	    need not be invalidated.  Introduced in version 2.4 */
+	unsigned int keep_cache : 1;
+
+	/** Indicates a flush operation.  Set in flush operation, also
+	    maybe set in highlevel lock operation and lowlevel release
+	    operation.	Introduced in version 2.6 */
+	unsigned int flush : 1;
+
+	/** Can be filled in by open, to indicate that the file is not
+	    seekable.  Introduced in version 2.8 */
+	unsigned int nonseekable : 1;
+
+	/* Indicates that flock locks for this file should be
+	   released.  If set, lock_owner shall contain a valid value.
+	   May only be set in ->release().  Introduced in version
+	   2.9 */
+	unsigned int flock_release : 1;
+
+	/** Padding.  Do not use*/
+	unsigned int padding : 27;
+
+	/** File handle.  May be filled in by filesystem in open().
+	    Available in all other file operations */
+	uint64_t fh;
+
+	/** Lock owner id.  Available in locking operations and flush */
+	uint64_t lock_owner;
+};
+
+/**
+ * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want'
+ *
+ * FUSE_CAP_ASYNC_READ: filesystem supports asynchronous read requests
+ * FUSE_CAP_POSIX_LOCKS: filesystem supports "remote" locking
+ * FUSE_CAP_ATOMIC_O_TRUNC: filesystem handles the O_TRUNC open flag
+ * FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
+ * FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
+ * FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
+ * FUSE_CAP_SPLICE_WRITE: ability to use splice() to write to the fuse device
+ * FUSE_CAP_SPLICE_MOVE: ability to move data to the fuse device with splice()
+ * FUSE_CAP_SPLICE_READ: ability to use splice() to read from the fuse device
+ * FUSE_CAP_IOCTL_DIR: ioctl support on directories
+ */
+#define FUSE_CAP_ASYNC_READ	(1 << 0)
+#define FUSE_CAP_POSIX_LOCKS	(1 << 1)
+#define FUSE_CAP_ATOMIC_O_TRUNC	(1 << 3)
+#define FUSE_CAP_EXPORT_SUPPORT	(1 << 4)
+#define FUSE_CAP_BIG_WRITES	(1 << 5)
+#define FUSE_CAP_DONT_MASK	(1 << 6)
+#define FUSE_CAP_SPLICE_WRITE	(1 << 7)
+#define FUSE_CAP_SPLICE_MOVE	(1 << 8)
+#define FUSE_CAP_SPLICE_READ	(1 << 9)
+#define FUSE_CAP_FLOCK_LOCKS	(1 << 10)
+#define FUSE_CAP_IOCTL_DIR	(1 << 11)
+
+/**
+ * Ioctl flags
+ *
+ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
+ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
+ * FUSE_IOCTL_RETRY: retry with new iovecs
+ * FUSE_IOCTL_DIR: is a directory
+ *
+ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
+ */
+#define FUSE_IOCTL_COMPAT	(1 << 0)
+#define FUSE_IOCTL_UNRESTRICTED	(1 << 1)
+#define FUSE_IOCTL_RETRY	(1 << 2)
+#define FUSE_IOCTL_DIR		(1 << 4)
+
+#define FUSE_IOCTL_MAX_IOV	256
+
+/**
+ * Connection information, passed to the ->init() method
+ *
+ * Some of the elements are read-write, these can be changed to
+ * indicate the value requested by the filesystem.  The requested
+ * value must usually be smaller than the indicated value.
+ */
+struct fuse_conn_info {
+	/**
+	 * Major version of the protocol (read-only)
+	 */
+	unsigned proto_major;
+
+	/**
+	 * Minor version of the protocol (read-only)
+	 */
+	unsigned proto_minor;
+
+	/**
+	 * Is asynchronous read supported (read-write)
+	 */
+	unsigned async_read;
+
+	/**
+	 * Maximum size of the write buffer
+	 */
+	unsigned max_write;
+
+	/**
+	 * Maximum readahead
+	 */
+	unsigned max_readahead;
+
+	/**
+	 * Capability flags, that the kernel supports
+	 */
+	unsigned capable;
+
+	/**
+	 * Capability flags, that the filesystem wants to enable
+	 */
+	unsigned want;
+
+	/**
+	 * Maximum number of backgrounded requests
+	 */
+	unsigned max_background;
+
+	/**
+	 * Kernel congestion threshold parameter
+	 */
+	unsigned congestion_threshold;
+
+	/**
+	 * For future use.
+	 */
+	unsigned reserved[23];
+};
+
+struct fuse_session;
+struct fuse_chan;
+struct fuse_pollhandle;
+
+/**
+ * Create a FUSE mountpoint
+ *
+ * Returns a control file descriptor suitable for passing to
+ * fuse_new()
+ *
+ * @param mountpoint the mount point path
+ * @param args argument vector
+ * @return the communication channel on success, NULL on failure
+ */
+struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args);
+
+/**
+ * Umount a FUSE mountpoint
+ *
+ * @param mountpoint the mount point path
+ * @param ch the communication channel
+ */
+void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
+
+/**
+ * Parse common options
+ *
+ * The following options are parsed:
+ *
+ *   '-f'	     foreground
+ *   '-d' '-odebug'  foreground, but keep the debug option
+ *   '-s'	     single threaded
+ *   '-h' '--help'   help
+ *   '-ho'	     help without header
+ *   '-ofsname=..'   file system name, if not present, then set to the program
+ *		     name
+ *
+ * All parameters may be NULL
+ *
+ * @param args argument vector
+ * @param mountpoint the returned mountpoint, should be freed after use
+ * @param multithreaded set to 1 unless the '-s' option is present
+ * @param foreground set to 1 if one of the relevant options is present
+ * @return 0 on success, -1 on failure
+ */
+int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint,
+		       int *multithreaded, int *foreground);
+
+/**
+ * Go into the background
+ *
+ * @param foreground if true, stay in the foreground
+ * @return 0 on success, -1 on failure
+ */
+int fuse_daemonize(int foreground);
+
+/**
+ * Get the version of the library
+ *
+ * @return the version
+ */
+int fuse_version(void);
+
+/**
+ * Destroy poll handle
+ *
+ * @param ph the poll handle
+ */
+void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
+
+/* ----------------------------------------------------------- *
+ * Data buffer						       *
+ * ----------------------------------------------------------- */
+
+/**
+ * Buffer flags
+ */
+enum fuse_buf_flags {
+	/**
+	 * Buffer contains a file descriptor
+	 *
+	 * If this flag is set, the .fd field is valid, otherwise the
+	 * .mem fields is valid.
+	 */
+	FUSE_BUF_IS_FD		= (1 << 1),
+
+	/**
+	 * Seek on the file descriptor
+	 *
+	 * If this flag is set then the .pos field is valid and is
+	 * used to seek to the given offset before performing
+	 * operation on file descriptor.
+	 */
+	FUSE_BUF_FD_SEEK	= (1 << 2),
+
+	/**
+	 * Retry operation on file descriptor
+	 *
+	 * If this flag is set then retry operation on file descriptor
+	 * until .size bytes have been copied or an error or EOF is
+	 * detected.
+	 */
+	FUSE_BUF_FD_RETRY	= (1 << 3),
+};
+
+/**
+ * Buffer copy flags
+ */
+enum fuse_buf_copy_flags {
+	/**
+	 * Don't use splice(2)
+	 *
+	 * Always fall back to using read and write instead of
+	 * splice(2) to copy data from one file descriptor to another.
+	 *
+	 * If this flag is not set, then only fall back if splice is
+	 * unavailable.
+	 */
+	FUSE_BUF_NO_SPLICE	= (1 << 1),
+
+	/**
+	 * Force splice
+	 *
+	 * Always use splice(2) to copy data from one file descriptor
+	 * to another.  If splice is not available, return -EINVAL.
+	 */
+	FUSE_BUF_FORCE_SPLICE	= (1 << 2),
+
+	/**
+	 * Try to move data with splice.
+	 *
+	 * If splice is used, try to move pages from the source to the
+	 * destination instead of copying.  See documentation of
+	 * SPLICE_F_MOVE in splice(2) man page.
+	 */
+	FUSE_BUF_SPLICE_MOVE	= (1 << 3),
+
+	/**
+	 * Don't block on the pipe when copying data with splice
+	 *
+	 * Makes the operations on the pipe non-blocking (if the pipe
+	 * is full or empty).  See SPLICE_F_NONBLOCK in the splice(2)
+	 * man page.
+	 */
+	FUSE_BUF_SPLICE_NONBLOCK= (1 << 4),
+};
+
+/**
+ * Single data buffer
+ *
+ * Generic data buffer for I/O, extended attributes, etc...  Data may
+ * be supplied as a memory pointer or as a file descriptor
+ */
+struct fuse_buf {
+	/**
+	 * Size of data in bytes
+	 */
+	size_t size;
+
+	/**
+	 * Buffer flags
+	 */
+	enum fuse_buf_flags flags;
+
+	/**
+	 * Memory pointer
+	 *
+	 * Used unless FUSE_BUF_IS_FD flag is set.
+	 */
+	void *mem;
+
+	/**
+	 * File descriptor
+	 *
+	 * Used if FUSE_BUF_IS_FD flag is set.
+	 */
+	int fd;
+
+	/**
+	 * File position
+	 *
+	 * Used if FUSE_BUF_FD_SEEK flag is set.
+	 */
+	off64_t pos;
+};
+
+/**
+ * Data buffer vector
+ *
+ * An array of data buffers, each containing a memory pointer or a
+ * file descriptor.
+ *
+ * Allocate dynamically to add more than one buffer.
+ */
+struct fuse_bufvec {
+	/**
+	 * Number of buffers in the array
+	 */
+	size_t count;
+
+	/**
+	 * Index of current buffer within the array
+	 */
+	size_t idx;
+
+	/**
+	 * Current offset within the current buffer
+	 */
+	size_t off;
+
+	/**
+	 * Array of buffers
+	 */
+	struct fuse_buf buf[1];
+};
+
+/* Initialize bufvec with a single buffer of given size */
+#define FUSE_BUFVEC_INIT(size__) 				\
+	((struct fuse_bufvec) {					\
+		/* .count= */ 1,				\
+		/* .idx =  */ 0,				\
+		/* .off =  */ 0,				\
+		/* .buf =  */ { /* [0] = */ {			\
+			/* .size =  */ (size__),		\
+			/* .flags = */ (enum fuse_buf_flags) 0,	\
+			/* .mem =   */ NULL,			\
+			/* .fd =    */ -1,			\
+			/* .pos =   */ 0,			\
+		} }						\
+	} )
+
+/**
+ * Get total size of data in a fuse buffer vector
+ *
+ * @param bufv buffer vector
+ * @return size of data
+ */
+size_t fuse_buf_size(const struct fuse_bufvec *bufv);
+
+/**
+ * Copy data from one buffer vector to another
+ *
+ * @param dst destination buffer vector
+ * @param src source buffer vector
+ * @param flags flags controlling the copy
+ * @return actual number of bytes copied or -errno on error
+ */
+ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
+		      enum fuse_buf_copy_flags flags);
+
+/* ----------------------------------------------------------- *
+ * Signal handling					       *
+ * ----------------------------------------------------------- */
+
+/**
+ * Exit session on HUP, TERM and INT signals and ignore PIPE signal
+ *
+ * Stores session in a global variable.	 May only be called once per
+ * process until fuse_remove_signal_handlers() is called.
+ *
+ * @param se the session to exit
+ * @return 0 on success, -1 on failure
+ */
+int fuse_set_signal_handlers(struct fuse_session *se);
+
+/**
+ * Restore default signal handlers
+ *
+ * Resets global session.  After this fuse_set_signal_handlers() may
+ * be called again.
+ *
+ * @param se the same session as given in fuse_set_signal_handlers()
+ */
+void fuse_remove_signal_handlers(struct fuse_session *se);
+
+/* ----------------------------------------------------------- *
+ * Compatibility stuff					       *
+ * ----------------------------------------------------------- */
+
+#if FUSE_USE_VERSION < 26
+#    ifdef __FreeBSD__
+#	 if FUSE_USE_VERSION < 25
+#	     error On FreeBSD API version 25 or greater must be used
+#	 endif
+#    endif
+#    include "fuse_common_compat.h"
+#    undef FUSE_MINOR_VERSION
+#    undef fuse_main
+#    define fuse_unmount fuse_unmount_compat22
+#    if FUSE_USE_VERSION == 25
+#	 define FUSE_MINOR_VERSION 5
+#	 define fuse_mount fuse_mount_compat25
+#    elif FUSE_USE_VERSION == 24 || FUSE_USE_VERSION == 22
+#	 define FUSE_MINOR_VERSION 4
+#	 define fuse_mount fuse_mount_compat22
+#    elif FUSE_USE_VERSION == 21
+#	 define FUSE_MINOR_VERSION 1
+#	 define fuse_mount fuse_mount_compat22
+#    elif FUSE_USE_VERSION == 11
+#	 warning Compatibility with API version 11 is deprecated
+#	 undef FUSE_MAJOR_VERSION
+#	 define FUSE_MAJOR_VERSION 1
+#	 define FUSE_MINOR_VERSION 1
+#	 define fuse_mount fuse_mount_compat1
+#    else
+#	 error Compatibility with API version other than 21, 22, 24, 25 and 11 not supported
+#    endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_COMMON_H_ */
diff --git a/fuse/include/fuse_common_compat.h b/fuse/include/fuse_common_compat.h
new file mode 100644
index 0000000..34440ff
--- /dev/null
+++ b/fuse/include/fuse_common_compat.h
@@ -0,0 +1,26 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+/* these definitions provide source compatibility to prior versions.
+   Do not include this file directly! */
+
+struct fuse_file_info_compat {
+	int flags;
+	unsigned long fh;
+	int writepage;
+	unsigned int direct_io : 1;
+	unsigned int keep_cache : 1;
+};
+
+int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args);
+
+int fuse_mount_compat22(const char *mountpoint, const char *opts);
+
+int fuse_mount_compat1(const char *mountpoint, const char *args[]);
+
+void fuse_unmount_compat22(const char *mountpoint);
diff --git a/fuse/include/fuse_compat.h b/fuse/include/fuse_compat.h
new file mode 100644
index 0000000..d093238
--- /dev/null
+++ b/fuse/include/fuse_compat.h
@@ -0,0 +1,201 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+/* these definitions provide source compatibility to prior versions.
+   Do not include this file directly! */
+
+struct fuse_operations_compat25 {
+	int (*getattr) (const char *, struct stat *);
+	int (*readlink) (const char *, char *, size_t);
+	int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
+	int (*mknod) (const char *, mode_t, dev_t);
+	int (*mkdir) (const char *, mode_t);
+	int (*unlink) (const char *);
+	int (*rmdir) (const char *);
+	int (*symlink) (const char *, const char *);
+	int (*rename) (const char *, const char *);
+	int (*link) (const char *, const char *);
+	int (*chmod) (const char *, mode_t);
+	int (*chown) (const char *, uid_t, gid_t);
+	int (*truncate) (const char *, off64_t);
+	int (*utime) (const char *, struct utimbuf *);
+	int (*open) (const char *, struct fuse_file_info *);
+	int (*read) (const char *, char *, size_t, off64_t,
+		     struct fuse_file_info *);
+	int (*write) (const char *, const char *, size_t, off64_t,
+		      struct fuse_file_info *);
+	int (*statfs) (const char *, struct statvfs *);
+	int (*flush) (const char *, struct fuse_file_info *);
+	int (*release) (const char *, struct fuse_file_info *);
+	int (*fsync) (const char *, int, struct fuse_file_info *);
+	int (*setxattr) (const char *, const char *, const char *, size_t, int);
+	int (*getxattr) (const char *, const char *, char *, size_t);
+	int (*listxattr) (const char *, char *, size_t);
+	int (*removexattr) (const char *, const char *);
+	int (*opendir) (const char *, struct fuse_file_info *);
+	int (*readdir) (const char *, void *, fuse_fill_dir_t, off64_t,
+			struct fuse_file_info *);
+	int (*releasedir) (const char *, struct fuse_file_info *);
+	int (*fsyncdir) (const char *, int, struct fuse_file_info *);
+	void *(*init) (void);
+	void (*destroy) (void *);
+	int (*access) (const char *, int);
+	int (*create) (const char *, mode_t, struct fuse_file_info *);
+	int (*ftruncate) (const char *, off64_t, struct fuse_file_info *);
+	int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
+};
+
+struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
+			       const struct fuse_operations_compat25 *op,
+			       size_t op_size);
+
+int fuse_main_real_compat25(int argc, char *argv[],
+			    const struct fuse_operations_compat25 *op,
+			    size_t op_size);
+
+struct fuse *fuse_setup_compat25(int argc, char *argv[],
+				 const struct fuse_operations_compat25 *op,
+				 size_t op_size, char **mountpoint,
+				 int *multithreaded, int *fd);
+
+void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint);
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+#include <sys/statfs.h>
+
+struct fuse_operations_compat22 {
+	int (*getattr) (const char *, struct stat *);
+	int (*readlink) (const char *, char *, size_t);
+	int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
+	int (*mknod) (const char *, mode_t, dev_t);
+	int (*mkdir) (const char *, mode_t);
+	int (*unlink) (const char *);
+	int (*rmdir) (const char *);
+	int (*symlink) (const char *, const char *);
+	int (*rename) (const char *, const char *);
+	int (*link) (const char *, const char *);
+	int (*chmod) (const char *, mode_t);
+	int (*chown) (const char *, uid_t, gid_t);
+	int (*truncate) (const char *, off64_t);
+	int (*utime) (const char *, struct utimbuf *);
+	int (*open) (const char *, struct fuse_file_info_compat *);
+	int (*read) (const char *, char *, size_t, off64_t,
+		     struct fuse_file_info_compat *);
+	int (*write) (const char *, const char *, size_t, off64_t,
+		      struct fuse_file_info_compat *);
+	int (*statfs) (const char *, struct statfs *);
+	int (*flush) (const char *, struct fuse_file_info_compat *);
+	int (*release) (const char *, struct fuse_file_info_compat *);
+	int (*fsync) (const char *, int, struct fuse_file_info_compat *);
+	int (*setxattr) (const char *, const char *, const char *, size_t, int);
+	int (*getxattr) (const char *, const char *, char *, size_t);
+	int (*listxattr) (const char *, char *, size_t);
+	int (*removexattr) (const char *, const char *);
+	int (*opendir) (const char *, struct fuse_file_info_compat *);
+	int (*readdir) (const char *, void *, fuse_fill_dir_t, off64_t,
+			struct fuse_file_info_compat *);
+	int (*releasedir) (const char *, struct fuse_file_info_compat *);
+	int (*fsyncdir) (const char *, int, struct fuse_file_info_compat *);
+	void *(*init) (void);
+	void (*destroy) (void *);
+};
+
+struct fuse *fuse_new_compat22(int fd, const char *opts,
+			       const struct fuse_operations_compat22 *op,
+			       size_t op_size);
+
+struct fuse *fuse_setup_compat22(int argc, char *argv[],
+				 const struct fuse_operations_compat22 *op,
+				 size_t op_size, char **mountpoint,
+				 int *multithreaded, int *fd);
+
+int fuse_main_real_compat22(int argc, char *argv[],
+			    const struct fuse_operations_compat22 *op,
+			    size_t op_size);
+
+typedef int (*fuse_dirfil_t_compat) (fuse_dirh_t h, const char *name, int type);
+struct fuse_operations_compat2 {
+	int (*getattr)	   (const char *, struct stat *);
+	int (*readlink)	   (const char *, char *, size_t);
+	int (*getdir)	   (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
+	int (*mknod)	   (const char *, mode_t, dev_t);
+	int (*mkdir)	   (const char *, mode_t);
+	int (*unlink)	   (const char *);
+	int (*rmdir)	   (const char *);
+	int (*symlink)	   (const char *, const char *);
+	int (*rename)	   (const char *, const char *);
+	int (*link)	   (const char *, const char *);
+	int (*chmod)	   (const char *, mode_t);
+	int (*chown)	   (const char *, uid_t, gid_t);
+	int (*truncate)	   (const char *, off64_t);
+	int (*utime)	   (const char *, struct utimbuf *);
+	int (*open)	   (const char *, int);
+	int (*read)	   (const char *, char *, size_t, off64_t);
+	int (*write)	   (const char *, const char *, size_t, off64_t);
+	int (*statfs)	   (const char *, struct statfs *);
+	int (*flush)	   (const char *);
+	int (*release)	   (const char *, int);
+	int (*fsync)	   (const char *, int);
+	int (*setxattr)	   (const char *, const char *, const char *,
+			    size_t, int);
+	int (*getxattr)	   (const char *, const char *, char *, size_t);
+	int (*listxattr)   (const char *, char *, size_t);
+	int (*removexattr) (const char *, const char *);
+};
+
+int fuse_main_compat2(int argc, char *argv[],
+		      const struct fuse_operations_compat2 *op);
+
+struct fuse *fuse_new_compat2(int fd, const char *opts,
+			      const struct fuse_operations_compat2 *op);
+
+struct fuse *fuse_setup_compat2(int argc, char *argv[],
+				const struct fuse_operations_compat2 *op,
+				char **mountpoint, int *multithreaded, int *fd);
+
+struct fuse_statfs_compat1 {
+	long block_size;
+	long blocks;
+	long blocks_free;
+	long files;
+	long files_free;
+	long namelen;
+};
+
+struct fuse_operations_compat1 {
+	int (*getattr)	(const char *, struct stat *);
+	int (*readlink) (const char *, char *, size_t);
+	int (*getdir)	(const char *, fuse_dirh_t, fuse_dirfil_t_compat);
+	int (*mknod)	(const char *, mode_t, dev_t);
+	int (*mkdir)	(const char *, mode_t);
+	int (*unlink)	(const char *);
+	int (*rmdir)	(const char *);
+	int (*symlink)	(const char *, const char *);
+	int (*rename)	(const char *, const char *);
+	int (*link)	(const char *, const char *);
+	int (*chmod)	(const char *, mode_t);
+	int (*chown)	(const char *, uid_t, gid_t);
+	int (*truncate) (const char *, off64_t);
+	int (*utime)	(const char *, struct utimbuf *);
+	int (*open)	(const char *, int);
+	int (*read)	(const char *, char *, size_t, off64_t);
+	int (*write)	(const char *, const char *, size_t, off64_t);
+	int (*statfs)	(struct fuse_statfs_compat1 *);
+	int (*release)	(const char *, int);
+	int (*fsync)	(const char *, int);
+};
+
+#define FUSE_DEBUG_COMPAT1	 (1 << 1)
+
+struct fuse *fuse_new_compat1(int fd, int flags,
+			      const struct fuse_operations_compat1 *op);
+
+void fuse_main_compat1(int argc, char *argv[],
+		       const struct fuse_operations_compat1 *op);
+
+#endif /* __FreeBSD__ || __NetBSD__ */
diff --git a/fuse/include/fuse_kernel.h b/fuse/include/fuse_kernel.h
new file mode 100644
index 0000000..c632b58
--- /dev/null
+++ b/fuse/include/fuse_kernel.h
@@ -0,0 +1,691 @@
+/*
+    This file defines the kernel interface of FUSE
+    Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU GPL.
+    See the file COPYING.
+
+    This -- and only this -- header file may also be distributed under
+    the terms of the BSD Licence as follows:
+
+    Copyright (C) 2001-2007 Miklos Szeredi. 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.
+
+    THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+*/
+
+/*
+ * This file defines the kernel interface of FUSE
+ *
+ * Protocol changelog:
+ *
+ * 7.9:
+ *  - new fuse_getattr_in input argument of GETATTR
+ *  - add lk_flags in fuse_lk_in
+ *  - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
+ *  - add blksize field to fuse_attr
+ *  - add file flags field to fuse_read_in and fuse_write_in
+ *
+ * 7.10
+ *  - add nonseekable open flag
+ *
+ * 7.11
+ *  - add IOCTL message
+ *  - add unsolicited notification support
+ *  - add POLL message and NOTIFY_POLL notification
+ *
+ * 7.12
+ *  - add umask flag to input argument of open, mknod and mkdir
+ *  - add notification messages for invalidation of inodes and
+ *    directory entries
+ *
+ * 7.13
+ *  - make max number of background requests and congestion threshold
+ *    tunables
+ *
+ * 7.14
+ *  - add splice support to fuse device
+ *
+ * 7.15
+ *  - add store notify
+ *  - add retrieve notify
+ *
+ * 7.16
+ *  - add BATCH_FORGET request
+ *  - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct
+ *    fuse_ioctl_iovec' instead of ambiguous 'struct iovec'
+ *  - add FUSE_IOCTL_32BIT flag
+ *
+ * 7.17
+ *  - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK
+ *
+ * 7.18
+ *  - add FUSE_IOCTL_DIR flag
+ *  - add FUSE_NOTIFY_DELETE
+ *
+ * 7.19
+ *  - add FUSE_FALLOCATE
+ */
+
+#ifndef _LINUX_FUSE_H
+#define _LINUX_FUSE_H
+
+#include <sys/types.h>
+#define __u64 uint64_t
+#define __s64 int64_t
+#define __u32 uint32_t
+#define __s32 int32_t
+#define __u16 uint16_t
+
+/*
+ * Version negotiation:
+ *
+ * Both the kernel and userspace send the version they support in the
+ * INIT request and reply respectively.
+ *
+ * If the major versions match then both shall use the smallest
+ * of the two minor versions for communication.
+ *
+ * If the kernel supports a larger major version, then userspace shall
+ * reply with the major version it supports, ignore the rest of the
+ * INIT message and expect a new INIT message from the kernel with a
+ * matching major version.
+ *
+ * If the library supports a larger major version, then it shall fall
+ * back to the major protocol version sent by the kernel for
+ * communication and reply with that major version (and an arbitrary
+ * supported minor version).
+ */
+
+/** Version number of this interface */
+#define FUSE_KERNEL_VERSION 7
+
+/** Minor version number of this interface */
+#define FUSE_KERNEL_MINOR_VERSION 19
+
+/** The node ID of the root inode */
+#define FUSE_ROOT_ID 1
+
+/* Make sure all structures are padded to 64bit boundary, so 32bit
+   userspace works under 64bit kernels */
+
+struct fuse_attr {
+	__u64	ino;
+	__u64	size;
+	__u64	blocks;
+	__u64	atime;
+	__u64	mtime;
+	__u64	ctime;
+	__u32	atimensec;
+	__u32	mtimensec;
+	__u32	ctimensec;
+	__u32	mode;
+	__u32	nlink;
+	__u32	uid;
+	__u32	gid;
+	__u32	rdev;
+	__u32	blksize;
+	__u32	padding;
+};
+
+struct fuse_kstatfs {
+	__u64	blocks;
+	__u64	bfree;
+	__u64	bavail;
+	__u64	files;
+	__u64	ffree;
+	__u32	bsize;
+	__u32	namelen;
+	__u32	frsize;
+	__u32	padding;
+	__u32	spare[6];
+};
+
+struct fuse_file_lock {
+	__u64	start;
+	__u64	end;
+	__u32	type;
+	__u32	pid; /* tgid */
+};
+
+/**
+ * Bitmasks for fuse_setattr_in.valid
+ */
+#define FATTR_MODE	(1 << 0)
+#define FATTR_UID	(1 << 1)
+#define FATTR_GID	(1 << 2)
+#define FATTR_SIZE	(1 << 3)
+#define FATTR_ATIME	(1 << 4)
+#define FATTR_MTIME	(1 << 5)
+#define FATTR_FH	(1 << 6)
+#define FATTR_ATIME_NOW	(1 << 7)
+#define FATTR_MTIME_NOW	(1 << 8)
+#define FATTR_LOCKOWNER	(1 << 9)
+
+/**
+ * Flags returned by the OPEN request
+ *
+ * FOPEN_DIRECT_IO: bypass page cache for this open file
+ * FOPEN_KEEP_CACHE: don't invalidate the data cache on open
+ * FOPEN_NONSEEKABLE: the file is not seekable
+ */
+#define FOPEN_DIRECT_IO		(1 << 0)
+#define FOPEN_KEEP_CACHE	(1 << 1)
+#define FOPEN_NONSEEKABLE	(1 << 2)
+
+/**
+ * INIT request/reply flags
+ *
+ * FUSE_POSIX_LOCKS: remote locking for POSIX file locks
+ * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
+ * FUSE_DONT_MASK: don't apply umask to file mode on create operations
+ * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks
+ */
+#define FUSE_ASYNC_READ		(1 << 0)
+#define FUSE_POSIX_LOCKS	(1 << 1)
+#define FUSE_FILE_OPS		(1 << 2)
+#define FUSE_ATOMIC_O_TRUNC	(1 << 3)
+#define FUSE_EXPORT_SUPPORT	(1 << 4)
+#define FUSE_BIG_WRITES		(1 << 5)
+#define FUSE_DONT_MASK		(1 << 6)
+#define FUSE_FLOCK_LOCKS	(1 << 10)
+
+/**
+ * CUSE INIT request/reply flags
+ *
+ * CUSE_UNRESTRICTED_IOCTL:  use unrestricted ioctl
+ */
+#define CUSE_UNRESTRICTED_IOCTL	(1 << 0)
+
+/**
+ * Release flags
+ */
+#define FUSE_RELEASE_FLUSH	(1 << 0)
+#define FUSE_RELEASE_FLOCK_UNLOCK	(1 << 1)
+
+/**
+ * Getattr flags
+ */
+#define FUSE_GETATTR_FH		(1 << 0)
+
+/**
+ * Lock flags
+ */
+#define FUSE_LK_FLOCK		(1 << 0)
+
+/**
+ * WRITE flags
+ *
+ * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
+ * FUSE_WRITE_LOCKOWNER: lock_owner field is valid
+ */
+#define FUSE_WRITE_CACHE	(1 << 0)
+#define FUSE_WRITE_LOCKOWNER	(1 << 1)
+
+/**
+ * Read flags
+ */
+#define FUSE_READ_LOCKOWNER	(1 << 1)
+
+/**
+ * Ioctl flags
+ *
+ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
+ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
+ * FUSE_IOCTL_RETRY: retry with new iovecs
+ * FUSE_IOCTL_32BIT: 32bit ioctl
+ * FUSE_IOCTL_DIR: is a directory
+ *
+ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
+ */
+#define FUSE_IOCTL_COMPAT	(1 << 0)
+#define FUSE_IOCTL_UNRESTRICTED	(1 << 1)
+#define FUSE_IOCTL_RETRY	(1 << 2)
+#define FUSE_IOCTL_32BIT	(1 << 3)
+#define FUSE_IOCTL_DIR		(1 << 4)
+
+#define FUSE_IOCTL_MAX_IOV	256
+
+/**
+ * Poll flags
+ *
+ * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify
+ */
+#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
+
+enum fuse_opcode {
+	FUSE_LOOKUP	   = 1,
+	FUSE_FORGET	   = 2,  /* no reply */
+	FUSE_GETATTR	   = 3,
+	FUSE_SETATTR	   = 4,
+	FUSE_READLINK	   = 5,
+	FUSE_SYMLINK	   = 6,
+	FUSE_MKNOD	   = 8,
+	FUSE_MKDIR	   = 9,
+	FUSE_UNLINK	   = 10,
+	FUSE_RMDIR	   = 11,
+	FUSE_RENAME	   = 12,
+	FUSE_LINK	   = 13,
+	FUSE_OPEN	   = 14,
+	FUSE_READ	   = 15,
+	FUSE_WRITE	   = 16,
+	FUSE_STATFS	   = 17,
+	FUSE_RELEASE       = 18,
+	FUSE_FSYNC         = 20,
+	FUSE_SETXATTR      = 21,
+	FUSE_GETXATTR      = 22,
+	FUSE_LISTXATTR     = 23,
+	FUSE_REMOVEXATTR   = 24,
+	FUSE_FLUSH         = 25,
+	FUSE_INIT          = 26,
+	FUSE_OPENDIR       = 27,
+	FUSE_READDIR       = 28,
+	FUSE_RELEASEDIR    = 29,
+	FUSE_FSYNCDIR      = 30,
+	FUSE_GETLK         = 31,
+	FUSE_SETLK         = 32,
+	FUSE_SETLKW        = 33,
+	FUSE_ACCESS        = 34,
+	FUSE_CREATE        = 35,
+	FUSE_INTERRUPT     = 36,
+	FUSE_BMAP          = 37,
+	FUSE_DESTROY       = 38,
+	FUSE_IOCTL         = 39,
+	FUSE_POLL          = 40,
+	FUSE_NOTIFY_REPLY  = 41,
+	FUSE_BATCH_FORGET  = 42,
+	FUSE_FALLOCATE     = 43,
+
+	/* CUSE specific operations */
+	CUSE_INIT          = 4096,
+};
+
+enum fuse_notify_code {
+	FUSE_NOTIFY_POLL   = 1,
+	FUSE_NOTIFY_INVAL_INODE = 2,
+	FUSE_NOTIFY_INVAL_ENTRY = 3,
+	FUSE_NOTIFY_STORE = 4,
+	FUSE_NOTIFY_RETRIEVE = 5,
+	FUSE_NOTIFY_DELETE = 6,
+	FUSE_NOTIFY_CODE_MAX,
+};
+
+/* The read buffer is required to be at least 8k, but may be much larger */
+#define FUSE_MIN_READ_BUFFER 8192
+
+#define FUSE_COMPAT_ENTRY_OUT_SIZE 120
+
+struct fuse_entry_out {
+	__u64	nodeid;		/* Inode ID */
+	__u64	generation;	/* Inode generation: nodeid:gen must
+				   be unique for the fs's lifetime */
+	__u64	entry_valid;	/* Cache timeout for the name */
+	__u64	attr_valid;	/* Cache timeout for the attributes */
+	__u32	entry_valid_nsec;
+	__u32	attr_valid_nsec;
+	struct fuse_attr attr;
+};
+
+struct fuse_forget_in {
+	__u64	nlookup;
+};
+
+struct fuse_forget_one {
+	__u64	nodeid;
+	__u64	nlookup;
+};
+
+struct fuse_batch_forget_in {
+	__u32	count;
+	__u32	dummy;
+};
+
+struct fuse_getattr_in {
+	__u32	getattr_flags;
+	__u32	dummy;
+	__u64	fh;
+};
+
+#define FUSE_COMPAT_ATTR_OUT_SIZE 96
+
+struct fuse_attr_out {
+	__u64	attr_valid;	/* Cache timeout for the attributes */
+	__u32	attr_valid_nsec;
+	__u32	dummy;
+	struct fuse_attr attr;
+};
+
+#define FUSE_COMPAT_MKNOD_IN_SIZE 8
+
+struct fuse_mknod_in {
+	__u32	mode;
+	__u32	rdev;
+	__u32	umask;
+	__u32	padding;
+};
+
+struct fuse_mkdir_in {
+	__u32	mode;
+	__u32	umask;
+};
+
+struct fuse_rename_in {
+	__u64	newdir;
+};
+
+struct fuse_link_in {
+	__u64	oldnodeid;
+};
+
+struct fuse_setattr_in {
+	__u32	valid;
+	__u32	padding;
+	__u64	fh;
+	__u64	size;
+	__u64	lock_owner;
+	__u64	atime;
+	__u64	mtime;
+	__u64	unused2;
+	__u32	atimensec;
+	__u32	mtimensec;
+	__u32	unused3;
+	__u32	mode;
+	__u32	unused4;
+	__u32	uid;
+	__u32	gid;
+	__u32	unused5;
+};
+
+struct fuse_open_in {
+	__u32	flags;
+	__u32	unused;
+};
+
+struct fuse_create_in {
+	__u32	flags;
+	__u32	mode;
+	__u32	umask;
+	__u32	padding;
+};
+
+struct fuse_open_out {
+	__u64	fh;
+	__u32	open_flags;
+	__u32	padding;
+};
+
+struct fuse_release_in {
+	__u64	fh;
+	__u32	flags;
+	__u32	release_flags;
+	__u64	lock_owner;
+};
+
+struct fuse_flush_in {
+	__u64	fh;
+	__u32	unused;
+	__u32	padding;
+	__u64	lock_owner;
+};
+
+struct fuse_read_in {
+	__u64	fh;
+	__u64	offset;
+	__u32	size;
+	__u32	read_flags;
+	__u64	lock_owner;
+	__u32	flags;
+	__u32	padding;
+};
+
+#define FUSE_COMPAT_WRITE_IN_SIZE 24
+
+struct fuse_write_in {
+	__u64	fh;
+	__u64	offset;
+	__u32	size;
+	__u32	write_flags;
+	__u64	lock_owner;
+	__u32	flags;
+	__u32	padding;
+};
+
+struct fuse_write_out {
+	__u32	size;
+	__u32	padding;
+};
+
+#define FUSE_COMPAT_STATFS_SIZE 48
+
+struct fuse_statfs_out {
+	struct fuse_kstatfs st;
+};
+
+struct fuse_fsync_in {
+	__u64	fh;
+	__u32	fsync_flags;
+	__u32	padding;
+};
+
+struct fuse_setxattr_in {
+	__u32	size;
+	__u32	flags;
+};
+
+struct fuse_getxattr_in {
+	__u32	size;
+	__u32	padding;
+};
+
+struct fuse_getxattr_out {
+	__u32	size;
+	__u32	padding;
+};
+
+struct fuse_lk_in {
+	__u64	fh;
+	__u64	owner;
+	struct fuse_file_lock lk;
+	__u32	lk_flags;
+	__u32	padding;
+};
+
+struct fuse_lk_out {
+	struct fuse_file_lock lk;
+};
+
+struct fuse_access_in {
+	__u32	mask;
+	__u32	padding;
+};
+
+struct fuse_init_in {
+	__u32	major;
+	__u32	minor;
+	__u32	max_readahead;
+	__u32	flags;
+};
+
+struct fuse_init_out {
+	__u32	major;
+	__u32	minor;
+	__u32	max_readahead;
+	__u32	flags;
+	__u16   max_background;
+	__u16   congestion_threshold;
+	__u32	max_write;
+};
+
+#define CUSE_INIT_INFO_MAX 4096
+
+struct cuse_init_in {
+	__u32	major;
+	__u32	minor;
+	__u32	unused;
+	__u32	flags;
+};
+
+struct cuse_init_out {
+	__u32	major;
+	__u32	minor;
+	__u32	unused;
+	__u32	flags;
+	__u32	max_read;
+	__u32	max_write;
+	__u32	dev_major;		/* chardev major */
+	__u32	dev_minor;		/* chardev minor */
+	__u32	spare[10];
+};
+
+struct fuse_interrupt_in {
+	__u64	unique;
+};
+
+struct fuse_bmap_in {
+	__u64	block;
+	__u32	blocksize;
+	__u32	padding;
+};
+
+struct fuse_bmap_out {
+	__u64	block;
+};
+
+struct fuse_ioctl_in {
+	__u64	fh;
+	__u32	flags;
+	__u32	cmd;
+	__u64	arg;
+	__u32	in_size;
+	__u32	out_size;
+};
+
+struct fuse_ioctl_iovec {
+	__u64	base;
+	__u64	len;
+};
+
+struct fuse_ioctl_out {
+	__s32	result;
+	__u32	flags;
+	__u32	in_iovs;
+	__u32	out_iovs;
+};
+
+struct fuse_poll_in {
+	__u64	fh;
+	__u64	kh;
+	__u32	flags;
+	__u32   padding;
+};
+
+struct fuse_poll_out {
+	__u32	revents;
+	__u32	padding;
+};
+
+struct fuse_notify_poll_wakeup_out {
+	__u64	kh;
+};
+
+struct fuse_fallocate_in {
+	__u64	fh;
+	__u64	offset;
+	__u64	length;
+	__u32	mode;
+	__u32	padding;
+};
+
+struct fuse_in_header {
+	__u32	len;
+	__u32	opcode;
+	__u64	unique;
+	__u64	nodeid;
+	__u32	uid;
+	__u32	gid;
+	__u32	pid;
+	__u32	padding;
+};
+
+struct fuse_out_header {
+	__u32	len;
+	__s32	error;
+	__u64	unique;
+};
+
+struct fuse_dirent {
+	__u64	ino;
+	__u64	off;
+	__u32	namelen;
+	__u32	type;
+	char name[];
+};
+
+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
+#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1))
+#define FUSE_DIRENT_SIZE(d) \
+	FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
+
+struct fuse_notify_inval_inode_out {
+	__u64	ino;
+	__s64	off;
+	__s64	len;
+};
+
+struct fuse_notify_inval_entry_out {
+	__u64	parent;
+	__u32	namelen;
+	__u32	padding;
+};
+
+struct fuse_notify_delete_out {
+	__u64	parent;
+	__u64	child;
+	__u32	namelen;
+	__u32	padding;
+};
+
+struct fuse_notify_store_out {
+	__u64	nodeid;
+	__u64	offset;
+	__u32	size;
+	__u32	padding;
+};
+
+struct fuse_notify_retrieve_out {
+	__u64	notify_unique;
+	__u64	nodeid;
+	__u64	offset;
+	__u32	size;
+	__u32	padding;
+};
+
+/* Matches the size of fuse_write_in */
+struct fuse_notify_retrieve_in {
+	__u64	dummy1;
+	__u64	offset;
+	__u32	size;
+	__u32	dummy2;
+	__u64	dummy3;
+	__u64	dummy4;
+};
+
+#endif /* _LINUX_FUSE_H */
diff --git a/fuse/include/fuse_lowlevel.h b/fuse/include/fuse_lowlevel.h
new file mode 100644
index 0000000..36cf26d
--- /dev/null
+++ b/fuse/include/fuse_lowlevel.h
@@ -0,0 +1,1837 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#ifndef _FUSE_LOWLEVEL_H_
+#define _FUSE_LOWLEVEL_H_
+
+/** @file
+ *
+ * Low level API
+ *
+ * IMPORTANT: you should define FUSE_USE_VERSION before including this
+ * header.  To use the newest API define it to 26 (recommended for any
+ * new application), to use the old API define it to 24 (default) or
+ * 25
+ */
+
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 24
+#endif
+
+#include "fuse_common.h"
+
+#include <utime.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ----------------------------------------------------------- *
+ * Miscellaneous definitions				       *
+ * ----------------------------------------------------------- */
+
+/** The node ID of the root inode */
+#define FUSE_ROOT_ID 1
+
+/** Inode number type */
+typedef unsigned long fuse_ino_t;
+
+/** Request pointer type */
+typedef struct fuse_req *fuse_req_t;
+
+/**
+ * Session
+ *
+ * This provides hooks for processing requests, and exiting
+ */
+struct fuse_session;
+
+/**
+ * Channel
+ *
+ * A communication channel, providing hooks for sending and receiving
+ * messages
+ */
+struct fuse_chan;
+
+/** Directory entry parameters supplied to fuse_reply_entry() */
+struct fuse_entry_param {
+	/** Unique inode number
+	 *
+	 * In lookup, zero means negative entry (from version 2.5)
+	 * Returning ENOENT also means negative entry, but by setting zero
+	 * ino the kernel may cache negative entries for entry_timeout
+	 * seconds.
+	 */
+	fuse_ino_t ino;
+
+	/** Generation number for this entry.
+	 *
+	 * If the file system will be exported over NFS, the
+	 * ino/generation pairs need to be unique over the file
+	 * system's lifetime (rather than just the mount time). So if
+	 * the file system reuses an inode after it has been deleted,
+	 * it must assign a new, previously unused generation number
+	 * to the inode at the same time.
+	 *
+	 * The generation must be non-zero, otherwise FUSE will treat
+	 * it as an error.
+	 *
+	 */
+	unsigned long generation;
+
+	/** Inode attributes.
+	 *
+	 * Even if attr_timeout == 0, attr must be correct. For example,
+	 * for open(), FUSE uses attr.st_size from lookup() to determine
+	 * how many bytes to request. If this value is not correct,
+	 * incorrect data will be returned.
+	 */
+	struct stat attr;
+
+	/** Validity timeout (in seconds) for the attributes */
+	double attr_timeout;
+
+	/** Validity timeout (in seconds) for the name */
+	double entry_timeout;
+};
+
+/** Additional context associated with requests */
+struct fuse_ctx {
+	/** User ID of the calling process */
+	uid_t uid;
+
+	/** Group ID of the calling process */
+	gid_t gid;
+
+	/** Thread ID of the calling process */
+	pid_t pid;
+
+	/** Umask of the calling process (introduced in version 2.8) */
+	mode_t umask;
+};
+
+struct fuse_forget_data {
+	uint64_t ino;
+	uint64_t nlookup;
+};
+
+/* 'to_set' flags in setattr */
+#define FUSE_SET_ATTR_MODE	(1 << 0)
+#define FUSE_SET_ATTR_UID	(1 << 1)
+#define FUSE_SET_ATTR_GID	(1 << 2)
+#define FUSE_SET_ATTR_SIZE	(1 << 3)
+#define FUSE_SET_ATTR_ATIME	(1 << 4)
+#define FUSE_SET_ATTR_MTIME	(1 << 5)
+#define FUSE_SET_ATTR_ATIME_NOW	(1 << 7)
+#define FUSE_SET_ATTR_MTIME_NOW	(1 << 8)
+
+/* ----------------------------------------------------------- *
+ * Request methods and replies				       *
+ * ----------------------------------------------------------- */
+
+/**
+ * Low level filesystem operations
+ *
+ * Most of the methods (with the exception of init and destroy)
+ * receive a request handle (fuse_req_t) as their first argument.
+ * This handle must be passed to one of the specified reply functions.
+ *
+ * This may be done inside the method invocation, or after the call
+ * has returned.  The request handle is valid until one of the reply
+ * functions is called.
+ *
+ * Other pointer arguments (name, fuse_file_info, etc) are not valid
+ * after the call has returned, so if they are needed later, their
+ * contents have to be copied.
+ *
+ * The filesystem sometimes needs to handle a return value of -ENOENT
+ * from the reply function, which means, that the request was
+ * interrupted, and the reply discarded.  For example if
+ * fuse_reply_open() return -ENOENT means, that the release method for
+ * this file will not be called.
+ */
+struct fuse_lowlevel_ops {
+	/**
+	 * Initialize filesystem
+	 *
+	 * Called before any other filesystem method
+	 *
+	 * There's no reply to this function
+	 *
+	 * @param userdata the user data passed to fuse_lowlevel_new()
+	 */
+	void (*init) (void *userdata, struct fuse_conn_info *conn);
+
+	/**
+	 * Clean up filesystem
+	 *
+	 * Called on filesystem exit
+	 *
+	 * There's no reply to this function
+	 *
+	 * @param userdata the user data passed to fuse_lowlevel_new()
+	 */
+	void (*destroy) (void *userdata);
+
+	/**
+	 * Look up a directory entry by name and get its attributes.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_entry
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param parent inode number of the parent directory
+	 * @param name the name to look up
+	 */
+	void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+
+	/**
+	 * Forget about an inode
+	 *
+	 * This function is called when the kernel removes an inode
+	 * from its internal caches.
+	 *
+	 * The inode's lookup count increases by one for every call to
+	 * fuse_reply_entry and fuse_reply_create. The nlookup parameter
+	 * indicates by how much the lookup count should be decreased.
+	 *
+	 * Inodes with a non-zero lookup count may receive request from
+	 * the kernel even after calls to unlink, rmdir or (when
+	 * overwriting an existing file) rename. Filesystems must handle
+	 * such requests properly and it is recommended to defer removal
+	 * of the inode until the lookup count reaches zero. Calls to
+	 * unlink, remdir or rename will be followed closely by forget
+	 * unless the file or directory is open, in which case the
+	 * kernel issues forget only after the release or releasedir
+	 * calls.
+	 *
+	 * Note that if a file system will be exported over NFS the
+	 * inodes lifetime must extend even beyond forget. See the
+	 * generation field in struct fuse_entry_param above.
+	 *
+	 * On unmount the lookup count for all inodes implicitly drops
+	 * to zero. It is not guaranteed that the file system will
+	 * receive corresponding forget messages for the affected
+	 * inodes.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_none
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param nlookup the number of lookups to forget
+	 */
+	void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+
+	/**
+	 * Get file attributes
+	 *
+	 * Valid replies:
+	 *   fuse_reply_attr
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi for future use, currently always NULL
+	 */
+	void (*getattr) (fuse_req_t req, fuse_ino_t ino,
+			 struct fuse_file_info *fi);
+
+	/**
+	 * Set file attributes
+	 *
+	 * In the 'attr' argument only members indicated by the 'to_set'
+	 * bitmask contain valid values.  Other members contain undefined
+	 * values.
+	 *
+	 * If the setattr was invoked from the ftruncate() system call
+	 * under Linux kernel versions 2.6.15 or later, the fi->fh will
+	 * contain the value set by the open method or will be undefined
+	 * if the open method didn't set any value.  Otherwise (not
+	 * ftruncate call, or kernel version earlier than 2.6.15) the fi
+	 * parameter will be NULL.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_attr
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param attr the attributes
+	 * @param to_set bit mask of attributes which should be set
+	 * @param fi file information, or NULL
+	 *
+	 * Changed in version 2.5:
+	 *     file information filled in for ftruncate
+	 */
+	void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+			 int to_set, struct fuse_file_info *fi);
+
+	/**
+	 * Read symbolic link
+	 *
+	 * Valid replies:
+	 *   fuse_reply_readlink
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 */
+	void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+
+	/**
+	 * Create file node
+	 *
+	 * Create a regular file, character device, block device, fifo or
+	 * socket node.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_entry
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param parent inode number of the parent directory
+	 * @param name to create
+	 * @param mode file type and mode with which to create the new file
+	 * @param rdev the device number (only valid if created file is a device)
+	 */
+	void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
+		       mode_t mode, dev_t rdev);
+
+	/**
+	 * Create a directory
+	 *
+	 * Valid replies:
+	 *   fuse_reply_entry
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param parent inode number of the parent directory
+	 * @param name to create
+	 * @param mode with which to create the new file
+	 */
+	void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
+		       mode_t mode);
+
+	/**
+	 * Remove a file
+	 *
+	 * If the file's inode's lookup count is non-zero, the file
+	 * system is expected to postpone any removal of the inode
+	 * until the lookup count reaches zero (see description of the
+	 * forget function).
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param parent inode number of the parent directory
+	 * @param name to remove
+	 */
+	void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+
+	/**
+	 * Remove a directory
+	 *
+	 * If the directory's inode's lookup count is non-zero, the
+	 * file system is expected to postpone any removal of the
+	 * inode until the lookup count reaches zero (see description
+	 * of the forget function).
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param parent inode number of the parent directory
+	 * @param name to remove
+	 */
+	void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
+
+	/**
+	 * Create a symbolic link
+	 *
+	 * Valid replies:
+	 *   fuse_reply_entry
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param link the contents of the symbolic link
+	 * @param parent inode number of the parent directory
+	 * @param name to create
+	 */
+	void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+			 const char *name);
+
+	/** Rename a file
+	 *
+	 * If the target exists it should be atomically replaced. If
+	 * the target's inode's lookup count is non-zero, the file
+	 * system is expected to postpone any removal of the inode
+	 * until the lookup count reaches zero (see description of the
+	 * forget function).
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param parent inode number of the old parent directory
+	 * @param name old name
+	 * @param newparent inode number of the new parent directory
+	 * @param newname new name
+	 */
+	void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+			fuse_ino_t newparent, const char *newname);
+
+	/**
+	 * Create a hard link
+	 *
+	 * Valid replies:
+	 *   fuse_reply_entry
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the old inode number
+	 * @param newparent inode number of the new parent directory
+	 * @param newname new name to create
+	 */
+	void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+		      const char *newname);
+
+	/**
+	 * Open a file
+	 *
+	 * Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and
+	 * O_TRUNC) are available in fi->flags.
+	 *
+	 * Filesystem may store an arbitrary file handle (pointer, index,
+	 * etc) in fi->fh, and use this in other all other file operations
+	 * (read, write, flush, release, fsync).
+	 *
+	 * Filesystem may also implement stateless file I/O and not store
+	 * anything in fi->fh.
+	 *
+	 * There are also some flags (direct_io, keep_cache) which the
+	 * filesystem may set in fi, to change the way the file is opened.
+	 * See fuse_file_info structure in <fuse_common.h> for more details.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_open
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi file information
+	 */
+	void (*open) (fuse_req_t req, fuse_ino_t ino,
+		      struct fuse_file_info *fi);
+
+	/**
+	 * Read data
+	 *
+	 * Read should send exactly the number of bytes requested except
+	 * on EOF or error, otherwise the rest of the data will be
+	 * substituted with zeroes.  An exception to this is when the file
+	 * has been opened in 'direct_io' mode, in which case the return
+	 * value of the read system call will reflect the return value of
+	 * this operation.
+	 *
+	 * fi->fh will contain the value set by the open method, or will
+	 * be undefined if the open method didn't set any value.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_buf
+	 *   fuse_reply_iov
+	 *   fuse_reply_data
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param size number of bytes to read
+	 * @param off offset to read from
+	 * @param fi file information
+	 */
+	void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+		      struct fuse_file_info *fi);
+
+	/**
+	 * Write data
+	 *
+	 * Write should return exactly the number of bytes requested
+	 * except on error.  An exception to this is when the file has
+	 * been opened in 'direct_io' mode, in which case the return value
+	 * of the write system call will reflect the return value of this
+	 * operation.
+	 *
+	 * fi->fh will contain the value set by the open method, or will
+	 * be undefined if the open method didn't set any value.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_write
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param buf data to write
+	 * @param size number of bytes to write
+	 * @param off offset to write to
+	 * @param fi file information
+	 */
+	void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+		       size_t size, off64_t off, struct fuse_file_info *fi);
+
+	/**
+	 * Flush method
+	 *
+	 * This is called on each close() of the opened file.
+	 *
+	 * Since file descriptors can be duplicated (dup, dup2, fork), for
+	 * one open call there may be many flush calls.
+	 *
+	 * Filesystems shouldn't assume that flush will always be called
+	 * after some writes, or that if will be called at all.
+	 *
+	 * fi->fh will contain the value set by the open method, or will
+	 * be undefined if the open method didn't set any value.
+	 *
+	 * NOTE: the name of the method is misleading, since (unlike
+	 * fsync) the filesystem is not forced to flush pending writes.
+	 * One reason to flush data, is if the filesystem wants to return
+	 * write errors.
+	 *
+	 * If the filesystem supports file locking operations (setlk,
+	 * getlk) it should remove all locks belonging to 'fi->owner'.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi file information
+	 */
+	void (*flush) (fuse_req_t req, fuse_ino_t ino,
+		       struct fuse_file_info *fi);
+
+	/**
+	 * Release an open file
+	 *
+	 * Release is called when there are no more references to an open
+	 * file: all file descriptors are closed and all memory mappings
+	 * are unmapped.
+	 *
+	 * For every open call there will be exactly one release call.
+	 *
+	 * The filesystem may reply with an error, but error values are
+	 * not returned to close() or munmap() which triggered the
+	 * release.
+	 *
+	 * fi->fh will contain the value set by the open method, or will
+	 * be undefined if the open method didn't set any value.
+	 * fi->flags will contain the same flags as for open.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi file information
+	 */
+	void (*release) (fuse_req_t req, fuse_ino_t ino,
+			 struct fuse_file_info *fi);
+
+	/**
+	 * Synchronize file contents
+	 *
+	 * If the datasync parameter is non-zero, then only the user data
+	 * should be flushed, not the meta data.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param datasync flag indicating if only data should be flushed
+	 * @param fi file information
+	 */
+	void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
+		       struct fuse_file_info *fi);
+
+	/**
+	 * Open a directory
+	 *
+	 * Filesystem may store an arbitrary file handle (pointer, index,
+	 * etc) in fi->fh, and use this in other all other directory
+	 * stream operations (readdir, releasedir, fsyncdir).
+	 *
+	 * Filesystem may also implement stateless directory I/O and not
+	 * store anything in fi->fh, though that makes it impossible to
+	 * implement standard conforming directory stream operations in
+	 * case the contents of the directory can change between opendir
+	 * and releasedir.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_open
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi file information
+	 */
+	void (*opendir) (fuse_req_t req, fuse_ino_t ino,
+			 struct fuse_file_info *fi);
+
+	/**
+	 * Read directory
+	 *
+	 * Send a buffer filled using fuse_add_direntry(), with size not
+	 * exceeding the requested size.  Send an empty buffer on end of
+	 * stream.
+	 *
+	 * fi->fh will contain the value set by the opendir method, or
+	 * will be undefined if the opendir method didn't set any value.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_buf
+	 *   fuse_reply_data
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param size maximum number of bytes to send
+	 * @param off offset to continue reading the directory stream
+	 * @param fi file information
+	 */
+	void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+			 struct fuse_file_info *fi);
+
+	/**
+	 * Release an open directory
+	 *
+	 * For every opendir call there will be exactly one releasedir
+	 * call.
+	 *
+	 * fi->fh will contain the value set by the opendir method, or
+	 * will be undefined if the opendir method didn't set any value.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi file information
+	 */
+	void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+			    struct fuse_file_info *fi);
+
+	/**
+	 * Synchronize directory contents
+	 *
+	 * If the datasync parameter is non-zero, then only the directory
+	 * contents should be flushed, not the meta data.
+	 *
+	 * fi->fh will contain the value set by the opendir method, or
+	 * will be undefined if the opendir method didn't set any value.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param datasync flag indicating if only data should be flushed
+	 * @param fi file information
+	 */
+	void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+			  struct fuse_file_info *fi);
+
+	/**
+	 * Get file system statistics
+	 *
+	 * Valid replies:
+	 *   fuse_reply_statfs
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number, zero means "undefined"
+	 */
+	void (*statfs) (fuse_req_t req, fuse_ino_t ino);
+
+	/**
+	 * Set an extended attribute
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 */
+	void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+			  const char *value, size_t size, int flags);
+
+	/**
+	 * Get an extended attribute
+	 *
+	 * If size is zero, the size of the value should be sent with
+	 * fuse_reply_xattr.
+	 *
+	 * If the size is non-zero, and the value fits in the buffer, the
+	 * value should be sent with fuse_reply_buf.
+	 *
+	 * If the size is too small for the value, the ERANGE error should
+	 * be sent.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_buf
+	 *   fuse_reply_data
+	 *   fuse_reply_xattr
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param name of the extended attribute
+	 * @param size maximum size of the value to send
+	 */
+	void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+			  size_t size);
+
+	/**
+	 * List extended attribute names
+	 *
+	 * If size is zero, the total size of the attribute list should be
+	 * sent with fuse_reply_xattr.
+	 *
+	 * If the size is non-zero, and the null character separated
+	 * attribute list fits in the buffer, the list should be sent with
+	 * fuse_reply_buf.
+	 *
+	 * If the size is too small for the list, the ERANGE error should
+	 * be sent.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_buf
+	 *   fuse_reply_data
+	 *   fuse_reply_xattr
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param size maximum size of the list to send
+	 */
+	void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
+
+	/**
+	 * Remove an extended attribute
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param name of the extended attribute
+	 */
+	void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
+
+	/**
+	 * Check file access permissions
+	 *
+	 * This will be called for the access() system call.  If the
+	 * 'default_permissions' mount option is given, this method is not
+	 * called.
+	 *
+	 * This method is not called under Linux kernel versions 2.4.x
+	 *
+	 * Introduced in version 2.5
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param mask requested access mode
+	 */
+	void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
+
+	/**
+	 * Create and open a file
+	 *
+	 * If the file does not exist, first create it with the specified
+	 * mode, and then open it.
+	 *
+	 * Open flags (with the exception of O_NOCTTY) are available in
+	 * fi->flags.
+	 *
+	 * Filesystem may store an arbitrary file handle (pointer, index,
+	 * etc) in fi->fh, and use this in other all other file operations
+	 * (read, write, flush, release, fsync).
+	 *
+	 * There are also some flags (direct_io, keep_cache) which the
+	 * filesystem may set in fi, to change the way the file is opened.
+	 * See fuse_file_info structure in <fuse_common.h> for more details.
+	 *
+	 * If this method is not implemented or under Linux kernel
+	 * versions earlier than 2.6.15, the mknod() and open() methods
+	 * will be called instead.
+	 *
+	 * Introduced in version 2.5
+	 *
+	 * Valid replies:
+	 *   fuse_reply_create
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param parent inode number of the parent directory
+	 * @param name to create
+	 * @param mode file type and mode with which to create the new file
+	 * @param fi file information
+	 */
+	void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
+			mode_t mode, struct fuse_file_info *fi);
+
+	/**
+	 * Test for a POSIX file lock
+	 *
+	 * Introduced in version 2.6
+	 *
+	 * Valid replies:
+	 *   fuse_reply_lock
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi file information
+	 * @param lock the region/type to test
+	 */
+	void (*getlk) (fuse_req_t req, fuse_ino_t ino,
+		       struct fuse_file_info *fi, struct flock *lock);
+
+	/**
+	 * Acquire, modify or release a POSIX file lock
+	 *
+	 * For POSIX threads (NPTL) there's a 1-1 relation between pid and
+	 * owner, but otherwise this is not always the case.  For checking
+	 * lock ownership, 'fi->owner' must be used.  The l_pid field in
+	 * 'struct flock' should only be used to fill in this field in
+	 * getlk().
+	 *
+	 * Note: if the locking methods are not implemented, the kernel
+	 * will still allow file locking to work locally.  Hence these are
+	 * only interesting for network filesystems and similar.
+	 *
+	 * Introduced in version 2.6
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi file information
+	 * @param lock the region/type to set
+	 * @param sleep locking operation may sleep
+	 */
+	void (*setlk) (fuse_req_t req, fuse_ino_t ino,
+		       struct fuse_file_info *fi,
+		       struct flock *lock, int sleep);
+
+	/**
+	 * Map block index within file to block index within device
+	 *
+	 * Note: This makes sense only for block device backed filesystems
+	 * mounted with the 'blkdev' option
+	 *
+	 * Introduced in version 2.6
+	 *
+	 * Valid replies:
+	 *   fuse_reply_bmap
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param blocksize unit of block index
+	 * @param idx block index within file
+	 */
+	void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
+		      uint64_t idx);
+
+	/**
+	 * Ioctl
+	 *
+	 * Note: For unrestricted ioctls (not allowed for FUSE
+	 * servers), data in and out areas can be discovered by giving
+	 * iovs and setting FUSE_IOCTL_RETRY in @flags.  For
+	 * restricted ioctls, kernel prepares in/out data area
+	 * according to the information encoded in cmd.
+	 *
+	 * Introduced in version 2.8
+	 *
+	 * Valid replies:
+	 *   fuse_reply_ioctl_retry
+	 *   fuse_reply_ioctl
+	 *   fuse_reply_ioctl_iov
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param cmd ioctl command
+	 * @param arg ioctl argument
+	 * @param fi file information
+	 * @param flags for FUSE_IOCTL_* flags
+	 * @param in_buf data fetched from the caller
+	 * @param in_bufsz number of fetched bytes
+	 * @param out_bufsz maximum size of output data
+	 */
+	void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
+		       struct fuse_file_info *fi, unsigned flags,
+		       const void *in_buf, size_t in_bufsz, size_t out_bufsz);
+
+	/**
+	 * Poll for IO readiness
+	 *
+	 * Introduced in version 2.8
+	 *
+	 * Note: If ph is non-NULL, the client should notify
+	 * when IO readiness events occur by calling
+	 * fuse_lowelevel_notify_poll() with the specified ph.
+	 *
+	 * Regardless of the number of times poll with a non-NULL ph
+	 * is received, single notification is enough to clear all.
+	 * Notifying more times incurs overhead but doesn't harm
+	 * correctness.
+	 *
+	 * The callee is responsible for destroying ph with
+	 * fuse_pollhandle_destroy() when no longer in use.
+	 *
+	 * Valid replies:
+	 *   fuse_reply_poll
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi file information
+	 * @param ph poll handle to be used for notification
+	 */
+	void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
+		      struct fuse_pollhandle *ph);
+
+	/**
+	 * Write data made available in a buffer
+	 *
+	 * This is a more generic version of the ->write() method.  If
+	 * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the
+	 * kernel supports splicing from the fuse device, then the
+	 * data will be made available in pipe for supporting zero
+	 * copy data transfer.
+	 *
+	 * Introduced in version 2.9
+	 *
+	 * Valid replies:
+	 *   fuse_reply_write
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param bufv buffer containing the data
+	 * @param off offset to write to
+	 * @param fi file information
+	 */
+	void (*write_buf) (fuse_req_t req, fuse_ino_t ino,
+			   struct fuse_bufvec *bufv, off64_t off,
+			   struct fuse_file_info *fi);
+
+	/**
+	 * Callback function for the retrieve request
+	 *
+	 * Introduced in version 2.9
+	 *
+	 * Valid replies:
+	 *	fuse_reply_none
+	 *
+	 * @param req request handle
+	 * @param cookie user data supplied to fuse_lowlevel_notify_retrieve()
+	 * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve()
+	 * @param offset the offset supplied to fuse_lowlevel_notify_retrieve()
+	 * @param bufv the buffer containing the returned data
+	 */
+	void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino,
+				off64_t offset, struct fuse_bufvec *bufv);
+
+	/**
+	 * Forget about multiple inodes
+	 *
+	 * See description of the forget function for more
+	 * information.
+	 *
+	 * Introduced in version 2.9
+	 *
+	 * Valid replies:
+	 *   fuse_reply_none
+	 *
+	 * @param req request handle
+	 */
+	void (*forget_multi) (fuse_req_t req, size_t count,
+			      struct fuse_forget_data *forgets);
+
+	/**
+	 * Acquire, modify or release a BSD file lock
+	 *
+	 * Note: if the locking methods are not implemented, the kernel
+	 * will still allow file locking to work locally.  Hence these are
+	 * only interesting for network filesystems and similar.
+	 *
+	 * Introduced in version 2.9
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param fi file information
+	 * @param op the locking operation, see flock(2)
+	 */
+	void (*flock) (fuse_req_t req, fuse_ino_t ino,
+		       struct fuse_file_info *fi, int op);
+
+	/**
+	 * Allocate requested space. If this function returns success then
+	 * subsequent writes to the specified range shall not fail due to the lack
+	 * of free space on the file system storage media.
+	 *
+	 * Introduced in version 2.9
+	 *
+	 * Valid replies:
+	 *   fuse_reply_err
+	 *
+	 * @param req request handle
+	 * @param ino the inode number
+	 * @param offset starting point for allocated region
+	 * @param length size of allocated region
+	 * @param mode determines the operation to be performed on the given range,
+	 *             see fallocate(2)
+	 */
+	void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
+		       off64_t offset, off64_t length, struct fuse_file_info *fi);
+};
+
+/**
+ * Reply with an error code or success
+ *
+ * Possible requests:
+ *   all except forget
+ *
+ * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr,
+ * removexattr and setlk may send a zero code
+ *
+ * @param req request handle
+ * @param err the positive error value, or zero for success
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_err(fuse_req_t req, int err);
+
+/**
+ * Don't send reply
+ *
+ * Possible requests:
+ *   forget
+ *
+ * @param req request handle
+ */
+void fuse_reply_none(fuse_req_t req);
+
+/**
+ * Reply with a directory entry
+ *
+ * Possible requests:
+ *   lookup, mknod, mkdir, symlink, link
+ *
+ * Side effects:
+ *   increments the lookup count on success
+ *
+ * @param req request handle
+ * @param e the entry parameters
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
+
+/**
+ * Reply with a directory entry and open parameters
+ *
+ * currently the following members of 'fi' are used:
+ *   fh, direct_io, keep_cache
+ *
+ * Possible requests:
+ *   create
+ *
+ * Side effects:
+ *   increments the lookup count on success
+ *
+ * @param req request handle
+ * @param e the entry parameters
+ * @param fi file information
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
+		      const struct fuse_file_info *fi);
+
+/**
+ * Reply with attributes
+ *
+ * Possible requests:
+ *   getattr, setattr
+ *
+ * @param req request handle
+ * @param attr the attributes
+ * @param attr_timeout	validity timeout (in seconds) for the attributes
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
+		    double attr_timeout);
+
+/**
+ * Reply with the contents of a symbolic link
+ *
+ * Possible requests:
+ *   readlink
+ *
+ * @param req request handle
+ * @param link symbolic link contents
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_readlink(fuse_req_t req, const char *link);
+
+/**
+ * Reply with open parameters
+ *
+ * currently the following members of 'fi' are used:
+ *   fh, direct_io, keep_cache
+ *
+ * Possible requests:
+ *   open, opendir
+ *
+ * @param req request handle
+ * @param fi file information
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi);
+
+/**
+ * Reply with number of bytes written
+ *
+ * Possible requests:
+ *   write
+ *
+ * @param req request handle
+ * @param count the number of bytes written
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_write(fuse_req_t req, size_t count);
+
+/**
+ * Reply with data
+ *
+ * Possible requests:
+ *   read, readdir, getxattr, listxattr
+ *
+ * @param req request handle
+ * @param buf buffer containing data
+ * @param size the size of data in bytes
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size);
+
+/**
+ * Reply with data copied/moved from buffer(s)
+ *
+ * Possible requests:
+ *   read, readdir, getxattr, listxattr
+ *
+ * @param req request handle
+ * @param bufv buffer vector
+ * @param flags flags controlling the copy
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
+		    enum fuse_buf_copy_flags flags);
+
+/**
+ * Reply with data vector
+ *
+ * Possible requests:
+ *   read, readdir, getxattr, listxattr
+ *
+ * @param req request handle
+ * @param iov the vector containing the data
+ * @param count the size of vector
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count);
+
+/**
+ * Reply with filesystem statistics
+ *
+ * Possible requests:
+ *   statfs
+ *
+ * @param req request handle
+ * @param stbuf filesystem statistics
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf);
+
+/**
+ * Reply with needed buffer size
+ *
+ * Possible requests:
+ *   getxattr, listxattr
+ *
+ * @param req request handle
+ * @param count the buffer size needed in bytes
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_xattr(fuse_req_t req, size_t count);
+
+/**
+ * Reply with file lock information
+ *
+ * Possible requests:
+ *   getlk
+ *
+ * @param req request handle
+ * @param lock the lock information
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
+
+/**
+ * Reply with block index
+ *
+ * Possible requests:
+ *   bmap
+ *
+ * @param req request handle
+ * @param idx block index within device
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
+
+/* ----------------------------------------------------------- *
+ * Filling a buffer in readdir				       *
+ * ----------------------------------------------------------- */
+
+/**
+ * Add a directory entry to the buffer
+ *
+ * Buffer needs to be large enough to hold the entry.  If it's not,
+ * then the entry is not filled in but the size of the entry is still
+ * returned.  The caller can check this by comparing the bufsize
+ * parameter with the returned entry size.  If the entry size is
+ * larger than the buffer size, the operation failed.
+ *
+ * From the 'stbuf' argument the st_ino field and bits 12-15 of the
+ * st_mode field are used.  The other fields are ignored.
+ *
+ * Note: offsets do not necessarily represent physical offsets, and
+ * could be any marker, that enables the implementation to find a
+ * specific point in the directory stream.
+ *
+ * @param req request handle
+ * @param buf the point where the new entry will be added to the buffer
+ * @param bufsize remaining size of the buffer
+ * @param name the name of the entry
+ * @param stbuf the file attributes
+ * @param off the offset of the next entry
+ * @return the space needed for the entry
+ */
+size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
+			 const char *name, const struct stat *stbuf,
+			 off64_t off);
+
+/**
+ * Reply to ask for data fetch and output buffer preparation.  ioctl
+ * will be retried with the specified input data fetched and output
+ * buffer prepared.
+ *
+ * Possible requests:
+ *   ioctl
+ *
+ * @param req request handle
+ * @param in_iov iovec specifying data to fetch from the caller
+ * @param in_count number of entries in in_iov
+ * @param out_iov iovec specifying addresses to write output to
+ * @param out_count number of entries in out_iov
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_ioctl_retry(fuse_req_t req,
+			   const struct iovec *in_iov, size_t in_count,
+			   const struct iovec *out_iov, size_t out_count);
+
+/**
+ * Reply to finish ioctl
+ *
+ * Possible requests:
+ *   ioctl
+ *
+ * @param req request handle
+ * @param result result to be passed to the caller
+ * @param buf buffer containing output data
+ * @param size length of output data
+ */
+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
+
+/**
+ * Reply to finish ioctl with iov buffer
+ *
+ * Possible requests:
+ *   ioctl
+ *
+ * @param req request handle
+ * @param result result to be passed to the caller
+ * @param iov the vector containing the data
+ * @param count the size of vector
+ */
+int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
+			 int count);
+
+/**
+ * Reply with poll result event mask
+ *
+ * @param req request handle
+ * @param revents poll result event mask
+ */
+int fuse_reply_poll(fuse_req_t req, unsigned revents);
+
+/* ----------------------------------------------------------- *
+ * Notification						       *
+ * ----------------------------------------------------------- */
+
+/**
+ * Notify IO readiness event
+ *
+ * For more information, please read comment for poll operation.
+ *
+ * @param ph poll handle to notify IO readiness event for
+ */
+int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
+
+/**
+ * Notify to invalidate cache for an inode
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param ino the inode number
+ * @param off the offset in the inode where to start invalidating
+ *            or negative to invalidate attributes only
+ * @param len the amount of cache to invalidate or 0 for all
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch, fuse_ino_t ino,
+                                     off64_t off, off64_t len);
+
+/**
+ * Notify to invalidate parent attributes and the dentry matching
+ * parent/name
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param parent inode number
+ * @param name file name
+ * @param namelen strlen() of file name
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
+                                     const char *name, size_t namelen);
+
+/**
+ * Notify to invalidate parent attributes and delete the dentry matching
+ * parent/name if the dentry's inode number matches child (otherwise it
+ * will invalidate the matching dentry).
+ *
+ * @param ch the channel through which to send the notification
+ * @param parent inode number
+ * @param child inode number
+ * @param name file name
+ * @param namelen strlen() of file name
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_delete(struct fuse_chan *ch,
+				fuse_ino_t parent, fuse_ino_t child,
+				const char *name, size_t namelen);
+
+/**
+ * Store data to the kernel buffers
+ *
+ * Synchronously store data in the kernel buffers belonging to the
+ * given inode.  The stored data is marked up-to-date (no read will be
+ * performed against it, unless it's invalidated or evicted from the
+ * cache).
+ *
+ * If the stored data overflows the current file size, then the size
+ * is extended, similarly to a write(2) on the filesystem.
+ *
+ * If this function returns an error, then the store wasn't fully
+ * completed, but it may have been partially completed.
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param ino the inode number
+ * @param offset the starting offset into the file to store to
+ * @param bufv buffer vector
+ * @param flags flags controlling the copy
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
+			       off64_t offset, struct fuse_bufvec *bufv,
+			       enum fuse_buf_copy_flags flags);
+/**
+ * Retrieve data from the kernel buffers
+ *
+ * Retrieve data in the kernel buffers belonging to the given inode.
+ * If successful then the retrieve_reply() method will be called with
+ * the returned data.
+ *
+ * Only present pages are returned in the retrieve reply.  Retrieving
+ * stops when it finds a non-present page and only data prior to that is
+ * returned.
+ *
+ * If this function returns an error, then the retrieve will not be
+ * completed and no reply will be sent.
+ *
+ * This function doesn't change the dirty state of pages in the kernel
+ * buffer.  For dirty pages the write() method will be called
+ * regardless of having been retrieved previously.
+ *
+ * @param ch the channel through which to send the invalidation
+ * @param ino the inode number
+ * @param size the number of bytes to retrieve
+ * @param offset the starting offset into the file to retrieve from
+ * @param cookie user data to supply to the reply callback
+ * @return zero for success, -errno for failure
+ */
+int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino,
+				  size_t size, off64_t offset, void *cookie);
+
+
+/* ----------------------------------------------------------- *
+ * Utility functions					       *
+ * ----------------------------------------------------------- */
+
+/**
+ * Get the userdata from the request
+ *
+ * @param req request handle
+ * @return the user data passed to fuse_lowlevel_new()
+ */
+void *fuse_req_userdata(fuse_req_t req);
+
+/**
+ * Get the context from the request
+ *
+ * The pointer returned by this function will only be valid for the
+ * request's lifetime
+ *
+ * @param req request handle
+ * @return the context structure
+ */
+const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
+
+/**
+ * Get the current supplementary group IDs for the specified request
+ *
+ * Similar to the getgroups(2) system call, except the return value is
+ * always the total number of group IDs, even if it is larger than the
+ * specified size.
+ *
+ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass
+ * the group list to userspace, hence this function needs to parse
+ * "/proc/$TID/task/$TID/status" to get the group IDs.
+ *
+ * This feature may not be supported on all operating systems.  In
+ * such a case this function will return -ENOSYS.
+ *
+ * @param req request handle
+ * @param size size of given array
+ * @param list array of group IDs to be filled in
+ * @return the total number of supplementary group IDs or -errno on failure
+ */
+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
+
+/**
+ * Callback function for an interrupt
+ *
+ * @param req interrupted request
+ * @param data user data
+ */
+typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data);
+
+/**
+ * Register/unregister callback for an interrupt
+ *
+ * If an interrupt has already happened, then the callback function is
+ * called from within this function, hence it's not possible for
+ * interrupts to be lost.
+ *
+ * @param req request handle
+ * @param func the callback function or NULL for unregister
+ * @param data user data passed to the callback function
+ */
+void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
+			     void *data);
+
+/**
+ * Check if a request has already been interrupted
+ *
+ * @param req request handle
+ * @return 1 if the request has been interrupted, 0 otherwise
+ */
+int fuse_req_interrupted(fuse_req_t req);
+
+/* ----------------------------------------------------------- *
+ * Filesystem setup					       *
+ * ----------------------------------------------------------- */
+
+/* Deprecated, don't use */
+int fuse_lowlevel_is_lib_option(const char *opt);
+
+/**
+ * Create a low level session
+ *
+ * @param args argument vector
+ * @param op the low level filesystem operations
+ * @param op_size sizeof(struct fuse_lowlevel_ops)
+ * @param userdata user data
+ * @return the created session object, or NULL on failure
+ */
+struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
+				       const struct fuse_lowlevel_ops *op,
+				       size_t op_size, void *userdata);
+
+/* ----------------------------------------------------------- *
+ * Session interface					       *
+ * ----------------------------------------------------------- */
+
+/**
+ * Session operations
+ *
+ * This is used in session creation
+ */
+struct fuse_session_ops {
+	/**
+	 * Hook to process a request (mandatory)
+	 *
+	 * @param data user data passed to fuse_session_new()
+	 * @param buf buffer containing the raw request
+	 * @param len request length
+	 * @param ch channel on which the request was received
+	 */
+	void (*process) (void *data, const char *buf, size_t len,
+			 struct fuse_chan *ch);
+
+	/**
+	 * Hook for session exit and reset (optional)
+	 *
+	 * @param data user data passed to fuse_session_new()
+	 * @param val exited status (1 - exited, 0 - not exited)
+	 */
+	void (*exit) (void *data, int val);
+
+	/**
+	 * Hook for querying the current exited status (optional)
+	 *
+	 * @param data user data passed to fuse_session_new()
+	 * @return 1 if exited, 0 if not exited
+	 */
+	int (*exited) (void *data);
+
+	/**
+	 * Hook for cleaning up the channel on destroy (optional)
+	 *
+	 * @param data user data passed to fuse_session_new()
+	 */
+	void (*destroy) (void *data);
+};
+
+/**
+ * Create a new session
+ *
+ * @param op session operations
+ * @param data user data
+ * @return new session object, or NULL on failure
+ */
+struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data);
+
+/**
+ * Assign a channel to a session
+ *
+ * Note: currently only a single channel may be assigned.  This may
+ * change in the future
+ *
+ * If a session is destroyed, the assigned channel is also destroyed
+ *
+ * @param se the session
+ * @param ch the channel
+ */
+void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch);
+
+/**
+ * Remove a channel from a session
+ *
+ * If the channel is not assigned to a session, then this is a no-op
+ *
+ * @param ch the channel to remove
+ */
+void fuse_session_remove_chan(struct fuse_chan *ch);
+
+/**
+ * Iterate over the channels assigned to a session
+ *
+ * The iterating function needs to start with a NULL channel, and
+ * after that needs to pass the previously returned channel to the
+ * function.
+ *
+ * @param se the session
+ * @param ch the previous channel, or NULL
+ * @return the next channel, or NULL if no more channels exist
+ */
+struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
+					 struct fuse_chan *ch);
+
+/**
+ * Process a raw request
+ *
+ * @param se the session
+ * @param buf buffer containing the raw request
+ * @param len request length
+ * @param ch channel on which the request was received
+ */
+void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
+			  struct fuse_chan *ch);
+
+/**
+ * Process a raw request supplied in a generic buffer
+ *
+ * This is a more generic version of fuse_session_process().  The
+ * fuse_buf may contain a memory buffer or a pipe file descriptor.
+ *
+ * @param se the session
+ * @param buf the fuse_buf containing the request
+ * @param ch channel on which the request was received
+ */
+void fuse_session_process_buf(struct fuse_session *se,
+			      const struct fuse_buf *buf, struct fuse_chan *ch);
+
+/**
+ * Receive a raw request supplied in a generic buffer
+ *
+ * This is a more generic version of fuse_chan_recv().  The fuse_buf
+ * supplied to this function contains a suitably allocated memory
+ * buffer.  This may be overwritten with a file descriptor buffer.
+ *
+ * @param se the session
+ * @param buf the fuse_buf to store the request in
+ * @param chp pointer to the channel
+ * @return the actual size of the raw request, or -errno on error
+ */
+int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+			     struct fuse_chan **chp);
+
+/**
+ * Destroy a session
+ *
+ * @param se the session
+ */
+void fuse_session_destroy(struct fuse_session *se);
+
+/**
+ * Exit a session
+ *
+ * @param se the session
+ */
+void fuse_session_exit(struct fuse_session *se);
+
+/**
+ * Reset the exited status of a session
+ *
+ * @param se the session
+ */
+void fuse_session_reset(struct fuse_session *se);
+
+/**
+ * Query the exited status of a session
+ *
+ * @param se the session
+ * @return 1 if exited, 0 if not exited
+ */
+int fuse_session_exited(struct fuse_session *se);
+
+/**
+ * Get the user data provided to the session
+ *
+ * @param se the session
+ * @return the user data
+ */
+void *fuse_session_data(struct fuse_session *se);
+
+/**
+ * Enter a single threaded event loop
+ *
+ * @param se the session
+ * @return 0 on success, -1 on error
+ */
+int fuse_session_loop(struct fuse_session *se);
+
+/**
+ * Enter a multi-threaded event loop
+ *
+ * @param se the session
+ * @return 0 on success, -1 on error
+ */
+int fuse_session_loop_mt(struct fuse_session *se);
+
+/* ----------------------------------------------------------- *
+ * Channel interface					       *
+ * ----------------------------------------------------------- */
+
+/**
+ * Channel operations
+ *
+ * This is used in channel creation
+ */
+struct fuse_chan_ops {
+	/**
+	 * Hook for receiving a raw request
+	 *
+	 * @param ch pointer to the channel
+	 * @param buf the buffer to store the request in
+	 * @param size the size of the buffer
+	 * @return the actual size of the raw request, or -1 on error
+	 */
+	int (*receive)(struct fuse_chan **chp, char *buf, size_t size);
+
+	/**
+	 * Hook for sending a raw reply
+	 *
+	 * A return value of -ENOENT means, that the request was
+	 * interrupted, and the reply was discarded
+	 *
+	 * @param ch the channel
+	 * @param iov vector of blocks
+	 * @param count the number of blocks in vector
+	 * @return zero on success, -errno on failure
+	 */
+	int (*send)(struct fuse_chan *ch, const struct iovec iov[],
+		    size_t count);
+
+	/**
+	 * Destroy the channel
+	 *
+	 * @param ch the channel
+	 */
+	void (*destroy)(struct fuse_chan *ch);
+};
+
+/**
+ * Create a new channel
+ *
+ * @param op channel operations
+ * @param fd file descriptor of the channel
+ * @param bufsize the minimal receive buffer size
+ * @param data user data
+ * @return the new channel object, or NULL on failure
+ */
+struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
+				size_t bufsize, void *data);
+
+/**
+ * Query the file descriptor of the channel
+ *
+ * @param ch the channel
+ * @return the file descriptor passed to fuse_chan_new()
+ */
+int fuse_chan_fd(struct fuse_chan *ch);
+
+/**
+ * Query the minimal receive buffer size
+ *
+ * @param ch the channel
+ * @return the buffer size passed to fuse_chan_new()
+ */
+size_t fuse_chan_bufsize(struct fuse_chan *ch);
+
+/**
+ * Query the user data
+ *
+ * @param ch the channel
+ * @return the user data passed to fuse_chan_new()
+ */
+void *fuse_chan_data(struct fuse_chan *ch);
+
+/**
+ * Query the session to which this channel is assigned
+ *
+ * @param ch the channel
+ * @return the session, or NULL if the channel is not assigned
+ */
+struct fuse_session *fuse_chan_session(struct fuse_chan *ch);
+
+/**
+ * Receive a raw request
+ *
+ * A return value of -ENODEV means, that the filesystem was unmounted
+ *
+ * @param ch pointer to the channel
+ * @param buf the buffer to store the request in
+ * @param size the size of the buffer
+ * @return the actual size of the raw request, or -errno on error
+ */
+int fuse_chan_recv(struct fuse_chan **ch, char *buf, size_t size);
+
+/**
+ * Send a raw reply
+ *
+ * A return value of -ENOENT means, that the request was
+ * interrupted, and the reply was discarded
+ *
+ * @param ch the channel
+ * @param iov vector of blocks
+ * @param count the number of blocks in vector
+ * @return zero on success, -errno on failure
+ */
+int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[],
+		   size_t count);
+
+/**
+ * Destroy a channel
+ *
+ * @param ch the channel
+ */
+void fuse_chan_destroy(struct fuse_chan *ch);
+
+/* ----------------------------------------------------------- *
+ * Compatibility stuff					       *
+ * ----------------------------------------------------------- */
+
+#if FUSE_USE_VERSION < 26
+#  include "fuse_lowlevel_compat.h"
+#  define fuse_chan_ops fuse_chan_ops_compat24
+#  define fuse_chan_new fuse_chan_new_compat24
+#  if FUSE_USE_VERSION == 25
+#    define fuse_lowlevel_ops fuse_lowlevel_ops_compat25
+#    define fuse_lowlevel_new fuse_lowlevel_new_compat25
+#  elif FUSE_USE_VERSION == 24
+#    define fuse_lowlevel_ops fuse_lowlevel_ops_compat
+#    define fuse_lowlevel_new fuse_lowlevel_new_compat
+#    define fuse_file_info fuse_file_info_compat
+#    define fuse_reply_statfs fuse_reply_statfs_compat
+#    define fuse_reply_open fuse_reply_open_compat
+#  else
+#    error Compatibility with low-level API version < 24 not supported
+#  endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_LOWLEVEL_H_ */
diff --git a/fuse/include/fuse_lowlevel_compat.h b/fuse/include/fuse_lowlevel_compat.h
new file mode 100644
index 0000000..78b7c2b
--- /dev/null
+++ b/fuse/include/fuse_lowlevel_compat.h
@@ -0,0 +1,155 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+/* these definitions provide source compatibility to prior versions.
+   Do not include this file directly! */
+
+struct fuse_lowlevel_ops_compat25 {
+	void (*init) (void *userdata);
+	void (*destroy) (void *userdata);
+	void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+	void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+	void (*getattr) (fuse_req_t req, fuse_ino_t ino,
+			 struct fuse_file_info *fi);
+	void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+			 int to_set, struct fuse_file_info *fi);
+	void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+	void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
+		       mode_t mode, dev_t rdev);
+	void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
+		       mode_t mode);
+	void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+	void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
+	void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+			 const char *name);
+	void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+			fuse_ino_t newparent, const char *newname);
+	void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+		      const char *newname);
+	void (*open) (fuse_req_t req, fuse_ino_t ino,
+		      struct fuse_file_info *fi);
+	void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+		      struct fuse_file_info *fi);
+	void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+		       size_t size, off64_t off, struct fuse_file_info *fi);
+	void (*flush) (fuse_req_t req, fuse_ino_t ino,
+		       struct fuse_file_info *fi);
+	void (*release) (fuse_req_t req, fuse_ino_t ino,
+			 struct fuse_file_info *fi);
+	void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
+		       struct fuse_file_info *fi);
+	void (*opendir) (fuse_req_t req, fuse_ino_t ino,
+			 struct fuse_file_info *fi);
+	void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+			 struct fuse_file_info *fi);
+	void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+			    struct fuse_file_info *fi);
+	void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+			  struct fuse_file_info *fi);
+	void (*statfs) (fuse_req_t req);
+	void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+			  const char *value, size_t size, int flags);
+	void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+			  size_t size);
+	void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
+	void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
+	void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
+	void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
+			mode_t mode, struct fuse_file_info *fi);
+};
+
+struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
+				const struct fuse_lowlevel_ops_compat25 *op,
+				size_t op_size, void *userdata);
+
+size_t fuse_dirent_size(size_t namelen);
+
+char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
+		      off64_t off);
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+#include <sys/statfs.h>
+
+struct fuse_lowlevel_ops_compat {
+	void (*init) (void *userdata);
+	void (*destroy) (void *userdata);
+	void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+	void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+	void (*getattr) (fuse_req_t req, fuse_ino_t ino,
+			 struct fuse_file_info_compat *fi);
+	void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+			 int to_set, struct fuse_file_info_compat *fi);
+	void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+	void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
+		       mode_t mode, dev_t rdev);
+	void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
+		       mode_t mode);
+	void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+	void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
+	void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+			 const char *name);
+	void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+			fuse_ino_t newparent, const char *newname);
+	void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+		      const char *newname);
+	void (*open) (fuse_req_t req, fuse_ino_t ino,
+		      struct fuse_file_info_compat *fi);
+	void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+		      struct fuse_file_info_compat *fi);
+	void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+		       size_t size, off64_t off, struct fuse_file_info_compat *fi);
+	void (*flush) (fuse_req_t req, fuse_ino_t ino,
+		       struct fuse_file_info_compat *fi);
+	void (*release) (fuse_req_t req, fuse_ino_t ino,
+			 struct fuse_file_info_compat *fi);
+	void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
+		       struct fuse_file_info_compat *fi);
+	void (*opendir) (fuse_req_t req, fuse_ino_t ino,
+			 struct fuse_file_info_compat *fi);
+	void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off64_t off,
+			 struct fuse_file_info_compat *fi);
+	void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+			    struct fuse_file_info_compat *fi);
+	void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+			  struct fuse_file_info_compat *fi);
+	void (*statfs) (fuse_req_t req);
+	void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+			  const char *value, size_t size, int flags);
+	void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+			  size_t size);
+	void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
+	void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
+	void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
+	void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
+			mode_t mode, struct fuse_file_info_compat *fi);
+};
+
+int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf);
+
+int fuse_reply_open_compat(fuse_req_t req,
+			   const struct fuse_file_info_compat *fi);
+
+struct fuse_session *fuse_lowlevel_new_compat(const char *opts,
+				const struct fuse_lowlevel_ops_compat *op,
+				size_t op_size, void *userdata);
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+struct fuse_chan_ops_compat24 {
+	int (*receive)(struct fuse_chan *ch, char *buf, size_t size);
+	int (*send)(struct fuse_chan *ch, const struct iovec iov[],
+		    size_t count);
+	void (*destroy)(struct fuse_chan *ch);
+};
+
+struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
+					 int fd, size_t bufsize, void *data);
+
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size);
+struct fuse_chan *fuse_kern_chan_new(int fd);
diff --git a/fuse/include/fuse_opt.h b/fuse/include/fuse_opt.h
new file mode 100644
index 0000000..add0a30
--- /dev/null
+++ b/fuse/include/fuse_opt.h
@@ -0,0 +1,270 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#ifndef _FUSE_OPT_H_
+#define _FUSE_OPT_H_
+
+/** @file
+ *
+ * This file defines the option parsing interface of FUSE
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Option description
+ *
+ * This structure describes a single option, and action associated
+ * with it, in case it matches.
+ *
+ * More than one such match may occur, in which case the action for
+ * each match is executed.
+ *
+ * There are three possible actions in case of a match:
+ *
+ * i) An integer (int or unsigned) variable determined by 'offset' is
+ *    set to 'value'
+ *
+ * ii) The processing function is called, with 'value' as the key
+ *
+ * iii) An integer (any) or string (char *) variable determined by
+ *    'offset' is set to the value of an option parameter
+ *
+ * 'offset' should normally be either set to
+ *
+ *  - 'offsetof(struct foo, member)'  actions i) and iii)
+ *
+ *  - -1			      action ii)
+ *
+ * The 'offsetof()' macro is defined in the <stddef.h> header.
+ *
+ * The template determines which options match, and also have an
+ * effect on the action.  Normally the action is either i) or ii), but
+ * if a format is present in the template, then action iii) is
+ * performed.
+ *
+ * The types of templates are:
+ *
+ * 1) "-x", "-foo", "--foo", "--foo-bar", etc.	These match only
+ *   themselves.  Invalid values are "--" and anything beginning
+ *   with "-o"
+ *
+ * 2) "foo", "foo-bar", etc.  These match "-ofoo", "-ofoo-bar" or
+ *    the relevant option in a comma separated option list
+ *
+ * 3) "bar=", "--foo=", etc.  These are variations of 1) and 2)
+ *    which have a parameter
+ *
+ * 4) "bar=%s", "--foo=%lu", etc.  Same matching as above but perform
+ *    action iii).
+ *
+ * 5) "-x ", etc.  Matches either "-xparam" or "-x param" as
+ *    two separate arguments
+ *
+ * 6) "-x %s", etc.  Combination of 4) and 5)
+ *
+ * If the format is "%s", memory is allocated for the string unlike
+ * with scanf().
+ */
+struct fuse_opt {
+	/** Matching template and optional parameter formatting */
+	const char *templ;
+
+	/**
+	 * Offset of variable within 'data' parameter of fuse_opt_parse()
+	 * or -1
+	 */
+	unsigned long offset;
+
+	/**
+	 * Value to set the variable to, or to be passed as 'key' to the
+	 * processing function.	 Ignored if template has a format
+	 */
+	int value;
+};
+
+/**
+ * Key option.	In case of a match, the processing function will be
+ * called with the specified key.
+ */
+#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
+
+/**
+ * Last option.	 An array of 'struct fuse_opt' must end with a NULL
+ * template value
+ */
+#define FUSE_OPT_END { NULL, 0, 0 }
+
+/**
+ * Argument list
+ */
+struct fuse_args {
+	/** Argument count */
+	int argc;
+
+	/** Argument vector.  NULL terminated */
+	char **argv;
+
+	/** Is 'argv' allocated? */
+	int allocated;
+};
+
+/**
+ * Initializer for 'struct fuse_args'
+ */
+#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
+
+/**
+ * Key value passed to the processing function if an option did not
+ * match any template
+ */
+#define FUSE_OPT_KEY_OPT     -1
+
+/**
+ * Key value passed to the processing function for all non-options
+ *
+ * Non-options are the arguments beginning with a character other than
+ * '-' or all arguments after the special '--' option
+ */
+#define FUSE_OPT_KEY_NONOPT  -2
+
+/**
+ * Special key value for options to keep
+ *
+ * Argument is not passed to processing function, but behave as if the
+ * processing function returned 1
+ */
+#define FUSE_OPT_KEY_KEEP -3
+
+/**
+ * Special key value for options to discard
+ *
+ * Argument is not passed to processing function, but behave as if the
+ * processing function returned zero
+ */
+#define FUSE_OPT_KEY_DISCARD -4
+
+/**
+ * Processing function
+ *
+ * This function is called if
+ *    - option did not match any 'struct fuse_opt'
+ *    - argument is a non-option
+ *    - option did match and offset was set to -1
+ *
+ * The 'arg' parameter will always contain the whole argument or
+ * option including the parameter if exists.  A two-argument option
+ * ("-x foo") is always converted to single argument option of the
+ * form "-xfoo" before this function is called.
+ *
+ * Options of the form '-ofoo' are passed to this function without the
+ * '-o' prefix.
+ *
+ * The return value of this function determines whether this argument
+ * is to be inserted into the output argument vector, or discarded.
+ *
+ * @param data is the user data passed to the fuse_opt_parse() function
+ * @param arg is the whole argument or option
+ * @param key determines why the processing function was called
+ * @param outargs the current output argument list
+ * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
+ */
+typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
+			       struct fuse_args *outargs);
+
+/**
+ * Option parsing function
+ *
+ * If 'args' was returned from a previous call to fuse_opt_parse() or
+ * it was constructed from
+ *
+ * A NULL 'args' is equivalent to an empty argument vector
+ *
+ * A NULL 'opts' is equivalent to an 'opts' array containing a single
+ * end marker
+ *
+ * A NULL 'proc' is equivalent to a processing function always
+ * returning '1'
+ *
+ * @param args is the input and output argument list
+ * @param data is the user data
+ * @param opts is the option description array
+ * @param proc is the processing function
+ * @return -1 on error, 0 on success
+ */
+int fuse_opt_parse(struct fuse_args *args, void *data,
+		   const struct fuse_opt opts[], fuse_opt_proc_t proc);
+
+/**
+ * Add an option to a comma separated option list
+ *
+ * @param opts is a pointer to an option list, may point to a NULL value
+ * @param opt is the option to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_add_opt(char **opts, const char *opt);
+
+/**
+ * Add an option, escaping commas, to a comma separated option list
+ *
+ * @param opts is a pointer to an option list, may point to a NULL value
+ * @param opt is the option to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_add_opt_escaped(char **opts, const char *opt);
+
+/**
+ * Add an argument to a NULL terminated argument vector
+ *
+ * @param args is the structure containing the current argument list
+ * @param arg is the new argument to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
+
+/**
+ * Add an argument at the specified position in a NULL terminated
+ * argument vector
+ *
+ * Adds the argument to the N-th position.  This is useful for adding
+ * options at the beginning of the array which must not come after the
+ * special '--' option.
+ *
+ * @param args is the structure containing the current argument list
+ * @param pos is the position at which to add the argument
+ * @param arg is the new argument to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
+
+/**
+ * Free the contents of argument list
+ *
+ * The structure itself is not freed
+ *
+ * @param args is the structure containing the argument list
+ */
+void fuse_opt_free_args(struct fuse_args *args);
+
+
+/**
+ * Check if an option matches
+ *
+ * @param opts is the option description array
+ * @param opt is the option to match
+ * @return 1 if a match is found, 0 if not
+ */
+int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_OPT_H_ */
diff --git a/fuse/include/old/fuse.h b/fuse/include/old/fuse.h
new file mode 100644
index 0000000..3db0945
--- /dev/null
+++ b/fuse/include/old/fuse.h
@@ -0,0 +1,9 @@
+/*
+   This header is for compatibility with older software using FUSE.
+
+   Please use 'pkg-config --cflags fuse' to set include path.  The
+   correct usage is still '#include <fuse.h>', not '#include
+   <fuse/fuse.h>'.
+*/
+
+#include "fuse/fuse.h"
diff --git a/fuse/include/stamp-h1 b/fuse/include/stamp-h1
new file mode 100644
index 0000000..b330768
--- /dev/null
+++ b/fuse/include/stamp-h1
@@ -0,0 +1 @@
+timestamp for include/config.h
diff --git a/fuse/include/sys/statvfs.h b/fuse/include/sys/statvfs.h
new file mode 100644
index 0000000..6e3e39f
--- /dev/null
+++ b/fuse/include/sys/statvfs.h
@@ -0,0 +1,18 @@
+#ifndef __STATVFS_H
+#define __STATVFS_H
+
+struct statvfs {
+	unsigned long  f_bsize;    /* file system block size */
+	unsigned long  f_frsize;   /* fragment size */
+	fsblkcnt_t     f_blocks;   /* size of fs in f_frsize units */
+	fsblkcnt_t     f_bfree;    /* # free blocks */
+	fsblkcnt_t     f_bavail;   /* # free blocks for non-root */
+	fsfilcnt_t     f_files;    /* # inodes */
+	fsfilcnt_t     f_ffree;    /* # free inodes */
+	fsfilcnt_t     f_favail;   /* # free inodes for non-root */
+	unsigned long  f_fsid;     /* file system ID */
+	unsigned long  f_flag;     /* mount flags */
+	unsigned long  f_namemax;  /* maximum filename length */
+};
+
+#endif
diff --git a/fuse/include/ulockmgr.h b/fuse/include/ulockmgr.h
new file mode 100644
index 0000000..ad55579
--- /dev/null
+++ b/fuse/include/ulockmgr.h
@@ -0,0 +1,24 @@
+/*
+  libulockmgr: Userspace Lock Manager Library
+  Copyright (C) 2006  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+/**
+ * Perform POSIX locking operation
+ *
+ * @param fd the file descriptor
+ * @param cmd the locking command (F_GETFL, F_SETLK or F_SETLKW)
+ * @param lock the lock parameters
+ * @param owner the lock owner ID cookie
+ * @param owner_len length of the lock owner ID cookie
+ * @return 0 on success -errno on error
+ */
+int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
+		size_t owner_len);
diff --git a/fuse/mount.c b/fuse/mount.c
new file mode 100644
index 0000000..af7218f
--- /dev/null
+++ b/fuse/mount.c
@@ -0,0 +1,638 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_opt.h"
+#include "fuse_common_compat.h"
+#include "mount_util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#ifdef __NetBSD__
+#include <perfuse.h>
+
+#define MS_RDONLY 	MNT_RDONLY
+#define MS_NOSUID 	MNT_NOSUID
+#define MS_NODEV 	MNT_NODEV
+#define MS_NOEXEC 	MNT_NOEXEC
+#define MS_SYNCHRONOUS 	MNT_SYNCHRONOUS
+#define MS_NOATIME 	MNT_NOATIME
+
+
+#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
+#endif
+
+#define FUSERMOUNT_PROG		"fusermount"
+#define FUSE_COMMFD_ENV		"_FUSE_COMMFD"
+
+#ifndef HAVE_FORK
+#define fork() vfork()
+#endif
+
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC 128
+#endif
+
+enum {
+	KEY_KERN_FLAG,
+	KEY_KERN_OPT,
+	KEY_FUSERMOUNT_OPT,
+	KEY_SUBTYPE_OPT,
+	KEY_MTAB_OPT,
+	KEY_ALLOW_ROOT,
+	KEY_RO,
+	KEY_HELP,
+	KEY_VERSION,
+};
+
+struct mount_opts {
+	int allow_other;
+	int allow_root;
+	int ishelp;
+	int flags;
+	int nonempty;
+	int auto_unmount;
+	int blkdev;
+	char *fsname;
+	char *subtype;
+	char *subtype_opt;
+	char *mtab_opts;
+	char *fusermount_opts;
+	char *kernel_opts;
+};
+
+#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
+
+static const struct fuse_opt fuse_mount_opts[] = {
+	FUSE_MOUNT_OPT("allow_other",		allow_other),
+	FUSE_MOUNT_OPT("allow_root",		allow_root),
+	FUSE_MOUNT_OPT("nonempty",		nonempty),
+	FUSE_MOUNT_OPT("blkdev",		blkdev),
+	FUSE_MOUNT_OPT("auto_unmount",		auto_unmount),
+	FUSE_MOUNT_OPT("fsname=%s",		fsname),
+	FUSE_MOUNT_OPT("subtype=%s",		subtype),
+	FUSE_OPT_KEY("allow_other",		KEY_KERN_OPT),
+	FUSE_OPT_KEY("allow_root",		KEY_ALLOW_ROOT),
+	FUSE_OPT_KEY("nonempty",		KEY_FUSERMOUNT_OPT),
+	FUSE_OPT_KEY("auto_unmount",		KEY_FUSERMOUNT_OPT),
+	FUSE_OPT_KEY("blkdev",			KEY_FUSERMOUNT_OPT),
+	FUSE_OPT_KEY("fsname=",			KEY_FUSERMOUNT_OPT),
+	FUSE_OPT_KEY("subtype=",		KEY_SUBTYPE_OPT),
+	FUSE_OPT_KEY("large_read",		KEY_KERN_OPT),
+	FUSE_OPT_KEY("blksize=",		KEY_KERN_OPT),
+	FUSE_OPT_KEY("default_permissions",	KEY_KERN_OPT),
+	FUSE_OPT_KEY("max_read=",		KEY_KERN_OPT),
+	FUSE_OPT_KEY("max_read=",		FUSE_OPT_KEY_KEEP),
+	FUSE_OPT_KEY("user=",			KEY_MTAB_OPT),
+	FUSE_OPT_KEY("-r",			KEY_RO),
+	FUSE_OPT_KEY("ro",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("rw",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("suid",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("nosuid",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("dev",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("nodev",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("exec",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("noexec",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("async",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("sync",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("dirsync",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("atime",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("noatime",			KEY_KERN_FLAG),
+	FUSE_OPT_KEY("-h",			KEY_HELP),
+	FUSE_OPT_KEY("--help",			KEY_HELP),
+	FUSE_OPT_KEY("-V",			KEY_VERSION),
+	FUSE_OPT_KEY("--version",		KEY_VERSION),
+	FUSE_OPT_END
+};
+
+static void mount_help(void)
+{
+	fprintf(stderr,
+"    -o allow_other         allow access to other users\n"
+"    -o allow_root          allow access to root\n"
+"    -o auto_unmount        auto unmount on process termination\n"
+"    -o nonempty            allow mounts over non-empty file/dir\n"
+"    -o default_permissions enable permission checking by kernel\n"
+"    -o fsname=NAME         set filesystem name\n"
+"    -o subtype=NAME        set filesystem type\n"
+"    -o large_read          issue large read requests (2.4 only)\n"
+"    -o max_read=N          set maximum size of read requests\n"
+"\n");
+}
+
+#define FUSERMOUNT_DIR "/usr/bin"
+static void exec_fusermount(const char *argv[])
+{
+	execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
+	execvp(FUSERMOUNT_PROG, (char **) argv);
+}
+
+static void mount_version(void)
+{
+	int pid = fork();
+	if (!pid) {
+		const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
+		exec_fusermount(argv);
+		_exit(1);
+	} else if (pid != -1)
+		waitpid(pid, NULL, 0);
+}
+
+struct mount_flags {
+	const char *opt;
+	unsigned long flag;
+	int on;
+};
+
+static const struct mount_flags mount_flags[] = {
+	{"rw",	    MS_RDONLY,	    0},
+	{"ro",	    MS_RDONLY,	    1},
+	{"suid",    MS_NOSUID,	    0},
+	{"nosuid",  MS_NOSUID,	    1},
+	{"dev",	    MS_NODEV,	    0},
+	{"nodev",   MS_NODEV,	    1},
+	{"exec",    MS_NOEXEC,	    0},
+	{"noexec",  MS_NOEXEC,	    1},
+	{"async",   MS_SYNCHRONOUS, 0},
+	{"sync",    MS_SYNCHRONOUS, 1},
+	{"atime",   MS_NOATIME,	    0},
+	{"noatime", MS_NOATIME,	    1},
+#ifndef __NetBSD__
+	{"dirsync", MS_DIRSYNC,	    1},
+#endif
+	{NULL,	    0,		    0}
+};
+
+static void set_mount_flag(const char *s, int *flags)
+{
+	int i;
+
+	for (i = 0; mount_flags[i].opt != NULL; i++) {
+		const char *opt = mount_flags[i].opt;
+		if (strcmp(opt, s) == 0) {
+			if (mount_flags[i].on)
+				*flags |= mount_flags[i].flag;
+			else
+				*flags &= ~mount_flags[i].flag;
+			return;
+		}
+	}
+	fprintf(stderr, "fuse: internal error, can't find mount flag\n");
+	abort();
+}
+
+static int fuse_mount_opt_proc(void *data, const char *arg, int key,
+			       struct fuse_args *outargs)
+{
+	struct mount_opts *mo = data;
+
+	switch (key) {
+	case KEY_ALLOW_ROOT:
+		if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
+		    fuse_opt_add_arg(outargs, "-oallow_root") == -1)
+			return -1;
+		return 0;
+
+	case KEY_RO:
+		arg = "ro";
+		/* fall through */
+	case KEY_KERN_FLAG:
+		set_mount_flag(arg, &mo->flags);
+		return 0;
+
+	case KEY_KERN_OPT:
+		return fuse_opt_add_opt(&mo->kernel_opts, arg);
+
+	case KEY_FUSERMOUNT_OPT:
+		return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
+
+	case KEY_SUBTYPE_OPT:
+		return fuse_opt_add_opt(&mo->subtype_opt, arg);
+
+	case KEY_MTAB_OPT:
+		return fuse_opt_add_opt(&mo->mtab_opts, arg);
+
+	case KEY_HELP:
+		mount_help();
+		mo->ishelp = 1;
+		break;
+
+	case KEY_VERSION:
+		mount_version();
+		mo->ishelp = 1;
+		break;
+	}
+	return 1;
+}
+
+/* return value:
+ * >= 0	 => fd
+ * -1	 => error
+ */
+static int receive_fd(int fd)
+{
+	struct msghdr msg;
+	struct iovec iov;
+	char buf[1];
+	int rv;
+	size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
+	struct cmsghdr *cmsg;
+
+	iov.iov_base = buf;
+	iov.iov_len = 1;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_name = 0;
+	msg.msg_namelen = 0;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	/* old BSD implementations should use msg_accrights instead of
+	 * msg_control; the interface is different. */
+	msg.msg_control = ccmsg;
+	msg.msg_controllen = sizeof(ccmsg);
+
+	while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
+	if (rv == -1) {
+		perror("recvmsg");
+		return -1;
+	}
+	if(!rv) {
+		/* EOF */
+		return -1;
+	}
+
+	cmsg = CMSG_FIRSTHDR(&msg);
+	if (!cmsg->cmsg_type == SCM_RIGHTS) {
+		fprintf(stderr, "got control message of unknown type %d\n",
+			cmsg->cmsg_type);
+		return -1;
+	}
+	return *(int*)CMSG_DATA(cmsg);
+}
+
+void fuse_kern_unmount(const char *mountpoint, int fd)
+{
+	int res;
+	int pid;
+
+	if (!mountpoint)
+		return;
+
+	if (fd != -1) {
+		struct pollfd pfd;
+
+		pfd.fd = fd;
+		pfd.events = 0;
+		res = poll(&pfd, 1, 0);
+		/* If file poll returns POLLERR on the device file descriptor,
+		   then the filesystem is already unmounted */
+		if (res == 1 && (pfd.revents & POLLERR))
+			return;
+
+		/* Need to close file descriptor, otherwise synchronous umount
+		   would recurse into filesystem, and deadlock */
+		close(fd);
+	}
+
+	if (geteuid() == 0) {
+		fuse_mnt_umount("fuse", mountpoint, mountpoint,  1);
+		return;
+	}
+
+	res = umount2(mountpoint, 2);
+	if (res == 0)
+		return;
+
+	pid = fork();
+	if(pid == -1)
+		return;
+
+	if(pid == 0) {
+		const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
+				       "--", mountpoint, NULL };
+
+		exec_fusermount(argv);
+		_exit(1);
+	}
+	waitpid(pid, NULL, 0);
+}
+
+void fuse_unmount_compat22(const char *mountpoint)
+{
+	fuse_kern_unmount(mountpoint, -1);
+}
+
+static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
+		const char *opts, int quiet)
+{
+	int fds[2], pid;
+	int res;
+	int rv;
+
+	if (!mountpoint) {
+		fprintf(stderr, "fuse: missing mountpoint parameter\n");
+		return -1;
+	}
+
+	res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
+	if(res == -1) {
+		perror("fuse: socketpair() failed");
+		return -1;
+	}
+
+	pid = fork();
+	if(pid == -1) {
+		perror("fuse: fork() failed");
+		close(fds[0]);
+		close(fds[1]);
+		return -1;
+	}
+
+	if(pid == 0) {
+		char env[10];
+		const char *argv[32];
+		int a = 0;
+
+		if (quiet) {
+			int fd = open("/dev/null", O_RDONLY);
+			if (fd != -1) {
+				dup2(fd, 1);
+				dup2(fd, 2);
+			}
+		}
+
+		argv[a++] = FUSERMOUNT_PROG;
+		if (opts) {
+			argv[a++] = "-o";
+			argv[a++] = opts;
+		}
+		argv[a++] = "--";
+		argv[a++] = mountpoint;
+		argv[a++] = NULL;
+
+		close(fds[1]);
+		fcntl(fds[0], F_SETFD, 0);
+		snprintf(env, sizeof(env), "%i", fds[0]);
+		setenv(FUSE_COMMFD_ENV, env, 1);
+		exec_fusermount(argv);
+		perror("fuse: failed to exec fusermount");
+		_exit(1);
+	}
+
+	close(fds[0]);
+	rv = receive_fd(fds[1]);
+
+	if (!mo->auto_unmount) {
+		/* with auto_unmount option fusermount will not exit until 
+		   this socket is closed */
+		close(fds[1]);
+		waitpid(pid, NULL, 0); /* bury zombie */
+	}
+
+	return rv;
+}
+
+int fuse_mount_compat22(const char *mountpoint, const char *opts)
+{
+	struct mount_opts mo;
+	memset(&mo, 0, sizeof(mo));
+	mo.flags = MS_NOSUID | MS_NODEV;
+
+	return fuse_mount_fusermount(mountpoint, &mo, opts, 0);
+}
+
+static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
+			  const char *mnt_opts)
+{
+	char tmp[128];
+	const char *devname = "/dev/fuse";
+	char *source = NULL;
+	char *type = NULL;
+	struct stat stbuf;
+	int fd;
+	int res;
+
+	if (!mnt) {
+		fprintf(stderr, "fuse: missing mountpoint parameter\n");
+		return -1;
+	}
+
+	res = stat(mnt, &stbuf);
+	if (res == -1) {
+		fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
+			mnt, strerror(errno));
+		return -1;
+	}
+
+	if (!mo->nonempty) {
+		res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode,
+					   stbuf.st_size);
+		if (res == -1)
+			return -1;
+	}
+
+	if (mo->auto_unmount) {
+		/* Tell the caller to fallback to fusermount because
+		   auto-unmount does not work otherwise. */
+		return -2;
+	}
+
+	fd = open(devname, O_RDWR);
+	if (fd == -1) {
+		if (errno == ENODEV || errno == ENOENT)
+			fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
+		else
+			fprintf(stderr, "fuse: failed to open %s: %s\n",
+				devname, strerror(errno));
+		return -1;
+	}
+
+	snprintf(tmp, sizeof(tmp),  "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
+		 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
+
+	res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
+	if (res == -1)
+		goto out_close;
+
+	source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
+			(mo->subtype ? strlen(mo->subtype) : 0) +
+			strlen(devname) + 32);
+
+	type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
+	if (!type || !source) {
+		fprintf(stderr, "fuse: failed to allocate memory\n");
+		goto out_close;
+	}
+
+	strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
+	if (mo->subtype) {
+		strcat(type, ".");
+		strcat(type, mo->subtype);
+	}
+	strcpy(source,
+	       mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
+
+	res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
+	if (res == -1 && errno == ENODEV && mo->subtype) {
+		/* Probably missing subtype support */
+		strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
+		if (mo->fsname) {
+			if (!mo->blkdev)
+				sprintf(source, "%s#%s", mo->subtype,
+					mo->fsname);
+		} else {
+			strcpy(source, type);
+		}
+		res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
+	}
+	if (res == -1) {
+		/*
+		 * Maybe kernel doesn't support unprivileged mounts, in this
+		 * case try falling back to fusermount
+		 */
+		if (errno == EPERM) {
+			res = -2;
+		} else {
+			int errno_save = errno;
+			if (mo->blkdev && errno == ENODEV &&
+			    !fuse_mnt_check_fuseblk())
+				fprintf(stderr,
+					"fuse: 'fuseblk' support missing\n");
+			else
+				fprintf(stderr, "fuse: mount failed: %s\n",
+					strerror(errno_save));
+		}
+
+		goto out_close;
+	}
+
+#ifndef __NetBSD__
+#ifndef IGNORE_MTAB
+	if (geteuid() == 0) {
+		char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
+		res = -1;
+		if (!newmnt)
+			goto out_umount;
+
+		res = fuse_mnt_add_mount("fuse", source, newmnt, type,
+					 mnt_opts);
+		free(newmnt);
+		if (res == -1)
+			goto out_umount;
+	}
+#endif /* IGNORE_MTAB */
+#endif /* __NetBSD__ */
+	free(type);
+	free(source);
+
+	return fd;
+
+out_umount:
+	umount2(mnt, 2); /* lazy umount */
+out_close:
+	free(type);
+	free(source);
+	close(fd);
+	return res;
+}
+
+static int get_mnt_flag_opts(char **mnt_optsp, int flags)
+{
+	int i;
+
+	if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
+		return -1;
+
+	for (i = 0; mount_flags[i].opt != NULL; i++) {
+		if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
+		    fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
+			return -1;
+	}
+	return 0;
+}
+
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
+{
+	struct mount_opts mo;
+	int res = -1;
+	char *mnt_opts = NULL;
+
+	memset(&mo, 0, sizeof(mo));
+	mo.flags = MS_NOSUID | MS_NODEV;
+
+	if (args &&
+	    fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
+		return -1;
+
+	if (mo.allow_other && mo.allow_root) {
+		fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
+		goto out;
+	}
+	res = 0;
+	if (mo.ishelp)
+		goto out;
+
+	res = -1;
+	if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
+		goto out;
+	if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
+		goto out;
+	if (mo.mtab_opts &&  fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
+		goto out;
+
+	res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
+	if (res == -2) {
+		if (mo.fusermount_opts &&
+		    fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
+			goto out;
+
+		if (mo.subtype) {
+			char *tmp_opts = NULL;
+
+			res = -1;
+			if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
+			    fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
+				free(tmp_opts);
+				goto out;
+			}
+
+			res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
+			free(tmp_opts);
+			if (res == -1)
+				res = fuse_mount_fusermount(mountpoint, &mo,
+							    mnt_opts, 0);
+		} else {
+			res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
+		}
+	}
+out:
+	free(mnt_opts);
+	free(mo.fsname);
+	free(mo.subtype);
+	free(mo.fusermount_opts);
+	free(mo.subtype_opt);
+	free(mo.kernel_opts);
+	free(mo.mtab_opts);
+	return res;
+}
+
+FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
+FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
diff --git a/fuse/mount_bsd.c b/fuse/mount_bsd.c
new file mode 100644
index 0000000..62443ac
--- /dev/null
+++ b/fuse/mount_bsd.c
@@ -0,0 +1,388 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_opt.h"
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <paths.h>
+#include <limits.h>
+
+#define FUSERMOUNT_PROG		"mount_fusefs"
+#define FUSE_DEV_TRUNK		"/dev/fuse"
+
+enum {
+	KEY_ALLOW_ROOT,
+	KEY_RO,
+	KEY_HELP,
+	KEY_VERSION,
+	KEY_KERN
+};
+
+struct mount_opts {
+	int allow_other;
+	int allow_root;
+	int ishelp;
+	char *kernel_opts;
+};
+
+#define FUSE_DUAL_OPT_KEY(templ, key) 				\
+	FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
+
+static const struct fuse_opt fuse_mount_opts[] = {
+	{ "allow_other", offsetof(struct mount_opts, allow_other), 1 },
+	{ "allow_root", offsetof(struct mount_opts, allow_root), 1 },
+	FUSE_OPT_KEY("allow_root",		KEY_ALLOW_ROOT),
+	FUSE_OPT_KEY("-r",			KEY_RO),
+	FUSE_OPT_KEY("-h",			KEY_HELP),
+	FUSE_OPT_KEY("--help",			KEY_HELP),
+	FUSE_OPT_KEY("-V",			KEY_VERSION),
+	FUSE_OPT_KEY("--version",		KEY_VERSION),
+	/* standard FreeBSD mount options */
+	FUSE_DUAL_OPT_KEY("dev",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("async",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("atime",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("dev",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("exec",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("suid",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("symfollow",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("rdonly",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("sync",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("union",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("userquota",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("groupquota",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("clusterr",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("clusterw",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("suiddir",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("snapshot",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("multilabel",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("acls",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("force",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("update",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("ro",			KEY_KERN),
+	FUSE_DUAL_OPT_KEY("rw",			KEY_KERN),
+	FUSE_DUAL_OPT_KEY("auto",		KEY_KERN),
+	/* options supported under both Linux and FBSD */
+	FUSE_DUAL_OPT_KEY("allow_other",	KEY_KERN),
+	FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
+	FUSE_OPT_KEY("max_read=",		KEY_KERN),
+	FUSE_OPT_KEY("subtype=",		KEY_KERN),
+	/* FBSD FUSE specific mount options */
+	FUSE_DUAL_OPT_KEY("private",		KEY_KERN),
+	FUSE_DUAL_OPT_KEY("neglect_shares",	KEY_KERN),
+	FUSE_DUAL_OPT_KEY("push_symlinks_in",	KEY_KERN),
+	FUSE_OPT_KEY("nosync_unmount",		KEY_KERN),
+	/* stock FBSD mountopt parsing routine lets anything be negated... */
+	/*
+	 * Linux specific mount options, but let just the mount util
+	 * handle them
+	 */
+	FUSE_OPT_KEY("fsname=",			KEY_KERN),
+	FUSE_OPT_KEY("nonempty",		KEY_KERN),
+	FUSE_OPT_KEY("large_read",		KEY_KERN),
+	FUSE_OPT_END
+};
+
+static void mount_help(void)
+{
+	fprintf(stderr,
+		"    -o allow_root          allow access to root\n"
+		);
+	system(FUSERMOUNT_PROG " --help");
+	fputc('\n', stderr);
+}
+
+static void mount_version(void)
+{
+	system(FUSERMOUNT_PROG " --version");
+}
+
+static int fuse_mount_opt_proc(void *data, const char *arg, int key,
+			       struct fuse_args *outargs)
+{
+	struct mount_opts *mo = data;
+
+	switch (key) {
+	case KEY_ALLOW_ROOT:
+		if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
+		    fuse_opt_add_arg(outargs, "-oallow_root") == -1)
+			return -1;
+		return 0;
+
+	case KEY_RO:
+		arg = "ro";
+		/* fall through */
+
+	case KEY_KERN:
+		return fuse_opt_add_opt(&mo->kernel_opts, arg);
+
+	case KEY_HELP:
+		mount_help();
+		mo->ishelp = 1;
+		break;
+
+	case KEY_VERSION:
+		mount_version();
+		mo->ishelp = 1;
+		break;
+	}
+	return 1;
+}
+
+void fuse_unmount_compat22(const char *mountpoint)
+{
+	char dev[128];
+	char *ssc, *umount_cmd;
+	FILE *sf;
+	int rv;
+	char seekscript[] =
+		/* error message is annoying in help output */
+		"exec 2>/dev/null; "
+		"/usr/bin/fstat " FUSE_DEV_TRUNK "* | "
+		"/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; "
+		"              { if ($3 == %d) print $10; }' | "
+		"/usr/bin/sort | "
+		"/usr/bin/uniq | "
+		"/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'";
+
+	(void) mountpoint;
+
+	/*
+	 * If we don't know the fd, we have to resort to the scripted
+	 * solution -- iterating over the fd-s is unpractical, as we
+	 * don't know how many of open files we have. (This could be
+	 * looked up in procfs -- however, that's optional on FBSD; or
+	 * read out from the kmem -- however, that's bound to
+	 * privileges (in fact, that's what happens when we call the
+	 * setgid kmem fstat(1) utility).
+	 */
+	if (asprintf(&ssc, seekscript, getpid()) == -1)
+		return;
+
+	errno = 0;
+	sf = popen(ssc, "r");
+	free(ssc);
+	if (! sf)
+		return;
+
+	fgets(dev, sizeof(dev), sf);
+	rv = pclose(sf);
+	if (rv)
+		return;
+
+	if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1)
+		return;
+	system(umount_cmd);
+	free(umount_cmd);
+}
+
+static void do_unmount(char *dev, int fd)
+{
+	char device_path[SPECNAMELEN + 12];
+	const char *argv[4];
+	const char umount_cmd[] = "/sbin/umount";
+	pid_t pid;
+
+	snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
+
+	argv[0] = umount_cmd;
+	argv[1] = "-f";
+	argv[2] = device_path;
+	argv[3] = NULL;
+
+	pid = fork();
+
+	if (pid == -1)
+		return;
+
+	if (pid == 0) {
+		close(fd);
+		execvp(umount_cmd, (char **)argv);
+		exit(1);
+	}
+
+	waitpid(pid, NULL, 0);
+}
+
+void fuse_kern_unmount(const char *mountpoint, int fd)
+{
+	char *ep, dev[128];
+	struct stat sbuf;
+
+	(void)mountpoint;
+
+	if (fstat(fd, &sbuf) == -1)
+		return;
+
+	devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
+
+	if (strncmp(dev, "fuse", 4))
+		return;
+
+	strtol(dev + 4, &ep, 10);
+	if (*ep != '\0')
+		return;
+
+	do_unmount(dev, fd);
+}
+
+/* Check if kernel is doing init in background */
+static int init_backgrounded(void)
+{
+	unsigned ibg, len;
+
+	len = sizeof(ibg);
+
+	if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
+		return 0;
+
+	return ibg;
+}
+
+
+static int fuse_mount_core(const char *mountpoint, const char *opts)
+{
+	const char *mountprog = FUSERMOUNT_PROG;
+	int fd;
+	char *fdnam, *dev;
+	pid_t pid, cpid;
+	int status;
+
+	fdnam = getenv("FUSE_DEV_FD");
+
+	if (fdnam) {
+		char *ep;
+
+		fd = strtol(fdnam, &ep, 10);
+
+		if (*ep != '\0') {
+			fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
+			return -1;
+		}
+
+		if (fd < 0)
+			return -1;
+
+		goto mount;
+	}
+
+	dev = getenv("FUSE_DEV_NAME");
+
+	if (! dev)
+		dev = (char *)FUSE_DEV_TRUNK;
+
+	if ((fd = open(dev, O_RDWR)) < 0) {
+		perror("fuse: failed to open fuse device");
+		return -1;
+	}
+
+mount:
+	if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
+		goto out;
+
+	pid = fork();
+	cpid = pid;
+
+	if (pid == -1) {
+		perror("fuse: fork() failed");
+		close(fd);
+		return -1;
+	}
+
+	if (pid == 0) {
+		if (! init_backgrounded()) {
+			/*
+			 * If init is not backgrounded, we have to
+			 * call the mount util backgrounded, to avoid
+			 * deadlock.
+			 */
+
+			pid = fork();
+
+			if (pid == -1) {
+				perror("fuse: fork() failed");
+				close(fd);
+				exit(1);
+			}
+		}
+
+		if (pid == 0) {
+			const char *argv[32];
+			int a = 0;
+
+			if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
+				perror("fuse: failed to assemble mount arguments");
+				exit(1);
+			}
+
+			argv[a++] = mountprog;
+			if (opts) {
+				argv[a++] = "-o";
+				argv[a++] = opts;
+			}
+			argv[a++] = fdnam;
+			argv[a++] = mountpoint;
+			argv[a++] = NULL;
+			execvp(mountprog, (char **) argv);
+			perror("fuse: failed to exec mount program");
+			exit(1);
+		}
+
+		exit(0);
+	}
+
+	if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
+		perror("fuse: failed to mount file system");
+		close(fd);
+		return -1;
+	}
+
+out:
+	return fd;
+}
+
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
+{
+	struct mount_opts mo;
+	int res = -1;
+
+	memset(&mo, 0, sizeof(mo));
+	/* mount util should not try to spawn the daemon */
+	setenv("MOUNT_FUSEFS_SAFE", "1", 1);
+	/* to notify the mount util it's called from lib */
+	setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
+
+	if (args &&
+	    fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
+		return -1;
+
+	if (mo.allow_other && mo.allow_root) {
+		fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
+		goto out;
+	}
+	if (mo.ishelp)
+		return 0;
+
+	res = fuse_mount_core(mountpoint, mo.kernel_opts);
+out:
+	free(mo.kernel_opts);
+	return res;
+}
+
+FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
diff --git a/fuse/mount_util.c b/fuse/mount_util.c
new file mode 100644
index 0000000..bfd801f
--- /dev/null
+++ b/fuse/mount_util.c
@@ -0,0 +1,350 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include "mount_util.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <mntent.h>
+#include <paths.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+
+#ifdef __NetBSD__
+#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
+#define mtab_needs_update(mnt) 0
+#else
+static int mtab_needs_update(const char *mnt)
+{
+	int res;
+	struct stat stbuf;
+
+	/* If mtab is within new mount, don't touch it */
+	if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
+	    _PATH_MOUNTED[strlen(mnt)] == '/')
+		return 0;
+
+	/*
+	 * Skip mtab update if /etc/mtab:
+	 *
+	 *  - doesn't exist,
+	 *  - is a symlink,
+	 *  - is on a read-only filesystem.
+	 */
+	res = lstat(_PATH_MOUNTED, &stbuf);
+	if (res == -1) {
+		if (errno == ENOENT)
+			return 0;
+	} else {
+		uid_t ruid;
+		int err;
+
+		if (S_ISLNK(stbuf.st_mode))
+			return 0;
+
+		ruid = getuid();
+		if (ruid != 0)
+			setreuid(0, -1);
+
+		res = access(_PATH_MOUNTED, W_OK);
+		err = (res == -1) ? errno : 0;
+		if (ruid != 0)
+			setreuid(ruid, -1);
+
+		if (err == EROFS)
+			return 0;
+	}
+
+	return 1;
+}
+#endif /* __NetBSD__ */
+
+static int add_mount(const char *progname, const char *fsname,
+		       const char *mnt, const char *type, const char *opts)
+{
+	int res;
+	int status;
+	sigset_t blockmask;
+	sigset_t oldmask;
+
+	sigemptyset(&blockmask);
+	sigaddset(&blockmask, SIGCHLD);
+	res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
+	if (res == -1) {
+		fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
+		return -1;
+	}
+
+	res = fork();
+	if (res == -1) {
+		fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
+		goto out_restore;
+	}
+	if (res == 0) {
+		sigprocmask(SIG_SETMASK, &oldmask, NULL);
+		setuid(geteuid());
+		execl("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
+		      "-f", "-t", type, "-o", opts, fsname, mnt, NULL);
+		fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
+			progname, strerror(errno));
+		exit(1);
+	}
+	res = waitpid(res, &status, 0);
+	if (res == -1)
+		fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
+
+	if (status != 0)
+		res = -1;
+
+ out_restore:
+	sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+	return res;
+}
+
+int fuse_mnt_add_mount(const char *progname, const char *fsname,
+		       const char *mnt, const char *type, const char *opts)
+{
+	if (!mtab_needs_update(mnt))
+		return 0;
+
+	return add_mount(progname, fsname, mnt, type, opts);
+}
+
+static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
+{
+	int res;
+	int status;
+	sigset_t blockmask;
+	sigset_t oldmask;
+
+	sigemptyset(&blockmask);
+	sigaddset(&blockmask, SIGCHLD);
+	res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
+	if (res == -1) {
+		fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
+		return -1;
+	}
+
+	res = fork();
+	if (res == -1) {
+		fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
+		goto out_restore;
+	}
+	if (res == 0) {
+		sigprocmask(SIG_SETMASK, &oldmask, NULL);
+		setuid(geteuid());
+		execl("/bin/umount", "/bin/umount", "-i", rel_mnt,
+		      lazy ? "-l" : NULL, NULL);
+		fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
+			progname, strerror(errno));
+		exit(1);
+	}
+	res = waitpid(res, &status, 0);
+	if (res == -1)
+		fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
+
+	if (status != 0) {
+		res = -1;
+	}
+
+ out_restore:
+	sigprocmask(SIG_SETMASK, &oldmask, NULL);
+	return res;
+
+}
+
+int fuse_mnt_umount(const char *progname, const char *abs_mnt,
+		    const char *rel_mnt, int lazy)
+{
+	int res;
+
+	if (!mtab_needs_update(abs_mnt)) {
+		res = umount2(rel_mnt, lazy ? 2 : 0);
+		if (res == -1)
+			fprintf(stderr, "%s: failed to unmount %s: %s\n",
+				progname, abs_mnt, strerror(errno));
+		return res;
+	}
+
+	return exec_umount(progname, rel_mnt, lazy);
+}
+
+static int remove_mount(const char *progname, const char *mnt)
+{
+	int res;
+	int status;
+	sigset_t blockmask;
+	sigset_t oldmask;
+
+	sigemptyset(&blockmask);
+	sigaddset(&blockmask, SIGCHLD);
+	res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
+	if (res == -1) {
+		fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
+		return -1;
+	}
+
+	res = fork();
+	if (res == -1) {
+		fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
+		goto out_restore;
+	}
+	if (res == 0) {
+		sigprocmask(SIG_SETMASK, &oldmask, NULL);
+		setuid(geteuid());
+		execl("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
+		      "--fake", mnt, NULL);
+		fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
+			progname, strerror(errno));
+		exit(1);
+	}
+	res = waitpid(res, &status, 0);
+	if (res == -1)
+		fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
+
+	if (status != 0)
+		res = -1;
+
+ out_restore:
+	sigprocmask(SIG_SETMASK, &oldmask, NULL);
+	return res;
+}
+
+int fuse_mnt_remove_mount(const char *progname, const char *mnt)
+{
+	if (!mtab_needs_update(mnt))
+		return 0;
+
+	return remove_mount(progname, mnt);
+}
+
+char *fuse_mnt_resolve_path(const char *progname, const char *orig)
+{
+	char buf[PATH_MAX];
+	char *copy;
+	char *dst;
+	char *end;
+	char *lastcomp;
+	const char *toresolv;
+
+	if (!orig[0]) {
+		fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
+			orig);
+		return NULL;
+	}
+
+	copy = strdup(orig);
+	if (copy == NULL) {
+		fprintf(stderr, "%s: failed to allocate memory\n", progname);
+		return NULL;
+	}
+
+	toresolv = copy;
+	lastcomp = NULL;
+	for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
+	if (end[0] != '/') {
+		char *tmp;
+		end[1] = '\0';
+		tmp = strrchr(copy, '/');
+		if (tmp == NULL) {
+			lastcomp = copy;
+			toresolv = ".";
+		} else {
+			lastcomp = tmp + 1;
+			if (tmp == copy)
+				toresolv = "/";
+		}
+		if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
+			lastcomp = NULL;
+			toresolv = copy;
+		}
+		else if (tmp)
+			tmp[0] = '\0';
+	}
+	if (realpath(toresolv, buf) == NULL) {
+		fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
+			strerror(errno));
+		free(copy);
+		return NULL;
+	}
+	if (lastcomp == NULL)
+		dst = strdup(buf);
+	else {
+		dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
+		if (dst) {
+			unsigned buflen = strlen(buf);
+			if (buflen && buf[buflen-1] == '/')
+				sprintf(dst, "%s%s", buf, lastcomp);
+			else
+				sprintf(dst, "%s/%s", buf, lastcomp);
+		}
+	}
+	free(copy);
+	if (dst == NULL)
+		fprintf(stderr, "%s: failed to allocate memory\n", progname);
+	return dst;
+}
+
+int fuse_mnt_check_empty(const char *progname, const char *mnt,
+			 mode_t rootmode, off64_t rootsize)
+{
+	int isempty = 1;
+
+	if (S_ISDIR(rootmode)) {
+		struct dirent *ent;
+		DIR *dp = opendir(mnt);
+		if (dp == NULL) {
+			fprintf(stderr,
+				"%s: failed to open mountpoint for reading: %s\n",
+				progname, strerror(errno));
+			return -1;
+		}
+		while ((ent = readdir(dp)) != NULL) {
+			if (strcmp(ent->d_name, ".") != 0 &&
+			    strcmp(ent->d_name, "..") != 0) {
+				isempty = 0;
+				break;
+			}
+		}
+		closedir(dp);
+	} else if (rootsize)
+		isempty = 0;
+
+	if (!isempty) {
+		fprintf(stderr, "%s: mountpoint is not empty\n", progname);
+		fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
+		return -1;
+	}
+	return 0;
+}
+
+int fuse_mnt_check_fuseblk(void)
+{
+	char buf[256];
+	FILE *f = fopen("/proc/filesystems", "r");
+	if (!f)
+		return 1;
+
+	while (fgets(buf, sizeof(buf), f))
+		if (strstr(buf, "fuseblk\n")) {
+			fclose(f);
+			return 1;
+		}
+
+	fclose(f);
+	return 0;
+}
diff --git a/fuse/mount_util.h b/fuse/mount_util.h
new file mode 100644
index 0000000..f89c115
--- /dev/null
+++ b/fuse/mount_util.h
@@ -0,0 +1,19 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#include <sys/types.h>
+
+int fuse_mnt_add_mount(const char *progname, const char *fsname,
+		       const char *mnt, const char *type, const char *opts);
+int fuse_mnt_remove_mount(const char *progname, const char *mnt);
+int fuse_mnt_umount(const char *progname, const char *abs_mnt,
+		    const char *rel_mnt, int lazy);
+char *fuse_mnt_resolve_path(const char *progname, const char *orig);
+int fuse_mnt_check_empty(const char *progname, const char *mnt,
+			 mode_t rootmode, off64_t rootsize);
+int fuse_mnt_check_fuseblk(void);
diff --git a/fuse/ulockmgr.c b/fuse/ulockmgr.c
new file mode 100644
index 0000000..f0523ae
--- /dev/null
+++ b/fuse/ulockmgr.c
@@ -0,0 +1,444 @@
+/*
+  libulockmgr: Userspace Lock Manager Library
+  Copyright (C) 2006  Miklos Szeredi <miklos@szeredi.hu>
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+/* #define DEBUG 1 */
+
+#include "ulockmgr.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+struct message {
+	unsigned intr : 1;
+	unsigned nofd : 1;
+	pthread_t thr;
+	int cmd;
+	int fd;
+	struct flock lock;
+	int error;
+};
+
+struct fd_store {
+	struct fd_store *next;
+	int fd;
+	int inuse;
+};
+
+struct owner {
+	struct owner *next;
+	struct owner *prev;
+	struct fd_store *fds;
+	void *id;
+	size_t id_len;
+	int cfd;
+};
+
+static pthread_mutex_t ulockmgr_lock;
+static int ulockmgr_cfd = -1;
+static struct owner owner_list = { .next = &owner_list, .prev = &owner_list };
+
+#define MAX_SEND_FDS 2
+
+static void list_del_owner(struct owner *owner)
+{
+	struct owner *prev = owner->prev;
+	struct owner *next = owner->next;
+	prev->next = next;
+	next->prev = prev;
+}
+
+static void list_add_owner(struct owner *owner, struct owner *next)
+{
+	struct owner *prev = next->prev;
+	owner->next = next;
+	owner->prev = prev;
+	prev->next = owner;
+	next->prev = owner;
+}
+
+/*
+ * There's a bug in the linux kernel (< 2.6.22) recv() implementation
+ * on AF_UNIX, SOCK_STREAM sockets, that could cause it to return
+ * zero, even if data was available.  Retrying the recv will return
+ * the data in this case.
+ */
+static int do_recv(int sock, void *buf, size_t len, int flags)
+{
+	int res = recv(sock, buf, len, flags);
+	if (res == 0)
+		res = recv(sock, buf, len, flags);
+
+	return res;
+}
+
+static int ulockmgr_send_message(int sock, void *buf, size_t buflen,
+				 int *fdp, int numfds)
+{
+	struct msghdr msg;
+	struct cmsghdr *p_cmsg;
+	struct iovec vec;
+	size_t cmsgbuf[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
+	int res;
+
+	assert(numfds <= MAX_SEND_FDS);
+	msg.msg_control = cmsgbuf;
+	msg.msg_controllen = sizeof(cmsgbuf);
+	p_cmsg = CMSG_FIRSTHDR(&msg);
+	p_cmsg->cmsg_level = SOL_SOCKET;
+	p_cmsg->cmsg_type = SCM_RIGHTS;
+	p_cmsg->cmsg_len = CMSG_LEN(sizeof(int) * numfds);
+	memcpy(CMSG_DATA(p_cmsg), fdp, sizeof(int) * numfds);
+	msg.msg_controllen = p_cmsg->cmsg_len;
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_iov = &vec;
+	msg.msg_iovlen = 1;
+	msg.msg_flags = 0;
+	vec.iov_base = buf;
+	vec.iov_len = buflen;
+	res = sendmsg(sock, &msg, MSG_NOSIGNAL);
+	if (res == -1) {
+		perror("libulockmgr: sendmsg");
+		return -1;
+	}
+	if ((size_t) res != buflen) {
+		fprintf(stderr, "libulockmgr: sendmsg short\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int ulockmgr_start_daemon(void)
+{
+	int sv[2];
+	int res;
+	char tmp[64];
+
+	res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+	if (res == -1) {
+		perror("libulockmgr: socketpair");
+		return -1;
+	}
+	snprintf(tmp, sizeof(tmp), "exec ulockmgr_server %i", sv[0]);
+	res = system(tmp);
+	close(sv[0]);
+	if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) {
+		close(sv[1]);
+		return -1;
+	}
+	ulockmgr_cfd = sv[1];
+	return 0;
+}
+
+static struct owner *ulockmgr_new_owner(const void *id, size_t id_len)
+{
+	int sv[2];
+	int res;
+	char c = 'm';
+	struct owner *o;
+
+	if (ulockmgr_cfd == -1 && ulockmgr_start_daemon() == -1)
+		return NULL;
+
+	o = calloc(1, sizeof(struct owner) + id_len);
+	if (!o) {
+		fprintf(stderr, "libulockmgr: failed to allocate memory\n");
+		return NULL;
+	}
+	o->id = o + 1;
+	o->id_len = id_len;
+	res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+	if (res == -1) {
+		perror("libulockmgr: socketpair");
+		goto out_free;
+	}
+	res = ulockmgr_send_message(ulockmgr_cfd, &c, sizeof(c), &sv[0], 1);
+	close(sv[0]);
+	if (res == -1) {
+		close(ulockmgr_cfd);
+		ulockmgr_cfd = -1;
+		goto out_close;
+	}
+
+	o->cfd = sv[1];
+	memcpy(o->id, id, id_len);
+	list_add_owner(o, &owner_list);
+
+	return o;
+
+out_close:
+	close(sv[1]);
+out_free:
+	free(o);
+	return NULL;
+}
+
+static int ulockmgr_send_request(struct message *msg, const void *id,
+				 size_t id_len)
+{
+	int sv[2];
+	int cfd;
+	struct owner *o;
+	struct fd_store *f = NULL;
+	struct fd_store *newf = NULL;
+	struct fd_store **fp;
+	int fd = msg->fd;
+	int cmd = msg->cmd;
+	int res;
+	int unlockall = (cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
+			 msg->lock.l_start == 0 && msg->lock.l_len == 0);
+
+	for (o = owner_list.next; o != &owner_list; o = o->next)
+		if (o->id_len == id_len && memcmp(o->id, id, id_len) == 0)
+			break;
+
+	if (o == &owner_list)
+		o = NULL;
+
+	if (!o && cmd != F_GETLK && msg->lock.l_type != F_UNLCK)
+		o = ulockmgr_new_owner(id, id_len);
+
+	if (!o) {
+		if (cmd == F_GETLK) {
+			res = fcntl(msg->fd, F_GETLK, &msg->lock);
+			return (res == -1) ? -errno : 0;
+		} else if (msg->lock.l_type == F_UNLCK)
+			return 0;
+		else
+			return -ENOLCK;
+	}
+
+	if (unlockall)
+		msg->nofd = 1;
+	else {
+		for (fp = &o->fds; *fp; fp = &(*fp)->next) {
+			f = *fp;
+			if (f->fd == fd) {
+				msg->nofd = 1;
+				break;
+			}
+		}
+	}
+
+	if (!msg->nofd) {
+		newf = f = calloc(1, sizeof(struct fd_store));
+		if (!f) {
+			fprintf(stderr, "libulockmgr: failed to allocate memory\n");
+			return -ENOLCK;
+		}
+	}
+
+	res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+	if (res == -1) {
+		perror("libulockmgr: socketpair");
+		free(newf);
+		return -ENOLCK;
+	}
+
+	cfd = sv[1];
+	sv[1] = msg->fd;
+	res = ulockmgr_send_message(o->cfd, msg, sizeof(struct message), sv,
+				    msg->nofd ? 1 : 2);
+	close(sv[0]);
+	if (res == -1) {
+		free(newf);
+		close(cfd);
+		return -EIO;
+	}
+
+	if (newf) {
+		newf->fd = msg->fd;
+		newf->next = o->fds;
+		o->fds = newf;
+	}
+	if (f)
+		f->inuse++;
+
+	res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL);
+	if (res == -1) {
+		perror("libulockmgr: recv");
+		msg->error = EIO;
+	} else if (res != sizeof(struct message)) {
+		fprintf(stderr, "libulockmgr: recv short\n");
+		msg->error = EIO;
+	} else if (cmd == F_SETLKW && msg->error == EAGAIN) {
+		pthread_mutex_unlock(&ulockmgr_lock);
+		while (1) {
+			sigset_t old;
+			sigset_t unblock;
+			int errno_save;
+
+			sigemptyset(&unblock);
+			sigaddset(&unblock, SIGUSR1);
+			pthread_sigmask(SIG_UNBLOCK, &unblock, &old);
+			res = do_recv(cfd, msg, sizeof(struct message),
+				      MSG_WAITALL);
+			errno_save = errno;
+			pthread_sigmask(SIG_SETMASK, &old, NULL);
+			if (res == sizeof(struct message))
+				break;
+			else if (res >= 0) {
+				fprintf(stderr, "libulockmgr: recv short\n");
+				msg->error = EIO;
+				break;
+			} else if (errno_save != EINTR) {
+				errno = errno_save;
+				perror("libulockmgr: recv");
+				msg->error = EIO;
+				break;
+			}
+			msg->intr = 1;
+			res = send(o->cfd, msg, sizeof(struct message),
+				   MSG_NOSIGNAL);
+			if (res == -1) {
+				perror("libulockmgr: send");
+				msg->error = EIO;
+				break;
+			}
+			if (res != sizeof(struct message)) {
+				fprintf(stderr, "libulockmgr: send short\n");
+				msg->error = EIO;
+				break;
+			}
+		}
+		pthread_mutex_lock(&ulockmgr_lock);
+
+	}
+	if (f)
+		f->inuse--;
+	close(cfd);
+	if (unlockall) {
+		for (fp = &o->fds; *fp;) {
+			f = *fp;
+			if (f->fd == fd && !f->inuse) {
+				*fp = f->next;
+				free(f);
+			} else
+				fp = &f->next;
+		}
+		if (!o->fds) {
+			list_del_owner(o);
+			close(o->cfd);
+			free(o);
+		}
+		/* Force OK on unlock-all, since it _will_ succeed once the
+		   owner is deleted */
+		msg->error = 0;
+	}
+
+	return -msg->error;
+}
+
+#ifdef DEBUG
+static uint32_t owner_hash(const unsigned char *id, size_t id_len)
+{
+	uint32_t h = 0;
+	size_t i;
+	for (i = 0; i < id_len; i++)
+		h = ((h << 8) | (h >> 24)) ^ id[i];
+
+	return h;
+}
+#endif
+
+static int ulockmgr_canonicalize(int fd, struct flock *lock)
+{
+	off64_t offset;
+	if (lock->l_whence == SEEK_CUR) {
+		offset = lseek(fd, 0, SEEK_CUR);
+		if (offset == (off64_t) -1)
+			return -errno;
+	} else if (lock->l_whence == SEEK_END) {
+		struct stat stbuf;
+		int res = fstat(fd, &stbuf);
+		if (res == -1)
+			return -errno;
+
+		offset = stbuf.st_size;
+	} else
+		offset = 0;
+
+	lock->l_whence = SEEK_SET;
+	lock->l_start += offset;
+
+	if (lock->l_start < 0)
+		return -EINVAL;
+
+	if (lock->l_len < 0) {
+		lock->l_start += lock->l_len;
+		if (lock->l_start < 0)
+			return -EINVAL;
+		lock->l_len = -lock->l_len;
+	}
+	if (lock->l_len && lock->l_start + lock->l_len - 1 < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
+		size_t owner_len)
+{
+	int err;
+	struct message msg;
+	sigset_t old;
+	sigset_t block;
+
+	if (cmd != F_GETLK && cmd != F_SETLK && cmd != F_SETLKW)
+		return -EINVAL;
+
+	if (lock->l_type != F_RDLCK && lock->l_type != F_WRLCK &&
+	    lock->l_type != F_UNLCK)
+		return -EINVAL;
+
+	if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR &&
+	    lock->l_whence != SEEK_END)
+		return -EINVAL;
+
+#ifdef DEBUG
+	fprintf(stderr, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
+		cmd, lock->l_type, lock->l_whence, lock->l_start, lock->l_len,
+		owner_hash(owner, owner_len));
+#endif
+
+	/* Unlock should never block anyway */
+	if (cmd == F_SETLKW && lock->l_type == F_UNLCK)
+		cmd = F_SETLK;
+
+	memset(&msg, 0, sizeof(struct message));
+	msg.cmd = cmd;
+	msg.fd = fd;
+	msg.lock = *lock;
+	err = ulockmgr_canonicalize(fd, &msg.lock);
+	if (err)
+		return err;
+
+	sigemptyset(&block);
+	sigaddset(&block, SIGUSR1);
+	pthread_sigmask(SIG_BLOCK, &block, &old);
+	pthread_mutex_lock(&ulockmgr_lock);
+	err = ulockmgr_send_request(&msg, owner, owner_len);
+	pthread_mutex_unlock(&ulockmgr_lock);
+	pthread_sigmask(SIG_SETMASK, &old, NULL);
+	if (!err && cmd == F_GETLK) {
+		if (msg.lock.l_type == F_UNLCK)
+			lock->l_type = F_UNLCK;
+		else
+			*lock = msg.lock;
+	}
+
+	return err;
+}
diff --git a/gui/Android.mk b/gui/Android.mk
new file mode 100644
index 0000000..96b2fee
--- /dev/null
+++ b/gui/Android.mk
@@ -0,0 +1,140 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := -fno-strict-aliasing
+
+LOCAL_SRC_FILES := \
+    gui.cpp \
+    resources.cpp \
+    pages.cpp \
+    text.cpp \
+    image.cpp \
+    action.cpp \
+    console.cpp \
+    fill.cpp \
+    button.cpp \
+    checkbox.cpp \
+    fileselector.cpp \
+    progressbar.cpp \
+    animation.cpp \
+    object.cpp \
+    slider.cpp \
+    slidervalue.cpp \
+    listbox.cpp \
+    keyboard.cpp \
+    input.cpp \
+    blanktimer.cpp \
+    partitionlist.cpp \
+    mousecursor.cpp
+
+ifneq ($(TWRP_CUSTOM_KEYBOARD),)
+  LOCAL_SRC_FILES += $(TWRP_CUSTOM_KEYBOARD)
+else
+  LOCAL_SRC_FILES += hardwarekeyboard.cpp
+endif
+
+LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++
+LOCAL_MODULE := libguitwrp
+
+# Use this flag to create a build that simulates threaded actions like installing zips, backups, restores, and wipes for theme testing
+#TWRP_SIMULATE_ACTIONS := true
+ifeq ($(TWRP_SIMULATE_ACTIONS), true)
+LOCAL_CFLAGS += -D_SIMULATE_ACTIONS
+endif
+
+#TWRP_EVENT_LOGGING := true
+ifeq ($(TWRP_EVENT_LOGGING), true)
+LOCAL_CFLAGS += -D_EVENT_LOGGING
+endif
+
+ifneq ($(TW_NO_SCREEN_BLANK),)
+	LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK
+endif
+ifneq ($(TW_NO_SCREEN_TIMEOUT),)
+	LOCAL_CFLAGS += -DTW_NO_SCREEN_TIMEOUT
+endif
+ifeq ($(HAVE_SELINUX), true)
+LOCAL_CFLAGS += -DHAVE_SELINUX
+endif
+ifeq ($(TW_OEM_BUILD), true)
+    LOCAL_CFLAGS += -DTW_OEM_BUILD
+endif
+ifeq ($(TW_DISABLE_TTF), true)
+    LOCAL_CFLAGS += -DTW_DISABLE_TTF
+endif
+
+ifeq ($(DEVICE_RESOLUTION),)
+$(warning ********************************************************************************)
+$(warning * DEVICE_RESOLUTION is NOT SET in BoardConfig.mk )
+$(warning * Please see http://tinyw.in/nP7d for details    )
+$(warning ********************************************************************************)
+$(error stopping)
+endif
+
+ifeq ($(TW_CUSTOM_THEME),)
+	ifeq "$(wildcard $(commands_recovery_local_path)/gui/devices/$(DEVICE_RESOLUTION))" ""
+	$(warning ********************************************************************************)
+	$(warning * DEVICE_RESOLUTION ($(DEVICE_RESOLUTION)) does NOT EXIST in $(commands_recovery_local_path)/gui/devices )
+	$(warning * Please choose an existing theme or create a new one for your device )
+	$(warning ********************************************************************************)
+	$(error stopping)
+	endif
+endif
+
+LOCAL_C_INCLUDES += bionic external/stlport/stlport $(commands_recovery_local_path)/gui/devices/$(DEVICE_RESOLUTION)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# Transfer in the resources for the device
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrp
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/res
+TWRP_RES_LOC := $(commands_recovery_local_path)/gui/devices/common/res
+TWRP_COMMON_XML := $(hide) echo "No common TWRP XML resources"
+
+ifeq ($(TW_CUSTOM_THEME),)
+	PORTRAIT := 320x480 480x800 480x854 540x960 720x1280 800x1280 1080x1920 1200x1920 1440x2560 1600x2560
+	LANDSCAPE := 800x480 1024x600 1024x768 1280x800 1920x1200 2560x1600
+	WATCH := 240x240 280x280 320x320
+	TWRP_THEME_LOC := $(commands_recovery_local_path)/gui/devices/$(DEVICE_RESOLUTION)/res
+	ifneq ($(filter $(DEVICE_RESOLUTION), $(PORTRAIT)),)
+		TWRP_COMMON_XML := cp -fr $(commands_recovery_local_path)/gui/devices/portrait/res/* $(TARGET_RECOVERY_ROOT_OUT)/res/
+	else ifneq ($(filter $(DEVICE_RESOLUTION), $(LANDSCAPE)),)
+		TWRP_COMMON_XML := cp -fr $(commands_recovery_local_path)/gui/devices/landscape/res/* $(TARGET_RECOVERY_ROOT_OUT)/res/
+	else ifneq ($(filter $(DEVICE_RESOLUTION), $(WATCH)),)
+		TWRP_COMMON_XML := cp -fr $(commands_recovery_local_path)/gui/devices/watch/res/* $(TARGET_RECOVERY_ROOT_OUT)/res/
+	endif
+else
+	TWRP_THEME_LOC := $(TW_CUSTOM_THEME)
+endif
+
+ifeq ($(TW_DISABLE_TTF), true)
+	TWRP_REMOVE_FONT := rm -f $(TARGET_RECOVERY_ROOT_OUT)/res/fonts/*.ttf
+else
+	TWRP_REMOVE_FONT := rm -f $(TARGET_RECOVERY_ROOT_OUT)/res/fonts/*.dat
+endif
+
+TWRP_RES_GEN := $(intermediates)/twrp
+ifneq ($(TW_USE_TOOLBOX), true)
+	TWRP_SH_TARGET := /sbin/busybox
+else
+	TWRP_SH_TARGET := /sbin/mksh
+endif
+
+$(TWRP_RES_GEN):
+	mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/res/
+	cp -fr $(TWRP_RES_LOC)/* $(TARGET_RECOVERY_ROOT_OUT)/res/
+	cp -fr $(TWRP_THEME_LOC)/* $(TARGET_RECOVERY_ROOT_OUT)/res/
+	$(TWRP_COMMON_XML)
+	$(TWRP_REMOVE_FONT)
+	mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/sbin/
+	ln -sf $(TWRP_SH_TARGET) $(TARGET_RECOVERY_ROOT_OUT)/sbin/sh
+	ln -sf /sbin/pigz $(TARGET_RECOVERY_ROOT_OUT)/sbin/gzip
+	ln -sf /sbin/unpigz $(TARGET_RECOVERY_ROOT_OUT)/sbin/gunzip
+
+
+LOCAL_GENERATED_SOURCES := $(TWRP_RES_GEN)
+LOCAL_SRC_FILES := twrp $(TWRP_RES_GEN)
+include $(BUILD_PREBUILT)
diff --git a/gui/action.cpp b/gui/action.cpp
new file mode 100644
index 0000000..398ef50
--- /dev/null
+++ b/gui/action.cpp
@@ -0,0 +1,1581 @@
+/*update
+	Copyright 2013 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <linux/input.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <pwd.h>
+
+#include <string>
+#include <sstream>
+#include "../partitions.hpp"
+#include "../twrp-functions.hpp"
+#include "../openrecoveryscript.hpp"
+
+#include "../adb_install.h"
+#ifndef TW_NO_SCREEN_TIMEOUT
+#include "blanktimer.hpp"
+#endif
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+#include "../variables.h"
+#include "../twinstall.h"
+#include "cutils/properties.h"
+#include "../minadbd/adb.h"
+
+int TWinstall_zip(const char* path, int* wipe_cache);
+void run_script(const char *str1, const char *str2, const char *str3, const char *str4, const char *str5, const char *str6, const char *str7, int request_confirm);
+int gui_console_only();
+int gui_start();
+};
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+#ifndef TW_NO_SCREEN_TIMEOUT
+extern blanktimer blankTimer;
+#endif
+
+void curtainClose(void);
+
+GUIAction::GUIAction(xml_node<>* node)
+	: GUIObject(node)
+{
+	xml_node<>* child;
+	xml_node<>* actions;
+	xml_attribute<>* attr;
+
+	if (!node)  return;
+
+	// First, get the action
+	actions = node->first_node("actions");
+	if (actions)	child = actions->first_node("action");
+	else			child = node->first_node("action");
+
+	if (!child) return;
+
+	while (child)
+	{
+		Action action;
+
+		attr = child->first_attribute("function");
+		if (!attr)  return;
+
+		action.mFunction = attr->value();
+		action.mArg = child->value();
+		mActions.push_back(action);
+
+		child = child->next_sibling("action");
+	}
+
+	// Now, let's get either the key or region
+	child = node->first_node("touch");
+	if (child)
+	{
+		attr = child->first_attribute("key");
+		if (attr)
+		{
+			std::vector<std::string> keys = TWFunc::Split_String(attr->value(), "+");
+			for(size_t i = 0; i < keys.size(); ++i)
+			{
+				const int key = getKeyByName(keys[i]);
+				mKeys[key] = false;
+			}
+		}
+		else
+		{
+			attr = child->first_attribute("x");
+			if (!attr)  return;
+			mActionX = atol(attr->value());
+			attr = child->first_attribute("y");
+			if (!attr)  return;
+			mActionY = atol(attr->value());
+			attr = child->first_attribute("w");
+			if (!attr)  return;
+			mActionW = atol(attr->value());
+			attr = child->first_attribute("h");
+			if (!attr)  return;
+			mActionH = atol(attr->value());
+		}
+	}
+}
+
+int GUIAction::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if (state == TOUCH_RELEASE)
+		doActions();
+
+	return 0;
+}
+
+int GUIAction::NotifyKey(int key, bool down)
+{
+	if (mKeys.empty())
+		return 0;
+
+	std::map<int, bool>::iterator itr = mKeys.find(key);
+	if(itr == mKeys.end())
+		return 0;
+
+	bool prevState = itr->second;
+	itr->second = down;
+
+	// If there is only one key for this action, wait for key up so it
+	// doesn't trigger with multi-key actions.
+	// Else, check if all buttons are pressed, then consume their release events
+	// so they don't trigger one-button actions and reset mKeys pressed status
+	if(mKeys.size() == 1) {
+		if(!down && prevState)
+			doActions();
+	} else if(down) {
+		for(itr = mKeys.begin(); itr != mKeys.end(); ++itr) {
+			if(!itr->second)
+				return 0;
+		}
+
+		// Passed, all req buttons are pressed, reset them and consume release events
+		HardwareKeyboard *kb = PageManager::GetHardwareKeyboard();
+		for(itr = mKeys.begin(); itr != mKeys.end(); ++itr) {
+			kb->ConsumeKeyRelease(itr->first);
+			itr->second = false;
+		}
+
+		doActions();
+	}
+
+	return 0;
+}
+
+int GUIAction::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIObject::NotifyVarChange(varName, value);
+
+	if (varName.empty() && !isConditionValid() && mKeys.empty() && !mActionW)
+		doActions();
+	else if((varName.empty() || IsConditionVariable(varName)) && isConditionValid() && isConditionTrue())
+		doActions();
+
+	return 0;
+}
+
+void GUIAction::simulate_progress_bar(void)
+{
+	gui_print("Simulating actions...\n");
+	for (int i = 0; i < 5; i++)
+	{
+		usleep(500000);
+		DataManager::SetValue("ui_progress", i * 20);
+	}
+}
+
+int GUIAction::flash_zip(std::string filename, std::string pageName, const int simulate, int* wipe_cache)
+{
+	int ret_val = 0;
+
+	DataManager::SetValue("ui_progress", 0);
+
+	if (filename.empty())
+	{
+		LOGERR("No file specified.\n");
+		return -1;
+	}
+
+	// We're going to jump to this page first, like a loading page
+	gui_changePage(pageName);
+
+	int fd = -1;
+	ZipArchive zip;
+
+	if (!PartitionManager.Mount_By_Path(filename, true))
+		return -1;
+
+	if (mzOpenZipArchive(filename.c_str(), &zip))
+	{
+		LOGERR("Unable to open zip file.\n");
+		return -1;
+	}
+
+	// Check the zip to see if it has a custom installer theme
+	const ZipEntry* twrp = mzFindZipEntry(&zip, "META-INF/teamwin/twrp.zip");
+	if (twrp != NULL)
+	{
+		unlink("/tmp/twrp.zip");
+		fd = creat("/tmp/twrp.zip", 0666);
+	}
+	if (fd >= 0 && twrp != NULL &&
+		mzExtractZipEntryToFile(&zip, twrp, fd) &&
+		!PageManager::LoadPackage("install", "/tmp/twrp.zip", "main"))
+	{
+		mzCloseZipArchive(&zip);
+		PageManager::SelectPackage("install");
+		gui_changePage("main");
+	}
+	else
+	{
+		// In this case, we just use the default page
+		mzCloseZipArchive(&zip);
+		gui_changePage(pageName);
+	}
+	if (fd >= 0)
+		close(fd);
+
+	if (simulate) {
+		simulate_progress_bar();
+	} else {
+		ret_val = TWinstall_zip(filename.c_str(), wipe_cache);
+
+		// Now, check if we need to ensure TWRP remains installed...
+		struct stat st;
+		if (stat("/sbin/installTwrp", &st) == 0)
+		{
+			DataManager::SetValue("tw_operation", "Configuring TWRP");
+			DataManager::SetValue("tw_partition", "");
+			gui_print("Configuring TWRP...\n");
+			if (TWFunc::Exec_Cmd("/sbin/installTwrp reinstall") < 0)
+			{
+				gui_print("Unable to configure TWRP with this kernel.\n");
+			}
+		}
+	}
+
+	// Done
+	DataManager::SetValue("ui_progress", 100);
+	DataManager::SetValue("ui_progress", 0);
+	return ret_val;
+}
+
+int GUIAction::doActions()
+{
+	if (mActions.size() < 1)	return -1;
+	if (mActions.size() == 1)
+		return doAction(mActions.at(0), 0);
+
+	// For multi-action, we always use a thread
+	pthread_t t;
+	pthread_attr_t tattr;
+
+	if (pthread_attr_init(&tattr)) {
+		LOGERR("Unable to pthread_attr_init\n");
+		return -1;
+	}
+	if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) {
+		LOGERR("Error setting pthread_attr_setdetachstate\n");
+		return -1;
+	}
+	if (pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM)) {
+		LOGERR("Error setting pthread_attr_setscope\n");
+		return -1;
+	}
+	/*if (pthread_attr_setstacksize(&tattr, 524288)) {
+		LOGERR("Error setting pthread_attr_setstacksize\n");
+		return -1;
+	}
+	*/
+	int ret = pthread_create(&t, &tattr, thread_start, this);
+	if (ret) {
+		LOGERR("Unable to create more threads for actions... continuing in same thread! %i\n", ret);
+		thread_start(this);
+	} else {
+		if (pthread_join(t, NULL)) {
+			LOGERR("Error joining threads\n");
+		}
+	}
+	if (pthread_attr_destroy(&tattr)) {
+		LOGERR("Failed to pthread_attr_destroy\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+void* GUIAction::thread_start(void *cookie)
+{
+	GUIAction* ourThis = (GUIAction*) cookie;
+
+	DataManager::SetValue(TW_ACTION_BUSY, 1);
+
+	if (ourThis->mActions.size() > 1)
+	{
+		std::vector<Action>::iterator iter;
+		for (iter = ourThis->mActions.begin(); iter != ourThis->mActions.end(); iter++)
+			ourThis->doAction(*iter, 1);
+	}
+	else
+	{
+		ourThis->doAction(ourThis->mActions.at(0), 1);
+	}
+	int check = 0;
+	DataManager::GetValue("tw_background_thread_running", check);
+	if (check == 0)
+		DataManager::SetValue(TW_ACTION_BUSY, 0);
+	return NULL;
+}
+
+void GUIAction::operation_start(const string operation_name)
+{
+	time(&Start);
+	DataManager::SetValue(TW_ACTION_BUSY, 1);
+	DataManager::SetValue("ui_progress", 0);
+	DataManager::SetValue("tw_operation", operation_name);
+	DataManager::SetValue("tw_operation_status", 0);
+	DataManager::SetValue("tw_operation_state", 0);
+}
+
+void GUIAction::operation_end(const int operation_status, const int simulate)
+{
+	time_t Stop;
+	int simulate_fail;
+	DataManager::SetValue("ui_progress", 100);
+	if (simulate) {
+		DataManager::GetValue(TW_SIMULATE_FAIL, simulate_fail);
+		if (simulate_fail != 0)
+			DataManager::SetValue("tw_operation_status", 1);
+		else
+			DataManager::SetValue("tw_operation_status", 0);
+	} else {
+		if (operation_status != 0) {
+			DataManager::SetValue("tw_operation_status", 1);
+		}
+		else {
+			DataManager::SetValue("tw_operation_status", 0);
+		}
+	}
+	DataManager::SetValue("tw_operation_state", 1);
+	DataManager::SetValue(TW_ACTION_BUSY, 0);
+#ifndef TW_NO_SCREEN_TIMEOUT
+	blankTimer.resetTimerAndUnblank();
+#endif
+	time(&Stop);
+	if ((int) difftime(Stop, Start) > 10)
+		DataManager::Vibrate("tw_action_vibrate");
+}
+
+int GUIAction::doAction(Action action, int isThreaded /* = 0 */)
+{
+	static string zip_queue[10];
+	static int zip_queue_index;
+	static pthread_t terminal_command;
+	int simulate;
+
+	std::string arg = gui_parse_text(action.mArg);
+
+	std::string function = gui_parse_text(action.mFunction);
+
+	DataManager::GetValue(TW_SIMULATE_ACTIONS, simulate);
+
+	if (function == "reboot")
+	{
+			//curtainClose(); this sometimes causes a crash
+
+		sync();
+		DataManager::SetValue("tw_gui_done", 1);
+		DataManager::SetValue("tw_reboot_arg", arg);
+
+		return 0;
+	}
+	if (function == "home")
+	{
+		PageManager::SelectPackage("TWRP");
+		gui_changePage("main");
+		return 0;
+	}
+
+	if (function == "key")
+	{
+		const int key = getKeyByName(arg);
+		PageManager::NotifyKey(key, true);
+		PageManager::NotifyKey(key, false);
+		return 0;
+	}
+
+	if (function == "page") {
+		std::string page_name = gui_parse_text(arg);
+		return gui_changePage(page_name);
+	}
+
+	if (function == "reload") {
+		int check = 0, ret_val = 0;
+		std::string theme_path;
+
+		operation_start("Reload Theme");
+		theme_path = DataManager::GetSettingsStoragePath();
+		if (PartitionManager.Mount_By_Path(theme_path.c_str(), 1) < 0) {
+			LOGERR("Unable to mount %s during reload function startup.\n", theme_path.c_str());
+			check = 1;
+		}
+
+		theme_path += "/TWRP/theme/ui.zip";
+		if (check != 0 || PageManager::ReloadPackage("TWRP", theme_path) != 0)
+		{
+			// Loading the custom theme failed - try loading the stock theme
+			LOGINFO("Attempting to reload stock theme...\n");
+			if (PageManager::ReloadPackage("TWRP", "/res/ui.xml"))
+			{
+				LOGERR("Failed to load base packages.\n");
+				ret_val = 1;
+			}
+		}
+		operation_end(ret_val, simulate);
+		return 0;
+	}
+
+	if (function == "readBackup")
+	{
+		string Restore_Name;
+		DataManager::GetValue("tw_restore", Restore_Name);
+		PartitionManager.Set_Restore_Files(Restore_Name);
+		return 0;
+	}
+
+	if (function == "set")
+	{
+		if (arg.find('=') != string::npos)
+		{
+			string varName = arg.substr(0, arg.find('='));
+			string value = arg.substr(arg.find('=') + 1, string::npos);
+
+			DataManager::GetValue(value, value);
+			DataManager::SetValue(varName, value);
+		}
+		else
+			DataManager::SetValue(arg, "1");
+		return 0;
+	}
+	if (function == "clear")
+	{
+		DataManager::SetValue(arg, "0");
+		return 0;
+	}
+
+	if (function == "mount") {
+		if (arg == "usb") {
+			DataManager::SetValue(TW_ACTION_BUSY, 1);
+			if (!simulate)
+				PartitionManager.usb_storage_enable();
+			else
+				gui_print("Simulating actions...\n");
+		} else if (!simulate) {
+			PartitionManager.Mount_By_Path(arg, true);
+		} else
+			gui_print("Simulating actions...\n");
+		return 0;
+	}
+
+	if (function == "umount" || function == "unmount") {
+		if (arg == "usb") {
+			if (!simulate)
+				PartitionManager.usb_storage_disable();
+			else
+				gui_print("Simulating actions...\n");
+			DataManager::SetValue(TW_ACTION_BUSY, 0);
+		} else if (!simulate) {
+			PartitionManager.UnMount_By_Path(arg, true);
+		} else
+			gui_print("Simulating actions...\n");
+		return 0;
+	}
+
+	if (function == "restoredefaultsettings")
+	{
+		operation_start("Restore Defaults");
+		if (simulate) // Simulated so that people don't accidently wipe out the "simulation is on" setting
+			gui_print("Simulating actions...\n");
+		else {
+			DataManager::ResetDefaults();
+			PartitionManager.Update_System_Details();
+			PartitionManager.Mount_Current_Storage(true);
+		}
+		operation_end(0, simulate);
+		return 0;
+	}
+
+	if (function == "copylog")
+	{
+		operation_start("Copy Log");
+		if (!simulate)
+		{
+			string dst;
+			PartitionManager.Mount_Current_Storage(true);
+			dst = DataManager::GetCurrentStoragePath() + "/recovery.log";
+			TWFunc::copy_file("/tmp/recovery.log", dst.c_str(), 0755);
+			sync();
+			gui_print("Copied recovery log to %s.\n", DataManager::GetCurrentStoragePath().c_str());
+		} else
+			simulate_progress_bar();
+		operation_end(0, simulate);
+		return 0;
+	}
+
+	if (function == "compute" || function == "addsubtract")
+	{
+		if (arg.find("+") != string::npos)
+		{
+			string varName = arg.substr(0, arg.find('+'));
+			string string_to_add = arg.substr(arg.find('+') + 1, string::npos);
+			int amount_to_add = atoi(string_to_add.c_str());
+			int value;
+
+			DataManager::GetValue(varName, value);
+			DataManager::SetValue(varName, value + amount_to_add);
+			return 0;
+		}
+		if (arg.find("-") != string::npos)
+		{
+			string varName = arg.substr(0, arg.find('-'));
+			string string_to_subtract = arg.substr(arg.find('-') + 1, string::npos);
+			int amount_to_subtract = atoi(string_to_subtract.c_str());
+			int value;
+
+			DataManager::GetValue(varName, value);
+			value -= amount_to_subtract;
+			if (value <= 0)
+				value = 0;
+			DataManager::SetValue(varName, value);
+			return 0;
+		}
+		if (arg.find("*") != string::npos)
+		{
+			string varName = arg.substr(0, arg.find('*'));
+			string multiply_by_str = gui_parse_text(arg.substr(arg.find('*') + 1, string::npos));
+			int multiply_by = atoi(multiply_by_str.c_str());
+			int value;
+
+			DataManager::GetValue(varName, value);
+			DataManager::SetValue(varName, value*multiply_by);
+			return 0;
+		}
+		if (arg.find("/") != string::npos)
+		{
+			string varName = arg.substr(0, arg.find('/'));
+			string divide_by_str = gui_parse_text(arg.substr(arg.find('/') + 1, string::npos));
+			int divide_by = atoi(divide_by_str.c_str());
+			int value;
+
+			if(divide_by != 0)
+			{
+				DataManager::GetValue(varName, value);
+				DataManager::SetValue(varName, value/divide_by);
+			}
+			return 0;
+		}
+		LOGERR("Unable to perform compute '%s'\n", arg.c_str());
+		return -1;
+	}
+
+	if (function == "setguitimezone")
+	{
+		string SelectedZone;
+		DataManager::GetValue(TW_TIME_ZONE_GUISEL, SelectedZone); // read the selected time zone into SelectedZone
+		string Zone = SelectedZone.substr(0, SelectedZone.find(';')); // parse to get time zone
+		string DSTZone = SelectedZone.substr(SelectedZone.find(';') + 1, string::npos); // parse to get DST component
+
+		int dst;
+		DataManager::GetValue(TW_TIME_ZONE_GUIDST, dst); // check wether user chose to use DST
+
+		string offset;
+		DataManager::GetValue(TW_TIME_ZONE_GUIOFFSET, offset); // pull in offset
+
+		string NewTimeZone = Zone;
+		if (offset != "0")
+			NewTimeZone += ":" + offset;
+
+		if (dst != 0)
+			NewTimeZone += DSTZone;
+
+		DataManager::SetValue(TW_TIME_ZONE_VAR, NewTimeZone);
+		DataManager::update_tz_environment_variables();
+		return 0;
+	}
+
+	if (function == "togglestorage") {
+		LOGERR("togglestorage action was deprecated from TWRP\n");
+		return 0;
+	}
+
+	if (function == "overlay")
+		return gui_changeOverlay(arg);
+
+	if (function == "queuezip")
+	{
+		if (zip_queue_index >= 10) {
+			gui_print("Maximum zip queue reached!\n");
+			return 0;
+		}
+		DataManager::GetValue("tw_filename", zip_queue[zip_queue_index]);
+		if (strlen(zip_queue[zip_queue_index].c_str()) > 0) {
+			zip_queue_index++;
+			DataManager::SetValue(TW_ZIP_QUEUE_COUNT, zip_queue_index);
+		}
+		return 0;
+	}
+
+	if (function == "cancelzip")
+	{
+		if (zip_queue_index <= 0) {
+			gui_print("Minimum zip queue reached!\n");
+			return 0;
+		} else {
+			zip_queue_index--;
+			DataManager::SetValue(TW_ZIP_QUEUE_COUNT, zip_queue_index);
+		}
+		return 0;
+	}
+
+	if (function == "queueclear")
+	{
+		zip_queue_index = 0;
+		DataManager::SetValue(TW_ZIP_QUEUE_COUNT, zip_queue_index);
+		return 0;
+	}
+
+	if (function == "sleep")
+	{
+		operation_start("Sleep");
+		usleep(atoi(arg.c_str()));
+		operation_end(0, simulate);
+		return 0;
+	}
+
+	if (function == "appenddatetobackupname")
+	{
+		operation_start("AppendDateToBackupName");
+		string Backup_Name;
+		DataManager::GetValue(TW_BACKUP_NAME, Backup_Name);
+		Backup_Name += TWFunc::Get_Current_Date();
+		if (Backup_Name.size() > MAX_BACKUP_NAME_LEN)
+			Backup_Name.resize(MAX_BACKUP_NAME_LEN);
+		DataManager::SetValue(TW_BACKUP_NAME, Backup_Name);
+		operation_end(0, simulate);
+		return 0;
+	}
+
+	if (function == "generatebackupname")
+	{
+		operation_start("GenerateBackupName");
+		TWFunc::Auto_Generate_Backup_Name();
+		operation_end(0, simulate);
+		return 0;
+	}
+	if (function == "checkpartitionlist") {
+		string Wipe_List, wipe_path;
+		int count = 0;
+
+		DataManager::GetValue("tw_wipe_list", Wipe_List);
+		LOGINFO("checkpartitionlist list '%s'\n", Wipe_List.c_str());
+		if (!Wipe_List.empty()) {
+			size_t start_pos = 0, end_pos = Wipe_List.find(";", start_pos);
+			while (end_pos != string::npos && start_pos < Wipe_List.size()) {
+				wipe_path = Wipe_List.substr(start_pos, end_pos - start_pos);
+				LOGINFO("checkpartitionlist wipe_path '%s'\n", wipe_path.c_str());
+				if (wipe_path == "/and-sec" || wipe_path == "DALVIK" || wipe_path == "INTERNAL") {
+					// Do nothing
+				} else {
+					count++;
+				}
+				start_pos = end_pos + 1;
+				end_pos = Wipe_List.find(";", start_pos);
+			}
+			DataManager::SetValue("tw_check_partition_list", count);
+		} else {
+			DataManager::SetValue("tw_check_partition_list", 0);
+		}
+		return 0;
+	}
+	if (function == "getpartitiondetails") {
+		string Wipe_List, wipe_path;
+		int count = 0;
+
+		DataManager::GetValue("tw_wipe_list", Wipe_List);
+		LOGINFO("getpartitiondetails list '%s'\n", Wipe_List.c_str());
+		if (!Wipe_List.empty()) {
+			size_t start_pos = 0, end_pos = Wipe_List.find(";", start_pos);
+			while (end_pos != string::npos && start_pos < Wipe_List.size()) {
+				wipe_path = Wipe_List.substr(start_pos, end_pos - start_pos);
+				LOGINFO("getpartitiondetails wipe_path '%s'\n", wipe_path.c_str());
+				if (wipe_path == "/and-sec" || wipe_path == "DALVIK" || wipe_path == "INTERNAL") {
+					// Do nothing
+				} else {
+					DataManager::SetValue("tw_partition_path", wipe_path);
+					break;
+				}
+				start_pos = end_pos + 1;
+				end_pos = Wipe_List.find(";", start_pos);
+			}
+			if (!wipe_path.empty()) {
+				TWPartition* Part = PartitionManager.Find_Partition_By_Path(wipe_path);
+				if (Part) {
+					unsigned long long mb = 1048576;
+
+					DataManager::SetValue("tw_partition_name", Part->Display_Name);
+					DataManager::SetValue("tw_partition_mount_point", Part->Mount_Point);
+					DataManager::SetValue("tw_partition_file_system", Part->Current_File_System);
+					DataManager::SetValue("tw_partition_size", Part->Size / mb);
+					DataManager::SetValue("tw_partition_used", Part->Used / mb);
+					DataManager::SetValue("tw_partition_free", Part->Free / mb);
+					DataManager::SetValue("tw_partition_backup_size", Part->Backup_Size / mb);
+					DataManager::SetValue("tw_partition_removable", Part->Removable);
+					DataManager::SetValue("tw_partition_is_present", Part->Is_Present);
+
+					if (Part->Can_Repair())
+						DataManager::SetValue("tw_partition_can_repair", 1);
+					else
+						DataManager::SetValue("tw_partition_can_repair", 0);
+					if (TWFunc::Path_Exists("/sbin/mkdosfs"))
+						DataManager::SetValue("tw_partition_vfat", 1);
+					else
+						DataManager::SetValue("tw_partition_vfat", 0);
+					if (TWFunc::Path_Exists("/sbin/mkfs.exfat"))
+						DataManager::SetValue("tw_partition_exfat", 1);
+					else
+						DataManager::SetValue("tw_partition_exfat", 0);
+					if (TWFunc::Path_Exists("/sbin/mkfs.f2fs"))
+						DataManager::SetValue("tw_partition_f2fs", 1);
+					else
+						DataManager::SetValue("tw_partition_f2fs", 0);
+					if (TWFunc::Path_Exists("/sbin/mke2fs"))
+						DataManager::SetValue("tw_partition_ext", 1);
+					else
+						DataManager::SetValue("tw_partition_ext", 0);
+					return 0;
+				} else {
+					LOGERR("Unable to locate partition: '%s'\n", wipe_path.c_str());
+				}
+			}
+		}
+		DataManager::SetValue("tw_partition_name", "");
+		DataManager::SetValue("tw_partition_file_system", "");
+		return 0;
+	}
+
+	if (function == "screenshot")
+	{
+		time_t tm;
+		char path[256];
+		int path_len;
+		uid_t uid = -1;
+		gid_t gid = -1;
+
+		struct passwd *pwd = getpwnam("media_rw");
+		if(pwd) {
+			uid = pwd->pw_uid;
+			gid = pwd->pw_gid;
+		}
+
+		const std::string storage = DataManager::GetCurrentStoragePath();
+		if(PartitionManager.Is_Mounted_By_Path(storage)) {
+			snprintf(path, sizeof(path), "%s/Pictures/Screenshots/", storage.c_str());
+		} else {
+			strcpy(path, "/tmp/");
+		}
+
+		if(!TWFunc::Create_Dir_Recursive(path, 0666, uid, gid))
+			return 0;
+
+		tm = time(NULL);
+		path_len = strlen(path);
+
+		// Screenshot_2014-01-01-18-21-38.png
+		strftime(path+path_len, sizeof(path)-path_len, "Screenshot_%Y-%m-%d-%H-%M-%S.png", localtime(&tm));
+
+		int res = gr_save_screenshot(path);
+		if(res == 0) {
+			chmod(path, 0666);
+			chown(path, uid, gid);
+
+			gui_print("Screenshot was saved to %s\n", path);
+
+			// blink to notify that the screenshow was taken
+			gr_color(255, 255, 255, 255);
+			gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+			gr_flip();
+			gui_forceRender();
+		} else {
+			LOGERR("Failed to take a screenshot!\n");
+		}
+		return 0;
+	}
+
+	if (function == "setbrightness")
+	{
+		return TWFunc::Set_Brightness(arg);
+	}
+
+	if (isThreaded)
+	{
+		if (function == "fileexists")
+		{
+			struct stat st;
+			string newpath = arg + "/.";
+
+			operation_start("FileExists");
+			if (stat(arg.c_str(), &st) == 0 || stat(newpath.c_str(), &st) == 0)
+				operation_end(0, simulate);
+			else
+				operation_end(1, simulate);
+			return 0;
+		}
+
+		if (function == "flash")
+		{
+			int i, ret_val = 0, wipe_cache = 0;
+
+			for (i=0; i<zip_queue_index; i++) {
+				operation_start("Flashing");
+				DataManager::SetValue("tw_filename", zip_queue[i]);
+				DataManager::SetValue(TW_ZIP_INDEX, (i + 1));
+
+				TWFunc::SetPerformanceMode(true);
+				ret_val = flash_zip(zip_queue[i], arg, simulate, &wipe_cache);
+				TWFunc::SetPerformanceMode(false);
+				if (ret_val != 0) {
+					gui_print("Error flashing zip '%s'\n", zip_queue[i].c_str());
+					i = 10; // Error flashing zip - exit queue
+					ret_val = 1;
+				}
+			}
+			zip_queue_index = 0;
+			DataManager::SetValue(TW_ZIP_QUEUE_COUNT, zip_queue_index);
+
+			if (wipe_cache)
+				PartitionManager.Wipe_By_Path("/cache");
+
+			if (DataManager::GetIntValue(TW_HAS_INJECTTWRP) == 1 && DataManager::GetIntValue(TW_INJECT_AFTER_ZIP) == 1) {
+				operation_start("ReinjectTWRP");
+				gui_print("Injecting TWRP into boot image...\n");
+				if (simulate) {
+					simulate_progress_bar();
+				} else {
+					TWPartition* Boot = PartitionManager.Find_Partition_By_Path("/boot");
+					if (Boot == NULL || Boot->Current_File_System != "emmc")
+						TWFunc::Exec_Cmd("injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash");
+					else {
+						string injectcmd = "injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash bd=" + Boot->Actual_Block_Device;
+						TWFunc::Exec_Cmd(injectcmd);
+					}
+					gui_print("TWRP injection complete.\n");
+				}
+			}
+			PartitionManager.Update_System_Details();
+			operation_end(ret_val, simulate);
+			return 0;
+		}
+		if (function == "wipe")
+		{
+			operation_start("Format");
+			DataManager::SetValue("tw_partition", arg);
+
+			int ret_val = false;
+
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				if (arg == "data")
+					ret_val = PartitionManager.Factory_Reset();
+				else if (arg == "battery")
+					ret_val = PartitionManager.Wipe_Battery_Stats();
+				else if (arg == "rotate")
+					ret_val = PartitionManager.Wipe_Rotate_Data();
+				else if (arg == "dalvik")
+					ret_val = PartitionManager.Wipe_Dalvik_Cache();
+				else if (arg == "DATAMEDIA") {
+					ret_val = PartitionManager.Format_Data();
+				} else if (arg == "INTERNAL") {
+					int has_datamedia, dual_storage;
+
+					DataManager::GetValue(TW_HAS_DATA_MEDIA, has_datamedia);
+					if (has_datamedia) {
+						ret_val = PartitionManager.Wipe_Media_From_Data();
+					} else {
+						ret_val = PartitionManager.Wipe_By_Path(DataManager::GetSettingsStoragePath());
+					}
+				} else if (arg == "EXTERNAL") {
+					string External_Path;
+
+					DataManager::GetValue(TW_EXTERNAL_PATH, External_Path);
+					ret_val = PartitionManager.Wipe_By_Path(External_Path);
+				} else if (arg == "ANDROIDSECURE") {
+					ret_val = PartitionManager.Wipe_Android_Secure();
+				} else if (arg == "LIST") {
+					string Wipe_List, wipe_path;
+					bool skip = false;
+					ret_val = true;
+					TWPartition* wipe_part = NULL;
+
+					DataManager::GetValue("tw_wipe_list", Wipe_List);
+					LOGINFO("wipe list '%s'\n", Wipe_List.c_str());
+					if (!Wipe_List.empty()) {
+						size_t start_pos = 0, end_pos = Wipe_List.find(";", start_pos);
+						while (end_pos != string::npos && start_pos < Wipe_List.size()) {
+							wipe_path = Wipe_List.substr(start_pos, end_pos - start_pos);
+							LOGINFO("wipe_path '%s'\n", wipe_path.c_str());
+							if (wipe_path == "/and-sec") {
+								if (!PartitionManager.Wipe_Android_Secure()) {
+									LOGERR("Unable to wipe android secure\n");
+									ret_val = false;
+									break;
+								} else {
+									skip = true;
+								}
+							} else if (wipe_path == "DALVIK") {
+								if (!PartitionManager.Wipe_Dalvik_Cache()) {
+									LOGERR("Failed to wipe dalvik\n");
+									ret_val = false;
+									break;
+								} else {
+									skip = true;
+								}
+							} else if (wipe_path == "INTERNAL") {
+								if (!PartitionManager.Wipe_Media_From_Data()) {
+									ret_val = false;
+									break;
+								} else {
+									skip = true;
+								}
+							}
+							if (!skip) {
+								if (!PartitionManager.Wipe_By_Path(wipe_path)) {
+									LOGERR("Unable to wipe '%s'\n", wipe_path.c_str());
+									ret_val = false;
+									break;
+								} else if (wipe_path == DataManager::GetSettingsStoragePath()) {
+									arg = wipe_path;
+								}
+							} else {
+								skip = false;
+							}
+							start_pos = end_pos + 1;
+							end_pos = Wipe_List.find(";", start_pos);
+						}
+					}
+				} else
+					ret_val = PartitionManager.Wipe_By_Path(arg);
+#ifdef TW_OEM_BUILD
+				if (arg == DataManager::GetSettingsStoragePath()) {
+					// If we wiped the settings storage path, recreate the TWRP folder and dump the settings
+					string Storage_Path = DataManager::GetSettingsStoragePath();
+
+					if (PartitionManager.Mount_By_Path(Storage_Path, true)) {
+						LOGINFO("Making TWRP folder and saving settings.\n");
+						Storage_Path += "/TWRP";
+						mkdir(Storage_Path.c_str(), 0777);
+						DataManager::Flush();
+					} else {
+						LOGERR("Unable to recreate TWRP folder and save settings.\n");
+					}
+				}
+#endif
+			}
+			PartitionManager.Update_System_Details();
+			if (ret_val)
+				ret_val = 0; // 0 is success
+			else
+				ret_val = 1; // 1 is failure
+			operation_end(ret_val, simulate);
+			return 0;
+		}
+		if (function == "refreshsizes")
+		{
+			operation_start("Refreshing Sizes");
+			if (simulate) {
+				simulate_progress_bar();
+			} else
+				PartitionManager.Update_System_Details();
+			operation_end(0, simulate);
+			return 0;
+		}
+		if (function == "nandroid")
+		{
+			operation_start("Nandroid");
+			int ret = 0;
+
+			if (simulate) {
+				DataManager::SetValue("tw_partition", "Simulation");
+				simulate_progress_bar();
+			} else {
+				if (arg == "backup") {
+					string Backup_Name;
+					DataManager::GetValue(TW_BACKUP_NAME, Backup_Name);
+					if (Backup_Name == "(Auto Generate)" || Backup_Name == "(Current Date)" || Backup_Name == "0" || Backup_Name == "(" || PartitionManager.Check_Backup_Name(true) == 0) {
+						ret = PartitionManager.Run_Backup();
+					}
+					else {
+						operation_end(1, simulate);
+						return -1;
+
+					}
+					DataManager::SetValue(TW_BACKUP_NAME, "(Auto Generate)");
+				} else if (arg == "restore") {
+					string Restore_Name;
+					DataManager::GetValue("tw_restore", Restore_Name);
+					ret = PartitionManager.Run_Restore(Restore_Name);
+				} else {
+					operation_end(1, simulate);
+					return -1;
+				}
+			}
+			DataManager::SetValue("tw_encrypt_backup", 0);
+			if (ret == false)
+				ret = 1; // 1 for failure
+			else
+				ret = 0; // 0 for success
+			operation_end(ret, simulate);
+			return 0;
+		}
+		if (function == "fixpermissions")
+		{
+			operation_start("Fix Permissions");
+			LOGINFO("fix permissions started!\n");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				int op_status = PartitionManager.Fix_Permissions();
+				if (op_status != 0)
+					op_status = 1; // failure
+				operation_end(op_status, simulate);
+			}
+			return 0;
+		}
+		if (function == "dd")
+		{
+			operation_start("imaging");
+
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				string cmd = "dd " + arg;
+				TWFunc::Exec_Cmd(cmd);
+			}
+			operation_end(0, simulate);
+			return 0;
+		}
+		if (function == "partitionsd")
+		{
+			operation_start("Partition SD Card");
+			int ret_val = 0;
+
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				int allow_partition;
+				DataManager::GetValue(TW_ALLOW_PARTITION_SDCARD, allow_partition);
+				if (allow_partition == 0) {
+					gui_print("This device does not have a real SD Card!\nAborting!\n");
+				} else {
+					if (!PartitionManager.Partition_SDCard())
+						ret_val = 1; // failed
+				}
+			}
+			operation_end(ret_val, simulate);
+			return 0;
+		}
+		if (function == "installhtcdumlock")
+		{
+			operation_start("Install HTC Dumlock");
+			if (simulate) {
+				simulate_progress_bar();
+			} else
+				TWFunc::install_htc_dumlock();
+
+			operation_end(0, simulate);
+			return 0;
+		}
+		if (function == "htcdumlockrestoreboot")
+		{
+			operation_start("HTC Dumlock Restore Boot");
+			if (simulate) {
+				simulate_progress_bar();
+			} else
+				TWFunc::htc_dumlock_restore_original_boot();
+
+			operation_end(0, simulate);
+			return 0;
+		}
+		if (function == "htcdumlockreflashrecovery")
+		{
+			operation_start("HTC Dumlock Reflash Recovery");
+			if (simulate) {
+				simulate_progress_bar();
+			} else
+				TWFunc::htc_dumlock_reflash_recovery_to_boot();
+
+			operation_end(0, simulate);
+			return 0;
+		}
+		if (function == "cmd")
+		{
+			int op_status = 0;
+
+			operation_start("Command");
+			LOGINFO("Running command: '%s'\n", arg.c_str());
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				op_status = TWFunc::Exec_Cmd(arg);
+				if (op_status != 0)
+					op_status = 1;
+			}
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "terminalcommand")
+		{
+			int op_status = 0;
+			string cmdpath, command;
+
+			DataManager::GetValue("tw_terminal_location", cmdpath);
+			operation_start("CommandOutput");
+			gui_print("%s # %s\n", cmdpath.c_str(), arg.c_str());
+			if (simulate) {
+				simulate_progress_bar();
+				operation_end(op_status, simulate);
+			} else {
+				command = "cd \"" + cmdpath + "\" && " + arg + " 2>&1";;
+				LOGINFO("Actual command is: '%s'\n", command.c_str());
+				DataManager::SetValue("tw_terminal_command_thread", command);
+				DataManager::SetValue("tw_terminal_state", 1);
+				DataManager::SetValue("tw_background_thread_running", 1);
+				op_status = pthread_create(&terminal_command, NULL, command_thread, NULL);
+				if (op_status != 0) {
+					LOGERR("Error starting terminal command thread, %i.\n", op_status);
+					DataManager::SetValue("tw_terminal_state", 0);
+					DataManager::SetValue("tw_background_thread_running", 0);
+					operation_end(1, simulate);
+				}
+			}
+			return 0;
+		}
+		if (function == "killterminal")
+		{
+			int op_status = 0;
+
+			LOGINFO("Sending kill command...\n");
+			operation_start("KillCommand");
+			DataManager::SetValue("tw_operation_status", 0);
+			DataManager::SetValue("tw_operation_state", 1);
+			DataManager::SetValue("tw_terminal_state", 0);
+			DataManager::SetValue("tw_background_thread_running", 0);
+			DataManager::SetValue(TW_ACTION_BUSY, 0);
+			return 0;
+		}
+		if (function == "reinjecttwrp")
+		{
+			int op_status = 0;
+			operation_start("ReinjectTWRP");
+			gui_print("Injecting TWRP into boot image...\n");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				TWFunc::Exec_Cmd("injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash");
+				gui_print("TWRP injection complete.\n");
+			}
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "checkbackupname")
+		{
+			int op_status = 0;
+
+			operation_start("CheckBackupName");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				op_status = PartitionManager.Check_Backup_Name(true);
+				if (op_status != 0)
+					op_status = 1;
+			}
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "decrypt")
+		{
+			int op_status = 0;
+
+			operation_start("Decrypt");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				string Password;
+				DataManager::GetValue("tw_crypto_password", Password);
+				op_status = PartitionManager.Decrypt_Device(Password);
+				if (op_status != 0)
+					op_status = 1;
+				else {
+					int load_theme = 1;
+
+					DataManager::SetValue(TW_IS_ENCRYPTED, 0);
+
+					if (load_theme) {
+						int has_datamedia;
+
+						// Check for a custom theme and load it if exists
+						DataManager::GetValue(TW_HAS_DATA_MEDIA, has_datamedia);
+						if (has_datamedia != 0) {
+							struct stat st;
+							int check = 0;
+							std::string theme_path;
+
+							theme_path = DataManager::GetSettingsStoragePath();
+							if (PartitionManager.Mount_By_Path(theme_path.c_str(), 1) < 0) {
+								LOGERR("Unable to mount %s during reload function startup.\n", theme_path.c_str());
+								check = 1;
+							}
+
+							theme_path += "/TWRP/theme/ui.zip";
+							if (check == 0 && stat(theme_path.c_str(), &st) == 0) {
+								if (PageManager::ReloadPackage("TWRP", theme_path) != 0)
+								{
+									// Loading the custom theme failed - try loading the stock theme
+									LOGINFO("Attempting to reload stock theme...\n");
+									if (PageManager::ReloadPackage("TWRP", "/res/ui.xml"))
+									{
+										LOGERR("Failed to load base packages.\n");
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "adbsideload")
+		{
+			int ret = 0;
+
+			operation_start("Sideload");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				int wipe_cache = 0;
+				int wipe_dalvik = 0;
+				string Sideload_File;
+
+				if (!PartitionManager.Mount_Current_Storage(false)) {
+					gui_print("Using RAM for sideload storage.\n");
+					Sideload_File = "/tmp/sideload.zip";
+				} else {
+					Sideload_File = DataManager::GetCurrentStoragePath() + "/sideload.zip";
+				}
+				if (TWFunc::Path_Exists(Sideload_File)) {
+					unlink(Sideload_File.c_str());
+				}
+				gui_print("Starting ADB sideload feature...\n");
+				DataManager::GetValue("tw_wipe_dalvik", wipe_dalvik);
+				ret = apply_from_adb(Sideload_File.c_str());
+				DataManager::SetValue("tw_has_cancel", 0); // Remove cancel button from gui now that the zip install is going to start
+				if (ret != 0) {
+					ret = 1; // failure
+				} else if (TWinstall_zip(Sideload_File.c_str(), &wipe_cache) == 0) {
+					if (wipe_cache || DataManager::GetIntValue("tw_wipe_cache"))
+						PartitionManager.Wipe_By_Path("/cache");
+					if (wipe_dalvik)
+						PartitionManager.Wipe_Dalvik_Cache();
+				} else {
+					ret = 1; // failure
+				}
+				if (DataManager::GetIntValue(TW_HAS_INJECTTWRP) == 1 && DataManager::GetIntValue(TW_INJECT_AFTER_ZIP) == 1) {
+					operation_start("ReinjectTWRP");
+					gui_print("Injecting TWRP into boot image...\n");
+					if (simulate) {
+						simulate_progress_bar();
+					} else {
+						TWPartition* Boot = PartitionManager.Find_Partition_By_Path("/boot");
+						if (Boot == NULL || Boot->Current_File_System != "emmc")
+							TWFunc::Exec_Cmd("injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash");
+						else {
+							string injectcmd = "injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash bd=" + Boot->Actual_Block_Device;
+							TWFunc::Exec_Cmd(injectcmd);
+						}
+						gui_print("TWRP injection complete.\n");
+					}
+				}
+			}
+			operation_end(ret, simulate);
+			return 0;
+		}
+		if (function == "adbsideloadcancel")
+		{
+			int child_pid;
+			char child_prop[PROPERTY_VALUE_MAX];
+			string Sideload_File;
+			Sideload_File = DataManager::GetCurrentStoragePath() + "/sideload.zip";
+			unlink(Sideload_File.c_str());
+			property_get("tw_child_pid", child_prop, "error");
+			if (strcmp(child_prop, "error") == 0) {
+				LOGERR("Unable to get child ID from prop\n");
+				return 0;
+			}
+			child_pid = atoi(child_prop);
+			gui_print("Cancelling ADB sideload...\n");
+			kill(child_pid, SIGTERM);
+			DataManager::SetValue("tw_page_done", "1"); // For OpenRecoveryScript support
+			return 0;
+		}
+		if (function == "openrecoveryscript") {
+			operation_start("OpenRecoveryScript");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				// Check for the SCRIPT_FILE_TMP first as these are AOSP recovery commands
+				// that we converted to ORS commands during boot in recovery.cpp.
+				// Run those first.
+				int reboot = 0;
+				if (TWFunc::Path_Exists(SCRIPT_FILE_TMP)) {
+					gui_print("Processing AOSP recovery commands...\n");
+					if (OpenRecoveryScript::run_script_file() == 0) {
+						reboot = 1;
+					}
+				}
+				// Check for the ORS file in /cache and attempt to run those commands.
+				if (OpenRecoveryScript::check_for_script_file()) {
+					gui_print("Processing OpenRecoveryScript file...\n");
+					if (OpenRecoveryScript::run_script_file() == 0) {
+						reboot = 1;
+					}
+				}
+				if (reboot) {
+					usleep(2000000); // Sleep for 2 seconds before rebooting
+					TWFunc::tw_reboot(rb_system);
+				} else {
+					DataManager::SetValue("tw_page_done", 1);
+				}
+			}
+			return 0;
+		}
+		if (function == "installsu")
+		{
+			int op_status = 0;
+
+			operation_start("Install SuperSU");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				if (!TWFunc::Install_SuperSU())
+					op_status = 1;
+			}
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "fixsu")
+		{
+			int op_status = 0;
+
+			operation_start("Fixing Superuser Permissions");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				LOGERR("Fixing su permissions was deprecated from TWRP.\n");
+				LOGERR("4.3+ ROMs with SELinux will always lose su perms.\n");
+			}
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "decrypt_backup")
+		{
+			int op_status = 0;
+
+			operation_start("Try Restore Decrypt");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				string Restore_Path, Filename, Password;
+				DataManager::GetValue("tw_restore", Restore_Path);
+				Restore_Path += "/";
+				DataManager::GetValue("tw_restore_password", Password);
+				TWFunc::SetPerformanceMode(true);
+				if (TWFunc::Try_Decrypting_Backup(Restore_Path, Password))
+					op_status = 0; // success
+				else
+					op_status = 1; // fail
+				TWFunc::SetPerformanceMode(false);
+			}
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "repair")
+		{
+			int op_status = 0;
+
+			operation_start("Repair Partition");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				string part_path;
+				DataManager::GetValue("tw_partition_mount_point", part_path);
+				if (PartitionManager.Repair_By_Path(part_path, true)) {
+					op_status = 0; // success
+				} else {
+					LOGERR("Error repairing file system.\n");
+					op_status = 1; // fail
+				}
+			}
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "changefilesystem")
+		{
+			int op_status = 0;
+
+			operation_start("Change File System");
+			if (simulate) {
+				simulate_progress_bar();
+			} else {
+				string part_path, file_system;
+				DataManager::GetValue("tw_partition_mount_point", part_path);
+				DataManager::GetValue("tw_action_new_file_system", file_system);
+				if (PartitionManager.Wipe_By_Path(part_path, file_system)) {
+					op_status = 0; // success
+				} else {
+					LOGERR("Error changing file system.\n");
+					op_status = 1; // fail
+				}
+			}
+			PartitionManager.Update_System_Details();
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "startmtp")
+		{
+			int op_status = 0;
+
+			operation_start("Start MTP");
+			if (PartitionManager.Enable_MTP())
+				op_status = 0; // success
+			else
+				op_status = 1; // fail
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+		if (function == "stopmtp")
+		{
+			int op_status = 0;
+
+			operation_start("Stop MTP");
+			if (PartitionManager.Disable_MTP())
+				op_status = 0; // success
+			else
+				op_status = 1; // fail
+
+			operation_end(op_status, simulate);
+			return 0;
+		}
+	}
+	else
+	{
+		pthread_t t;
+		pthread_create(&t, NULL, thread_start, this);
+		return 0;
+	}
+	LOGERR("Unknown action '%s'\n", function.c_str());
+	return -1;
+}
+
+int GUIAction::getKeyByName(std::string key)
+{
+	if (key == "home")			return KEY_HOME;
+	else if (key == "menu")		return KEY_MENU;
+	else if (key == "back")	 	return KEY_BACK;
+	else if (key == "search")	return KEY_SEARCH;
+	else if (key == "voldown")	return KEY_VOLUMEDOWN;
+	else if (key == "volup")	return KEY_VOLUMEUP;
+	else if (key == "power") {
+		int ret_val;
+		DataManager::GetValue(TW_POWER_BUTTON, ret_val);
+		if (!ret_val)
+			return KEY_POWER;
+		else
+			return ret_val;
+	}
+
+	return atol(key.c_str());
+}
+
+void* GUIAction::command_thread(void *cookie)
+{
+	string command;
+	FILE* fp;
+	char line[512];
+
+	DataManager::GetValue("tw_terminal_command_thread", command);
+	fp = popen(command.c_str(), "r");
+	if (fp == NULL) {
+		LOGERR("Error opening command to run.\n");
+	} else {
+		int fd = fileno(fp), has_data = 0, check = 0, keep_going = -1, bytes_read = 0;
+		struct timeval timeout;
+		fd_set fdset;
+
+		while(keep_going)
+		{
+			FD_ZERO(&fdset);
+			FD_SET(fd, &fdset);
+			timeout.tv_sec = 0;
+			timeout.tv_usec = 400000;
+			has_data = select(fd+1, &fdset, NULL, NULL, &timeout);
+			if (has_data == 0) {
+				// Timeout reached
+				DataManager::GetValue("tw_terminal_state", check);
+				if (check == 0) {
+					keep_going = 0;
+				}
+			} else if (has_data < 0) {
+				// End of execution
+				keep_going = 0;
+			} else {
+				// Try to read output
+				memset(line, 0, sizeof(line));
+				bytes_read = read(fd, line, sizeof(line));
+				if (bytes_read > 0)
+					gui_print("%s", line); // Display output
+				else
+					keep_going = 0; // Done executing
+			}
+		}
+		fclose(fp);
+	}
+	DataManager::SetValue("tw_operation_status", 0);
+	DataManager::SetValue("tw_operation_state", 1);
+	DataManager::SetValue("tw_terminal_state", 0);
+	DataManager::SetValue("tw_background_thread_running", 0);
+	DataManager::SetValue(TW_ACTION_BUSY, 0);
+	return NULL;
+}
diff --git a/gui/animation.cpp b/gui/animation.cpp
new file mode 100644
index 0000000..771e1c1
--- /dev/null
+++ b/gui/animation.cpp
@@ -0,0 +1,142 @@
+// animation.cpp - GUIAnimation object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+
+GUIAnimation::GUIAnimation(xml_node<>* node) : GUIObject(node)
+{
+	xml_node<>* child;
+	xml_attribute<>* attr;
+
+	mAnimation = NULL;
+	mFrame = 1;
+	mFPS = 1;
+	mLoop = -1;
+	mRender = 1;
+	mUpdateCount = 0;
+
+	if (!node)  return;
+
+	child = node->first_node("resource");
+	if (child)
+	{
+		attr = child->first_attribute("name");
+		if (attr)
+			mAnimation = (AnimationResource*) PageManager::FindResource(attr->value());
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, NULL, NULL, &mPlacement);
+
+	child = node->first_node("speed");
+	if (child)
+	{
+		attr = child->first_attribute("fps");
+		if (attr)
+			mFPS = atoi(attr->value());
+		attr = child->first_attribute("render");
+		if (attr)
+			mRender = atoi(attr->value());
+	}
+	if (mFPS > 30)  mFPS = 30;
+
+	child = node->first_node("loop");
+	if (child)
+	{
+		attr = child->first_attribute("frame");
+		if (attr)
+			mLoop = atoi(attr->value()) - 1;
+		attr = child->first_attribute("start");
+		if (attr)
+			mFrame = atoi(attr->value());
+	}
+
+	// Fetch the render sizes
+	if (mAnimation && mAnimation->GetResource())
+	{
+		mRenderW = gr_get_width(mAnimation->GetResource());
+		mRenderH = gr_get_height(mAnimation->GetResource());
+
+		// Adjust for placement
+		if (mPlacement != TOP_LEFT && mPlacement != BOTTOM_LEFT)
+		{
+			if (mPlacement == CENTER)
+				mRenderX -= (mRenderW / 2);
+			else
+				mRenderX -= mRenderW;
+		}
+		if (mPlacement != TOP_LEFT && mPlacement != TOP_RIGHT)
+		{
+			if (mPlacement == CENTER)
+				mRenderY -= (mRenderH / 2);
+			else
+				mRenderY -= mRenderH;
+		}
+		SetPlacement(TOP_LEFT);
+	}
+}
+
+int GUIAnimation::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mAnimation || !mAnimation->GetResource(mFrame))	return -1;
+
+	gr_blit(mAnimation->GetResource(mFrame), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
+	return 0;
+}
+
+int GUIAnimation::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mAnimation)		return -1;
+
+	// Handle the "end-of-animation" state
+	if (mLoop == -2)		return 0;
+
+	// Determine if we need the next frame yet...
+	if (++mUpdateCount > 30 / mFPS)
+	{
+		mUpdateCount = 0;
+		if (++mFrame >= mAnimation->GetResourceCount())
+		{
+			if (mLoop < 0)
+			{
+				mFrame = mAnimation->GetResourceCount() - 1;
+				mLoop = -2;
+			}
+			else
+				mFrame = mLoop;
+		}
+		if (mRender == 2)	return 2;
+		return (Render() == 0 ? 1 : -1);
+	}
+	return 0;
+}
+
diff --git a/gui/blanktimer.cpp b/gui/blanktimer.cpp
new file mode 100644
index 0000000..1b18429
--- /dev/null
+++ b/gui/blanktimer.cpp
@@ -0,0 +1,154 @@
+/*
+        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using namespace std;
+#include "rapidxml.hpp"
+using namespace rapidxml;
+extern "C" {
+#include "../minuitwrp/minui.h"
+}
+#include <string>
+#include <vector>
+#include <map>
+#include "resources.hpp"
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <pixelflinger/pixelflinger.h>
+#include <linux/kd.h>
+#include <linux/fb.h>
+#include <sstream>
+#include "pages.hpp"
+#include "blanktimer.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+extern "C" {
+#include "../twcommon.h"
+#ifdef HAVE_SELINUX
+#include "../minzip/Zip.h"
+#else
+#include "../minzipold/Zip.h"
+#endif
+}
+#include "../twrp-functions.hpp"
+#include "../variables.h"
+
+blanktimer::blanktimer(void) {
+	setTime(0);
+	setConBlank(0);
+	orig_brightness = getBrightness();
+	screenoff = false;
+}
+
+bool blanktimer::IsScreenOff() {
+	return screenoff;
+}
+
+void blanktimer::setTime(int newtime) {
+	sleepTimer = newtime;
+}
+
+int blanktimer::setTimerThread(void) {
+	pthread_t thread;
+	ThreadPtr blankptr = &blanktimer::setClockTimer;
+	PThreadPtr p = *(PThreadPtr*)&blankptr;
+	pthread_create(&thread, NULL, p, this);
+	return 0;
+}
+
+void blanktimer::setConBlank(int blank) {
+	pthread_mutex_lock(&conblankmutex);
+	conblank = blank;
+	pthread_mutex_unlock(&conblankmutex);
+}
+
+void blanktimer::setTimer(void) {
+	pthread_mutex_lock(&timermutex);
+	clock_gettime(CLOCK_MONOTONIC, &btimer);
+	pthread_mutex_unlock(&timermutex);
+}
+
+timespec blanktimer::getTimer(void) {
+	return btimer;
+}
+
+int  blanktimer::setClockTimer(void) {
+	timespec curTime, diff;
+	for(;;) {
+		usleep(1000000);
+		clock_gettime(CLOCK_MONOTONIC, &curTime);
+		diff = TWFunc::timespec_diff(btimer, curTime);
+		if (sleepTimer > 2 && diff.tv_sec > (sleepTimer - 2) && conblank == 0) {
+			orig_brightness = getBrightness();
+			setConBlank(1);
+			TWFunc::Set_Brightness("5");
+		}
+		if (sleepTimer && diff.tv_sec > sleepTimer && conblank < 2) {
+			setConBlank(2);
+			TWFunc::Set_Brightness("0");
+			screenoff = true;
+			TWFunc::check_and_run_script("/sbin/postscreenblank.sh", "blank");
+			PageManager::ChangeOverlay("lock");
+		}
+#ifndef TW_NO_SCREEN_BLANK
+		if (conblank == 2 && gr_fb_blank(1) >= 0) {
+			setConBlank(3);
+		}
+#endif
+	}
+	return -1; //shouldn't get here
+}
+
+string blanktimer::getBrightness(void) {
+	string result;
+	string brightness_path;
+	DataManager::GetValue("tw_brightness_file", brightness_path);
+	if (brightness_path == "/nobrightness")
+		return brightness_path;
+	DataManager::GetValue("tw_brightness", result);
+	if (result == "") {
+		result = "255";
+	}
+	return result;
+
+}
+
+void blanktimer::resetTimerAndUnblank(void) {
+	setTimer();
+	switch (conblank) {
+		case 3:
+#ifndef TW_NO_SCREEN_BLANK
+			if (gr_fb_blank(0) < 0) {
+				LOGINFO("blanktimer::resetTimerAndUnblank failed to gr_fb_blank(0)\n");
+				break;
+			}
+#endif
+			TWFunc::check_and_run_script("/sbin/postscreenunblank.sh", "unblank");
+			// No break here, we want to keep going
+		case 2:
+			gui_forceRender();
+			screenoff = false;
+			// No break here, we want to keep going
+		case 1:
+			if (orig_brightness != "/nobrightness")
+				TWFunc::Set_Brightness(orig_brightness);
+			setConBlank(0);
+			break;
+	}
+}
diff --git a/gui/blanktimer.hpp b/gui/blanktimer.hpp
new file mode 100644
index 0000000..c8159f6
--- /dev/null
+++ b/gui/blanktimer.hpp
@@ -0,0 +1,60 @@
+/*
+        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __BLANKTIMER_HEADER_HPP
+#define __BLANKTIMER_HEADER_HPP
+
+#include <pthread.h>
+#include <sys/time.h>
+
+using namespace std;
+
+class blanktimer
+{
+public:
+	blanktimer(void);
+
+	int setTimerThread(void);
+	void resetTimerAndUnblank(void);
+	void setTime(int newtime);
+	bool IsScreenOff();
+
+private:
+	typedef int (blanktimer::*ThreadPtr)(void);
+	typedef void* (*PThreadPtr)(void*);
+
+	void setConBlank(int blank);
+	void setTimer(void);
+	timespec getTimer(void);
+	string getBrightness(void);
+	int setBrightness(int brightness);
+	int setBlankTimer(void);
+	int setClockTimer(void);
+
+	pthread_mutex_t conblankmutex;
+	pthread_mutex_t timermutex;
+	int conblank;
+	timespec btimer;
+	unsigned long long sleepTimer;
+	string orig_brightness;
+	bool screenoff;
+};
+
+extern blanktimer blankTimer;
+
+#endif // __BLANKTIMER_HEADER_HPP
diff --git a/gui/button.cpp b/gui/button.cpp
new file mode 100644
index 0000000..097bf71
--- /dev/null
+++ b/gui/button.cpp
@@ -0,0 +1,296 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "../data.hpp"
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUIButton::GUIButton(xml_node<>* node)
+	: GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mButtonImg = NULL;
+	mButtonIcon = NULL;
+	mButtonLabel = NULL;
+	mAction = NULL;
+	mRendered = false;
+	hasHighlightColor = false;
+	renderHighlight = false;
+	hasFill = false;
+
+	if (!node)  return;
+
+	// These can be loaded directly from the node
+	mButtonLabel = new GUIText(node);
+	mAction = new GUIAction(node);
+
+	child = node->first_node("image");
+	if (child)
+	{
+		mButtonImg = new GUIImage(node);
+		if (mButtonImg->Render() < 0)
+		{
+			delete mButtonImg;
+			mButtonImg = NULL;
+		}
+	}
+	if (mButtonLabel->Render() < 0)
+	{
+		delete mButtonLabel;
+		mButtonLabel = NULL;
+	}
+	// Load fill if it exists
+	memset(&mFillColor, 0, sizeof(COLOR));
+	child = node->first_node("fill");
+	if (child)
+	{
+		attr = child->first_attribute("color");
+		if (attr) {
+			hasFill = true;
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mFillColor);
+		}
+	}
+	if (!hasFill && mButtonImg == NULL) {
+		LOGERR("No image resource or fill specified for button.\n");
+	}
+
+	// The icon is a special case
+	child = node->first_node("icon");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mButtonIcon = PageManager::FindResource(attr->value());
+	}
+
+	memset(&mHighlightColor, 0, sizeof(COLOR));
+	child = node->first_node("highlight");
+	if (child) {
+		attr = child->first_attribute("color");
+		if (attr) {
+			hasHighlightColor = true;
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHighlightColor);
+		}
+	}
+
+	int x, y, w, h;
+	TextPlacement = TOP_LEFT;
+	if (mButtonImg) {
+		mButtonImg->GetRenderPos(x, y, w, h);
+	} else if (hasFill) {
+		LoadPlacement(node->first_node("placement"), &x, &y, &w, &h, &TextPlacement);
+	}
+	SetRenderPos(x, y, w, h);
+	return;
+}
+
+GUIButton::~GUIButton()
+{
+	if (mButtonImg)	 	delete mButtonImg;
+	if (mButtonLabel)   delete mButtonLabel;
+	if (mAction)		delete mAction;
+	if (mButtonIcon)	delete mButtonIcon;
+}
+
+int GUIButton::Render(void)
+{
+	if (!isConditionTrue())
+	{
+		mRendered = false;
+		return 0;
+	}
+
+	int ret = 0;
+
+	if (mButtonImg)	 ret = mButtonImg->Render();
+	if (ret < 0)		return ret;
+	if (hasFill) {
+		gr_color(mFillColor.red, mFillColor.green, mFillColor.blue, mFillColor.alpha);
+		gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
+	}
+	if (mButtonIcon && mButtonIcon->GetResource())
+		gr_blit(mButtonIcon->GetResource(), 0, 0, mIconW, mIconH, mIconX, mIconY);
+	if (mButtonLabel) {
+		int w, h;
+		mButtonLabel->GetCurrentBounds(w, h);
+		if (w != mTextW) {
+			mTextW = w;
+			if (TextPlacement == CENTER_X_ONLY) {
+				mTextX = ((mRenderW - mRenderX) / 2);
+			} else if (mTextW > mRenderW) { // As a special case, we'll allow large text which automatically moves it to the right.
+				mTextX = mRenderW + mRenderX + 5;
+				mRenderW += mTextW + 5;
+			} else {
+				mTextX = mRenderX + ((mRenderW - mTextW) / 2);
+			}
+			mButtonLabel->SetRenderPos(mTextX, mTextY);
+		}
+		ret = mButtonLabel->Render();
+		if (ret < 0)		return ret;
+	}
+	if (renderHighlight && hasHighlightColor) {
+		gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
+		gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
+	}
+	mRendered = true;
+	return ret;
+}
+
+int GUIButton::Update(void)
+{
+	if (!isConditionTrue())	return (mRendered ? 2 : 0);
+	if (!mRendered)			return 2;
+
+	int ret = 0, ret2 = 0;
+
+	if (mButtonImg)			ret = mButtonImg->Update();
+	if (ret < 0)			return ret;
+
+	if (ret == 0)
+	{
+		if (mButtonLabel) {
+			ret2 = mButtonLabel->Update();
+			if (ret2 < 0)	return ret2;
+			if (ret2 > ret)	ret = ret2;
+		}
+	}
+	else if (ret == 1)
+	{
+		// The button re-rendered, so everyone else is a render
+		if (mButtonIcon && mButtonIcon->GetResource())
+			gr_blit(mButtonIcon->GetResource(), 0, 0, mIconW, mIconH, mIconX, mIconY);
+		if (mButtonLabel)   ret = mButtonLabel->Render();
+		if (ret < 0)		return ret;
+		ret = 1;
+	}
+	else
+	{
+		// Aparently, the button needs a background update
+		ret = 2;
+	}
+	return ret;
+}
+
+int GUIButton::SetRenderPos(int x, int y, int w, int h)
+{
+	mRenderX = x;
+	mRenderY = y;
+	if (w || h)
+	{
+		mRenderW = w;
+		mRenderH = h;
+	}
+
+	mIconW = 0;	 mIconH = 0;
+	if (mButtonIcon && mButtonIcon->GetResource())
+	{
+		mIconW = gr_get_width(mButtonIcon->GetResource());
+		mIconH = gr_get_height(mButtonIcon->GetResource());
+	}
+
+	mTextH = 0;
+	mTextW = 0;
+	mIconX = mRenderX + ((mRenderW - mIconW) / 2);
+	if (mButtonLabel)   mButtonLabel->GetCurrentBounds(mTextW, mTextH);
+	if (mTextW)
+	{
+		if (TextPlacement == CENTER_X_ONLY) {
+			mTextX = ((mRenderW - mRenderX) / 2);
+		} else if (mTextW > mRenderW) { // As a special case, we'll allow large text which automatically moves it to the right.
+			mTextX = mRenderW + mRenderX + 5;
+			mRenderW += mTextW + 5;
+		} else {
+			mTextX = mRenderX + ((mRenderW - mTextW) / 2);
+		}
+	}
+
+	if (mIconH == 0 || mTextH == 0 || mIconH + mTextH > mRenderH)
+	{
+		mIconY = mRenderY + (mRenderH / 2) - (mIconH / 2);
+		mTextY = mRenderY + (mRenderH / 2) - (mTextH / 2);
+	}
+	else
+	{
+		int divisor = mRenderH - (mIconH + mTextH);
+		mIconY = mRenderY + (divisor / 3);
+		mTextY = mRenderY + (divisor * 2 / 3) + mIconH;
+	}
+
+	if (mButtonLabel)   mButtonLabel->SetRenderPos(mTextX, mTextY);
+	if (mAction)		mAction->SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	return 0;
+}
+
+int GUIButton::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	static int last_state = 0;
+
+	if (!isConditionTrue())	 return -1;
+	if (x < mRenderX || x - mRenderX > mRenderW || y < mRenderY || y - mRenderY > mRenderH || state == TOUCH_RELEASE) {
+		if (last_state == 1) {
+			last_state = 0;
+			if (mButtonLabel != NULL)
+				mButtonLabel->isHighlighted = false;
+			if (mButtonImg != NULL)
+				mButtonImg->isHighlighted = false;
+			renderHighlight = false;
+			mRendered = false;
+		}
+	} else {
+		if (last_state == 0) {
+			last_state = 1;
+			DataManager::Vibrate("tw_button_vibrate");
+			if (mButtonLabel != NULL)
+				mButtonLabel->isHighlighted = true;
+			if (mButtonImg != NULL)
+				mButtonImg->isHighlighted = true;
+			renderHighlight = true;
+			mRendered = false;
+		}
+	}
+	if (x < mRenderX || x - mRenderX > mRenderW || y < mRenderY || y - mRenderY > mRenderH)
+		return 0;
+	return (mAction ? mAction->NotifyTouch(state, x, y) : 1);
+}
diff --git a/gui/checkbox.cpp b/gui/checkbox.cpp
new file mode 100644
index 0000000..fe5f557
--- /dev/null
+++ b/gui/checkbox.cpp
@@ -0,0 +1,174 @@
+// checkbox.cpp - GUICheckbox object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUICheckbox::GUICheckbox(xml_node<>* node)
+	: GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mChecked = NULL;
+	mUnchecked = NULL;
+	mLabel = NULL;
+	mRendered = false;
+
+	mLastState = 0;
+
+	if (!node)
+		return;
+
+	// The label can be loaded directly
+	mLabel = new GUIText(node);
+
+	// Read the check states
+	child = node->first_node("image");
+	if (child)
+	{
+		attr = child->first_attribute("checked");
+		if (attr)
+			mChecked = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("unchecked");
+		if (attr)
+			mUnchecked = PageManager::FindResource(attr->value());
+	}
+
+	// Get the variable data
+	child = node->first_node("data");
+	if (child)
+	{
+		attr = child->first_attribute("variable");
+		if (attr)
+			mVarName = attr->value();
+		attr = child->first_attribute("default");
+		if (attr)
+			DataManager::SetValue(mVarName, attr->value());
+	}
+
+	mCheckW = 0;	mCheckH = 0;
+	if (mChecked && mChecked->GetResource())
+	{
+		mCheckW = gr_get_width(mChecked->GetResource());
+		mCheckH = gr_get_height(mChecked->GetResource());
+	}
+	else if (mUnchecked && mUnchecked->GetResource())
+	{
+		mCheckW = gr_get_width(mUnchecked->GetResource());
+		mCheckH = gr_get_height(mUnchecked->GetResource());
+	}
+
+	int x, y, w, h;
+	mLabel->GetRenderPos(x, y, w, h);
+	SetRenderPos(x, y, 0, 0);
+	return;
+}
+
+GUICheckbox::~GUICheckbox()
+{
+}
+
+int GUICheckbox::Render(void)
+{
+	if (!isConditionTrue())
+	{
+		mRendered = false;
+		return 0;
+	}
+
+	int ret = 0;
+	int lastState = 0;
+	DataManager::GetValue(mVarName, lastState);
+
+	if (lastState)
+	{
+		if (mChecked && mChecked->GetResource())
+			gr_blit(mChecked->GetResource(), 0, 0, mCheckW, mCheckH, mRenderX, mRenderY);
+	}
+	else
+	{
+		if (mUnchecked && mUnchecked->GetResource())
+			gr_blit(mUnchecked->GetResource(), 0, 0, mCheckW, mCheckH, mRenderX, mRenderY);
+	}
+	if (mLabel)
+		ret = mLabel->Render();
+	mLastState = lastState;
+	mRendered = true;
+	return ret;
+}
+
+int GUICheckbox::Update(void)
+{
+	if (!isConditionTrue())	return (mRendered ? 2 : 0);
+	if (!mRendered)			return 2;
+
+	int lastState = 0;
+	DataManager::GetValue(mVarName, lastState);
+
+	if (lastState != mLastState)
+		return 2;
+	return 0;
+}
+
+int GUICheckbox::SetRenderPos(int x, int y, int w, int h)
+{
+	mRenderX = x;
+	mRenderY = y;
+
+	if (w || h)
+		return -1;
+
+	int textW, textH;
+	mLabel->GetCurrentBounds(textW, textH);
+
+	w = textW + mCheckW + 5;
+	mRenderW = w;
+	mRenderH = mCheckH;
+
+	mTextX = mRenderX + mCheckW + 5;
+	mTextY = mRenderY + ((mCheckH / 2) - (textH / 2));
+
+	mLabel->SetRenderPos(mTextX, mTextY, 0, 0);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	return 0;
+}
+
+int GUICheckbox::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if (!isConditionTrue())
+		return -1;
+
+	if (state == TOUCH_RELEASE)
+	{
+		int lastState;
+		DataManager::GetValue(mVarName, lastState);
+		lastState = (lastState == 0) ? 1 : 0;
+		DataManager::SetValue(mVarName, lastState);
+
+		DataManager::Vibrate("tw_button_vibrate");
+	}
+	return 0;
+}
+
diff --git a/gui/console.cpp b/gui/console.cpp
new file mode 100644
index 0000000..aad392c
--- /dev/null
+++ b/gui/console.cpp
@@ -0,0 +1,426 @@
+// console.cpp - GUIConsole object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+
+static std::vector<std::string> gConsole;
+static std::vector<std::string> gConsoleColor;
+static FILE* ors_file;
+
+extern "C" void __gui_print(const char *color, char *buf)
+{
+	char *start, *next;
+
+	if (buf[0] == '\n' && strlen(buf) < 2) {
+		// This prevents the double lines bug seen in the console during zip installs
+		return;
+	}
+
+	for (start = next = buf; *next != '\0';)
+	{
+		if (*next == '\n')
+		{
+			*next = '\0';
+			gConsole.push_back(start);
+			gConsoleColor.push_back(color);
+
+			start = ++next;
+		}
+		else
+			++next;
+	}
+
+	// The text after last \n (or whole string if there is no \n)
+	if(*start) {
+		gConsole.push_back(start);
+		gConsoleColor.push_back(color);
+	}
+	if (ors_file) {
+		fprintf(ors_file, "%s\n", buf);
+		fflush(ors_file);
+	}
+}
+
+extern "C" void gui_print(const char *fmt, ...)
+{
+	char buf[512];		// We're going to limit a single request to 512 bytes
+
+	va_list ap;
+	va_start(ap, fmt);
+	vsnprintf(buf, 512, fmt, ap);
+	va_end(ap);
+
+	fputs(buf, stdout);
+
+	__gui_print("normal", buf);
+	return;
+}
+
+extern "C" void gui_print_color(const char *color, const char *fmt, ...)
+{
+	char buf[512];		// We're going to limit a single request to 512 bytes
+
+	va_list ap;
+	va_start(ap, fmt);
+	vsnprintf(buf, 512, fmt, ap);
+	va_end(ap);
+
+	fputs(buf, stdout);
+
+	__gui_print(color, buf);
+	return;
+}
+
+extern "C" void gui_set_FILE(FILE* f)
+{
+	ors_file = f;
+}
+
+GUIConsole::GUIConsole(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mFont = NULL;
+	mCurrentLine = -1;
+	memset(&mForegroundColor, 255, sizeof(COLOR));
+	memset(&mBackgroundColor, 0, sizeof(COLOR));
+	mBackgroundColor.alpha = 255;
+	memset(&mScrollColor, 0x08, sizeof(COLOR));
+	mScrollColor.alpha = 255;
+	mLastCount = 0;
+	mSlideout = 0;
+	RenderCount = 0;
+	mSlideoutState = hidden;
+	mRender = true;
+
+	mRenderX = 0; mRenderY = 0; mRenderW = gr_fb_width(); mRenderH = gr_fb_height();
+
+	if (!node)
+	{
+		mSlideoutX = 0; mSlideoutY = 0; mSlideoutW = 0; mSlideoutH = 0;
+		mConsoleX = 0;  mConsoleY = 0;  mConsoleW = gr_fb_width();  mConsoleH = gr_fb_height();
+	}
+	else
+	{
+		child = node->first_node("font");
+		if (child)
+		{
+			attr = child->first_attribute("resource");
+			if (attr)
+				mFont = PageManager::FindResource(attr->value());
+		}
+
+		child = node->first_node("color");
+		if (child)
+		{
+			attr = child->first_attribute("foreground");
+			if (attr)
+			{
+				std::string color = attr->value();
+				ConvertStrToColor(color, &mForegroundColor);
+			}
+			attr = child->first_attribute("background");
+			if (attr)
+			{
+				std::string color = attr->value();
+				ConvertStrToColor(color, &mBackgroundColor);
+			}
+			attr = child->first_attribute("scroll");
+			if (attr)
+			{
+				std::string color = attr->value();
+				ConvertStrToColor(color, &mScrollColor);
+			}
+		}
+
+		// Load the placement
+		LoadPlacement(node->first_node("placement"), &mConsoleX, &mConsoleY, &mConsoleW, &mConsoleH);
+
+		child = node->first_node("slideout");
+		if (child)
+		{
+			mSlideout = 1;
+			LoadPlacement(child, &mSlideoutX, &mSlideoutY);
+
+			attr = child->first_attribute("resource");
+			if (attr)   mSlideoutImage = PageManager::FindResource(attr->value());
+
+			if (mSlideoutImage && mSlideoutImage->GetResource())
+			{
+				mSlideoutW = gr_get_width(mSlideoutImage->GetResource());
+				mSlideoutH = gr_get_height(mSlideoutImage->GetResource());
+			}
+		}
+	}
+
+	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	SetRenderPos(mConsoleX, mConsoleY);
+	return;
+}
+
+int GUIConsole::RenderSlideout(void)
+{
+	if (!mSlideoutImage || !mSlideoutImage->GetResource())
+		return -1;
+
+	gr_blit(mSlideoutImage->GetResource(), 0, 0, mSlideoutW, mSlideoutH, mSlideoutX, mSlideoutY);
+	return 0;
+}
+
+int GUIConsole::RenderConsole(void)
+{
+	void* fontResource = NULL;
+	if (mFont)
+		fontResource = mFont->GetResource();
+
+	// We fill the background
+	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
+	gr_fill(mConsoleX, mConsoleY, mConsoleW, mConsoleH);
+
+	gr_color(mScrollColor.red, mScrollColor.green, mScrollColor.blue, mScrollColor.alpha);
+	gr_fill(mConsoleX + (mConsoleW * 9 / 10), mConsoleY, (mConsoleW / 10), mConsoleH);
+
+	// Don't try to continue to render without data
+	int prevCount = mLastCount;
+	mLastCount = gConsole.size();
+	mRender = false;
+	if (mLastCount == 0)
+		return (mSlideout ? RenderSlideout() : 0);
+
+	// Due to word wrap, figure out what / how the newly added text needs to be added to the render vector that is word wrapped
+	// Note, that multiple consoles on different GUI pages may be different widths or use different fonts, so the word wrapping
+	// may different in different console windows
+	for (int i = prevCount; i < mLastCount; i++) {
+		string curr_line = gConsole[i];
+		string curr_color = gConsoleColor[i];
+		int line_char_width;
+		for(;;) {
+			line_char_width = gr_maxExW(curr_line.c_str(), fontResource, mConsoleW);
+			if (line_char_width < curr_line.size()) {
+				rConsole.push_back(curr_line.substr(0, line_char_width));
+				rConsoleColor.push_back(curr_color);
+				curr_line = curr_line.substr(line_char_width);
+			} else {
+				rConsole.push_back(curr_line);
+				rConsoleColor.push_back(curr_color);
+				break;
+			}
+		}
+	}
+	RenderCount = rConsole.size();
+
+	// Find the start point
+	int start;
+	int curLine = mCurrentLine; // Thread-safing (Another thread updates this value)
+	if (curLine == -1)
+	{
+		start = RenderCount - mMaxRows;
+	}
+	else
+	{
+		if (curLine > (int) RenderCount)
+			curLine = (int) RenderCount;
+		if ((int) mMaxRows > curLine)
+			curLine = (int) mMaxRows;
+		start = curLine - mMaxRows;
+	}
+
+	unsigned int line;
+	for (line = 0; line < mMaxRows; line++)
+	{
+		if ((start + (int) line) >= 0 && (start + (int) line) < (int) RenderCount) {
+			if (rConsoleColor[start + line] == "normal") {
+				gr_color(mForegroundColor.red, mForegroundColor.green, mForegroundColor.blue, mForegroundColor.alpha);
+			} else {
+				COLOR mFontColor;
+				std::string color = rConsoleColor[start + line];
+				ConvertStrToColor(color, &mFontColor);
+				mFontColor.alpha = 255;
+				gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
+			}
+			gr_textExW(mConsoleX, mStartY + (line * mFontHeight), rConsole[start + line].c_str(), fontResource, mConsoleW + mConsoleX);
+		}
+	}
+	return (mSlideout ? RenderSlideout() : 0);
+}
+
+int GUIConsole::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (mSlideout && mSlideoutState == hidden)
+		return RenderSlideout();
+
+	return RenderConsole();
+}
+
+int GUIConsole::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (mSlideout && mSlideoutState != visible)
+	{
+		if (mSlideoutState == hidden)
+			return 0;
+
+		if (mSlideoutState == request_hide)
+			mSlideoutState = hidden;
+
+		if (mSlideoutState == request_show)
+			mSlideoutState = visible;
+
+		// Any time we activate the slider, we reset the position
+		mCurrentLine = -1;
+		return 2;
+	}
+
+	if (mCurrentLine == -1 && mLastCount != gConsole.size())
+	{
+		// We can use Render, and return for just a flip
+		Render();
+		return 2;
+	}
+	else if (mRender)
+	{
+		// They're still touching, so re-render
+		Render();
+		return 2;
+	}
+	return 0;
+}
+
+int GUIConsole::SetRenderPos(int x, int y, int w, int h)
+{
+	// Adjust the stub position accordingly
+	mSlideoutX += (x - mConsoleX);
+	mSlideoutY += (y - mConsoleY);
+
+	mConsoleX = x;
+	mConsoleY = y;
+	if (w || h)
+	{
+		mConsoleW = w;
+		mConsoleH = h;
+	}
+
+	// Calculate the max rows
+	mMaxRows = mConsoleH / mFontHeight;
+
+	// Adjust so we always fit to bottom
+	mStartY = mConsoleY + (mConsoleH % mFontHeight);
+	return 0;
+}
+
+// IsInRegion - Checks if the request is handled by this object
+//  Return 0 if this object handles the request, 1 if not
+int GUIConsole::IsInRegion(int x, int y)
+{
+	if (mSlideout)
+	{
+		// Check if they tapped the slideout button
+		if (x >= mSlideoutX && x <= mSlideoutX + mSlideoutW && y >= mSlideoutY && y < mSlideoutY + mSlideoutH)
+			return 1;
+
+		// If we're only rendering the slideout, bail now
+		if (mSlideoutState == hidden)
+			return 0;
+	}
+
+	return (x < mConsoleX || x > mConsoleX + mConsoleW || y < mConsoleY || y > mConsoleY + mConsoleH) ? 0 : 1;
+}
+
+// NotifyTouch - Notify of a touch event
+//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+int GUIConsole::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if(!isConditionTrue())
+		return -1;
+
+	if (mSlideout && mSlideoutState == hidden)
+	{
+		if (state == TOUCH_START)
+		{
+			mSlideoutState = request_show;
+			return 1;
+		}
+	}
+	else if (mSlideout && mSlideoutState == visible)
+	{
+		// Are we sliding it back in?
+		if (state == TOUCH_START && x > mSlideoutX && x < (mSlideoutX + mSlideoutW) && y > mSlideoutY && y < (mSlideoutY + mSlideoutH))
+		{
+			mSlideoutState = request_hide;
+			return 1;
+		}
+	}
+
+	// If we don't have enough lines to scroll, throw this away.
+	if (RenderCount < mMaxRows)   return 1;
+
+	// We are scrolling!!!
+	switch (state)
+	{
+	case TOUCH_START:
+		mLastTouchX = x;
+		mLastTouchY = y;
+		break;
+
+	case TOUCH_DRAG:
+		if (x < mConsoleX || x > mConsoleX + mConsoleW || y < mConsoleY || y > mConsoleY + mConsoleH)
+			break; // touch is outside of the console area -- do nothing
+		if (y > mLastTouchY + mFontHeight) {
+			while (y > mLastTouchY + mFontHeight) {
+				if (mCurrentLine == -1)
+					mCurrentLine = RenderCount - 1;
+				else if (mCurrentLine > mMaxRows)
+					mCurrentLine--;
+				mLastTouchY += mFontHeight;
+			}
+			mRender = true;
+		} else if (y < mLastTouchY - mFontHeight) {
+			while (y < mLastTouchY - mFontHeight) {
+				if (mCurrentLine >= 0)
+					mCurrentLine++;
+				mLastTouchY -= mFontHeight;
+			}
+			if (mCurrentLine >= (int) RenderCount)
+				mCurrentLine = -1;
+			mRender = true;
+		}
+		break;
+
+	case TOUCH_RELEASE:
+		mLastTouchY = -1;
+	case TOUCH_REPEAT:
+	case TOUCH_HOLD:
+		break;
+	}
+	return 0;
+}
diff --git a/gui/devices/1024x600/res/fonts/Roboto-Regular-20.dat b/gui/devices/1024x600/res/fonts/Roboto-Regular-20.dat
new file mode 100755
index 0000000..6588b41
--- /dev/null
+++ b/gui/devices/1024x600/res/fonts/Roboto-Regular-20.dat
Binary files differ
diff --git a/gui/devices/1024x600/res/images/back-icon.png b/gui/devices/1024x600/res/images/back-icon.png
new file mode 100644
index 0000000..1096c90
--- /dev/null
+++ b/gui/devices/1024x600/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/background.jpg b/gui/devices/1024x600/res/images/background.jpg
new file mode 100644
index 0000000..2ac48e0
--- /dev/null
+++ b/gui/devices/1024x600/res/images/background.jpg
Binary files differ
diff --git a/gui/devices/1024x600/res/images/button.png b/gui/devices/1024x600/res/images/button.png
new file mode 100644
index 0000000..6ae29ae
--- /dev/null
+++ b/gui/devices/1024x600/res/images/button.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/checkbox_checked.png b/gui/devices/1024x600/res/images/checkbox_checked.png
new file mode 100644
index 0000000..3447349
--- /dev/null
+++ b/gui/devices/1024x600/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/checkbox_empty.png b/gui/devices/1024x600/res/images/checkbox_empty.png
new file mode 100644
index 0000000..f5f35d8
--- /dev/null
+++ b/gui/devices/1024x600/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/console-icon.png b/gui/devices/1024x600/res/images/console-icon.png
new file mode 100644
index 0000000..91a727d
--- /dev/null
+++ b/gui/devices/1024x600/res/images/console-icon.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/console-toggle.png b/gui/devices/1024x600/res/images/console-toggle.png
new file mode 100644
index 0000000..963b9fd
--- /dev/null
+++ b/gui/devices/1024x600/res/images/console-toggle.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/cursor.png b/gui/devices/1024x600/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/1024x600/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/curtain.jpg b/gui/devices/1024x600/res/images/curtain.jpg
new file mode 100644
index 0000000..bb66e99
--- /dev/null
+++ b/gui/devices/1024x600/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/1024x600/res/images/file.png b/gui/devices/1024x600/res/images/file.png
new file mode 100644
index 0000000..8556bc7
--- /dev/null
+++ b/gui/devices/1024x600/res/images/file.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/folder.png b/gui/devices/1024x600/res/images/folder.png
new file mode 100644
index 0000000..a3a5f69
--- /dev/null
+++ b/gui/devices/1024x600/res/images/folder.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/home-icon.png b/gui/devices/1024x600/res/images/home-icon.png
new file mode 100644
index 0000000..e42d774
--- /dev/null
+++ b/gui/devices/1024x600/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/indeterminate001.png b/gui/devices/1024x600/res/images/indeterminate001.png
new file mode 100755
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/1024x600/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/indeterminate002.png b/gui/devices/1024x600/res/images/indeterminate002.png
new file mode 100755
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/1024x600/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/indeterminate003.png b/gui/devices/1024x600/res/images/indeterminate003.png
new file mode 100755
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/1024x600/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/indeterminate004.png b/gui/devices/1024x600/res/images/indeterminate004.png
new file mode 100755
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/1024x600/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/indeterminate005.png b/gui/devices/1024x600/res/images/indeterminate005.png
new file mode 100755
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/1024x600/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/indeterminate006.png b/gui/devices/1024x600/res/images/indeterminate006.png
new file mode 100755
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/1024x600/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/keyboard1.png b/gui/devices/1024x600/res/images/keyboard1.png
new file mode 100644
index 0000000..c2aa0a0
--- /dev/null
+++ b/gui/devices/1024x600/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/keyboard2.png b/gui/devices/1024x600/res/images/keyboard2.png
new file mode 100644
index 0000000..8851145
--- /dev/null
+++ b/gui/devices/1024x600/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/keyboard3.png b/gui/devices/1024x600/res/images/keyboard3.png
new file mode 100644
index 0000000..5c99fad
--- /dev/null
+++ b/gui/devices/1024x600/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/keyboard4.png b/gui/devices/1024x600/res/images/keyboard4.png
new file mode 100644
index 0000000..30bae14
--- /dev/null
+++ b/gui/devices/1024x600/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/medium-button.png b/gui/devices/1024x600/res/images/medium-button.png
new file mode 100644
index 0000000..77dc540
--- /dev/null
+++ b/gui/devices/1024x600/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/mediumwide-button.png b/gui/devices/1024x600/res/images/mediumwide-button.png
new file mode 100644
index 0000000..b2b7d4d
--- /dev/null
+++ b/gui/devices/1024x600/res/images/mediumwide-button.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/minus-button.png b/gui/devices/1024x600/res/images/minus-button.png
new file mode 100644
index 0000000..5a49c75
--- /dev/null
+++ b/gui/devices/1024x600/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/plus-button.png b/gui/devices/1024x600/res/images/plus-button.png
new file mode 100644
index 0000000..da1326c
--- /dev/null
+++ b/gui/devices/1024x600/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/progress_empty.png b/gui/devices/1024x600/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/1024x600/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/progress_fill.png b/gui/devices/1024x600/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/1024x600/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/radio_empty.png b/gui/devices/1024x600/res/images/radio_empty.png
new file mode 100644
index 0000000..88d1c1f
--- /dev/null
+++ b/gui/devices/1024x600/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/radio_selected.png b/gui/devices/1024x600/res/images/radio_selected.png
new file mode 100644
index 0000000..864065d
--- /dev/null
+++ b/gui/devices/1024x600/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/slider-touch.png b/gui/devices/1024x600/res/images/slider-touch.png
new file mode 100644
index 0000000..d8647b8
--- /dev/null
+++ b/gui/devices/1024x600/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/slider-used.png b/gui/devices/1024x600/res/images/slider-used.png
new file mode 100644
index 0000000..bf6cad9
--- /dev/null
+++ b/gui/devices/1024x600/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/slider.png b/gui/devices/1024x600/res/images/slider.png
new file mode 100644
index 0000000..4081ea5
--- /dev/null
+++ b/gui/devices/1024x600/res/images/slider.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/sort-button.png b/gui/devices/1024x600/res/images/sort-button.png
new file mode 100644
index 0000000..13ab929
--- /dev/null
+++ b/gui/devices/1024x600/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/1024x600/res/images/unlock.png b/gui/devices/1024x600/res/images/unlock.png
new file mode 100644
index 0000000..9041221
--- /dev/null
+++ b/gui/devices/1024x600/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/1024x600/res/ui.xml b/gui/devices/1024x600/res/ui.xml
new file mode 100644
index 0000000..7b7ad8c
--- /dev/null
+++ b/gui/devices/1024x600/res/ui.xml
@@ -0,0 +1,439 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="1024" height="600" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="landscape.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="base" type="image" filename="background.jpg" />
+		<resource name="main_button" type="image" filename="button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="mediumwide_button" type="image" filename="mediumwide-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="console_button" type="image" filename="console-toggle" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="23" />
+		<variable name="col2_x" value="269" />
+		<variable name="col3_x" value="515" />
+		<variable name="col4_x" value="761" />
+		<variable name="row1_y" value="127" />
+		<variable name="row2_y" value="388" />
+		<variable name="col_center_x" value="392" />
+		<variable name="center_x" value="512" />
+		<variable name="screen_width" value="1024" />
+		<variable name="screen_height" value="600" />
+		<variable name="col_progressbar_x" value="386" />
+		<variable name="row_progressbar_y" value="540" />
+		<variable name="col1_medium_x" value="257" />
+		<variable name="col2_medium_x" value="387" />
+		<variable name="col3_medium_x" value="517" />
+		<variable name="col4_medium_x" value="647" />
+		<variable name="row1_medium_y" value="105" />
+		<variable name="row2_medium_y" value="200" />
+		<variable name="row3_medium_y" value="245" />
+		<variable name="row4_medium_y" value="440" />
+		<variable name="row5_medium_y" value="405" />
+		<variable name="row1_text_y" value="58" />
+		<variable name="row2_text_y" value="105" />
+		<variable name="row3_text_y" value="140" />
+		<variable name="row4_text_y" value="170" />
+		<variable name="row5_text_y" value="200" />
+		<variable name="row6_text_y" value="230" />
+		<variable name="row7_text_y" value="260" />
+		<variable name="row8_text_y" value="290" />
+		<variable name="row9_text_y" value="320" />
+		<variable name="row10_text_y" value="350" />
+		<variable name="row11_text_y" value="380" />
+		<variable name="row12_text_y" value="410" />
+		<variable name="row13_text_y" value="440" />
+		<variable name="row14_text_y" value="470" />
+		<variable name="row15_text_y" value="500" />
+		<variable name="row16_text_y" value="530" />
+		<variable name="row17_text_y" value="560" />
+		<variable name="row18_text_y" value="590" />
+		<variable name="row_offsetmedium_y" value="465" />
+		<variable name="home_button_x" value="813" />
+		<variable name="home_button_y" value="5" />
+		<variable name="back_button_x" value="883" />
+		<variable name="back_button_y" value="5" />
+		<variable name="console_button_x" value="953" />
+		<variable name="console_button_y" value="5" />
+		<variable name="nandcheck_col1" value="150" />
+		<variable name="nandcheck_col2" value="450" />
+		<variable name="nandcheck_row1" value="150" />
+		<variable name="nandcheck_row2" value="195" />
+		<variable name="nandcheck_row3" value="240" />
+		<variable name="nandcheck_row4" value="285" />
+		<variable name="nandcheck_row5" value="330" />
+		<variable name="nandcheck_row6" value="375" />
+		<variable name="nandcheck_row7" value="410" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#A0A0A0" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="slider_x" value="307" />
+		<variable name="slider_y" value="470" />
+		<variable name="slider_text_y" value="520" />
+		<variable name="sort_text_x" value="270" />
+		<variable name="sort_asc_text_y" value="510" />
+		<variable name="sort_asc_button_y" value="505" />
+		<variable name="sort_desc_text_y" value="545" />
+		<variable name="sort_desc_button_y" value="542" />
+		<variable name="sort_col1_button_x" value="440" />
+		<variable name="sort_col2_button_x" value="510" />
+		<variable name="sort_col3_button_x" value="580" />
+		<variable name="col1_sdext_x" value="370" />
+		<variable name="col2_sdext_x" value="600" />
+		<variable name="row1_sdext_y" value="115" />
+		<variable name="row2_sdext_y" value="180" />
+		<variable name="row_extsize_y" value="115" />
+		<variable name="row_swapsize_y" value="180" />
+		<variable name="input_x" value="28" />
+		<variable name="input_width" value="944" />
+		<variable name="input_height" value="30" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="25" />
+		<variable name="console_width" value="974" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="320" />
+		<variable name="console_install_height" value="440" />
+		<variable name="console_installdone_height" value="300" />
+		<variable name="fileselector_folder_x" value="28" />
+		<variable name="fileselector_folder_width" value="282" />
+		<variable name="fileselector_folderonly_width" value="460" />
+		<variable name="fileselector_file_x" value="317" />
+		<variable name="fileselector_file_width" value="682" />
+		<variable name="fileselector_install_y" value="120" />
+		<variable name="fileselector_install_height" value="382" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="18" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="32" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="24" />
+		<variable name="fastscroll_recth" value="40" />
+		<variable name="zipstorage_text_y" value="88" />
+		<variable name="listbox_x" value="269" />
+		<variable name="listbox_y" value="90" />
+		<variable name="listbox_width" value="460" />
+		<variable name="listbox_tz_height" value="290" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="18" />
+		<variable name="sd_plus_x" value="280" />
+		<variable name="lock_x" value="312" />
+		<variable name="lock_y" value="184" />
+		<variable name="filemanager_select_x" value="761" />
+		<variable name="filemanager_select_y" value="495" />
+		<variable name="backup_name_text_y" value="440" />
+		<variable name="backup_name_button_y" value="240" />
+		<variable name="col_right_x" value="996" />
+		<variable name="cancel_button_y" value="220" />
+		<variable name="terminal_console_y" value="0" />
+		<variable name="terminal_console_height" value="300" />
+		<variable name="terminal_text_y" value="307" />
+		<variable name="terminal_button_y" value="298" />
+		<variable name="terminal_input_width" value="775" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="512" />
+		<variable name="button_fill_main_width" value="486" />
+		<variable name="button_fill_main_height" value="150" />
+		<variable name="button_fill_half_height" value="50" />
+		<variable name="button_fill_quarter_height" value="30" />
+		<variable name="button_full_center_x" value="256" />
+		<variable name="backup_list_x" value="23" />
+		<variable name="backup_list_y" value="105" />
+		<variable name="backup_list_width" value="486" />
+		<variable name="backup_list_height" value="360" />
+		<variable name="backup_storage_y" value="250" />
+		<variable name="backup_encrypt_y" value="310" />
+		<variable name="restore_list_y" value="140" />
+		<variable name="restore_list_height" value="320" />
+		<variable name="mount_list_height" value="400" />
+		<variable name="mount_storage_row" value="500" />
+		<variable name="wipe_list_height" value="300" />
+		<variable name="wipe_button_y" value="190" />
+		<variable name="slidervalue_x" value="256" />
+		<variable name="slidervalue_w" value="512" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="2" />
+		<variable name="slidervalue_padding" value="0" />
+		<variable name="slidervalue_sliderw" value="10" />
+		<variable name="slidervalue_sliderh" value="50" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="base" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="200" y="5" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="200" y="30" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery Level: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="400" y="30" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="700" y="30" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="475" y="30" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="flash_zip_console">
+			<object type="console">
+				<placement x="%console_x%" y="85" w="%console_width%" h="340" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="200" w="%console_width%" h="265" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="console_button" x="%console_button_x%" y="%console_button_y%" />
+				<placement x="%console_x%" y="75" w="%console_width%" h="497" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="341" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="65" width="92" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" key11="104:c:8" />
+					<row2 key01="138:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="l" key10="150:action" />
+					<row3 key01="102:layout2" key02="91:z" key03="91:x" key04="91:c" key05="91:v" key06="91:b" key07="91:n" key08="91:m" key09="91:," long09="!" key10="91:." long10="?" key11="103::" long11="+" />
+					<row4 key01="132:layout3" key02="92:" key03="/" long03="@" key04="404: " key05="'" long05="92:c:34" key06="-" long06="_" />
+				</layout1>
+				<layout2>
+					<keysize height="65" width="92" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" key11="104:c:8" />
+					<row2 key01="138:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="L" key10="150:action" />
+					<row3 key01="102:layout1" key02="91:Z" key03="91:X" key04="91:C" key05="91:V" key06="91:B" key07="91:N" key08="91:M" key09="91:," long09="!" key10="91:." long10="?" key11="103::" long11="+" />
+					<row4 key01="132:layout3" key02="92:" key03="/" long03="@" key04="404: " key05="'" long05="92:c:34" key06="-" long06="_" />
+				</layout2>
+				<layout3>
+					<keysize height="65" width="92" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" key11="104:c:8" />
+					<row2 key01="138:#" key02="$" key03="%" key04="&" key05="*" key06="-" key07="+" key08="(" key09=")" key10="150:action" />
+					<row3 key01="102:layout4" key02="91:<" key03="91:>" key04="91:=" key05="91::" key06="91:;" key07="91:," key08="91:." key09="91:!" key10="91:?" key11="103:/" />
+					<row4 key01="132:layout1" key02="92:" key03="@" key04="404: " key05="92:c:34" key06="_" />
+				</layout3>
+				<layout4>
+					<keysize height="65" width="92" />
+					<row1 key01="~" key02="`" key03="|" key04="92:" key05="92:" key06="92:" key07="92:" key08="92:" key09="92:" key10="92:" key11="104:c:8" />
+					<row2 key01="138:" key02="92:" key03="92:" key04="92:" key05="^" key06="92:" key07="92:" key08="{" key09="}" key10="150:action" />
+					<row3 key01="102:layout3" key02="91:\" key03="91:" key04="91:" key05="91:" key06="91:" key07="91:[" key08="91:]" key09="91:!" key10="91:?" />
+					<row4 key01="132:layout1" key02="92:" key03="92:" key04="404: " />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/1024x768/res/fonts/Roboto-Regular-20.dat b/gui/devices/1024x768/res/fonts/Roboto-Regular-20.dat
new file mode 100755
index 0000000..6588b41
--- /dev/null
+++ b/gui/devices/1024x768/res/fonts/Roboto-Regular-20.dat
Binary files differ
diff --git a/gui/devices/1024x768/res/images/back-icon.png b/gui/devices/1024x768/res/images/back-icon.png
new file mode 100644
index 0000000..1096c90
--- /dev/null
+++ b/gui/devices/1024x768/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/background.jpg b/gui/devices/1024x768/res/images/background.jpg
new file mode 100644
index 0000000..2ac48e0
--- /dev/null
+++ b/gui/devices/1024x768/res/images/background.jpg
Binary files differ
diff --git a/gui/devices/1024x768/res/images/button.png b/gui/devices/1024x768/res/images/button.png
new file mode 100644
index 0000000..6ae29ae
--- /dev/null
+++ b/gui/devices/1024x768/res/images/button.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/checkbox_checked.png b/gui/devices/1024x768/res/images/checkbox_checked.png
new file mode 100644
index 0000000..3447349
--- /dev/null
+++ b/gui/devices/1024x768/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/checkbox_empty.png b/gui/devices/1024x768/res/images/checkbox_empty.png
new file mode 100644
index 0000000..f5f35d8
--- /dev/null
+++ b/gui/devices/1024x768/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/console-icon.png b/gui/devices/1024x768/res/images/console-icon.png
new file mode 100644
index 0000000..91a727d
--- /dev/null
+++ b/gui/devices/1024x768/res/images/console-icon.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/console-toggle.png b/gui/devices/1024x768/res/images/console-toggle.png
new file mode 100644
index 0000000..963b9fd
--- /dev/null
+++ b/gui/devices/1024x768/res/images/console-toggle.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/cursor.png b/gui/devices/1024x768/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/1024x768/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/curtain.jpg b/gui/devices/1024x768/res/images/curtain.jpg
new file mode 100644
index 0000000..38cc331
--- /dev/null
+++ b/gui/devices/1024x768/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/1024x768/res/images/file.png b/gui/devices/1024x768/res/images/file.png
new file mode 100644
index 0000000..8556bc7
--- /dev/null
+++ b/gui/devices/1024x768/res/images/file.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/folder.png b/gui/devices/1024x768/res/images/folder.png
new file mode 100644
index 0000000..a3a5f69
--- /dev/null
+++ b/gui/devices/1024x768/res/images/folder.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/home-icon.png b/gui/devices/1024x768/res/images/home-icon.png
new file mode 100644
index 0000000..e42d774
--- /dev/null
+++ b/gui/devices/1024x768/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/indeterminate001.png b/gui/devices/1024x768/res/images/indeterminate001.png
new file mode 100755
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/1024x768/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/indeterminate002.png b/gui/devices/1024x768/res/images/indeterminate002.png
new file mode 100755
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/1024x768/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/indeterminate003.png b/gui/devices/1024x768/res/images/indeterminate003.png
new file mode 100755
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/1024x768/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/indeterminate004.png b/gui/devices/1024x768/res/images/indeterminate004.png
new file mode 100755
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/1024x768/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/indeterminate005.png b/gui/devices/1024x768/res/images/indeterminate005.png
new file mode 100755
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/1024x768/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/indeterminate006.png b/gui/devices/1024x768/res/images/indeterminate006.png
new file mode 100755
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/1024x768/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/keyboard1.png b/gui/devices/1024x768/res/images/keyboard1.png
new file mode 100644
index 0000000..1c262e6
--- /dev/null
+++ b/gui/devices/1024x768/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/keyboard2.png b/gui/devices/1024x768/res/images/keyboard2.png
new file mode 100644
index 0000000..140503b
--- /dev/null
+++ b/gui/devices/1024x768/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/keyboard3.png b/gui/devices/1024x768/res/images/keyboard3.png
new file mode 100644
index 0000000..bb8f58d
--- /dev/null
+++ b/gui/devices/1024x768/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/keyboard4.png b/gui/devices/1024x768/res/images/keyboard4.png
new file mode 100644
index 0000000..f3b4445
--- /dev/null
+++ b/gui/devices/1024x768/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/medium-button.png b/gui/devices/1024x768/res/images/medium-button.png
new file mode 100644
index 0000000..77dc540
--- /dev/null
+++ b/gui/devices/1024x768/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/mediumwide-button.png b/gui/devices/1024x768/res/images/mediumwide-button.png
new file mode 100644
index 0000000..b2b7d4d
--- /dev/null
+++ b/gui/devices/1024x768/res/images/mediumwide-button.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/minus-button.png b/gui/devices/1024x768/res/images/minus-button.png
new file mode 100644
index 0000000..5a49c75
--- /dev/null
+++ b/gui/devices/1024x768/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/plus-button.png b/gui/devices/1024x768/res/images/plus-button.png
new file mode 100644
index 0000000..da1326c
--- /dev/null
+++ b/gui/devices/1024x768/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/progress_empty.png b/gui/devices/1024x768/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/1024x768/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/progress_fill.png b/gui/devices/1024x768/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/1024x768/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/radio_empty.png b/gui/devices/1024x768/res/images/radio_empty.png
new file mode 100644
index 0000000..88d1c1f
--- /dev/null
+++ b/gui/devices/1024x768/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/radio_selected.png b/gui/devices/1024x768/res/images/radio_selected.png
new file mode 100644
index 0000000..864065d
--- /dev/null
+++ b/gui/devices/1024x768/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/slider-touch.png b/gui/devices/1024x768/res/images/slider-touch.png
new file mode 100644
index 0000000..d8647b8
--- /dev/null
+++ b/gui/devices/1024x768/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/slider-used.png b/gui/devices/1024x768/res/images/slider-used.png
new file mode 100644
index 0000000..bf6cad9
--- /dev/null
+++ b/gui/devices/1024x768/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/slider.png b/gui/devices/1024x768/res/images/slider.png
new file mode 100644
index 0000000..4081ea5
--- /dev/null
+++ b/gui/devices/1024x768/res/images/slider.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/sort-button.png b/gui/devices/1024x768/res/images/sort-button.png
new file mode 100644
index 0000000..13ab929
--- /dev/null
+++ b/gui/devices/1024x768/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/1024x768/res/images/unlock.png b/gui/devices/1024x768/res/images/unlock.png
new file mode 100644
index 0000000..9041221
--- /dev/null
+++ b/gui/devices/1024x768/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/1024x768/res/ui.xml b/gui/devices/1024x768/res/ui.xml
new file mode 100644
index 0000000..b5ce1b4
--- /dev/null
+++ b/gui/devices/1024x768/res/ui.xml
@@ -0,0 +1,439 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="1024" height="768" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="landscape.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="base" type="image" filename="background.jpg" />
+		<resource name="main_button" type="image" filename="button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="mediumwide_button" type="image" filename="mediumwide-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="console_button" type="image" filename="console-toggle" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="23" />
+		<variable name="col2_x" value="269" />
+		<variable name="col3_x" value="515" />
+		<variable name="col4_x" value="761" />
+		<variable name="row1_y" value="127" />
+		<variable name="row2_y" value="388" />
+		<variable name="col_center_x" value="392" />
+		<variable name="center_x" value="512" />
+		<variable name="screen_width" value="1024" />
+		<variable name="screen_height" value="768" />
+		<variable name="col_progressbar_x" value="386" />
+		<variable name="row_progressbar_y" value="700" />
+		<variable name="col1_medium_x" value="257" />
+		<variable name="col2_medium_x" value="387" />
+		<variable name="col3_medium_x" value="517" />
+		<variable name="col4_medium_x" value="647" />
+		<variable name="row1_medium_y" value="105" />
+		<variable name="row2_medium_y" value="245" />
+		<variable name="row3_medium_y" value="245" />
+		<variable name="row4_medium_y" value="450" />
+		<variable name="row5_medium_y" value="405" />
+		<variable name="row1_text_y" value="58" />
+		<variable name="row2_text_y" value="105" />
+		<variable name="row3_text_y" value="140" />
+		<variable name="row4_text_y" value="170" />
+		<variable name="row5_text_y" value="200" />
+		<variable name="row6_text_y" value="230" />
+		<variable name="row7_text_y" value="260" />
+		<variable name="row8_text_y" value="290" />
+		<variable name="row9_text_y" value="320" />
+		<variable name="row10_text_y" value="350" />
+		<variable name="row11_text_y" value="380" />
+		<variable name="row12_text_y" value="410" />
+		<variable name="row13_text_y" value="440" />
+		<variable name="row14_text_y" value="470" />
+		<variable name="row15_text_y" value="500" />
+		<variable name="row16_text_y" value="530" />
+		<variable name="row17_text_y" value="560" />
+		<variable name="row18_text_y" value="590" />
+		<variable name="row_offsetmedium_y" value="465" />
+		<variable name="home_button_x" value="813" />
+		<variable name="home_button_y" value="5" />
+		<variable name="back_button_x" value="883" />
+		<variable name="back_button_y" value="5" />
+		<variable name="console_button_x" value="953" />
+		<variable name="console_button_y" value="5" />
+		<variable name="nandcheck_col1" value="150" />
+		<variable name="nandcheck_col2" value="450" />
+		<variable name="nandcheck_row1" value="150" />
+		<variable name="nandcheck_row2" value="200" />
+		<variable name="nandcheck_row3" value="250" />
+		<variable name="nandcheck_row4" value="300" />
+		<variable name="nandcheck_row5" value="350" />
+		<variable name="nandcheck_row6" value="400" />
+		<variable name="nandcheck_row7" value="450" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#A0A0A0" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="slider_x" value="307" />
+		<variable name="slider_y" value="600" />
+		<variable name="slider_text_y" value="650" />
+		<variable name="sort_text_x" value="270" />
+		<variable name="sort_asc_text_y" value="655" />
+		<variable name="sort_asc_button_y" value="650" />
+		<variable name="sort_desc_text_y" value="700" />
+		<variable name="sort_desc_button_y" value="695" />
+		<variable name="sort_col1_button_x" value="440" />
+		<variable name="sort_col2_button_x" value="510" />
+		<variable name="sort_col3_button_x" value="580" />
+		<variable name="col1_sdext_x" value="370" />
+		<variable name="col2_sdext_x" value="600" />
+		<variable name="row1_sdext_y" value="115" />
+		<variable name="row2_sdext_y" value="180" />
+		<variable name="row_extsize_y" value="115" />
+		<variable name="row_swapsize_y" value="180" />
+		<variable name="input_x" value="28" />
+		<variable name="input_width" value="944" />
+		<variable name="input_height" value="30" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="25" />
+		<variable name="console_width" value="974" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="320" />
+		<variable name="console_install_height" value="440" />
+		<variable name="console_installdone_height" value="300" />
+		<variable name="fileselector_folder_x" value="28" />
+		<variable name="fileselector_folder_width" value="282" />
+		<variable name="fileselector_folderonly_width" value="460" />
+		<variable name="fileselector_file_x" value="317" />
+		<variable name="fileselector_file_width" value="682" />
+		<variable name="fileselector_install_y" value="120" />
+		<variable name="fileselector_install_height" value="510" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="18" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="32" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="24" />
+		<variable name="fastscroll_recth" value="40" />
+		<variable name="zipstorage_text_y" value="88" />
+		<variable name="listbox_x" value="269" />
+		<variable name="listbox_y" value="90" />
+		<variable name="listbox_width" value="460" />
+		<variable name="listbox_tz_height" value="290" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="18" />
+		<variable name="sd_plus_x" value="280" />
+		<variable name="lock_x" value="312" />
+		<variable name="lock_y" value="184" />
+		<variable name="filemanager_select_x" value="761" />
+		<variable name="filemanager_select_y" value="620" />
+		<variable name="backup_name_text_y" value="440" />
+		<variable name="backup_name_button_y" value="240" />
+		<variable name="col_right_x" value="996" />
+		<variable name="cancel_button_y" value="240" />
+		<variable name="terminal_console_y" value="0" />
+		<variable name="terminal_console_height" value="362" />
+		<variable name="terminal_text_y" value="368" />
+		<variable name="terminal_button_y" value="358" />
+		<variable name="terminal_input_width" value="775" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="512" />
+		<variable name="button_fill_main_width" value="486" />
+		<variable name="button_fill_main_height" value="150" />
+		<variable name="button_fill_half_height" value="50" />
+		<variable name="button_fill_quarter_height" value="30" />
+		<variable name="button_full_center_x" value="256" />
+		<variable name="backup_list_x" value="23" />
+		<variable name="backup_list_y" value="105" />
+		<variable name="backup_list_width" value="486" />
+		<variable name="backup_list_height" value="450" />
+		<variable name="backup_storage_y" value="250" />
+		<variable name="backup_encrypt_y" value="310" />
+		<variable name="restore_list_y" value="140" />
+		<variable name="restore_list_height" value="400" />
+		<variable name="mount_list_height" value="500" />
+		<variable name="mount_storage_row" value="500" />
+		<variable name="wipe_list_height" value="400" />
+		<variable name="wipe_button_y" value="190" />
+		<variable name="slidervalue_x" value="256" />
+		<variable name="slidervalue_w" value="512" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="2" />
+		<variable name="slidervalue_padding" value="0" />
+		<variable name="slidervalue_sliderw" value="10" />
+		<variable name="slidervalue_sliderh" value="50" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="base" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="200" y="5" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="200" y="30" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery Level: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="400" y="30" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="700" y="30" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="475" y="30" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="flash_zip_console">
+			<object type="console">
+				<placement x="%console_x%" y="85" w="%console_width%" h="430" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="200" w="%console_width%" h="380" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="console_button" x="%console_button_x%" y="%console_button_y%" />
+				<placement x="%console_x%" y="75" w="%console_width%" h="665" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="408" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="90" width="85" />
+					<row1 key01="80:" key02="q" long02="1" key03="w" long03="2" key04="e" long04="3" key05="r" long05="4" key06="t" long06="5" key07="y" long07="6" key08="u" long08="7" key09="i" long09="8" key10="o" long10="9" key11="p" long11="0" key12="94:c:8" />
+					<row2 key01="115:layout3" key02="83:a" key03="83:s" key04="83:d" key05="83:f" key06="83:g" key07="83:h" key08="83:j" key09="83:k" key10="83:l" key11="162:action" />
+					<row3 key01="140:layout2" key02="82:z" key03="82:x" key04="82:c" key05="82:v" key06="82:b" key07="82:n" key08="82:m" key09="82:," long09="!" key10="82:." long10="?" key11="144:layout2" />
+					<row4 key01="320:" key02="385: " key03="80:/" long03=":" key04="80:-" long04="_" />
+				</layout1>
+				<layout2>
+					<keysize height="90" width="85" capslock="0" revert_layout="1" />
+					<row1 key01="80:" key02="Q" long02="1" key03="W" long03="2" key04="E" long04="3" key05="R" long05="4" key06="T" long06="5" key07="Y" long07="6" key08="U" long08="7" key09="I" long09="8" key10="O" long10="9" key11="P" long11="0" key12="94:c:8" />
+					<row2 key01="115:layout3" key02="83:A" key03="83:S" key04="83:D" key05="83:F" key06="83:G" key07="83:H" key08="83:J" key09="83:K" key10="83:L" key11="162:action" />
+					<row3 key01="140:layout1" key02="82:Z" key03="82:X" key04="82:C" key05="82:V" key06="82:B" key07="82:N" key08="82:M" key09="82:," long09="!" key10="82:." long10="?" key11="144:layout1" />
+					<row4 key01="320:" key02="385: " key03="80:/" long03=":" key04="80:-" long04="_" />
+				</layout2>
+				<layout3>
+					<keysize height="90" width="85" />
+					<row1 key01="80:" key02="1" key03="2" key04="3" key05="4" key06="5" key07="6" key08="7" key09="8" key10="9" key11="0" key12="94:c:8" />
+					<row2 key01="115:layout1" key02="83:#" key03="83:$" key04="83:%" key05="83:&" key06="83:*" key07="83:-" key08="83:+" key09="83:(" key10="83:)" key11="162:action" />
+					<row3 key01="140:layout4" key02="82:<" key03="82:>" key04="82:=" key05="82:'" key06="82:;" key07="82:," key08="82:." key09="82:!" key10="82:?" key11="144:layout4" />
+					<row4 key01="155:" key02="85:/" key03="85:@" key04="385: " key05="80:c:34" key06="_" />
+				</layout3>
+				<layout4>
+					<keysize height="90" width="85" />
+					<row1 key01="80:" key02="~" key03="`" key04="|" key05="85:" key06="85:" key07="85:" key08="85:" key09="85:" key10="85:" key11="85:" key12="94:c:8" />
+					<row2 key01="115:layout1" key02="83:" key03="83:" key04="83:" key05="83:" key06="83:^" key07="83:" key08="83:" key09="83:{" key10="83:}" key11="162:action" />
+					<row3 key01="140:layout3" key02="82:\" key03="82:" key04="82:" key05="82:" key06="82:" key07="82:[" key08="82:]" key09="82:!" key10="82:?" key11="144:layout3" />
+					<row4 key01="320:" key02="385: " />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/1080x1920/res/fonts/Roboto-Condensed-40.dat b/gui/devices/1080x1920/res/fonts/Roboto-Condensed-40.dat
new file mode 100644
index 0000000..ff23add
--- /dev/null
+++ b/gui/devices/1080x1920/res/fonts/Roboto-Condensed-40.dat
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/back-icon.png b/gui/devices/1080x1920/res/images/back-icon.png
new file mode 100644
index 0000000..85c6a5d
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/checkbox_checked.png b/gui/devices/1080x1920/res/images/checkbox_checked.png
new file mode 100644
index 0000000..65d0b34
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/checkbox_empty.png b/gui/devices/1080x1920/res/images/checkbox_empty.png
new file mode 100644
index 0000000..89b4c96
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/cursor.png b/gui/devices/1080x1920/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/curtain.jpg b/gui/devices/1080x1920/res/images/curtain.jpg
new file mode 100644
index 0000000..269941d
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/file.png b/gui/devices/1080x1920/res/images/file.png
new file mode 100644
index 0000000..a69b619
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/file.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/folder.png b/gui/devices/1080x1920/res/images/folder.png
new file mode 100644
index 0000000..fbea7a9
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/folder.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/home-icon.png b/gui/devices/1080x1920/res/images/home-icon.png
new file mode 100644
index 0000000..d6bc73d
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/indeterminate001.png b/gui/devices/1080x1920/res/images/indeterminate001.png
new file mode 100644
index 0000000..a205e91
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/indeterminate002.png b/gui/devices/1080x1920/res/images/indeterminate002.png
new file mode 100644
index 0000000..f777408
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/indeterminate003.png b/gui/devices/1080x1920/res/images/indeterminate003.png
new file mode 100644
index 0000000..cbb5974
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/indeterminate004.png b/gui/devices/1080x1920/res/images/indeterminate004.png
new file mode 100644
index 0000000..5ff7734
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/indeterminate005.png b/gui/devices/1080x1920/res/images/indeterminate005.png
new file mode 100644
index 0000000..7cc9abb
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/indeterminate006.png b/gui/devices/1080x1920/res/images/indeterminate006.png
new file mode 100644
index 0000000..9eed176
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/keyboard1.png b/gui/devices/1080x1920/res/images/keyboard1.png
new file mode 100644
index 0000000..79a8b69
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/keyboard2.png b/gui/devices/1080x1920/res/images/keyboard2.png
new file mode 100644
index 0000000..63a5b6a
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/keyboard3.png b/gui/devices/1080x1920/res/images/keyboard3.png
new file mode 100644
index 0000000..a646c75
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/keyboard4.png b/gui/devices/1080x1920/res/images/keyboard4.png
new file mode 100644
index 0000000..3b2905a
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/medium-button.png b/gui/devices/1080x1920/res/images/medium-button.png
new file mode 100644
index 0000000..a3bf30d
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/menu-button.png b/gui/devices/1080x1920/res/images/menu-button.png
new file mode 100644
index 0000000..36d3f01
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/minus-button.png b/gui/devices/1080x1920/res/images/minus-button.png
new file mode 100644
index 0000000..e2edbe4
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/plus-button.png b/gui/devices/1080x1920/res/images/plus-button.png
new file mode 100644
index 0000000..b67d9b9
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/progress_empty.png b/gui/devices/1080x1920/res/images/progress_empty.png
new file mode 100644
index 0000000..d4ea0c2
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/progress_fill.png b/gui/devices/1080x1920/res/images/progress_fill.png
new file mode 100644
index 0000000..836e437
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/radio_empty.png b/gui/devices/1080x1920/res/images/radio_empty.png
new file mode 100644
index 0000000..9c2c66b
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/radio_selected.png b/gui/devices/1080x1920/res/images/radio_selected.png
new file mode 100644
index 0000000..8d05b9d
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/slideout.png b/gui/devices/1080x1920/res/images/slideout.png
new file mode 100644
index 0000000..56a9ac2
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/slider-touch.png b/gui/devices/1080x1920/res/images/slider-touch.png
new file mode 100644
index 0000000..b3365af
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/slider-used.png b/gui/devices/1080x1920/res/images/slider-used.png
new file mode 100644
index 0000000..229d910
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/slider.png b/gui/devices/1080x1920/res/images/slider.png
new file mode 100644
index 0000000..1e034a6
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/slider.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/sort-button.png b/gui/devices/1080x1920/res/images/sort-button.png
new file mode 100644
index 0000000..c6783a1
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/top-bar.jpg b/gui/devices/1080x1920/res/images/top-bar.jpg
new file mode 100644
index 0000000..5121a03
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/1080x1920/res/images/unlock.png b/gui/devices/1080x1920/res/images/unlock.png
new file mode 100644
index 0000000..dc3894b
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/1080x1920/res/ui.xml b/gui/devices/1080x1920/res/ui.xml
new file mode 100644
index 0000000..1367015
--- /dev/null
+++ b/gui/devices/1080x1920/res/ui.xml
@@ -0,0 +1,426 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="1080" height="1920" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Condensed-40" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Condensed-40" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Condensed-40" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="10" />
+		<variable name="col2_x" value="565" />
+		<variable name="col_center_x" value="288" />
+		<variable name="col_center_medium_x" value="414" />
+		<variable name="center_x" value="540" />
+		<variable name="row1_y" value="255" />
+		<variable name="row2_y" value="615" />
+		<variable name="row3_y" value="975" />
+		<variable name="row4_y" value="1335" />
+		<variable name="col1_center_x" value="179" />
+		<variable name="col2_center_x" value="552" />
+		<variable name="row1_text2_y" value="310" />
+		<variable name="row2_text2_y" value="550" />
+		<variable name="row_queue_y" value="1140" />
+		<variable name="row1_header_y" value="180" />
+		<variable name="row1_text_y" value="255" />
+		<variable name="row2_text_y" value="330" />
+		<variable name="row3_text_y" value="405" />
+		<variable name="row4_text_y" value="480" />
+		<variable name="row5_text_y" value="555" />
+		<variable name="row6_text_y" value="630" />
+		<variable name="row7_text_y" value="705" />
+		<variable name="row8_text_y" value="780" />
+		<variable name="row9_text_y" value="855" />
+		<variable name="row10_text_y" value="930" />
+		<variable name="row11_text_y" value="1005" />
+		<variable name="row12_text_y" value="1080" />
+		<variable name="row13_text_y" value="1155" />
+		<variable name="row14_text_y" value="1230" />
+		<variable name="row15_text_y" value="1305" />
+		<variable name="row16_text_y" value="1380" />
+		<variable name="row17_text_y" value="1455" />
+		<variable name="row18_text_y" value="1530" />
+		<variable name="zip_status_y" value="922" />
+		<variable name="tz_selected_y" value="240" />
+		<variable name="tz_set_y" value="1500" />
+		<variable name="tz_current_y" value="1425" />
+		<variable name="col_progressbar_x" value="351" />
+		<variable name="row_progressbar_y" value="1650" />
+		<variable name="col1_medium_x" value="10" />
+		<variable name="col2_medium_x" value="282" />
+		<variable name="col3_medium_x" value="545" />
+		<variable name="col4_medium_x" value="817" />
+		<variable name="row1_medium_y" value="195" />
+		<variable name="row2_medium_y" value="345" />
+		<variable name="row3_medium_y" value="392" />
+		<variable name="row4_medium_y" value="645" />
+		<variable name="row5_medium_y" value="795" />
+		<variable name="row6_medium_y" value="1260" />
+		<variable name="row7_medium_y" value="730" />
+		<variable name="slider_x" value="101" />
+		<variable name="slider_y" value="1575" />
+		<variable name="slider_text_y" value="1676" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="10" />
+		<variable name="home_button_y" value="1839" />
+		<variable name="back_button_x" value="944" />
+		<variable name="back_button_y" value="1839" />
+		<variable name="sort_text_x" value="10" />
+		<variable name="sort_asc_text_y" value="1635" />
+		<variable name="sort_asc_button_y" value="1620" />
+		<variable name="sort_desc_text_y" value="1725" />
+		<variable name="sort_desc_button_y" value="1710" />
+		<variable name="sort_col1_button_x" value="390" />
+		<variable name="sort_col2_button_x" value="540" />
+		<variable name="sort_col3_button_x" value="690" />
+		<variable name="input_width" value="1060" />
+		<variable name="input_height" value="75" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="4" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="1080" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="705" />
+		<variable name="console_install_height" value="900" />
+		<variable name="console_installdone_height" value="660" />
+		<variable name="fileselector_x" value="5" />
+		<variable name="fileselector_width" value="1070" />
+		<variable name="fileselector_install_height" value="1170" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="4" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="3" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="48" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="90" />
+		<variable name="fastscroll_linew" value="3" />
+		<variable name="fastscroll_rectw" value="60" />
+		<variable name="fastscroll_recth" value="105" />
+		<variable name="listbox_x" value="5" />
+		<variable name="listbox_width" value="1070" />
+		<variable name="listbox_tz_height" value="885" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="24" />
+		<variable name="sd_plus_x" value="525" />
+		<variable name="sdext_text_x" value="165" />
+		<variable name="sdext_text_y" value="270" />
+		<variable name="sdswap_button_y" value="390" />
+		<variable name="sdswap_text_x" value="165" />
+		<variable name="sdswap_text_y" value="405" />
+		<variable name="sdfilesystem_text_y" value="510" />
+		<variable name="sdfilesystem_button_y" value="570" />
+		<variable name="lock_x" value="240" />
+		<variable name="lock_y" value="600" />
+		<variable name="filemanager_select_x" value="840" />
+		<variable name="filemanager_select_y" value="1620" />
+		<variable name="backup_name_y" value="825" />
+		<variable name="terminal_console_height" value="1050" />
+		<variable name="terminal_text_y" value="1095" />
+		<variable name="terminal_button_y" value="1050" />
+		<variable name="row_dst_text_y" value="1080" />
+		<variable name="row_offset_text_y" value="1155" />
+		<variable name="row_offset_medium_y" value="1260" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="1060" />
+		<variable name="button_fill_main_width" value="505" />
+		<variable name="button_fill_main_height" value="324" />
+		<variable name="button_fill_half_height" value="162" />
+		<variable name="button_fill_quarter_height" value="81" />
+		<variable name="backup_list_height" value="780" />
+		<variable name="backup_button_row1" value="1118" />
+		<variable name="backup_button_row2" value="1220" />
+		<variable name="mount_list_height" value="1035" />
+		<variable name="mount_storage_row" value="1240" />
+		<variable name="storage_list_height" value="1000" />
+		<variable name="wipe_list_height" value="1105" />
+		<variable name="wipe_button_row1" value="1350" />
+		<variable name="wipe_button_y" value="975" />
+		<variable name="slidervalue_w" value="1060" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="3" />
+		<variable name="slidervalue_padding" value="30" />
+		<variable name="slidervalue_sliderw" value="15" />
+		<variable name="slidervalue_sliderh" value="90" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="180" y="15" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="180" y="60" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="180" y="114" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="405" y="114" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="760" y="114" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="0" y="%row2_y%" w="1080" h="705" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="500" y="1841" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="1841" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="1200" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="161" width="108" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="162:a" long01="@" key02="s" long02="#" key03="d" long03="$" key04="f" long04="%" key05="g" long05="&" key06="h" long06="*" key07="j" long07="-" key08="k" long08="+" key09="162:l" long09="_" />
+					<row3 key01="162:layout2" key02="z" long02="!" key03="x" key04="c" long04="'" key05="v" long05=":" key06="b" long06=";" key07="n" long07="/" key08="m" long08="?" key09="162:c:8" />
+					<row4 key01="162:layout3" key02="108:c:47" key03="108:" key04="432: " key05="." key06="162:action" />
+				</layout1>
+				<layout2>
+					<keysize height="161" width="108" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="162:A" long01="@" key02="S" long02="#" key03="D" long03="$" key04="F" long04="%" key05="G" long05="&" key06="H" long06="*" key07="J" long07="-" key08="K" long08="+" key09="162:L" long09="_" />
+					<row3 key01="162:layout1" key02="Z" long02="!" key03="X" key04="C" long04="'" key05="V" long05=":" key06="B" long06=";" key07="N" long07="/" key08="M" long08="?" key09="162:c:8" />
+					<row4 key01="162:layout3" key02="/" key03="108:" key04="432: " key05="." key06="162:action" />
+				</layout2>
+				<layout3>
+					<keysize height="161" width="108" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="162:layout4" key02="!" key03="108:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="162:c:8" />
+					<row4 key01="162:layout1" key02="," key03="108:" key04="432: " key05="." key06="162:action" />
+				</layout3>
+				<layout4>
+					<keysize height="161" width="108" />
+					<row1 key01="~" key02="`" key03="|" key04="108:" key05="108:" key06="108:" key07="%" key08="108:" key09="{" key10="}" />
+					<row2 key01="108:" key02="108:" key03="108:" key04="108:" key05="108:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="162:layout3" key02="108:" key03="108:" key04="108:" key05="108:" key06="\" key07="<" key08=">" key09="162:c:8" />
+					<row4 key01="162:layout1" key02="108:c:34" key03="108:" key04="432: " key05="." key06="162:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/1200x1920/res/fonts/Roboto-Condensed-40.dat b/gui/devices/1200x1920/res/fonts/Roboto-Condensed-40.dat
new file mode 100644
index 0000000..ff23add
--- /dev/null
+++ b/gui/devices/1200x1920/res/fonts/Roboto-Condensed-40.dat
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/back-icon.png b/gui/devices/1200x1920/res/images/back-icon.png
new file mode 100644
index 0000000..85c6a5d
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/checkbox_checked.png b/gui/devices/1200x1920/res/images/checkbox_checked.png
new file mode 100644
index 0000000..65d0b34
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/checkbox_empty.png b/gui/devices/1200x1920/res/images/checkbox_empty.png
new file mode 100644
index 0000000..89b4c96
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/cursor.png b/gui/devices/1200x1920/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/curtain.jpg b/gui/devices/1200x1920/res/images/curtain.jpg
new file mode 100644
index 0000000..d2160ab
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/file.png b/gui/devices/1200x1920/res/images/file.png
new file mode 100644
index 0000000..a69b619
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/file.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/folder.png b/gui/devices/1200x1920/res/images/folder.png
new file mode 100644
index 0000000..fbea7a9
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/folder.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/home-icon.png b/gui/devices/1200x1920/res/images/home-icon.png
new file mode 100644
index 0000000..d6bc73d
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/indeterminate001.png b/gui/devices/1200x1920/res/images/indeterminate001.png
new file mode 100644
index 0000000..a205e91
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/indeterminate002.png b/gui/devices/1200x1920/res/images/indeterminate002.png
new file mode 100644
index 0000000..f777408
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/indeterminate003.png b/gui/devices/1200x1920/res/images/indeterminate003.png
new file mode 100644
index 0000000..cbb5974
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/indeterminate004.png b/gui/devices/1200x1920/res/images/indeterminate004.png
new file mode 100644
index 0000000..5ff7734
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/indeterminate005.png b/gui/devices/1200x1920/res/images/indeterminate005.png
new file mode 100644
index 0000000..7cc9abb
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/indeterminate006.png b/gui/devices/1200x1920/res/images/indeterminate006.png
new file mode 100644
index 0000000..9eed176
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/keyboard1.png b/gui/devices/1200x1920/res/images/keyboard1.png
new file mode 100644
index 0000000..d746933
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/keyboard2.png b/gui/devices/1200x1920/res/images/keyboard2.png
new file mode 100644
index 0000000..23b46ab
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/keyboard3.png b/gui/devices/1200x1920/res/images/keyboard3.png
new file mode 100644
index 0000000..56ed4df
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/keyboard4.png b/gui/devices/1200x1920/res/images/keyboard4.png
new file mode 100644
index 0000000..b40d6c2
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/medium-button.png b/gui/devices/1200x1920/res/images/medium-button.png
new file mode 100644
index 0000000..a3bf30d
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/menu-button.png b/gui/devices/1200x1920/res/images/menu-button.png
new file mode 100644
index 0000000..7b48be3
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/minus-button.png b/gui/devices/1200x1920/res/images/minus-button.png
new file mode 100644
index 0000000..e2edbe4
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/plus-button.png b/gui/devices/1200x1920/res/images/plus-button.png
new file mode 100644
index 0000000..b67d9b9
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/progress_empty.png b/gui/devices/1200x1920/res/images/progress_empty.png
new file mode 100644
index 0000000..d4ea0c2
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/progress_fill.png b/gui/devices/1200x1920/res/images/progress_fill.png
new file mode 100644
index 0000000..836e437
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/radio_empty.png b/gui/devices/1200x1920/res/images/radio_empty.png
new file mode 100644
index 0000000..9c2c66b
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/radio_selected.png b/gui/devices/1200x1920/res/images/radio_selected.png
new file mode 100644
index 0000000..8d05b9d
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/slideout.png b/gui/devices/1200x1920/res/images/slideout.png
new file mode 100644
index 0000000..56a9ac2
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/slider-touch.png b/gui/devices/1200x1920/res/images/slider-touch.png
new file mode 100644
index 0000000..b3365af
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/slider-used.png b/gui/devices/1200x1920/res/images/slider-used.png
new file mode 100644
index 0000000..229d910
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/slider.png b/gui/devices/1200x1920/res/images/slider.png
new file mode 100644
index 0000000..1e034a6
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/slider.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/sort-button.png b/gui/devices/1200x1920/res/images/sort-button.png
new file mode 100644
index 0000000..c6783a1
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/top-bar.jpg b/gui/devices/1200x1920/res/images/top-bar.jpg
new file mode 100644
index 0000000..5d35f29
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/1200x1920/res/images/unlock.png b/gui/devices/1200x1920/res/images/unlock.png
new file mode 100644
index 0000000..dc3894b
--- /dev/null
+++ b/gui/devices/1200x1920/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/1200x1920/res/ui.xml b/gui/devices/1200x1920/res/ui.xml
new file mode 100644
index 0000000..77af05b
--- /dev/null
+++ b/gui/devices/1200x1920/res/ui.xml
@@ -0,0 +1,429 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="1200" height="1920" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Condensed-40" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Condensed-40" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Condensed-40" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="10" />
+		<variable name="col2_x" value="624" />
+		<variable name="col_center_x" value="317" />
+		<variable name="col_center_medium_x" value="473" />
+		<variable name="center_x" value="600" />
+		<variable name="row1_y" value="255" />
+		<variable name="row2_y" value="615" />
+		<variable name="row3_y" value="975" />
+		<variable name="row4_y" value="1335" />
+
+		<!-- these two are unused? -->
+		<variable name="col1_center_x" value="179" />
+		<variable name="col2_center_x" value="552" />
+
+		<variable name="row1_text2_y" value="310" />
+		<variable name="row2_text2_y" value="550" />
+		<variable name="row_queue_y" value="1140" />
+		<variable name="row1_header_y" value="180" />
+		<variable name="row1_text_y" value="255" />
+		<variable name="row2_text_y" value="330" />
+		<variable name="row3_text_y" value="405" />
+		<variable name="row4_text_y" value="480" />
+		<variable name="row5_text_y" value="555" />
+		<variable name="row6_text_y" value="630" />
+		<variable name="row7_text_y" value="705" />
+		<variable name="row8_text_y" value="780" />
+		<variable name="row9_text_y" value="855" />
+		<variable name="row10_text_y" value="930" />
+		<variable name="row11_text_y" value="1005" />
+		<variable name="row12_text_y" value="1080" />
+		<variable name="row13_text_y" value="1155" />
+		<variable name="row14_text_y" value="1230" />
+		<variable name="row15_text_y" value="1305" />
+		<variable name="row16_text_y" value="1380" />
+		<variable name="row17_text_y" value="1455" />
+		<variable name="row18_text_y" value="1530" />
+		<variable name="zip_status_y" value="922" />
+		<variable name="tz_selected_y" value="240" />
+		<variable name="tz_set_y" value="1500" />
+		<variable name="tz_current_y" value="1425" />
+		<variable name="col_progressbar_x" value="411" />
+		<variable name="row_progressbar_y" value="1650" />
+		<variable name="col1_medium_x" value="10" />
+		<variable name="col2_medium_x" value="318" />
+		<variable name="col3_medium_x" value="626" />
+		<variable name="col4_medium_x" value="934" />
+		<variable name="row1_medium_y" value="195" />
+		<variable name="row2_medium_y" value="345" />
+		<variable name="row3_medium_y" value="392" />
+		<variable name="row4_medium_y" value="645" />
+		<variable name="row5_medium_y" value="795" />
+		<variable name="row6_medium_y" value="1260" />
+		<variable name="row7_medium_y" value="730" />
+		<variable name="slider_x" value="161" />
+		<variable name="slider_y" value="1575" />
+		<variable name="slider_text_y" value="1676" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="10" />
+		<variable name="home_button_y" value="1839" />
+		<variable name="back_button_x" value="1064" />
+		<variable name="back_button_y" value="1839" />
+		<variable name="sort_text_x" value="10" />
+		<variable name="sort_asc_text_y" value="1635" />
+		<variable name="sort_asc_button_y" value="1620" />
+		<variable name="sort_desc_text_y" value="1725" />
+		<variable name="sort_desc_button_y" value="1710" />
+		<variable name="sort_col1_button_x" value="390" />
+		<variable name="sort_col2_button_x" value="540" />
+		<variable name="sort_col3_button_x" value="690" />
+		<variable name="input_width" value="1180" />
+		<variable name="input_height" value="75" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="4" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="1200" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="705" />
+		<variable name="console_install_height" value="900" />
+		<variable name="console_installdone_height" value="660" />
+		<variable name="fileselector_x" value="5" />
+		<variable name="fileselector_width" value="1190" />
+		<variable name="fileselector_install_height" value="1170" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="4" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="3" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="48" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="90" />
+		<variable name="fastscroll_linew" value="3" />
+		<variable name="fastscroll_rectw" value="60" />
+		<variable name="fastscroll_recth" value="105" />
+		<variable name="listbox_x" value="5" />
+		<variable name="listbox_width" value="1190" />
+		<variable name="listbox_tz_height" value="885" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="24" />
+		<variable name="sd_plus_x" value="525" />
+		<variable name="sdext_text_x" value="165" />
+		<variable name="sdext_text_y" value="270" />
+		<variable name="sdswap_button_y" value="390" />
+		<variable name="sdswap_text_x" value="165" />
+		<variable name="sdswap_text_y" value="405" />
+		<variable name="sdfilesystem_text_y" value="510" />
+		<variable name="sdfilesystem_button_y" value="570" />
+		<variable name="lock_x" value="240" />
+		<variable name="lock_y" value="600" />
+		<variable name="filemanager_select_x" value="840" />
+		<variable name="filemanager_select_y" value="1620" />
+		<variable name="backup_name_y" value="825" />
+		<variable name="terminal_console_height" value="980" />
+		<variable name="terminal_text_y" value="1025" />
+		<variable name="terminal_button_y" value="1050" />
+		<variable name="row_dst_text_y" value="1080" />
+		<variable name="row_offset_text_y" value="1155" />
+		<variable name="row_offset_medium_y" value="1260" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="1180" />
+		<variable name="button_fill_main_width" value="565" />
+		<variable name="button_fill_main_height" value="324" />
+		<variable name="button_fill_half_height" value="162" />
+		<variable name="button_fill_quarter_height" value="81" />
+		<variable name="backup_list_height" value="780" />
+		<variable name="backup_button_row1" value="1118" />
+		<variable name="backup_button_row2" value="1220" />
+		<variable name="mount_list_height" value="1035" />
+		<variable name="mount_storage_row" value="1240" />
+		<variable name="storage_list_height" value="1000" />
+		<variable name="wipe_list_height" value="1105" />
+		<variable name="wipe_button_row1" value="1350" />
+		<variable name="wipe_button_y" value="975" />
+		<variable name="slidervalue_w" value="1180" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="3" />
+		<variable name="slidervalue_padding" value="30" />
+		<variable name="slidervalue_sliderw" value="15" />
+		<variable name="slidervalue_sliderh" value="90" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="180" y="15" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="180" y="60" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="180" y="114" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="405" y="114" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="730" y="114" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="0" y="%row2_y%" w="1200" h="705" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="540" y="1841" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="1841" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="1129" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="179" width="120" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="180:a" long01="@" key02="s" long02="#" key03="d" long03="$" key04="f" long04="%" key05="g" long05="&" key06="h" long06="*" key07="j" long07="-" key08="k" long08="+" key09="162:l" long09="_" />
+					<row3 key01="180:layout2" key02="z" long02="!" key03="x" key04="c" long04="'" key05="v" long05=":" key06="b" long06=";" key07="n" long07="/" key08="m" long08="?" key09="182:c:8" />
+					<row4 key01="180:layout3" key02="120:c:47" key03="600: " key04="." key05="180:action" />
+				</layout1>
+				<layout2>
+					<keysize height="179" width="120" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="180:A" long01="@" key02="S" long02="#" key03="D" long03="$" key04="F" long04="%" key05="G" long05="&" key06="H" long06="*" key07="J" long07="-" key08="K" long08="+" key09="162:L" long09="_" />
+					<row3 key01="180:layout1" key02="Z" long02="!" key03="X" key04="C" long04="'" key05="V" long05=":" key06="B" long06=";" key07="N" long07="/" key08="M" long08="?" key09="180:c:8" />
+					<row4 key01="180:layout3" key02="120:c:47" key03="600: " key04="." key05="180:action" />
+				</layout2>
+				<layout3>
+					<keysize height="179" width="120" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="180:layout4" key02="!" key03="120:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="180:c:8" />
+					<row4 key01="180:layout1" key02="," key03="600: " key04="." key05="180:action" />
+				</layout3>
+				<layout4>
+					<keysize height="179" width="120" />
+					<row1 key01="~" key02="`" key03="|" key04="120:" key05="120:" key06="120:" key07="%" key08="120:" key09="{" key10="}" />
+					<row2 key01="120:" key02="120:" key03="120:" key04="120:" key05="120:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="180:layout3" key02="120:" key03="120:" key04="120:" key05="120:" key06="\" key07="<" key08=">" key09="180:c:8" />
+					<row4 key01="180:layout1" key02="120:c:34" key03="600: " key04="." key05="180:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/1280x800/res/fonts/Roboto-Regular-20.dat b/gui/devices/1280x800/res/fonts/Roboto-Regular-20.dat
new file mode 100755
index 0000000..6588b41
--- /dev/null
+++ b/gui/devices/1280x800/res/fonts/Roboto-Regular-20.dat
Binary files differ
diff --git a/gui/devices/1280x800/res/images/back-icon.png b/gui/devices/1280x800/res/images/back-icon.png
new file mode 100644
index 0000000..1096c90
--- /dev/null
+++ b/gui/devices/1280x800/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/background.jpg b/gui/devices/1280x800/res/images/background.jpg
new file mode 100644
index 0000000..d9681c6
--- /dev/null
+++ b/gui/devices/1280x800/res/images/background.jpg
Binary files differ
diff --git a/gui/devices/1280x800/res/images/button.png b/gui/devices/1280x800/res/images/button.png
new file mode 100644
index 0000000..6ae29ae
--- /dev/null
+++ b/gui/devices/1280x800/res/images/button.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/checkbox_checked.png b/gui/devices/1280x800/res/images/checkbox_checked.png
new file mode 100644
index 0000000..3447349
--- /dev/null
+++ b/gui/devices/1280x800/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/checkbox_empty.png b/gui/devices/1280x800/res/images/checkbox_empty.png
new file mode 100644
index 0000000..f5f35d8
--- /dev/null
+++ b/gui/devices/1280x800/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/console-icon.png b/gui/devices/1280x800/res/images/console-icon.png
new file mode 100644
index 0000000..91a727d
--- /dev/null
+++ b/gui/devices/1280x800/res/images/console-icon.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/console-toggle.png b/gui/devices/1280x800/res/images/console-toggle.png
new file mode 100644
index 0000000..963b9fd
--- /dev/null
+++ b/gui/devices/1280x800/res/images/console-toggle.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/cursor.png b/gui/devices/1280x800/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/1280x800/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/curtain.jpg b/gui/devices/1280x800/res/images/curtain.jpg
new file mode 100644
index 0000000..7646e3e
--- /dev/null
+++ b/gui/devices/1280x800/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/1280x800/res/images/file.png b/gui/devices/1280x800/res/images/file.png
new file mode 100644
index 0000000..8556bc7
--- /dev/null
+++ b/gui/devices/1280x800/res/images/file.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/folder.png b/gui/devices/1280x800/res/images/folder.png
new file mode 100644
index 0000000..a3a5f69
--- /dev/null
+++ b/gui/devices/1280x800/res/images/folder.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/home-icon.png b/gui/devices/1280x800/res/images/home-icon.png
new file mode 100644
index 0000000..e42d774
--- /dev/null
+++ b/gui/devices/1280x800/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/indeterminate001.png b/gui/devices/1280x800/res/images/indeterminate001.png
new file mode 100755
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/1280x800/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/indeterminate002.png b/gui/devices/1280x800/res/images/indeterminate002.png
new file mode 100755
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/1280x800/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/indeterminate003.png b/gui/devices/1280x800/res/images/indeterminate003.png
new file mode 100755
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/1280x800/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/indeterminate004.png b/gui/devices/1280x800/res/images/indeterminate004.png
new file mode 100755
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/1280x800/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/indeterminate005.png b/gui/devices/1280x800/res/images/indeterminate005.png
new file mode 100755
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/1280x800/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/indeterminate006.png b/gui/devices/1280x800/res/images/indeterminate006.png
new file mode 100755
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/1280x800/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/keyboard1.png b/gui/devices/1280x800/res/images/keyboard1.png
new file mode 100644
index 0000000..d75550f
--- /dev/null
+++ b/gui/devices/1280x800/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/keyboard2.png b/gui/devices/1280x800/res/images/keyboard2.png
new file mode 100644
index 0000000..cf2eb8f
--- /dev/null
+++ b/gui/devices/1280x800/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/keyboard3.png b/gui/devices/1280x800/res/images/keyboard3.png
new file mode 100644
index 0000000..c8f7b03
--- /dev/null
+++ b/gui/devices/1280x800/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/keyboard4.png b/gui/devices/1280x800/res/images/keyboard4.png
new file mode 100644
index 0000000..7dd3987
--- /dev/null
+++ b/gui/devices/1280x800/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/medium-button.png b/gui/devices/1280x800/res/images/medium-button.png
new file mode 100644
index 0000000..77dc540
--- /dev/null
+++ b/gui/devices/1280x800/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/mediumwide-button.png b/gui/devices/1280x800/res/images/mediumwide-button.png
new file mode 100644
index 0000000..b2b7d4d
--- /dev/null
+++ b/gui/devices/1280x800/res/images/mediumwide-button.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/minus-button.png b/gui/devices/1280x800/res/images/minus-button.png
new file mode 100644
index 0000000..5a49c75
--- /dev/null
+++ b/gui/devices/1280x800/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/plus-button.png b/gui/devices/1280x800/res/images/plus-button.png
new file mode 100644
index 0000000..da1326c
--- /dev/null
+++ b/gui/devices/1280x800/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/progress_empty.png b/gui/devices/1280x800/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/1280x800/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/progress_fill.png b/gui/devices/1280x800/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/1280x800/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/radio_empty.png b/gui/devices/1280x800/res/images/radio_empty.png
new file mode 100644
index 0000000..88d1c1f
--- /dev/null
+++ b/gui/devices/1280x800/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/radio_selected.png b/gui/devices/1280x800/res/images/radio_selected.png
new file mode 100644
index 0000000..864065d
--- /dev/null
+++ b/gui/devices/1280x800/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/slider-touch.png b/gui/devices/1280x800/res/images/slider-touch.png
new file mode 100644
index 0000000..d8647b8
--- /dev/null
+++ b/gui/devices/1280x800/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/slider-used.png b/gui/devices/1280x800/res/images/slider-used.png
new file mode 100644
index 0000000..bf6cad9
--- /dev/null
+++ b/gui/devices/1280x800/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/slider.png b/gui/devices/1280x800/res/images/slider.png
new file mode 100644
index 0000000..4081ea5
--- /dev/null
+++ b/gui/devices/1280x800/res/images/slider.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/sort-button.png b/gui/devices/1280x800/res/images/sort-button.png
new file mode 100644
index 0000000..13ab929
--- /dev/null
+++ b/gui/devices/1280x800/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/1280x800/res/images/unlock.png b/gui/devices/1280x800/res/images/unlock.png
new file mode 100644
index 0000000..9041221
--- /dev/null
+++ b/gui/devices/1280x800/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/1280x800/res/ui.xml b/gui/devices/1280x800/res/ui.xml
new file mode 100644
index 0000000..716dadc
--- /dev/null
+++ b/gui/devices/1280x800/res/ui.xml
@@ -0,0 +1,439 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="1280" height="800" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="landscape.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="base" type="image" filename="background.jpg" />
+		<resource name="main_button" type="image" filename="button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="mediumwide_button" type="image" filename="mediumwide-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="console_button" type="image" filename="console-toggle" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="151" />
+		<variable name="col2_x" value="397" />
+		<variable name="col3_x" value="643" />
+		<variable name="col4_x" value="889" />
+		<variable name="row1_y" value="127" />
+		<variable name="row2_y" value="388" />
+		<variable name="col_center_x" value="520" />
+		<variable name="center_x" value="640" />
+		<variable name="screen_width" value="1280" />
+		<variable name="screen_height" value="800" />
+		<variable name="col_progressbar_x" value="514" />
+		<variable name="row_progressbar_y" value="700" />
+		<variable name="col1_medium_x" value="385" />
+		<variable name="col2_medium_x" value="515" />
+		<variable name="col3_medium_x" value="645" />
+		<variable name="col4_medium_x" value="775" />
+		<variable name="row1_medium_y" value="105" />
+		<variable name="row2_medium_y" value="245" />
+		<variable name="row3_medium_y" value="245" />
+		<variable name="row4_medium_y" value="450" />
+		<variable name="row5_medium_y" value="405" />
+		<variable name="row1_text_y" value="58" />
+		<variable name="row2_text_y" value="105" />
+		<variable name="row3_text_y" value="140" />
+		<variable name="row4_text_y" value="170" />
+		<variable name="row5_text_y" value="200" />
+		<variable name="row6_text_y" value="230" />
+		<variable name="row7_text_y" value="260" />
+		<variable name="row8_text_y" value="290" />
+		<variable name="row9_text_y" value="320" />
+		<variable name="row10_text_y" value="350" />
+		<variable name="row11_text_y" value="380" />
+		<variable name="row12_text_y" value="410" />
+		<variable name="row13_text_y" value="440" />
+		<variable name="row14_text_y" value="470" />
+		<variable name="row15_text_y" value="500" />
+		<variable name="row16_text_y" value="530" />
+		<variable name="row17_text_y" value="560" />
+		<variable name="row18_text_y" value="590" />
+		<variable name="row_offsetmedium_y" value="465" />
+		<variable name="home_button_x" value="1069" />
+		<variable name="home_button_y" value="5" />
+		<variable name="back_button_x" value="1139" />
+		<variable name="back_button_y" value="5" />
+		<variable name="console_button_x" value="1209" />
+		<variable name="console_button_y" value="5" />
+		<variable name="nandcheck_col1" value="328" />
+		<variable name="nandcheck_col2" value="600" />
+		<variable name="nandcheck_row1" value="150" />
+		<variable name="nandcheck_row2" value="200" />
+		<variable name="nandcheck_row3" value="250" />
+		<variable name="nandcheck_row4" value="300" />
+		<variable name="nandcheck_row5" value="350" />
+		<variable name="nandcheck_row6" value="400" />
+		<variable name="nandcheck_row7" value="450" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#A0A0A0" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="slider_x" value="435" />
+		<variable name="slider_y" value="600" />
+		<variable name="slider_text_y" value="650" />
+		<variable name="sort_text_x" value="398" />
+		<variable name="sort_asc_text_y" value="655" />
+		<variable name="sort_asc_button_y" value="650" />
+		<variable name="sort_desc_text_y" value="700" />
+		<variable name="sort_desc_button_y" value="695" />
+		<variable name="sort_col1_button_x" value="568" />
+		<variable name="sort_col2_button_x" value="638" />
+		<variable name="sort_col3_button_x" value="708" />
+		<variable name="col1_sdext_x" value="508" />
+		<variable name="col2_sdext_x" value="718" />
+		<variable name="row1_sdext_y" value="115" />
+		<variable name="row2_sdext_y" value="180" />
+		<variable name="row_extsize_y" value="115" />
+		<variable name="row_swapsize_y" value="180" />
+		<variable name="input_x" value="28" />
+		<variable name="input_width" value="1200" />
+		<variable name="input_height" value="30" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="25" />
+		<variable name="console_width" value="1230" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="320" />
+		<variable name="console_install_height" value="440" />
+		<variable name="console_installdone_height" value="300" />
+		<variable name="fileselector_folder_x" value="28" />
+		<variable name="fileselector_folder_width" value="410" />
+		<variable name="fileselector_folderonly_width" value="588" />
+		<variable name="fileselector_file_x" value="445" />
+		<variable name="fileselector_file_width" value="810" />
+		<variable name="fileselector_install_y" value="130" />
+		<variable name="fileselector_install_height" value="510" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="18" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="40" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="30" />
+		<variable name="fastscroll_recth" value="50" />
+		<variable name="zipstorage_text_y" value="88" />
+		<variable name="listbox_x" value="397" />
+		<variable name="listbox_y" value="90" />
+		<variable name="listbox_width" value="588" />
+		<variable name="listbox_tz_height" value="290" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="18" />
+		<variable name="sd_plus_x" value="408" />
+		<variable name="lock_x" value="440" />
+		<variable name="lock_y" value="184" />
+		<variable name="filemanager_select_x" value="1017" />
+		<variable name="filemanager_select_y" value="620" />
+		<variable name="backup_name_text_y" value="440" />
+		<variable name="backup_name_button_y" value="240" />
+		<variable name="col_right_x" value="1252" />
+		<variable name="cancel_button_y" value="240" />
+		<variable name="terminal_console_y" value="0" />
+		<variable name="terminal_console_height" value="362" />
+		<variable name="terminal_text_y" value="368" />
+		<variable name="terminal_button_y" value="358" />
+		<variable name="terminal_input_width" value="1031" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="640" />
+		<variable name="button_fill_main_width" value="609" />
+		<variable name="button_fill_main_height" value="160" />
+		<variable name="button_fill_half_height" value="53" />
+		<variable name="button_fill_quarter_height" value="40" />
+		<variable name="button_full_center_x" value="320" />
+		<variable name="backup_list_x" value="28" />
+		<variable name="backup_list_y" value="105" />
+		<variable name="backup_list_width" value="609" />
+		<variable name="backup_list_height" value="440" />
+		<variable name="backup_storage_y" value="270" />
+		<variable name="backup_encrypt_y" value="330" />
+		<variable name="restore_list_y" value="140" />
+		<variable name="restore_list_height" value="400" />
+		<variable name="mount_list_height" value="600" />
+		<variable name="mount_storage_row" value="550" />
+		<variable name="wipe_list_height" value="420" />
+		<variable name="wipe_button_y" value="250" />
+		<variable name="slidervalue_x" value="320" />
+		<variable name="slidervalue_w" value="640" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="2" />
+		<variable name="slidervalue_padding" value="0" />
+		<variable name="slidervalue_sliderw" value="15" />
+		<variable name="slidervalue_sliderh" value="60" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="base" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="250" y="5" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="250" y="30" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery Level: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="450" y="30" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="850" y="30" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="550" y="30" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="flash_zip_console">
+			<object type="console">
+				<placement x="%console_x%" y="85" w="%console_width%" h="430" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="200" w="%console_width%" h="380" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="console_button" x="%console_button_x%" y="%console_button_y%" />
+				<placement x="%console_x%" y="75" w="%console_width%" h="665" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="438" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="91" width="106" />
+					<row1 key01="102:" key02="q" long02="1" key03="w" long03="2" key04="e" long04="3" key05="r" long05="4" key06="t" long06="5" key07="y" long07="6" key08="u" long08="7" key09="i" long09="8" key10="o" long10="9" key11="p" long11="0" key12="118:c:8" />
+					<row2 key01="142:layout3" key02="104:a" key03="104:s" key04="104:d" key05="104:f" key06="104:g" key07="104:h" key08="104:j" key09="104:k" key10="104:l" key11="202:action" />
+					<row3 key01="176:layout2" key02="103:z" key03="103:x" key04="103:c" key05="103:v" key06="103:b" key07="103:n" key08="103:m" key09="103:," long09="!" key10="103:." long10="?" key11="177:layout2" />
+					<row4 key01="190:" key02=":" long02="+" key03="104:/" long03="@" key04="480: " key05="102:'" long05="102:c:34" key06="-" long06="_" />
+				</layout1>
+				<layout2>
+					<keysize height="90" width="106" capslock="0" revert_layout="1" />
+					<row1 key01="102:" key02="Q" long02="1" key03="W" long03="2" key04="E" long04="3" key05="R" long05="4" key06="T" long06="5" key07="Y" long07="6" key08="U" long08="7" key09="I" long09="8" key10="O" long10="9" key11="P" long11="0" key12="118:c:8" />
+					<row2 key01="142:layout3" key02="104:A" key03="104:S" key04="104:D" key05="104:F" key06="104:G" key07="104:H" key08="104:J" key09="104:K" key10="104:L" key11="202:action" />
+					<row3 key01="176:layout1" key02="103:Z" key03="103:X" key04="103:C" key05="103:V" key06="103:B" key07="103:N" key08="103:M" key09="103:," long09="!" key10="103:." long10="?" key11="177:layout1" />
+					<row4 key01="190:" key02=":" long02="+" key03="104:/" long03="@" key04="480: " key05="102:'" long05="102:c:34" key06="-" long06="_" />
+				</layout2>
+				<layout3>
+					<keysize height="90" width="106" />
+					<row1 key01="102:" key02="1" key03="2" key04="3" key05="4" key06="5" key07="6" key08="7" key09="8" key10="9" key11="0" key12="118:c:8" />
+					<row2 key01="142:layout1" key02="104:#" key03="104:$" key04="104:%" key05="104:&" key06="104:*" key07="104:-" key08="104:+" key09="104:(" key10="104:)" key11="202:action" />
+					<row3 key01="176:layout4" key02="103:<" key03="103:>" key04="103:=" key05="103:'" key06="103:;" key07="103:," key08="103:." key09="103:!" key10="103:?" key11="177:layout4" />
+					<row4 key01="190:" key02="/" key03="104:@" key04="480: " key05="102:c:34" key06="_" />
+				</layout3>
+				<layout4>
+					<keysize height="91" width="106" />
+					<row1 key01="102:" key02="~" key03="`" key04="|" key05="106:" key06="106:" key07="106:" key08="106:" key09="106:" key10="106:" key11="106:" key12="118:c:8" />
+					<row2 key01="142:layout1" key02="104:" key03="104:" key04="104:" key05="104:" key06="104:^" key07="104:" key08="104:" key09="104:{" key10="104:}" key11="202:action" />
+					<row3 key01="176:layout3" key02="103:\" key03="103:" key04="103:" key05="103:" key06="103:" key07="103:[" key08="103:]" key09="103:!" key10="103:?" key11="177:layout3" />
+					<row4 key01="400:" key02="480: " />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/1440x2560/res/fonts/Roboto-Condensed-50.dat b/gui/devices/1440x2560/res/fonts/Roboto-Condensed-50.dat
new file mode 100644
index 0000000..ae9c0f6
--- /dev/null
+++ b/gui/devices/1440x2560/res/fonts/Roboto-Condensed-50.dat
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/back-icon.png b/gui/devices/1440x2560/res/images/back-icon.png
new file mode 100644
index 0000000..439ec19
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/checkbox_checked.png b/gui/devices/1440x2560/res/images/checkbox_checked.png
new file mode 100644
index 0000000..65d0b34
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/checkbox_empty.png b/gui/devices/1440x2560/res/images/checkbox_empty.png
new file mode 100644
index 0000000..89b4c96
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/cursor.png b/gui/devices/1440x2560/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/curtain.jpg b/gui/devices/1440x2560/res/images/curtain.jpg
new file mode 100644
index 0000000..c168882
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/file.png b/gui/devices/1440x2560/res/images/file.png
new file mode 100644
index 0000000..a69b619
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/file.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/folder.png b/gui/devices/1440x2560/res/images/folder.png
new file mode 100644
index 0000000..fbea7a9
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/folder.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/home-icon.png b/gui/devices/1440x2560/res/images/home-icon.png
new file mode 100644
index 0000000..b9e4039
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/indeterminate001.png b/gui/devices/1440x2560/res/images/indeterminate001.png
new file mode 100644
index 0000000..f5e1646
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/indeterminate002.png b/gui/devices/1440x2560/res/images/indeterminate002.png
new file mode 100644
index 0000000..c841fb4
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/indeterminate003.png b/gui/devices/1440x2560/res/images/indeterminate003.png
new file mode 100644
index 0000000..651f9e7
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/indeterminate004.png b/gui/devices/1440x2560/res/images/indeterminate004.png
new file mode 100644
index 0000000..21e9fca
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/indeterminate005.png b/gui/devices/1440x2560/res/images/indeterminate005.png
new file mode 100644
index 0000000..1dd0172
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/indeterminate006.png b/gui/devices/1440x2560/res/images/indeterminate006.png
new file mode 100644
index 0000000..4e5803b
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/keyboard1.png b/gui/devices/1440x2560/res/images/keyboard1.png
new file mode 100644
index 0000000..400f62d
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/keyboard2.png b/gui/devices/1440x2560/res/images/keyboard2.png
new file mode 100644
index 0000000..8b864a9
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/keyboard3.png b/gui/devices/1440x2560/res/images/keyboard3.png
new file mode 100644
index 0000000..be7ca47
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/keyboard4.png b/gui/devices/1440x2560/res/images/keyboard4.png
new file mode 100644
index 0000000..306c4b3
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/medium-button.png b/gui/devices/1440x2560/res/images/medium-button.png
new file mode 100644
index 0000000..a3bf30d
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/menu-button.png b/gui/devices/1440x2560/res/images/menu-button.png
new file mode 100644
index 0000000..d651055
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/minus-button.png b/gui/devices/1440x2560/res/images/minus-button.png
new file mode 100644
index 0000000..e2edbe4
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/plus-button.png b/gui/devices/1440x2560/res/images/plus-button.png
new file mode 100644
index 0000000..28fb825
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/progress_empty.png b/gui/devices/1440x2560/res/images/progress_empty.png
new file mode 100644
index 0000000..4d610c6
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/progress_fill.png b/gui/devices/1440x2560/res/images/progress_fill.png
new file mode 100644
index 0000000..3860c24
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/radio_empty.png b/gui/devices/1440x2560/res/images/radio_empty.png
new file mode 100644
index 0000000..9c2c66b
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/radio_selected.png b/gui/devices/1440x2560/res/images/radio_selected.png
new file mode 100644
index 0000000..8d05b9d
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/slideout.png b/gui/devices/1440x2560/res/images/slideout.png
new file mode 100644
index 0000000..a2f2be1
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/slider-touch.png b/gui/devices/1440x2560/res/images/slider-touch.png
new file mode 100644
index 0000000..f6dac62
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/slider-used.png b/gui/devices/1440x2560/res/images/slider-used.png
new file mode 100644
index 0000000..f18f8ce
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/slider.png b/gui/devices/1440x2560/res/images/slider.png
new file mode 100644
index 0000000..46fa928
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/slider.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/sort-button.png b/gui/devices/1440x2560/res/images/sort-button.png
new file mode 100644
index 0000000..c4f9edd
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/top-bar.jpg b/gui/devices/1440x2560/res/images/top-bar.jpg
new file mode 100755
index 0000000..93137b8
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/1440x2560/res/images/unlock.png b/gui/devices/1440x2560/res/images/unlock.png
new file mode 100644
index 0000000..dc3894b
--- /dev/null
+++ b/gui/devices/1440x2560/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/1440x2560/res/ui.xml b/gui/devices/1440x2560/res/ui.xml
new file mode 100644
index 0000000..79f12cf
--- /dev/null
+++ b/gui/devices/1440x2560/res/ui.xml
@@ -0,0 +1,426 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="1440" height="2560" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="50" fallback="Roboto-Condensed-50" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="50" fallback="Roboto-Condensed-50" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="50" fallback="Roboto-Condensed-50" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="0" />
+		<variable name="col2_x" value="780" />
+		<variable name="col_center_x" value="410" />
+		<variable name="col_center_medium_x" value="550" />
+		<variable name="center_x" value="720" />
+		<variable name="row1_y" value="339" />
+		<variable name="row2_y" value="915" />
+		<variable name="row3_y" value="1480" />
+		<variable name="row4_y" value="2052" />
+		<variable name="col1_center_x" value="238" />
+		<variable name="col2_center_x" value="734" />
+		<variable name="row1_text2_y" value="412" />
+		<variable name="row2_text2_y" value="732" />
+		<variable name="row_queue_y" value="1516" />
+		<variable name="row1_header_y" value="239" />
+		<variable name="row1_text_y" value="339" />
+		<variable name="row2_text_y" value="439" />
+		<variable name="row3_text_y" value="539" />
+		<variable name="row4_text_y" value="638" />
+		<variable name="row5_text_y" value="738" />
+		<variable name="row6_text_y" value="838" />
+		<variable name="row7_text_y" value="938" />
+		<variable name="row8_text_y" value="1037" />
+		<variable name="row9_text_y" value="1137" />
+		<variable name="row10_text_y" value="1237" />
+		<variable name="row11_text_y" value="1337" />
+		<variable name="row12_text_y" value="1436" />
+		<variable name="row13_text_y" value="1536" />
+		<variable name="row14_text_y" value="1636" />
+		<variable name="row15_text_y" value="1741" />
+		<variable name="row16_text_y" value="1835" />
+		<variable name="row17_text_y" value="1935" />
+		<variable name="row18_text_y" value="2035" />
+		<variable name="zip_status_y" value="1375" />
+		<variable name="tz_selected_y" value="319" />
+		<variable name="tz_set_y" value="1995" />
+		<variable name="tz_current_y" value="1895" />
+		<variable name="col_progressbar_x" value="`0" />
+		<variable name="row_progressbar_y" value="2195" />
+		<variable name="col1_medium_x" value="13" />
+		<variable name="col2_medium_x" value="370" />
+		<variable name="col3_medium_x" value="730" />
+		<variable name="col4_medium_x" value="1080" />
+		<variable name="row1_medium_y" value="259" />
+		<variable name="row2_medium_y" value="459" />
+		<variable name="row3_medium_y" value="521" />
+		<variable name="row4_medium_y" value="858" />
+		<variable name="row5_medium_y" value="1057" />
+		<variable name="row6_medium_y" value="1676" />
+		<variable name="row7_medium_y" value="971" />
+		<variable name="slider_x" value="40" />
+		<variable name="slider_y" value="2025" />
+		<variable name="slider_text_y" value="2149" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="40" />
+		<variable name="home_button_y" value="2446" />
+		<variable name="back_button_x" value="1250" />
+		<variable name="back_button_y" value="2446" />
+		<variable name="sort_text_x" value="13" />
+		<variable name="sort_asc_text_y" value="2175" />
+		<variable name="sort_asc_button_y" value="2155" />
+		<variable name="sort_desc_text_y" value="2294" />
+		<variable name="sort_desc_button_y" value="2274" />
+		<variable name="sort_col1_button_x" value="510" />
+		<variable name="sort_col2_button_x" value="720" />
+		<variable name="sort_col3_button_x" value="920" />
+		<variable name="input_width" value="1569" />
+		<variable name="input_height" value="100" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="4" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="1440" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="938" />
+		<variable name="console_install_height" value="1200" />
+		<variable name="console_installdone_height" value="950" />
+		<variable name="fileselector_x" value="6" />
+		<variable name="fileselector_width" value="1420" />
+		<variable name="fileselector_install_height" value="1556" />
+		<variable name="fileselector_header_background" value="#1c1c1c" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="5" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="4" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="64" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="120" />
+		<variable name="fastscroll_linew" value="4" />
+		<variable name="fastscroll_rectw" value="80" />
+		<variable name="fastscroll_recth" value="140" />
+		<variable name="listbox_x" value="6" />
+		<variable name="listbox_width" value="1583" />
+		<variable name="listbox_tz_height" value="1177" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="62" />
+		<variable name="sd_plus_x" value="698" />
+		<variable name="sdext_text_x" value="219" />
+		<variable name="sdext_text_y" value="360" />
+		<variable name="sdswap_button_y" value="519" />
+		<variable name="sdswap_text_x" value="220" />
+		<variable name="sdswap_text_y" value="539" />
+		<variable name="sdfilesystem_text_y" value="678" />
+		<variable name="sdfilesystem_button_y" value="758" />
+		<variable name="lock_x" value="420" />
+		<variable name="lock_y" value="800" />
+		<variable name="filemanager_select_x" value="1117" />
+		<variable name="filemanager_select_y" value="2155" />
+		<variable name="backup_name_y" value="1097" />
+		<variable name="terminal_console_height" value="1303" />
+		<variable name="terminal_text_y" value="1363" />
+		<variable name="terminal_button_y" value="1397" />
+		<variable name="row_dst_text_y" value="1436" />
+		<variable name="row_offset_text_y" value="1536" />
+		<variable name="row_offset_medium_y" value="1676" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="1569" />
+		<variable name="button_fill_main_width" value="751" />
+		<variable name="button_fill_main_height" value="431" />
+		<variable name="button_fill_half_height" value="216" />
+		<variable name="button_fill_quarter_height" value="108" />
+		<variable name="backup_list_height" value="1037" />
+		<variable name="backup_button_row1" value="1487" />
+		<variable name="backup_button_row2" value="1623" />
+		<variable name="mount_list_height" value="1377" />
+		<variable name="mount_storage_row" value="1649" />
+		<variable name="storage_list_height" value="1330" />
+		<variable name="wipe_list_height" value="1320" />
+		<variable name="wipe_button_row1" value="1684" />
+		<variable name="wipe_button_y" value="1298" />
+		<variable name="slidervalue_w" value="1569" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="3" />
+		<variable name="slidervalue_padding" value="30" />
+		<variable name="slidervalue_sliderw" value="15" />
+		<variable name="slidervalue_sliderh" value="90" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="239" y="20" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="239" y="80" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="239" y="132" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="539" y="132" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="839" y="132" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="0" y="%row2_y%" w="1440" h="938" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="650" y="2448" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="2448" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="1440" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="215" width="144" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="216:a" long01="@" key02="s" long02="#" key03="d" long03="$" key04="f" long04="%" key05="g" long05="&" key06="h" long06="*" key07="j" long07="-" key08="k" long08="+" key09="216:l" long09="_" />
+					<row3 key01="216:layout2" key02="z" long02="!" key03="x" key04="c" long04="'" key05="v" long05=":" key06="b" long06=";" key07="n" long07="/" key08="m" long08="?" key09="216:c:8" />
+					<row4 key01="216:layout3" key02="/" key03="720: " key04="." key05="216:action" />
+				</layout1>
+				<layout2>
+					<keysize height="214" width="144" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="216:A" long01="@" key02="S" long02="#" key03="D" long03="$" key04="F" long04="%" key05="G" long05="&" key06="H" long06="*" key07="J" long07="-" key08="K" long08="+" key09="216:L" long09="_" />
+					<row3 key01="216:layout1" key02="Z" long02="!" key03="X" key04="C" long04="'" key05="V" long05=":" key06="B" long06=";" key07="N" long07="/" key08="M" long08="?" key09="216:c:8" />
+					<row4 key01="216:layout3" key02="/" key03="720: " key04="." key05="216:action" />
+				</layout2>
+				<layout3>
+					<keysize height="215" width="144" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="216:layout4" key02="!" key03="144:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="216:c:8" />
+					<row4 key01="216:layout1" key02="," key03="720: " key04="." key05="216:action" />
+				</layout3>
+				<layout4>
+					<keysize height="214" width="144" />
+					<row1 key01="~" key02="`" key03="|" key04="144:" key05="144:" key06="144:" key07="144:" key08="144:" key09="{" key10="}" />
+					<row2 key01="144:" key02="144:" key03="144:" key04="144:" key05="144:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="216:layout3" key02="144:" key03="144:" key04="144:" key05="144:" key06="\" key07="<" key08=">" key09="216:c:8" />
+					<row4 key01="216:layout1" key02="144:c:34" key03="720: " key04="." key05="216:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/1600x2560/res/fonts/Roboto-Condensed-40.dat b/gui/devices/1600x2560/res/fonts/Roboto-Condensed-40.dat
new file mode 100644
index 0000000..ff23add
--- /dev/null
+++ b/gui/devices/1600x2560/res/fonts/Roboto-Condensed-40.dat
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/back-icon.png b/gui/devices/1600x2560/res/images/back-icon.png
new file mode 100644
index 0000000..85c6a5d
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/checkbox_checked.png b/gui/devices/1600x2560/res/images/checkbox_checked.png
new file mode 100644
index 0000000..65d0b34
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/checkbox_empty.png b/gui/devices/1600x2560/res/images/checkbox_empty.png
new file mode 100644
index 0000000..89b4c96
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/cursor.png b/gui/devices/1600x2560/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/curtain.jpg b/gui/devices/1600x2560/res/images/curtain.jpg
new file mode 100644
index 0000000..bcbfa46
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/file.png b/gui/devices/1600x2560/res/images/file.png
new file mode 100644
index 0000000..a69b619
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/file.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/folder.png b/gui/devices/1600x2560/res/images/folder.png
new file mode 100644
index 0000000..fbea7a9
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/folder.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/home-icon.png b/gui/devices/1600x2560/res/images/home-icon.png
new file mode 100644
index 0000000..d6bc73d
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/indeterminate001.png b/gui/devices/1600x2560/res/images/indeterminate001.png
new file mode 100644
index 0000000..a205e91
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/indeterminate002.png b/gui/devices/1600x2560/res/images/indeterminate002.png
new file mode 100644
index 0000000..f777408
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/indeterminate003.png b/gui/devices/1600x2560/res/images/indeterminate003.png
new file mode 100644
index 0000000..cbb5974
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/indeterminate004.png b/gui/devices/1600x2560/res/images/indeterminate004.png
new file mode 100644
index 0000000..5ff7734
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/indeterminate005.png b/gui/devices/1600x2560/res/images/indeterminate005.png
new file mode 100644
index 0000000..7cc9abb
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/indeterminate006.png b/gui/devices/1600x2560/res/images/indeterminate006.png
new file mode 100644
index 0000000..9eed176
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/keyboard1.png b/gui/devices/1600x2560/res/images/keyboard1.png
new file mode 100644
index 0000000..fbec19b
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/keyboard2.png b/gui/devices/1600x2560/res/images/keyboard2.png
new file mode 100644
index 0000000..9e6ea26
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/keyboard3.png b/gui/devices/1600x2560/res/images/keyboard3.png
new file mode 100644
index 0000000..bba12d0
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/keyboard4.png b/gui/devices/1600x2560/res/images/keyboard4.png
new file mode 100644
index 0000000..bd5a80c
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/medium-button.png b/gui/devices/1600x2560/res/images/medium-button.png
new file mode 100644
index 0000000..a3bf30d
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/menu-button.png b/gui/devices/1600x2560/res/images/menu-button.png
new file mode 100644
index 0000000..7b48be3
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/minus-button.png b/gui/devices/1600x2560/res/images/minus-button.png
new file mode 100644
index 0000000..e2edbe4
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/plus-button.png b/gui/devices/1600x2560/res/images/plus-button.png
new file mode 100644
index 0000000..b67d9b9
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/progress_empty.png b/gui/devices/1600x2560/res/images/progress_empty.png
new file mode 100644
index 0000000..d4ea0c2
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/progress_fill.png b/gui/devices/1600x2560/res/images/progress_fill.png
new file mode 100644
index 0000000..836e437
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/radio_empty.png b/gui/devices/1600x2560/res/images/radio_empty.png
new file mode 100644
index 0000000..9c2c66b
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/radio_selected.png b/gui/devices/1600x2560/res/images/radio_selected.png
new file mode 100644
index 0000000..8d05b9d
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/slideout.png b/gui/devices/1600x2560/res/images/slideout.png
new file mode 100644
index 0000000..56a9ac2
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/slider-touch.png b/gui/devices/1600x2560/res/images/slider-touch.png
new file mode 100644
index 0000000..b3365af
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/slider-used.png b/gui/devices/1600x2560/res/images/slider-used.png
new file mode 100644
index 0000000..229d910
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/slider.png b/gui/devices/1600x2560/res/images/slider.png
new file mode 100644
index 0000000..1e034a6
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/slider.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/sort-button.png b/gui/devices/1600x2560/res/images/sort-button.png
new file mode 100644
index 0000000..c6783a1
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/top-bar.jpg b/gui/devices/1600x2560/res/images/top-bar.jpg
new file mode 100644
index 0000000..93137b8
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/1600x2560/res/images/unlock.png b/gui/devices/1600x2560/res/images/unlock.png
new file mode 100644
index 0000000..dc3894b
--- /dev/null
+++ b/gui/devices/1600x2560/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/1600x2560/res/ui.xml b/gui/devices/1600x2560/res/ui.xml
new file mode 100644
index 0000000..09c7408
--- /dev/null
+++ b/gui/devices/1600x2560/res/ui.xml
@@ -0,0 +1,424 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="1600" height="2560" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Condensed-40" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Condensed-40" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Condensed-40" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="13" />
+		<variable name="col2_x" value="830" />
+		<variable name="col_center_x" value="422" />
+		<variable name="col_center_medium_x" value="629" />
+		<variable name="center_x" value="798" />
+		<variable name="row1_y" value="339" />
+		<variable name="row2_y" value="818" />
+		<variable name="row3_y" value="1297" />
+		<variable name="row4_y" value="1776" />
+
+		<!-- these two are unused? -->
+		<variable name="col1_center_x" value="238" />
+		<variable name="col2_center_x" value="734" />
+
+		<variable name="row1_text2_y" value="412" />
+		<variable name="row2_text2_y" value="732" />
+		<variable name="row_queue_y" value="1516" />
+		<variable name="row1_header_y" value="239" />
+		<variable name="row1_text_y" value="339" />
+		<variable name="row2_text_y" value="439" />
+		<variable name="row3_text_y" value="539" />
+		<variable name="row4_text_y" value="638" />
+		<variable name="row5_text_y" value="738" />
+		<variable name="row6_text_y" value="838" />
+		<variable name="row7_text_y" value="938" />
+		<variable name="row8_text_y" value="1037" />
+		<variable name="row9_text_y" value="1137" />
+		<variable name="row10_text_y" value="1237" />
+		<variable name="row11_text_y" value="1337" />
+		<variable name="row12_text_y" value="1436" />
+		<variable name="row13_text_y" value="1536" />
+		<variable name="row14_text_y" value="1636" />
+		<variable name="row15_text_y" value="1736" />
+		<variable name="row16_text_y" value="1835" />
+		<variable name="row17_text_y" value="1935" />
+		<variable name="row18_text_y" value="2035" />
+		<variable name="zip_status_y" value="1226" />
+		<variable name="tz_selected_y" value="319" />
+		<variable name="tz_set_y" value="1995" />
+		<variable name="tz_current_y" value="1895" />
+		<variable name="col_progressbar_x" value="547" />
+		<variable name="row_progressbar_y" value="2195" />
+		<variable name="col1_medium_x" value="13" />
+		<variable name="col2_medium_x" value="423" />
+		<variable name="col3_medium_x" value="833" />
+		<variable name="col4_medium_x" value="1242" />
+		<variable name="row1_medium_y" value="259" />
+		<variable name="row2_medium_y" value="459" />
+		<variable name="row3_medium_y" value="521" />
+		<variable name="row4_medium_y" value="858" />
+		<variable name="row5_medium_y" value="1057" />
+		<variable name="row6_medium_y" value="1676" />
+		<variable name="row7_medium_y" value="971" />
+		<variable name="slider_x" value="324" />
+		<variable name="slider_y" value="2125" />
+		<variable name="slider_text_y" value="2229" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="13" />
+		<variable name="home_button_y" value="2446" />
+		<variable name="back_button_x" value="1415" />
+		<variable name="back_button_y" value="2446" />
+		<variable name="sort_text_x" value="13" />
+		<variable name="sort_asc_text_y" value="2175" />
+		<variable name="sort_asc_button_y" value="2155" />
+		<variable name="sort_desc_text_y" value="2294" />
+		<variable name="sort_desc_button_y" value="2274" />
+		<variable name="sort_col1_button_x" value="519" />
+		<variable name="sort_col2_button_x" value="718" />
+		<variable name="sort_col3_button_x" value="918" />
+		<variable name="input_width" value="1569" />
+		<variable name="input_height" value="100" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="4" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="1600" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="938" />
+		<variable name="console_install_height" value="1200" />
+		<variable name="console_installdone_height" value="1169" />
+		<variable name="fileselector_x" value="7" />
+		<variable name="fileselector_width" value="1583" />
+		<variable name="fileselector_install_height" value="1556" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="5" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="4" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="64" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="120" />
+		<variable name="fastscroll_linew" value="4" />
+		<variable name="fastscroll_rectw" value="80" />
+		<variable name="fastscroll_recth" value="140" />
+		<variable name="listbox_x" value="7" />
+		<variable name="listbox_width" value="1583" />
+		<variable name="listbox_tz_height" value="1177" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="62" />
+		<variable name="sd_plus_x" value="698" />
+		<variable name="sdext_text_x" value="219" />
+		<variable name="sdext_text_y" value="360" />
+		<variable name="sdswap_button_y" value="519" />
+		<variable name="sdswap_text_x" value="220" />
+		<variable name="sdswap_text_y" value="539" />
+		<variable name="sdfilesystem_text_y" value="678" />
+		<variable name="sdfilesystem_button_y" value="758" />
+		<variable name="lock_x" value="480" />
+		<variable name="lock_y" value="800" />
+		<variable name="filemanager_select_x" value="1117" />
+		<variable name="filemanager_select_y" value="2155" />
+		<variable name="backup_name_y" value="1097" />
+		<variable name="terminal_console_height" value="1303" />
+		<variable name="terminal_text_y" value="1363" />
+		<variable name="terminal_button_y" value="1397" />
+		<variable name="row_dst_text_y" value="1436" />
+		<variable name="row_offset_text_y" value="1536" />
+		<variable name="row_offset_medium_y" value="1676" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="1569" />
+		<variable name="button_fill_main_width" value="751" />
+		<variable name="button_fill_main_height" value="431" />
+		<variable name="button_fill_half_height" value="216" />
+		<variable name="button_fill_quarter_height" value="108" />
+		<variable name="backup_list_height" value="1037" />
+		<variable name="backup_button_row1" value="1487" />
+		<variable name="backup_button_row2" value="1623" />
+		<variable name="mount_list_height" value="1377" />
+		<variable name="mount_storage_row" value="1649" />
+		<variable name="storage_list_height" value="1330" />
+		<variable name="wipe_list_height" value="1620" />
+		<variable name="wipe_button_row1" value="1884" />
+		<variable name="wipe_button_y" value="1298" />
+		<variable name="slidervalue_w" value="1569" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="3" />
+		<variable name="slidervalue_padding" value="30" />
+		<variable name="slidervalue_sliderw" value="15" />
+		<variable name="slidervalue_sliderh" value="90" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="239" y="20" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="239" y="80" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="239" y="132" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="539" y="132" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="739" y="132" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="0" y="%row2_y%" w="1600" h="938" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="718" y="2448" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="2448" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="1487" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="238" width="160" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="238:a" long01="@" key02="s" long02="#" key03="d" long03="$" key04="f" long04="%" key05="g" long05="&" key06="h" long06="*" key07="j" long07="-" key08="k" long08="+" key09="162:l" long09="_" />
+					<row3 key01="238:layout2" key02="z" long02="!" key03="x" key04="c" long04="'" key05="v" long05=":" key06="b" long06=";" key07="n" long07="/" key08="m" long08="?" key09="238:c:8" />
+					<row4 key01="238:layout3" key02="160:c:47" key03="800: " key04="." key05="238:action" />
+				</layout1>
+				<layout2>
+					<keysize height="238" width="160" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="238:A" long01="@" key02="S" long02="#" key03="D" long03="$" key04="F" long04="%" key05="G" long05="&" key06="H" long06="*" key07="J" long07="-" key08="K" long08="+" key09="162:L" long09="_" />
+					<row3 key01="238:layout1" key02="Z" long02="!" key03="X" key04="C" long04="'" key05="V" long05=":" key06="B" long06=";" key07="N" long07="/" key08="M" long08="?" key09="238:c:8" />
+					<row4 key01="238:layout3" key02="160:c:47" key03="800: " key04="." key05="238:action" />
+				</layout2>
+				<layout3>
+					<keysize height="238" width="160" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="238:layout4" key02="!" key03="160:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="238:c:8" />
+					<row4 key01="238:layout1" key02="," key03="800: " key04="." key05="238:action" />
+				</layout3>
+				<layout4>
+					<keysize height="238" width="160" />
+					<row1 key01="~" key02="`" key03="|" key04="160:" key05="160:" key06="160:" key07="%" key08="160:" key09="{" key10="}" />
+					<row2 key01="160:" key02="160:" key03="160:" key04="160:" key05="160:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="238:layout3" key02="160:" key03="160:" key04="160:" key05="160:" key06="\" key07="<" key08=">" key09="238:c:8" />
+					<row4 key01="238:layout1" key02="160:c:34" key03="800: " key04="." key05="238:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/1920x1200/res/fonts/Roboto-Regular-30.dat b/gui/devices/1920x1200/res/fonts/Roboto-Regular-30.dat
new file mode 100644
index 0000000..9f8082c
--- /dev/null
+++ b/gui/devices/1920x1200/res/fonts/Roboto-Regular-30.dat
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/back-icon.png b/gui/devices/1920x1200/res/images/back-icon.png
new file mode 100644
index 0000000..94dd699
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/background.jpg b/gui/devices/1920x1200/res/images/background.jpg
new file mode 100644
index 0000000..9e61f9e
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/background.jpg
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/button.png b/gui/devices/1920x1200/res/images/button.png
new file mode 100644
index 0000000..e1b49fc
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/button.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/checkbox_checked.png b/gui/devices/1920x1200/res/images/checkbox_checked.png
new file mode 100644
index 0000000..cac9a30
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/checkbox_empty.png b/gui/devices/1920x1200/res/images/checkbox_empty.png
new file mode 100644
index 0000000..ee01fa8
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/console-icon.png b/gui/devices/1920x1200/res/images/console-icon.png
new file mode 100644
index 0000000..c1f4d71
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/console-icon.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/console-toggle.png b/gui/devices/1920x1200/res/images/console-toggle.png
new file mode 100644
index 0000000..2495165
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/console-toggle.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/cursor.png b/gui/devices/1920x1200/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/curtain.jpg b/gui/devices/1920x1200/res/images/curtain.jpg
new file mode 100644
index 0000000..409664c
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/file.png b/gui/devices/1920x1200/res/images/file.png
new file mode 100644
index 0000000..c77a1f4
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/file.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/folder.png b/gui/devices/1920x1200/res/images/folder.png
new file mode 100644
index 0000000..2770f0a
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/folder.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/home-icon.png b/gui/devices/1920x1200/res/images/home-icon.png
new file mode 100644
index 0000000..8c1dde3
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/indeterminate001.png b/gui/devices/1920x1200/res/images/indeterminate001.png
new file mode 100755
index 0000000..ea2b5e2
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/indeterminate002.png b/gui/devices/1920x1200/res/images/indeterminate002.png
new file mode 100755
index 0000000..f59b298
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/indeterminate003.png b/gui/devices/1920x1200/res/images/indeterminate003.png
new file mode 100755
index 0000000..cc6d7b5
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/indeterminate004.png b/gui/devices/1920x1200/res/images/indeterminate004.png
new file mode 100755
index 0000000..9483117
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/indeterminate005.png b/gui/devices/1920x1200/res/images/indeterminate005.png
new file mode 100755
index 0000000..850de0f
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/indeterminate006.png b/gui/devices/1920x1200/res/images/indeterminate006.png
new file mode 100755
index 0000000..e5194b3
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/keyboard1.png b/gui/devices/1920x1200/res/images/keyboard1.png
new file mode 100644
index 0000000..8ee2416
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/keyboard2.png b/gui/devices/1920x1200/res/images/keyboard2.png
new file mode 100644
index 0000000..259bfec
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/keyboard3.png b/gui/devices/1920x1200/res/images/keyboard3.png
new file mode 100644
index 0000000..aaebd30
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/keyboard4.png b/gui/devices/1920x1200/res/images/keyboard4.png
new file mode 100644
index 0000000..1fe8565
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/medium-button.png b/gui/devices/1920x1200/res/images/medium-button.png
new file mode 100644
index 0000000..b4eb493
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/mediumwide-button.png b/gui/devices/1920x1200/res/images/mediumwide-button.png
new file mode 100644
index 0000000..3ca436e
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/mediumwide-button.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/minus-button.png b/gui/devices/1920x1200/res/images/minus-button.png
new file mode 100644
index 0000000..8dc17bf
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/plus-button.png b/gui/devices/1920x1200/res/images/plus-button.png
new file mode 100644
index 0000000..05b3439
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/progress_empty.png b/gui/devices/1920x1200/res/images/progress_empty.png
new file mode 100644
index 0000000..ff02df6
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/progress_fill.png b/gui/devices/1920x1200/res/images/progress_fill.png
new file mode 100644
index 0000000..84a5828
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/radio_empty.png b/gui/devices/1920x1200/res/images/radio_empty.png
new file mode 100644
index 0000000..c771300
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/radio_selected.png b/gui/devices/1920x1200/res/images/radio_selected.png
new file mode 100644
index 0000000..d3b2278
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/slider-touch.png b/gui/devices/1920x1200/res/images/slider-touch.png
new file mode 100644
index 0000000..8a21a3b
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/slider-used.png b/gui/devices/1920x1200/res/images/slider-used.png
new file mode 100644
index 0000000..4963ff5
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/slider.png b/gui/devices/1920x1200/res/images/slider.png
new file mode 100644
index 0000000..dfeea6e
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/slider.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/sort-button.png b/gui/devices/1920x1200/res/images/sort-button.png
new file mode 100644
index 0000000..b1f8218
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/images/unlock.png b/gui/devices/1920x1200/res/images/unlock.png
new file mode 100644
index 0000000..6ca95d1
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/1920x1200/res/ui.xml b/gui/devices/1920x1200/res/ui.xml
new file mode 100644
index 0000000..e36d0ae
--- /dev/null
+++ b/gui/devices/1920x1200/res/ui.xml
@@ -0,0 +1,439 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="1920" height="1200" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="landscape.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="30" fallback="Roboto-Regular-30" />
+		<resource name="base" type="image" filename="background.jpg" />
+		<resource name="main_button" type="image" filename="button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="mediumwide_button" type="image" filename="mediumwide-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="console_button" type="image" filename="console-toggle" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="210" />
+		<variable name="col2_x" value="590" />
+		<variable name="col3_x" value="970" />
+		<variable name="col4_x" value="1350" />
+		<variable name="row1_y" value="220" />
+		<variable name="row2_y" value="680" />
+		<variable name="col_center_x" value="780" />
+		<variable name="center_x" value="960" />
+		<variable name="screen_width" value="1920" />
+		<variable name="screen_height" value="1200" />
+		<variable name="col_progressbar_x" value="771" />
+		<variable name="row_progressbar_y" value="1100" />
+		<variable name="col1_medium_x" value="570" />
+		<variable name="col2_medium_x" value="770" />
+		<variable name="col3_medium_x" value="970" />
+		<variable name="col4_medium_x" value="1170" />
+		<variable name="row1_medium_y" value="105" />
+		<variable name="row2_medium_y" value="365" />
+		<variable name="row3_medium_y" value="245" />
+		<variable name="row4_medium_y" value="720" />
+		<variable name="row5_medium_y" value="700" />
+		<variable name="row1_text_y" value="90" />
+		<variable name="row2_text_y" value="140" />
+		<variable name="row3_text_y" value="190" />
+		<variable name="row4_text_y" value="240" />
+		<variable name="row5_text_y" value="290" />
+		<variable name="row6_text_y" value="340" />
+		<variable name="row7_text_y" value="390" />
+		<variable name="row8_text_y" value="440" />
+		<variable name="row9_text_y" value="490" />
+		<variable name="row10_text_y" value="540" />
+		<variable name="row11_text_y" value="590" />
+		<variable name="row12_text_y" value="640" />
+		<variable name="row13_text_y" value="690" />
+		<variable name="row14_text_y" value="740" />
+		<variable name="row15_text_y" value="790" />
+		<variable name="row16_text_y" value="840" />
+		<variable name="row17_text_y" value="890" />
+		<variable name="row18_text_y" value="940" />
+		<variable name="row_offsetmedium_y" value="775" />
+		<variable name="home_button_x" value="1620" />
+		<variable name="home_button_y" value="5" />
+		<variable name="back_button_x" value="1720" />
+		<variable name="back_button_y" value="5" />
+		<variable name="console_button_x" value="1820" />
+		<variable name="console_button_y" value="5" />
+		<variable name="nandcheck_col1" value="328" />
+		<variable name="nandcheck_col2" value="800" />
+		<variable name="nandcheck_row1" value="200" />
+		<variable name="nandcheck_row2" value="275" />
+		<variable name="nandcheck_row3" value="350" />
+		<variable name="nandcheck_row4" value="425" />
+		<variable name="nandcheck_row5" value="500" />
+		<variable name="nandcheck_row6" value="575" />
+		<variable name="nandcheck_row7" value="650" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#A0A0A0" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="slider_x" value="630" />
+		<variable name="slider_y" value="1000" />
+		<variable name="slider_text_y" value="1075" />
+		<variable name="sort_text_x" value="400" />
+		<variable name="sort_asc_text_y" value="1045" />
+		<variable name="sort_asc_button_y" value="1040" />
+		<variable name="sort_desc_text_y" value="1105" />
+		<variable name="sort_desc_button_y" value="1100" />
+		<variable name="sort_col1_button_x" value="670" />
+		<variable name="sort_col2_button_x" value="770" />
+		<variable name="sort_col3_button_x" value="870" />
+		<variable name="col1_sdext_x" value="720" />
+		<variable name="col2_sdext_x" value="1100" />
+		<variable name="row1_sdext_y" value="180" />
+		<variable name="row2_sdext_y" value="265" />
+		<variable name="row_extsize_y" value="175" />
+		<variable name="row_swapsize_y" value="260" />
+		<variable name="input_x" value="50" />
+		<variable name="input_width" value="1820" />
+		<variable name="input_height" value="50" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="50" />
+		<variable name="console_width" value="1820" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="320" />
+		<variable name="console_install_height" value="440" />
+		<variable name="console_installdone_height" value="300" />
+		<variable name="fileselector_folder_x" value="50" />
+		<variable name="fileselector_folder_width" value="610" />
+		<variable name="fileselector_folderonly_width" value="800" />
+		<variable name="fileselector_file_x" value="700" />
+		<variable name="fileselector_file_width" value="1170" />
+		<variable name="fileselector_install_y" value="205" />
+		<variable name="fileselector_install_height" value="800" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="18" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="60" />
+		<variable name="fastscroll_linew" value="3" />
+		<variable name="fastscroll_rectw" value="45" />
+		<variable name="fastscroll_recth" value="75" />
+		<variable name="zipstorage_text_y" value="130" />
+		<variable name="listbox_x" value="560" />
+		<variable name="listbox_y" value="150" />
+		<variable name="listbox_width" value="800" />
+		<variable name="listbox_tz_height" value="400" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="18" />
+		<variable name="sd_plus_x" value="408" />
+		<variable name="lock_x" value="660" />
+		<variable name="lock_y" value="300" />
+		<variable name="filemanager_select_x" value="1500" />
+		<variable name="filemanager_select_y" value="980" />
+		<variable name="backup_name_text_y" value="440" />
+		<variable name="backup_name_button_y" value="390" />
+		<variable name="col_right_x" value="1870" />
+		<variable name="cancel_button_y" value="240" />
+		<variable name="terminal_console_y" value="0" />
+		<variable name="terminal_console_height" value="610" />
+		<variable name="terminal_text_y" value="624" />
+		<variable name="terminal_button_y" value="615" />
+		<variable name="terminal_input_width" value="1550" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="960" />
+		<variable name="button_fill_main_width" value="900" />
+		<variable name="button_fill_main_height" value="240" />
+		<variable name="button_fill_half_height" value="80" />
+		<variable name="button_fill_quarter_height" value="60" />
+		<variable name="button_full_center_x" value="480" />
+		<variable name="backup_list_x" value="50" />
+		<variable name="backup_list_y" value="160" />
+		<variable name="backup_list_width" value="900" />
+		<variable name="backup_list_height" value="660" />
+		<variable name="backup_storage_y" value="405" />
+		<variable name="backup_encrypt_y" value="495" />
+		<variable name="restore_list_y" value="190" />
+		<variable name="restore_list_height" value="600" />
+		<variable name="mount_list_height" value="900" />
+		<variable name="mount_storage_row" value="850" />
+		<variable name="wipe_list_height" value="850" />
+		<variable name="wipe_button_y" value="375" />
+		<variable name="slidervalue_x" value="540" />
+		<variable name="slidervalue_w" value="960" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="3" />
+		<variable name="slidervalue_padding" value="0" />
+		<variable name="slidervalue_sliderw" value="20" />
+		<variable name="slidervalue_sliderh" value="80" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="base" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="250" y="5" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="250" y="40" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery Level: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="600" y="40" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="1150" y="40" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="750" y="40" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="6" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="flash_zip_console">
+			<object type="console">
+				<placement x="%console_x%" y="140" w="%console_width%" h="700" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="400" w="%console_width%" h="600" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="console_button" x="%console_button_x%" y="%console_button_y%" />
+				<placement x="%console_x%" y="150" w="%console_width%" h="1000" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="684" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="129" width="159" />
+					<row1 key01="153:" key02="q" long02="1" key03="w" long03="2" key04="e" long04="3" key05="r" long05="4" key06="t" long06="5" key07="y" long07="6" key08="u" long08="7" key09="i" long09="8" key10="o" long10="9" key11="p" long11="0" key12="177:c:8" />
+					<row2 key01="213:layout3" key02="156:a" key03="156:s" key04="156:d" key05="156:f" key06="156:g" key07="156:h" key08="156:j" key09="k" key10="156:l" key11="303:action" />
+					<row3 key01="269:layout2" key02="154:z" key03="154:x" key04="154:c" key05="154:v" key06="154:b" key07="154:n" key08="154:m" key09="154:," long09="!" key10="154:." long10="?" key11="265:layout2" />
+					<row4 key01="600:" key02="720: " key03="/" long03=":" key04="-" long0="_" />
+				</layout1>
+				<layout2>
+					<keysize height="129" width="159" capslock="0" revert_layout="1" />
+					<row1 key01="153:" key02="Q" long02="1" key03="W" long03="2" key04="E" long04="3" key05="R" long05="4" key06="T" long06="5" key07="Y" long07="6" key08="U" long08="7" key09="I" long09="8" key10="O" long10="9" key11="P" long11="0" key12="177:c:8" />
+					<row2 key01="213:layout3" key02="156:A" key03="156:S" key04="156:D" key05="156:F" key06="156:G" key07="156:H" key08="156:J" key09="156:K" key10="156:L" key11="303:action" />
+					<row3 key01="269:layout1" key02="154:Z" key03="154:X" key04="154:C" key05="154:V" key06="154:B" key07="154:N" key08="154:M" key09="154:," long09="!" key10="154:." long10="?" key11="265:layout1" />
+					<row4 key01="600:" key02="720: " key03="/" long03=":" key04="-" long0="_" />
+				</layout2>
+				<layout3>
+					<keysize height="129" width="159" />
+					<row1 key01="153:" key02="1" key03="2" key04="3" key05="4" key06="5" key07="6" key08="7" key09="8" key10="9" key11="0" key12="177:c:8" />
+					<row2 key01="213:layout1" key02="156:#" key03="156:$" key04="156:%" key05="156:&" key06="156:*" key07="156:-" key08="156:+" key09="156:(" key10="156:)" key11="303:action" />
+					<row3 key01="269:layout4" key02="154:<" key03="154:>" key04="154:=" key05="154:'" key06="154:;" key07="154:," key08="154:." key09="154:!" key10="154:?" key11="265:layout4" />
+					<row4 key01="282:" key02="/" key03="@" key04="720: " key05="159:c:34" key06="_" />
+				</layout3>
+				<layout4>
+					<keysize height="129" width="159" />
+					<row1 key01="153:" key02="~" key03="`" key04="|" key05="159:" key06="159:" key07="159:" key08="159:" key09="159:" key10="159:" key11="159:" key12="177:c:8" />
+					<row2 key01="213:layout1" key02="156:" key03="156:" key04="156:" key05="156:" key06="156:^" key07="156:" key08="156:" key09="156:{" key10="156:}" key11="303:action" />
+					<row3 key01="269:layout3" key02="154:\" key03="154:" key04="154:" key05="154:" key06="154:" key07="154:[" key08="154:]" key09="154:!" key10="154:?" key11="265:layout3" />
+					<row4 key01="600:" key02="720: " />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/240x240/res/fonts/Roboto-Condensed-12.dat b/gui/devices/240x240/res/fonts/Roboto-Condensed-12.dat
new file mode 100644
index 0000000..b48c4f2
--- /dev/null
+++ b/gui/devices/240x240/res/fonts/Roboto-Condensed-12.dat
Binary files differ
diff --git a/gui/devices/240x240/res/images/android.png b/gui/devices/240x240/res/images/android.png
new file mode 100644
index 0000000..0ccd02c
--- /dev/null
+++ b/gui/devices/240x240/res/images/android.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/back-icon.png b/gui/devices/240x240/res/images/back-icon.png
new file mode 100644
index 0000000..97b17dd
--- /dev/null
+++ b/gui/devices/240x240/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/checkbox_checked.png b/gui/devices/240x240/res/images/checkbox_checked.png
new file mode 100644
index 0000000..c50b53b
--- /dev/null
+++ b/gui/devices/240x240/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/checkbox_empty.png b/gui/devices/240x240/res/images/checkbox_empty.png
new file mode 100644
index 0000000..42ab7f5
--- /dev/null
+++ b/gui/devices/240x240/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/curtain.jpg b/gui/devices/240x240/res/images/curtain.jpg
new file mode 100644
index 0000000..83d4895
--- /dev/null
+++ b/gui/devices/240x240/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/240x240/res/images/file.png b/gui/devices/240x240/res/images/file.png
new file mode 100644
index 0000000..cc91f2f
--- /dev/null
+++ b/gui/devices/240x240/res/images/file.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/folder.png b/gui/devices/240x240/res/images/folder.png
new file mode 100644
index 0000000..9825a41
--- /dev/null
+++ b/gui/devices/240x240/res/images/folder.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/home-icon.png b/gui/devices/240x240/res/images/home-icon.png
new file mode 100644
index 0000000..f746790
--- /dev/null
+++ b/gui/devices/240x240/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/indeterminate001.png b/gui/devices/240x240/res/images/indeterminate001.png
new file mode 100644
index 0000000..bd58cb6
--- /dev/null
+++ b/gui/devices/240x240/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/indeterminate002.png b/gui/devices/240x240/res/images/indeterminate002.png
new file mode 100644
index 0000000..aaa7b9f
--- /dev/null
+++ b/gui/devices/240x240/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/indeterminate003.png b/gui/devices/240x240/res/images/indeterminate003.png
new file mode 100644
index 0000000..e2b2475
--- /dev/null
+++ b/gui/devices/240x240/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/indeterminate004.png b/gui/devices/240x240/res/images/indeterminate004.png
new file mode 100644
index 0000000..fadd737
--- /dev/null
+++ b/gui/devices/240x240/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/indeterminate005.png b/gui/devices/240x240/res/images/indeterminate005.png
new file mode 100644
index 0000000..5751dd5
--- /dev/null
+++ b/gui/devices/240x240/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/indeterminate006.png b/gui/devices/240x240/res/images/indeterminate006.png
new file mode 100644
index 0000000..92481dc
--- /dev/null
+++ b/gui/devices/240x240/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/keyboard1.png b/gui/devices/240x240/res/images/keyboard1.png
new file mode 100644
index 0000000..22cdcec
--- /dev/null
+++ b/gui/devices/240x240/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/keyboard2.png b/gui/devices/240x240/res/images/keyboard2.png
new file mode 100644
index 0000000..4c303f2
--- /dev/null
+++ b/gui/devices/240x240/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/keyboard3.png b/gui/devices/240x240/res/images/keyboard3.png
new file mode 100644
index 0000000..2bb32e9
--- /dev/null
+++ b/gui/devices/240x240/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/keyboard4.png b/gui/devices/240x240/res/images/keyboard4.png
new file mode 100644
index 0000000..c5b1d60
--- /dev/null
+++ b/gui/devices/240x240/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/medium-button.png b/gui/devices/240x240/res/images/medium-button.png
new file mode 100644
index 0000000..38f2597
--- /dev/null
+++ b/gui/devices/240x240/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/menu-button.png b/gui/devices/240x240/res/images/menu-button.png
new file mode 100644
index 0000000..f3001c2
--- /dev/null
+++ b/gui/devices/240x240/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/minus-button.png b/gui/devices/240x240/res/images/minus-button.png
new file mode 100644
index 0000000..52d24d5
--- /dev/null
+++ b/gui/devices/240x240/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/plus-button.png b/gui/devices/240x240/res/images/plus-button.png
new file mode 100644
index 0000000..188cae8
--- /dev/null
+++ b/gui/devices/240x240/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/progress_empty.png b/gui/devices/240x240/res/images/progress_empty.png
new file mode 100644
index 0000000..c065bca
--- /dev/null
+++ b/gui/devices/240x240/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/progress_fill.png b/gui/devices/240x240/res/images/progress_fill.png
new file mode 100644
index 0000000..5f10be7
--- /dev/null
+++ b/gui/devices/240x240/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/radio_empty.png b/gui/devices/240x240/res/images/radio_empty.png
new file mode 100644
index 0000000..1697acb
--- /dev/null
+++ b/gui/devices/240x240/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/radio_selected.png b/gui/devices/240x240/res/images/radio_selected.png
new file mode 100644
index 0000000..5c67cd8
--- /dev/null
+++ b/gui/devices/240x240/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/slideout.png b/gui/devices/240x240/res/images/slideout.png
new file mode 100644
index 0000000..9658400
--- /dev/null
+++ b/gui/devices/240x240/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/slider-touch.png b/gui/devices/240x240/res/images/slider-touch.png
new file mode 100644
index 0000000..93b72f5
--- /dev/null
+++ b/gui/devices/240x240/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/slider-used.png b/gui/devices/240x240/res/images/slider-used.png
new file mode 100644
index 0000000..fa6d601
--- /dev/null
+++ b/gui/devices/240x240/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/slider.png b/gui/devices/240x240/res/images/slider.png
new file mode 100644
index 0000000..2aa9557
--- /dev/null
+++ b/gui/devices/240x240/res/images/slider.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/sort-button.png b/gui/devices/240x240/res/images/sort-button.png
new file mode 100644
index 0000000..1bb5c72
--- /dev/null
+++ b/gui/devices/240x240/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/240x240/res/images/top-bar.jpg b/gui/devices/240x240/res/images/top-bar.jpg
new file mode 100644
index 0000000..57de155
--- /dev/null
+++ b/gui/devices/240x240/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/240x240/res/images/unlock.png b/gui/devices/240x240/res/images/unlock.png
new file mode 100644
index 0000000..7d0f490
--- /dev/null
+++ b/gui/devices/240x240/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/240x240/res/ui.xml b/gui/devices/240x240/res/ui.xml
new file mode 100644
index 0000000..d157f85
--- /dev/null
+++ b/gui/devices/240x240/res/ui.xml
@@ -0,0 +1,415 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="240" height="240" />
+		<author>masteroftime</author>
+		<title>TWRP x201</title>
+		<description>tDPI Smartwatch Theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="watch.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="12" fallback="Roboto-Condensed-12" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="12" fallback="Roboto-Condensed-12" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="12" fallback="Roboto-Condensed-12" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="6" />
+		<variable name="col2_x" value="126" />
+		<variable name="col_center_x" value="62" />
+		<variable name="col_center_medium_x" value="122" />
+		<variable name="center_x" value="120" />
+		<variable name="row1_home_y" value="40" />
+		<variable name="row2_home_y" value="80" />
+		<variable name="row3_home_y" value="120" />
+		<variable name="row4_home_y" value="160" />
+		<variable name="row1_y" value="20" />
+		<variable name="row2_y" value="65" />
+		<variable name="row3_y" value="110" />
+		<variable name="row4_y" value="155" />
+		<variable name="row_queue_y" value="134" />
+		<variable name="row1_header_y" value="0" />
+		<variable name="row1_text_y" value="16" />
+		<variable name="row2_text_y" value="32" />
+		<variable name="row3_text_y" value="48" />
+		<variable name="row4_text_y" value="64" />
+		<variable name="row5_text_y" value="80" />
+		<variable name="row6_text_y" value="96" />
+		<variable name="row7_text_y" value="112" />
+		<variable name="row8_text_y" value="128" />
+		<variable name="row9_text_y" value="144" />
+		<variable name="row10_text_y" value="160" />
+		<variable name="row11_text_y" value="176" />
+		<variable name="row12_text_y" value="192" />
+		<variable name="row13_text_y" value="208" />
+		<variable name="zip_status_y" value="224" />
+		<variable name="backup_text_y" value="60" />
+		<variable name="tz_set_y" value="200" />
+		<variable name="tz_current_y" value="184" />
+		<variable name="col_progressbar_x" value="25" />
+		<variable name="row_progressbar_y" value="200" />
+		<variable name="col1_medium_x" value="6" />
+		<variable name="col2_medium_x" value="70" />
+		<variable name="col3_medium_x" value="134" />
+		<variable name="col4_medium_x" value="198" />
+		<variable name="row1_medium_y" value="24" />
+		<variable name="row2_medium_y" value="40" />
+		<variable name="row3_medium_y" value="56" />
+		<variable name="row4_medium_y" value="72" />
+		<variable name="row5_medium_y" value="88" />
+		<variable name="row6_medium_y" value="104" />
+		<variable name="row7_medium_y" value="120" />
+		<variable name="slider_x" value="34" />
+		<variable name="slider_y" value="200" />
+		<variable name="slider_text_y" value="218" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="3" />
+		<variable name="home_button_y" value="220" />
+		<variable name="back_button_x" value="209" />
+		<variable name="back_button_y" value="220" />
+		<variable name="sort_text_x" value="8" />
+		<variable name="sort_asc_text_y" value="174" />
+		<variable name="sort_asc_button_y" value="173" />
+		<variable name="sort_desc_text_y" value="198" />
+		<variable name="sort_desc_button_y" value="197" />
+		<variable name="sort_col1_button_x" value="120" />
+		<variable name="sort_col2_button_x" value="160" />
+		<variable name="sort_col3_button_x" value="200" />
+		<variable name="input_width" value="228" />
+		<variable name="input_width_conf" value="10" />
+		<variable name="input_height" value="18" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="2" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="240" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="120" />
+		<variable name="console_install_height" value="120" />
+		<variable name="console_installdone_height" value="120" />
+		<variable name="fileselector_x" value="0" />
+		<variable name="fileselector_y" value="38" />
+		<variable name="fileselector_width" value="240" />
+		<variable name="fileselector_install_height" value="132" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="2" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="1" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="12" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="32" />
+		<variable name="fastscroll_linew" value="1" />
+		<variable name="fastscroll_rectw" value="18" />
+		<variable name="fastscroll_recth" value="31" />
+		<variable name="listbox_x" value="0" />
+		<variable name="listbox_width" value="240" />
+		<variable name="listbox_tz_height" value="114" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="12" />
+		<variable name="sd_plus_x" value="174" />
+		<variable name="sdext_text_x" value="56" />
+		<variable name="sdext_text_y" value="18" />
+		<variable name="sdswap_button_y" value="45" />
+		<variable name="sdswap_text_x" value="56" />
+		<variable name="sdswap_text_y" value="47" />
+		<variable name="sdfilesystem_text_y" value="92" />
+		<variable name="sdfilesystem_button_y" value="120" />
+		<variable name="lock_x" value="72" />
+		<variable name="lock_y" value="46" />
+		<variable name="filemanager_select_x" value="98" />
+		<variable name="filemanager_select_y" value="210" />
+		<variable name="terminal_console_height" value="71" />
+		<variable name="terminal_text_y" value="72" />
+		<variable name="terminal_button_y" value="220" />
+		<variable name="row_dst_text_y" value="116" />
+		<variable name="row_offset_text_y" value="132" />
+		<variable name="row_offset_medium_y" value="148" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="228" />
+		<variable name="button_fill_main_width" value="110" />
+		<variable name="button_fill_med_width" value="72" />
+		<variable name="button_fill_small_width" value="24" />
+		<variable name="button_fill_main_height" value="38" />
+		<variable name="button_fill_half_height" value="19" />
+		<variable name="button_fill_quarter_height" value="19" />
+		<variable name="button_refresh_x" value="152" />
+		<variable name="backup_list_y" value="26" />
+		<variable name="backup_list_height" value="129" />
+		<variable name="restore_list_height" value="155" />
+		<variable name="backup_button_row1" value="159" />
+		<variable name="backup_button_row2" value="180" />
+		<variable name="mount_list_height" value="146" />
+		<variable name="mount_storage_row" value="195" />
+		<variable name="storage_list_height" value="134" />
+		<variable name="wipe_list_height" value="170" />
+		<variable name="wipe_button_y" value="136" />
+		<variable name="slidervalue_w" value="228" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="1" />
+		<variable name="slidervalue_padding" value="8" />
+		<variable name="slidervalue_sliderw" value="4" />
+		<variable name="slidervalue_sliderh" value="20" />
+		<variable name="wipe_button_row1" value="180" />
+		<variable name="invalid_partition_y" value="167" />
+	</variables>
+
+	<templates>
+		<template name="twrpheader">
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="1" y="3" />
+				<text>TWRP v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="3" y="21" />
+				<text>SIMULATION</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="89" y="3" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="236" y="3" placement="1" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>%tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="145" y="3" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>%tw_cpu_temp% C</text>
+			</object>
+		</template>
+
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="%row2_y%" w="%console_width%" h="%console_action_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="97" y="220" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="220" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="90" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="32" width="24" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="36:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="36:l" />
+					<row3 key01="36:layout2" key02="z" key03="x" key04="c" key05="v" key06="b" key07="n" key08="m" key09="36:c:8" />
+					<row4 key01="36:layout3" key02="24:" key03="120: " key04="24:." key05="36:a:action" />
+				</layout1>
+				<layout2>
+					<keysize height="32" width="24" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="36:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="36:L" />
+					<row3 key01="36:layout1" key02="Z" key03="X" key04="C" key05="V" key06="B" key07="N" key08="M" key09="36:c:8" />
+					<row4 key01="36:layout3" key02="32:" key03="120: " key04="32:." key05="36:action" />
+				</layout2>
+				<layout3>
+					<keysize height="32" width="24" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="36:layout4" key02="!" key03="24:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="36:c:8" />
+					<row4 key01="36:layout1" key02="24:," key03="120: " key04="24:." key05="36:action" />
+				</layout3>
+				<layout4>
+					<keysize height="32" width="24" />
+					<row1 key01="~" key02="`" key03="|" key04="24:" key05="24:" key06="24:" key07="%" key08="24:" key09="{" key10="}" />
+					<row2 key01="24:" key02="24:" key03="24:" key04="24:" key05="24:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="36:layout3" key02="24:" key03="24:" key04="24:" key05="24:" key06="\" key07="<" key08=">" key09="36:c:8" />
+					<row4 key01="36:layout1" key02="24:c:34" key03="120: " key04="24:." key05="36:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/2560x1600/res/fonts/Roboto-Regular-40.dat b/gui/devices/2560x1600/res/fonts/Roboto-Regular-40.dat
new file mode 100644
index 0000000..637d9fe
--- /dev/null
+++ b/gui/devices/2560x1600/res/fonts/Roboto-Regular-40.dat
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/back-icon.png b/gui/devices/2560x1600/res/images/back-icon.png
new file mode 100644
index 0000000..effe411
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/background.jpg b/gui/devices/2560x1600/res/images/background.jpg
new file mode 100644
index 0000000..3b8a71a
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/background.jpg
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/button.png b/gui/devices/2560x1600/res/images/button.png
new file mode 100644
index 0000000..4743ba5
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/button.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/checkbox_checked.png b/gui/devices/2560x1600/res/images/checkbox_checked.png
new file mode 100644
index 0000000..c3ceeef
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/checkbox_empty.png b/gui/devices/2560x1600/res/images/checkbox_empty.png
new file mode 100644
index 0000000..87d6dd9
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/console-icon.png b/gui/devices/2560x1600/res/images/console-icon.png
new file mode 100644
index 0000000..4dd1a14
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/console-icon.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/console-toggle.png b/gui/devices/2560x1600/res/images/console-toggle.png
new file mode 100644
index 0000000..3bf8a1a
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/console-toggle.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/cursor.png b/gui/devices/2560x1600/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/curtain.jpg b/gui/devices/2560x1600/res/images/curtain.jpg
new file mode 100644
index 0000000..e58811a
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/file.png b/gui/devices/2560x1600/res/images/file.png
new file mode 100644
index 0000000..793b61d
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/file.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/folder.png b/gui/devices/2560x1600/res/images/folder.png
new file mode 100644
index 0000000..f3ab28f
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/folder.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/home-icon.png b/gui/devices/2560x1600/res/images/home-icon.png
new file mode 100644
index 0000000..692d8dd
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/indeterminate001.png b/gui/devices/2560x1600/res/images/indeterminate001.png
new file mode 100755
index 0000000..f0cb4b2
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/indeterminate002.png b/gui/devices/2560x1600/res/images/indeterminate002.png
new file mode 100755
index 0000000..eba46eb
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/indeterminate003.png b/gui/devices/2560x1600/res/images/indeterminate003.png
new file mode 100755
index 0000000..34ef03e
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/indeterminate004.png b/gui/devices/2560x1600/res/images/indeterminate004.png
new file mode 100755
index 0000000..de6f4d9
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/indeterminate005.png b/gui/devices/2560x1600/res/images/indeterminate005.png
new file mode 100755
index 0000000..d993f2c
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/indeterminate006.png b/gui/devices/2560x1600/res/images/indeterminate006.png
new file mode 100755
index 0000000..08ca229
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/keyboard1.png b/gui/devices/2560x1600/res/images/keyboard1.png
new file mode 100644
index 0000000..cef79d9
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/keyboard2.png b/gui/devices/2560x1600/res/images/keyboard2.png
new file mode 100644
index 0000000..d860279
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/keyboard3.png b/gui/devices/2560x1600/res/images/keyboard3.png
new file mode 100644
index 0000000..3478097
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/keyboard4.png b/gui/devices/2560x1600/res/images/keyboard4.png
new file mode 100644
index 0000000..d491625
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/medium-button.png b/gui/devices/2560x1600/res/images/medium-button.png
new file mode 100644
index 0000000..ef731cd
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/mediumwide-button.png b/gui/devices/2560x1600/res/images/mediumwide-button.png
new file mode 100644
index 0000000..94af419
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/mediumwide-button.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/minus-button.png b/gui/devices/2560x1600/res/images/minus-button.png
new file mode 100644
index 0000000..8d343a7
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/plus-button.png b/gui/devices/2560x1600/res/images/plus-button.png
new file mode 100644
index 0000000..cc4c70a
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/progress_empty.png b/gui/devices/2560x1600/res/images/progress_empty.png
new file mode 100644
index 0000000..8f3f02a
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/progress_fill.png b/gui/devices/2560x1600/res/images/progress_fill.png
new file mode 100644
index 0000000..88d82d5
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/radio_empty.png b/gui/devices/2560x1600/res/images/radio_empty.png
new file mode 100644
index 0000000..946d46d
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/radio_selected.png b/gui/devices/2560x1600/res/images/radio_selected.png
new file mode 100644
index 0000000..d68e855
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/slider-touch.png b/gui/devices/2560x1600/res/images/slider-touch.png
new file mode 100644
index 0000000..14c8f99
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/slider-used.png b/gui/devices/2560x1600/res/images/slider-used.png
new file mode 100644
index 0000000..21372da
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/slider.png b/gui/devices/2560x1600/res/images/slider.png
new file mode 100644
index 0000000..ec27200
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/slider.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/sort-button.png b/gui/devices/2560x1600/res/images/sort-button.png
new file mode 100644
index 0000000..d577096
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/images/unlock.png b/gui/devices/2560x1600/res/images/unlock.png
new file mode 100644
index 0000000..a605d05
--- /dev/null
+++ b/gui/devices/2560x1600/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/2560x1600/res/ui.xml b/gui/devices/2560x1600/res/ui.xml
new file mode 100644
index 0000000..12a2d55
--- /dev/null
+++ b/gui/devices/2560x1600/res/ui.xml
@@ -0,0 +1,439 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="2560" height="1600" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="landscape.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="40" fallback="Roboto-Regular-40" />
+		<resource name="base" type="image" filename="background.jpg" />
+		<resource name="main_button" type="image" filename="button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="mediumwide_button" type="image" filename="mediumwide-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="console_button" type="image" filename="console-toggle" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="260" />
+		<variable name="col2_x" value="770" />
+		<variable name="col3_x" value="1310" />
+		<variable name="col4_x" value="1820" />
+		<variable name="row1_y" value="300" />
+		<variable name="row2_y" value="1000" />
+		<variable name="col_center_x" value="1040" />
+		<variable name="center_x" value="1280" />
+		<variable name="screen_width" value="1920" />
+		<variable name="screen_height" value="1200" />
+		<variable name="col_progressbar_x" value="1028" />
+		<variable name="row_progressbar_y" value="1500" />
+		<variable name="col1_medium_x" value="755" />
+		<variable name="col2_medium_x" value="1025" />
+		<variable name="col3_medium_x" value="1310" />
+		<variable name="col4_medium_x" value="1580" />
+		<variable name="row1_medium_y" value="105" />
+		<variable name="row2_medium_y" value="500" />
+		<variable name="row3_medium_y" value="245" />
+		<variable name="row4_medium_y" value="1000" />
+		<variable name="row5_medium_y" value="950" />
+		<variable name="row1_text_y" value="120" />
+		<variable name="row2_text_y" value="195" />
+		<variable name="row3_text_y" value="270" />
+		<variable name="row4_text_y" value="345" />
+		<variable name="row5_text_y" value="420" />
+		<variable name="row6_text_y" value="495" />
+		<variable name="row7_text_y" value="570" />
+		<variable name="row8_text_y" value="645" />
+		<variable name="row9_text_y" value="720" />
+		<variable name="row10_text_y" value="795" />
+		<variable name="row11_text_y" value="870" />
+		<variable name="row12_text_y" value="945" />
+		<variable name="row13_text_y" value="1020" />
+		<variable name="row14_text_y" value="1095" />
+		<variable name="row15_text_y" value="1170" />
+		<variable name="row16_text_y" value="1240" />
+		<variable name="row17_text_y" value="1310" />
+		<variable name="row18_text_y" value="1395" />
+		<variable name="row_offsetmedium_y" value="1100" />
+		<variable name="home_button_x" value="2200" />
+		<variable name="home_button_y" value="15" />
+		<variable name="back_button_x" value="2320" />
+		<variable name="back_button_y" value="15" />
+		<variable name="console_button_x" value="2440" />
+		<variable name="console_button_y" value="15" />
+		<variable name="nandcheck_col1" value="328" />
+		<variable name="nandcheck_col2" value="800" />
+		<variable name="nandcheck_row1" value="260" />
+		<variable name="nandcheck_row2" value="360" />
+		<variable name="nandcheck_row3" value="460" />
+		<variable name="nandcheck_row4" value="560" />
+		<variable name="nandcheck_row5" value="660" />
+		<variable name="nandcheck_row6" value="760" />
+		<variable name="nandcheck_row7" value="860" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#A0A0A0" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="slider_x" value="841" />
+		<variable name="slider_y" value="1335" />
+		<variable name="slider_text_y" value="1435" />
+		<variable name="sort_text_x" value="800" />
+		<variable name="sort_asc_text_y" value="1410" />
+		<variable name="sort_asc_button_y" value="1400" />
+		<variable name="sort_desc_text_y" value="1490" />
+		<variable name="sort_desc_button_y" value="1480" />
+		<variable name="sort_col1_button_x" value="1200" />
+		<variable name="sort_col2_button_x" value="1350" />
+		<variable name="sort_col3_button_x" value="1500" />
+		<variable name="col1_sdext_x" value="960" />
+		<variable name="col2_sdext_x" value="1600" />
+		<variable name="row1_sdext_y" value="180" />
+		<variable name="row2_sdext_y" value="265" />
+		<variable name="row_extsize_y" value="175" />
+		<variable name="row_swapsize_y" value="260" />
+		<variable name="input_x" value="50" />
+		<variable name="input_width" value="2460" />
+		<variable name="input_height" value="65" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="50" />
+		<variable name="console_width" value="2460" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="320" />
+		<variable name="console_install_height" value="440" />
+		<variable name="console_installdone_height" value="300" />
+		<variable name="fileselector_folder_x" value="70" />
+		<variable name="fileselector_folder_width" value="910" />
+		<variable name="fileselector_folderonly_width" value="1200" />
+		<variable name="fileselector_file_x" value="1000" />
+		<variable name="fileselector_file_width" value="1500" />
+		<variable name="fileselector_install_y" value="290" />
+		<variable name="fileselector_install_height" value="1100" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="18" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="80" />
+		<variable name="fastscroll_linew" value="4" />
+		<variable name="fastscroll_rectw" value="60" />
+		<variable name="fastscroll_recth" value="100" />
+		<variable name="zipstorage_text_y" value="190" />
+		<variable name="listbox_x" value="680" />
+		<variable name="listbox_y" value="195" />
+		<variable name="listbox_width" value="1200" />
+		<variable name="listbox_tz_height" value="650" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="18" />
+		<variable name="sd_plus_x" value="408" />
+		<variable name="lock_x" value="880" />
+		<variable name="lock_y" value="300" />
+		<variable name="filemanager_select_x" value="2000" />
+		<variable name="filemanager_select_y" value="1360" />
+		<variable name="backup_name_text_y" value="440" />
+		<variable name="backup_name_button_y" value="950" />
+		<variable name="col_right_x" value="2510" />
+		<variable name="cancel_button_y" value="430" />
+		<variable name="terminal_console_y" value="0" />
+		<variable name="terminal_console_height" value="810" />
+		<variable name="terminal_text_y" value="837" />
+		<variable name="terminal_button_y" value="815" />
+		<variable name="terminal_input_width" value="2100" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="1280" />
+		<variable name="button_fill_main_width" value="1200" />
+		<variable name="button_fill_main_height" value="320" />
+		<variable name="button_fill_half_height" value="107" />
+		<variable name="button_fill_quarter_height" value="80" />
+		<variable name="button_full_center_x" value="640" />
+		<variable name="backup_list_x" value="50" />
+		<variable name="backup_list_y" value="210" />
+		<variable name="backup_list_width" value="1200" />
+		<variable name="backup_list_height" value="880" />
+		<variable name="backup_storage_y" value="540" />
+		<variable name="backup_encrypt_y" value="650" />
+		<variable name="restore_list_y" value="280" />
+		<variable name="restore_list_height" value="800" />
+		<variable name="mount_list_height" value="1200" />
+		<variable name="mount_storage_row" value="1100" />
+		<variable name="wipe_list_height" value="1100" />
+		<variable name="wipe_button_y" value="500" />
+		<variable name="slidervalue_x" value="640" />
+		<variable name="slidervalue_w" value="1280" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="4" />
+		<variable name="slidervalue_padding" value="0" />
+		<variable name="slidervalue_sliderw" value="30" />
+		<variable name="slidervalue_sliderh" value="90" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="base" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="400" y="8" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="400" y="55" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery Level: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="900" y="55" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="1650" y="55" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="1100" y="55" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="flash_zip_console">
+			<object type="console">
+				<placement x="%console_x%" y="195" w="%console_width%" h="950" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="400" w="%console_width%" h="800" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="console_button" x="%console_button_x%" y="%console_button_y%" />
+				<placement x="%console_x%" y="195" w="%console_width%" h="1335" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="912" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="172" width="212" />
+					<row1 key01="204:" key02="q" long02="1" key03="w" long03="2" key04="e" long04="3" key05="r" long05="4" key06="t" long06="5" key07="y" long07="6" key08="u" long08="7" key09="i" long09="8" key10="o" long10="9" key11="p" long11="0" key12="236:c:8" />
+					<row2 key01="286:layout3" key02="208:a" key03="208:s" key04="208:d" key05="208:f" key06="208:g" key07="208:h" key08="208:j" key09="208:k" key10="208:l" key11="402:action" />
+					<row3 key01="354:layout2" key02="206:z" key03="206:x" key04="206:c" key05="206:v" key06="206:b" key07="206:n" key08="206:m" key09="206:," long09="!" key10="206:." long10="?" key11="352:layout2" />
+					<row4 key01="560:" key02="206:/" long02="@" key03="1030: " key04="206:'" long04="206:c:34" key05="212:-" long05="212:_" />
+				</layout1>
+				<layout2>
+					<keysize height="172" width="212" capslock="0" revert_layout="1" />
+					<row1 key01="204:" key02="Q" long02="1" key03="W" long03="2" key04="E" long04="3" key05="R" long05="4" key06="T" long06="5" key07="Y" long07="6" key08="U" long08="7" key09="I" long09="8" key10="O" long10="9" key11="P" long11="0" key12="236:c:8" />
+					<row2 key01="286:layout3" key02="208:A" key03="208:S" key04="208:D" key05="208:F" key06="208:G" key07="208:H" key08="208:J" key09="208:K" key10="208:L" key11="402:action" />
+					<row3 key01="354:layout1" key02="206:Z" key03="206:X" key04="206:C" key05="206:V" key06="206:B" key07="206:N" key08="206:M" key09="206:," long09="!" key10="206:." long10="?" key11="352:layout1" />
+					<row4 key01="560:" key02="206:/" long02="@" key03="1030: " key04="206:'" long04="206:c:34" key05="-" long05="_" />
+				</layout2>
+				<layout3>
+					<keysize height="172" width="212" />
+					<row1 key01="204:" key02="1" key03="2" key04="3" key05="4" key06="5" key07="6" key08="7" key09="8" key10="9" key11="0" key12="236:c:8" />
+					<row2 key01="286:layout1" key02="208:#" key03="208:$" key04="208:%" key05="208:&" key06="208:*" key07="208:-" key08="208:+" key09="208:(" key10="208:)" key11="402:action" />
+					<row3 key01="354:layout4" key02="206:<" key03="206:>" key04="206:=" key05="206:'" key06="206:;" key07="206:," key08="206:." key09="206:!" key10="206:?" key11="352:layout4" />
+					<row4 key01="354:" key02="206:/" key03="206:@" key04="1030: " key05="206:c:34" key06="206:_" />
+				</layout3>
+				<layout4>
+					<keysize height="172" width="212" />
+					<row1 key01="204:" key02="~" key03="`" key04="|" key05="212:" key06="212:" key07="212:" key08="212:" key09="212:" key10="212:" key11="212:" key12="236:c:8" />
+					<row2 key01="286:layout1" key02="208:" key03="208:" key04="208:" key05="208:" key06="208:^" key07="208:" key08="208:" key09="208:{" key10="208:}" key11="402:action" />
+					<row3 key01="354:layout3" key02="206:\" key03="206:" key04="206:" key05="206:" key06="206:" key07="206:[" key08="206:]" key09="206:!" key10="206:?" key11="352:layout3" />
+					<row4 key01="766:" key02="1030: " />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/280x280/res/fonts/Roboto-Condensed-12.dat b/gui/devices/280x280/res/fonts/Roboto-Condensed-12.dat
new file mode 100644
index 0000000..b48c4f2
--- /dev/null
+++ b/gui/devices/280x280/res/fonts/Roboto-Condensed-12.dat
Binary files differ
diff --git a/gui/devices/280x280/res/images/back-icon.png b/gui/devices/280x280/res/images/back-icon.png
new file mode 100644
index 0000000..a136c5d
--- /dev/null
+++ b/gui/devices/280x280/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/checkbox_checked.png b/gui/devices/280x280/res/images/checkbox_checked.png
new file mode 100644
index 0000000..82cde7e
--- /dev/null
+++ b/gui/devices/280x280/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/checkbox_empty.png b/gui/devices/280x280/res/images/checkbox_empty.png
new file mode 100644
index 0000000..720a810
--- /dev/null
+++ b/gui/devices/280x280/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/curtain.jpg b/gui/devices/280x280/res/images/curtain.jpg
new file mode 100644
index 0000000..c0865b8
--- /dev/null
+++ b/gui/devices/280x280/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/280x280/res/images/file.png b/gui/devices/280x280/res/images/file.png
new file mode 100644
index 0000000..dcc7374
--- /dev/null
+++ b/gui/devices/280x280/res/images/file.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/folder.png b/gui/devices/280x280/res/images/folder.png
new file mode 100644
index 0000000..6fb128f
--- /dev/null
+++ b/gui/devices/280x280/res/images/folder.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/home-icon.png b/gui/devices/280x280/res/images/home-icon.png
new file mode 100644
index 0000000..d24bd26
--- /dev/null
+++ b/gui/devices/280x280/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/indeterminate001.png b/gui/devices/280x280/res/images/indeterminate001.png
new file mode 100644
index 0000000..0b2b9b5
--- /dev/null
+++ b/gui/devices/280x280/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/indeterminate002.png b/gui/devices/280x280/res/images/indeterminate002.png
new file mode 100644
index 0000000..dd94cb6
--- /dev/null
+++ b/gui/devices/280x280/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/indeterminate003.png b/gui/devices/280x280/res/images/indeterminate003.png
new file mode 100644
index 0000000..53716cd
--- /dev/null
+++ b/gui/devices/280x280/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/indeterminate004.png b/gui/devices/280x280/res/images/indeterminate004.png
new file mode 100644
index 0000000..71a6b58
--- /dev/null
+++ b/gui/devices/280x280/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/indeterminate005.png b/gui/devices/280x280/res/images/indeterminate005.png
new file mode 100644
index 0000000..bda971c
--- /dev/null
+++ b/gui/devices/280x280/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/indeterminate006.png b/gui/devices/280x280/res/images/indeterminate006.png
new file mode 100644
index 0000000..bfd38e2
--- /dev/null
+++ b/gui/devices/280x280/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/keyboard1.png b/gui/devices/280x280/res/images/keyboard1.png
new file mode 100644
index 0000000..b6ce15d
--- /dev/null
+++ b/gui/devices/280x280/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/keyboard2.png b/gui/devices/280x280/res/images/keyboard2.png
new file mode 100644
index 0000000..2c0a99e
--- /dev/null
+++ b/gui/devices/280x280/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/keyboard3.png b/gui/devices/280x280/res/images/keyboard3.png
new file mode 100644
index 0000000..3a3f898
--- /dev/null
+++ b/gui/devices/280x280/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/keyboard4.png b/gui/devices/280x280/res/images/keyboard4.png
new file mode 100644
index 0000000..cba351a
--- /dev/null
+++ b/gui/devices/280x280/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/medium-button.png b/gui/devices/280x280/res/images/medium-button.png
new file mode 100644
index 0000000..4422c16
--- /dev/null
+++ b/gui/devices/280x280/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/menu-button.png b/gui/devices/280x280/res/images/menu-button.png
new file mode 100644
index 0000000..2dd0428
--- /dev/null
+++ b/gui/devices/280x280/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/minus-button.png b/gui/devices/280x280/res/images/minus-button.png
new file mode 100644
index 0000000..345139e
--- /dev/null
+++ b/gui/devices/280x280/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/plus-button.png b/gui/devices/280x280/res/images/plus-button.png
new file mode 100644
index 0000000..a44f764
--- /dev/null
+++ b/gui/devices/280x280/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/progress_empty.png b/gui/devices/280x280/res/images/progress_empty.png
new file mode 100644
index 0000000..cdbb9b0
--- /dev/null
+++ b/gui/devices/280x280/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/progress_fill.png b/gui/devices/280x280/res/images/progress_fill.png
new file mode 100644
index 0000000..eb4cdca
--- /dev/null
+++ b/gui/devices/280x280/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/radio_empty.png b/gui/devices/280x280/res/images/radio_empty.png
new file mode 100644
index 0000000..f4cfa4f
--- /dev/null
+++ b/gui/devices/280x280/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/radio_selected.png b/gui/devices/280x280/res/images/radio_selected.png
new file mode 100644
index 0000000..d92f3ce
--- /dev/null
+++ b/gui/devices/280x280/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/slideout.png b/gui/devices/280x280/res/images/slideout.png
new file mode 100644
index 0000000..14508c8
--- /dev/null
+++ b/gui/devices/280x280/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/slider-touch.png b/gui/devices/280x280/res/images/slider-touch.png
new file mode 100644
index 0000000..25d5e1c
--- /dev/null
+++ b/gui/devices/280x280/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/slider-used.png b/gui/devices/280x280/res/images/slider-used.png
new file mode 100644
index 0000000..13f1c03
--- /dev/null
+++ b/gui/devices/280x280/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/slider.png b/gui/devices/280x280/res/images/slider.png
new file mode 100644
index 0000000..283f0a3
--- /dev/null
+++ b/gui/devices/280x280/res/images/slider.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/sort-button.png b/gui/devices/280x280/res/images/sort-button.png
new file mode 100644
index 0000000..229f392
--- /dev/null
+++ b/gui/devices/280x280/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/280x280/res/images/top-bar.jpg b/gui/devices/280x280/res/images/top-bar.jpg
new file mode 100644
index 0000000..5768497
--- /dev/null
+++ b/gui/devices/280x280/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/280x280/res/images/unlock.png b/gui/devices/280x280/res/images/unlock.png
new file mode 100644
index 0000000..1867559
--- /dev/null
+++ b/gui/devices/280x280/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/280x280/res/ui.xml b/gui/devices/280x280/res/ui.xml
new file mode 100644
index 0000000..971e637
--- /dev/null
+++ b/gui/devices/280x280/res/ui.xml
@@ -0,0 +1,415 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="280" height="280" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="watch.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="12" fallback="Roboto-Condensed-12" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="12" fallback="Roboto-Condensed-12" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="12" fallback="Roboto-Condensed-12" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="6" />
+		<variable name="col2_x" value="148" />
+		<variable name="col_center_x" value="77" />
+		<variable name="col_center_medium_x" value="115" />
+		<variable name="center_x" value="140" />
+		<variable name="row1_home_y" value="40" />
+		<variable name="row2_home_y" value="95" />
+		<variable name="row3_home_y" value="150" />
+		<variable name="row4_home_y" value="205" />
+		<variable name="row1_y" value="25" />
+		<variable name="row2_y" value="85" />
+		<variable name="row3_y" value="145" />
+		<variable name="row4_y" value="205" />
+		<variable name="row_queue_y" value="164" />
+		<variable name="row1_header_y" value="0" />
+		<variable name="row1_text_y" value="19" />
+		<variable name="row2_text_y" value="38" />
+		<variable name="row3_text_y" value="57" />
+		<variable name="row4_text_y" value="76" />
+		<variable name="row5_text_y" value="95" />
+		<variable name="row6_text_y" value="114" />
+		<variable name="row7_text_y" value="133" />
+		<variable name="row8_text_y" value="152" />
+		<variable name="row9_text_y" value="171" />
+		<variable name="row10_text_y" value="190" />
+		<variable name="row11_text_y" value="209" />
+		<variable name="row12_text_y" value="228" />
+		<variable name="row13_text_y" value="237" />
+		<variable name="zip_status_y" value="265" />
+		<variable name="backup_text_y" value="50" />
+		<variable name="col_progressbar_x" value="31" />
+		<variable name="row_progressbar_y" value="250" />
+		<variable name="col1_medium_x" value="6" />
+		<variable name="col2_medium_x" value="79" />
+		<variable name="col3_medium_x" value="150" />
+		<variable name="col4_medium_x" value="223" />
+		<variable name="row1_medium_y" value="24" />
+		<variable name="row2_medium_y" value="64" />
+		<variable name="row3_medium_y" value="104" />
+		<variable name="row4_medium_y" value="144" />
+		<variable name="row5_medium_y" value="184" />
+		<variable name="row6_medium_y" value="224" />
+		<variable name="row7_medium_y" value="264" />
+		<variable name="slider_x" value="40" />
+		<variable name="slider_y" value="234" />
+		<variable name="slider_text_y" value="254" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="3" />
+		<variable name="home_button_y" value="262" />
+		<variable name="back_button_x" value="244" />
+		<variable name="back_button_y" value="262" />
+		<variable name="sort_text_x" value="16" />
+		<variable name="sort_asc_text_y" value="205" />
+		<variable name="sort_asc_button_y" value="203" />
+		<variable name="sort_desc_text_y" value="230" />
+		<variable name="sort_desc_button_y" value="228" />
+		<variable name="sort_col1_button_x" value="125" />
+		<variable name="sort_col2_button_x" value="175" />
+		<variable name="sort_col3_button_x" value="225" />
+		<variable name="input_width" value="268" />
+		<variable name="input_width_conf" value="10" />
+		<variable name="input_height" value="18" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="2" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="280" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="140" />
+		<variable name="console_install_height" value="140" />
+		<variable name="console_installdone_height" value="140" />
+		<variable name="fileselector_x" value="0" />
+		<variable name="fileselector_y" value="44" />
+		<variable name="fileselector_width" value="280" />
+		<variable name="fileselector_install_height" value="152" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="2" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="1" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="12" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="32" />
+		<variable name="fastscroll_linew" value="1" />
+		<variable name="fastscroll_rectw" value="18" />
+		<variable name="fastscroll_recth" value="31" />
+		<variable name="listbox_x" value="0" />
+		<variable name="listbox_width" value="280" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="12" />
+		<variable name="sd_plus_x" value="174" />
+		<variable name="sdext_text_x" value="56" />
+		<variable name="sdext_text_y" value="30" />
+		<variable name="sdswap_button_y" value="60" />
+		<variable name="sdswap_text_x" value="56" />
+		<variable name="sdswap_text_y" value="63" />
+		<variable name="sdfilesystem_text_y" value="92" />
+		<variable name="sdfilesystem_button_y" value="120" />
+		<variable name="lock_x" value="96" />
+		<variable name="lock_y" value="70" />
+		<variable name="filemanager_select_x" value="115" />
+		<variable name="filemanager_select_y" value="250" />
+		<variable name="terminal_console_height" value="114" />
+		<variable name="terminal_text_y" value="116" />
+		<variable name="terminal_button_y" value="250" />
+		<variable name="listbox_tz_height" value="139" />
+		<variable name="row_dst_text_y" value="142" />
+		<variable name="row_offset_text_y" value="163" />
+		<variable name="row_offset_medium_y" value="179" />
+		<variable name="tz_set_y" value="237" />
+		<variable name="tz_current_y" value="221" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="268" />
+		<variable name="button_fill_main_width" value="126" />
+		<variable name="button_fill_med_width" value="65" />
+		<variable name="button_fill_small_width" value="32" />
+		<variable name="button_fill_main_height" value="40" />
+		<variable name="button_fill_half_height" value="20" />
+		<variable name="button_fill_quarter_height" value="20" />
+		<variable name="button_refresh_x" value="152" />
+		<variable name="backup_list_y" value="26" />
+		<variable name="backup_list_height" value="154" />
+		<variable name="restore_list_height" value="180" />
+		<variable name="backup_button_row1" value="184" />
+		<variable name="backup_button_row2" value="208" />
+		<variable name="mount_list_height" value="170" />
+		<variable name="mount_storage_row" value="175" />
+		<variable name="storage_list_height" value="134" />
+		<variable name="wipe_list_height" value="194" />
+		<variable name="wipe_button_y" value="170" />
+		<variable name="slidervalue_w" value="268" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="1" />
+		<variable name="slidervalue_padding" value="8" />
+		<variable name="slidervalue_sliderw" value="4" />
+		<variable name="slidervalue_sliderh" value="20" />
+		<variable name="wipe_button_row1" value="210" />
+		<variable name="invalid_partition_y" value="195" />
+	</variables>
+
+	<templates>
+		<template name="twrpheader">
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="3" y="3" />
+				<text>TWRP v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="3" y="21" />
+				<text>SIMULATION</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="95" y="3" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="277" y="3" placement="1" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>%tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="160" y="3" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+		</template>
+
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="%row2_y%" w="%console_width%" h="%console_action_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="113" y="255" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="255" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="136" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="32" width="28" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="42:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="42:l" />
+					<row3 key01="42:layout2" key02="z" key03="x" key04="c" key05="v" key06="b" key07="n" key08="m" key09="42:c:8" />
+					<row4 key01="42:layout3" key02="28:," key03="140: " key04="28:." key05="42:a:action" />
+				</layout1>
+				<layout2>
+					<keysize height="32" width="28" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="42:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="42:L" />
+					<row3 key01="42:layout1" key02="Z" key03="X" key04="C" key05="V" key06="B" key07="N" key08="M" key09="42:c:8" />
+					<row4 key01="42:layout3" key02="28:," key03="140: " key04="28:." key05="42:action" />
+				</layout2>
+				<layout3>
+					<keysize height="32" width="28" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="42:layout4" key02="!" key03="28:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="42:c:8" />
+					<row4 key01="42:layout1" key02="28:," key03="140: " key04="28:." key05="42:action" />
+				</layout3>
+				<layout4>
+					<keysize height="32" width="28" />
+					<row1 key01="~" key02="`" key03="|" key04="28:" key05="28:" key06="28:" key07="%" key08="28:" key09="{" key10="}" />
+					<row2 key01="28:" key02="28:" key03="28:" key04="28:" key05="28:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="42:layout3" key02="28:" key03="28:" key04="28:" key05="28:" key06="\" key07="<" key08=">" key09="42:c:8" />
+					<row4 key01="42:layout1" key02="28:c:34" key03="140: " key04="28:." key05="42:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/320x320/res/fonts/Roboto-Condensed-14.dat b/gui/devices/320x320/res/fonts/Roboto-Condensed-14.dat
new file mode 100644
index 0000000..f7b174c
--- /dev/null
+++ b/gui/devices/320x320/res/fonts/Roboto-Condensed-14.dat
Binary files differ
diff --git a/gui/devices/320x320/res/images/back-icon.png b/gui/devices/320x320/res/images/back-icon.png
new file mode 100644
index 0000000..fa365f7
--- /dev/null
+++ b/gui/devices/320x320/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/checkbox_checked.png b/gui/devices/320x320/res/images/checkbox_checked.png
new file mode 100644
index 0000000..81037cb
--- /dev/null
+++ b/gui/devices/320x320/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/checkbox_empty.png b/gui/devices/320x320/res/images/checkbox_empty.png
new file mode 100644
index 0000000..94bb057
--- /dev/null
+++ b/gui/devices/320x320/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/curtain.jpg b/gui/devices/320x320/res/images/curtain.jpg
new file mode 100644
index 0000000..0368a65
--- /dev/null
+++ b/gui/devices/320x320/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/320x320/res/images/file.png b/gui/devices/320x320/res/images/file.png
new file mode 100644
index 0000000..81ba381
--- /dev/null
+++ b/gui/devices/320x320/res/images/file.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/folder.png b/gui/devices/320x320/res/images/folder.png
new file mode 100644
index 0000000..feeffe1
--- /dev/null
+++ b/gui/devices/320x320/res/images/folder.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/home-icon.png b/gui/devices/320x320/res/images/home-icon.png
new file mode 100644
index 0000000..444ffa5
--- /dev/null
+++ b/gui/devices/320x320/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/indeterminate001.png b/gui/devices/320x320/res/images/indeterminate001.png
new file mode 100644
index 0000000..4a80bc5
--- /dev/null
+++ b/gui/devices/320x320/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/indeterminate002.png b/gui/devices/320x320/res/images/indeterminate002.png
new file mode 100644
index 0000000..a0b1a03
--- /dev/null
+++ b/gui/devices/320x320/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/indeterminate003.png b/gui/devices/320x320/res/images/indeterminate003.png
new file mode 100644
index 0000000..c59c242
--- /dev/null
+++ b/gui/devices/320x320/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/indeterminate004.png b/gui/devices/320x320/res/images/indeterminate004.png
new file mode 100644
index 0000000..0d7a3a4
--- /dev/null
+++ b/gui/devices/320x320/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/indeterminate005.png b/gui/devices/320x320/res/images/indeterminate005.png
new file mode 100644
index 0000000..a9a6638
--- /dev/null
+++ b/gui/devices/320x320/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/indeterminate006.png b/gui/devices/320x320/res/images/indeterminate006.png
new file mode 100644
index 0000000..386abba
--- /dev/null
+++ b/gui/devices/320x320/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/keyboard1.png b/gui/devices/320x320/res/images/keyboard1.png
new file mode 100644
index 0000000..49e2d50
--- /dev/null
+++ b/gui/devices/320x320/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/keyboard2.png b/gui/devices/320x320/res/images/keyboard2.png
new file mode 100644
index 0000000..bf7668a
--- /dev/null
+++ b/gui/devices/320x320/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/keyboard3.png b/gui/devices/320x320/res/images/keyboard3.png
new file mode 100644
index 0000000..ff4d63e
--- /dev/null
+++ b/gui/devices/320x320/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/keyboard4.png b/gui/devices/320x320/res/images/keyboard4.png
new file mode 100644
index 0000000..5939b52
--- /dev/null
+++ b/gui/devices/320x320/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/medium-button.png b/gui/devices/320x320/res/images/medium-button.png
new file mode 100644
index 0000000..379eb86
--- /dev/null
+++ b/gui/devices/320x320/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/menu-button.png b/gui/devices/320x320/res/images/menu-button.png
new file mode 100644
index 0000000..109a148
--- /dev/null
+++ b/gui/devices/320x320/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/minus-button.png b/gui/devices/320x320/res/images/minus-button.png
new file mode 100644
index 0000000..2f1b27f
--- /dev/null
+++ b/gui/devices/320x320/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/plus-button.png b/gui/devices/320x320/res/images/plus-button.png
new file mode 100644
index 0000000..f223a98
--- /dev/null
+++ b/gui/devices/320x320/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/progress_empty.png b/gui/devices/320x320/res/images/progress_empty.png
new file mode 100644
index 0000000..6d906d2
--- /dev/null
+++ b/gui/devices/320x320/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/progress_fill.png b/gui/devices/320x320/res/images/progress_fill.png
new file mode 100644
index 0000000..c4b5903
--- /dev/null
+++ b/gui/devices/320x320/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/radio_empty.png b/gui/devices/320x320/res/images/radio_empty.png
new file mode 100644
index 0000000..0766a30
--- /dev/null
+++ b/gui/devices/320x320/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/radio_selected.png b/gui/devices/320x320/res/images/radio_selected.png
new file mode 100644
index 0000000..cf0d925
--- /dev/null
+++ b/gui/devices/320x320/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/slideout.png b/gui/devices/320x320/res/images/slideout.png
new file mode 100644
index 0000000..df7a0fa
--- /dev/null
+++ b/gui/devices/320x320/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/slider-touch.png b/gui/devices/320x320/res/images/slider-touch.png
new file mode 100644
index 0000000..e069c0fb
--- /dev/null
+++ b/gui/devices/320x320/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/slider-used.png b/gui/devices/320x320/res/images/slider-used.png
new file mode 100644
index 0000000..bd37e54
--- /dev/null
+++ b/gui/devices/320x320/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/slider.png b/gui/devices/320x320/res/images/slider.png
new file mode 100644
index 0000000..d735417
--- /dev/null
+++ b/gui/devices/320x320/res/images/slider.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/sort-button.png b/gui/devices/320x320/res/images/sort-button.png
new file mode 100644
index 0000000..07b1b24
--- /dev/null
+++ b/gui/devices/320x320/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/320x320/res/images/top-bar.jpg b/gui/devices/320x320/res/images/top-bar.jpg
new file mode 100644
index 0000000..5c02ec8
--- /dev/null
+++ b/gui/devices/320x320/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/320x320/res/images/unlock.png b/gui/devices/320x320/res/images/unlock.png
new file mode 100644
index 0000000..c6b5cff
--- /dev/null
+++ b/gui/devices/320x320/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/320x320/res/ui.xml b/gui/devices/320x320/res/ui.xml
new file mode 100644
index 0000000..4678e85
--- /dev/null
+++ b/gui/devices/320x320/res/ui.xml
@@ -0,0 +1,415 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="320" height="320" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="watch.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="14" fallback="Roboto-Condensed-14" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="14" fallback="Roboto-Condensed-14" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="14" fallback="Roboto-Condensed-14" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="6" />
+		<variable name="col2_x" value="170" />
+		<variable name="col_center_x" value="88" />
+		<variable name="col_center_medium_x" value="130" />
+		<variable name="center_x" value="160" />
+		<variable name="row1_home_y" value="50" />
+		<variable name="row2_home_y" value="110" />
+		<variable name="row3_home_y" value="170" />
+		<variable name="row4_home_y" value="230" />
+		<variable name="row1_y" value="30" />
+		<variable name="row2_y" value="100" />
+		<variable name="row3_y" value="170" />
+		<variable name="row4_y" value="240" />
+		<variable name="row_queue_y" value="184" />
+		<variable name="row1_header_y" value="0" />
+		<variable name="row1_text_y" value="22" />
+		<variable name="row2_text_y" value="44" />
+		<variable name="row3_text_y" value="66" />
+		<variable name="row4_text_y" value="88" />
+		<variable name="row5_text_y" value="110" />
+		<variable name="row6_text_y" value="132" />
+		<variable name="row7_text_y" value="154" />
+		<variable name="row8_text_y" value="176" />
+		<variable name="row9_text_y" value="198" />
+		<variable name="row10_text_y" value="220" />
+		<variable name="row11_text_y" value="242" />
+		<variable name="row12_text_y" value="284" />
+		<variable name="row13_text_y" value="306" />
+		<variable name="zip_status_y" value="300" />
+		<variable name="backup_text_y" value="60" />
+		<variable name="col_progressbar_x" value="34" />
+		<variable name="row_progressbar_y" value="285" />
+		<variable name="col1_medium_x" value="6" />
+		<variable name="col2_medium_x" value="83" />
+		<variable name="col3_medium_x" value="174" />
+		<variable name="col4_medium_x" value="254" />
+		<variable name="row1_medium_y" value="24" />
+		<variable name="row2_medium_y" value="40" />
+		<variable name="row3_medium_y" value="56" />
+		<variable name="row4_medium_y" value="72" />
+		<variable name="row5_medium_y" value="88" />
+		<variable name="row6_medium_y" value="104" />
+		<variable name="row7_medium_y" value="120" />
+		<variable name="slider_x" value="45" />
+		<variable name="slider_y" value="268" />
+		<variable name="slider_text_y" value="294" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="3" />
+		<variable name="home_button_y" value="300" />
+		<variable name="back_button_x" value="280" />
+		<variable name="back_button_y" value="300" />
+		<variable name="sort_text_x" value="20" />
+		<variable name="sort_asc_text_y" value="232" />
+		<variable name="sort_asc_button_y" value="230" />
+		<variable name="sort_desc_text_y" value="262" />
+		<variable name="sort_desc_button_y" value="260" />
+		<variable name="sort_col1_button_x" value="135" />
+		<variable name="sort_col2_button_x" value="195" />
+		<variable name="sort_col3_button_x" value="255" />
+		<variable name="input_width" value="308" />
+		<variable name="input_width_conf" value="10" />
+		<variable name="input_height" value="18" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="2" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="320" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="160" />
+		<variable name="console_install_height" value="160" />
+		<variable name="console_installdone_height" value="160" />
+		<variable name="fileselector_x" value="0" />
+		<variable name="fileselector_y" value="44" />
+		<variable name="fileselector_width" value="320" />
+		<variable name="fileselector_install_height" value="182" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="2" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="1" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="12" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="32" />
+		<variable name="fastscroll_linew" value="1" />
+		<variable name="fastscroll_rectw" value="18" />
+		<variable name="fastscroll_recth" value="31" />
+		<variable name="listbox_x" value="0" />
+		<variable name="listbox_width" value="320" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="12" />
+		<variable name="sd_plus_x" value="174" />
+		<variable name="sdext_text_x" value="56" />
+		<variable name="sdext_text_y" value="18" />
+		<variable name="sdswap_button_y" value="45" />
+		<variable name="sdswap_text_x" value="56" />
+		<variable name="sdswap_text_y" value="47" />
+		<variable name="sdfilesystem_text_y" value="92" />
+		<variable name="sdfilesystem_button_y" value="120" />
+		<variable name="lock_x" value="96" />
+		<variable name="lock_y" value="70" />
+		<variable name="filemanager_select_x" value="130" />
+		<variable name="filemanager_select_y" value="280" />
+		<variable name="terminal_console_height" value="126" />
+		<variable name="terminal_text_y" value="128" />
+		<variable name="terminal_button_y" value="300" />
+		<variable name="listbox_tz_height" value="159" />
+		<variable name="row_dst_text_y" value="162" />
+		<variable name="row_offset_text_y" value="184" />
+		<variable name="row_offset_medium_y" value="206" />
+		<variable name="tz_set_y" value="271" />
+		<variable name="tz_current_y" value="249" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="308" />
+		<variable name="button_fill_main_width" value="150" />
+		<variable name="button_fill_med_width" value="72" />
+		<variable name="button_fill_small_width" value="36" />
+		<variable name="button_fill_main_height" value="44" />
+		<variable name="button_fill_half_height" value="22" />
+		<variable name="button_fill_quarter_height" value="22" />
+		<variable name="button_refresh_x" value="152" />
+		<variable name="backup_list_y" value="26" />
+		<variable name="backup_list_height" value="184" />
+		<variable name="restore_list_height" value="206" />
+		<variable name="backup_button_row1" value="214" />
+		<variable name="backup_button_row2" value="242" />
+		<variable name="mount_list_height" value="190" />
+		<variable name="mount_storage_row" value="200" />
+		<variable name="storage_list_height" value="134" />
+		<variable name="wipe_list_height" value="216" />
+		<variable name="wipe_button_y" value="170" />
+		<variable name="slidervalue_w" value="308" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="1" />
+		<variable name="slidervalue_padding" value="8" />
+		<variable name="slidervalue_sliderw" value="4" />
+		<variable name="slidervalue_sliderh" value="20" />
+		<variable name="wipe_button_row1" value="240" />
+		<variable name="invalid_partition_y" value="218" />
+	</variables>
+
+	<templates>
+		<template name="twrpheader">
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="3" y="3" />
+				<text>TWRP v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="3" y="21" />
+				<text>SIMULATION</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="110" y="3" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="316" y="3" placement="1" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>%tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="190" y="3" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+		</template>
+
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="%row2_y%" w="%console_width%" h="%console_action_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="130" y="288" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="288" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="150" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="36" width="32" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="48:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="48:l" />
+					<row3 key01="48:layout2" key02="z" key03="x" key04="c" key05="v" key06="b" key07="n" key08="m" key09="48:c:8" />
+					<row4 key01="48:layout3" key02="32:," key03="160: " key04="32:." key05="48:a:action" />
+				</layout1>
+				<layout2>
+					<keysize height="36" width="32" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="48:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="48:L" />
+					<row3 key01="48:layout1" key02="Z" key03="X" key04="C" key05="V" key06="B" key07="N" key08="M" key09="48:c:8" />
+					<row4 key01="48:layout3" key02="32:," key03="160: " key04="32:." key05="48:action" />
+				</layout2>
+				<layout3>
+					<keysize height="36" width="32" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="48:layout4" key02="!" key03="32:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="48:c:8" />
+					<row4 key01="48:layout1" key02="32:," key03="160: " key04="32:." key05="48:action" />
+				</layout3>
+				<layout4>
+					<keysize height="36" width="32" />
+					<row1 key01="~" key02="`" key03="|" key04="32:" key05="32:" key06="32:" key07="%" key08="32:" key09="{" key10="}" />
+					<row2 key01="32:" key02="32:" key03="32:" key04="32:" key05="32:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="48:layout3" key02="32:" key03="32:" key04="32:" key05="32:" key06="\" key07="<" key08=">" key09="48:c:8" />
+					<row4 key01="48:layout1" key02="32:c:34" key03="160: " key04="32:." key05="48:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/320x480/res/fonts/Roboto-Condensed-14.dat b/gui/devices/320x480/res/fonts/Roboto-Condensed-14.dat
new file mode 100644
index 0000000..f7b174c
--- /dev/null
+++ b/gui/devices/320x480/res/fonts/Roboto-Condensed-14.dat
Binary files differ
diff --git a/gui/devices/320x480/res/fonts/Roboto-Condensed-16.dat b/gui/devices/320x480/res/fonts/Roboto-Condensed-16.dat
new file mode 100644
index 0000000..19c1147
--- /dev/null
+++ b/gui/devices/320x480/res/fonts/Roboto-Condensed-16.dat
Binary files differ
diff --git a/gui/devices/320x480/res/images/android.png b/gui/devices/320x480/res/images/android.png
new file mode 100644
index 0000000..0ccd02c
--- /dev/null
+++ b/gui/devices/320x480/res/images/android.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/back-icon.png b/gui/devices/320x480/res/images/back-icon.png
new file mode 100644
index 0000000..26778d9
--- /dev/null
+++ b/gui/devices/320x480/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/checkbox_checked.png b/gui/devices/320x480/res/images/checkbox_checked.png
new file mode 100644
index 0000000..620fee8
--- /dev/null
+++ b/gui/devices/320x480/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/checkbox_empty.png b/gui/devices/320x480/res/images/checkbox_empty.png
new file mode 100644
index 0000000..fa86e0d
--- /dev/null
+++ b/gui/devices/320x480/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/cursor.png b/gui/devices/320x480/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/320x480/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/curtain.jpg b/gui/devices/320x480/res/images/curtain.jpg
new file mode 100644
index 0000000..63c9619
--- /dev/null
+++ b/gui/devices/320x480/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/320x480/res/images/file.png b/gui/devices/320x480/res/images/file.png
new file mode 100644
index 0000000..8556bc7
--- /dev/null
+++ b/gui/devices/320x480/res/images/file.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/folder.png b/gui/devices/320x480/res/images/folder.png
new file mode 100644
index 0000000..a3a5f69
--- /dev/null
+++ b/gui/devices/320x480/res/images/folder.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/hdpi-medium-button.png b/gui/devices/320x480/res/images/hdpi-medium-button.png
new file mode 100644
index 0000000..cb223b1
--- /dev/null
+++ b/gui/devices/320x480/res/images/hdpi-medium-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/hdpi-menu-button.png b/gui/devices/320x480/res/images/hdpi-menu-button.png
new file mode 100644
index 0000000..1fa62cf
--- /dev/null
+++ b/gui/devices/320x480/res/images/hdpi-menu-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/hdpi-minus-button.png b/gui/devices/320x480/res/images/hdpi-minus-button.png
new file mode 100644
index 0000000..025b379
--- /dev/null
+++ b/gui/devices/320x480/res/images/hdpi-minus-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/hdpi-plus-button.png b/gui/devices/320x480/res/images/hdpi-plus-button.png
new file mode 100644
index 0000000..f567e1d
--- /dev/null
+++ b/gui/devices/320x480/res/images/hdpi-plus-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/hdpi-small-button.png b/gui/devices/320x480/res/images/hdpi-small-button.png
new file mode 100644
index 0000000..3694098
--- /dev/null
+++ b/gui/devices/320x480/res/images/hdpi-small-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/hdpi-sort-button.png b/gui/devices/320x480/res/images/hdpi-sort-button.png
new file mode 100644
index 0000000..3662f23
--- /dev/null
+++ b/gui/devices/320x480/res/images/hdpi-sort-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/home-icon.png b/gui/devices/320x480/res/images/home-icon.png
new file mode 100644
index 0000000..690bfaf
--- /dev/null
+++ b/gui/devices/320x480/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/indeterminate001.png b/gui/devices/320x480/res/images/indeterminate001.png
new file mode 100644
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/320x480/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/indeterminate002.png b/gui/devices/320x480/res/images/indeterminate002.png
new file mode 100644
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/320x480/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/indeterminate003.png b/gui/devices/320x480/res/images/indeterminate003.png
new file mode 100644
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/320x480/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/indeterminate004.png b/gui/devices/320x480/res/images/indeterminate004.png
new file mode 100644
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/320x480/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/indeterminate005.png b/gui/devices/320x480/res/images/indeterminate005.png
new file mode 100644
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/320x480/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/indeterminate006.png b/gui/devices/320x480/res/images/indeterminate006.png
new file mode 100644
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/320x480/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/keyboard1.png b/gui/devices/320x480/res/images/keyboard1.png
new file mode 100644
index 0000000..914304c
--- /dev/null
+++ b/gui/devices/320x480/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/keyboard2.png b/gui/devices/320x480/res/images/keyboard2.png
new file mode 100644
index 0000000..4761f3a
--- /dev/null
+++ b/gui/devices/320x480/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/keyboard3.png b/gui/devices/320x480/res/images/keyboard3.png
new file mode 100644
index 0000000..23103dc
--- /dev/null
+++ b/gui/devices/320x480/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/keyboard4.png b/gui/devices/320x480/res/images/keyboard4.png
new file mode 100644
index 0000000..58c7289
--- /dev/null
+++ b/gui/devices/320x480/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/medium-button.png b/gui/devices/320x480/res/images/medium-button.png
new file mode 100644
index 0000000..eee6cce
--- /dev/null
+++ b/gui/devices/320x480/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/menu-button.png b/gui/devices/320x480/res/images/menu-button.png
new file mode 100644
index 0000000..731453b
--- /dev/null
+++ b/gui/devices/320x480/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/minus-button.png b/gui/devices/320x480/res/images/minus-button.png
new file mode 100644
index 0000000..21cd774
--- /dev/null
+++ b/gui/devices/320x480/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/plus-button.png b/gui/devices/320x480/res/images/plus-button.png
new file mode 100644
index 0000000..ce9ec4e
--- /dev/null
+++ b/gui/devices/320x480/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/progress_empty.png b/gui/devices/320x480/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/320x480/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/progress_fill.png b/gui/devices/320x480/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/320x480/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/radio_empty.png b/gui/devices/320x480/res/images/radio_empty.png
new file mode 100644
index 0000000..d993c4a
--- /dev/null
+++ b/gui/devices/320x480/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/radio_selected.png b/gui/devices/320x480/res/images/radio_selected.png
new file mode 100644
index 0000000..c89aac3
--- /dev/null
+++ b/gui/devices/320x480/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/slideout.png b/gui/devices/320x480/res/images/slideout.png
new file mode 100644
index 0000000..8974fef
--- /dev/null
+++ b/gui/devices/320x480/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/slider-touch.png b/gui/devices/320x480/res/images/slider-touch.png
new file mode 100644
index 0000000..2f36fa7
--- /dev/null
+++ b/gui/devices/320x480/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/slider-used.png b/gui/devices/320x480/res/images/slider-used.png
new file mode 100644
index 0000000..94a09f3
--- /dev/null
+++ b/gui/devices/320x480/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/slider.png b/gui/devices/320x480/res/images/slider.png
new file mode 100644
index 0000000..5cbb27e
--- /dev/null
+++ b/gui/devices/320x480/res/images/slider.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/sort-button.png b/gui/devices/320x480/res/images/sort-button.png
new file mode 100644
index 0000000..579c75f
--- /dev/null
+++ b/gui/devices/320x480/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/top-bar.jpg b/gui/devices/320x480/res/images/top-bar.jpg
new file mode 100644
index 0000000..84f7f1e
--- /dev/null
+++ b/gui/devices/320x480/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/320x480/res/images/top-bar.png b/gui/devices/320x480/res/images/top-bar.png
new file mode 100644
index 0000000..887f629
--- /dev/null
+++ b/gui/devices/320x480/res/images/top-bar.png
Binary files differ
diff --git a/gui/devices/320x480/res/images/unlock.png b/gui/devices/320x480/res/images/unlock.png
new file mode 100644
index 0000000..09e9efd
--- /dev/null
+++ b/gui/devices/320x480/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/320x480/res/ui.xml b/gui/devices/320x480/res/ui.xml
new file mode 100644
index 0000000..dd4cdbc
--- /dev/null
+++ b/gui/devices/320x480/res/ui.xml
@@ -0,0 +1,413 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="320" height="480" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="14" fallback="Roboto-Condensed-16" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="14" fallback="Roboto-Condensed-14" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="14" fallback="Roboto-Condensed-14" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="8" />
+		<variable name="col2_x" value="162" />
+		<variable name="col_center_x" value="85" />
+		<variable name="col_center_medium_x" value="122" />
+		<variable name="center_x" value="160" />
+		<variable name="row1_y" value="84" />
+		<variable name="row2_y" value="174" />
+		<variable name="row3_y" value="270" />
+		<variable name="row4_y" value="366" />
+		<variable name="row_queue_y" value="306" />
+		<variable name="row1_header_y" value="64" />
+		<variable name="row1_text_y" value="85" />
+		<variable name="row2_text_y" value="104" />
+		<variable name="row3_text_y" value="123" />
+		<variable name="row4_text_y" value="142" />
+		<variable name="row5_text_y" value="161" />
+		<variable name="row6_text_y" value="180" />
+		<variable name="row7_text_y" value="199" />
+		<variable name="row8_text_y" value="218" />
+		<variable name="row9_text_y" value="237" />
+		<variable name="row10_text_y" value="256" />
+		<variable name="row11_text_y" value="275" />
+		<variable name="row12_text_y" value="294" />
+		<variable name="row13_text_y" value="313" />
+		<variable name="row14_text_y" value="332" />
+		<variable name="row15_text_y" value="351" />
+		<variable name="row16_text_y" value="370" />
+		<variable name="row17_text_y" value="389" />
+		<variable name="row18_text_y" value="408" />
+		<variable name="zip_status_y" value="350" />
+		<variable name="tz_selected_y" value="66" />
+		<variable name="tz_set_y" value="348" />
+		<variable name="tz_current_y" value="438" />
+		<variable name="col_progressbar_x" value="32" />
+		<variable name="row_progressbar_y" value="432" />
+		<variable name="col1_medium_x" value="7" />
+		<variable name="col2_medium_x" value="83" />
+		<variable name="col3_medium_x" value="160" />
+		<variable name="col4_medium_x" value="237" />
+		<variable name="row1_medium_y" value="78" />
+		<variable name="row2_medium_y" value="123" />
+		<variable name="row3_medium_y" value="168" />
+		<variable name="row4_medium_y" value="213" />
+		<variable name="row5_medium_y" value="258" />
+		<variable name="row6_medium_y" value="303" />
+		<variable name="row7_medium_y" value="348" />
+		<variable name="slider_x" value="39" />
+		<variable name="slider_y" value="400" />
+		<variable name="slider_text_y" value="425" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="7" />
+		<variable name="home_button_y" value="460" />
+		<variable name="back_button_x" value="275" />
+		<variable name="back_button_y" value="460" />
+		<variable name="sort_text_x" value="8" />
+		<variable name="sort_asc_text_y" value="411" />
+		<variable name="sort_asc_button_y" value="411" />
+		<variable name="sort_desc_text_y" value="435" />
+		<variable name="sort_desc_button_y" value="435" />
+		<variable name="sort_col1_button_x" value="120" />
+		<variable name="sort_col2_button_x" value="160" />
+		<variable name="sort_col3_button_x" value="200" />
+		<variable name="input_width" value="307" />
+		<variable name="input_height" value="24" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="2" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="320" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="192" />
+		<variable name="console_install_height" value="264" />
+		<variable name="console_installdone_height" value="180" />
+		<variable name="fileselector_x" value="3" />
+		<variable name="fileselector_width" value="313" />
+		<variable name="fileselector_install_height" value="284" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="2" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="1" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="12" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="27" />
+		<variable name="fastscroll_linew" value="1" />
+		<variable name="fastscroll_rectw" value="18" />
+		<variable name="fastscroll_recth" value="31" />
+		<variable name="listbox_x" value="3" />
+		<variable name="listbox_width" value="313" />
+		<variable name="listbox_tz_height" value="189" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="12" />
+		<variable name="sd_plus_x" value="187" />
+		<variable name="sdext_text_x" value="56" />
+		<variable name="sdext_text_y" value="83" />
+		<variable name="sdswap_button_y" value="111" />
+		<variable name="sdswap_text_x" value="56" />
+		<variable name="sdswap_text_y" value="110" />
+		<variable name="sdfilesystem_text_y" value="144" />
+		<variable name="sdfilesystem_button_y" value="168" />
+		<variable name="lock_x" value="27" />
+		<variable name="lock_y" value="120" />
+		<variable name="filemanager_select_x" value="243" />
+		<variable name="filemanager_select_y" value="414" />
+		<variable name="terminal_console_height" value="220" />
+		<variable name="terminal_text_y" value="225" />
+		<variable name="terminal_button_y" value="220" />
+		<variable name="row_dst_text_y" value="256" />
+		<variable name="row_offset_text_y" value="275" />
+		<variable name="row_offset_medium_y" value="303" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="304" />
+		<variable name="button_fill_main_width" value="150" />
+		<variable name="button_fill_main_height" value="81" />
+		<variable name="button_fill_half_height" value="40" />
+		<variable name="button_fill_quarter_height" value="24" />
+		<variable name="backup_list_height" value="193" />
+		<variable name="backup_button_row1" value="300" />
+		<variable name="backup_button_row2" value="326" />
+		<variable name="mount_list_height" value="270" />
+		<variable name="mount_storage_row" value="340" />
+		<variable name="storage_list_height" value="290" />
+		<variable name="wipe_list_height" value="270" />
+		<variable name="wipe_button_row1" value="350" />
+		<variable name="wipe_button_y" value="270" />
+		<variable name="slidervalue_w" value="304" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="1" />
+		<variable name="slidervalue_padding" value="8" />
+		<variable name="slidervalue_sliderw" value="4" />
+		<variable name="slidervalue_sliderh" value="20" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="1" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="65" y="3" />
+				<text>TeamWin Recovery v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="65" y="21" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="65" y="39" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="135" y="39" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="265" y="39" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>%tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="%row2_y%" w="%console_width%" h="%console_action_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="mediumfont" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="140" y="456" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="456" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="253" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="52" width="32" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="48:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="48:l" />
+					<row3 key01="48:layout2" key02="z" key03="x" key04="c" key05="v" key06="b" key07="n" key08="m" key09="48:c:8" />
+					<row4 key01="48:layout3" key02="32:" key03="160: " key04="32:." key05="48:a:action" />
+				</layout1>
+				<layout2>
+					<keysize height="52" width="32" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="48:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="48:L" />
+					<row3 key01="48:layout1" key02="Z" key03="X" key04="C" key05="V" key06="B" key07="N" key08="M" key09="48:c:8" />
+					<row4 key01="48:layout3" key02="32:" key03="160: " key04="32:." key05="48:action" />
+				</layout2>
+				<layout3>
+					<keysize height="52" width="32" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="48:layout4" key02="!" key03="32:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="48:c:8" />
+					<row4 key01="48:layout1" key02="32:," key03="160: " key04="32:." key05="48:action" />
+				</layout3>
+				<layout4>
+					<keysize height="52" width="32" />
+					<row1 key01="~" key02="`" key03="|" key04="32:" key05="32:" key06="32:" key07="%" key08="32:" key09="{" key10="}" />
+					<row2 key01="32:" key02="32:" key03="32:" key04="32:" key05="32:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="48:layout3" key02="32:" key03="32:" key04="32:" key05="32:" key06="\" key07="<" key08=">" key09="48:c:8" />
+					<row4 key01="48:layout1" key02="32:c:34" key03="160: " key04="32:." key05="48:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/480x800/res/fonts/Roboto-Regular-20.dat b/gui/devices/480x800/res/fonts/Roboto-Regular-20.dat
new file mode 100644
index 0000000..6588b41
--- /dev/null
+++ b/gui/devices/480x800/res/fonts/Roboto-Regular-20.dat
Binary files differ
diff --git a/gui/devices/480x800/res/images/back-icon.png b/gui/devices/480x800/res/images/back-icon.png
new file mode 100644
index 0000000..688436e
--- /dev/null
+++ b/gui/devices/480x800/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/checkbox_checked.png b/gui/devices/480x800/res/images/checkbox_checked.png
new file mode 100644
index 0000000..3447349
--- /dev/null
+++ b/gui/devices/480x800/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/checkbox_empty.png b/gui/devices/480x800/res/images/checkbox_empty.png
new file mode 100644
index 0000000..f5f35d8
--- /dev/null
+++ b/gui/devices/480x800/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/cursor.png b/gui/devices/480x800/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/480x800/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/curtain.jpg b/gui/devices/480x800/res/images/curtain.jpg
new file mode 100644
index 0000000..dfd59d9
--- /dev/null
+++ b/gui/devices/480x800/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/480x800/res/images/file.png b/gui/devices/480x800/res/images/file.png
new file mode 100644
index 0000000..8556bc7
--- /dev/null
+++ b/gui/devices/480x800/res/images/file.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/folder.png b/gui/devices/480x800/res/images/folder.png
new file mode 100644
index 0000000..a3a5f69
--- /dev/null
+++ b/gui/devices/480x800/res/images/folder.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/home-icon.png b/gui/devices/480x800/res/images/home-icon.png
new file mode 100644
index 0000000..5519520
--- /dev/null
+++ b/gui/devices/480x800/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/indeterminate001.png b/gui/devices/480x800/res/images/indeterminate001.png
new file mode 100644
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/480x800/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/indeterminate002.png b/gui/devices/480x800/res/images/indeterminate002.png
new file mode 100644
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/480x800/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/indeterminate003.png b/gui/devices/480x800/res/images/indeterminate003.png
new file mode 100644
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/480x800/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/indeterminate004.png b/gui/devices/480x800/res/images/indeterminate004.png
new file mode 100644
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/480x800/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/indeterminate005.png b/gui/devices/480x800/res/images/indeterminate005.png
new file mode 100644
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/480x800/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/indeterminate006.png b/gui/devices/480x800/res/images/indeterminate006.png
new file mode 100644
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/480x800/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/keyboard1.png b/gui/devices/480x800/res/images/keyboard1.png
new file mode 100644
index 0000000..5a691ac
--- /dev/null
+++ b/gui/devices/480x800/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/keyboard2.png b/gui/devices/480x800/res/images/keyboard2.png
new file mode 100644
index 0000000..69b0154
--- /dev/null
+++ b/gui/devices/480x800/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/keyboard3.png b/gui/devices/480x800/res/images/keyboard3.png
new file mode 100644
index 0000000..498cef8
--- /dev/null
+++ b/gui/devices/480x800/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/keyboard4.png b/gui/devices/480x800/res/images/keyboard4.png
new file mode 100644
index 0000000..e8f9dde
--- /dev/null
+++ b/gui/devices/480x800/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/medium-button.png b/gui/devices/480x800/res/images/medium-button.png
new file mode 100644
index 0000000..1d5b816
--- /dev/null
+++ b/gui/devices/480x800/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/menu-button.png b/gui/devices/480x800/res/images/menu-button.png
new file mode 100644
index 0000000..c8d3794
--- /dev/null
+++ b/gui/devices/480x800/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/minus-button.png b/gui/devices/480x800/res/images/minus-button.png
new file mode 100644
index 0000000..9c92d2f
--- /dev/null
+++ b/gui/devices/480x800/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/plus-button.png b/gui/devices/480x800/res/images/plus-button.png
new file mode 100644
index 0000000..5d3068e
--- /dev/null
+++ b/gui/devices/480x800/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/progress_empty.png b/gui/devices/480x800/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/480x800/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/progress_fill.png b/gui/devices/480x800/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/480x800/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/radio_empty.png b/gui/devices/480x800/res/images/radio_empty.png
new file mode 100644
index 0000000..88d1c1f
--- /dev/null
+++ b/gui/devices/480x800/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/radio_selected.png b/gui/devices/480x800/res/images/radio_selected.png
new file mode 100644
index 0000000..864065d
--- /dev/null
+++ b/gui/devices/480x800/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/slideout.png b/gui/devices/480x800/res/images/slideout.png
new file mode 100644
index 0000000..963b9fd
--- /dev/null
+++ b/gui/devices/480x800/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/slider-touch.png b/gui/devices/480x800/res/images/slider-touch.png
new file mode 100644
index 0000000..c8c0f43
--- /dev/null
+++ b/gui/devices/480x800/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/slider-used.png b/gui/devices/480x800/res/images/slider-used.png
new file mode 100644
index 0000000..2f3d519
--- /dev/null
+++ b/gui/devices/480x800/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/slider.png b/gui/devices/480x800/res/images/slider.png
new file mode 100644
index 0000000..6fcce6f
--- /dev/null
+++ b/gui/devices/480x800/res/images/slider.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/sort-button.png b/gui/devices/480x800/res/images/sort-button.png
new file mode 100644
index 0000000..69d511b
--- /dev/null
+++ b/gui/devices/480x800/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/480x800/res/images/top-bar.jpg b/gui/devices/480x800/res/images/top-bar.jpg
new file mode 100644
index 0000000..3e0e5b5
--- /dev/null
+++ b/gui/devices/480x800/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/480x800/res/images/unlock.png b/gui/devices/480x800/res/images/unlock.png
new file mode 100644
index 0000000..9041221
--- /dev/null
+++ b/gui/devices/480x800/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/480x800/res/ui.xml b/gui/devices/480x800/res/ui.xml
new file mode 100644
index 0000000..0e0f65d
--- /dev/null
+++ b/gui/devices/480x800/res/ui.xml
@@ -0,0 +1,413 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="480" height="800" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="10" />
+		<variable name="col2_x" value="240" />
+		<variable name="col_center_x" value="128" />
+		<variable name="col_center_medium_x" value="183" />
+		<variable name="center_x" value="240" />
+		<variable name="row1_y" value="140" />
+		<variable name="row2_y" value="290" />
+		<variable name="row3_y" value="450" />
+		<variable name="row4_y" value="610" />
+		<variable name="row_queue_y" value="510" />
+		<variable name="row1_header_y" value="110" />
+		<variable name="row1_text_y" value="140" />
+		<variable name="row2_text_y" value="170" />
+		<variable name="row3_text_y" value="200" />
+		<variable name="row4_text_y" value="230" />
+		<variable name="row5_text_y" value="260" />
+		<variable name="row6_text_y" value="290" />
+		<variable name="row7_text_y" value="320" />
+		<variable name="row8_text_y" value="350" />
+		<variable name="row9_text_y" value="380" />
+		<variable name="row10_text_y" value="410" />
+		<variable name="row11_text_y" value="440" />
+		<variable name="row12_text_y" value="470" />
+		<variable name="row13_text_y" value="500" />
+		<variable name="row14_text_y" value="530" />
+		<variable name="row15_text_y" value="560" />
+		<variable name="row16_text_y" value="590" />
+		<variable name="row17_text_y" value="620" />
+		<variable name="row18_text_y" value="650" />
+		<variable name="zip_status_y" value="585" />
+		<variable name="tz_selected_y" value="110" />
+		<variable name="tz_set_y" value="580" />
+		<variable name="tz_current_y" value="730" />
+		<variable name="col_progressbar_x" value="114" />
+		<variable name="row_progressbar_y" value="720" />
+		<variable name="col1_medium_x" value="10" />
+		<variable name="col2_medium_x" value="125" />
+		<variable name="col3_medium_x" value="240" />
+		<variable name="col4_medium_x" value="355" />
+		<variable name="row1_medium_y" value="130" />
+		<variable name="row2_medium_y" value="205" />
+		<variable name="row3_medium_y" value="280" />
+		<variable name="row4_medium_y" value="355" />
+		<variable name="row5_medium_y" value="430" />
+		<variable name="row6_medium_y" value="505" />
+		<variable name="row7_medium_y" value="580" />
+		<variable name="slider_x" value="58" />
+		<variable name="slider_y" value="680" />
+		<variable name="slider_text_y" value="721" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="10" />
+		<variable name="home_button_y" value="766" />
+		<variable name="back_button_x" value="413" />
+		<variable name="back_button_y" value="766" />
+		<variable name="sort_asc_text_y" value="685" />
+		<variable name="sort_asc_button_y" value="685" />
+		<variable name="sort_desc_text_y" value="725" />
+		<variable name="sort_desc_button_y" value="725" />
+		<variable name="sort_col1_button_x" value="180" />
+		<variable name="sort_col2_button_x" value="240" />
+		<variable name="sort_col3_button_x" value="300" />
+		<variable name="input_width" value="460" />
+		<variable name="input_height" value="40" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="480" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="320" />
+		<variable name="console_install_height" value="440" />
+		<variable name="console_installdone_height" value="300" />
+		<variable name="fileselector_x" value="5" />
+		<variable name="fileselector_width" value="470" />
+		<variable name="fileselector_install_height" value="480" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="18" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="40" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="27" />
+		<variable name="fastscroll_recth" value="47" />
+		<variable name="listbox_x" value="5" />
+		<variable name="listbox_width" value="470" />
+		<variable name="listbox_tz_height" value="310" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="18" />
+		<variable name="sd_plus_x" value="280" />
+		<variable name="sdext_text_x" value="84" />
+		<variable name="sdext_text_y" value="145" />
+		<variable name="sdswap_button_y" value="185" />
+		<variable name="sdswap_text_x" value="84" />
+		<variable name="sdswap_text_y" value="190" />
+		<variable name="sdfilesystem_text_y" value="240" />
+		<variable name="sdfilesystem_button_y" value="280" />
+		<variable name="lock_x" value="40" />
+		<variable name="lock_y" value="200" />
+		<variable name="filemanager_select_x" value="365" />
+		<variable name="filemanager_select_y" value="690" />
+		<variable name="backup_name_y" value="350" />
+		<variable name="terminal_console_height" value="370" />
+		<variable name="terminal_text_y" value="390" />
+		<variable name="terminal_button_y" value="380" />
+		<variable name="row_dst_text_y" value="440" />
+		<variable name="row_offset_text_y" value="480" />
+		<variable name="row_offset_medium_y" value="505" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="455" />
+		<variable name="button_fill_main_width" value="225" />
+		<variable name="button_fill_main_height" value="135" />
+		<variable name="button_fill_half_height" value="67" />
+		<variable name="button_fill_quarter_height" value="34" />
+		<variable name="backup_list_height" value="300" />
+		<variable name="backup_button_row1" value="480" />
+		<variable name="backup_button_row2" value="520" />
+		<variable name="mount_list_height" value="380" />
+		<variable name="mount_storage_row" value="500" />
+		<variable name="storage_list_height" value="400" />
+		<variable name="wipe_list_height" value="460" />
+		<variable name="wipe_button_row1" value="600" />
+		<variable name="wipe_button_y" value="400" />
+		<variable name="slidervalue_w" value="460" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="1" />
+		<variable name="slidervalue_padding" value="13" />
+		<variable name="slidervalue_sliderw" value="7" />
+		<variable name="slidervalue_sliderh" value="40" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="1.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="%backgroundcolor%" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="100" y="10" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="100" y="40" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="100" y="70" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="190" y="70" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="350" y="70" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="%row2_y%" w="%console_width%" h="%console_action_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="210" y="760" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="760" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="450" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="78" width="48" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="72:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="72:l" />
+					<row3 key01="72:layout2" key02="z" key03="x" key04="c" key05="v" key06="b" key07="n" key08="m" key09="72:c:8" />
+					<row4 key01="72:layout3" key02="48:" key03="240: " key04="48:." key05="72:a:action" />
+				</layout1>
+				<layout2>
+					<keysize height="78" width="48" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="72:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="72:L" />
+					<row3 key01="72:layout1" key02="Z" key03="X" key04="C" key05="V" key06="B" key07="N" key08="M" key09="72:c:8" />
+					<row4 key01="72:layout3" key02="48:" key03="240: " key04="48:." key05="72:action" />
+				</layout2>
+				<layout3>
+					<keysize height="78" width="48" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="72:layout4" key02="!" key03="48:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="72:c:8" />
+					<row4 key01="72:layout1" key02="48:," key03="240: " key04="48:." key05="72:action" />
+				</layout3>
+				<layout4>
+					<keysize height="78" width="48" />
+					<row1 key01="~" key02="`" key03="|" key04="48:" key05="48:" key06="48:" key07="%" key08="48:" key09="{" key10="}" />
+					<row2 key01="48:" key02="48:" key03="48:" key04="48:" key05="48:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="72:layout3" key02="48:" key03="48:" key04="48:" key05="48:" key06="\" key07="<" key08=">" key09="72:c:8" />
+					<row4 key01="72:layout1" key02="48:c:34" key03="240: " key04="48:." key05="72:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/480x854/res/fonts/Roboto-Regular-20.dat b/gui/devices/480x854/res/fonts/Roboto-Regular-20.dat
new file mode 100644
index 0000000..6588b41
--- /dev/null
+++ b/gui/devices/480x854/res/fonts/Roboto-Regular-20.dat
Binary files differ
diff --git a/gui/devices/480x854/res/fonts/Roboto-Regular-40.dat b/gui/devices/480x854/res/fonts/Roboto-Regular-40.dat
new file mode 100644
index 0000000..637d9fe
--- /dev/null
+++ b/gui/devices/480x854/res/fonts/Roboto-Regular-40.dat
Binary files differ
diff --git a/gui/devices/480x854/res/images/back-icon.png b/gui/devices/480x854/res/images/back-icon.png
new file mode 100644
index 0000000..688436e
--- /dev/null
+++ b/gui/devices/480x854/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/checkbox_checked.png b/gui/devices/480x854/res/images/checkbox_checked.png
new file mode 100644
index 0000000..3447349
--- /dev/null
+++ b/gui/devices/480x854/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/checkbox_empty.png b/gui/devices/480x854/res/images/checkbox_empty.png
new file mode 100644
index 0000000..f5f35d8
--- /dev/null
+++ b/gui/devices/480x854/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/cursor.png b/gui/devices/480x854/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/480x854/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/curtain.jpg b/gui/devices/480x854/res/images/curtain.jpg
new file mode 100644
index 0000000..6e60648
--- /dev/null
+++ b/gui/devices/480x854/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/480x854/res/images/file.png b/gui/devices/480x854/res/images/file.png
new file mode 100644
index 0000000..8556bc7
--- /dev/null
+++ b/gui/devices/480x854/res/images/file.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/folder.png b/gui/devices/480x854/res/images/folder.png
new file mode 100644
index 0000000..a3a5f69
--- /dev/null
+++ b/gui/devices/480x854/res/images/folder.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/home-icon.png b/gui/devices/480x854/res/images/home-icon.png
new file mode 100644
index 0000000..5519520
--- /dev/null
+++ b/gui/devices/480x854/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/indeterminate001.png b/gui/devices/480x854/res/images/indeterminate001.png
new file mode 100644
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/480x854/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/indeterminate002.png b/gui/devices/480x854/res/images/indeterminate002.png
new file mode 100644
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/480x854/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/indeterminate003.png b/gui/devices/480x854/res/images/indeterminate003.png
new file mode 100644
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/480x854/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/indeterminate004.png b/gui/devices/480x854/res/images/indeterminate004.png
new file mode 100644
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/480x854/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/indeterminate005.png b/gui/devices/480x854/res/images/indeterminate005.png
new file mode 100644
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/480x854/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/indeterminate006.png b/gui/devices/480x854/res/images/indeterminate006.png
new file mode 100644
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/480x854/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/keyboard1.png b/gui/devices/480x854/res/images/keyboard1.png
new file mode 100644
index 0000000..5a691ac
--- /dev/null
+++ b/gui/devices/480x854/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/keyboard2.png b/gui/devices/480x854/res/images/keyboard2.png
new file mode 100644
index 0000000..69b0154
--- /dev/null
+++ b/gui/devices/480x854/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/keyboard3.png b/gui/devices/480x854/res/images/keyboard3.png
new file mode 100644
index 0000000..498cef8
--- /dev/null
+++ b/gui/devices/480x854/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/keyboard4.png b/gui/devices/480x854/res/images/keyboard4.png
new file mode 100644
index 0000000..e8f9dde
--- /dev/null
+++ b/gui/devices/480x854/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/medium-button.png b/gui/devices/480x854/res/images/medium-button.png
new file mode 100644
index 0000000..1d5b816
--- /dev/null
+++ b/gui/devices/480x854/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/menu-button.png b/gui/devices/480x854/res/images/menu-button.png
new file mode 100644
index 0000000..c8d3794
--- /dev/null
+++ b/gui/devices/480x854/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/minus-button.png b/gui/devices/480x854/res/images/minus-button.png
new file mode 100644
index 0000000..9c92d2f
--- /dev/null
+++ b/gui/devices/480x854/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/plus-button.png b/gui/devices/480x854/res/images/plus-button.png
new file mode 100644
index 0000000..5d3068e
--- /dev/null
+++ b/gui/devices/480x854/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/progress_empty.png b/gui/devices/480x854/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/480x854/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/progress_fill.png b/gui/devices/480x854/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/480x854/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/radio_empty.png b/gui/devices/480x854/res/images/radio_empty.png
new file mode 100644
index 0000000..88d1c1f
--- /dev/null
+++ b/gui/devices/480x854/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/radio_selected.png b/gui/devices/480x854/res/images/radio_selected.png
new file mode 100644
index 0000000..864065d
--- /dev/null
+++ b/gui/devices/480x854/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/slideout.png b/gui/devices/480x854/res/images/slideout.png
new file mode 100644
index 0000000..963b9fd
--- /dev/null
+++ b/gui/devices/480x854/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/slider-touch.png b/gui/devices/480x854/res/images/slider-touch.png
new file mode 100644
index 0000000..c8c0f43
--- /dev/null
+++ b/gui/devices/480x854/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/slider-used.png b/gui/devices/480x854/res/images/slider-used.png
new file mode 100644
index 0000000..2f3d519
--- /dev/null
+++ b/gui/devices/480x854/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/slider.png b/gui/devices/480x854/res/images/slider.png
new file mode 100644
index 0000000..6fcce6f
--- /dev/null
+++ b/gui/devices/480x854/res/images/slider.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/sort-button.png b/gui/devices/480x854/res/images/sort-button.png
new file mode 100644
index 0000000..69d511b
--- /dev/null
+++ b/gui/devices/480x854/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/480x854/res/images/top-bar.jpg b/gui/devices/480x854/res/images/top-bar.jpg
new file mode 100644
index 0000000..3e0e5b5
--- /dev/null
+++ b/gui/devices/480x854/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/480x854/res/images/unlock.png b/gui/devices/480x854/res/images/unlock.png
new file mode 100644
index 0000000..9041221
--- /dev/null
+++ b/gui/devices/480x854/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/480x854/res/ui.xml b/gui/devices/480x854/res/ui.xml
new file mode 100644
index 0000000..26d8b28
--- /dev/null
+++ b/gui/devices/480x854/res/ui.xml
@@ -0,0 +1,412 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="480" height="854" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="10" />
+		<variable name="col2_x" value="240" />
+		<variable name="col_center_x" value="128" />
+		<variable name="center_x" value="240" />
+		<variable name="row1_y" value="140" />
+		<variable name="row2_y" value="290" />
+		<variable name="row3_y" value="450" />
+		<variable name="row4_y" value="610" />
+		<variable name="row_queue_y" value="510" />
+		<variable name="row1_header_y" value="110" />
+		<variable name="row1_text_y" value="140" />
+		<variable name="row2_text_y" value="170" />
+		<variable name="row3_text_y" value="200" />
+		<variable name="row4_text_y" value="230" />
+		<variable name="row5_text_y" value="260" />
+		<variable name="row6_text_y" value="290" />
+		<variable name="row7_text_y" value="320" />
+		<variable name="row8_text_y" value="350" />
+		<variable name="row9_text_y" value="380" />
+		<variable name="row10_text_y" value="410" />
+		<variable name="row11_text_y" value="440" />
+		<variable name="row12_text_y" value="480" />
+		<variable name="row13_text_y" value="510" />
+		<variable name="row14_text_y" value="540" />
+		<variable name="row15_text_y" value="570" />
+		<variable name="row16_text_y" value="600" />
+		<variable name="row17_text_y" value="630" />
+		<variable name="row18_text_y" value="660" />
+		<variable name="zip_status_y" value="585" />
+		<variable name="tz_selected_y" value="110" />
+		<variable name="tz_set_y" value="580" />
+		<variable name="tz_current_y" value="730" />
+		<variable name="col_progressbar_x" value="114" />
+		<variable name="row_progressbar_y" value="720" />
+		<variable name="col1_medium_x" value="10" />
+		<variable name="col2_medium_x" value="125" />
+		<variable name="col3_medium_x" value="240" />
+		<variable name="col4_medium_x" value="355" />
+		<variable name="row1_medium_y" value="130" />
+		<variable name="row2_medium_y" value="205" />
+		<variable name="row3_medium_y" value="280" />
+		<variable name="row4_medium_y" value="355" />
+		<variable name="row5_medium_y" value="430" />
+		<variable name="row6_medium_y" value="505" />
+		<variable name="row7_medium_y" value="580" />
+		<variable name="slider_x" value="58" />
+		<variable name="slider_y" value="680" />
+		<variable name="slider_text_y" value="721" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="10" />
+		<variable name="home_button_y" value="820" />
+		<variable name="back_button_x" value="413" />
+		<variable name="back_button_y" value="820" />
+		<variable name="sort_asc_text_y" value="739" />
+		<variable name="sort_asc_button_y" value="739" />
+		<variable name="sort_desc_text_y" value="779" />
+		<variable name="sort_desc_button_y" value="779" />
+		<variable name="sort_col1_button_x" value="180" />
+		<variable name="sort_col2_button_x" value="240" />
+		<variable name="sort_col3_button_x" value="300" />
+		<variable name="input_width" value="460" />
+		<variable name="input_height" value="40" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="480" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="320" />
+		<variable name="console_install_height" value="440" />
+		<variable name="console_installdone_height" value="300" />
+		<variable name="fileselector_x" value="5" />
+		<variable name="fileselector_width" value="470" />
+		<variable name="fileselector_install_height" value="534" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="18" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="40" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="27" />
+		<variable name="fastscroll_recth" value="47" />
+		<variable name="listbox_x" value="5" />
+		<variable name="listbox_width" value="470" />
+		<variable name="listbox_tz_height" value="310" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="18" />
+		<variable name="sd_plus_x" value="280" />
+		<variable name="sdext_text_x" value="84" />
+		<variable name="sdext_text_y" value="150" />
+		<variable name="sdswap_button_y" value="185" />
+		<variable name="sdswap_text_x" value="84" />
+		<variable name="sdswap_text_y" value="195" />
+		<variable name="sdfilesystem_text_y" value="240" />
+		<variable name="sdfilesystem_button_y" value="280" />
+		<variable name="lock_x" value="40" />
+		<variable name="lock_y" value="200" />
+		<variable name="filemanager_select_x" value="365" />
+		<variable name="filemanager_select_y" value="744" />
+		<variable name="backup_name_y" value="350" />
+		<variable name="terminal_console_height" value="424" />
+		<variable name="terminal_text_y" value="444" />
+		<variable name="terminal_button_y" value="434" />
+		<variable name="row_dst_text_y" value="440" />
+		<variable name="row_offset_text_y" value="480" />
+		<variable name="row_offset_medium_y" value="505" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="455" />
+		<variable name="button_fill_main_width" value="225" />
+		<variable name="button_fill_main_height" value="135" />
+		<variable name="button_fill_half_height" value="67" />
+		<variable name="button_fill_quarter_height" value="34" />
+		<variable name="backup_list_height" value="300" />
+		<variable name="backup_button_row1" value="480" />
+		<variable name="backup_button_row2" value="520" />
+		<variable name="mount_list_height" value="380" />
+		<variable name="mount_storage_row" value="500" />
+		<variable name="storage_list_height" value="400" />
+		<variable name="wipe_list_height" value="460" />
+		<variable name="wipe_button_row1" value="600" />
+		<variable name="wipe_button_y" value="400" />
+		<variable name="slidervalue_w" value="460" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="1" />
+		<variable name="slidervalue_padding" value="13" />
+		<variable name="slidervalue_sliderw" value="7" />
+		<variable name="slidervalue_sliderh" value="40" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="1.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="%backgroundcolor%" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="100" y="10" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="100" y="40" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="100" y="70" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="190" y="70" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="350" y="70" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="%row2_y%" w="%console_width%" h="%console_action_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="210" y="814" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="814" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="504" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="78" width="48" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="72:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="72:l" />
+					<row3 key01="72:layout2" key02="z" key03="x" key04="c" key05="v" key06="b" key07="n" key08="m" key09="72:c:8" />
+					<row4 key01="72:layout3" key02="48:" key03="240: " key04="48:." key05="72:a:action" />
+				</layout1>
+				<layout2>
+					<keysize height="78" width="48" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="72:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="72:L" />
+					<row3 key01="72:layout1" key02="Z" key03="X" key04="C" key05="V" key06="B" key07="N" key08="M" key09="72:c:8" />
+					<row4 key01="72:layout3" key02="48:" key03="240: " key04="48:." key05="72:action" />
+				</layout2>
+				<layout3>
+					<keysize height="78" width="48" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="72:layout4" key02="!" key03="48:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="72:c:8" />
+					<row4 key01="72:layout1" key02="48:," key03="240: " key04="48:." key05="72:action" />
+				</layout3>
+				<layout4>
+					<keysize height="78" width="48" />
+					<row1 key01="~" key02="`" key03="|" key04="48:" key05="48:" key06="48:" key07="%" key08="48:" key09="{" key10="}" />
+					<row2 key01="48:" key02="48:" key03="48:" key04="48:" key05="48:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="72:layout3" key02="48:" key03="48:" key04="48:" key05="48:" key06="\" key07="<" key08=">" key09="72:c:8" />
+					<row4 key01="72:layout1" key02="48:c:34" key03="240: " key04="48:." key05="72:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/540x960/res/fonts/Roboto-Regular-20.dat b/gui/devices/540x960/res/fonts/Roboto-Regular-20.dat
new file mode 100644
index 0000000..6588b41
--- /dev/null
+++ b/gui/devices/540x960/res/fonts/Roboto-Regular-20.dat
Binary files differ
diff --git a/gui/devices/540x960/res/fonts/Roboto-Regular-25.dat b/gui/devices/540x960/res/fonts/Roboto-Regular-25.dat
new file mode 100644
index 0000000..392cce9
--- /dev/null
+++ b/gui/devices/540x960/res/fonts/Roboto-Regular-25.dat
Binary files differ
diff --git a/gui/devices/540x960/res/fonts/Roboto-Regular-40.dat b/gui/devices/540x960/res/fonts/Roboto-Regular-40.dat
new file mode 100644
index 0000000..637d9fe
--- /dev/null
+++ b/gui/devices/540x960/res/fonts/Roboto-Regular-40.dat
Binary files differ
diff --git a/gui/devices/540x960/res/images/.BridgeSort b/gui/devices/540x960/res/images/.BridgeSort
new file mode 100644
index 0000000..b475f87
--- /dev/null
+++ b/gui/devices/540x960/res/images/.BridgeSort
@@ -0,0 +1,29 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<dirinfo>
+<files>
+<item key='checkbox_checked.png20120416172546' />
+<item key='checkbox_empty.png20120416172604' />
+<item key='file.png20120411171839' />
+<item key='folder.png20120411173532' />
+<item key='indeterminate001.png20120412113445' />
+<item key='indeterminate002.png20120412113445' />
+<item key='indeterminate003.png20120412113445' />
+<item key='indeterminate004.png20120412113445' />
+<item key='indeterminate005.png20120412113445' />
+<item key='indeterminate006.png20120412113445' />
+<item key='qhd-medium-button.png20120417101729' />
+<item key='qhd-menu-button.png20120417101649' />
+<item key='qhd-minus-button.png20120417101827' />
+<item key='qhd-plus-button.png20120417101847' />
+<item key='qhd-small-button.png20120417101930' />
+<item key='qhd-sort-button.png20120417102006' />
+<item key='radio_empty.png20120411175722' />
+<item key='radio_selected.png20120411175739' />
+<item key='slideout.png20120416182450' />
+<item key='slider-touch.png20120412103144' />
+<item key='slider-used.png20120412103705' />
+<item key='slider.png20120412103630' />
+<item key='top-bar-01.jpg20120417101442' />
+<item key='unlock.png20120412111348' />
+</files>
+</dirinfo>
diff --git a/gui/devices/540x960/res/images/back-icon.png b/gui/devices/540x960/res/images/back-icon.png
new file mode 100644
index 0000000..f5ba872
--- /dev/null
+++ b/gui/devices/540x960/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/checkbox_checked.png b/gui/devices/540x960/res/images/checkbox_checked.png
new file mode 100644
index 0000000..3447349
--- /dev/null
+++ b/gui/devices/540x960/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/checkbox_empty.png b/gui/devices/540x960/res/images/checkbox_empty.png
new file mode 100644
index 0000000..f5f35d8
--- /dev/null
+++ b/gui/devices/540x960/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/cursor.png b/gui/devices/540x960/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/540x960/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/curtain.jpg b/gui/devices/540x960/res/images/curtain.jpg
new file mode 100644
index 0000000..f1a6316
--- /dev/null
+++ b/gui/devices/540x960/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/540x960/res/images/file.png b/gui/devices/540x960/res/images/file.png
new file mode 100644
index 0000000..8556bc7
--- /dev/null
+++ b/gui/devices/540x960/res/images/file.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/folder.png b/gui/devices/540x960/res/images/folder.png
new file mode 100644
index 0000000..a3a5f69
--- /dev/null
+++ b/gui/devices/540x960/res/images/folder.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/home-icon.png b/gui/devices/540x960/res/images/home-icon.png
new file mode 100644
index 0000000..46ccf6a
--- /dev/null
+++ b/gui/devices/540x960/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/indeterminate001.png b/gui/devices/540x960/res/images/indeterminate001.png
new file mode 100644
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/540x960/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/indeterminate002.png b/gui/devices/540x960/res/images/indeterminate002.png
new file mode 100644
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/540x960/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/indeterminate003.png b/gui/devices/540x960/res/images/indeterminate003.png
new file mode 100644
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/540x960/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/indeterminate004.png b/gui/devices/540x960/res/images/indeterminate004.png
new file mode 100644
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/540x960/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/indeterminate005.png b/gui/devices/540x960/res/images/indeterminate005.png
new file mode 100644
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/540x960/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/indeterminate006.png b/gui/devices/540x960/res/images/indeterminate006.png
new file mode 100644
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/540x960/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/keyboard1.png b/gui/devices/540x960/res/images/keyboard1.png
new file mode 100644
index 0000000..9fecdcd
--- /dev/null
+++ b/gui/devices/540x960/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/keyboard2.png b/gui/devices/540x960/res/images/keyboard2.png
new file mode 100644
index 0000000..0d1e966
--- /dev/null
+++ b/gui/devices/540x960/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/keyboard3.png b/gui/devices/540x960/res/images/keyboard3.png
new file mode 100644
index 0000000..b9eab17
--- /dev/null
+++ b/gui/devices/540x960/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/keyboard4.png b/gui/devices/540x960/res/images/keyboard4.png
new file mode 100644
index 0000000..909956b
--- /dev/null
+++ b/gui/devices/540x960/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/progress_empty.png b/gui/devices/540x960/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/540x960/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/progress_fill.png b/gui/devices/540x960/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/540x960/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/qhd-medium-button.png b/gui/devices/540x960/res/images/qhd-medium-button.png
new file mode 100644
index 0000000..b91e7fc
--- /dev/null
+++ b/gui/devices/540x960/res/images/qhd-medium-button.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/qhd-menu-button.png b/gui/devices/540x960/res/images/qhd-menu-button.png
new file mode 100644
index 0000000..ec2b1fd
--- /dev/null
+++ b/gui/devices/540x960/res/images/qhd-menu-button.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/qhd-minus-button.png b/gui/devices/540x960/res/images/qhd-minus-button.png
new file mode 100644
index 0000000..0cbc5ce
--- /dev/null
+++ b/gui/devices/540x960/res/images/qhd-minus-button.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/qhd-plus-button.png b/gui/devices/540x960/res/images/qhd-plus-button.png
new file mode 100644
index 0000000..89c8048
--- /dev/null
+++ b/gui/devices/540x960/res/images/qhd-plus-button.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/qhd-small-button.png b/gui/devices/540x960/res/images/qhd-small-button.png
new file mode 100644
index 0000000..255d09d
--- /dev/null
+++ b/gui/devices/540x960/res/images/qhd-small-button.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/qhd-sort-button.png b/gui/devices/540x960/res/images/qhd-sort-button.png
new file mode 100644
index 0000000..e06618e
--- /dev/null
+++ b/gui/devices/540x960/res/images/qhd-sort-button.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/radio_empty.png b/gui/devices/540x960/res/images/radio_empty.png
new file mode 100644
index 0000000..88d1c1f
--- /dev/null
+++ b/gui/devices/540x960/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/radio_selected.png b/gui/devices/540x960/res/images/radio_selected.png
new file mode 100644
index 0000000..864065d
--- /dev/null
+++ b/gui/devices/540x960/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/slideout.png b/gui/devices/540x960/res/images/slideout.png
new file mode 100644
index 0000000..963b9fd
--- /dev/null
+++ b/gui/devices/540x960/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/slider-touch.png b/gui/devices/540x960/res/images/slider-touch.png
new file mode 100644
index 0000000..d8647b8
--- /dev/null
+++ b/gui/devices/540x960/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/slider-used.png b/gui/devices/540x960/res/images/slider-used.png
new file mode 100644
index 0000000..bf6cad9
--- /dev/null
+++ b/gui/devices/540x960/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/slider.png b/gui/devices/540x960/res/images/slider.png
new file mode 100644
index 0000000..4081ea5
--- /dev/null
+++ b/gui/devices/540x960/res/images/slider.png
Binary files differ
diff --git a/gui/devices/540x960/res/images/top-bar.jpg b/gui/devices/540x960/res/images/top-bar.jpg
new file mode 100644
index 0000000..fbe7230
--- /dev/null
+++ b/gui/devices/540x960/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/540x960/res/images/unlock.png b/gui/devices/540x960/res/images/unlock.png
new file mode 100644
index 0000000..9041221
--- /dev/null
+++ b/gui/devices/540x960/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/540x960/res/ui.xml b/gui/devices/540x960/res/ui.xml
new file mode 100644
index 0000000..dc647ce
--- /dev/null
+++ b/gui/devices/540x960/res/ui.xml
@@ -0,0 +1,413 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="540" height="960" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-20" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="20" fallback="Roboto-Regular-25" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="qhd-menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="qhd-medium-button" />
+		<resource name="sort_button" type="image" filename="qhd-sort-button" />
+		<resource name="minus_button" type="image" filename="qhd-minus-button" />
+		<resource name="plus_button" type="image" filename="qhd-plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="10" />
+		<variable name="col2_x" value="277" />
+		<variable name="col_center_x" value="143" />
+		<variable name="col_center_medium_x" value="206" />
+		<variable name="center_x" value="270" />
+		<variable name="row1_y" value="140" />
+		<variable name="row2_y" value="332" />
+		<variable name="row3_y" value="524" />
+		<variable name="row4_y" value="716" />
+		<variable name="row_queue_y" value="625" />
+		<variable name="row1_header_y" value="110" />
+		<variable name="row1_text_y" value="140" />
+		<variable name="row2_text_y" value="180" />
+		<variable name="row3_text_y" value="220" />
+		<variable name="row4_text_y" value="260" />
+		<variable name="row5_text_y" value="300" />
+		<variable name="row6_text_y" value="340" />
+		<variable name="row7_text_y" value="380" />
+		<variable name="row8_text_y" value="420" />
+		<variable name="row9_text_y" value="460" />
+		<variable name="row10_text_y" value="500" />
+		<variable name="row11_text_y" value="540" />
+		<variable name="row12_text_y" value="580" />
+		<variable name="row13_text_y" value="620" />
+		<variable name="row14_text_y" value="660" />
+		<variable name="row15_text_y" value="700" />
+		<variable name="row16_text_y" value="740" />
+		<variable name="row17_text_y" value="780" />
+		<variable name="row18_text_y" value="820" />
+		<variable name="zip_status_y" value="696" />
+		<variable name="tz_selected_y" value="110" />
+		<variable name="tz_set_y" value="720" />
+		<variable name="tz_current_y" value="895" />
+		<variable name="col_progressbar_x" value="144" />
+		<variable name="row_progressbar_y" value="850" />
+		<variable name="col1_medium_x" value="10" />
+		<variable name="col2_medium_x" value="145" />
+		<variable name="col3_medium_x" value="280" />
+		<variable name="col4_medium_x" value="415" />
+		<variable name="row1_medium_y" value="130" />
+		<variable name="row2_medium_y" value="230" />
+		<variable name="row3_medium_y" value="330" />
+		<variable name="row4_medium_y" value="430" />
+		<variable name="row5_medium_y" value="530" />
+		<variable name="row6_medium_y" value="630" />
+		<variable name="row7_medium_y" value="730" />
+		<variable name="slider_x" value="50" />
+		<variable name="slider_y" value="820" />
+		<variable name="slider_text_y" value="870" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="10" />
+		<variable name="home_button_y" value="919" />
+		<variable name="back_button_x" value="466" />
+		<variable name="back_button_y" value="919" />
+		<variable name="sort_asc_text_y" value="830" />
+		<variable name="sort_asc_button_y" value="820" />
+		<variable name="sort_desc_text_y" value="880" />
+		<variable name="sort_desc_button_y" value="870" />
+		<variable name="sort_col1_button_x" value="180" />
+		<variable name="sort_col2_button_x" value="255" />
+		<variable name="sort_col3_button_x" value="330" />
+		<variable name="input_width" value="520" />
+		<variable name="input_height" value="40" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="540" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="380" />
+		<variable name="console_install_height" value="580" />
+		<variable name="console_installdone_height" value="380" />
+		<variable name="fileselector_x" value="5" />
+		<variable name="fileselector_width" value="530" />
+		<variable name="fileselector_install_height" value="590" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="18" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="45" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="30" />
+		<variable name="fastscroll_recth" value="52" />
+		<variable name="listbox_x" value="5" />
+		<variable name="listbox_width" value="530" />
+		<variable name="listbox_tz_height" value="420" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="18" />
+		<variable name="sd_plus_x" value="280" />
+		<variable name="sdext_text_x" value="84" />
+		<variable name="sdext_text_y" value="150" />
+		<variable name="sdswap_button_y" value="185" />
+		<variable name="sdswap_text_x" value="84" />
+		<variable name="sdswap_text_y" value="195" />
+		<variable name="sdfilesystem_text_y" value="240" />
+		<variable name="sdfilesystem_button_y" value="280" />
+		<variable name="lock_x" value="70" />
+		<variable name="lock_y" value="250" />
+		<variable name="filemanager_select_x" value="405" />
+		<variable name="filemanager_select_y" value="825" />
+		<variable name="backup_name_y" value="420" />
+		<variable name="terminal_console_height" value="530" />
+		<variable name="terminal_text_y" value="550" />
+		<variable name="terminal_button_y" value="525" />
+		<variable name="row_dst_text_y" value="540" />
+		<variable name="row_offset_text_y" value="580" />
+		<variable name="row_offset_medium_y" value="630" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="520" />
+		<variable name="button_fill_main_width" value="253" />
+		<variable name="button_fill_main_height" value="162" />
+		<variable name="button_fill_half_height" value="81" />
+		<variable name="button_fill_quarter_height" value="40" />
+		<variable name="backup_list_height" value="410" />
+		<variable name="backup_button_row1" value="600" />
+		<variable name="backup_button_row2" value="645" />
+		<variable name="mount_list_height" value="500" />
+		<variable name="mount_storage_row" value="630" />
+		<variable name="storage_list_height" value="575" />
+		<variable name="wipe_list_height" value="570" />
+		<variable name="wipe_button_row1" value="700" />
+		<variable name="wipe_button_y" value="475" />
+		<variable name="slidervalue_w" value="520" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="2" />
+		<variable name="slidervalue_padding" value="15" />
+		<variable name="slidervalue_sliderw" value="8" />
+		<variable name="slidervalue_sliderh" value="45" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="1.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="100" y="10" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="100" y="40" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="100" y="70" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="200" y="70" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="400" y="70" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="%row2_y%" w="%console_width%" h="%console_action_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="UItext" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="UItext" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="UItext" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="UItext" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="UItext" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="UItext" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="240" y="920" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="920" />
+				<color foreground="#A0A0A0" background="#303030" scroll="#303030" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="600" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="78" width="54" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="81:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="81:l" />
+					<row3 key01="81:layout2" key02="z" key03="x" key04="c" key05="v" key06="b" key07="n" key08="m" key09="81:c:8" />
+					<row4 key01="81:layout3" key02="54:/" key03="270: " key04="54:." key05="81:a:action" />
+				</layout1>
+				<layout2>
+					<keysize height="78" width="54" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="81:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="81:L" />
+					<row3 key01="81:layout1" key02="Z" key03="X" key04="C" key05="V" key06="B" key07="N" key08="M" key09="81:c:8" />
+					<row4 key01="81:layout3" key02="54:/" key03="270: " key04="54:." key05="81:action" />
+				</layout2>
+				<layout3>
+					<keysize height="78" width="54" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="81:layout4" key02="!" key03="54:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="81:c:8" />
+					<row4 key01="81:layout1" key02="54:," key03="270: " key04="54:." key05="81:action" />
+				</layout3>
+				<layout4>
+					<keysize height="78" width="54" />
+					<row1 key01="~" key02="`" key03="|" key04="54:" key05="54:" key06="54:" key07="%" key08="54:" key09="{" key10="}" />
+					<row2 key01="54:" key02="54:" key03="54:" key04="54:" key05="54:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="81:layout3" key02="54:" key03="54:" key04="54:" key05="54:" key06="\" key07="<" key08=">" key09="81:c:8" />
+					<row4 key01="81:layout1" key02="54:c:34" key03="270: " key04="54:." key05="81:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/720x1280/res/fonts/Roboto-Condensed-30.dat b/gui/devices/720x1280/res/fonts/Roboto-Condensed-30.dat
new file mode 100644
index 0000000..621b332
--- /dev/null
+++ b/gui/devices/720x1280/res/fonts/Roboto-Condensed-30.dat
Binary files differ
diff --git a/gui/devices/720x1280/res/images/back-icon.png b/gui/devices/720x1280/res/images/back-icon.png
new file mode 100644
index 0000000..e0c0442
--- /dev/null
+++ b/gui/devices/720x1280/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/checkbox_checked.png b/gui/devices/720x1280/res/images/checkbox_checked.png
new file mode 100644
index 0000000..2af7609
--- /dev/null
+++ b/gui/devices/720x1280/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/checkbox_empty.png b/gui/devices/720x1280/res/images/checkbox_empty.png
new file mode 100644
index 0000000..5f65c26
--- /dev/null
+++ b/gui/devices/720x1280/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/cursor.png b/gui/devices/720x1280/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/720x1280/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/curtain.jpg b/gui/devices/720x1280/res/images/curtain.jpg
new file mode 100644
index 0000000..a10cc02
--- /dev/null
+++ b/gui/devices/720x1280/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/720x1280/res/images/file.png b/gui/devices/720x1280/res/images/file.png
new file mode 100644
index 0000000..aea3ac2
--- /dev/null
+++ b/gui/devices/720x1280/res/images/file.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/folder.png b/gui/devices/720x1280/res/images/folder.png
new file mode 100644
index 0000000..56f4bc2
--- /dev/null
+++ b/gui/devices/720x1280/res/images/folder.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/home-icon.png b/gui/devices/720x1280/res/images/home-icon.png
new file mode 100644
index 0000000..d4373b7
--- /dev/null
+++ b/gui/devices/720x1280/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/indeterminate001.png b/gui/devices/720x1280/res/images/indeterminate001.png
new file mode 100644
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/720x1280/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/indeterminate002.png b/gui/devices/720x1280/res/images/indeterminate002.png
new file mode 100644
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/720x1280/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/indeterminate003.png b/gui/devices/720x1280/res/images/indeterminate003.png
new file mode 100644
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/720x1280/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/indeterminate004.png b/gui/devices/720x1280/res/images/indeterminate004.png
new file mode 100644
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/720x1280/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/indeterminate005.png b/gui/devices/720x1280/res/images/indeterminate005.png
new file mode 100644
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/720x1280/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/indeterminate006.png b/gui/devices/720x1280/res/images/indeterminate006.png
new file mode 100644
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/720x1280/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/keyboard1.png b/gui/devices/720x1280/res/images/keyboard1.png
new file mode 100644
index 0000000..a1af160
--- /dev/null
+++ b/gui/devices/720x1280/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/keyboard2.png b/gui/devices/720x1280/res/images/keyboard2.png
new file mode 100644
index 0000000..5b0a4b5
--- /dev/null
+++ b/gui/devices/720x1280/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/keyboard3.png b/gui/devices/720x1280/res/images/keyboard3.png
new file mode 100644
index 0000000..38ee5af
--- /dev/null
+++ b/gui/devices/720x1280/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/keyboard4.png b/gui/devices/720x1280/res/images/keyboard4.png
new file mode 100644
index 0000000..dc9a976
--- /dev/null
+++ b/gui/devices/720x1280/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/medium-button.png b/gui/devices/720x1280/res/images/medium-button.png
new file mode 100644
index 0000000..73faebb
--- /dev/null
+++ b/gui/devices/720x1280/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/menu-button.png b/gui/devices/720x1280/res/images/menu-button.png
new file mode 100644
index 0000000..c24f155
--- /dev/null
+++ b/gui/devices/720x1280/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/minus-button.png b/gui/devices/720x1280/res/images/minus-button.png
new file mode 100644
index 0000000..7b5f79f
--- /dev/null
+++ b/gui/devices/720x1280/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/plus-button.png b/gui/devices/720x1280/res/images/plus-button.png
new file mode 100644
index 0000000..b328577
--- /dev/null
+++ b/gui/devices/720x1280/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/progress_empty.png b/gui/devices/720x1280/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/720x1280/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/progress_fill.png b/gui/devices/720x1280/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/720x1280/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/radio_empty.png b/gui/devices/720x1280/res/images/radio_empty.png
new file mode 100644
index 0000000..88d1c1f
--- /dev/null
+++ b/gui/devices/720x1280/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/radio_selected.png b/gui/devices/720x1280/res/images/radio_selected.png
new file mode 100644
index 0000000..864065d
--- /dev/null
+++ b/gui/devices/720x1280/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/slideout.png b/gui/devices/720x1280/res/images/slideout.png
new file mode 100644
index 0000000..c3c65e4
--- /dev/null
+++ b/gui/devices/720x1280/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/slider-touch.png b/gui/devices/720x1280/res/images/slider-touch.png
new file mode 100644
index 0000000..83fce08
--- /dev/null
+++ b/gui/devices/720x1280/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/slider-used.png b/gui/devices/720x1280/res/images/slider-used.png
new file mode 100644
index 0000000..4e4a1f4
--- /dev/null
+++ b/gui/devices/720x1280/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/slider.png b/gui/devices/720x1280/res/images/slider.png
new file mode 100644
index 0000000..118edc6
--- /dev/null
+++ b/gui/devices/720x1280/res/images/slider.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/sort-button.png b/gui/devices/720x1280/res/images/sort-button.png
new file mode 100644
index 0000000..125c2ce
--- /dev/null
+++ b/gui/devices/720x1280/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/720x1280/res/images/top-bar.jpg b/gui/devices/720x1280/res/images/top-bar.jpg
new file mode 100644
index 0000000..02c8051
--- /dev/null
+++ b/gui/devices/720x1280/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/720x1280/res/images/unlock.png b/gui/devices/720x1280/res/images/unlock.png
new file mode 100644
index 0000000..9041221
--- /dev/null
+++ b/gui/devices/720x1280/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/720x1280/res/ui.xml b/gui/devices/720x1280/res/ui.xml
new file mode 100644
index 0000000..2f1fbc7
--- /dev/null
+++ b/gui/devices/720x1280/res/ui.xml
@@ -0,0 +1,426 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="720" height="1280" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="30" fallback="Roboto-Condensed-30" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="30" fallback="Roboto-Condensed-30" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="30" fallback="Roboto-Condensed-30" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="10" />
+		<variable name="col2_x" value="373" />
+		<variable name="col_center_x" value="191" />
+		<variable name="col_center_medium_x" value="275" />
+		<variable name="center_x" value="360" />
+		<variable name="row1_y" value="170" />
+		<variable name="row2_y" value="410" />
+		<variable name="row3_y" value="650" />
+		<variable name="row4_y" value="890" />
+		<variable name="col1_center_x" value="179" />
+		<variable name="col2_center_x" value="552" />
+		<variable name="row1_text2_y" value="310" />
+		<variable name="row2_text2_y" value="550" />
+		<variable name="row_queue_y" value="760" />
+		<variable name="row1_header_y" value="120" />
+		<variable name="row1_text_y" value="170" />
+		<variable name="row2_text_y" value="220" />
+		<variable name="row3_text_y" value="270" />
+		<variable name="row4_text_y" value="320" />
+		<variable name="row5_text_y" value="370" />
+		<variable name="row6_text_y" value="420" />
+		<variable name="row7_text_y" value="470" />
+		<variable name="row8_text_y" value="520" />
+		<variable name="row9_text_y" value="570" />
+		<variable name="row10_text_y" value="620" />
+		<variable name="row11_text_y" value="670" />
+		<variable name="row12_text_y" value="720" />
+		<variable name="row13_text_y" value="770" />
+		<variable name="row14_text_y" value="820" />
+		<variable name="row15_text_y" value="870" />
+		<variable name="row16_text_y" value="920" />
+		<variable name="row17_text_y" value="970" />
+		<variable name="row18_text_y" value="1020" />
+		<variable name="zip_status_y" value="615" />
+		<variable name="tz_selected_y" value="160" />
+		<variable name="tz_set_y" value="950" />
+		<variable name="tz_current_y" value="1180" />
+		<variable name="col_progressbar_x" value="234" />
+		<variable name="row_progressbar_y" value="1100" />
+		<variable name="col1_medium_x" value="10" />
+		<variable name="col2_medium_x" value="185" />
+		<variable name="col3_medium_x" value="365" />
+		<variable name="col4_medium_x" value="540" />
+		<variable name="row1_medium_y" value="130" />
+		<variable name="row2_medium_y" value="230" />
+		<variable name="row3_medium_y" value="330" />
+		<variable name="row4_medium_y" value="430" />
+		<variable name="row5_medium_y" value="530" />
+		<variable name="row6_medium_y" value="840" />
+		<variable name="row7_medium_y" value="730" />
+		<variable name="slider_x" value="68" />
+		<variable name="slider_y" value="1050" />
+		<variable name="slider_text_y" value="1117" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="10" />
+		<variable name="home_button_y" value="1226" />
+		<variable name="back_button_x" value="625" />
+		<variable name="back_button_y" value="1226" />
+		<variable name="sort_text_x" value="10" />
+		<variable name="sort_asc_text_y" value="1090" />
+		<variable name="sort_asc_button_y" value="1080" />
+		<variable name="sort_desc_text_y" value="1150" />
+		<variable name="sort_desc_button_y" value="1140" />
+		<variable name="sort_col1_button_x" value="260" />
+		<variable name="sort_col2_button_x" value="360" />
+		<variable name="sort_col3_button_x" value="460" />
+		<variable name="input_width" value="700" />
+		<variable name="input_height" value="50" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="720" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="470" />
+		<variable name="console_install_height" value="600" />
+		<variable name="console_installdone_height" value="440" />
+		<variable name="fileselector_x" value="5" />
+		<variable name="fileselector_width" value="710" />
+		<variable name="fileselector_install_height" value="780" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="36" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="60" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="40" />
+		<variable name="fastscroll_recth" value="70" />
+		<variable name="listbox_x" value="5" />
+		<variable name="listbox_width" value="710" />
+		<variable name="listbox_tz_height" value="590" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="16" />
+		<variable name="sd_plus_x" value="350" />
+		<variable name="sdext_text_x" value="110" />
+		<variable name="sdext_text_y" value="180" />
+		<variable name="sdswap_button_y" value="260" />
+		<variable name="sdswap_text_x" value="110" />
+		<variable name="sdswap_text_y" value="270" />
+		<variable name="sdfilesystem_text_y" value="340" />
+		<variable name="sdfilesystem_button_y" value="380" />
+		<variable name="lock_x" value="160" />
+		<variable name="lock_y" value="400" />
+		<variable name="filemanager_select_x" value="560" />
+		<variable name="filemanager_select_y" value="1080" />
+		<variable name="backup_name_y" value="550" />
+		<variable name="terminal_console_height" value="700" />
+		<variable name="terminal_text_y" value="730" />
+		<variable name="terminal_button_y" value="700" />
+		<variable name="row_dst_text_y" value="720" />
+		<variable name="row_offset_text_y" value="770" />
+		<variable name="row_offset_medium_y" value="840" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="700" />
+		<variable name="button_fill_main_width" value="337" />
+		<variable name="button_fill_main_height" value="216" />
+		<variable name="button_fill_half_height" value="108" />
+		<variable name="button_fill_quarter_height" value="54" />
+		<variable name="backup_list_height" value="520" />
+		<variable name="backup_button_row1" value="745" />
+		<variable name="backup_button_row2" value="810" />
+		<variable name="mount_list_height" value="690" />
+		<variable name="mount_storage_row" value="820" />
+		<variable name="storage_list_height" value="775" />
+		<variable name="wipe_list_height" value="670" />
+		<variable name="wipe_button_row1" value="900" />
+		<variable name="wipe_button_y" value="650" />
+		<variable name="slidervalue_w" value="700" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="2" />
+		<variable name="slidervalue_padding" value="20" />
+		<variable name="slidervalue_sliderw" value="10" />
+		<variable name="slidervalue_sliderh" value="60" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="120" y="10" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="120" y="45" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="120" y="76" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="270" y="76" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="500" y="76" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="0" y="%row2_y%" w="720" h="470" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="325" y="1220" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="1220" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="800" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="106" width="72" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="108:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="108:l" />
+					<row3 key01="108:layout2" key02="z" key03="x" key04="c" key05="v" key06="b" key07="n" key08="m" key09="108:c:8" />
+					<row4 key01="100:layout3" key02="130:" key03="260: " key04="65:." key05="165:a:action" />
+				</layout1>
+				<layout2>
+					<keysize height="106" width="72" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="108:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="108:L" />
+					<row3 key01="108:layout1" key02="Z" key03="X" key04="C" key05="V" key06="B" key07="N" key08="M" key09="108:c:8" />
+					<row4 key01="100:layout3" key02="130:" key03="260: " key04="65:." key05="165:action" />
+				</layout2>
+				<layout3>
+					<keysize height="106" width="72" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="108:layout4" key02="!" key03="72:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="108:c:8" />
+					<row4 key01="100:layout1" key02="65:" key03="65:," key04="260: " key05="65:." key06="165:action" />
+				</layout3>
+				<layout4>
+					<keysize height="106" width="72" />
+					<row1 key01="~" key02="`" key03="|" key04="72:" key05="72:" key06="72:" key07="%" key08="72:" key09="{" key10="}" />
+					<row2 key01="72:" key02="72:" key03="72:" key04="72:" key05="72:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="108:layout3" key02="72:" key03="72:" key04="72:" key05="72:" key06="\" key07="<" key08=">" key09="108:c:8" />
+					<row4 key01="100:layout1" key02="65:" key03="65:c:34" key04="260: " key05="65:." key06="165:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/800x1280/res/fonts/Roboto-Condensed-30.dat b/gui/devices/800x1280/res/fonts/Roboto-Condensed-30.dat
new file mode 100644
index 0000000..621b332
--- /dev/null
+++ b/gui/devices/800x1280/res/fonts/Roboto-Condensed-30.dat
Binary files differ
diff --git a/gui/devices/800x1280/res/images/back-icon.png b/gui/devices/800x1280/res/images/back-icon.png
new file mode 100644
index 0000000..e0c0442
--- /dev/null
+++ b/gui/devices/800x1280/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/checkbox_checked.png b/gui/devices/800x1280/res/images/checkbox_checked.png
new file mode 100644
index 0000000..2af7609
--- /dev/null
+++ b/gui/devices/800x1280/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/checkbox_empty.png b/gui/devices/800x1280/res/images/checkbox_empty.png
new file mode 100644
index 0000000..5f65c26
--- /dev/null
+++ b/gui/devices/800x1280/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/cursor.png b/gui/devices/800x1280/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/800x1280/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/curtain.jpg b/gui/devices/800x1280/res/images/curtain.jpg
new file mode 100644
index 0000000..b21df31
--- /dev/null
+++ b/gui/devices/800x1280/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/800x1280/res/images/file.png b/gui/devices/800x1280/res/images/file.png
new file mode 100644
index 0000000..aea3ac2
--- /dev/null
+++ b/gui/devices/800x1280/res/images/file.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/folder.png b/gui/devices/800x1280/res/images/folder.png
new file mode 100644
index 0000000..56f4bc2
--- /dev/null
+++ b/gui/devices/800x1280/res/images/folder.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/home-icon.png b/gui/devices/800x1280/res/images/home-icon.png
new file mode 100644
index 0000000..d4373b7
--- /dev/null
+++ b/gui/devices/800x1280/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/indeterminate001.png b/gui/devices/800x1280/res/images/indeterminate001.png
new file mode 100644
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/800x1280/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/indeterminate002.png b/gui/devices/800x1280/res/images/indeterminate002.png
new file mode 100644
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/800x1280/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/indeterminate003.png b/gui/devices/800x1280/res/images/indeterminate003.png
new file mode 100644
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/800x1280/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/indeterminate004.png b/gui/devices/800x1280/res/images/indeterminate004.png
new file mode 100644
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/800x1280/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/indeterminate005.png b/gui/devices/800x1280/res/images/indeterminate005.png
new file mode 100644
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/800x1280/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/indeterminate006.png b/gui/devices/800x1280/res/images/indeterminate006.png
new file mode 100644
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/800x1280/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/keyboard1.png b/gui/devices/800x1280/res/images/keyboard1.png
new file mode 100755
index 0000000..17205c8
--- /dev/null
+++ b/gui/devices/800x1280/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/keyboard2.png b/gui/devices/800x1280/res/images/keyboard2.png
new file mode 100755
index 0000000..e4f5c36
--- /dev/null
+++ b/gui/devices/800x1280/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/keyboard3.png b/gui/devices/800x1280/res/images/keyboard3.png
new file mode 100755
index 0000000..7214ff3
--- /dev/null
+++ b/gui/devices/800x1280/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/keyboard4.png b/gui/devices/800x1280/res/images/keyboard4.png
new file mode 100755
index 0000000..efe6dc6
--- /dev/null
+++ b/gui/devices/800x1280/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/medium-button.png b/gui/devices/800x1280/res/images/medium-button.png
new file mode 100644
index 0000000..207faef
--- /dev/null
+++ b/gui/devices/800x1280/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/menu-button.png b/gui/devices/800x1280/res/images/menu-button.png
new file mode 100644
index 0000000..0642f38
--- /dev/null
+++ b/gui/devices/800x1280/res/images/menu-button.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/minus-button.png b/gui/devices/800x1280/res/images/minus-button.png
new file mode 100644
index 0000000..7b5f79f
--- /dev/null
+++ b/gui/devices/800x1280/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/plus-button.png b/gui/devices/800x1280/res/images/plus-button.png
new file mode 100644
index 0000000..b328577
--- /dev/null
+++ b/gui/devices/800x1280/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/progress_empty.png b/gui/devices/800x1280/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/800x1280/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/progress_fill.png b/gui/devices/800x1280/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/800x1280/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/radio_empty.png b/gui/devices/800x1280/res/images/radio_empty.png
new file mode 100644
index 0000000..88d1c1f
--- /dev/null
+++ b/gui/devices/800x1280/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/radio_selected.png b/gui/devices/800x1280/res/images/radio_selected.png
new file mode 100644
index 0000000..864065d
--- /dev/null
+++ b/gui/devices/800x1280/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/slideout.png b/gui/devices/800x1280/res/images/slideout.png
new file mode 100644
index 0000000..c3c65e4
--- /dev/null
+++ b/gui/devices/800x1280/res/images/slideout.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/slider-touch.png b/gui/devices/800x1280/res/images/slider-touch.png
new file mode 100644
index 0000000..83fce08
--- /dev/null
+++ b/gui/devices/800x1280/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/slider-used.png b/gui/devices/800x1280/res/images/slider-used.png
new file mode 100644
index 0000000..4e4a1f4
--- /dev/null
+++ b/gui/devices/800x1280/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/slider.png b/gui/devices/800x1280/res/images/slider.png
new file mode 100644
index 0000000..118edc6
--- /dev/null
+++ b/gui/devices/800x1280/res/images/slider.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/sort-button.png b/gui/devices/800x1280/res/images/sort-button.png
new file mode 100644
index 0000000..125c2ce
--- /dev/null
+++ b/gui/devices/800x1280/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/800x1280/res/images/top-bar.jpg b/gui/devices/800x1280/res/images/top-bar.jpg
new file mode 100644
index 0000000..265b717
--- /dev/null
+++ b/gui/devices/800x1280/res/images/top-bar.jpg
Binary files differ
diff --git a/gui/devices/800x1280/res/images/unlock.png b/gui/devices/800x1280/res/images/unlock.png
new file mode 100644
index 0000000..9041221
--- /dev/null
+++ b/gui/devices/800x1280/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/800x1280/res/ui.xml b/gui/devices/800x1280/res/ui.xml
new file mode 100644
index 0000000..03b6144
--- /dev/null
+++ b/gui/devices/800x1280/res/ui.xml
@@ -0,0 +1,415 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="800" height="1280" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="portrait.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="30" fallback="Roboto-Condensed-30" />
+		<resource name="mediumfont" type="font" filename="RobotoCondensed-Regular.ttf" size="30" fallback="Roboto-Condensed-30" />
+		<resource name="filelist" type="font" filename="RobotoCondensed-Regular.ttf" size="30" fallback="Roboto-Condensed-30" />
+		<resource name="top_bar" type="image" filename="top-bar.jpg" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="slideout" type="image" filename="slideout" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="10" />
+		<variable name="col2_x" value="415" />
+		<variable name="col_center_x" value="213" />
+		<variable name="col_center_medium_x" value="306" />
+		<variable name="center_x" value="400" />
+		<variable name="row1_y" value="185" />
+		<variable name="row2_y" value="425" />
+		<variable name="row3_y" value="665" />
+		<variable name="row4_y" value="905" />
+		<variable name="row_queue_y" value="710" />
+		<variable name="row1_header_y" value="131" />
+		<variable name="text_row_height" value="50" />
+		<variable name="row1_text_y" value="170" />
+		<variable name="row2_text_y" value="%row1_text_y%+%text_row_height%" />
+		<variable name="row3_text_y" value="%row2_text_y%+%text_row_height%" />
+		<variable name="row4_text_y" value="%row3_text_y%+%text_row_height%" />
+		<variable name="row5_text_y" value="%row4_text_y%+%text_row_height%" />
+		<variable name="row6_text_y" value="%row5_text_y%+%text_row_height%" />
+		<variable name="row7_text_y" value="%row6_text_y%+%text_row_height%" />
+		<variable name="row8_text_y" value="%row7_text_y%+%text_row_height%" />
+		<variable name="row9_text_y" value="%row8_text_y%+%text_row_height%" />
+		<variable name="row10_text_y" value="%row9_text_y%+%text_row_height%" />
+		<variable name="row11_text_y" value="%row10_text_y%+%text_row_height%" />
+		<variable name="row12_text_y" value="%row11_text_y%+%text_row_height%" />
+		<variable name="row13_text_y" value="%row12_text_y%+%text_row_height%" />
+		<variable name="row14_text_y" value="%row13_text_y%+%text_row_height%" />
+		<variable name="row15_text_y" value="%row14_text_y%+%text_row_height%" />
+		<variable name="row16_text_y" value="%row15_text_y%+%text_row_height%" />
+		<variable name="row17_text_y" value="%row16_text_y%+%text_row_height%" />
+		<variable name="row18_text_y" value="%row17_text_y%+%text_row_height%" />
+		<variable name="zip_status_y" value="640" />
+		<variable name="tz_selected_y" value="160" />
+		<variable name="tz_set_y" value="955" />
+		<variable name="tz_current_y" value="1185" />
+		<variable name="col_progressbar_x" value="264" />
+		<variable name="row_progressbar_y" value="1100" />
+		<variable name="col1_medium_x" value="10" />
+		<variable name="col2_medium_x" value="208" />
+		<variable name="col3_medium_x" value="406" />
+		<variable name="col4_medium_x" value="604" />
+		<variable name="row1_medium_y" value="130" />
+		<variable name="row2_medium_y" value="230" />
+		<variable name="row3_medium_y" value="330" />
+		<variable name="row4_medium_y" value="430" />
+		<variable name="row5_medium_y" value="530" />
+		<variable name="row6_medium_y" value="840" />
+		<variable name="row7_medium_y" value="730" />
+		<variable name="slider_x" value="98" />
+		<variable name="slider_y" value="1030" />
+		<variable name="slider_text_y" value="1096" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#FFFFFF" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="home_button_x" value="50" />
+		<variable name="home_button_y" value="1226" />
+		<variable name="back_button_x" value="666" />
+		<variable name="back_button_y" value="1226" />
+		<variable name="sort_text_x" value="85" />
+		<variable name="sort_asc_text_y" value="1090" />
+		<variable name="sort_asc_button_y" value="1080" />
+		<variable name="sort_desc_text_y" value="1150" />
+		<variable name="sort_desc_button_y" value="1140" />
+		<variable name="sort_col1_button_x" value="330" />
+		<variable name="sort_col2_button_x" value="430" />
+		<variable name="sort_col3_button_x" value="530" />
+		<variable name="input_width" value="750" />
+		<variable name="input_height" value="50" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="3" />
+		<variable name="console_x" value="0" />
+		<variable name="console_width" value="800" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="470" />
+		<variable name="console_install_height" value="600" />
+		<variable name="console_installdone_height" value="440" />
+		<variable name="fileselector_x" value="5" />
+		<variable name="fileselector_width" value="790" />
+		<variable name="fileselector_install_height" value="750" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="3" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="2" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="36"/>
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="60" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="40" />
+		<variable name="fastscroll_recth" value="70" />
+		<variable name="listbox_x" value="5" />
+		<variable name="listbox_width" value="790" />
+		<variable name="listbox_tz_height" value="580" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="16" />
+		<variable name="sd_plus_x" value="350" />
+		<variable name="sdext_text_x" value="115" />
+		<variable name="sdext_text_y" value="190" />
+		<variable name="sdswap_button_y" value="260" />
+		<variable name="sdswap_text_x" value="115" />
+		<variable name="sdswap_text_y" value="265" />
+		<variable name="sdfilesystem_text_y" value="340" />
+		<variable name="sdfilesystem_button_y" value="380" />
+		<variable name="lock_x" value="200" />
+		<variable name="lock_y" value="400" />
+		<variable name="filemanager_select_x" value="320" />
+		<variable name="filemanager_select_y" value="950" />
+		<variable name="backup_name_y" value="550" />
+		<variable name="terminal_console_height" value="600" />
+		<variable name="terminal_text_y" value="650" />
+		<variable name="terminal_button_y" value="620" />
+		<variable name="row_dst_text_y" value="720" />
+		<variable name="row_offset_text_y" value="800" />
+		<variable name="row_offset_medium_y" value="840" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="780" />
+		<variable name="button_fill_main_width" value="375" />
+		<variable name="button_fill_main_height" value="240" />
+		<variable name="button_fill_half_height" value="120" />
+		<variable name="button_fill_quarter_height" value="54" />
+		<variable name="backup_list_height" value="520" />
+		<variable name="backup_button_row1" value="745" />
+		<variable name="backup_button_row2" value="810" />
+		<variable name="mount_list_height" value="690" />
+		<variable name="mount_storage_row" value="830" />
+		<variable name="storage_list_height" value="775" />
+		<variable name="wipe_list_height" value="670" />
+		<variable name="wipe_button_row1" value="900" />
+		<variable name="wipe_button_y" value="650" />
+		<variable name="slidervalue_w" value="780" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="2" />
+		<variable name="slidervalue_padding" value="20" />
+		<variable name="slidervalue_sliderw" value="10" />
+		<variable name="slidervalue_sliderh" value="60" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="2.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="top_bar" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="120" y="10" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="120" y="45" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="120" y="76" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="270" y="76" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="420" y="76" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="0" y="%row3_text_y%" w="800" h="600" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="slideout" x="355" y="1220" />
+				<placement x="%console_x%" y="0" w="%console_width%" h="1220" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="740" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="115" width="80" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" />
+					<row2 key01="117:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="123:l" />
+					<row3 key01="117:layout2" key02="z" key03="x" key04="c" key05="v" key06="b" key07="n" key08="m" key09="123:c:8" />
+					<row4 key01="117:layout3" key02="80:" key03="400: " key04="80:." key05="123:a:action" />
+				</layout1>
+				<layout2>
+					<keysize height="116" width="80" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" />
+					<row2 key01="117:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="123:L" />
+					<row3 key01="117:layout1" key02="Z" key03="X" key04="C" key05="V" key06="B" key07="N" key08="M" key09="123:c:8" />
+					<row4 key01="117:layout3" key02="80:," key03="400: " key04="80:." key05="123:action" />
+				</layout2>
+				<layout3>
+					<keysize height="115" width="80" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" />
+					<row2 key01="@" key02="#" key03="$" key04="%" key05="&" key06="*" key07="-" key08="+" key09="(" key10=")" />
+					<row3 key01="117:layout4" key02="!" key03="80:c:34" key04="'" key05=":" key06=";" key07="/" key08="?" key09="123:c:8" />
+					<row4 key01="117:layout1" key02="80:," key03="400: " key04="80:." key05="123:action" />
+				</layout3>
+				<layout4>
+					<keysize height="116" width="80" />
+					<row1 key01="~" key02="`" key03="|" key04="80:" key05="80:" key06="80:" key07="80:" key08="80:" key09="{" key10="}" />
+					<row2 key01="80:" key02="80:" key03="80:" key04="80:" key05="80:" key06="^" key07="_" key08="=" key09="[" key10="]" />
+					<row3 key01="117:layout3" key02="80:" key03="80:" key04="80:" key05="80:" key06="\" key07="<" key08=">" key09="123:c:8" />
+					<row4 key01="117:layout1" key02="80:" key03="400: " key04="80:" key05="123:action" />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/800x480/res/fonts/Roboto-Condensed-16.dat b/gui/devices/800x480/res/fonts/Roboto-Condensed-16.dat
new file mode 100644
index 0000000..19c1147
--- /dev/null
+++ b/gui/devices/800x480/res/fonts/Roboto-Condensed-16.dat
Binary files differ
diff --git a/gui/devices/800x480/res/images/back-icon.png b/gui/devices/800x480/res/images/back-icon.png
new file mode 100644
index 0000000..1096c90
--- /dev/null
+++ b/gui/devices/800x480/res/images/back-icon.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/background.jpg b/gui/devices/800x480/res/images/background.jpg
new file mode 100644
index 0000000..af82531
--- /dev/null
+++ b/gui/devices/800x480/res/images/background.jpg
Binary files differ
diff --git a/gui/devices/800x480/res/images/button.png b/gui/devices/800x480/res/images/button.png
new file mode 100644
index 0000000..49b8784
--- /dev/null
+++ b/gui/devices/800x480/res/images/button.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/checkbox_checked.png b/gui/devices/800x480/res/images/checkbox_checked.png
new file mode 100644
index 0000000..3759b7f
--- /dev/null
+++ b/gui/devices/800x480/res/images/checkbox_checked.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/checkbox_empty.png b/gui/devices/800x480/res/images/checkbox_empty.png
new file mode 100644
index 0000000..43a6404
--- /dev/null
+++ b/gui/devices/800x480/res/images/checkbox_empty.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/console-icon.png b/gui/devices/800x480/res/images/console-icon.png
new file mode 100644
index 0000000..91a727d
--- /dev/null
+++ b/gui/devices/800x480/res/images/console-icon.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/console-toggle.png b/gui/devices/800x480/res/images/console-toggle.png
new file mode 100644
index 0000000..963b9fd
--- /dev/null
+++ b/gui/devices/800x480/res/images/console-toggle.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/cursor.png b/gui/devices/800x480/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/800x480/res/images/cursor.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/curtain.jpg b/gui/devices/800x480/res/images/curtain.jpg
new file mode 100644
index 0000000..1f03eb5
--- /dev/null
+++ b/gui/devices/800x480/res/images/curtain.jpg
Binary files differ
diff --git a/gui/devices/800x480/res/images/file.png b/gui/devices/800x480/res/images/file.png
new file mode 100644
index 0000000..8556bc7
--- /dev/null
+++ b/gui/devices/800x480/res/images/file.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/folder.png b/gui/devices/800x480/res/images/folder.png
new file mode 100644
index 0000000..a3a5f69
--- /dev/null
+++ b/gui/devices/800x480/res/images/folder.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/home-icon.png b/gui/devices/800x480/res/images/home-icon.png
new file mode 100644
index 0000000..e42d774
--- /dev/null
+++ b/gui/devices/800x480/res/images/home-icon.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/indeterminate001.png b/gui/devices/800x480/res/images/indeterminate001.png
new file mode 100755
index 0000000..e6fa1c5
--- /dev/null
+++ b/gui/devices/800x480/res/images/indeterminate001.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/indeterminate002.png b/gui/devices/800x480/res/images/indeterminate002.png
new file mode 100755
index 0000000..e1fceab
--- /dev/null
+++ b/gui/devices/800x480/res/images/indeterminate002.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/indeterminate003.png b/gui/devices/800x480/res/images/indeterminate003.png
new file mode 100755
index 0000000..6702867
--- /dev/null
+++ b/gui/devices/800x480/res/images/indeterminate003.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/indeterminate004.png b/gui/devices/800x480/res/images/indeterminate004.png
new file mode 100755
index 0000000..ff65e09
--- /dev/null
+++ b/gui/devices/800x480/res/images/indeterminate004.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/indeterminate005.png b/gui/devices/800x480/res/images/indeterminate005.png
new file mode 100755
index 0000000..e61e2cc
--- /dev/null
+++ b/gui/devices/800x480/res/images/indeterminate005.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/indeterminate006.png b/gui/devices/800x480/res/images/indeterminate006.png
new file mode 100755
index 0000000..c9c21ba
--- /dev/null
+++ b/gui/devices/800x480/res/images/indeterminate006.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/keyboard1.png b/gui/devices/800x480/res/images/keyboard1.png
new file mode 100644
index 0000000..1ccb64b
--- /dev/null
+++ b/gui/devices/800x480/res/images/keyboard1.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/keyboard2.png b/gui/devices/800x480/res/images/keyboard2.png
new file mode 100644
index 0000000..f19eacc
--- /dev/null
+++ b/gui/devices/800x480/res/images/keyboard2.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/keyboard3.png b/gui/devices/800x480/res/images/keyboard3.png
new file mode 100644
index 0000000..0220860
--- /dev/null
+++ b/gui/devices/800x480/res/images/keyboard3.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/keyboard4.png b/gui/devices/800x480/res/images/keyboard4.png
new file mode 100644
index 0000000..7fc0ea5
--- /dev/null
+++ b/gui/devices/800x480/res/images/keyboard4.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/medium-button.png b/gui/devices/800x480/res/images/medium-button.png
new file mode 100644
index 0000000..77dc540
--- /dev/null
+++ b/gui/devices/800x480/res/images/medium-button.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/mediumwide-button.png b/gui/devices/800x480/res/images/mediumwide-button.png
new file mode 100644
index 0000000..b2b7d4d
--- /dev/null
+++ b/gui/devices/800x480/res/images/mediumwide-button.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/minus-button.png b/gui/devices/800x480/res/images/minus-button.png
new file mode 100644
index 0000000..5a49c75
--- /dev/null
+++ b/gui/devices/800x480/res/images/minus-button.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/plus-button.png b/gui/devices/800x480/res/images/plus-button.png
new file mode 100644
index 0000000..da1326c
--- /dev/null
+++ b/gui/devices/800x480/res/images/plus-button.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/progress_empty.png b/gui/devices/800x480/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/800x480/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/progress_fill.png b/gui/devices/800x480/res/images/progress_fill.png
new file mode 100644
index 0000000..669c6ef
--- /dev/null
+++ b/gui/devices/800x480/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/radio_empty.png b/gui/devices/800x480/res/images/radio_empty.png
new file mode 100644
index 0000000..d118aaf
--- /dev/null
+++ b/gui/devices/800x480/res/images/radio_empty.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/radio_selected.png b/gui/devices/800x480/res/images/radio_selected.png
new file mode 100644
index 0000000..5a8a0fd
--- /dev/null
+++ b/gui/devices/800x480/res/images/radio_selected.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/slider-touch.png b/gui/devices/800x480/res/images/slider-touch.png
new file mode 100644
index 0000000..dee19ee
--- /dev/null
+++ b/gui/devices/800x480/res/images/slider-touch.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/slider-used.png b/gui/devices/800x480/res/images/slider-used.png
new file mode 100644
index 0000000..754395a
--- /dev/null
+++ b/gui/devices/800x480/res/images/slider-used.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/slider.png b/gui/devices/800x480/res/images/slider.png
new file mode 100644
index 0000000..b0cc079
--- /dev/null
+++ b/gui/devices/800x480/res/images/slider.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/sort-button.png b/gui/devices/800x480/res/images/sort-button.png
new file mode 100644
index 0000000..13ab929
--- /dev/null
+++ b/gui/devices/800x480/res/images/sort-button.png
Binary files differ
diff --git a/gui/devices/800x480/res/images/unlock.png b/gui/devices/800x480/res/images/unlock.png
new file mode 100644
index 0000000..914c60d
--- /dev/null
+++ b/gui/devices/800x480/res/images/unlock.png
Binary files differ
diff --git a/gui/devices/800x480/res/ui.xml b/gui/devices/800x480/res/ui.xml
new file mode 100644
index 0000000..ed1c842
--- /dev/null
+++ b/gui/devices/800x480/res/ui.xml
@@ -0,0 +1,447 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="800" height="480" />
+		<author>TeamWin</author>
+		<title>Backup Naowz</title>
+		<description>Default basic theme</description>
+		<preview>preview.jpg</preview>
+	</details>
+
+	<include>
+		<xmlfile name="landscape.xml" />
+	</include>
+
+	<resources>
+		<resource name="font" type="font" filename="RobotoCondensed-Regular.ttf" size="16" fallback="Roboto-Condensed-16" />
+		<resource name="base" type="image" filename="background.jpg" />
+		<resource name="main_button" type="image" filename="button" />
+		<resource name="file_icon" type="image" filename="file" />
+		<resource name="folder_icon" type="image" filename="folder" />
+		<resource name="progress" type="animation" filename="indeterminate" />
+		<resource name="progress_empty" type="image" filename="progress_empty" />
+		<resource name="progress_full" type="image" filename="progress_fill" />
+		<resource name="checkbox_false" type="image" filename="checkbox_empty" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" />
+		<resource name="radio_false" type="image" filename="radio_empty" />
+		<resource name="radio_true" type="image" filename="radio_selected" />
+		<resource name="medium_button" type="image" filename="medium-button" />
+		<resource name="mediumwide_button" type="image" filename="mediumwide-button" />
+		<resource name="sort_button" type="image" filename="sort-button" />
+		<resource name="minus_button" type="image" filename="minus-button" />
+		<resource name="plus_button" type="image" filename="plus-button" />
+		<resource name="home_icon" type="image" filename="home-icon" />
+		<resource name="back_icon" type="image" filename="back-icon" />
+		<resource name="console_button" type="image" filename="console-toggle" />
+		<resource name="slider" type="image" filename="slider" />
+		<resource name="slider-used" type="image" filename="slider-used" />
+		<resource name="slider-touch" type="image" filename="slider-touch" />
+		<resource name="unlock-icon" type="image" filename="unlock" />
+		<resource name="keyboard1" type="image" filename="keyboard1" />
+		<resource name="keyboard2" type="image" filename="keyboard2" />
+		<resource name="keyboard3" type="image" filename="keyboard3" />
+		<resource name="keyboard4" type="image" filename="keyboard4" />
+		<resource name="cursor" type="image" filename="cursor" />
+	</resources>
+
+	<variables>
+		<variable name="col1_x" value="23" />
+		<variable name="col2_x" value="210" />
+		<variable name="col3_x" value="410" />
+		<variable name="col4_x" value="610" />
+		<variable name="row1_y" value="90" />
+		<variable name="row2_y" value="340" />
+		<variable name="col_center_x" value="310" />
+		<variable name="center_x" value="400" />
+		<variable name="screen_width" value="800" />
+		<variable name="screen_height" value="480" />
+		<variable name="col_progressbar_x" value="300" />
+		<variable name="row_progressbar_y" value="440" />
+		<variable name="col1_medium_x" value="120" />
+		<variable name="col2_medium_x" value="250" />
+		<variable name="col3_medium_x" value="380" />
+		<variable name="col4_medium_x" value="510" />
+		<variable name="row1_medium_y" value="105" />
+		<variable name="row2_medium_y" value="175" />
+		<variable name="row3_medium_y" value="245" />
+		<variable name="row4_medium_y" value="340" />
+		<variable name="row5_medium_y" value="320" />
+		<variable name="row1_text_y" value="58" />
+		<variable name="row2_text_y" value="78" />
+		<variable name="row3_text_y" value="100" />
+		<variable name="row4_text_y" value="120" />
+		<variable name="row5_text_y" value="140" />
+		<variable name="row6_text_y" value="160" />
+		<variable name="row7_text_y" value="180" />
+		<variable name="row8_text_y" value="200" />
+		<variable name="row9_text_y" value="220" />
+		<variable name="row10_text_y" value="240" />
+		<variable name="row11_text_y" value="260" />
+		<variable name="row12_text_y" value="280" />
+		<variable name="row13_text_y" value="300" />
+		<variable name="row14_text_y" value="320" />
+		<variable name="row15_text_y" value="340" />
+		<variable name="row16_text_y" value="360" />
+		<variable name="row17_text_y" value="380" />
+		<variable name="row18_text_y" value="400" />
+		<variable name="row_offsetmedium_y" value="320" />
+		<variable name="home_button_x" value="589" />
+		<variable name="home_button_y" value="5" />
+		<variable name="back_button_x" value="659" />
+		<variable name="back_button_y" value="5" />
+		<variable name="console_button_x" value="729" />
+		<variable name="console_button_y" value="5" />
+		<variable name="nandcheck_col1" value="23" />
+		<variable name="nandcheck_col2" value="360" />
+		<variable name="nandcheck_row1" value="130" />
+		<variable name="nandcheck_row2" value="160" />
+		<variable name="nandcheck_row3" value="190" />
+		<variable name="nandcheck_row4" value="220" />
+		<variable name="nandcheck_row5" value="250" />
+		<variable name="nandcheck_row6" value="280" />
+		<variable name="nandcheck_row7" value="310" />
+		<variable name="button_text_color" value="#AAAAAA" />
+		<variable name="text_color" value="#A0A0A0" />
+		<variable name="text_success_color" value="#33B5E5" />
+		<variable name="text_fail_color" value="#FF0101" />
+		<variable name="highlight_color" value="#90909080" />
+		<variable name="caps_highlight_color" value="#33B5E580" />
+		<variable name="slider_x" value="225" />
+		<variable name="slider_y" value="390" />
+		<variable name="slider_text_y" value="425" />
+		<variable name="sort_text_x" value="170" />
+		<variable name="sort_asc_text_y" value="418" />
+		<variable name="sort_asc_button_y" value="413" />
+		<variable name="sort_desc_text_y" value="452" />
+		<variable name="sort_desc_button_y" value="447" />
+		<variable name="sort_col1_button_x" value="300" />
+		<variable name="sort_col2_button_x" value="370" />
+		<variable name="sort_col3_button_x" value="440" />
+		<variable name="col1_sdext_x" value="265" />
+		<variable name="col2_sdext_x" value="475" />
+		<variable name="row1_sdext_y" value="85" />
+		<variable name="row2_sdext_y" value="150" />
+		<variable name="row_extsize_y" value="85" />
+		<variable name="row_swapsize_y" value="150" />
+		<variable name="input_x" value="28" />
+		<variable name="input_width" value="720" />
+		<variable name="input_height" value="25" />
+		<variable name="input_background_color" value="#303030" />
+		<variable name="input_cursor_color" value="#33B5E5" />
+		<variable name="input_cursor_width" value="2" />
+		<variable name="console_x" value="25" />
+		<variable name="console_width" value="750" />
+		<variable name="console_foreground" value="#A0A0A0" />
+		<variable name="warning" value="#F8F8A0" />
+		<variable name="error" value="#FF4040" />
+		<variable name="highlight" value="#33B5E5" />
+		<variable name="console_background" value="#303030" />
+		<variable name="console_scroll" value="#303030" />
+		<variable name="console_action_height" value="230" />
+		<variable name="console_install_height" value="260" />
+		<variable name="console_installdone_height" value="260" />
+		<variable name="fileselector_folder_x" value="28" />
+		<variable name="fileselector_folder_width" value="248" />
+		<variable name="fileselector_folderonly_width" value="400" />
+		<variable name="fileselector_file_x" value="278" />
+		<variable name="fileselector_file_width" value="506" />
+		<variable name="fileselector_install_y" value="120" />
+		<variable name="fileselector_install_height" value="290" />
+		<variable name="fileselector_header_background" value="#202020" />
+		<variable name="fileselector_header_textcolor" value="#AAAAAA" />
+		<variable name="fileselector_header_separatorcolor" value="#33B5E5" />
+		<variable name="fileselector_header_separatorheight" value="2" />
+		<variable name="fileselector_separatorcolor" value="#505050" />
+		<variable name="fileselector_separatorheight" value="1" />
+		<variable name="fileselector_background" value="#303030" />
+		<variable name="fileselector_highlight_color" value="#505050" />
+		<variable name="fileselector_highlight_font_color" value="#33B5E5" />
+		<variable name="fileselector_spacing" value="12" />
+		<variable name="fastscroll_linecolor" value="#808080" />
+		<variable name="fastscroll_rectcolor" value="#808080" />
+		<variable name="fastscroll_w" value="25" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="19" />
+		<variable name="fastscroll_recth" value="31" />
+		<variable name="zipstorage_text_y" value="88" />
+		<variable name="listbox_x" value="156" />
+		<variable name="listbox_y" value="90" />
+		<variable name="listbox_width" value="460" />
+		<variable name="listbox_tz_height" value="170" />
+		<variable name="listbox_background" value="#303030" />
+		<variable name="listbox_spacing" value="12" />
+		<variable name="sd_plus_x" value="280" />
+		<variable name="lock_x" value="250" />
+		<variable name="lock_y" value="90" />
+		<variable name="filemanager_select_x" value="610" />
+		<variable name="filemanager_select_y" value="413" />
+		<variable name="backup_name_text_y" value="440" />
+		<variable name="backup_name_button_y" value="195" />
+		<variable name="col_right_x" value="772" />
+		<variable name="cancel_button_y" value="180" />
+		<variable name="terminal_console_y" value="0" />
+		<variable name="terminal_console_height" value="240" />
+		<variable name="terminal_text_y" value="247" />
+		<variable name="terminal_button_y" value="237" />
+		<variable name="terminal_input_width" value="551" />
+		<variable name="button_fill_color" value="#303030" />
+		<variable name="button_fill_full_width" value="400" />
+		<variable name="button_fill_main_width" value="367" />
+		<variable name="button_fill_main_height" value="120" />
+		<variable name="button_fill_half_height" value="40" />
+		<variable name="button_fill_quarter_height" value="30" />
+		<variable name="button_full_center_x" value="200" />
+		<variable name="backup_list_x" value="23" />
+		<variable name="backup_list_y" value="80" />
+		<variable name="backup_list_width" value="367" />
+		<variable name="backup_list_height" value="290" />
+		<variable name="backup_storage_y" value="190" />
+		<variable name="backup_encrypt_y" value="235" />
+		<variable name="restore_list_y" value="100" />
+		<variable name="restore_list_height" value="270" />
+		<variable name="mount_list_height" value="360" />
+		<variable name="mount_storage_row" value="500" />
+		<variable name="wipe_list_height" value="300" />
+		<variable name="wipe_button_y" value="150" />
+		<variable name="slidervalue_x" value="200" />
+		<variable name="slidervalue_w" value="400" />
+		<variable name="slidervalue_line_clr" value="#FFFFFF" />
+		<variable name="slidervalue_slider_clr" value="#33B5E5" />
+		<variable name="slidervalue_lineh" value="1" />
+		<variable name="slidervalue_padding" value="0" />
+		<variable name="slidervalue_sliderw" value="7" />
+		<variable name="slidervalue_sliderh" value="40" />
+	</variables>
+
+	<mousecursor>
+		<placement w="15" h="15" />
+		<background color="#FFFF00FF" resource="cursor" />
+		<speed multiplier="1.5" />
+	</mousecursor>
+
+	<templates>
+		<template name="header">
+			<background color="#000000FF" />
+
+			<object type="image">
+				<image resource="base" />
+				<placement x="0" y="0" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="250" y="2" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="250" y="37" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op=">" var2="0" />
+					<condition var1="tw_battery" op="<" var2="101" />
+				</conditions>
+				<text>Battery Level: %tw_battery%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="425" y="37" />
+				<text>%tw_time%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="500" y="37" />
+				<conditions>
+					<condition var1="tw_no_cpu_temp" var2="0" />
+				</conditions>
+				<text>CPU: %tw_cpu_temp% C</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<font resource="font" />
+				<placement x="250" y="20" />
+				<text>SIMULATING ACTIONS</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%home_button_x%" y="%home_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%back_button_x%" y="%back_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+		</template>
+
+		<template name="progress_bar">
+			<object type="progressbar">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource empty="progress_empty" full="progress_full" />
+				<data name="ui_progress" />
+			</object>
+
+			<object type="animation">
+				<placement x="%col_progressbar_x%" y="%row_progressbar_y%" />
+				<resource name="progress" />
+				<speed fps="15" render="2" />
+				<loop frame="1" />
+			</object>
+		</template>
+
+		<template name="sort_options">
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_asc_text_y%" />
+				<text>Sort Ascending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_asc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=3</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sort_text_x%" y="%sort_desc_text_y%" />
+				<text>Sort Descending:</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col1_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Name</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-1</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col2_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Date</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-2</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sort_col3_button_x%" y="%sort_desc_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Size</text>
+				<image resource="sort_button" />
+				<action function="set">tw_gui_sort_order=-3</action>
+			</object>
+		</template>
+
+		<template name="flash_zip_console">
+			<object type="console">
+				<placement x="%console_x%" y="85" w="%console_width%" h="260" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="action_page_console">
+			<object type="console">
+				<placement x="%console_x%" y="160" w="%console_width%" h="230" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="footer">
+			<object type="console">
+				<slideout resource="console_button" x="%console_button_x%" y="%console_button_y%" />
+				<placement x="%console_x%" y="60" w="%console_width%" h="380" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+		</template>
+
+		<template name="keyboardtemplate">
+			<object type="keyboard">
+				<placement x="0" y="277" />
+				<layout resource1="keyboard1" resource2="keyboard2" resource3="keyboard3" resource4="keyboard4" />
+				<highlight color="%highlight_color%" />
+				<capshighlight color="%caps_highlight_color%" />
+				<layout1>
+					<keysize height="51" width="73" />
+					<row1 key01="q" long01="1" key02="w" long02="2" key03="e" long03="3" key04="r" long04="4" key05="t" long05="5" key06="y" long06="6" key07="u" long07="7" key08="i" long08="8" key09="o" long09="9" key10="p" long10="0" key11="70:c:8" />
+					<row2 key01="107:a" key02="s" key03="d" key04="f" key05="g" key06="h" key07="j" key08="k" key09="l" key10="109:action" />
+					<row3 key01="79:layout2" key02="72:z" key03="72:x" key04="72:c" key05="72:v" key06="72:b" key07="72:n" key08="72:m" key09="72:," long09="!" key10="72:." long10="?" key11="73::" long11="+" />
+					<row4 key01="103:layout3" key02="72:" key03="/" long03="@" key04="315: " key05="'" long05="73:c:34" key06="-" long06="_" />
+				</layout1>
+				<layout2>
+					<keysize height="51" width="73" capslock="0" revert_layout="1" />
+					<row1 key01="Q" long01="1" key02="W" long02="2" key03="E" long03="3" key04="R" long04="4" key05="T" long05="5" key06="Y" long06="6" key07="U" long07="7" key08="I" long08="8" key09="O" long09="9" key10="P" long10="0" key11="70:c:8" />
+					<row2 key01="107:A" key02="S" key03="D" key04="F" key05="G" key06="H" key07="J" key08="K" key09="L" key10="109:action" />
+					<row3 key01="79:layout1" key02="72:Z" key03="72:X" key04="72:C" key05="72:V" key06="72:B" key07="72:N" key08="72:M" key09="72:," long09="!" key10="72:." long10="?" key11="73::" long11="+" />
+					<row4 key01="103:layout3" key02="72:" key03="/" long03="@" key04="315: " key05="'" long05="73:c:34" key06="-" long06="_" />
+				</layout2>
+				<layout3>
+					<keysize height="51" width="73" />
+					<row1 key01="1" key02="2" key03="3" key04="4" key05="5" key06="6" key07="7" key08="8" key09="9" key10="0" key11="70:c:8" />
+					<row2 key01="107:#" key02="$" key03="%" key04="&" key05="*" key06="-" key07="+" key08="(" key09=")" key10="109:action" />
+					<row3 key01="79:layout4" key02="72:<" key03="72:>" key04="72:=" key05="72::" key06="72:;" key07="72:," key08="72:." key09="72:!" key10="72:?" key11="73:/" />
+					<row4 key01="103:layout1" key02="72:" key03="@" key04="315: " key05="73:c:34" key06="_" />
+				</layout3>
+				<layout4>
+					<keysize height="51" width="73" />
+					<row1 key01="~" key02="`" key03="|" key04="73:" key05="73:" key06="73:" key07="73:" key08="73:" key09="73:" key10="73:" key11="70:c:8" />
+					<row2 key01="107:" key02="73:" key03="73:" key04="73:" key05="^" key06="73:" key07="73:" key08="{" key09="}" key10="109:action" />
+					<row3 key01="79:layout3" key02="72:\" key03="72:" key04="72:" key05="72:" key06="72:" key07="72:[" key08="72:]" key09="72:!" key10="72:?" />
+					<row4 key01="103:layout1" key02="72:" key03="72:" key04="315: " />
+				</layout4>
+			</object>
+		</template>
+	</templates>
+</recovery>
diff --git a/gui/devices/common/res/fonts/RobotoCondensed-Regular.ttf b/gui/devices/common/res/fonts/RobotoCondensed-Regular.ttf
new file mode 100644
index 0000000..b9fc49c
--- /dev/null
+++ b/gui/devices/common/res/fonts/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/gui/devices/common/res/images/progress_empty.png b/gui/devices/common/res/images/progress_empty.png
new file mode 100644
index 0000000..b853710
--- /dev/null
+++ b/gui/devices/common/res/images/progress_empty.png
Binary files differ
diff --git a/gui/devices/common/res/images/progress_fill.png b/gui/devices/common/res/images/progress_fill.png
new file mode 100644
index 0000000..6731dae
--- /dev/null
+++ b/gui/devices/common/res/images/progress_fill.png
Binary files differ
diff --git a/gui/devices/landscape/res/landscape.xml b/gui/devices/landscape/res/landscape.xml
new file mode 100644
index 0000000..cee7c15
--- /dev/null
+++ b/gui/devices/landscape/res/landscape.xml
@@ -0,0 +1,3929 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<pages>
+		<page name="main">
+			<object type="action">
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="main2">
+			<object type="template" name="header" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Install</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Backup</text>
+				<image resource="main_button" />
+				<action function="page">backup</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Restore</text>
+				<image resource="main_button" />
+				<action function="page">restore</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Wipe</text>
+				<image resource="main_button" />
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Mount</text>
+				<image resource="main_button" />
+				<action function="page">mount</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Settings</text>
+				<image resource="main_button" />
+				<action function="page">settings</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Advanced</text>
+				<image resource="main_button" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reboot</text>
+				<image resource="main_button" />
+				<action function="page">reboot</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="install">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Select Zip to Install</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%button_full_center_x%" y="%zipstorage_text_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=install</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_folder_x%" y="%fileselector_install_y%" w="%fileselector_folder_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Folders:</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="1" files="0" />
+				<path name="tw_zip_location" default="/sdcard" />
+				<data name="select" />
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_file_x%" y="%fileselector_install_y%" w="%fileselector_file_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_zip_location%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter extn=".zip" folders="0" files="1" />
+				<path name="tw_zip_location" />
+				<data name="tw_filename" />
+				<selection name="tw_file" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<condition var1="tw_filename" op="modified" />
+				<actions>
+					<action function="queuezip"></action>
+					<action function="page">flash_confirm</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="flash_confirm">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>WARNING: This operation may install incompatible software and render your device unusable.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Folder:</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_zip_location%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>File to flash:</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>%tw_file%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row6_text_y%" placement="5" />
+				<text>Press back to cancel adding this zip.</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col2_x%" y="%row7_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Zip file signature verification?</text>
+				<data variable="tw_signed_zip_verify" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row10_text_y%" placement="5" />
+				<text>File %tw_zip_queue_count% of max of 10</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_zip_queue_count" op="!=" var2="10"></condition>
+				<placement x="%col2_x%" y="%row5_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Add More Zips</text>
+				<image resource="mediumwide_button" />
+				<action function="page">install</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row5_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Clear Queue</text>
+				<image resource="mediumwide_button" />
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="flash">flash_zip</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Confirm Flash</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="cancelzip"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="flash_zip">
+			<object type="template" name="header" />
+
+			<object type="template" name="flash_zip_console" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row16_text_y%" />
+				<text>Flashing file %tw_zip_index% of %tw_zip_queue_count%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row17_text_y%" />
+				<text>%tw_filename%</text>
+			</object>
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<action function="page">flash_done</action>
+			</object>
+		</page>
+
+		<page name="flash_done">
+			<object type="template" name="header" />
+
+			<object type="template" name="flash_zip_console" />
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row17_text_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text" color="%text_success_color%">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row17_text_y%" placement="5" />
+				<text>Successful</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%slider_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Wipe Cache/Dalvik</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=flash_done</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=/cache</action>
+					<action function="set">tw_has_action2=1</action>
+					<action function="set">tw_action2=wipe</action>
+					<action function="set">tw_action2_param=dalvik</action>
+					<action function="set">tw_text1=Wipe Cache & Dalvik?</action>
+					<action function="set">tw_action_text1=Wiping Cache & Dalvik...</action>
+					<action function="set">tw_complete_text1=Cache & Dalvik Wipe Complete</action>
+					<action function="set">tw_slider_text=Swipe to Wipe</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_system" var2="1" />
+				<placement x="%col4_x%" y="%slider_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reboot System</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=main2</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=system</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_text3=</action>
+					<action function="set">tw_text4=</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%slider_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Home</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=install</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="clear_vars">
+			<object type="action">
+				<action function="set">tw_operation_state=0</action>
+				<action function="set">tw_text1=</action>
+				<action function="set">tw_text2=</action>
+				<action function="set">tw_text3=</action>
+				<action function="set">tw_text4=</action>
+				<action function="set">tw_action_text1=</action>
+				<action function="set">tw_action_text2=</action>
+				<action function="set">tw_action_param=</action>
+				<action function="set">tw_has_action2=0</action>
+				<action function="set">tw_action2=</action>
+				<action function="set">tw_action2_param=</action>
+				<action function="set">tw_has_cancel=0</action>
+				<action function="set">tw_cancel_action=</action>
+				<action function="set">tw_cancel_param=</action>
+				<action function="set">tw_show_exclamation=0</action>
+				<action function="set">tw_show_reboot=0</action>
+				<action function="page">%tw_clear_destination%</action>
+			</object>
+		</page>
+
+		<page name="confirm_action">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_text2%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_text3%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_text4%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row12_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">action_page</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>%tw_slider_text%</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="action_page">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_action_text2%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_has_cancel" var2="1" />
+				<placement x="%col4_x%" y="%slider_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="%tw_cancel_action%">%tw_cancel_param%</action>
+			</object>
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="0" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="1" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+					<action function="%tw_action2%">%tw_action2_param%</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="singleaction_page">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_action_text2%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="set">tw_page_done=1</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="0" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="1" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+					<action function="%tw_action2%">%tw_action2_param%</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="action_complete">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_complete_text1%</text>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text" color="%text_success_color%">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Successful</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_reboot" var2="0" />
+				<placement x="%col_center_x%" y="%slider_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Back</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_reboot" var2="1" />
+				<placement x="%col_center_x%" y="%slider_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reboot System</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=main2</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=system</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_text3=</action>
+					<action function="set">tw_text4=</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="filecheck">
+			<object type="action">
+				<action function="fileexists">%tw_filecheck%</action>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_fileexists=1</action>
+					<action function="page">%tw_existpage%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" var2="1" />
+				</conditions>
+				<actions>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">%tw_notexistpage%</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="rebootcheck">
+			<object type="action">
+				<condition var1="tw_backup_system_size" op=">=" var2="%tw_min_system%" />
+				<action function="reboot">%tw_action_param%</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_backup_system_size" op="<" var2="%tw_min_system%" />
+				<action function="page">confirm_action</action>
+			</object>
+		</page>
+
+		<page name="reboot">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Reboot Menu</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_system" var2="1" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>System</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=system</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_poweroff" var2="1" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Power Off</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=poweroff</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to power off?</action>
+					<action function="set">tw_action_text1=Turning Off...</action>
+					<action function="set">tw_complete_text1=Turning Off...</action>
+					<action function="set">tw_slider_text=Swipe to Power Off</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_recovery" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Recovery</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=recovery</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_bootloader" var2="1" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Bootloader</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=bootloader</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_download_mode" var2="1" />
+				<placement x="%col3_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Download</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=download</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="selectstorage">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%col2_x%" y="%fileselector_install_y%" w="%fileselector_folderonly_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Storage:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_storage_path" />
+				<listtype name="storage" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>OK</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="page">%tw_back%</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="mount">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Menu</text>
+			</object>
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%backup_list_x%" y="%backup_list_y%" w="%backup_list_width%" h="%mount_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Mount:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<listtype name="mount" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_is_decrypted" var2="0" />
+				</conditions>
+				<placement x="%col3_x%" y="row1_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Decrypt Data</text>
+				<image resource="main_button" />
+				<action function="page">decrypt</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_has_usb_storage" var2="1" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Mount USB Storage</text>
+				<image resource="main_button" />
+				<action function="page">usb_mount</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="0" />
+				</conditions>
+				<placement x="%col3_x%" y="row1_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Enable MTP</text>
+				<image resource="main_button" />
+				<action function="startmtp"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="1" />
+				</conditions>
+				<placement x="%col3_x%" y="row1_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Disable MTP</text>
+				<image resource="main_button" />
+				<action function="stopmtp"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_is_decrypted" var2="0" />
+				</conditions>
+				<placement x="%col3_x%" y="row1_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Decrypt Data</text>
+				<image resource="main_button" />
+				<action function="page">decrypt</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col3_x%" y="%backup_storage_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=mount</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="usb_mount">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<font resource="font" />
+				<text>USB Storage Mounted -- Be sure to safely remove your device from your computer before unmounting!</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" />
+				<text></text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Unmount</text>
+				<image resource="main_button" />
+				<action function="page">usb_umount</action>
+			</object>
+
+			<object type="action">
+				<action function="mount">usb</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="usb_umount">
+			<object type="action">
+				<action function="unmount">usb</action>
+			</object>
+
+			<object type="action">
+				<action function="page">mount</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="wipe">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Factory Reset: Wipes Data, Cache, and Dalvik</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_has_data_media" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>(not including internal storage)</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<conditions>
+					<condition var1="tw_has_android_secure" var2="1" />
+					<condition var1="fileexists" var2="/and-sec" />
+				</conditions>
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="1" />
+				<text>Android Secure  </text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_has_sdext_partition" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" />
+				<text>  SD-EXT</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Most of the time this is the only wipe that you need.</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%wipe_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Advanced Wipe</text>
+				<image resource="main_button" />
+				<action function="page">advancedwipe</action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_has_internal" var2="1" />
+					<condition var1="tw_has_data_media" var2="1" />
+				</conditions>
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%wipe_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Format Data</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="page">formatdata</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row12_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=wipe</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=data</action>
+					<action function="set">tw_action_text1=Factory Reset...</action>
+					<action function="set">tw_complete_text1=Factory Reset Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Factory Reset</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="advancedwipe">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%backup_list_x%" y="%backup_list_y%" w="%backup_list_width%" h="%backup_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Wipe:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_wipe_list" />
+				<listtype name="wipe" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col3_x%" y="%backup_storage_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Repair or Change File System</text>
+				<actions>
+					<action function="checkpartitionlist"></action>
+					<action function="page">checkpartitionlist</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="partitionlisterror" var2="1" />
+				<font resource="font" />
+				<placement x="%col3_x%" y="%backup_storage_y%" />
+				<text>Invalid partition selection</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=advancedwipe</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=LIST</action>
+					<action function="set">tw_text1=Wipe Selected Partition(s)?</action>
+					<action function="set">tw_action_text1=Wiping Partition(s)...</action>
+					<action function="set">tw_complete_text1=Wipe Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Wipe</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="formatdata">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Format Data will wipe all of your apps, backups, pictures,</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>videos, media, and removes encryption on internal storage.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>This cannot be undone. Press back to cancel.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Type yes to continue.</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row6_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_confirm_formatdata%</text>
+				<data name="tw_confirm_formatdata" />
+				<restrict minlen="3" maxlen="3" allow="yes" />
+				<action function="page">formatdata_confirm</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="formatdata_confirm">
+			<object type="action">
+				<condition var1="tw_confirm_formatdata" op="=" var2="yes" />
+				<actions>
+					<action function="set">tw_back=formatdata</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=DATAMEDIA</action>
+					<action function="set">tw_action_text1=Formatting Data...</action>
+					<action function="set">tw_complete_text1=Data Format Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_confirm_formatdata" op="!=" var2="yes" />
+				<action function="page">formatdata</function>
+			</object>
+		</page>
+
+		<page name="checkpartitionlist">
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="getpartitiondetails"></action>
+					<action function="page">partitionoptions</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="!=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=1</action>
+					<action function="set">tw_wipe_list=</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="partitionoptions">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<text>Partition Options for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col_right_x%" y="%row1_text_y%" placement="1" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_is_present" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Present: Yes</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_is_present" op="=" var2="0" />
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Present: No</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_removable" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%col3_x%" y="%row3_text_y%" />
+				<text>Removable: Yes</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_removable" op="=" var2="0" />
+				<font resource="font" />
+				<placement x="%col3_x%" y="%row3_text_y%" />
+				<text>Removable: No</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Size: %tw_partition_size%MB</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row4_text_y%" />
+				<text>Used: %tw_partition_used%MB</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col3_x%" y="%row4_text_y%" />
+				<text>Free: %tw_partition_free%MB</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col4_x%" y="%row4_text_y%" />
+				<text>Backup Size: %tw_partition_backup_size%MB</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_can_repair" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Repair</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=partitionoptions</action>
+					<action function="set">tw_action=repair</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Repair %tw_partition_name%?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Repairing...</action>
+					<action function="set">tw_complete_text1=Repair Complete</action>
+					<action function="set">tw_slider_text=Swipe to Repair</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Change File System</text>
+				<image resource="main_button" />
+				<action function="page">selectfilesystem</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advancedwipe</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="refreshfilesystem">
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="getpartitiondetails"></action>
+					<action function="page">selectfilesystem</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="!=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=1</action>
+					<action function="set">tw_wipe_list=</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="selectfilesystem">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<text>Change file system for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col_right_x%" y="%row1_text_y%" placement="1" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Some ROMs or kernels may not support some file systems. Proceed with caution!</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_ext" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT2</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=ext2</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to EXT2?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_ext" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT3</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=ext3</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to EXT3?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_ext" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT4</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=ext4</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to EXT4?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_f2fs" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>F2FS</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=f2fs</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to F2FS?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_vfat" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>FAT</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=vfat</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to FAT?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_exfat" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>exFAT</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=exfat</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to exFAT?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">partitionoptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backup">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Back Up Device</text>
+			</object>
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%backup_list_x%" y="%backup_list_y%" w="%backup_list_width%" h="%backup_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Back Up:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_backup_list" />
+				<listtype name="backup" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col_right_x%" y="%row2_text_y%" placement="1" />
+				<text>Backup Name: %tw_backup_name%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Refresh Sizes</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="refreshsizes"></action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Set Backup Name</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">backupname1</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col3_x%" y="%backup_storage_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=backup</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="0" />
+				</conditions>
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col3_x%" y="%backup_encrypt_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>No Encryption</text>
+				<actions>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="1" />
+				</conditions>
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col3_x%" y="%backup_encrypt_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Using Encryption</text>
+				<actions>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col3_x%" y="%nandcheck_row6%" />
+				<font resource="font" color="%text_color%" />
+				<text>Enable Compression (Requires more time)</text>
+				<data variable="tw_use_compression" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col3_x%" y="%nandcheck_row7%" />
+				<font resource="font" color="%text_color%" />
+				<text>Skip MD5 generation on backups</text>
+				<data variable="tw_skip_md5_generate" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_operation_state=0</action>
+					<action function="page">backup_run</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Back Up</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backupname1">
+			<object type="action">
+				<condition var1="tw_backup_name" op="=" var2="(Auto Generate)" />
+				<action function="generatebackupname"></function>
+			</object>
+
+			<object type="action">
+				<action function="page">backupname2</function>
+			</object>
+		</page>
+
+		<page name="backupname2">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a Backup Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_name%</text>
+				<data name="tw_backup_name" />
+				<restrict minlen="1" maxlen="64" allow=" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.{}[]" />
+				<actions>
+					<action function="set">tw_filecheck=%tw_backups_folder%/%tw_backup_name%</action>
+					<action function="set">tw_existpage=backupname2</action>
+					<action function="set">tw_notexistpage=backup</action>
+					<action function="page">filecheck</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<font resource="font" />
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Append Date</text>
+				<image resource="main_button" />
+				<action function="appenddatetobackupname"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel / Clear</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_backup_name=(Auto Generate)</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<actions>
+					<action function="set">tw_backup_name=(Auto Generate)</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_backup_name=(Auto Generate)</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backupencryption">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Encrypt your backup? Please enter a password:</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_encrypt_display%</text>
+				<data name="tw_backup_password" mask="*" maskvariable="tw_backup_encrypt_display" />
+				<restrict minlen="1" maxlen="32" allow="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" />
+				<action function="page">backupencryption2</action>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_password_not_match" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Passwords Do Not Match</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_encrypt_backup=0</action>
+					<action function="set">tw_backup_password=</action>
+					<action function="set">tw_backup_password2=</action>
+					<action function="set">tw_backup_encrypt_display=</action>
+					<action function="set">tw_backup_encrypt_display2=</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">backup</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backupencryption2">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Encrypt your backup? Please Enter Password Again:</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_encrypt_display2%</text>
+				<data name="tw_backup_password2" mask="*" maskvariable="tw_backup_encrypt_display2" />
+				<restrict minlen="1" maxlen="32" allow="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" />
+				<actions>
+					<action function="page">checkbackuppassword</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_encrypt_backup=0</action>
+					<action function="set">tw_backup_password=</action>
+					<action function="set">tw_backup_password2=</action>
+					<action function="set">tw_backup_encrypt_display=</action>
+					<action function="set">tw_backup_encrypt_display2=</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">backup</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="checkbackuppassword">
+			<object type="action">
+				<condition var1="tw_backup_password2" var2="tw_backup_password" />
+				<actions>
+					<action function="set">tw_encrypt_backup=1</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_backup_password2" op="!=" var2="tw_backup_password" />
+				<actions>
+					<action function="set">tw_encrypt_backup=0</action>
+					<action function="set">tw_password_not_match=1</action>
+					<action function="set">tw_backup_password=</action>
+					<action function="set">tw_backup_password2=</action>
+					<action function="set">tw_backup_encrypt_display=</action>
+					<action function="set">tw_backup_encrypt_display2=</action>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="backup_run">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<text>%tw_file_progress%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col_right_x%" y="%row2_text_y%" placement="1" />
+				<text>%tw_size_progress%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<action function="nandroid">backup</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="set">tw_back=backup</action>
+					<action function="set">tw_complete_text1=Backup Complete</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="restore">
+			<object type="template" name="header" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col2_x%" y="%zipstorage_text_y%" w="%fileselector_folderonly_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%col2_x%" y="%fileselector_install_y%" w="%fileselector_folderonly_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Package to Restore:</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<filter folders="1" files="0" nav="0" />
+				<path name="tw_backups_folder" />
+				<data name="tw_restore" default="" />
+				<selection name="tw_restore_name" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<condition var1="tw_restore" op="modified" />
+				<actions>
+					<action function="readBackup"></action>
+					<action function="page">restore_read</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="restore_read">
+			<object type="action">
+				<condition var1="tw_restore_encrypted" var2="1" />
+				<actions>
+					<action function="set">tw_password_fail=0</action>
+					<action function="page">restore_decrypt</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_restore_encrypted" var2="0" />
+				<actions>
+					<action function="page">restore_select</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="restore_decrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Backup encrypted. Please enter your password:</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_restore_display%</text>
+				<data name="tw_restore_password" mask="*" maskvariable="tw_restore_display" />
+				<restrict minlen="1" maxlen="32" allow="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" />
+				<actions>
+					<action function="page">try_restore_decrypt</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">restore</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Delete</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="set">tw_action=cmd</action>
+					<action function="set">tw_action_param=cd %tw_backups_folder% && rm -rf &quot;%tw_restore_name%&quot;</action>
+					<action function="set">tw_text1=Delete Backup? %tw_restore_name%</action>
+					<action function="set">tw_text2=This cannot be undone!</action>
+					<action function="set">tw_action_text1=Deleting Backup...</action>
+					<action function="set">tw_complete_text1=Backup Delete Complete</action>
+					<action function="set">tw_slider_text=Swipe to Delete</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">restore</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="try_restore_decrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Trying Decryption with Your Password</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<action function="decrypt_backup"></action>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="!=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_password_fail=1</action>
+					<action function="page">restore_decrypt</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">restore_select</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="restore_select">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Package to Restore: %tw_restore_name%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Package Date: %tw_restore_file_date%</text>
+			</object>
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%backup_list_x%" y="%restore_list_y%" w="%backup_list_width%" h="%restore_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Restore:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_restore_list" selectedlist="tw_restore_selected" />
+				<listtype name="restore" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col3_x%" y="%nandcheck_row6%" />
+				<font resource="font" color="%text_color%" />
+				<text>Enable MD5 checking of backup files</text>
+				<data variable="tw_skip_md5_check" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Rename Backup</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_backup_rename=</action>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">renamebackup</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Delete Backup</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="set">tw_action=cmd</action>
+					<action function="set">tw_action_param=cd %tw_backups_folder% && rm -rf &quot;%tw_restore_name%&quot;</action>
+					<action function="set">tw_text1=Delete Backup? %tw_restore_name%</action>
+					<action function="set">tw_text2=This cannot be undone!</action>
+					<action function="set">tw_action_text1=Deleting Backup...</action>
+					<action function="set">tw_complete_text1=Backup Delete Complete</action>
+					<action function="set">tw_slider_text=Swipe to Delete</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">restore_run</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Restore</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">restore</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="renamebackup">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a New Backup Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_rename%</text>
+				<data name="tw_backup_rename" />
+				<restrict minlen="1" maxlen="64" allow=" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.{}[]" />
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="set">tw_action=cmd</action>
+					<action function="set">tw_action_param=cd %tw_backups_folder% && mv &quot;%tw_restore_name%&quot; &quot;%tw_backup_rename%&quot;</action>
+					<action function="set">tw_text1=Rename Backup?</action>
+					<action function="set">tw_text2=This cannot be undone!</action>
+					<action function="set">tw_action_text1=Renaming Backup...</action>
+					<action function="set">tw_complete_text1=Backup Rename Complete</action>
+					<action function="set">tw_slider_text=Swipe to Rename</action>
+					<action function="set">tw_filecheck=%tw_backups_folder%/%tw_backup_rename%</action>
+					<action function="set">tw_existpage=renamebackup</action>
+					<action function="set">tw_notexistpage=confirm_action</action>
+					<action function="page">filecheck</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<font resource="font" />
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">restore_select</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">restore_select</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="restore_run">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_size_progress%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="set">tw_back=restore_select</action>
+					<action function="set">tw_complete_text1=Restore Complete</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<action function="nandroid">restore</action>
+			</object>
+		</page>
+
+		<page name="settings">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Settings</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Zip file signature verification?</text>
+				<data variable="tw_signed_zip_verify" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Use rm -rf instead of formatting?</text>
+				<data variable="tw_rm_rf" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Skip MD5 generation on backups</text>
+				<data variable="tw_skip_md5_generate" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Enable MD5 checking of backup files</text>
+				<data variable="tw_skip_md5_check" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row6_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Use 24-hour clock</text>
+				<data variable="tw_military_time" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row7_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Simulate most actions for theme testing</text>
+				<data variable="tw_simulate_actions" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<placement x="%col1_x%" y="%row8_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Simulate failure for actions</text>
+				<data variable="tw_simulate_fail" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Time Zone</text>
+				<image resource="main_button" />
+				<action function="page">timezone</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Restore Defaults</text>
+				<image resource="main_button" />
+				<action function="restoredefaultsettings"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Vibration Duration</text>
+				<image resource="main_button" />
+				<action function="page">Vibrate</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Screen</text>
+				<image resource="main_button" />
+				<action function="page">screen</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="timezone">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Select Time Zone</text>
+			</object>
+
+			<object type="listbox">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%listbox_y%" w="%listbox_width%" h="%listbox_tz_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Time Zone:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="font" spacing="%listbox_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_time_zone_guisel" />
+				<listitem name="(UTC -11) Samoa, Midway Island">BST11;BDT</listitem>
+				<listitem name="(UTC -10) Hawaii">HST10;HDT</listitem>
+				<listitem name="(UTC -9) Alaska">AST9;ADT</listitem>
+				<listitem name="(UTC -8) Pacific Time">PST8;PDT</listitem>
+				<listitem name="(UTC -7) Mountain Time">MST7;MDT</listitem>
+				<listitem name="(UTC -6) Central Time">CST6;CDT</listitem>
+				<listitem name="(UTC -5) Eastern Time">EST5;EDT</listitem>
+				<listitem name="(UTC -4) Atlantic Time">AST4;ADT</listitem>
+				<listitem name="(UTC -3) Brazil, Buenos Aires">GRNLNDST3;GRNLNDDT</listitem>
+				<listitem name="(UTC -2) Mid-Atlantic">FALKST2;FALKDT</listitem>
+				<listitem name="(UTC -1) Azores, Cape Verde">AZOREST1;AZOREDT</listitem>
+				<listitem name="(UTC  0) London, Dublin, Lisbon">GMT0;BST</listitem>
+				<listitem name="(UTC +1) Berlin, Brussels, Paris">NFT-1;DFT</listitem>
+				<listitem name="(UTC +2) Athens, Istanbul, South Africa">WET-2;WET</listitem>
+				<listitem name="(UTC +3) Moscow, Baghdad">SAUST-3;SAUDT</listitem>
+				<listitem name="(UTC +4) Abu Dhabi, Tbilisi, Muscat">WST-4;WDT</listitem>
+				<listitem name="(UTC +5) Yekaterinburg, Islamabad">PAKST-5;PAKDT</listitem>
+				<listitem name="(UTC +6) Almaty, Dhaka, Colombo">TASHST-6;TASHDT</listitem>
+				<listitem name="(UTC +7) Bangkok, Hanoi, Jakarta">THAIST-7;THAIDT</listitem>
+				<listitem name="(UTC +8) Beijing, Singapore, Hong Kong">TAIST-8;TAIDT</listitem>
+				<listitem name="(UTC +9) Tokyo, Seoul, Yakutsk">JST-9;JSTDT</listitem>
+				<listitem name="(UTC +10) Eastern Australia, Guam">EET-10;EETDT</listitem>
+				<listitem name="(UTC +11) Vladivostok, Solomon Islands">MET-11;METDT</listitem>
+				<listitem name="(UTC +12) Auckland, Wellington, Fiji">NZST-12;NZDT</listitem>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_medium_x%" y="%row11_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Do you use daylight savings time (DST)?</text>
+				<data variable="tw_time_zone_guidst" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row13_text_y%" placement="5" />
+				<text>Offset (usually 0): %tw_time_zone_guioffset%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_medium_x%" y="%row_offsetmedium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>0</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=0</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_medium_x%" y="%row_offsetmedium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>15</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=15</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_medium_x%" y="%row_offsetmedium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>30</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=30</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_medium_x%" y="%row_offsetmedium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>45</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=45</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Set Time Zone</text>
+				<image resource="main_button" />
+				<action function="setguitimezone"></action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row17_text_y%" placement="5" />
+				<text>Current Time Zone: %tw_time_zone%</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">settings</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="screen">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Screen Settings</text>
+			</object>
+
+			<object type="button">
+				<placement x="%slidervalue_x%" y="%row3_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_screen_timeout_secs" op="=" var2="0" />
+				<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				<text>Enable screen timeout.</text>
+				<image resource="checkbox_false" />
+				<action function="set">tw_screen_timeout_secs=60</action>
+			</object>
+
+			<object type="button">
+				<placement x="%slidervalue_x%" y="%row3_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_screen_timeout_secs" op="!=" var2="0" />
+				<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				<text>Enable screen timeout.</text>
+				<image resource="checkbox_true" />
+				<action function="set">tw_screen_timeout_secs=0</action>
+			</object>
+
+			<object type="slidervalue">
+				<condition var1="tw_screen_timeout_secs" op="!=" var2="0" />
+				<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				<placement x="slidervalue_x" y="%row5_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+				<dimensions lineh="%slidervalue_lineh%" linepadding="%slidervalue_padding%" sliderw="%slidervalue_sliderw%" sliderh="%slidervalue_sliderh%" />
+				<text>Screen timeout in seconds:</text>
+				<data variable="tw_screen_timeout_secs" min="15" max="300" />
+			</object>
+
+			<object type="slidervalue">
+				<condition var1="tw_has_brightnesss_file" var2="1" />
+				<placement x="slidervalue_x" y="%row12_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+				<dimensions lineh="%slidervalue_lineh%" linepadding="%slidervalue_padding%" sliderw="%slidervalue_sliderw%" sliderh="%slidervalue_sliderh%" />
+				<text>Brightness: %tw_brightness_pct%%</text>
+				<data variable="tw_brightness_pct" min="10" max="100" />
+				<actions>
+					<action function="set">tw_brightness=%tw_brightness_max%</action>
+					<action function="compute">tw_brightness*%tw_brightness_pct%</action>
+					<action function="compute">tw_brightness/100</action>
+					<action function="setbrightness">%tw_brightness%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">settings</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="Vibrate">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Vibration Settings :</text>
+			</object>
+
+			<object type="slidervalue">
+				<placement x="slidervalue_x" y="%row3_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<text>Button Vibration:</text>
+				<data variable="tw_button_vibrate" min="0" max="300" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="slidervalue_x" y="%row7_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<text>Keyboard Vibration:</text>
+				<data variable="tw_keyboard_vibrate" min="0" max="300" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="slidervalue_x" y="%row11_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<text>Action Vibration:</text>
+				<data variable="tw_action_vibrate" min="0" max="500" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">settings</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="advanced">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Advanced</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Copy Log to SD</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=copylog</action>
+					<action function="set">tw_text1=Copy Log to SD Card?</action>
+					<action function="set">tw_action_text1=Copying Log to SD Card...</action>
+					<action function="set">tw_complete_text1=Log Copy Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Fix Permissions</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=fixpermissions</action>
+					<action function="set">tw_text1=Fix Permissions?</action>
+					<action function="set">tw_action_text1=Fixing Permissions...</action>
+					<action function="set">tw_complete_text1=Fix Permissions Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Terminal Command</text>
+				<image resource="main_button" />
+				<action function="page">terminalfolder</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>ADB Sideload</text>
+				<image resource="main_button" />
+				<action function="page">sideload</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_allow_partition_sdcard" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Partition SD Card</text>
+				<image resource="main_button" />
+				<action function="page">partsdcard</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>File Manager</text>
+				<image resource="main_button" />
+				<action function="page">filemanagerlist</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reload Theme</text>
+				<image resource="main_button" />
+				<action function="reload"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col4_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>HTC Dumlock</text>
+				<image resource="main_button" />
+				<action function="page">htcdumlock</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="partsdcard">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Partition SD Card</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_sdext_x%" y="%row1_sdext_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_sdext_size-128</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_sdext_x%" y="%row1_sdext_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_sdext_size+128</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row_extsize_y%" placement="5" />
+				<text>EXT Size: %tw_sdext_size%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_sdext_x%" y="%row2_sdext_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_swap_size-32</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_sdext_x%" y="%row2_sdext_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_swap_size+32</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row_swapsize_y%" placement="5" />
+				<text>Swap Size: %tw_swap_size%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row8_text_y%" placement="5" />
+				<text>File system: %tw_sdpart_file_system%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_medium_x%" y="%row4_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT3</text>
+				<image resource="medium_button" />
+				<action function="set">tw_sdpart_file_system=ext3</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_sdext_disable_ext4" var2="0" />
+				<placement x="%col3_medium_x%" y="%row4_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT4</text>
+				<image resource="medium_button" />
+				<action function="set">tw_sdpart_file_system=ext4</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row12_text_y%" placement="5" />
+				<text>You will lose all files on your SD card!</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row13_text_y%" placement="5" />
+				<text>This action cannot be undone!</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=partsdcard</action>
+					<action function="set">tw_action=partitionsd</action>
+					<action function="set">tw_has_action2=1</action>
+					<action function="set">tw_action2=set</action>
+					<action function="set">tw_action2_param=tw_zip_location=/sdcard</action>
+					<action function="set">tw_action_text1=Partitioning SD Card...</action>
+					<action function="set">tw_action_text2=This will take a few minutes.</action>
+					<action function="set">tw_complete_text1=Partitioning Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Confirm Partition</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+
+		<page name="htcdumlock">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>HTC Dumlock</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Restore Original Boot</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=htcdumlock</action>
+					<action function="set">tw_action=htcdumlockrestoreboot</action>
+					<action function="set">tw_text1=Restore original boot image?</action>
+					<action function="set">tw_action_text1=Restoring Original Boot...</action>
+					<action function="set">tw_complete_text1=Restore Original Boot Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reflash Recovery->Boot</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=htcdumlock</action>
+					<action function="set">tw_action=htcdumlockreflashrecovery</action>
+					<action function="set">tw_text1=Reflash recovery to boot?</action>
+					<action function="set">tw_action_text1=Flashing recovery to boot...</action>
+					<action function="set">tw_complete_text1=Recovery Flash to Boot Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Install HTC Dumlock</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=htcdumlock</action>
+					<action function="set">tw_action=installhtcdumlock</action>
+					<action function="set">tw_text1=Install HTC dumlock files to ROM?</action>
+					<action function="set">tw_action_text1=Installing HTC Dumlock...</action>
+					<action function="set">tw_complete_text1=HTC Dumlock Install Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="lock">
+			<background color="#000000A0" />
+
+			<object type="image">
+				<image resource="unlock-icon" />
+				<placement x="%lock_x%" y="%lock_y%" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="overlay"></action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Unlock</text>
+			</object>
+		</page>
+
+
+		<page name="filemanagerlist">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>File Manager: Select a File or Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_folder_x%" y="%fileselector_install_y%" w="%fileselector_folder_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Folders:</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="1" files="0" />
+				<path name="tw_file_location1" default="/sdcard" />
+				<data name="select" />
+				<selection name="tw_selection1" />
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_file_x%" y="%fileselector_install_y%" w="%fileselector_file_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_file_location1%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="0" files="1" />
+				<path name="tw_file_location1" default="/" />
+				<data name="tw_filename1" />
+				<selection name="tw_selection1" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<actions>
+					<action function="set">tw_fm_type=File</action>
+					<action function="set">tw_fm_isfolder=0</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_filename1" op="modified" />
+				<actions>
+					<action function="page">filemanageroptions</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Select Folder</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filename1=tw_file_location1</action>
+					<action function="set">tw_fm_isfolder=1</action>
+					<action function="set">tw_fm_type=Folder</action>
+					<action function="page">filemanageroptions</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanageroptions">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_type% Selected:</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Copy File</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=cp</action>
+					<action function="set">tw_fm_text1=Copying</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Copy Folder</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=cd &quot;%tw_file_location1%&quot; && cd .. && cp -R</action>
+					<action function="set">tw_fm_text1=Copying</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Move</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=mv</action>
+					<action function="set">tw_fm_text1=Moving</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>chmod 755</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=chmod 755</action>
+					<action function="set">tw_fm_text1=chmod 755</action>
+					<action function="set">tw_fm_text2=</action>
+					<action function="set">tw_fm_text3=</action>
+					<action function="set">tw_include_text3=0</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>chmod</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_rename=0000</action>
+					<action function="set">tw_fm_text2=</action>
+					<action function="set">tw_fm_text3=</action>
+					<action function="set">tw_include_text3=0</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerchmod</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Delete</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=rm -rf</action>
+					<action function="set">tw_fm_text1=Deleting</action>
+					<action function="set">tw_fm_text2=</action>
+					<action function="set">tw_fm_text3=</action>
+					<action function="set">tw_include_text3=0</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Rename File</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_rename=tw_selection1</action>
+					<action function="set">tw_fm_text1=Renaming</action>
+					<action function="set">tw_filemanager_command=mv</action>
+					<action function="page">filemanagerrenamefile</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Rename Folder</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_rename=tw_selection1</action>
+					<action function="set">tw_fm_text1=Renaming</action>
+					<action function="set">tw_filemanager_command=cd &quot;%tw_file_location1%&quot; && cd .. && mv</action>
+					<action function="page">filemanagerrenamefolder</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanagerlist</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="choosedestinationfolder">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>Browse to Destination Folder & Press Select</text>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%col2_x%" y="%fileselector_install_y%" w="%fileselector_folderonly_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_file_location2%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<filter folders="1" files="0" />
+				<path name="tw_file_location2" default="/sdcard" />
+				<data name="tw_filename2" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Select Folder</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_fm_text2=to</action>
+					<action function="set">tw_fm_text3=%tw_file_location2%</action>
+					<action function="set">tw_include_text3=1</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerrenamefile">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a New %tw_fm_type% Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_filemanager_rename%</text>
+				<data name="tw_filemanager_rename" />
+				<restrict minlen="1" maxlen="128" />
+				<actions>
+					<action function="set">tw_fm_text2=to</action>
+					<action function="set">tw_fm_text3=&quot;%tw_file_location1%/%tw_filemanager_rename%&quot;</action>
+					<action function="set">tw_include_text3=1</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerrenamefolder">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a New %tw_fm_type% Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_filemanager_rename%</text>
+				<data name="tw_filemanager_rename" />
+				<restrict minlen="1" maxlen="128" />
+				<actions>
+					<action function="set">tw_fm_text2=to</action>
+					<action function="set">tw_fm_text3=%tw_filemanager_rename%</action>
+					<action function="set">tw_include_text3=1</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerchmod">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter New Permissions</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_filemanager_rename%</text>
+				<data name="tw_filemanager_rename" />
+				<restrict minlen="3" maxlen="4" allow="0123456789" />
+				<actions>
+					<action function="set">tw_filemanager_command=chmod %tw_filemanager_rename%</action>
+					<action function="set">tw_fm_text1=chmod %tw_filemanager_rename%</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerconfirm">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>%tw_fm_text2%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>%tw_fm_text3%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row10_text_y%" placement="5"/>
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">filemanageracction</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Confirm</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">%tw_back%</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanageracction">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_fm_text1%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_back=filemanagerlist</action>
+					<action function="set">tw_complete_text1=File Operation Complete</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="!=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_complete_text1=File Operation Complete</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_include_text3" var2="0" />
+				<actions>
+					<action function="cmd">%tw_filemanager_command% &quot;%tw_filename1%&quot;</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_include_text3" var2="1" />
+				<actions>
+					<action function="cmd">%tw_filemanager_command% &quot;%tw_filename1%&quot; &quot;%tw_fm_text3%&quot;</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="decrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Please Enter Your Password</text>
+			</object>
+
+			<object type="input">
+				<placement x="%input_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_crypto_display%</text>
+				<data name="tw_crypto_password" mask="*" maskvariable="tw_crypto_display" />
+				<restrict minlen="1" maxlen="254" />
+				<actions>
+					<action function="page">trydecrypt</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="trydecrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Trying Decryption with Your Password</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<action function="decrypt"></action>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="!=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_password_fail=1</action>
+					<action function="page">decrypt</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="terminalfolder">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>Browse to Starting Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%col2_x%" y="%fileselector_install_y%" w="%fileselector_folderonly_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_terminal_location%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="1" files="0" />
+				<path name="tw_terminal_location" default="/" />
+				<data name="tw_terminal" />
+				<selection name="tw_terminal_selection" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Select Folder</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="page">terminalcommand</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="terminalcommand">
+			<background color="#000000FF" />
+
+			<object type="console">
+				<placement x="%console_x%" y="%terminal_console_y%" w="%console_width%" h="%terminal_console_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<placement x="%input_x%" y="%terminal_text_y%" placement="0" />
+				<font resource="font" />
+				<text>Starting Path: %tw_terminal_location%</text>
+			</object>
+
+			<object type="input">
+				<condition var1="tw_terminal_state" var2="0" />
+				<placement x="%input_x%" y="%terminal_text_y%" w="%terminal_input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_terminal_command%</text>
+				<data name="tw_terminal_command" />
+				<restrict minlen="1" />
+				<action function="terminalcommand">%tw_terminal_command%</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_terminal_state" var2="1" />
+				<placement x="%filemanager_select_x%" y="%terminal_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>KILL</text>
+				<image resource="medium_button" />
+				<action function="killterminal"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%home_button_x%" y="%terminal_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="home_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">home</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%back_button_x%" y="%terminal_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="back_icon" />
+				<condition var1="tw_busy" var2="0" />
+				<action function="key">back</action>
+			</object>
+
+			<object type="action">
+				<touch key="power" />
+				<action function="overlay">lock</action>
+			</object>
+
+			<object type="action">
+				<touch key="power+voldown" />
+				<action function="screenshot" />
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">terminalfolder</action>
+			</object>
+		</page>
+
+		<page name="sideload">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>ADB Sideload</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Wipe Dalvik Cache.</text>
+				<data variable="tw_wipe_dalvik" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Wipe Cache.</text>
+				<data variable="tw_wipe_cache" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=adbsideload</action>
+					<action function="set">tw_action_text1=ADB Sideload</action>
+					<action function="set">tw_action_text2=Usage: adb sideload filename.zip</action>
+					<action function="set">tw_complete_text1=ADB Sideload Complete</action>
+					<action function="set">tw_has_cancel=1</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="set">tw_cancel_action=adbsideloadcancel</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Start Sideload</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="installsu">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>Install SuperSU?</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>Your device does not appear to be rooted.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>Install SuperSU now? This will root your device.</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Do Not Install</text>
+				<image resource="main_button" />
+				<action function="set">tw_page_done=1</action>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_action=installsu</action>
+					<action function="set">tw_action_text1=Installing SuperSU</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="page">singleaction_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Install</text>
+			</object>
+		</page>
+	</pages>
+</recovery>
diff --git a/gui/devices/portrait/res/portrait.xml b/gui/devices/portrait/res/portrait.xml
new file mode 100644
index 0000000..9a95710
--- /dev/null
+++ b/gui/devices/portrait/res/portrait.xml
@@ -0,0 +1,3935 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<pages>
+		<page name="main">
+			<object type="action">
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="main2">
+			<object type="template" name="header" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Install</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Wipe</text>
+				<image resource="main_button" />
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Backup</text>
+				<image resource="main_button" />
+				<action function="page">backup</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Restore</text>
+				<image resource="main_button" />
+				<action function="page">restore</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Mount</text>
+				<image resource="main_button" />
+				<action function="page">mount</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Settings</text>
+				<image resource="main_button" />
+				<action function="page">settings</action>
+			</object>
+
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Advanced</text>
+				<image resource="main_button" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reboot</text>
+				<image resource="main_button" />
+				<action function="page">reboot</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="install">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Select Zip to Install</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%row1_text_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=install</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%row3_text_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_zip_location%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter extn=".zip" folders="1" files="1" />
+				<path name="tw_zip_location" default="/sdcard" />
+				<data name="tw_filename" />
+				<selection name="tw_file" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<condition var1="tw_filename" op="modified" />
+				<actions>
+					<action function="queuezip"></action>
+					<action function="page">flash_confirm</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="flash_confirm">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>This operation may install incompatible</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>software and render your device unusable.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>Folder:</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_zip_location%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>File to flash:</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%center_x%" y="%row6_text_y%" placement="5" />
+				<text>%tw_file%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row7_text_y%" placement="5" />
+				<text>Press back to cancel adding this zip.</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row8_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Zip file signature verification.</text>
+				<data variable="tw_signed_zip_verify" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_has_injecttwrp" var2="1" />
+				<placement x="%col1_x%" y="%row10_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Inject TWRP after install.</text>
+				<data variable="tw_inject_after_zip" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row11_text_y%" placement="5" />
+				<text>File %tw_zip_queue_count% of max of 10</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" placement="5" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="flash">flash_zip</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Confirm Flash</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_zip_queue_count" op="!=" var2="10"></condition>
+				<placement x="%col1_x%" y="%row_queue_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Add More Zips</text>
+				<image resource="main_button" />
+				<action function="page">install</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row_queue_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Clear Zip Queue</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="cancelzip"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="flash_zip">
+			<object type="template" name="header" />
+
+			<object type="console">
+				<placement x="%console_x%" y="%row1_y%" w="%console_width%" h="%console_install_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row17_text_y%" placement="5" />
+				<text>Flashing file %tw_zip_index% of %tw_zip_queue_count%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%center_x%" y="%row18_text_y%" placement="5" />
+				<text>%tw_filename%</text>
+			</object>
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<action function="page">flash_done</action>
+			</object>
+		</page>
+
+		<page name="flash_done">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Zip Install Complete</text>
+			</object>
+
+			<object type="console">
+				<placement x="%console_x%" y="%row1_y%" w="%console_width%" h="%console_installdone_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Wipe cache/dalvik</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=flash_done</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=/cache</action>
+					<action function="set">tw_has_action2=1</action>
+					<action function="set">tw_action2=wipe</action>
+					<action function="set">tw_action2_param=dalvik</action>
+					<action function="set">tw_text1=Wipe Cache & Dalvik?</action>
+					<action function="set">tw_action_text1=Wiping Cache & Dalvik...</action>
+					<action function="set">tw_complete_text1=Cache & Dalvik Wipe Complete</action>
+					<action function="set">tw_slider_text=Swipe to Wipe</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reboot System</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=main2</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=system</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_text3=</action>
+					<action function="set">tw_text4=</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Home</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%zip_status_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text" color="%text_success_color%">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%zip_status_y%" placement="5" />
+				<text>Successful</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=install</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="clear_vars">
+			<object type="action">
+				<action function="set">tw_operation_state=0</action>
+				<action function="set">tw_text1=</action>
+				<action function="set">tw_text2=</action>
+				<action function="set">tw_text3=</action>
+				<action function="set">tw_text4=</action>
+				<action function="set">tw_action_text1=</action>
+				<action function="set">tw_action_text2=</action>
+				<action function="set">tw_action_param=</action>
+				<action function="set">tw_has_action2=0</action>
+				<action function="set">tw_action2=</action>
+				<action function="set">tw_action2_param=</action>
+				<action function="set">tw_has_cancel=0</action>
+				<action function="set">tw_cancel_action=</action>
+				<action function="set">tw_cancel_param=</action>
+				<action function="set">tw_show_exclamation=0</action>
+				<action function="set">tw_show_reboot=0</action>
+				<action function="page">%tw_clear_destination%</action>
+			</object>
+		</page>
+
+		<page name="confirm_action">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_text2%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_text3%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_text4%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row15_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">action_page</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>%tw_slider_text%</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="action_page">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_action_text2%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_has_cancel" var2="1" />
+				<placement x="%col_center_medium_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="medium_button" />
+				<action function="%tw_cancel_action%">%tw_cancel_param%</action>
+			</object>
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="0" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="1" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+					<action function="%tw_action2%">%tw_action2_param%</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="singleaction_page">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_action_text2%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="set">tw_page_done=1</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="0" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="1" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+					<action function="%tw_action2%">%tw_action2_param%</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="action_complete">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_complete_text1%</text>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text" color="%text_success_color%">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Successful</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_reboot" var2="0" />
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Back</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_reboot" var2="1" />
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reboot System</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=main2</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=system</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_text3=</action>
+					<action function="set">tw_text4=</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="filecheck">
+			<object type="action">
+				<action function="fileexists">%tw_filecheck%</action>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_fileexists=1</action>
+					<action function="page">%tw_existpage%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" var2="1" />
+				</conditions>
+				<actions>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">%tw_notexistpage%</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="rebootcheck">
+			<object type="action">
+				<condition var1="tw_backup_system_size" op=">=" var2="%tw_min_system%" />
+				<action function="reboot">%tw_action_param%</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_backup_system_size" op="<" var2="%tw_min_system%" />
+				<action function="page">confirm_action</action>
+			</object>
+		</page>
+
+		<page name="wipe">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Factory Reset</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Wipes Data, Cache, and Dalvik</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_has_data_media" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>(not including internal storage)</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<conditions>
+					<condition var1="tw_has_android_secure" var2="1" />
+					<condition var1="fileexists" var2="/and-sec" />
+				</conditions>
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Android Secure</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_has_sdext_partition" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>SD-EXT</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row6_text_y%" placement="5" />
+				<text>Most of the time this is</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row7_text_y%" placement="5" />
+				<text>the only wipe that you need.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row16_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%wipe_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Advanced Wipe</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_has_internal" var2="1" />
+					<condition var1="tw_has_data_media" var2="1" />
+				</conditions>
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%wipe_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Format Data</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="page">formatdata</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=wipe</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=data</action>
+					<action function="set">tw_action_text1=Factory Reset...</action>
+					<action function="set">tw_complete_text1=Factory Reset Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Factory Reset</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="advancedwipe">
+			<object type="template" name="header" />
+
+			<object type="action">
+				<action function="set">tw_wipe_list=</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Wipe Menu</text>
+			</object>
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%wipe_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Wipe:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_wipe_list" />
+				<listtype name="wipe" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=advancedwipe</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=LIST</action>
+					<action function="set">tw_text1=Wipe Selected Partition(s)?</action>
+					<action function="set">tw_action_text1=Wiping Partition(s)...</action>
+					<action function="set">tw_complete_text1=Wipe Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%wipe_button_row1%" w="%button_fill_full_width%" h="%button_fill_half_height%" placement="5" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Repair or Change File System</text>
+				<actions>
+					<action function="checkpartitionlist"></action>
+					<action function="page">checkpartitionlist</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="partitionlisterror" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%wipe_button_row1%" placement="5" />
+				<text>Invalid partition selection</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Wipe</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="formatdata">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Format Data will wipe all of your apps,</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>backups, pictures, videos, media, and</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>removes encryption on internal storage.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>This cannot be undone. Press back to cancel.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Type yes to continue.</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row6_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_confirm_formatdata%</text>
+				<data name="tw_confirm_formatdata" />
+				<restrict minlen="3" maxlen="3" allow="yes" />
+				<action function="page">formatdata_confirm</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="formatdata_confirm">
+			<object type="action">
+				<condition var1="tw_confirm_formatdata" op="=" var2="yes" />
+				<actions>
+					<action function="set">tw_back=formatdata</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=DATAMEDIA</action>
+					<action function="set">tw_action_text1=Formatting Data...</action>
+					<action function="set">tw_complete_text1=Data Format Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_confirm_formatdata" op="!=" var2="yes" />
+				<action function="page">formatdata</function>
+			</object>
+		</page>
+
+		<page name="checkpartitionlist">
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="getpartitiondetails"></action>
+					<action function="page">partitionoptions</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="!=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=1</action>
+					<action function="set">tw_wipe_list=</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="partitionoptions">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Partition Options for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_is_present" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Present: Yes</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_is_present" op="=" var2="0" />
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Present: No</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_removable" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Removable: Yes</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_removable" op="=" var2="0" />
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Removable: No</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Size: %tw_partition_size%MB</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row4_text_y%" />
+				<text>Used: %tw_partition_used%MB</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>Free: %tw_partition_free%MB</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row5_text_y%" />
+				<text>Backup Size: %tw_partition_backup_size%MB</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_can_repair" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Repair</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=partitionoptions</action>
+					<action function="set">tw_action=repair</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Repair %tw_partition_name%?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Repairing...</action>
+					<action function="set">tw_complete_text1=Repair Complete</action>
+					<action function="set">tw_slider_text=Swipe to Repair</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Change File System</text>
+				<image resource="main_button" />
+				<action function="page">selectfilesystem</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advancedwipe</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="refreshfilesystem">
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="getpartitiondetails"></action>
+					<action function="page">selectfilesystem</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="!=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=1</action>
+					<action function="set">tw_wipe_list=</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="selectfilesystem">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Change file system for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>Some ROMs or kernels may not support some</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>file systems. Proceed with caution!</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_ext" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT2</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=ext2</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to EXT2?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_ext" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT3</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=ext3</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to EXT3?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_ext" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT4</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=ext4</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to EXT4?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_f2fs" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>F2FS</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=f2fs</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to F2FS?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_vfat" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>FAT</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=vfat</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to FAT?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_exfat" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>exFAT</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=exfat</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to exFAT?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">partitionoptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backup">
+			<object type="template" name="header" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%row1_header_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" placement="5" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Backup Name: %tw_backup_name%</text>
+				<actions>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">backupname1</action>
+				</actions>
+			</object>
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row2_text_y%" w="%listbox_width%" h="%backup_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Back Up:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_backup_list" />
+				<listtype name="backup" />
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="0" />
+				</conditions>
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>No Encryption</text>
+				<actions>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="1" />
+				</conditions>
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Using Encryption</text>
+				<actions>
+					<action function="set">tw_password_not_match=0</action>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col2_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Refresh Sizes</text>
+				<actions>
+					<action function="refreshsizes"></action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%backup_button_row2%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=backup</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row15_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Enable compression.</text>
+				<data variable="tw_use_compression" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row16_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Skip MD5 generation during backup.</text>
+				<data variable="tw_skip_md5_generate" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">backup_run</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Back Up</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backupname1">
+			<object type="action">
+				<condition var1="tw_backup_name" op="=" var2="(Auto Generate)" />
+				<action function="generatebackupname"></function>
+			</object>
+
+			<object type="action">
+				<action function="page">backupname2</function>
+			</object>
+		</page>
+
+		<page name="backupname2">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a Backup Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_name%</text>
+				<data name="tw_backup_name" />
+				<restrict minlen="1" maxlen="64" allow=" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.{}[]" />
+				<actions>
+					<action function="set">tw_filecheck=%tw_backups_folder%/%tw_backup_name%</action>
+					<action function="set">tw_existpage=backupname2</action>
+					<action function="set">tw_notexistpage=backup</action>
+					<action function="page">filecheck</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<font resource="font" />
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Append Date</text>
+				<image resource="main_button" />
+				<action function="appenddatetobackupname"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_backup_name=(Auto Generate)</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<actions>
+					<action function="set">tw_backup_name=(Auto Generate)</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_backup_name=(Auto Generate)</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backupencryption">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Encrypt your backup?</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Please Enter A Password:</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_encrypt_display%</text>
+				<data name="tw_backup_password" mask="*" maskvariable="tw_backup_encrypt_display" />
+				<restrict minlen="1" maxlen="32" allow="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" />
+				<action function="page">backupencryption2</action>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_password_not_match" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Passwords Do Not Match</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_encrypt_backup=0</action>
+					<action function="set">tw_backup_password=</action>
+					<action function="set">tw_backup_password2=</action>
+					<action function="set">tw_backup_encrypt_display=</action>
+					<action function="set">tw_backup_encrypt_display2=</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">backup</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backupencryption2">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Encrypt your backup?</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Please Enter Password Again:</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_encrypt_display2%</text>
+				<data name="tw_backup_password2" mask="*" maskvariable="tw_backup_encrypt_display2" />
+				<restrict minlen="1" maxlen="32" allow="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" />
+				<actions>
+					<action function="page">checkbackuppassword</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_encrypt_backup=0</action>
+					<action function="set">tw_backup_password=</action>
+					<action function="set">tw_backup_password2=</action>
+					<action function="set">tw_backup_encrypt_display=</action>
+					<action function="set">tw_backup_encrypt_display2=</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">backup</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="checkbackuppassword">
+			<object type="action">
+				<condition var1="tw_backup_password2" var2="tw_backup_password" />
+				<actions>
+					<action function="set">tw_encrypt_backup=1</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_backup_password2" op="!=" var2="tw_backup_password" />
+				<actions>
+					<action function="set">tw_encrypt_backup=0</action>
+					<action function="set">tw_password_not_match=1</action>
+					<action function="set">tw_backup_password=</action>
+					<action function="set">tw_backup_password2=</action>
+					<action function="set">tw_backup_encrypt_display=</action>
+					<action function="set">tw_backup_encrypt_display2=</action>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="backup_run">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_file_progress%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_size_progress%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<action function="nandroid">backup</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="set">tw_back=backup</action>
+					<action function="set">tw_complete_text1=Backup Complete</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="restore">
+			<object type="template" name="header" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%row1_header_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%row2_text_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Package to Restore:</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="1" files="0" nav="0" />
+				<path name="tw_backups_folder" />
+				<data name="tw_restore" default="" />
+				<selection name="tw_restore_name" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<condition var1="tw_restore" op="modified" />
+				<actions>
+					<action function="readBackup"></action>
+					<action function="page">restore_read</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="restore_read">
+			<object type="action">
+				<condition var1="tw_restore_encrypted" var2="1" />
+				<actions>
+					<action function="set">tw_password_fail=0</action>
+					<action function="page">restore_decrypt</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_restore_encrypted" var2="0" />
+				<actions>
+					<action function="page">restore_select</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="restore_decrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Backup Encrypted</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Please Enter Your Password:</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_restore_display%</text>
+				<data name="tw_restore_password" mask="*" maskvariable="tw_restore_display" />
+				<restrict minlen="1" maxlen="32" allow="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" />
+				<actions>
+					<action function="page">try_restore_decrypt</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">restore</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Delete</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="set">tw_action=cmd</action>
+					<action function="set">tw_action_param=cd %tw_backups_folder% && rm -rf &quot;%tw_restore_name%&quot;</action>
+					<action function="set">tw_text1=Delete Backup? %tw_restore_name%</action>
+					<action function="set">tw_text2=This cannot be undone!</action>
+					<action function="set">tw_action_text1=Deleting Backup...</action>
+					<action function="set">tw_complete_text1=Backup Delete Complete</action>
+					<action function="set">tw_slider_text=Swipe to Delete</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">restore</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="try_restore_decrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Trying Decryption with Your Password</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<action function="decrypt_backup"></action>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="!=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_password_fail=1</action>
+					<action function="page">restore_decrypt</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">restore_select</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="restore_select">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%backup_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Restoring: %tw_restore_name%</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_restore_list" selectedlist="tw_restore_selected" />
+				<listtype name="restore" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Rename Backup</text>
+				<actions>
+					<action function="set">tw_backup_rename=</action>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">renamebackup</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col2_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Delete Backup</text>
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="set">tw_action=cmd</action>
+					<action function="set">tw_action_param=cd %tw_backups_folder% && rm -rf &quot;%tw_restore_name%&quot;</action>
+					<action function="set">tw_text1=Delete Backup? %tw_restore_name%</action>
+					<action function="set">tw_text2=This cannot be undone!</action>
+					<action function="set">tw_action_text1=Deleting Backup...</action>
+					<action function="set">tw_complete_text1=Backup Delete Complete</action>
+					<action function="set">tw_slider_text=Swipe to Delete</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row15_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Enable MD5 verification of backup files.</text>
+				<data variable="tw_skip_md5_check" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row16_text_y%" placement="5" />
+				<text>Package Date: %tw_restore_file_date%</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">restore_run</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Restore</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">restore</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="renamebackup">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a New Backup Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_rename%</text>
+				<data name="tw_backup_rename" />
+				<restrict minlen="1" maxlen="64" allow=" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.{}[]" />
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="set">tw_action=cmd</action>
+					<action function="set">tw_action_param=cd %tw_backups_folder% && mv &quot;%tw_restore_name%&quot; &quot;%tw_backup_rename%&quot;</action>
+					<action function="set">tw_text1=Rename Backup?</action>
+					<action function="set">tw_text2=This cannot be undone!</action>
+					<action function="set">tw_action_text1=Renaming Backup...</action>
+					<action function="set">tw_complete_text1=Backup Rename Complete</action>
+					<action function="set">tw_slider_text=Swipe to Rename</action>
+					<action function="set">tw_filecheck=%tw_backups_folder%/%tw_backup_rename%</action>
+					<action function="set">tw_existpage=renamebackup</action>
+					<action function="set">tw_notexistpage=confirm_action</action>
+					<action function="page">filecheck</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<font resource="font" />
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">restore_select</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">restore_select</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="restore_run">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_size_progress%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="set">tw_back=restore_select</action>
+					<action function="set">tw_complete_text1=Restore Complete</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<action function="nandroid">restore</action>
+			</object>
+		</page>
+
+		<page name="selectstorage">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%storage_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Storage:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_storage_path" />
+				<listtype name="storage" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>OK</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="page">%tw_back%</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="mount">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%mount_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Mount:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<listtype name="mount" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%mount_storage_row%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=mount</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_has_usb_storage" var2="1" />
+				<placement x="%col1_x%" y="row4_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Mount USB Storage</text>
+				<image resource="main_button" />
+				<action function="page">usb_mount</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Enable MTP</text>
+				<image resource="main_button" />
+				<action function="startmtp"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="1" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Disable MTP</text>
+				<image resource="main_button" />
+				<action function="stopmtp"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_is_decrypted" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Decrypt Data</text>
+				<image resource="main_button" />
+				<action function="page">decrypt</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="usb_mount">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>USB Storage Mounted</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<font resource="font" />
+				<text>Be sure to safely remove your device</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" />
+				<text>from your computer before unmounting!</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Unmount</text>
+				<image resource="main_button" />
+				<action function="page">usb_umount</action>
+			</object>
+
+			<object type="action">
+				<action function="mount">usb</action>
+				<action function="set">tw_busy=1</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="usb_umount">
+			<object type="action">
+				<action function="unmount">usb</action>
+			</object>
+
+			<object type="action">
+				<action function="page">mount</action>
+				<action function="set">tw_busy=0</action>
+			</object>
+		</page>
+
+		<page name="reboot">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Reboot Menu</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_system" var2="1" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>System</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=system</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_poweroff" var2="1" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Power Off</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=poweroff</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to power off?</action>
+					<action function="set">tw_action_text1=Turning Off...</action>
+					<action function="set">tw_complete_text1=Turning Off...</action>
+					<action function="set">tw_slider_text=Swipe to Power Off</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_recovery" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Recovery</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=recovery</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_bootloader" var2="1" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Bootloader</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=bootloader</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_download_mode" var2="1" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Download</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=download</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="settings">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Settings</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Zip file signature verification.</text>
+				<data variable="tw_signed_zip_verify" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Use rm -rf instead of formatting.</text>
+				<data variable="tw_rm_rf" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Skip MD5 generation during backup.</text>
+				<data variable="tw_skip_md5_generate" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Enable MD5 verification of backup files.</text>
+				<data variable="tw_skip_md5_check" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Use 24-hour clock.</text>
+				<data variable="tw_military_time" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row6_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Simulate actions for theme testing.</text>
+				<data variable="tw_simulate_actions" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<placement x="%col1_x%" y="%row7_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Simulate failure for actions.</text>
+				<data variable="tw_simulate_fail" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Time Zone</text>
+				<image resource="main_button" />
+				<action function="page">timezone</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Screen</text>
+				<image resource="main_button" />
+				<action function="page">screen</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Restore Defaults</text>
+				<image resource="main_button" />
+				<action function="restoredefaultsettings"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Vibration Duration</text>
+				<image resource="main_button" />
+				<action function="page">Vibrate</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="timezone">
+			<object type="template" name="header" />
+
+			<object type="listbox">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%listbox_tz_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Time Zone:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="font" spacing="%listbox_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_time_zone_guisel" />
+				<listitem name="(UTC -11) Samoa, Midway Island">BST11;BDT</listitem>
+				<listitem name="(UTC -10) Hawaii">HST10;HDT</listitem>
+				<listitem name="(UTC -9) Alaska">AST9;ADT</listitem>
+				<listitem name="(UTC -8) Pacific Time">PST8;PDT</listitem>
+				<listitem name="(UTC -7) Mountain Time">MST7;MDT</listitem>
+				<listitem name="(UTC -6) Central Time">CST6;CDT</listitem>
+				<listitem name="(UTC -5) Eastern Time">EST5;EDT</listitem>
+				<listitem name="(UTC -4) Atlantic Time">AST4;ADT</listitem>
+				<listitem name="(UTC -3) Brazil, Buenos Aires">GRNLNDST3;GRNLNDDT</listitem>
+				<listitem name="(UTC -2) Mid-Atlantic">FALKST2;FALKDT</listitem>
+				<listitem name="(UTC -1) Azores, Cape Verde">AZOREST1;AZOREDT</listitem>
+				<listitem name="(UTC  0) London, Dublin, Lisbon">GMT0;BST</listitem>
+				<listitem name="(UTC +1) Berlin, Brussels, Paris">NFT-1;DFT</listitem>
+				<listitem name="(UTC +2) Athens, Istanbul, South Africa">WET-2;WET</listitem>
+				<listitem name="(UTC +3) Moscow, Baghdad">SAUST-3;SAUDT</listitem>
+				<listitem name="(UTC +4) Abu Dhabi, Tbilisi, Muscat">WST-4;WDT</listitem>
+				<listitem name="(UTC +5) Yekaterinburg, Islamabad">PAKST-5;PAKDT</listitem>
+				<listitem name="(UTC +6) Almaty, Dhaka, Colombo">TASHST-6;TASHDT</listitem>
+				<listitem name="(UTC +7) Bangkok, Hanoi, Jakarta">THAIST-7;THAIDT</listitem>
+				<listitem name="(UTC +8) Beijing, Singapore, Hong Kong">TAIST-8;TAIDT</listitem>
+				<listitem name="(UTC +9) Tokyo, Seoul, Yakutsk">JST-9;JSTDT</listitem>
+				<listitem name="(UTC +10) Eastern Australia, Guam">EET-10;EETDT</listitem>
+				<listitem name="(UTC +11) Vladivostok, Solomon Islands">MET-11;METDT</listitem>
+				<listitem name="(UTC +12) Auckland, Wellington, Fiji">NZST-12;NZDT</listitem>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row_dst_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Do you use daylight savings time (DST)?</text>
+				<data variable="tw_time_zone_guidst" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row_offset_text_y%" placement="5" />
+				<text>Offset (usually 0): %tw_time_zone_guioffset%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_medium_x%" y="%row_offset_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>None</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=0</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_medium_x%" y="%row_offset_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>15</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=15</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_medium_x%" y="%row_offset_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>30</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=30</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_medium_x%" y="%row_offset_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>45</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=45</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%tz_set_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Set Time Zone</text>
+				<image resource="main_button" />
+				<action function="setguitimezone"></action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%tz_current_y%" placement="5" />
+				<text>Current Time Zone: %tw_time_zone%</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">settings</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="screen">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Screen Settings</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_screen_timeout_secs" op="=" var2="0" />
+				<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				<text>Enable screen timeout.</text>
+				<image resource="checkbox_false" />
+				<action function="set">tw_screen_timeout_secs=60</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_screen_timeout_secs" op="!=" var2="0" />
+				<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				<text>Enable screen timeout.</text>
+				<image resource="checkbox_true" />
+				<action function="set">tw_screen_timeout_secs=0</action>
+			</object>
+
+			<object type="slidervalue">
+				<condition var1="tw_screen_timeout_secs" op="!=" var2="0" />
+				<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				<placement x="col1_x" y="%row4_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+				<dimensions lineh="%slidervalue_lineh%" linepadding="%slidervalue_padding%" sliderw="%slidervalue_sliderw%" sliderh="%slidervalue_sliderh%" />
+				<text>Screen timeout in seconds:</text>
+				<data variable="tw_screen_timeout_secs" min="15" max="300" />
+			</object>
+
+			<object type="slidervalue">
+				<condition var1="tw_has_brightnesss_file" var2="1" />
+				<placement x="col1_x" y="%row12_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+				<dimensions lineh="%slidervalue_lineh%" linepadding="%slidervalue_padding%" sliderw="%slidervalue_sliderw%" sliderh="%slidervalue_sliderh%" />
+				<text>Brightness: %tw_brightness_pct%%</text>
+				<data variable="tw_brightness_pct" min="10" max="100" />
+				<actions>
+					<action function="set">tw_brightness=%tw_brightness_max%</action>
+					<action function="compute">tw_brightness*%tw_brightness_pct%</action>
+					<action function="compute">tw_brightness/100</action>
+					<action function="setbrightness">%tw_brightness%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">settings</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="Vibrate">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Vibration Settings :</text>
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row4_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<text>Button Vibration:</text>
+				<data variable="tw_button_vibrate" min="0" max="300" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row8_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<text>Keyboard Vibration:</text>
+				<data variable="tw_keyboard_vibrate" min="0" max="300" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row12_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<text>Action Vibration:</text>
+				<data variable="tw_action_vibrate" min="0" max="500" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">settings</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="advanced">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Advanced</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Copy Log to SD</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=copylog</action>
+					<action function="set">tw_text1=Copy Log to SD Card?</action>
+					<action function="set">tw_action_text1=Copying Log to SD Card...</action>
+					<action function="set">tw_complete_text1=Log Copy Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Fix Permissions</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=fixpermissions</action>
+					<action function="set">tw_text1=Fix Permissions?</action>
+					<action function="set">tw_action_text1=Fixing Permissions...</action>
+					<action function="set">tw_complete_text1=Fix Permissions Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_allow_partition_sdcard" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Partition SD Card</text>
+				<image resource="main_button" />
+				<action function="page">partsdcard</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>File Manager</text>
+				<image resource="main_button" />
+				<action function="page">filemanagerlist</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Terminal Command</text>
+				<image resource="main_button" />
+				<action function="page">terminalfolder</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reload Theme</text>
+				<image resource="main_button" />
+				<action function="reload"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>ADB Sideload</text>
+				<image resource="main_button" />
+				<action function="page">sideload</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>HTC Dumlock</text>
+				<image resource="main_button" />
+				<action function="page">htcdumlock</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_has_injecttwrp" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Re-Inject TWRP</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=reinjecttwrp</action>
+					<action function="set">tw_text1=Re-Inject TWRP?</action>
+					<action function="set">tw_action_text1=Re-Injecting TWRP...</action>
+					<action function="set">tw_complete_text1=TWRP Injection Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="partsdcard">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Partition SD Card</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_sdext_size-128</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sd_plus_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_sdext_size+128</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sdext_text_x%" y="%sdext_text_y%" />
+				<text>EXT Size: %tw_sdext_size%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%sdswap_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_swap_size-32</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sd_plus_x%" y="%sdswap_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_swap_size+32</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sdswap_text_x%" y="%sdswap_text_y%" />
+				<text>Swap Size: %tw_swap_size%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%sdfilesystem_text_y%" />
+				<text>File system: %tw_sdpart_file_system%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%sdfilesystem_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT3</text>
+				<image resource="main_button" />
+				<action function="set">tw_sdpart_file_system=ext3</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_sdext_disable_ext4" var2="0" />
+				<placement x="%col2_x%" y="%sdfilesystem_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT4</text>
+				<image resource="main_button" />
+				<action function="set">tw_sdpart_file_system=ext4</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row12_text_y%" />
+				<text>You will lose all files on your SD card!</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row13_text_y%" />
+				<text>This action cannot be undone!</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">partsdcardaction</action>
+				<actions>
+					<action function="set">tw_back=partsdcard</action>
+					<action function="set">tw_action=partitionsd</action>
+					<action function="set">tw_has_action2=1</action>
+					<action function="set">tw_action2=set</action>
+					<action function="set">tw_action2_param=tw_zip_location=/sdcard</action>
+					<action function="set">tw_action_text1=Partitioning SD Card...</action>
+					<action function="set">tw_action_text2=This will take a few minutes.</action>
+					<action function="set">tw_complete_text1=Partitioning Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Partition</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="htcdumlock">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>HTC Dumlock</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Restore Original Boot</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=htcdumlock</action>
+					<action function="set">tw_action=htcdumlockrestoreboot</action>
+					<action function="set">tw_text1=Restore original boot image?</action>
+					<action function="set">tw_action_text1=Restoring Original Boot...</action>
+					<action function="set">tw_complete_text1=Restore Original Boot Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reflash Recovery</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=htcdumlock</action>
+					<action function="set">tw_action=htcdumlockreflashrecovery</action>
+					<action function="set">tw_text1=Reflash recovery to boot?</action>
+					<action function="set">tw_action_text1=Flashing recovery to boot...</action>
+					<action function="set">tw_complete_text1=Recovery Flash to Boot Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Install HTC Dumlock</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=htcdumlock</action>
+					<action function="set">tw_action=installhtcdumlock</action>
+					<action function="set">tw_text1=Install HTC dumlock files to ROM?</action>
+					<action function="set">tw_action_text1=Installing HTC Dumlock...</action>
+					<action function="set">tw_complete_text1=HTC Dumlock Install Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="lock">
+			<background color="#000000A0" />
+
+			<object type="image">
+				<image resource="unlock-icon" />
+				<placement x="%lock_x%" y="%lock_y%" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="overlay"></action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Unlock</text>
+			</object>
+		</page>
+
+		<page name="filemanagerlist">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>File Manager: Select a File or Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_file_location1%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<folders="1" files="1" />
+				<path name="tw_file_location1" default="/" />
+				<data name="tw_filename1" />
+				<selection name="tw_selection1" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<actions>
+					<action function="set">tw_fm_type=File</action>
+					<action function="set">tw_fm_isfolder=0</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_filename1" op="modified" />
+				<actions>
+					<action function="page">filemanageroptions</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Select</text>
+				<image resource="medium_button" />
+				<actions>
+					<action function="set">tw_filename1=tw_file_location1</action>
+					<action function="set">tw_fm_isfolder=1</action>
+					<action function="set">tw_fm_type=Folder</action>
+					<action function="page">filemanageroptions</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanageroptions">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_type% Selected:</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Copy File</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=cp</action>
+					<action function="set">tw_fm_text1=Copying</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Copy Folder</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=cd &quot;%tw_file_location1%&quot; && cd .. && cp -R</action>
+					<action function="set">tw_fm_text1=Copying</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Move</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=mv</action>
+					<action function="set">tw_fm_text1=Moving</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>chmod 755</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=chmod 755</action>
+					<action function="set">tw_fm_text1=chmod 755</action>
+					<action function="set">tw_fm_text2=</action>
+					<action function="set">tw_fm_text3=</action>
+					<action function="set">tw_include_text3=0</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>chmod</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_rename=0000</action>
+					<action function="set">tw_fm_text2=</action>
+					<action function="set">tw_fm_text3=</action>
+					<action function="set">tw_include_text3=0</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerchmod</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Delete</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=rm -rf</action>
+					<action function="set">tw_fm_text1=Deleting</action>
+					<action function="set">tw_fm_text2=</action>
+					<action function="set">tw_fm_text3=</action>
+					<action function="set">tw_include_text3=0</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Rename File</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_rename=tw_selection1</action>
+					<action function="set">tw_fm_text1=Renaming</action>
+					<action function="set">tw_filemanager_command=mv</action>
+					<action function="page">filemanagerrenamefile</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Rename Folder</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_rename=tw_selection1</action>
+					<action function="set">tw_fm_text1=Renaming</action>
+					<action function="set">tw_filemanager_command=cd &quot;%tw_file_location1%&quot; && cd .. && mv</action>
+					<action function="page">filemanagerrenamefolder</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanagerlist</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="choosedestinationfolder">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Browse to Destination Folder & Press Select</text>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_file_location2%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="1" files="0" />
+				<path name="tw_file_location2" default="/" />
+				<data name="tw_filename2" />
+				<selection name="tw_selection2" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Select</text>
+				<image resource="medium_button" />
+				<actions>
+					<action function="set">tw_fm_text2=to</action>
+					<action function="set">tw_fm_text3=%tw_file_location2%</action>
+					<action function="set">tw_include_text3=1</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerrenamefile">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a New %tw_fm_type% Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_filemanager_rename%</text>
+				<data name="tw_filemanager_rename" />
+				<restrict minlen="1" maxlen="128" />
+				<actions>
+					<action function="set">tw_fm_text2=to</action>
+					<action function="set">tw_fm_text3=&quot;%tw_file_location1%/%tw_filemanager_rename%&quot;</action>
+					<action function="set">tw_include_text3=1</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerrenamefolder">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a New %tw_fm_type% Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_filemanager_rename%</text>
+				<data name="tw_filemanager_rename" />
+				<restrict minlen="1" maxlen="128" />
+				<actions>
+					<action function="set">tw_fm_text2=to</action>
+					<action function="set">tw_fm_text3=%tw_filemanager_rename%</action>
+					<action function="set">tw_include_text3=1</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerchmod">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter New Permissions</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_filemanager_rename%</text>
+				<data name="tw_filemanager_rename" />
+				<restrict minlen="3" maxlen="4" allow="0123456789" />
+				<actions>
+					<action function="set">tw_filemanager_command=chmod %tw_filemanager_rename%</action>
+					<action function="set">tw_fm_text1=chmod %tw_filemanager_rename%</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerconfirm">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>%tw_fm_text2%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>%tw_fm_text3%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row10_text_y%" placement="5"/>
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">filemanageracction</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Confirm</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">%tw_back%</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanageracction">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_fm_text1%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_back=filemanagerlist</action>
+					<action function="set">tw_complete_text1=File Operation Complete</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="!=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_complete_text1=File Operation Complete</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_include_text3" var2="0" />
+				<actions>
+					<action function="cmd">%tw_filemanager_command% &quot;%tw_filename1%&quot;</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_include_text3" var2="1" />
+				<actions>
+					<action function="cmd">%tw_filemanager_command% &quot;%tw_filename1%&quot; &quot;%tw_fm_text3%&quot;</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="decrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Please Enter Your Password</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_crypto_display%</text>
+				<data name="tw_crypto_password" mask="*" maskvariable="tw_crypto_display" />
+				<restrict minlen="1" maxlen="254" />
+				<actions>
+					<action function="page">trydecrypt</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="trydecrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Trying Decryption with Your Password</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<action function="decrypt"></action>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="!=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_password_fail=1</action>
+					<action function="page">decrypt</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="terminalfolder">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Browse to Starting Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_terminal_location%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="1" files="0" />
+				<path name="tw_terminal_location" default="/" />
+				<data name="tw_terminal" />
+				<selection name="tw_terminal_selection" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Select</text>
+				<image resource="medium_button" />
+				<actions>
+					<action function="page">terminalcommand</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="terminalcommand">
+			<object type="template" name="header" />
+
+			<object type="console">
+				<placement x="%console_x%" y="0" w="%console_width%" h="%terminal_console_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<placement x="%col1_x%" y="%terminal_text_y%" placement="0" />
+				<font resource="font" />
+				<text>Starting Path: %tw_terminal_location%</text>
+			</object>
+
+			<object type="input">
+				<condition var1="tw_terminal_state" var2="0" />
+				<placement x="%col1_x%" y="%terminal_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_terminal_command%</text>
+				<data name="tw_terminal_command" />
+				<restrict minlen="1" />
+				<action function="terminalcommand">%tw_terminal_command%</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_terminal_state" var2="1" />
+				<placement x="%filemanager_select_x%" y="%terminal_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>KILL</text>
+				<image resource="medium_button" />
+				<action function="killterminal"></action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">terminalfolder</action>
+			</object>
+		</page>
+
+		<page name="sideload">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>ADB Sideload</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Wipe Dalvik Cache.</text>
+				<data variable="tw_wipe_dalvik" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Wipe Cache.</text>
+				<data variable="tw_wipe_cache" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=adbsideload</action>
+					<action function="set">tw_action_text1=ADB Sideload</action>
+					<action function="set">tw_action_text2=Usage: adb sideload filename.zip</action>
+					<action function="set">tw_complete_text1=ADB Sideload Complete</action>
+					<action function="set">tw_has_cancel=1</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="set">tw_cancel_action=adbsideloadcancel</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Start Sideload</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="installsu">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Install SuperSU?</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>Your device does not appear to be rooted.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>Install SuperSU now?</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>This will root your device.</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Do Not Install</text>
+				<image resource="main_button" />
+				<action function="set">tw_page_done=1</action>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_action=installsu</action>
+					<action function="set">tw_action_text1=Installing SuperSU</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="page">singleaction_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Install</text>
+			</object>
+		</page>
+	</pages>
+</recovery>
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-10.dat b/gui/devices/resources/fonts/Roboto-Condensed-10.dat
new file mode 100644
index 0000000..02e869b
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-10.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-12.dat b/gui/devices/resources/fonts/Roboto-Condensed-12.dat
new file mode 100644
index 0000000..b48c4f2
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-12.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-14.dat b/gui/devices/resources/fonts/Roboto-Condensed-14.dat
new file mode 100644
index 0000000..f7b174c
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-14.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-16.dat b/gui/devices/resources/fonts/Roboto-Condensed-16.dat
new file mode 100644
index 0000000..19c1147
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-16.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-20.dat b/gui/devices/resources/fonts/Roboto-Condensed-20.dat
new file mode 100644
index 0000000..531d5fd
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-20.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-25.dat b/gui/devices/resources/fonts/Roboto-Condensed-25.dat
new file mode 100644
index 0000000..7cee713
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-25.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-30.dat b/gui/devices/resources/fonts/Roboto-Condensed-30.dat
new file mode 100644
index 0000000..621b332
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-30.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-40.dat b/gui/devices/resources/fonts/Roboto-Condensed-40.dat
new file mode 100644
index 0000000..ff23add
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-40.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-50.dat b/gui/devices/resources/fonts/Roboto-Condensed-50.dat
new file mode 100644
index 0000000..ae9c0f6
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-50.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Condensed-60.dat b/gui/devices/resources/fonts/Roboto-Condensed-60.dat
new file mode 100644
index 0000000..22f9acc
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Condensed-60.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-10.dat b/gui/devices/resources/fonts/Roboto-Regular-10.dat
new file mode 100644
index 0000000..b1faa7d
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-10.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-12.dat b/gui/devices/resources/fonts/Roboto-Regular-12.dat
new file mode 100644
index 0000000..1c9f058
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-12.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-14.dat b/gui/devices/resources/fonts/Roboto-Regular-14.dat
new file mode 100644
index 0000000..5a6fff8
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-14.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-16.dat b/gui/devices/resources/fonts/Roboto-Regular-16.dat
new file mode 100644
index 0000000..9f6d900
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-16.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-20.dat b/gui/devices/resources/fonts/Roboto-Regular-20.dat
new file mode 100644
index 0000000..6588b41
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-20.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-25.dat b/gui/devices/resources/fonts/Roboto-Regular-25.dat
new file mode 100644
index 0000000..392cce9
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-25.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-30.dat b/gui/devices/resources/fonts/Roboto-Regular-30.dat
new file mode 100644
index 0000000..9f8082c
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-30.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-40.dat b/gui/devices/resources/fonts/Roboto-Regular-40.dat
new file mode 100644
index 0000000..637d9fe
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-40.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-50.dat b/gui/devices/resources/fonts/Roboto-Regular-50.dat
new file mode 100644
index 0000000..aecce38
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-50.dat
Binary files differ
diff --git a/gui/devices/resources/fonts/Roboto-Regular-60.dat b/gui/devices/resources/fonts/Roboto-Regular-60.dat
new file mode 100644
index 0000000..5840bed
--- /dev/null
+++ b/gui/devices/resources/fonts/Roboto-Regular-60.dat
Binary files differ
diff --git a/gui/devices/watch/res/watch.xml b/gui/devices/watch/res/watch.xml
new file mode 100644
index 0000000..d3ddc54
--- /dev/null
+++ b/gui/devices/watch/res/watch.xml
@@ -0,0 +1,3885 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<pages>
+		<page name="main">
+			<object type="action">
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="main2">
+			<object type="template" name="twrpheader" />
+
+			<object type="template" name="header" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_home_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Install</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row1_home_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Wipe</text>
+				<image resource="main_button" />
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_home_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Backup</text>
+				<image resource="main_button" />
+				<action function="page">backup</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_home_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Restore</text>
+				<image resource="main_button" />
+				<action function="page">restore</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_home_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Mount</text>
+				<image resource="main_button" />
+				<action function="page">mount</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_home_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Settings</text>
+				<image resource="main_button" />
+				<action function="page">settings</action>
+			</object>
+
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_home_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Advanced</text>
+				<image resource="main_button" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_home_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reboot</text>
+				<image resource="main_button" />
+				<action function="page">reboot</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="install">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Select Zip to Install</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%row1_text_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=install</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%fileselector_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_zip_location%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter extn=".zip" folders="1" files="1" />
+				<path name="tw_zip_location" default="/sdcard" />
+				<data name="tw_filename" />
+				<selection name="tw_file" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<condition var1="tw_filename" op="modified" />
+				<actions>
+					<action function="queuezip"></action>
+					<action function="page">flash_confirm</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="flash_confirm">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>This operation may install incompatible</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>software and render your device unusable.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Folder and File:</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_zip_location%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_file%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>Press back to cancel adding this zip.</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row6_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Zip file signature verification.</text>
+				<data variable="tw_signed_zip_verify" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_has_injecttwrp" var2="1" />
+				<placement x="%col1_x%" y="%row7_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Inject TWRP after install.</text>
+				<data variable="tw_inject_after_zip" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row11_text_y%" placement="5" />
+				<text>File %tw_zip_queue_count% of max of 10</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" placement="5" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="flash">flash_zip</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Confirm Flash</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_zip_queue_count" op="!=" var2="10"></condition>
+				<placement x="%col1_x%" y="%row_queue_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Add More Zips</text>
+				<image resource="main_button" />
+				<action function="page">install</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row_queue_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Clear Zip Queue</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="cancelzip"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+		</page>
+
+		<page name="flash_zip">
+			<object type="template" name="header" />
+
+			<object type="console">
+				<placement x="%console_x%" y="%row1_y%" w="%console_width%" h="%console_install_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row17_text_y%" placement="5" />
+				<text>Flashing file %tw_zip_index% of %tw_zip_queue_count%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="mediumfont" />
+				<placement x="%center_x%" y="%row18_text_y%" placement="5" />
+				<text>%tw_filename%</text>
+			</object>
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<action function="page">flash_done</action>
+			</object>
+		</page>
+
+		<page name="flash_done">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Zip Install Complete</text>
+			</object>
+
+			<object type="console">
+				<placement x="%console_x%" y="%row1_y%" w="%console_width%" h="%console_installdone_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Wipe cache/dalvik</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=flash_done</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=/cache</action>
+					<action function="set">tw_has_action2=1</action>
+					<action function="set">tw_action2=wipe</action>
+					<action function="set">tw_action2_param=dalvik</action>
+					<action function="set">tw_text1=Wipe Cache & Dalvik?</action>
+					<action function="set">tw_action_text1=Wiping Cache & Dalvik...</action>
+					<action function="set">tw_complete_text1=Cache & Dalvik Wipe Complete</action>
+					<action function="set">tw_slider_text=Swipe to Wipe</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reboot System</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=main2</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=system</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_text3=</action>
+					<action function="set">tw_text4=</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%zip_status_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text" color="%text_success_color%">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%zip_status_y%" placement="5" />
+				<text>Successful</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=install</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="clear_vars">
+			<object type="action">
+				<action function="set">tw_operation_state=0</action>
+				<action function="set">tw_text1=</action>
+				<action function="set">tw_text2=</action>
+				<action function="set">tw_text3=</action>
+				<action function="set">tw_text4=</action>
+				<action function="set">tw_action_text1=</action>
+				<action function="set">tw_action_text2=</action>
+				<action function="set">tw_action_param=</action>
+				<action function="set">tw_has_action2=0</action>
+				<action function="set">tw_action2=</action>
+				<action function="set">tw_action2_param=</action>
+				<action function="set">tw_has_cancel=0</action>
+				<action function="set">tw_cancel_action=</action>
+				<action function="set">tw_cancel_param=</action>
+				<action function="set">tw_show_exclamation=0</action>
+				<action function="set">tw_show_reboot=0</action>
+				<action function="page">%tw_clear_destination%</action>
+			</object>
+		</page>
+
+		<page name="confirm_action">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_text2%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_text3%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_text4%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row15_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">action_page</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>%tw_slider_text%</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+		</page>
+
+		<page name="action_page">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_action_text2%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_has_cancel" var2="1" />
+				<placement x="%col_center_medium_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="medium_button" />
+				<action function="%tw_cancel_action%">%tw_cancel_param%</action>
+			</object>
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="0" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="1" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+					<action function="%tw_action2%">%tw_action2_param%</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="singleaction_page">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_action_text2%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="set">tw_page_done=1</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="0" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_has_action2" var2="1" />
+				<actions>
+					<action function="%tw_action%">%tw_action_param%</action>
+					<action function="%tw_action2%">%tw_action2_param%</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="action_complete">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_complete_text1%</text>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text" color="%text_success_color%">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Successful</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="action">
+				<touch key="home" />
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="filecheck">
+			<object type="action">
+				<action function="fileexists">%tw_filecheck%</action>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_fileexists=1</action>
+					<action function="page">%tw_existpage%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" var2="1" />
+				</conditions>
+				<actions>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">%tw_notexistpage%</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="rebootcheck">
+			<object type="action">
+				<condition var1="tw_backup_system_size" op=">=" var2="%tw_min_system%" />
+				<action function="reboot">%tw_action_param%</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_backup_system_size" op="<" var2="%tw_min_system%" />
+				<action function="page">confirm_action</action>
+			</object>
+		</page>
+
+		<page name="wipe">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Factory Reset</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Wipes Data, Cache, and Dalvik</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_has_data_media" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>(not including internal storage)</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<conditions>
+					<condition var1="tw_has_android_secure" var2="1" />
+					<condition var1="fileexists" var2="/and-sec" />
+				</conditions>
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row4_text_y%" placement="1" />
+				<text>Android Secure  </text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_has_sdext_partition" var2="1" />
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row4_text_y%" />
+				<text>  SD-EXT</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>Most of the time this is</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row6_text_y%" placement="5" />
+				<text>the only wipe that you need.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row16_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%wipe_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Advanced Wipe</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_has_internal" var2="1" />
+					<condition var1="tw_has_data_media" var2="1" />
+				</conditions>
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%wipe_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Format Data</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="page">formatdata</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=wipe</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=data</action>
+					<action function="set">tw_action_text1=Factory Reset...</action>
+					<action function="set">tw_complete_text1=Factory Reset Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Factory Reset</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+		</page>
+
+		<page name="advancedwipe">
+			<object type="template" name="header" />
+
+			<object type="action">
+				<action function="set">tw_wipe_list=</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Wipe Menu</text>
+			</object>
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%wipe_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Wipe:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_wipe_list" />
+				<listtype name="wipe" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=advancedwipe</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=LIST</action>
+					<action function="set">tw_text1=Wipe Selected Partition(s)?</action>
+					<action function="set">tw_action_text1=Wiping Partition(s)...</action>
+					<action function="set">tw_complete_text1=Wipe Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%wipe_button_row1%" w="%button_fill_full_width%" h="%button_fill_half_height%" placement="5" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Repair or Change File System</text>
+				<actions>
+					<action function="checkpartitionlist"></action>
+					<action function="page">checkpartitionlist</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="partitionlisterror" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%invalid_partition_y%" placement="5" />
+				<text>Invalid partition selection</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Wipe</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">wipe</action>
+			</object>
+		</page>
+
+		<page name="formatdata">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Format Data will wipe all of your apps,</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>backups, pictures, videos, media, and</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>removes encryption on internal storage.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>This cannot be undone. Press back to cancel.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Type yes to continue.</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row6_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_confirm_formatdata%</text>
+				<data name="tw_confirm_formatdata" />
+				<restrict minlen="3" maxlen="3" allow="yes" />
+				<action function="page">formatdata_confirm</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="formatdata_confirm">
+			<object type="action">
+				<condition var1="tw_confirm_formatdata" op="=" var2="yes" />
+				<actions>
+					<action function="set">tw_back=formatdata</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=DATAMEDIA</action>
+					<action function="set">tw_action_text1=Formatting Data...</action>
+					<action function="set">tw_complete_text1=Data Format Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_confirm_formatdata" op="!=" var2="yes" />
+				<action function="page">formatdata</function>
+			</object>
+		</page>
+
+		<page name="checkpartitionlist">
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="getpartitiondetails"></action>
+					<action function="page">partitionoptions</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="!=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=1</action>
+					<action function="set">tw_wipe_list=</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="partitionoptions">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Partition Options for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_is_present" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Present: Yes</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_is_present" op="=" var2="0" />
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Present: No</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_removable" op="!=" var2="0" />
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Removable: Yes</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<condition var1="tw_partition_removable" op="=" var2="0" />
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Removable: No</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Size: %tw_partition_size%MB</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row4_text_y%" />
+				<text>Used: %tw_partition_used%MB</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>Free: %tw_partition_free%MB</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col2_x%" y="%row5_text_y%" />
+				<text>Backup Size: %tw_partition_backup_size%MB</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_can_repair" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Repair</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=partitionoptions</action>
+					<action function="set">tw_action=repair</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Repair %tw_partition_name%?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Repairing...</action>
+					<action function="set">tw_complete_text1=Repair Complete</action>
+					<action function="set">tw_slider_text=Swipe to Repair</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Change File Sys</text>
+				<image resource="main_button" />
+				<action function="page">selectfilesystem</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advancedwipe</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="refreshfilesystem">
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="getpartitiondetails"></action>
+					<action function="page">selectfilesystem</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_check_partition_list" op="!=" var2="1" />
+				<actions>
+					<action function="set">partitionlisterror=1</action>
+					<action function="set">tw_wipe_list=</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="selectfilesystem">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Change file system for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>Proceed with caution!</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_ext" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT2</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=ext2</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to EXT2?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_ext" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT3</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=ext3</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to EXT3?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_ext" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT4</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=ext4</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to EXT4?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_f2fs" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>F2FS</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=f2fs</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to F2FS?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_vfat" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>FAT</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=vfat</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to FAT?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_exfat" op="=" var2="1" />
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>exFAT</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=refreshfilesystem</action>
+					<action function="set">tw_action=changefilesystem</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_action_new_file_system=exfat</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Change %tw_partition_name% to exFAT?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting...</action>
+					<action function="set">tw_complete_text1=Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Change</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">partitionoptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backup">
+			<object type="template" name="header" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%row1_header_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" placement="5" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Backup Name: %tw_backup_name%</text>
+				<actions>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">backupname1</action>
+				</actions>
+			</object>
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%backup_list_y%" w="%listbox_width%" h="%backup_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Back Up:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_backup_list" />
+				<listtype name="backup" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col2_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>More...</text>
+				<action function="page">backupoptions</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%backup_button_row2%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=backup</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%backup_button_row1%" />
+				<font resource="font" color="%text_color%" />
+				<text>Compression</text>
+				<data variable="tw_use_compression" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">backup_run</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Back Up</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+		</page>
+
+		<page name="backupoptions">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>More Backup Options</text>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="0" />
+				</conditions>
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%row6_text_y%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>No Encryption</text>
+				<actions>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="1" />
+				</conditions>
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%row6_text_y%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Using Encryption</text>
+				<actions>
+					<action function="set">tw_password_not_match=0</action>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col2_x%" y="%row6_text_y%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Refresh Sizes</text>
+				<actions>
+					<action function="refreshsizes"></action>
+					<action function="page">backupoptions</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%backup_button_row2%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=backupotions</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Enable compression.</text>
+				<data variable="tw_use_compression" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Skip MD5 generation during backup.</text>
+				<data variable="tw_skip_md5_generate" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">backup</action>
+			</object>
+		</page>
+
+		<page name="backupname1">
+			<object type="action">
+				<condition var1="tw_backup_name" op="=" var2="(Auto Generate)" />
+				<action function="generatebackupname"></function>
+			</object>
+
+			<object type="action">
+				<action function="page">backupname2</function>
+			</object>
+		</page>
+
+		<page name="backupname2">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a Backup Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_name%</text>
+				<data name="tw_backup_name" />
+				<restrict minlen="1" maxlen="64" allow=" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.{}[]" />
+				<actions>
+					<action function="set">tw_filecheck=%tw_backups_folder%/%tw_backup_name%</action>
+					<action function="set">tw_existpage=backupname2</action>
+					<action function="set">tw_notexistpage=backup</action>
+					<action function="page">filecheck</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<font resource="font" />
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Append Date</text>
+				<image resource="main_button" />
+				<action function="appenddatetobackupname"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_backup_name=(Auto Generate)</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<actions>
+					<action function="set">tw_backup_name=(Auto Generate)</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_backup_name=(Auto Generate)</action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backupencryption">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Encrypt your backup?</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Please Enter A Password:</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_encrypt_display%</text>
+				<data name="tw_backup_password" mask="*" maskvariable="tw_backup_encrypt_display" />
+				<restrict minlen="1" maxlen="32" allow="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" />
+				<action function="page">backupencryption2</action>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_password_not_match" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Passwords Do Not Match</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_encrypt_backup=0</action>
+					<action function="set">tw_backup_password=</action>
+					<action function="set">tw_backup_password2=</action>
+					<action function="set">tw_backup_encrypt_display=</action>
+					<action function="set">tw_backup_encrypt_display2=</action>
+					<action function="page">backupoptions</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">backupoptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="backupencryption2">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Encrypt your backup?</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Please Enter Password Again:</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_encrypt_display2%</text>
+				<data name="tw_backup_password2" mask="*" maskvariable="tw_backup_encrypt_display2" />
+				<restrict minlen="1" maxlen="32" allow="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" />
+				<actions>
+					<action function="page">checkbackuppassword</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_encrypt_backup=0</action>
+					<action function="set">tw_backup_password=</action>
+					<action function="set">tw_backup_password2=</action>
+					<action function="set">tw_backup_encrypt_display=</action>
+					<action function="set">tw_backup_encrypt_display2=</action>
+					<action function="page">backupoptions</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">backupoptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="checkbackuppassword">
+			<object type="action">
+				<condition var1="tw_backup_password2" var2="tw_backup_password" />
+				<actions>
+					<action function="set">tw_encrypt_backup=1</action>
+					<action function="page">backupoptions</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_backup_password2" op="!=" var2="tw_backup_password" />
+				<actions>
+					<action function="set">tw_encrypt_backup=0</action>
+					<action function="set">tw_password_not_match=1</action>
+					<action function="set">tw_backup_password=</action>
+					<action function="set">tw_backup_password2=</action>
+					<action function="set">tw_backup_encrypt_display=</action>
+					<action function="set">tw_backup_encrypt_display2=</action>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="backup_run">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_file_progress%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_size_progress%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<action function="nandroid">backup</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="set">tw_back=backup</action>
+					<action function="set">tw_complete_text1=Backup Complete</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="restore">
+			<object type="template" name="header" />
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%row1_header_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%row2_text_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Package to Restore:</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="1" files="0" nav="0" />
+				<path name="tw_backups_folder" />
+				<data name="tw_restore" default="" />
+				<selection name="tw_restore_name" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<condition var1="tw_restore" op="modified" />
+				<actions>
+					<action function="readBackup"></action>
+					<action function="page">restore_read</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="restore_read">
+			<object type="action">
+				<condition var1="tw_restore_encrypted" var2="1" />
+				<actions>
+					<action function="set">tw_password_fail=0</action>
+					<action function="page">restore_decrypt</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_restore_encrypted" var2="0" />
+				<actions>
+					<action function="page">restore_select</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="restore_decrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Backup Encrypted</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Please Enter Your Password:</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_restore_display%</text>
+				<data name="tw_restore_password" mask="*" maskvariable="tw_restore_display" />
+				<restrict minlen="1" maxlen="32" allow="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" />
+				<actions>
+					<action function="page">try_restore_decrypt</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">restore</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Delete</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="set">tw_action=cmd</action>
+					<action function="set">tw_action_param=cd %tw_backups_folder% && rm -rf &quot;%tw_restore_name%&quot;</action>
+					<action function="set">tw_text1=Delete Backup? %tw_restore_name%</action>
+					<action function="set">tw_text2=This cannot be undone!</action>
+					<action function="set">tw_action_text1=Deleting Backup...</action>
+					<action function="set">tw_complete_text1=Backup Delete Complete</action>
+					<action function="set">tw_slider_text=Swipe to Delete</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">restore</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="try_restore_decrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Trying Decryption with Your Password</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<action function="decrypt_backup"></action>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="!=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_password_fail=1</action>
+					<action function="page">restore_decrypt</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">restore_select</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="restore_select">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%restore_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Restoring: %tw_restore_name%</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_restore_list" selectedlist="tw_restore_selected" />
+				<listtype name="restore" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%backup_button_row2%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Rename Backup</text>
+				<actions>
+					<action function="set">tw_backup_rename=</action>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">renamebackup</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col2_x%" y="%backup_button_row2%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Delete Backup</text>
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="set">tw_action=cmd</action>
+					<action function="set">tw_action_param=cd %tw_backups_folder% && rm -rf &quot;%tw_restore_name%&quot;</action>
+					<action function="set">tw_text1=Delete Backup? %tw_restore_name%</action>
+					<action function="set">tw_text2=This cannot be undone!</action>
+					<action function="set">tw_action_text1=Deleting Backup...</action>
+					<action function="set">tw_complete_text1=Backup Delete Complete</action>
+					<action function="set">tw_slider_text=Swipe to Delete</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%backup_button_row1%" />
+				<font resource="font" color="%text_color%" />
+				<text>Enable MD5 verification of backup.</text>
+				<data variable="tw_skip_md5_check" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">restore_run</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Restore</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">restore</action>
+			</object>
+		</page>
+
+		<page name="renamebackup">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a New Backup Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_backup_rename%</text>
+				<data name="tw_backup_rename" />
+				<restrict minlen="1" maxlen="64" allow=" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.{}[]" />
+				<actions>
+					<action function="set">tw_back=restore</action>
+					<action function="set">tw_action=cmd</action>
+					<action function="set">tw_action_param=cd %tw_backups_folder% && mv &quot;%tw_restore_name%&quot; &quot;%tw_backup_rename%&quot;</action>
+					<action function="set">tw_text1=Rename Backup?</action>
+					<action function="set">tw_text2=This cannot be undone!</action>
+					<action function="set">tw_action_text1=Renaming Backup...</action>
+					<action function="set">tw_complete_text1=Backup Rename Complete</action>
+					<action function="set">tw_slider_text=Swipe to Rename</action>
+					<action function="set">tw_filecheck=%tw_backups_folder%/%tw_backup_rename%</action>
+					<action function="set">tw_existpage=renamebackup</action>
+					<action function="set">tw_notexistpage=confirm_action</action>
+					<action function="page">filecheck</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<font resource="font" />
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">restore_select</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">restore_select</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="restore_run">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_size_progress%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<actions>
+					<action function="set">tw_back=restore_select</action>
+					<action function="set">tw_complete_text1=Restore Complete</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<action function="nandroid">restore</action>
+			</object>
+		</page>
+
+		<page name="selectstorage">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%storage_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Storage:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_storage_path" />
+				<listtype name="storage" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>OK</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="page">%tw_back%</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="mount">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%mount_list_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Partitions to Mount:</text>
+				<icon selected="checkbox_true" unselected="checkbox_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<listtype name="mount" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<fill color="%button_fill_color%" />
+				<placement x="%col1_x%" y="%mount_storage_row%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=mount</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_has_usb_storage" var2="1" />
+				<placement x="%col1_x%" y="row4_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>USB Storage</text>
+				<image resource="main_button" />
+				<action function="page">usb_mount</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Enable MTP</text>
+				<image resource="main_button" />
+				<action function="startmtp"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="1" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Disable MTP</text>
+				<image resource="main_button" />
+				<action function="stopmtp"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_is_decrypted" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Decrypt Data</text>
+				<image resource="main_button" />
+				<action function="page">decrypt</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="usb_mount">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>USB Storage Mounted</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<font resource="font" />
+				<text>Be sure to safely remove your device</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" />
+				<text>from your computer before unmounting!</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Unmount</text>
+				<image resource="main_button" />
+				<action function="page">usb_umount</action>
+			</object>
+
+			<object type="action">
+				<action function="mount">usb</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="usb_umount">
+			<object type="action">
+				<action function="unmount">usb</action>
+			</object>
+
+			<object type="action">
+				<action function="page">mount</action>
+			</object>
+		</page>
+
+		<page name="reboot">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Reboot Menu</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_system" var2="1" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>System</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=system</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_poweroff" var2="1" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Power Off</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=poweroff</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to power off?</action>
+					<action function="set">tw_action_text1=Turning Off...</action>
+					<action function="set">tw_complete_text1=Turning Off...</action>
+					<action function="set">tw_slider_text=Swipe to Power Off</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_recovery" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Recovery</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=recovery</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_reboot_bootloader" var2="1" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Bootloader</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=bootloader</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_download_mode" var2="1" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Download</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=download</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=No OS Installed! Are you</action>
+					<action function="set">tw_text2=sure you wish to reboot?</action>
+					<action function="set">tw_action_text1=Rebooting...</action>
+					<action function="set">tw_complete_text1=Rebooting...</action>
+					<action function="set">tw_slider_text=Swipe to Reboot</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="settings">
+			<object type="template" name="header" />
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row1_header_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Zip file signature verification.</text>
+				<data variable="tw_signed_zip_verify" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Use rm -rf instead of formatting.</text>
+				<data variable="tw_rm_rf" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Skip MD5 generation during backup.</text>
+				<data variable="tw_skip_md5_generate" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Enable MD5 verification of backup files.</text>
+				<data variable="tw_skip_md5_check" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Use 24-hour clock.</text>
+				<data variable="tw_military_time" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Simulate actions for theme testing.</text>
+				<data variable="tw_simulate_actions" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<placement x="%col1_x%" y="%row6_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Simulate failure for actions.</text>
+				<data variable="tw_simulate_fail" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Time Zone</text>
+				<image resource="main_button" />
+				<action function="page">timezone</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Screen</text>
+				<image resource="main_button" />
+				<action function="page">screen</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Restore Defaults</text>
+				<image resource="main_button" />
+				<action function="restoredefaultsettings"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Vibration</text>
+				<image resource="main_button" />
+				<action function="page">vibrate</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="timezone">
+			<object type="template" name="header" />
+
+			<object type="listbox">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%listbox_tz_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>Select Time Zone:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<background color="%listbox_background%" />
+				<font resource="font" spacing="%listbox_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<data name="tw_time_zone_guisel" />
+				<listitem name="(UTC -11) Samoa, Midway Island">BST11;BDT</listitem>
+				<listitem name="(UTC -10) Hawaii">HST10;HDT</listitem>
+				<listitem name="(UTC -9) Alaska">AST9;ADT</listitem>
+				<listitem name="(UTC -8) Pacific Time">PST8;PDT</listitem>
+				<listitem name="(UTC -7) Mountain Time">MST7;MDT</listitem>
+				<listitem name="(UTC -6) Central Time">CST6;CDT</listitem>
+				<listitem name="(UTC -5) Eastern Time">EST5;EDT</listitem>
+				<listitem name="(UTC -4) Atlantic Time">AST4;ADT</listitem>
+				<listitem name="(UTC -3) Brazil, Buenos Aires">GRNLNDST3;GRNLNDDT</listitem>
+				<listitem name="(UTC -2) Mid-Atlantic">FALKST2;FALKDT</listitem>
+				<listitem name="(UTC -1) Azores, Cape Verde">AZOREST1;AZOREDT</listitem>
+				<listitem name="(UTC  0) London, Dublin, Lisbon">GMT0;BST</listitem>
+				<listitem name="(UTC +1) Berlin, Brussels, Paris">NFT-1;DFT</listitem>
+				<listitem name="(UTC +2) Athens, Istanbul, South Africa">WET-2;WET</listitem>
+				<listitem name="(UTC +3) Moscow, Baghdad">SAUST-3;SAUDT</listitem>
+				<listitem name="(UTC +4) Abu Dhabi, Tbilisi, Muscat">WST-4;WDT</listitem>
+				<listitem name="(UTC +5) Yekaterinburg, Islamabad">PAKST-5;PAKDT</listitem>
+				<listitem name="(UTC +6) Almaty, Dhaka, Colombo">TASHST-6;TASHDT</listitem>
+				<listitem name="(UTC +7) Bangkok, Hanoi, Jakarta">THAIST-7;THAIDT</listitem>
+				<listitem name="(UTC +8) Beijing, Singapore, Hong Kong">TAIST-8;TAIDT</listitem>
+				<listitem name="(UTC +9) Tokyo, Seoul, Yakutsk">JST-9;JSTDT</listitem>
+				<listitem name="(UTC +10) Eastern Australia, Guam">EET-10;EETDT</listitem>
+				<listitem name="(UTC +11) Vladivostok, Solomon Islands">MET-11;METDT</listitem>
+				<listitem name="(UTC +12) Auckland, Wellington, Fiji">NZST-12;NZDT</listitem>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row_dst_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Do you use daylight savings time (DST)?</text>
+				<data variable="tw_time_zone_guidst" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row_offset_text_y%" placement="5" />
+				<text>Offset (usually 0): %tw_time_zone_guioffset%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_medium_x%" y="%row_offset_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>None</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=0</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_medium_x%" y="%row_offset_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>15</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=15</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col3_medium_x%" y="%row_offset_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>30</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=30</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col4_medium_x%" y="%row_offset_medium_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>45</text>
+				<image resource="medium_button" />
+				<action function="set">tw_time_zone_guioffset=45</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%tz_set_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Set Time Zone</text>
+				<image resource="main_button" />
+				<action function="setguitimezone"></action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%tz_current_y%" placement="5" />
+				<text>Current Time Zone: %tw_time_zone%</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">settings</action>
+			</object>
+		</page>
+
+		<page name="screen">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Screen Settings</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_screen_timeout_secs" op="=" var2="0" />
+				<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				<text>Enable screen timeout.</text>
+				<image resource="checkbox_false" />
+				<action function="set">tw_screen_timeout_secs=60</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_screen_timeout_secs" op="!=" var2="0" />
+				<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				<text>Enable screen timeout.</text>
+				<image resource="checkbox_true" />
+				<action function="set">tw_screen_timeout_secs=0</action>
+			</object>
+
+			<object type="slidervalue">
+				<condition var1="tw_screen_timeout_secs" op="!=" var2="0" />
+				<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				<placement x="col1_x" y="%row4_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+				<dimensions lineh="%slidervalue_lineh%" linepadding="%slidervalue_padding%" sliderw="%slidervalue_sliderw%" sliderh="%slidervalue_sliderh%" />
+				<text>Screen timeout in seconds:</text>
+				<data variable="tw_screen_timeout_secs" min="15" max="300" />
+			</object>
+
+			<object type="slidervalue">
+				<condition var1="tw_has_brightnesss_file" var2="1" />
+				<placement x="col1_x" y="%row8_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+				<dimensions lineh="%slidervalue_lineh%" linepadding="%slidervalue_padding%" sliderw="%slidervalue_sliderw%" sliderh="%slidervalue_sliderh%" />
+				<text>Brightness: %tw_brightness_pct%%</text>
+				<data variable="tw_brightness_pct" min="10" max="100" />
+				<actions>
+					<action function="set">tw_brightness=%tw_brightness_max%</action>
+					<action function="compute">tw_brightness*%tw_brightness_pct%</action>
+					<action function="compute">tw_brightness/100</action>
+					<action function="setbrightness">%tw_brightness%</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">settings</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="vibrate">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Vibration Settings :</text>
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row1_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<text>Button Vibration:</text>
+				<data variable="tw_button_vibrate" min="0" max="300" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row5_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<text>Keyboard Vibration:</text>
+				<data variable="tw_keyboard_vibrate" min="0" max="300" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row9_text_y%" w="%slidervalue_w%" />
+				<font resource="font" color="%text_color%" />
+				<text>Action Vibration:</text>
+				<data variable="tw_action_vibrate" min="0" max="500" />
+				<colors line="%slidervalue_line_clr%" slider="%slidervalue_slider_clr%" />
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">settings</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="advanced">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Advanced</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Copy Log to SD</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=copylog</action>
+					<action function="set">tw_text1=Copy Log to SD Card?</action>
+					<action function="set">tw_action_text1=Copying Log to SD Card...</action>
+					<action function="set">tw_complete_text1=Log Copy Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Fix Permissions</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=fixpermissions</action>
+					<action function="set">tw_text1=Fix Permissions?</action>
+					<action function="set">tw_action_text1=Fixing Permissions...</action>
+					<action function="set">tw_complete_text1=Fix Permissions Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_allow_partition_sdcard" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Partition SD Card</text>
+				<image resource="main_button" />
+				<action function="page">partsdcard</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>File Manager</text>
+				<image resource="main_button" />
+				<action function="page">filemanagerlist</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Terminal Command</text>
+				<image resource="main_button" />
+				<action function="page">terminalfolder</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reload Theme</text>
+				<image resource="main_button" />
+				<action function="reload"></action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>ADB Sideload</text>
+				<image resource="main_button" />
+				<action function="page">sideload</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>HTC Dumlock</text>
+				<image resource="main_button" />
+				<action function="page">htcdumlock</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_has_injecttwrp" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Re-Inject TWRP</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=reinjecttwrp</action>
+					<action function="set">tw_text1=Re-Inject TWRP?</action>
+					<action function="set">tw_action_text1=Re-Injecting TWRP...</action>
+					<action function="set">tw_complete_text1=TWRP Injection Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="partsdcard">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Partition SD Card</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_sdext_size-128</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sd_plus_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_sdext_size+128</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sdext_text_x%" y="%sdext_text_y%" />
+				<text>EXT Size: %tw_sdext_size%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%sdswap_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_swap_size-32</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%sd_plus_x%" y="%sdswap_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_swap_size+32</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%sdswap_text_x%" y="%sdswap_text_y%" />
+				<text>Swap Size: %tw_swap_size%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%sdfilesystem_text_y%" />
+				<text>File system: %tw_sdpart_file_system%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%sdfilesystem_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT3</text>
+				<image resource="main_button" />
+				<action function="set">tw_sdpart_file_system=ext3</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_sdext_disable_ext4" var2="0" />
+				<placement x="%col2_x%" y="%sdfilesystem_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT4</text>
+				<image resource="main_button" />
+				<action function="set">tw_sdpart_file_system=ext4</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row10_text_y%" />
+				<text>You will lose all files on your SD card!</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%col1_x%" y="%row11_text_y%" />
+				<text>This action cannot be undone!</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">partsdcardaction</action>
+				<actions>
+					<action function="set">tw_back=partsdcard</action>
+					<action function="set">tw_action=partitionsd</action>
+					<action function="set">tw_has_action2=1</action>
+					<action function="set">tw_action2=set</action>
+					<action function="set">tw_action2_param=tw_zip_location=/sdcard</action>
+					<action function="set">tw_action_text1=Partitioning SD Card...</action>
+					<action function="set">tw_action_text2=This will take a few minutes.</action>
+					<action function="set">tw_complete_text1=Partitioning Complete</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Partition</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+		</page>
+
+		<page name="htcdumlock">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>HTC Dumlock</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Restore Original Boot</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=htcdumlock</action>
+					<action function="set">tw_action=htcdumlockrestoreboot</action>
+					<action function="set">tw_text1=Restore original boot image?</action>
+					<action function="set">tw_action_text1=Restoring Original Boot...</action>
+					<action function="set">tw_complete_text1=Restore Original Boot Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Reflash Recovery</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=htcdumlock</action>
+					<action function="set">tw_action=htcdumlockreflashrecovery</action>
+					<action function="set">tw_text1=Reflash recovery to boot?</action>
+					<action function="set">tw_action_text1=Flashing recovery to boot...</action>
+					<action function="set">tw_complete_text1=Recovery Flash to Boot Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Install HTC Dumlock</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_back=htcdumlock</action>
+					<action function="set">tw_action=installhtcdumlock</action>
+					<action function="set">tw_text1=Install HTC dumlock files to ROM?</action>
+					<action function="set">tw_action_text1=Installing HTC Dumlock...</action>
+					<action function="set">tw_complete_text1=HTC Dumlock Install Complete</action>
+					<action function="set">tw_slider_text=Swipe to Confirm</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="lock">
+			<background color="#000000A0" />
+
+			<object type="image">
+				<image resource="unlock-icon" />
+				<placement x="%lock_x%" y="%lock_y%" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="overlay"></action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Unlock</text>
+			</object>
+		</page>
+
+		<page name="filemanagerlist">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>File Manager: Select a File or Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_file_location1%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<folders="1" files="1" />
+				<path name="tw_file_location1" default="/" />
+				<data name="tw_filename1" />
+				<selection name="tw_selection1" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<actions>
+					<action function="set">tw_fm_type=File</action>
+					<action function="set">tw_fm_isfolder=0</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_filename1" op="modified" />
+				<actions>
+					<action function="page">filemanageroptions</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Select</text>
+				<image resource="medium_button" />
+				<actions>
+					<action function="set">tw_filename1=tw_file_location1</action>
+					<action function="set">tw_fm_isfolder=1</action>
+					<action function="set">tw_fm_type=Folder</action>
+					<action function="page">filemanageroptions</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="filemanageroptions">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_type% Selected:</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Copy File</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=cp</action>
+					<action function="set">tw_fm_text1=Copying</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Copy Folder</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=cd &quot;%tw_file_location1%&quot; && cd .. && cp -R</action>
+					<action function="set">tw_fm_text1=Copying</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Move</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=mv</action>
+					<action function="set">tw_fm_text1=Moving</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>chmod 755</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=chmod 755</action>
+					<action function="set">tw_fm_text1=chmod 755</action>
+					<action function="set">tw_fm_text2=</action>
+					<action function="set">tw_fm_text3=</action>
+					<action function="set">tw_include_text3=0</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>chmod</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_rename=0000</action>
+					<action function="set">tw_fm_text2=</action>
+					<action function="set">tw_fm_text3=</action>
+					<action function="set">tw_include_text3=0</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerchmod</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Delete</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_command=rm -rf</action>
+					<action function="set">tw_fm_text1=Deleting</action>
+					<action function="set">tw_fm_text2=</action>
+					<action function="set">tw_fm_text3=</action>
+					<action function="set">tw_include_text3=0</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Rename File</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_rename=tw_selection1</action>
+					<action function="set">tw_fm_text1=Renaming</action>
+					<action function="set">tw_filemanager_command=mv</action>
+					<action function="page">filemanagerrenamefile</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Rename Folder</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_filemanager_rename=tw_selection1</action>
+					<action function="set">tw_fm_text1=Renaming</action>
+					<action function="set">tw_filemanager_command=cd &quot;%tw_file_location1%&quot; && cd .. && mv</action>
+					<action function="page">filemanagerrenamefolder</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanagerlist</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="choosedestinationfolder">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Browse to Destination & Press Select</text>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_file_location2%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="1" files="0" />
+				<path name="tw_file_location2" default="/" />
+				<data name="tw_filename2" />
+				<selection name="tw_selection2" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Select</text>
+				<image resource="medium_button" />
+				<actions>
+					<action function="set">tw_fm_text2=to</action>
+					<action function="set">tw_fm_text3=%tw_file_location2%</action>
+					<action function="set">tw_include_text3=1</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="filemanagerrenamefile">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a New %tw_fm_type% Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_filemanager_rename%</text>
+				<data name="tw_filemanager_rename" />
+				<restrict minlen="1" maxlen="128" />
+				<actions>
+					<action function="set">tw_fm_text2=to</action>
+					<action function="set">tw_fm_text3=&quot;%tw_file_location1%/%tw_filemanager_rename%&quot;</action>
+					<action function="set">tw_include_text3=1</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerrenamefolder">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter a New %tw_fm_type% Name</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_filemanager_rename%</text>
+				<data name="tw_filemanager_rename" />
+				<restrict minlen="1" maxlen="128" />
+				<actions>
+					<action function="set">tw_fm_text2=to</action>
+					<action function="set">tw_fm_text3=%tw_filemanager_rename%</action>
+					<action function="set">tw_include_text3=1</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerchmod">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<font resource="font" />
+				<text>Please Enter New Permissions</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_filemanager_rename%</text>
+				<data name="tw_filemanager_rename" />
+				<restrict minlen="3" maxlen="4" allow="0123456789" />
+				<actions>
+					<action function="set">tw_filemanager_command=chmod %tw_filemanager_rename%</action>
+					<action function="set">tw_fm_text1=chmod %tw_filemanager_rename%</action>
+					<action function="set">tw_back=filemanageroptions</action>
+					<action function="page">filemanagerconfirm</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">filemanageroptions</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="filemanagerconfirm">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_text1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>%tw_fm_text2%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>%tw_fm_text3%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row10_text_y%" placement="5"/>
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<action function="page">filemanageracction</action>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Confirm</text>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">%tw_back%</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+		</page>
+
+		<page name="filemanageracction">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_fm_text1%</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_back=filemanagerlist</action>
+					<action function="set">tw_complete_text1=File Operation Complete</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="!=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_complete_text1=File Operation Complete</action>
+					<action function="page">action_complete</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_include_text3" var2="0" />
+				<actions>
+					<action function="cmd">%tw_filemanager_command% &quot;%tw_filename1%&quot;</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_include_text3" var2="1" />
+				<actions>
+					<action function="cmd">%tw_filemanager_command% &quot;%tw_filename1%&quot; &quot;%tw_fm_text3%&quot;</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="decrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Please Enter Your Password</text>
+			</object>
+
+			<object type="input">
+				<placement x="%col1_x%" y="%row3_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_crypto_display%</text>
+				<data name="tw_crypto_password" mask="*" maskvariable="tw_crypto_display" />
+				<restrict minlen="1" maxlen="254" />
+				<actions>
+					<action function="page">trydecrypt</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_fail_color%">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Cancel</text>
+				<image resource="main_button" />
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="trydecrypt">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Trying Decryption with Your Password</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="template" name="progress_bar" />
+
+			<object type="action">
+				<action function="decrypt"></action>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="!=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_password_fail=1</action>
+					<action function="page">decrypt</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<conditions>
+					<condition var1="tw_operation_state" var2="1" />
+					<condition var1="tw_operation_status" op="=" var2="0" />
+				</conditions>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="terminalfolder">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Browse to Starting Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<highlight color="%fileselector_highlight_color%" />
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<header background="%fileselector_header_background%" textcolor="%fileselector_header_textcolor%" separatorcolor="%fileselector_header_separatorcolor%" separatorheight="%fileselector_header_separatorheight%" />
+				<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+				<text>%tw_terminal_location%</text>
+				<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+				<sort name="tw_gui_sort_order" />
+				<icon folder="folder_icon" file="file_icon" />
+				<background color="%fileselector_background%" />
+				<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+				<filter folders="1" files="0" />
+				<path name="tw_terminal_location" default="/" />
+				<data name="tw_terminal" />
+				<selection name="tw_terminal_selection" />
+			</object>
+
+			<object type="template" name="sort_options" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Select</text>
+				<image resource="medium_button" />
+				<actions>
+					<action function="page">terminalcommand</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="terminalcommand">
+			<object type="template" name="header" />
+
+			<object type="console">
+				<placement x="%console_x%" y="0" w="%console_width%" h="%terminal_console_height%" />
+				<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+				<font resource="fixed" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<placement x="%col1_x%" y="%terminal_text_y%" placement="0" />
+				<font resource="font" />
+				<text>Starting Path: %tw_terminal_location%</text>
+			</object>
+
+			<object type="input">
+				<condition var1="tw_terminal_state" var2="0" />
+				<placement x="%col1_x%" y="%terminal_text_y%" w="%input_width%" h="%input_height%" placement="0" />
+				<background color="%input_background_color%" />
+				<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+				<font resource="font" color="%text_color%" />
+				<text>%tw_terminal_command%</text>
+				<data name="tw_terminal_command" />
+				<restrict minlen="1" />
+				<action function="terminalcommand">%tw_terminal_command%</action>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<condition var1="tw_terminal_state" var2="1" />
+				<placement x="%filemanager_select_x%" y="%terminal_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>KILL</text>
+				<image resource="medium_button" />
+				<action function="killterminal"></action>
+			</object>
+
+			<object type="template" name="keyboardtemplate" />
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">terminalfolder</action>
+			</object>
+		</page>
+
+		<page name="sideload">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>ADB Sideload</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Wipe Dalvik Cache.</text>
+				<data variable="tw_wipe_dalvik" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Wipe Cache.</text>
+				<data variable="tw_wipe_cache" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=adbsideload</action>
+					<action function="set">tw_action_text1=ADB Sideload</action>
+					<action function="set">tw_action_text2=Usage: adb sideload filename.zip</action>
+					<action function="set">tw_complete_text1=ADB Sideload Complete</action>
+					<action function="set">tw_has_cancel=1</action>
+					<action function="set">tw_show_reboot=1</action>
+					<action function="set">tw_cancel_action=adbsideloadcancel</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Start Sideload</text>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">advanced</action>
+			</object>
+		</page>
+
+		<page name="installsu">
+			<object type="template" name="header" />
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Install SuperSU?</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>Your device does not appear to be rooted.</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>Install SuperSU now?</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>This will root your device.</text>
+			</object>
+
+			<object type="button">
+				<highlight color="%highlight_color%" />
+				<placement x="%col_center_x%" y="%row3_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>Do Not Install</text>
+				<image resource="main_button" />
+				<action function="set">tw_page_done=1</action>
+			</object>
+
+			<object type="slider">
+				<placement x="%slider_x%" y="%slider_y%" />
+				<resource base="slider" used="slider-used" touch="slider-touch" />
+				<actions>
+					<action function="set">tw_action=installsu</action>
+					<action function="set">tw_action_text1=Installing SuperSU</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="page">singleaction_page</action>
+				</actions>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="%center_x%" y="%slider_text_y%" placement="4" />
+				<text>Swipe to Install</text>
+			</object>
+		</page>
+	</pages>
+</recovery>
diff --git a/gui/fileselector.cpp b/gui/fileselector.cpp
new file mode 100644
index 0000000..cf7a9a9
--- /dev/null
+++ b/gui/fileselector.cpp
@@ -0,0 +1,1040 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <linux/input.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include <algorithm>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+#include "../twrp-functions.hpp"
+
+#define TW_FILESELECTOR_UP_A_LEVEL "(Up A Level)"
+
+#define SCROLLING_SPEED_DECREMENT 6
+#define SCROLLING_FLOOR 10
+#define SCROLLING_MULTIPLIER 6
+
+int GUIFileSelector::mSortOrder = 0;
+
+GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+	int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
+
+	mStart = mLineSpacing = startY = mFontHeight = mSeparatorH = scrollingY = scrollingSpeed = 0;
+	mIconWidth = mIconHeight = mFolderIconHeight = mFileIconHeight = mFolderIconWidth = mFileIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0;
+	mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0;
+	mFolderIcon = mFileIcon = mBackground = mFont = mHeaderIcon = NULL;
+	mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0;
+	mShowFolders = mShowFiles = mShowNavFolders = 1;
+	mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
+	mFastScrollRectX = mFastScrollRectY = -1;
+	mUpdate = 0;
+	touchDebounce = 6;
+	mPathVar = "cwd";
+	ConvertStrToColor("black", &mBackgroundColor);
+	ConvertStrToColor("black", &mHeaderBackgroundColor);
+	ConvertStrToColor("black", &mSeparatorColor);
+	ConvertStrToColor("black", &mHeaderSeparatorColor);
+	ConvertStrToColor("white", &mFontColor);
+	ConvertStrToColor("white", &mHeaderFontColor);
+	ConvertStrToColor("white", &mFastScrollLineColor);
+	ConvertStrToColor("white", &mFastScrollRectColor);
+	hasHighlightColor = false;
+	hasFontHighlightColor = false;
+	isHighlighted = false;
+	updateFileList = false;
+	startSelection = -1;
+
+	// Load header text
+	child = node->first_node("header");
+	if (child)
+	{
+		attr = child->first_attribute("icon");
+		if (attr)
+			mHeaderIcon = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("background");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderBackgroundColor);
+			header_background_color_specified = -1;
+		}
+		attr = child->first_attribute("textcolor");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderFontColor);
+			header_text_color_specified = -1;
+		}
+		attr = child->first_attribute("separatorcolor");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderSeparatorColor);
+			header_separator_color_specified = -1;
+		}
+		attr = child->first_attribute("separatorheight");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mHeaderSeparatorH = atoi(parsevalue.c_str());
+			header_separator_height_specified = -1;
+		}
+	}
+	child = node->first_node("text");
+	if (child)  mHeaderText = child->value();
+
+	memset(&mHighlightColor, 0, sizeof(COLOR));
+	child = node->first_node("highlight");
+	if (child) {
+		attr = child->first_attribute("color");
+		if (attr) {
+			hasHighlightColor = true;
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHighlightColor);
+		}
+	}
+
+	// Simple way to check for static state
+	mLastValue = gui_parse_text(mHeaderText);
+	if (mLastValue != mHeaderText)
+		mHeaderIsStatic = 0;
+	else
+		mHeaderIsStatic = -1;
+
+	child = node->first_node("icon");
+	if (child)
+	{
+		attr = child->first_attribute("folder");
+		if (attr)
+			mFolderIcon = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("file");
+		if (attr)
+			mFileIcon = PageManager::FindResource(attr->value());
+	}
+	child = node->first_node("background");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mBackground = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mBackgroundColor);
+			if (!header_background_color_specified)
+				ConvertStrToColor(color, &mHeaderBackgroundColor);
+		}
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+
+	// Load the font, and possibly override the color
+	child = node->first_node("font");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mFont = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mFontColor);
+			if (!header_text_color_specified)
+				ConvertStrToColor(color, &mHeaderFontColor);
+		}
+
+		attr = child->first_attribute("spacing");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mLineSpacing = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("highlightcolor");
+		memset(&mFontHighlightColor, 0, sizeof(COLOR));
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mFontHighlightColor);
+			hasFontHighlightColor = true;
+		}
+	}
+
+	// Load the separator if it exists
+	child = node->first_node("separator");
+	if (child)
+	{
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mSeparatorColor);
+			if (!header_separator_color_specified)
+				ConvertStrToColor(color, &mHeaderSeparatorColor);
+		}
+
+		attr = child->first_attribute("height");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mSeparatorH = atoi(parsevalue.c_str());
+			if (!header_separator_height_specified)
+				mHeaderSeparatorH = mSeparatorH;
+		}
+	}
+
+	child = node->first_node("filter");
+	if (child)
+	{
+		attr = child->first_attribute("extn");
+		if (attr)
+			mExtn = attr->value();
+		attr = child->first_attribute("folders");
+		if (attr)
+			mShowFolders = atoi(attr->value());
+		attr = child->first_attribute("files");
+		if (attr)
+			mShowFiles = atoi(attr->value());
+		attr = child->first_attribute("nav");
+		if (attr)
+			mShowNavFolders = atoi(attr->value());
+	}
+
+	// Handle the path variable
+	child = node->first_node("path");
+	if (child)
+	{
+		attr = child->first_attribute("name");
+		if (attr)
+			mPathVar = attr->value();
+		attr = child->first_attribute("default");
+		if (attr)
+			DataManager::SetValue(mPathVar, attr->value());
+	}
+
+	// Handle the result variable
+	child = node->first_node("data");
+	if (child)
+	{
+		attr = child->first_attribute("name");
+		if (attr)
+			mVariable = attr->value();
+		attr = child->first_attribute("default");
+		if (attr)
+			DataManager::SetValue(mVariable, attr->value());
+	}
+
+	// Handle the sort variable
+	child = node->first_node("sort");
+	if (child)
+	{
+		attr = child->first_attribute("name");
+		if (attr)
+			mSortVariable = attr->value();
+		attr = child->first_attribute("default");
+		if (attr)
+			DataManager::SetValue(mSortVariable, attr->value());
+
+		DataManager::GetValue(mSortVariable, mSortOrder);
+	}
+
+	// Handle the selection variable
+	child = node->first_node("selection");
+	if (child)
+	{
+		attr = child->first_attribute("name");
+		if (attr)
+			mSelection = attr->value();
+		else
+			mSelection = "0";
+	} else
+		mSelection = "0";
+
+	// Fast scroll colors
+	child = node->first_node("fastscroll");
+	if (child)
+	{
+		attr = child->first_attribute("linecolor");
+		if(attr)
+			ConvertStrToColor(attr->value(), &mFastScrollLineColor);
+
+		attr = child->first_attribute("rectcolor");
+		if(attr)
+			ConvertStrToColor(attr->value(), &mFastScrollRectColor);
+
+		attr = child->first_attribute("w");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("linew");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollLineW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("rectw");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollRectW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("recth");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollRectH = atoi(parsevalue.c_str());
+		}
+	}
+
+	// Retrieve the line height
+	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
+	mLineHeight = mFontHeight;
+	mHeaderH = mFontHeight;
+
+	if (mFolderIcon && mFolderIcon->GetResource())
+	{
+		mFolderIconWidth = gr_get_width(mFolderIcon->GetResource());
+		mFolderIconHeight = gr_get_height(mFolderIcon->GetResource());
+		if (mFolderIconHeight > (int)mLineHeight)
+			mLineHeight = mFolderIconHeight;
+		mIconWidth = mFolderIconWidth;
+	}
+
+	if (mFileIcon && mFileIcon->GetResource())
+	{
+		mFileIconWidth = gr_get_width(mFileIcon->GetResource());
+		mFileIconHeight = gr_get_height(mFileIcon->GetResource());
+		if (mFileIconHeight > (int)mLineHeight)
+			mLineHeight = mFileIconHeight;
+		if (mFileIconWidth > mIconWidth)
+			mIconWidth = mFileIconWidth;
+	}
+
+	if (mHeaderIcon && mHeaderIcon->GetResource())
+	{
+		mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
+		mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
+		if (mHeaderIconHeight > mHeaderH)
+			mHeaderH = mHeaderIconHeight;
+		if (mHeaderIconWidth > mIconWidth)
+			mIconWidth = mHeaderIconWidth;
+	}
+
+	mHeaderH += mLineSpacing + mHeaderSeparatorH;
+	actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
+	if (mHeaderH < actualLineHeight)
+		mHeaderH = actualLineHeight;
+
+	if (actualLineHeight / 3 > 6)
+		touchDebounce = actualLineHeight / 3;
+
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundW = gr_get_width(mBackground->GetResource());
+		mBackgroundH = gr_get_height(mBackground->GetResource());
+	}
+
+	// Fetch the file/folder list
+	std::string value;
+	DataManager::GetValue(mPathVar, value);
+	GetFileList(value);
+}
+
+GUIFileSelector::~GUIFileSelector()
+{
+}
+
+int GUIFileSelector::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	// First step, fill background
+	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
+	gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
+
+	// Next, render the background resource (if it exists)
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
+		mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
+		gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
+	}
+
+	// Update the file list if needed
+	if (updateFileList) {
+		string value;
+		DataManager::GetValue(mPathVar, value);
+		if (GetFileList(value) == 0)
+			updateFileList = false;
+		else
+			return 0;
+	}
+
+	// This tells us how many lines we can actually render
+	int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+	int line;
+
+	int folderSize = mShowFolders ? mFolderList.size() : 0;
+	int fileSize = mShowFiles ? mFileList.size() : 0;
+
+	int listW = mRenderW;
+
+	if (folderSize + fileSize < lines) {
+		lines = folderSize + fileSize;
+		scrollingY = 0;
+		mFastScrollRectX = mFastScrollRectY = -1;
+	} else {
+		listW -= mFastScrollW; // space for fast scroll
+		lines++;
+		if (lines < folderSize + fileSize)
+			lines++;
+	}
+
+	void* fontResource = NULL;
+	if (mFont)  fontResource = mFont->GetResource();
+
+	int yPos = mRenderY + mHeaderH + scrollingY;
+	int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
+	int currentIconHeight = 0, currentIconWidth = 0;
+	int currentIconOffsetY = 0, currentIconOffsetX = 0;
+	int folderIconOffsetY = (int)((actualLineHeight - mFolderIconHeight) / 2), fileIconOffsetY = (int)((actualLineHeight - mFileIconHeight) / 2);
+	int folderIconOffsetX = (mIconWidth - mFolderIconWidth) / 2, fileIconOffsetX = (mIconWidth - mFileIconWidth) / 2;
+	int actualSelection = mStart;
+
+	if (isHighlighted) {
+		int selectY = scrollingY;
+
+		// Locate the correct line for highlighting
+		while (selectY + actualLineHeight < startSelection) {
+			selectY += actualLineHeight;
+			actualSelection++;
+		}
+		if (hasHighlightColor) {
+			// Highlight the area
+			gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
+			int HighlightHeight = actualLineHeight;
+			if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
+				HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
+			}
+			gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
+		}
+	}
+
+	for (line = 0; line < lines; line++)
+	{
+		Resource* icon;
+		std::string label;
+
+		if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
+			// Use the highlight color for the font
+			gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
+		} else {
+			// Set the color for the font
+			gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
+		}
+
+		if (line + mStart < folderSize)
+		{
+			icon = mFolderIcon;
+			label = mFolderList.at(line + mStart).fileName;
+			currentIconHeight = mFolderIconHeight;
+			currentIconWidth = mFolderIconWidth;
+			currentIconOffsetY = folderIconOffsetY;
+			currentIconOffsetX = folderIconOffsetX;
+		}
+		else if (line + mStart < folderSize + fileSize)
+		{
+			icon = mFileIcon;
+			label = mFileList.at((line + mStart) - folderSize).fileName;
+			currentIconHeight = mFileIconHeight;
+			currentIconWidth = mFileIconWidth;
+			currentIconOffsetY = fileIconOffsetY;
+			currentIconOffsetX = fileIconOffsetX;
+		} else {
+			continue;
+		}
+
+		if (icon && icon->GetResource())
+		{
+			int rect_y = 0, image_y = (yPos + currentIconOffsetY);
+			if (image_y + currentIconHeight > mRenderY + mRenderH)
+				rect_y = mRenderY + mRenderH - image_y;
+			else
+				rect_y = currentIconHeight;
+			gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
+		}
+		gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
+
+		// Add the separator
+		if (yPos + actualLineHeight < mRenderH + mRenderY) {
+			gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
+			gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
+		}
+
+		// Move the yPos
+		yPos += actualLineHeight;
+	}
+
+	// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
+	// First step, fill background
+	gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
+	gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
+
+	// Now, we need the header (icon + text)
+	yPos = mRenderY;
+	{
+		Resource* headerIcon;
+		int mIconOffsetX = 0;
+
+		// render the icon if it exists
+		headerIcon = mHeaderIcon;
+		if (headerIcon && headerIcon->GetResource())
+		{
+			gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
+			mIconOffsetX = mIconWidth;
+		}
+
+		// render the text
+		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
+		gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
+
+		// Add the separator
+		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
+		gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
+	}
+
+	// render fast scroll
+	lines = (mRenderH - mHeaderH) / (actualLineHeight);
+	if(mFastScrollW > 0 && folderSize + fileSize > lines)
+	{
+		int startX = listW + mRenderX;
+		int fWidth = mRenderW - listW;
+		int fHeight = mRenderH - mHeaderH;
+
+		// line
+		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
+		gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
+
+		// rect
+		int pct = ((mStart*actualLineHeight - scrollingY)*100)/((folderSize + fileSize)*actualLineHeight-lines*actualLineHeight);
+		mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
+		mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
+
+		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
+		gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
+	}
+
+	// If a change came in during the render then we need to do another redraw so leave mUpdate alone if updateFileList is true.
+	if (!updateFileList) {
+		mUpdate = 0;
+	}
+	return 0;
+}
+
+int GUIFileSelector::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mHeaderIsStatic) {
+		std::string newValue = gui_parse_text(mHeaderText);
+		if (mLastValue != newValue) {
+			mLastValue = newValue;
+			mUpdate = 1;
+		}
+	}
+
+	if (mUpdate)
+	{
+		mUpdate = 0;
+		if (Render() == 0)
+			return 2;
+	}
+
+	// Handle kinetic scrolling
+	if (scrollingSpeed == 0) {
+		// Do nothing
+	} else if (scrollingSpeed > 0) {
+		if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
+			scrollingY += scrollingSpeed;
+			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
+		} else {
+			scrollingY += ((int) (actualLineHeight * 2.5));
+			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
+		}
+		while (mStart && scrollingY > 0) {
+			mStart--;
+			scrollingY -= actualLineHeight;
+		}
+		if (mStart == 0 && scrollingY > 0) {
+			scrollingY = 0;
+			scrollingSpeed = 0;
+		} else if (scrollingSpeed < SCROLLING_FLOOR)
+			scrollingSpeed = 0;
+		mUpdate = 1;
+	} else if (scrollingSpeed < 0) {
+		int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
+		int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+
+		if (totalSize > lines) {
+			int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
+
+			bottom_offset -= actualLineHeight;
+
+			if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
+				scrollingY += scrollingSpeed;
+				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
+			} else {
+				scrollingY -= ((int) (actualLineHeight * 2.5));
+				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
+			}
+			while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
+				mStart++;
+				scrollingY += actualLineHeight;
+			}
+			if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
+				mStart = totalSize - lines - 1;
+				scrollingY = bottom_offset;
+			} else if (mStart + lines >= totalSize && scrollingY < 0) {
+				mStart = totalSize - lines;
+				scrollingY = 0;
+			} else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
+				scrollingSpeed = 0;
+			mUpdate = 1;
+		}
+	}
+
+	return 0;
+}
+
+int GUIFileSelector::GetSelection(int x, int y)
+{
+	// We only care about y position
+	if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH)
+		return -1;
+
+	return (y - mRenderY - mHeaderH);
+}
+
+int GUIFileSelector::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if(!isConditionTrue())
+		return -1;
+
+	static int lastY = 0, last2Y = 0, fastScroll = 0;
+	int selection = 0;
+
+	switch (state)
+	{
+	case TOUCH_START:
+		if (scrollingSpeed != 0)
+			startSelection = -1;
+		else
+			startSelection = GetSelection(x,y);
+		isHighlighted = (startSelection > -1);
+		if (isHighlighted)
+			mUpdate = 1;
+		startY = lastY = last2Y = y;
+		scrollingSpeed = 0;
+
+		if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
+			fastScroll = 1;
+		break;
+	case TOUCH_DRAG:
+		// Check if we dragged out of the selection window
+		if (GetSelection(x, y) == -1) {
+			last2Y = lastY = 0;
+			if (isHighlighted) {
+				isHighlighted = false;
+				mUpdate = 1;
+			}
+			break;
+		}
+
+		// Fast scroll
+		if(fastScroll)
+		{
+			int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
+			int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
+			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+
+			float l = float((totalSize-lines)*pct)/100;
+			if(l + lines >= totalSize)
+			{
+				mStart = totalSize - lines;
+				scrollingY = 0;
+			}
+			else
+			{
+				mStart = l;
+				scrollingY = -(l - int(l))*actualLineHeight;
+			}
+
+			startSelection = -1;
+			mUpdate = 1;
+			scrollingSpeed = 0;
+			isHighlighted = false;
+			break;
+		}
+
+		// Provide some debounce on initial touches
+		if (startSelection != -1 && abs(y - startY) < touchDebounce) {
+			isHighlighted = true;
+			mUpdate = 1;
+			break;
+		}
+
+		isHighlighted = false;
+		last2Y = lastY;
+		lastY = y;
+		startSelection = -1;
+
+		// Handle scrolling
+		scrollingY += y - startY;
+		startY = y;
+		while(mStart && scrollingY > 0) {
+			mStart--;
+			scrollingY -= actualLineHeight;
+		}
+		if (mStart == 0 && scrollingY > 0)
+			scrollingY = 0;
+		{
+			int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
+			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+
+			if (totalSize > lines) {
+				int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
+
+				bottom_offset -= actualLineHeight;
+
+				while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
+					mStart++;
+					scrollingY += actualLineHeight;
+				}
+				if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
+					mStart = totalSize - lines - 1;
+					scrollingY = bottom_offset;
+				} else if (mStart + lines >= totalSize && scrollingY < 0) {
+					mStart = totalSize - lines;
+					scrollingY = 0;
+				}
+			} else
+				scrollingY = 0;
+		}
+		mUpdate = 1;
+		break;
+
+	case TOUCH_RELEASE:
+		isHighlighted = false;
+		fastScroll = 0;
+		if (startSelection >= 0)
+		{
+			// We've selected an item!
+			std::string str;
+
+			int folderSize = mShowFolders ? mFolderList.size() : 0;
+			int fileSize = mShowFiles ? mFileList.size() : 0;
+			int selectY = scrollingY, actualSelection = mStart;
+
+			// Move the selection to the proper place in the array
+			while (selectY + actualLineHeight < startSelection) {
+				selectY += actualLineHeight;
+				actualSelection++;
+			}
+			startSelection = actualSelection;
+
+			if (startSelection < folderSize + fileSize)
+			{
+				DataManager::Vibrate("tw_button_vibrate");
+
+				if (startSelection < folderSize)
+				{
+					std::string oldcwd;
+					std::string cwd;
+
+					str = mFolderList.at(startSelection).fileName;
+					if (mSelection != "0")
+						DataManager::SetValue(mSelection, str);
+					DataManager::GetValue(mPathVar, cwd);
+
+					oldcwd = cwd;
+					// Ignore requests to do nothing
+					if (str == ".")	 return 0;
+					if (str == TW_FILESELECTOR_UP_A_LEVEL)
+					{
+						if (cwd != "/")
+						{
+							size_t found;
+							found = cwd.find_last_of('/');
+							cwd = cwd.substr(0,found);
+
+							if (cwd.length() < 2)   cwd = "/";
+						}
+					}
+					else
+					{
+						// Add a slash if we're not the root folder
+						if (cwd != "/")	 cwd += "/";
+						cwd += str;
+					}
+
+					if (mShowNavFolders == 0 && mShowFiles == 0)
+					{
+						// This is a "folder" selection
+						DataManager::SetValue(mVariable, cwd);
+					}
+					else
+					{
+						DataManager::SetValue(mPathVar, cwd);
+						mStart = 0;
+						scrollingY = 0;
+						mUpdate = 1;
+					}
+				}
+				else if (!mVariable.empty())
+				{
+					str = mFileList.at(startSelection - folderSize).fileName;
+					if (mSelection != "0")
+						DataManager::SetValue(mSelection, str);
+
+					std::string cwd;
+					DataManager::GetValue(mPathVar, cwd);
+					if (cwd != "/")	 cwd += "/";
+					DataManager::SetValue(mVariable, cwd + str);
+				}
+			}
+		} else {
+			// This is for kinetic scrolling
+			scrollingSpeed = lastY - last2Y;
+			if (abs(scrollingSpeed) > SCROLLING_FLOOR)
+				scrollingSpeed *= SCROLLING_MULTIPLIER;
+			else
+				scrollingSpeed = 0;
+		}
+	case TOUCH_REPEAT:
+	case TOUCH_HOLD:
+		break;
+	}
+	return 0;
+}
+
+int GUIFileSelector::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIObject::NotifyVarChange(varName, value);
+
+	if(!isConditionTrue())
+		return 0;
+
+	if (varName.empty()) {
+		// Always clear the data variable so we know to use it
+		DataManager::SetValue(mVariable, "");
+	}
+	if (!mHeaderIsStatic) {
+		std::string newValue = gui_parse_text(mHeaderText);
+		if (mLastValue != newValue) {
+			mLastValue = newValue;
+			mStart = 0;
+			scrollingY = 0;
+			scrollingSpeed = 0;
+			mUpdate = 1;
+		}
+	}
+	if (varName == mPathVar || varName == mSortVariable)
+	{
+		if (varName == mSortVariable) {
+			DataManager::GetValue(mSortVariable, mSortOrder);
+		}
+		updateFileList = true;
+		mStart = 0;
+		scrollingY = 0;
+		scrollingSpeed = 0;
+		mUpdate = 1;
+		return 0;
+	}
+	return 0;
+}
+
+int GUIFileSelector::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
+{
+	mRenderX = x;
+	mRenderY = y;
+	if (w || h)
+	{
+		mRenderW = w;
+		mRenderH = h;
+	}
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	mUpdate = 1;
+	return 0;
+}
+
+bool GUIFileSelector::fileSort(FileData d1, FileData d2)
+{
+	if (d1.fileName == ".")
+		return -1;
+	if (d2.fileName == ".")
+		return 0;
+	if (d1.fileName == TW_FILESELECTOR_UP_A_LEVEL)
+		return -1;
+	if (d2.fileName == TW_FILESELECTOR_UP_A_LEVEL)
+		return 0;
+
+	switch (mSortOrder) {
+		case 3: // by size largest first
+			if (d1.fileSize == d2.fileSize || d1.fileType == DT_DIR) // some directories report a different size than others - but this is not the size of the files inside the directory, so we just sort by name on directories
+				return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
+			return d1.fileSize < d2.fileSize;
+		case -3: // by size smallest first
+			if (d1.fileSize == d2.fileSize || d1.fileType == DT_DIR) // some directories report a different size than others - but this is not the size of the files inside the directory, so we just sort by name on directories
+				return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
+			return d1.fileSize > d2.fileSize;
+		case 2: // by last modified date newest first
+			if (d1.lastModified == d2.lastModified)
+				return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
+			return d1.lastModified < d2.lastModified;
+		case -2: // by date oldest first
+			if (d1.lastModified == d2.lastModified)
+				return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
+			return d1.lastModified > d2.lastModified;
+		case -1: // by name descending
+			return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
+		default: // should be a 1 - sort by name ascending
+			return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
+	}
+}
+
+int GUIFileSelector::GetFileList(const std::string folder)
+{
+	DIR* d;
+	struct dirent* de;
+	struct stat st;
+
+	// Clear all data
+	mFolderList.clear();
+	mFileList.clear();
+
+	d = opendir(folder.c_str());
+	if (d == NULL)
+	{
+		LOGINFO("Unable to open '%s'\n", folder.c_str());
+		if (folder != "/" && (mShowNavFolders != 0 || mShowFiles != 0)) {
+			size_t found;
+			found = folder.find_last_of('/');
+			if (found != string::npos) {
+				string new_folder = folder.substr(0, found);
+
+				if (new_folder.length() < 2)
+					new_folder = "/";
+				DataManager::SetValue(mPathVar, new_folder);
+			}
+		}
+		return -1;
+	}
+
+	while ((de = readdir(d)) != NULL)
+	{
+		FileData data;
+
+		data.fileName = de->d_name;
+		if (data.fileName == ".")
+			continue;
+		if (data.fileName == ".." && folder == "/")
+			continue;
+		if (data.fileName == "..") {
+			data.fileName = TW_FILESELECTOR_UP_A_LEVEL;
+			data.fileType = DT_DIR;
+		} else {
+			data.fileType = de->d_type;
+		}
+
+		std::string path = folder + "/" + data.fileName;
+		stat(path.c_str(), &st);
+		data.protection = st.st_mode;
+		data.userId = st.st_uid;
+		data.groupId = st.st_gid;
+		data.fileSize = st.st_size;
+		data.lastAccess = st.st_atime;
+		data.lastModified = st.st_mtime;
+		data.lastStatChange = st.st_ctime;
+
+		if (data.fileType == DT_UNKNOWN) {
+			data.fileType = TWFunc::Get_D_Type_From_Stat(path);
+		}
+		if (data.fileType == DT_DIR)
+		{
+			if (mShowNavFolders || (data.fileName != "." && data.fileName != TW_FILESELECTOR_UP_A_LEVEL))
+				mFolderList.push_back(data);
+		}
+		else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK)
+		{
+			if (mExtn.empty() || (data.fileName.length() > mExtn.length() && data.fileName.substr(data.fileName.length() - mExtn.length()) == mExtn))
+			{
+				mFileList.push_back(data);
+			}
+		}
+	}
+	closedir(d);
+
+	std::sort(mFolderList.begin(), mFolderList.end(), fileSort);
+	std::sort(mFileList.begin(), mFileList.end(), fileSort);
+
+	return 0;
+}
+
+void GUIFileSelector::SetPageFocus(int inFocus)
+{
+	if (inFocus)
+	{
+		updateFileList = true;
+		scrollingY = 0;
+		scrollingSpeed = 0;
+		mUpdate = 1;
+	}
+}
diff --git a/gui/fill.cpp b/gui/fill.cpp
new file mode 100644
index 0000000..1ddefaa
--- /dev/null
+++ b/gui/fill.cpp
@@ -0,0 +1,60 @@
+// fill.cpp - GUIFill object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUIFill::GUIFill(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	if (!node)
+		return;
+
+	attr = node->first_attribute("color");
+	if (!attr) {
+		LOGERR("No color specified for fill\n");
+		return;
+	}
+
+	std::string color = attr->value();
+	ConvertStrToColor(color, &mColor);
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
+
+	return;
+}
+
+int GUIFill::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	gr_color(mColor.red, mColor.green, mColor.blue, mColor.alpha);
+	gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
+	return 0;
+}
+
diff --git a/gui/gui.cpp b/gui/gui.cpp
new file mode 100644
index 0000000..2c6418e
--- /dev/null
+++ b/gui/gui.cpp
@@ -0,0 +1,950 @@
+/*
+        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <linux/input.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+extern "C"
+{
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+#ifdef HAVE_SELINUX
+#include "../minzip/Zip.h"
+#else
+#include "../minzipold/Zip.h"
+#endif
+#include <pixelflinger/pixelflinger.h>
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+#include "../variables.h"
+#include "../partitions.hpp"
+#include "../twrp-functions.hpp"
+#include "../openrecoveryscript.hpp"
+#include "../orscmd/orscmd.h"
+#ifndef TW_NO_SCREEN_TIMEOUT
+#include "blanktimer.hpp"
+#endif
+
+// Enable to print render time of each frame to the log file
+//#define PRINT_RENDER_TIME 1
+
+const static int CURTAIN_FADE = 32;
+
+using namespace rapidxml;
+
+// Global values
+static gr_surface gCurtain = NULL;
+static int gGuiInitialized = 0;
+static int gGuiConsoleRunning = 0;
+static int gGuiConsoleTerminate = 0;
+static int gForceRender = 0;
+pthread_mutex_t gForceRendermutex;
+static int gNoAnimation = 1;
+static int gGuiInputRunning = 0;
+static int gCmdLineRunning = 0;
+#ifndef TW_NO_SCREEN_TIMEOUT
+blanktimer blankTimer;
+#endif
+
+// Needed by pages.cpp too
+int gGuiRunning = 0;
+
+static int gRecorder = -1;
+
+extern "C" void gr_write_frame_to_file(int fd);
+
+void flip(void)
+{
+	if (gRecorder != -1)
+	{
+		timespec time;
+		clock_gettime(CLOCK_MONOTONIC, &time);
+		write(gRecorder, &time, sizeof(timespec));
+		gr_write_frame_to_file(gRecorder);
+	}
+	gr_flip();
+}
+
+void rapidxml::parse_error_handler(const char *what, void *where)
+{
+	fprintf(stderr, "Parser error: %s\n", what);
+	fprintf(stderr, "  Start of string: %s\n",(char *) where);
+	LOGERR("Error parsing XML file.\n");
+	//abort();
+}
+
+static void curtainSet()
+{
+	gr_color(0, 0, 0, 255);
+	gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+	gr_blit(gCurtain, 0, 0, gr_get_width(gCurtain), gr_get_height(gCurtain), 0, 0);
+	gr_flip();
+}
+
+static void curtainRaise(gr_surface surface)
+{
+	int sy = 0;
+	int h = gr_get_height(gCurtain) - 1;
+	int w = gr_get_width(gCurtain);
+	int fy = 1;
+
+	int msw = gr_get_width(surface);
+	int msh = gr_get_height(surface);
+	int CURTAIN_RATE = msh / 30;
+
+	if (gNoAnimation == 0)
+	{
+		for (; h > 0; h -= CURTAIN_RATE, sy += CURTAIN_RATE, fy += CURTAIN_RATE)
+		{
+			gr_blit(surface, 0, 0, msw, msh, 0, 0);
+			gr_blit(gCurtain, 0, sy, w, h, 0, 0);
+			gr_flip();
+		}
+	}
+	gr_blit(surface, 0, 0, msw, msh, 0, 0);
+	flip();
+}
+
+void curtainClose()
+{
+#if 0
+	int w = gr_get_width(gCurtain);
+	int h = 1;
+	int sy = gr_get_height(gCurtain) - 1;
+	int fbh = gr_fb_height();
+	int CURTAIN_RATE = fbh / 30;
+
+	if (gNoAnimation == 0)
+	{
+		for (; h < fbh; h += CURTAIN_RATE, sy -= CURTAIN_RATE)
+		{
+			gr_blit(gCurtain, 0, sy, w, h, 0, 0);
+			gr_flip();
+		}
+		gr_blit(gCurtain, 0, 0, gr_get_width(gCurtain),
+		gr_get_height(gCurtain), 0, 0);
+		gr_flip();
+
+		if (gRecorder != -1)
+			close(gRecorder);
+
+		int fade;
+		for (fade = 16; fade < 255; fade += CURTAIN_FADE)
+		{
+			gr_blit(gCurtain, 0, 0, gr_get_width(gCurtain),
+			gr_get_height(gCurtain), 0, 0);
+			gr_color(0, 0, 0, fade);
+			gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+			gr_flip();
+		}
+		gr_color(0, 0, 0, 255);
+		gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+		gr_flip();
+	}
+#else
+	gr_blit(gCurtain, 0, 0, gr_get_width(gCurtain), gr_get_height(gCurtain), 0, 0);
+	gr_flip();
+#endif
+}
+
+static void * input_thread(void *cookie)
+{
+
+	int drag = 0;
+	static int touch_and_hold = 0, dontwait = 0;
+	static int touch_repeat = 0, key_repeat = 0;
+	static int x = 0, y = 0;
+	static int lshift = 0, rshift = 0;
+	static struct timeval touchStart;
+	string seconds;
+	HardwareKeyboard *kb = PageManager::GetHardwareKeyboard();
+	MouseCursor *cursor = PageManager::GetMouseCursor();
+
+#ifndef TW_NO_SCREEN_TIMEOUT
+	//start screen timeout threads
+	blankTimer.setTimerThread();
+	DataManager::GetValue("tw_screen_timeout_secs", seconds);
+	blankTimer.setTime(atoi(seconds.c_str()));
+#else
+	LOGINFO("Skipping screen timeout threads: TW_NO_SCREEN_TIMEOUT is set\n");
+#endif
+
+	for (;;)
+	{
+		// wait for the next event
+		struct input_event ev;
+		int state = 0, ret = 0;
+
+		ret = ev_get(&ev, dontwait);
+
+		if (ret < 0)
+		{
+			struct timeval curTime;
+			gettimeofday(&curTime, NULL);
+			long mtime, seconds, useconds;
+
+			seconds = curTime.tv_sec - touchStart.tv_sec;
+			useconds = curTime.tv_usec - touchStart.tv_usec;
+
+			mtime = ((seconds) * 1000 + useconds / 1000.0) + 0.5;
+			if (touch_and_hold && mtime > 500)
+			{
+				touch_and_hold = 0;
+				touch_repeat = 1;
+				gettimeofday(&touchStart, NULL);
+#ifdef _EVENT_LOGGING
+				LOGERR("TOUCH_HOLD: %d,%d\n", x, y);
+#endif
+				PageManager::NotifyTouch(TOUCH_HOLD, x, y);
+#ifndef TW_NO_SCREEN_TIMEOUT
+				blankTimer.resetTimerAndUnblank();
+#endif
+			}
+			else if (touch_repeat && mtime > 100)
+			{
+#ifdef _EVENT_LOGGING
+				LOGERR("TOUCH_REPEAT: %d,%d\n", x, y);
+#endif
+				gettimeofday(&touchStart, NULL);
+				PageManager::NotifyTouch(TOUCH_REPEAT, x, y);
+#ifndef TW_NO_SCREEN_TIMEOUT
+				blankTimer.resetTimerAndUnblank();
+#endif
+			}
+			else if (key_repeat == 1 && mtime > 500)
+			{
+#ifdef _EVENT_LOGGING
+				LOGERR("KEY_HOLD: %d,%d\n", x, y);
+#endif
+				gettimeofday(&touchStart, NULL);
+				key_repeat = 2;
+				kb->KeyRepeat();
+#ifndef TW_NO_SCREEN_TIMEOUT
+				blankTimer.resetTimerAndUnblank();
+#endif
+
+			}
+			else if (key_repeat == 2 && mtime > 100)
+			{
+#ifdef _EVENT_LOGGING
+				LOGERR("KEY_REPEAT: %d,%d\n", x, y);
+#endif
+				gettimeofday(&touchStart, NULL);
+				kb->KeyRepeat();
+#ifndef TW_NO_SCREEN_TIMEOUT
+				blankTimer.resetTimerAndUnblank();
+#endif
+			}
+		}
+		else if (ev.type == EV_ABS)
+		{
+
+			x = ev.value >> 16;
+			y = ev.value & 0xFFFF;
+
+			if (ev.code == 0)
+			{
+				if (state == 0)
+				{
+#ifdef _EVENT_LOGGING
+					LOGERR("TOUCH_RELEASE: %d,%d\n", x, y);
+#endif
+					PageManager::NotifyTouch(TOUCH_RELEASE, x, y);
+#ifndef TW_NO_SCREEN_TIMEOUT
+					blankTimer.resetTimerAndUnblank();
+#endif
+					touch_and_hold = 0;
+					touch_repeat = 0;
+					if (!key_repeat)
+						dontwait = 0;
+				}
+				state = 0;
+				drag = 0;
+			}
+			else
+			{
+				if (!drag)
+				{
+					if (x != 0 && y != 0) {
+#ifdef _EVENT_LOGGING
+						LOGERR("TOUCH_START: %d,%d\n", x, y);
+#endif
+						if (PageManager::NotifyTouch(TOUCH_START, x, y) > 0)
+							state = 1;
+						drag = 1;
+						touch_and_hold = 1;
+						dontwait = 1;
+						key_repeat = 0;
+						gettimeofday(&touchStart, NULL);
+					}
+#ifndef TW_NO_SCREEN_TIMEOUT
+					blankTimer.resetTimerAndUnblank();
+#endif
+				}
+				else
+				{
+					if (state == 0)
+					{
+#ifdef _EVENT_LOGGING
+						LOGERR("TOUCH_DRAG: %d,%d\n", x, y);
+#endif
+						if (PageManager::NotifyTouch(TOUCH_DRAG, x, y) > 0)
+							state = 1;
+						key_repeat = 0;
+#ifndef TW_NO_SCREEN_TIMEOUT
+						blankTimer.resetTimerAndUnblank();
+#endif
+					}
+				}
+			}
+		}
+		else if (ev.type == EV_KEY)
+		{
+			// Handle key-press here
+#ifdef _EVENT_LOGGING
+			LOGERR("TOUCH_KEY: %d\n", ev.code);
+#endif
+			// Left mouse button
+			if(ev.code == BTN_LEFT)
+			{
+				if(ev.value == 1)
+				{
+					cursor->GetPos(x, y);
+
+					if (PageManager::NotifyTouch(TOUCH_START, x, y) > 0)
+						state = 1;
+					drag = 1;
+					touch_and_hold = 1;
+					dontwait = 1;
+					key_repeat = 0;
+					gettimeofday(&touchStart, NULL);
+				}
+				else if(drag == 1)
+				{
+					if (state == 0)
+					{
+						cursor->GetPos(x, y);
+
+#ifdef _EVENT_LOGGING
+						LOGERR("TOUCH_RELEASE: %d,%d\n", x, y);
+#endif
+						PageManager::NotifyTouch(TOUCH_RELEASE, x, y);
+
+						touch_and_hold = 0;
+						touch_repeat = 0;
+						if (!key_repeat)
+							dontwait = 0;
+					}
+					state = 0;
+					drag = 0;
+				}
+			}
+			// side mouse button, often used for "back" function
+			else if(ev.code == BTN_SIDE)
+			{
+				if(ev.value == 1)
+					kb->KeyDown(KEY_BACK);
+				else
+					kb->KeyUp(KEY_BACK);
+			} else if (ev.value != 0) {
+				// This is a key press
+				if (kb->KeyDown(ev.code)) {
+					// Key repeat is enabled for this key
+					key_repeat = 1;
+					touch_and_hold = 0;
+					touch_repeat = 0;
+					dontwait = 1;
+					gettimeofday(&touchStart, NULL);
+#ifndef TW_NO_SCREEN_TIMEOUT
+					blankTimer.resetTimerAndUnblank();
+#endif
+				} else {
+					key_repeat = 0;
+					touch_and_hold = 0;
+					touch_repeat = 0;
+					dontwait = 0;
+#ifndef TW_NO_SCREEN_TIMEOUT
+					blankTimer.resetTimerAndUnblank();
+#endif
+				}
+			} else {
+				// This is a key release
+				kb->KeyUp(ev.code);
+				key_repeat = 0;
+				touch_and_hold = 0;
+				touch_repeat = 0;
+				dontwait = 0;
+#ifndef TW_NO_SCREEN_TIMEOUT
+				blankTimer.resetTimerAndUnblank();
+#endif
+			}
+		}
+		else if(ev.type == EV_REL)
+		{
+#ifdef _EVENT_LOGGING
+			LOGERR("EV_REL %d %d\n", ev.code, ev.value);
+#endif
+			if(ev.code == REL_X)
+				cursor->Move(ev.value, 0);
+			else if(ev.code == REL_Y)
+				cursor->Move(0, ev.value);
+
+			if(drag == 1) {
+				cursor->GetPos(x, y);
+#ifdef _EVENT_LOGGING
+				LOGERR("TOUCH_DRAG: %d, %d\n", x, y);
+#endif
+				if (PageManager::NotifyTouch(TOUCH_DRAG, x, y) > 0)
+					state = 1;
+				key_repeat = 0;
+			}
+		}
+	}
+	return NULL;
+}
+
+static void * command_thread(void *cookie)
+{
+	int read_fd;
+	FILE* orsout;
+	char command[1024], result[512];
+
+	LOGINFO("Starting command line thread\n");
+
+	unlink(ORS_INPUT_FILE);
+	if (mkfifo(ORS_INPUT_FILE, 06660) != 0) {
+		LOGINFO("Unable to mkfifo %s\n", ORS_INPUT_FILE);
+		return 0;
+	}
+	unlink(ORS_OUTPUT_FILE);
+	if (mkfifo(ORS_OUTPUT_FILE, 06666) != 0) {
+		LOGINFO("Unable to mkfifo %s\n", ORS_OUTPUT_FILE);
+		unlink(ORS_INPUT_FILE);
+		return 0;
+	}
+
+	read_fd = open(ORS_INPUT_FILE, O_RDONLY);
+	if (read_fd < 0) {
+		LOGINFO("Unable to open %s\n", ORS_INPUT_FILE);
+		unlink(ORS_INPUT_FILE);
+		unlink(ORS_OUTPUT_FILE);
+		return 0;
+	}
+
+	while (!gGuiRunning)
+		sleep(1);
+
+	for (;;) {
+		while (read(read_fd, &command, sizeof(command)) > 0) {
+			command[1022] = '\n';
+			command[1023] = '\0';
+			LOGINFO("Command '%s' received\n", command);
+			orsout = fopen(ORS_OUTPUT_FILE, "w");
+			if (!orsout) {
+				close(read_fd);
+				LOGINFO("Unable to fopen %s\n", ORS_OUTPUT_FILE);
+				unlink(ORS_INPUT_FILE);
+				unlink(ORS_OUTPUT_FILE);
+				return 0;
+			}
+			if (DataManager::GetIntValue("tw_busy") != 0) {
+				strcpy(result, "Failed, operation in progress\n");
+				fprintf(orsout, "%s", result);
+				LOGINFO("Command cannot be performed, operation in progress.\n");
+			} else {
+				if (gui_console_only() == 0) {
+					LOGINFO("Console started successfully\n");
+					gui_set_FILE(orsout);
+					if (strlen(command) > 11 && strncmp(command, "runscript", 9) == 0) {
+						char* filename = command + 11;
+						if (OpenRecoveryScript::copy_script_file(filename) == 0) {
+							LOGERR("Unable to copy script file\n");
+						} else {
+							OpenRecoveryScript::run_script_file();
+						}
+					} else if (strlen(command) > 5 && strncmp(command, "get", 3) == 0) {
+						char* varname = command + 4;
+						string temp;
+						DataManager::GetValue(varname, temp);
+						gui_print("%s = %s\n", varname, temp.c_str());
+					} else if (OpenRecoveryScript::Insert_ORS_Command(command)) {
+						OpenRecoveryScript::run_script_file();
+					}
+					gui_set_FILE(NULL);
+					gGuiConsoleTerminate = 1;
+				}
+			}
+			fclose(orsout);
+		}
+	}
+	close(read_fd);
+	LOGINFO("Command thread exiting\n");
+	return 0;
+}
+
+// This special function will return immediately the first time, but then
+// always returns 1/30th of a second (or immediately if called later) from
+// the last time it was called
+static void loopTimer(void)
+{
+	static timespec lastCall;
+	static int initialized = 0;
+
+	if (!initialized)
+	{
+		clock_gettime(CLOCK_MONOTONIC, &lastCall);
+		initialized = 1;
+		return;
+	}
+
+	do
+	{
+		timespec curTime;
+		clock_gettime(CLOCK_MONOTONIC, &curTime);
+
+		timespec diff = TWFunc::timespec_diff(lastCall, curTime);
+
+		// This is really 30 times per second
+		if (diff.tv_sec || diff.tv_nsec > 33333333)
+		{
+			lastCall = curTime;
+			return;
+		}
+
+		// We need to sleep some period time microseconds
+		unsigned int sleepTime = 33333 -(diff.tv_nsec / 1000);
+		usleep(sleepTime);
+	} while (1);
+}
+
+static int runPages(void)
+{
+	// Raise the curtain
+	if (gCurtain != NULL)
+	{
+		gr_surface surface;
+
+		PageManager::Render();
+		gr_get_surface(&surface);
+		curtainRaise(surface);
+		gr_free_surface(surface);
+	}
+
+	gGuiRunning = 1;
+
+	DataManager::SetValue("tw_loaded", 1);
+
+#ifdef PRINT_RENDER_TIME
+	timespec start, end;
+	int32_t render_t, flip_t;
+#endif
+
+	for (;;)
+	{
+		loopTimer();
+
+		if (gGuiConsoleRunning) {
+			continue;
+		}
+
+		if (!gForceRender)
+		{
+			int ret;
+
+			ret = PageManager::Update();
+
+#ifndef PRINT_RENDER_TIME
+			if (ret > 1)
+				PageManager::Render();
+
+			if (ret > 0)
+				flip();
+#else
+			if (ret > 1)
+			{
+				clock_gettime(CLOCK_MONOTONIC, &start);
+				PageManager::Render();
+				clock_gettime(CLOCK_MONOTONIC, &end);
+				render_t = TWFunc::timespec_diff_ms(start, end);
+
+				flip();
+				clock_gettime(CLOCK_MONOTONIC, &start);
+				flip_t = TWFunc::timespec_diff_ms(end, start);
+
+				LOGINFO("Render(): %u ms, flip(): %u ms, total: %u ms\n", render_t, flip_t, render_t+flip_t);
+			}
+			else if(ret == 1)
+				flip();
+#endif
+		}
+		else
+		{
+			pthread_mutex_lock(&gForceRendermutex);
+			gForceRender = 0;
+			pthread_mutex_unlock(&gForceRendermutex);
+			PageManager::Render();
+			flip();
+		}
+
+		if (DataManager::GetIntValue("tw_gui_done") != 0)
+			break;
+	}
+
+	gGuiRunning = 0;
+	return 0;
+}
+
+static int runPage(const char *page_name)
+{
+	gui_changePage(page_name);
+
+	// Raise the curtain
+	if (gCurtain != NULL)
+	{
+		gr_surface surface;
+
+		PageManager::Render();
+		gr_get_surface(&surface);
+		curtainRaise(surface);
+		gr_free_surface(surface);
+	}
+
+	gGuiRunning = 1;
+
+	DataManager::SetValue("tw_loaded", 1);
+
+	for (;;)
+	{
+		loopTimer();
+
+		if (!gForceRender)
+		{
+			int ret;
+
+			ret = PageManager::Update();
+			if (ret > 1)
+				PageManager::Render();
+
+			if (ret > 0)
+				flip();
+		}
+		else
+		{
+			pthread_mutex_lock(&gForceRendermutex);
+			gForceRender = 0;
+			pthread_mutex_unlock(&gForceRendermutex);
+			PageManager::Render();
+			flip();
+		}
+		if (DataManager::GetIntValue("tw_page_done") != 0)
+		{
+			gui_changePage("main");
+			break;
+		}
+	}
+
+	gGuiRunning = 0;
+	return 0;
+}
+
+int gui_forceRender(void)
+{
+	pthread_mutex_lock(&gForceRendermutex);
+	gForceRender = 1;
+	pthread_mutex_unlock(&gForceRendermutex);
+	return 0;
+}
+
+int gui_changePage(std::string newPage)
+{
+	LOGINFO("Set page: '%s'\n", newPage.c_str());
+	PageManager::ChangePage(newPage);
+	pthread_mutex_lock(&gForceRendermutex);
+	gForceRender = 1;
+	pthread_mutex_unlock(&gForceRendermutex);
+	return 0;
+}
+
+int gui_changeOverlay(std::string overlay)
+{
+	PageManager::ChangeOverlay(overlay);
+	pthread_mutex_lock(&gForceRendermutex);
+	gForceRender = 1;
+	pthread_mutex_unlock(&gForceRendermutex);
+	return 0;
+}
+
+int gui_changePackage(std::string newPackage)
+{
+	PageManager::SelectPackage(newPackage);
+	pthread_mutex_lock(&gForceRendermutex);
+	gForceRender = 1;
+	pthread_mutex_unlock(&gForceRendermutex);
+	return 0;
+}
+
+std::string gui_parse_text(string inText)
+{
+	// Copied from std::string GUIText::parseText(void)
+	// This function parses text for DataManager values encompassed by %value% in the XML
+	static int counter = 0;
+	std::string str = inText;
+	size_t pos = 0;
+	size_t next = 0, end = 0;
+
+	while (1)
+	{
+		next = str.find('%', pos);
+		if (next == std::string::npos)
+			return str;
+
+		end = str.find('%', next + 1);
+		if (end == std::string::npos)
+			return str;
+
+		// We have a block of data
+		std::string var = str.substr(next + 1,(end - next) - 1);
+		str.erase(next,(end - next) + 1);
+
+		if (next + 1 == end)
+			str.insert(next, 1, '%');
+		else
+		{
+			std::string value;
+			if (DataManager::GetValue(var, value) == 0)
+				str.insert(next, value);
+		}
+
+		pos = next + 1;
+	}
+}
+
+extern "C" int gui_init(void)
+{
+	int fd;
+
+	gr_init();
+
+	if (res_create_surface("/res/images/curtain.jpg", &gCurtain))
+	{
+		printf
+		("Unable to locate '/res/images/curtain.jpg'\nDid you set a DEVICE_RESOLUTION in your config files?\n");
+		return -1;
+	}
+
+	curtainSet();
+
+	ev_init();
+	return 0;
+}
+
+extern "C" int gui_loadResources(void)
+{
+#ifndef TW_OEM_BUILD
+	int check = 0;
+	DataManager::GetValue(TW_IS_ENCRYPTED, check);
+	if (check)
+	{
+		if (PageManager::LoadPackage("TWRP", "/res/ui.xml", "decrypt"))
+		{
+			LOGERR("Failed to load base packages.\n");
+			goto error;
+		}
+		else
+			check = 1;
+	}
+
+	if (check == 0 && PageManager::LoadPackage("TWRP", "/script/ui.xml", "main"))
+	{
+		std::string theme_path;
+
+		theme_path = DataManager::GetSettingsStoragePath();
+		if (!PartitionManager.Mount_Settings_Storage(false))
+		{
+			int retry_count = 5;
+			while (retry_count > 0 && !PartitionManager.Mount_Settings_Storage(false))
+			{
+				usleep(500000);
+				retry_count--;
+			}
+
+			if (!PartitionManager.Mount_Settings_Storage(false))
+			{
+				LOGERR("Unable to mount %s during GUI startup.\n",
+					   theme_path.c_str());
+				check = 1;
+			}
+		}
+
+		theme_path += "/TWRP/theme/ui.zip";
+		if (check || PageManager::LoadPackage("TWRP", theme_path, "main"))
+		{
+#endif // ifndef TW_OEM_BUILD
+			if (PageManager::LoadPackage("TWRP", "/res/ui.xml", "main"))
+			{
+				LOGERR("Failed to load base packages.\n");
+				goto error;
+			}
+#ifndef TW_OEM_BUILD
+		}
+	}
+#endif // ifndef TW_OEM_BUILD
+	// Set the default package
+	PageManager::SelectPackage("TWRP");
+
+	gGuiInitialized = 1;
+	return 0;
+
+error:
+	LOGERR("An internal error has occurred: unable to load theme.\n");
+	gGuiInitialized = 0;
+	return -1;
+}
+
+extern "C" int gui_start(void)
+{
+	if (!gGuiInitialized)
+		return -1;
+
+	gGuiConsoleTerminate = 1;
+
+	while (gGuiConsoleRunning)
+		loopTimer();
+
+	// Set the default package
+	PageManager::SelectPackage("TWRP");
+
+	if (!gGuiInputRunning)
+	{
+		// Start by spinning off an input handler.
+		pthread_t t;
+		pthread_create(&t, NULL, input_thread, NULL);
+		gGuiInputRunning = 1;
+	}
+#ifndef TW_OEM_BUILD
+	if (!gCmdLineRunning)
+	{
+		// Start by spinning off an input handler.
+		pthread_t t;
+		pthread_create(&t, NULL, command_thread, NULL);
+		gCmdLineRunning = 1;
+	}
+#endif
+	return runPages();
+}
+
+extern "C" int gui_startPage(const char *page_name)
+{
+	if (!gGuiInitialized)
+		return -1;
+
+	gGuiConsoleTerminate = 1;
+
+	while (gGuiConsoleRunning)
+		loopTimer();
+
+	// Set the default package
+	PageManager::SelectPackage("TWRP");
+
+	if (!gGuiInputRunning)
+	{
+		// Start by spinning off an input handler.
+		pthread_t t;
+		pthread_create(&t, NULL, input_thread, NULL);
+		gGuiInputRunning = 1;
+	}
+
+	DataManager::SetValue("tw_page_done", 0);
+	return runPage(page_name);
+}
+
+static void * console_thread(void *cookie)
+{
+	PageManager::SwitchToConsole();
+
+	while (!gGuiConsoleTerminate)
+	{
+		loopTimer();
+
+		if (!gForceRender)
+		{
+			int ret;
+
+			ret = PageManager::Update();
+			if (ret > 1)
+				PageManager::Render();
+
+			if (ret > 0)
+				flip();
+
+			if (ret < 0)
+				LOGERR("An update request has failed.\n");
+		}
+		else
+		{
+			pthread_mutex_lock(&gForceRendermutex);
+			gForceRender = 0;
+			pthread_mutex_unlock(&gForceRendermutex);
+			PageManager::Render();
+			flip();
+		}
+	}
+	gGuiConsoleRunning = 0;
+	gForceRender = 1; // this will kickstart the GUI to render again
+	PageManager::EndConsole();
+	LOGINFO("Console stopping\n");
+	return NULL;
+}
+
+extern "C" int gui_console_only(void)
+{
+	if (!gGuiInitialized)
+		return -1;
+
+	gGuiConsoleTerminate = 0;
+
+	if (gGuiConsoleRunning)
+		return 0;
+
+	gGuiConsoleRunning = 1;
+
+	// Start by spinning off an input handler.
+	pthread_t t;
+	pthread_create(&t, NULL, console_thread, NULL);
+
+	return 0;
+}
diff --git a/gui/gui.h b/gui/gui.h
new file mode 100644
index 0000000..8e0371e
--- /dev/null
+++ b/gui/gui.h
@@ -0,0 +1,34 @@
+/*
+        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _GUI_HEADER
+#define _GUI_HEADER
+
+#include <stdio.h>
+
+int gui_console_only();
+int gui_init();
+int gui_loadResources();
+int gui_start();
+int gui_startPage(const char* page_name);
+void gui_print(const char *fmt, ...);
+void gui_print_color(const char *color, const char *fmt, ...);
+void gui_set_FILE(FILE* f);
+
+#endif  // _GUI_HEADER
+
diff --git a/gui/hardwarekeyboard.cpp b/gui/hardwarekeyboard.cpp
new file mode 100644
index 0000000..d8cdfd4
--- /dev/null
+++ b/gui/hardwarekeyboard.cpp
@@ -0,0 +1,83 @@
+// hardwarekeyboard.cpp - HardwareKeyboard object
+// Shell file used for most devices. A custom hardwarekeyboard.cpp is needed for devices with a hardware keyboard.
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+HardwareKeyboard::HardwareKeyboard(void)
+{
+
+}
+
+HardwareKeyboard::~HardwareKeyboard()
+{
+
+}
+
+int HardwareKeyboard::KeyDown(int key_code)
+{
+	mPressedKeys.insert(key_code);
+	PageManager::NotifyKey(key_code, true);
+#ifdef _EVENT_LOGGING
+	LOGERR("HardwareKeyboard::KeyDown %i\n", key_code);
+#endif
+	return 0; // 0 = no key repeat anything else turns on key repeat
+}
+
+int HardwareKeyboard::KeyUp(int key_code)
+{
+	std::set<int>::iterator itr = mPressedKeys.find(key_code);
+	if(itr != mPressedKeys.end())
+	{
+		mPressedKeys.erase(itr);
+		PageManager::NotifyKey(key_code, false);
+	}
+#ifdef _EVENT_LOGGING
+	LOGERR("HardwareKeyboard::KeyUp %i\n", key_code);
+#endif
+	return 0;
+}
+
+int HardwareKeyboard::KeyRepeat(void)
+{
+	/*
+	 * Uncomment when key repeats are sent somewhere.
+	 * std::set<int>::iterator itr = mPressedKeys.find(key_code);
+	 * if(itr != mPressedKeys.end())
+	 * {
+	 *	Send repeats somewhere, don't remove itr from mPressedKeys
+	 * }
+	 */
+
+#ifdef _EVENT_LOGGING
+	LOGERR("HardwareKeyboard::KeyRepeat\n");
+#endif
+	return 0;
+}
+
+void HardwareKeyboard::ConsumeKeyRelease(int key)
+{
+	mPressedKeys.erase(key);
+}
diff --git a/gui/image.cpp b/gui/image.cpp
new file mode 100644
index 0000000..2cf3b68
--- /dev/null
+++ b/gui/image.cpp
@@ -0,0 +1,105 @@
+// image.cpp - GUIImage object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUIImage::GUIImage(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mImage = NULL;
+	mHighlightImage = NULL;
+	isHighlighted = false;
+
+	if (!node)
+		return;
+
+	child = node->first_node("image");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mImage = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("highlightresource");
+		if (attr)
+			mHighlightImage = PageManager::FindResource(attr->value());
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, NULL, NULL, &mPlacement);
+
+	if (mImage && mImage->GetResource())
+	{
+		mRenderW = gr_get_width(mImage->GetResource());
+		mRenderH = gr_get_height(mImage->GetResource());
+
+		// Adjust for placement
+		if (mPlacement != TOP_LEFT && mPlacement != BOTTOM_LEFT)
+		{
+			if (mPlacement == CENTER)
+				mRenderX -= (mRenderW / 2);
+			else
+				mRenderX -= mRenderW;
+		}
+		if (mPlacement != TOP_LEFT && mPlacement != TOP_RIGHT)
+		{
+			if (mPlacement == CENTER)
+				mRenderY -= (mRenderH / 2);
+			else
+				mRenderY -= mRenderH;
+		}
+		SetPlacement(TOP_LEFT);
+	}
+
+	return;
+}
+
+int GUIImage::Render(void)
+{
+	if (!isConditionTrue())
+		return 0;
+
+	if (isHighlighted && mHighlightImage && mHighlightImage->GetResource()) {
+		gr_blit(mHighlightImage->GetResource(), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
+		return 0;
+	}
+	else if (!mImage || !mImage->GetResource())
+		return -1;
+
+	gr_blit(mImage->GetResource(), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
+	return 0;
+}
+
+int GUIImage::SetRenderPos(int x, int y, int w, int h)
+{
+	if (w || h)
+		return -1;
+
+	mRenderX = x;
+	mRenderY = y;
+	return 0;
+}
+
diff --git a/gui/input.cpp b/gui/input.cpp
new file mode 100644
index 0000000..84ee17b
--- /dev/null
+++ b/gui/input.cpp
@@ -0,0 +1,758 @@
+/*
+        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// input.cpp - GUIInput object
+
+#include <linux/input.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+
+GUIInput::GUIInput(xml_node<>* node)
+	: GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mInputText = NULL;
+	mAction = NULL;
+	mBackground = NULL;
+	mCursor = NULL;
+	mFont = NULL;
+	mRendered = false;
+	HasMask = false;
+	DrawCursor = false;
+	isLocalChange = true;
+	HasAllowed = false;
+	HasDisabled = false;
+	skipChars = scrollingX = mFontHeight = mFontY = lastX = 0;
+	mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = MinLen = MaxLen = 0;
+	mCursorLocation = -1; // -1 is always the end of the string
+	CursorWidth = 3;
+	ConvertStrToColor("black", &mBackgroundColor);
+	ConvertStrToColor("white", &mCursorColor);
+
+	if (!node)
+		return;
+
+	// Load text directly from the node
+	mInputText = new GUIText(node);
+	// Load action directly from the node
+	mAction = new GUIAction(node);
+
+	if (mInputText->Render() < 0)
+	{
+		delete mInputText;
+		mInputText = NULL;
+	}
+
+	// Load the background
+	child = node->first_node("background");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mBackground = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mBackgroundColor);
+		}
+	}
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundW = gr_get_width(mBackground->GetResource());
+		mBackgroundH = gr_get_height(mBackground->GetResource());
+	}
+
+	// Load the cursor color
+	child = node->first_node("cursor");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mCursor = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mCursorColor);
+		}
+		attr = child->first_attribute("hasfocus");
+		if (attr)
+		{
+			std::string color = attr->value();
+			SetInputFocus(atoi(color.c_str()));
+		}
+		attr = child->first_attribute("width");
+		if (attr)
+		{
+			std::string cwidth = gui_parse_text(attr->value());
+			CursorWidth = atoi(cwidth.c_str());
+		}
+	}
+	DrawCursor = HasInputFocus;
+
+	// Load the font, and possibly override the color
+	child = node->first_node("font");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr) {
+			mFont = PageManager::FindResource(attr->value());
+			mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
+		}
+	}
+
+	child = node->first_node("text");
+	if (child)  mText = child->value();
+	mLastValue = gui_parse_text(mText);
+
+	child = node->first_node("data");
+	if (child)
+	{
+		attr = child->first_attribute("name");
+		if (attr)
+			mVariable = attr->value();
+		attr = child->first_attribute("default");
+		if (attr)
+			DataManager::SetValue(mVariable, attr->value());
+		attr = child->first_attribute("mask");
+		if (attr) {
+			mMask = attr->value();
+			HasMask = true;
+		}
+		attr = child->first_attribute("maskvariable");
+		if (attr)
+			mMaskVariable = attr->value();
+		else
+			mMaskVariable = mVariable;
+	}
+
+	// Load input restrictions
+	child = node->first_node("restrict");
+	if (child)
+	{
+		attr = child->first_attribute("minlen");
+		if (attr) {
+			std::string attrib = attr->value();
+			MinLen = atoi(attrib.c_str());
+		}
+		attr = child->first_attribute("maxlen");
+		if (attr) {
+			std::string attrib = attr->value();
+			MaxLen = atoi(attrib.c_str());
+		}
+		attr = child->first_attribute("allow");
+		if (attr) {
+			HasAllowed = true;
+			AllowedList = attr->value();
+		}
+		attr = child->first_attribute("disable");
+		if (attr) {
+			HasDisabled = true;
+			DisabledList = attr->value();
+		}
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+
+	if (mInputText && mFontHeight && mFontHeight < (unsigned)mRenderH) {
+		mFontY = ((mRenderH - mFontHeight) / 2) + mRenderY;
+		mInputText->SetRenderPos(mRenderX, mFontY);
+	} else
+		mFontY = mRenderY;
+
+	if (mInputText)
+		mInputText->SetMaxWidth(mRenderW);
+
+	isLocalChange = false;
+	HandleTextLocation(-3);
+}
+
+GUIInput::~GUIInput()
+{
+	if (mInputText)	 	delete mInputText;
+	if (mBackground)	delete mBackground;
+	if (mCursor)		delete mCursor;
+	if (mAction)		delete mAction;
+}
+
+int GUIInput::HandleTextLocation(int x)
+{
+	int textWidth;
+	string displayValue, originalValue, insertChar;
+	void* fontResource = NULL;
+
+	if (mFont)
+		fontResource = mFont->GetResource();
+
+	DataManager::GetValue(mVariable, originalValue);
+	displayValue = originalValue;
+	if (HasMask) {
+		int index, string_size = displayValue.size();
+		string maskedValue;
+		for (index=0; index<string_size; index++)
+			maskedValue += mMask;
+		displayValue = maskedValue;
+	}
+	textWidth = gr_measureEx(displayValue.c_str(), fontResource);
+	if (textWidth <= mRenderW) {
+		lastX = x;
+		scrollingX = 0;
+		skipChars = 0;
+		mInputText->SkipCharCount(skipChars);
+		mRendered = false;
+		return 0;
+	}
+
+	if (skipChars && skipChars < displayValue.size())
+		displayValue.erase(0, skipChars);
+
+	textWidth = gr_measureEx(displayValue.c_str(), fontResource);
+	mRendered = false;
+
+	int deltaX, deltaText, newWidth;
+
+	if (x < -1000) {
+		// No change in scrolling
+		if (x == -1003)
+			mCursorLocation = -1;
+
+		if (mCursorLocation == -1) {
+			displayValue = originalValue;
+			skipChars = 0;
+			textWidth = gr_measureEx(displayValue.c_str(), fontResource);
+			while (textWidth > mRenderW) {
+				displayValue.erase(0, 1);
+				skipChars++;
+				textWidth = gr_measureEx(displayValue.c_str(), fontResource);
+			}
+			scrollingX = mRenderW - textWidth;
+			mInputText->SkipCharCount(skipChars);
+		} else if (x == -1001) {
+			// Added a new character
+			int adjust_scrollingX = 0;
+			string cursorLocate;
+
+			cursorLocate = displayValue;
+			cursorLocate.resize(mCursorLocation);
+			textWidth = gr_measureEx(cursorLocate.c_str(), fontResource);
+			while (textWidth > mRenderW) {
+				skipChars++;
+				mCursorLocation--;
+				cursorLocate.erase(0, 1);
+				textWidth = gr_measureEx(cursorLocate.c_str(), fontResource);
+				adjust_scrollingX = -1;
+			}
+			if (adjust_scrollingX) {
+				scrollingX = mRenderW - textWidth;
+				if (scrollingX < 0)
+					scrollingX = 0;
+			}
+			mInputText->SkipCharCount(skipChars);
+		} else if (x == -1002) {
+			// Deleted a character
+			while (-1) {
+				if (skipChars == 0) {
+					scrollingX = 0;
+					mInputText->SkipCharCount(skipChars);
+					return 0;
+				}
+				insertChar = originalValue.substr(skipChars - 1, 1);
+				displayValue.insert(0, insertChar);
+				newWidth = gr_measureEx(displayValue.c_str(), fontResource);
+				deltaText = newWidth - textWidth;
+				if (newWidth > mRenderW) {
+					scrollingX = mRenderW - textWidth;
+					if (scrollingX < 0)
+						scrollingX = 0;
+					mInputText->SkipCharCount(skipChars);
+					return 0;
+				} else {
+					textWidth = newWidth;
+					skipChars--;
+					mCursorLocation++;
+				}
+			}
+		} else
+			LOGINFO("GUIInput::HandleTextLocation -> We really shouldn't ever get here...\n");
+	} else if (x > lastX) {
+		// Dragging to right, scrolling left
+		while (-1) {
+			deltaX = x - lastX + scrollingX;
+			if (skipChars == 0 || deltaX == 0) {
+				scrollingX = 0;
+				lastX = x;
+				mInputText->SkipCharCount(skipChars);
+				return 0;
+			}
+			insertChar = originalValue.substr(skipChars - 1, 1);
+			displayValue.insert(0, insertChar);
+			newWidth = gr_measureEx(displayValue.c_str(), fontResource);
+			deltaText = newWidth - textWidth;
+			if (deltaText < deltaX) {
+				lastX += deltaText;
+				textWidth = newWidth;
+				skipChars--;
+			} else {
+				scrollingX = deltaX;
+				lastX = x;
+				mInputText->SkipCharCount(skipChars);
+				return 0;
+			}
+		}
+	} else if (x < lastX) {
+		// Dragging to left, scrolling right
+		if (textWidth <= mRenderW) {
+			lastX = x;
+			scrollingX = mRenderW - textWidth;
+			return 0;
+		}
+		if (scrollingX) {
+			deltaX = lastX - x;
+			if (scrollingX > deltaX) {
+				scrollingX -= deltaX;
+				lastX = x;
+				return 0;
+			} else {
+				lastX -= deltaX;
+				scrollingX = 0;
+			}
+		}
+		while (-1) {
+			deltaX = lastX - x;
+			displayValue.erase(0, 1);
+			skipChars++;
+			newWidth = gr_measureEx(displayValue.c_str(), fontResource);
+			deltaText = textWidth - newWidth;
+			if (newWidth <= mRenderW) {
+				scrollingX = mRenderW - newWidth;
+				lastX = x;
+				mInputText->SkipCharCount(skipChars);
+				return 0;
+			}
+			if (deltaText < deltaX) {
+				lastX -= deltaText;
+				textWidth = newWidth;
+			} else {
+				scrollingX = deltaText - deltaX;
+				lastX = x;
+				mInputText->SkipCharCount(skipChars);
+				return 0;
+			}
+		}
+	}
+	return 0;
+}
+
+int GUIInput::Render(void)
+{
+	if (!isConditionTrue())
+	{
+		mRendered = false;
+		return 0;
+	}
+
+	void* fontResource = NULL;
+	if (mFont)  fontResource = mFont->GetResource();
+
+	// First step, fill background
+	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
+	gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
+
+	// Next, render the background resource (if it exists)
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
+		mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
+		gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
+	}
+
+	int ret = 0;
+
+	// Render the text
+	mInputText->SetRenderPos(mRenderX + scrollingX, mFontY);
+	mInputText->SetMaxWidth(mRenderW - scrollingX);
+	if (mInputText)	 ret = mInputText->Render();
+	if (ret < 0)		return ret;
+
+	if (HasInputFocus && DrawCursor) {
+		// Render the cursor
+		string displayValue;
+		int cursorX;
+		DataManager::GetValue(mVariable, displayValue);
+		if (HasMask) {
+			int index, string_size = displayValue.size();
+			string maskedValue;
+			for (index=0; index<string_size; index++)
+				maskedValue += mMask;
+			displayValue = maskedValue;
+		}
+		if (displayValue.size() == 0) {
+			skipChars = 0;
+			mCursorLocation = -1;
+			cursorX = mRenderX;
+		} else {
+			if (skipChars && skipChars < displayValue.size()) {
+				displayValue.erase(0, skipChars);
+			}
+			if (mCursorLocation == 0) {
+				// Cursor is at the beginning
+				cursorX = mRenderX;
+			} else if (mCursorLocation > 0) {
+				// Cursor is in the middle
+				if (displayValue.size() > (unsigned)mCursorLocation) {
+					string cursorDisplay;
+
+					cursorDisplay = displayValue;
+					cursorDisplay.resize(mCursorLocation);
+					cursorX = gr_measureEx(cursorDisplay.c_str(), fontResource) + mRenderX;
+				} else {
+					// Cursor location is after the end of the text  - reset to -1
+					mCursorLocation = -1;
+					cursorX = gr_measureEx(displayValue.c_str(), fontResource) + mRenderX;
+				}
+			} else {
+				// Cursor is at the end (-1)
+				cursorX = gr_measureEx(displayValue.c_str(), fontResource) + mRenderX;
+			}
+		}
+		cursorX += scrollingX;
+		// Make sure that the cursor doesn't go past the boundaries of the box
+		if (cursorX + (int)CursorWidth > mRenderX + mRenderW)
+			cursorX = mRenderX + mRenderW - CursorWidth;
+
+		// Set the color for the cursor
+		gr_color(mCursorColor.red, mCursorColor.green, mCursorColor.blue, 255);
+		gr_fill(cursorX, mFontY, CursorWidth, mFontHeight);
+	}
+
+	mRendered = true;
+	return ret;
+}
+
+int GUIInput::Update(void)
+{
+	if (!isConditionTrue())	 return (mRendered ? 2 : 0);
+	if (!mRendered)			 return 2;
+
+	int ret = 0;
+
+	if (mInputText)		 ret = mInputText->Update();
+	if (ret < 0)			return ret;
+
+	return ret;
+}
+
+int GUIInput::GetSelection(int x, int y)
+{
+	if (x < mRenderX || x - mRenderX > mRenderW || y < mRenderY || y - mRenderY > mRenderH) return -1;
+	return (x - mRenderX);
+}
+
+int GUIInput::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	static int startSelection = -1;
+	int textWidth;
+	string displayValue, originalValue;
+	void* fontResource = NULL;
+
+	if (mFont)  fontResource = mFont->GetResource();
+
+	if (!isConditionTrue())
+		return -1;
+
+	if (!HasInputFocus) {
+		if (state != TOUCH_RELEASE)
+			return 0; // Only change focus if touch releases within the input box
+		if (GetSelection(x, y) >= 0) {
+			// When changing focus, we don't scroll or change the cursor location
+			PageManager::SetKeyBoardFocus(0);
+			PageManager::NotifyKeyboard(0);
+			SetInputFocus(1);
+			DrawCursor = true;
+			mRendered = false;
+		}
+	} else {
+		switch (state) {
+		case TOUCH_HOLD:
+		case TOUCH_REPEAT:
+			break;
+		case TOUCH_START:
+			startSelection = GetSelection(x,y);
+			lastX = x;
+			DrawCursor = false;
+			mRendered = false;
+			break;
+
+		case TOUCH_DRAG:
+			// Check if we dragged out of the selection window
+			if (GetSelection(x, y) == -1) {
+				lastX = 0;
+				break;
+			}
+
+			DrawCursor = false;
+
+			// Provide some debounce on initial touches
+			if (startSelection != -1 && abs(x - lastX) < 6) {
+				break;
+			}
+
+			startSelection = -1;
+			if (lastX != x)
+				HandleTextLocation(x);
+			break;
+
+		case TOUCH_RELEASE:
+			// We've moved the cursor location
+			int relativeX = x - mRenderX;
+
+			mRendered = false;
+			DrawCursor = true;
+			DataManager::GetValue(mVariable, displayValue);
+			if (HasMask) {
+				int index, string_size = displayValue.size();
+				string maskedValue;
+				for (index=0; index<string_size; index++)
+					maskedValue += mMask;
+				displayValue = maskedValue;
+			}
+			if (displayValue.size() == 0) {
+				skipChars = 0;
+				mCursorLocation = -1;
+				return 0;
+			} else if (skipChars && skipChars < displayValue.size()) {
+				displayValue.erase(0, skipChars);
+			}
+
+			string cursorString;
+			int cursorX = 0;
+			unsigned index = 0;
+
+			for(index=0; index<displayValue.size(); index++)
+			{
+				cursorString = displayValue.substr(0, index);
+				cursorX = gr_measureEx(cursorString.c_str(), fontResource) + mRenderX;
+				if (cursorX > x) {
+					if (index > 0)
+						mCursorLocation = index - 1;
+					else
+						mCursorLocation = index;
+					return 0;
+				}
+			}
+			mCursorLocation = -1;
+			break;
+		}
+	}
+	return 0;
+}
+
+int GUIInput::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIObject::NotifyVarChange(varName, value);
+
+	if (varName == mVariable && !isLocalChange) {
+		HandleTextLocation(-1003);
+		return 0;
+	}
+	return 0;
+}
+
+int GUIInput::NotifyKeyboard(int key)
+{
+	string variableValue;
+
+	if (HasInputFocus) {
+		if (key == KEYBOARD_BACKSPACE) {
+			//Backspace
+			DataManager::GetValue(mVariable, variableValue);
+			if (variableValue.size() > 0 && (mCursorLocation + skipChars != 0 || mCursorLocation == -1)) {
+				if (mCursorLocation == -1) {
+					variableValue.resize(variableValue.size() - 1);
+				} else {
+					variableValue.erase(mCursorLocation + skipChars - 1, 1);
+					if (mCursorLocation > 0)
+						mCursorLocation--;
+					else if (skipChars > 0)
+						skipChars--;
+				}
+				isLocalChange = true;
+				DataManager::SetValue(mVariable, variableValue);
+				isLocalChange = false;
+
+				if (HasMask) {
+					int index, string_size = variableValue.size();
+					string maskedValue;
+					for (index=0; index<string_size; index++)
+						maskedValue += mMask;
+					DataManager::SetValue(mMaskVariable, maskedValue);
+				}
+				HandleTextLocation(-1002);
+			}
+		} else if (key == KEYBOARD_SWIPE_LEFT) {
+			// Delete all
+			isLocalChange = true;
+			if (mCursorLocation == -1) {
+				DataManager::SetValue (mVariable, "");
+				if (HasMask)
+					DataManager::SetValue(mMaskVariable, "");
+				mCursorLocation = -1;
+			} else {
+				DataManager::GetValue(mVariable, variableValue);
+				variableValue.erase(0, mCursorLocation + skipChars);
+				DataManager::SetValue(mVariable, variableValue);
+				if (HasMask) {
+					DataManager::GetValue(mMaskVariable, variableValue);
+					variableValue.erase(0, mCursorLocation + skipChars);
+					DataManager::SetValue(mMaskVariable, variableValue);
+				}
+				mCursorLocation = 0;
+			}
+			skipChars = 0;
+			scrollingX = 0;
+			mInputText->SkipCharCount(skipChars);
+			isLocalChange = false;
+			mRendered = false;
+			return 0;
+		} else if (key == KEYBOARD_ARROW_LEFT) {
+			if (mCursorLocation == 0 && skipChars == 0)
+				return 0; // we're already at the beginning
+			if (mCursorLocation == -1) {
+				DataManager::GetValue(mVariable, variableValue);
+				if (variableValue.size() == 0)
+					return 0;
+				mCursorLocation = variableValue.size() - skipChars - 1;
+			} else if (mCursorLocation == 0) {
+				skipChars--;
+				HandleTextLocation(-1002);
+			} else {
+				mCursorLocation--;
+				HandleTextLocation(-1002);
+			}
+			mRendered = false;
+			return 0;
+		} else if (key == KEYBOARD_ARROW_RIGHT) {
+			if (mCursorLocation == -1)
+				return 0; // we're already at the end
+			mCursorLocation++;
+			DataManager::GetValue(mVariable, variableValue);
+			if (variableValue.size() <= mCursorLocation + skipChars)
+				mCursorLocation = -1;
+			HandleTextLocation(-1001);
+			mRendered = false;
+			return 0;
+		} else if (key == KEYBOARD_HOME || key == KEYBOARD_ARROW_UP) {
+			DataManager::GetValue(mVariable, variableValue);
+			if (variableValue.size() == 0)
+				return 0;
+			mCursorLocation = 0;
+			skipChars = 0;
+			mRendered = false;
+			HandleTextLocation(-1002);
+			return 0;
+		} else if (key == KEYBOARD_END || key == KEYBOARD_ARROW_DOWN) {
+			mCursorLocation = -1;
+			mRendered = false;
+			HandleTextLocation(-1003);
+			return 0;
+		} else if (key < KEYBOARD_SPECIAL_KEYS && key > 0) {
+			// Regular key
+			if (HasAllowed && AllowedList.find((char)key) == string::npos) {
+				return 0;
+			}
+			if (HasDisabled && DisabledList.find((char)key) != string::npos) {
+				return 0;
+			}
+			DataManager::GetValue(mVariable, variableValue);
+			if (MaxLen != 0 && variableValue.size() >= MaxLen) {
+				return 0;
+			}
+			if (mCursorLocation == -1) {
+				variableValue += key;
+			} else {
+				const char newchar = (char)key;
+				const char* a = &newchar;
+				string newstring = a;
+				newstring.resize(1);
+				variableValue.insert(mCursorLocation + skipChars, newstring);
+				mCursorLocation++;
+			}
+			isLocalChange = true;
+			DataManager::SetValue(mVariable, variableValue);
+			HandleTextLocation(-1001);
+			isLocalChange = false;
+
+			if (HasMask) {
+				int index, string_size = variableValue.size();
+				string maskedValue;
+				for (index=0; index<string_size; index++)
+					maskedValue += mMask;
+				DataManager::SetValue(mMaskVariable, maskedValue);
+			}
+		} else if (key == KEYBOARD_ACTION) {
+			// Action
+			DataManager::GetValue(mVariable, variableValue);
+			if (mAction) {
+				unsigned inputLen = variableValue.length();
+				if (inputLen < MinLen)
+					return 0;
+				else if (MaxLen != 0 && inputLen > MaxLen)
+					return 0;
+				else
+					return (mAction ? mAction->NotifyTouch(TOUCH_RELEASE, mRenderX, mRenderY) : 1);
+			}
+		}
+		return 0;
+	} else {
+		if (key == 0) {
+			// Somewhat ugly hack-ish way to tell the box to redraw after losing focus to remove the cursor
+			mRendered = false;
+			return 1;
+		}
+	}
+	return 1;
+}
diff --git a/gui/keyboard.cpp b/gui/keyboard.cpp
new file mode 100644
index 0000000..bb52d55
--- /dev/null
+++ b/gui/keyboard.cpp
@@ -0,0 +1,578 @@
+/*
+        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "../data.hpp"
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUIKeyboard::GUIKeyboard(xml_node<>* node)
+	: GUIObject(node)
+{
+	int layoutindex, rowindex, keyindex, Xindex, Yindex, keyHeight = 0, keyWidth = 0;
+	rowY = colX = -1;
+	highlightRenderCount = hasHighlight = hasCapsHighlight = 0;
+	char resource[10], layout[8], row[5], key[6], longpress[7];
+	xml_attribute<>* attr;
+	xml_node<>* child;
+	xml_node<>* keylayout;
+	xml_node<>* keyrow;
+
+	for (layoutindex=0; layoutindex<MAX_KEYBOARD_LAYOUTS; layoutindex++)
+		keyboardImg[layoutindex] = NULL;
+
+	mRendered = false;
+	currentLayout = 1;
+	mAction = NULL;
+	KeyboardHeight = KeyboardWidth = cursorLocation = 0;
+
+	if (!node)  return;
+
+	// Load the action
+	child = node->first_node("action");
+	if (child)
+	{
+		mAction = new GUIAction(node);
+	}
+
+	memset(&mHighlightColor, 0, sizeof(COLOR));
+	child = node->first_node("highlight");
+	if (child) {
+		attr = child->first_attribute("color");
+		if (attr) {
+			hasHighlight = 1;
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHighlightColor);
+		}
+	}
+
+	memset(&mCapsHighlightColor, 0, sizeof(COLOR));
+	child = node->first_node("capshighlight");
+	if (child) {
+		attr = child->first_attribute("color");
+		if (attr) {
+			hasCapsHighlight = 1;
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mCapsHighlightColor);
+		}
+	}
+
+	// Load the images for the different layouts
+	child = node->first_node("layout");
+	if (child)
+	{
+		layoutindex = 1;
+		strcpy(resource, "resource1");
+		attr = child->first_attribute(resource);
+		while (attr && layoutindex < (MAX_KEYBOARD_LAYOUTS + 1)) {
+			keyboardImg[layoutindex - 1] = PageManager::FindResource(attr->value());
+
+			layoutindex++;
+			resource[8] = (char)(layoutindex + 48);
+			attr = child->first_attribute(resource);
+		}
+	}
+
+	// Check the first image to get height and width
+	if (keyboardImg[0] && keyboardImg[0]->GetResource())
+	{
+		KeyboardWidth = gr_get_width(keyboardImg[0]->GetResource());
+		KeyboardHeight = gr_get_height(keyboardImg[0]->GetResource());
+	}
+
+	// Load all of the layout maps
+	layoutindex = 1;
+	strcpy(layout, "layout1");
+	keylayout = node->first_node(layout);
+	while (keylayout)
+	{
+		if (layoutindex > MAX_KEYBOARD_LAYOUTS) {
+			LOGERR("Too many layouts defined in keyboard.\n");
+			return;
+		}
+
+		child = keylayout->first_node("keysize");
+		if (child) {
+			attr = child->first_attribute("height");
+			if (attr)
+				keyHeight = atoi(attr->value());
+			else
+				keyHeight = 0;
+			attr = child->first_attribute("width");
+			if (attr)
+				keyWidth = atoi(attr->value());
+			else
+				keyWidth = 0;
+			attr = child->first_attribute("capslock");
+			if (attr)
+				caps_tracking[layoutindex - 1].capslock = atoi(attr->value());
+			else
+				caps_tracking[layoutindex - 1].capslock = 1;
+			attr = child->first_attribute("revert_layout");
+			if (attr)
+				caps_tracking[layoutindex - 1].revert_layout = atoi(attr->value());
+			else
+				caps_tracking[layoutindex - 1].revert_layout = -1;
+		}
+
+		rowindex = 1;
+		Yindex = 0;
+		strcpy(row, "row1");
+		keyrow = keylayout->first_node(row);
+		while (keyrow)
+		{
+			if (rowindex > MAX_KEYBOARD_ROWS) {
+				LOGERR("Too many rows defined in keyboard.\n");
+				return;
+			}
+
+			Yindex += keyHeight;
+			row_heights[layoutindex - 1][rowindex - 1] = Yindex;
+
+			keyindex = 1;
+			Xindex = 0;
+			strcpy(key, "key01");
+			attr = keyrow->first_attribute(key);
+
+			while (attr) {
+				string stratt;
+				char keyinfo[255];
+
+				if (keyindex > MAX_KEYBOARD_KEYS) {
+					LOGERR("Too many keys defined in a keyboard row.\n");
+					return;
+				}
+
+				stratt = attr->value();
+				if (strlen(stratt.c_str()) >= 255) {
+					LOGERR("Key info on layout%i, row%i, key%dd is too long.\n", layoutindex, rowindex, keyindex);
+					return;
+				}
+
+				strcpy(keyinfo, stratt.c_str());
+
+				if (strlen(keyinfo) == 0) {
+					LOGERR("No key info on layout%i, row%i, key%dd.\n", layoutindex, rowindex, keyindex);
+					return;
+				}
+
+				if (strlen(keyinfo) == 1) {
+					// This is a single key, simple definition
+					keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].key = keyinfo[0];
+					Xindex += keyWidth;
+					keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].end_x = Xindex - 1;
+				} else {
+					// This key has extra data
+					char* ptr;
+					char* offset;
+					char* keyitem;
+					char foratoi[10];
+
+					ptr = keyinfo;
+					offset = keyinfo;
+					while (*ptr > 32 && *ptr != ':')
+						ptr++;
+					if (*ptr != 0)
+						*ptr = 0;
+
+					strcpy(foratoi, offset);
+					Xindex += atoi(foratoi);
+					keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].end_x = Xindex - 1;
+
+					ptr++;
+					if (*ptr == 0) {
+						// This is an empty area
+						keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].key = 0;
+					} else if (strlen(ptr) == 1) {
+						// This is the character that this key uses
+						keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].key = *ptr;
+					} else if (*ptr == 'c') {
+						// This is an ASCII character code
+						keyitem = ptr + 2;
+						strcpy(foratoi, keyitem);
+						keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].key = atoi(foratoi);
+					} else if (*ptr == 'l') {
+						// This is a different layout
+						keyitem = ptr + 6;
+						keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].key = KEYBOARD_LAYOUT;
+						strcpy(foratoi, keyitem);
+						keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].layout = atoi(foratoi);
+					} else if (*ptr == 'a') {
+						// This is an action
+						keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].key = KEYBOARD_ACTION;
+					} else
+						LOGERR("Invalid key info on layout%i, row%i, key%02i.\n", layoutindex, rowindex, keyindex);
+				}
+
+				// PROCESS LONG PRESS INFO IF EXISTS
+				sprintf(longpress, "long%02i", keyindex);
+				attr = keyrow->first_attribute(longpress);
+				if (attr) {
+					stratt = attr->value();
+					if (strlen(stratt.c_str()) >= 255) {
+						LOGERR("Key info on layout%i, row%i, key%dd is too long.\n", layoutindex, rowindex, keyindex);
+						return;
+					}
+
+					strcpy(keyinfo, stratt.c_str());
+
+					if (strlen(keyinfo) == 0) {
+						LOGERR("No long press info on layout%i, row%i, long%dd.\n", layoutindex, rowindex, keyindex);
+						return;
+					}
+
+					if (strlen(keyinfo) == 1) {
+						// This is a single key, simple definition
+						keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].longpresskey = keyinfo[0];
+					} else {
+						// This key has extra data
+						char* ptr;
+						char* offset;
+						char* keyitem;
+						char foratoi[10];
+
+						ptr = keyinfo;
+						offset = keyinfo;
+						while (*ptr > 32 && *ptr != ':')
+							ptr++;
+						if (*ptr != 0)
+							*ptr = 0;
+
+						strcpy(foratoi, offset);
+						Xindex += atoi(foratoi);
+
+						ptr++;
+						if (*ptr == 0) {
+							// This is an empty area
+							keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].longpresskey = 0;
+						} else if (strlen(ptr) == 1) {
+							// This is the character that this key uses
+							keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].longpresskey = *ptr;
+						} else if (*ptr == 'c') {
+							// This is an ASCII character code
+							keyitem = ptr + 2;
+							strcpy(foratoi, keyitem);
+							keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].longpresskey = atoi(foratoi);
+						} else if (*ptr == 'l') {
+							// This is a different layout
+							keyitem = ptr + 6;
+							keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].longpresskey = KEYBOARD_LAYOUT;
+							strcpy(foratoi, keyitem);
+							keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].layout = atoi(foratoi);
+						} else if (*ptr == 'a') {
+							// This is an action
+							keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1].longpresskey = KEYBOARD_ACTION;
+						} else
+							LOGERR("Invalid long press key info on layout%i, row%i, long%02i.\n", layoutindex, rowindex, keyindex);
+					}
+				}
+				keyindex++;
+				sprintf(key, "key%02i", keyindex);
+				attr = keyrow->first_attribute(key);
+			}
+			rowindex++;
+			row[3] = (char)(rowindex + 48);
+			keyrow = keylayout->first_node(row);
+		}
+		layoutindex++;
+		layout[6] = (char)(layoutindex + 48);
+		keylayout = node->first_node(layout);
+	}
+
+	int x, y, w, h;
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &x, &y, &w, &h);
+	SetActionPos(x, y, KeyboardWidth, KeyboardHeight);
+	SetRenderPos(x, y, w, h);
+	return;
+}
+
+GUIKeyboard::~GUIKeyboard()
+{
+
+}
+
+int GUIKeyboard::Render(void)
+{
+	if (!isConditionTrue())
+	{
+		mRendered = false;
+		return 0;
+	}
+
+	int ret = 0;
+
+	if (keyboardImg[currentLayout - 1] && keyboardImg[currentLayout - 1]->GetResource())
+		gr_blit(keyboardImg[currentLayout - 1]->GetResource(), 0, 0, KeyboardWidth, KeyboardHeight, mRenderX, mRenderY);
+
+	// Draw highlight for capslock
+	if (hasCapsHighlight && caps_tracking[currentLayout - 1].capslock == 0 && caps_tracking[currentLayout - 1].set_capslock) {
+		int boxheight, boxwidth, x;
+		gr_color(mCapsHighlightColor.red, mCapsHighlightColor.green, mCapsHighlightColor.blue, mCapsHighlightColor.alpha);
+		for (int indexy=0; indexy<MAX_KEYBOARD_ROWS; indexy++) {
+			for (int indexx=0; indexx<MAX_KEYBOARD_KEYS; indexx++) {
+				if ((int)keyboard_keys[currentLayout - 1][indexy][indexx].key == KEYBOARD_LAYOUT && (int)keyboard_keys[currentLayout - 1][indexy][indexx].layout == caps_tracking[currentLayout - 1].revert_layout) {
+					if (indexy == 0)
+						boxheight = row_heights[currentLayout - 1][indexy];
+					else
+						boxheight = row_heights[currentLayout - 1][indexy] - row_heights[currentLayout - 1][indexy - 1];
+					if (indexx == 0) {
+						x = mRenderX;
+						boxwidth = keyboard_keys[currentLayout - 1][indexy][indexx].end_x;
+					} else {
+						x = mRenderX + keyboard_keys[currentLayout - 1][indexy][indexx - 1].end_x;
+						boxwidth = keyboard_keys[currentLayout - 1][indexy][indexx].end_x - keyboard_keys[currentLayout - 1][indexy][indexx - 1].end_x;
+					}
+					gr_fill(x, mRenderY + row_heights[currentLayout - 1][indexy - 1], boxwidth, boxheight);
+				}
+			}
+		}
+	}
+
+	if (hasHighlight && highlightRenderCount != 0) {
+		int boxheight, boxwidth, x;
+		if (rowY == 0)
+			boxheight = row_heights[currentLayout - 1][rowY];
+		else
+			boxheight = row_heights[currentLayout - 1][rowY] - row_heights[currentLayout - 1][rowY - 1];
+		if (colX == 0) {
+			x = mRenderX;
+			boxwidth = keyboard_keys[currentLayout - 1][rowY][colX].end_x;
+		} else {
+			x = mRenderX + keyboard_keys[currentLayout - 1][rowY][colX - 1].end_x;
+			boxwidth = keyboard_keys[currentLayout - 1][rowY][colX].end_x - keyboard_keys[currentLayout - 1][rowY][colX - 1].end_x;
+		}
+		gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
+		gr_fill(x, mRenderY + row_heights[currentLayout - 1][rowY - 1], boxwidth, boxheight);
+		if (highlightRenderCount > 0)
+			highlightRenderCount--;
+	} else
+		mRendered = true;
+
+	return ret;
+}
+
+int GUIKeyboard::Update(void)
+{
+	if (!isConditionTrue())	 return (mRendered ? 2 : 0);
+	if (!mRendered)			 return 2;
+
+	return 0;
+}
+
+int GUIKeyboard::SetRenderPos(int x, int y, int w, int h)
+{
+	mRenderX = x;
+	mRenderY = y;
+	if (w || h)
+	{
+		mRenderW = KeyboardWidth;
+		mRenderH = KeyboardHeight;
+	}
+
+	if (mAction)		mAction->SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	return 0;
+}
+
+int GUIKeyboard::GetSelection(int x, int y)
+{
+	if (x < mRenderX || x - mRenderX > (int)KeyboardWidth || y < mRenderY || y - mRenderY > (int)KeyboardHeight) return -1;
+	return 0;
+}
+
+int GUIKeyboard::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	static int startSelection = -1, was_held = 0, startX = 0;
+	static unsigned char initial_key = 0;
+	unsigned int indexy, indexx, rely, relx, rowIndex = 0;
+
+	rely = y - mRenderY;
+	relx = x - mRenderX;
+
+	if (!isConditionTrue())	 return -1;
+
+	switch (state)
+	{
+	case TOUCH_START:
+		if (GetSelection(x, y) == 0) {
+			startSelection = -1;
+			was_held = 0;
+			startX = x;
+			// Find the correct row
+			for (indexy=0; indexy<MAX_KEYBOARD_ROWS; indexy++) {
+				if (row_heights[currentLayout - 1][indexy] > rely) {
+					rowIndex = indexy;
+					rowY = indexy;
+					indexy = MAX_KEYBOARD_ROWS;
+				}
+			}
+
+			// Find the correct key (column)
+			for (indexx=0; indexx<MAX_KEYBOARD_KEYS; indexx++) {
+				if (keyboard_keys[currentLayout - 1][rowIndex][indexx].end_x > relx) {
+					// This is the key that was pressed!
+					initial_key = keyboard_keys[currentLayout - 1][rowIndex][indexx].key;
+					colX = indexx;
+					indexx = MAX_KEYBOARD_KEYS;
+				}
+			}
+			if (initial_key != 0)
+				highlightRenderCount = -1;
+			else
+				highlightRenderCount = 0;
+			mRendered = false;
+		} else {
+			if (highlightRenderCount != 0)
+				mRendered = false;
+			highlightRenderCount = 0;
+			startSelection = 0;
+		}
+		break;
+	case TOUCH_DRAG:
+		break;
+	case TOUCH_RELEASE:
+		if (x < startX - (KeyboardWidth * 0.5)) {
+			if (highlightRenderCount != 0) {
+				highlightRenderCount = 0;
+				mRendered = false;
+			}
+			PageManager::NotifyKeyboard(KEYBOARD_SWIPE_LEFT);
+			return 0;
+		} else if (x > startX + (KeyboardWidth * 0.5)) {
+			if (highlightRenderCount != 0) {
+				highlightRenderCount = 0;
+				mRendered = false;
+			}
+			PageManager::NotifyKeyboard(KEYBOARD_SWIPE_RIGHT);
+			return 0;
+		}
+
+	case TOUCH_HOLD:
+	case TOUCH_REPEAT:
+
+		if (startSelection == 0 || GetSelection(x, y) == -1) {
+			if (highlightRenderCount != 0) {
+				highlightRenderCount = 0;
+				mRendered = false;
+			}
+			return 0;
+		} else if (highlightRenderCount != 0) {
+			if (state == TOUCH_RELEASE)
+				highlightRenderCount = 2;
+			else
+				highlightRenderCount = -1;
+			mRendered = false;
+		}
+
+		// Find the correct row
+		for (indexy=0; indexy<MAX_KEYBOARD_ROWS; indexy++) {
+			if (row_heights[currentLayout - 1][indexy] > rely) {
+				rowIndex = indexy;
+				indexy = MAX_KEYBOARD_ROWS;
+			}
+		}
+
+		// Find the correct key (column)
+		for (indexx=0; indexx<MAX_KEYBOARD_KEYS; indexx++) {
+			if (keyboard_keys[currentLayout - 1][rowIndex][indexx].end_x > relx) {
+				// This is the key that was pressed!
+				if (keyboard_keys[currentLayout - 1][rowIndex][indexx].key != initial_key) {
+					// We dragged off of the starting key
+					startSelection = 0;
+					break;
+				} else if (state == TOUCH_RELEASE && was_held == 0) {
+					DataManager::Vibrate("tw_keyboard_vibrate");
+					if ((int)keyboard_keys[currentLayout - 1][rowIndex][indexx].key < KEYBOARD_SPECIAL_KEYS && (int)keyboard_keys[currentLayout - 1][rowIndex][indexx].key > 0) {
+						// Regular key
+						PageManager::NotifyKeyboard(keyboard_keys[currentLayout - 1][rowIndex][indexx].key);
+						if (caps_tracking[currentLayout - 1].capslock == 0 && !caps_tracking[currentLayout - 1].set_capslock) {
+							// caps lock was not set, change layouts
+							currentLayout = caps_tracking[currentLayout - 1].revert_layout;
+							mRendered = false;
+						}
+					} else if ((int)keyboard_keys[currentLayout - 1][rowIndex][indexx].key == KEYBOARD_LAYOUT) {
+						// Switch layouts
+						if (caps_tracking[currentLayout - 1].capslock == 0 && keyboard_keys[currentLayout - 1][rowIndex][indexx].layout == caps_tracking[currentLayout - 1].revert_layout) {
+							if (!caps_tracking[currentLayout - 1].set_capslock) {
+								caps_tracking[currentLayout - 1].set_capslock = 1; // Set the caps lock
+							} else {
+								caps_tracking[currentLayout - 1].set_capslock = 0; // Unset the caps lock and change layouts
+								currentLayout = keyboard_keys[currentLayout - 1][rowIndex][indexx].layout;
+							}
+						} else {
+							currentLayout = keyboard_keys[currentLayout - 1][rowIndex][indexx].layout;
+						}
+						mRendered = false;
+					} else if ((int)keyboard_keys[currentLayout - 1][rowIndex][indexx].key == KEYBOARD_ACTION) {
+						// Action
+						highlightRenderCount = 0;
+						if (mAction) {
+							// Keyboard has its own action defined
+							return (mAction ? mAction->NotifyTouch(state, x, y) : 1);
+						} else {
+							// Send action notification
+							PageManager::NotifyKeyboard(keyboard_keys[currentLayout - 1][rowIndex][indexx].key);
+						}
+					}
+				} else if (state == TOUCH_HOLD) {
+					was_held = 1;
+					if ((int)keyboard_keys[currentLayout - 1][rowIndex][indexx].key == KEYBOARD_BACKSPACE) {
+						// Repeat backspace
+						PageManager::NotifyKeyboard(keyboard_keys[currentLayout - 1][rowIndex][indexx].key);
+					} else if ((int)keyboard_keys[currentLayout - 1][rowIndex][indexx].longpresskey < KEYBOARD_SPECIAL_KEYS && (int)keyboard_keys[currentLayout - 1][rowIndex][indexx].longpresskey > 0) {
+						// Long Press Key
+						DataManager::Vibrate("tw_keyboard_vibrate");
+						PageManager::NotifyKeyboard(keyboard_keys[currentLayout - 1][rowIndex][indexx].longpresskey);
+					}
+				} else if (state == TOUCH_REPEAT) {
+					was_held = 1;
+					if ((int)keyboard_keys[currentLayout - 1][rowIndex][indexx].key == KEYBOARD_BACKSPACE) {
+						// Repeat backspace
+						PageManager::NotifyKeyboard(keyboard_keys[currentLayout - 1][rowIndex][indexx].key);
+					}
+				}
+				indexx = MAX_KEYBOARD_KEYS;
+			}
+		}
+		break;
+	}
+
+	return 0;
+}
diff --git a/gui/listbox.cpp b/gui/listbox.cpp
new file mode 100644
index 0000000..851b373
--- /dev/null
+++ b/gui/listbox.cpp
@@ -0,0 +1,833 @@
+/*
+	Copyright 2013 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <linux/input.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include <algorithm>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+#include "../twrp-functions.hpp"
+
+#define SCROLLING_SPEED_DECREMENT 6
+#define SCROLLING_FLOOR 10
+#define SCROLLING_MULTIPLIER 6
+
+GUIListBox::GUIListBox(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+	int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
+
+	mStart = mLineSpacing = startY = mFontHeight = mSeparatorH = scrollingY = scrollingSpeed = 0;
+	mIconWidth = mIconHeight = mSelectedIconHeight = mSelectedIconWidth = mUnselectedIconHeight = mUnselectedIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0;
+	mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0;
+	mIconSelected = mIconUnselected = mBackground = mFont = mHeaderIcon = NULL;
+	mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0;
+	mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
+	mFastScrollRectX = mFastScrollRectY = -1;
+	mUpdate = 0;
+	touchDebounce = 6;
+	ConvertStrToColor("black", &mBackgroundColor);
+	ConvertStrToColor("black", &mHeaderBackgroundColor);
+	ConvertStrToColor("black", &mSeparatorColor);
+	ConvertStrToColor("black", &mHeaderSeparatorColor);
+	ConvertStrToColor("white", &mFontColor);
+	ConvertStrToColor("white", &mHeaderFontColor);
+	ConvertStrToColor("white", &mFastScrollLineColor);
+	ConvertStrToColor("white", &mFastScrollRectColor);
+	hasHighlightColor = false;
+	hasFontHighlightColor = false;
+	isHighlighted = false;
+	startSelection = -1;
+
+	// Load header text
+	child = node->first_node("header");
+	if (child)
+	{
+		attr = child->first_attribute("icon");
+		if (attr)
+			mHeaderIcon = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("background");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderBackgroundColor);
+			header_background_color_specified = -1;
+		}
+		attr = child->first_attribute("textcolor");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderFontColor);
+			header_text_color_specified = -1;
+		}
+		attr = child->first_attribute("separatorcolor");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderSeparatorColor);
+			header_separator_color_specified = -1;
+		}
+		attr = child->first_attribute("separatorheight");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mHeaderSeparatorH = atoi(parsevalue.c_str());
+			header_separator_height_specified = -1;
+		}
+	}
+	child = node->first_node("text");
+	if (child)  mHeaderText = child->value();
+
+	memset(&mHighlightColor, 0, sizeof(COLOR));
+	child = node->first_node("highlight");
+	if (child) {
+		attr = child->first_attribute("color");
+		if (attr) {
+			hasHighlightColor = true;
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHighlightColor);
+		}
+	}
+
+	// Simple way to check for static state
+	mLastValue = gui_parse_text(mHeaderText);
+	if (mLastValue != mHeaderText)
+		mHeaderIsStatic = 0;
+	else
+		mHeaderIsStatic = -1;
+
+	child = node->first_node("icon");
+	if (child)
+	{
+		attr = child->first_attribute("selected");
+		if (attr)
+			mIconSelected = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("unselected");
+		if (attr)
+			mIconUnselected = PageManager::FindResource(attr->value());
+	}
+	child = node->first_node("background");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mBackground = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mBackgroundColor);
+			if (!header_background_color_specified)
+				ConvertStrToColor(color, &mHeaderBackgroundColor);
+		}
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+
+	// Load the font, and possibly override the color
+	child = node->first_node("font");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mFont = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mFontColor);
+			if (!header_text_color_specified)
+				ConvertStrToColor(color, &mHeaderFontColor);
+		}
+
+		attr = child->first_attribute("spacing");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mLineSpacing = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("highlightcolor");
+		memset(&mFontHighlightColor, 0, sizeof(COLOR));
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mFontHighlightColor);
+			hasFontHighlightColor = true;
+		}
+	}
+
+	// Load the separator if it exists
+	child = node->first_node("separator");
+	if (child)
+	{
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mSeparatorColor);
+			if (!header_separator_color_specified)
+				ConvertStrToColor(color, &mHeaderSeparatorColor);
+		}
+
+		attr = child->first_attribute("height");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mSeparatorH = atoi(parsevalue.c_str());
+			if (!header_separator_height_specified)
+				mHeaderSeparatorH = mSeparatorH;
+		}
+	}
+
+	// Handle the result variable
+	child = node->first_node("data");
+	if (child)
+	{
+		attr = child->first_attribute("name");
+		if (attr)
+			mVariable = attr->value();
+		attr = child->first_attribute("default");
+		if (attr)
+			DataManager::SetValue(mVariable, attr->value());
+	}
+
+	// Fast scroll colors
+	child = node->first_node("fastscroll");
+	if (child)
+	{
+		attr = child->first_attribute("linecolor");
+		if(attr)
+			ConvertStrToColor(attr->value(), &mFastScrollLineColor);
+
+		attr = child->first_attribute("rectcolor");
+		if(attr)
+			ConvertStrToColor(attr->value(), &mFastScrollRectColor);
+
+		attr = child->first_attribute("w");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("linew");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollLineW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("rectw");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollRectW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("recth");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollRectH = atoi(parsevalue.c_str());
+		}
+	}
+
+	// Retrieve the line height
+	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
+	mLineHeight = mFontHeight;
+	mHeaderH = mFontHeight;
+
+	if (mIconSelected && mIconSelected->GetResource())
+	{
+		mSelectedIconWidth = gr_get_width(mIconSelected->GetResource());
+		mSelectedIconHeight = gr_get_height(mIconSelected->GetResource());
+		if (mSelectedIconHeight > (int)mLineHeight)
+			mLineHeight = mSelectedIconHeight;
+		mIconWidth = mSelectedIconWidth;
+	}
+
+	if (mIconUnselected && mIconUnselected->GetResource())
+	{
+		mUnselectedIconWidth = gr_get_width(mIconUnselected->GetResource());
+		mUnselectedIconHeight = gr_get_height(mIconUnselected->GetResource());
+		if (mUnselectedIconHeight > (int)mLineHeight)
+			mLineHeight = mUnselectedIconHeight;
+		if (mUnselectedIconWidth > mIconWidth)
+			mIconWidth = mUnselectedIconWidth;
+	}
+
+	if (mHeaderIcon && mHeaderIcon->GetResource())
+	{
+		mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
+		mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
+		if (mHeaderIconHeight > mHeaderH)
+			mHeaderH = mHeaderIconHeight;
+		if (mHeaderIconWidth > mIconWidth)
+			mIconWidth = mHeaderIconWidth;
+	}
+
+	mHeaderH += mLineSpacing + mHeaderSeparatorH;
+	actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
+	if (mHeaderH < actualLineHeight)
+		mHeaderH = actualLineHeight;
+
+	if (actualLineHeight / 3 > 6)
+		touchDebounce = actualLineHeight / 3;
+
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundW = gr_get_width(mBackground->GetResource());
+		mBackgroundH = gr_get_height(mBackground->GetResource());
+	}
+
+	// Get the currently selected value for the list
+	DataManager::GetValue(mVariable, currentValue);
+
+	// Get the data for the list
+	child = node->first_node("listitem");
+	if (!child) return;
+
+	while (child)
+	{
+		ListData data;
+
+		attr = child->first_attribute("name");
+		if (!attr) return;
+		data.displayName = attr->value();
+
+		data.variableValue = child->value();
+		if (child->value() == currentValue) {
+			data.selected = 1;
+		} else {
+			data.selected = 0;
+		}
+
+		mList.push_back(data);
+
+		child = child->next_sibling("listitem");
+	}
+}
+
+GUIListBox::~GUIListBox()
+{
+}
+
+int GUIListBox::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	// First step, fill background
+	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
+	gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
+
+	// Next, render the background resource (if it exists)
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
+		mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
+		gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
+	}
+
+	// This tells us how many lines we can actually render
+	int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+	int line;
+
+	int listSize = mList.size();
+	int listW = mRenderW;
+
+	if (listSize < lines) {
+		lines = listSize;
+		scrollingY = 0;
+		mFastScrollRectX = mFastScrollRectY = -1;
+	} else {
+		listW -= mFastScrollW; // space for fast scroll
+		lines++;
+		if (lines < listSize)
+			lines++;
+	}
+
+	void* fontResource = NULL;
+	if (mFont)  fontResource = mFont->GetResource();
+
+	int yPos = mRenderY + mHeaderH + scrollingY;
+	int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
+	int currentIconHeight = 0, currentIconWidth = 0;
+	int currentIconOffsetY = 0, currentIconOffsetX = 0;
+	int UnselectedIconOffsetY = (int)((actualLineHeight - mUnselectedIconHeight) / 2), SelectedIconOffsetY = (int)((actualLineHeight - mSelectedIconHeight) / 2);
+	int UnselectedIconOffsetX = (mIconWidth - mUnselectedIconWidth) / 2, SelectedIconOffsetX = (mIconWidth - mSelectedIconWidth) / 2;
+	int actualSelection = mStart;
+
+	if (isHighlighted) {
+		int selectY = scrollingY;
+
+		// Locate the correct line for highlighting
+		while (selectY + actualLineHeight < startSelection) {
+			selectY += actualLineHeight;
+			actualSelection++;
+		}
+		if (hasHighlightColor) {
+			// Highlight the area
+			gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
+			int HighlightHeight = actualLineHeight;
+			if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
+				HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
+			}
+			gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
+		}
+	}
+
+	for (line = 0; line < lines; line++)
+	{
+		Resource* icon;
+		std::string label;
+
+		if (line + mStart >= listSize)
+			continue;
+
+		label = mList.at(line + mStart).displayName;
+		if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
+			// Use the highlight color for the font
+			gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
+		} else {
+			// Set the color for the font
+			gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
+		}
+
+		if (mList.at(line + mStart).selected != 0)
+		{
+			icon = mIconSelected;
+			currentIconHeight = mSelectedIconHeight;
+			currentIconWidth = mSelectedIconWidth;
+			currentIconOffsetY = SelectedIconOffsetY;
+			currentIconOffsetX = SelectedIconOffsetX;
+		}
+		else
+		{
+			icon = mIconUnselected;
+			currentIconHeight = mSelectedIconHeight;
+			currentIconWidth = mSelectedIconWidth;
+			currentIconOffsetY = SelectedIconOffsetY;
+			currentIconOffsetX = SelectedIconOffsetX;
+		}
+
+		if (icon && icon->GetResource())
+		{
+			int rect_y = 0, image_y = (yPos + currentIconOffsetY);
+			if (image_y + currentIconHeight > mRenderY + mRenderH)
+				rect_y = mRenderY + mRenderH - image_y;
+			else
+				rect_y = currentIconHeight;
+			gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
+		}
+		gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
+
+		// Add the separator
+		if (yPos + actualLineHeight < mRenderH + mRenderY) {
+			gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
+			gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
+		}
+
+		// Move the yPos
+		yPos += actualLineHeight;
+	}
+
+	// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
+	// First step, fill background
+	gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
+	gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
+
+	// Now, we need the header (icon + text)
+	yPos = mRenderY;
+	{
+		Resource* headerIcon;
+		int mIconOffsetX = 0;
+
+		// render the icon if it exists
+		headerIcon = mHeaderIcon;
+		if (headerIcon && headerIcon->GetResource())
+		{
+			gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
+			mIconOffsetX = mIconWidth;
+		}
+
+		// render the text
+		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
+		gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
+
+		// Add the separator
+		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
+		gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
+	}
+
+	// render fast scroll
+	lines = (mRenderH - mHeaderH) / (actualLineHeight);
+	if(mFastScrollW > 0 &&  listSize > lines)
+	{
+		int startX = listW + mRenderX;
+		int fWidth = mRenderW - listW;
+		int fHeight = mRenderH - mHeaderH;
+
+		// line
+		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
+		gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
+
+		// rect
+		int pct = ((mStart*actualLineHeight - scrollingY)*100)/((listSize)*actualLineHeight-lines*actualLineHeight);
+		mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
+		mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
+
+		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
+		gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
+	}
+
+	mUpdate = 0;
+	return 0;
+}
+
+int GUIListBox::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mHeaderIsStatic) {
+		std::string newValue = gui_parse_text(mHeaderText);
+		if (mLastValue != newValue) {
+			mLastValue = newValue;
+			mUpdate = 1;
+		}
+	}
+
+	if (mUpdate)
+	{
+		mUpdate = 0;
+		if (Render() == 0)
+			return 2;
+	}
+
+	// Handle kinetic scrolling
+	if (scrollingSpeed == 0) {
+		// Do nothing
+	} else if (scrollingSpeed > 0) {
+		if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
+			scrollingY += scrollingSpeed;
+			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
+		} else {
+			scrollingY += ((int) (actualLineHeight * 2.5));
+			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
+		}
+		while (mStart && scrollingY > 0) {
+			mStart--;
+			scrollingY -= actualLineHeight;
+		}
+		if (mStart == 0 && scrollingY > 0) {
+			scrollingY = 0;
+			scrollingSpeed = 0;
+		} else if (scrollingSpeed < SCROLLING_FLOOR)
+			scrollingSpeed = 0;
+		mUpdate = 1;
+	} else if (scrollingSpeed < 0) {
+		int totalSize = mList.size();
+		int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+
+		if (totalSize > lines) {
+			int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
+
+			bottom_offset -= actualLineHeight;
+
+			if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
+				scrollingY += scrollingSpeed;
+				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
+			} else {
+				scrollingY -= ((int) (actualLineHeight * 2.5));
+				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
+			}
+			while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
+				mStart++;
+				scrollingY += actualLineHeight;
+			}
+			if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
+				mStart = totalSize - lines - 1;
+				scrollingY = bottom_offset;
+			} else if (mStart + lines >= totalSize && scrollingY < 0) {
+				mStart = totalSize - lines;
+				scrollingY = 0;
+			} else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
+				scrollingSpeed = 0;
+			mUpdate = 1;
+		}
+	}
+
+	return 0;
+}
+
+int GUIListBox::GetSelection(int x, int y)
+{
+	// We only care about y position
+	if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH) return -1;
+	return (y - mRenderY - mHeaderH);
+}
+
+int GUIListBox::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if(!isConditionTrue())
+		return -1;
+
+	static int lastY = 0, last2Y = 0, fastScroll = 0;
+	int selection = 0;
+
+	switch (state)
+	{
+	case TOUCH_START:
+		if (scrollingSpeed != 0)
+			startSelection = -1;
+		else
+			startSelection = GetSelection(x,y);
+		isHighlighted = (startSelection > -1);
+		if (isHighlighted)
+			mUpdate = 1;
+		startY = lastY = last2Y = y;
+		scrollingSpeed = 0;
+
+		if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
+			fastScroll = 1;
+		break;
+
+	case TOUCH_DRAG:
+		// Check if we dragged out of the selection window
+		if (GetSelection(x, y) == -1) {
+			last2Y = lastY = 0;
+			if (isHighlighted) {
+				isHighlighted = false;
+				mUpdate = 1;
+			}
+			break;
+		}
+
+		// Fast scroll
+		if(fastScroll)
+		{
+			int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
+			int totalSize = mList.size();
+			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+
+			float l = float((totalSize-lines)*pct)/100;
+			if(l + lines >= totalSize)
+			{
+				mStart = totalSize - lines;
+				scrollingY = 0;
+			}
+			else
+			{
+				mStart = l;
+				scrollingY = -(l - int(l))*actualLineHeight;
+			}
+
+			startSelection = -1;
+			mUpdate = 1;
+			scrollingSpeed = 0;
+			isHighlighted = false;
+			break;
+		}
+
+		// Provide some debounce on initial touches
+		if (startSelection != -1 && abs(y - startY) < touchDebounce) {
+			isHighlighted = true;
+			mUpdate = 1;
+			break;
+		}
+
+		isHighlighted = false;
+		last2Y = lastY;
+		lastY = y;
+		startSelection = -1;
+
+		// Handle scrolling
+		scrollingY += y - startY;
+		startY = y;
+		while(mStart && scrollingY > 0) {
+			mStart--;
+			scrollingY -= actualLineHeight;
+		}
+		if (mStart == 0 && scrollingY > 0)
+			scrollingY = 0;
+		{
+			int totalSize = mList.size();
+			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+
+			if (totalSize > lines) {
+				int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
+
+				bottom_offset -= actualLineHeight;
+
+				while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
+					mStart++;
+					scrollingY += actualLineHeight;
+				}
+				if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
+					mStart = totalSize - lines - 1;
+					scrollingY = bottom_offset;
+				} else if (mStart + lines >= totalSize && scrollingY < 0) {
+					mStart = totalSize - lines;
+					scrollingY = 0;
+				}
+			} else
+				scrollingY = 0;
+		}
+		mUpdate = 1;
+		break;
+
+	case TOUCH_RELEASE:
+		isHighlighted = false;
+		fastScroll = 0;
+		if (startSelection >= 0)
+		{
+			// We've selected an item!
+			std::string str;
+
+			int listSize = mList.size();
+			int selectY = scrollingY, actualSelection = mStart;
+
+			// Move the selection to the proper place in the array
+			while (selectY + actualLineHeight < startSelection) {
+				selectY += actualLineHeight;
+				actualSelection++;
+			}
+
+			if (actualSelection < listSize && !mVariable.empty())
+			{
+				int i;
+				for (i=0; i<listSize; i++)
+					mList.at(i).selected = 0;
+
+				str = mList.at(actualSelection).variableValue;
+				mList.at(actualSelection).selected = 1;
+				DataManager::SetValue(mVariable, str);
+				mUpdate = 1;
+
+				DataManager::Vibrate("tw_button_vibrate");
+			}
+		} else {
+			// This is for kinetic scrolling
+			scrollingSpeed = lastY - last2Y;
+			if (abs(scrollingSpeed) > SCROLLING_FLOOR)
+				scrollingSpeed *= SCROLLING_MULTIPLIER;
+			else
+				scrollingSpeed = 0;
+		}
+	case TOUCH_REPEAT:
+	case TOUCH_HOLD:
+		break;
+	}
+	return 0;
+}
+
+int GUIListBox::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIObject::NotifyVarChange(varName, value);
+
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mHeaderIsStatic) {
+		std::string newValue = gui_parse_text(mHeaderText);
+		if (mLastValue != newValue) {
+			mLastValue = newValue;
+			mStart = 0;
+			scrollingY = 0;
+			scrollingSpeed = 0;
+			mUpdate = 1;
+		}
+	}
+	if (varName == mVariable)
+	{
+		int i, listSize = mList.size(), selected_index = 0;
+
+		currentValue = value;
+
+		for (i=0; i<listSize; i++) {
+			if (mList.at(i).variableValue == currentValue) {
+				mList.at(i).selected = 1;
+				selected_index = i;
+			} else
+				mList.at(i).selected = 0;
+		}
+
+		int lines = mRenderH / (mLineHeight + mLineSpacing);
+		int line;
+
+		if (selected_index > mStart + lines - 1)
+			mStart = selected_index;
+		if (mStart > listSize - lines) {
+			mStart = listSize - lines;
+		} else if (selected_index < mStart) {
+			mStart = selected_index;
+		}
+
+		mUpdate = 1;
+		return 0;
+	}
+	return 0;
+}
+
+int GUIListBox::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
+{
+	mRenderX = x;
+	mRenderY = y;
+	if (w || h)
+	{
+		mRenderW = w;
+		mRenderH = h;
+	}
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	mUpdate = 1;
+	return 0;
+}
+
+void GUIListBox::SetPageFocus(int inFocus)
+{
+	if (inFocus)
+	{
+		DataManager::GetValue(mVariable, currentValue);
+		NotifyVarChange(mVariable, currentValue);
+		mUpdate = 1;
+	}
+}
diff --git a/gui/mousecursor.cpp b/gui/mousecursor.cpp
new file mode 100644
index 0000000..1c22356
--- /dev/null
+++ b/gui/mousecursor.cpp
@@ -0,0 +1,154 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+MouseCursor::MouseCursor(int resX, int resY)
+{
+	ResetData(resX, resY);
+}
+
+MouseCursor::~MouseCursor()
+{
+}
+
+void MouseCursor::ResetData(int resX, int resY)
+{
+	m_resX = resX;
+	m_resY = resY;
+	m_moved = false;
+	m_speedMultiplier = 2.5f;
+	m_image = NULL;
+	m_present = false;
+
+	ConvertStrToColor("red", &m_color);
+
+	SetRenderPos(resX/2, resY/2, 10, 10);
+}
+
+void MouseCursor::LoadData(xml_node<>* node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	child = node->first_node("placement");
+	if(child)
+		LoadPlacement(child, &mRenderX, &mRenderY, &mRenderW, &mRenderH);
+
+	child = node->first_node("background");
+	if(child)
+	{
+		attr = child->first_attribute("color");
+		if(attr)
+			ConvertStrToColor(attr->value(), &m_color);
+
+		attr = child->first_attribute("resource");
+		if(attr)
+		{
+			m_image = PageManager::FindResource(attr->value());
+			if(m_image)
+			{
+				mRenderW = gr_get_width(m_image->GetResource());
+				mRenderH = gr_get_height(m_image->GetResource());
+			}
+		}
+	}
+
+	child = node->first_node("speed");
+	if(child)
+	{
+		attr = child->first_attribute("multiplier");
+		if(attr)
+			m_speedMultiplier = atof(attr->value());
+	}
+}
+
+int MouseCursor::Render(void)
+{
+	if(!m_present)
+		return 0;
+
+	if(m_image)
+	{
+		gr_blit(m_image->GetResource(), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
+	}
+	else
+	{
+		gr_color(m_color.red, m_color.green, m_color.blue, m_color.alpha);
+		gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
+	}
+	return 0;
+}
+
+int MouseCursor::Update(void)
+{
+	if(m_present != ev_has_mouse())
+	{
+		m_present = ev_has_mouse();
+		if(m_present)
+			SetRenderPos(m_resX/2, m_resY/2);
+		return 2;
+	}
+
+	if(m_present && m_moved)
+	{
+		m_moved = false;
+		return 2;
+	}
+	return 0;
+}
+
+int MouseCursor::SetRenderPos(int x, int y, int w, int h)
+{
+	if(x == mRenderX && y == mRenderY)
+		m_moved = true;
+
+	return RenderObject::SetRenderPos(x, y, w, h);
+}
+
+void MouseCursor::Move(int deltaX, int deltaY)
+{
+	if(deltaX != 0)
+	{
+		mRenderX += deltaX*m_speedMultiplier;
+		mRenderX = (std::min)(mRenderX, m_resX);
+		mRenderX = (std::max)(mRenderX, 0);
+
+		m_moved = true;
+	}
+
+	if(deltaY != 0)
+	{
+		mRenderY += deltaY*m_speedMultiplier;
+		mRenderY = (std::min)(mRenderY, m_resY);
+		mRenderY = (std::max)(mRenderY, 0);
+
+		m_moved = true;
+	}
+}
+
+void MouseCursor::GetPos(int& x, int& y)
+{
+	x = mRenderX;
+	y = mRenderY;
+}
diff --git a/gui/object.cpp b/gui/object.cpp
new file mode 100644
index 0000000..d496414
--- /dev/null
+++ b/gui/object.cpp
@@ -0,0 +1,210 @@
+// checkbox.cpp - GUICheckbox object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+#include "../variables.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+
+GUIObject::GUIObject(xml_node<>* node)
+{
+	mConditionsResult = true;
+
+	// Break out early, it's too hard to check if valid every step
+	if (!node)		return;
+
+	// First, get the action
+	xml_node<>* condition = node->first_node("conditions");
+	if (condition)  condition = condition->first_node("condition");
+	else			condition = node->first_node("condition");
+
+	if (!condition)	return;
+
+	while (condition)
+	{
+		Condition cond;
+
+		cond.mCompareOp = "=";
+
+		xml_attribute<>* attr;
+		attr = condition->first_attribute("var1");
+		if (attr)   cond.mVar1 = attr->value();
+
+		attr = condition->first_attribute("op");
+		if (attr)   cond.mCompareOp = attr->value();
+
+		attr = condition->first_attribute("var2");
+		if (attr)   cond.mVar2 = attr->value();
+
+		mConditions.push_back(cond);
+
+		condition = condition->next_sibling("condition");
+	}
+}
+
+GUIObject::~GUIObject()
+{
+}
+
+bool GUIObject::IsConditionVariable(std::string var)
+{
+	std::vector<Condition>::iterator iter;
+	for (iter = mConditions.begin(); iter != mConditions.end(); iter++)
+	{
+		if (iter->mVar1 == var)
+			return true;
+	}
+	return false;
+}
+
+bool GUIObject::isConditionTrue()
+{
+	return mConditionsResult;
+}
+
+bool GUIObject::isConditionTrue(Condition* condition)
+{
+	// This is used to hold the proper value of "true" based on the '!' NOT flag
+	bool bTrue = true;
+
+	if (condition->mVar1.empty())
+		return bTrue;
+
+	if (!condition->mCompareOp.empty() && condition->mCompareOp[0] == '!')
+		bTrue = false;
+
+	if (condition->mVar2.empty() && condition->mCompareOp != "modified")
+	{
+		if (!DataManager::GetStrValue(condition->mVar1).empty())
+			return bTrue;
+
+		return !bTrue;
+	}
+
+	string var1, var2;
+	if (DataManager::GetValue(condition->mVar1, var1))
+		var1 = condition->mVar1;
+	if (DataManager::GetValue(condition->mVar2, var2))
+		var2 = condition->mVar2;
+
+	// This is a special case, we stat the file and that determines our result
+	if (var1 == "fileexists")
+	{
+		struct stat st;
+		if (stat(var2.c_str(), &st) == 0)
+			var2 = var1;
+		else
+			var2 = "FAILED";
+	}
+	if (var1 == "mounted")
+	{
+		if (isMounted(condition->mVar2))
+			var2 = var1;
+		else
+			var2 = "FAILED";
+	}
+
+	if (condition->mCompareOp.find('=') != string::npos && var1 == var2)
+		return bTrue;
+
+	if (condition->mCompareOp.find('>') != string::npos && (atof(var1.c_str()) > atof(var2.c_str())))
+		return bTrue;
+
+	if (condition->mCompareOp.find('<') != string::npos && (atof(var1.c_str()) < atof(var2.c_str())))
+		return bTrue;
+
+	if (condition->mCompareOp == "modified")
+	{
+		// This is a hack to allow areas to reset the default value
+		if (var1.empty())
+		{
+			condition->mLastVal = var1;
+			return !bTrue;
+		}
+
+		if (var1 != condition->mLastVal)
+			return bTrue;
+	}
+
+	return !bTrue;
+}
+
+bool GUIObject::isConditionValid()
+{
+	return !mConditions.empty();
+}
+
+int GUIObject::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	mConditionsResult = true;
+
+	const bool varNameEmpty = varName.empty();
+	std::vector<Condition>::iterator iter;
+	for (iter = mConditions.begin(); iter != mConditions.end(); ++iter)
+	{
+		if(varNameEmpty && iter->mCompareOp == "modified")
+		{
+			string val;
+
+			// If this fails, val will not be set, which is perfect
+			if (DataManager::GetValue(iter->mVar1, val))
+			{
+				DataManager::SetValue(iter->mVar1, "");
+				DataManager::GetValue(iter->mVar1, val);
+			}
+			iter->mLastVal = val;
+		}
+
+		if(varNameEmpty || iter->mVar1 == varName || iter->mVar2 == varName)
+			iter->mLastResult = isConditionTrue(&(*iter));
+
+		if(!iter->mLastResult)
+			mConditionsResult = false;
+	}
+	return 0;
+}
+
+bool GUIObject::isMounted(string vol)
+{
+	FILE *fp;
+	char tmpOutput[255];
+
+	fp = fopen("/proc/mounts", "rt");
+	while (fgets(tmpOutput,255,fp) != NULL)
+	{
+		char* mnt = tmpOutput;
+		while (*mnt > 32)			mnt++;
+		while (*mnt > 0 && *mnt <= 32)	mnt++;
+		char* pos = mnt;
+		while (*pos > 32)   pos++;
+		*pos = 0;
+		if (vol == mnt)
+		{
+			fclose(fp);
+			return true;
+		}
+	}
+	fclose(fp);
+	return false;
+}
diff --git a/gui/objects.hpp b/gui/objects.hpp
new file mode 100644
index 0000000..cf20d7e
--- /dev/null
+++ b/gui/objects.hpp
@@ -0,0 +1,1038 @@
+/*
+	Copyright 2013 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// objects.hpp - Base classes for object manager of GUI
+
+#ifndef _OBJECTS_HEADER
+#define _OBJECTS_HEADER
+
+#include "rapidxml.hpp"
+#include <vector>
+#include <string>
+#include <map>
+#include <set>
+#include <time.h>
+
+extern "C" {
+#ifdef HAVE_SELINUX
+#include "../minzip/Zip.h"
+#else
+#include "../minzipold/Zip.h"
+#endif
+}
+
+using namespace rapidxml;
+
+#include "../data.hpp"
+#include "resources.hpp"
+#include "pages.hpp"
+#include "../partitions.hpp"
+
+class RenderObject
+{
+public:
+	enum Placement {
+		TOP_LEFT = 0,
+		TOP_RIGHT = 1,
+		BOTTOM_LEFT = 2,
+		BOTTOM_RIGHT = 3,
+		CENTER = 4,
+		CENTER_X_ONLY = 5,
+	};
+
+public:
+	RenderObject() { mRenderX = 0; mRenderY = 0; mRenderW = 0; mRenderH = 0; mPlacement = TOP_LEFT; }
+	virtual ~RenderObject() {}
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void) = 0;
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void) { return 0; }
+
+	// GetRenderPos - Returns the current position of the object
+	virtual int GetRenderPos(int& x, int& y, int& w, int& h) { x = mRenderX; y = mRenderY; w = mRenderW; h = mRenderH; return 0; }
+
+	// SetRenderPos - Update the position of the object
+	//  Return 0 on success, <0 on error
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0) { mRenderX = x; mRenderY = y; if (w || h) { mRenderW = w; mRenderH = h; } return 0; }
+
+	// GetPlacement - Returns the current placement
+	virtual int GetPlacement(Placement& placement) { placement = mPlacement; return 0; }
+
+	// SetPlacement - Update the current placement
+	virtual int SetPlacement(Placement placement) { mPlacement = placement; return 0; }
+
+	// SetPageFocus - Notify when a page gains or loses focus
+	virtual void SetPageFocus(int inFocus) { return; }
+
+protected:
+	int mRenderX, mRenderY, mRenderW, mRenderH;
+	Placement mPlacement;
+};
+
+class ActionObject
+{
+public:
+	ActionObject() { mActionX = 0; mActionY = 0; mActionW = 0; mActionH = 0; }
+	virtual ~ActionObject() {}
+
+public:
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y) { return 0; }
+
+	// NotifyKey - Notify of a key press
+	//  Return 0 on success (and consume key), >0 to pass key to next handler, and <0 on error
+	virtual int NotifyKey(int key, bool down) { return 1; }
+
+	// GetRenderPos - Returns the current position of the object
+	virtual int GetActionPos(int& x, int& y, int& w, int& h) { x = mActionX; y = mActionY; w = mActionW; h = mActionH; return 0; }
+
+	// SetRenderPos - Update the position of the object
+	//  Return 0 on success, <0 on error
+	virtual int SetActionPos(int x, int y, int w = 0, int h = 0);
+
+	// IsInRegion - Checks if the request is handled by this object
+	//  Return 0 if this object handles the request, 1 if not
+	virtual int IsInRegion(int x, int y) { return ((x < mActionX || x > mActionX + mActionW || y < mActionY || y > mActionY + mActionH) ? 0 : 1); }
+
+protected:
+	int mActionX, mActionY, mActionW, mActionH;
+};
+
+class GUIObject
+{
+public:
+	GUIObject(xml_node<>* node);
+	virtual ~GUIObject();
+
+public:
+	bool IsConditionVariable(std::string var);
+	bool isConditionTrue();
+	bool isConditionValid();
+
+	// NotifyVarChange - Notify of a variable change
+	//  Returns 0 on success, <0 on error
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+
+protected:
+	class Condition
+	{
+	public:
+		Condition() {
+			mLastResult = true;
+		}
+
+		std::string mVar1;
+		std::string mVar2;
+		std::string mCompareOp;
+		std::string mLastVal;
+		bool mLastResult;
+	};
+
+	std::vector<Condition> mConditions;
+
+protected:
+	bool isMounted(std::string vol);
+	bool isConditionTrue(Condition* condition);
+
+	bool mConditionsResult;
+};
+
+class InputObject
+{
+public:
+	InputObject() { HasInputFocus = 0; }
+	virtual ~InputObject() {}
+
+public:
+	// NotifyKeyboard - Notify of keyboard input
+	//  Return 0 on success (and consume key), >0 to pass key to next handler, and <0 on error
+	virtual int NotifyKeyboard(int key) { return 1; }
+
+	virtual int SetInputFocus(int focus) { HasInputFocus = focus; return 1; }
+
+protected:
+	int HasInputFocus;
+};
+
+// Derived Objects
+// GUIText - Used for static text
+class GUIText : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	// w and h may be ignored, in which case, no bounding box is applied
+	GUIText(xml_node<>* node);
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// Retrieve the size of the current string (dynamic strings may change per call)
+	virtual int GetCurrentBounds(int& w, int& h);
+
+	// Notify of a variable change
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+
+	// Set maximum width in pixels
+	virtual int SetMaxWidth(unsigned width);
+
+	// Set number of characters to skip (for scrolling)
+	virtual int SkipCharCount(unsigned skip);
+
+public:
+	bool isHighlighted;
+
+protected:
+	std::string mText;
+	std::string mLastValue;
+	COLOR mColor;
+	COLOR mHighlightColor;
+	Resource* mFont;
+	int mIsStatic;
+	int mVarChanged;
+	int mFontHeight;
+	unsigned maxWidth;
+	unsigned charSkip;
+	bool hasHighlightColor;
+
+protected:
+	std::string parseText(void);
+};
+
+// GUIImage - Used for static image
+class GUIImage : public GUIObject, public RenderObject
+{
+public:
+	GUIImage(xml_node<>* node);
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// SetRenderPos - Update the position of the object
+	//  Return 0 on success, <0 on error
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+public:
+	bool isHighlighted;
+
+protected:
+	Resource* mImage;
+	Resource* mHighlightImage;
+};
+
+// GUIFill - Used for fill colors
+class GUIFill : public GUIObject, public RenderObject
+{
+public:
+	GUIFill(xml_node<>* node);
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+protected:
+	COLOR mColor;
+};
+
+// GUIAction - Used for standard actions
+class GUIAction : public GUIObject, public ActionObject
+{
+public:
+	GUIAction(xml_node<>* node);
+
+public:
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+	virtual int NotifyKey(int key, bool down);
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+	virtual int doActions();
+
+protected:
+	class Action
+	{
+	public:
+		std::string mFunction;
+		std::string mArg;
+	};
+
+	std::vector<Action> mActions;
+	std::map<int, bool> mKeys;
+
+protected:
+	int getKeyByName(std::string key);
+	virtual int doAction(Action action, int isThreaded = 0);
+	static void* thread_start(void *cookie);
+	void simulate_progress_bar(void);
+	int flash_zip(std::string filename, std::string pageName, const int simulate, int* wipe_cache);
+	void operation_start(const string operation_name);
+	void operation_end(const int operation_status, const int simulate);
+	static void* command_thread(void *cookie);
+	time_t Start;
+};
+
+class GUIConsole : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIConsole(xml_node<>* node);
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// SetRenderPos - Update the position of the object
+	//  Return 0 on success, <0 on error
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+	// IsInRegion - Checks if the request is handled by this object
+	//  Return 0 if this object handles the request, 1 if not
+	virtual int IsInRegion(int x, int y);
+
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error (Return error to allow other handlers)
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+
+protected:
+	enum SlideoutState
+	{
+		hidden = 0,
+		visible,
+		request_hide,
+		request_show
+	};
+
+	Resource* mFont;
+	Resource* mSlideoutImage;
+	COLOR mForegroundColor;
+	COLOR mBackgroundColor;
+	COLOR mScrollColor;
+	unsigned int mFontHeight;
+	int mCurrentLine;
+	unsigned int mLastCount;
+	unsigned int RenderCount;
+	unsigned int mMaxRows;
+	int mStartY;
+	int mSlideoutX, mSlideoutY, mSlideoutW, mSlideoutH;
+	int mSlideinX, mSlideinY, mSlideinW, mSlideinH;
+	int mConsoleX, mConsoleY, mConsoleW, mConsoleH;
+	int mLastTouchX, mLastTouchY;
+	int mSlideout;
+	SlideoutState mSlideoutState;
+	std::vector<std::string> rConsole;
+	std::vector<std::string> rConsoleColor;
+	bool mRender;
+
+protected:
+	virtual int RenderSlideout(void);
+	virtual int RenderConsole(void);
+};
+
+class GUIButton : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIButton(xml_node<>* node);
+	virtual ~GUIButton();
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// SetPos - Update the position of the render object
+	//  Return 0 on success, <0 on error
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+
+protected:
+	GUIImage* mButtonImg;
+	Resource* mButtonIcon;
+	GUIText* mButtonLabel;
+	GUIAction* mAction;
+	int mTextX, mTextY, mTextW, mTextH;
+	int mIconX, mIconY, mIconW, mIconH;
+	bool mRendered;
+	bool hasHighlightColor;
+	bool renderHighlight;
+	bool hasFill;
+	COLOR mFillColor;
+	COLOR mHighlightColor;
+	Placement TextPlacement;
+};
+
+class GUICheckbox: public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUICheckbox(xml_node<>* node);
+	virtual ~GUICheckbox();
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// SetPos - Update the position of the render object
+	//  Return 0 on success, <0 on error
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+
+protected:
+	Resource* mChecked;
+	Resource* mUnchecked;
+	GUIText* mLabel;
+	int mTextX, mTextY;
+	int mCheckX, mCheckY, mCheckW, mCheckH;
+	int mLastState;
+	bool mRendered;
+	std::string mVarName;
+};
+
+class GUIFileSelector : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIFileSelector(xml_node<>* node);
+	virtual ~GUIFileSelector();
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+
+	// NotifyVarChange - Notify of a variable change
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+
+	// SetPos - Update the position of the render object
+	//  Return 0 on success, <0 on error
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+	// SetPageFocus - Notify when a page gains or loses focus
+	virtual void SetPageFocus(int inFocus);
+
+protected:
+	struct FileData {
+		std::string fileName;
+		unsigned char fileType;	 // Uses d_type format from struct dirent
+		mode_t protection;		  // Uses mode_t format from stat
+		uid_t userId;
+		gid_t groupId;
+		off_t fileSize;
+		time_t lastAccess;		  // Uses time_t format from stat
+		time_t lastModified;		// Uses time_t format from stat
+		time_t lastStatChange;	  // Uses time_t format from stat
+	};
+
+protected:
+	virtual int GetSelection(int x, int y);
+
+	virtual int GetFileList(const std::string folder);
+	static bool fileSort(FileData d1, FileData d2);
+
+protected:
+	std::vector<FileData> mFolderList;
+	std::vector<FileData> mFileList;
+	std::string mPathVar;
+	std::string mExtn;
+	std::string mVariable;
+	std::string mSortVariable;
+	std::string mSelection;
+	std::string mHeaderText;
+	std::string mLastValue;
+	int actualLineHeight;
+	int mStart;
+	int mLineSpacing;
+	int mSeparatorH;
+	int mHeaderSeparatorH;
+	int mShowFolders, mShowFiles, mShowNavFolders;
+	int mUpdate;
+	int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH;
+	int mHeaderH;
+	int mFastScrollW;
+	int mFastScrollLineW;
+	int mFastScrollRectW;
+	int mFastScrollRectH;
+	int mFastScrollRectX;
+	int mFastScrollRectY;
+	static int mSortOrder;
+	int startY;
+	int scrollingSpeed;
+	int scrollingY;
+	int mHeaderIsStatic;
+	int touchDebounce;
+	unsigned mFontHeight;
+	unsigned mLineHeight;
+	int mIconWidth, mIconHeight, mFolderIconHeight, mFileIconHeight, mFolderIconWidth, mFileIconWidth, mHeaderIconHeight, mHeaderIconWidth;
+	Resource* mHeaderIcon;
+	Resource* mFolderIcon;
+	Resource* mFileIcon;
+	Resource* mBackground;
+	Resource* mFont;
+	COLOR mBackgroundColor;
+	COLOR mFontColor;
+	COLOR mHeaderBackgroundColor;
+	COLOR mHeaderFontColor;
+	COLOR mSeparatorColor;
+	COLOR mHeaderSeparatorColor;
+	COLOR mFastScrollLineColor;
+	COLOR mFastScrollRectColor;
+	bool hasHighlightColor;
+	bool hasFontHighlightColor;
+	bool isHighlighted;
+	COLOR mHighlightColor;
+	COLOR mFontHighlightColor;
+	int startSelection;
+	bool updateFileList;
+};
+
+class GUIListBox : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIListBox(xml_node<>* node);
+	virtual ~GUIListBox();
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+
+	// NotifyVarChange - Notify of a variable change
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+
+	// SetPos - Update the position of the render object
+	//  Return 0 on success, <0 on error
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+	// SetPageFocus - Notify when a page gains or loses focus
+	virtual void SetPageFocus(int inFocus);
+
+protected:
+	struct ListData {
+		std::string displayName;
+		std::string variableValue;
+		unsigned int selected;
+	};
+
+protected:
+	virtual int GetSelection(int x, int y);
+
+protected:
+	std::vector<ListData> mList;
+	std::string mVariable;
+	std::string mSelection;
+	std::string currentValue;
+	std::string mHeaderText;
+	std::string mLastValue;
+	int actualLineHeight;
+	int mStart;
+	int startY;
+	int mSeparatorH, mHeaderSeparatorH;
+	int mLineSpacing;
+	int mUpdate;
+	int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH, mHeaderH;
+	int mFastScrollW;
+	int mFastScrollLineW;
+	int mFastScrollRectW;
+	int mFastScrollRectH;
+	int mFastScrollRectX;
+	int mFastScrollRectY;
+	int mIconWidth, mIconHeight, mSelectedIconWidth, mSelectedIconHeight, mUnselectedIconWidth, mUnselectedIconHeight, mHeaderIconHeight, mHeaderIconWidth;
+	int scrollingSpeed;
+	int scrollingY;
+	static int mSortOrder;
+	unsigned mFontHeight;
+	unsigned mLineHeight;
+	Resource* mHeaderIcon;
+	Resource* mIconSelected;
+	Resource* mIconUnselected;
+	Resource* mBackground;
+	Resource* mFont;
+	COLOR mBackgroundColor;
+	COLOR mFontColor;
+	COLOR mHeaderBackgroundColor;
+	COLOR mHeaderFontColor;
+	COLOR mSeparatorColor;
+	COLOR mHeaderSeparatorColor;
+	COLOR mFastScrollLineColor;
+	COLOR mFastScrollRectColor;
+	bool hasHighlightColor;
+	bool hasFontHighlightColor;
+	bool isHighlighted;
+	COLOR mHighlightColor;
+	COLOR mFontHighlightColor;
+	int mHeaderIsStatic;
+	int startSelection;
+	int touchDebounce;
+};
+
+class GUIPartitionList : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIPartitionList(xml_node<>* node);
+	virtual ~GUIPartitionList();
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+
+	// NotifyVarChange - Notify of a variable change
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+
+	// SetPos - Update the position of the render object
+	//  Return 0 on success, <0 on error
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+	// SetPageFocus - Notify when a page gains or loses focus
+	virtual void SetPageFocus(int inFocus);
+
+protected:
+	virtual int GetSelection(int x, int y);
+	virtual void MatchList(void);
+
+protected:
+	std::vector<PartitionList> mList;
+	std::string ListType;
+	std::string mVariable;
+	std::string selectedList;
+	std::string currentValue;
+	std::string mHeaderText;
+	std::string mLastValue;
+	int actualLineHeight;
+	int mStart;
+	int startY;
+	int mSeparatorH, mHeaderSeparatorH;
+	int mLineSpacing;
+	int mUpdate;
+	int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH, mHeaderH;
+	int mFastScrollW;
+	int mFastScrollLineW;
+	int mFastScrollRectW;
+	int mFastScrollRectH;
+	int mFastScrollRectX;
+	int mFastScrollRectY;
+	int mIconWidth, mIconHeight, mSelectedIconWidth, mSelectedIconHeight, mUnselectedIconWidth, mUnselectedIconHeight, mHeaderIconHeight, mHeaderIconWidth;
+	int scrollingSpeed;
+	int scrollingY;
+	static int mSortOrder;
+	unsigned mFontHeight;
+	unsigned mLineHeight;
+	Resource* mHeaderIcon;
+	Resource* mIconSelected;
+	Resource* mIconUnselected;
+	Resource* mBackground;
+	Resource* mFont;
+	COLOR mBackgroundColor;
+	COLOR mFontColor;
+	COLOR mHeaderBackgroundColor;
+	COLOR mHeaderFontColor;
+	COLOR mSeparatorColor;
+	COLOR mHeaderSeparatorColor;
+	COLOR mFastScrollLineColor;
+	COLOR mFastScrollRectColor;
+	bool hasHighlightColor;
+	bool hasFontHighlightColor;
+	bool isHighlighted;
+	COLOR mHighlightColor;
+	COLOR mFontHighlightColor;
+	int mHeaderIsStatic;
+	int startSelection;
+	int touchDebounce;
+	bool updateList;
+};
+
+// GUIAnimation - Used for animations
+class GUIAnimation : public GUIObject, public RenderObject
+{
+public:
+	GUIAnimation(xml_node<>* node);
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+protected:
+	AnimationResource* mAnimation;
+	int mFrame;
+	int mFPS;
+	int mLoop;
+	int mRender;
+	int mUpdateCount;
+};
+
+class GUIProgressBar : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIProgressBar(xml_node<>* node);
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// NotifyVarChange - Notify of a variable change
+	//  Returns 0 on success, <0 on error
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+
+protected:
+	Resource* mEmptyBar;
+	Resource* mFullBar;
+	std::string mMinValVar;
+	std::string mMaxValVar;
+	std::string mCurValVar;
+	float mSlide;
+	float mSlideInc;
+	int mSlideFrames;
+	int mLastPos;
+
+protected:
+	virtual int RenderInternal(void);	   // Does the actual render
+};
+
+class GUISlider : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUISlider(xml_node<>* node);
+	virtual ~GUISlider();
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+
+protected:
+	GUIAction* sAction;
+	Resource* sSlider;
+	Resource* sSliderUsed;
+	Resource* sTouch;
+	int sTouchW, sTouchH;
+	int sCurTouchX;
+	int sUpdate;
+};
+
+#define MAX_KEYBOARD_LAYOUTS 5
+#define MAX_KEYBOARD_ROWS 9
+#define MAX_KEYBOARD_KEYS 20
+#define KEYBOARD_ACTION 253
+#define KEYBOARD_LAYOUT 254
+#define KEYBOARD_SWIPE_LEFT 252
+#define KEYBOARD_SWIPE_RIGHT 251
+#define KEYBOARD_ARROW_LEFT 250
+#define KEYBOARD_ARROW_RIGHT 249
+#define KEYBOARD_HOME 248
+#define KEYBOARD_END 247
+#define KEYBOARD_ARROW_UP 246
+#define KEYBOARD_ARROW_DOWN 245
+#define KEYBOARD_SPECIAL_KEYS 245
+#define KEYBOARD_BACKSPACE 8
+
+class GUIKeyboard : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIKeyboard(xml_node<>* node);
+	virtual ~GUIKeyboard();
+
+public:
+	virtual int Render(void);
+	virtual int Update(void);
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+protected:
+	virtual int GetSelection(int x, int y);
+
+protected:
+	struct keyboard_key_class
+	{
+		unsigned char key;
+		unsigned char longpresskey;
+		unsigned int end_x;
+		unsigned int layout;
+	};
+	struct capslock_tracking_struct
+	{
+		int capslock;
+		int set_capslock;
+		int revert_layout;
+	};
+
+	Resource* keyboardImg[MAX_KEYBOARD_LAYOUTS];
+	struct keyboard_key_class keyboard_keys[MAX_KEYBOARD_LAYOUTS][MAX_KEYBOARD_ROWS][MAX_KEYBOARD_KEYS];
+	struct capslock_tracking_struct caps_tracking[MAX_KEYBOARD_LAYOUTS];
+	bool mRendered;
+	std::string mVariable;
+	unsigned int cursorLocation;
+	unsigned int currentLayout;
+	unsigned int row_heights[MAX_KEYBOARD_LAYOUTS][MAX_KEYBOARD_ROWS];
+	unsigned int KeyboardWidth, KeyboardHeight;
+	int rowY, colX, highlightRenderCount, hasHighlight, hasCapsHighlight;
+	GUIAction* mAction;
+	COLOR mHighlightColor;
+	COLOR mCapsHighlightColor;
+};
+
+// GUIInput - Used for keyboard input
+class GUIInput : public GUIObject, public RenderObject, public ActionObject, public InputObject
+{
+public:
+	// w and h may be ignored, in which case, no bounding box is applied
+	GUIInput(xml_node<>* node);
+	virtual ~GUIInput();
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// Notify of a variable change
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+
+	virtual int NotifyKeyboard(int key);
+
+protected:
+	virtual int GetSelection(int x, int y);
+
+	// Handles displaying the text properly when chars are added, deleted, or for scrolling
+	virtual int HandleTextLocation(int x);
+
+protected:
+	GUIText* mInputText;
+	GUIAction* mAction;
+	Resource* mBackground;
+	Resource* mCursor;
+	Resource* mFont;
+	std::string mText;
+	std::string mLastValue;
+	std::string mVariable;
+	std::string mMask;
+	std::string mMaskVariable;
+	COLOR mBackgroundColor;
+	COLOR mCursorColor;
+	int scrollingX;
+	int lastX;
+	int mCursorLocation;
+	int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH;
+	int mFontY;
+	unsigned skipChars;
+	unsigned mFontHeight;
+	unsigned CursorWidth;
+	bool mRendered;
+	bool HasMask;
+	bool DrawCursor;
+	bool isLocalChange;
+	bool HasAllowed;
+	bool HasDisabled;
+	std::string AllowedList;
+	std::string DisabledList;
+	unsigned MinLen;
+	unsigned MaxLen;
+};
+
+class HardwareKeyboard
+{
+public:
+	HardwareKeyboard(void);
+	virtual ~HardwareKeyboard();
+
+public:
+	virtual int KeyDown(int key_code);
+	virtual int KeyUp(int key_code);
+	virtual int KeyRepeat(void);
+
+	void ConsumeKeyRelease(int key);
+
+private:
+	std::set<int> mPressedKeys;
+};
+
+class GUISliderValue: public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUISliderValue(xml_node<>* node);
+	virtual ~GUISliderValue();
+
+public:
+	// Render - Render the full object to the GL surface
+	//  Return 0 on success, <0 on error
+	virtual int Render(void);
+
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// SetPos - Update the position of the render object
+	//  Return 0 on success, <0 on error
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+	// NotifyTouch - Notify of a touch event
+	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+
+	// Notify of a variable change
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+
+	// SetPageFocus - Notify when a page gains or loses focus
+	virtual void SetPageFocus(int inFocus);
+
+protected:
+	int measureText(const std::string& str);
+	int valueFromPct(float pct);
+	float pctFromValue(int value);
+	void loadValue(bool force = false);
+
+	std::string mVariable;
+	int mMax;
+	int mMin;
+	int mValue;
+	char *mValueStr;
+	float mValuePct;
+	std::string mMaxStr;
+	std::string mMinStr;
+	Resource *mFont;
+	GUIText* mLabel;
+	int mLabelW;
+	COLOR mTextColor;
+	COLOR mLineColor;
+	COLOR mSliderColor;
+	bool mShowRange;
+	bool mShowCurr;
+	int mLineX;
+	int mLineY;
+	int mLineH;
+	int mLinePadding;
+	int mPadding;
+	int mSliderY;
+	int mSliderW;
+	int mSliderH;
+	bool mRendered;
+	int mFontHeight;
+	GUIAction *mAction;
+	bool mChangeOnDrag;
+	int mLineW;
+	bool mDragging;
+	Resource *mBackgroundImage;
+	Resource *mHandleImage;
+	Resource *mHandleHoverImage;
+};
+
+class MouseCursor : public RenderObject
+{
+public:
+	MouseCursor(int posX, int posY);
+	virtual ~MouseCursor();
+
+	virtual int Render(void);
+	virtual int Update(void);
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+	void Move(int deltaX, int deltaY);
+	void GetPos(int& x, int& y);
+	void LoadData(xml_node<>* node);
+	void ResetData(int resX, int resY);
+
+private:
+	int m_resX;
+	int m_resY;
+	bool m_moved;
+	float m_speedMultiplier;
+	COLOR m_color;
+	Resource *m_image;
+	bool m_present;
+};
+
+// Helper APIs
+bool LoadPlacement(xml_node<>* node, int* x, int* y, int* w = NULL, int* h = NULL, RenderObject::Placement* placement = NULL);
+
+#endif  // _OBJECTS_HEADER
+
diff --git a/gui/pages.cpp b/gui/pages.cpp
new file mode 100644
index 0000000..3632cc3
--- /dev/null
+++ b/gui/pages.cpp
@@ -0,0 +1,1207 @@
+/*
+	Copyright 2013 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// pages.cpp - Source to manage GUI base objects
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#ifndef TW_NO_SCREEN_TIMEOUT
+#include "blanktimer.hpp"
+#endif
+
+extern int gGuiRunning;
+#ifndef TW_NO_SCREEN_TIMEOUT
+extern blanktimer blankTimer;
+#endif
+
+std::map<std::string, PageSet*> PageManager::mPageSets;
+PageSet* PageManager::mCurrentSet;
+PageSet* PageManager::mBaseSet = NULL;
+MouseCursor *PageManager::mMouseCursor = NULL;
+HardwareKeyboard *PageManager::mHardwareKeyboard = NULL;
+
+// Helper routine to convert a string to a color declaration
+int ConvertStrToColor(std::string str, COLOR* color)
+{
+	// Set the default, solid black
+	memset(color, 0, sizeof(COLOR));
+	color->alpha = 255;
+
+	// Translate variables
+	DataManager::GetValue(str, str);
+
+	// Look for some defaults
+	if (str == "black")			return 0;
+	else if (str == "white")	{ color->red = color->green = color->blue = 255; return 0; }
+	else if (str == "red")		{ color->red = 255; return 0; }
+	else if (str == "green")	{ color->green = 255; return 0; }
+	else if (str == "blue")		{ color->blue = 255; return 0; }
+
+	// At this point, we require an RGB(A) color
+	if (str[0] != '#')
+		return -1;
+
+	str.erase(0, 1);
+
+	int result;
+	if (str.size() >= 8) {
+		// We have alpha channel
+		string alpha = str.substr(6, 2);
+		result = strtol(alpha.c_str(), NULL, 16);
+		color->alpha = result & 0x000000FF;
+		str.resize(6);
+		result = strtol(str.c_str(), NULL, 16);
+		color->red = (result >> 16) & 0x000000FF;
+		color->green = (result >> 8) & 0x000000FF;
+		color->blue = result & 0x000000FF;
+	} else {
+		result = strtol(str.c_str(), NULL, 16);
+		color->red = (result >> 16) & 0x000000FF;
+		color->green = (result >> 8) & 0x000000FF;
+		color->blue = result & 0x000000FF;
+	}
+	return 0;
+}
+
+// Helper APIs
+bool LoadPlacement(xml_node<>* node, int* x, int* y, int* w /* = NULL */, int* h /* = NULL */, RenderObject::Placement* placement /* = NULL */)
+{
+	if (!node)
+		return false;
+
+	std::string value;
+	if (node->first_attribute("x"))
+	{
+		value = node->first_attribute("x")->value();
+		DataManager::GetValue(value, value);
+		*x = atol(value.c_str());
+	}
+
+	if (node->first_attribute("y"))
+	{
+		value = node->first_attribute("y")->value();
+		DataManager::GetValue(value, value);
+		*y = atol(value.c_str());
+	}
+
+	if (w && node->first_attribute("w"))
+	{
+		value = node->first_attribute("w")->value();
+		DataManager::GetValue(value, value);
+		*w = atol(value.c_str());
+	}
+
+	if (h && node->first_attribute("h"))
+	{
+		value = node->first_attribute("h")->value();
+		DataManager::GetValue(value, value);
+		*h = atol(value.c_str());
+	}
+
+	if (placement && node->first_attribute("placement"))
+	{
+		value = node->first_attribute("placement")->value();
+		DataManager::GetValue(value, value);
+		*placement = (RenderObject::Placement) atol(value.c_str());
+	}
+
+	return true;
+}
+
+int ActionObject::SetActionPos(int x, int y, int w, int h)
+{
+	if (x < 0 || y < 0)
+		return -1;
+
+	mActionX = x;
+	mActionY = y;
+	if (w || h)
+	{
+		mActionW = w;
+		mActionH = h;
+	}
+	return 0;
+}
+
+Page::Page(xml_node<>* page, std::vector<xml_node<>*> *templates /* = NULL */)
+{
+	mTouchStart = NULL;
+
+	// We can memset the whole structure, because the alpha channel is ignored
+	memset(&mBackground, 0, sizeof(COLOR));
+
+	// With NULL, we make a console-only display
+	if (!page)
+	{
+		mName = "console";
+
+		GUIConsole* element = new GUIConsole(NULL);
+		mRenders.push_back(element);
+		mActions.push_back(element);
+		return;
+	}
+
+	if (page->first_attribute("name"))
+		mName = page->first_attribute("name")->value();
+	else
+	{
+		LOGERR("No page name attribute found!\n");
+		return;
+	}
+
+	LOGINFO("Loading page %s\n", mName.c_str());
+
+	// This is a recursive routine for template handling
+	ProcessNode(page, templates);
+
+	return;
+}
+
+Page::~Page()
+{
+	for (std::vector<GUIObject*>::iterator itr = mObjects.begin(); itr != mObjects.end(); ++itr)
+		delete *itr;
+}
+
+bool Page::ProcessNode(xml_node<>* page, std::vector<xml_node<>*> *templates /* = NULL */, int depth /* = 0 */)
+{
+	if (depth == 10)
+	{
+		LOGERR("Page processing depth has exceeded 10. Failing out. This is likely a recursive template.\n");
+		return false;
+	}
+
+	// Let's retrieve the background value, if any
+	xml_node<>* bg = page->first_node("background");
+	if (bg)
+	{
+		xml_attribute<>* attr = bg->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mBackground);
+		}
+	}
+
+	xml_node<>* child;
+	child = page->first_node("object");
+	while (child)
+	{
+		if (!child->first_attribute("type"))
+			break;
+
+		std::string type = child->first_attribute("type")->value();
+
+		if (type == "text")
+		{
+			GUIText* element = new GUIText(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "image")
+		{
+			GUIImage* element = new GUIImage(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+		}
+		else if (type == "fill")
+		{
+			GUIFill* element = new GUIFill(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+		}
+		else if (type == "action")
+		{
+			GUIAction* element = new GUIAction(child);
+			mObjects.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "console")
+		{
+			GUIConsole* element = new GUIConsole(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "button")
+		{
+			GUIButton* element = new GUIButton(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "checkbox")
+		{
+			GUICheckbox* element = new GUICheckbox(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "fileselector")
+		{
+			GUIFileSelector* element = new GUIFileSelector(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "animation")
+		{
+			GUIAnimation* element = new GUIAnimation(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+		}
+		else if (type == "progressbar")
+		{
+			GUIProgressBar* element = new GUIProgressBar(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "slider")
+		{
+			GUISlider* element = new GUISlider(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "slidervalue")
+		{
+			GUISliderValue *element = new GUISliderValue(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "listbox")
+		{
+			GUIListBox* element = new GUIListBox(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "keyboard")
+		{
+			GUIKeyboard* element = new GUIKeyboard(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "input")
+		{
+			GUIInput* element = new GUIInput(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+			mInputs.push_back(element);
+		}
+		else if (type == "partitionlist")
+		{
+			GUIPartitionList* element = new GUIPartitionList(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
+		else if (type == "template")
+		{
+			if (!templates || !child->first_attribute("name"))
+			{
+				LOGERR("Invalid template request.\n");
+			}
+			else
+			{
+				std::string name = child->first_attribute("name")->value();
+				xml_node<>* node;
+				bool node_found = false;
+
+				// We need to find the correct template
+				for (std::vector<xml_node<>*>::iterator itr = templates->begin(); itr != templates->end(); itr++) {
+					node = (*itr)->first_node("template");
+
+					while (node)
+					{
+						if (!node->first_attribute("name"))
+							continue;
+
+						if (name == node->first_attribute("name")->value())
+						{
+							if (!ProcessNode(node, templates, depth + 1))
+								return false;
+							else {
+								node_found = true;
+								break;
+							}
+						}
+						if (node_found)
+							break;
+						node = node->next_sibling("template");
+					}
+				}
+			}
+		}
+		else
+		{
+			LOGERR("Unknown object type.\n");
+		}
+		child = child->next_sibling("object");
+	}
+	return true;
+}
+
+int Page::Render(void)
+{
+	// Render background
+	gr_color(mBackground.red, mBackground.green, mBackground.blue, mBackground.alpha);
+	gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+
+	// Render remaining objects
+	std::vector<RenderObject*>::iterator iter;
+	for (iter = mRenders.begin(); iter != mRenders.end(); iter++)
+	{
+		if ((*iter)->Render())
+			LOGERR("A render request has failed.\n");
+	}
+	return 0;
+}
+
+int Page::Update(void)
+{
+	int retCode = 0;
+
+	std::vector<RenderObject*>::iterator iter;
+	for (iter = mRenders.begin(); iter != mRenders.end(); iter++)
+	{
+		int ret = (*iter)->Update();
+		if (ret < 0)
+			LOGERR("An update request has failed.\n");
+		else if (ret > retCode)
+			retCode = ret;
+	}
+
+	return retCode;
+}
+
+int Page::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	// By default, return 1 to ignore further touches if nobody is listening
+	int ret = 1;
+
+	// Don't try to handle a lack of handlers
+	if (mActions.size() == 0)
+		return ret;
+
+	// We record mTouchStart so we can pass all the touch stream to the same handler
+	if (state == TOUCH_START)
+	{
+		std::vector<ActionObject*>::reverse_iterator iter;
+		// We work backwards, from top-most element to bottom-most element
+		for (iter = mActions.rbegin(); iter != mActions.rend(); iter++)
+		{
+			if ((*iter)->IsInRegion(x, y))
+			{
+				mTouchStart = (*iter);
+				ret = mTouchStart->NotifyTouch(state, x, y);
+				if (ret >= 0)
+					break;
+				mTouchStart = NULL;
+			}
+		}
+	}
+	else if (state == TOUCH_RELEASE && mTouchStart != NULL)
+	{
+		ret = mTouchStart->NotifyTouch(state, x, y);
+		mTouchStart = NULL;
+	}
+	else if ((state == TOUCH_DRAG || state == TOUCH_HOLD || state == TOUCH_REPEAT) && mTouchStart != NULL)
+	{
+		ret = mTouchStart->NotifyTouch(state, x, y);
+	}
+	return ret;
+}
+
+int Page::NotifyKey(int key, bool down)
+{
+	std::vector<ActionObject*>::reverse_iterator iter;
+
+	// Don't try to handle a lack of handlers
+	if (mActions.size() == 0)
+		return 1;
+
+	int ret = 1;
+	// We work backwards, from top-most element to bottom-most element
+	for (iter = mActions.rbegin(); iter != mActions.rend(); iter++)
+	{
+		ret = (*iter)->NotifyKey(key, down);
+		if (ret < 0) {
+			LOGERR("An action handler has returned an error\n");
+			ret = 1;
+		}
+	}
+	return ret;
+}
+
+int Page::NotifyKeyboard(int key)
+{
+	std::vector<InputObject*>::reverse_iterator iter;
+
+	// Don't try to handle a lack of handlers
+	if (mInputs.size() == 0)
+		return 1;
+
+	// We work backwards, from top-most element to bottom-most element
+	for (iter = mInputs.rbegin(); iter != mInputs.rend(); iter++)
+	{
+		int ret = (*iter)->NotifyKeyboard(key);
+		if (ret == 0)
+			return 0;
+		else if (ret < 0)
+			LOGERR("A keyboard handler has returned an error");
+	}
+	return 1;
+}
+
+int Page::SetKeyBoardFocus(int inFocus)
+{
+	std::vector<InputObject*>::reverse_iterator iter;
+
+	// Don't try to handle a lack of handlers
+	if (mInputs.size() == 0)
+		return 1;
+
+	// We work backwards, from top-most element to bottom-most element
+	for (iter = mInputs.rbegin(); iter != mInputs.rend(); iter++)
+	{
+		int ret = (*iter)->SetInputFocus(inFocus);
+		if (ret == 0)
+			return 0;
+		else if (ret < 0)
+			LOGERR("An input focus handler has returned an error");
+	}
+	return 1;
+}
+
+void Page::SetPageFocus(int inFocus)
+{
+	// Render remaining objects
+	std::vector<RenderObject*>::iterator iter;
+	for (iter = mRenders.begin(); iter != mRenders.end(); iter++)
+		(*iter)->SetPageFocus(inFocus);
+
+	return;
+}
+
+int Page::NotifyVarChange(std::string varName, std::string value)
+{
+	std::vector<GUIObject*>::iterator iter;
+	for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
+	{
+		if ((*iter)->NotifyVarChange(varName, value))
+			LOGERR("An action handler errored on NotifyVarChange.\n");
+	}
+	return 0;
+}
+
+PageSet::PageSet(char* xmlFile)
+{
+	mResources = NULL;
+	mCurrentPage = NULL;
+	mOverlayPage = NULL;
+
+	mXmlFile = xmlFile;
+	if (xmlFile)
+		mDoc.parse<0>(mXmlFile);
+	else
+		mCurrentPage = new Page(NULL);
+}
+
+PageSet::~PageSet()
+{
+	for (std::vector<Page*>::iterator itr = mPages.begin(); itr != mPages.end(); ++itr)
+		delete *itr;
+	for (std::vector<xml_node<>*>::iterator itr2 = templates.begin(); itr2 != templates.end(); ++itr2)
+		delete *itr2;
+
+	delete mResources;
+	free(mXmlFile);
+}
+
+int PageSet::Load(ZipArchive* package)
+{
+	xml_node<>* parent;
+	xml_node<>* child;
+	xml_node<>* xmltemplate;
+	xml_node<>* blank_templates;
+	int pages_loaded = -1, ret;
+
+	parent = mDoc.first_node("recovery");
+	if (!parent)
+		parent = mDoc.first_node("install");
+
+	// Now, let's parse the XML
+	LOGINFO("Loading resources...\n");
+	child = parent->first_node("resources");
+	if (child)
+		mResources = new ResourceManager(child, package);
+
+	LOGINFO("Loading variables...\n");
+	child = parent->first_node("variables");
+	if (child)
+		LoadVariables(child);
+
+	LOGINFO("Loading mouse cursor...\n");
+	child = parent->first_node("mousecursor");
+	if(child)
+		PageManager::LoadCursorData(child);
+
+	LOGINFO("Loading pages...\n");
+	// This may be NULL if no templates are present
+	xmltemplate = parent->first_node("templates");
+	if (xmltemplate)
+		templates.push_back(xmltemplate);
+
+	child = parent->first_node("pages");
+	if (child) {
+		if (LoadPages(child)) {
+			LOGERR("PageSet::Load returning -1\n");
+			return -1;
+		}
+	}
+	
+	return CheckInclude(package, &mDoc);
+}
+
+int PageSet::CheckInclude(ZipArchive* package, xml_document<> *parentDoc)
+{
+	xml_node<>* par;
+	xml_node<>* par2;
+	xml_node<>* chld;
+	xml_node<>* parent;
+	xml_node<>* child;
+	xml_node<>* xmltemplate;
+	long len;
+	char* xmlFile = NULL;
+	string filename;
+	xml_document<> doc;
+
+	par = parentDoc->first_node("recovery");
+	if (!par) {
+		par = parentDoc->first_node("install");
+	}
+	if (!par) {
+		return 0;
+	}
+
+	par2 = par->first_node("include");
+	if (!par2)
+		return 0;
+	chld = par2->first_node("xmlfile");
+	while (chld != NULL) {
+		xml_attribute<>* attr = chld->first_attribute("name");
+		if (!attr)
+			break;
+
+		LOGINFO("PageSet::CheckInclude loading filename: '%s'\n", filename.c_str());
+		if (!package) {
+			// We can try to load the XML directly...
+			filename = "/res/";
+			filename += attr->value();
+			struct stat st;
+			if(stat(filename.c_str(),&st) != 0) {
+				LOGERR("Unable to locate '%s'\n", filename.c_str());
+				return -1;
+			}
+
+			len = st.st_size;
+			xmlFile = (char*) malloc(len + 1);
+			if (!xmlFile)
+				return -1;
+
+			int fd = open(filename.c_str(), O_RDONLY);
+			if (fd == -1)
+				return -1;
+
+			read(fd, xmlFile, len);
+			close(fd);
+		} else {
+			filename += attr->value();
+			const ZipEntry* ui_xml = mzFindZipEntry(package, filename.c_str());
+			if (ui_xml == NULL)
+			{
+				LOGERR("Unable to locate '%s' in zip file\n", filename.c_str());
+				return -1;
+			}
+
+			// Allocate the buffer for the file
+			len = mzGetZipEntryUncompLen(ui_xml);
+			xmlFile = (char*) malloc(len + 1);
+			if (!xmlFile)
+				return -1;
+
+			if (!mzExtractZipEntryToBuffer(package, ui_xml, (unsigned char*) xmlFile))
+			{
+				LOGERR("Unable to extract '%s'\n", filename.c_str());
+				return -1;
+			}
+		}
+		doc.parse<0>(xmlFile);
+
+		parent = doc.first_node("recovery");
+		if (!parent)
+			parent = doc.first_node("install");
+
+		// Now, let's parse the XML
+		LOGINFO("Loading included resources...\n");
+		child = parent->first_node("resources");
+		if (child)
+			mResources->LoadResources(child, package);
+
+		LOGINFO("Loading included variables...\n");
+		child = parent->first_node("variables");
+		if (child)
+			LoadVariables(child);
+
+		LOGINFO("Loading mouse cursor...\n");
+		child = parent->first_node("mousecursor");
+		if(child)
+			PageManager::LoadCursorData(child);
+
+		LOGINFO("Loading included pages...\n");
+		// This may be NULL if no templates are present
+		xmltemplate = parent->first_node("templates");
+		if (xmltemplate)
+			templates.push_back(xmltemplate);
+
+		child = parent->first_node("pages");
+		if (child)
+			if (LoadPages(child))
+				return -1;
+
+		if (CheckInclude(package, &doc))
+			return -1;
+
+		chld = chld->next_sibling("xmlfile");
+	}
+
+	return 0;
+}
+
+int PageSet::SetPage(std::string page)
+{
+	Page* tmp = FindPage(page);
+	if (tmp)
+	{
+		if (mCurrentPage)   mCurrentPage->SetPageFocus(0);
+		mCurrentPage = tmp;
+		mCurrentPage->SetPageFocus(1);
+		mCurrentPage->NotifyVarChange("", "");
+		return 0;
+	}
+	else
+	{
+		LOGERR("Unable to locate page (%s)\n", page.c_str());
+	}
+	return -1;
+}
+
+int PageSet::SetOverlay(Page* page)
+{
+	if (mOverlayPage)   mOverlayPage->SetPageFocus(0);
+	mOverlayPage = page;
+	if (mOverlayPage)
+	{
+		mOverlayPage->SetPageFocus(1);
+		mOverlayPage->NotifyVarChange("", "");
+	}
+	return 0;
+}
+
+Resource* PageSet::FindResource(std::string name)
+{
+	return mResources ? mResources->FindResource(name) : NULL;
+}
+
+Page* PageSet::FindPage(std::string name)
+{
+	std::vector<Page*>::iterator iter;
+
+	for (iter = mPages.begin(); iter != mPages.end(); iter++)
+	{
+		if (name == (*iter)->GetName())
+			return (*iter);
+	}
+	return NULL;
+}
+
+int PageSet::LoadVariables(xml_node<>* vars)
+{
+	xml_node<>* child;
+	xml_attribute<> *name, *value, *persist;
+	int p;
+
+	child = vars->first_node("variable");
+	while (child)
+	{
+		name = child->first_attribute("name");
+		value = child->first_attribute("value");
+		persist = child->first_attribute("persist");
+		if(name && value)
+		{
+			p = persist ? atoi(persist->value()) : 0;
+			string temp = value->value();
+			string valstr = gui_parse_text(temp);
+
+			if (valstr.find("+") != string::npos) {
+				string val1str = valstr;
+				val1str = val1str.substr(0, val1str.find('+'));
+				string val2str = valstr;
+				val2str = val2str.substr(val2str.find('+') + 1, string::npos);
+				int val1 = atoi(val1str.c_str());
+				int val2 = atoi(val2str.c_str());
+				int val = val1 + val2;
+
+				DataManager::SetValue(name->value(), val, p);
+			} else if (valstr.find("-") != string::npos) {
+				string val1str = valstr;
+				val1str = val1str.substr(0, val1str.find('-'));
+				string val2str = valstr;
+				val2str = val2str.substr(val2str.find('-') + 1, string::npos);
+				int val1 = atoi(val1str.c_str());
+				int val2 = atoi(val2str.c_str());
+				int val = val1 - val2;
+
+				DataManager::SetValue(name->value(), val, p);
+			} else {
+				DataManager::SetValue(name->value(), valstr, p);
+			}
+		}
+
+		child = child->next_sibling("variable");
+	}
+	return 0;
+}
+
+int PageSet::LoadPages(xml_node<>* pages)
+{
+	xml_node<>* child;
+
+	if (!pages)
+		return -1;
+
+	child = pages->first_node("page");
+	while (child != NULL)
+	{
+		Page* page = new Page(child, &templates);
+		if (page->GetName().empty())
+		{
+			LOGERR("Unable to process load page\n");
+			delete page;
+		}
+		else
+		{
+			mPages.push_back(page);
+		}
+		child = child->next_sibling("page");
+	}
+	if (mPages.size() > 0)
+		return 0;
+	return -1;
+}
+
+int PageSet::IsCurrentPage(Page* page)
+{
+	return ((mCurrentPage && mCurrentPage == page) ? 1 : 0);
+}
+
+int PageSet::Render(void)
+{
+	int ret;
+
+	ret = (mCurrentPage ? mCurrentPage->Render() : -1);
+	if (ret < 0)
+		return ret;
+	ret = (mOverlayPage ? mOverlayPage->Render() : -1);
+	return ret;
+}
+
+int PageSet::Update(void)
+{
+	int ret;
+
+	ret = (mCurrentPage ? mCurrentPage->Update() : -1);
+	if (ret < 0 || ret > 1)
+		return ret;
+	ret = (mOverlayPage ? mOverlayPage->Update() : -1);
+	return ret;
+}
+
+int PageSet::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if (mOverlayPage)
+		return (mOverlayPage->NotifyTouch(state, x, y));
+
+	return (mCurrentPage ? mCurrentPage->NotifyTouch(state, x, y) : -1);
+}
+
+int PageSet::NotifyKey(int key, bool down)
+{
+	if (mOverlayPage)
+		return (mOverlayPage->NotifyKey(key, down));
+
+	return (mCurrentPage ? mCurrentPage->NotifyKey(key, down) : -1);
+}
+
+int PageSet::NotifyKeyboard(int key)
+{
+	if (mOverlayPage)
+		return (mOverlayPage->NotifyKeyboard(key));
+
+	return (mCurrentPage ? mCurrentPage->NotifyKeyboard(key) : -1);
+}
+
+int PageSet::SetKeyBoardFocus(int inFocus)
+{
+	if (mOverlayPage)
+		return (mOverlayPage->SetKeyBoardFocus(inFocus));
+
+	return (mCurrentPage ? mCurrentPage->SetKeyBoardFocus(inFocus) : -1);
+}
+
+int PageSet::NotifyVarChange(std::string varName, std::string value)
+{
+	if (mOverlayPage)
+		mOverlayPage->NotifyVarChange(varName, value);
+
+	return (mCurrentPage ? mCurrentPage->NotifyVarChange(varName, value) : -1);
+}
+
+int PageManager::LoadPackage(std::string name, std::string package, std::string startpage)
+{
+	int fd;
+	ZipArchive zip, *pZip = NULL;
+	long len;
+	char* xmlFile = NULL;
+	PageSet* pageSet = NULL;
+	int ret;
+
+	// Open the XML file
+	LOGINFO("Loading package: %s (%s)\n", name.c_str(), package.c_str());
+	if (mzOpenZipArchive(package.c_str(), &zip))
+	{
+		// We can try to load the XML directly...
+		struct stat st;
+		if(stat(package.c_str(),&st) != 0)
+			return -1;
+
+		len = st.st_size;
+		xmlFile = (char*) malloc(len + 1);
+		if (!xmlFile)
+			return -1;
+
+		fd = open(package.c_str(), O_RDONLY);
+		if (fd == -1)
+			goto error;
+
+		read(fd, xmlFile, len);
+		close(fd);
+	}
+	else
+	{
+		pZip = &zip;
+		const ZipEntry* ui_xml = mzFindZipEntry(&zip, "ui.xml");
+		if (ui_xml == NULL)
+		{
+			LOGERR("Unable to locate ui.xml in zip file\n");
+			goto error;
+		}
+
+		// Allocate the buffer for the file
+		len = mzGetZipEntryUncompLen(ui_xml);
+		xmlFile = (char*) malloc(len + 1);
+		if (!xmlFile)
+			goto error;
+
+		if (!mzExtractZipEntryToBuffer(&zip, ui_xml, (unsigned char*) xmlFile))
+		{
+			LOGERR("Unable to extract ui.xml\n");
+			goto error;
+		}
+	}
+
+	// NULL-terminate the string
+	xmlFile[len] = 0x00;
+
+	// Before loading, mCurrentSet must be the loading package so we can find resources
+	pageSet = mCurrentSet;
+	mCurrentSet = new PageSet(xmlFile);
+
+	ret = mCurrentSet->Load(pZip);
+	if (ret == 0)
+	{
+		mCurrentSet->SetPage(startpage);
+		mPageSets.insert(std::pair<std::string, PageSet*>(name, mCurrentSet));
+	}
+	else
+	{
+		LOGERR("Package %s failed to load.\n", name.c_str());
+	}
+
+	// The first successful package we loaded is the base
+	if (mBaseSet == NULL)
+		mBaseSet = mCurrentSet;
+
+	mCurrentSet = pageSet;
+
+	if (pZip)
+		mzCloseZipArchive(pZip);
+	return ret;
+
+error:
+	LOGERR("An internal error has occurred.\n");
+	if (pZip)
+		mzCloseZipArchive(pZip);
+	if (xmlFile)
+		free(xmlFile);
+	return -1;
+}
+
+PageSet* PageManager::FindPackage(std::string name)
+{
+	std::map<std::string, PageSet*>::iterator iter;
+
+	iter = mPageSets.find(name);
+	if (iter != mPageSets.end())
+		return (*iter).second;
+
+	LOGERR("Unable to locate package %s\n", name.c_str());
+	return NULL;
+}
+
+PageSet* PageManager::SelectPackage(std::string name)
+{
+	LOGINFO("Switching packages (%s)\n", name.c_str());
+	PageSet* tmp;
+
+	tmp = FindPackage(name);
+	if (tmp)
+	{
+		mCurrentSet = tmp;
+		mCurrentSet->NotifyVarChange("", "");
+	}
+	else
+		LOGERR("Unable to find package.\n");
+
+	return mCurrentSet;
+}
+
+int PageManager::ReloadPackage(std::string name, std::string package)
+{
+	std::map<std::string, PageSet*>::iterator iter;
+
+	iter = mPageSets.find(name);
+	if (iter == mPageSets.end())
+		return -1;
+
+	if(mMouseCursor)
+		mMouseCursor->ResetData(gr_fb_width(), gr_fb_height());
+
+	PageSet* set = (*iter).second;
+	mPageSets.erase(iter);
+
+	if (LoadPackage(name, package, "main") != 0)
+	{
+		LOGERR("Failed to load package.\n");
+		mPageSets.insert(std::pair<std::string, PageSet*>(name, set));
+		return -1;
+	}
+	if (mCurrentSet == set)
+		SelectPackage(name);
+	if (mBaseSet == set)
+		mBaseSet = mCurrentSet;
+	delete set;
+	return 0;
+}
+
+void PageManager::ReleasePackage(std::string name)
+{
+	std::map<std::string, PageSet*>::iterator iter;
+
+	iter = mPageSets.find(name);
+	if (iter == mPageSets.end())
+		return;
+
+	PageSet* set = (*iter).second;
+	mPageSets.erase(iter);
+	delete set;
+	return;
+}
+
+int PageManager::ChangePage(std::string name)
+{
+	DataManager::SetValue("tw_operation_state", 0);
+	int ret = (mCurrentSet ? mCurrentSet->SetPage(name) : -1);
+	return ret;
+}
+
+int PageManager::ChangeOverlay(std::string name)
+{
+	if (name.empty())
+		return mCurrentSet->SetOverlay(NULL);
+	else
+	{
+		Page* page = mBaseSet ? mBaseSet->FindPage(name) : NULL;
+		return mCurrentSet->SetOverlay(page);
+	}
+}
+
+Resource* PageManager::FindResource(std::string name)
+{
+	return (mCurrentSet ? mCurrentSet->FindResource(name) : NULL);
+}
+
+Resource* PageManager::FindResource(std::string package, std::string name)
+{
+	PageSet* tmp;
+
+	tmp = FindPackage(name);
+	return (tmp ? tmp->FindResource(name) : NULL);
+}
+
+int PageManager::SwitchToConsole(void)
+{
+	PageSet* console = new PageSet(NULL);
+
+	mCurrentSet = console;
+	return 0;
+}
+
+int PageManager::EndConsole(void)
+{
+	if (mCurrentSet && mBaseSet) {
+		delete mCurrentSet;
+		mCurrentSet = mBaseSet;
+		return 0;
+	}
+	return -1;
+}
+
+int PageManager::IsCurrentPage(Page* page)
+{
+	return (mCurrentSet ? mCurrentSet->IsCurrentPage(page) : 0);
+}
+
+int PageManager::Render(void)
+{
+	int res = (mCurrentSet ? mCurrentSet->Render() : -1);
+	if(mMouseCursor)
+		mMouseCursor->Render();
+	return res;
+}
+
+HardwareKeyboard *PageManager::GetHardwareKeyboard()
+{
+	if(!mHardwareKeyboard)
+		mHardwareKeyboard = new HardwareKeyboard();
+	return mHardwareKeyboard;
+}
+
+MouseCursor *PageManager::GetMouseCursor()
+{
+	if(!mMouseCursor)
+		mMouseCursor = new MouseCursor(gr_fb_width(), gr_fb_height());
+	return mMouseCursor;
+}
+
+void PageManager::LoadCursorData(xml_node<>* node)
+{
+	if(!mMouseCursor)
+		mMouseCursor = new MouseCursor(gr_fb_width(), gr_fb_height());
+
+	mMouseCursor->LoadData(node);
+}
+
+int PageManager::Update(void)
+{
+#ifndef TW_NO_SCREEN_TIMEOUT
+	if(blankTimer.IsScreenOff())
+		return 0;
+#endif
+
+	int res = (mCurrentSet ? mCurrentSet->Update() : -1);
+
+	if(mMouseCursor)
+	{
+		int c_res = mMouseCursor->Update();
+		if(c_res > res)
+			res = c_res;
+	}
+	return res;
+}
+
+int PageManager::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	return (mCurrentSet ? mCurrentSet->NotifyTouch(state, x, y) : -1);
+}
+
+int PageManager::NotifyKey(int key, bool down)
+{
+	return (mCurrentSet ? mCurrentSet->NotifyKey(key, down) : -1);
+}
+
+int PageManager::NotifyKeyboard(int key)
+{
+	return (mCurrentSet ? mCurrentSet->NotifyKeyboard(key) : -1);
+}
+
+int PageManager::SetKeyBoardFocus(int inFocus)
+{
+	return (mCurrentSet ? mCurrentSet->SetKeyBoardFocus(inFocus) : -1);
+}
+
+int PageManager::NotifyVarChange(std::string varName, std::string value)
+{
+	return (mCurrentSet ? mCurrentSet->NotifyVarChange(varName, value) : -1);
+}
+
+extern "C" void gui_notifyVarChange(const char *name, const char* value)
+{
+	if (!gGuiRunning)
+		return;
+
+	PageManager::NotifyVarChange(name, value);
+}
diff --git a/gui/pages.h b/gui/pages.h
new file mode 100644
index 0000000..d407162
--- /dev/null
+++ b/gui/pages.h
@@ -0,0 +1,7 @@
+#ifndef _PAGES_HEADER
+#define _PAGES_HEADER
+
+void gui_notifyVarChange(const char *name, const char* value);
+
+#endif  // _PAGES_HEADER
+
diff --git a/gui/pages.hpp b/gui/pages.hpp
new file mode 100644
index 0000000..2afbe78
--- /dev/null
+++ b/gui/pages.hpp
@@ -0,0 +1,155 @@
+// pages.hpp - Base classes for page manager of GUI
+
+#ifndef _PAGES_HEADER_HPP
+#define _PAGES_HEADER_HPP
+
+#ifdef HAVE_SELINUX
+#include "../minzip/Zip.h"
+#else
+#include "../minzipold/Zip.h"
+#endif
+
+typedef struct {
+	unsigned char red;
+	unsigned char green;
+	unsigned char blue;
+	unsigned char alpha;
+} COLOR;
+
+// Utility Functions
+int ConvertStrToColor(std::string str, COLOR* color);
+int gui_forceRender(void);
+int gui_changePage(std::string newPage);
+int gui_changeOverlay(std::string newPage);
+std::string gui_parse_text(string inText);
+
+class Resource;
+class ResourceManager;
+class RenderObject;
+class ActionObject;
+class InputObject;
+class MouseCursor;
+class GUIObject;
+class HardwareKeyboard;
+
+class Page
+{
+public:
+	Page(xml_node<>* page, std::vector<xml_node<>*> *templates = NULL);
+	virtual ~Page();
+
+	std::string GetName(void)   { return mName; }
+
+public:
+	virtual int Render(void);
+	virtual int Update(void);
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+	virtual int NotifyKey(int key, bool down);
+	virtual int NotifyKeyboard(int key);
+	virtual int SetKeyBoardFocus(int inFocus);
+	virtual int NotifyVarChange(std::string varName, std::string value);
+	virtual void SetPageFocus(int inFocus);
+
+protected:
+	std::string mName;
+	std::vector<GUIObject*> mObjects;
+	std::vector<RenderObject*> mRenders;
+	std::vector<ActionObject*> mActions;
+	std::vector<InputObject*> mInputs;
+
+	ActionObject* mTouchStart;
+	COLOR mBackground;
+
+protected:
+	bool ProcessNode(xml_node<>* page, std::vector<xml_node<>*> *templates = NULL, int depth = 0);
+};
+
+class PageSet
+{
+public:
+	PageSet(char* xmlFile);
+	virtual ~PageSet();
+
+public:
+	int Load(ZipArchive* package);
+	int CheckInclude(ZipArchive* package, xml_document<> *parentDoc);
+
+	Page* FindPage(std::string name);
+	int SetPage(std::string page);
+	int SetOverlay(Page* page);
+	Resource* FindResource(std::string name);
+
+	// Helper routine for identifing if we're the current page
+	int IsCurrentPage(Page* page);
+
+	// These are routing routines
+	int Render(void);
+	int Update(void);
+	int NotifyTouch(TOUCH_STATE state, int x, int y);
+	int NotifyKey(int key, bool down);
+	int NotifyKeyboard(int key);
+	int SetKeyBoardFocus(int inFocus);
+	int NotifyVarChange(std::string varName, std::string value);
+
+protected:
+	int LoadPages(xml_node<>* pages);
+	int LoadVariables(xml_node<>* vars);
+
+protected:
+	char* mXmlFile;
+	xml_document<> mDoc;
+	ResourceManager* mResources;
+	std::vector<Page*> mPages;
+	std::vector<xml_node<>*> templates;
+	Page* mCurrentPage;
+	Page* mOverlayPage; // This is a special case, used for "locking" the screen
+};
+
+class PageManager
+{
+public:
+	// Used by GUI
+	static int LoadPackage(std::string name, std::string package, std::string startpage);
+	static PageSet* SelectPackage(std::string name);
+	static int ReloadPackage(std::string name, std::string package);
+	static void ReleasePackage(std::string name);
+
+	// Used for actions and pages
+	static int ChangePage(std::string name);
+	static int ChangeOverlay(std::string name);
+	static Resource* FindResource(std::string name);
+	static Resource* FindResource(std::string package, std::string name);
+
+	// Used for console-only mode
+	static int SwitchToConsole(void);
+	static int EndConsole(void);
+
+	// Helper to identify if a particular page is the active page
+	static int IsCurrentPage(Page* page);
+
+	// These are routing routines
+	static int Render(void);
+	static int Update(void);
+	static int NotifyTouch(TOUCH_STATE state, int x, int y);
+	static int NotifyKey(int key, bool down);
+	static int NotifyKeyboard(int key);
+	static int SetKeyBoardFocus(int inFocus);
+	static int NotifyVarChange(std::string varName, std::string value);
+
+	static MouseCursor *GetMouseCursor();
+	static void LoadCursorData(xml_node<>* node);
+
+	static HardwareKeyboard *GetHardwareKeyboard();
+
+protected:
+	static PageSet* FindPackage(std::string name);
+
+protected:
+	static std::map<std::string, PageSet*> mPageSets;
+	static PageSet* mCurrentSet;
+	static PageSet* mBaseSet;
+	static MouseCursor *mMouseCursor;
+	static HardwareKeyboard *mHardwareKeyboard;
+};
+
+#endif  // _PAGES_HEADER_HPP
diff --git a/gui/partitionlist.cpp b/gui/partitionlist.cpp
new file mode 100644
index 0000000..2d464e1
--- /dev/null
+++ b/gui/partitionlist.cpp
@@ -0,0 +1,942 @@
+/*
+	Copyright 2013 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <linux/input.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include <algorithm>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+#include "../twrp-functions.hpp"
+#include "../partitions.hpp"
+
+#define SCROLLING_SPEED_DECREMENT 6
+#define SCROLLING_FLOOR 10
+#define SCROLLING_MULTIPLIER 6
+
+GUIPartitionList::GUIPartitionList(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+	int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
+
+	mStart = mLineSpacing = startY = mFontHeight = mSeparatorH = scrollingY = scrollingSpeed = 0;
+	mIconWidth = mIconHeight = mSelectedIconHeight = mSelectedIconWidth = mUnselectedIconHeight = mUnselectedIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0;
+	mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0;
+	mIconSelected = mIconUnselected = mBackground = mFont = mHeaderIcon = NULL;
+	mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0;
+	mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
+	mFastScrollRectX = mFastScrollRectY = -1;
+	mUpdate = 0;
+	touchDebounce = 6;
+	ConvertStrToColor("black", &mBackgroundColor);
+	ConvertStrToColor("black", &mHeaderBackgroundColor);
+	ConvertStrToColor("black", &mSeparatorColor);
+	ConvertStrToColor("black", &mHeaderSeparatorColor);
+	ConvertStrToColor("white", &mFontColor);
+	ConvertStrToColor("white", &mHeaderFontColor);
+	ConvertStrToColor("white", &mFastScrollLineColor);
+	ConvertStrToColor("white", &mFastScrollRectColor);
+	hasHighlightColor = false;
+	hasFontHighlightColor = false;
+	isHighlighted = false;
+	updateList = false;
+	startSelection = -1;
+
+	// Load header text
+	child = node->first_node("header");
+	if (child)
+	{
+		attr = child->first_attribute("icon");
+		if (attr)
+			mHeaderIcon = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("background");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderBackgroundColor);
+			header_background_color_specified = -1;
+		}
+		attr = child->first_attribute("textcolor");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderFontColor);
+			header_text_color_specified = -1;
+		}
+		attr = child->first_attribute("separatorcolor");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderSeparatorColor);
+			header_separator_color_specified = -1;
+		}
+		attr = child->first_attribute("separatorheight");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mHeaderSeparatorH = atoi(parsevalue.c_str());
+			header_separator_height_specified = -1;
+		}
+	}
+	child = node->first_node("text");
+	if (child)  mHeaderText = child->value();
+
+	memset(&mHighlightColor, 0, sizeof(COLOR));
+	child = node->first_node("highlight");
+	if (child) {
+		attr = child->first_attribute("color");
+		if (attr) {
+			hasHighlightColor = true;
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHighlightColor);
+		}
+	}
+
+	// Simple way to check for static state
+	mLastValue = gui_parse_text(mHeaderText);
+	if (mLastValue != mHeaderText)
+		mHeaderIsStatic = 0;
+	else
+		mHeaderIsStatic = -1;
+
+	child = node->first_node("icon");
+	if (child)
+	{
+		attr = child->first_attribute("selected");
+		if (attr)
+			mIconSelected = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("unselected");
+		if (attr)
+			mIconUnselected = PageManager::FindResource(attr->value());
+	}
+	child = node->first_node("background");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mBackground = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mBackgroundColor);
+			if (!header_background_color_specified)
+				ConvertStrToColor(color, &mHeaderBackgroundColor);
+		}
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+
+	// Load the font, and possibly override the color
+	child = node->first_node("font");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mFont = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mFontColor);
+			if (!header_text_color_specified)
+				ConvertStrToColor(color, &mHeaderFontColor);
+		}
+
+		attr = child->first_attribute("spacing");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mLineSpacing = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("highlightcolor");
+		memset(&mFontHighlightColor, 0, sizeof(COLOR));
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mFontHighlightColor);
+			hasFontHighlightColor = true;
+		}
+	}
+
+	// Load the separator if it exists
+	child = node->first_node("separator");
+	if (child)
+	{
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mSeparatorColor);
+			if (!header_separator_color_specified)
+				ConvertStrToColor(color, &mHeaderSeparatorColor);
+		}
+
+		attr = child->first_attribute("height");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mSeparatorH = atoi(parsevalue.c_str());
+			if (!header_separator_height_specified)
+				mHeaderSeparatorH = mSeparatorH;
+		}
+	}
+
+	// Handle the result variable
+	child = node->first_node("data");
+	if (child)
+	{
+		attr = child->first_attribute("name");
+		if (attr)
+			mVariable = attr->value();
+		attr = child->first_attribute("selectedlist");
+		if (attr)
+			selectedList = attr->value();
+	}
+
+	// Fast scroll colors
+	child = node->first_node("fastscroll");
+	if (child)
+	{
+		attr = child->first_attribute("linecolor");
+		if(attr)
+			ConvertStrToColor(attr->value(), &mFastScrollLineColor);
+
+		attr = child->first_attribute("rectcolor");
+		if(attr)
+			ConvertStrToColor(attr->value(), &mFastScrollRectColor);
+
+		attr = child->first_attribute("w");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("linew");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollLineW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("rectw");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollRectW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("recth");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollRectH = atoi(parsevalue.c_str());
+		}
+	}
+
+	// Retrieve the line height
+	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
+	mLineHeight = mFontHeight;
+	mHeaderH = mFontHeight;
+
+	if (mIconSelected && mIconSelected->GetResource())
+	{
+		mSelectedIconWidth = gr_get_width(mIconSelected->GetResource());
+		mSelectedIconHeight = gr_get_height(mIconSelected->GetResource());
+		if (mSelectedIconHeight > (int)mLineHeight)
+			mLineHeight = mSelectedIconHeight;
+		mIconWidth = mSelectedIconWidth;
+	}
+
+	if (mIconUnselected && mIconUnselected->GetResource())
+	{
+		mUnselectedIconWidth = gr_get_width(mIconUnselected->GetResource());
+		mUnselectedIconHeight = gr_get_height(mIconUnselected->GetResource());
+		if (mUnselectedIconHeight > (int)mLineHeight)
+			mLineHeight = mUnselectedIconHeight;
+		if (mUnselectedIconWidth > mIconWidth)
+			mIconWidth = mUnselectedIconWidth;
+	}
+
+	if (mHeaderIcon && mHeaderIcon->GetResource())
+	{
+		mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
+		mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
+		if (mHeaderIconHeight > mHeaderH)
+			mHeaderH = mHeaderIconHeight;
+		if (mHeaderIconWidth > mIconWidth)
+			mIconWidth = mHeaderIconWidth;
+	}
+
+	mHeaderH += mLineSpacing + mHeaderSeparatorH;
+	actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
+	if (mHeaderH < actualLineHeight)
+		mHeaderH = actualLineHeight;
+
+	if (actualLineHeight / 3 > 6)
+		touchDebounce = actualLineHeight / 3;
+
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundW = gr_get_width(mBackground->GetResource());
+		mBackgroundH = gr_get_height(mBackground->GetResource());
+	}
+
+	child = node->first_node("listtype");
+	if (child) {
+		attr = child->first_attribute("name");
+		if (attr) {
+			ListType = attr->value();
+			PartitionManager.Get_Partition_List(ListType, &mList);
+		} else {
+			mList.clear();
+			LOGERR("No partition listtype name specified for partitionlist GUI element\n");
+			return;
+		}
+	} else {
+		mList.clear();
+		LOGERR("No partition listtype specified for partitionlist GUI element\n");
+		return;
+	}
+}
+
+GUIPartitionList::~GUIPartitionList()
+{
+}
+
+int GUIPartitionList::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	// First step, fill background
+	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
+	gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
+
+	// Next, render the background resource (if it exists)
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
+		mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
+		gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
+	}
+
+	// This tells us how many lines we can actually render
+	int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+	int line;
+
+	if (updateList) {
+		mList.clear();
+		PartitionManager.Get_Partition_List(ListType, &mList);
+		updateList = false;
+		if (ListType == "backup")
+			MatchList();
+	}
+
+	int listSize = mList.size();
+	int listW = mRenderW;
+
+	if (listSize < lines) {
+		lines = listSize;
+		scrollingY = 0;
+		mFastScrollRectX = mFastScrollRectY = -1;
+	} else {
+		lines++;
+		if (lines < listSize)
+			lines++;
+		if (listSize >= lines)
+			listW -= mFastScrollW; // space for fast scrollbar
+		else
+			mFastScrollRectX = mFastScrollRectY = -1; // no fast scrollbar
+	}
+
+	void* fontResource = NULL;
+	if (mFont)  fontResource = mFont->GetResource();
+
+	int yPos = mRenderY + mHeaderH + scrollingY;
+	int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
+	int currentIconHeight = 0, currentIconWidth = 0;
+	int currentIconOffsetY = 0, currentIconOffsetX = 0;
+	int UnselectedIconOffsetY = (int)((actualLineHeight - mUnselectedIconHeight) / 2), SelectedIconOffsetY = (int)((actualLineHeight - mSelectedIconHeight) / 2);
+	int UnselectedIconOffsetX = (mIconWidth - mUnselectedIconWidth) / 2, SelectedIconOffsetX = (mIconWidth - mSelectedIconWidth) / 2;
+	int actualSelection = mStart;
+
+	if (isHighlighted) {
+		int selectY = scrollingY;
+
+		// Locate the correct line for highlighting
+		while (selectY + actualLineHeight < startSelection) {
+			selectY += actualLineHeight;
+			actualSelection++;
+		}
+		if (hasHighlightColor) {
+			// Highlight the area
+			gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
+			int HighlightHeight = actualLineHeight;
+			if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
+				HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
+			}
+			gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
+		}
+	}
+
+	for (line = 0; line < lines; line++)
+	{
+		Resource* icon;
+		std::string label;
+
+		if (line + mStart >= listSize)
+			continue;
+
+		label = mList.at(line + mStart).Display_Name;
+		if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
+			// Use the highlight color for the font
+			gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
+		} else {
+			// Set the color for the font
+			gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
+		}
+
+		if (mList.at(line + mStart).selected != 0)
+		{
+			icon = mIconSelected;
+			currentIconHeight = mSelectedIconHeight;
+			currentIconWidth = mSelectedIconWidth;
+			currentIconOffsetY = SelectedIconOffsetY;
+			currentIconOffsetX = SelectedIconOffsetX;
+		}
+		else
+		{
+			icon = mIconUnselected;
+			currentIconHeight = mSelectedIconHeight;
+			currentIconWidth = mSelectedIconWidth;
+			currentIconOffsetY = SelectedIconOffsetY;
+			currentIconOffsetX = SelectedIconOffsetX;
+		}
+
+		if (icon && icon->GetResource())
+		{
+			int rect_y = 0, image_y = (yPos + currentIconOffsetY);
+			if (image_y + currentIconHeight > mRenderY + mRenderH)
+				rect_y = mRenderY + mRenderH - image_y;
+			else
+				rect_y = currentIconHeight;
+			gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
+		}
+		gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
+
+		// Add the separator
+		if (yPos + actualLineHeight < mRenderH + mRenderY) {
+			gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
+			gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
+		}
+
+		// Move the yPos
+		yPos += actualLineHeight;
+	}
+
+	// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
+	// First step, fill background
+	gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
+	gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
+
+	// Now, we need the header (icon + text)
+	yPos = mRenderY;
+	{
+		Resource* headerIcon;
+		int mIconOffsetX = 0;
+
+		// render the icon if it exists
+		headerIcon = mHeaderIcon;
+		if (headerIcon && headerIcon->GetResource())
+		{
+			gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
+			mIconOffsetX = mIconWidth;
+		}
+
+		// render the text
+		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
+		gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
+
+		// Add the separator
+		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
+		gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
+	}
+
+	// render fast scroll
+	lines = (mRenderH - mHeaderH) / (actualLineHeight);
+	if(mFastScrollW > 0 && listSize > lines)
+	{
+		int startX = listW + mRenderX;
+		int fWidth = mRenderW - listW;
+		int fHeight = mRenderH - mHeaderH;
+
+		// line
+		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
+		gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
+
+		// rect
+		int pct = ((mStart*actualLineHeight - scrollingY)*100)/((listSize)*actualLineHeight-lines*actualLineHeight);
+		mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
+		mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
+
+		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
+		gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
+	}
+
+	mUpdate = 0;
+	return 0;
+}
+
+int GUIPartitionList::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mHeaderIsStatic) {
+		std::string newValue = gui_parse_text(mHeaderText);
+		if (mLastValue != newValue) {
+			mLastValue = newValue;
+			mUpdate = 1;
+		}
+	}
+
+	// Check for changes in mount points if the list type is mount and update the list and render if needed
+	if (ListType == "mount") {
+		int listSize = mList.size();
+		for (int i = 0; i < listSize; i++) {
+			if (PartitionManager.Is_Mounted_By_Path(mList.at(i).Mount_Point) && !mList.at(i).selected) {
+				mList.at(i).selected = 1;
+				mUpdate = 1;
+			} else if (!PartitionManager.Is_Mounted_By_Path(mList.at(i).Mount_Point) && mList.at(i).selected) {
+				mList.at(i).selected = 0;
+				mUpdate = 1;
+			}
+		}
+	}
+
+	if (mUpdate)
+	{
+		mUpdate = 0;
+		if (Render() == 0)
+			return 2;
+	}
+
+	// Handle kinetic scrolling
+	if (scrollingSpeed == 0) {
+		// Do nothing
+	} else if (scrollingSpeed > 0) {
+		if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
+			scrollingY += scrollingSpeed;
+			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
+		} else {
+			scrollingY += ((int) (actualLineHeight * 2.5));
+			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
+		}
+		while (mStart && scrollingY > 0) {
+			mStart--;
+			scrollingY -= actualLineHeight;
+		}
+		if (mStart == 0 && scrollingY > 0) {
+			scrollingY = 0;
+			scrollingSpeed = 0;
+		} else if (scrollingSpeed < SCROLLING_FLOOR)
+			scrollingSpeed = 0;
+		mUpdate = 1;
+	} else if (scrollingSpeed < 0) {
+		int totalSize = mList.size();
+		int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+
+		if (totalSize > lines) {
+			int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
+
+			bottom_offset -= actualLineHeight;
+
+			if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
+				scrollingY += scrollingSpeed;
+				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
+			} else {
+				scrollingY -= ((int) (actualLineHeight * 2.5));
+				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
+			}
+			while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
+				mStart++;
+				scrollingY += actualLineHeight;
+			}
+			if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
+				mStart = totalSize - lines - 1;
+				scrollingY = bottom_offset;
+			} else if (mStart + lines >= totalSize && scrollingY < 0) {
+				mStart = totalSize - lines;
+				scrollingY = 0;
+			} else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
+				scrollingSpeed = 0;
+			mUpdate = 1;
+		}
+	}
+
+	return 0;
+}
+
+int GUIPartitionList::GetSelection(int x, int y)
+{
+	// We only care about y position
+	if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH) return -1;
+	return (y - mRenderY - mHeaderH);
+}
+
+int GUIPartitionList::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if(!isConditionTrue())
+		return -1;
+
+	static int lastY = 0, last2Y = 0;
+	int selection = 0;
+
+	switch (state)
+	{
+	case TOUCH_START:
+		if (scrollingSpeed != 0)
+			startSelection = -1;
+		else
+			startSelection = GetSelection(x,y);
+		isHighlighted = (startSelection > -1);
+		if (isHighlighted)
+			mUpdate = 1;
+		startY = lastY = last2Y = y;
+		scrollingSpeed = 0;
+		break;
+
+	case TOUCH_DRAG:
+		// Check if we dragged out of the selection window
+		if (GetSelection(x, y) == -1) {
+			last2Y = lastY = 0;
+			if (isHighlighted) {
+				isHighlighted = false;
+				mUpdate = 1;
+			}
+			break;
+		}
+
+		// Fast scroll
+		if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
+		{
+			int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
+			int totalSize = mList.size();
+			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+
+			float l = float((totalSize-lines)*pct)/100;
+			if(l + lines >= totalSize)
+			{
+				mStart = totalSize - lines;
+				scrollingY = 0;
+			}
+			else
+			{
+				mStart = l;
+				scrollingY = -(l - int(l))*actualLineHeight;
+			}
+
+			startSelection = -1;
+			mUpdate = 1;
+			scrollingSpeed = 0;
+			isHighlighted = false;
+			break;
+		}
+
+		// Provide some debounce on initial touches
+		if (startSelection != -1 && abs(y - startY) < touchDebounce) {
+			isHighlighted = true;
+			mUpdate = 1;
+			break;
+		}
+
+		isHighlighted = false;
+		last2Y = lastY;
+		lastY = y;
+		startSelection = -1;
+
+		// Handle scrolling
+		scrollingY += y - startY;
+		startY = y;
+		while(mStart && scrollingY > 0) {
+			mStart--;
+			scrollingY -= actualLineHeight;
+		}
+		if (mStart == 0 && scrollingY > 0)
+			scrollingY = 0;
+		{
+			int totalSize = mList.size();
+			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
+
+			if (totalSize > lines) {
+				int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
+
+				bottom_offset -= actualLineHeight;
+
+				while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
+					mStart++;
+					scrollingY += actualLineHeight;
+				}
+				if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
+					mStart = totalSize - lines - 1;
+					scrollingY = bottom_offset;
+				} else if (mStart + lines >= totalSize && scrollingY < 0) {
+					mStart = totalSize - lines;
+					scrollingY = 0;
+				}
+			} else
+				scrollingY = 0;
+		}
+		mUpdate = 1;
+		break;
+
+	case TOUCH_RELEASE:
+		isHighlighted = false;
+		if (startSelection >= 0)
+		{
+			// We've selected an item!
+			int listSize = mList.size();
+			int selectY = scrollingY, actualSelection = mStart;
+
+			// Move the selection to the proper place in the array
+			while (selectY + actualLineHeight < startSelection) {
+				selectY += actualLineHeight;
+				actualSelection++;
+			}
+
+			if (actualSelection < listSize && ListType == "mount") {
+				DataManager::Vibrate("tw_button_vibrate");
+
+				if (!mList.at(actualSelection).selected) {
+					if (PartitionManager.Mount_By_Path(mList.at(actualSelection).Mount_Point, true)) {
+						mList.at(actualSelection).selected = 1;
+						mUpdate = 1;
+					}
+				} else {
+					if (PartitionManager.UnMount_By_Path(mList.at(actualSelection).Mount_Point, true)) {
+						mList.at(actualSelection).selected = 0;
+						mUpdate = 1;
+					}
+				}
+			} else if (actualSelection < listSize && !mVariable.empty()) {
+				DataManager::Vibrate("tw_button_vibrate");
+
+				if (ListType == "storage") {
+					int i;
+					std::string str = mList.at(actualSelection).Mount_Point;
+					bool update_size = false;
+					TWPartition* Part = PartitionManager.Find_Partition_By_Path(str);
+					if (Part == NULL) {
+						LOGERR("Unable to locate partition for '%s'\n", str.c_str());
+						return 0;
+					}
+					if (!Part->Is_Mounted() && Part->Removable)
+						update_size = true;
+					if (!Part->Mount(true)) {
+						// Do Nothing
+					} else if (update_size && !Part->Update_Size(true)) {
+						// Do Nothing
+					} else {
+						for (i=0; i<listSize; i++)
+							mList.at(i).selected = 0;
+
+						if (update_size) {
+							char free_space[255];
+							sprintf(free_space, "%llu", Part->Free / 1024 / 1024);
+							mList.at(actualSelection).Display_Name = Part->Storage_Name + " (";
+							mList.at(actualSelection).Display_Name += free_space;
+							mList.at(actualSelection).Display_Name += "MB)";
+						}
+						mList.at(actualSelection).selected = 1;
+						mUpdate = 1;
+
+						DataManager::SetValue(mVariable, str);
+					}
+				} else {
+					if (mList.at(actualSelection).selected)
+						mList.at(actualSelection).selected = 0;
+					else
+						mList.at(actualSelection).selected = 1;
+
+					int i;
+					string variablelist;
+					for (i=0; i<listSize; i++) {
+						if (mList.at(i).selected) {
+							variablelist += mList.at(i).Mount_Point + ";";
+						}
+					}
+
+					mUpdate = 1;
+					if (selectedList.empty())
+						DataManager::SetValue(mVariable, variablelist);
+					else
+						DataManager::SetValue(selectedList, variablelist);
+				}
+			}
+		} else {
+			// This is for kinetic scrolling
+			scrollingSpeed = lastY - last2Y;
+			if (abs(scrollingSpeed) > SCROLLING_FLOOR)
+				scrollingSpeed *= SCROLLING_MULTIPLIER;
+			else
+				scrollingSpeed = 0;
+		}
+	case TOUCH_REPEAT:
+	case TOUCH_HOLD:
+		break;
+	}
+	return 0;
+}
+
+int GUIPartitionList::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIObject::NotifyVarChange(varName, value);
+
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mHeaderIsStatic) {
+		std::string newValue = gui_parse_text(mHeaderText);
+		if (mLastValue != newValue) {
+			mLastValue = newValue;
+			mStart = 0;
+			scrollingY = 0;
+			scrollingSpeed = 0;
+			mUpdate = 1;
+		}
+	}
+	if (varName == mVariable && !mUpdate)
+	{
+		if (ListType == "storage") {
+			int i, listSize = mList.size(), selected_index = 0;
+
+			currentValue = value;
+
+			for (i=0; i<listSize; i++) {
+				if (mList.at(i).Mount_Point == currentValue) {
+					mList.at(i).selected = 1;
+					selected_index = i;
+				} else
+					mList.at(i).selected = 0;
+			}
+
+			int lines = mRenderH / (mLineHeight + mLineSpacing);
+			int line;
+
+			if (selected_index > mStart + lines - 1) {
+				mStart = selected_index;
+			} else if (selected_index < mStart) {
+				mStart = selected_index;
+			}
+		} else if (ListType == "backup") {
+			MatchList();
+		} else if (ListType == "restore") {
+			updateList = true;
+		}
+
+		mUpdate = 1;
+		return 0;
+	}
+	return 0;
+}
+
+int GUIPartitionList::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
+{
+	mRenderX = x;
+	mRenderY = y;
+	if (w || h)
+	{
+		mRenderW = w;
+		mRenderH = h;
+	}
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	mUpdate = 1;
+	return 0;
+}
+
+void GUIPartitionList::SetPageFocus(int inFocus)
+{
+	if (inFocus) {
+		if (ListType == "storage") {
+			int i, listSize = mList.size(), selected_index = 0;
+
+			DataManager::GetValue(mVariable, currentValue);
+
+			for (i=0; i<listSize; i++) {
+				if (mList.at(i).Mount_Point == currentValue) {
+					mList.at(i).selected = 1;
+					selected_index = i;
+				} else
+					mList.at(i).selected = 0;
+			}
+
+			int lines = mRenderH / (mLineHeight + mLineSpacing);
+			int line;
+
+			if (selected_index > mStart + lines - 1) {
+				mStart = selected_index;
+			} else if (selected_index < mStart) {
+				mStart = selected_index;
+			}
+		}
+		updateList = true;
+		mUpdate = 1;
+	}
+}
+
+void GUIPartitionList::MatchList(void) {
+	int i, listSize = mList.size();
+	string variablelist, searchvalue;
+	size_t pos;
+
+	DataManager::GetValue(mVariable, variablelist);
+
+	for (i=0; i<listSize; i++) {
+		searchvalue = mList.at(i).Mount_Point + ";";
+		pos = variablelist.find(searchvalue);
+		if (pos != string::npos) {
+			mList.at(i).selected = 1;
+		} else {
+			mList.at(i).selected = 0;
+		}
+	}
+}
diff --git a/gui/progressbar.cpp b/gui/progressbar.cpp
new file mode 100644
index 0000000..a4d1329
--- /dev/null
+++ b/gui/progressbar.cpp
@@ -0,0 +1,241 @@
+// progressbar.cpp - GUIProgressBar object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUIProgressBar::GUIProgressBar(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mEmptyBar = NULL;
+	mFullBar = NULL;
+	mLastPos = 0;
+	mSlide = 0.0;
+	mSlideInc = 0.0;
+
+	if (!node)
+	{
+		LOGERR("GUIProgressBar created without XML node\n");
+		return;
+	}
+
+	child = node->first_node("resource");
+	if (child)
+	{
+		attr = child->first_attribute("empty");
+		if (attr)
+			mEmptyBar = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("full");
+		if (attr)
+			mFullBar = PageManager::FindResource(attr->value());
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY);
+
+	// Load the data
+	child = node->first_node("data");
+	if (child)
+	{
+		attr = child->first_attribute("min");
+		if (attr)   mMinValVar = attr->value();
+
+		attr = child->first_attribute("max");
+		if (attr)   mMaxValVar = attr->value();
+
+		attr = child->first_attribute("name");
+		if (attr)   mCurValVar = attr->value();
+	}
+
+	if (mEmptyBar && mEmptyBar->GetResource())
+	{
+		mRenderW = gr_get_width(mEmptyBar->GetResource());
+		mRenderH = gr_get_height(mEmptyBar->GetResource());
+	}
+
+	return;
+}
+
+int GUIProgressBar::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	// This handles making sure timing updates occur
+	Update();
+	return RenderInternal();
+}
+
+int GUIProgressBar::RenderInternal(void)
+{
+	if (!mEmptyBar || !mEmptyBar->GetResource())
+		return -1;
+
+	if (!mFullBar || !mFullBar->GetResource())
+		return -1;
+
+	gr_blit(mEmptyBar->GetResource(), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
+	gr_blit(mFullBar->GetResource(), 0, 0, mLastPos, mRenderH, mRenderX, mRenderY);
+	return 0;
+}
+
+int GUIProgressBar::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	std::string str;
+	int min, max, cur, pos;
+
+	if (mMinValVar.empty())
+		min = 0;
+	else
+	{
+		str.clear();
+		if (atoi(mMinValVar.c_str()) != 0)
+			str = mMinValVar;
+		else
+			DataManager::GetValue(mMinValVar, str);
+		min = atoi(str.c_str());
+	}
+
+	if (mMaxValVar.empty())
+		max = 100;
+	else
+	{
+		str.clear();
+		if (atoi(mMaxValVar.c_str()) != 0)
+			str = mMaxValVar;
+		else
+			DataManager::GetValue(mMaxValVar, str);
+		max = atoi(str.c_str());
+	}
+
+	str.clear();
+	DataManager::GetValue(mCurValVar, str);
+	cur = atoi(str.c_str());
+
+	// Do slide, if needed
+	if (mSlideFrames)
+	{
+		mSlide += mSlideInc;
+		mSlideFrames--;
+		if (cur != (int) mSlide)
+		{
+			cur = (int) mSlide;
+			DataManager::SetValue(mCurValVar, cur);
+		}
+	}
+
+	// Normalize to 0
+	max -= min;
+	cur -= min;
+	min = 0;
+
+	if (cur < min)
+		cur = min;
+	if (cur > max)
+		cur = max;
+
+	if (max == 0)
+		pos = 0;
+	else
+		pos = (cur * mRenderW) / max;
+
+	if (pos == mLastPos)
+		return 0;
+
+	mLastPos = pos;
+
+	if (RenderInternal() != 0)
+		return -1;
+	return 2;
+}
+
+int GUIProgressBar::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIObject::NotifyVarChange(varName, value);
+
+	if(!isConditionTrue())
+		return 0;
+
+	static int nextPush = 0;
+
+	if (varName.empty())
+	{
+		nextPush = 0;
+		mLastPos = 0;
+		mSlide = 0.0;
+		mSlideInc = 0.0;
+		return 0;
+	}
+
+	if (varName == "ui_progress_portion" || varName == "ui_progress_frames")
+	{
+		std::string str;
+		int cur;
+
+		if (mSlideFrames)
+		{
+			mSlide += (mSlideInc * mSlideFrames);
+			cur = (int) mSlide;
+			DataManager::SetValue(mCurValVar, cur);
+			mSlideFrames = 0;
+		}
+
+		if (nextPush)
+		{
+			mSlide += nextPush;
+			cur = (int) mSlide;
+			DataManager::SetValue(mCurValVar, cur);
+			nextPush = 0;
+		}
+
+		if (varName == "ui_progress_portion")   mSlide = atof(value.c_str());
+		else
+		{
+			mSlideFrames = atol(value.c_str());
+			if (mSlideFrames == 0)
+			{
+				// We're just holding this progress until the next push
+				nextPush = mSlide;
+			}
+		}
+
+		if (mSlide > 0 && mSlideFrames > 0)
+		{
+			// Get the current position
+			str.clear();
+			DataManager::GetValue(mCurValVar, str);
+			cur = atoi(str.c_str());
+
+			mSlideInc = (float) mSlide / (float) mSlideFrames;
+			mSlide = cur;
+		}
+	}
+	return 0;
+}
diff --git a/gui/rapidxml.hpp b/gui/rapidxml.hpp
new file mode 100644
index 0000000..c0a7be4
--- /dev/null
+++ b/gui/rapidxml.hpp
@@ -0,0 +1,2616 @@
+#ifndef RAPIDXML_HPP_INCLUDED

+#define RAPIDXML_HPP_INCLUDED

+

+#define RAPIDXML_NO_EXCEPTIONS

+

+// Copyright (C) 2006, 2009 Marcin Kalicinski

+// Version 1.13

+// Revision $DateTime: 2009/05/13 01:46:17 $

+//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation

+

+// If standard library is disabled, user must provide implementations of required functions and typedefs

+#if !defined(RAPIDXML_NO_STDLIB)

+    #include <cstdlib>      // For std::size_t

+    #include <cassert>      // For assert

+    #include <new>          // For placement new

+#endif

+

+// On MSVC, disable "conditional expression is constant" warning (level 4). 

+// This warning is almost impossible to avoid with certain types of templated code

+#ifdef _MSC_VER

+    #pragma warning(push)

+    #pragma warning(disable:4127)   // Conditional expression is constant

+#endif

+

+///////////////////////////////////////////////////////////////////////////

+// RAPIDXML_PARSE_ERROR

+    

+#if defined(RAPIDXML_NO_EXCEPTIONS)

+

+#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); }

+

+namespace rapidxml

+{

+    //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, 

+    //! this function is called to notify user about the error.

+    //! It must be defined by the user.

+    //! <br><br>

+    //! This function cannot return. If it does, the results are undefined.

+    //! <br><br>

+    //! A very simple definition might look like that:

+    //! <pre>

+    //! void %rapidxml::%parse_error_handler(const char *what, void *where)

+    //! {

+    //!     std::cout << "Parse error: " << what << "\n";

+    //!     std::abort();

+    //! }

+    //! </pre>

+    //! \param what Human readable description of the error.

+    //! \param where Pointer to character data where error was detected.

+    void parse_error_handler(const char *what, void *where);

+}

+

+#else

+    

+#include <exception>    // For std::exception

+

+#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where)

+

+namespace rapidxml

+{

+

+    //! Parse error exception. 

+    //! This exception is thrown by the parser when an error occurs. 

+    //! Use what() function to get human-readable error message. 

+    //! Use where() function to get a pointer to position within source text where error was detected.

+    //! <br><br>

+    //! If throwing exceptions by the parser is undesirable, 

+    //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included.

+    //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception.

+    //! This function must be defined by the user.

+    //! <br><br>

+    //! This class derives from <code>std::exception</code> class.

+    class parse_error: public std::exception

+    {

+    

+    public:

+    

+        //! Constructs parse error

+        parse_error(const char *what, void *where)

+            : m_what(what)

+            , m_where(where)

+        {

+        }

+

+        //! Gets human readable description of error.

+        //! \return Pointer to null terminated description of the error.

+        virtual const char *what() const throw()

+        {

+            return m_what;

+        }

+

+        //! Gets pointer to character data where error happened.

+        //! Ch should be the same as char type of xml_document that produced the error.

+        //! \return Pointer to location within the parsed string where error occured.

+        template<class Ch>

+        Ch *where() const

+        {

+            return reinterpret_cast<Ch *>(m_where);

+        }

+

+    private:  

+

+        const char *m_what;

+        void *m_where;

+

+    };

+}

+

+#endif

+

+///////////////////////////////////////////////////////////////////////////

+// Pool sizes

+

+#ifndef RAPIDXML_STATIC_POOL_SIZE

+    // Size of static memory block of memory_pool.

+    // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value.

+    // No dynamic memory allocations are performed by memory_pool until static memory is exhausted.

+    #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024)

+#endif

+

+#ifndef RAPIDXML_DYNAMIC_POOL_SIZE

+    // Size of dynamic memory block of memory_pool.

+    // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value.

+    // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool.

+    #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024)

+#endif

+

+#ifndef RAPIDXML_ALIGNMENT

+    // Memory allocation alignment.

+    // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer.

+    // All memory allocations for nodes, attributes and strings will be aligned to this value.

+    // This must be a power of 2 and at least 1, otherwise memory_pool will not work.

+    #define RAPIDXML_ALIGNMENT sizeof(void *)

+#endif

+

+namespace rapidxml

+{

+    // Forward declarations

+    template<class Ch> class xml_node;

+    template<class Ch> class xml_attribute;

+    template<class Ch> class xml_document;

+    

+    //! Enumeration listing all node types produced by the parser.

+    //! Use xml_node::type() function to query node type.

+    enum node_type

+    {

+        node_document,      //!< A document node. Name and value are empty.

+        node_element,       //!< An element node. Name contains element name. Value contains text of first data node.

+        node_data,          //!< A data node. Name is empty. Value contains data text.

+        node_cdata,         //!< A CDATA node. Name is empty. Value contains data text.

+        node_comment,       //!< A comment node. Name is empty. Value contains comment text.

+        node_declaration,   //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes.

+        node_doctype,       //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text.

+        node_pi             //!< A PI node. Name contains target. Value contains instructions.

+    };

+

+    ///////////////////////////////////////////////////////////////////////

+    // Parsing flags

+

+    //! Parse flag instructing the parser to not create data nodes. 

+    //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_no_data_nodes = 0x1;            

+

+    //! Parse flag instructing the parser to not use text of first data node as a value of parent element.

+    //! Can be combined with other flags by use of | operator.

+    //! Note that child data nodes of element node take precendence over its value when printing. 

+    //! That is, if element has one or more child data nodes <em>and</em> a value, the value will be ignored.

+    //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_no_element_values = 0x2;

+    

+    //! Parse flag instructing the parser to not place zero terminators after strings in the source text.

+    //! By default zero terminators are placed, modifying source text.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_no_string_terminators = 0x4;

+    

+    //! Parse flag instructing the parser to not translate entities in the source text.

+    //! By default entities are translated, modifying source text.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_no_entity_translation = 0x8;

+    

+    //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters.

+    //! By default, UTF-8 handling is enabled.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_no_utf8 = 0x10;

+    

+    //! Parse flag instructing the parser to create XML declaration node.

+    //! By default, declaration node is not created.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_declaration_node = 0x20;

+    

+    //! Parse flag instructing the parser to create comments nodes.

+    //! By default, comment nodes are not created.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_comment_nodes = 0x40;

+    

+    //! Parse flag instructing the parser to create DOCTYPE node.

+    //! By default, doctype node is not created.

+    //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_doctype_node = 0x80;

+    

+    //! Parse flag instructing the parser to create PI nodes.

+    //! By default, PI nodes are not created.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_pi_nodes = 0x100;

+    

+    //! Parse flag instructing the parser to validate closing tag names. 

+    //! If not set, name inside closing tag is irrelevant to the parser.

+    //! By default, closing tags are not validated.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_validate_closing_tags = 0x200;

+    

+    //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes.

+    //! By default, whitespace is not trimmed. 

+    //! This flag does not cause the parser to modify source text.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_trim_whitespace = 0x400;

+

+    //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character.

+    //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag.

+    //! By default, whitespace is not normalized. 

+    //! If this flag is specified, source text will be modified.

+    //! Can be combined with other flags by use of | operator.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_normalize_whitespace = 0x800;

+

+    // Compound flags

+    

+    //! Parse flags which represent default behaviour of the parser. 

+    //! This is always equal to 0, so that all other flags can be simply ored together.

+    //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values.

+    //! This also means that meaning of each flag is a <i>negation</i> of the default setting. 

+    //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is <i>enabled</i> by default,

+    //! and using the flag will disable it.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_default = 0;

+    

+    //! A combination of parse flags that forbids any modifications of the source text. 

+    //! This also results in faster parsing. However, note that the following will occur:

+    //! <ul>

+    //! <li>names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends</li>

+    //! <li>entities will not be translated</li>

+    //! <li>whitespace will not be normalized</li>

+    //! </ul>

+    //! See xml_document::parse() function.

+    const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation;

+    

+    //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_fastest = parse_non_destructive | parse_no_data_nodes;

+    

+    //! A combination of parse flags resulting in largest amount of data being extracted. 

+    //! This usually results in slowest parsing.

+    //! <br><br>

+    //! See xml_document::parse() function.

+    const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags;

+

+    ///////////////////////////////////////////////////////////////////////

+    // Internals

+

+    //! \cond internal

+    namespace internal

+    {

+

+        // Struct that contains lookup tables for the parser

+        // It must be a template to allow correct linking (because it has static data members, which are defined in a header file).

+        template<int Dummy>

+        struct lookup_tables

+        {

+            static const unsigned char lookup_whitespace[256];              // Whitespace table

+            static const unsigned char lookup_node_name[256];               // Node name table

+            static const unsigned char lookup_text[256];                    // Text table

+            static const unsigned char lookup_text_pure_no_ws[256];         // Text table

+            static const unsigned char lookup_text_pure_with_ws[256];       // Text table

+            static const unsigned char lookup_attribute_name[256];          // Attribute name table

+            static const unsigned char lookup_attribute_data_1[256];        // Attribute data table with single quote

+            static const unsigned char lookup_attribute_data_1_pure[256];   // Attribute data table with single quote

+            static const unsigned char lookup_attribute_data_2[256];        // Attribute data table with double quotes

+            static const unsigned char lookup_attribute_data_2_pure[256];   // Attribute data table with double quotes

+            static const unsigned char lookup_digits[256];                  // Digits

+            static const unsigned char lookup_upcase[256];                  // To uppercase conversion table for ASCII characters

+        };

+

+        // Find length of the string

+        template<class Ch>

+        inline std::size_t measure(const Ch *p)

+        {

+            const Ch *tmp = p;

+            while (*tmp) 

+                ++tmp;

+            return tmp - p;

+        }

+

+        // Compare strings for equality

+        template<class Ch>

+        inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive)

+        {

+            if (size1 != size2)

+                return false;

+            if (case_sensitive)

+            {

+                for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2)

+                    if (*p1 != *p2)

+                        return false;

+            }

+            else

+            {

+                for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2)

+                    if (lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p1)] != lookup_tables<0>::lookup_upcase[static_cast<unsigned char>(*p2)])

+                        return false;

+            }

+            return true;

+        }

+    }

+    //! \endcond

+

+    ///////////////////////////////////////////////////////////////////////

+    // Memory pool

+    

+    //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation.

+    //! In most cases, you will not need to use this class directly. 

+    //! However, if you need to create nodes manually or modify names/values of nodes, 

+    //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. 

+    //! Not only is this faster than allocating them by using <code>new</code> operator, 

+    //! but also their lifetime will be tied to the lifetime of document, 

+    //! possibly simplyfing memory management. 

+    //! <br><br>

+    //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. 

+    //! You can also call allocate_string() function to allocate strings.

+    //! Such strings can then be used as names or values of nodes without worrying about their lifetime.

+    //! Note that there is no <code>free()</code> function -- all allocations are freed at once when clear() function is called, 

+    //! or when the pool is destroyed.

+    //! <br><br>

+    //! It is also possible to create a standalone memory_pool, and use it 

+    //! to allocate nodes, whose lifetime will not be tied to any document.

+    //! <br><br>

+    //! Pool maintains <code>RAPIDXML_STATIC_POOL_SIZE</code> bytes of statically allocated memory. 

+    //! Until static memory is exhausted, no dynamic memory allocations are done.

+    //! When static memory is exhausted, pool allocates additional blocks of memory of size <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> each,

+    //! by using global <code>new[]</code> and <code>delete[]</code> operators. 

+    //! This behaviour can be changed by setting custom allocation routines. 

+    //! Use set_allocator() function to set them.

+    //! <br><br>

+    //! Allocations for nodes, attributes and strings are aligned at <code>RAPIDXML_ALIGNMENT</code> bytes.

+    //! This value defaults to the size of pointer on target architecture.

+    //! <br><br>

+    //! To obtain absolutely top performance from the parser,

+    //! it is important that all nodes are allocated from a single, contiguous block of memory.

+    //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably.

+    //! If required, you can tweak <code>RAPIDXML_STATIC_POOL_SIZE</code>, <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> and <code>RAPIDXML_ALIGNMENT</code> 

+    //! to obtain best wasted memory to performance compromise.

+    //! To do it, define their values before rapidxml.hpp file is included.

+    //! \param Ch Character type of created nodes. 

+    template<class Ch = char>

+    class memory_pool

+    {

+        

+    public:

+

+        //! \cond internal

+        typedef void *(alloc_func)(std::size_t);       // Type of user-defined function used to allocate memory

+        typedef void (free_func)(void *);              // Type of user-defined function used to free memory

+        //! \endcond

+        

+        //! Constructs empty pool with default allocator functions.

+        memory_pool()

+            : m_alloc_func(0)

+            , m_free_func(0)

+        {

+            init();

+        }

+

+        //! Destroys pool and frees all the memory. 

+        //! This causes memory occupied by nodes allocated by the pool to be freed.

+        //! Nodes allocated from the pool are no longer valid.

+        ~memory_pool()

+        {

+            clear();

+        }

+

+        //! Allocates a new node from the pool, and optionally assigns name and value to it. 

+        //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.

+        //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function

+        //! will call rapidxml::parse_error_handler() function.

+        //! \param type Type of node to create.

+        //! \param name Name to assign to the node, or 0 to assign no name.

+        //! \param value Value to assign to the node, or 0 to assign no value.

+        //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string.

+        //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string.

+        //! \return Pointer to allocated node. This pointer will never be NULL.

+        xml_node<Ch> *allocate_node(node_type type, 

+                                    const Ch *name = 0, const Ch *value = 0, 

+                                    std::size_t name_size = 0, std::size_t value_size = 0)

+        {

+            void *memory = allocate_aligned(sizeof(xml_node<Ch>));

+            xml_node<Ch> *node = new(memory) xml_node<Ch>(type);

+            if (name)

+            {

+                if (name_size > 0)

+                    node->name(name, name_size);

+                else

+                    node->name(name);

+            }

+            if (value)

+            {

+                if (value_size > 0)

+                    node->value(value, value_size);

+                else

+                    node->value(value);

+            }

+            return node;

+        }

+

+        //! Allocates a new attribute from the pool, and optionally assigns name and value to it.

+        //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.

+        //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function

+        //! will call rapidxml::parse_error_handler() function.

+        //! \param name Name to assign to the attribute, or 0 to assign no name.

+        //! \param value Value to assign to the attribute, or 0 to assign no value.

+        //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string.

+        //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string.

+        //! \return Pointer to allocated attribute. This pointer will never be NULL.

+        xml_attribute<Ch> *allocate_attribute(const Ch *name = 0, const Ch *value = 0, 

+                                              std::size_t name_size = 0, std::size_t value_size = 0)

+        {

+            void *memory = allocate_aligned(sizeof(xml_attribute<Ch>));

+            xml_attribute<Ch> *attribute = new(memory) xml_attribute<Ch>;

+            if (name)

+            {

+                if (name_size > 0)

+                    attribute->name(name, name_size);

+                else

+                    attribute->name(name);

+            }

+            if (value)

+            {

+                if (value_size > 0)

+                    attribute->value(value, value_size);

+                else

+                    attribute->value(value);

+            }

+            return attribute;

+        }

+

+        //! Allocates a char array of given size from the pool, and optionally copies a given string to it.

+        //! If the allocation request cannot be accomodated, this function will throw <code>std::bad_alloc</code>.

+        //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function

+        //! will call rapidxml::parse_error_handler() function.

+        //! \param source String to initialize the allocated memory with, or 0 to not initialize it.

+        //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated.

+        //! \return Pointer to allocated char array. This pointer will never be NULL.

+        Ch *allocate_string(const Ch *source = 0, std::size_t size = 0)

+        {

+            assert(source || size);     // Either source or size (or both) must be specified

+            if (size == 0)

+                size = internal::measure(source) + 1;

+            Ch *result = static_cast<Ch *>(allocate_aligned(size * sizeof(Ch)));

+            if (source)

+                for (std::size_t i = 0; i < size; ++i)

+                    result[i] = source[i];

+            return result;

+        }

+

+        //! Clones an xml_node and its hierarchy of child nodes and attributes.

+        //! Nodes and attributes are allocated from this memory pool.

+        //! Names and values are not cloned, they are shared between the clone and the source.

+        //! Result node can be optionally specified as a second parameter, 

+        //! in which case its contents will be replaced with cloned source node.

+        //! This is useful when you want to clone entire document.

+        //! \param source Node to clone.

+        //! \param result Node to put results in, or 0 to automatically allocate result node

+        //! \return Pointer to cloned node. This pointer will never be NULL.

+        xml_node<Ch> *clone_node(const xml_node<Ch> *source, xml_node<Ch> *result = 0)

+        {

+            // Prepare result node

+            if (result)

+            {

+                result->remove_all_attributes();

+                result->remove_all_nodes();

+                result->type(source->type());

+            }

+            else

+                result = allocate_node(source->type());

+

+            // Clone name and value

+            result->name(source->name(), source->name_size());

+            result->value(source->value(), source->value_size());

+

+            // Clone child nodes and attributes

+            for (xml_node<Ch> *child = source->first_node(); child; child = child->next_sibling())

+                result->append_node(clone_node(child));

+            for (xml_attribute<Ch> *attr = source->first_attribute(); attr; attr = attr->next_attribute())

+                result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size()));

+

+            return result;

+        }

+

+        //! Clears the pool. 

+        //! This causes memory occupied by nodes allocated by the pool to be freed.

+        //! Any nodes or strings allocated from the pool will no longer be valid.

+        void clear()

+        {

+            while (m_begin != m_static_memory)

+            {

+                char *previous_begin = reinterpret_cast<header *>(align(m_begin))->previous_begin;

+                if (m_free_func)

+                    m_free_func(m_begin);

+                else

+                    delete[] m_begin;

+                m_begin = previous_begin;

+            }

+            init();

+        }

+

+        //! Sets or resets the user-defined memory allocation functions for the pool.

+        //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined.

+        //! Allocation function must not return invalid pointer on failure. It should either throw,

+        //! stop the program, or use <code>longjmp()</code> function to pass control to other place of program. 

+        //! If it returns invalid pointer, results are undefined.

+        //! <br><br>

+        //! User defined allocation functions must have the following forms:

+        //! <br><code>

+        //! <br>void *allocate(std::size_t size);

+        //! <br>void free(void *pointer);

+        //! </code><br>

+        //! \param af Allocation function, or 0 to restore default function

+        //! \param ff Free function, or 0 to restore default function

+        void set_allocator(alloc_func *af, free_func *ff)

+        {

+            assert(m_begin == m_static_memory && m_ptr == align(m_begin));    // Verify that no memory is allocated yet

+            m_alloc_func = af;

+            m_free_func = ff;

+        }

+

+    private:

+

+        struct header

+        {

+            char *previous_begin;

+        };

+

+        void init()

+        {

+            m_begin = m_static_memory;

+            m_ptr = align(m_begin);

+            m_end = m_static_memory + sizeof(m_static_memory);

+        }

+        

+        char *align(char *ptr)

+        {

+            std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1));

+            return ptr + alignment;

+        }

+        

+        char *allocate_raw(std::size_t size)

+        {

+            // Allocate

+            void *memory;   

+            if (m_alloc_func)   // Allocate memory using either user-specified allocation function or global operator new[]

+            {

+                memory = m_alloc_func(size);

+                assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp

+            }

+            else

+            {

+                memory = new char[size];

+#ifdef RAPIDXML_NO_EXCEPTIONS

+                if (!memory)            // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc

+                    RAPIDXML_PARSE_ERROR("out of memory", 0);

+#endif

+            }

+            return static_cast<char *>(memory);

+        }

+        

+        void *allocate_aligned(std::size_t size)

+        {

+            // Calculate aligned pointer

+            char *result = align(m_ptr);

+

+            // If not enough memory left in current pool, allocate a new pool

+            if (result + size > m_end)

+            {

+                // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE)

+                std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE;

+                if (pool_size < size)

+                    pool_size = size;

+                

+                // Allocate

+                std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size;     // 2 alignments required in worst case: one for header, one for actual allocation

+                char *raw_memory = allocate_raw(alloc_size);

+                    

+                // Setup new pool in allocated memory

+                char *pool = align(raw_memory);

+                header *new_header = reinterpret_cast<header *>(pool);

+                new_header->previous_begin = m_begin;

+                m_begin = raw_memory;

+                m_ptr = pool + sizeof(header);

+                m_end = raw_memory + alloc_size;

+

+                // Calculate aligned pointer again using new pool

+                result = align(m_ptr);

+            }

+

+            // Update pool and return aligned pointer

+            m_ptr = result + size;

+            return result;

+        }

+

+        char *m_begin;                                      // Start of raw memory making up current pool

+        char *m_ptr;                                        // First free byte in current pool

+        char *m_end;                                        // One past last available byte in current pool

+        char m_static_memory[RAPIDXML_STATIC_POOL_SIZE];    // Static raw memory

+        alloc_func *m_alloc_func;                           // Allocator function, or 0 if default is to be used

+        free_func *m_free_func;                             // Free function, or 0 if default is to be used

+    };

+

+    ///////////////////////////////////////////////////////////////////////////

+    // XML base

+

+    //! Base class for xml_node and xml_attribute implementing common functions: 

+    //! name(), name_size(), value(), value_size() and parent().

+    //! \param Ch Character type to use

+    template<class Ch = char>

+    class xml_base

+    {

+

+    public:

+        

+        ///////////////////////////////////////////////////////////////////////////

+        // Construction & destruction

+    

+        // Construct a base with empty name, value and parent

+        xml_base()

+            : m_name(0)

+            , m_value(0)

+            , m_parent(0)

+        {

+        }

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Node data access

+    

+        //! Gets name of the node. 

+        //! Interpretation of name depends on type of node.

+        //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.

+        //! <br><br>

+        //! Use name_size() function to determine length of the name.

+        //! \return Name of node, or empty string if node has no name.

+        Ch *name() const

+        {

+            return m_name ? m_name : nullstr();

+        }

+

+        //! Gets size of node name, not including terminator character.

+        //! This function works correctly irrespective of whether name is or is not zero terminated.

+        //! \return Size of node name, in characters.

+        std::size_t name_size() const

+        {

+            return m_name ? m_name_size : 0;

+        }

+

+        //! Gets value of node. 

+        //! Interpretation of value depends on type of node.

+        //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.

+        //! <br><br>

+        //! Use value_size() function to determine length of the value.

+        //! \return Value of node, or empty string if node has no value.

+        Ch *value() const

+        {

+            return m_value ? m_value : nullstr();

+        }

+

+        //! Gets size of node value, not including terminator character.

+        //! This function works correctly irrespective of whether value is or is not zero terminated.

+        //! \return Size of node value, in characters.

+        std::size_t value_size() const

+        {

+            return m_value ? m_value_size : 0;

+        }

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Node modification

+    

+        //! Sets name of node to a non zero-terminated string.

+        //! See \ref ownership_of_strings.

+        //! <br><br>

+        //! Note that node does not own its name or value, it only stores a pointer to it. 

+        //! It will not delete or otherwise free the pointer on destruction.

+        //! It is reponsibility of the user to properly manage lifetime of the string.

+        //! The easiest way to achieve it is to use memory_pool of the document to allocate the string -

+        //! on destruction of the document the string will be automatically freed.

+        //! <br><br>

+        //! Size of name must be specified separately, because name does not have to be zero terminated.

+        //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated).

+        //! \param name Name of node to set. Does not have to be zero terminated.

+        //! \param size Size of name, in characters. This does not include zero terminator, if one is present.

+        void name(const Ch *name, std::size_t size)

+        {

+            m_name = const_cast<Ch *>(name);

+            m_name_size = size;

+        }

+

+        //! Sets name of node to a zero-terminated string.

+        //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t).

+        //! \param name Name of node to set. Must be zero terminated.

+        void name(const Ch *name)

+        {

+            this->name(name, internal::measure(name));

+        }

+

+        //! Sets value of node to a non zero-terminated string.

+        //! See \ref ownership_of_strings.

+        //! <br><br>

+        //! Note that node does not own its name or value, it only stores a pointer to it. 

+        //! It will not delete or otherwise free the pointer on destruction.

+        //! It is reponsibility of the user to properly manage lifetime of the string.

+        //! The easiest way to achieve it is to use memory_pool of the document to allocate the string -

+        //! on destruction of the document the string will be automatically freed.

+        //! <br><br>

+        //! Size of value must be specified separately, because it does not have to be zero terminated.

+        //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated).

+        //! <br><br>

+        //! If an element has a child node of type node_data, it will take precedence over element value when printing.

+        //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser.

+        //! \param value value of node to set. Does not have to be zero terminated.

+        //! \param size Size of value, in characters. This does not include zero terminator, if one is present.

+        void value(const Ch *value, std::size_t size)

+        {

+            m_value = const_cast<Ch *>(value);

+            m_value_size = size;

+        }

+

+        //! Sets value of node to a zero-terminated string.

+        //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t).

+        //! \param value Vame of node to set. Must be zero terminated.

+        void value(const Ch *value)

+        {

+            this->value(value, internal::measure(value));

+        }

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Related nodes access

+    

+        //! Gets node parent.

+        //! \return Pointer to parent node, or 0 if there is no parent.

+        xml_node<Ch> *parent() const

+        {

+            return m_parent;

+        }

+

+    protected:

+

+        // Return empty string

+        static Ch *nullstr()

+        {

+            static Ch zero = Ch('\0');

+            return &zero;

+        }

+

+        Ch *m_name;                         // Name of node, or 0 if no name

+        Ch *m_value;                        // Value of node, or 0 if no value

+        std::size_t m_name_size;            // Length of node name, or undefined of no name

+        std::size_t m_value_size;           // Length of node value, or undefined if no value

+        xml_node<Ch> *m_parent;             // Pointer to parent node, or 0 if none

+

+    };

+

+    //! Class representing attribute node of XML document. 

+    //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base).

+    //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. 

+    //! Thus, this text must persist in memory for the lifetime of attribute.

+    //! \param Ch Character type to use.

+    template<class Ch = char>

+    class xml_attribute: public xml_base<Ch>

+    {

+

+        friend class xml_node<Ch>;

+    

+    public:

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Construction & destruction

+    

+        //! Constructs an empty attribute with the specified type. 

+        //! Consider using memory_pool of appropriate xml_document if allocating attributes manually.

+        xml_attribute()

+        {

+        }

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Related nodes access

+    

+        //! Gets document of which attribute is a child.

+        //! \return Pointer to document that contains this attribute, or 0 if there is no parent document.

+        xml_document<Ch> *document() const

+        {

+            if (xml_node<Ch> *node = this->parent())

+            {

+                while (node->parent())

+                    node = node->parent();

+                return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0;

+            }

+            else

+                return 0;

+        }

+

+        //! Gets previous attribute, optionally matching attribute name. 

+        //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero

+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string

+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

+        //! \return Pointer to found attribute, or 0 if not found.

+        xml_attribute<Ch> *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const

+        {

+            if (name)

+            {

+                if (name_size == 0)

+                    name_size = internal::measure(name);

+                for (xml_attribute<Ch> *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute)

+                    if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive))

+                        return attribute;

+                return 0;

+            }

+            else

+                return this->m_parent ? m_prev_attribute : 0;

+        }

+

+        //! Gets next attribute, optionally matching attribute name. 

+        //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero

+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string

+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

+        //! \return Pointer to found attribute, or 0 if not found.

+        xml_attribute<Ch> *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const

+        {

+            if (name)

+            {

+                if (name_size == 0)

+                    name_size = internal::measure(name);

+                for (xml_attribute<Ch> *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute)

+                    if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive))

+                        return attribute;

+                return 0;

+            }

+            else

+                return this->m_parent ? m_next_attribute : 0;

+        }

+

+    private:

+

+        xml_attribute<Ch> *m_prev_attribute;        // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero

+        xml_attribute<Ch> *m_next_attribute;        // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero

+    

+    };

+

+    ///////////////////////////////////////////////////////////////////////////

+    // XML node

+

+    //! Class representing a node of XML document. 

+    //! Each node may have associated name and value strings, which are available through name() and value() functions. 

+    //! Interpretation of name and value depends on type of the node.

+    //! Type of node can be determined by using type() function.

+    //! <br><br>

+    //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. 

+    //! Thus, this text must persist in the memory for the lifetime of node.

+    //! \param Ch Character type to use.

+    template<class Ch = char>

+    class xml_node: public xml_base<Ch>

+    {

+

+    public:

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Construction & destruction

+    

+        //! Constructs an empty node with the specified type. 

+        //! Consider using memory_pool of appropriate document to allocate nodes manually.

+        //! \param type Type of node to construct.

+        xml_node(node_type type)

+            : m_type(type)

+            , m_first_node(0)

+            , m_first_attribute(0)

+        {

+        }

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Node data access

+    

+        //! Gets type of node.

+        //! \return Type of node.

+        node_type type() const

+        {

+            return m_type;

+        }

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Related nodes access

+    

+        //! Gets document of which node is a child.

+        //! \return Pointer to document that contains this node, or 0 if there is no parent document.

+        xml_document<Ch> *document() const

+        {

+            xml_node<Ch> *node = const_cast<xml_node<Ch> *>(this);

+            while (node->parent())

+                node = node->parent();

+            return node->type() == node_document ? static_cast<xml_document<Ch> *>(node) : 0;

+        }

+

+        //! Gets first child node, optionally matching node name.

+        //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero

+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string

+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

+        //! \return Pointer to found child, or 0 if not found.

+        xml_node<Ch> *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const

+        {

+            if (name)

+            {

+                if (name_size == 0)

+                    name_size = internal::measure(name);

+                for (xml_node<Ch> *child = m_first_node; child; child = child->next_sibling())

+                    if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive))

+                        return child;

+                return 0;

+            }

+            else

+                return m_first_node;

+        }

+

+        //! Gets last child node, optionally matching node name. 

+        //! Behaviour is undefined if node has no children.

+        //! Use first_node() to test if node has children.

+        //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero

+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string

+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

+        //! \return Pointer to found child, or 0 if not found.

+        xml_node<Ch> *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const

+        {

+            assert(m_first_node);  // Cannot query for last child if node has no children

+            if (name)

+            {

+                if (name_size == 0)

+                    name_size = internal::measure(name);

+                for (xml_node<Ch> *child = m_last_node; child; child = child->previous_sibling())

+                    if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive))

+                        return child;

+                return 0;

+            }

+            else

+                return m_last_node;

+        }

+

+        //! Gets previous sibling node, optionally matching node name. 

+        //! Behaviour is undefined if node has no parent.

+        //! Use parent() to test if node has a parent.

+        //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero

+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string

+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

+        //! \return Pointer to found sibling, or 0 if not found.

+        xml_node<Ch> *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const

+        {

+            assert(this->m_parent);     // Cannot query for siblings if node has no parent

+            if (name)

+            {

+                if (name_size == 0)

+                    name_size = internal::measure(name);

+                for (xml_node<Ch> *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling)

+                    if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive))

+                        return sibling;

+                return 0;

+            }

+            else

+                return m_prev_sibling;

+        }

+

+        //! Gets next sibling node, optionally matching node name. 

+        //! Behaviour is undefined if node has no parent.

+        //! Use parent() to test if node has a parent.

+        //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero

+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string

+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

+        //! \return Pointer to found sibling, or 0 if not found.

+        xml_node<Ch> *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const

+        {

+            assert(this->m_parent);     // Cannot query for siblings if node has no parent

+            if (name)

+            {

+                if (name_size == 0)

+                    name_size = internal::measure(name);

+                for (xml_node<Ch> *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling)

+                    if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive))

+                        return sibling;

+                return 0;

+            }

+            else

+                return m_next_sibling;

+        }

+

+        //! Gets first attribute of node, optionally matching attribute name.

+        //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero

+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string

+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

+        //! \return Pointer to found attribute, or 0 if not found.

+        xml_attribute<Ch> *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const

+        {

+            if (name)

+            {

+                if (name_size == 0)

+                    name_size = internal::measure(name);

+                for (xml_attribute<Ch> *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute)

+                    if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive))

+                        return attribute;

+                return 0;

+            }

+            else

+                return m_first_attribute;

+        }

+

+        //! Gets last attribute of node, optionally matching attribute name.

+        //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero

+        //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string

+        //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

+        //! \return Pointer to found attribute, or 0 if not found.

+        xml_attribute<Ch> *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const

+        {

+            if (name)

+            {

+                if (name_size == 0)

+                    name_size = internal::measure(name);

+                for (xml_attribute<Ch> *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute)

+                    if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive))

+                        return attribute;

+                return 0;

+            }

+            else

+                return m_first_attribute ? m_last_attribute : 0;

+        }

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Node modification

+    

+        //! Sets type of node.

+        //! \param type Type of node to set.

+        void type(node_type type)

+        {

+            m_type = type;

+        }

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Node manipulation

+

+        //! Prepends a new child node.

+        //! The prepended child becomes the first child, and all existing children are moved one position back.

+        //! \param child Node to prepend.

+        void prepend_node(xml_node<Ch> *child)

+        {

+            assert(child && !child->parent() && child->type() != node_document);

+            if (first_node())

+            {

+                child->m_next_sibling = m_first_node;

+                m_first_node->m_prev_sibling = child;

+            }

+            else

+            {

+                child->m_next_sibling = 0;

+                m_last_node = child;

+            }

+            m_first_node = child;

+            child->m_parent = this;

+            child->m_prev_sibling = 0;

+        }

+

+        //! Appends a new child node. 

+        //! The appended child becomes the last child.

+        //! \param child Node to append.

+        void append_node(xml_node<Ch> *child)

+        {

+            assert(child && !child->parent() && child->type() != node_document);

+            if (first_node())

+            {

+                child->m_prev_sibling = m_last_node;

+                m_last_node->m_next_sibling = child;

+            }

+            else

+            {

+                child->m_prev_sibling = 0;

+                m_first_node = child;

+            }

+            m_last_node = child;

+            child->m_parent = this;

+            child->m_next_sibling = 0;

+        }

+

+        //! Inserts a new child node at specified place inside the node. 

+        //! All children after and including the specified node are moved one position back.

+        //! \param where Place where to insert the child, or 0 to insert at the back.

+        //! \param child Node to insert.

+        void insert_node(xml_node<Ch> *where, xml_node<Ch> *child)

+        {

+            assert(!where || where->parent() == this);

+            assert(child && !child->parent() && child->type() != node_document);

+            if (where == m_first_node)

+                prepend_node(child);

+            else if (where == 0)

+                append_node(child);

+            else

+            {

+                child->m_prev_sibling = where->m_prev_sibling;

+                child->m_next_sibling = where;

+                where->m_prev_sibling->m_next_sibling = child;

+                where->m_prev_sibling = child;

+                child->m_parent = this;

+            }

+        }

+

+        //! Removes first child node. 

+        //! If node has no children, behaviour is undefined.

+        //! Use first_node() to test if node has children.

+        void remove_first_node()

+        {

+            assert(first_node());

+            xml_node<Ch> *child = m_first_node;

+            m_first_node = child->m_next_sibling;

+            if (child->m_next_sibling)

+                child->m_next_sibling->m_prev_sibling = 0;

+            else

+                m_last_node = 0;

+            child->m_parent = 0;

+        }

+

+        //! Removes last child of the node. 

+        //! If node has no children, behaviour is undefined.

+        //! Use first_node() to test if node has children.

+        void remove_last_node()

+        {

+            assert(first_node());

+            xml_node<Ch> *child = m_last_node;

+            if (child->m_prev_sibling)

+            {

+                m_last_node = child->m_prev_sibling;

+                child->m_prev_sibling->m_next_sibling = 0;

+            }

+            else

+                m_first_node = 0;

+            child->m_parent = 0;

+        }

+

+        //! Removes specified child from the node

+        // \param where Pointer to child to be removed.

+        void remove_node(xml_node<Ch> *where)

+        {

+            assert(where && where->parent() == this);

+            assert(first_node());

+            if (where == m_first_node)

+                remove_first_node();

+            else if (where == m_last_node)

+                remove_last_node();

+            else

+            {

+                where->m_prev_sibling->m_next_sibling = where->m_next_sibling;

+                where->m_next_sibling->m_prev_sibling = where->m_prev_sibling;

+                where->m_parent = 0;

+            }

+        }

+

+        //! Removes all child nodes (but not attributes).

+        void remove_all_nodes()

+        {

+            for (xml_node<Ch> *node = first_node(); node; node = node->m_next_sibling)

+                node->m_parent = 0;

+            m_first_node = 0;

+        }

+

+        //! Prepends a new attribute to the node.

+        //! \param attribute Attribute to prepend.

+        void prepend_attribute(xml_attribute<Ch> *attribute)

+        {

+            assert(attribute && !attribute->parent());

+            if (first_attribute())

+            {

+                attribute->m_next_attribute = m_first_attribute;

+                m_first_attribute->m_prev_attribute = attribute;

+            }

+            else

+            {

+                attribute->m_next_attribute = 0;

+                m_last_attribute = attribute;

+            }

+            m_first_attribute = attribute;

+            attribute->m_parent = this;

+            attribute->m_prev_attribute = 0;

+        }

+

+        //! Appends a new attribute to the node.

+        //! \param attribute Attribute to append.

+        void append_attribute(xml_attribute<Ch> *attribute)

+        {

+            assert(attribute && !attribute->parent());

+            if (first_attribute())

+            {

+                attribute->m_prev_attribute = m_last_attribute;

+                m_last_attribute->m_next_attribute = attribute;

+            }

+            else

+            {

+                attribute->m_prev_attribute = 0;

+                m_first_attribute = attribute;

+            }

+            m_last_attribute = attribute;

+            attribute->m_parent = this;

+            attribute->m_next_attribute = 0;

+        }

+

+        //! Inserts a new attribute at specified place inside the node. 

+        //! All attributes after and including the specified attribute are moved one position back.

+        //! \param where Place where to insert the attribute, or 0 to insert at the back.

+        //! \param attribute Attribute to insert.

+        void insert_attribute(xml_attribute<Ch> *where, xml_attribute<Ch> *attribute)

+        {

+            assert(!where || where->parent() == this);

+            assert(attribute && !attribute->parent());

+            if (where == m_first_attribute)

+                prepend_attribute(attribute);

+            else if (where == 0)

+                append_attribute(attribute);

+            else

+            {

+                attribute->m_prev_attribute = where->m_prev_attribute;

+                attribute->m_next_attribute = where;

+                where->m_prev_attribute->m_next_attribute = attribute;

+                where->m_prev_attribute = attribute;

+                attribute->m_parent = this;

+            }

+        }

+

+        //! Removes first attribute of the node. 

+        //! If node has no attributes, behaviour is undefined.

+        //! Use first_attribute() to test if node has attributes.

+        void remove_first_attribute()

+        {

+            assert(first_attribute());

+            xml_attribute<Ch> *attribute = m_first_attribute;

+            if (attribute->m_next_attribute)

+            {

+                attribute->m_next_attribute->m_prev_attribute = 0;

+            }

+            else

+                m_last_attribute = 0;

+            attribute->m_parent = 0;

+            m_first_attribute = attribute->m_next_attribute;

+        }

+

+        //! Removes last attribute of the node. 

+        //! If node has no attributes, behaviour is undefined.

+        //! Use first_attribute() to test if node has attributes.

+        void remove_last_attribute()

+        {

+            assert(first_attribute());

+            xml_attribute<Ch> *attribute = m_last_attribute;

+            if (attribute->m_prev_attribute)

+            {

+                attribute->m_prev_attribute->m_next_attribute = 0;

+                m_last_attribute = attribute->m_prev_attribute;

+            }

+            else

+                m_first_attribute = 0;

+            attribute->m_parent = 0;

+        }

+

+        //! Removes specified attribute from node.

+        //! \param where Pointer to attribute to be removed.

+        void remove_attribute(xml_attribute<Ch> *where)

+        {

+            assert(first_attribute() && where->parent() == this);

+            if (where == m_first_attribute)

+                remove_first_attribute();

+            else if (where == m_last_attribute)

+                remove_last_attribute();

+            else

+            {

+                where->m_prev_attribute->m_next_attribute = where->m_next_attribute;

+                where->m_next_attribute->m_prev_attribute = where->m_prev_attribute;

+                where->m_parent = 0;

+            }

+        }

+

+        //! Removes all attributes of node.

+        void remove_all_attributes()

+        {

+            for (xml_attribute<Ch> *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute)

+                attribute->m_parent = 0;

+            m_first_attribute = 0;

+        }

+        

+    private:

+

+        ///////////////////////////////////////////////////////////////////////////

+        // Restrictions

+

+        // No copying

+        xml_node(const xml_node &);

+        void operator =(const xml_node &);

+    

+        ///////////////////////////////////////////////////////////////////////////

+        // Data members

+    

+        // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0.

+        // This is required for maximum performance, as it allows the parser to omit initialization of 

+        // unneded/redundant values.

+        //

+        // The rules are as follows:

+        // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively

+        // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage

+        // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage

+

+        node_type m_type;                       // Type of node; always valid

+        xml_node<Ch> *m_first_node;             // Pointer to first child node, or 0 if none; always valid

+        xml_node<Ch> *m_last_node;              // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero

+        xml_attribute<Ch> *m_first_attribute;   // Pointer to first attribute of node, or 0 if none; always valid

+        xml_attribute<Ch> *m_last_attribute;    // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero

+        xml_node<Ch> *m_prev_sibling;           // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero

+        xml_node<Ch> *m_next_sibling;           // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero

+

+    };

+

+    ///////////////////////////////////////////////////////////////////////////

+    // XML document

+    

+    //! This class represents root of the DOM hierarchy. 

+    //! It is also an xml_node and a memory_pool through public inheritance.

+    //! Use parse() function to build a DOM tree from a zero-terminated XML text string.

+    //! parse() function allocates memory for nodes and attributes by using functions of xml_document, 

+    //! which are inherited from memory_pool.

+    //! To access root node of the document, use the document itself, as if it was an xml_node.

+    //! \param Ch Character type to use.

+    template<class Ch = char>

+    class xml_document: public xml_node<Ch>, public memory_pool<Ch>

+    {

+    

+    public:

+

+        //! Constructs empty XML document

+        xml_document()

+            : xml_node<Ch>(node_document)

+        {

+        }

+

+        //! Parses zero-terminated XML string according to given flags.

+        //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used.

+        //! The string must persist for the lifetime of the document.

+        //! In case of error, rapidxml::parse_error exception will be thrown.

+        //! <br><br>

+        //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning.

+        //! Make sure that data is zero-terminated.

+        //! <br><br>

+        //! Document can be parsed into multiple times. 

+        //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool.

+        //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser.

+        template<int Flags>

+        void parse(Ch *text)

+        {

+            assert(text);

+            

+            // Remove current contents

+            this->remove_all_nodes();

+            this->remove_all_attributes();

+            

+            // Parse BOM, if any

+            parse_bom<Flags>(text);

+            

+            // Parse children

+            while (1)

+            {

+                // Skip whitespace before node

+                skip<whitespace_pred, Flags>(text);

+                if (*text == 0)

+                    break;

+

+                // Parse and append new child

+                if (*text == Ch('<'))

+                {

+                    ++text;     // Skip '<'

+                    if (xml_node<Ch> *node = parse_node<Flags>(text))

+                        this->append_node(node);

+                }

+                else

+                    RAPIDXML_PARSE_ERROR("expected <", text);

+            }

+

+        }

+

+        //! Clears the document by deleting all nodes and clearing the memory pool.

+        //! All nodes owned by document pool are destroyed.

+        void clear()

+        {

+            this->remove_all_nodes();

+            this->remove_all_attributes();

+            memory_pool<Ch>::clear();

+        }

+        

+    private:

+

+        ///////////////////////////////////////////////////////////////////////

+        // Internal character utility functions

+        

+        // Detect whitespace character

+        struct whitespace_pred

+        {

+            static unsigned char test(Ch ch)

+            {

+                return internal::lookup_tables<0>::lookup_whitespace[static_cast<unsigned char>(ch)];

+            }

+        };

+

+        // Detect node name character

+        struct node_name_pred

+        {

+            static unsigned char test(Ch ch)

+            {

+                return internal::lookup_tables<0>::lookup_node_name[static_cast<unsigned char>(ch)];

+            }

+        };

+

+        // Detect attribute name character

+        struct attribute_name_pred

+        {

+            static unsigned char test(Ch ch)

+            {

+                return internal::lookup_tables<0>::lookup_attribute_name[static_cast<unsigned char>(ch)];

+            }

+        };

+

+        // Detect text character (PCDATA)

+        struct text_pred

+        {

+            static unsigned char test(Ch ch)

+            {

+                return internal::lookup_tables<0>::lookup_text[static_cast<unsigned char>(ch)];

+            }

+        };

+

+        // Detect text character (PCDATA) that does not require processing

+        struct text_pure_no_ws_pred

+        {

+            static unsigned char test(Ch ch)

+            {

+                return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast<unsigned char>(ch)];

+            }

+        };

+

+        // Detect text character (PCDATA) that does not require processing

+        struct text_pure_with_ws_pred

+        {

+            static unsigned char test(Ch ch)

+            {

+                return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast<unsigned char>(ch)];

+            }

+        };

+

+        // Detect attribute value character

+        template<Ch Quote>

+        struct attribute_value_pred

+        {

+            static unsigned char test(Ch ch)

+            {

+                if (Quote == Ch('\''))

+                    return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast<unsigned char>(ch)];

+                if (Quote == Ch('\"'))

+                    return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast<unsigned char>(ch)];

+                return 0;       // Should never be executed, to avoid warnings on Comeau

+            }

+        };

+

+        // Detect attribute value character

+        template<Ch Quote>

+        struct attribute_value_pure_pred

+        {

+            static unsigned char test(Ch ch)

+            {

+                if (Quote == Ch('\''))

+                    return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast<unsigned char>(ch)];

+                if (Quote == Ch('\"'))

+                    return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast<unsigned char>(ch)];

+                return 0;       // Should never be executed, to avoid warnings on Comeau

+            }

+        };

+

+        // Insert coded character, using UTF8 or 8-bit ASCII

+        template<int Flags>

+        static void insert_coded_character(Ch *&text, unsigned long code)

+        {

+            if (Flags & parse_no_utf8)

+            {

+                // Insert 8-bit ASCII character

+                // Todo: possibly verify that code is less than 256 and use replacement char otherwise?

+                text[0] = static_cast<unsigned char>(code);

+                text += 1;

+            }

+            else

+            {

+                // Insert UTF8 sequence

+                if (code < 0x80)    // 1 byte sequence

+                {

+	                text[0] = static_cast<unsigned char>(code);

+                    text += 1;

+                }

+                else if (code < 0x800)  // 2 byte sequence

+                {

+	                text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;

+	                text[0] = static_cast<unsigned char>(code | 0xC0);

+                    text += 2;

+                }

+	            else if (code < 0x10000)    // 3 byte sequence

+                {

+	                text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;

+	                text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;

+	                text[0] = static_cast<unsigned char>(code | 0xE0);

+                    text += 3;

+                }

+	            else if (code < 0x110000)   // 4 byte sequence

+                {

+	                text[3] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;

+	                text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;

+	                text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;

+	                text[0] = static_cast<unsigned char>(code | 0xF0);

+                    text += 4;

+                }

+                else    // Invalid, only codes up to 0x10FFFF are allowed in Unicode

+                {

+                    RAPIDXML_PARSE_ERROR("invalid numeric character entity", text);

+                }

+            }

+        }

+

+        // Skip characters until predicate evaluates to true

+        template<class StopPred, int Flags>

+        static void skip(Ch *&text)

+        {

+            Ch *tmp = text;

+            while (StopPred::test(*tmp))

+                ++tmp;

+            text = tmp;

+        }

+

+        // Skip characters until predicate evaluates to true while doing the following:

+        // - replacing XML character entity references with proper characters (&apos; &amp; &quot; &lt; &gt; &#...;)

+        // - condensing whitespace sequences to single space character

+        template<class StopPred, class StopPredPure, int Flags>

+        static Ch *skip_and_expand_character_refs(Ch *&text)

+        {

+            // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip

+            if (Flags & parse_no_entity_translation && 

+                !(Flags & parse_normalize_whitespace) &&

+                !(Flags & parse_trim_whitespace))

+            {

+                skip<StopPred, Flags>(text);

+                return text;

+            }

+            

+            // Use simple skip until first modification is detected

+            skip<StopPredPure, Flags>(text);

+

+            // Use translation skip

+            Ch *src = text;

+            Ch *dest = src;

+            while (StopPred::test(*src))

+            {

+                // If entity translation is enabled    

+                if (!(Flags & parse_no_entity_translation))

+                {

+                    // Test if replacement is needed

+                    if (src[0] == Ch('&'))

+                    {

+                        switch (src[1])

+                        {

+

+                        // &amp; &apos;

+                        case Ch('a'): 

+                            if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';'))

+                            {

+                                *dest = Ch('&');

+                                ++dest;

+                                src += 5;

+                                continue;

+                            }

+                            if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';'))

+                            {

+                                *dest = Ch('\'');

+                                ++dest;

+                                src += 6;

+                                continue;

+                            }

+                            break;

+

+                        // &quot;

+                        case Ch('q'): 

+                            if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';'))

+                            {

+                                *dest = Ch('"');

+                                ++dest;

+                                src += 6;

+                                continue;

+                            }

+                            break;

+

+                        // &gt;

+                        case Ch('g'): 

+                            if (src[2] == Ch('t') && src[3] == Ch(';'))

+                            {

+                                *dest = Ch('>');

+                                ++dest;

+                                src += 4;

+                                continue;

+                            }

+                            break;

+

+                        // &lt;

+                        case Ch('l'): 

+                            if (src[2] == Ch('t') && src[3] == Ch(';'))

+                            {

+                                *dest = Ch('<');

+                                ++dest;

+                                src += 4;

+                                continue;

+                            }

+                            break;

+

+                        // &#...; - assumes ASCII

+                        case Ch('#'): 

+                            if (src[2] == Ch('x'))

+                            {

+                                unsigned long code = 0;

+                                src += 3;   // Skip &#x

+                                while (1)

+                                {

+                                    unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast<unsigned char>(*src)];

+                                    if (digit == 0xFF)

+                                        break;

+                                    code = code * 16 + digit;

+                                    ++src;

+                                }

+                                insert_coded_character<Flags>(dest, code);    // Put character in output

+                            }

+                            else

+                            {

+                                unsigned long code = 0;

+                                src += 2;   // Skip &#

+                                while (1)

+                                {

+                                    unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast<unsigned char>(*src)];

+                                    if (digit == 0xFF)

+                                        break;

+                                    code = code * 10 + digit;

+                                    ++src;

+                                }

+                                insert_coded_character<Flags>(dest, code);    // Put character in output

+                            }

+                            if (*src == Ch(';'))

+                                ++src;

+                            else

+                                RAPIDXML_PARSE_ERROR("expected ;", src);

+                            continue;

+

+                        // Something else

+                        default:

+                            // Ignore, just copy '&' verbatim

+                            break;

+

+                        }

+                    }

+                }

+                

+                // If whitespace condensing is enabled

+                if (Flags & parse_normalize_whitespace)

+                {

+                    // Test if condensing is needed                 

+                    if (whitespace_pred::test(*src))

+                    {

+                        *dest = Ch(' '); ++dest;    // Put single space in dest

+                        ++src;                      // Skip first whitespace char

+                        // Skip remaining whitespace chars

+                        while (whitespace_pred::test(*src))

+                            ++src;

+                        continue;

+                    }

+                }

+

+                // No replacement, only copy character

+                *dest++ = *src++;

+

+            }

+

+            // Return new end

+            text = src;

+            return dest;

+

+        }

+

+        ///////////////////////////////////////////////////////////////////////

+        // Internal parsing functions

+        

+        // Parse BOM, if any

+        template<int Flags>

+        void parse_bom(Ch *&text)

+        {

+            // UTF-8?

+            if (static_cast<unsigned char>(text[0]) == 0xEF && 

+                static_cast<unsigned char>(text[1]) == 0xBB && 

+                static_cast<unsigned char>(text[2]) == 0xBF)

+            {

+                text += 3;      // Skup utf-8 bom

+            }

+        }

+

+        // Parse XML declaration (<?xml...)

+        template<int Flags>

+        xml_node<Ch> *parse_xml_declaration(Ch *&text)

+        {

+            // If parsing of declaration is disabled

+            if (!(Flags & parse_declaration_node))

+            {

+                // Skip until end of declaration

+                while (text[0] != Ch('?') || text[1] != Ch('>'))

+                {

+                    if (!text[0]) {

+                        RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+						return 0;

+					}

+                    ++text;

+                }

+                text += 2;    // Skip '?>'

+                return 0;

+            }

+

+            // Create declaration

+            xml_node<Ch> *declaration = this->allocate_node(node_declaration);

+

+            // Skip whitespace before attributes or ?>

+            skip<whitespace_pred, Flags>(text);

+

+            // Parse declaration attributes

+            parse_node_attributes<Flags>(text, declaration);

+            

+            // Skip ?>

+            if (text[0] != Ch('?') || text[1] != Ch('>'))

+                RAPIDXML_PARSE_ERROR("expected ?>", text);

+            text += 2;

+            

+            return declaration;

+        }

+

+        // Parse XML comment (<!--...)

+        template<int Flags>

+        xml_node<Ch> *parse_comment(Ch *&text)

+        {

+            // If parsing of comments is disabled

+            if (!(Flags & parse_comment_nodes))

+            {

+                // Skip until end of comment

+                while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>'))

+                {

+                    if (!text[0]) {

+                        RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+						return 0;

+					}

+                    ++text;

+                }

+                text += 3;     // Skip '-->'

+                return 0;      // Do not produce comment node

+            }

+

+            // Remember value start

+            Ch *value = text;

+

+            // Skip until end of comment

+            while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>'))

+            {

+                if (!text[0]) {

+                    RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+					return 0;

+				}

+                ++text;

+            }

+

+            // Create comment node

+            xml_node<Ch> *comment = this->allocate_node(node_comment);

+            comment->value(value, text - value);

+            

+            // Place zero terminator after comment value

+            if (!(Flags & parse_no_string_terminators))

+                *text = Ch('\0');

+            

+            text += 3;     // Skip '-->'

+            return comment;

+        }

+

+        // Parse DOCTYPE

+        template<int Flags>

+        xml_node<Ch> *parse_doctype(Ch *&text)

+        {

+            // Remember value start

+            Ch *value = text;

+

+            // Skip to >

+            while (*text != Ch('>'))

+            {

+                // Determine character type

+                switch (*text)

+                {

+                

+                // If '[' encountered, scan for matching ending ']' using naive algorithm with depth

+                // This works for all W3C test files except for 2 most wicked

+                case Ch('['):

+                {

+                    ++text;     // Skip '['

+                    int depth = 1;

+                    while (depth > 0)

+                    {

+                        switch (*text)

+                        {

+                            case Ch('['): ++depth; break;

+                            case Ch(']'): --depth; break;

+                            case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); return 0;

+                        }

+                        ++text;

+                    }

+                    break;

+                }

+                

+                // Error on end of text

+                case Ch('\0'):

+                    RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+					return 0;

+                

+                // Other character, skip it

+                default:

+                    ++text;

+

+                }

+            }

+            

+            // If DOCTYPE nodes enabled

+            if (Flags & parse_doctype_node)

+            {

+                // Create a new doctype node

+                xml_node<Ch> *doctype = this->allocate_node(node_doctype);

+                doctype->value(value, text - value);

+                

+                // Place zero terminator after value

+                if (!(Flags & parse_no_string_terminators))

+                    *text = Ch('\0');

+

+                text += 1;      // skip '>'

+                return doctype;

+            }

+            else

+            {

+                text += 1;      // skip '>'

+                return 0;

+            }

+

+        }

+

+        // Parse PI

+        template<int Flags>

+        xml_node<Ch> *parse_pi(Ch *&text)

+        {

+            // If creation of PI nodes is enabled

+            if (Flags & parse_pi_nodes)

+            {

+                // Create pi node

+                xml_node<Ch> *pi = this->allocate_node(node_pi);

+

+                // Extract PI target name

+                Ch *name = text;

+                skip<node_name_pred, Flags>(text);

+                if (text == name)

+                    RAPIDXML_PARSE_ERROR("expected PI target", text);

+                pi->name(name, text - name);

+                

+                // Skip whitespace between pi target and pi

+                skip<whitespace_pred, Flags>(text);

+

+                // Remember start of pi

+                Ch *value = text;

+                

+                // Skip to '?>'

+                while (text[0] != Ch('?') || text[1] != Ch('>'))

+                {

+                    if (*text == Ch('\0')) {

+                        RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+						return 0;

+					}

+                    ++text;

+                }

+

+                // Set pi value (verbatim, no entity expansion or whitespace normalization)

+                pi->value(value, text - value);     

+                

+                // Place zero terminator after name and value

+                if (!(Flags & parse_no_string_terminators))

+                {

+                    pi->name()[pi->name_size()] = Ch('\0');

+                    pi->value()[pi->value_size()] = Ch('\0');

+                }

+                

+                text += 2;                          // Skip '?>'

+                return pi;

+            }

+            else

+            {

+                // Skip to '?>'

+                while (text[0] != Ch('?') || text[1] != Ch('>'))

+                {

+                    if (*text == Ch('\0')) {

+                        RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+						return 0;

+					}

+                    ++text;

+                }

+                text += 2;    // Skip '?>'

+                return 0;

+            }

+        }

+

+        // Parse and append data

+        // Return character that ends data.

+        // This is necessary because this character might have been overwritten by a terminating 0

+        template<int Flags>

+        Ch parse_and_append_data(xml_node<Ch> *node, Ch *&text, Ch *contents_start)

+        {

+            // Backup to contents start if whitespace trimming is disabled

+            if (!(Flags & parse_trim_whitespace))

+                text = contents_start;     

+            

+            // Skip until end of data

+            Ch *value = text, *end;

+            if (Flags & parse_normalize_whitespace)

+                end = skip_and_expand_character_refs<text_pred, text_pure_with_ws_pred, Flags>(text);   

+            else

+                end = skip_and_expand_character_refs<text_pred, text_pure_no_ws_pred, Flags>(text);

+

+            // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after >

+            if (Flags & parse_trim_whitespace)

+            {

+                if (Flags & parse_normalize_whitespace)

+                {

+                    // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end

+                    if (*(end - 1) == Ch(' '))

+                        --end;

+                }

+                else

+                {

+                    // Backup until non-whitespace character is found

+                    while (whitespace_pred::test(*(end - 1)))

+                        --end;

+                }

+            }

+            

+            // If characters are still left between end and value (this test is only necessary if normalization is enabled)

+            // Create new data node

+            if (!(Flags & parse_no_data_nodes))

+            {

+                xml_node<Ch> *data = this->allocate_node(node_data);

+                data->value(value, end - value);

+                node->append_node(data);

+            }

+

+            // Add data to parent node if no data exists yet

+            if (!(Flags & parse_no_element_values)) 

+                if (*node->value() == Ch('\0'))

+                    node->value(value, end - value);

+

+            // Place zero terminator after value

+            if (!(Flags & parse_no_string_terminators))

+            {

+                Ch ch = *text;

+                *end = Ch('\0');

+                return ch;      // Return character that ends data; this is required because zero terminator overwritten it

+            }

+

+            // Return character that ends data

+            return *text;

+        }

+

+        // Parse CDATA

+        template<int Flags>

+        xml_node<Ch> *parse_cdata(Ch *&text)

+        {

+            // If CDATA is disabled

+            if (Flags & parse_no_data_nodes)

+            {

+                // Skip until end of cdata

+                while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>'))

+                {

+                    if (!text[0]) {

+                        RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+						return 0;

+					}

+                    ++text;

+                }

+                text += 3;      // Skip ]]>

+                return 0;       // Do not produce CDATA node

+            }

+

+            // Skip until end of cdata

+            Ch *value = text;

+            while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>'))

+            {

+                if (!text[0]) {

+                    RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+					return 0;

+				}

+                ++text;

+            }

+

+            // Create new cdata node

+            xml_node<Ch> *cdata = this->allocate_node(node_cdata);

+            cdata->value(value, text - value);

+

+            // Place zero terminator after value

+            if (!(Flags & parse_no_string_terminators))

+                *text = Ch('\0');

+

+            text += 3;      // Skip ]]>

+            return cdata;

+        }

+        

+        // Parse element node

+        template<int Flags>

+        xml_node<Ch> *parse_element(Ch *&text)

+        {

+            // Create element node

+            xml_node<Ch> *element = this->allocate_node(node_element);

+

+            // Extract element name

+            Ch *name = text;

+            skip<node_name_pred, Flags>(text);

+            if (text == name)

+                RAPIDXML_PARSE_ERROR("expected element name", text);

+            element->name(name, text - name);

+            

+            // Skip whitespace between element name and attributes or >

+            skip<whitespace_pred, Flags>(text);

+

+            // Parse attributes, if any

+            parse_node_attributes<Flags>(text, element);

+

+            // Determine ending type

+            if (*text == Ch('>'))

+            {

+                ++text;

+                parse_node_contents<Flags>(text, element);

+            }

+            else if (*text == Ch('/'))

+            {

+                ++text;

+                if (*text != Ch('>'))

+                    RAPIDXML_PARSE_ERROR("expected >", text);

+                ++text;

+            }

+            else

+                RAPIDXML_PARSE_ERROR("expected >", text);

+

+            // Place zero terminator after name

+            if (!(Flags & parse_no_string_terminators))

+                element->name()[element->name_size()] = Ch('\0');

+

+            // Return parsed element

+            return element;

+        }

+

+        // Determine node type, and parse it

+        template<int Flags>

+        xml_node<Ch> *parse_node(Ch *&text)

+        {

+            // Parse proper node type

+            switch (text[0])

+            {

+

+            // <...

+            default: 

+                // Parse and append element node

+                return parse_element<Flags>(text);

+

+            // <?...

+            case Ch('?'): 

+                ++text;     // Skip ?

+                if ((text[0] == Ch('x') || text[0] == Ch('X')) &&

+                    (text[1] == Ch('m') || text[1] == Ch('M')) && 

+                    (text[2] == Ch('l') || text[2] == Ch('L')) &&

+                    whitespace_pred::test(text[3]))

+                {

+                    // '<?xml ' - xml declaration

+                    text += 4;      // Skip 'xml '

+                    return parse_xml_declaration<Flags>(text);

+                }

+                else

+                {

+                    // Parse PI

+                    return parse_pi<Flags>(text);

+                }

+            

+            // <!...

+            case Ch('!'): 

+

+                // Parse proper subset of <! node

+                switch (text[1])    

+                {

+                

+                // <!-

+                case Ch('-'):

+                    if (text[2] == Ch('-'))

+                    {

+                        // '<!--' - xml comment

+                        text += 3;     // Skip '!--'

+                        return parse_comment<Flags>(text);

+                    }

+                    break;

+

+                // <![

+                case Ch('['):

+                    if (text[2] == Ch('C') && text[3] == Ch('D') && text[4] == Ch('A') && 

+                        text[5] == Ch('T') && text[6] == Ch('A') && text[7] == Ch('['))

+                    {

+                        // '<![CDATA[' - cdata

+                        text += 8;     // Skip '![CDATA['

+                        return parse_cdata<Flags>(text);

+                    }

+                    break;

+

+                // <!D

+                case Ch('D'):

+                    if (text[2] == Ch('O') && text[3] == Ch('C') && text[4] == Ch('T') && 

+                        text[5] == Ch('Y') && text[6] == Ch('P') && text[7] == Ch('E') && 

+                        whitespace_pred::test(text[8]))

+                    {

+                        // '<!DOCTYPE ' - doctype

+                        text += 9;      // skip '!DOCTYPE '

+                        return parse_doctype<Flags>(text);

+                    }

+

+                }   // switch

+

+                // Attempt to skip other, unrecognized node types starting with <!

+                ++text;     // Skip !

+                while (*text != Ch('>'))

+                {

+                    if (*text == 0) {

+                        RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+						return 0;

+					}

+                    ++text;

+                }

+                ++text;     // Skip '>'

+                return 0;   // No node recognized

+

+            }

+        }

+

+        // Parse contents of the node - children, data etc.

+        template<int Flags>

+        void parse_node_contents(Ch *&text, xml_node<Ch> *node)

+        {

+            // For all children and text

+            while (1)

+            {

+                // Skip whitespace between > and node contents

+                Ch *contents_start = text;      // Store start of node contents before whitespace is skipped

+                skip<whitespace_pred, Flags>(text);

+                Ch next_char = *text;

+

+            // After data nodes, instead of continuing the loop, control jumps here.

+            // This is because zero termination inside parse_and_append_data() function

+            // would wreak havoc with the above code.

+            // Also, skipping whitespace after data nodes is unnecessary.

+            after_data_node:    

+                

+                // Determine what comes next: node closing, child node, data node, or 0?

+                switch (next_char)

+                {

+                

+                // Node closing or child node

+                case Ch('<'):

+                    if (text[1] == Ch('/'))

+                    {

+                        // Node closing

+                        text += 2;      // Skip '</'

+                        if (Flags & parse_validate_closing_tags)

+                        {

+                            // Skip and validate closing tag name

+                            Ch *closing_name = text;

+                            skip<node_name_pred, Flags>(text);

+                            if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true))

+                                RAPIDXML_PARSE_ERROR("invalid closing tag name", text);

+                        }

+                        else

+                        {

+                            // No validation, just skip name

+                            skip<node_name_pred, Flags>(text);

+                        }

+                        // Skip remaining whitespace after node name

+                        skip<whitespace_pred, Flags>(text);

+                        if (*text != Ch('>'))

+                            RAPIDXML_PARSE_ERROR("expected >", text);

+                        ++text;     // Skip '>'

+                        return;     // Node closed, finished parsing contents

+                    }

+                    else

+                    {

+                        // Child node

+                        ++text;     // Skip '<'

+                        if (xml_node<Ch> *child = parse_node<Flags>(text))

+                            node->append_node(child);

+                    }

+                    break;

+

+                // End of data - error

+                case Ch('\0'):

+                    RAPIDXML_PARSE_ERROR("unexpected end of data", text);

+					return;

+

+                // Data node

+                default:

+                    next_char = parse_and_append_data<Flags>(node, text, contents_start);

+                    goto after_data_node;   // Bypass regular processing after data nodes

+

+                }

+            }

+        }

+        

+        // Parse XML attributes of the node

+        template<int Flags>

+        void parse_node_attributes(Ch *&text, xml_node<Ch> *node)

+        {

+            // For all attributes 

+            while (attribute_name_pred::test(*text))

+            {

+                // Extract attribute name

+                Ch *name = text;

+                ++text;     // Skip first character of attribute name

+                skip<attribute_name_pred, Flags>(text);

+                if (text == name)

+                    RAPIDXML_PARSE_ERROR("expected attribute name", name);

+

+                // Create new attribute

+                xml_attribute<Ch> *attribute = this->allocate_attribute();

+                attribute->name(name, text - name);

+                node->append_attribute(attribute);

+

+                // Skip whitespace after attribute name

+                skip<whitespace_pred, Flags>(text);

+

+                // Skip =

+                if (*text != Ch('='))

+                    RAPIDXML_PARSE_ERROR("expected =", text);

+                ++text;

+

+                // Add terminating zero after name

+                if (!(Flags & parse_no_string_terminators))

+                    attribute->name()[attribute->name_size()] = 0;

+

+                // Skip whitespace after =

+                skip<whitespace_pred, Flags>(text);

+

+                // Skip quote and remember if it was ' or "

+                Ch quote = *text;

+                if (quote != Ch('\'') && quote != Ch('"'))

+                    RAPIDXML_PARSE_ERROR("expected ' or \"", text);

+                ++text;

+

+                // Extract attribute value and expand char refs in it

+                Ch *value = text, *end;

+                const int AttFlags = Flags & ~parse_normalize_whitespace;   // No whitespace normalization in attributes

+                if (quote == Ch('\''))

+                    end = skip_and_expand_character_refs<attribute_value_pred<Ch('\'')>, attribute_value_pure_pred<Ch('\'')>, AttFlags>(text);

+                else

+                    end = skip_and_expand_character_refs<attribute_value_pred<Ch('"')>, attribute_value_pure_pred<Ch('"')>, AttFlags>(text);

+                

+                // Set attribute value

+                attribute->value(value, end - value);

+                

+                // Make sure that end quote is present

+                if (*text != quote)

+                    RAPIDXML_PARSE_ERROR("expected ' or \"", text);

+                ++text;     // Skip quote

+

+                // Add terminating zero after value

+                if (!(Flags & parse_no_string_terminators))

+                    attribute->value()[attribute->value_size()] = 0;

+

+                // Skip whitespace after attribute value

+                skip<whitespace_pred, Flags>(text);

+            }

+        }

+

+    };

+

+    //! \cond internal

+    namespace internal

+    {

+

+        // Whitespace (space \n \r \t)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_whitespace[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  0,  0,  1,  0,  0,  // 0

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1

+             1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 2

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 3

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 4

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 5

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 6

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 7

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 8

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 9

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // A

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // B

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // C

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // D

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // E

+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0   // F

+        };

+

+        // Node name (anything but space \n \r \t / > ? \0)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_node_name[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  1,  1,  0,  1,  1,  // 0

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  // 2

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  // 3

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F

+        };

+

+        // Text (i.e. PCDATA) (anything but < \0)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_text[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  // 3

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F

+        };

+

+        // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled 

+        // (anything but < \0 &)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_text_pure_no_ws[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1

+             1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  // 3

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F

+        };

+

+        // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled

+        // (anything but < \0 & space \n \r \t)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_text_pure_with_ws[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  1,  1,  0,  1,  1,  // 0

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1

+             0,  1,  1,  1,  1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  // 3

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F

+        };

+

+        // Attribute name (anything but space \n \r \t / < > = ? ! \0)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_attribute_name[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  1,  1,  0,  1,  1,  // 0

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1

+             0,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  // 2

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  // 3

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F

+        };

+

+        // Attribute data with single quote (anything but ' \0)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_attribute_data_1[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1

+             1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  // 2

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 3

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F

+        };

+

+        // Attribute data with single quote that does not require processing (anything but ' \0 &)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_attribute_data_1_pure[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1

+             1,  1,  1,  1,  1,  1,  0,  0,  1,  1,  1,  1,  1,  1,  1,  1,  // 2

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 3

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F

+        };

+

+        // Attribute data with double quote (anything but " \0)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_attribute_data_2[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1

+             1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 3

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F

+        };

+

+        // Attribute data with double quote that does not require processing (anything but " \0 &)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_attribute_data_2_pure[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+             0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1

+             1,  1,  0,  1,  1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 3

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E

+             1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F

+        };

+

+        // Digits (dec and hex, 255 denotes end of numeric character reference)

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_digits[256] = 

+        {

+          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 0

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 1

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 2

+             0,  1,  2,  3,  4,  5,  6,  7,  8,  9,255,255,255,255,255,255,  // 3

+           255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255,  // 4

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 5

+           255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255,  // 6

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 7

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 8

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 9

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // A

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // B

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // C

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // D

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // E

+           255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255   // F

+        };

+    

+        // Upper case conversion

+        template<int Dummy>

+        const unsigned char lookup_tables<Dummy>::lookup_upcase[256] = 

+        {

+          // 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  A   B   C   D   E   F

+           0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,   // 0

+           16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,   // 1

+           32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,   // 2

+           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,   // 3

+           64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,   // 4

+           80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,   // 5

+           96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,   // 6

+           80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127,  // 7

+           128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,  // 8

+           144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,  // 9

+           160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,  // A

+           176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,  // B

+           192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,  // C

+           208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,  // D

+           224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,  // E

+           240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255   // F

+        };

+    }

+    //! \endcond

+

+}

+

+// Undefine internal macros

+#undef RAPIDXML_PARSE_ERROR

+

+// On MSVC, restore warnings state

+#ifdef _MSC_VER

+    #pragma warning(pop)

+#endif

+

+#endif

diff --git a/gui/remaining.txt b/gui/remaining.txt
new file mode 100644
index 0000000..298ee90
--- /dev/null
+++ b/gui/remaining.txt
@@ -0,0 +1,4 @@
+* Confirmation of action
+* Multi-String Variables
+* String Box
+
diff --git a/gui/resources.cpp b/gui/resources.cpp
new file mode 100644
index 0000000..4fce0ca
--- /dev/null
+++ b/gui/resources.cpp
@@ -0,0 +1,337 @@
+// resource.cpp - Source to manage GUI resources
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <iomanip>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+#define TMP_RESOURCE_NAME   "/tmp/extract.bin"
+
+Resource::Resource(xml_node<>* node, ZipArchive* pZip)
+{
+	if (node && node->first_attribute("name"))
+		mName = node->first_attribute("name")->value();
+}
+
+int Resource::ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile)
+{
+	if (!pZip)
+		return -1;
+
+	std::string src = folderName + "/" + fileName + fileExtn;
+
+	const ZipEntry* binary = mzFindZipEntry(pZip, src.c_str());
+	if (binary == NULL) {
+		return -1;
+	}
+
+	unlink(destFile.c_str());
+	int fd = creat(destFile.c_str(), 0666);
+	if (fd < 0)
+		return -1;
+
+	int ret = 0;
+	if (!mzExtractZipEntryToFile(pZip, binary, fd))
+		ret = -1;
+
+	close(fd);
+	return ret;
+}
+
+FontResource::FontResource(xml_node<>* node, ZipArchive* pZip)
+ : Resource(node, pZip)
+{
+	std::string file;
+	xml_attribute<>* attr;
+
+	mFont = NULL;
+	if (!node)
+		return;
+
+	attr = node->first_attribute("filename");
+	if (!attr)
+		return;
+
+	file = attr->value();
+
+#ifndef TW_DISABLE_TTF
+	if(file.size() >= 4 && file.compare(file.size()-4, 4, ".ttf") == 0)
+	{
+		m_type = TYPE_TTF;
+
+		attr = node->first_attribute("size");
+		if(!attr)
+			return;
+
+		int size = atoi(attr->value());
+		int dpi = 300;
+
+		attr = node->first_attribute("dpi");
+		if(attr)
+			dpi = atoi(attr->value());
+
+		if (ExtractResource(pZip, "fonts", file, "", TMP_RESOURCE_NAME) == 0)
+		{
+			mFont = gr_ttf_loadFont(TMP_RESOURCE_NAME, size, dpi);
+			unlink(TMP_RESOURCE_NAME);
+		}
+		else
+		{
+			file = std::string("/res/fonts/") + file;
+			mFont = gr_ttf_loadFont(file.c_str(), size, dpi);
+		}
+	}
+	else
+#endif
+	{
+		m_type = TYPE_TWRP;
+
+		if(file.size() >= 4 && file.compare(file.size()-4, 4, ".ttf") == 0)
+		{
+			attr = node->first_attribute("fallback");
+			if (!attr)
+				return;
+
+			file = attr->value();
+		}
+
+		if (ExtractResource(pZip, "fonts", file, ".dat", TMP_RESOURCE_NAME) == 0)
+		{
+			mFont = gr_loadFont(TMP_RESOURCE_NAME);
+			unlink(TMP_RESOURCE_NAME);
+		}
+		else
+		{
+			mFont = gr_loadFont(file.c_str());
+		}
+	}
+}
+
+FontResource::~FontResource()
+{
+	if(mFont)
+	{
+#ifndef TW_DISABLE_TTF
+		if(m_type == TYPE_TTF)
+			gr_ttf_freeFont(mFont);
+		else
+#endif
+			gr_freeFont(mFont);
+	}
+}
+
+ImageResource::ImageResource(xml_node<>* node, ZipArchive* pZip)
+ : Resource(node, pZip)
+{
+	std::string file;
+
+	mSurface = NULL;
+	if (!node)
+		return;
+
+	if (node->first_attribute("filename"))
+		file = node->first_attribute("filename")->value();
+
+	if (ExtractResource(pZip, "images", file, ".png", TMP_RESOURCE_NAME) == 0)
+	{
+		res_create_surface(TMP_RESOURCE_NAME, &mSurface);
+		unlink(TMP_RESOURCE_NAME);
+	}
+	else if (ExtractResource(pZip, "images", file, "", TMP_RESOURCE_NAME) == 0)
+	{
+		// JPG includes the .jpg extension in the filename so extension should be blank
+		res_create_surface(TMP_RESOURCE_NAME, &mSurface);
+		unlink(TMP_RESOURCE_NAME);
+	}
+	else
+		res_create_surface(file.c_str(), &mSurface);
+}
+
+ImageResource::~ImageResource()
+{
+	if (mSurface)
+		res_free_surface(mSurface);
+}
+
+AnimationResource::AnimationResource(xml_node<>* node, ZipArchive* pZip)
+ : Resource(node, pZip)
+{
+	std::string file;
+	int fileNum = 1;
+
+	if (!node)
+		return;
+
+	if (node->first_attribute("filename"))
+		file = node->first_attribute("filename")->value();
+
+	for (;;)
+	{
+		std::ostringstream fileName;
+		fileName << file << std::setfill ('0') << std::setw (3) << fileNum;
+
+		gr_surface surface;
+		if (pZip)
+		{
+			if (ExtractResource(pZip, "images", fileName.str(), ".png", TMP_RESOURCE_NAME) != 0)
+				break;
+
+			if (res_create_surface(TMP_RESOURCE_NAME, &surface))
+				break;
+
+			unlink(TMP_RESOURCE_NAME);
+		}
+		else
+		{
+			if (res_create_surface(fileName.str().c_str(), &surface))
+				break;
+		}
+		mSurfaces.push_back(surface);
+		fileNum++;
+	}
+}
+
+AnimationResource::~AnimationResource()
+{
+	std::vector<gr_surface>::iterator it;
+
+	for (it = mSurfaces.begin(); it != mSurfaces.end(); ++it)
+		res_free_surface(*it);
+
+	mSurfaces.clear();
+}
+
+Resource* ResourceManager::FindResource(std::string name)
+{
+	std::vector<Resource*>::iterator iter;
+
+	for (iter = mResources.begin(); iter != mResources.end(); iter++)
+	{
+		if (name == (*iter)->GetName())
+			return (*iter);
+	}
+	return NULL;
+}
+
+ResourceManager::ResourceManager(xml_node<>* resList, ZipArchive* pZip)
+{
+	LoadResources(resList, pZip);
+}
+
+void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip)
+{
+	xml_node<>* child;
+
+	if (!resList)
+		return;
+
+	child = resList->first_node("resource");
+	while (child != NULL)
+	{
+		xml_attribute<>* attr = child->first_attribute("type");
+		if (!attr)
+			break;
+
+		std::string type = attr->value();
+
+		if (type == "font")
+		{
+			FontResource* res = new FontResource(child, pZip);
+			if (res == NULL || res->GetResource() == NULL)
+			{
+				xml_attribute<>* attr_name = child->first_attribute("name");
+
+				if (!attr_name) {
+					std::string res_name = attr_name->value();
+					LOGERR("Resource (%s)-(%s) failed to load\n", type.c_str(), res_name.c_str());
+				} else
+					LOGERR("Resource type (%s) failed to load\n", type.c_str());
+
+				delete res;
+			}
+			else
+			{
+				mResources.push_back((Resource*) res);
+			}
+		}
+		else if (type == "image")
+		{
+			ImageResource* res = new ImageResource(child, pZip);
+			if (res == NULL || res->GetResource() == NULL)
+			{
+				xml_attribute<>* attr_name = child->first_attribute("name");
+
+				if (!attr_name) {
+					std::string res_name = attr_name->value();
+					LOGERR("Resource (%s)-(%s) failed to load\n", type.c_str(), res_name.c_str());
+				} else
+					LOGERR("Resource type (%s) failed to load\n", type.c_str());
+
+				delete res;
+			}
+			else
+			{
+				mResources.push_back((Resource*) res);
+			}
+		}
+		else if (type == "animation")
+		{
+			AnimationResource* res = new AnimationResource(child, pZip);
+			if (res == NULL || res->GetResource() == NULL)
+			{
+				xml_attribute<>* attr_name = child->first_attribute("name");
+
+				if (!attr_name) {
+					std::string res_name = attr_name->value();
+					LOGERR("Resource (%s)-(%s) failed to load\n", type.c_str(), res_name.c_str());
+				} else
+					LOGERR("Resource type (%s) failed to load\n", type.c_str());
+
+				delete res;
+			}
+			else
+			{
+				mResources.push_back((Resource*) res);
+			}
+		}
+		else
+		{
+			LOGERR("Resource type (%s) not supported.\n", type.c_str());
+		}
+
+		child = child->next_sibling("resource");
+	}
+}
+
+ResourceManager::~ResourceManager()
+{
+	std::vector<Resource*>::iterator iter;
+
+	for (iter = mResources.begin(); iter != mResources.end(); iter++)
+		delete *iter;
+
+	mResources.clear();
+}
diff --git a/gui/resources.hpp b/gui/resources.hpp
new file mode 100644
index 0000000..3cb5281
--- /dev/null
+++ b/gui/resources.hpp
@@ -0,0 +1,102 @@
+// resources.hpp - Base classes for resource management of GUI
+
+#ifndef _RESOURCE_HEADER
+#define _RESOURCE_HEADER
+
+#ifdef HAVE_SELINUX
+#include "../minzip/Zip.h"
+#else
+#include "../minzipold/Zip.h"
+#endif
+
+// Base Objects
+class Resource
+{
+public:
+	Resource(xml_node<>* node, ZipArchive* pZip);
+	virtual ~Resource() {}
+
+public:
+	virtual void* GetResource(void) = 0;
+	std::string GetName(void) { return mName; }
+
+private:
+	std::string mName;
+
+protected:
+	static int ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile);
+};
+
+typedef enum {
+	TOUCH_START = 0,
+	TOUCH_DRAG = 1,
+	TOUCH_RELEASE = 2,
+	TOUCH_HOLD = 3,
+	TOUCH_REPEAT = 4
+} TOUCH_STATE;
+
+class FontResource : public Resource
+{
+public:
+	enum Type
+	{
+		TYPE_TWRP,
+#ifndef TW_DISABLE_TTF
+		TYPE_TTF,
+#endif
+	};
+
+	FontResource(xml_node<>* node, ZipArchive* pZip);
+	virtual ~FontResource();
+
+public:
+	virtual void* GetResource(void) { return mFont; }
+
+protected:
+	void* mFont;
+	Type m_type;
+};
+
+class ImageResource : public Resource
+{
+public:
+	ImageResource(xml_node<>* node, ZipArchive* pZip);
+	virtual ~ImageResource();
+
+public:
+	virtual void* GetResource(void) { return mSurface; }
+
+protected:
+	gr_surface mSurface;
+};
+
+class AnimationResource : public Resource
+{
+public:
+	AnimationResource(xml_node<>* node, ZipArchive* pZip);
+	virtual ~AnimationResource();
+
+public:
+	virtual void* GetResource(void) { return mSurfaces.at(0); }
+	virtual void* GetResource(int entry) { return mSurfaces.at(entry); }
+	virtual int GetResourceCount(void) { return mSurfaces.size(); }
+
+protected:
+	std::vector<gr_surface> mSurfaces;
+};
+
+class ResourceManager
+{
+public:
+	ResourceManager(xml_node<>* resList, ZipArchive* pZip);
+	virtual ~ResourceManager();
+	void LoadResources(xml_node<>* resList, ZipArchive* pZip);
+
+public:
+	Resource* FindResource(std::string name);
+
+private:
+	std::vector<Resource*> mResources;
+};
+
+#endif  // _RESOURCE_HEADER
diff --git a/gui/slider.cpp b/gui/slider.cpp
new file mode 100644
index 0000000..98d2dde
--- /dev/null
+++ b/gui/slider.cpp
@@ -0,0 +1,182 @@
+// slider.cpp - GUISlider object
+// Pulled & ported from https://raw.github.com/agrabren/RecoverWin/master/gui/slider.cpp
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "../data.hpp"
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUISlider::GUISlider(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	sAction = NULL;
+	sSlider = NULL;
+	sSliderUsed = NULL;
+	sTouch = NULL;
+	sTouchW = 20;
+
+	if (!node)
+	{
+		LOGERR("GUISlider created without XML node\n");
+		return;
+	}
+
+	child = node->first_node("resource");
+	if (child)
+	{
+		attr = child->first_attribute("base");
+		if (attr)
+			sSlider = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("used");
+		if (attr)
+			sSliderUsed = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("touch");
+		if (attr)
+			sTouch = PageManager::FindResource(attr->value());
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY);
+
+	if (sSlider && sSlider->GetResource())
+	{
+		mRenderW = gr_get_width(sSlider->GetResource());
+		mRenderH = gr_get_height(sSlider->GetResource());
+	}
+	if (sTouch && sTouch->GetResource())
+	{
+		sTouchW = gr_get_width(sTouch->GetResource());  // Width of the "touch image" that follows the touch (arrow)
+		sTouchH = gr_get_height(sTouch->GetResource()); // Height of the "touch image" that follows the touch (arrow)
+	}
+
+	//LOGINFO("mRenderW: %i mTouchW: %i\n", mRenderW, mTouchW);
+	mActionX = mRenderX;
+	mActionY = mRenderY;
+	mActionW = mRenderW;
+	mActionH = mRenderH;
+
+	sAction = new GUIAction(node);
+
+	sCurTouchX = mRenderX;
+	sUpdate = 1;
+}
+
+GUISlider::~GUISlider()
+{
+	delete sAction;
+}
+
+int GUISlider::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (!sSlider || !sSlider->GetResource())
+		return -1;
+
+	// Draw the slider
+	gr_blit(sSlider->GetResource(), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
+
+	// Draw the used
+	if (sSliderUsed && sSliderUsed->GetResource() && sCurTouchX > mRenderX)
+		gr_blit(sSliderUsed->GetResource(), 0, 0, sCurTouchX - mRenderX, mRenderH, mRenderX, mRenderY);
+
+	// Draw the touch icon
+	if (sTouch && sTouch->GetResource())
+		gr_blit(sTouch->GetResource(), 0, 0, sTouchW, sTouchH, sCurTouchX, (mRenderY + ((mRenderH - sTouchH) / 2)));
+
+	sUpdate = 0;
+	return 0;
+}
+
+int GUISlider::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (sUpdate)
+		return 2;
+	return 0;
+}
+
+int GUISlider::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if(!isConditionTrue())
+		return -1;
+
+	static bool dragging = false;
+
+	switch (state)
+	{
+	case TOUCH_START:
+		if (x >= mRenderX && x <= mRenderX + sTouchW &&
+			y >= mRenderY && y <= mRenderY + mRenderH)
+		{
+			sCurTouchX = x - (sTouchW / 2);
+			if (sCurTouchX < mRenderX)
+				sCurTouchX = mRenderX;
+			dragging = true;
+		}
+		break;
+
+	case TOUCH_DRAG:
+		if (!dragging)
+			return 0;
+		if (y < mRenderY - sTouchH || y > mRenderY + (sTouchH * 2))
+		{
+			sCurTouchX = mRenderX;
+			dragging = false;
+			sUpdate = 1;
+			break;
+		}
+		sCurTouchX = x - (sTouchW / 2);
+		if (sCurTouchX < mRenderX)
+			sCurTouchX = mRenderX;
+		if (sCurTouchX > mRenderX + mRenderW - sTouchW)
+			sCurTouchX = mRenderX + mRenderW - sTouchW;
+		sUpdate = 1;
+		break;
+
+	case TOUCH_RELEASE:
+		if (!dragging)
+			return 0;
+
+		if (sCurTouchX >= mRenderX + mRenderW - sTouchW) {
+			DataManager::Vibrate("tw_button_vibrate");
+			sAction->doActions();
+		}
+
+		sCurTouchX = mRenderX;
+		dragging = false;
+		sUpdate = 1;
+	case TOUCH_REPEAT:
+	case TOUCH_HOLD:
+		break;
+	}
+	return 0;
+}
diff --git a/gui/slidervalue.cpp b/gui/slidervalue.cpp
new file mode 100644
index 0000000..700d7d5
--- /dev/null
+++ b/gui/slidervalue.cpp
@@ -0,0 +1,467 @@
+// slidervalue.cpp - GUISliderValue object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUISliderValue::GUISliderValue(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mMin = 0;
+	mMax = 100;
+	mValue = 0;
+	mLineH = 2;
+	mLinePadding = 10;
+	mSliderW = 5;
+	mSliderH = 30;
+	mLineX = 0;
+	mLineY = 0;
+	mValueStr = NULL;
+	mAction = NULL;
+	mShowCurr = true;
+	mShowRange = false;
+	mChangeOnDrag = false;
+	mRendered = false;
+	mBackgroundImage = NULL;
+	mHandleImage = NULL;
+	mHandleHoverImage = NULL;
+	mDragging = false;
+
+	mLabel = NULL;
+	ConvertStrToColor("white", &mTextColor);
+	ConvertStrToColor("white", &mLineColor);
+	ConvertStrToColor("blue", &mSliderColor);
+
+	if (!node)
+	{
+		LOGERR("GUISliderValue created without XML node\n");
+		return;
+	}
+
+	mLabel = new GUIText(node);
+	if(mLabel->Render() < 0)
+	{
+		delete mLabel;
+		mLabel = NULL;
+	}
+
+	mAction = new GUIAction(node);
+
+	child = node->first_node("font");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mFont = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mTextColor);
+		}
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW);
+
+	child = node->first_node("colors");
+	if (child)
+	{
+		attr = child->first_attribute("line");
+		if (attr)
+			ConvertStrToColor(attr->value(), &mLineColor);
+
+		attr = child->first_attribute("slider");
+		if (attr)
+			ConvertStrToColor(attr->value(), &mSliderColor);
+	}
+
+	child = node->first_node("resource");
+	if (child)
+	{
+		attr = child->first_attribute("background");
+		if(attr)
+			mBackgroundImage = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("handle");
+		if(attr)
+			mHandleImage = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("handlehover");
+		if(attr)
+			mHandleHoverImage = PageManager::FindResource(attr->value());
+	}
+
+	child = node->first_node("data");
+	if (child)
+	{
+		attr = child->first_attribute("variable");
+		if (attr)
+			mVariable = attr->value();
+
+		attr = child->first_attribute("min");
+		if (attr)
+		{
+			mMinStr = gui_parse_text(attr->value());
+			mMin = atoi(mMinStr.c_str());
+		}
+
+		attr = child->first_attribute("max");
+		if (attr)
+		{
+			mMaxStr = gui_parse_text(attr->value());
+			mMax = atoi(mMaxStr.c_str());
+		}
+
+		if (mMin > mMax)
+			mMin = mMax;
+
+		attr = child->first_attribute("default");
+		if (attr)
+		{
+			string parsevalue = gui_parse_text(attr->value());
+			int def = atoi(parsevalue.c_str());
+
+			if (def < mMin)
+				def = mMin;
+			else if (def > mMax)
+				def = mMax;
+			DataManager::SetValue(mVariable, def);
+		}
+
+		attr = child->first_attribute("showrange");
+		if (attr)
+			mShowRange = atoi(attr->value());
+
+		attr = child->first_attribute("showcurr");
+		if (attr)
+			mShowCurr = atoi(attr->value());
+
+		attr = child->first_attribute("changeondrag");
+		if (attr)
+			mChangeOnDrag = atoi(attr->value());
+	}
+
+	child = node->first_node("dimensions");
+	if (child)
+	{
+		attr = child->first_attribute("lineh");
+		if (attr)
+		{
+			string parsevalue = gui_parse_text(attr->value());
+			mLineH = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("linepadding");
+		if (attr)
+		{
+			string parsevalue = gui_parse_text(attr->value());
+			mPadding = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("sliderw");
+		if (attr)
+		{
+			string parsevalue = gui_parse_text(attr->value());
+			mSliderW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("sliderh");
+		if (attr)
+		{
+			string parsevalue = gui_parse_text(attr->value());
+			mSliderH = atoi(parsevalue.c_str());
+		}
+	}
+
+	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
+
+	if(mShowCurr)
+	{
+		int maxLen = std::max(strlen(mMinStr.c_str()), strlen(mMaxStr.c_str()));
+		mValueStr = new char[maxLen+1];
+	}
+
+	loadValue(true);
+
+	mLinePadding = mPadding;
+	if (mShowRange)
+	{
+		int textW = std::max(measureText(mMaxStr), measureText(mMinStr));
+		mLinePadding += textW;
+	}
+
+	SetRenderPos(mRenderX, mRenderY, mRenderW);
+}
+
+GUISliderValue::~GUISliderValue()
+{
+	delete mLabel;
+	delete mAction;
+	delete[] mValueStr;
+}
+
+void GUISliderValue::loadValue(bool force)
+{
+	if(!mVariable.empty())
+	{
+		int value = DataManager::GetIntValue(mVariable);
+		if(mValue == value && !force)
+			return;
+
+		mValue = value;
+	}
+
+	mValue = std::max(mValue, mMin);
+	mValue = std::min(mValue, mMax);
+	mValuePct = pctFromValue(mValue);
+	mRendered = false;
+}
+
+int GUISliderValue::SetRenderPos(int x, int y, int w, int h)
+{
+	mRenderX = x;
+	mRenderY = y;
+	if (w || h)
+	{
+		mRenderW = w;
+		mRenderH = h;
+	}
+
+	mRenderH = mSliderH;
+	if(mShowCurr)
+		mRenderH += mFontHeight;
+
+	if (mLabel)
+	{
+		int lw, lh;
+		mLabel->GetCurrentBounds(lw, lh);
+		int textX = mRenderX + (mRenderW/2 - lw/2);
+
+		mLabel->SetRenderPos(textX, mRenderY);
+
+		y += lh;
+		mRenderH += lh;
+	}
+
+	mSliderY = y;
+
+	mActionX = mRenderX;
+	mActionY = mRenderY;
+	mActionW = mRenderW;
+	mActionH = mRenderH;
+
+	if(mBackgroundImage && mBackgroundImage->GetResource())
+	{
+		mLineW = gr_get_width(mBackgroundImage->GetResource());
+		mLineH = gr_get_height(mBackgroundImage->GetResource());
+	}
+	else
+		mLineW = mRenderW - (mLinePadding * 2);
+
+	mLineY = y + (mSliderH/2 - mLineH/2);
+	mLineX = mRenderX + (mRenderW/2 - mLineW/2);
+
+	return 0;
+}
+
+int GUISliderValue::measureText(const std::string& str)
+{
+	void* fontResource = NULL;
+	if (mFont)  fontResource = mFont->GetResource();
+
+	return gr_measureEx(str.c_str(), fontResource);
+}
+
+int GUISliderValue::Render(void)
+{
+	if (!isConditionTrue())
+	{
+		mRendered = false;
+		return 0;
+	}
+
+	if(mLabel)
+	{
+		int w, h;
+		mLabel->GetCurrentBounds(w, h);
+		if (w != mLabelW) {
+			mLabelW = w;
+			int textX = mRenderX + (mRenderW/2 - mLabelW/2);
+			mLabel->SetRenderPos(textX, mRenderY);
+		}
+		int res = mLabel->Render();
+		if(res < 0)
+			return res;
+	}
+
+	// line
+	if(mBackgroundImage && mBackgroundImage->GetResource())
+	{
+		gr_blit(mBackgroundImage->GetResource(), 0, 0, mLineW, mLineH, mLineX, mLineY);
+	}
+	else
+	{
+		gr_color(mLineColor.red, mLineColor.green, mLineColor.blue, mLineColor.alpha);
+		gr_fill(mLineX, mLineY, mLineW, mLineH);
+	}
+
+	// slider
+	uint32_t sliderX = mLineX + (mValuePct*(mLineW - mSliderW))/100;
+
+	if(mHandleImage && mHandleImage->GetResource())
+	{
+		gr_surface s = mHandleImage->GetResource();
+		if(mDragging && mHandleHoverImage && mHandleHoverImage->GetResource())
+			s = mHandleHoverImage->GetResource();
+		gr_blit(s, 0, 0, mSliderW, mSliderH, sliderX, mLineY + (mLineH/2 - mSliderH/2));
+	}
+	else
+	{
+		gr_color(mSliderColor.red, mSliderColor.green, mSliderColor.blue, mSliderColor.alpha);
+		gr_fill(sliderX, mSliderY, mSliderW, mSliderH);
+	}
+
+	void *fontResource = NULL;
+	if(mFont) fontResource = mFont->GetResource();
+	gr_color(mTextColor.red, mTextColor.green, mTextColor.blue, mTextColor.alpha);
+	if(mShowRange)
+	{
+		int rangeY = (mLineY - mLineH/2) - mFontHeight/2;
+		gr_textEx(mRenderX + mPadding/2, rangeY, mMinStr.c_str(), fontResource);
+		gr_textEx(mLineX + mLineW + mPadding/2, rangeY, mMaxStr.c_str(), fontResource);
+	}
+
+	if(mValueStr && mShowCurr)
+	{
+		sprintf(mValueStr, "%d", mValue);
+		int textW = measureText(mValueStr);
+		gr_textEx(mRenderX + (mRenderW/2 - textW/2), mSliderY+mSliderH, mValueStr, fontResource);
+	}
+
+	mRendered = true;
+	return 0;
+}
+
+int GUISliderValue::Update(void)
+{
+	if (!isConditionTrue())
+		return mRendered ? 2 : 0;
+	if (!mRendered)
+		return 2;
+
+	if(mLabel)
+		return mLabel->Update();
+	return 0;
+}
+
+int GUISliderValue::valueFromPct(float pct)
+{
+	int range = abs(mMax - mMin);
+	return mMin + (pct * range) / 100;
+}
+
+float GUISliderValue::pctFromValue(int value)
+{
+	return float((value - mMin) * 100) / abs(mMax - mMin);
+}
+
+int GUISliderValue::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if (!isConditionTrue())
+		return -1;
+
+	switch (state)
+	{
+	case TOUCH_START:
+		if (x >= mLineX && x <= mLineX + mLineW &&
+			y >= mRenderY && y <= mRenderY + mRenderH)
+		{
+			mDragging = true;
+		}
+		// no break
+	case TOUCH_DRAG:
+	{
+		if (!mDragging)  return 0;
+
+		x = std::max(mLineX + mSliderW/2, x);
+		x = std::min(mLineX + mLineW - mSliderW/2, x);
+
+		mValuePct = float(((x - (mLineX + mSliderW/2)) * 100) / (mLineW - mSliderW));
+		int newVal = valueFromPct(mValuePct);
+		if (newVal != mValue) {
+			mRendered = false;
+			mValue = newVal;
+			if (mChangeOnDrag) {
+				if (!mVariable.empty())
+					DataManager::SetValue(mVariable, mValue);
+				if (mAction)
+					mAction->doActions();
+			}
+		}
+		break;
+	}
+	case TOUCH_RELEASE:
+	{
+		if (!mDragging)  return 0;
+		mDragging = false;
+
+		if (!mVariable.empty())
+			DataManager::SetValue(mVariable, mValue);
+		if (mAction)
+			mAction->doActions();
+		break;
+	}
+	case TOUCH_REPEAT:
+	case TOUCH_HOLD:
+		break;
+	}
+	return 0;
+}
+
+int GUISliderValue::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIObject::NotifyVarChange(varName, value);
+
+	if (mLabel)
+		mLabel->NotifyVarChange(varName, value);
+	if (varName == mVariable) {
+		int newVal = atoi(value.c_str());
+		if(newVal != mValue) {
+			mValue = newVal;
+			mValuePct = pctFromValue(mValue);
+			mRendered = false;
+		}
+	}
+	return 0;
+}
+
+void GUISliderValue::SetPageFocus(int inFocus)
+{
+	if (inFocus)
+		loadValue();
+}
diff --git a/gui/text.cpp b/gui/text.cpp
new file mode 100644
index 0000000..29d7ad9
--- /dev/null
+++ b/gui/text.cpp
@@ -0,0 +1,245 @@
+// text.cpp - GUIText object
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUIText::GUIText(xml_node<>* node)
+	: GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mFont = NULL;
+	mIsStatic = 1;
+	mVarChanged = 0;
+	mFontHeight = 0;
+	maxWidth = 0;
+	charSkip = 0;
+	isHighlighted = false;
+	hasHighlightColor = false;
+
+	if (!node)
+		return;
+
+	// Initialize color to solid black
+	memset(&mColor, 0, sizeof(COLOR));
+	mColor.alpha = 255;
+	memset(&mHighlightColor, 0, sizeof(COLOR));
+	mHighlightColor.alpha = 255;
+
+	attr = node->first_attribute("color");
+	if (attr)
+	{
+		std::string color = attr->value();
+		ConvertStrToColor(color, &mColor);
+	}
+	attr = node->first_attribute("highlightcolor");
+	if (attr)
+	{
+		std::string color = attr->value();
+		ConvertStrToColor(color, &mHighlightColor);
+		hasHighlightColor = true;
+	}
+
+	// Load the font, and possibly override the color
+	child = node->first_node("font");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mFont = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mColor);
+		}
+
+		attr = child->first_attribute("highlightcolor");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHighlightColor);
+			hasHighlightColor = true;
+		}
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH, &mPlacement);
+
+	child = node->first_node("text");
+	if (child)  mText = child->value();
+
+	// Simple way to check for static state
+	mLastValue = parseText();
+	if (mLastValue != mText)   mIsStatic = 0;
+
+	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
+	return;
+}
+
+int GUIText::Render(void)
+{
+	if (!isConditionTrue())
+		return 0;
+
+	void* fontResource = NULL;
+	string displayValue;
+
+	if (mFont)
+		fontResource = mFont->GetResource();
+
+	mLastValue = parseText();
+	displayValue = mLastValue;
+
+	if (charSkip)
+		displayValue.erase(0, charSkip);
+
+	mVarChanged = 0;
+
+	int x = mRenderX, y = mRenderY;
+	int width = gr_measureEx(displayValue.c_str(), fontResource);
+
+	if (mPlacement != TOP_LEFT && mPlacement != BOTTOM_LEFT)
+	{
+		if (mPlacement == CENTER || mPlacement == CENTER_X_ONLY)
+			x -= (width / 2);
+		else
+			x -= width;
+	}
+	if (mPlacement != TOP_LEFT && mPlacement != TOP_RIGHT)
+	{
+		if (mPlacement == CENTER)
+			y -= (mFontHeight / 2);
+		else if (mPlacement == BOTTOM_LEFT || mPlacement == BOTTOM_RIGHT)
+			y -= mFontHeight;
+	}
+
+	if (hasHighlightColor && isHighlighted)
+		gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
+	else
+		gr_color(mColor.red, mColor.green, mColor.blue, mColor.alpha);
+
+	if (maxWidth)
+		gr_textExW(x, y, displayValue.c_str(), fontResource, maxWidth + x);
+	else
+		gr_textEx(x, y, displayValue.c_str(), fontResource);
+	return 0;
+}
+
+int GUIText::Update(void)
+{
+	if (!isConditionTrue())
+		return 0;
+
+	static int updateCounter = 3;
+
+	// This hack just makes sure we update at least once a minute for things like clock and battery
+	if (updateCounter)  updateCounter--;
+	else
+	{
+		mVarChanged = 1;
+		updateCounter = 3;
+	}
+
+	if (mIsStatic || !mVarChanged)
+		return 0;
+
+	std::string newValue = parseText();
+	if (mLastValue == newValue)
+		return 0;
+	else
+		mLastValue = newValue;
+	return 2;
+}
+
+int GUIText::GetCurrentBounds(int& w, int& h)
+{
+	void* fontResource = NULL;
+
+	if (mFont)
+		fontResource = mFont->GetResource();
+
+	h = mFontHeight;
+	mLastValue = parseText();
+	w = gr_measureEx(mLastValue.c_str(), fontResource);
+	return 0;
+}
+
+std::string GUIText::parseText(void)
+{
+	static int counter = 0;
+	std::string str = mText;
+	size_t pos = 0;
+	size_t next = 0, end = 0;
+
+	while (1)
+	{
+		next = str.find('%', pos);
+		if (next == std::string::npos) return str;
+		end = str.find('%', next + 1);
+		if (end == std::string::npos) return str;
+
+		// We have a block of data
+		std::string var = str.substr(next + 1, (end - next) - 1);
+		str.erase(next, (end - next) + 1);
+
+		if (next + 1 == end)
+		{
+			str.insert(next, 1, '%');
+		}
+		else
+		{
+			std::string value;
+			if (DataManager::GetValue(var, value) == 0)
+				str.insert(next, value);
+		}
+
+		pos = next + 1;
+	}
+}
+
+int GUIText::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIObject::NotifyVarChange(varName, value);
+
+	mVarChanged = 1;
+	return 0;
+}
+
+int GUIText::SetMaxWidth(unsigned width)
+{
+	maxWidth = width;
+	mVarChanged = 1;
+	return 0;
+}
+
+int GUIText::SkipCharCount(unsigned skip)
+{
+	charSkip = skip;
+	mVarChanged = 1;
+	return 0;
+}
diff --git a/gui/twrp b/gui/twrp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gui/twrp
diff --git a/htcdumlock/Android.mk b/htcdumlock/Android.mk
new file mode 100644
index 0000000..242f124
--- /dev/null
+++ b/htcdumlock/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+ifeq ($(TW_INCLUDE_DUMLOCK), true)
+	LOCAL_SRC_FILES:= \
+		htcdumlock.c
+	LOCAL_CFLAGS:= -g -c -W
+	LOCAL_MODULE:=htcdumlock
+	LOCAL_MODULE_TAGS:= eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+	include $(BUILD_EXECUTABLE)
+endif
\ No newline at end of file
diff --git a/htcdumlock/htcdumlock.c b/htcdumlock/htcdumlock.c
new file mode 100644
index 0000000..ec9469f
--- /dev/null
+++ b/htcdumlock/htcdumlock.c
@@ -0,0 +1,406 @@
+/*
+ * This binary is a workaround for HTC's unlock method that doesn't let
+ * you flash boot while booted to recovery.  It is designed to dump
+ * recovery and boot to the sdcard then flash recovery to boot. When
+ * used with a supported recovery, you can reflash the dumped copy of
+ * boot once you enter the recovery.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * The code was written from scratch by Dees_Troy dees_troy at
+ * yahoo
+ *
+ * Copyright (c) 2012
+ *
+ * Note that this all could probably be done as a shell script, but
+ * I am much better at C than I am at scripting. :)
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+// Number of bytes in the ramdisks to compare
+#define SCAN_SIZE 60
+
+#define CMDLINE_SERIALNO        "androidboot.serialno="
+#define CMDLINE_SERIALNO_LEN    (strlen(CMDLINE_SERIALNO))
+#define CPUINFO_SERIALNO        "Serial"
+#define CPUINFO_SERIALNO_LEN    (strlen(CPUINFO_SERIALNO))
+#define CPUINFO_HARDWARE        "Hardware"
+#define CPUINFO_HARDWARE_LEN    (strlen(CPUINFO_HARDWARE))
+
+char device_id[255];
+int verbose = 0, java = 0;
+
+void get_device_id(void)
+{
+	FILE *fp;
+    char line[2048];
+	char hardware_id[32];
+	char* token;
+
+    // Assign a blank device_id to start with
+    device_id[0] = 0;
+
+    // First, try the cmdline to see if the serial number was supplied
+	fp = fopen("/proc/cmdline", "rt");
+	if (fp != NULL)
+    {
+        // First step, read the line. For cmdline, it's one long line
+        fgets(line, sizeof(line), fp);
+        fclose(fp);
+
+        // Now, let's tokenize the string
+        token = strtok(line, " ");
+
+        // Let's walk through the line, looking for the CMDLINE_SERIALNO token
+        while (token)
+        {
+            // We don't need to verify the length of token, because if it's too short, it will mismatch CMDLINE_SERIALNO at the NULL
+            if (memcmp(token, CMDLINE_SERIALNO, CMDLINE_SERIALNO_LEN) == 0)
+            {
+                // We found the serial number!
+                strcpy(device_id, token + CMDLINE_SERIALNO_LEN);
+                return;
+            }
+            token = strtok(NULL, " ");
+        }
+    }
+
+	// Now we'll try cpuinfo for a serial number
+	fp = fopen("/proc/cpuinfo", "rt");
+	if (fp != NULL)
+    {
+		while (fgets(line, sizeof(line), fp) != NULL) { // First step, read the line.
+			if (memcmp(line, CPUINFO_SERIALNO, CPUINFO_SERIALNO_LEN) == 0)  // check the beginning of the line for "Serial"
+			{
+				// We found the serial number!
+				token = line + CPUINFO_SERIALNO_LEN; // skip past "Serial"
+				while (((int)*token > 0 && (int)*token <= 32 ) || (int)*token == ':') token++; // skip over all spaces and the colon
+				if (*token != NULL) {
+                    token[30] = 0;
+					if (token[strlen(token)-1] == 10) { // checking for endline chars and dropping them from the end of the string if needed
+						memset(device_id, 0, sizeof(device_id));
+						strncpy(device_id, token, strlen(token) - 1);
+					} else {
+						strcpy(device_id, token);
+					}
+					if (verbose)
+						printf("serial from cpuinfo: '%s'\n", device_id);
+					fclose(fp);
+					return;
+				}
+			} else if (memcmp(line, CPUINFO_HARDWARE, CPUINFO_HARDWARE_LEN) == 0) {// We're also going to look for the hardware line in cpuinfo and save it for later in case we don't find the device ID
+				// We found the hardware ID
+				token = line + CPUINFO_HARDWARE_LEN; // skip past "Hardware"
+				while (((int)*token > 0 && (int)*token <= 32 ) || (int)*token == ':')  token++; // skip over all spaces and the colon
+				if (*token != NULL) {
+                    token[30] = 0;
+					if (token[strlen(token)-1] == 10) { // checking for endline chars and dropping them from the end of the string if needed
+                        memset(hardware_id, 0, sizeof(hardware_id));
+						strncpy(hardware_id, token, strlen(token) - 1);
+					} else {
+						strcpy(hardware_id, token);
+					}
+					if (verbose)
+						printf("hardware id from cpuinfo: '%s'\n", hardware_id);
+				}
+			}
+		}
+		fclose(fp);
+    }
+
+	if (hardware_id[0] != 0) {
+		if (verbose)
+			printf("using hardware id for device id: '%s'\n", hardware_id);
+		strcpy(device_id, hardware_id);
+		return;
+	}
+
+    strcpy(device_id, "serialno");
+	if (verbose)
+		printf("device id not found, using '%s'.", device_id);
+    return;
+}
+
+void reboot_device() {
+	// Reboot
+	printf("Rebooting!\n");
+	system("reboot system");
+}
+
+void scan_for_ramdisk_data(char *filename, char *ramdisk) {
+	FILE *pFile;
+	unsigned long lSize;
+	unsigned char *buffer;
+	size_t result;
+	int i;
+
+	pFile = fopen(filename, "rb");
+	if(pFile==NULL){
+		printf("Unabled to open image.\nFailed\n");
+		exit(1);
+	}
+
+	fseek (pFile , 0 , SEEK_END);
+	lSize = ftell(pFile);
+	rewind(pFile);
+
+	//printf("\n\nFile is %ld bytes big\n\n", lSize);
+
+	buffer = (unsigned char*)malloc(sizeof(unsigned char) * lSize);
+	if(buffer == NULL){
+		printf("File read error!\nFailed\n");
+		exit(2);
+	}
+
+	result = fread (buffer, 1, lSize, pFile);
+	if (result != lSize) {
+		printf("Error reading file '%s'\nFailed\n", filename);
+		exit(3);
+	}
+
+	unsigned char needle[6] = {0x00, 0x00, 0x00, 0x00, 0x1f, 0x8b};
+	unsigned char *last_needle = NULL;
+	//char *p = memmem(needle, lSize, buffer, sizeof(needle));
+	unsigned char *p = memmem(buffer + 2048, lSize - 2048, needle, sizeof(needle));
+	if (!p) {
+		fclose(pFile);
+		printf("Ramdisk not found in '%s', error!\nFailed\n", filename);
+		exit(4);
+	} else {
+		//printf("Ramdisk found in '%s'!\n", filename);
+	}
+
+	memcpy(ramdisk, p, sizeof(char) * SCAN_SIZE);
+	fclose(pFile);
+	free(buffer);
+}
+
+int compare_ramdisks(char *boot_path, char *recovery_path) {
+	char boot_data[SCAN_SIZE], recovery_data[SCAN_SIZE];
+
+	scan_for_ramdisk_data(boot_path, (char*)&boot_data);
+	scan_for_ramdisk_data(recovery_path, (char*)&recovery_data);
+	if (memcmp(boot_data, recovery_data, sizeof(boot_data)) == 0) {
+		if (verbose)
+			printf("Boot and recovery are the same.\n");
+		return 0;
+	} else {
+		if (verbose)
+			printf("Boot and recovery are NOT the same.\n");
+		return 1;
+	}
+}
+
+void flash_recovery_to_boot(int no_flash, int no_reboot) {
+	char twrp_device_path[255], recovery_path[255], boot_path[255],
+		exec[255], md5recovery[255], md5boot[255],
+		recoveryimg[255], bootimg[255], tempimg[255];
+	int ret_val = 0;
+	FILE *fp;
+	char* token;
+
+	// Create folders
+	if (verbose)
+		printf("Making '/sdcard/TWRP'\n");
+	mkdir("/sdcard/TWRP", 0777);
+	if (verbose)
+		printf("Making folder '/sdcard/TWRP/htcdumlock'\n");
+	mkdir("/sdcard/TWRP/htcdumlock", 0777);
+	strcpy(twrp_device_path, "/sdcard/TWRP/htcdumlock/");
+	strcat(twrp_device_path, device_id);
+	if (verbose)
+		printf("Making folder '%s'\n", twrp_device_path);
+	mkdir(twrp_device_path, 0777);
+	// Make folder for recovery
+	strcpy(recovery_path, twrp_device_path);
+	strcat(recovery_path, "/recovery");
+	if (verbose)
+		printf("Making folder '%s'\n", recovery_path);
+	mkdir(recovery_path, 0777);
+	strcat(recovery_path, "/");
+	// Make folder for boot
+	strcpy(boot_path, twrp_device_path);
+	strcat(boot_path, "/boot");
+	if (verbose)
+		printf("Making folder '%s'\n", boot_path);
+	mkdir(boot_path, 0777);
+	strcat(boot_path, "/");
+
+	// Set up file locations
+	strcpy(recoveryimg, recovery_path);
+	strcat(recoveryimg, "recovery.img");
+	strcpy(bootimg, boot_path);
+	strcat(bootimg, "boot.img");
+	strcpy(tempimg, twrp_device_path);
+	strcat(tempimg, "/temp.img");
+
+	// Dump recovery
+	strcpy(exec, "dump_image recovery ");
+	strcat(exec, recoveryimg);
+	if (verbose)
+		printf("Running command: '%s'\n", exec);
+	ret_val = system(exec);
+	if (ret_val != 0) {
+		printf("Unable to dump recovery.\nFailed\n");
+		return;
+	}
+
+	// Dump boot (kernel)
+	strcpy(exec, "dump_image boot ");
+	strcat(exec, tempimg);
+	if (verbose)
+		printf("Running command: '%s'\n", exec);
+	ret_val = system(exec);
+	if (ret_val != 0) {
+		printf("Unable to dump recovery.\nFailed\n");
+		return;
+	}
+
+	// Compare the ramdisks of the images from boot and recovery to make sure they are different
+	// If they are the same, then recovery is already flashed to boot and we don't want to wipe
+	// out our existing backup of boot
+	if (compare_ramdisks(tempimg, recoveryimg) != 0) {
+		if (verbose)
+			printf("Boot and recovery do not match so recovery is not flashed to boot yet...\n");
+		strcpy(exec, "mv ");
+		strcat(exec, tempimg);
+		strcat(exec, " ");
+		strcat(exec, bootimg);
+		if (verbose)
+			printf("Moving temporary boot.img: '%s'\n", exec);
+		ret_val = system(exec);
+		if (ret_val != 0) {
+			printf("Unable to move temporary boot image.\nFailed\n");
+			return;
+		}
+	} else {
+		if (!java)
+			printf("Ramdisk recovery and boot matches! Recovery is already flashed to boot!\n");
+		if (!no_reboot)
+			reboot_device();
+		return;
+	}
+
+	// Flash recovery to boot
+	strcpy(exec, "flash_image boot ");
+	strcat(exec, recoveryimg);
+	if (no_flash) {
+		if (verbose)
+			printf("NOT flashing recovery to boot due to argument 'noflash', command is '%s'\n", exec);
+	} else {
+		if (verbose)
+			printf("Running command: '%s'\n", exec);
+		ret_val = system(exec);
+		if (ret_val != 0) {
+			printf("Unable to flash recovery to boot.\nFailed\n");
+			return;
+		}
+	}
+
+	if (!no_reboot && !ret_val)
+		reboot_device();
+}
+
+void restore_original_boot(int no_flash) {
+	char boot_path[255], exec[255];
+
+	// Restore original boot partition
+	strcpy(boot_path, "/sdcard/TWRP/htcdumlock/");
+	strcat(boot_path, device_id);
+	strcat(boot_path, "/boot/");
+	strcpy(exec, "flash_image boot ");
+	strcat(exec, boot_path);
+	strcat(exec, "boot.img");
+	if (no_flash) {
+		if (verbose)
+			printf("NOT restoring boot due to argument 'noflash', command is '%s'\n", exec);
+	} else {
+		if (verbose)
+			printf("Running command: '%s'\n", exec);
+		system(exec);
+	}
+}
+
+int main(int argc, char** argv)
+{
+	int recovery = 0, no_flash = 0, restore_boot = 0, arg_error = 0,
+		no_reboot = 0, i;
+
+	// Parse the arguments
+	if (argc < 2)
+		arg_error = 1;
+	else {
+		for (i=1; i<argc; i++) {
+			if (strcmp(argv[i], "recovery") == 0) {
+				// Check to see if restore option is already set
+				// Do not allow user to do recovery and restore at the same time
+				if (restore_boot)
+					arg_error = 1;
+				recovery = 1;
+			} else if (strcmp(argv[i], "restore") == 0) {
+				// Check to see if recovery option is already set
+				// Do not allow user to do recovery and restore at the same time
+				if (recovery)
+					arg_error = 1;
+				restore_boot = 1;
+			} else if (strcmp(argv[i], "noflash") == 0)
+				no_flash = 1;
+			else if (strcmp(argv[i], "noreboot") == 0)
+				no_reboot = 1;
+			else if (strcmp(argv[i], "verbose") == 0)
+				verbose = 1;
+			else if (strcmp(argv[i], "java") == 0)
+				java = 1;
+			else
+				arg_error = 1;
+		}
+	}
+	if (arg_error) {
+		printf("Invalid argument given.\n");
+		printf("Valid arguments are:\n");
+		printf("  recovery -- backs up boot and recovery and flashes recovery to boot\n");
+		printf("  restore  -- restores the most recent backup of boot made by this utility\n");
+		printf("  noflash  -- same as 'recovery' but does not flash boot or reboot at the end\n");
+		printf("  noreboot -- does not reboot after flashing boot during 'recovery'\n");
+		printf("  verbose  -- show extra debug information\n");
+		printf("\nNOTE: You cannot do 'recovery' and 'restore' in the same operation.\nFailed\n");
+		return 0;
+	}
+
+	get_device_id();
+	if (verbose)
+		printf("Device ID is: '%s'\n", device_id);
+	if (strcmp(device_id, "0000000000000000") == 0) {
+		printf("Error, device ID is all zeros!\n");
+		printf("Did you 'su' first? HTC Dumlock requires root access.\nFailed\n");
+		return 0;
+	}
+
+	if (recovery) {
+		if (!java)
+			printf("Flashing recovery to boot, this may take a few minutes . . .\n");
+		flash_recovery_to_boot(no_flash, no_reboot);
+	}
+	if (restore_boot) {
+		printf("Restoring boot, this may take a few minutes . . .\n");
+		restore_original_boot(no_flash);
+	}
+
+	return 0;
+}
diff --git a/infomanager.cpp b/infomanager.cpp
new file mode 100644
index 0000000..080fe1d
--- /dev/null
+++ b/infomanager.cpp
@@ -0,0 +1,206 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <linux/input.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+#include <utility>
+#include <map>
+#include <fstream>
+#include <sstream>
+
+#include "infomanager.hpp"
+#include "twcommon.h"
+#include "partitions.hpp"
+
+using namespace std;
+
+InfoManager::InfoManager(const string filename) {
+	File = filename;
+}
+
+InfoManager::~InfoManager(void) {
+	mValues.clear();
+}
+
+int InfoManager::LoadValues(void) {
+	string str;
+
+	// Read in the file, if possible
+	FILE* in = fopen(File.c_str(), "rb");
+	if (!in) {
+		LOGINFO("InfoManager file '%s' not found.\n", File.c_str());
+		return -1;
+	} else {
+		LOGINFO("InfoManager loading from '%s'.\n", File.c_str());
+	}
+
+	while (!feof(in)) {
+		string Name;
+		string Value;
+		unsigned short length;
+		char array[512];
+
+		if (fread(&length, 1, sizeof(unsigned short), in) != sizeof(unsigned short))	goto error;
+		if (length >= 512)																goto error;
+		if (fread(array, 1, length, in) != length)										goto error;
+		Name = array;
+
+		if (fread(&length, 1, sizeof(unsigned short), in) != sizeof(unsigned short))	goto error;
+		if (length >= 512)																goto error;
+		if (fread(array, 1, length, in) != length)										goto error;
+		Value = array;
+
+		map<string, string>::iterator pos;
+
+		pos = mValues.find(Name);
+		if (pos != mValues.end()) {
+			pos->second = Value;
+		} else {
+			mValues.insert(make_pair(Name, Value));
+		}
+	}
+error:
+	fclose(in);
+	return 0;
+}
+
+int InfoManager::SaveValues(void) {
+	if (File.empty())
+		return -1;
+
+	PartitionManager.Mount_By_Path(File, true);
+LOGINFO("InfoManager saving '%s'\n", File.c_str());
+	FILE* out = fopen(File.c_str(), "wb");
+	if (!out)
+		return -1;
+
+	map<string, string>::iterator iter;
+	for (iter = mValues.begin(); iter != mValues.end(); ++iter) {
+		unsigned short length = (unsigned short) iter->first.length() + 1;
+		fwrite(&length, 1, sizeof(unsigned short), out);
+		fwrite(iter->first.c_str(), 1, length, out);
+		length = (unsigned short) iter->second.length() + 1;
+		fwrite(&length, 1, sizeof(unsigned short), out);
+		fwrite(iter->second.c_str(), 1, length, out);
+	}
+	fclose(out);
+	return 0;
+}
+
+int InfoManager::GetValue(const string varName, string& value) {
+	string localStr = varName;
+
+	map<string, string>::iterator pos;
+	pos = mValues.find(localStr);
+	if (pos == mValues.end())
+		return -1;
+
+	value = pos->second;
+	return 0;
+}
+
+int InfoManager::GetValue(const string varName, int& value) {
+	string data;
+
+	if (GetValue(varName,data) != 0)
+		return -1;
+
+	value = atoi(data.c_str());
+	return 0;
+}
+
+int InfoManager::GetValue(const string varName, float& value) {
+	string data;
+
+	if (GetValue(varName,data) != 0)
+		return -1;
+
+	value = atof(data.c_str());
+	return 0;
+}
+
+unsigned long long InfoManager::GetValue(const string varName, unsigned long long& value) {
+	string data;
+
+	if (GetValue(varName,data) != 0)
+		return -1;
+
+	value = strtoull(data.c_str(), NULL, 10);
+	return 0;
+}
+
+// This function will return an empty string if the value doesn't exist
+string InfoManager::GetStrValue(const string varName) {
+	string retVal;
+
+	GetValue(varName, retVal);
+	return retVal;
+}
+
+// This function will return 0 if the value doesn't exist
+int InfoManager::GetIntValue(const string varName) {
+	string retVal;
+	GetValue(varName, retVal);
+	return atoi(retVal.c_str());
+}
+
+int InfoManager::SetValue(const string varName, string value) {
+	// Don't allow empty values or numerical starting values
+	if (varName.empty() || (varName[0] >= '0' && varName[0] <= '9'))
+		return -1;
+
+	map<string, string>::iterator pos;
+	pos = mValues.find(varName);
+	if (pos == mValues.end())
+		mValues.insert(make_pair(varName, value));
+	else
+		pos->second = value;
+
+	return 0;
+}
+
+int InfoManager::SetValue(const string varName, int value) {
+	ostringstream valStr;
+	valStr << value;
+	return SetValue(varName, valStr.str());
+}
+
+int InfoManager::SetValue(const string varName, float value) {
+	ostringstream valStr;
+	valStr << value;
+	return SetValue(varName, valStr.str());
+}
+
+int InfoManager::SetValue(const string varName, unsigned long long value) {
+	ostringstream valStr;
+	valStr << value;
+	return SetValue(varName, valStr.str());
+}
diff --git a/infomanager.hpp b/infomanager.hpp
new file mode 100644
index 0000000..de8aef4
--- /dev/null
+++ b/infomanager.hpp
@@ -0,0 +1,58 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _INFOMANAGER_HPP_HEADER
+#define _INFOMANAGER_HPP_HEADER
+
+#include <string>
+#include <utility>
+#include <map>
+
+using namespace std;
+
+class InfoManager
+{
+public:
+	InfoManager(const string filename);
+	virtual ~InfoManager();
+	int LoadValues();
+	int SaveValues();
+
+	// Core get routines
+	int GetValue(const string varName, string& value);
+	int GetValue(const string varName, int& value);
+	int GetValue(const string varName, float& value);
+	unsigned long long GetValue(const string varName, unsigned long long& value);
+
+	string GetStrValue(const string varName);
+	int GetIntValue(const string varName);
+
+	// Core set routines
+	int SetValue(const string varName, string value);
+	int SetValue(const string varName, int value);
+	int SetValue(const string varName, float value);
+	int SetValue(const string varName, unsigned long long value);
+
+private:
+	string File;
+	map<string, string> mValues;
+
+};
+
+#endif // _DATAMANAGER_HPP_HEADER
+
diff --git a/injecttwrp/Android.mk b/injecttwrp/Android.mk
new file mode 100644
index 0000000..2557523
--- /dev/null
+++ b/injecttwrp/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+	LOCAL_SRC_FILES:= \
+		injecttwrp.c
+	LOCAL_CFLAGS:= -g -c -W
+	LOCAL_MODULE:=injecttwrp
+	LOCAL_MODULE_TAGS:= eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+	include $(BUILD_EXECUTABLE)
+endif
diff --git a/injecttwrp/injecttwrp.c b/injecttwrp/injecttwrp.c
new file mode 100644
index 0000000..2d52ae0
--- /dev/null
+++ b/injecttwrp/injecttwrp.c
@@ -0,0 +1,433 @@
+/*
+ * This binary allows you to back up the second (recovery) ramdisk on
+ * typical Samsung boot images and to inject a new second ramdisk into
+ * an existing boot image.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * The code was written from scratch by Dees_Troy dees_troy at
+ * yahoo
+ *
+ * Copyright (c) 2012
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#define INJECT_USE_TMP 1
+
+int scan_file_for_data(char *filename, unsigned char *data, int data_size, unsigned long start_location, unsigned long *data_address) {
+	FILE *pFile;
+	unsigned long lSize;
+	unsigned char *buffer, *last_needle = NULL;
+	unsigned char needle[data_size];
+	size_t result;
+	int i, return_val = 0;
+
+	pFile = fopen(filename, "rb");
+	if(pFile==NULL){
+		printf("Unabled to open file '%s'.\nFailed\n", filename);
+		return -1;
+	}
+
+	fseek (pFile , 0 , SEEK_END);
+	lSize = ftell(pFile);
+	rewind(pFile);
+
+	buffer = (unsigned char*)malloc(sizeof(unsigned char) * lSize);
+	if(buffer == NULL){
+		printf("Memory allocation error on '%s'!\nFailed\n", filename);
+		return -1;
+	}
+
+	result = fread(buffer, 1, lSize, pFile);
+	if (result != lSize) {
+		printf("Error reading file '%s'\nFailed\n", filename);
+		return -1;
+	}
+
+	for (i=0; i<data_size; i++) {
+		needle[i] = *data;
+		data++;
+	}
+
+	unsigned char *p = memmem(buffer + start_location, lSize - start_location, needle, data_size);
+
+	if (!p) {
+		return_val = -1;
+	} else {
+		*data_address = p - buffer + data_size;
+	}
+
+	fclose(pFile);
+	free(buffer);
+	return return_val;
+}
+
+int write_new_ramdisk(char *bootimage, char *newramdisk, unsigned long start_location, char *outputimage) {
+	FILE *bFile; // bootimage
+	FILE *rFile; // ramdisk
+	FILE *oFile; // output file
+	unsigned long blSize, rlSize, offset_table, ramdisk_len;
+	unsigned char *bbuffer, *rbuffer;
+	size_t result;
+	int return_val;
+
+	// Read the original boot image
+	printf("Reading the original boot image...\n");
+	bFile = fopen(bootimage, "rb");
+	if(bFile==NULL){
+		printf("Unabled to open original boot image '%s'.\nFailed\n", bootimage);
+		exit(0);
+	}
+
+	fseek (bFile , 0 , SEEK_END);
+	blSize = ftell(bFile);
+	rewind(bFile);
+	printf("Size of original boot is %lu\n", blSize);
+
+	bbuffer = (unsigned char*)malloc(sizeof(unsigned char) * blSize);
+	if(bbuffer == NULL){
+		printf("File read error on original boot image '%s'!\nFailed\n", bootimage);
+		exit(0);
+	}
+
+	result = fread(bbuffer, 1, blSize, bFile);
+	if (result != blSize) {
+		printf("Error reading original boot image '%s'\nFailed\n", bootimage);
+		exit(0);
+	}
+
+	// Find the ramdisk offset table
+	unsigned char needle[13] = "recovery_len=";
+	return_val = scan_file_for_data(bootimage, &needle, 13, 0, &offset_table);
+	if (return_val < 0) {
+		fclose(bFile);
+		printf("Ramdisk offset table not found in %s!\nFailed\n", bootimage);
+		exit(0);
+	}
+	printf("Ramdisk offset table found at 0x%08x\n", offset_table);
+
+	// Read the ramdisk to insert into the boot image
+	printf("Reading the ramdisk...\n");
+	rFile = fopen(newramdisk, "rb");
+	if(rFile==NULL){
+		printf("Unabled to open ramdisk image '%s'.\nFailed\n", newramdisk);
+		exit(0);
+	}
+
+	fseek (rFile , 0 , SEEK_END);
+	rlSize = ftell(rFile);
+	rewind(rFile);
+	printf("Size of new ramdisk is %lu\n", rlSize);
+	ramdisk_len = rlSize / 512;
+	if ((rlSize % 512) != 0)
+		ramdisk_len++;
+	printf("Ramdisk length for offset table: %lu\n", ramdisk_len);
+
+	rbuffer = (unsigned char*)malloc(sizeof(unsigned char) * blSize);
+	if(rbuffer == NULL){
+		printf("File read error on ramdisk image '%s'!\nFailed\n", newramdisk);
+		exit(0);
+	}
+
+	result = fread(rbuffer, 1, rlSize, rFile);
+	if (result != rlSize) {
+		printf("Error reading ramdisk image '%s'\nFailed\n", newramdisk);
+		exit(0);
+	}
+
+	// Open the output image for writing
+	printf("Opening the output image for writing...\n");
+	oFile = fopen(outputimage, "wb");
+	if(oFile==NULL){
+		printf("Unabled to open output image '%s'.\nFailed\n", outputimage);
+		exit(0);
+	}
+
+	printf("Writing kernel and first ramdisk...\n");
+	result = fwrite(bbuffer, 1, start_location - 1, oFile);
+	if (result != start_location - 1) {
+		printf("Write count does not match! (1)\n");
+	}
+	fseek(oFile, start_location, SEEK_SET);
+	printf("Writing new second ramdisk...\n");
+	result = fwrite(rbuffer, 1, rlSize, oFile);
+	if (result != rlSize) {
+		printf("Write count does not match! (2)\n");
+	} else {
+		printf("Finished writing new boot image '%s'\n", outputimage);
+	}
+
+	// Write new ramdisk_len to offset table
+	printf("Writing new ramdisk length to offset table...\n");
+	fseek(oFile, offset_table, SEEK_SET);
+	char ramdisk_lens[20];
+	sprintf(ramdisk_lens, "%lu;\n\n", ramdisk_len);
+	fwrite(ramdisk_lens, 1, strlen(ramdisk_lens), oFile);
+
+	fclose(bFile);
+	fclose(rFile);
+	fclose(oFile);
+	free(bbuffer);
+	free(rbuffer);
+
+	printf("All done writing new image: '%s'\n", outputimage);
+	return 1;
+}
+
+int backup_recovery_ramdisk(char *bootimage, unsigned long start_location, char *outputimage) {
+	FILE *bFile; // bootimage
+	FILE *rFile; // ramdisk
+	FILE *oFile; // output file
+	unsigned long blSize, offset_table, ramdisk_len;
+	unsigned char *bbuffer;
+	size_t result;
+	unsigned char ramdisk_lens[4], *p;
+	int return_val, i;
+
+	// Read the original boot image
+	printf("Reading the original boot image...\n");
+	bFile = fopen(bootimage, "rb");
+	if(bFile==NULL){
+		printf("Unabled to open original boot image '%s'.\nFailed\n", bootimage);
+		exit(0);
+	}
+
+	fseek (bFile , 0 , SEEK_END);
+	blSize = ftell(bFile);
+	rewind(bFile);
+	printf("Size of original boot is %lu\n", blSize);
+
+	bbuffer = (unsigned char*)malloc(sizeof(unsigned char) * blSize);
+	if(bbuffer == NULL){
+		printf("File read error on original boot image '%s'!\nFailed\n", bootimage);
+		exit(0);
+	}
+
+	result = fread(bbuffer, 1, blSize, bFile);
+	if (result != blSize) {
+		printf("Error reading original boot image '%s'\nFailed\n", bootimage);
+		exit(0);
+	}
+
+	// Find the ramdisk offset table
+	unsigned char needle[13] = "recovery_len=";
+	return_val = scan_file_for_data(bootimage, &needle, 13, 0, &offset_table);
+	if (return_val < 0) {
+		fclose(bFile);
+		printf("Ramdisk offset table not found in %s!\nFailed\n", bootimage);
+		exit(0);
+	}
+	printf("Ramdisk offset table found at 0x%08x\n", offset_table);
+	for (i=0; i<4; i++) {
+		p = bbuffer + offset_table + i;
+		if (*p == ';') {
+			ramdisk_lens[i] = 0;
+		} else {
+			ramdisk_lens[i] = *p;
+		}
+	}
+	ramdisk_len = atoi(ramdisk_lens);
+	ramdisk_len *= 512;
+	printf("Ramdisk length: %lu\n", ramdisk_len);
+
+	// Open the output image for writing
+	printf("Opening the output image for writing...\n");
+	oFile = fopen(outputimage, "wb");
+	if(oFile==NULL){
+		printf("Unabled to open output image '%s'.\nFailed\n", outputimage);
+		exit(0);
+	}
+
+	printf("Writing backup ramdisk...\n");
+	result = fwrite(bbuffer + start_location, 1, ramdisk_len, oFile);
+	if (result != ramdisk_len) {
+		printf("Write count does not match! (1)\n");
+	} else {
+		printf("Finished backing up ramdisk image '%s'\n", outputimage);
+	}
+
+	fclose(bFile);
+	fclose(oFile);
+	free(bbuffer);
+	return 1;
+}
+
+int find_gzip_recovery_ramdisk(char *boot_image, unsigned long *ramdisk_address) {
+	unsigned char gzip_ramdisk[6] = {0x00, 0x00, 0x00, 0x00, 0x1f, 0x8b};
+	unsigned long address1, address2;
+	int return_val;
+
+	// Find the first ramdisk
+	return_val = scan_file_for_data(boot_image, &gzip_ramdisk, 6, 0, &address1);
+	if (return_val < 0) {
+		printf("No ramdisk found in '%s'\nFailed\n", boot_image);
+		printf("This boot image may not be using gzip compression.\n");
+		return -1;
+	}
+	address1 -= 2;
+	printf("Ramdisk found in '%s' at offset 0x%08x\n", boot_image, address1);
+
+	// Find the second (recovery) ramdisk
+	return_val = scan_file_for_data(boot_image, &gzip_ramdisk, 6, address1 + 50, &address2);
+	if (return_val < 0) {
+		printf("No recovery ramdisk found in '%s'\nFailed\n", boot_image, address2);
+		return -1;
+	}
+	address2 -= 2;
+	printf("Recovery ramdisk found in '%s' at offset 0x%08x\n", boot_image, address2);
+
+	*ramdisk_address = address2;
+	return 0;
+}
+
+int main(int argc, char** argv) {
+	int arg_error = 0, delete_ind = 0, return_val, index, len;
+	unsigned long address2;
+	unsigned char regular_check[8] = "ANDROID!";
+	char boot_image[512], backup_image[512], boot_block_device[512], command[512];
+
+	printf("-- InjectTWRP Recovery Ramdisk Injection Tool for Samsung devices. --\n");
+	printf("--                  by Dees_Troy and Team Win                      --\n");
+	printf("--                       http://teamw.in                           --\n");
+	printf("--                Bringing some win to Samsung!                    --\n");
+	printf("--        This tool comes with no warranties whatsoever!           --\n");
+	printf("--        Use at your own risk and always keep a backup!           --\n\n");
+	printf("Version 0.2 beta\n\n");
+
+	// Parse the arguments
+	if (argc < 2 || argc > 6)
+		arg_error = 1;
+	else {
+		strcpy(boot_block_device, "boot");
+		for (index = 1; index < argc; index++) {
+			len = strlen(argv[index]);
+			if (len > 3 && strncmp(argv[index], "bd=", 3) == 0) {
+				strcpy(boot_block_device, argv[index] + 3);
+				index = argc;
+			}
+		}
+		if ((argc >= 2 && argc <= 4) && (strcmp(argv[1], "-b") == 0 || strcmp(argv[1], "--backup") == 0)) {
+			// Backup existing boot image
+			printf("Dumping boot image...\n");
+#ifdef INJECT_USE_TMP
+			sprintf(command, "dump_image %s /tmp/original_boot.img", boot_block_device);
+			system(command);
+			strcpy(boot_image, "/tmp/original_boot.img");
+
+			if (argc == 2)
+				strcpy(backup_image, "/tmp/recovery_ramdisk.img");
+			else
+				strcpy(backup_image, argv[2]);
+#else
+			system("mount /cache");
+			sprintf(command, "dump_image %s /cache/original_boot.img", boot_block_device);
+			system(command);
+			strcpy(boot_image, "/cache/original_boot.img");
+
+			if (argc == 2)
+				strcpy(backup_image, "/cache/recovery_ramdisk.img");
+			else
+				strcpy(backup_image, argv[2]);
+#endif
+
+			// Check if this is a normal Android image or a Samsung image
+			return_val = scan_file_for_data(boot_image, &regular_check, 8, 0, &address2);
+			if (return_val >= 0) {
+				printf("This is not a properly formatted Samsung boot image!\nFailed\n");
+				return 1;
+			}
+
+			// Find the ramdisk
+			return_val = find_gzip_recovery_ramdisk(boot_image, &address2);
+			if (return_val < 0) {
+				return 1;
+			}
+
+			backup_recovery_ramdisk(boot_image, address2, backup_image);
+			return 0;
+		} else {
+			// Inject new ramdisk
+			if (strcmp(argv[1], "-d") == 0 || strcmp(argv[1], "--dump") == 0) {
+				printf("Dumping boot image...\n");
+#ifdef INJECT_USE_TMP
+				sprintf(command, "dump_image %s /tmp/original_boot.img", boot_block_device);
+				system(command);
+				strcpy(boot_image, "/tmp/original_boot.img");
+#else
+				system("mount /cache");
+				sprintf(command, "dump_image %s /cache/original_boot.img", boot_block_device);
+				system(command);
+				strcpy(boot_image, "/cache/original_boot.img");
+				delete_ind = -1;
+#endif
+			} else
+				strcpy(boot_image, argv[1]);
+
+			// Check if this is a normal Android image or a Samsung image
+			return_val = scan_file_for_data(boot_image, &regular_check, 8, 0, &address2);
+			if (return_val >= 0) {
+				printf("This is not a properly formatted Samsung boot image!\nFailed\n");
+				return 1;
+			}
+
+			// Find the ramdisk
+			return_val = find_gzip_recovery_ramdisk(boot_image, &address2);
+			if (return_val < 0) {
+				return 1;
+			}
+
+			// Write the new image
+			write_new_ramdisk(boot_image, argv[2], address2, argv[3]);
+
+			// Delete --dump image if needed
+			if (delete_ind) {
+				printf("Deleting dumped boot image from /cache\n");
+				system("rm /cache/original_boot.img");
+			}
+
+			if (argc >= 5 && (strcmp(argv[4], "-f") == 0 || strcmp(argv[4], "--flash") == 0)) {
+				printf("Flashing new image...\n");
+				sprintf(command, "erase_image %s", boot_block_device);
+				system(command);
+				sprintf(command, "flash_image %s %s", boot_block_device, argv[3]);
+				system(command);
+				printf("Flash complete.\n");
+			}
+			return 0;
+		}
+	}
+
+	if (arg_error) {
+		printf("Invalid arguments supplied.\n");
+		printf("Usage:\n\n");
+		printf("Backup existing recovery ramdisk (requires dump_image):\n");
+		printf("injecttwrp --backup [optionalbackuplocation.img]\n\n");
+		printf("Inject new recovery ramdisk:\n");
+		printf("injecttwrp originalboot.img ramdisk-recovery.img outputboot.img\n");
+		printf("injecttwrp --dump ramdisk-recovery.img outputboot.img [--flash]\n");
+		printf("--dump will use dump_image to dump your existing boot image\n");
+		printf("--flash will use flash_image to flash the new boot image\n\n");
+		printf("NOTE: dump_image, erase_image, and flash_image must already be installed!\n\n");
+		printf("If needed you can add bd=/dev/block/mmcblk0p5 to indicate the location\n");
+		printf("of the boot partition on emmc devices as the final parameter.\n");
+		return 0;
+	}
+
+	return 0;
+}
diff --git a/install.h b/install.h
index 53c0d31..b5ac410 100644
--- a/install.h
+++ b/install.h
@@ -18,6 +18,7 @@
 #define RECOVERY_INSTALL_H_
 
 #include "common.h"
+#include "mincrypt/rsa.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -29,6 +30,7 @@
 // cache partition.
 int install_package(const char *root_path, int* wipe_cache,
                     const char* install_file, bool needs_mount);
+RSAPublicKey* load_keys(const char* filename, int* numKeys);
 
 #ifdef __cplusplus
 }
diff --git a/legacy_properties.h b/legacy_properties.h
new file mode 100644
index 0000000..44cdd95
--- /dev/null
+++ b/legacy_properties.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ */
+
+#ifndef _INCLUDE_LEGACY_PROPERTIES_H
+#define _INCLUDE_LEGACY_PROPERTIES_H
+
+#include <sys/system_properties.h>
+
+typedef struct prop_area prop_area;
+typedef struct prop_msg prop_msg;
+
+#define PROP_AREA_MAGIC   0x504f5250
+#define PROP_AREA_VERSION 0x45434f76
+
+#define PROP_SERVICE_NAME "property_service"
+
+/* #define PROP_MAX_ENTRIES 247 */
+/* 247 -> 32620 bytes (<32768) */
+
+#define TOC_NAME_LEN(toc)       ((toc) >> 24)
+#define TOC_TO_INFO(area, toc)  ((prop_info*) (((char*) area) + ((toc) & 0xFFFFFF)))
+
+struct prop_area {
+    unsigned volatile count;
+    unsigned volatile serial;
+    unsigned magic;
+    unsigned version;
+    unsigned reserved[4];
+    unsigned toc[1];
+};
+
+#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
+#define SERIAL_DIRTY(serial) ((serial) & 1)
+
+struct prop_info {
+    char name[PROP_NAME_MAX];
+    unsigned volatile serial;
+    char value[PROP_VALUE_MAX];
+};
+
+struct prop_msg
+{
+    unsigned cmd;
+    char name[PROP_NAME_MAX];
+    char value[PROP_VALUE_MAX];
+};
+
+#define PROP_MSG_SETPROP 1
+
+/*
+** Rules:
+**
+** - there is only one writer, but many readers
+** - prop_area.count will never decrease in value
+** - once allocated, a prop_info's name will not change
+** - once allocated, a prop_info's offset will not change
+** - reading a value requires the following steps
+**   1. serial = pi->serial
+**   2. if SERIAL_DIRTY(serial), wait*, then goto 1
+**   3. memcpy(local, pi->value, SERIAL_VALUE_LEN(serial) + 1)
+**   4. if pi->serial != serial, goto 2
+**
+** - writing a value requires the following steps
+**   1. pi->serial = pi->serial | 1
+**   2. memcpy(pi->value, local_value, value_len)
+**   3. pi->serial = (value_len << 24) | ((pi->serial + 1) & 0xffffff)
+**
+** Improvements:
+** - maintain the toc sorted by pi->name to allow lookup
+**   by binary search
+**
+*/
+
+#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
+#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
+#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
+#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"
+
+#endif
diff --git a/legacy_property_service.c b/legacy_property_service.c
new file mode 100644
index 0000000..12865c4
--- /dev/null
+++ b/legacy_property_service.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <cutils/properties.h>
+
+#include "legacy_properties.h"
+
+#include <sys/mman.h>
+#include <sys/atomics.h>
+#include "legacy_property_service.h"
+
+static int persistent_properties_loaded = 0;
+static int property_area_inited = 0;
+
+static int property_set_fd = -1;
+
+typedef struct {
+    void *data;
+    size_t size;
+    int fd;
+} workspace;
+
+static int init_workspace(workspace *w, size_t size)
+{
+    void *data;
+    int fd;
+
+        /* dev is a tmpfs that we can use to carve a shared workspace
+         * out of, so let's do that...
+         */
+    fd = open("/dev/__legacy_properties__", O_RDWR | O_CREAT, 0600);
+    if (fd < 0)
+        return -1;
+
+    if (ftruncate(fd, size) < 0)
+        goto out;
+
+    data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if(data == MAP_FAILED)
+        goto out;
+
+    close(fd);
+
+    fd = open("/dev/__legacy_properties__", O_RDONLY);
+    if (fd < 0)
+        return -1;
+
+    unlink("/dev/__legacy_properties__");
+
+    w->data = data;
+    w->size = size;
+    w->fd = fd;
+    return 0;
+
+out:
+    close(fd);
+    return -1;
+}
+
+/* (8 header words + 247 toc words) = 1020 bytes */
+/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */
+
+#define PA_COUNT_MAX  247
+#define PA_INFO_START 1024
+#define PA_SIZE       32768
+
+static workspace pa_workspace;
+static prop_info *pa_info_array;
+
+prop_area *__legacy_property_area__;
+
+static int init_property_area(void)
+{
+    prop_area *pa;
+
+    if(pa_info_array)
+        return -1;
+
+    if(init_workspace(&pa_workspace, PA_SIZE))
+        return -1;
+
+    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
+
+    pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);
+
+    pa = pa_workspace.data;
+    memset(pa, 0, PA_SIZE);
+    pa->magic = PROP_AREA_MAGIC;
+    pa->version = PROP_AREA_VERSION;
+
+    /* plug into the lib property services */
+    __legacy_property_area__ = pa;
+    property_area_inited = 1;
+    return 0;
+}
+
+static void update_prop_info(prop_info *pi, const char *value, unsigned len)
+{
+    pi->serial = pi->serial | 1;
+    memcpy(pi->value, value, len + 1);
+    pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);
+    __futex_wake(&pi->serial, INT32_MAX);
+}
+
+static const prop_info *__legacy_property_find(const char *name)
+{
+    prop_area *pa = __legacy_property_area__;
+    unsigned count = pa->count;
+    unsigned *toc = pa->toc;
+    unsigned len = strlen(name);
+    prop_info *pi;
+
+    while(count--) {
+        unsigned entry = *toc++;
+        if(TOC_NAME_LEN(entry) != len) continue;
+
+        pi = TOC_TO_INFO(pa, entry);
+        if(memcmp(name, pi->name, len)) continue;
+
+        return pi;
+    }
+
+    return 0;
+}
+
+static int legacy_property_set(const char *name, const char *value)
+{
+    prop_area *pa;
+    prop_info *pi;
+
+    int namelen = strlen(name);
+    int valuelen = strlen(value);
+
+    if(namelen >= PROP_NAME_MAX) return -1;
+    if(valuelen >= PROP_VALUE_MAX) return -1;
+    if(namelen < 1) return -1;
+
+    pi = (prop_info*) __legacy_property_find(name);
+
+
+    if(pi != 0) {
+        /* ro.* properties may NEVER be modified once set */
+        if(!strncmp(name, "ro.", 3)) return -1;
+
+        pa = __legacy_property_area__;
+        update_prop_info(pi, value, valuelen);
+        pa->serial++;
+        __futex_wake(&pa->serial, INT32_MAX);
+    } else {
+        pa = __legacy_property_area__;
+        if(pa->count == PA_COUNT_MAX) return -1;
+
+        pi = pa_info_array + pa->count;
+        pi->serial = (valuelen << 24);
+        memcpy(pi->name, name, namelen + 1);
+        memcpy(pi->value, value, valuelen + 1);
+
+        pa->toc[pa->count] =
+            (namelen << 24) | (((unsigned) pi) - ((unsigned) pa));
+
+        pa->count++;
+        pa->serial++;
+        __futex_wake(&pa->serial, INT32_MAX);
+    }
+
+    return 0;
+}
+
+void legacy_get_property_workspace(int *fd, int *sz)
+{
+    *fd = pa_workspace.fd;
+    *sz = pa_workspace.size;
+}
+
+static void copy_property_to_legacy(const char *key, const char *value, void *cookie)
+{
+    legacy_property_set(key, value);
+}
+
+int legacy_properties_init()
+{
+    if(init_property_area() != 0)
+        return -1;
+
+    if(property_list(copy_property_to_legacy, 0) != 0)
+        return -1;
+
+    return 0;
+}
diff --git a/legacy_property_service.h b/legacy_property_service.h
new file mode 100644
index 0000000..d20bdef
--- /dev/null
+++ b/legacy_property_service.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LEGACY_PROPERTY_H
+#define _LEGACY_PROPERTY_H
+
+#include <stdbool.h>
+
+void legacy_get_property_workspace(int *fd, int *sz);
+int legacy_properties_init();
+
+#endif	/* _LEGACY_PROPERTY_H */
diff --git a/libblkid/Android.mk b/libblkid/Android.mk
new file mode 100644
index 0000000..d6c203f
--- /dev/null
+++ b/libblkid/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libblkid
+LOCAL_MODULE_TAGS := optional
+#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DHAVE_LOFF_T
+LOCAL_SRC_FILES = aix.c at.c befs.c bfs.c blkdev.c bsd.c btrfs.c cache.c canonicalize.c colors.c config.c cramfs.c crc32.c ddf_raid.c dev.c devname.c devno.c dm.c dos.c drbd.c drbdproxy_datalog.c encode.c env.c evaluate.c evms.c exec_shell.c exfat.c ext.c f2fs.c fileutils.c getsize.c gfs.c gpt.c hfs.c highpoint_raid.c hpfs.c ioctl.c ismounted.c iso9660.c isw_raid.c jfs.c jmicron_raid.c langinfo.c linux_raid.c linux_version.c llseek.c loopdev.c lsi_raid.c luks.c lvm1.c lvm2.c mac.c mangle.c match.c mbsalign.c md5.c md.c minix1.c minix2.c netware.c nilfs.c ntfs.c nvidia_raid.c ocfs.c pager.c partitions.c path.c probe.c procutils.c promise_raid.c randutils.c read.c reiserfs.c resolve.c romfs.c save.c setproctitle.c sgi.c silicon_raid.c solaris_x86.c squashfs.c sun.c superblocks.c swap.c sysfs1.c sysfs2.c sysv.c tag.c topology.c  ubifs.c udf.c ufs.c ultrix.c unixware.c verify.c version.c vfat.c via_raid.c vmfs.c vxfs.c wholedisk.c xfs.c zfs.c adaptec_raid.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+LOCAL_SHARED_LIBRARIES += libc
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libblkid/adaptec_raid.c b/libblkid/adaptec_raid.c
new file mode 100644
index 0000000..02e900d
--- /dev/null
+++ b/libblkid/adaptec_raid.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct adaptec_metadata {
+
+	uint32_t	b0idcode;
+	uint8_t		lunsave[8];
+	uint16_t	sdtype;
+	uint16_t	ssavecyl;
+	uint8_t		ssavehed;
+	uint8_t		ssavesec;
+	uint8_t		sb0flags;
+	uint8_t		jbodEnable;
+	uint8_t		lundsave;
+	uint8_t		svpdirty;
+	uint16_t	biosInfo;
+	uint16_t	svwbskip;
+	uint16_t	svwbcln;
+	uint16_t	svwbmax;
+	uint16_t	res3;
+	uint16_t	svwbmin;
+	uint16_t	res4;
+	uint16_t	svrcacth;
+	uint16_t	svwcacth;
+	uint16_t	svwbdly;
+	uint8_t		svsdtime;
+	uint8_t		res5;
+	uint16_t	firmval;
+	uint16_t	firmbln;
+	uint32_t	firmblk;
+	uint32_t	fstrsvrb;
+	uint16_t	svBlockStorageTid;
+	uint16_t	svtid;
+	uint8_t		svseccfl;
+	uint8_t		res6;
+	uint8_t		svhbanum;
+	uint8_t		resver;
+	uint32_t	drivemagic;
+	uint8_t		reserved[20];
+	uint8_t		testnum;
+	uint8_t		testflags;
+	uint16_t	maxErrorCount;
+	uint32_t	count;
+	uint32_t	startTime;
+	uint32_t	interval;
+	uint8_t		tstxt0;
+	uint8_t		tstxt1;
+	uint8_t		serNum[32];
+	uint8_t		res8[102];
+	uint32_t	fwTestMagic;
+	uint32_t	fwTestSeqNum;
+	uint8_t		fwTestRes[8];
+	uint32_t	smagic;
+	uint32_t	raidtbl;
+	uint16_t	raidline;
+	uint8_t		res9[0xF6];
+} __attribute__((packed));
+
+#define AD_SIGNATURE	0x4450544D	/* "DPTM" */
+#define AD_MAGIC	0x37FC4D1E
+
+static int probe_adraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct adaptec_metadata *ad;
+
+	if (pr->size < 0x10000)
+		return -1;
+
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200)-1) * 0x200;
+	ad = (struct adaptec_metadata *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct adaptec_metadata));
+	if (!ad)
+		return -1;
+	if (ad->smagic != be32_to_cpu(AD_SIGNATURE))
+		return -1;
+	if (ad->b0idcode != be32_to_cpu(AD_MAGIC))
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%u", ad->resver) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(ad->b0idcode),
+				(unsigned char *) &ad->b0idcode))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo adraid_idinfo = {
+	.name		= "adaptec_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_adraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/aix.c b/libblkid/aix.c
new file mode 100644
index 0000000..de397bf
--- /dev/null
+++ b/libblkid/aix.c
@@ -0,0 +1,59 @@
+/*
+ * aix partitions
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+#include "aix.h"
+
+static int probe_aix_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	blkid_partlist ls;
+	blkid_parttable tab;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "aix", 0);
+	if (!tab)
+		goto err;
+
+	return 0;
+err:
+	return -1;
+}
+
+/*
+ * We know nothing about AIX on-disk structures. Everything what we know is the
+ * magic number at begin of the disk.
+ *
+ * Note, Linux kernel is tring to be smart and AIX signature is ignored when
+ * there is a valid DOS partitions table. We don't support such behaviour. All
+ * fdisk-like programs has to properly wipe the fist sector. Everything other
+ * is a bug.
+ */
+const struct blkid_idinfo aix_pt_idinfo =
+{
+	.name		= "aix",
+	.probefunc	= probe_aix_pt,
+	.magics		=
+	{
+		{ .magic = BLKID_AIX_MAGIC_STRING, .len = BLKID_AIX_MAGIC_STRLEN },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/aix.h b/libblkid/aix.h
new file mode 100644
index 0000000..f767c5a
--- /dev/null
+++ b/libblkid/aix.h
@@ -0,0 +1,7 @@
+#ifndef BLKID_PARTITIONS_AIX_H
+#define BLKID_PARTITIONS_AIX_H
+
+#define BLKID_AIX_MAGIC_STRING	"\xC9\xC2\xD4\xC1"
+#define BLKID_AIX_MAGIC_STRLEN	(sizeof(BLKID_AIX_MAGIC_STRING) - 1)
+
+#endif
diff --git a/libblkid/all-io.h b/libblkid/all-io.h
new file mode 100644
index 0000000..424ab7d
--- /dev/null
+++ b/libblkid/all-io.h
@@ -0,0 +1,82 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ *            Petr Uzel <petr.uzel@suse.cz>
+ */
+
+#ifndef UTIL_LINUX_ALL_IO_H
+#define UTIL_LINUX_ALL_IO_H
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "c.h"
+
+static inline int write_all(int fd, const void *buf, size_t count)
+{
+	while (count) {
+		ssize_t tmp;
+
+		errno = 0;
+		tmp = write(fd, buf, count);
+		if (tmp > 0) {
+			count -= tmp;
+			if (count)
+				buf = (void *) ((char *) buf + tmp);
+		} else if (errno != EINTR && errno != EAGAIN)
+			return -1;
+		if (errno == EAGAIN)	/* Try later, *sigh* */
+			usleep(10000);
+	}
+	return 0;
+}
+
+static inline int fwrite_all(const void *ptr, size_t size,
+			     size_t nmemb, FILE *stream)
+{
+	while (nmemb) {
+		size_t tmp;
+
+		errno = 0;
+		tmp = fwrite(ptr, size, nmemb, stream);
+		if (tmp > 0) {
+			nmemb -= tmp;
+			if (nmemb)
+				ptr = (void *) ((char *) ptr + (tmp * size));
+		} else if (errno != EINTR && errno != EAGAIN)
+			return -1;
+		if (errno == EAGAIN)	/* Try later, *sigh* */
+			usleep(10000);
+	}
+	return 0;
+}
+
+static inline ssize_t read_all(int fd, char *buf, size_t count)
+{
+	ssize_t ret;
+	ssize_t c = 0;
+	int tries = 0;
+
+	memset(buf, 0, count);
+	while (count > 0) {
+		ret = read(fd, buf, count);
+		if (ret <= 0) {
+			if ((errno == EAGAIN || errno == EINTR || ret == 0) &&
+			    (tries++ < 5))
+				continue;
+			return c ? c : -1;
+		}
+		if (ret > 0)
+			tries = 0;
+		count -= ret;
+		buf += ret;
+		c += ret;
+	}
+	return c;
+}
+
+
+#endif /* UTIL_LINUX_ALL_IO_H */
diff --git a/libblkid/at.c b/libblkid/at.c
new file mode 100644
index 0000000..f8bfe13
--- /dev/null
+++ b/libblkid/at.c
@@ -0,0 +1,143 @@
+/*
+ * Portable xxxat() functions.
+ *
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "at.h"
+#include "c.h"
+
+#ifdef HAVE_FSTATAT
+int fstat_at(int dir, const char *dirname __attribute__ ((__unused__)),
+	     const char *filename, struct stat *st, int nofollow)
+{
+	return fstatat(dir, filename, st,
+			nofollow ? AT_SYMLINK_NOFOLLOW : 0);
+}
+#else
+int fstat_at(int dir, const char *dirname, const char *filename,
+				struct stat *st, int nofollow)
+{
+
+	if (*filename != '/') {
+		char path[PATH_MAX];
+		int len;
+
+		len = snprintf(path, sizeof(path), "%s/%s", dirname, filename);
+		if (len < 0 || len + 1 > sizeof(path))
+			return -1;
+
+		return nofollow ? lstat(path, st) : stat(path, st);
+	}
+
+	return nofollow ? lstat(filename, st) : stat(filename, st);
+}
+#endif
+
+#ifdef HAVE_FSTATAT
+int open_at(int dir, const char *dirname __attribute__ ((__unused__)),
+	    const char *filename, int flags)
+{
+	return openat(dir, filename, flags);
+}
+#else
+int open_at(int dir, const char *dirname, const char *filename, int flags)
+{
+	if (*filename != '/') {
+		char path[PATH_MAX];
+		int len;
+
+		len = snprintf(path, sizeof(path), "%s/%s", dirname, filename);
+		if (len < 0 || len + 1 > sizeof(path))
+			return -1;
+
+		return open(path, flags);
+	}
+	return open(filename, flags);
+}
+#endif
+
+FILE *fopen_at(int dir, const char *dirname, const char *filename, int flags,
+			const char *mode)
+{
+	int fd = open_at(dir, dirname, filename, flags);
+
+	if (fd < 0)
+		return NULL;
+
+	return fdopen(fd, mode);
+}
+
+#ifdef HAVE_FSTATAT
+ssize_t readlink_at(int dir, const char *dirname __attribute__ ((__unused__)),
+		    const char *pathname, char *buf, size_t bufsiz)
+{
+	return readlinkat(dir, pathname, buf, bufsiz);
+}
+#else
+ssize_t readlink_at(int dir, const char *dirname, const char *pathname,
+		    char *buf, size_t bufsiz)
+{
+	if (*pathname != '/') {
+		char path[PATH_MAX];
+		int len;
+
+		len = snprintf(path, sizeof(path), "%s/%s", dirname, pathname);
+		if (len < 0 || len + 1 > sizeof(path))
+			return -1;
+
+		return readlink(path, buf, bufsiz);
+	}
+	return readlink(pathname, buf, bufsiz);
+}
+#endif
+
+#ifdef TEST_PROGRAM_AT
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+	DIR *dir;
+	struct dirent *d;
+	char *dirname;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s <directory>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+	dirname = argv[1];
+
+	dir = opendir(dirname);
+	if (!dir)
+		err(EXIT_FAILURE, "cannot open %s", dirname);
+
+	while ((d = readdir(dir))) {
+		struct stat st;
+		FILE *f;
+
+		printf("%32s ", d->d_name);
+
+		if (fstat_at(dirfd(dir), dirname, d->d_name, &st, 0) == 0)
+			printf("%16jd bytes ", st.st_size);
+		else
+			printf("%16s bytes ", "???");
+
+		f = fopen_at(dirfd(dir), dirname, d->d_name, O_RDONLY, "r");
+		printf("   %s\n", f ? "OK" : strerror(errno));
+		if (f)
+			fclose(f);
+	}
+	closedir(dir);
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/at.h b/libblkid/at.h
new file mode 100644
index 0000000..63a80f0
--- /dev/null
+++ b/libblkid/at.h
@@ -0,0 +1,32 @@
+/*
+ * wrappers for "at" functions.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_AT_H
+#define UTIL_LINUX_AT_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "c.h"
+
+extern int fstat_at(int dir, const char *dirname,
+			const char *filename, struct stat *st, int nofollow);
+
+extern int open_at(int dir, const char *dirname,
+			const char *filename, int flags);
+
+extern FILE *fopen_at(int dir, const char *dirname, const char *filename,
+			int flags, const char *mode);
+
+extern ssize_t readlink_at(int dir, const char *dirname, const char *pathname,
+                    char *buf, size_t bufsiz);
+
+
+#endif /* UTIL_LINUX_AT_H */
diff --git a/libblkid/befs.c b/libblkid/befs.c
new file mode 100644
index 0000000..847860a
--- /dev/null
+++ b/libblkid/befs.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2010 Jeroen Oortwijn <oortwijn@gmail.com>
+ *
+ * Partly based on the Haiku BFS driver by
+ *     Axel Dörfler <axeld@pinc-software.de>
+ *
+ * Also inspired by the Linux BeFS driver by
+ *     Will Dyson <will_dyson@pobox.com>, et al.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+#include <byteswap.h>
+#include "bitops.h"
+#include "superblocks.h"
+
+#define B_OS_NAME_LENGTH	0x20
+#define SUPER_BLOCK_MAGIC1	0x42465331	/* BFS1 */
+#define SUPER_BLOCK_MAGIC2	0xdd121031
+#define SUPER_BLOCK_MAGIC3	0x15b6830e
+#define SUPER_BLOCK_FS_ENDIAN	0x42494745	/* BIGE */
+#define INODE_MAGIC1		0x3bbe0ad9
+#define BPLUSTREE_MAGIC		0x69f6c2e8
+#define BPLUSTREE_NULL		-1LL
+#define NUM_DIRECT_BLOCKS	12
+#define B_UINT64_TYPE		0x554c4c47	/* ULLG */
+#define KEY_NAME		"be:volume_id"
+#define KEY_SIZE		8
+#define FS16_TO_CPU(value, fs_is_le) (fs_is_le ? le16_to_cpu(value) \
+							: be16_to_cpu(value))
+#define FS32_TO_CPU(value, fs_is_le) (fs_is_le ? le32_to_cpu(value) \
+							: be32_to_cpu(value))
+#define FS64_TO_CPU(value, fs_is_le) (fs_is_le ? le64_to_cpu(value) \
+							: be64_to_cpu(value))
+
+typedef struct block_run {
+	int32_t		allocation_group;
+	uint16_t	start;
+	uint16_t	len;
+} __attribute__((packed)) block_run, inode_addr;
+
+struct befs_super_block {
+	char		name[B_OS_NAME_LENGTH];
+	int32_t		magic1;
+	int32_t		fs_byte_order;
+	uint32_t	block_size;
+	uint32_t	block_shift;
+	int64_t		num_blocks;
+	int64_t		used_blocks;
+	int32_t		inode_size;
+	int32_t		magic2;
+	int32_t		blocks_per_ag;
+	int32_t		ag_shift;
+	int32_t		num_ags;
+	int32_t		flags;
+	block_run	log_blocks;
+	int64_t		log_start;
+	int64_t		log_end;
+	int32_t		magic3;
+	inode_addr	root_dir;
+	inode_addr	indices;
+	int32_t		pad[8];
+} __attribute__((packed));
+
+typedef struct data_stream {
+	block_run	direct[NUM_DIRECT_BLOCKS];
+	int64_t		max_direct_range;
+	block_run	indirect;
+	int64_t		max_indirect_range;
+	block_run	double_indirect;
+	int64_t		max_double_indirect_range;
+	int64_t		size;
+} __attribute__((packed)) data_stream;
+
+struct befs_inode {
+	int32_t		magic1;
+	inode_addr	inode_num;
+	int32_t		uid;
+	int32_t		gid;
+	int32_t		mode;
+	int32_t		flags;
+	int64_t		create_time;
+	int64_t		last_modified_time;
+	inode_addr	parent;
+	inode_addr	attributes;
+	uint32_t	type;
+	int32_t		inode_size;
+	uint32_t	etc;
+	data_stream	data;
+	int32_t		pad[4];
+	int32_t		small_data[0];
+} __attribute__((packed));
+
+struct small_data {
+	uint32_t	type;
+	uint16_t	name_size;
+	uint16_t	data_size;
+	char		name[0];
+} __attribute__((packed));
+
+struct bplustree_header {
+	uint32_t	magic;
+	uint32_t	node_size;
+	uint32_t	max_number_of_levels;
+	uint32_t	data_type;
+	int64_t		root_node_pointer;
+	int64_t		free_node_pointer;
+	int64_t		maximum_size;
+} __attribute__((packed));
+
+struct bplustree_node {
+	int64_t		left_link;
+	int64_t		right_link;
+	int64_t		overflow_link;
+	uint16_t	all_key_count;
+	uint16_t	all_key_length;
+	char		name[0];
+} __attribute__((packed));
+
+static unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs,
+					const struct block_run *br, int fs_le)
+{
+	return blkid_probe_get_buffer(pr,
+			((blkid_loff_t) FS32_TO_CPU(br->allocation_group, fs_le)
+					<< FS32_TO_CPU(bs->ag_shift, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+				+ ((blkid_loff_t) FS16_TO_CPU(br->start, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le)),
+			(blkid_loff_t) FS16_TO_CPU(br->len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le));
+}
+
+static unsigned char *get_custom_block_run(blkid_probe pr,
+				const struct befs_super_block *bs,
+				const struct block_run *br,
+				int64_t offset, uint32_t length, int fs_le)
+{
+	if (offset + length > (int64_t) FS16_TO_CPU(br->len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+		return NULL;
+
+	return blkid_probe_get_buffer(pr,
+			((blkid_loff_t) FS32_TO_CPU(br->allocation_group, fs_le)
+					<< FS32_TO_CPU(bs->ag_shift, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+				+ ((blkid_loff_t) FS16_TO_CPU(br->start, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+				+ offset,
+			length);
+}
+
+static unsigned char *get_tree_node(blkid_probe pr, const struct befs_super_block *bs,
+				const struct data_stream *ds,
+				int64_t start, uint32_t length, int fs_le)
+{
+	if (start < (int64_t) FS64_TO_CPU(ds->max_direct_range, fs_le)) {
+		int64_t br_len;
+		size_t i;
+
+		for (i = 0; i < NUM_DIRECT_BLOCKS; i++) {
+			br_len = (int64_t) FS16_TO_CPU(ds->direct[i].len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le);
+			if (start < br_len)
+				return get_custom_block_run(pr, bs,
+							&ds->direct[i],
+							start, length, fs_le);
+			else
+				start -= br_len;
+		}
+	} else if (start < (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le)) {
+		struct block_run *br;
+		int64_t max_br, br_len, i;
+
+		start -= FS64_TO_CPU(ds->max_direct_range, fs_le);
+		max_br = ((int64_t) FS16_TO_CPU(ds->indirect.len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+				/ sizeof(struct block_run);
+
+		br = (struct block_run *) get_block_run(pr, bs, &ds->indirect,
+									fs_le);
+		if (!br)
+			return NULL;
+
+		for (i = 0; i < max_br; i++) {
+			br_len = (int64_t) FS16_TO_CPU(br[i].len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le);
+			if (start < br_len)
+				return get_custom_block_run(pr, bs, &br[i],
+							start, length, fs_le);
+			else
+				start -= br_len;
+		}
+	} else if (start < (int64_t) FS64_TO_CPU(ds->max_double_indirect_range, fs_le)) {
+		struct block_run *br;
+		int64_t di_br_size, br_per_di_br, di_index, i_index;
+
+		start -= (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le);
+
+		di_br_size = (int64_t) FS16_TO_CPU(ds->double_indirect.len,
+				fs_le) << FS32_TO_CPU(bs->block_shift, fs_le);
+		if (di_br_size == 0)
+			return NULL;
+
+		br_per_di_br = di_br_size / sizeof(struct block_run);
+		if (br_per_di_br == 0)
+			return NULL;
+
+		di_index = start / (br_per_di_br * di_br_size);
+		i_index = (start % (br_per_di_br * di_br_size)) / di_br_size;
+		start = (start % (br_per_di_br * di_br_size)) % di_br_size;
+
+		br = (struct block_run *) get_block_run(pr, bs,
+						&ds->double_indirect, fs_le);
+		if (!br)
+			return NULL;
+
+		br = (struct block_run *) get_block_run(pr, bs, &br[di_index],
+									fs_le);
+		if (!br)
+			return NULL;
+
+		return get_custom_block_run(pr, bs, &br[i_index], start, length,
+									fs_le);
+	}
+	return NULL;
+}
+
+static int32_t compare_keys(const char keys1[], uint16_t keylengths1[], int32_t index,
+			const char *key2, uint16_t keylength2, int fs_le)
+{
+	const char *key1;
+	uint16_t keylength1;
+	int32_t result;
+
+	key1 = &keys1[index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
+									fs_le)];
+	keylength1 = FS16_TO_CPU(keylengths1[index], fs_le)
+			- (index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
+									fs_le));
+
+	result = strncmp(key1, key2, min(keylength1, keylength2));
+
+	if (result == 0)
+		return keylength1 - keylength2;
+
+	return result;
+}
+
+static int64_t get_key_value(blkid_probe pr, const struct befs_super_block *bs,
+			const struct befs_inode *bi, const char *key, int fs_le)
+{
+	struct bplustree_header *bh;
+	struct bplustree_node *bn;
+	uint16_t *keylengths;
+	int64_t *values;
+	int64_t node_pointer;
+	int32_t first, last, mid, cmp;
+
+	bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0,
+					sizeof(struct bplustree_header), fs_le);
+	if (!bh)
+		return -1;
+
+	if ((int32_t) FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC)
+		return -1;
+
+	node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le);
+
+	do {
+		bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data,
+			node_pointer, FS32_TO_CPU(bh->node_size, fs_le), fs_le);
+		if (!bn)
+			return -1;
+
+		keylengths = (uint16_t *) ((uint8_t *) bn
+				+ ((sizeof(struct bplustree_node)
+					+ FS16_TO_CPU(bn->all_key_length, fs_le)
+					+ sizeof(int64_t) - 1)
+						& ~(sizeof(int64_t) - 1)));
+		values = (int64_t *) ((uint8_t *) keylengths
+					+ FS16_TO_CPU(bn->all_key_count, fs_le)
+						* sizeof(uint16_t));
+		first = 0;
+		mid = 0;
+		last = FS16_TO_CPU(bn->all_key_count, fs_le) - 1;
+
+		cmp = compare_keys(bn->name, keylengths, last, key, strlen(key),
+									fs_le);
+		if (cmp == 0) {
+			if ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
+							== BPLUSTREE_NULL)
+				return FS64_TO_CPU(values[last], fs_le);
+			else
+				node_pointer = FS64_TO_CPU(values[last], fs_le);
+		} else if (cmp < 0)
+			node_pointer = FS64_TO_CPU(bn->overflow_link, fs_le);
+		else {
+			while (first <= last) {
+				mid = (first + last) / 2;
+
+				cmp = compare_keys(bn->name, keylengths, mid,
+						key, strlen(key), fs_le);
+				if (cmp == 0) {
+					if ((int64_t) FS64_TO_CPU(bn->overflow_link,
+						fs_le) == BPLUSTREE_NULL)
+						return FS64_TO_CPU(values[mid],
+									fs_le);
+					else
+						break;
+				} else if (cmp < 0)
+					first = mid + 1;
+				else
+					last = mid - 1;
+			}
+			if (cmp < 0)
+				node_pointer = FS64_TO_CPU(values[mid + 1],
+									fs_le);
+			else
+				node_pointer = FS64_TO_CPU(values[mid], fs_le);
+		}
+	} while ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
+						!= BPLUSTREE_NULL);
+	return 0;
+}
+
+static int get_uuid(blkid_probe pr, const struct befs_super_block *bs,
+					uint64_t * const uuid, int fs_le)
+{
+	struct befs_inode *bi;
+	struct small_data *sd;
+
+	bi = (struct befs_inode *) get_block_run(pr, bs, &bs->root_dir, fs_le);
+	if (!bi)
+		return -1;
+
+	if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+		return -1;
+
+	sd = (struct small_data *) bi->small_data;
+
+	do {
+		if (FS32_TO_CPU(sd->type, fs_le) == B_UINT64_TYPE
+			&& FS16_TO_CPU(sd->name_size, fs_le) == strlen(KEY_NAME)
+			&& FS16_TO_CPU(sd->data_size, fs_le) == KEY_SIZE
+			&& strcmp(sd->name, KEY_NAME) == 0) {
+
+			memcpy(uuid,
+			       sd->name + FS16_TO_CPU(sd->name_size, fs_le) + 3,
+			       sizeof(uint64_t));
+
+			break;
+		} else if (FS32_TO_CPU(sd->type, fs_le) == 0
+				&& FS16_TO_CPU(sd->name_size, fs_le) == 0
+				&& FS16_TO_CPU(sd->data_size, fs_le) == 0)
+			break;
+
+		sd = (struct small_data *) ((uint8_t *) sd
+				+ sizeof(struct small_data)
+				+ FS16_TO_CPU(sd->name_size, fs_le) + 3
+				+ FS16_TO_CPU(sd->data_size, fs_le) + 1);
+
+	} while ((intptr_t) sd < (intptr_t) bi
+				+ (int32_t) FS32_TO_CPU(bi->inode_size, fs_le)
+				- (int32_t) sizeof(struct small_data));
+	if (*uuid == 0
+		&& (FS32_TO_CPU(bi->attributes.allocation_group, fs_le) != 0
+			|| FS16_TO_CPU(bi->attributes.start, fs_le) != 0
+			|| FS16_TO_CPU(bi->attributes.len, fs_le) != 0)) {
+		int64_t value;
+
+		bi = (struct befs_inode *) get_block_run(pr, bs,
+							&bi->attributes, fs_le);
+		if (!bi)
+			return -1;
+
+		if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+			return -1;
+
+		value = get_key_value(pr, bs, bi, KEY_NAME, fs_le);
+
+		if (value < 0)
+			return value;
+		else if (value > 0) {
+			bi = (struct befs_inode *) blkid_probe_get_buffer(pr,
+				value << FS32_TO_CPU(bs->block_shift, fs_le),
+				FS32_TO_CPU(bs->block_size, fs_le));
+			if (!bi)
+				return -1;
+
+			if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+				return -1;
+
+			if (FS32_TO_CPU(bi->type, fs_le) == B_UINT64_TYPE
+				&& FS64_TO_CPU(bi->data.size, fs_le) == KEY_SIZE
+				&& FS16_TO_CPU(bi->data.direct[0].len, fs_le)
+									== 1) {
+				uint64_t *attr_data;
+
+				attr_data = (uint64_t *) get_block_run(pr, bs,
+						&bi->data.direct[0], fs_le);
+				if (!attr_data)
+					return -1;
+
+				*uuid = *attr_data;
+			}
+		}
+	}
+	return 0;
+}
+
+static int probe_befs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct befs_super_block *bs;
+	const char *version = NULL;
+	uint64_t volume_id = 0;
+	int fs_le, ret;
+
+	bs = (struct befs_super_block *) blkid_probe_get_buffer(pr,
+					mag->sboff - B_OS_NAME_LENGTH,
+					sizeof(struct befs_super_block));
+	if (!bs)
+		return -1;
+
+	if (le32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
+		&& le32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
+		&& le32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
+		&& le32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
+		fs_le = 1;
+		version = "little-endian";
+	} else if (be32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
+		&& be32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
+		&& be32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
+		&& be32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
+		fs_le = 0;
+		version = "big-endian";
+	} else
+		return -1;
+
+	ret = get_uuid(pr, bs, &volume_id, fs_le);
+
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * all checks pass, set LABEL, VERSION and UUID
+	 */
+	if (strlen(bs->name))
+		blkid_probe_set_label(pr, (unsigned char *) bs->name,
+							sizeof(bs->name));
+	if (version)
+		blkid_probe_set_version(pr, version);
+
+	if (volume_id)
+		blkid_probe_sprintf_uuid(pr, (unsigned char *) &volume_id,
+					sizeof(volume_id), "%016" PRIx64,
+					FS64_TO_CPU(volume_id, fs_le));
+	return 0;
+}
+
+const struct blkid_idinfo befs_idinfo =
+{
+	.name		= "befs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_befs,
+	.minsz		= 1024 * 1440,
+	.magics		= {
+		{ .magic = "BFS1", .len = 4, .sboff = B_OS_NAME_LENGTH },
+		{ .magic = "1SFB", .len = 4, .sboff = B_OS_NAME_LENGTH },
+		{ .magic = "BFS1", .len = 4, .sboff = 0x200 +
+							B_OS_NAME_LENGTH },
+		{ .magic = "1SFB", .len = 4, .sboff = 0x200 +
+							B_OS_NAME_LENGTH },
+		{ NULL }
+	}
+};
diff --git a/libblkid/bfs.c b/libblkid/bfs.c
new file mode 100644
index 0000000..8a34c58
--- /dev/null
+++ b/libblkid/bfs.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include "superblocks.h"
+
+/*
+ * BFS actually has two different labels in the superblock, each
+ * of them only 6 bytes long.  Until we find out what their use
+ * we just ignore them.
+ */
+const struct blkid_idinfo bfs_idinfo =
+{
+	.name		= "bfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.magics		= {
+		{ .magic = "\xce\xfa\xad\x1b", .len = 4 },
+		{ NULL }
+	}
+};
diff --git a/libblkid/bitops.h b/libblkid/bitops.h
new file mode 100644
index 0000000..7439ece
--- /dev/null
+++ b/libblkid/bitops.h
@@ -0,0 +1,126 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef BITOPS_H
+#define BITOPS_H
+
+#include <stdint.h>
+#include <sys/param.h>
+
+#if defined(HAVE_BYTESWAP_H)
+# include <byteswap.h>
+#endif
+
+#define HAVE_SYS_ENDIAN_H
+#if defined(HAVE_ENDIAN_H)
+#  include <endian.h>
+#elif defined(HAVE_SYS_ENDIAN_H)	/* BSDs have them here */
+#  include <sys/endian.h>
+#endif
+
+#if defined(__OpenBSD__)
+# include <sys/types.h>
+# define be16toh(x) betoh16(x)
+# define be32toh(x) betoh32(x)
+# define be64toh(x) betoh64(x)
+#endif
+
+/*
+ * Fallbacks
+ */
+#ifndef bswap_16
+# define bswap_16(x)   ((((x) & 0x00FF) << 8) | \
+			(((x) & 0xFF00) >> 8))
+#endif
+
+#ifndef bswap_32
+# define bswap_32(x)   ((((x) & 0x000000FF) << 24) | \
+			(((x) & 0x0000FF00) << 8)  | \
+			(((x) & 0x00FF0000) >> 8)  | \
+			(((x) & 0xFF000000) >> 24))
+#endif
+
+#ifndef bswap_64
+# define bswap_64(x) ((((x) & 0x00000000000000FFULL) << 56) | \
+                      (((x) & 0x000000000000FF00ULL) << 40) | \
+                      (((x) & 0x0000000000FF0000ULL) << 24) | \
+                      (((x) & 0x00000000FF000000ULL) << 8)  | \
+                      (((x) & 0x000000FF00000000ULL) >> 8)  | \
+                      (((x) & 0x0000FF0000000000ULL) >> 24) | \
+                      (((x) & 0x00FF000000000000ULL) >> 40) | \
+                      (((x) & 0xFF00000000000000ULL) >> 56))
+#endif
+
+//#ifndef htobe16
+//# if !defined(WORDS_BIGENDIAN)
+//#  define htobe16(x) bswap_16 (x)
+#  define htole16(x) (x)
+#  define be16toh(x) bswap_16 (x)
+#  define le16toh(x) (x)
+//#  define htobe32(x) bswap_32 (x)
+#  define htole32(x) (x)
+#  define be32toh(x) bswap_32 (x)
+#  define le32toh(x) (x)
+//#  define htobe64(x) bswap_64 (x)
+#  define htole64(x) (x)
+#  define be64toh(x) bswap_64 (x)
+#  define le64toh(x) (x)
+/*
+# else
+#  define htobe16(x) (x)
+#  define htole16(x) bswap_16 (x)
+#  define be16toh(x) (x)
+#  define le16toh(x) bswap_16 (x)
+#  define htobe32(x) (x)
+#  define htole32(x) bswap_32 (x)
+#  define be32toh(x) (x)
+#  define le32toh(x) bswap_32 (x)
+#  define htobe64(x) (x)
+#  define htole64(x) bswap_64 (x)
+#  define be64toh(x) (x)
+#  define le64toh(x) bswap_64 (x)
+# endif
+#endif
+*/
+/*
+ * Byte swab macros (based on linux/byteorder/swab.h)
+ */
+#define swab16(x) bswap_16(x)
+#define swab32(x) bswap_32(x)
+#define swab64(x) bswap_64(x)
+
+#define cpu_to_le16(x) ((uint16_t) htole16(x))
+#define cpu_to_le32(x) ((uint32_t) htole32(x))
+#define cpu_to_le64(x) ((uint64_t) htole64(x))
+
+#define cpu_to_be16(x) ((uint16_t) htobe16(x))
+#define cpu_to_be32(x) ((uint32_t) htobe32(x))
+#define cpu_to_be64(x) ((uint64_t) htobe64(x))
+
+#define le16_to_cpu(x) ((uint16_t) le16toh(x))
+#define le32_to_cpu(x) ((uint32_t) le32toh(x))
+#define le64_to_cpu(x) ((uint64_t) le64toh(x))
+
+#define be16_to_cpu(x) ((uint16_t) be16toh(x))
+#define be32_to_cpu(x) ((uint32_t) be32toh(x))
+#define be64_to_cpu(x) ((uint64_t) be64toh(x))
+
+/*
+ * Bit map related macros. Usually provided by libc.
+ */
+#ifndef NBBY
+# define NBBY            CHAR_BIT
+#endif
+
+#ifndef setbit
+# define setbit(a,i)	((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+# define clrbit(a,i)	((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+# define isset(a,i)	((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+# define isclr(a,i)	(((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#endif
+
+#endif /* BITOPS_H */
+
diff --git a/libblkid/blkdev.c b/libblkid/blkdev.c
new file mode 100644
index 0000000..514082e
--- /dev/null
+++ b/libblkid/blkdev.c
@@ -0,0 +1,375 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#endif
+
+#ifdef HAVE_SYS_DISK_H
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h> /* for LIST_HEAD */
+#endif
+#include <sys/disk.h>
+#endif
+
+#include "blkdev.h"
+#include "c.h"
+#include "linux_version.h"
+#include "xalloc.h"
+
+static long
+blkdev_valid_offset (int fd, off_t offset) {
+	char ch;
+
+	if (lseek (fd, offset, 0) < 0)
+		return 0;
+	if (read (fd, &ch, 1) < 1)
+		return 0;
+	return 1;
+}
+
+int is_blkdev(int fd)
+{
+	struct stat st;
+	return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode));
+}
+
+off_t
+blkdev_find_size (int fd) {
+	uintmax_t high, low = 0;
+
+	for (high = 1024; blkdev_valid_offset (fd, high); ) {
+		if (high == UINTMAX_MAX)
+			return -1;
+
+		low = high;
+
+		if (high >= UINTMAX_MAX/2)
+			high = UINTMAX_MAX;
+		else
+			high *= 2;
+	}
+
+	while (low < high - 1)
+	{
+		uintmax_t mid = (low + high) / 2;
+
+		if (blkdev_valid_offset (fd, mid))
+			low = mid;
+		else
+			high = mid;
+	}
+	blkdev_valid_offset (fd, 0);
+	return (low + 1);
+}
+
+/* get size in bytes */
+int
+blkdev_get_size(int fd, unsigned long long *bytes)
+{
+#ifdef DKIOCGETBLOCKCOUNT
+	/* Apple Darwin */
+	if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) {
+		*bytes <<= 9;
+		return 0;
+	}
+#endif
+
+#ifdef BLKGETSIZE64
+	{
+#ifdef __linux__
+		int ver = get_linux_version();
+
+		/* kernels 2.4.15-2.4.17, had a broken BLKGETSIZE64 */
+		if (ver >= KERNEL_VERSION (2,6,0) ||
+		   (ver >= KERNEL_VERSION (2,4,18) && ver < KERNEL_VERSION (2,5,0)))
+#endif
+			if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
+				return 0;
+	}
+#endif /* BLKGETSIZE64 */
+
+#ifdef BLKGETSIZE
+	{
+		unsigned long size;
+
+		if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+			*bytes = ((unsigned long long)size << 9);
+			return 0;
+		}
+	}
+
+#endif /* BLKGETSIZE */
+
+#ifdef DIOCGMEDIASIZE
+	/* FreeBSD */
+	if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0)
+		return 0;
+#endif
+
+#ifdef FDGETPRM
+	{
+		struct floppy_struct this_floppy;
+
+		if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
+			*bytes = this_floppy.size << 9;
+			return 0;
+		}
+	}
+#endif /* FDGETPRM */
+
+#ifdef HAVE_SYS_DISKLABEL_H
+	{
+		/*
+		 * This code works for FreeBSD 4.11 i386, except for the full device
+		 * (such as /dev/ad0). It doesn't work properly for newer FreeBSD
+		 * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
+		 * above however.
+		 *
+		 * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
+		 * character) devices, so we need to check for S_ISCHR, too.
+		 */
+		int part = -1;
+		struct disklabel lab;
+		struct partition *pp;
+		char ch;
+		struct stat st;
+
+		if ((fstat(fd, &st) >= 0) &&
+		    (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
+			part = st.st_rdev & 7;
+
+		if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+			pp = &lab.d_partitions[part];
+			if (pp->p_size) {
+				 *bytes = pp->p_size << 9;
+				 return 0;
+			}
+		}
+	}
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+	{
+		struct stat st;
+
+		if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
+			*bytes = st.st_size;
+			return 0;
+		}
+		if (!S_ISBLK(st.st_mode))
+			return -1;
+	}
+
+	*bytes = blkdev_find_size(fd);
+	return 0;
+}
+
+/* get 512-byte sector count */
+int
+blkdev_get_sectors(int fd, unsigned long long *sectors)
+{
+	unsigned long long bytes;
+
+	if (blkdev_get_size(fd, &bytes) == 0) {
+		*sectors = (bytes >> 9);
+		return 0;
+	}
+
+	return -1;
+}
+
+/*
+ * Get logical sector size.
+ *
+ * This is the smallest unit the storage device can
+ * address. It is typically 512 bytes.
+ */
+int blkdev_get_sector_size(int fd, int *sector_size)
+{
+#ifdef BLKSSZGET
+	if (ioctl(fd, BLKSSZGET, sector_size) >= 0)
+		return 0;
+	return -1;
+#else
+	*sector_size = DEFAULT_SECTOR_SIZE;
+	return 0;
+#endif
+}
+
+/*
+ * Get physical block device size. The BLKPBSZGET is supported since Linux
+ * 2.6.32. For old kernels is probably the best to assume that physical sector
+ * size is the same as logical sector size.
+ *
+ * Example:
+ *
+ * rc = blkdev_get_physector_size(fd, &physec);
+ * if (rc || physec == 0) {
+ *	rc = blkdev_get_sector_size(fd, &physec);
+ *	if (rc)
+ *		physec = DEFAULT_SECTOR_SIZE;
+ * }
+ */
+int blkdev_get_physector_size(int fd, int *sector_size)
+{
+#ifdef BLKPBSZGET
+	if (ioctl(fd, BLKPBSZGET, &sector_size) >= 0)
+		return 0;
+	return -1;
+#else
+	*sector_size = DEFAULT_SECTOR_SIZE;
+	return 0;
+#endif
+}
+
+/*
+ * Return the alignment status of a device
+ */
+int blkdev_is_misaligned(int fd)
+{
+#ifdef BLKALIGNOFF
+	int aligned;
+
+	if (ioctl(fd, BLKALIGNOFF, &aligned) < 0)
+		return 0;			/* probably kernel < 2.6.32 */
+	/*
+	 * Note that kernel returns -1 as alignement offset if no compatible
+	 * sizes and alignments exist for stacked devices
+	 */
+	return aligned != 0 ? 1 : 0;
+#else
+	return 0;
+#endif
+}
+
+int blkdev_is_cdrom(int fd)
+{
+#ifdef CDROM_GET_CAPABILITY
+	int ret;
+
+	if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0)
+		return 0;
+	else
+		return ret;
+#else
+	return 0;
+#endif
+}
+
+/*
+ * Get kernel's interpretation of the device's geometry.
+ *
+ * Returns the heads and sectors - but not cylinders
+ * as it's truncated for disks with more than 65535 tracks.
+ *
+ * Note that this is deprecated in favor of LBA addressing.
+ */
+int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s)
+{
+#ifdef HDIO_GETGEO
+	struct hd_geometry geometry;
+
+	if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) {
+		*h = geometry.heads;
+		*s = geometry.sectors;
+		return 0;
+	}
+#else
+	*h = 0;
+	*s = 0;
+#endif
+	return -1;
+}
+
+/*
+ * Convert scsi type to human readable string.
+ */
+const char *blkdev_scsi_type_to_name(int type)
+{
+	switch (type) {
+	case SCSI_TYPE_DISK:
+		return "disk";
+	case SCSI_TYPE_TAPE:
+		return "tape";
+	case SCSI_TYPE_PRINTER:
+		return "printer";
+	case SCSI_TYPE_PROCESSOR:
+		return "processor";
+	case SCSI_TYPE_WORM:
+		return "worm";
+	case SCSI_TYPE_ROM:
+		return "rom";
+	case SCSI_TYPE_SCANNER:
+		return "scanner";
+	case SCSI_TYPE_MOD:
+		return "mo-disk";
+	case SCSI_TYPE_MEDIUM_CHANGER:
+		return "changer";
+	case SCSI_TYPE_COMM:
+		return "comm";
+	case SCSI_TYPE_RAID:
+		return "raid";
+	case SCSI_TYPE_ENCLOSURE:
+		return "enclosure";
+	case SCSI_TYPE_RBC:
+		return "rbc";
+	case SCSI_TYPE_OSD:
+		return "osd";
+	case SCSI_TYPE_NO_LUN:
+		return "no-lun";
+	default:
+		break;
+	}
+	return NULL;
+}
+
+#ifdef TEST_PROGRAM
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+int
+main(int argc, char **argv)
+{
+	unsigned long long bytes;
+	unsigned long long sectors;
+	int sector_size, phy_sector_size;
+	int fd;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s device\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if ((fd = open(argv[1], O_RDONLY)) < 0)
+		err(EXIT_FAILURE, "open %s failed", argv[1]);
+
+	if (blkdev_get_size(fd, &bytes) < 0)
+		err(EXIT_FAILURE, "blkdev_get_size() failed");
+	if (blkdev_get_sectors(fd, &sectors) < 0)
+		err(EXIT_FAILURE, "blkdev_get_sectors() failed");
+	if (blkdev_get_sector_size(fd, &sector_size) < 0)
+		err(EXIT_FAILURE, "blkdev_get_sector_size() failed");
+	if (blkdev_get_physector_size(fd, &phy_sector_size) < 0)
+		err(EXIT_FAILURE, "blkdev_get_physector_size() failed");
+
+	printf("          bytes: %llu\n", bytes);
+	printf("        sectors: %llu\n", sectors);
+	printf("    sector size: %d\n", sector_size);
+	printf("phy-sector size: %d\n", phy_sector_size);
+
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/blkdev.h b/libblkid/blkdev.h
new file mode 100644
index 0000000..ade0887
--- /dev/null
+++ b/libblkid/blkdev.h
@@ -0,0 +1,146 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef BLKDEV_H
+#define BLKDEV_H
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_IOCCOM_H
+# include <sys/ioccom.h> /* for _IO macro on e.g. Solaris */
+#endif
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_MKDEV_H
+# include <sys/mkdev.h>		/* major and minor on Solaris */
+#endif
+
+#define DEFAULT_SECTOR_SIZE       512
+
+#ifdef __linux__
+/* very basic ioclts, should be available everywhere */
+# ifndef BLKROSET
+#  define BLKROSET   _IO(0x12,93)	/* set device read-only (0 = read-write) */
+#  define BLKROGET   _IO(0x12,94)	/* get read-only status (0 = read_write) */
+#  define BLKRRPART  _IO(0x12,95)	/* re-read partition table */
+#  define BLKGETSIZE _IO(0x12,96)	/* return device size /512 (long *arg) */
+#  define BLKFLSBUF  _IO(0x12,97)	/* flush buffer cache */
+#  define BLKRASET   _IO(0x12,98)	/* set read ahead for block device */
+#  define BLKRAGET   _IO(0x12,99)	/* get current read ahead setting */
+#  define BLKFRASET  _IO(0x12,100)	/* set filesystem (mm/filemap.c) read-ahead */
+#  define BLKFRAGET  _IO(0x12,101)	/* get filesystem (mm/filemap.c) read-ahead */
+#  define BLKSECTSET _IO(0x12,102)	/* set max sectors per request (ll_rw_blk.c) */
+#  define BLKSECTGET _IO(0x12,103)	/* get max sectors per request (ll_rw_blk.c) */
+#  define BLKSSZGET  _IO(0x12,104)	/* get block device sector size */
+
+/* ioctls introduced in 2.2.16, removed in 2.5.58 */
+#  define BLKELVGET  _IOR(0x12,106,size_t) /* elevator get */
+#  define BLKELVSET  _IOW(0x12,107,size_t) /* elevator set */
+
+#  define BLKBSZGET  _IOR(0x12,112,size_t)
+#  define BLKBSZSET  _IOW(0x12,113,size_t)
+# endif /* !BLKROSET */
+
+# ifndef BLKGETSIZE64
+#  define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
+# endif
+
+/* block device topology ioctls, introduced in 2.6.32 (commit ac481c20) */
+# ifndef BLKIOMIN
+#  define BLKIOMIN   _IO(0x12,120)
+#  define BLKIOOPT   _IO(0x12,121)
+#  define BLKALIGNOFF _IO(0x12,122)
+#  define BLKPBSZGET _IO(0x12,123)
+# endif
+
+/* discard zeroes support, introduced in 2.6.33 (commit 98262f27) */
+# ifndef BLKDISCARDZEROES
+#  define BLKDISCARDZEROES _IO(0x12,124)
+# endif
+
+/* filesystem freeze, introduced in 2.6.29 (commit fcccf502) */
+# ifndef FIFREEZE
+#  define FIFREEZE   _IOWR('X', 119, int)    /* Freeze */
+#  define FITHAW     _IOWR('X', 120, int)    /* Thaw */
+# endif
+
+/* uniform CD-ROM information */
+# ifndef CDROM_GET_CAPABILITY
+#  define CDROM_GET_CAPABILITY 0x5331
+# endif
+
+#endif /* __linux */
+
+
+#ifdef APPLE_DARWIN
+# define BLKGETSIZE DKIOCGETBLOCKCOUNT32
+#endif
+
+#ifndef HDIO_GETGEO
+# ifdef __linux__
+#  define HDIO_GETGEO 0x0301
+# endif
+
+struct hd_geometry {
+	unsigned char heads;
+	unsigned char sectors;
+	unsigned short cylinders;	/* truncated */
+	unsigned long start;
+};
+#endif /* HDIO_GETGEO */
+
+
+/* are we working with block device? */
+int is_blkdev(int fd);
+
+/* Determine size in bytes */
+off_t blkdev_find_size (int fd);
+
+/* get size in bytes */
+int blkdev_get_size(int fd, unsigned long long *bytes);
+
+/* get 512-byte sector count */
+int blkdev_get_sectors(int fd, unsigned long long *sectors);
+
+/* get hardware sector size */
+int blkdev_get_sector_size(int fd, int *sector_size);
+
+/* specifies whether or not the device is misaligned */
+int blkdev_is_misaligned(int fd);
+
+/* get physical block device size */
+int blkdev_get_physector_size(int fd, int *sector_size);
+
+/* is the device cdrom capable? */
+int blkdev_is_cdrom(int fd);
+
+/* get device's geometry - legacy */
+int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s);
+
+/* SCSI device types.  Copied almost as-is from kernel header.
+ * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=include/scsi/scsi.h */
+#define SCSI_TYPE_DISK			0x00
+#define SCSI_TYPE_TAPE			0x01
+#define SCSI_TYPE_PRINTER		0x02
+#define SCSI_TYPE_PROCESSOR		0x03	/* HP scanners use this */
+#define SCSI_TYPE_WORM			0x04	/* Treated as ROM by our system */
+#define SCSI_TYPE_ROM			0x05
+#define SCSI_TYPE_SCANNER		0x06
+#define SCSI_TYPE_MOD			0x07	/* Magneto-optical disk - treated as SCSI_TYPE_DISK */
+#define SCSI_TYPE_MEDIUM_CHANGER	0x08
+#define SCSI_TYPE_COMM			0x09	/* Communications device */
+#define SCSI_TYPE_RAID			0x0c
+#define SCSI_TYPE_ENCLOSURE		0x0d	/* Enclosure Services Device */
+#define SCSI_TYPE_RBC			0x0e
+#define SCSI_TYPE_OSD			0x11
+#define SCSI_TYPE_NO_LUN		0x7f
+
+/* convert scsi type code to name */
+const char *blkdev_scsi_type_to_name(int type);
+
+
+#endif /* BLKDEV_H */
diff --git a/libblkid/blkid.h b/libblkid/blkid.h
new file mode 100644
index 0000000..b283009
--- /dev/null
+++ b/libblkid/blkid.h
@@ -0,0 +1,458 @@
+/*
+ * blkid.h - Interface for libblkid, a library to identify block devices
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _BLKID_BLKID_H
+#define _BLKID_BLKID_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#define _SC_HOST_NAME_MAX 255
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BLKID_VERSION   "2.22.0"
+#define BLKID_DATE      "04-Sep-2012"
+
+/**
+ * blkid_dev:
+ *
+ * The device object keeps information about one device
+ */
+typedef struct blkid_struct_dev *blkid_dev;
+
+/**
+ * blkid_cache:
+ *
+ * information about all system devices
+ */
+typedef struct blkid_struct_cache *blkid_cache;
+
+/**
+ * blkid_probe:
+ *
+ * low-level probing setting
+ */
+typedef struct blkid_struct_probe *blkid_probe;
+
+/**
+ * blkid_topology:
+ *
+ * device topology information
+ */
+typedef struct blkid_struct_topology *blkid_topology;
+
+/**
+ * blkid_partlist
+ *
+ * list of all detected partitions and partitions tables
+ */
+typedef struct blkid_struct_partlist *blkid_partlist;
+
+/**
+ * blkid_partition:
+ *
+ * information about a partition
+ */
+typedef struct blkid_struct_partition *blkid_partition;
+
+/**
+ * blkid_parttable:
+ *
+ * information about a partition table
+ */
+typedef struct blkid_struct_parttable *blkid_parttable;
+
+/**
+ * blkid_loff_t:
+ *
+ * 64-bit signed number for offsets and sizes
+ */
+typedef int64_t blkid_loff_t;
+
+/**
+ * blkid_tag_iterate:
+ *
+ * tags iterator for high-level (blkid_cache) API
+ */
+typedef struct blkid_struct_tag_iterate *blkid_tag_iterate;
+
+/**
+ * blkid_dev_iterate:
+ *
+ * devices iterator for high-level (blkid_cache) API
+ */
+typedef struct blkid_struct_dev_iterate *blkid_dev_iterate;
+
+/*
+ * Flags for blkid_get_dev
+ *
+ * BLKID_DEV_CREATE	Create an empty device structure if not found
+ *			in the cache.
+ * BLKID_DEV_VERIFY	Make sure the device structure corresponds
+ *			with reality.
+ * BLKID_DEV_FIND	Just look up a device entry, and return NULL
+ *			if it is not found.
+ * BLKID_DEV_NORMAL	Get a valid device structure, either from the
+ *			cache or by probing the device.
+ */
+#define BLKID_DEV_FIND		0x0000
+#define BLKID_DEV_CREATE	0x0001
+#define BLKID_DEV_VERIFY	0x0002
+#define BLKID_DEV_NORMAL	(BLKID_DEV_CREATE | BLKID_DEV_VERIFY)
+
+
+#ifndef __GNUC_PREREQ
+# if defined __GNUC__ && defined __GNUC_MINOR__
+#  define __GNUC_PREREQ(maj, min)  ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+# else
+#  define __GNUC_PREREQ(maj, min) 0
+# endif
+#endif
+
+#ifndef __ul_attribute__
+# if __GNUC_PREREQ (3, 4)
+#  define __ul_attribute__(_a_) __attribute__(_a_)
+# else
+#  define __ul_attribute__(_a_)
+# endif
+#endif
+
+/* cache.c */
+extern void blkid_put_cache(blkid_cache cache);
+extern int blkid_get_cache(blkid_cache *cache, const char *filename);
+extern void blkid_gc_cache(blkid_cache cache);
+
+/* dev.c */
+extern const char *blkid_dev_devname(blkid_dev dev)
+			__ul_attribute__((warn_unused_result));
+
+extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
+			__ul_attribute__((nonnull));
+extern int blkid_dev_set_search(blkid_dev_iterate iter,
+				char *search_type, char *search_value);
+extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev);
+extern void blkid_dev_iterate_end(blkid_dev_iterate iterate);
+
+/* devno.c */
+extern char *blkid_devno_to_devname(dev_t devno)
+			__ul_attribute__((warn_unused_result));
+extern int blkid_devno_to_wholedisk(dev_t dev, char *diskname,
+                        size_t len, dev_t *diskdevno)
+			__ul_attribute__((warn_unused_result));
+
+/* devname.c */
+extern int blkid_probe_all(blkid_cache cache);
+extern int blkid_probe_all_new(blkid_cache cache);
+extern int blkid_probe_all_removable(blkid_cache cache);
+
+extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags);
+
+/* getsize.c */
+extern blkid_loff_t blkid_get_dev_size(int fd)
+			__ul_attribute__((warn_unused_result));
+
+/* verify.c */
+extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev);
+
+/* read.c */
+
+/* resolve.c */
+extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+				       const char *devname)
+			__ul_attribute__((warn_unused_result));
+extern char *blkid_get_devname(blkid_cache cache, const char *token,
+			       const char *value)
+			__ul_attribute__((warn_unused_result));
+
+/* tag.c */
+extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
+			__ul_attribute__((warn_unused_result));
+extern int blkid_tag_next(blkid_tag_iterate iterate,
+			      const char **type, const char **value);
+extern void blkid_tag_iterate_end(blkid_tag_iterate iterate);
+extern int blkid_dev_has_tag(blkid_dev dev, const char *type, const char *value)
+			__ul_attribute__((nonnull(1,2)));
+
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+					 const char *type,
+					 const char *value)
+			__ul_attribute__((warn_unused_result));
+
+extern int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val);
+
+/* version.c */
+extern int blkid_parse_version_string(const char *ver_string)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+extern int blkid_get_library_version(const char **ver_string,
+				     const char **date_string);
+
+/* encode.c */
+extern int blkid_encode_string(const char *str, char *str_enc, size_t len);
+extern int blkid_safe_string(const char *str, char *str_safe, size_t len);
+
+/* evaluate.c */
+extern int blkid_send_uevent(const char *devname, const char *action);
+extern char *blkid_evaluate_tag(const char *token, const char *value,
+				blkid_cache *cache)
+			__ul_attribute__((warn_unused_result));
+extern char *blkid_evaluate_spec(const char *spec, blkid_cache *cache)
+			__ul_attribute__((warn_unused_result));
+
+/* probe.c */
+extern blkid_probe blkid_new_probe(void)
+			__ul_attribute__((warn_unused_result));
+extern blkid_probe blkid_new_probe_from_filename(const char *filename)
+			__ul_attribute__((warn_unused_result));
+extern void blkid_free_probe(blkid_probe pr);
+
+extern void blkid_reset_probe(blkid_probe pr);
+
+extern int blkid_probe_set_device(blkid_probe pr, int fd,
+	                blkid_loff_t off, blkid_loff_t size);
+
+extern dev_t blkid_probe_get_devno(blkid_probe pr)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+
+extern dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+
+extern int blkid_probe_is_wholedisk(blkid_probe pr)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+
+extern blkid_loff_t blkid_probe_get_size(blkid_probe pr)
+			__ul_attribute__((warn_unused_result));
+extern blkid_loff_t blkid_probe_get_offset(blkid_probe pr)
+			__ul_attribute__((warn_unused_result));
+extern unsigned int blkid_probe_get_sectorsize(blkid_probe pr)
+			__ul_attribute__((warn_unused_result));
+extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr)
+			__ul_attribute__((warn_unused_result));
+
+extern int blkid_probe_get_fd(blkid_probe pr)
+			__ul_attribute__((warn_unused_result));
+
+/*
+ * superblocks probing
+ */
+extern int blkid_known_fstype(const char *fstype)
+			__ul_attribute__((warn_unused_result));
+
+extern int blkid_superblocks_get_name(size_t idx, const char **name, int *usage);
+
+extern int blkid_probe_enable_superblocks(blkid_probe pr, int enable);
+
+#define BLKID_SUBLKS_LABEL	(1 << 1) /* read LABEL from superblock */
+#define BLKID_SUBLKS_LABELRAW	(1 << 2) /* read and define LABEL_RAW result value*/
+#define BLKID_SUBLKS_UUID	(1 << 3) /* read UUID from superblock */
+#define BLKID_SUBLKS_UUIDRAW	(1 << 4) /* read and define UUID_RAW result value */
+#define BLKID_SUBLKS_TYPE	(1 << 5) /* define TYPE result value */
+#define BLKID_SUBLKS_SECTYPE	(1 << 6) /* define compatible fs type (second type) */
+#define BLKID_SUBLKS_USAGE	(1 << 7) /* define USAGE result value */
+#define BLKID_SUBLKS_VERSION	(1 << 8) /* read FS type from superblock */
+#define BLKID_SUBLKS_MAGIC	(1 << 9) /* define SBMAGIC and SBMAGIC_OFFSET */
+
+#define BLKID_SUBLKS_DEFAULT	(BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | \
+				 BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE)
+
+extern int blkid_probe_set_superblocks_flags(blkid_probe pr, int flags);
+extern int blkid_probe_reset_superblocks_filter(blkid_probe pr);
+extern int blkid_probe_invert_superblocks_filter(blkid_probe pr);
+
+/**
+ * BLKID_FLTR_NOTIN
+ */
+#define BLKID_FLTR_NOTIN		1
+/**
+ * BLKID_FLTR_ONLYIN
+ */
+#define BLKID_FLTR_ONLYIN		2
+extern int blkid_probe_filter_superblocks_type(blkid_probe pr, int flag, char *names[]);
+
+#define BLKID_USAGE_FILESYSTEM		(1 << 1)
+#define BLKID_USAGE_RAID		(1 << 2)
+#define BLKID_USAGE_CRYPTO		(1 << 3)
+#define BLKID_USAGE_OTHER		(1 << 4)
+extern int blkid_probe_filter_superblocks_usage(blkid_probe pr, int flag, int usage);
+
+/*
+ * topology probing
+ */
+extern int blkid_probe_enable_topology(blkid_probe pr, int enable);
+
+/* binary interface */
+extern blkid_topology blkid_probe_get_topology(blkid_probe pr)
+			__ul_attribute__((warn_unused_result));
+
+extern unsigned long blkid_topology_get_alignment_offset(blkid_topology tp)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+extern unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+extern unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+extern unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+extern unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+
+/*
+ * partitions probing
+ */
+extern int blkid_known_pttype(const char *pttype)
+			__ul_attribute__((warn_unused_result));
+
+extern int blkid_probe_enable_partitions(blkid_probe pr, int enable);
+
+extern int blkid_probe_reset_partitions_filter(blkid_probe pr);
+extern int blkid_probe_invert_partitions_filter(blkid_probe pr);
+extern int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[]);
+
+/* partitions probing flags */
+#define BLKID_PARTS_FORCE_GPT		(1 << 1)
+#define BLKID_PARTS_ENTRY_DETAILS	(1 << 2)
+#define BLKID_PARTS_MAGIC		(1 << 3)
+extern int blkid_probe_set_partitions_flags(blkid_probe pr, int flags);
+
+/* binary interface */
+extern blkid_partlist blkid_probe_get_partitions(blkid_probe pr)
+			__ul_attribute__((warn_unused_result));
+
+extern int blkid_partlist_numof_partitions(blkid_partlist ls)
+			__ul_attribute__((warn_unused_result));
+extern blkid_parttable blkid_partlist_get_table(blkid_partlist ls)
+			__ul_attribute__((warn_unused_result));
+extern blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n)
+			__ul_attribute__((warn_unused_result));
+extern blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno)
+			__ul_attribute__((warn_unused_result));
+extern blkid_parttable blkid_partition_get_table(blkid_partition par)
+			__ul_attribute__((warn_unused_result));
+
+extern const char *blkid_partition_get_name(blkid_partition par)
+			__ul_attribute__((warn_unused_result));
+extern const char *blkid_partition_get_uuid(blkid_partition par)
+			__ul_attribute__((warn_unused_result));
+extern int blkid_partition_get_partno(blkid_partition par)
+			__ul_attribute__((warn_unused_result));
+extern blkid_loff_t blkid_partition_get_start(blkid_partition par)
+			__ul_attribute__((warn_unused_result));
+extern blkid_loff_t blkid_partition_get_size(blkid_partition par)
+			__ul_attribute__((warn_unused_result));
+
+extern int blkid_partition_get_type(blkid_partition par)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+
+extern const char *blkid_partition_get_type_string(blkid_partition par)
+			__ul_attribute__((warn_unused_result));
+
+extern unsigned long long blkid_partition_get_flags(blkid_partition par)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+
+extern int blkid_partition_is_logical(blkid_partition par)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+extern int blkid_partition_is_extended(blkid_partition par)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+extern int blkid_partition_is_primary(blkid_partition par)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+
+extern const char *blkid_parttable_get_type(blkid_parttable tab)
+			__ul_attribute__((warn_unused_result));
+
+extern const char *blkid_parttable_get_id(blkid_parttable tab)
+			__ul_attribute__((warn_unused_result));
+
+extern blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab)
+			__ul_attribute__((warn_unused_result));
+extern blkid_partition blkid_parttable_get_parent(blkid_parttable tab)
+			__ul_attribute__((warn_unused_result));
+
+/*
+ * NAME=value low-level interface
+ */
+extern int blkid_do_probe(blkid_probe pr);
+extern int blkid_do_safeprobe(blkid_probe pr);
+extern int blkid_do_fullprobe(blkid_probe pr);
+
+extern int blkid_probe_numof_values(blkid_probe pr)
+			__ul_attribute__((warn_unused_result));
+extern int blkid_probe_get_value(blkid_probe pr, int num, const char **name,
+                        const char **data, size_t *len);
+extern int blkid_probe_lookup_value(blkid_probe pr, const char *name,
+                        const char **data, size_t *len);
+extern int blkid_probe_has_value(blkid_probe pr, const char *name)
+			__ul_attribute__((nonnull))
+			__ul_attribute__((warn_unused_result));
+
+extern int blkid_do_wipe(blkid_probe pr, int dryrun);
+extern int blkid_probe_step_back(blkid_probe pr);
+
+/*
+ * Deprecated functions/macros
+ */
+#ifndef BLKID_DISABLE_DEPRECATED
+
+#define BLKID_PROBREQ_LABEL     BLKID_SUBLKS_LABEL
+#define BLKID_PROBREQ_LABELRAW  BLKID_SUBLKS_LABELRAW
+#define BLKID_PROBREQ_UUID      BLKID_SUBLKS_UUID
+#define BLKID_PROBREQ_UUIDRAW   BLKID_SUBLKS_UUIDRAW
+#define BLKID_PROBREQ_TYPE      BLKID_SUBLKS_TYPE
+#define BLKID_PROBREQ_SECTYPE   BLKID_SUBLKS_SECTYPE
+#define BLKID_PROBREQ_USAGE     BLKID_SUBLKS_USAGE
+#define BLKID_PROBREQ_VERSION   BLKID_SUBLKS_VERSION
+
+extern int blkid_probe_set_request(blkid_probe pr, int flags)
+			__ul_attribute__((deprecated));
+
+extern int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage)
+			__ul_attribute__((deprecated));
+
+extern int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[])
+			__ul_attribute__((deprecated));
+
+extern int blkid_probe_invert_filter(blkid_probe pr)
+			__ul_attribute__((deprecated));
+
+extern int blkid_probe_reset_filter(blkid_probe pr)
+			__ul_attribute__((deprecated));
+
+#endif /* BLKID_DISABLE_DEPRECATED */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_BLKID_H */
diff --git a/libblkid/blkidP.h b/libblkid/blkidP.h
new file mode 100644
index 0000000..2d5e8cb
--- /dev/null
+++ b/libblkid/blkidP.h
@@ -0,0 +1,551 @@
+/*
+ * blkidP.h - Internal interfaces for libblkid
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_BLKIDP_H
+#define _BLKID_BLKIDP_H
+
+/* support debug output if LIBBLKID_DEBUG env. variable is set */
+#define CONFIG_BLKID_DEBUG 1
+
+/* Always confirm that /dev/disk-by symlinks match with LABEL/UUID on device */
+/* #define CONFIG_BLKID_VERIFY_UDEV 1 */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include "c.h"
+#include "bitops.h"	/* $(top_srcdir)/include/ */
+#include "blkdev.h"
+
+#include "blkid.h"
+#include "list.h"
+
+/*
+ * This describes the attributes of a specific device.
+ * We can traverse all of the tags by bid_tags (linking to the tag bit_names).
+ * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag
+ * values, if they exist.
+ */
+struct blkid_struct_dev
+{
+	struct list_head	bid_devs;	/* All devices in the cache */
+	struct list_head	bid_tags;	/* All tags for this device */
+	blkid_cache		bid_cache;	/* Dev belongs to this cache */
+	char			*bid_name;	/* Device inode pathname */
+	char			*bid_type;	/* Preferred device TYPE */
+	int			bid_pri;	/* Device priority */
+	dev_t			bid_devno;	/* Device major/minor number */
+	time_t			bid_time;	/* Last update time of device */
+	suseconds_t		bid_utime;	/* Last update time (microseconds) */
+	unsigned int		bid_flags;	/* Device status bitflags */
+	char			*bid_label;	/* Shortcut to device LABEL */
+	char			*bid_uuid;	/* Shortcut to binary UUID */
+};
+
+#define BLKID_BID_FL_VERIFIED	0x0001	/* Device data validated from disk */
+#define BLKID_BID_FL_INVALID	0x0004	/* Device is invalid */
+#define BLKID_BID_FL_REMOVABLE	0x0008	/* Device added by blkid_probe_all_removable() */
+
+/*
+ * Each tag defines a NAME=value pair for a particular device.  The tags
+ * are linked via bit_names for a single device, so that traversing the
+ * names list will get you a list of all tags associated with a device.
+ * They are also linked via bit_values for all devices, so one can easily
+ * search all tags with a given NAME for a specific value.
+ */
+struct blkid_struct_tag
+{
+	struct list_head	bit_tags;	/* All tags for this device */
+	struct list_head	bit_names;	/* All tags with given NAME */
+	char			*bit_name;	/* NAME of tag (shared) */
+	char			*bit_val;	/* value of tag */
+	blkid_dev		bit_dev;	/* pointer to device */
+};
+typedef struct blkid_struct_tag *blkid_tag;
+
+/*
+ * Chain IDs
+ */
+enum {
+	BLKID_CHAIN_SUBLKS,	/* FS/RAID superblocks (enabled by default) */
+	BLKID_CHAIN_TOPLGY,	/* Block device topology */
+	BLKID_CHAIN_PARTS,	/* Partition tables */
+
+	BLKID_NCHAINS		/* number of chains */
+};
+
+struct blkid_chain {
+	const struct blkid_chaindrv *driver;	/* chain driver */
+
+	int		enabled;	/* boolean */
+	int		flags;		/* BLKID_<chain>_* */
+	int		binary;		/* boolean */
+	int		idx;		/* index of the current prober (or -1) */
+	unsigned long	*fltr;		/* filter or NULL */
+	void		*data;		/* private chain data or NULL */
+};
+
+/*
+ * Chain driver
+ */
+struct blkid_chaindrv {
+	const size_t	id;		/* BLKID_CHAIN_* */
+	const char	*name;		/* name of chain (for debug purpose) */
+	const int	dflt_flags;	/* default chain flags */
+	const int	dflt_enabled;	/* default enabled boolean */
+	int		has_fltr;	/* boolean */
+
+	const struct blkid_idinfo **idinfos; /* description of probing functions */
+	const size_t	nidinfos;	/* number of idinfos */
+
+	/* driver operations */
+	int		(*probe)(blkid_probe, struct blkid_chain *);
+	int		(*safeprobe)(blkid_probe, struct blkid_chain *);
+	void		(*free_data)(blkid_probe, void *);
+};
+
+/*
+ * Low-level probe result
+ */
+#define BLKID_PROBVAL_BUFSIZ	128
+
+#define BLKID_NVALS_SUBLKS	18
+#define BLKID_NVALS_TOPLGY	5
+#define BLKID_NVALS_PARTS	13
+
+/* Max number of all values in probing result */
+#define BLKID_NVALS             (BLKID_NVALS_SUBLKS + \
+				 BLKID_NVALS_TOPLGY + \
+				 BLKID_NVALS_PARTS)
+
+struct blkid_prval
+{
+	const char	*name;			/* value name */
+	unsigned char	data[BLKID_PROBVAL_BUFSIZ]; /* value data */
+	size_t		len;			/* length of value data */
+
+	struct blkid_chain	*chain;		/* owner */
+};
+
+/*
+ * Filesystem / Raid magic strings
+ */
+struct blkid_idmag
+{
+	const char	*magic;		/* magic string */
+	unsigned int	len;		/* length of magic */
+
+	long		kboff;		/* kilobyte offset of superblock */
+	unsigned int	sboff;		/* byte offset within superblock */
+};
+
+/*
+ * Filesystem / Raid description
+ */
+struct blkid_idinfo
+{
+	const char	*name;		/* fs, raid or partition table name */
+	int		usage;		/* BLKID_USAGE_* flag */
+	int		flags;		/* BLKID_IDINFO_* flags */
+	int		minsz;		/* minimal device size */
+
+					/* probe function */
+	int		(*probefunc)(blkid_probe pr, const struct blkid_idmag *mag);
+
+	struct blkid_idmag	magics[];	/* NULL or array with magic strings */
+};
+
+#define BLKID_NONE_MAGIC	{{ NULL }}
+
+/*
+ * tolerant FS - can share the same device with more filesystems (e.g. typical
+ * on CD-ROMs). We need this flag to detect ambivalent results (e.g. valid fat
+ * and valid linux swap on the same device).
+ */
+#define BLKID_IDINFO_TOLERANT	(1 << 1)
+
+struct blkid_bufinfo {
+	unsigned char		*data;
+	blkid_loff_t		off;
+	blkid_loff_t		len;
+	struct list_head	bufs;	/* list of buffers */
+};
+
+/*
+ * Low-level probing control struct
+ */
+struct blkid_struct_probe
+{
+	int			fd;		/* device file descriptor */
+	blkid_loff_t		off;		/* begin of data on the device */
+	blkid_loff_t		size;		/* end of data on the device */
+
+	dev_t			devno;		/* device number (st.st_rdev) */
+	dev_t			disk_devno;	/* devno of the whole-disk or 0 */
+	unsigned int		blkssz;		/* sector size (BLKSSZGET ioctl) */
+	mode_t			mode;		/* struct stat.sb_mode */
+
+	int			flags;		/* private libray flags */
+	int			prob_flags;	/* always zeroized by blkid_do_*() */
+
+	blkid_loff_t		wipe_off;	/* begin of the wiped area */
+	blkid_loff_t		wipe_size;	/* size of the wiped area */
+	struct blkid_chain	*wipe_chain;	/* superblock, partition, ... */
+
+	struct list_head	buffers;	/* list of buffers */
+
+	struct blkid_chain	chains[BLKID_NCHAINS];	/* array of chains */
+	struct blkid_chain	*cur_chain;		/* current chain */
+
+	struct blkid_prval	vals[BLKID_NVALS];	/* results */
+	int			nvals;		/* number of assigned vals */
+
+	struct blkid_struct_probe *parent;	/* for clones */
+	struct blkid_struct_probe *disk_probe;	/* whole-disk probing */
+};
+
+/* private flags library flags */
+#define BLKID_FL_PRIVATE_FD	(1 << 1)	/* see blkid_new_probe_from_filename() */
+#define BLKID_FL_TINY_DEV	(1 << 2)	/* <= 1.47MiB (floppy or so) */
+#define BLKID_FL_CDROM_DEV	(1 << 3)	/* is a CD/DVD drive */
+
+/* private per-probing flags */
+#define BLKID_PROBE_FL_IGNORE_PT (1 << 1)	/* ignore partition table */
+#define BLKID_PROBE_FL_IGNORE_BACKUP (1 << 2)	/* ignore backup superblocks or PT */
+
+extern int blkid_probe_ignore_backup(blkid_probe pr);
+
+extern blkid_probe blkid_clone_probe(blkid_probe parent);
+extern blkid_probe blkid_probe_get_wholedisk_probe(blkid_probe pr);
+
+/*
+ * Evaluation methods (for blkid_eval_* API)
+ */
+enum {
+	BLKID_EVAL_UDEV = 0,
+	BLKID_EVAL_SCAN,
+
+	__BLKID_EVAL_LAST
+};
+
+/*
+ * Library config options
+ */
+struct blkid_config {
+	int eval[__BLKID_EVAL_LAST];	/* array with EVALUATION=<udev,cache> options */
+	int nevals;			/* number of elems in eval array */
+	int uevent;			/* SEND_UEVENT=<yes|not> option */
+	char *cachefile;		/* CACHE_FILE=<path> option */
+};
+
+extern struct blkid_config *blkid_read_config(const char *filename)
+			__ul_attribute__((warn_unused_result));
+extern void blkid_free_config(struct blkid_config *conf);
+
+/*
+ * Minimum number of seconds between device probes, even when reading
+ * from the cache.  This is to avoid re-probing all devices which were
+ * just probed by another program that does not share the cache.
+ */
+#define BLKID_PROBE_MIN		2
+
+/*
+ * Time in seconds an entry remains verified in the in-memory cache
+ * before being reverified (in case of long-running processes that
+ * keep a cache in memory and continue to use it for a long time).
+ */
+#define BLKID_PROBE_INTERVAL	200
+
+/* This describes an entire blkid cache file and probed devices.
+ * We can traverse all of the found devices via bic_list.
+ * We can traverse all of the tag types by bic_tags, which hold empty tags
+ * for each tag type.  Those tags can be used as list_heads for iterating
+ * through all devices with a specific tag type (e.g. LABEL).
+ */
+struct blkid_struct_cache
+{
+	struct list_head	bic_devs;	/* List head of all devices */
+	struct list_head	bic_tags;	/* List head of all tag types */
+	time_t			bic_time;	/* Last probe time */
+	time_t			bic_ftime;	/* Mod time of the cachefile */
+	unsigned int		bic_flags;	/* Status flags of the cache */
+	char			*bic_filename;	/* filename of cache */
+	blkid_probe		probe;		/* low-level probing stuff */
+};
+
+#define BLKID_BIC_FL_PROBED	0x0002	/* We probed /proc/partition devices */
+#define BLKID_BIC_FL_CHANGED	0x0004	/* Cache has changed from disk */
+
+/* config file */
+#define BLKID_CONFIG_FILE	"/etc/blkid.conf"
+
+/* cache file on systemds with /run */
+#define BLKID_RUNTIME_TOPDIR	"/run"
+#define BLKID_RUNTIME_DIR	BLKID_RUNTIME_TOPDIR "/blkid"
+#define BLKID_CACHE_FILE	BLKID_RUNTIME_DIR "/blkid.tab"
+
+/* old systems */
+#define BLKID_CACHE_FILE_OLD	"/etc/blkid.tab"
+
+#define BLKID_ERR_IO	 5
+#define BLKID_ERR_PROC	 9
+#define BLKID_ERR_MEM	12
+#define BLKID_ERR_CACHE	14
+#define BLKID_ERR_DEV	19
+#define BLKID_ERR_PARAM	22
+#define BLKID_ERR_BIG	27
+
+/*
+ * Priority settings for different types of devices
+ */
+#define BLKID_PRI_UBI	50
+#define BLKID_PRI_DM	40
+#define BLKID_PRI_EVMS	30
+#define BLKID_PRI_LVM	20
+#define BLKID_PRI_MD	10
+
+#if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG)
+#define CONFIG_BLKID_DEBUG
+#endif
+
+#define DEBUG_CACHE	0x0001
+#define DEBUG_DUMP	0x0002
+#define DEBUG_DEV	0x0004
+#define DEBUG_DEVNAME	0x0008
+#define DEBUG_DEVNO	0x0010
+#define DEBUG_PROBE	0x0020
+#define DEBUG_READ	0x0040
+#define DEBUG_RESOLVE	0x0080
+#define DEBUG_SAVE	0x0100
+#define DEBUG_TAG	0x0200
+#define DEBUG_LOWPROBE	0x0400
+#define DEBUG_CONFIG	0x0800
+#define DEBUG_EVALUATE	0x1000
+#define DEBUG_INIT	0x8000
+#define DEBUG_ALL	0xFFFF
+
+#ifdef CONFIG_BLKID_DEBUG
+extern int blkid_debug_mask;
+extern void blkid_init_debug(int mask);
+extern void blkid_debug_dump_dev(blkid_dev dev);
+extern void blkid_debug_dump_tag(blkid_tag tag);
+
+#define DBG(m,x)	do { if ((m) & blkid_debug_mask) x; } while (0)
+
+#else /* !CONFIG_BLKID_DEBUG */
+#define DBG(m,x)
+#define blkid_init_debug(x)
+#endif /* CONFIG_BLKID_DEBUG */
+
+/* devno.c */
+struct dir_list {
+	char	*name;
+	struct dir_list *next;
+};
+extern void blkid__scan_dir(char *, dev_t, struct dir_list **, char **)
+			__attribute__((nonnull(1,4)));
+extern int blkid_driver_has_major(const char *drvname, int major)
+			__attribute__((warn_unused_result));
+
+/* lseek.c */
+extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence);
+
+/* read.c */
+extern void blkid_read_cache(blkid_cache cache)
+			__attribute__((nonnull));
+
+/* save.c */
+extern int blkid_flush_cache(blkid_cache cache)
+			__attribute__((nonnull));
+
+/* cache */
+extern char *blkid_safe_getenv(const char *arg)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern char *blkid_get_cache_filename(struct blkid_config *conf)
+			__attribute__((warn_unused_result));
+/*
+ * Functions to create and find a specific tag type: tag.c
+ */
+extern void blkid_free_tag(blkid_tag tag);
+extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern int blkid_set_tag(blkid_dev dev, const char *name,
+			 const char *value, const int vlength)
+			__attribute__((nonnull(1,2)));
+
+/*
+ * Functions to create and find a specific tag type: dev.c
+ */
+extern blkid_dev blkid_new_dev(void)
+			__attribute__((warn_unused_result));
+extern void blkid_free_dev(blkid_dev dev);
+
+/* probe.c */
+extern int blkid_probe_is_tiny(blkid_probe pr)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+extern int blkid_probe_is_cdrom(blkid_probe pr)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern unsigned char *blkid_probe_get_buffer(blkid_probe pr,
+                                blkid_loff_t off, blkid_loff_t len)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern int blkid_probe_get_dimension(blkid_probe pr,
+	                blkid_loff_t *off, blkid_loff_t *size)
+			__attribute__((nonnull));
+
+extern int blkid_probe_set_dimension(blkid_probe pr,
+	                blkid_loff_t off, blkid_loff_t size)
+			__attribute__((nonnull));
+
+extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
+			blkid_loff_t *offset, const struct blkid_idmag **res)
+			__attribute__((nonnull(1)));
+
+/* returns superblok according to 'struct blkid_idmag' */
+#define blkid_probe_get_sb(_pr, _mag, type) \
+			((type *) blkid_probe_get_buffer((_pr),\
+					(_mag)->kboff << 10, sizeof(type)))
+
+extern blkid_partlist blkid_probe_get_partlist(blkid_probe pr)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern int blkid_probe_is_covered_by_pt(blkid_probe pr,
+					blkid_loff_t offset, blkid_loff_t size)
+			__attribute__((warn_unused_result));
+
+extern void blkid_probe_chain_reset_vals(blkid_probe pr, struct blkid_chain *chn)
+			__attribute__((nonnull));
+extern int blkid_probe_chain_copy_vals(blkid_probe pr,
+				       struct blkid_chain *chn,
+			               struct blkid_prval *vals,
+				       int nvals)
+			__attribute__((nonnull));
+
+extern struct blkid_prval *blkid_probe_assign_value(blkid_probe pr,
+					const char *name)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern int blkid_probe_reset_last_value(blkid_probe pr)
+			__attribute__((nonnull));
+extern void blkid_probe_append_vals(blkid_probe pr,
+				    struct blkid_prval *vals,
+				    int nvals)
+			__attribute__((nonnull));
+
+extern struct blkid_chain *blkid_probe_get_chain(blkid_probe pr)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern struct blkid_prval *__blkid_probe_get_value(blkid_probe pr, int num)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern unsigned long *blkid_probe_get_filter(blkid_probe pr, int chain, int create)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern int __blkid_probe_invert_filter(blkid_probe pr, int chain)
+			__attribute__((nonnull));
+extern int __blkid_probe_reset_filter(blkid_probe pr, int chain)
+			__attribute__((nonnull));
+extern int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[])
+			__attribute__((nonnull));
+
+extern void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+
+extern int blkid_probe_set_value(blkid_probe pr, const char *name,
+				unsigned char *data, size_t len)
+			__attribute__((nonnull));
+
+extern int blkid_probe_vsprintf_value(blkid_probe pr, const char *name,
+				const char *fmt, va_list ap)
+			__attribute__((nonnull));
+
+extern int blkid_probe_sprintf_value(blkid_probe pr, const char *name,
+				const char *fmt, ...)
+			__attribute__((nonnull))
+			__attribute__ ((__format__ (__printf__, 3, 4)));
+
+extern int blkid_probe_set_magic(blkid_probe pr, blkid_loff_t offset,
+				size_t len, unsigned char *magic)
+			__attribute__((nonnull));
+
+extern void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len)
+			__attribute__((nonnull));
+extern size_t blkid_rtrim_whitespace(unsigned char *str)
+			__attribute__((nonnull));
+extern size_t blkid_ltrim_whitespace(unsigned char *str)
+			__attribute__((nonnull));
+
+extern void blkid_probe_set_wiper(blkid_probe pr, blkid_loff_t off,
+				  blkid_loff_t size)
+			__attribute__((nonnull));
+extern int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn,
+		                blkid_loff_t off, blkid_loff_t size)
+			__attribute__((nonnull))
+			__attribute__((warn_unused_result));
+extern void blkid_probe_use_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size)
+			__attribute__((nonnull));
+
+/* filter bitmap macros */
+#define blkid_bmp_wordsize		(8 * sizeof(unsigned long))
+#define blkid_bmp_idx_bit(item)		(1UL << ((item) % blkid_bmp_wordsize))
+#define blkid_bmp_idx_byte(item)	((item) / blkid_bmp_wordsize)
+
+#define blkid_bmp_set_item(bmp, item)	\
+		((bmp)[ blkid_bmp_idx_byte(item) ] |= blkid_bmp_idx_bit(item))
+
+#define blkid_bmp_unset_item(bmp, item)	\
+		((bmp)[ blkid_bmp_idx_byte(item) ] &= ~blkid_bmp_idx_bit(item))
+
+#define blkid_bmp_get_item(bmp, item)	\
+		((bmp)[ blkid_bmp_idx_byte(item) ] & blkid_bmp_idx_bit(item))
+
+#define blkid_bmp_nwords(max_items) \
+		(((max_items) + blkid_bmp_wordsize) / blkid_bmp_wordsize)
+
+#define blkid_bmp_nbytes(max_items) \
+		(blkid_bmp_nwords(max_items) * sizeof(unsigned long))
+
+/* encode.c */
+extern size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len,
+				const unsigned char *src, size_t count)
+			__attribute__((nonnull));
+
+#define BLKID_ENC_UTF16BE	0
+#define BLKID_ENC_UTF16LE	1
+
+#endif /* _BLKID_BLKIDP_H */
diff --git a/libblkid/blkid_parttypes.h b/libblkid/blkid_parttypes.h
new file mode 100644
index 0000000..b0aad86
--- /dev/null
+++ b/libblkid/blkid_parttypes.h
@@ -0,0 +1,121 @@
+/*
+ * Partition types
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/* Note, _L32M means <32M (less), for example FAT16_L32M */
+
+enum {
+	BLKID_EMPTY_PARTITION			= 0x00,
+	BLKID_FAT12_PARTITION			= 0x01,
+	BLKID_XENIX_ROOT_PARTITION		= 0x02,
+	BLKID_XENIX_USR_PARTITION		= 0x03,
+	BLKID_FAT16_LESS32M_PARTITION		= 0x04,
+	BLKID_DOS_EXTENDED_PARTITION		= 0x05,
+	BLKID_FAT16_PARTITION			= 0x06, /* DOS 16-bit >=32M */
+	BLKID_HPFS_NTFS_PARTITION		= 0x07, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+	BLKID_AIX_PARTITION			= 0x08, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+	BLKID_AIX_BOOTABLE_PARTITION		= 0x09, /* AIX data or Coherent */
+	BLKID_OS2_BOOTMNGR_PARTITION		= 0x0a, /* OS/2 Boot Manager */
+	BLKID_W95_FAT32_PARTITION		= 0x0b,
+	BLKID_W95_FAT32_LBA_PARTITION		= 0x0c, /* LBA really is `Extended Int 13h' */
+	BLKID_W95_FAT16_LBA_PARTITION		= 0x0e,
+	BLKID_W95_EXTENDED_PARTITION		= 0x0f,
+	BLKID_OPUS_PARTITION			= 0x10,
+	BLKID_HIDDEN_FAT12_PARTITION		= 0x11,
+	BLKID_COMPAQ_DIAGNOSTICS_PARTITION	= 0x12,
+	BLKID_HIDDEN_FAT16_L32M_PARTITION	= 0x14,
+	BLKID_HIDDEN_FAT16_PARTITION		= 0x16,
+	BLKID_HIDDEN_HPFS_NTFS_PARTITION	= 0x17,
+	BLKID_AST_SMARTSLEEP_PARTITION		= 0x18,
+	BLKID_HIDDEN_W95_FAT32_PARTITION	= 0x1b,
+	BLKID_HIDDEN_W95_FAT32LBA_PARTITION	= 0x1c,
+	BLKID_HIDDEN_W95_FAT16LBA_PARTITION	= 0x1e,
+	BLKID_NEC_DOS_PARTITION			= 0x24,
+	BLKID_PLAN9_PARTITION			= 0x39,
+	BLKID_PARTITIONMAGIC_PARTITION		= 0x3c,
+	BLKID_VENIX80286_PARTITION		= 0x40,
+	BLKID_PPC_PREP_BOOT_PARTITION		= 0x41,
+	BLKID_SFS_PARTITION			= 0x42,
+	BLKID_QNX_4X_PARTITION			= 0x4d,
+	BLKID_QNX_4X_2ND_PARTITION		= 0x4e,
+	BLKID_QNX_4X_3RD_PARTITION		= 0x4f,
+	BLKID_DM_PARTITION			= 0x50,
+	BLKID_DM6_AUX1_PARTITION		= 0x51, /* (or Novell) */
+	BLKID_CPM_PARTITION			= 0x52, /* CP/M or Microport SysV/AT */
+	BLKID_DM6_AUX3_PARTITION		= 0x53,
+	BLKID_DM6_PARTITION			= 0x54,
+	BLKID_EZ_DRIVE_PARTITION		= 0x55,
+	BLKID_GOLDEN_BOW_PARTITION		= 0x56,
+	BLKID_PRIAM_EDISK_PARTITION		= 0x5c,
+	BLKID_SPEEDSTOR_PARTITION		= 0x61,
+	BLKID_GNU_HURD_PARTITION		= 0x63, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+	BLKID_UNIXWARE_PARTITION		= BLKID_GNU_HURD_PARTITION,
+	BLKID_NETWARE_286_PARTITION		= 0x64,
+	BLKID_NETWARE_386_PARTITION		= 0x65,
+	BLKID_DISKSECURE_MULTIBOOT_PARTITION	= 0x70,
+	BLKID_PC_IX_PARTITION			= 0x75,
+	BLKID_OLD_MINIX_PARTITION		= 0x80, /* Minix 1.4a and earlier */
+	BLKID_MINIX_PARTITION			= 0x81, /* Minix 1.4b and later */
+	BLKID_LINUX_SWAP_PARTITION		= 0x82,
+	BLKID_SOLARIS_X86_PARTITION		= BLKID_LINUX_SWAP_PARTITION,
+	BLKID_LINUX_DATA_PARTITION		= 0x83,
+	BLKID_OS2_HIDDEN_DRIVE_PARTITION	= 0x84,
+	BLKID_LINUX_EXTENDED_PARTITION		= 0x85,
+	BLKID_NTFS_VOL_SET1_PARTITION		= 0x86,
+	BLKID_NTFS_VOL_SET2_PARTITION		= 0x87,
+	BLKID_LINUX_PLAINTEXT_PARTITION		= 0x88,
+	BLKID_LINUX_LVM_PARTITION		= 0x8e,
+	BLKID_AMOEBA_PARTITION			= 0x93,
+	BLKID_AMOEBA_BBT_PARTITION		= 0x94, /* (bad block table) */
+	BLKID_BSD_OS_PARTITION			= 0x9f, /* BSDI */
+	BLKID_THINKPAD_HIBERNATION_PARTITION	= 0xa0,
+	BLKID_FREEBSD_PARTITION			= 0xa5, /* various BSD flavours */
+	BLKID_OPENBSD_PARTITION			= 0xa6,
+	BLKID_NEXTSTEP_PARTITION		= 0xa7,
+	BLKID_DARWIN_UFS_PARTITION		= 0xa8,
+	BLKID_NETBSD_PARTITION			= 0xa9,
+	BLKID_DARWIN_BOOT_PARTITION		= 0xab,
+	BLKID_HFS_HFS_PARTITION			= 0xaf,
+	BLKID_BSDI_FS_PARTITION			= 0xb7,
+	BLKID_BSDI_SWAP_PARTITION		= 0xb8,
+	BLKID_BOOTWIZARD_HIDDEN_PARTITION	= 0xbb,
+	BLKID_SOLARIS_BOOT_PARTITION		= 0xbe,
+	BLKID_SOLARIS_PARTITION			= 0xbf,
+	BLKID_DRDOS_FAT12_PARTITION		= 0xc1,
+	BLKID_DRDOS_FAT16_L32M_PARTITION	= 0xc4,
+	BLKID_DRDOS_FAT16_PARTITION		= 0xc6,
+	BLKID_SYRINX_PARTITION			= 0xc7,
+	BLKID_NONFS_DATA_PARTITION		= 0xda,
+	BLKID_CPM_CTOS_PARTITION		= 0xdb, /* CP/M or Concurrent CP/M or Concurrent DOS or CTOS */
+	BLKID_DELL_UTILITY_PARTITION		= 0xde, /* Dell PowerEdge Server utilities */
+	BLKID_BOOTIT_PARTITION			= 0xdf, /* BootIt EMBRM */
+	BLKID_DOS_ACCESS_PARTITION		= 0xe1, /* DOS access or SpeedStor 12-bit FAT extended partition */
+	BLKID_DOS_RO_PARTITION			= 0xe3, /* DOS R/O or SpeedStor */
+	BLKID_SPEEDSTOR_EXTENDED_PARTITION	= 0xe4, /* SpeedStor 16-bit FAT extended partition < 1024 cyl. */
+	BLKID_BEOS_FS_PARTITION			= 0xeb,
+	BLKID_GPT_PARTITION			= 0xee, /* Intel EFI GUID Partition Table */
+	BLKID_EFI_SYSTEM_PARTITION		= 0xef, /* Intel EFI System Partition */
+	BLKID_LINUX_PARISC_BOOT_PARTITION	= 0xf0, /* Linux/PA-RISC boot loader */
+	BLKID_SPEEDSTOR1_PARTITION		= 0xf1,
+	BLKID_SPEEDSTOR2_PARTITION		= 0xf4, /* SpeedStor large partition */
+	BLKID_DOS_SECONDARY_PARTITION		= 0xf2, /* DOS 3.3+ secondary */
+	BLKID_VMWARE_VMFS_PARTITION		= 0xfb,
+	BLKID_VMWARE_VMKCORE_PARTITION		= 0xfc, /* VMware kernel dump partition */
+	BLKID_LINUX_RAID_PARTITION		= 0xfd, /* New (2.2.x) raid partition with autodetect using persistent superblock */
+	BLKID_LANSTEP_PARTITION			= 0xfe, /* SpeedStor >1024 cyl. or LANstep */
+	BLKID_XENIX_BBT_PARTITION		= 0xff, /* Xenix Bad Block Table */
+};
diff --git a/libblkid/bsd.c b/libblkid/bsd.c
new file mode 100644
index 0000000..ee15ad2
--- /dev/null
+++ b/libblkid/bsd.c
@@ -0,0 +1,243 @@
+/*
+ * BSD/OSF partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Inspired by fdisk, partx, Linux kernel, libparted and openbsd header files.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+#define BSD_MAXPARTITIONS	16
+#define BSD_FS_UNUSED		0
+
+struct bsd_disklabel {
+	uint32_t	d_magic;		/* the magic number */
+	int16_t		d_type;			/* drive type */
+	int16_t		d_subtype;		/* controller/d_type specific */
+	char		d_typename[16];		/* type name, e.g. "eagle" */
+	char		d_packname[16];		/* pack identifier */
+
+			/* disk geometry: */
+	uint32_t	d_secsize;		/* # of bytes per sector */
+	uint32_t	d_nsectors;		/* # of data sectors per track */
+	uint32_t	d_ntracks;		/* # of tracks per cylinder */
+	uint32_t	d_ncylinders;		/* # of data cylinders per unit */
+	uint32_t	d_secpercyl;		/* # of data sectors per cylinder */
+	uint32_t	d_secperunit;		/* # of data sectors per unit */
+
+	/*
+	 * Spares (bad sector replacements) below
+	 * are not counted in d_nsectors or d_secpercyl.
+	 * Spare sectors are assumed to be physical sectors
+	 * which occupy space at the end of each track and/or cylinder.
+	 */
+	uint16_t	d_sparespertrack;	/* # of spare sectors per track */
+	uint16_t	d_sparespercyl;		/* # of spare sectors per cylinder */
+
+	/*
+	 * Alternate cylinders include maintenance, replacement,
+	 * configuration description areas, etc.
+	 */
+	uint32_t	d_acylinders;		/* # of alt. cylinders per unit */
+
+			/* hardware characteristics: */
+	/*
+	 * d_interleave, d_trackskew and d_cylskew describe perturbations
+	 * in the media format used to compensate for a slow controller.
+	 * Interleave is physical sector interleave, set up by the formatter
+	 * or controller when formatting.  When interleaving is in use,
+	 * logically adjacent sectors are not physically contiguous,
+	 * but instead are separated by some number of sectors.
+	 * It is specified as the ratio of physical sectors traversed
+	 * per logical sector.  Thus an interleave of 1:1 implies contiguous
+	 * layout, while 2:1 implies that logical sector 0 is separated
+	 * by one sector from logical sector 1.
+	 * d_trackskew is the offset of sector 0 on track N
+	 * relative to sector 0 on track N-1 on the same cylinder.
+	 * Finally, d_cylskew is the offset of sector 0 on cylinder N
+	 * relative to sector 0 on cylinder N-1.
+	 */
+	uint16_t	d_rpm;			/* rotational speed */
+	uint16_t	d_interleave;		/* hardware sector interleave */
+	uint16_t	d_trackskew;		/* sector 0 skew, per track */
+	uint16_t	d_cylskew;		/* sector 0 skew, per cylinder */
+	uint32_t	d_headswitch;		/* head switch time, usec */
+	uint32_t	d_trkseek;		/* track-to-track seek, usec */
+	uint32_t	d_flags;		/* generic flags */
+	uint32_t	d_drivedata[5];		/* drive-type specific information */
+	uint32_t	d_spare[5];		/* reserved for future use */
+	uint32_t	d_magic2;		/* the magic number (again) */
+	uint16_t	d_checksum;		/* xor of data incl. partitions */
+
+			/* filesystem and partition information: */
+	uint16_t	d_npartitions;	        /* number of partitions in following */
+	uint32_t	d_bbsize;	        /* size of boot area at sn0, bytes */
+	uint32_t	d_sbsize;	        /* max size of fs superblock, bytes */
+
+	struct bsd_partition	 {		/* the partition table */
+		uint32_t	p_size;	        /* number of sectors in partition */
+		uint32_t	p_offset;       /* starting sector */
+		uint32_t	p_fsize;        /* filesystem basic fragment size */
+		uint8_t		p_fstype;	/* filesystem type, see below */
+		uint8_t		p_frag;		/* filesystem fragments per block */
+		uint16_t	p_cpg;	        /* filesystem cylinders per group */
+	} __attribute__((packed)) d_partitions[BSD_MAXPARTITIONS];	/* actually may be more */
+} __attribute__((packed));
+
+
+/* Returns 'blkid_idmag' in 512-sectors */
+#define BLKID_MAG_SECTOR(_mag)  (((_mag)->kboff * 2)  + ((_mag)->sboff >> 9))
+
+/* Returns 'blkid_idmag' in bytes */
+#define BLKID_MAG_OFFSET(_mag)  ((_mag)->kboff >> 10) + ((_mag)->sboff)
+
+/* Returns 'blkid_idmag' offset in bytes within the last sector */
+#define BLKID_MAG_LASTOFFSET(_mag) \
+		 (BLKID_MAG_OFFSET(_mag) - (BLKID_MAG_SECTOR(_mag) << 9))
+
+static int probe_bsd_pt(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct bsd_disklabel *l;
+	struct bsd_partition *p;
+	const char *name = "bsd" ;
+	blkid_parttable tab = NULL;
+	blkid_partition parent;
+	blkid_partlist ls;
+	int i, nparts = BSD_MAXPARTITIONS;
+	unsigned char *data;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	data = blkid_probe_get_sector(pr, BLKID_MAG_SECTOR(mag));
+	if (!data)
+		goto nothing;
+
+	l = (struct bsd_disklabel *) data + BLKID_MAG_LASTOFFSET(mag);
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	/* try to determine the real type of BSD system according to
+	 * (parental) primary partition */
+	parent = blkid_partlist_get_parent(ls);
+	if (parent) {
+		switch(blkid_partition_get_type(parent)) {
+		case BLKID_FREEBSD_PARTITION:
+			name = "freebsd";
+			break;
+		case BLKID_NETBSD_PARTITION:
+			name = "netbsd";
+			break;
+		case BLKID_OPENBSD_PARTITION:
+			name = "openbsd";
+			break;
+		default:
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: BSD label detected on unknown (0x%x) "
+				"primary partition\n",
+				blkid_partition_get_type(parent)));
+			break;
+		}
+	}
+
+	tab = blkid_partlist_new_parttable(ls, name, BLKID_MAG_OFFSET(mag));
+	if (!tab)
+		goto err;
+
+	if (le16_to_cpu(l->d_npartitions) < BSD_MAXPARTITIONS)
+		nparts = le16_to_cpu(l->d_npartitions);
+
+	else if (le16_to_cpu(l->d_npartitions) > BSD_MAXPARTITIONS)
+		DBG(DEBUG_LOWPROBE, printf(
+			"WARNING: ignore %d more BSD partitions\n",
+			le16_to_cpu(l->d_npartitions) - BSD_MAXPARTITIONS));
+
+	for (i = 0, p = l->d_partitions; i < nparts; i++, p++) {
+		blkid_partition par;
+		uint32_t start, size;
+
+		/* TODO: in fdisk-mode returns all non-zero (p_size) partitions */
+		if (p->p_fstype == BSD_FS_UNUSED)
+			continue;
+
+		start = le32_to_cpu(p->p_offset);
+		size = le32_to_cpu(p->p_size);
+
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: BSD partition (%d) overflow "
+				"detected, ignore\n", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, p->p_fstype);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+/*
+ * All BSD variants use the same magic string (little-endian),
+ * and the same disklabel.
+ *
+ * The difference between {Free,Open,...}BSD is in the (parental)
+ * primary partition type.
+ *
+ * See also: http://en.wikipedia.org/wiki/BSD_disklabel
+ *
+ * The location of BSD disk label is architecture specific and in defined by
+ * LABELSECTOR and LABELOFFSET macros in the disklabel.h file. The location
+ * also depends on BSD variant, FreeBSD uses only one location, NetBSD and
+ * OpenBSD are more creative...
+ *
+ * The basic overview:
+ *
+ * arch                    | LABELSECTOR | LABELOFFSET
+ * ------------------------+-------------+------------
+ * amd64 arm hppa hppa64   |             |
+ * i386, macppc, mvmeppc   |      1      |      0
+ * sgi, aviion, sh, socppc |             |
+ * ------------------------+-------------+------------
+ * alpha luna88k mac68k    |      0      |     64
+ * sparc(OpenBSD) vax      |             |
+ * ------------------------+-------------+------------
+ * spark64 sparc(NetBSD)   |      0      |    128
+ * ------------------------+-------------+------------
+ *
+ * ...and more (see http://fxr.watson.org/fxr/ident?v=NETBSD;i=LABELSECTOR)
+ *
+ */
+const struct blkid_idinfo bsd_pt_idinfo =
+{
+	.name		= "bsd",
+	.probefunc	= probe_bsd_pt,
+	.magics		=
+	{
+		{ .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 512 },
+		{ .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 64 },
+		{ .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 128 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/btrfs.c b/libblkid/btrfs.c
new file mode 100644
index 0000000..5813035
--- /dev/null
+++ b/libblkid/btrfs.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct btrfs_super_block {
+	uint8_t csum[32];
+	uint8_t fsid[16];
+	uint64_t bytenr;
+	uint64_t flags;
+	uint8_t magic[8];
+	uint64_t generation;
+	uint64_t root;
+	uint64_t chunk_root;
+	uint64_t log_root;
+	uint64_t log_root_transid;
+	uint64_t total_bytes;
+	uint64_t bytes_used;
+	uint64_t root_dir_objectid;
+	uint64_t num_devices;
+	uint32_t sectorsize;
+	uint32_t nodesize;
+	uint32_t leafsize;
+	uint32_t stripesize;
+	uint32_t sys_chunk_array_size;
+	uint64_t chunk_root_generation;
+	uint64_t compat_flags;
+	uint64_t compat_ro_flags;
+	uint64_t incompat_flags;
+	uint16_t csum_type;
+	uint8_t root_level;
+	uint8_t chunk_root_level;
+	uint8_t log_root_level;
+	struct btrfs_dev_item {
+		uint64_t devid;
+		uint64_t total_bytes;
+		uint64_t bytes_used;
+		uint32_t io_align;
+		uint32_t io_width;
+		uint32_t sector_size;
+		uint64_t type;
+		uint64_t generation;
+		uint64_t start_offset;
+		uint32_t dev_group;
+		uint8_t seek_speed;
+		uint8_t bandwidth;
+		uint8_t uuid[16];
+		uint8_t fsid[16];
+	} __attribute__ ((__packed__)) dev_item;
+	uint8_t label[256];
+} __attribute__ ((__packed__));
+
+static int probe_btrfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct btrfs_super_block *bfs;
+
+	if (mag->kboff > 64 && blkid_probe_ignore_backup(pr)) {
+		DBG(DEBUG_LOWPROBE, printf("btrfs: found backup superblock, ignore\n"));
+		return 1;
+	}
+
+	bfs = blkid_probe_get_sb(pr, mag, struct btrfs_super_block);
+	if (!bfs)
+		return -1;
+
+	if (*bfs->label)
+		blkid_probe_set_label(pr,
+				(unsigned char *) bfs->label,
+				sizeof(bfs->label));
+
+	blkid_probe_set_uuid(pr, bfs->fsid);
+	blkid_probe_set_uuid_as(pr, bfs->dev_item.uuid, "UUID_SUB");
+
+	return 0;
+}
+
+const struct blkid_idinfo btrfs_idinfo =
+{
+	.name		= "btrfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_btrfs,
+	.minsz		= 1024 * 1024,
+	.magics		=
+	{
+	  { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40, .kboff = 64 },
+	  { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40, .kboff = 64 * 1024 },
+	  { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40, .kboff = 256 * 1024 * 1024 },
+	  { NULL }
+	}
+};
+
diff --git a/libblkid/c.h b/libblkid/c.h
new file mode 100644
index 0000000..037a099
--- /dev/null
+++ b/libblkid/c.h
@@ -0,0 +1,280 @@
+/*
+ * Fundamental C definitions.
+ */
+
+#ifndef UTIL_LINUX_C_H
+#define UTIL_LINUX_C_H
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "blkid.h"
+#ifdef HAVE_ERR_H
+# include <err.h>
+#endif
+
+#ifndef HAVE_USLEEP
+# include <time.h>
+#endif
+
+/*
+ * Compiler specific stuff
+ */
+#ifndef __GNUC_PREREQ
+# if defined __GNUC__ && defined __GNUC_MINOR__
+#  define __GNUC_PREREQ(maj, min) \
+	((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+# else
+#  define __GNUC_PREREQ(maj, min) 0
+# endif
+#endif
+
+#ifdef __GNUC__
+
+/* &a[0] degrades to a pointer: a different type from an array */
+# define __must_be_array(a) \
+	UL_BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(__typeof__(a), __typeof__(&a[0])))
+
+# define ignore_result(x) ({ \
+	__typeof__(x) __dummy __attribute__((__unused__)) = (x); (void) __dummy; \
+})
+
+#else /* !__GNUC__ */
+# define __must_be_array(a)	0
+# define __attribute__(_arg_)
+# define ignore_result(x) ((void) (x))
+#endif /* !__GNUC__ */
+
+/*
+ * Function attributes
+ */
+#ifndef __ul_alloc_size
+# if __GNUC_PREREQ (4, 3)
+#  define __ul_alloc_size(s) __attribute__((alloc_size(s)))
+# else
+#  define __ul_alloc_size(s)
+# endif
+#endif
+
+#ifndef __ul_calloc_size
+# if __GNUC_PREREQ (4, 3)
+#  define __ul_calloc_size(n, s) __attribute__((alloc_size(n, s)))
+# else
+#  define __ul_calloc_size(n, s)
+# endif
+#endif
+
+/* Force a compilation error if condition is true, but also produce a
+ * result (of value 0 and type size_t), so the expression can be used
+ * e.g. in a structure initializer (or where-ever else comma expressions
+ * aren't permitted).
+ */
+#define UL_BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef min
+# define min(x, y) ({				\
+	__typeof__(x) _min1 = (x);		\
+	__typeof__(y) _min2 = (y);		\
+	(void) (&_min1 == &_min2);		\
+	_min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#ifndef max
+# define max(x, y) ({				\
+	__typeof__(x) _max1 = (x);		\
+	__typeof__(y) _max2 = (y);		\
+	(void) (&_max1 == &_max2);		\
+	_max1 > _max2 ? _max1 : _max2; })
+#endif
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({                       \
+	const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+#ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME
+# ifdef HAVE___PROGNAME
+extern char *__progname;
+#  define program_invocation_short_name __progname
+# else
+#  ifdef HAVE_GETEXECNAME
+#   define program_invocation_short_name \
+		prog_inv_sh_nm_from_file(getexecname(), 0)
+#  else
+#   define program_invocation_short_name \
+		prog_inv_sh_nm_from_file(__FILE__, 1)
+#  endif
+static char prog_inv_sh_nm_buf[256];
+static inline char *
+prog_inv_sh_nm_from_file(char *f, char stripext)
+{
+	char *t;
+
+	if ((t = strrchr(f, '/')) != NULL)
+		t++;
+	else
+		t = f;
+
+	strncpy(prog_inv_sh_nm_buf, t, sizeof(prog_inv_sh_nm_buf) - 1);
+	prog_inv_sh_nm_buf[sizeof(prog_inv_sh_nm_buf) - 1] = '\0';
+
+	if (stripext && (t = strrchr(prog_inv_sh_nm_buf, '.')) != NULL)
+		*t = '\0';
+
+	return prog_inv_sh_nm_buf;
+}
+# endif
+#endif
+
+
+#ifndef HAVE_ERR_H
+static inline void
+errmsg(char doexit, int excode, char adderr, const char *fmt, ...)
+{
+	fprintf(stderr, "%s: ", program_invocation_short_name);
+	if (fmt != NULL) {
+		va_list argp;
+		va_start(argp, fmt);
+		vfprintf(stderr, fmt, argp);
+		va_end(argp);
+		if (adderr)
+			fprintf(stderr, ": ");
+	}
+	if (adderr)
+		fprintf(stderr, "%m");
+	fprintf(stderr, "\n");
+	if (doexit)
+		exit(excode);
+}
+
+#ifndef HAVE_ERR
+# define err(E, FMT...) errmsg(1, E, 1, FMT)
+#endif
+
+#ifndef HAVE_ERRX
+# define errx(E, FMT...) errmsg(1, E, 0, FMT)
+#endif
+
+#ifndef HAVE_WARN
+# define warn(FMT...) errmsg(0, 0, 1, FMT)
+#endif
+
+#ifndef HAVE_WARNX
+# define warnx(FMT...) errmsg(0, 0, 0, FMT)
+#endif
+#endif /* !HAVE_ERR_H */
+
+
+static inline __attribute__((const)) int is_power_of_2(unsigned long num)
+{
+	return (num != 0 && ((num & (num - 1)) == 0));
+}
+
+#ifndef HAVE_LOFF_T
+typedef int64_t loff_t;
+#endif
+
+#if !defined(HAVE_DIRFD) && (!defined(HAVE_DECL_DIRFD) || HAVE_DECL_DIRFD == 0) && defined(HAVE_DIR_DD_FD)
+#include <sys/types.h>
+#include <dirent.h>
+static inline int dirfd(DIR *d)
+{
+	return d->dd_fd;
+}
+#endif
+
+/*
+ * Fallback defines for old versions of glibc
+ */
+#include <fcntl.h>
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0x0020
+#endif
+
+#ifndef IUTF8
+#define IUTF8 0040000
+#endif
+
+/*
+ * MAXHOSTNAMELEN replacement
+ */
+static inline size_t get_hostname_max(void)
+{
+	long len = sysconf(_SC_HOST_NAME_MAX);
+
+	if (0 < len)
+		return len;
+
+#ifdef MAXHOSTNAMELEN
+	return MAXHOSTNAMELEN;
+#elif HOST_NAME_MAX
+	return HOST_NAME_MAX;
+#endif
+	return 64;
+}
+
+/*
+ * Constant strings for usage() functions. For more info see
+ * Documentation/howto-usage-function.txt and sys-utils/arch.c
+ */
+#define USAGE_HEADER     _("\nUsage:\n")
+#define USAGE_OPTIONS    _("\nOptions:\n")
+#define USAGE_SEPARATOR  _("\n")
+#define USAGE_HELP       _(" -h, --help     display this help and exit\n")
+#define USAGE_VERSION    _(" -V, --version  output version information and exit\n")
+#define USAGE_MAN_TAIL(_man)   _("\nFor more details see %s.\n"), _man
+
+#define UTIL_LINUX_VERSION _("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING
+
+/*
+ * scanf modifiers for "strings allocation"
+ */
+#ifdef HAVE_SCANF_MS_MODIFIER
+#define UL_SCNsA	"%ms"
+#elif defined(HAVE_SCANF_AS_MODIFIER)
+#define UL_SCNsA	"%as"
+#endif
+
+/*
+ * seek stuff
+ */
+#ifndef SEEK_DATA
+# define SEEK_DATA	3
+#endif
+#ifndef SEEK_HOLE
+# define SEEK_HOLE	4
+#endif
+
+#endif /* UTIL_LINUX_C_H */
diff --git a/libblkid/cache.c b/libblkid/cache.c
new file mode 100644
index 0000000..cc2cf8e
--- /dev/null
+++ b/libblkid/cache.c
@@ -0,0 +1,275 @@
+/*
+ * cache.c - allocation/initialization/free routines for cache
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include "blkidP.h"
+#include "env.h"
+
+int blkid_debug_mask = 0;
+
+/**
+ * SECTION:cache
+ * @title: Cache
+ * @short_description: basic routines to work with libblkid cache
+ *
+ * Block device information is normally kept in a cache file blkid.tab and is
+ * verified to still be valid before being returned to the user (if the user has
+ * read permission on the raw block device, otherwise not).  The cache file also
+ * allows unprivileged users (normally anyone other than root, or those not in the
+ * "disk" group) to locate devices by label/id.  The standard location of the
+ * cache file can be overridden by the environment variable BLKID_FILE.
+ *
+ * In situations where one is getting information about a single known device, it
+ * does not impact performance whether the cache is used or not (unless you are
+ * not able to read the block device directly).  If you are dealing with multiple
+ * devices, use of the cache is highly recommended (even if empty) as devices will
+ * be scanned at most one time and the on-disk cache will be updated if possible.
+ * There is rarely a reason not to use the cache.
+ *
+ * In some cases (modular kernels), block devices are not even visible until after
+ * they are accessed the first time, so it is critical that there is some way to
+ * locate these devices without enumerating only visible devices, so the use of
+ * the cache file is required in this situation.
+ */
+
+#if 0 /* ifdef CONFIG_BLKID_DEBUG */
+static blkid_debug_dump_cache(int mask, blkid_cache cache)
+{
+	struct list_head *p;
+
+	if (!cache) {
+		printf("cache: NULL\n");
+		return;
+	}
+
+	printf("cache: time = %lu\n", cache->bic_time);
+	printf("cache: flags = 0x%08X\n", cache->bic_flags);
+
+	list_for_each(p, &cache->bic_devs) {
+		blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+		blkid_debug_dump_dev(dev);
+	}
+}
+#endif
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_init_debug(int mask)
+{
+	if (blkid_debug_mask & DEBUG_INIT)
+		return;
+
+	if (!mask)
+	{
+		char *dstr = getenv("LIBBLKID_DEBUG");
+
+		if (!dstr)
+			dstr = getenv("BLKID_DEBUG");	/* for backward compatibility */
+		if (dstr)
+			blkid_debug_mask = strtoul(dstr, 0, 0);
+	} else
+		blkid_debug_mask = mask;
+
+	if (blkid_debug_mask)
+		printf("libblkid: debug mask set to 0x%04x.\n", blkid_debug_mask);
+
+	blkid_debug_mask |= DEBUG_INIT;
+}
+#endif
+
+static const char *get_default_cache_filename(void)
+{
+	struct stat st;
+
+	if (stat(BLKID_RUNTIME_TOPDIR, &st) == 0 && S_ISDIR(st.st_mode))
+		return BLKID_CACHE_FILE;	/* cache in /run */
+
+	return BLKID_CACHE_FILE_OLD;	/* cache in /etc */
+}
+
+/* returns allocated path to cache */
+char *blkid_get_cache_filename(struct blkid_config *conf)
+{
+	char *filename;
+
+	filename = safe_getenv("BLKID_FILE");
+	if (filename)
+		filename = strdup(filename);
+	else if (conf)
+		filename = conf->cachefile ? strdup(conf->cachefile) : NULL;
+	else {
+		struct blkid_config *c = blkid_read_config(NULL);
+		if (!c)
+			filename = strdup(get_default_cache_filename());
+		else {
+			filename = c->cachefile;  /* already allocated */
+			c->cachefile = NULL;
+			blkid_free_config(c);
+		}
+	}
+	return filename;
+}
+
+/**
+ * blkid_get_cache:
+ * @cache: pointer to return cache handler
+ * @filename: path to the cache file or NULL for the default path
+ *
+ * Allocates and initialize librray cache handler.
+ *
+ * Returns: 0 on success or number less than zero in case of error.
+ */
+int blkid_get_cache(blkid_cache *ret_cache, const char *filename)
+{
+	blkid_cache cache;
+
+	if (!ret_cache)
+		return -BLKID_ERR_PARAM;
+
+	blkid_init_debug(0);
+
+	DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n",
+				filename ? filename : "default cache"));
+
+	if (!(cache = (blkid_cache) calloc(1, sizeof(struct blkid_struct_cache))))
+		return -BLKID_ERR_MEM;
+
+	INIT_LIST_HEAD(&cache->bic_devs);
+	INIT_LIST_HEAD(&cache->bic_tags);
+
+	if (filename && !*filename)
+		filename = NULL;
+	if (filename)
+		cache->bic_filename = strdup(filename);
+	else
+		cache->bic_filename = blkid_get_cache_filename(NULL);
+
+	blkid_read_cache(cache);
+	*ret_cache = cache;
+	return 0;
+}
+
+/**
+ * blkid_put_cache:
+ * @cache: cache handler
+ *
+ * Saves changes to cache file.
+ */
+void blkid_put_cache(blkid_cache cache)
+{
+	if (!cache)
+		return;
+
+	(void) blkid_flush_cache(cache);
+
+	DBG(DEBUG_CACHE, printf("freeing cache struct\n"));
+
+	/* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */
+
+	while (!list_empty(&cache->bic_devs)) {
+		blkid_dev dev = list_entry(cache->bic_devs.next,
+					   struct blkid_struct_dev,
+					    bid_devs);
+		blkid_free_dev(dev);
+	}
+
+	while (!list_empty(&cache->bic_tags)) {
+		blkid_tag tag = list_entry(cache->bic_tags.next,
+					   struct blkid_struct_tag,
+					   bit_tags);
+
+		while (!list_empty(&tag->bit_names)) {
+			blkid_tag bad = list_entry(tag->bit_names.next,
+						   struct blkid_struct_tag,
+						   bit_names);
+
+			DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n",
+						bad->bit_name, bad->bit_val));
+			blkid_free_tag(bad);
+		}
+		blkid_free_tag(tag);
+	}
+
+	blkid_free_probe(cache->probe);
+
+	free(cache->bic_filename);
+	free(cache);
+}
+
+/**
+ * blkid_gc_cache:
+ * @cache: cache handler
+ *
+ * Removes garbage (non-existing devices) from the cache.
+ */
+void blkid_gc_cache(blkid_cache cache)
+{
+	struct list_head *p, *pnext;
+	struct stat st;
+
+	if (!cache)
+		return;
+
+	list_for_each_safe(p, pnext, &cache->bic_devs) {
+		blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+		if (stat(dev->bid_name, &st) < 0) {
+			DBG(DEBUG_CACHE,
+			    printf("freeing %s\n", dev->bid_name));
+			blkid_free_dev(dev);
+			cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+		} else {
+			DBG(DEBUG_CACHE,
+			    printf("Device %s exists\n", dev->bid_name));
+		}
+	}
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(DEBUG_ALL);
+
+	if ((argc > 2)) {
+		fprintf(stderr, "Usage: %s [filename] \n", argv[0]);
+		exit(1);
+	}
+
+	if ((ret = blkid_get_cache(&cache, argv[1])) < 0) {
+		fprintf(stderr, "error %d parsing cache file %s\n", ret,
+			argv[1] ? argv[1] : blkid_get_cache_filename(NULL));
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	if ((ret = blkid_probe_all(cache)) < 0)
+		fprintf(stderr, "error probing devices\n");
+
+	blkid_put_cache(cache);
+
+	return ret;
+}
+#endif
diff --git a/libblkid/canonicalize.c b/libblkid/canonicalize.c
new file mode 100644
index 0000000..1e8aff4
--- /dev/null
+++ b/libblkid/canonicalize.c
@@ -0,0 +1,247 @@
+/*
+ * canonicalize.c -- canonicalize pathname by removing symlinks
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+/*
+ * This routine is part of libc.  We include it nevertheless,
+ * since the libc version has some security flaws.
+ *
+ * TODO: use canonicalize_file_name() when exist in glibc
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "canonicalize.h"
+
+#ifndef MAXSYMLINKS
+# define MAXSYMLINKS 256
+#endif
+
+static char *
+myrealpath(const char *path, char *resolved_path, int maxreslth) {
+	int readlinks = 0;
+	char *npath;
+	char link_path[PATH_MAX+1];
+	int n;
+	char *buf = NULL;
+
+	npath = resolved_path;
+
+	/* If it's a relative pathname use getcwd for starters. */
+	if (*path != '/') {
+		if (!getcwd(npath, maxreslth-2))
+			return NULL;
+		npath += strlen(npath);
+		if (npath[-1] != '/')
+			*npath++ = '/';
+	} else {
+		*npath++ = '/';
+		path++;
+	}
+
+	/* Expand each slash-separated pathname component. */
+	while (*path != '\0') {
+		/* Ignore stray "/" */
+		if (*path == '/') {
+			path++;
+			continue;
+		}
+		if (*path == '.' && (path[1] == '\0' || path[1] == '/')) {
+			/* Ignore "." */
+			path++;
+			continue;
+		}
+		if (*path == '.' && path[1] == '.' &&
+		    (path[2] == '\0' || path[2] == '/')) {
+			/* Backup for ".." */
+			path += 2;
+			while (npath > resolved_path+1 &&
+			       (--npath)[-1] != '/')
+				;
+			continue;
+		}
+		/* Safely copy the next pathname component. */
+		while (*path != '\0' && *path != '/') {
+			if (npath-resolved_path > maxreslth-2) {
+				errno = ENAMETOOLONG;
+				goto err;
+			}
+			*npath++ = *path++;
+		}
+
+		/* Protect against infinite loops. */
+		if (readlinks++ > MAXSYMLINKS) {
+			errno = ELOOP;
+			goto err;
+		}
+
+		/* See if last pathname component is a symlink. */
+		*npath = '\0';
+		n = readlink(resolved_path, link_path, PATH_MAX);
+		if (n < 0) {
+			/* EINVAL means the file exists but isn't a symlink. */
+			if (errno != EINVAL)
+				goto err;
+		} else {
+			int m;
+			char *newbuf;
+
+			/* Note: readlink doesn't add the null byte. */
+			link_path[n] = '\0';
+			if (*link_path == '/')
+				/* Start over for an absolute symlink. */
+				npath = resolved_path;
+			else
+				/* Otherwise back up over this component. */
+				while (*(--npath) != '/')
+					;
+
+			/* Insert symlink contents into path. */
+			m = strlen(path);
+			newbuf = malloc(m + n + 1);
+			if (!newbuf)
+				goto err;
+			memcpy(newbuf, link_path, n);
+			memcpy(newbuf + n, path, m + 1);
+			free(buf);
+			path = buf = newbuf;
+		}
+		*npath++ = '/';
+	}
+	/* Delete trailing slash but don't whomp a lone slash. */
+	if (npath != resolved_path+1 && npath[-1] == '/')
+		npath--;
+	/* Make sure it's null terminated. */
+	*npath = '\0';
+
+	free(buf);
+	return resolved_path;
+
+ err:
+	free(buf);
+	return NULL;
+}
+
+/*
+ * Converts private "dm-N" names to "/dev/mapper/<name>"
+ *
+ * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
+ * provides the real DM device names in /sys/block/<ptname>/dm/name
+ */
+char *
+canonicalize_dm_name(const char *ptname)
+{
+	FILE	*f;
+	size_t	sz;
+	char	path[256], name[256], *res = NULL;
+
+	snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);
+	if (!(f = fopen(path, "r")))
+		return NULL;
+
+	/* read "<name>\n" from sysfs */
+	if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
+		name[sz - 1] = '\0';
+		snprintf(path, sizeof(path), "/dev/mapper/%s", name);
+		res = strdup(path);
+	}
+	fclose(f);
+	return res;
+}
+
+char *
+canonicalize_path(const char *path)
+{
+	char canonical[PATH_MAX+2];
+	char *p;
+
+	if (path == NULL)
+		return NULL;
+
+	if (!myrealpath(path, canonical, PATH_MAX+1))
+		return strdup(path);
+
+
+	p = strrchr(canonical, '/');
+	if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) {
+		p = canonicalize_dm_name(p+1);
+		if (p)
+			return p;
+	}
+
+	return strdup(canonical);
+}
+
+char *
+canonicalize_path_restricted(const char *path)
+{
+	char canonical[PATH_MAX+2];
+	char *p = NULL;
+	int errsv;
+	uid_t euid;
+	gid_t egid;
+
+	if (path == NULL)
+		return NULL;
+
+	euid = geteuid();
+	egid = getegid();
+
+	/* drop permissions */
+	if (setegid(getgid()) < 0 || seteuid(getuid()) < 0)
+		return NULL;
+
+	errsv = errno = 0;
+
+	if (myrealpath(path, canonical, PATH_MAX+1)) {
+		p = strrchr(canonical, '/');
+		if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4)))
+			p = canonicalize_dm_name(p+1);
+		else
+			p = NULL;
+		if (!p)
+			p = strdup(canonical);
+	} else
+		errsv = errno;
+
+	/* restore */
+	if (setegid(egid) < 0 || seteuid(euid) < 0) {
+		free(p);
+		return NULL;
+	}
+
+	errno = errsv;
+	return p;
+}
+
+
+#ifdef TEST_PROGRAM_CANONICALIZE
+int main(int argc, char **argv)
+{
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	fprintf(stdout, "orig: %s\n", argv[1]);
+	fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));
+
+	exit(EXIT_SUCCESS);
+}
+#endif
diff --git a/libblkid/canonicalize.h b/libblkid/canonicalize.h
new file mode 100644
index 0000000..7a18aca
--- /dev/null
+++ b/libblkid/canonicalize.h
@@ -0,0 +1,21 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library Public License for more details.
+ */
+#ifndef CANONICALIZE_H
+#define CANONICALIZE_H
+
+#include "c.h"	/* for PATH_MAX */
+
+extern char *canonicalize_path(const char *path);
+extern char *canonicalize_path_restricted(const char *path);
+extern char *canonicalize_dm_name(const char *ptname);
+
+#endif /* CANONICALIZE_H */
diff --git a/libblkid/carefulputc.h b/libblkid/carefulputc.h
new file mode 100644
index 0000000..2d857eb
--- /dev/null
+++ b/libblkid/carefulputc.h
@@ -0,0 +1,29 @@
+#ifndef _CAREFUULPUTC_H
+#define _CAREFUULPUTC_H
+
+/* putc() for use in write and wall (that sometimes are sgid tty) */
+/* Avoid control characters in our locale, and also ASCII control characters.
+   Note that the locale of the recipient is unknown. */
+#include <stdio.h>
+#include <ctype.h>
+
+#define iso8859x_iscntrl(c) \
+	(((c) & 0x7f) < 0x20 || (c) == 0x7f)
+
+static inline int carefulputc(int c, FILE *fp) {
+	int ret;
+
+	if (c == '\007' || c == '\t' || c == '\r' || c == '\n' ||
+	    (!iso8859x_iscntrl(c) && (isprint(c) || isspace(c))))
+		ret = putc(c, fp);
+	else if ((c & 0x80) || !isprint(c^0x40))
+		ret = fprintf(fp, "\\%3o", (unsigned char) c);
+	else {
+		ret = putc('^', fp);
+		if (ret != EOF)
+			ret = putc(c^0x40, fp);
+	}
+	return (ret < 0) ? EOF : 0;
+}
+
+#endif  /*  _CAREFUULPUTC_H  */
diff --git a/libblkid/closestream.h b/libblkid/closestream.h
new file mode 100644
index 0000000..d61b83b
--- /dev/null
+++ b/libblkid/closestream.h
@@ -0,0 +1,51 @@
+#ifndef UTIL_LINUX_CLOSESTREAM_H
+#define UTIL_LINUX_CLOSESTREAM_H
+
+#include <stdio.h>
+#ifdef HAVE_STDIO_EXT_H
+#include <stdio_ext.h>
+#endif
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+
+#ifndef HAVE___FPENDING
+static inline int
+__fpending(FILE *stream __attribute__((__unused__)))
+{
+	return 0;
+}
+#endif
+
+static inline int
+close_stream(FILE * stream)
+{
+	const int some_pending = (__fpending(stream) != 0);
+	const int prev_fail = (ferror(stream) != 0);
+	const int fclose_fail = (fclose(stream) != 0);
+	if (prev_fail || (fclose_fail && (some_pending || errno != EBADF))) {
+		if (!fclose_fail)
+			errno = 0;
+		return EOF;
+	}
+	return 0;
+}
+
+/* Meant to be used atexit(close_stdout); */
+static inline void
+close_stdout(void)
+{
+	if (close_stream(stdout) != 0 && !(errno == EPIPE)) {
+		if (errno)
+			warn(_("write error"));
+		else
+			warnx(_("write error"));
+		_exit(EXIT_FAILURE);
+	}
+
+	if (close_stream(stderr) != 0)
+		_exit(EXIT_FAILURE);
+}
+
+#endif /* UTIL_LINUX_CLOSESTREAM_H */
diff --git a/libblkid/colors.c b/libblkid/colors.c
new file mode 100644
index 0000000..bb9c057
--- /dev/null
+++ b/libblkid/colors.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include "colors.h"
+
+static int ul_color_term_ok;
+
+int colors_init(void)
+{
+	ul_color_term_ok = isatty(STDOUT_FILENO);
+	return ul_color_term_ok;
+}
+
+void color_enable(const char *color_scheme)
+{
+	if (ul_color_term_ok)
+		fputs(color_scheme, stdout);
+}
+
+void color_disable(void)
+{
+	if (ul_color_term_ok)
+		fputs(UL_COLOR_RESET, stdout);
+}
diff --git a/libblkid/colors.h b/libblkid/colors.h
new file mode 100644
index 0000000..0eb1946
--- /dev/null
+++ b/libblkid/colors.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_COLORS_H
+#define UTIL_LINUX_COLORS_H
+
+#include <stdio.h>
+#include <unistd.h>
+
+#define UL_COLOR_RESET		"\033[0m"
+#define UL_COLOR_BOLD		"\033[1m"
+#define UL_COLOR_HALFBRIGHT	"\033[2m"
+#define UL_COLOR_UNDERSCORE	"\033[4m"
+#define UL_COLOR_BLINK		"\033[5m"
+#define UL_COLOR_REVERSE	"\033[7m"
+
+/* Standard colors */
+#define UL_COLOR_BLACK		"\033[30m"
+#define UL_COLOR_RED		"\033[31m"
+#define UL_COLOR_GREEN		"\033[32m"
+#define UL_COLOR_YELLOW		"\033[33m"
+#define UL_COLOR_BLUE		"\033[34m"
+#define UL_COLOR_MAGENTA	"\033[35m"
+#define UL_COLOR_CYAN		"\033[36m"
+#define UL_COLOR_GRAY		"\033[37m"
+
+/* Bold variants */
+#define UL_COLOR_DARK_GRAY	"\033[1;30m"
+#define UL_COLOR_BOLD_RED	"\033[1;31m"
+#define UL_COLOR_BOLD_GREEN	"\033[1;32m"
+#define UL_COLOR_BOLD_YELLOW	"\033[1;33m"
+#define UL_COLOR_BOLD_BLUE	"\033[1;34m"
+#define UL_COLOR_BOLD_MAGENTA	"\033[1;35m"
+#define UL_COLOR_BOLD_CYAN	"\033[1;36m"
+
+#define UL_COLOR_WHITE		"\033[1;37m"
+
+/* Initialize the global variable OUT_IS_TERM */
+extern int colors_init(void);
+
+/* Set the color to CLR_SCHEME */
+extern void color_enable(const char *clr_scheme);
+
+/* Reset colors to default */
+extern void color_disable(void);
+
+#endif /* UTIL_LINUX_COLORS_H */
diff --git a/libblkid/config.c b/libblkid/config.c
new file mode 100644
index 0000000..edad6cd
--- /dev/null
+++ b/libblkid/config.c
@@ -0,0 +1,201 @@
+/*
+ * config.c - blkid.conf routines
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdint.h>
+#include <stdarg.h>
+
+#include "blkidP.h"
+#include "env.h"
+
+static int parse_evaluate(struct blkid_config *conf, char *s)
+{
+	while(s && *s) {
+		char *sep;
+
+		if (conf->nevals >= __BLKID_EVAL_LAST)
+			goto err;
+		sep = strchr(s, ',');
+		if (sep)
+			*sep = '\0';
+		if (strcmp(s, "udev") == 0)
+			conf->eval[conf->nevals] = BLKID_EVAL_UDEV;
+		else if (strcmp(s, "scan") == 0)
+			conf->eval[conf->nevals] = BLKID_EVAL_SCAN;
+		else
+			goto err;
+		conf->nevals++;
+		if (sep)
+			s = sep + 1;
+		else
+			break;
+	}
+	return 0;
+err:
+	DBG(DEBUG_CONFIG, printf(
+		"config file: unknown evaluation method '%s'.\n", s));
+	return -1;
+}
+
+static int parse_next(FILE *fd, struct blkid_config *conf)
+{
+	char buf[BUFSIZ];
+	char *s;
+
+	/* read the next non-blank non-comment line */
+	do {
+		if (fgets (buf, sizeof(buf), fd) == NULL)
+			return feof(fd) ? 0 : -1;
+		s = strchr (buf, '\n');
+		if (!s) {
+			/* Missing final newline?  Otherwise extremely */
+			/* long line - assume file was corrupted */
+			if (feof(fd))
+				s = strchr (buf, '\0');
+			else {
+				DBG(DEBUG_CONFIG, fprintf(stderr,
+					"libblkid: config file: missing newline at line '%s'.\n",
+					buf));
+				return -1;
+			}
+		}
+		*s = '\0';
+		if (--s >= buf && *s == '\r')
+			*s = '\0';
+
+		s = buf;
+		while (*s == ' ' || *s == '\t')		/* skip space */
+			s++;
+
+	} while (*s == '\0' || *s == '#');
+
+	if (!strncmp(s, "SEND_UEVENT=", 12)) {
+		s += 13;
+		if (*s && !strcasecmp(s, "yes"))
+			conf->uevent = TRUE;
+		else if (*s)
+			conf->uevent = FALSE;
+	} else if (!strncmp(s, "CACHE_FILE=", 11)) {
+		s += 11;
+		if (*s)
+			conf->cachefile = strdup(s);
+	} else if (!strncmp(s, "EVALUATE=", 9)) {
+		s += 9;
+		if (*s && parse_evaluate(conf, s) == -1)
+			return -1;
+	} else {
+		DBG(DEBUG_CONFIG, printf(
+			"config file: unknown option '%s'.\n", s));
+		return -1;
+	}
+	return 0;
+}
+
+/* return real config data or built-in default */
+struct blkid_config *blkid_read_config(const char *filename)
+{
+	struct blkid_config *conf;
+	FILE *f;
+
+	if (!filename)
+		filename = safe_getenv("BLKID_CONF");
+	if (!filename)
+		filename = BLKID_CONFIG_FILE;
+
+	conf = (struct blkid_config *) calloc(1, sizeof(*conf));
+	if (!conf)
+		return NULL;
+	conf->uevent = -1;
+
+	DBG(DEBUG_CONFIG, fprintf(stderr,
+		"reading config file: %s.\n", filename));
+
+	f = fopen(filename, "r");
+	if (!f) {
+		DBG(DEBUG_CONFIG, fprintf(stderr,
+			"%s: does not exist, using built-in default\n", filename));
+		goto dflt;
+	}
+	while (!feof(f)) {
+		if (parse_next(f, conf)) {
+			DBG(DEBUG_CONFIG, fprintf(stderr,
+				"%s: parse error\n", filename));
+			goto err;
+		}
+	}
+dflt:
+	if (!conf->nevals) {
+		conf->eval[0] = BLKID_EVAL_UDEV;
+		conf->eval[1] = BLKID_EVAL_SCAN;
+		conf->nevals = 2;
+	}
+	if (!conf->cachefile)
+		conf->cachefile = strdup(BLKID_CACHE_FILE);
+	if (conf->uevent == -1)
+		conf->uevent = TRUE;
+	if (f)
+		fclose(f);
+	return conf;
+err:
+	free(conf);
+	fclose(f);
+	return NULL;
+}
+
+void blkid_free_config(struct blkid_config *conf)
+{
+	if (!conf)
+		return;
+	free(conf->cachefile);
+	free(conf);
+}
+
+#ifdef TEST_PROGRAM
+/*
+ * usage: tst_config [<filename>]
+ */
+int main(int argc, char *argv[])
+{
+	int i;
+	struct blkid_config *conf;
+	char *filename = NULL;
+
+	blkid_init_debug(DEBUG_ALL);
+
+	if (argc == 2)
+		filename = argv[1];
+
+	conf = blkid_read_config(filename);
+	if (!conf)
+		return EXIT_FAILURE;
+
+	printf("EVALUATE:    ");
+	for (i = 0; i < conf->nevals; i++)
+		printf("%s ", conf->eval[i] == BLKID_EVAL_UDEV ? "udev" : "scan");
+	printf("\n");
+
+	printf("SEND UEVENT: %s\n", conf->uevent ? "TRUE" : "FALSE");
+	printf("CACHE_FILE:  %s\n", conf->cachefile);
+
+	blkid_free_config(conf);
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/config.h b/libblkid/config.h
new file mode 100644
index 0000000..3b4b18b
--- /dev/null
+++ b/libblkid/config.h
@@ -0,0 +1,654 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Should chfn and chsh require the user to enter the password? */
+#define CHFN_CHSH_PASSWORD 1
+
+/* Define to 1 if translation of program messages to the user's native
+   language is requested. */
+#define ENABLE_NLS 1
+
+/* search path for fs helpers */
+#define FS_SEARCH_PATH "/sbin:/sbin/fs.d:/sbin/fs"
+
+/* Define to 1 if you have the <asm/io.h> header file. */
+/* #undef HAVE_ASM_IO_H */
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+#define HAVE_BYTESWAP_H 1
+
+/* Define to 1 if the system has the type `cpu_set_t'. */
+#define HAVE_CPU_SET_T 1
+
+/* Define to 1 if you have the <crypt.h> header file. */
+#define HAVE_CRYPT_H 1
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+   */
+#define HAVE_DCGETTEXT 1
+
+/* Define to 1 if you have the declaration of `ADDR_COMPAT_LAYOUT', and to 0
+   if you don't. */
+#define HAVE_DECL_ADDR_COMPAT_LAYOUT 1
+
+/* Define to 1 if you have the declaration of `ADDR_LIMIT_32BIT', and to 0 if
+   you don't. */
+#define HAVE_DECL_ADDR_LIMIT_32BIT 1
+
+/* Define to 1 if you have the declaration of `ADDR_LIMIT_3GB', and to 0 if
+   you don't. */
+#define HAVE_DECL_ADDR_LIMIT_3GB 1
+
+/* Define to 1 if you have the declaration of `ADDR_NO_RANDOMIZE', and to 0 if
+   you don't. */
+#define HAVE_DECL_ADDR_NO_RANDOMIZE 1
+
+/* Define to 1 if you have the declaration of `CPU_ALLOC', and to 0 if you
+   don't. */
+#define HAVE_DECL_CPU_ALLOC 1
+
+/* Define to 1 if you have the declaration of `dirfd', and to 0 if you don't.
+   */
+/* #undef HAVE_DECL_DIRFD */
+
+/* Define to 1 if you have the declaration of `FDPIC_FUNCPTRS', and to 0 if
+   you don't. */
+#define HAVE_DECL_FDPIC_FUNCPTRS 1
+
+/* Define to 1 if you have the declaration of `MMAP_PAGE_ZERO', and to 0 if
+   you don't. */
+#define HAVE_DECL_MMAP_PAGE_ZERO 1
+
+/* Define to 1 if you have the declaration of `READ_IMPLIES_EXEC', and to 0 if
+   you don't. */
+#define HAVE_DECL_READ_IMPLIES_EXEC 1
+
+/* Define to 1 if you have the declaration of `STICKY_TIMEOUTS', and to 0 if
+   you don't. */
+#define HAVE_DECL_STICKY_TIMEOUTS 1
+
+/* Define to 1 if you have the declaration of `UNAME26', and to 0 if you
+   don't. */
+#define HAVE_DECL_UNAME26 1
+
+/* Define to 1 if you have the declaration of `WHOLE_SECONDS', and to 0 if you
+   don't. */
+#define HAVE_DECL_WHOLE_SECONDS 1
+
+/* Define to 1 if you have the declaration of `_NL_TIME_WEEK_1STDAY', and to 0
+   if you don't. */
+#define HAVE_DECL__NL_TIME_WEEK_1STDAY 1
+
+/* Define to 1 if you have the `dirfd' function. */
+#define HAVE_DIRFD 1
+
+/* Define to 1 if `dd_fd' is a member of `DIR'. */
+/* #undef HAVE_DIR_DD_FD */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <endian.h> header file. */
+#define HAVE_ENDIAN_H 1
+
+/* Define to 1 if have **environ prototype */
+#define HAVE_ENVIRON_DECL 1
+
+/* Define to 1 if you have the `err' function. */
+#define HAVE_ERR 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the `errx' function. */
+#define HAVE_ERRX 1
+
+/* Define to 1 if you have the <err.h> header file. */
+#define HAVE_ERR_H 1
+
+/* Have valid fallocate() function */
+#define HAVE_FALLOCATE 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `fstatat' function. */
+#define HAVE_FSTATAT 1
+
+/* Define to 1 if you have the `fsync' function. */
+#define HAVE_FSYNC 1
+
+/* Define to 1 if you have the `futimens' function. */
+#define HAVE_FUTIMENS 1
+
+/* Define to 1 if you have the `getdomainname' function. */
+#define HAVE_GETDOMAINNAME 1
+
+/* Define to 1 if you have the `getdtablesize' function. */
+#define HAVE_GETDTABLESIZE 1
+
+/* Define to 1 if you have the `getexecname' function. */
+/* #undef HAVE_GETEXECNAME */
+
+/* Define to 1 if you have the `getmntinfo' function. */
+/* #undef HAVE_GETMNTINFO */
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getrlimit' function. */
+#define HAVE_GETRLIMIT 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Define if you have the iconv() function. */
+/* #undef HAVE_ICONV */
+
+/* Define to 1 if you have the `inotify_init' function. */
+#define HAVE_INOTIFY_INIT 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `ioperm' function. */
+#define HAVE_IOPERM 1
+
+/* Define to 1 if you have the `iopl' function. */
+#define HAVE_IOPL 1
+
+/* Define to 1 if you have the `jrand48' function. */
+#define HAVE_JRAND48 1
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the `audit' library (-laudit). */
+/* #undef HAVE_LIBAUDIT */
+
+/* Define to 1 if you have the -lblkid. */
+#define HAVE_LIBBLKID 1
+
+/* Define to 1 if you have the `cap-ng' library (-lcap-ng). */
+/* #undef HAVE_LIBCAP_NG */
+
+/* Do we need -lcrypt? */
+#define HAVE_LIBCRYPT 1
+
+/* Define to 1 if you have the `ncurses' library (-lncurses). */
+#define HAVE_LIBNCURSES 1
+
+/* Define to 1 if you have the `ncursesw' library (-lncursesw). */
+/* #undef HAVE_LIBNCURSESW */
+
+/* Define if SELinux is available */
+/* #undef HAVE_LIBSELINUX */
+
+/* Define to 1 if you have the `termcap' library (-ltermcap). */
+#define HAVE_LIBTERMCAP 1
+
+/* Define to 1 if you have the `udev' library (-ludev). */
+/* #undef HAVE_LIBUDEV */
+
+/* Define to 1 if you have the `user' library (-luser). */
+/* #undef HAVE_LIBUSER */
+
+/* Define to 1 if you have the `utempter' library (-lutempter). */
+/* #undef HAVE_LIBUTEMPTER */
+
+/* Define to 1 if you have the `util' library (-lutil). */
+#define HAVE_LIBUTIL 1
+
+/* Define to 1 if you have the -luuid. */
+#define HAVE_LIBUUID 1
+
+/* Define to 1 if you have the <linux/blkpg.h> header file. */
+#define HAVE_LINUX_BLKPG_H 1
+
+/* Define to 1 if you have the <linux/cdrom.h> header file. */
+#define HAVE_LINUX_CDROM_H 1
+
+/* Define to 1 if you have the <linux/compiler.h> header file. */
+/* #undef HAVE_LINUX_COMPILER_H */
+
+/* Define to 1 if you have the <linux/falloc.h> header file. */
+#define HAVE_LINUX_FALLOC_H 1
+
+/* Define to 1 if you have the <linux/fd.h> header file. */
+#define HAVE_LINUX_FD_H 1
+
+/* Define to 1 if you have the <linux/major.h> header file. */
+#define HAVE_LINUX_MAJOR_H 1
+
+/* Define to 1 if you have the <linux/raw.h> header file. */
+#define HAVE_LINUX_RAW_H 1
+
+/* Define to 1 if you have the <linux/tiocl.h> header file. */
+#define HAVE_LINUX_TIOCL_H 1
+
+/* Define to 1 if you have the <linux/version.h> header file. */
+#define HAVE_LINUX_VERSION_H 1
+
+/* Define to 1 if you have the <linux/watchdog.h> header file. */
+#define HAVE_LINUX_WATCHDOG_H 1
+
+/* Define to 1 if you have the `llseek' function. */
+#define HAVE_LLSEEK 1
+
+/* Define to 1 if have llseek prototype */
+/* #undef HAVE_LLSEEK_PROTOTYPE */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if the system has the type `loff_t'. */
+#define HAVE_LOFF_T 1
+
+/* Define to 1 if you have the `lseek64' function. */
+#define HAVE_LSEEK64 1
+
+/* Define to 1 if have lseek64 prototype */
+#define HAVE_LSEEK64_PROTOTYPE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mempcpy' function. */
+#define HAVE_MEMPCPY 0
+
+/* Define to 1 if you have the <mntent.h> header file. */
+#define HAVE_MNTENT_H 1
+
+/* Define to 1 if you have the `nanosleep' function. */
+#define HAVE_NANOSLEEP 1
+
+/* Define to 1 if you have the <ncursesw/ncurses.h> header file. */
+/* #undef HAVE_NCURSESW_NCURSES_H */
+
+/* Define to 1 if you have the <ncurses.h> header file. */
+#define HAVE_NCURSES_H 1
+
+/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
+/* #undef HAVE_NCURSES_NCURSES_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <net/if_dl.h> header file. */
+/* #undef HAVE_NET_IF_DL_H */
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define to 1 if you have the `openat' function. */
+#define HAVE_OPENAT 1
+
+/* Define to 1 if you have the <paths.h> header file. */
+#define HAVE_PATHS_H 1
+
+/* Define to 1 if you have the `personality' function. */
+#define HAVE_PERSONALITY 1
+
+/* Define to 1 if you have the `posix_fadvise' function. */
+#define HAVE_POSIX_FADVISE 1
+
+/* Define to 1 if you have the `prctl' function. */
+#define HAVE_PRCTL 1
+
+/* Define to 1 if you have the `prlimit' function. */
+#define HAVE_PRLIMIT 1
+
+/* Define if program_invocation_short_name is defined */
+#define HAVE_PROGRAM_INVOCATION_SHORT_NAME 1
+
+/* Define to 1 if you have the <pty.h> header file. */
+#define HAVE_PTY_H 1
+
+/* Define to 1 if you have the `rpmatch' function. */
+#define HAVE_RPMATCH 1
+
+/* Define if struct sockaddr contains sa_len */
+/* #undef HAVE_SA_LEN */
+
+/* Define to 1 if you have the `scandirat' function. */
+#define HAVE_SCANDIRAT 1
+
+/* scanf %as modifier */
+/* #undef HAVE_SCANF_AS_MODIFIER */
+
+/* scanf %ms modifier */
+#define HAVE_SCANF_MS_MODIFIER 1
+
+/* Define to 1 if you have the `secure_getenv' function. */
+/* #undef HAVE_SECURE_GETENV */
+
+/* Define to 1 if you have the <security/pam_misc.h> header file. */
+#define HAVE_SECURITY_PAM_MISC_H 1
+
+/* Define to 1 if you have the `setns' function. */
+#define HAVE_SETNS 1
+
+/* Define to 1 if you have the `setresgid' function. */
+#define HAVE_SETRESGID 1
+
+/* Define to 1 if you have the `setresuid' function. */
+#define HAVE_SETRESUID 1
+
+/* Define to 1 if you have the `sigqueue' function. */
+#define HAVE_SIGQUEUE 1
+
+/* Define to 1 if you have the <slang.h> header file. */
+/* #undef HAVE_SLANG_H */
+
+/* Define to 1 if you have the <slang/slang.h> header file. */
+/* #undef HAVE_SLANG_SLANG_H */
+
+/* Define to 1 if you have the <slang/slcurses.h> header file. */
+/* #undef HAVE_SLANG_SLCURSES_H */
+
+/* Define to 1 if you have the <slcurses.h> header file. */
+/* #undef HAVE_SLCURSES_H */
+
+/* Define to 1 if you have the `srandom' function. */
+#define HAVE_SRANDOM 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio_ext.h> header file. */
+#define HAVE_STDIO_EXT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strnchr' function. */
+/* #undef HAVE_STRNCHR */
+
+/* Define to 1 if you have the `strndup' function. */
+#define HAVE_STRNDUP 1
+
+/* Define to 1 if you have the `strnlen' function. */
+#define HAVE_STRNLEN 1
+
+/* Define to 1 if have strsignal function prototype */
+#define HAVE_STRSIGNAL_DECL 1
+
+/* Define to 1 if you have the `strtoull' function. */
+#define HAVE_STRTOULL 1
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+
+/* Define to 1 if `c_line' is a member of `struct termios'. */
+#define HAVE_STRUCT_TERMIOS_C_LINE 1
+
+/* Define to 1 if you have the `sysconf' function. */
+#define HAVE_SYSCONF 1
+
+/* Define to 1 if you have the <sys/disklabel.h> header file. */
+/* #undef HAVE_SYS_DISKLABEL_H */
+
+/* Define to 1 if you have the <sys/disk.h> header file. */
+/* #undef HAVE_SYS_DISK_H */
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+/* #undef HAVE_SYS_ENDIAN_H */
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/ioccom.h> header file. */
+/* #undef HAVE_SYS_IOCCOM_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/io.h> header file. */
+#define HAVE_SYS_IO_H 1
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+/* #undef HAVE_SYS_MKDEV_H */
+
+/* Define to 1 if you have the <sys/prctl.h> header file. */
+#define HAVE_SYS_PRCTL_H 1
+
+/* Define to 1 if you have the <sys/queue.h> header file. */
+#define HAVE_SYS_QUEUE_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+/* #undef HAVE_SYS_SOCKIO_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/swap.h> header file. */
+#define HAVE_SYS_SWAP_H 1
+
+/* Define to 1 if you have the <sys/syscall.h> header file. */
+#define HAVE_SYS_SYSCALL_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#define HAVE_SYS_UN_H 1
+
+/* Define to 1 if the target supports thread-local storage. */
+#define HAVE_TLS 1
+
+/* Does struct tm have a field tm_gmtoff? */
+#define HAVE_TM_GMTOFF 1
+
+/* Define to 1 if the system has the type `union semun'. */
+/* #undef HAVE_UNION_SEMUN */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unlinkat' function. */
+#define HAVE_UNLINKAT 1
+
+/* Define to 1 if you have the `unshare' function. */
+#define HAVE_UNSHARE 1
+
+/* Define to 1 if you have the `updwtmp' function. */
+#define HAVE_UPDWTMP 1
+
+/* Define to 1 if you have the `usleep' function. */
+#define HAVE_USLEEP 1
+
+/* Define to 1 if you want to use uuid daemon. */
+#define HAVE_UUIDD 1
+
+/* Define to 1 if you have the `warn' function. */
+#define HAVE_WARN 1
+
+/* Define to 1 if you have the `warnx' function. */
+#define HAVE_WARNX 1
+
+/* Do we have wide character support? */
+#define HAVE_WIDECHAR 1
+
+/* Define to 1 if you have the `__fpending' function. */
+#define HAVE___FPENDING 1
+
+/* Define if __progname is defined */
+#define HAVE___PROGNAME 1
+
+/* Define to 1 if you have the `__secure_getenv' function. */
+#define HAVE___SECURE_GETENV 1
+
+/* libblkid date string */
+#define LIBBLKID_DATE "04-Sep-2012"
+
+/* libblkid version string */
+#define LIBBLKID_VERSION "2.22.0"
+
+/* libmount version string */
+#define LIBMOUNT_VERSION "2.22.0"
+
+/* Should login chown /dev/vcsN? */
+/* #undef LOGIN_CHOWN_VCS */
+
+/* Should login stat() the mailbox? */
+/* #undef LOGIN_STAT_MAIL */
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Should chsh allow only shells in /etc/shells? */
+#define ONLY_LISTED_SHELLS 1
+
+/* Name of package */
+#define PACKAGE "util-linux"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "kzak@redhat.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "util-linux"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "util-linux 2.22.552-d48f6"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "util-linux"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL "http://www.kernel.org/pub/linux/utils/util-linux/"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.22.552-d48f6"
+
+/* Should pg ring the bell on invalid keys? */
+#define PG_BELL 1
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Is swapon() declared with two parameters? */
+#define SWAPON_HAS_TWO_ARGS 1
+
+/* Fallback syscall number for fallocate */
+/* #undef SYS_fallocate */
+
+/* Fallback syscall number for ioprio_get */
+/* #undef SYS_ioprio_get */
+
+/* Fallback syscall number for ioprio_set */
+/* #undef SYS_ioprio_set */
+
+/* Fallback syscall number for pivot_root */
+/* #undef SYS_pivot_root */
+
+/* Fallback syscall number for prlimit64 */
+/* #undef SYS_prlimit64 */
+
+/* Fallback syscall number for sched_getaffinity */
+/* #undef SYS_sched_getaffinity */
+
+/* Fallback syscall number for setns */
+/* #undef SYS_setns */
+
+/* Fallback syscall number for unshare */
+/* #undef SYS_unshare */
+
+/* Should uuidd support socket activation? */
+/* #undef USE_SOCKET_ACTIVATION */
+
+/* Should sulogin use a emergency mount of /dev and /proc? */
+/* #undef USE_SULOGIN_EMERGENCY_MOUNT */
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Should wall and write be installed setgid tty? */
+#define USE_TTY_GROUP 1
+
+/* Version number of package */
+#define VERSION "2.22.552-d48f6"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* #  undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+   code using `volatile' can become incorrect without. Disable with care. */
+/* #undef volatile */
diff --git a/libblkid/cpuset.c b/libblkid/cpuset.c
new file mode 100644
index 0000000..e5b6b9d
--- /dev/null
+++ b/libblkid/cpuset.c
@@ -0,0 +1,402 @@
+/*
+ * Terminology:
+ *
+ *	cpuset	- (libc) cpu_set_t data structure represents set of CPUs
+ *	cpumask	- string with hex mask (e.g. "0x00000001")
+ *	cpulist - string with CPU ranges (e.g. "0-3,5,7,8")
+ *
+ * Based on code from taskset.c and Linux kernel.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/syscall.h>
+
+#include "cpuset.h"
+#include "c.h"
+
+static inline int val_to_char(int v)
+{
+	if (v >= 0 && v < 10)
+		return '0' + v;
+	else if (v >= 10 && v < 16)
+		return ('a' - 10) + v;
+	else
+		return -1;
+}
+
+static inline int char_to_val(int c)
+{
+	int cl;
+
+	cl = tolower(c);
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	else if (cl >= 'a' && cl <= 'f')
+		return cl + (10 - 'a');
+	else
+		return -1;
+}
+
+static const char *nexttoken(const char *q,  int sep)
+{
+	if (q)
+		q = strchr(q, sep);
+	if (q)
+		q++;
+	return q;
+}
+
+/*
+ * Number of bits in a CPU bitmask on current system
+ */
+int get_max_number_of_cpus(void)
+{
+#ifdef SYS_sched_getaffinity
+	int n, cpus = 2048;
+	size_t setsize;
+	cpu_set_t *set = cpuset_alloc(cpus, &setsize, NULL);
+
+	if (!set)
+		return -1;	/* error */
+
+	for (;;) {
+		CPU_ZERO_S(setsize, set);
+
+		/* the library version does not return size of cpumask_t */
+		n = syscall(SYS_sched_getaffinity, 0, setsize, set);
+
+		if (n < 0 && errno == EINVAL && cpus < 1024 * 1024) {
+			cpuset_free(set);
+			cpus *= 2;
+			set = cpuset_alloc(cpus, &setsize, NULL);
+			if (!set)
+				return -1;	/* error */
+			continue;
+		}
+		cpuset_free(set);
+		return n * 8;
+	}
+#endif
+	return -1;
+}
+
+/*
+ * Allocates a new set for ncpus and returns size in bytes and size in bits
+ */
+cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits)
+{
+	cpu_set_t *set = CPU_ALLOC(ncpus);
+
+	if (!set)
+		return NULL;
+	if (setsize)
+		*setsize = CPU_ALLOC_SIZE(ncpus);
+	if (nbits)
+		*nbits = cpuset_nbits(CPU_ALLOC_SIZE(ncpus));
+	return set;
+}
+
+void cpuset_free(cpu_set_t *set)
+{
+	CPU_FREE(set);
+}
+
+#if !HAVE_DECL_CPU_ALLOC
+/* Please, use CPU_COUNT_S() macro. This is fallback */
+int __cpuset_count_s(size_t setsize, const cpu_set_t *set)
+{
+	int s = 0;
+	const __cpu_mask *p = set->__bits;
+	const __cpu_mask *end = &set->__bits[setsize / sizeof (__cpu_mask)];
+
+	while (p < end) {
+		__cpu_mask l = *p++;
+
+		if (l == 0)
+			continue;
+# if LONG_BIT > 32
+		l = (l & 0x5555555555555555ul) + ((l >> 1) & 0x5555555555555555ul);
+		l = (l & 0x3333333333333333ul) + ((l >> 2) & 0x3333333333333333ul);
+		l = (l & 0x0f0f0f0f0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0f0f0f0f0ful);
+		l = (l & 0x00ff00ff00ff00fful) + ((l >> 8) & 0x00ff00ff00ff00fful);
+		l = (l & 0x0000ffff0000fffful) + ((l >> 16) & 0x0000ffff0000fffful);
+		l = (l & 0x00000000fffffffful) + ((l >> 32) & 0x00000000fffffffful);
+# else
+		l = (l & 0x55555555ul) + ((l >> 1) & 0x55555555ul);
+		l = (l & 0x33333333ul) + ((l >> 2) & 0x33333333ul);
+		l = (l & 0x0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0ful);
+		l = (l & 0x00ff00fful) + ((l >> 8) & 0x00ff00fful);
+		l = (l & 0x0000fffful) + ((l >> 16) & 0x0000fffful);
+# endif
+		s += l;
+	}
+	return s;
+}
+#endif
+
+/*
+ * Returns human readable representation of the cpuset. The output format is
+ * a list of CPUs with ranges (for example, "0,1,3-9").
+ */
+char *cpulist_create(char *str, size_t len,
+			cpu_set_t *set, size_t setsize)
+{
+	size_t i;
+	char *ptr = str;
+	int entry_made = 0;
+	size_t max = cpuset_nbits(setsize);
+
+	for (i = 0; i < max; i++) {
+		if (CPU_ISSET_S(i, setsize, set)) {
+			int rlen;
+			size_t j, run = 0;
+			entry_made = 1;
+			for (j = i + 1; j < max; j++) {
+				if (CPU_ISSET_S(j, setsize, set))
+					run++;
+				else
+					break;
+			}
+			if (!run)
+				rlen = snprintf(ptr, len, "%zd,", i);
+			else if (run == 1) {
+				rlen = snprintf(ptr, len, "%zd,%zd,", i, i + 1);
+				i++;
+			} else {
+				rlen = snprintf(ptr, len, "%zd-%zd,", i, i + run);
+				i += run;
+			}
+			if (rlen < 0 || (size_t) rlen + 1 > len)
+				return NULL;
+			ptr += rlen;
+			if (rlen > 0 && len > (size_t) rlen)
+				len -= rlen;
+			else
+				len = 0;
+		}
+	}
+	ptr -= entry_made;
+	*ptr = '\0';
+
+	return str;
+}
+
+/*
+ * Returns string with CPU mask.
+ */
+char *cpumask_create(char *str, size_t len,
+			cpu_set_t *set, size_t setsize)
+{
+	char *ptr = str;
+	char *ret = NULL;
+	int cpu;
+
+	for (cpu = cpuset_nbits(setsize) - 4; cpu >= 0; cpu -= 4) {
+		char val = 0;
+
+		if (len == (size_t) (ptr - str))
+			break;
+
+		if (CPU_ISSET_S(cpu, setsize, set))
+			val |= 1;
+		if (CPU_ISSET_S(cpu + 1, setsize, set))
+			val |= 2;
+		if (CPU_ISSET_S(cpu + 2, setsize, set))
+			val |= 4;
+		if (CPU_ISSET_S(cpu + 3, setsize, set))
+			val |= 8;
+
+		if (!ret && val)
+			ret = ptr;
+		*ptr++ = val_to_char(val);
+	}
+	*ptr = '\0';
+	return ret ? ret : ptr - 1;
+}
+
+/*
+ * Parses string with CPUs mask.
+ */
+int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize)
+{
+	int len = strlen(str);
+	const char *ptr = str + len - 1;
+	int cpu = 0;
+
+	/* skip 0x, it's all hex anyway */
+	if (len > 1 && !memcmp(str, "0x", 2L))
+		str += 2;
+
+	CPU_ZERO_S(setsize, set);
+
+	while (ptr >= str) {
+		char val;
+
+		/* cpu masks in /sys uses comma as a separator */
+		if (*ptr == ',')
+			ptr--;
+
+		val = char_to_val(*ptr);
+		if (val == (char) -1)
+			return -1;
+		if (val & 1)
+			CPU_SET_S(cpu, setsize, set);
+		if (val & 2)
+			CPU_SET_S(cpu + 1, setsize, set);
+		if (val & 4)
+			CPU_SET_S(cpu + 2, setsize, set);
+		if (val & 8)
+			CPU_SET_S(cpu + 3, setsize, set);
+		len--;
+		ptr--;
+		cpu += 4;
+	}
+
+	return 0;
+}
+
+/*
+ * Parses string with list of CPU ranges.
+ * Returns 0 on success.
+ * Returns 1 on error.
+ * Returns 2 if fail is set and a cpu number passed in the list doesn't fit
+ * into the cpu_set. If fail is not set cpu numbers that do not fit are
+ * ignored and 0 is returned instead.
+ */
+int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
+{
+	size_t max = cpuset_nbits(setsize);
+	const char *p, *q;
+	int r = 0;
+
+	q = str;
+	CPU_ZERO_S(setsize, set);
+
+	while (p = q, q = nexttoken(q, ','), p) {
+		unsigned int a;	/* beginning of range */
+		unsigned int b;	/* end of range */
+		unsigned int s;	/* stride */
+		const char *c1, *c2;
+		char c;
+
+		if ((r = sscanf(p, "%u%c", &a, &c)) < 1)
+			return 1;
+		b = a;
+		s = 1;
+
+		c1 = nexttoken(p, '-');
+		c2 = nexttoken(p, ',');
+		if (c1 != NULL && (c2 == NULL || c1 < c2)) {
+			if ((r = sscanf(c1, "%u%c", &b, &c)) < 1)
+				return 1;
+			c1 = nexttoken(c1, ':');
+			if (c1 != NULL && (c2 == NULL || c1 < c2)) {
+				if ((r = sscanf(c1, "%u%c", &s, &c)) < 1)
+					return 1;
+				if (s == 0)
+					return 1;
+			}
+		}
+
+		if (!(a <= b))
+			return 1;
+		while (a <= b) {
+			if (fail && (a >= max))
+				return 2;
+			CPU_SET_S(a, setsize, set);
+			a += s;
+		}
+	}
+
+	if (r == 2)
+		return 1;
+	return 0;
+}
+
+#ifdef TEST_PROGRAM
+
+#include <getopt.h>
+
+int main(int argc, char *argv[])
+{
+	cpu_set_t *set;
+	size_t setsize, buflen, nbits;
+	char *buf, *mask = NULL, *range = NULL;
+	int ncpus = 2048, rc, c;
+
+	static const struct option longopts[] = {
+	    { "ncpus", 1, 0, 'n' },
+	    { "mask",  1, 0, 'm' },
+	    { "range", 1, 0, 'r' },
+	    { NULL,    0, 0, 0 }
+	};
+
+	while ((c = getopt_long(argc, argv, "n:m:r:", longopts, NULL)) != -1) {
+		switch(c) {
+		case 'n':
+			ncpus = atoi(optarg);
+			break;
+		case 'm':
+			mask = strdup(optarg);
+			break;
+		case 'r':
+			range = strdup(optarg);
+			break;
+		default:
+			goto usage_err;
+		}
+	}
+
+	if (!mask && !range)
+		goto usage_err;
+
+	set = cpuset_alloc(ncpus, &setsize, &nbits);
+	if (!set)
+		err(EXIT_FAILURE, "failed to allocate cpu set");
+
+	/*
+	fprintf(stderr, "ncpus: %d, cpuset bits: %zd, cpuset bytes: %zd\n",
+			ncpus, nbits, setsize);
+	*/
+
+	buflen = 7 * nbits;
+	buf = malloc(buflen);
+	if (!buf)
+		err(EXIT_FAILURE, "failed to allocate cpu set buffer");
+
+	if (mask)
+		rc = cpumask_parse(mask, set, setsize);
+	else
+		rc = cpulist_parse(range, set, setsize, 0);
+
+	if (rc)
+		errx(EXIT_FAILURE, "failed to parse string: %s", mask ? : range);
+
+	printf("%-15s = %15s ", mask ? : range,
+				cpumask_create(buf, buflen, set, setsize));
+	printf("[%s]\n", cpulist_create(buf, buflen, set, setsize));
+
+	free(buf);
+	free(range);
+	cpuset_free(set);
+
+	return EXIT_SUCCESS;
+
+usage_err:
+	fprintf(stderr,
+		"usage: %s [--ncpus <num>] --mask <mask> | --range <list>",
+		program_invocation_short_name);
+	exit(EXIT_FAILURE);
+}
+#endif
diff --git a/libblkid/cpuset.h b/libblkid/cpuset.h
new file mode 100644
index 0000000..f8948a9
--- /dev/null
+++ b/libblkid/cpuset.h
@@ -0,0 +1,99 @@
+/*
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_CPUSET_H
+#define UTIL_LINUX_CPUSET_H
+
+#include <sched.h>
+
+/*
+ * Fallback for old or obscure libcs without dynamically allocated cpusets
+ *
+ * The following macros are based on code from glibc.
+ *
+ * The GNU C Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+#if !HAVE_DECL_CPU_ALLOC
+
+# define CPU_ZERO_S(setsize, cpusetp) \
+  do {									      \
+    size_t __i;								      \
+    size_t __imax = (setsize) / sizeof (__cpu_mask);			      \
+    __cpu_mask *__bits = (cpusetp)->__bits;				      \
+    for (__i = 0; __i < __imax; ++__i)					      \
+      __bits[__i] = 0;							      \
+  } while (0)
+
+# define CPU_SET_S(cpu, setsize, cpusetp) \
+   ({ size_t __cpu = (cpu);						      \
+      __cpu < 8 * (setsize)						      \
+      ? (((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)]		      \
+	 |= __CPUMASK (__cpu))						      \
+      : 0; })
+
+# define CPU_ISSET_S(cpu, setsize, cpusetp) \
+   ({ size_t __cpu = (cpu);						      \
+      __cpu < 8 * (setsize)						      \
+      ? ((((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)]	      \
+	  & __CPUMASK (__cpu))) != 0					      \
+      : 0; })
+
+# define CPU_EQUAL_S(setsize, cpusetp1, cpusetp2) \
+   ({ __cpu_mask *__arr1 = (cpusetp1)->__bits;				      \
+      __cpu_mask *__arr2 = (cpusetp2)->__bits;				      \
+      size_t __imax = (setsize) / sizeof (__cpu_mask);			      \
+      size_t __i;							      \
+      for (__i = 0; __i < __imax; ++__i)				      \
+	if (__arr1[__i] != __arr2[__i])					      \
+	  break;							      \
+      __i == __imax; })
+
+extern int __cpuset_count_s(size_t setsize, const cpu_set_t *set);
+# define CPU_COUNT_S(setsize, cpusetp)	__cpuset_count_s(setsize, cpusetp)
+
+# define CPU_ALLOC_SIZE(count) \
+	  ((((count) + __NCPUBITS - 1) / __NCPUBITS) * sizeof (__cpu_mask))
+# define CPU_ALLOC(count)	(malloc(CPU_ALLOC_SIZE(count)))
+# define CPU_FREE(cpuset)	(free(cpuset))
+
+#endif /* !HAVE_DECL_CPU_ALLOC */
+
+
+#define cpuset_nbits(setsize)	(8 * (setsize))
+
+/*
+ * The @idx parametr returns an index of the first mask from @ary array where
+ * the @cpu is set.
+ *
+ * Returns: 0 if found, otherwise 1.
+ */
+static inline int cpuset_ary_isset(size_t cpu, cpu_set_t **ary, size_t nmemb,
+				   size_t setsize, size_t *idx)
+{
+	size_t i;
+
+	for (i = 0; i < nmemb; i++) {
+		if (CPU_ISSET_S(cpu, setsize, ary[i])) {
+			*idx = i;
+			return 0;
+		}
+	}
+	return 1;
+}
+
+extern int get_max_number_of_cpus(void);
+
+extern cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits);
+extern void cpuset_free(cpu_set_t *set);
+
+extern char *cpulist_create(char *str, size_t len, cpu_set_t *set, size_t setsize);
+extern int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail);
+
+extern char *cpumask_create(char *str, size_t len, cpu_set_t *set, size_t setsize);
+extern int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize);
+
+#endif /* UTIL_LINUX_CPUSET_H */
diff --git a/libblkid/cramfs.c b/libblkid/cramfs.c
new file mode 100644
index 0000000..b58ed08
--- /dev/null
+++ b/libblkid/cramfs.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct cramfs_super
+{
+	uint8_t		magic[4];
+	uint32_t	size;
+	uint32_t	flags;
+	uint32_t	future;
+	uint8_t		signature[16];
+	struct cramfs_info
+	{
+		uint32_t	crc;
+		uint32_t	edition;
+		uint32_t	blocks;
+		uint32_t	files;
+	} __attribute__((packed)) info;
+	uint8_t		name[16];
+} __attribute__((packed));
+
+static int probe_cramfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct cramfs_super *cs;
+
+	cs = blkid_probe_get_sb(pr, mag, struct cramfs_super);
+	if (!cs)
+		return -1;
+
+	blkid_probe_set_label(pr, cs->name, sizeof(cs->name));
+	return 0;
+}
+
+const struct blkid_idinfo cramfs_idinfo =
+{
+	.name		= "cramfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_cramfs,
+	.magics		=
+	{
+		{ "\x45\x3d\xcd\x28", 4, 0, 0 },
+		{ "\x28\xcd\x3d\x45", 4, 0, 0 },
+		{ NULL }
+	}
+};
+
+
diff --git a/libblkid/crc32.c b/libblkid/crc32.c
new file mode 100644
index 0000000..eaaa06a
--- /dev/null
+++ b/libblkid/crc32.c
@@ -0,0 +1,116 @@
+/*
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ *
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1.
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way,
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly.
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera-
+ *      tions for all combinations of data and CRC register values.
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc"
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions.
+ *      polynomial $edb88320
+ *
+ */
+
+#include <stdio.h>
+
+#include "crc32.h"
+
+
+static const uint32_t crc32_tab[] = {
+	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+	0x2d02ef8dL
+};
+
+/*
+ * This a generic crc32() function, it takes seed as an argument,
+ * and does __not__ xor at the end. Then individual users can do
+ * whatever they need.
+ */
+uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len)
+{
+	uint32_t crc = seed;
+	const unsigned char *p = buf;
+
+	while(len-- > 0)
+		crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+
+	return crc;
+}
+
diff --git a/libblkid/crc32.h b/libblkid/crc32.h
new file mode 100644
index 0000000..b454be9
--- /dev/null
+++ b/libblkid/crc32.h
@@ -0,0 +1,9 @@
+#ifndef UL_NG_CRC32_H
+#define UL_NG_CRC32_H
+
+#include <stdint.h>
+
+extern uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len);
+
+#endif
+
diff --git a/libblkid/ddf_raid.c b/libblkid/ddf_raid.c
new file mode 100644
index 0000000..24df421
--- /dev/null
+++ b/libblkid/ddf_raid.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/* http://www.snia.org/standards/home */
+#define DDF_GUID_LENGTH			24
+#define DDF_REV_LENGTH			8
+#define DDF_MAGIC			0xDE11DE11
+
+
+struct ddf_header {
+	uint32_t	signature;
+	uint32_t	crc;
+	uint8_t		guid[DDF_GUID_LENGTH];
+	char		ddf_rev[8];	/* 01.02.00 */
+	uint32_t	seq;		/* starts at '1' */
+	uint32_t	timestamp;
+	uint8_t		openflag;
+	uint8_t		foreignflag;
+	uint8_t		enforcegroups;
+	uint8_t		pad0;		/* 0xff */
+	uint8_t		pad1[12];	/* 12 * 0xff */
+	/* 64 bytes so far */
+	uint8_t		header_ext[32];	/* reserved: fill with 0xff */
+	uint64_t	primary_lba;
+	uint64_t	secondary_lba;
+	uint8_t		type;
+	uint8_t		pad2[3];	/* 0xff */
+	uint32_t	workspace_len;	/* sectors for vendor space -
+					 * at least 32768(sectors) */
+	uint64_t	workspace_lba;
+	uint16_t	max_pd_entries;	/* one of 15, 63, 255, 1023, 4095 */
+	uint16_t	max_vd_entries; /* 2^(4,6,8,10,12)-1 : i.e. as above */
+	uint16_t	max_partitions; /* i.e. max num of configuration
+					   record entries per disk */
+	uint16_t	config_record_len; /* 1 +ROUNDUP(max_primary_element_entries
+				           *12/512) */
+	uint16_t	max_primary_element_entries; /* 16, 64, 256, 1024, or 4096 */
+	uint8_t		pad3[54];	/* 0xff */
+	/* 192 bytes so far */
+	uint32_t	controller_section_offset;
+	uint32_t	controller_section_length;
+	uint32_t	phys_section_offset;
+	uint32_t	phys_section_length;
+	uint32_t	virt_section_offset;
+	uint32_t	virt_section_length;
+	uint32_t	config_section_offset;
+	uint32_t	config_section_length;
+	uint32_t	data_section_offset;
+	uint32_t	data_section_length;
+	uint32_t	bbm_section_offset;
+	uint32_t	bbm_section_length;
+	uint32_t	diag_space_offset;
+	uint32_t	diag_space_length;
+	uint32_t	vendor_offset;
+	uint32_t	vendor_length;
+	/* 256 bytes so far */
+	uint8_t		pad4[256];	/* 0xff */
+} __attribute__((packed));
+
+static int probe_ddf(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	int hdrs[] = { 1, 257 };
+	size_t i;
+	struct ddf_header *ddf = NULL;
+	char version[DDF_REV_LENGTH + 1];
+	uint64_t off, lba;
+
+	if (pr->size < 0x30000)
+		return -1;
+
+	for (i = 0; i < ARRAY_SIZE(hdrs); i++) {
+		off = ((pr->size / 0x200) - hdrs[i]) * 0x200;
+
+		ddf = (struct ddf_header *) blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct ddf_header));
+		if (!ddf)
+			return -1;
+
+		if (ddf->signature == cpu_to_be32(DDF_MAGIC) ||
+		    ddf->signature == cpu_to_le32(DDF_MAGIC))
+			break;
+		ddf = NULL;
+	}
+
+	if (!ddf)
+		return -1;
+
+	lba = ddf->signature == cpu_to_be32(DDF_MAGIC) ?
+			be64_to_cpu(ddf->primary_lba) :
+			le64_to_cpu(ddf->primary_lba);
+
+	if (lba > 0) {
+		/* check primary header */
+		unsigned char *buf;
+
+		buf = blkid_probe_get_buffer(pr,
+					lba << 9, sizeof(ddf->signature));
+		if (!buf || memcmp(buf, &ddf->signature, 4))
+			return -1;
+	}
+
+	blkid_probe_strncpy_uuid(pr, ddf->guid, sizeof(ddf->guid));
+
+	memcpy(version, ddf->ddf_rev, sizeof(ddf->ddf_rev));
+	*(version + sizeof(ddf->ddf_rev)) = '\0';
+
+	if (blkid_probe_set_version(pr, version) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off,
+			sizeof(ddf->signature),
+			(unsigned char *) &ddf->signature))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo ddfraid_idinfo = {
+	.name		= "ddf_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_ddf,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/dev.c b/libblkid/dev.c
new file mode 100644
index 0000000..62dfc24
--- /dev/null
+++ b/libblkid/dev.c
@@ -0,0 +1,272 @@
+/*
+ * dev.c - allocation/initialization/free routines for dev
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "blkidP.h"
+
+/*
+ * NOTE: reference manual is not structured as code. The following section is a generic
+ * section for all high-level cache search+iterate routines.
+ */
+
+/**
+ * SECTION:search
+ * @title: Search and iterate
+ * @short_description: search devices and iterate over devices in the cache.
+ *
+ * Note that high-level probing API provides information about superblocks
+ * (filesystems/raids) only.  For partitions and topology is necessary to use
+ * the low-level API.
+ */
+
+blkid_dev blkid_new_dev(void)
+{
+	blkid_dev dev;
+
+	if (!(dev = (blkid_dev) calloc(1, sizeof(struct blkid_struct_dev))))
+		return NULL;
+
+	INIT_LIST_HEAD(&dev->bid_devs);
+	INIT_LIST_HEAD(&dev->bid_tags);
+
+	return dev;
+}
+
+void blkid_free_dev(blkid_dev dev)
+{
+	if (!dev)
+		return;
+
+	DBG(DEBUG_DEV,
+	    printf("  freeing dev %s (%s)\n", dev->bid_name, dev->bid_type ?
+		   dev->bid_type : "(null)"));
+	DBG(DEBUG_DEV, blkid_debug_dump_dev(dev));
+
+	list_del(&dev->bid_devs);
+	while (!list_empty(&dev->bid_tags)) {
+		blkid_tag tag = list_entry(dev->bid_tags.next,
+					   struct blkid_struct_tag,
+					   bit_tags);
+		blkid_free_tag(tag);
+	}
+	free(dev->bid_name);
+	free(dev);
+}
+
+/*
+ * Given a blkid device, return its name
+ */
+extern const char *blkid_dev_devname(blkid_dev dev)
+{
+	return dev ? dev->bid_name : NULL;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_dev(blkid_dev dev)
+{
+	struct list_head *p;
+
+	if (!dev) {
+		printf("  dev: NULL\n");
+		return;
+	}
+
+	printf("  dev: name = %s\n", dev->bid_name);
+	printf("  dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno);
+	printf("  dev: TIME=\"%ld.%ld\"\n", (long)dev->bid_time, (long)dev->bid_utime);
+	printf("  dev: PRI=\"%d\"\n", dev->bid_pri);
+	printf("  dev: flags = 0x%08X\n", dev->bid_flags);
+
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+		if (tag)
+			printf("    tag: %s=\"%s\"\n", tag->bit_name,
+			       tag->bit_val);
+		else
+			printf("    tag: NULL\n");
+	}
+	printf("\n");
+}
+#endif
+
+/*
+ * dev iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implemenation.  I'm not convinced I want
+ * to keep list.h in the long term, anyway.  It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application.  [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all devices in a blkid cache
+ */
+#define DEV_ITERATE_MAGIC	0x01a5284c
+
+struct blkid_struct_dev_iterate {
+	int			magic;
+	blkid_cache		cache;
+	char			*search_type;
+	char			*search_value;
+	struct list_head	*p;
+};
+
+extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
+{
+	blkid_dev_iterate iter;
+
+	iter = malloc(sizeof(struct blkid_struct_dev_iterate));
+	if (iter) {
+		iter->magic = DEV_ITERATE_MAGIC;
+		iter->cache = cache;
+		iter->p	= cache->bic_devs.next;
+		iter->search_type = 0;
+		iter->search_value = 0;
+	}
+	return iter;
+}
+
+extern int blkid_dev_set_search(blkid_dev_iterate iter,
+				 char *search_type, char *search_value)
+{
+	char *new_type, *new_value;
+
+	if (!iter || iter->magic != DEV_ITERATE_MAGIC || !search_type ||
+	    !search_value)
+		return -1;
+	new_type = malloc(strlen(search_type)+1);
+	new_value = malloc(strlen(search_value)+1);
+	if (!new_type || !new_value) {
+		free(new_type);
+		free(new_value);
+		return -1;
+	}
+	strcpy(new_type, search_type);
+	strcpy(new_value, search_value);
+	free(iter->search_type);
+	free(iter->search_value);
+	iter->search_type = new_type;
+	iter->search_value = new_value;
+	return 0;
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_dev_next(blkid_dev_iterate iter,
+			  blkid_dev *ret_dev)
+{
+	blkid_dev		dev;
+
+	if  (!ret_dev || !iter || iter->magic != DEV_ITERATE_MAGIC)
+		return -1;
+	*ret_dev = 0;
+	while (iter->p != &iter->cache->bic_devs) {
+		dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs);
+		iter->p = iter->p->next;
+		if (iter->search_type &&
+		    !blkid_dev_has_tag(dev, iter->search_type,
+				       iter->search_value))
+			continue;
+		*ret_dev = dev;
+		return 0;
+	}
+	return -1;
+}
+
+extern void blkid_dev_iterate_end(blkid_dev_iterate iter)
+{
+	if (!iter || iter->magic != DEV_ITERATE_MAGIC)
+		return;
+	iter->magic = 0;
+	free(iter->search_type);
+	free(iter->search_value);
+	free(iter);
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+void __attribute__((__noreturn__)) usage(char *prog)
+{
+	fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog);
+	fprintf(stderr, "\tList all devices and exit\n");
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	blkid_dev_iterate	iter;
+	blkid_cache 		cache = NULL;
+	blkid_dev		dev;
+	int			c, ret;
+	char			*tmp;
+	char			*file = NULL;
+	char			*search_type = NULL;
+	char			*search_value = NULL;
+
+	while ((c = getopt (argc, argv, "m:f:")) != EOF)
+		switch (c) {
+		case 'f':
+			file = optarg;
+			break;
+		case 'm':
+		{
+			int mask = strtoul (optarg, &tmp, 0);
+			if (*tmp) {
+				fprintf(stderr, "Invalid debug mask: %s\n",
+					optarg);
+				exit(1);
+			}
+			blkid_init_debug(mask);
+			break;
+		}
+		case '?':
+			usage(argv[0]);
+		}
+	if (argc >= optind+2) {
+		search_type = argv[optind];
+		search_value = argv[optind+1];
+		optind += 2;
+	}
+	if (argc != optind)
+		usage(argv[0]);
+
+	if ((ret = blkid_get_cache(&cache, file)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+
+	iter = blkid_dev_iterate_begin(cache);
+	if (search_type)
+		blkid_dev_set_search(iter, search_type, search_value);
+	while (blkid_dev_next(iter, &dev) == 0) {
+		printf("Device: %s\n", blkid_dev_devname(dev));
+	}
+	blkid_dev_iterate_end(iter);
+
+
+	blkid_put_cache(cache);
+	return (0);
+}
+#endif
diff --git a/libblkid/devname.c b/libblkid/devname.c
new file mode 100644
index 0000000..b27b661
--- /dev/null
+++ b/libblkid/devname.c
@@ -0,0 +1,678 @@
+/*
+ * devname.c - get a dev by its device inode name
+ *
+ * Copyright (C) Andries Brouwer
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#define _GNU_SOURCE 1
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <dirent.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+
+#include "blkidP.h"
+
+#include "canonicalize.h"		/* $(top_srcdir)/include */
+#include "pathnames.h"
+#include "sysfs.h"
+#include "at.h"
+
+/*
+ * Find a dev struct in the cache by device name, if available.
+ *
+ * If there is no entry with the specified device name, and the create
+ * flag is set, then create an empty device entry.
+ */
+blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
+{
+	blkid_dev dev = NULL, tmp;
+	struct list_head *p, *pnext;
+
+	if (!cache || !devname)
+		return NULL;
+
+	list_for_each(p, &cache->bic_devs) {
+		tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
+		if (strcmp(tmp->bid_name, devname))
+			continue;
+
+		DBG(DEBUG_DEVNAME,
+		    printf("found devname %s in cache\n", tmp->bid_name));
+		dev = tmp;
+		break;
+	}
+
+	if (!dev && (flags & BLKID_DEV_CREATE)) {
+		if (access(devname, F_OK) < 0)
+			return NULL;
+		dev = blkid_new_dev();
+		if (!dev)
+			return NULL;
+		dev->bid_time = INT_MIN;
+		dev->bid_name = strdup(devname);
+		dev->bid_cache = cache;
+		list_add_tail(&dev->bid_devs, &cache->bic_devs);
+		cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+	}
+
+	if (flags & BLKID_DEV_VERIFY) {
+		dev = blkid_verify(cache, dev);
+		if (!dev || !(dev->bid_flags & BLKID_BID_FL_VERIFIED))
+			return dev;
+		/*
+		 * If the device is verified, then search the blkid
+		 * cache for any entries that match on the type, uuid,
+		 * and label, and verify them; if a cache entry can
+		 * not be verified, then it's stale and so we remove
+		 * it.
+		 */
+		list_for_each_safe(p, pnext, &cache->bic_devs) {
+			blkid_dev dev2 = list_entry(p, struct blkid_struct_dev, bid_devs);
+			if (dev2->bid_flags & BLKID_BID_FL_VERIFIED)
+				continue;
+			if (!dev->bid_type || !dev2->bid_type ||
+			    strcmp(dev->bid_type, dev2->bid_type))
+				continue;
+			if (dev->bid_label && dev2->bid_label &&
+			    strcmp(dev->bid_label, dev2->bid_label))
+				continue;
+			if (dev->bid_uuid && dev2->bid_uuid &&
+			    strcmp(dev->bid_uuid, dev2->bid_uuid))
+				continue;
+			if ((dev->bid_label && !dev2->bid_label) ||
+			    (!dev->bid_label && dev2->bid_label) ||
+			    (dev->bid_uuid && !dev2->bid_uuid) ||
+			    (!dev->bid_uuid && dev2->bid_uuid))
+				continue;
+			dev2 = blkid_verify(cache, dev2);
+			if (dev2 && !(dev2->bid_flags & BLKID_BID_FL_VERIFIED))
+				blkid_free_dev(dev2);
+		}
+	}
+	return dev;
+}
+
+/* Directories where we will try to search for device names */
+static const char *dirlist[] = { "/dev", "/devfs", "/devices", NULL };
+
+static int is_dm_leaf(const char *devname)
+{
+	struct dirent	*de, *d_de;
+	DIR		*dir, *d_dir;
+	char		path[256];
+	int		ret = 1;
+
+	if ((dir = opendir("/sys/block")) == NULL)
+		return 0;
+	while ((de = readdir(dir)) != NULL) {
+		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
+		    !strcmp(de->d_name, devname) ||
+		    strncmp(de->d_name, "dm-", 3) ||
+		    strlen(de->d_name) > sizeof(path)-32)
+			continue;
+		sprintf(path, "/sys/block/%s/slaves", de->d_name);
+		if ((d_dir = opendir(path)) == NULL)
+			continue;
+		while ((d_de = readdir(d_dir)) != NULL) {
+			if (!strcmp(d_de->d_name, devname)) {
+				ret = 0;
+				break;
+			}
+		}
+		closedir(d_dir);
+		if (!ret)
+			break;
+	}
+	closedir(dir);
+	return ret;
+}
+
+/*
+ * Probe a single block device to add to the device cache.
+ */
+static void probe_one(blkid_cache cache, const char *ptname,
+		      dev_t devno, int pri, int only_if_new, int removable)
+{
+	blkid_dev dev = NULL;
+	struct list_head *p, *pnext;
+	const char **dir;
+	char *devname = NULL;
+
+	/* See if we already have this device number in the cache. */
+	list_for_each_safe(p, pnext, &cache->bic_devs) {
+		blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
+					   bid_devs);
+		if (tmp->bid_devno == devno) {
+			if (only_if_new && !access(tmp->bid_name, F_OK))
+				return;
+			dev = blkid_verify(cache, tmp);
+			if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED))
+				break;
+			dev = 0;
+		}
+	}
+	if (dev && dev->bid_devno == devno)
+		goto set_pri;
+
+	/* Try to translate private device-mapper dm-<N> names
+	 * to standard /dev/mapper/<name>.
+	 */
+	if (!strncmp(ptname, "dm-", 3) && isdigit(ptname[3])) {
+		devname = canonicalize_dm_name(ptname);
+		if (!devname)
+			blkid__scan_dir("/dev/mapper", devno, 0, &devname);
+		if (devname)
+			goto get_dev;
+	}
+
+	/*
+	 * Take a quick look at /dev/ptname for the device number.  We check
+	 * all of the likely device directories.  If we don't find it, or if
+	 * the stat information doesn't check out, use blkid_devno_to_devname()
+	 * to find it via an exhaustive search for the device major/minor.
+	 */
+	for (dir = dirlist; *dir; dir++) {
+		struct stat st;
+		char device[256];
+
+		snprintf(device, sizeof(device), "%s/%s", *dir, ptname);
+		if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
+		    dev->bid_devno == devno)
+			goto set_pri;
+
+		if (stat(device, &st) == 0 &&
+		    (S_ISBLK(st.st_mode) ||
+		     (S_ISCHR(st.st_mode) && !strncmp(ptname, "ubi", 3))) &&
+		    st.st_rdev == devno) {
+			devname = strdup(device);
+			goto get_dev;
+		}
+	}
+	/* Do a short-cut scan of /dev/mapper first */
+	if (!devname)
+		blkid__scan_dir("/dev/mapper", devno, 0, &devname);
+	if (!devname) {
+		devname = blkid_devno_to_devname(devno);
+		if (!devname)
+			return;
+	}
+
+get_dev:
+	dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
+	free(devname);
+
+set_pri:
+	if (dev) {
+		if (pri)
+			dev->bid_pri = pri;
+		else if (!strncmp(dev->bid_name, "/dev/mapper/", 11)) {
+			dev->bid_pri = BLKID_PRI_DM;
+			if (is_dm_leaf(ptname))
+				dev->bid_pri += 5;
+		} else if (!strncmp(ptname, "md", 2))
+			dev->bid_pri = BLKID_PRI_MD;
+		if (removable)
+			dev->bid_flags |= BLKID_BID_FL_REMOVABLE;
+	}
+	return;
+}
+
+#define PROC_PARTITIONS "/proc/partitions"
+#define VG_DIR		"/proc/lvm/VGs"
+
+/*
+ * This function initializes the UUID cache with devices from the LVM
+ * proc hierarchy.  We currently depend on the names of the LVM
+ * hierarchy giving us the device structure in /dev.  (XXX is this a
+ * safe thing to do?)
+ */
+#ifdef VG_DIR
+static dev_t lvm_get_devno(const char *lvm_device)
+{
+	FILE *lvf;
+	char buf[1024];
+	int ma, mi;
+	dev_t ret = 0;
+
+	DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
+	if ((lvf = fopen(lvm_device, "r")) == NULL) {
+		DBG(DEBUG_DEVNAME, printf("%s: (%d) %m\n", lvm_device, errno));
+		return 0;
+	}
+
+	while (fgets(buf, sizeof(buf), lvf)) {
+		if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
+			ret = makedev(ma, mi);
+			break;
+		}
+	}
+	fclose(lvf);
+
+	return ret;
+}
+
+static void lvm_probe_all(blkid_cache cache, int only_if_new)
+{
+	DIR		*vg_list;
+	struct dirent	*vg_iter;
+	int		vg_len = strlen(VG_DIR);
+	dev_t		dev;
+
+	if ((vg_list = opendir(VG_DIR)) == NULL)
+		return;
+
+	DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
+
+	while ((vg_iter = readdir(vg_list)) != NULL) {
+		DIR		*lv_list;
+		char		*vdirname;
+		char		*vg_name;
+		struct dirent	*lv_iter;
+
+		vg_name = vg_iter->d_name;
+		if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
+			continue;
+		vdirname = malloc(vg_len + strlen(vg_name) + 8);
+		if (!vdirname)
+			goto exit;
+		sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
+
+		lv_list = opendir(vdirname);
+		free(vdirname);
+		if (lv_list == NULL)
+			continue;
+
+		while ((lv_iter = readdir(lv_list)) != NULL) {
+			char		*lv_name, *lvm_device;
+
+			lv_name = lv_iter->d_name;
+			if (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
+				continue;
+
+			lvm_device = malloc(vg_len + strlen(vg_name) +
+					    strlen(lv_name) + 8);
+			if (!lvm_device) {
+				closedir(lv_list);
+				goto exit;
+			}
+			sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
+				lv_name);
+			dev = lvm_get_devno(lvm_device);
+			sprintf(lvm_device, "%s/%s", vg_name, lv_name);
+			DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
+						  lvm_device,
+						  (unsigned int) dev));
+			probe_one(cache, lvm_device, dev, BLKID_PRI_LVM,
+				  only_if_new, 0);
+			free(lvm_device);
+		}
+		closedir(lv_list);
+	}
+exit:
+	closedir(vg_list);
+}
+#endif
+
+#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
+
+static int
+evms_probe_all(blkid_cache cache, int only_if_new)
+{
+	char line[100];
+	int ma, mi, sz, num = 0;
+	FILE *procpt;
+	char device[110];
+
+	procpt = fopen(PROC_EVMS_VOLUMES, "r");
+	if (!procpt)
+		return 0;
+	while (fgets(line, sizeof(line), procpt)) {
+		if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
+			    &ma, &mi, &sz, device) != 4)
+			continue;
+
+		DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
+					  device, ma, mi));
+
+		probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS,
+			  only_if_new, 0);
+		num++;
+	}
+	fclose(procpt);
+	return num;
+}
+
+static void
+ubi_probe_all(blkid_cache cache, int only_if_new)
+{
+	const char **dirname;
+
+	for (dirname = dirlist; *dirname; dirname++) {
+		DBG(DEBUG_DEVNAME, printf("probing UBI volumes under %s\n",
+					  *dirname));
+
+		DIR		*dir;
+		struct dirent	*iter;
+
+		dir = opendir(*dirname);
+		if (dir == NULL)
+			continue ;
+
+		while ((iter = readdir(dir)) != NULL) {
+			char		*name;
+			struct stat	st;
+			dev_t		dev;
+
+			name = iter->d_name;
+#ifdef _DIRENT_HAVE_D_TYPE
+			if (iter->d_type != DT_UNKNOWN &&
+			    iter->d_type != DT_CHR && iter->d_type != DT_LNK)
+				continue;
+#endif
+			if (!strcmp(name, ".") || !strcmp(name, "..") ||
+			    !strstr(name, "ubi"))
+				continue;
+			if (!strcmp(name, "ubi_ctrl"))
+				continue;
+			if (fstat_at(dirfd(dir), *dirname, name, &st, 0))
+				continue;
+
+			dev = st.st_rdev;
+
+			if (!S_ISCHR(st.st_mode) || !minor(dev))
+				continue;
+			DBG(DEBUG_DEVNAME, printf("UBI vol %s/%s: devno 0x%04X\n",
+				  *dirname, name, (int) dev));
+			probe_one(cache, name, dev, BLKID_PRI_UBI, only_if_new, 0);
+		}
+		closedir(dir);
+	}
+}
+
+/*
+ * Read the device data for all available block devices in the system.
+ */
+static int probe_all(blkid_cache cache, int only_if_new)
+{
+	FILE *proc;
+	char line[1024];
+	char ptname0[128 + 1], ptname1[128 + 1], *ptname = 0;
+	char *ptnames[2];
+	dev_t devs[2];
+	int ma, mi;
+	unsigned long long sz;
+	int lens[2] = { 0, 0 };
+	int which = 0, last = 0;
+	struct list_head *p, *pnext;
+
+	ptnames[0] = ptname0;
+	ptnames[1] = ptname1;
+
+	if (!cache)
+		return -BLKID_ERR_PARAM;
+
+	if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
+	    time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
+		return 0;
+
+	blkid_read_cache(cache);
+	evms_probe_all(cache, only_if_new);
+#ifdef VG_DIR
+	lvm_probe_all(cache, only_if_new);
+#endif
+	ubi_probe_all(cache, only_if_new);
+
+	proc = fopen(PROC_PARTITIONS, "r");
+	if (!proc)
+		return -BLKID_ERR_PROC;
+
+	while (fgets(line, sizeof(line), proc)) {
+		last = which;
+		which ^= 1;
+		ptname = ptnames[which];
+
+		if (sscanf(line, " %d %d %llu %128[^\n ]",
+			   &ma, &mi, &sz, ptname) != 4)
+			continue;
+		devs[which] = makedev(ma, mi);
+
+		DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
+
+		/* Skip whole disk devs unless they have no partitions.
+		 * If base name of device has changed, also
+		 * check previous dev to see if it didn't have a partn.
+		 * heuristic: partition name ends in a digit, & partition
+		 * names contain whole device name as substring.
+		 *
+		 * Skip extended partitions.
+		 * heuristic: size is 1
+		 *
+		 * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
+		 */
+
+		lens[which] = strlen(ptname);
+
+		/* ends in a digit, clearly a partition, so check */
+		if (isdigit(ptname[lens[which] - 1])) {
+			DBG(DEBUG_DEVNAME,
+			    printf("partition dev %s, devno 0x%04X\n",
+				   ptname, (unsigned int) devs[which]));
+
+			if (sz > 1)
+				probe_one(cache, ptname, devs[which], 0,
+					  only_if_new, 0);
+			lens[which] = 0;	/* mark as checked */
+		}
+
+		/*
+		 * If last was a whole disk and we just found a partition
+		 * on it, remove the whole-disk dev from the cache if
+		 * it exists.
+		 */
+		if (lens[last] && !strncmp(ptnames[last], ptname, lens[last])) {
+			list_for_each_safe(p, pnext, &cache->bic_devs) {
+				blkid_dev tmp;
+
+				/* find blkid dev for the whole-disk devno */
+				tmp = list_entry(p, struct blkid_struct_dev,
+						 bid_devs);
+				if (tmp->bid_devno == devs[last]) {
+					DBG(DEBUG_DEVNAME,
+						printf("freeing %s\n",
+						       tmp->bid_name));
+					blkid_free_dev(tmp);
+					cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+					break;
+				}
+			}
+			lens[last] = 0;
+		}
+		/*
+		 * If last was not checked because it looked like a whole-disk
+		 * dev, and the device's base name has changed,
+		 * check last as well.
+		 */
+		if (lens[last] && strncmp(ptnames[last], ptname, lens[last])) {
+			DBG(DEBUG_DEVNAME,
+			    printf("whole dev %s, devno 0x%04X\n",
+				   ptnames[last], (unsigned int) devs[last]));
+			probe_one(cache, ptnames[last], devs[last], 0,
+				  only_if_new, 0);
+			lens[last] = 0;
+		}
+	}
+
+	/* Handle the last device if it wasn't partitioned */
+	if (lens[which])
+		probe_one(cache, ptname, devs[which], 0, only_if_new, 0);
+
+	fclose(proc);
+	blkid_flush_cache(cache);
+	return 0;
+}
+
+/* Don't use it by default -- it's pretty slow (because cdroms, floppy, ...)
+ */
+static int probe_all_removable(blkid_cache cache)
+{
+	DIR *dir;
+	struct dirent *d;
+
+	if (!cache)
+		return -BLKID_ERR_PARAM;
+
+	dir = opendir(_PATH_SYS_BLOCK);
+	if (!dir)
+		return -BLKID_ERR_PROC;
+
+	while((d = readdir(dir))) {
+		struct sysfs_cxt sysfs = UL_SYSFSCXT_EMPTY;
+		int removable = 0;
+		dev_t devno;
+
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK)
+			continue;
+#endif
+		if (d->d_name[0] == '.' &&
+		    ((d->d_name[1] == 0) ||
+		     ((d->d_name[1] == '.') && (d->d_name[2] == 0))))
+			continue;
+
+		devno = sysfs_devname_to_devno(d->d_name, NULL);
+		if (!devno)
+			continue;
+
+		if (sysfs_init(&sysfs, devno, NULL) == 0) {
+			sysfs_read_int(&sysfs, "removable", &removable);
+			sysfs_deinit(&sysfs);
+		}
+
+		if (removable)
+			probe_one(cache, d->d_name, devno, 0, 0, 1);
+	}
+
+	closedir(dir);
+	return 0;
+}
+
+
+/**
+ * blkid_probe_all:
+ * @cache: cache handler
+ *
+ * Probes all block devices.
+ *
+ * Returns: 0 on success, or number less than zero in case of error.
+ */
+int blkid_probe_all(blkid_cache cache)
+{
+	int ret;
+
+	DBG(DEBUG_PROBE, printf("Begin blkid_probe_all()\n"));
+	ret = probe_all(cache, 0);
+	if (ret == 0) {
+		cache->bic_time = time(0);
+		cache->bic_flags |= BLKID_BIC_FL_PROBED;
+	}
+	DBG(DEBUG_PROBE, printf("End blkid_probe_all() [rc=%d]\n", ret));
+	return ret;
+}
+
+/**
+ * blkid_probe_all_new:
+ * @cache: cache handler
+ *
+ * Probes all new block devices.
+ *
+ * Returns: 0 on success, or number less than zero in case of error.
+ */
+int blkid_probe_all_new(blkid_cache cache)
+{
+	int ret;
+
+	DBG(DEBUG_PROBE, printf("Begin blkid_probe_all_new()\n"));
+	ret = probe_all(cache, 1);
+	DBG(DEBUG_PROBE, printf("End blkid_probe_all_new() [rc=%d]\n", ret));
+	return ret;
+}
+
+/**
+ * blkid_probe_all_removable:
+ * @cache: cache handler
+ *
+ * The libblkid probing is based on devices from /proc/partitions by default.
+ * This file usually does not contain removable devices (e.g. CDROMs) and this kind
+ * of devices are invisible for libblkid.
+ *
+ * This function adds removable block devices to @cache (probing is based on
+ * information from the /sys directory). Don't forget that removable devices
+ * (floppies, CDROMs, ...) could be pretty slow. It's very bad idea to call
+ * this function by default.
+ *
+ * Note that devices which were detected by this function won't be written to
+ * blkid.tab cache file.
+ *
+ * Returns: 0 on success, or number less than zero in case of error.
+ */
+int blkid_probe_all_removable(blkid_cache cache)
+{
+	int ret;
+
+	DBG(DEBUG_PROBE, printf("Begin blkid_probe_all_removable()\n"));
+	ret = probe_all_removable(cache);
+	DBG(DEBUG_PROBE, printf("End blkid_probe_all_removable() [rc=%d]\n", ret));
+	return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(DEBUG_ALL);
+	if (argc != 1) {
+		fprintf(stderr, "Usage: %s\n"
+			"Probe all devices and exit\n", argv[0]);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	if (blkid_probe_all(cache) < 0)
+		printf("%s: error probing devices\n", argv[0]);
+
+	if (blkid_probe_all_removable(cache) < 0)
+		printf("%s: error probing removable devices\n", argv[0]);
+
+	blkid_put_cache(cache);
+	return (0);
+}
+#endif
diff --git a/libblkid/devno.c b/libblkid/devno.c
new file mode 100644
index 0000000..906c91f
--- /dev/null
+++ b/libblkid/devno.c
@@ -0,0 +1,379 @@
+/*
+ * devno.c - find a particular device by its device number (major/minor)
+ *
+ * Copyright (C) 2000, 2001, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <dirent.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include "blkidP.h"
+#include "pathnames.h"
+#include "at.h"
+#include "sysfs.h"
+
+static char *blkid_strconcat(const char *a, const char *b, const char *c)
+{
+	char *res, *p;
+	size_t len, al, bl, cl;
+
+	al = a ? strlen(a) : 0;
+	bl = b ? strlen(b) : 0;
+	cl = c ? strlen(c) : 0;
+
+	len = al + bl + cl;
+	if (!len)
+		return NULL;
+	p = res = malloc(len + 1);
+	if (!res)
+		return NULL;
+	if (al) {
+		memcpy(p, a, al);
+		p += al;
+	}
+	if (bl) {
+		memcpy(p, b, bl);
+		p += bl;
+	}
+	if (cl) {
+		memcpy(p, c, cl);
+		p += cl;
+	}
+	*p = '\0';
+	return res;
+}
+
+/*
+ * This function adds an entry to the directory list
+ */
+static void add_to_dirlist(const char *dir, const char *subdir,
+				struct dir_list **list)
+{
+	struct dir_list *dp;
+
+	dp = malloc(sizeof(struct dir_list));
+	if (!dp)
+		return;
+	dp->name = subdir ? blkid_strconcat(dir, "/", subdir) :
+		   dir ? strdup(dir) : NULL;
+
+	if (!dp->name) {
+		free(dp);
+		return;
+	}
+	dp->next = *list;
+	*list = dp;
+}
+
+/*
+ * This function frees a directory list
+ */
+static void free_dirlist(struct dir_list **list)
+{
+	struct dir_list *dp, *next;
+
+	for (dp = *list; dp; dp = next) {
+		next = dp->next;
+		free(dp->name);
+		free(dp);
+	}
+	*list = NULL;
+}
+
+void blkid__scan_dir(char *dirname, dev_t devno, struct dir_list **list,
+		     char **devname)
+{
+	DIR	*dir;
+	struct dirent *dp;
+	struct stat st;
+
+	if ((dir = opendir(dirname)) == NULL)
+		return;
+
+	while ((dp = readdir(dir)) != NULL) {
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_BLK &&
+		    dp->d_type != DT_LNK && dp->d_type != DT_DIR)
+			continue;
+#endif
+		if (dp->d_name[0] == '.' &&
+		    ((dp->d_name[1] == 0) ||
+		     ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+			continue;
+
+		if (fstat_at(dirfd(dir), dirname, dp->d_name, &st, 0))
+			continue;
+
+		if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
+			*devname = blkid_strconcat(dirname, "/", dp->d_name);
+			DBG(DEBUG_DEVNO,
+			    printf("found 0x%llx at %s\n", (long long)devno,
+				   *devname));
+			break;
+		}
+
+		if (!list || !S_ISDIR(st.st_mode))
+			continue;
+
+		/* add subdirectory (but not symlink) to the list */
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (dp->d_type == DT_LNK)
+			continue;
+		if (dp->d_type == DT_UNKNOWN)
+#endif
+		{
+			if (fstat_at(dirfd(dir), dirname, dp->d_name, &st, 1) ||
+			    !S_ISDIR(st.st_mode))
+				continue;	/* symlink or lstat() failed */
+		}
+
+		if (*dp->d_name == '.' || (
+#ifdef _DIRENT_HAVE_D_TYPE
+		    dp->d_type == DT_DIR &&
+#endif
+		    strcmp(dp->d_name, "shm") == 0))
+			/* ignore /dev/.{udev,mount,mdadm} and /dev/shm */
+			continue;
+
+		add_to_dirlist(dirname, dp->d_name, list);
+	}
+	closedir(dir);
+	return;
+}
+
+/* Directories where we will try to search for device numbers */
+static const char *devdirs[] = { "/devices", "/devfs", "/dev", NULL };
+
+/**
+ * SECTION: misc
+ * @title: Miscellaneous utils
+ * @short_description: mix of various utils for low-level and high-level API
+ */
+
+
+
+static char *scandev_devno_to_devpath(dev_t devno)
+{
+	struct dir_list *list = NULL, *new_list = NULL;
+	char *devname = NULL;
+	const char **dir;
+
+	/*
+	 * Add the starting directories to search in reverse order of
+	 * importance, since we are using a stack...
+	 */
+	for (dir = devdirs; *dir; dir++)
+		add_to_dirlist(*dir, NULL, &list);
+
+	while (list) {
+		struct dir_list *current = list;
+
+		list = list->next;
+		DBG(DEBUG_DEVNO, printf("directory %s\n", current->name));
+		blkid__scan_dir(current->name, devno, &new_list, &devname);
+		free(current->name);
+		free(current);
+		if (devname)
+			break;
+		/*
+		 * If we're done checking at this level, descend to
+		 * the next level of subdirectories. (breadth-first)
+		 */
+		if (list == NULL) {
+			list = new_list;
+			new_list = NULL;
+		}
+	}
+	free_dirlist(&list);
+	free_dirlist(&new_list);
+
+	return devname;
+}
+
+/**
+ * blkid_devno_to_devname:
+ * @devno: device number
+ *
+ * This function finds the pathname to a block device with a given
+ * device number.
+ *
+ * Returns: a pointer to allocated memory to the pathname on success,
+ * and NULL on failure.
+ */
+char *blkid_devno_to_devname(dev_t devno)
+{
+	char *path = NULL;
+	char buf[PATH_MAX];
+
+	path = sysfs_devno_to_devpath(devno, buf, sizeof(buf));
+	if (path)
+		path = strdup(path);
+	if (!path)
+		path = scandev_devno_to_devpath(devno);
+
+	if (!path) {
+		DBG(DEBUG_DEVNO,
+		    printf("blkid: couldn't find devno 0x%04lx\n",
+			   (unsigned long) devno));
+	} else {
+		DBG(DEBUG_DEVNO,
+		    printf("found devno 0x%04llx as %s\n", (long long)devno, path));
+	}
+
+	return path;
+}
+
+
+/**
+ * blkid_devno_to_wholedisk:
+ * @dev: device number
+ * @diskname: buffer to return diskname (or NULL)
+ * @len: diskname buffer size (or 0)
+ * @diskdevno: pointer to returns devno of entire disk (or NULL)
+ *
+ * This function uses sysfs to convert the @devno device number to the *name*
+ * of the whole disk. The function DOES NOT return full device name. The @dev
+ * argument could be partition or whole disk -- both is converted.
+ *
+ * For example: sda1, 0x0801 --> sda, 0x0800
+ *
+ * For conversion to the full disk *path* use blkid_devno_to_devname(), for
+ * example:
+ *
+ * <informalexample>
+ *  <programlisting>
+ *
+ *	dev_t dev = 0x0801, disk;		// sda1 = 8:1
+ *	char *diskpath, diskname[32];
+ *
+ *	blkid_devno_to_wholedisk(dev, diskname, sizeof(diskname), &disk);
+ *	diskpath = blkid_devno_to_devname(disk);
+ *
+ *	// print "0x0801: sda, /dev/sda, 8:0
+ *	printf("0x%x: %s, %s, %d:%d\n",
+ *		dev, diskname, diskpath, major(disk), minor(disk));
+ *
+ *	free(diskpath);
+ *
+ *  </programlisting>
+ * </informalexample>
+ *
+ * Returns: 0 on success or -1 in case of error.
+ */
+int blkid_devno_to_wholedisk(dev_t dev, char *diskname,
+			size_t len, dev_t *diskdevno)
+{
+	return sysfs_devno_to_wholedisk( dev, diskname, len, diskdevno);
+}
+
+/*
+ * Returns 1 if the @major number is associated with @drvname.
+ */
+int blkid_driver_has_major(const char *drvname, int major)
+{
+	FILE *f;
+	char buf[128];
+	int match = 0;
+
+	f = fopen(_PATH_PROC_DEVICES, "r");
+	if (!f)
+		return 0;
+
+	while (fgets(buf, sizeof(buf), f)) {	/* skip to block dev section */
+		if (strncmp("Block devices:\n", buf, sizeof(buf)) == 0)
+			break;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		int maj;
+		char name[64 + 1];
+
+		if (sscanf(buf, "%d %64[^\n ]", &maj, name) != 2)
+			continue;
+
+		if (maj == major && strcmp(name, drvname) == 0) {
+			match = 1;
+			break;
+		}
+	}
+
+	fclose(f);
+
+	DBG(DEBUG_DEVNO, printf("major %d %s associated with '%s' driver\n",
+			major, match ? "is" : "is NOT", drvname));
+	return match;
+}
+
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+	char	*devname, *tmp;
+	char	diskname[PATH_MAX];
+	int	major, minor;
+	dev_t	devno, disk_devno;
+	const char *errmsg = "Couldn't parse %s: %s\n";
+
+	blkid_init_debug(DEBUG_ALL);
+	if ((argc != 2) && (argc != 3)) {
+		fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
+			"Resolve a device number to a device name\n",
+			argv[0], argv[0]);
+		exit(1);
+	}
+	if (argc == 2) {
+		devno = strtoul(argv[1], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "device number", argv[1]);
+			exit(1);
+		}
+	} else {
+		major = strtoul(argv[1], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "major number", argv[1]);
+			exit(1);
+		}
+		minor = strtoul(argv[2], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "minor number", argv[2]);
+			exit(1);
+		}
+		devno = makedev(major, minor);
+	}
+	printf("Looking for device 0x%04llx\n", (long long)devno);
+	devname = blkid_devno_to_devname(devno);
+	free(devname);
+
+	printf("Looking for whole-device for 0x%04llx\n", (long long)devno);
+	if (blkid_devno_to_wholedisk(devno, diskname,
+				sizeof(diskname), &disk_devno) == 0)
+		printf("found devno 0x%04llx as /dev/%s\n", (long long) disk_devno, diskname);
+
+	return 0;
+}
+#endif
diff --git a/libblkid/dm.c b/libblkid/dm.c
new file mode 100644
index 0000000..72ec9bd
--- /dev/null
+++ b/libblkid/dm.c
@@ -0,0 +1,138 @@
+/*
+ * device-mapper (dm) topology
+ * -- this is fallback for old systems where the topology information is not
+ *    exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+static int is_dm_device(dev_t devno)
+{
+	return blkid_driver_has_major("device-mapper", major(devno));
+}
+
+static int probe_dm_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	const char *paths[] = {
+		"/usr/local/sbin/dmsetup",
+		"/usr/sbin/dmsetup",
+		"/sbin/dmsetup"
+	};
+	int dmpipe[] = { -1, -1 }, stripes, stripesize;
+	char *cmd = NULL;
+	FILE *stream = NULL;
+	long long  offset, size;
+	size_t i;
+	dev_t devno = blkid_probe_get_devno(pr);
+
+	if (!devno)
+		goto nothing;		/* probably not a block device */
+	if (!is_dm_device(devno))
+		goto nothing;
+
+	for (i = 0; i < ARRAY_SIZE(paths); i++) {
+		struct stat sb;
+		if (stat(paths[i], &sb) == 0) {
+			cmd = (char *) paths[i];
+			break;
+		}
+	}
+
+	if (!cmd)
+		goto nothing;
+	if (pipe(dmpipe) < 0) {
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to open pipe: errno=%d", errno));
+		goto nothing;
+	}
+
+	switch (fork()) {
+	case 0:
+	{
+		char *dmargv[7], maj[16], min[16];
+
+		/* Plumbing */
+		close(dmpipe[0]);
+
+		if (dmpipe[1] != STDOUT_FILENO)
+			dup2(dmpipe[1], STDOUT_FILENO);
+
+		/* The libblkid library could linked with setuid programs */
+		if (setgid(getgid()) < 0)
+			 exit(1);
+		if (setuid(getuid()) < 0)
+			 exit(1);
+
+		snprintf(maj, sizeof(maj), "%d", major(devno));
+		snprintf(min, sizeof(min), "%d", minor(devno));
+
+		dmargv[0] = cmd;
+		dmargv[1] = "table";
+		dmargv[2] = "-j";
+		dmargv[3] = maj;
+		dmargv[4] = "-m";
+		dmargv[5] = min;
+		dmargv[6] = NULL;
+
+		execv(dmargv[0], dmargv);
+
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to execute %s: errno=%d", cmd, errno));
+		exit(1);
+	}
+	case -1:
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to forking: errno=%d", errno));
+		goto nothing;
+	default:
+		break;
+	}
+
+	stream = fdopen(dmpipe[0], "r");
+	if (!stream)
+		goto nothing;
+
+	if (fscanf(stream, "%lld %lld striped %d %d ",
+			&offset, &size, &stripes, &stripesize) != 0)
+		goto nothing;
+
+	blkid_topology_set_minimum_io_size(pr, stripesize << 9);
+	blkid_topology_set_optimal_io_size(pr, (stripes * stripesize) << 9);
+
+	fclose(stream);
+	close(dmpipe[1]);
+	return 0;
+
+nothing:
+	if (stream)
+		fclose(stream);
+	else if (dmpipe[0] != -1)
+		close(dmpipe[0]);
+	if (dmpipe[1] != -1)
+		close(dmpipe[1]);
+	return 1;
+}
+
+const struct blkid_idinfo dm_tp_idinfo =
+{
+	.name		= "dm",
+	.probefunc	= probe_dm_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/dos.c b/libblkid/dos.c
new file mode 100644
index 0000000..5887769
--- /dev/null
+++ b/libblkid/dos.c
@@ -0,0 +1,297 @@
+/*
+ * MS-DOS partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Inspired by fdisk, partx, Linux kernel and libparted.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+#include "dos.h"
+#include "aix.h"
+
+/* see superblocks/vfat.c */
+extern int blkid_probe_is_vfat(blkid_probe pr);
+
+static const struct dos_subtypes {
+	unsigned char type;
+	const struct blkid_idinfo *id;
+} dos_nested[] = {
+	{ BLKID_FREEBSD_PARTITION, &bsd_pt_idinfo },
+	{ BLKID_NETBSD_PARTITION, &bsd_pt_idinfo },
+	{ BLKID_OPENBSD_PARTITION, &bsd_pt_idinfo },
+	{ BLKID_UNIXWARE_PARTITION, &unixware_pt_idinfo },
+	{ BLKID_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo },
+	{ BLKID_MINIX_PARTITION, &minix_pt_idinfo }
+};
+
+static inline int is_extended(struct dos_partition *p)
+{
+	return (p->sys_type == BLKID_DOS_EXTENDED_PARTITION ||
+		p->sys_type == BLKID_W95_EXTENDED_PARTITION ||
+		p->sys_type == BLKID_LINUX_EXTENDED_PARTITION);
+}
+
+static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
+		uint32_t ex_start, uint32_t ex_size, int ssf)
+{
+	blkid_partlist ls = blkid_probe_get_partlist(pr);
+	uint32_t cur_start = ex_start, cur_size = ex_size;
+	unsigned char *data;
+	int ct_nodata = 0;	/* count ext.partitions without data partitions */
+	int i;
+
+	while (1) {
+		struct dos_partition *p, *p0;
+		uint32_t start, size;
+
+		if (++ct_nodata > 100)
+			return 0;
+		data = blkid_probe_get_sector(pr, cur_start);
+		if (!data)
+			goto leave;	/* malformed partition? */
+
+		if (!is_valid_mbr_signature(data))
+			goto leave;
+
+		p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+		/* Usually, the first entry is the real data partition,
+		 * the 2nd entry is the next extended partition, or empty,
+		 * and the 3rd and 4th entries are unused.
+		 * However, DRDOS sometimes has the extended partition as
+		 * the first entry (when the data partition is empty),
+		 * and OS/2 seems to use all four entries.
+		 * -- Linux kernel fs/partitions/dos.c
+		 *
+		 * See also http://en.wikipedia.org/wiki/Extended_boot_record
+		 */
+
+		/* Parse data partition */
+		for (p = p0, i = 0; i < 4; i++, p++) {
+			uint32_t abs_start;
+			blkid_partition par;
+
+			/* the start is relative to the parental ext.partition */
+			start = dos_partition_start(p) * ssf;
+			size = dos_partition_size(p) * ssf;
+			abs_start = cur_start + start;	/* absolute start */
+
+			if (!size || is_extended(p))
+				continue;
+			if (i >= 2) {
+				/* extra checks to detect real data on
+				 * 3rd and 4th entries */
+				if (start + size > cur_size)
+					continue;
+				if (abs_start < ex_start)
+					continue;
+				if (abs_start + size > ex_start + ex_size)
+					continue;
+			}
+
+			par = blkid_partlist_add_partition(ls, tab, abs_start, size);
+			if (!par)
+				goto err;
+
+			blkid_partition_set_type(par, p->sys_type);
+			blkid_partition_set_flags(par, p->boot_ind);
+			ct_nodata = 0;
+		}
+		/* The first nested ext.partition should be a link to the next
+		 * logical partition. Everything other (recursive ext.partitions)
+		 * is junk.
+		 */
+		for (p = p0, i = 0; i < 4; i++, p++) {
+			start = dos_partition_start(p) * ssf;
+			size = dos_partition_size(p) * ssf;
+
+			if (size && is_extended(p))
+				break;
+		}
+		if (i == 4)
+			goto leave;
+
+		cur_start = ex_start + start;
+		cur_size = size;
+	}
+leave:
+	return 0;
+err:
+	return -1;
+}
+
+static int probe_dos_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	int i;
+	int ssf;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	struct dos_partition *p0, *p;
+	unsigned char *data;
+	uint32_t start, size, id;
+
+	data = blkid_probe_get_sector(pr, 0);
+	if (!data)
+		goto nothing;
+
+	/* ignore disks with AIX magic number -- for more details see aix.c */
+	if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0)
+		goto nothing;
+
+	/*
+	 * Now that the 55aa signature is present, this is probably
+	 * either the boot sector of a FAT filesystem or a DOS-type
+	 * partition table.
+	 */
+	if (blkid_probe_is_vfat(pr)) {
+		DBG(DEBUG_LOWPROBE, printf("probably FAT -- ignore\n"));
+		goto nothing;
+	}
+
+	p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+	/*
+	 * Reject PT where boot indicator is not 0 or 0x80.
+	 */
+	for (p = p0, i = 0; i < 4; i++, p++)
+		if (p->boot_ind != 0 && p->boot_ind != 0x80) {
+			DBG(DEBUG_LOWPROBE, printf("missing boot indicator -- ignore\n"));
+			goto nothing;
+		}
+
+	/*
+	 * GPT uses valid MBR
+	 */
+	for (p = p0, i = 0; i < 4; i++, p++) {
+		if (p->sys_type == BLKID_GPT_PARTITION) {
+			DBG(DEBUG_LOWPROBE, printf("probably GPT -- ignore\n"));
+			goto nothing;
+		}
+	}
+
+	blkid_probe_use_wiper(pr, BLKID_MSDOS_PT_OFFSET,
+				  512 - BLKID_MSDOS_PT_OFFSET);
+
+	/*
+	 * Well, all checks pass, it's MS-DOS partiton table
+	 */
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+
+	/* sector size factor (the start and size are in the real sectors, but
+	 * we need to convert all sizes to 512 logical sectors
+	 */
+	ssf = blkid_probe_get_sectorsize(pr) / 512;
+
+	/* allocate a new partition table */
+	tab = blkid_partlist_new_parttable(ls, "dos", BLKID_MSDOS_PT_OFFSET);
+	if (!tab)
+		goto err;
+
+	id = dos_parttable_id(data);
+	if (id) {
+		char buf[37];
+
+		snprintf(buf, sizeof(buf), "0x%08x", id);
+		blkid_parttable_set_id(tab, (unsigned char *) buf);
+	}
+
+
+	/* Parse primary partitions */
+	for (p = p0, i = 0; i < 4; i++, p++) {
+		blkid_partition par;
+
+		start = dos_partition_start(p) * ssf;
+		size = dos_partition_size(p) * ssf;
+
+		if (!size) {
+			/* Linux kernel ignores empty partitions, but partno for
+			 * the empty primary partitions is not reused */
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, p->sys_type);
+		blkid_partition_set_flags(par, p->boot_ind);
+	}
+
+	/* Linux uses partition numbers greater than 4
+	 * for all logical partition and all nested partition tables (bsd, ..)
+	 */
+	blkid_partlist_set_partno(ls, 5);
+
+	/* Parse logical partitions */
+	for (p = p0, i = 0; i < 4; i++, p++) {
+		start = dos_partition_start(p) * ssf;
+		size = dos_partition_size(p) * ssf;
+
+		if (!size)
+			continue;
+		if (is_extended(p) &&
+		    parse_dos_extended(pr, tab, start, size, ssf) == -1)
+			goto err;
+	}
+
+	/* Parse subtypes (nested partitions) on large disks */
+	if (!blkid_probe_is_tiny(pr)) {
+		for (p = p0, i = 0; i < 4; i++, p++) {
+			size_t n;
+
+			if (!dos_partition_size(p) || is_extended(p))
+				continue;
+
+			for (n = 0; n < ARRAY_SIZE(dos_nested); n++) {
+				if (dos_nested[n].type != p->sys_type)
+					continue;
+
+				if (blkid_partitions_do_subprobe(pr,
+						blkid_partlist_get_partition(ls, i),
+						dos_nested[n].id) == -1)
+					goto err;
+				break;
+			}
+		}
+	}
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+const struct blkid_idinfo dos_pt_idinfo =
+{
+	.name		= "dos",
+	.probefunc	= probe_dos_pt,
+	.magics		=
+	{
+		/* DOS master boot sector:
+		 *
+		 *     0 | Code Area
+		 *   440 | Optional Disk signature
+		 *   446 | Partition table
+		 *   510 | 0x55
+		 *   511 | 0xAA
+		 */
+		{ .magic = "\x55\xAA", .len = 2, .sboff = 510 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/dos.h b/libblkid/dos.h
new file mode 100644
index 0000000..d7588a8
--- /dev/null
+++ b/libblkid/dos.h
@@ -0,0 +1,41 @@
+#ifndef BLKID_PARTITIONS_DOS_H
+#define BLKID_PARTITIONS_DOS_H
+
+struct dos_partition {
+	unsigned char boot_ind;		/* 0x80 - active */
+	unsigned char bh, bs, bc;	/* begin CHS */
+	unsigned char sys_type;
+	unsigned char eh, es, ec;	/* end CHS */
+	unsigned char start_sect[4];
+	unsigned char nr_sects[4];
+} __attribute__((packed));
+
+#define BLKID_MSDOS_PT_OFFSET		0x1be
+
+/* assemble badly aligned little endian integer */
+static inline unsigned int assemble4le(const unsigned char *p)
+{
+	return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+static inline unsigned int dos_partition_start(struct dos_partition *p)
+{
+	return assemble4le(&(p->start_sect[0]));
+}
+
+static inline unsigned int dos_partition_size(struct dos_partition *p)
+{
+	return assemble4le(&(p->nr_sects[0]));
+}
+
+static inline int is_valid_mbr_signature(const unsigned char *mbr)
+{
+	return mbr[510] == 0x55 && mbr[511] == 0xaa ? 1 : 0;
+}
+
+static inline unsigned int dos_parttable_id(const unsigned char *mbr)
+{
+	return assemble4le(&mbr[440]);
+}
+
+#endif /* BLKID_PARTITIONS_DOS_H */
diff --git a/libblkid/drbd.c b/libblkid/drbd.c
new file mode 100644
index 0000000..43e544e
--- /dev/null
+++ b/libblkid/drbd.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2009 by Bastian Friedrich <bastian.friedrich@collax.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * defines, structs taken from drbd source; file names represent drbd source
+ * files.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+/*
+ * drbd/linux/drbd.h
+ */
+#define DRBD_MAGIC 0x83740267
+
+/*
+ * user/drbdmeta.c
+ * We only support v08 for now
+ */
+#define DRBD_MD_MAGIC_08         (DRBD_MAGIC+4)
+#define DRBD_MD_MAGIC_84_UNCLEAN (DRBD_MAGIC+5)
+
+/*
+ * drbd/linux/drbd.h
+ */
+enum drbd_uuid_index {
+	UI_CURRENT,
+	UI_BITMAP,
+	UI_HISTORY_START,
+	UI_HISTORY_END,
+	UI_SIZE,		/* nl-packet: number of dirty bits */
+	UI_FLAGS,		/* nl-packet: flags */
+	UI_EXTENDED_SIZE	/* Everything. */
+};
+
+/*
+ * user/drbdmeta.c
+ * Minor modifications wrt. types
+ */
+struct md_on_disk_08 {
+	uint64_t la_sect;         /* last agreed size. */
+	uint64_t uuid[UI_SIZE];   /* UUIDs */
+	uint64_t device_uuid;
+	uint64_t reserved_u64_1;
+	uint32_t flags;
+	uint32_t magic;
+	uint32_t md_size_sect;
+	int32_t  al_offset;       /* signed sector offset to this block */
+	uint32_t al_nr_extents;   /* important for restoring the AL */
+	int32_t  bm_offset;       /* signed sector offset to the bitmap, from here */
+	uint32_t bm_bytes_per_bit;
+	uint32_t reserved_u32[4];
+
+	char reserved[8 * 512 - (8*(UI_SIZE+3)+4*11)];
+};
+
+
+static int probe_drbd(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct md_on_disk_08 *md;
+	off_t off;
+
+	off = pr->size - sizeof(*md);
+
+	/* Small devices cannot be drbd (?) */
+	if (pr->size < 0x10000)
+		return -1;
+
+	md = (struct md_on_disk_08 *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct md_on_disk_08));
+	if (!md)
+		return -1;
+
+	if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_08 &&
+			be32_to_cpu(md->magic) != DRBD_MD_MAGIC_84_UNCLEAN)
+		return -1;
+
+	/*
+	 * DRBD does not have "real" uuids; the following resembles DRBD's
+	 * notion of uuids (64 bit, see struct above)
+	 */
+	blkid_probe_sprintf_uuid(pr,
+		(unsigned char *) &md->device_uuid, sizeof(md->device_uuid),
+		"%" PRIx64, be64_to_cpu(md->device_uuid));
+
+	blkid_probe_set_version(pr, "v08");
+
+	if (blkid_probe_set_magic(pr,
+				off + offsetof(struct md_on_disk_08, magic),
+				sizeof(md->magic),
+				(unsigned char *) &md->magic))
+		return -1;
+
+	return 0;
+}
+
+const struct blkid_idinfo drbd_idinfo =
+{
+	.name		= "drbd",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_drbd,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/drbdproxy_datalog.c b/libblkid/drbdproxy_datalog.c
new file mode 100644
index 0000000..afe4725
--- /dev/null
+++ b/libblkid/drbdproxy_datalog.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 by Philipp Marek <philipp.marek@linbit.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+
+struct log_header_t {
+	uint64_t magic;
+	uint64_t version;
+
+	unsigned char uuid[16];
+
+	uint64_t flags;
+} __attribute__((packed));
+
+
+static int probe_drbdproxy_datalog(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct log_header_t *lh;
+
+	lh = (struct log_header_t *) blkid_probe_get_buffer(pr, 0, sizeof(*lh));
+	if (!lh)
+		return -1;
+
+	blkid_probe_set_uuid(pr, lh->uuid);
+	blkid_probe_sprintf_version(pr, "v%jd", le64_to_cpu(lh->version));
+
+	return 0;
+}
+
+const struct blkid_idinfo drbdproxy_datalog_idinfo =
+{
+	.name		= "drbdproxy_datalog",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_drbdproxy_datalog,
+	.minsz		= 16*1024,
+	.magics		=
+	{
+		{ .magic = "DRBDdlh*", .len = 8, .sboff = 0, .kboff = 0 },
+		{ NULL }
+	}
+};
diff --git a/libblkid/encode.c b/libblkid/encode.c
new file mode 100644
index 0000000..ff57be4
--- /dev/null
+++ b/libblkid/encode.c
@@ -0,0 +1,340 @@
+
+/*
+ * encode.c - string conversion routines (mostly for compatibility with
+ *            udev/volume_id)
+ *
+ * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "blkidP.h"
+
+#define UDEV_ALLOWED_CHARS_INPUT               "/ $%?,"
+
+/**
+ * SECTION: encode
+ * @title: Encoding utils
+ * @short_description: encode strings to safe udev-compatible formats
+ *
+ */
+
+/* count of characters used to encode one unicode char */
+static int utf8_encoded_expected_len(const char *str)
+{
+	unsigned char c = (unsigned char)str[0];
+
+	if (c < 0x80)
+		return 1;
+	if ((c & 0xe0) == 0xc0)
+		return 2;
+	if ((c & 0xf0) == 0xe0)
+		return 3;
+	if ((c & 0xf8) == 0xf0)
+		return 4;
+	if ((c & 0xfc) == 0xf8)
+		return 5;
+	if ((c & 0xfe) == 0xfc)
+		return 6;
+	return 0;
+}
+
+/* decode one unicode char */
+static int utf8_encoded_to_unichar(const char *str)
+{
+	int unichar;
+	int len;
+	int i;
+
+	len = utf8_encoded_expected_len(str);
+	switch (len) {
+	case 1:
+		return (int)str[0];
+	case 2:
+		unichar = str[0] & 0x1f;
+		break;
+	case 3:
+		unichar = (int)str[0] & 0x0f;
+		break;
+	case 4:
+		unichar = (int)str[0] & 0x07;
+		break;
+	case 5:
+		unichar = (int)str[0] & 0x03;
+		break;
+	case 6:
+		unichar = (int)str[0] & 0x01;
+		break;
+	default:
+		return -1;
+	}
+
+	for (i = 1; i < len; i++) {
+		if (((int)str[i] & 0xc0) != 0x80)
+			return -1;
+		unichar <<= 6;
+		unichar |= (int)str[i] & 0x3f;
+	}
+
+	return unichar;
+}
+
+/* expected size used to encode one unicode char */
+static int utf8_unichar_to_encoded_len(int unichar)
+{
+	if (unichar < 0x80)
+		return 1;
+	if (unichar < 0x800)
+		return 2;
+	if (unichar < 0x10000)
+		return 3;
+	if (unichar < 0x200000)
+		return 4;
+	if (unichar < 0x4000000)
+		return 5;
+	return 6;
+}
+
+/* check if unicode char has a valid numeric range */
+static int utf8_unichar_valid_range(int unichar)
+{
+	if (unichar > 0x10ffff)
+		return 0;
+	if ((unichar & 0xfffff800) == 0xd800)
+		return 0;
+	if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
+		return 0;
+	if ((unichar & 0xffff) == 0xffff)
+		return 0;
+	return 1;
+}
+
+/* validate one encoded unicode char and return its length */
+static int utf8_encoded_valid_unichar(const char *str)
+{
+	int len;
+	int unichar;
+	int i;
+
+	len = utf8_encoded_expected_len(str);
+	if (len == 0)
+		return -1;
+
+	/* ascii is valid */
+	if (len == 1)
+		return 1;
+
+	/* check if expected encoded chars are available */
+	for (i = 0; i < len; i++)
+		if ((str[i] & 0x80) != 0x80)
+			return -1;
+
+	unichar = utf8_encoded_to_unichar(str);
+
+	/* check if encoded length matches encoded value */
+	if (utf8_unichar_to_encoded_len(unichar) != len)
+		return -1;
+
+	/* check if value has valid range */
+	if (!utf8_unichar_valid_range(unichar))
+		return -1;
+
+	return len;
+}
+
+static int replace_whitespace(const char *str, char *to, size_t len)
+{
+	size_t i, j;
+
+	/* strip trailing whitespace */
+	len = strnlen(str, len);
+	while (len && isspace(str[len-1]))
+		len--;
+
+	/* strip leading whitespace */
+	i = 0;
+	while (isspace(str[i]) && (i < len))
+		i++;
+
+	j = 0;
+	while (i < len) {
+		/* substitute multiple whitespace with a single '_' */
+		if (isspace(str[i])) {
+			while (isspace(str[i]))
+				i++;
+			to[j++] = '_';
+		}
+		to[j++] = str[i++];
+	}
+	to[j] = '\0';
+	return 0;
+}
+
+static int is_whitelisted(char c, const char *white)
+{
+	if ((c >= '0' && c <= '9') ||
+	    (c >= 'A' && c <= 'Z') ||
+	    (c >= 'a' && c <= 'z') ||
+	    strchr("#+-.:=@_", c) != NULL ||
+	    (white != NULL && strchr(white, c) != NULL))
+		return 1;
+	return 0;
+}
+
+/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
+static int replace_chars(char *str, const char *white)
+{
+	size_t i = 0;
+	int replaced = 0;
+
+	while (str[i] != '\0') {
+		int len;
+
+		if (is_whitelisted(str[i], white)) {
+			i++;
+			continue;
+		}
+
+		/* accept hex encoding */
+		if (str[i] == '\\' && str[i+1] == 'x') {
+			i += 2;
+			continue;
+		}
+
+		/* accept valid utf8 */
+		len = utf8_encoded_valid_unichar(&str[i]);
+		if (len > 1) {
+			i += len;
+			continue;
+		}
+
+		/* if space is allowed, replace whitespace with ordinary space */
+		if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
+			str[i] = ' ';
+			i++;
+			replaced++;
+			continue;
+		}
+
+		/* everything else is replaced with '_' */
+		str[i] = '_';
+		i++;
+		replaced++;
+	}
+	return replaced;
+}
+
+size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len,
+			const unsigned char *src, size_t count)
+{
+	size_t i, j;
+	uint16_t c;
+
+	for (j = i = 0; i + 2 <= count; i += 2) {
+		if (enc == BLKID_ENC_UTF16LE)
+			c = (src[i+1] << 8) | src[i];
+		else /* BLKID_ENC_UTF16BE */
+			c = (src[i] << 8) | src[i+1];
+		if (c == 0) {
+			dest[j] = '\0';
+			break;
+		} else if (c < 0x80) {
+			if (j+1 >= len)
+				break;
+			dest[j++] = (uint8_t) c;
+		} else if (c < 0x800) {
+			if (j+2 >= len)
+				break;
+			dest[j++] = (uint8_t) (0xc0 | (c >> 6));
+			dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+		} else {
+			if (j+3 >= len)
+				break;
+			dest[j++] = (uint8_t) (0xe0 | (c >> 12));
+			dest[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+			dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+		}
+	}
+	dest[j] = '\0';
+	return j;
+}
+
+/**
+ * blkid_encode_string:
+ * @str: input string to be encoded
+ * @str_enc: output string to store the encoded input string
+ * @len: maximum size of the output string, which may be
+ *       four times as long as the input string
+ *
+ * Encode all potentially unsafe characters of a string to the
+ * corresponding hex value prefixed by '\x'.
+ *
+ * Returns: 0 if the entire string was copied, non-zero otherwise.
+ **/
+int blkid_encode_string(const char *str, char *str_enc, size_t len)
+{
+	size_t i, j;
+
+	if (!str || !str_enc || !len)
+		return -1;
+
+	for (i = 0, j = 0; str[i] != '\0'; i++) {
+		int seqlen;
+
+		seqlen = utf8_encoded_valid_unichar(&str[i]);
+		if (seqlen > 1) {
+			if (len-j < (size_t)seqlen)
+				goto err;
+			memcpy(&str_enc[j], &str[i], seqlen);
+			j += seqlen;
+			i += (seqlen-1);
+		} else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
+			if (len-j < 4)
+				goto err;
+			sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
+			j += 4;
+		} else {
+			if (len-j < 1)
+				goto err;
+			str_enc[j] = str[i];
+			j++;
+		}
+		if (j+3 >= len)
+			goto err;
+	}
+	if (len-j < 1)
+		goto err;
+	str_enc[j] = '\0';
+	return 0;
+err:
+	return -1;
+}
+
+/**
+ * blkid_safe_string:
+ * @str: input string
+ * @str_safe: output string
+ * @len: size of output string
+ *
+ * Allows plain ascii, hex-escaping and valid utf8. Replaces all whitespaces
+ * with '_'.
+ *
+ * Returns: 0 on success or -1 in case of error.
+ */
+int blkid_safe_string(const char *str, char *str_safe, size_t len)
+{
+	if (!str || !str_safe || !len)
+		return -1;
+	replace_whitespace(str, str_safe, len);
+	replace_chars(str_safe, UDEV_ALLOWED_CHARS_INPUT);
+	return 0;
+}
diff --git a/libblkid/env.c b/libblkid/env.c
new file mode 100644
index 0000000..c79e0e0
--- /dev/null
+++ b/libblkid/env.c
@@ -0,0 +1,110 @@
+/*
+ * Security checks of environment
+ * Added from shadow-utils package
+ * by Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if (!defined(HAVE_PRCTL) && defined(linux))
+#include <sys/syscall.h>
+#endif
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "env.h"
+
+#ifndef HAVE_ENVIRON_DECL
+extern char **environ;
+#endif
+
+static char * const forbid[] = {
+        "_RLD_=",
+        "BASH_ENV=",    /* GNU creeping featurism strikes again... */
+        "ENV=",
+        "HOME=",
+        "IFS=",
+        "KRB_CONF=",
+        "LD_",          /* anything with the LD_ prefix */
+        "LIBPATH=",
+        "MAIL=",
+        "NLSPATH=",
+        "PATH=",
+        "SHELL=",
+        "SHLIB_PATH=",
+        (char *) 0
+};
+
+/* these are allowed, but with no slashes inside
+   (to work around security problems in GNU gettext) */
+static char * const noslash[] = {
+        "LANG=",
+        "LANGUAGE=",
+        "LC_",          /* anything with the LC_ prefix */
+        (char *) 0
+};
+
+void
+sanitize_env(void)
+{
+        char **envp = environ;
+        char * const *bad;
+        char **cur;
+        char **move;
+
+        for (cur = envp; *cur; cur++) {
+                for (bad = forbid; *bad; bad++) {
+                        if (strncmp(*cur, *bad, strlen(*bad)) == 0) {
+                                for (move = cur; *move; move++)
+                                        *move = *(move + 1);
+                                cur--;
+                                break;
+                        }
+                }
+        }
+
+        for (cur = envp; *cur; cur++) {
+                for (bad = noslash; *bad; bad++) {
+                        if (strncmp(*cur, *bad, strlen(*bad)) != 0)
+                                continue;
+                        if (!strchr(*cur, '/'))
+                                continue;  /* OK */
+                        for (move = cur; *move; move++)
+                                *move = *(move + 1);
+                        cur--;
+                        break;
+                }
+        }
+}
+
+
+char *safe_getenv(const char *arg)
+{
+	uid_t ruid = getuid();
+
+	if (ruid != 0 || (ruid != geteuid()) || (getgid() != getegid()))
+		return NULL;
+#ifdef HAVE_PRCTL
+	if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+		return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+	if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+		return NULL;
+#endif
+#endif
+#ifdef HAVE_SECURE_GETENV
+return secure_getenv(arg);
+#elif HAVE___SECURE_GETENV
+	return __secure_getenv(arg);
+#else
+	return getenv(arg);
+#endif
+}
diff --git a/libblkid/env.h b/libblkid/env.h
new file mode 100644
index 0000000..a53d310
--- /dev/null
+++ b/libblkid/env.h
@@ -0,0 +1,16 @@
+#ifndef UTIL_LINUX_ENV_H
+#define UTIL_LINUX_ENV_H
+
+#include "c.h"
+
+extern void sanitize_env(void);
+extern char *safe_getenv(const char *arg);
+
+static inline void xsetenv(char const *name, char const *val, int overwrite)
+{
+	if (setenv(name, val, overwrite) != 0)
+		err(EXIT_FAILURE, "failed to set the %s environment variable", name);
+}
+
+#endif /* UTIL_LINUX_ENV_H */
+
diff --git a/libblkid/evaluate.c b/libblkid/evaluate.c
new file mode 100644
index 0000000..2e1ca57
--- /dev/null
+++ b/libblkid/evaluate.c
@@ -0,0 +1,328 @@
+/*
+ * evaluate.c - very high-level API to evaluate LABELs or UUIDs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdint.h>
+#include <stdarg.h>
+
+#include "pathnames.h"
+#include "canonicalize.h"
+
+#include "blkidP.h"
+
+/**
+ * SECTION:evaluate
+ * @title: Tags and Spec evaluation
+ * @short_description: top-level API for LABEL and UUID evaluation.
+ *
+ * This API provides very simple and portable way how evaluate LABEL and UUID
+ * tags.  The blkid_evaluate_tag() and blkid_evaluate_spec() work on 2.4 and
+ * 2.6 systems and on systems with or without udev. Currently, the libblkid
+ * library supports "udev" and "scan" methods. The "udev" method uses udev
+ * /dev/disk/by-* symlinks and the "scan" method scans all block devices from
+ * the /proc/partitions file. The evaluation could be controlled by the
+ * /etc/blkid.conf config file. The default is to try "udev" and then "scan"
+ * method.
+ *
+ * The blkid_evaluate_tag() also automatically informs udevd when an obsolete
+ * /dev/disk/by-* symlink is detected.
+ *
+ * If you are not sure how translate LABEL or UUID to the device name use this
+ * API.
+ */
+
+#ifdef CONFIG_BLKID_VERIFY_UDEV
+/* returns zero when the device has NAME=value (LABEL/UUID) */
+static int verify_tag(const char *devname, const char *name, const char *value)
+{
+	blkid_probe pr;
+	int fd = -1, rc = -1;
+	size_t len;
+	const char *data;
+	int errsv = 0;
+
+	pr = blkid_new_probe();
+	if (!pr)
+		return -1;
+
+	blkid_probe_enable_superblocks(pr, TRUE);
+	blkid_probe_set_superblocks_flags(pr,
+			BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID);
+
+	blkid_probe_enable_partitions(pr, TRUE);
+	blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
+
+	fd = open(devname, O_RDONLY|O_CLOEXEC);
+	if (fd < 0) {
+		errsv = errno;
+		goto done;
+	}
+	if (blkid_probe_set_device(pr, fd, 0, 0))
+		goto done;
+	rc = blkid_do_safeprobe(pr);
+	if (rc)
+		goto done;
+	rc = blkid_probe_lookup_value(pr, name, &data, &len);
+	if (!rc)
+		rc = memcmp(value, data, len);
+done:
+	DBG(DEBUG_EVALUATE, printf("%s: %s verification %s\n",
+			devname, name, rc == 0 ? "PASS" : "FAILED"));
+	if (fd >= 0)
+		close(fd);
+	blkid_free_probe(pr);
+
+	/* for non-root users we use unverified udev links */
+	return errsv == EACCES ? 0 : rc;
+}
+#endif /* CONFIG_BLKID_VERIFY_UDEV*/
+
+/**
+ * blkid_send_uevent:
+ * @devname: absolute path to the device
+ * @action: event string
+ *
+ * Returns: -1 in case of failure, or 0 on success.
+ */
+int blkid_send_uevent(const char *devname, const char *action)
+{
+	char uevent[PATH_MAX];
+	struct stat st;
+	FILE *f;
+	int rc = -1;
+
+	DBG(DEBUG_EVALUATE, printf("%s: uevent '%s' requested\n", devname, action));
+
+	if (!devname || !action)
+		return -1;
+	if (stat(devname, &st) || !S_ISBLK(st.st_mode))
+		return -1;
+
+	snprintf(uevent, sizeof(uevent), "/sys/dev/block/%d:%d/uevent",
+			major(st.st_rdev), minor(st.st_rdev));
+
+	f = fopen(uevent, "w");
+	if (f) {
+		rc = 0;
+		if (fputs(action, f) >= 0)
+			rc = 0;
+		fclose(f);
+	}
+	DBG(DEBUG_EVALUATE, printf("%s: send uevent %s\n",
+			uevent, rc == 0 ? "SUCCES" : "FAILED"));
+	return rc;
+}
+
+static char *evaluate_by_udev(const char *token, const char *value, int uevent)
+{
+	char dev[PATH_MAX];
+	char *path = NULL;
+	size_t len;
+	struct stat st;
+
+	DBG(DEBUG_EVALUATE,
+	    printf("evaluating by udev %s=%s\n", token, value));
+
+	if (!strcmp(token, "UUID"))
+		strcpy(dev, _PATH_DEV_BYUUID "/");
+	else if (!strcmp(token, "LABEL"))
+		strcpy(dev, _PATH_DEV_BYLABEL "/");
+	else if (!strcmp(token, "PARTLABEL"))
+		strcpy(dev, _PATH_DEV_BYPARTLABEL "/");
+	else if (!strcmp(token, "PARTUUID"))
+		strcpy(dev, _PATH_DEV_BYPARTUUID "/");
+	else {
+		DBG(DEBUG_EVALUATE,
+		    printf("unsupported token %s\n", token));
+		return NULL;	/* unsupported tag */
+	}
+
+	len = strlen(dev);
+	if (blkid_encode_string(value, &dev[len], sizeof(dev) - len) != 0)
+		return NULL;
+
+	DBG(DEBUG_EVALUATE,
+	    printf("expected udev link: %s\n", dev));
+
+	if (stat(dev, &st))
+		goto failed;	/* link or device does not exist */
+
+	if (!S_ISBLK(st.st_mode))
+		return NULL;
+
+	path = canonicalize_path(dev);
+	if (!path)
+		return NULL;
+
+#ifdef CONFIG_BLKID_VERIFY_UDEV
+	if (verify_tag(path, token, value))
+		goto failed;
+#endif
+	return path;
+
+failed:
+	DBG(DEBUG_EVALUATE, printf("failed to evaluate by udev\n"));
+
+	if (uevent && path)
+		blkid_send_uevent(path, "change");
+	free(path);
+	return NULL;
+}
+
+static char *evaluate_by_scan(const char *token, const char *value,
+		blkid_cache *cache, struct blkid_config *conf)
+{
+	blkid_cache c = cache ? *cache : NULL;
+	char *res;
+
+	DBG(DEBUG_EVALUATE,
+	    printf("evaluating by blkid scan %s=%s\n", token, value));
+
+	if (!c) {
+		char *cachefile = blkid_get_cache_filename(conf);
+		blkid_get_cache(&c, cachefile);
+		free(cachefile);
+	}
+	if (!c)
+		return NULL;
+
+	res = blkid_get_devname(c, token, value);
+
+	if (cache)
+		*cache = c;
+	else
+		blkid_put_cache(c);
+
+	return res;
+}
+
+/**
+ * blkid_evaluate_tag:
+ * @token: token name (e.g "LABEL" or "UUID") or unparsed tag (e.g. "LABEL=foo")
+ * @value: token data (e.g. "foo")
+ * @cache: pointer to cache (or NULL when you don't want to re-use the cache)
+ *
+ * Returns: allocated string with a device name.
+ */
+char *blkid_evaluate_tag(const char *token, const char *value, blkid_cache *cache)
+{
+	struct blkid_config *conf = NULL;
+	char *t = NULL, *v = NULL;
+	char *ret = NULL;
+	int i;
+
+	if (!token)
+		return NULL;
+
+	if (!cache || !*cache)
+		blkid_init_debug(0);
+
+	DBG(DEBUG_EVALUATE,
+	    printf("evaluating  %s%s%s\n", token, value ? "=" : "",
+		   value ? value : ""));
+
+	if (!value) {
+		if (!strchr(token, '=')) {
+			ret = strdup(token);
+			goto out;
+		}
+		blkid_parse_tag_string(token, &t, &v);
+		if (!t || !v)
+			goto out;
+		token = t;
+		value = v;
+	}
+
+	conf = blkid_read_config(NULL);
+	if (!conf)
+		goto out;
+
+	for (i = 0; i < conf->nevals; i++) {
+		if (conf->eval[i] == BLKID_EVAL_UDEV)
+			ret = evaluate_by_udev(token, value, conf->uevent);
+		else if (conf->eval[i] == BLKID_EVAL_SCAN)
+			ret = evaluate_by_scan(token, value, cache, conf);
+		if (ret)
+			break;
+	}
+
+	DBG(DEBUG_EVALUATE,
+	    printf("%s=%s evaluated as %s\n", token, value, ret));
+out:
+	blkid_free_config(conf);
+	free(t);
+	free(v);
+	return ret;
+}
+
+/**
+ * blkid_evaluate_spec:
+ * @spec: unparsed tag (e.g. "LABEL=foo") or path (e.g. /dev/dm-0)
+ * @cache: pointer to cache (or NULL when you don't want to re-use the cache)
+ *
+ * All returned paths are canonicalized, device-mapper paths are converted
+ * to the /dev/mapper/name format.
+ *
+ * Returns: allocated string with a device name.
+ */
+char *blkid_evaluate_spec(const char *spec, blkid_cache *cache)
+{
+	char *t = NULL, *v = NULL, *res;
+
+	if (!spec)
+		return NULL;
+
+	if (strchr(spec, '=') &&
+	    blkid_parse_tag_string(spec, &t, &v) != 0)	/* parse error */
+		return NULL;
+
+	if (v)
+		res = blkid_evaluate_tag(t, v, cache);
+	else
+		res = canonicalize_path(spec);
+
+	free(t);
+	free(v);
+	return res;
+}
+
+
+#ifdef TEST_PROGRAM
+int main(int argc, char *argv[])
+{
+	blkid_cache cache = NULL;
+	char *res;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <tag> | <spec>\n", argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	blkid_init_debug(0);
+
+	res = blkid_evaluate_spec(argv[1], &cache);
+	if (res)
+		printf("%s\n", res);
+	if (cache)
+		blkid_put_cache(cache);
+
+	return res ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+#endif
diff --git a/libblkid/evms.c b/libblkid/evms.c
new file mode 100644
index 0000000..7a4fd55
--- /dev/null
+++ b/libblkid/evms.c
@@ -0,0 +1,77 @@
+/*
+ * Evms topology
+ * -- this is fallback for old systems where the toplogy information is not
+ *    exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+#define EVMS_MAJOR		117
+
+#ifndef _IOT__IOTBASE_u_int32_t
+#define _IOT__IOTBASE_u_int32_t IOT_SIMPLE(uint32_t)
+#endif
+#define _IOT_evms_stripe_info _IOT (_IOTS(uint32_t), 2, 0, 0, 0, 0)
+#define EVMS_GET_STRIPE_INFO	_IOR(EVMS_MAJOR, 0xF0, struct evms_stripe_info)
+
+struct evms_stripe_info {
+	uint32_t	size;		/* stripe unit 512-byte blocks */
+	uint32_t	width;		/* the number of stripe members or RAID data disks */
+} evms_stripe_info;
+
+static int is_evms_device(dev_t devno)
+{
+	if (major(devno) == EVMS_MAJOR)
+		return 1;
+	return blkid_driver_has_major("evms", major(devno));
+}
+
+static int probe_evms_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct evms_stripe_info evms;
+	dev_t devno = blkid_probe_get_devno(pr);
+
+	if (!devno)
+		goto nothing;		/* probably not a block device */
+
+	if (!is_evms_device(devno))
+		goto nothing;
+
+	memset(&evms, 0, sizeof(evms));
+
+	if (ioctl(pr->fd, EVMS_GET_STRIPE_INFO, &evms))
+		goto nothing;
+
+	blkid_topology_set_minimum_io_size(pr, evms.size << 9);
+	blkid_topology_set_optimal_io_size(pr, (evms.size * evms.width) << 9);
+
+	return 0;
+
+nothing:
+	return 1;
+}
+
+const struct blkid_idinfo evms_tp_idinfo =
+{
+	.name		= "evms",
+	.probefunc	= probe_evms_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/exec_shell.c b/libblkid/exec_shell.c
new file mode 100644
index 0000000..95620cd
--- /dev/null
+++ b/libblkid/exec_shell.c
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "nls.h"
+#include "c.h"
+#include "xalloc.h"
+
+#include "exec_shell.h"
+
+#define DEFAULT_SHELL "/bin/sh"
+
+void __attribute__((__noreturn__)) exec_shell(void) {
+	const char *shell = getenv("SHELL"), *shell_basename;
+	char *arg0;
+	if (!shell)
+		shell = DEFAULT_SHELL;
+
+	shell_basename = basename(shell);
+	arg0 = xmalloc(strlen(shell_basename) + 2);
+	arg0[0] = '-';
+	strcpy(arg0 + 1, shell_basename);
+
+	execl(shell, arg0, NULL);
+	err(EXIT_FAILURE, _("failed to execute %s"), shell);
+}
diff --git a/libblkid/exec_shell.h b/libblkid/exec_shell.h
new file mode 100644
index 0000000..a2aa757
--- /dev/null
+++ b/libblkid/exec_shell.h
@@ -0,0 +1 @@
+extern void __attribute__((__noreturn__)) exec_shell(void);
diff --git a/libblkid/exfat.c b/libblkid/exfat.c
new file mode 100644
index 0000000..215c671
--- /dev/null
+++ b/libblkid/exfat.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 Andrew Nayenko <resver@gmail.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include "superblocks.h"
+
+struct exfat_super_block {
+	uint8_t jump[3];
+	uint8_t oem_name[8];
+	uint8_t	__unused1[53];
+	uint64_t block_start;
+	uint64_t block_count;
+	uint32_t fat_block_start;
+	uint32_t fat_block_count;
+	uint32_t cluster_block_start;
+	uint32_t cluster_count;
+	uint32_t rootdir_cluster;
+	uint8_t volume_serial[4];
+	struct {
+		uint8_t minor;
+		uint8_t major;
+	} version;
+	uint16_t volume_state;
+	uint8_t block_bits;
+	uint8_t bpc_bits;
+	uint8_t fat_count;
+	uint8_t drive_no;
+	uint8_t allocated_percent;
+} __attribute__((__packed__));
+
+struct exfat_entry_label {
+	uint8_t type;
+	uint8_t length;
+	uint8_t name[30];
+} __attribute__((__packed__));
+
+#define BLOCK_SIZE(sb) (1 << (sb)->block_bits)
+#define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits)
+#define EXFAT_FIRST_DATA_CLUSTER 2
+#define EXFAT_LAST_DATA_CLUSTER 0xffffff6
+#define EXFAT_ENTRY_SIZE 32
+
+#define EXFAT_ENTRY_EOD		0x00
+#define EXFAT_ENTRY_LABEL	0x83
+
+static blkid_loff_t block_to_offset(const struct exfat_super_block *sb,
+		blkid_loff_t block)
+{
+	return (blkid_loff_t) block << sb->block_bits;
+}
+
+static blkid_loff_t cluster_to_block(const struct exfat_super_block *sb,
+		uint32_t cluster)
+{
+	return le32_to_cpu(sb->cluster_block_start) +
+			((blkid_loff_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
+					<< sb->bpc_bits);
+}
+
+static blkid_loff_t cluster_to_offset(const struct exfat_super_block *sb,
+		uint32_t cluster)
+{
+	return block_to_offset(sb, cluster_to_block(sb, cluster));
+}
+
+static uint32_t next_cluster(blkid_probe pr,
+		const struct exfat_super_block *sb, uint32_t cluster)
+{
+	uint32_t *next;
+	blkid_loff_t fat_offset;
+
+	fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start))
+		+ (blkid_loff_t) cluster * sizeof(cluster);
+	next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
+			sizeof(uint32_t));
+	if (!next)
+		return 0;
+	return le32_to_cpu(*next);
+}
+
+static struct exfat_entry_label *find_label(blkid_probe pr,
+		const struct exfat_super_block *sb)
+{
+	uint32_t cluster = le32_to_cpu(sb->rootdir_cluster);
+	blkid_loff_t offset = cluster_to_offset(sb, cluster);
+	uint8_t *entry;
+
+	for (;;) {
+		entry = (uint8_t *) blkid_probe_get_buffer(pr, offset,
+				EXFAT_ENTRY_SIZE);
+		if (!entry)
+			return NULL;
+		if (entry[0] == EXFAT_ENTRY_EOD)
+			return NULL;
+		if (entry[0] == EXFAT_ENTRY_LABEL)
+			return (struct exfat_entry_label *) entry;
+		offset += EXFAT_ENTRY_SIZE;
+		if (offset % CLUSTER_SIZE(sb) == 0) {
+			cluster = next_cluster(pr, sb, cluster);
+			if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+				return NULL;
+			if (cluster > EXFAT_LAST_DATA_CLUSTER)
+				return NULL;
+			offset = cluster_to_offset(sb, cluster);
+		}
+	}
+}
+
+static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct exfat_super_block *sb;
+	struct exfat_entry_label *label;
+
+	sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
+	if (!sb)
+		return -1;
+
+	label = find_label(pr, sb);
+	if (label)
+		blkid_probe_set_utf8label(pr, label->name,
+				min(label->length * 2, 30), BLKID_ENC_UTF16LE);
+
+	blkid_probe_sprintf_uuid(pr, sb->volume_serial, 4,
+			"%02hhX%02hhX-%02hhX%02hhX",
+			sb->volume_serial[3], sb->volume_serial[2],
+			sb->volume_serial[1], sb->volume_serial[0]);
+
+	blkid_probe_sprintf_version(pr, "%u.%u",
+			sb->version.major, sb->version.minor);
+
+	return 0;
+}
+
+const struct blkid_idinfo exfat_idinfo =
+{
+	.name		= "exfat",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_exfat,
+	.magics		=
+	{
+		{ .magic = "EXFAT   ", .len = 8, .sboff = 3 },
+		{ NULL }
+	}
+};
diff --git a/libblkid/exitcodes.h b/libblkid/exitcodes.h
new file mode 100644
index 0000000..24ee123
--- /dev/null
+++ b/libblkid/exitcodes.h
@@ -0,0 +1,35 @@
+#ifndef UTIL_LINUX_EXITCODES_H
+#define UTIL_LINUX_EXITCODES_H
+/*
+ * BE CAREFUL
+ *
+ * These exit codes are part of the official interface for mount,
+ * fsck, mkfs, etc. wrappers.
+ */
+
+/* Exit codes used by mkfs-type programs */
+#define MKFS_EX_OK	0	/* No errors */
+#define MKFS_EX_ERROR	8	/* Operational error */
+#define MKFS_EX_USAGE	16	/* Usage or syntax error */
+
+/* Exit codes used by fsck-type programs */
+#define FSCK_EX_OK		0	/* No errors */
+#define FSCK_EX_NONDESTRUCT	1	/* File system errors corrected */
+#define FSCK_EX_REBOOT		2	/* System should be rebooted */
+#define FSCK_EX_DESTRUCT	FSCK_EX_REBOOT	/* Alias */
+#define FSCK_EX_UNCORRECTED	4	/* File system errors left uncorrected */
+#define FSCK_EX_ERROR		8	/* Operational error */
+#define FSCK_EX_USAGE		16	/* Usage or syntax error */
+#define FSCK_EX_LIBRARY		128	/* Shared library error */
+
+/* Exit codes used by mount-line programs */
+#define MOUNT_EX_SUCCESS	0	/* No errors */
+#define MOUNT_EX_USAGE		1	/* incorrect invocation or permission */
+#define MOUNT_EX_SYSERR		2	/* out of memory, cannot fork, ... */
+#define MOUNT_EX_SOFTWARE	4	/* internal mount bug or wrong version */
+#define MOUNT_EX_USER		8	/* user interrupt */
+#define MOUNT_EX_FILEIO		16	/* problems writing, locking, ... mtab/fstab */
+#define MOUNT_EX_FAIL		32	/* mount failure */
+#define MOUNT_EX_SOMEOK		64	/* some mount succeeded */
+
+#endif	/* UTIL_LINUX_EXITCODES_H */
diff --git a/libblkid/ext.c b/libblkid/ext.c
new file mode 100644
index 0000000..eff96a0
--- /dev/null
+++ b/libblkid/ext.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 1999, 2001 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#include <time.h>
+
+#include "linux_version.h"
+#include "superblocks.h"
+
+struct ext2_super_block {
+	uint32_t		s_inodes_count;
+	uint32_t		s_blocks_count;
+	uint32_t		s_r_blocks_count;
+	uint32_t		s_free_blocks_count;
+	uint32_t		s_free_inodes_count;
+	uint32_t		s_first_data_block;
+	uint32_t		s_log_block_size;
+	uint32_t		s_dummy3[7];
+	unsigned char		s_magic[2];
+	uint16_t		s_state;
+	uint16_t		s_errors;
+	uint16_t		s_minor_rev_level;
+	uint32_t		s_lastcheck;
+	uint32_t		s_checkinterval;
+	uint32_t		s_creator_os;
+	uint32_t		s_rev_level;
+	uint16_t		s_def_resuid;
+	uint16_t		s_def_resgid;
+	uint32_t		s_first_ino;
+	uint16_t		s_inode_size;
+	uint16_t		s_block_group_nr;
+	uint32_t		s_feature_compat;
+	uint32_t		s_feature_incompat;
+	uint32_t		s_feature_ro_compat;
+	unsigned char		s_uuid[16];
+	char			s_volume_name[16];
+	char			s_last_mounted[64];
+	uint32_t		s_algorithm_usage_bitmap;
+	uint8_t			s_prealloc_blocks;
+	uint8_t			s_prealloc_dir_blocks;
+	uint16_t		s_reserved_gdt_blocks;
+	uint8_t			s_journal_uuid[16];
+	uint32_t		s_journal_inum;
+	uint32_t		s_journal_dev;
+	uint32_t		s_last_orphan;
+	uint32_t		s_hash_seed[4];
+	uint8_t			s_def_hash_version;
+	uint8_t			s_jnl_backup_type;
+	uint16_t		s_reserved_word_pad;
+	uint32_t		s_default_mount_opts;
+	uint32_t		s_first_meta_bg;
+	uint32_t		s_mkfs_time;
+	uint32_t		s_jnl_blocks[17];
+	uint32_t		s_blocks_count_hi;
+	uint32_t		s_r_blocks_count_hi;
+	uint32_t		s_free_blocks_hi;
+	uint16_t		s_min_extra_isize;
+	uint16_t		s_want_extra_isize;
+	uint32_t		s_flags;
+	uint16_t		s_raid_stride;
+	uint16_t		s_mmp_interval;
+	uint64_t		s_mmp_block;
+	uint32_t		s_raid_stripe_width;
+	uint32_t		s_reserved[163];
+} __attribute__((packed));
+
+/* magic string */
+#define EXT_SB_MAGIC				"\123\357"
+/* supper block offset */
+#define EXT_SB_OFF				0x400
+/* supper block offset in kB */
+#define EXT_SB_KBOFF				(EXT_SB_OFF >> 10)
+/* magic string offset within super block */
+#define EXT_MAG_OFF				0x38
+
+
+
+/* for s_flags */
+#define EXT2_FLAGS_TEST_FILESYS		0x0004
+
+/* for s_feature_compat */
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL		0x0004
+
+/* for s_feature_ro_compat */
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER	0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE	0x0002
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR	0x0004
+#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE	0x0008
+#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM		0x0010
+#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK	0x0020
+#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE	0x0040
+
+/* for s_feature_incompat */
+#define EXT2_FEATURE_INCOMPAT_FILETYPE		0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER		0x0004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV	0x0008
+#define EXT2_FEATURE_INCOMPAT_META_BG		0x0010
+#define EXT4_FEATURE_INCOMPAT_EXTENTS		0x0040 /* extents support */
+#define EXT4_FEATURE_INCOMPAT_64BIT		0x0080
+#define EXT4_FEATURE_INCOMPAT_MMP		0x0100
+#define EXT4_FEATURE_INCOMPAT_FLEX_BG		0x0200
+
+#define EXT2_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+					 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#define EXT2_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE| \
+					 EXT2_FEATURE_INCOMPAT_META_BG)
+#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED	~EXT2_FEATURE_INCOMPAT_SUPP
+#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED	~EXT2_FEATURE_RO_COMPAT_SUPP
+
+#define EXT3_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+					 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#define EXT3_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE| \
+					 EXT3_FEATURE_INCOMPAT_RECOVER| \
+					 EXT2_FEATURE_INCOMPAT_META_BG)
+#define EXT3_FEATURE_INCOMPAT_UNSUPPORTED	~EXT3_FEATURE_INCOMPAT_SUPP
+#define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED	~EXT3_FEATURE_RO_COMPAT_SUPP
+
+/*
+ * Check to see if a filesystem is in /proc/filesystems.
+ * Returns 1 if found, 0 if not
+ */
+static int fs_proc_check(const char *fs_name)
+{
+	FILE	*f;
+	char	buf[80], *cp, *t;
+
+	f = fopen("/proc/filesystems", "r");
+	if (!f)
+		return 0;
+	while (!feof(f)) {
+		if (!fgets(buf, sizeof(buf), f))
+			break;
+		cp = buf;
+		if (!isspace(*cp)) {
+			while (*cp && !isspace(*cp))
+				cp++;
+		}
+		while (*cp && isspace(*cp))
+			cp++;
+		if ((t = strchr(cp, '\n')) != NULL)
+			*t = 0;
+		if ((t = strchr(cp, '\t')) != NULL)
+			*t = 0;
+		if ((t = strchr(cp, ' ')) != NULL)
+			*t = 0;
+		if (!strcmp(fs_name, cp)) {
+			fclose(f);
+			return 1;
+		}
+	}
+	fclose(f);
+	return (0);
+}
+
+/*
+ * Check to see if a filesystem is available as a module
+ * Returns 1 if found, 0 if not
+ */
+static int check_for_modules(const char *fs_name)
+{
+#ifdef __linux__
+	struct utsname	uts;
+	FILE		*f;
+	char		buf[1024], *cp;
+	int		namesz;
+
+	if (uname(&uts))
+		return 0;
+	snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release);
+
+	f = fopen(buf, "r");
+	if (!f)
+		return 0;
+
+	namesz = strlen(fs_name);
+
+	while (!feof(f)) {
+		if (!fgets(buf, sizeof(buf), f))
+			break;
+		if ((cp = strchr(buf, ':')) != NULL)
+			*cp = 0;
+		else
+			continue;
+		if ((cp = strrchr(buf, '/')) == NULL)
+			continue;
+		cp++;
+
+		if (!strncmp(cp, fs_name, namesz) &&
+		    (!strcmp(cp + namesz, ".ko") ||
+		     !strcmp(cp + namesz, ".ko.gz"))) {
+			fclose(f);
+			return 1;
+		}
+	}
+	fclose(f);
+#endif /* __linux__ */
+	return 0;
+}
+
+/*
+ * Starting in 2.6.29, ext4 can be used to support filesystems
+ * without a journal.
+ */
+#define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29)
+
+static int system_supports_ext2(void)
+{
+	static time_t	last_check = 0;
+	static int	ret = -1;
+	time_t		now = time(0);
+
+	if (ret != -1 || (now - last_check) < 5)
+		return ret;
+	last_check = now;
+	ret = (fs_proc_check("ext2") || check_for_modules("ext2"));
+	return ret;
+}
+
+static int system_supports_ext4(void)
+{
+	static time_t	last_check = 0;
+	static int	ret = -1;
+	time_t		now = time(0);
+
+	if (ret != -1 || (now - last_check) < 5)
+		return ret;
+	last_check = now;
+	ret = (fs_proc_check("ext4") || check_for_modules("ext4"));
+	return ret;
+}
+
+static int system_supports_ext4dev(void)
+{
+	static time_t	last_check = 0;
+	static int	ret = -1;
+	time_t		now = time(0);
+
+	if (ret != -1 || (now - last_check) < 5)
+		return ret;
+	last_check = now;
+	ret = (fs_proc_check("ext4dev") || check_for_modules("ext4dev"));
+	return ret;
+}
+
+static int system_supports_ext4_ext2(void)
+{
+#ifdef __linux__
+	return get_linux_version() >= EXT4_SUPPORTS_EXT2;
+#else
+	return 0;
+#endif
+}
+/*
+ * reads superblock and returns:
+ *	fc = feature_compat
+ *	fi = feature_incompat
+ *	frc = feature_ro_compat
+ */
+static struct ext2_super_block *ext_get_super(
+		blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc)
+{
+	struct ext2_super_block *es;
+
+	es = (struct ext2_super_block *)
+			blkid_probe_get_buffer(pr, EXT_SB_OFF, 0x200);
+	if (!es)
+		return NULL;
+	if (fc)
+		*fc = le32_to_cpu(es->s_feature_compat);
+	if (fi)
+		*fi = le32_to_cpu(es->s_feature_incompat);
+	if (frc)
+		*frc = le32_to_cpu(es->s_feature_ro_compat);
+
+	return es;
+}
+
+static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n",
+		   le32_to_cpu(es->s_feature_compat),
+		   le32_to_cpu(es->s_feature_incompat),
+		   le32_to_cpu(es->s_feature_ro_compat)));
+
+	if (strlen(es->s_volume_name))
+		blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name,
+					sizeof(es->s_volume_name));
+	blkid_probe_set_uuid(pr, es->s_uuid);
+
+	if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
+		blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL");
+
+	if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) &&
+	    ((le32_to_cpu(es->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0))
+		blkid_probe_set_value(pr, "SEC_TYPE",
+				(unsigned char *) "ext2",
+				sizeof("ext2"));
+
+	blkid_probe_sprintf_version(pr, "%u.%u",
+		le32_to_cpu(es->s_rev_level),
+		le16_to_cpu(es->s_minor_rev_level));
+}
+
+
+static int probe_jbd(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fi;
+
+	es = ext_get_super(pr, NULL, &fi, NULL);
+	if (!es)
+		return -BLKID_ERR_PARAM;
+	if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
+		return -BLKID_ERR_PARAM;
+
+	ext_get_info(pr, 2, es);
+	return 0;
+}
+
+static int probe_ext2(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fc, frc, fi;
+
+	es = ext_get_super(pr, &fc, &fi, &frc);
+	if (!es)
+		return -BLKID_ERR_PARAM;
+
+	/* Distinguish between ext3 and ext2 */
+	if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
+		return -BLKID_ERR_PARAM;
+
+	/* Any features which ext2 doesn't understand */
+	if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+	    (fi  & EXT2_FEATURE_INCOMPAT_UNSUPPORTED))
+		return -BLKID_ERR_PARAM;
+
+	/*
+	 * If ext2 is not present, but ext4 or ext4dev are, then
+	 * disclaim we are ext2
+	 */
+	if (!system_supports_ext2() &&
+	    (system_supports_ext4() || system_supports_ext4dev()) &&
+	    system_supports_ext4_ext2())
+		return -BLKID_ERR_PARAM;
+
+	ext_get_info(pr, 2, es);
+	return 0;
+}
+
+static int probe_ext3(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fc, frc, fi;
+
+	es = ext_get_super(pr, &fc, &fi, &frc);
+	if (!es)
+		return -BLKID_ERR_PARAM;
+
+	/* ext3 requires journal */
+	if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+		return -BLKID_ERR_PARAM;
+
+	/* Any features which ext3 doesn't understand */
+	if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+	    (fi  & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+		return -BLKID_ERR_PARAM;
+
+	ext_get_info(pr, 3, es);
+	return 0;
+}
+
+
+static int probe_ext4dev(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fc, frc, fi;
+
+	es = ext_get_super(pr, &fc, &fi, &frc);
+	if (!es)
+		return -BLKID_ERR_PARAM;
+
+	/* Distinguish from jbd */
+	if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+		return -BLKID_ERR_PARAM;
+
+	/*
+	 * If the filesystem does not have a journal and ext2 and ext4
+	 * is not present, then force this to be detected as an
+	 * ext4dev filesystem.
+	 */
+	if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+	    !system_supports_ext2() && !system_supports_ext4() &&
+	    system_supports_ext4dev() &&
+	    system_supports_ext4_ext2())
+		goto force_ext4dev;
+
+	/*
+	 * If the filesystem is marked as OK for use by in-development
+	 * filesystem code, but ext4dev is not supported, and ext4 is,
+	 * then don't call ourselves ext4dev, since we should be
+	 * detected as ext4 in that case.
+	 *
+	 * If the filesystem is marked as in use by production
+	 * filesystem, then it can only be used by ext4 and NOT by
+	 * ext4dev, so always disclaim we are ext4dev in that case.
+	 */
+	if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
+		if (!system_supports_ext4dev() && system_supports_ext4())
+			return -BLKID_ERR_PARAM;
+	} else
+		return -BLKID_ERR_PARAM;
+
+force_ext4dev:
+	ext_get_info(pr, 4, es);
+	return 0;
+}
+
+static int probe_ext4(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fc, frc, fi;
+
+	es = ext_get_super(pr, &fc, &fi, &frc);
+	if (!es)
+		return -1;
+
+	/* Distinguish from jbd */
+	if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+		return -BLKID_ERR_PARAM;
+
+	/*
+	 * If the filesystem does not have a journal and ext2 is not
+	 * present, then force this to be detected as an ext2
+	 * filesystem.
+	 */
+	if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+	    !system_supports_ext2() && system_supports_ext4() &&
+	    system_supports_ext4_ext2())
+		goto force_ext4;
+
+	/* Ext4 has at least one feature which ext3 doesn't understand */
+	if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) &&
+	    !(fi  & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+		return -BLKID_ERR_PARAM;
+
+force_ext4:
+	/*
+	 * If the filesystem is a OK for use by in-development
+	 * filesystem code, and ext4dev is supported or ext4 is not
+	 * supported, then don't call ourselves ext4, so we can redo
+	 * the detection and mark the filesystem as ext4dev.
+	 *
+	 * If the filesystem is marked as in use by production
+	 * filesystem, then it can only be used by ext4 and NOT by
+	 * ext4dev.
+	 */
+	if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
+		if (system_supports_ext4dev() || !system_supports_ext4())
+			return -BLKID_ERR_PARAM;
+	}
+
+	ext_get_info(pr, 4, es);
+	return 0;
+}
+
+#define BLKID_EXT_MAGICS \
+	{ \
+		{	 \
+			.magic = EXT_SB_MAGIC, \
+			.len = sizeof(EXT_SB_MAGIC) - 1, \
+			.kboff = EXT_SB_KBOFF, \
+			.sboff = EXT_MAG_OFF \
+		}, \
+		{ NULL } \
+	}
+
+const struct blkid_idinfo jbd_idinfo =
+{
+	.name		= "jbd",
+	.usage		= BLKID_USAGE_OTHER,
+	.probefunc	= probe_jbd,
+	.magics		= BLKID_EXT_MAGICS
+};
+
+const struct blkid_idinfo ext2_idinfo =
+{
+	.name		= "ext2",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ext2,
+	.magics		= BLKID_EXT_MAGICS
+};
+
+const struct blkid_idinfo ext3_idinfo =
+{
+	.name		= "ext3",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ext3,
+	.magics		= BLKID_EXT_MAGICS
+};
+
+const struct blkid_idinfo ext4_idinfo =
+{
+	.name		= "ext4",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ext4,
+	.magics		= BLKID_EXT_MAGICS
+};
+
+const struct blkid_idinfo ext4dev_idinfo =
+{
+	.name		= "ext4dev",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ext4dev,
+	.magics		= BLKID_EXT_MAGICS
+};
+
diff --git a/libblkid/f2fs.c b/libblkid/f2fs.c
new file mode 100644
index 0000000..1543a7a
--- /dev/null
+++ b/libblkid/f2fs.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2013 Alejandro Martinez Ruiz <alex@nowcomputing.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "superblocks.h"
+
+#define F2FS_MAGIC		"\x10\x20\xF5\xF2"
+#define F2FS_MAGIC_OFF		0
+#define F2FS_UUID_SIZE		16
+#define F2FS_LABEL_SIZE		512
+#define F2FS_SB1_OFF		0x400
+#define F2FS_SB1_KBOFF		(F2FS_SB1_OFF >> 10)
+#define F2FS_SB2_OFF		0x1400
+#define F2FS_SB2_KBOFF		(F2FS_SB2_OFF >> 10)
+
+struct f2fs_super_block {					/* According to version 1.1 */
+/* 0x00 */	uint32_t	magic;				/* Magic Number */
+/* 0x04 */	uint16_t	major_ver;			/* Major Version */
+/* 0x06 */	uint16_t	minor_ver;			/* Minor Version */
+/* 0x08 */	uint32_t	log_sectorsize;			/* log2 sector size in bytes */
+/* 0x0C */	uint32_t	log_sectors_per_block;		/* log2 # of sectors per block */
+/* 0x10 */	uint32_t	log_blocksize;			/* log2 block size in bytes */
+/* 0x14 */	uint32_t	log_blocks_per_seg;		/* log2 # of blocks per segment */
+/* 0x18 */	uint32_t	segs_per_sec;			/* # of segments per section */
+/* 0x1C */	uint32_t	secs_per_zone;			/* # of sections per zone */
+/* 0x20 */	uint32_t	checksum_offset;		/* checksum offset inside super block */
+/* 0x24 */	uint64_t	block_count;			/* total # of user blocks */
+/* 0x2C */	uint32_t	section_count;			/* total # of sections */
+/* 0x30 */	uint32_t	segment_count;			/* total # of segments */
+/* 0x34 */	uint32_t	segment_count_ckpt;		/* # of segments for checkpoint */
+/* 0x38 */	uint32_t	segment_count_sit;		/* # of segments for SIT */
+/* 0x3C */	uint32_t	segment_count_nat;		/* # of segments for NAT */
+/* 0x40 */	uint32_t	segment_count_ssa;		/* # of segments for SSA */
+/* 0x44 */	uint32_t	segment_count_main;		/* # of segments for main area */
+/* 0x48 */	uint32_t	segment0_blkaddr;		/* start block address of segment 0 */
+/* 0x4C */	uint32_t	cp_blkaddr;			/* start block address of checkpoint */
+/* 0x50 */	uint32_t	sit_blkaddr;			/* start block address of SIT */
+/* 0x54 */	uint32_t	nat_blkaddr;			/* start block address of NAT */
+/* 0x58 */	uint32_t	ssa_blkaddr;			/* start block address of SSA */
+/* 0x5C */	uint32_t	main_blkaddr;			/* start block address of main area */
+/* 0x60 */	uint32_t	root_ino;			/* root inode number */
+/* 0x64 */	uint32_t	node_ino;			/* node inode number */
+/* 0x68 */	uint32_t	meta_ino;			/* meta inode number */
+/* 0x6C */	uint8_t		uuid[F2FS_UUID_SIZE];		/* 128-bit uuid for volume */
+/* 0x7C */	uint16_t	volume_name[F2FS_LABEL_SIZE];	/* volume name */
+#if 0
+/* 0x47C */	uint32_t	extension_count;		/* # of extensions below */
+/* 0x480 */	uint8_t		extension_list[64][8];		/* extension array */
+#endif
+} __attribute__((packed));
+
+static int probe_f2fs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct f2fs_super_block *sb;
+	uint16_t major, minor;
+
+	sb = blkid_probe_get_sb(pr, mag, struct f2fs_super_block);
+	if (!sb)
+		return -1;
+
+	major = le16_to_cpu(sb->major_ver);
+	minor = le16_to_cpu(sb->minor_ver);
+
+	/* For version 1.0 we cannot know the correct sb structure */
+	if (major == 1 && minor == 0)
+		return 0;
+
+	if (*((unsigned char *) sb->volume_name))
+		blkid_probe_set_utf8label(pr, (unsigned char *) sb->volume_name,
+						sizeof(sb->volume_name),
+						BLKID_ENC_UTF16LE);
+
+	blkid_probe_set_uuid(pr, sb->uuid);
+	blkid_probe_sprintf_version(pr, "%u.%u", major, minor);
+	return 0;
+}
+
+const struct blkid_idinfo f2fs_idinfo =
+{
+	.name           = "f2fs",
+	.usage          = BLKID_USAGE_FILESYSTEM,
+	.probefunc      = probe_f2fs,
+	.magics         =
+        {
+		{
+			.magic = F2FS_MAGIC,
+			.len = 4,
+			.kboff = F2FS_SB1_KBOFF,
+			.sboff = F2FS_MAGIC_OFF
+		},
+		{ NULL }
+	}
+};
diff --git a/libblkid/fileutils.c b/libblkid/fileutils.c
new file mode 100644
index 0000000..ebfb128
--- /dev/null
+++ b/libblkid/fileutils.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "c.h"
+#include "fileutils.h"
+#include "pathnames.h"
+#include "xalloc.h"
+
+#define _PATH_TMP "/tmp"
+/* Create open temporary file in safe way.  Please notice that the
+ * file permissions are -rw------- by default. */
+int xmkstemp(char **tmpname, char *dir)
+{
+	char *localtmp;
+	char *tmpenv;
+	mode_t old_mode;
+	int fd;
+
+	/* Some use cases must be capable of being moved atomically
+	 * with rename(2), which is the reason why dir is here.  */
+	if (dir != NULL)
+		tmpenv = dir;
+	else
+		tmpenv = getenv("TMPDIR");
+
+	if (tmpenv)
+		xasprintf(&localtmp, "%s/%s.XXXXXX", tmpenv,
+			  program_invocation_short_name);
+	else
+		xasprintf(&localtmp, "%s/%s.XXXXXX", _PATH_TMP,
+			  program_invocation_short_name);
+	old_mode = umask(077);
+	fd = mkstemp(localtmp);
+	umask(old_mode);
+	if (fd == -1) {
+		free(localtmp);
+		localtmp = NULL;
+	}
+	*tmpname = localtmp;
+	return fd;
+}
+
+/*
+ * portable getdtablesize()
+ */
+int get_fd_tabsize(void)
+{
+	int m;
+
+#if defined(HAVE_GETDTABLESIZE)
+	m = getdtablesize();
+#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
+	struct rlimit rl;
+
+	getrlimit(RLIMIT_NOFILE, &rl);
+	m = rl.rlim_cur;
+#elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
+	m = sysconf(_SC_OPEN_MAX);
+#else
+	m = OPEN_MAX;
+#endif
+	return m;
+}
+
+#ifdef TEST_PROGRAM
+int main(void)
+{
+	FILE *f;
+	char *tmpname;
+	f = xfmkstemp(&tmpname, NULL);
+	unlink(tmpname);
+	free(tmpname);
+	fclose(f);
+	return EXIT_FAILURE;
+}
+#endif
diff --git a/libblkid/fileutils.h b/libblkid/fileutils.h
new file mode 100644
index 0000000..cf29e1b
--- /dev/null
+++ b/libblkid/fileutils.h
@@ -0,0 +1,23 @@
+#ifndef UTIL_LINUX_FILEUTILS
+#define UTIL_LINUX_FILEUTILS
+
+extern int xmkstemp(char **tmpname, char *dir);
+
+static inline FILE *xfmkstemp(char **tmpname, char *dir)
+{
+	int fd;
+	FILE *ret;
+	fd = xmkstemp(tmpname, dir);
+	if (fd == -1) {
+		return NULL;
+	}
+	if (!(ret = fdopen(fd, "w+"))) {
+		close(fd);
+		return NULL;
+	}
+	return ret;
+}
+
+extern int get_fd_tabsize(void);
+
+#endif /* UTIL_LINUX_FILEUTILS */
diff --git a/libblkid/getsize.c b/libblkid/getsize.c
new file mode 100644
index 0000000..abe6ebc
--- /dev/null
+++ b/libblkid/getsize.c
@@ -0,0 +1,34 @@
+/*
+ * getsize.c --- get the size of a partition.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "blkidP.h"
+
+/**
+ * blkid_get_dev_size:
+ * @fd: file descriptor
+ *
+ * Returns: size (in bytes) of the block device or size of the regular file or 0.
+ */
+blkid_loff_t blkid_get_dev_size(int fd)
+{
+	unsigned long long bytes;
+
+	if (blkdev_get_size(fd, &bytes))
+		return 0;
+
+	return bytes;
+}
+
diff --git a/libblkid/gfs.c b/libblkid/gfs.c
new file mode 100644
index 0000000..b2c0163
--- /dev/null
+++ b/libblkid/gfs.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/* Common gfs/gfs2 constants: */
+#define GFS_LOCKNAME_LEN        64
+
+/* gfs1 constants: */
+#define GFS_FORMAT_FS           1309
+#define GFS_FORMAT_MULTI        1401
+/* gfs2 constants: */
+#define GFS2_FORMAT_FS          1801
+#define GFS2_FORMAT_MULTI       1900
+
+struct gfs2_meta_header {
+	uint32_t mh_magic;
+	uint32_t mh_type;
+	uint64_t __pad0;          /* Was generation number in gfs1 */
+	uint32_t mh_format;
+	uint32_t __pad1;          /* Was incarnation number in gfs1 */
+};
+
+struct gfs2_inum {
+	uint64_t no_formal_ino;
+	uint64_t no_addr;
+};
+
+struct gfs2_sb {
+	struct gfs2_meta_header sb_header;
+
+	uint32_t sb_fs_format;
+	uint32_t sb_multihost_format;
+	uint32_t  __pad0;  /* Was superblock flags in gfs1 */
+
+	uint32_t sb_bsize;
+	uint32_t sb_bsize_shift;
+	uint32_t __pad1;   /* Was journal segment size in gfs1 */
+
+	struct gfs2_inum sb_master_dir; /* Was jindex dinode in gfs1 */
+	struct gfs2_inum __pad2; /* Was rindex dinode in gfs1 */
+	struct gfs2_inum sb_root_dir;
+
+	char sb_lockproto[GFS_LOCKNAME_LEN];
+	char sb_locktable[GFS_LOCKNAME_LEN];
+
+	struct gfs2_inum __pad3; /* Was quota inode in gfs1 */
+	struct gfs2_inum __pad4; /* Was licence inode in gfs1 */
+	uint8_t sb_uuid[16]; /* The UUID maybe 0 for backwards compat */
+} __attribute__((packed));
+
+static int probe_gfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct gfs2_sb *sbd;
+
+	sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb);
+	if (!sbd)
+		return -1;
+
+	if (be32_to_cpu(sbd->sb_fs_format) == GFS_FORMAT_FS &&
+	    be32_to_cpu(sbd->sb_multihost_format) == GFS_FORMAT_MULTI)
+	{
+		if (*sbd->sb_locktable)
+			blkid_probe_set_label(pr,
+				(unsigned char *) sbd->sb_locktable,
+				sizeof(sbd->sb_locktable));
+
+		blkid_probe_set_uuid(pr, sbd->sb_uuid);
+		return 0;
+	}
+
+	return -1;
+}
+
+static int probe_gfs2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct gfs2_sb *sbd;
+
+	sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb);
+	if (!sbd)
+		return -1;
+
+	if (be32_to_cpu(sbd->sb_fs_format) == GFS2_FORMAT_FS &&
+	    be32_to_cpu(sbd->sb_multihost_format) == GFS2_FORMAT_MULTI)
+	{
+		if (*sbd->sb_locktable)
+			blkid_probe_set_label(pr,
+				(unsigned char *) sbd->sb_locktable,
+				sizeof(sbd->sb_locktable));
+		blkid_probe_set_uuid(pr, sbd->sb_uuid);
+		blkid_probe_set_version(pr, "1");
+		return 0;
+	}
+	return -1;
+}
+
+const struct blkid_idinfo gfs_idinfo =
+{
+	.name		= "gfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_gfs,
+	.minsz		= 32 * 1024 * 1024,	/* minimal size of GFS journal */
+	.magics		=
+	{
+		{ .magic = "\x01\x16\x19\x70", .len = 4, .kboff = 64 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo gfs2_idinfo =
+{
+	.name		= "gfs2",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_gfs2,
+	.minsz		= 32 * 1024 * 1024,	/* minimal size of GFS journal */
+	.magics		=
+	{
+		{ .magic = "\x01\x16\x19\x70", .len = 4, .kboff = 64 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/gpt.c b/libblkid/gpt.c
new file mode 100644
index 0000000..7288d68
--- /dev/null
+++ b/libblkid/gpt.c
@@ -0,0 +1,412 @@
+/*
+ * EFI GPT partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * This code is not copy & past from any other implementation.
+ *
+ * For more information about GPT start your study at:
+ * http://en.wikipedia.org/wiki/GUID_Partition_Table
+ * http://technet.microsoft.com/en-us/library/cc739412(WS.10).aspx
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "partitions.h"
+#include "crc32.h"
+#include "dos.h"
+
+#define GPT_PRIMARY_LBA	1
+
+/* Signature - “EFI PART” */
+#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
+#define GPT_HEADER_SIGNATURE_STR "EFI PART"
+
+/* basic types */
+typedef uint16_t efi_char16_t;
+
+/* UUID */
+typedef struct {
+	uint32_t time_low;
+	uint16_t time_mid;
+	uint16_t time_hi_and_version;
+	uint8_t clock_seq_hi;
+	uint8_t clock_seq_low;
+	uint8_t node[6];
+} efi_guid_t;
+
+
+#define GPT_UNUSED_ENTRY_GUID \
+	    ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
+	                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
+struct gpt_header {
+	uint64_t	signature;		/* "EFI PART" */
+	uint32_t	revision;
+	uint32_t	header_size;		/* usually 92 bytes */
+	uint32_t	header_crc32;		/* checksum of header with this
+						 * field zeroed during calculation */
+	uint32_t	reserved1;
+
+	uint64_t	my_lba;			/* location of this header copy */
+	uint64_t	alternate_lba;		/* location of the other header copy */
+	uint64_t	first_usable_lba;	/* lirst usable LBA for partitions */
+	uint64_t	last_usable_lba;	/* last usable LBA for partitions */
+
+	efi_guid_t	disk_guid;		/* disk UUID */
+
+	uint64_t	partition_entries_lba;	/* always 2 in primary header copy */
+	uint32_t	num_partition_entries;
+	uint32_t	sizeof_partition_entry;
+	uint32_t	partition_entry_array_crc32;
+
+	/*
+	 * The rest of the block is reserved by UEFI and must be zero. EFI
+	 * standard handles this by:
+	 *
+	 * uint8_t		reserved2[ BLKSSZGET - 92 ];
+	 *
+	 * This definition is useless in practice. It is necessary to read
+	 * whole block from the device rather than sizeof(struct gpt_header)
+	 * only.
+	 */
+} __attribute__ ((packed));
+
+/*** not used
+struct gpt_entry_attributes {
+	uint64_t	required_to_function:1;
+	uint64_t	reserved:47;
+        uint64_t	type_guid_specific:16;
+} __attribute__ ((packed));
+***/
+
+struct gpt_entry {
+	efi_guid_t	partition_type_guid;	/* type UUID */
+	efi_guid_t	unique_partition_guid;	/* partition UUID */
+	uint64_t	starting_lba;
+	uint64_t	ending_lba;
+
+	/*struct gpt_entry_attributes	attributes;*/
+
+	uint64_t	attributes;
+
+	efi_char16_t	partition_name[72 / sizeof(efi_char16_t)]; /* UTF-16LE string*/
+} __attribute__ ((packed));
+
+
+/*
+ * EFI uses crc32 with ~0 seed and xor's with ~0 at the end.
+ */
+static inline uint32_t count_crc32(const unsigned char *buf, size_t len)
+{
+	return (crc32(~0L, buf, len) ^ ~0L);
+}
+
+static inline unsigned char *get_lba_buffer(blkid_probe pr,
+					uint64_t lba, size_t bytes)
+{
+	return blkid_probe_get_buffer(pr,
+			blkid_probe_get_sectorsize(pr) * lba, bytes);
+}
+
+static inline int guidcmp(efi_guid_t left, efi_guid_t right)
+{
+	return memcmp(&left, &right, sizeof (efi_guid_t));
+}
+
+/*
+ * UUID is traditionally 16 byte big-endian array, except Intel EFI
+ * specification where the UUID is a structure of little-endian fields.
+ */
+static void swap_efi_guid(efi_guid_t *uid)
+{
+	uid->time_low = swab32(uid->time_low);
+	uid->time_mid = swab16(uid->time_mid);
+	uid->time_hi_and_version = swab16(uid->time_hi_and_version);
+}
+
+static int last_lba(blkid_probe pr, uint64_t *lba)
+{
+	blkid_loff_t sz = blkid_probe_get_size(pr);
+	unsigned int ssz = blkid_probe_get_sectorsize(pr);
+
+	if (sz < ssz)
+		return -1;
+
+	*lba = (sz / ssz) - 1ULL;
+	return 0;
+}
+
+/*
+ * Protective (legacy) MBR.
+ *
+ * This MBR contains standard DOS partition table with a single partition, type
+ * of 0xEE.  The partition usually encompassing the entire GPT drive - or 2TiB
+ * for large disks.
+ *
+ * Note that Apple uses GPT/MBR hybrid disks, where the DOS partition table is
+ * synchronized with GPT. This synchronization has many restriction of course
+ * (due DOS PT limitations).
+ *
+ * Note that the PMBR detection is optional (enabled by default) and could be
+ * disabled by BLKID_PARTS_FOPCE_GPT flag (see also blkid_paertitions_set_flags()).
+ */
+static int is_pmbr_valid(blkid_probe pr)
+{
+	int flags = blkid_partitions_get_flags(pr);
+	unsigned char *data;
+	struct dos_partition *p;
+	int i;
+
+	if (flags & BLKID_PARTS_FORCE_GPT)
+		goto ok;			/* skip PMBR check */
+
+	data = blkid_probe_get_sector(pr, 0);
+	if (!data)
+		goto failed;
+
+	if (!is_valid_mbr_signature(data))
+		goto failed;
+
+	p = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+	for (i = 0; i < 4; i++, p++) {
+		if (p->sys_type == BLKID_GPT_PARTITION)
+			goto ok;
+	}
+failed:
+	return 0;
+ok:
+	return 1;
+}
+
+/*
+ * Reads GPT header to @hdr and returns a pointer to @hdr or NULL in case of
+ * error. The function also returns GPT entries in @ents.
+ *
+ * Note, this function does not allocate any memory. The GPT header has fixed
+ * size so we use stack, and @ents returns memory from libblkid buffer (so the
+ * next blkid_probe_get_buffer() will overwrite this buffer).
+ *
+ * This function checks validity of header and entries array. A corrupted
+ * header is not returned.
+ */
+static struct gpt_header *get_gpt_header(
+				blkid_probe pr, struct gpt_header *hdr,
+				struct gpt_entry **ents, uint64_t lba,
+				uint64_t lastlba)
+{
+	struct gpt_header *h;
+	uint32_t crc, orgcrc;
+	uint64_t lu, fu;
+	size_t esz;
+	uint32_t hsz, ssz;
+
+	ssz = blkid_probe_get_sectorsize(pr);
+
+	/* whole sector is allocated for GPT header */
+	h = (struct gpt_header *) get_lba_buffer(pr, lba, ssz);
+	if (!h)
+		return NULL;
+
+	if (le64_to_cpu(h->signature) != GPT_HEADER_SIGNATURE)
+		return NULL;
+
+	hsz = le32_to_cpu(h->header_size);
+
+	/* EFI: The HeaderSize must be greater than 92 and must be less
+	 *      than or equal to the logical block size.
+	 */
+	if (hsz > ssz || hsz < sizeof(*h))
+		return NULL;
+
+	/* Header has to be verified when header_crc32 is zero */
+	orgcrc = h->header_crc32;
+	h->header_crc32 = 0;
+	crc = count_crc32((unsigned char *) h, hsz);
+	h->header_crc32 = orgcrc;
+
+	if (crc != le32_to_cpu(orgcrc)) {
+		DBG(DEBUG_LOWPROBE, printf("GPT header corrupted\n"));
+		return NULL;
+	}
+
+	/* Valid header has to be at MyLBA */
+	if (le64_to_cpu(h->my_lba) != lba) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"GPT->MyLBA mismatch with real position\n"));
+		return NULL;
+	}
+
+	fu = le64_to_cpu(h->first_usable_lba);
+	lu = le64_to_cpu(h->last_usable_lba);
+
+	/* Check if First and Last usable LBA makes sense */
+	if (lu < fu || fu > lastlba || lu > lastlba) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"GPT->{First,Last}UsableLBA out of range\n"));
+		return NULL;
+	}
+
+	/* The header has to be outside usable range */
+	if (fu < lba && lba < lu) {
+		DBG(DEBUG_LOWPROBE, printf("GPT header is inside usable area\n"));
+		return NULL;
+	}
+
+	/* Size of blocks with GPT entries */
+	esz = le32_to_cpu(h->num_partition_entries) *
+			le32_to_cpu(h->sizeof_partition_entry);
+	if (!esz) {
+		DBG(DEBUG_LOWPROBE, printf("GPT entries undefined\n"));
+		return NULL;
+	}
+
+	/* The header seems valid, save it
+	 * (we don't care about zeros in hdr->reserved2 area) */
+	memcpy(hdr, h, sizeof(*h));
+	h = hdr;
+
+	/* Read GPT entries */
+	*ents = (struct gpt_entry *) get_lba_buffer(pr,
+				le64_to_cpu(h->partition_entries_lba), esz);
+	if (!*ents) {
+		DBG(DEBUG_LOWPROBE, printf("GPT entries unreadable\n"));
+		return NULL;
+	}
+
+	/* Validate entries */
+	crc = count_crc32((unsigned char *) *ents, esz);
+	if (crc != le32_to_cpu(h->partition_entry_array_crc32)) {
+		DBG(DEBUG_LOWPROBE, printf("GPT entries corrupted\n"));
+		return NULL;
+	}
+
+	return h;
+}
+
+static int probe_gpt_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t lastlba = 0, lba;
+	struct gpt_header hdr, *h;
+	struct gpt_entry *e;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	uint64_t fu, lu;
+	uint32_t ssf, i;
+	efi_guid_t guid;
+
+	if (last_lba(pr, &lastlba))
+		goto nothing;
+
+	if (!is_pmbr_valid(pr))
+		goto nothing;
+
+	h = get_gpt_header(pr, &hdr, &e, (lba = GPT_PRIMARY_LBA), lastlba);
+	if (!h)
+		h = get_gpt_header(pr, &hdr, &e, (lba = lastlba), lastlba);
+
+	if (!h)
+		goto nothing;
+
+	blkid_probe_use_wiper(pr, lba * blkid_probe_get_size(pr), 8);
+
+	if (blkid_probe_set_magic(pr, lba << 9,
+			      sizeof(GPT_HEADER_SIGNATURE_STR) - 1,
+			      (unsigned char *) GPT_HEADER_SIGNATURE_STR))
+		goto err;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "gpt", lba << 9);
+	if (!tab)
+		goto err;
+
+	guid = h->disk_guid;
+	swap_efi_guid(&guid);
+	blkid_parttable_set_id(tab, (const unsigned char *) &guid);
+
+	ssf = blkid_probe_get_sectorsize(pr) / 512;
+
+	fu = le64_to_cpu(h->first_usable_lba);
+	lu = le64_to_cpu(h->last_usable_lba);
+
+	for (i = 0; i < le32_to_cpu(h->num_partition_entries); i++, e++) {
+
+		blkid_partition par;
+		uint64_t start = le64_to_cpu(e->starting_lba);
+		uint64_t size = le64_to_cpu(e->ending_lba) -
+					le64_to_cpu(e->starting_lba) + 1ULL;
+
+		/* 00000000-0000-0000-0000-000000000000 entry */
+		if (!guidcmp(e->partition_type_guid, GPT_UNUSED_ENTRY_GUID)) {
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+		/* the partition has to inside usable range */
+		if (start < fu || start + size - 1 > lu) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"GPT entry[%d] overflows usable area - ignore\n",
+				i));
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab,
+					start * ssf, size * ssf);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_utf8name(par,
+			(unsigned char *) e->partition_name,
+			sizeof(e->partition_name), BLKID_ENC_UTF16LE);
+
+		guid = e->unique_partition_guid;
+		swap_efi_guid(&guid);
+		blkid_partition_set_uuid(par, (const unsigned char *) &guid);
+
+		guid = e->partition_type_guid;
+		swap_efi_guid(&guid);
+		blkid_partition_set_type_uuid(par, (const unsigned char *) &guid);
+
+		blkid_partition_set_flags(par, e->attributes);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+const struct blkid_idinfo gpt_pt_idinfo =
+{
+	.name		= "gpt",
+	.probefunc	= probe_gpt_pt,
+	.minsz		= 1024 * 1440 + 1,	/* ignore floppies */
+
+	/*
+	 * It would be possible to check for DOS signature (0xAA55), but
+	 * unfortunately almost all EFI GPT implemenations allow to optionaly
+	 * skip the legacy MBR. We follows this behavior and MBR is optional.
+	 * See is_valid_pmbr().
+	 *
+	 * It means we have to always call probe_gpt_pt().
+	 */
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/hfs.c b/libblkid/hfs.c
new file mode 100644
index 0000000..6d960e9
--- /dev/null
+++ b/libblkid/hfs.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "superblocks.h"
+#include "md5.h"
+
+/* HFS / HFS+ */
+struct hfs_finder_info {
+        uint32_t        boot_folder;
+        uint32_t        start_app;
+        uint32_t        open_folder;
+        uint32_t        os9_folder;
+        uint32_t        reserved;
+        uint32_t        osx_folder;
+        uint8_t         id[8];
+} __attribute__((packed));
+
+struct hfs_mdb {
+        uint8_t         signature[2];
+        uint32_t        cr_date;
+        uint32_t        ls_Mod;
+        uint16_t        atrb;
+        uint16_t        nm_fls;
+        uint16_t        vbm_st;
+        uint16_t        alloc_ptr;
+        uint16_t        nm_al_blks;
+        uint32_t        al_blk_size;
+        uint32_t        clp_size;
+        uint16_t        al_bl_st;
+        uint32_t        nxt_cnid;
+        uint16_t        free_bks;
+        uint8_t         label_len;
+        uint8_t         label[27];
+        uint32_t        vol_bkup;
+        uint16_t        vol_seq_num;
+        uint32_t        wr_cnt;
+        uint32_t        xt_clump_size;
+        uint32_t        ct_clump_size;
+        uint16_t        num_root_dirs;
+        uint32_t        file_count;
+        uint32_t        dir_count;
+        struct hfs_finder_info finder_info;
+        uint8_t         embed_sig[2];
+        uint16_t        embed_startblock;
+        uint16_t        embed_blockcount;
+} __attribute__((packed));
+
+
+#define HFS_NODE_LEAF			0xff
+#define HFSPLUS_POR_CNID		1
+
+struct hfsplus_bnode_descriptor {
+	uint32_t		next;
+	uint32_t		prev;
+	uint8_t		type;
+	uint8_t		height;
+	uint16_t		num_recs;
+	uint16_t		reserved;
+} __attribute__((packed));
+
+struct hfsplus_bheader_record {
+	uint16_t		depth;
+	uint32_t		root;
+	uint32_t		leaf_count;
+	uint32_t		leaf_head;
+	uint32_t		leaf_tail;
+	uint16_t		node_size;
+} __attribute__((packed));
+
+struct hfsplus_catalog_key {
+	uint16_t	key_len;
+	uint32_t	parent_id;
+	uint16_t	unicode_len;
+	uint8_t		unicode[255 * 2];
+} __attribute__((packed));
+
+struct hfsplus_extent {
+	uint32_t		start_block;
+	uint32_t		block_count;
+} __attribute__((packed));
+
+#define HFSPLUS_EXTENT_COUNT		8
+struct hfsplus_fork {
+	uint64_t		total_size;
+	uint32_t		clump_size;
+	uint32_t		total_blocks;
+	struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+} __attribute__((packed));
+
+struct hfsplus_vol_header {
+	uint8_t		signature[2];
+	uint16_t		version;
+	uint32_t		attributes;
+	uint32_t		last_mount_vers;
+	uint32_t		reserved;
+	uint32_t		create_date;
+	uint32_t		modify_date;
+	uint32_t		backup_date;
+	uint32_t		checked_date;
+	uint32_t		file_count;
+	uint32_t		folder_count;
+	uint32_t		blocksize;
+	uint32_t		total_blocks;
+	uint32_t		free_blocks;
+	uint32_t		next_alloc;
+	uint32_t		rsrc_clump_sz;
+	uint32_t		data_clump_sz;
+	uint32_t		next_cnid;
+	uint32_t		write_count;
+	uint64_t		encodings_bmp;
+	struct hfs_finder_info finder_info;
+	struct hfsplus_fork alloc_file;
+	struct hfsplus_fork ext_file;
+	struct hfsplus_fork cat_file;
+	struct hfsplus_fork attr_file;
+	struct hfsplus_fork start_file;
+}  __attribute__((packed));
+
+#define HFSPLUS_SECTOR_SIZE        512
+
+static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t len)
+{
+	static unsigned char const hash_init[MD5LENGTH] = {
+		0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
+		0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
+	};
+	unsigned char uuid[MD5LENGTH];
+	struct MD5Context md5c;
+
+	if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
+		return -1;
+	MD5Init(&md5c);
+	MD5Update(&md5c, hash_init, MD5LENGTH);
+	MD5Update(&md5c, hfs_info, len);
+	MD5Final(uuid, &md5c);
+	uuid[6] = 0x30 | (uuid[6] & 0x0f);
+	uuid[8] = 0x80 | (uuid[8] & 0x3f);
+	return blkid_probe_set_uuid(pr, uuid);
+}
+
+static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct hfs_mdb	*hfs;
+
+	hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
+	if (!hfs)
+		return -1;
+
+	if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
+	    (memcmp(hfs->embed_sig, "HX", 2) == 0))
+		return 1;	/* Not hfs, but an embedded HFS+ */
+
+	hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
+
+	blkid_probe_set_label(pr, hfs->label, hfs->label_len);
+	return 0;
+}
+
+static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+	struct hfsplus_bnode_descriptor *descr;
+	struct hfsplus_bheader_record *bnode;
+	struct hfsplus_catalog_key *key;
+	struct hfsplus_vol_header *hfsplus;
+	struct hfs_mdb *sbd;
+	unsigned int alloc_block_size;
+	unsigned int alloc_first_block;
+	unsigned int embed_first_block;
+	unsigned int off = 0;
+	unsigned int blocksize;
+	unsigned int cat_block;
+	unsigned int ext_block_start;
+	unsigned int ext_block_count;
+	unsigned int record_count;
+	unsigned int leaf_node_head;
+	unsigned int leaf_node_count;
+	unsigned int leaf_node_size;
+	unsigned int leaf_block;
+	int ext;
+	uint64_t leaf_off;
+	unsigned char *buf;
+
+	sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
+	if (!sbd)
+		return -1;
+
+	/* Check for a HFS+ volume embedded in a HFS volume */
+	if (memcmp(sbd->signature, "BD", 2) == 0) {
+		if ((memcmp(sbd->embed_sig, "H+", 2) != 0) &&
+		    (memcmp(sbd->embed_sig, "HX", 2) != 0))
+			/* This must be an HFS volume, so fail */
+			return 1;
+
+		alloc_block_size = be32_to_cpu(sbd->al_blk_size);
+		alloc_first_block = be16_to_cpu(sbd->al_bl_st);
+		embed_first_block = be16_to_cpu(sbd->embed_startblock);
+		off = (alloc_first_block * 512) +
+			(embed_first_block * alloc_block_size);
+
+		buf = blkid_probe_get_buffer(pr,
+				off + (mag->kboff * 1024),
+				sizeof(struct hfsplus_vol_header));
+		hfsplus = (struct hfsplus_vol_header *) buf;
+
+	} else
+		hfsplus = blkid_probe_get_sb(pr, mag,
+				struct hfsplus_vol_header);
+
+	if (!hfsplus)
+		return -1;
+
+	if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
+	    (memcmp(hfsplus->signature, "HX", 2) != 0))
+		return 1;
+
+	hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
+
+	blocksize = be32_to_cpu(hfsplus->blocksize);
+	if (blocksize < HFSPLUS_SECTOR_SIZE)
+		return -1;
+
+	memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+	cat_block = be32_to_cpu(extents[0].start_block);
+
+	buf = blkid_probe_get_buffer(pr,
+			off + ((blkid_loff_t) cat_block * blocksize), 0x2000);
+	if (!buf)
+		return 0;
+
+	bnode = (struct hfsplus_bheader_record *)
+		&buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+	leaf_node_head = be32_to_cpu(bnode->leaf_head);
+	leaf_node_size = be16_to_cpu(bnode->node_size);
+	leaf_node_count = be32_to_cpu(bnode->leaf_count);
+	if (leaf_node_count == 0)
+		return 0;
+
+	leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
+
+	/* get physical location */
+	for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
+		ext_block_start = be32_to_cpu(extents[ext].start_block);
+		ext_block_count = be32_to_cpu(extents[ext].block_count);
+		if (ext_block_count == 0)
+			return 0;
+
+		/* this is our extent */
+		if (leaf_block < ext_block_count)
+			break;
+
+		leaf_block -= ext_block_count;
+	}
+	if (ext == HFSPLUS_EXTENT_COUNT)
+		return 0;
+
+	leaf_off = (ext_block_start + leaf_block) * blocksize;
+
+	buf = blkid_probe_get_buffer(pr,
+				(blkid_loff_t) off + leaf_off,
+				leaf_node_size);
+	if (!buf)
+		return 0;
+
+	descr = (struct hfsplus_bnode_descriptor *) buf;
+	record_count = be16_to_cpu(descr->num_recs);
+	if (record_count == 0)
+		return 0;
+
+	if (descr->type != HFS_NODE_LEAF)
+		return 0;
+
+	key = (struct hfsplus_catalog_key *)
+		&buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+	if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID)
+		return 0;
+
+	blkid_probe_set_utf8label(pr, key->unicode,
+			be16_to_cpu(key->unicode_len) * 2,
+			BLKID_ENC_UTF16BE);
+	return 0;
+}
+
+const struct blkid_idinfo hfs_idinfo =
+{
+	.name		= "hfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_hfs,
+	.flags		= BLKID_IDINFO_TOLERANT,
+	.magics		=
+	{
+		{ .magic = "BD", .len = 2, .kboff = 1 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo hfsplus_idinfo =
+{
+	.name		= "hfsplus",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_hfsplus,
+	.magics		=
+	{
+		{ .magic = "BD", .len = 2, .kboff = 1 },
+		{ .magic = "H+", .len = 2, .kboff = 1 },
+		{ .magic = "HX", .len = 2, .kboff = 1 },
+		{ NULL }
+	}
+};
diff --git a/libblkid/highpoint_raid.c b/libblkid/highpoint_raid.c
new file mode 100644
index 0000000..0b41344
--- /dev/null
+++ b/libblkid/highpoint_raid.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct hpt45x_metadata {
+	uint32_t	magic;
+};
+
+#define HPT45X_MAGIC_OK			0x5a7816f3
+#define HPT45X_MAGIC_BAD		0x5a7816fd
+
+static int probe_highpoint45x(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct hpt45x_metadata *hpt;
+	uint64_t off;
+	uint32_t magic;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 11) * 0x200;
+	hpt = (struct hpt45x_metadata *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct hpt45x_metadata));
+	if (!hpt)
+		return -1;
+	magic = le32_to_cpu(hpt->magic);
+	if (magic != HPT45X_MAGIC_OK && magic != HPT45X_MAGIC_BAD)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(hpt->magic),
+				(unsigned char *) &hpt->magic))
+		return -1;
+	return 0;
+}
+
+static int probe_highpoint37x(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+	return 0;
+}
+
+
+const struct blkid_idinfo highpoint45x_idinfo = {
+	.name		= "hpt45x_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_highpoint45x,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+const struct blkid_idinfo highpoint37x_idinfo = {
+	.name		= "hpt37x_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_highpoint37x,
+	.magics		= {
+		/*
+		 * Superblok offset:                      4608 bytes  (9 sectors)
+		 * Magic string offset within superblock:   32 bytes
+		 *
+		 * kboff = (4608 + 32) / 1024
+		 * sboff = (4608 + 32) % kboff
+		 */
+		{ .magic = "\xf0\x16\x78\x5a", .len = 4, .kboff = 4, .sboff = 544 },
+		{ .magic = "\xfd\x16\x78\x5a", .len = 4, .kboff = 4, .sboff = 544 },
+		{ NULL }
+	}
+};
+
+
diff --git a/libblkid/hpfs.c b/libblkid/hpfs.c
new file mode 100644
index 0000000..f9b851a
--- /dev/null
+++ b/libblkid/hpfs.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct hpfs_boot_block
+{
+	uint8_t		jmp[3];
+	uint8_t		oem_id[8];
+	uint8_t		bytes_per_sector[2];
+	uint8_t		sectors_per_cluster;
+	uint8_t		n_reserved_sectors[2];
+	uint8_t		n_fats;
+	uint8_t		n_rootdir_entries[2];
+	uint8_t		n_sectors_s[2];
+	uint8_t		media_byte;
+	uint16_t	sectors_per_fat;
+	uint16_t	sectors_per_track;
+	uint16_t	heads_per_cyl;
+	uint32_t	n_hidden_sectors;
+	uint32_t	n_sectors_l;
+	uint8_t		drive_number;
+	uint8_t		mbz;
+	uint8_t		sig_28h;
+	uint8_t		vol_serno[4];
+	uint8_t		vol_label[11];
+	uint8_t		sig_hpfs[8];
+	uint8_t		pad[448];
+	uint8_t		magic[2];
+} __attribute__((packed));
+
+struct hpfs_super_block
+{
+	uint8_t		magic[4];
+	uint8_t		magic1[4];
+	uint8_t		version;
+} __attribute__((packed));
+
+struct hpfs_spare_super
+{
+	uint8_t		magic[4];
+	uint8_t		magic1[4];
+} __attribute__((packed));
+
+
+#define HPFS_SB_OFFSET			0x2000
+#define HPFS_SBSPARE_OFFSET		0x2200
+
+static int probe_hpfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct hpfs_super_block *hs;
+	struct hpfs_spare_super *hss;
+	struct hpfs_boot_block *hbb;
+	uint8_t version;
+
+	/* super block */
+	hs = blkid_probe_get_sb(pr, mag, struct hpfs_super_block);
+	if (!hs)
+		return -1;
+	version = hs->version;
+
+	/* spare super block */
+	hss = (struct hpfs_spare_super *)
+			blkid_probe_get_buffer(pr,
+				HPFS_SBSPARE_OFFSET,
+				sizeof(struct hpfs_spare_super));
+	if (!hss)
+		return -1;
+	if (memcmp(hss->magic, "\x49\x18\x91\xf9", 4) != 0)
+		return -1;
+
+	/* boot block (with UUID and LABEL) */
+	hbb = (struct hpfs_boot_block *)
+			blkid_probe_get_buffer(pr,
+				0,
+				sizeof(struct hpfs_boot_block));
+	if (!hbb)
+		return -1;
+	if (memcmp(hbb->magic, "\x55\xaa", 2) == 0 &&
+	    memcmp(hbb->sig_hpfs, "HPFS", 4) == 0 &&
+	    hbb->sig_28h == 0x28) {
+		blkid_probe_set_label(pr, hbb->vol_label, sizeof(hbb->vol_label));
+		blkid_probe_sprintf_uuid(pr,
+				hbb->vol_serno, sizeof(hbb->vol_serno),
+				"%02X%02X-%02X%02X",
+				hbb->vol_serno[3], hbb->vol_serno[2],
+				hbb->vol_serno[1], hbb->vol_serno[0]);
+	}
+	blkid_probe_sprintf_version(pr, "%u", version);
+
+	return 0;
+}
+
+const struct blkid_idinfo hpfs_idinfo =
+{
+	.name		= "hpfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_hpfs,
+	.magics		=
+	{
+		{
+		  .magic = "\x49\xe8\x95\xf9",
+		  .len = 4,
+		  .kboff = (HPFS_SB_OFFSET >> 10)
+		},
+		{ NULL }
+	}
+};
+
+
diff --git a/libblkid/ioctl.c b/libblkid/ioctl.c
new file mode 100644
index 0000000..3aba09e
--- /dev/null
+++ b/libblkid/ioctl.c
@@ -0,0 +1,74 @@
+/*
+ * ioctl based topology -- gathers topology information
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "topology.h"
+
+/*
+ * ioctl topology values
+ */
+static struct topology_val {
+
+	long  ioc;
+
+	/* functions to set probing result */
+	int (*set_ulong)(blkid_probe, unsigned long);
+	int (*set_int)(blkid_probe, int);
+
+} topology_vals[] = {
+	{ BLKALIGNOFF, NULL, blkid_topology_set_alignment_offset },
+	{ BLKIOMIN, blkid_topology_set_minimum_io_size },
+	{ BLKIOOPT, blkid_topology_set_optimal_io_size },
+	{ BLKPBSZGET, blkid_topology_set_physical_sector_size }
+	/* we read BLKSSZGET in topology.c */
+};
+
+static int probe_ioctl_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(topology_vals); i++) {
+		struct topology_val *val = &topology_vals[i];
+		int rc = 1;
+		unsigned int data;
+
+		if (ioctl(pr->fd, val->ioc, &data) == -1)
+			goto nothing;
+
+		if (val->set_int)
+			rc = val->set_int(pr, (int) data);
+		else
+			rc = val->set_ulong(pr, (unsigned long) data);
+		if (rc)
+			goto err;
+	}
+
+	return 0;
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+const struct blkid_idinfo ioctl_tp_idinfo =
+{
+	.name		= "ioctl",
+	.probefunc	= probe_ioctl_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/ismounted.c b/libblkid/ismounted.c
new file mode 100644
index 0000000..d9f1f57
--- /dev/null
+++ b/libblkid/ismounted.c
@@ -0,0 +1,387 @@
+/*
+ * ismounted.c --- Check to see if the filesystem was mounted
+ *
+ * Copyright (C) 1995,1996,1997,1998,1999,2000,2008 Theodore Ts'o.
+ *
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#include <string.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <sys/param.h>
+#ifdef __APPLE__
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#endif
+
+#include "pathnames.h"
+#include "ismounted.h"
+#include "c.h"
+#ifdef __linux__
+# include "loopdev.h"
+#endif
+
+
+
+#ifdef HAVE_MNTENT_H
+/*
+ * Helper function which checks a file in /etc/mtab format to see if a
+ * filesystem is mounted.  Returns an error if the file doesn't exist
+ * or can't be opened.
+ */
+static int check_mntent_file(const char *mtab_file, const char *file,
+				   int *mount_flags, char *mtpt, int mtlen)
+{
+	struct mntent	*mnt;
+	struct stat	st_buf;
+	int		retval = 0;
+	dev_t		file_dev=0, file_rdev=0;
+	ino_t		file_ino=0;
+	FILE		*f;
+	int		fd;
+
+	*mount_flags = 0;
+	if ((f = setmntent (mtab_file, "r")) == NULL)
+		return errno;
+
+	if (stat(file, &st_buf) == 0) {
+		if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+			file_rdev = st_buf.st_rdev;
+#endif	/* __GNU__ */
+		} else {
+			file_dev = st_buf.st_dev;
+			file_ino = st_buf.st_ino;
+		}
+	}
+
+	while ((mnt = getmntent (f)) != NULL) {
+		if (mnt->mnt_fsname[0] != '/')
+			continue;
+		if (strcmp(file, mnt->mnt_fsname) == 0)
+			break;
+		if (stat(mnt->mnt_fsname, &st_buf) != 0)
+			continue;
+
+		if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__
+			if (file_rdev && file_rdev == st_buf.st_rdev)
+				break;
+#ifdef __linux__
+			/* maybe the file is loopdev backing file */
+			if (file_dev
+			    && major(st_buf.st_rdev) == LOOPDEV_MAJOR
+			    && loopdev_is_used(mnt->mnt_fsname, file, 0, 0))
+				break;
+#endif /* __linux__ */
+#endif	/* __GNU__ */
+		} else {
+			if (file_dev && ((file_dev == st_buf.st_dev) &&
+					 (file_ino == st_buf.st_ino)))
+				break;
+		}
+	}
+
+	if (mnt == NULL) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+		/*
+		 * Do an extra check to see if this is the root device.  We
+		 * can't trust /etc/mtab, and /proc/mounts will only list
+		 * /dev/root for the root filesystem.  Argh.  Instead we
+		 * check if the given device has the same major/minor number
+		 * as the device that the root directory is on.
+		 */
+		if (file_rdev && stat("/", &st_buf) == 0 &&
+		    st_buf.st_dev == file_rdev) {
+			*mount_flags = MF_MOUNTED;
+			if (mtpt)
+				strncpy(mtpt, "/", mtlen);
+			goto is_root;
+		}
+#endif	/* __GNU__ */
+		goto errout;
+	}
+#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */
+	/* Validate the entry in case /etc/mtab is out of date */
+	/*
+	 * We need to be paranoid, because some broken distributions
+	 * (read: Slackware) don't initialize /etc/mtab before checking
+	 * all of the non-root filesystems on the disk.
+	 */
+	if (stat(mnt->mnt_dir, &st_buf) < 0) {
+		retval = errno;
+		if (retval == ENOENT) {
+#ifdef DEBUG
+			printf("Bogus entry in %s!  (%s does not exist)\n",
+			       mtab_file, mnt->mnt_dir);
+#endif /* DEBUG */
+			retval = 0;
+		}
+		goto errout;
+	}
+	if (file_rdev && (st_buf.st_dev != file_rdev)) {
+#ifdef DEBUG
+		printf("Bogus entry in %s!  (%s not mounted on %s)\n",
+		       mtab_file, file, mnt->mnt_dir);
+#endif /* DEBUG */
+		goto errout;
+	}
+#endif /* __GNU__ */
+	*mount_flags = MF_MOUNTED;
+
+#ifdef MNTOPT_RO
+	/* Check to see if the ro option is set */
+	if (hasmntopt(mnt, MNTOPT_RO))
+		*mount_flags |= MF_READONLY;
+#endif
+
+	if (mtpt)
+		strncpy(mtpt, mnt->mnt_dir, mtlen);
+	/*
+	 * Check to see if we're referring to the root filesystem.
+	 * If so, do a manual check to see if we can open /etc/mtab
+	 * read/write, since if the root is mounted read/only, the
+	 * contents of /etc/mtab may not be accurate.
+	 */
+	if (!strcmp(mnt->mnt_dir, "/")) {
+is_root:
+#define TEST_FILE "/.ismount-test-file"
+		*mount_flags |= MF_ISROOT;
+		fd = open(TEST_FILE, O_RDWR|O_CREAT, 0600);
+		if (fd < 0) {
+			if (errno == EROFS)
+				*mount_flags |= MF_READONLY;
+		} else
+			close(fd);
+		(void) unlink(TEST_FILE);
+	}
+	retval = 0;
+errout:
+	endmntent (f);
+	return retval;
+}
+
+static int check_mntent(const char *file, int *mount_flags,
+			      char *mtpt, int mtlen)
+{
+	int	retval;
+
+#ifdef DEBUG
+	retval = check_mntent_file("/tmp/mtab", file, mount_flags,
+				   mtpt, mtlen);
+	if (retval == 0)
+		return 0;
+#endif /* DEBUG */
+#ifdef __linux__
+	retval = check_mntent_file("/proc/mounts", file, mount_flags,
+				   mtpt, mtlen);
+	if (retval == 0 && (*mount_flags != 0))
+		return 0;
+	if (access("/proc/mounts", R_OK) == 0) {
+		*mount_flags = 0;
+		return retval;
+	}
+#endif /* __linux__ */
+#if defined(MOUNTED) || defined(_PATH_MOUNTED)
+#ifndef MOUNTED
+#define MOUNTED _PATH_MOUNTED
+#endif /* MOUNTED */
+	retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen);
+	return retval;
+#else
+	*mount_flags = 0;
+	return 0;
+#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */
+}
+
+#else
+#if defined(HAVE_GETMNTINFO)
+
+static int check_getmntinfo(const char *file, int *mount_flags,
+				  char *mtpt, int mtlen)
+{
+	struct statfs *mp;
+        int    len, n;
+        const  char   *s1;
+	char	*s2;
+
+        n = getmntinfo(&mp, MNT_NOWAIT);
+        if (n == 0)
+		return errno;
+
+        len = sizeof(_PATH_DEV) - 1;
+        s1 = file;
+        if (strncmp(_PATH_DEV, s1, len) == 0)
+                s1 += len;
+
+	*mount_flags = 0;
+        while (--n >= 0) {
+                s2 = mp->f_mntfromname;
+                if (strncmp(_PATH_DEV, s2, len) == 0) {
+                        s2 += len - 1;
+                        *s2 = 'r';
+                }
+                if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) {
+			*mount_flags = MF_MOUNTED;
+			break;
+		}
+                ++mp;
+	}
+	if (mtpt)
+		strncpy(mtpt, mp->f_mntonname, mtlen);
+	return 0;
+}
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+
+/*
+ * Check to see if we're dealing with the swap device.
+ */
+static int is_swap_device(const char *file)
+{
+	FILE		*f;
+	char		buf[1024], *cp;
+	dev_t		file_dev;
+	struct stat	st_buf;
+	int		ret = 0;
+
+	file_dev = 0;
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+	if ((stat(file, &st_buf) == 0) &&
+	    S_ISBLK(st_buf.st_mode))
+		file_dev = st_buf.st_rdev;
+#endif	/* __GNU__ */
+
+	if (!(f = fopen("/proc/swaps", "r")))
+		return 0;
+	/* Skip the first line */
+	if (!fgets(buf, sizeof(buf), f))
+		goto leave;
+	if (*buf && strncmp(buf, "Filename\t", 9))
+		/* Linux <=2.6.19 contained a bug in the /proc/swaps
+		 * code where the header would not be displayed
+		 */
+		goto valid_first_line;
+
+	while (fgets(buf, sizeof(buf), f)) {
+valid_first_line:
+		if ((cp = strchr(buf, ' ')) != NULL)
+			*cp = 0;
+		if ((cp = strchr(buf, '\t')) != NULL)
+			*cp = 0;
+		if (strcmp(buf, file) == 0) {
+			ret++;
+			break;
+		}
+#ifndef __GNU__
+		if (file_dev && (stat(buf, &st_buf) == 0) &&
+		    S_ISBLK(st_buf.st_mode) &&
+		    file_dev == st_buf.st_rdev) {
+			ret++;
+			break;
+		}
+#endif	/* __GNU__ */
+	}
+
+leave:
+	fclose(f);
+	return ret;
+}
+
+
+/*
+ * check_mount_point() fills determines if the device is mounted or otherwise
+ * busy, and fills in mount_flags with one or more of the following flags:
+ * MF_MOUNTED, MF_ISROOT, MF_READONLY, MF_SWAP, and MF_BUSY.  If mtpt is
+ * non-NULL, the directory where the device is mounted is copied to where mtpt
+ * is pointing, up to mtlen characters.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+int check_mount_point(const char *device, int *mount_flags,
+				  char *mtpt, int mtlen)
+{
+	struct stat	st_buf;
+	int	retval = 0;
+	int		fd;
+
+	if (is_swap_device(device)) {
+		*mount_flags = MF_MOUNTED | MF_SWAP;
+		strncpy(mtpt, "<swap>", mtlen);
+	} else {
+#ifdef HAVE_MNTENT_H
+		retval = check_mntent(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef HAVE_GETMNTINFO
+		retval = check_getmntinfo(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef __GNUC__
+ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!"
+#endif
+		*mount_flags = 0;
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+	}
+	if (retval)
+		return retval;
+
+#ifdef __linux__ /* This only works on Linux 2.6+ systems */
+	if ((stat(device, &st_buf) != 0) ||
+	    !S_ISBLK(st_buf.st_mode))
+		return 0;
+	fd = open(device, O_RDONLY | O_EXCL);
+	if (fd < 0) {
+		if (errno == EBUSY)
+			*mount_flags |= MF_BUSY;
+	} else
+		close(fd);
+#endif
+
+	return 0;
+}
+
+int is_mounted(const char *file)
+{
+	int	retval;
+	int	mount_flags = 0;
+
+	retval = check_mount_point(file, &mount_flags, NULL, 0);
+	if (retval)
+		return 0;
+	return mount_flags & MF_MOUNTED;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	int flags = 0;
+	char devname[PATH_MAX];
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s device\n", argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	if (check_mount_point(argv[1], &flags, devname, sizeof(devname)) == 0 &&
+	    (flags & MF_MOUNTED)) {
+		if (flags & MF_SWAP)
+			printf("used swap device\n");
+		else
+			printf("mounted on %s\n", devname);
+		return EXIT_SUCCESS;
+	}
+
+	printf("not mounted\n");
+	return EXIT_FAILURE;
+}
+#endif /* DEBUG */
diff --git a/libblkid/ismounted.h b/libblkid/ismounted.h
new file mode 100644
index 0000000..57918cb
--- /dev/null
+++ b/libblkid/ismounted.h
@@ -0,0 +1,14 @@
+#ifndef IS_MOUNTED_H
+#define IS_MOUNTED_H
+
+#define MF_MOUNTED	1
+#define MF_ISROOT	2
+#define MF_READONLY	4
+#define MF_SWAP		8
+#define MF_BUSY		16
+
+extern int is_mounted(const char *file);
+extern int check_mount_point(const char *device, int *mount_flags,
+				 char *mtpt, int mtlen);
+
+#endif /* IS_MOUNTED_H */
diff --git a/libblkid/iso9660.c b/libblkid/iso9660.c
new file mode 100644
index 0000000..148587b
--- /dev/null
+++ b/libblkid/iso9660.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired also by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include "superblocks.h"
+
+struct iso9660_date {
+	unsigned char year[4];
+	unsigned char month[2];
+	unsigned char day[2];
+	unsigned char hour[2];
+	unsigned char minute[2];
+	unsigned char second[2];
+	unsigned char hundredth[2];
+	unsigned char offset;
+} __attribute__ ((packed));
+
+/* PVD - Primary volume descriptor */
+struct iso_volume_descriptor {
+	unsigned char	vd_type;
+	unsigned char	vd_id[5];
+	unsigned char	vd_version;
+	unsigned char	flags;
+	unsigned char	system_id[32];
+	unsigned char	volume_id[32];
+	unsigned char	unused[8];
+	unsigned char	space_size[8];
+	unsigned char	escape_sequences[8];
+	unsigned char  unused1[222];
+	unsigned char  publisher_id[128];
+	unsigned char  unused2[128];
+	unsigned char  application_id[128];
+	unsigned char  unused3[111];
+	struct iso9660_date created;
+	struct iso9660_date modified;
+} __attribute__((packed));
+
+/* Boot Record */
+struct boot_record {
+	unsigned char	vd_type;
+	unsigned char	vd_id[5];
+	unsigned char	vd_version;
+	unsigned char	boot_system_id[32];
+	unsigned char	boot_id[32];
+	unsigned char	unused[1];
+} __attribute__((packed));
+
+#define ISO_SUPERBLOCK_OFFSET		0x8000
+#define ISO_SECTOR_SIZE			0x800
+#define ISO_VD_OFFSET			(ISO_SUPERBLOCK_OFFSET + ISO_SECTOR_SIZE)
+#define ISO_VD_BOOT_RECORD		0x0
+#define ISO_VD_SUPPLEMENTARY		0x2
+#define ISO_VD_END			0xff
+#define ISO_VD_MAX			16
+
+struct high_sierra_volume_descriptor {
+	unsigned char	foo[8];
+	unsigned char	type;
+	unsigned char	id[5];
+	unsigned char	version;
+	unsigned char	unused1;
+	unsigned char	system_id[32];
+	unsigned char   volume_id[32];
+} __attribute__((packed));
+
+/* returns 1 if the begin of @ascii is equal to @utf16 string.
+ */
+static int ascii_eq_utf16be(unsigned char *ascii,
+			unsigned char *utf16, size_t len)
+{
+	size_t a, u;
+
+	for (a = 0, u = 0; u < len; a++, u += 2) {
+		if (utf16[u] != 0x0 || ascii[a] != utf16[u + 1])
+			return 0;
+	}
+	return 1;
+}
+
+/* old High Sierra format */
+static int probe_iso9660_hsfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct high_sierra_volume_descriptor *iso;
+
+	iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor);
+	if (!iso)
+		return -1;
+
+	blkid_probe_set_version(pr, "High Sierra");
+	blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id));
+	return 0;
+}
+
+static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *date)
+{
+	unsigned char buffer[16];
+	unsigned int i, zeros = 0;
+
+	buffer[0] = date->year[0];
+	buffer[1] = date->year[1];
+	buffer[2] = date->year[2];
+	buffer[3] = date->year[3];
+	buffer[4] = date->month[0];
+	buffer[5] = date->month[1];
+	buffer[6] = date->day[0];
+	buffer[7] = date->day[1];
+	buffer[8] = date->hour[0];
+	buffer[9] = date->hour[1];
+	buffer[10] = date->minute[0];
+	buffer[11] = date->minute[1];
+	buffer[12] = date->second[0];
+	buffer[13] = date->second[1];
+	buffer[14] = date->hundredth[0];
+	buffer[15] = date->hundredth[1];
+
+	/* count the number of zeros ('0') in the date buffer */
+	for (i = 0, zeros = 0; i < sizeof(buffer); i++)
+		if (buffer[i] == '0')
+			zeros++;
+
+	/* due to the iso9660 standard if all date fields are '0' and offset is 0, the date is unset */
+	if (zeros == sizeof(buffer) && date->offset == 0)
+		return 0;
+
+	/* generate an UUID using this date and return success */
+	blkid_probe_sprintf_uuid (pr, buffer, sizeof(buffer),
+		"%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
+		buffer[0], buffer[1], buffer[2], buffer[3],
+		buffer[4], buffer[5],
+		buffer[6], buffer[7],
+		buffer[8], buffer[9],
+		buffer[10], buffer[11],
+		buffer[12], buffer[13],
+		buffer[14], buffer[15]);
+
+	return 1;
+}
+
+static int is_str_empty(const unsigned char *str, size_t len)
+{
+	size_t i;
+
+	if (!str || !*str)
+		return 1;
+
+	for (i = 0; i < len; i++)
+		if (!isspace(str[i]))
+			return 0;
+	return 1;
+}
+
+/* iso9660 [+ Microsoft Joliet Extension] */
+int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct iso_volume_descriptor *iso;
+	unsigned char label[32];
+	int i;
+	int off;
+
+	if (strcmp(mag->magic, "CDROM") == 0)
+		return probe_iso9660_hsfs(pr, mag);
+
+	iso = blkid_probe_get_sb(pr, mag, struct iso_volume_descriptor);
+	if (!iso)
+		return -1;
+
+	memcpy(label, iso->volume_id, sizeof(label));
+
+	if (!is_str_empty(iso->system_id, sizeof(iso->system_id)))
+		blkid_probe_set_id_label(pr, "SYSTEM_ID",
+				iso->system_id, sizeof(iso->system_id));
+
+	if (!is_str_empty(iso->publisher_id, sizeof(iso->publisher_id)))
+		blkid_probe_set_id_label(pr, "PUBLISHER_ID",
+				iso->publisher_id, sizeof(iso->publisher_id));
+
+	if (!is_str_empty(iso->application_id, sizeof(iso->application_id)))
+		blkid_probe_set_id_label(pr, "APPLICATION_ID",
+				iso->application_id, sizeof(iso->application_id));
+
+	/* create an UUID using the modified/created date */
+	if (! probe_iso9660_set_uuid(pr, &iso->modified))
+		probe_iso9660_set_uuid(pr, &iso->created);
+
+	/* Joliet Extension and Boot Record */
+	off = ISO_VD_OFFSET;
+	for (i = 0; i < ISO_VD_MAX; i++) {
+		struct boot_record *boot= (struct boot_record *)
+			blkid_probe_get_buffer(pr,
+					off,
+					max(sizeof(struct boot_record),
+					    sizeof(struct iso_volume_descriptor)));
+
+		if (boot == NULL || boot->vd_type == ISO_VD_END)
+			break;
+
+		if (boot->vd_type == ISO_VD_BOOT_RECORD) {
+			if (!is_str_empty(boot->boot_system_id,
+					  sizeof(boot->boot_system_id)))
+				blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID",
+							boot->boot_system_id,
+							sizeof(boot->boot_system_id));
+			off += ISO_SECTOR_SIZE;
+			continue;
+		}
+
+		/* Not a Boot record, lets see if its supplemntary volume descriptor */
+		iso = (struct iso_volume_descriptor *) boot;
+
+		if (iso->vd_type != ISO_VD_SUPPLEMENTARY) {
+			off += ISO_SECTOR_SIZE;
+			continue;
+		}
+
+		if (memcmp(iso->escape_sequences, "%/@", 3) == 0 ||
+		    memcmp(iso->escape_sequences, "%/C", 3) == 0 ||
+		    memcmp(iso->escape_sequences, "%/E", 3) == 0) {
+
+			blkid_probe_set_version(pr, "Joliet Extension");
+
+			/* Is the Joliet (UTF16BE) label equal to the label in
+			 * the PVD? If yes, use PVD label.  The Jolied version
+			 * of the label could be trimed (because UTF16..).
+			 */
+			if (ascii_eq_utf16be(label, iso->volume_id, 32))
+				break;
+
+			blkid_probe_set_utf8label(pr,
+					iso->volume_id,
+					sizeof(iso->volume_id),
+					BLKID_ENC_UTF16BE);
+			goto has_label;
+		}
+		off += ISO_SECTOR_SIZE;
+	}
+
+	/* Joliet not found, let use standard iso label */
+	blkid_probe_set_label(pr, label, sizeof(label));
+
+has_label:
+	return 0;
+}
+
+const struct blkid_idinfo iso9660_idinfo =
+{
+	.name		= "iso9660",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_iso9660,
+	.flags		= BLKID_IDINFO_TOLERANT,
+	.magics		=
+	{
+		{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/iso9660.h b/libblkid/iso9660.h
new file mode 100644
index 0000000..a8d729d
--- /dev/null
+++ b/libblkid/iso9660.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2013 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef _BLKID_ISO9660_H
+#define _BLKID_ISO9660_H
+
+#include "blkidP.h"
+
+extern int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag);
+
+#endif /* _BLKID_ISO9660_H */
diff --git a/libblkid/isw_raid.c b/libblkid/isw_raid.c
new file mode 100644
index 0000000..755c1b6
--- /dev/null
+++ b/libblkid/isw_raid.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct isw_metadata {
+	uint8_t		sig[32];
+	uint32_t	check_sum;
+	uint32_t	mpb_size;
+	uint32_t	family_num;
+	uint32_t	generation_num;
+};
+
+#define ISW_SIGNATURE		"Intel Raid ISM Cfg Sig. "
+
+
+static int probe_iswraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct isw_metadata *isw;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 2) * 0x200;
+	isw = (struct isw_metadata *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct isw_metadata));
+	if (!isw)
+		return -1;
+	if (memcmp(isw->sig, ISW_SIGNATURE, sizeof(ISW_SIGNATURE)-1) != 0)
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%6s",
+			&isw->sig[sizeof(ISW_SIGNATURE)-1]) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(isw->sig),
+				(unsigned char *) isw->sig))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo iswraid_idinfo = {
+	.name		= "isw_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_iswraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/jfs.c b/libblkid/jfs.c
new file mode 100644
index 0000000..78c018c
--- /dev/null
+++ b/libblkid/jfs.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct jfs_super_block {
+	unsigned char	js_magic[4];
+	uint32_t	js_version;
+	uint64_t	js_size;
+	uint32_t	js_bsize;	/* 4: aggregate block size in bytes */
+	uint16_t	js_l2bsize;	/* 2: log2 of s_bsize */
+	uint16_t	js_l2bfactor;	/* 2: log2(s_bsize/hardware block size) */
+	uint32_t	js_pbsize;	/* 4: hardware/LVM block size in bytes */
+	uint16_t	js_l2pbsize;	/* 2: log2 of s_pbsize */
+	uint16_t	js_pad;		/* 2: padding necessary for alignment */
+	uint32_t	js_dummy2[26];
+	unsigned char	js_uuid[16];
+	unsigned char	js_label[16];
+	unsigned char	js_loguuid[16];
+};
+
+static int probe_jfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct jfs_super_block *js;
+
+	js = blkid_probe_get_sb(pr, mag, struct jfs_super_block);
+	if (!js)
+		return -1;
+	if (le32_to_cpu(js->js_bsize) != (1U << le16_to_cpu(js->js_l2bsize)))
+		return 1;
+	if (le32_to_cpu(js->js_pbsize) != (1U << le16_to_cpu(js->js_l2pbsize)))
+		return 1;
+	if ((le16_to_cpu(js->js_l2bsize) - le16_to_cpu(js->js_l2pbsize)) !=
+	    le16_to_cpu(js->js_l2bfactor))
+		return 1;
+
+	if (strlen((char *) js->js_label))
+		blkid_probe_set_label(pr, js->js_label, sizeof(js->js_label));
+	blkid_probe_set_uuid(pr, js->js_uuid);
+	return 0;
+}
+
+
+const struct blkid_idinfo jfs_idinfo =
+{
+	.name		= "jfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_jfs,
+	.minsz		= 16 * 1024 * 1024,
+	.magics		=
+	{
+		{ .magic = "JFS1", .len = 4, .kboff = 32 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/jmicron_raid.c b/libblkid/jmicron_raid.c
new file mode 100644
index 0000000..c708078
--- /dev/null
+++ b/libblkid/jmicron_raid.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct jm_metadata {
+	int8_t		signature[2];
+	uint8_t		minor_version;
+	uint8_t		major_version;
+	uint16_t	checksum;
+};
+
+#define JM_SIGNATURE		"JM"
+
+static int probe_jmraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct jm_metadata *jm;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 1) * 0x200;
+	jm = (struct jm_metadata *)
+		blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct jm_metadata));
+	if (!jm)
+		return -1;
+	if (memcmp(jm->signature, JM_SIGNATURE, sizeof(JM_SIGNATURE) - 1) != 0)
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%u.%u",
+				jm->major_version, jm->minor_version) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(jm->signature),
+				(unsigned char *) jm->signature))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo jmraid_idinfo = {
+	.name		= "jmicron_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_jmraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/langinfo.c b/libblkid/langinfo.c
new file mode 100644
index 0000000..deeab9b
--- /dev/null
+++ b/libblkid/langinfo.c
@@ -0,0 +1,121 @@
+/*
+ * This is callback solution for systems without nl_langinfo(), this function
+ * returns hardcoded and on locale setting independed value.
+ *
+ * See langinfo.h man page for more details.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+#include "nls.h"
+
+char *langinfo_fallback(nl_item item)
+{
+	switch (item) {
+	case CODESET:
+		return "ISO-8859-1";
+	case THOUSEP:
+		return ",";
+	case D_T_FMT:
+	case ERA_D_T_FMT:
+		return "%a %b %e %H:%M:%S %Y";
+	case D_FMT:
+	case ERA_D_FMT:
+		return "%m/%d/%y";
+	case T_FMT:
+	case ERA_T_FMT:
+		return "%H:%M:%S";
+	case T_FMT_AMPM:
+		return "%I:%M:%S %p";
+	case AM_STR:
+		return "AM";
+	case PM_STR:
+		return "PM";
+	case DAY_1:
+		return "Sunday";
+	case DAY_2:
+		return "Monday";
+	case DAY_3:
+		return "Tuesday";
+	case DAY_4:
+		return "Wednesday";
+	case DAY_5:
+		return "Thursday";
+	case DAY_6:
+		return "Friday";
+	case DAY_7:
+		return "Saturday";
+	case ABDAY_1:
+		return "Sun";
+	case ABDAY_2:
+		return "Mon";
+	case ABDAY_3:
+		return "Tue";
+	case ABDAY_4:
+		return "Wed";
+	case ABDAY_5:
+		return "Thu";
+	case ABDAY_6:
+		return "Fri";
+	case ABDAY_7:
+		return "Sat";
+	case MON_1:
+		return "January";
+	case MON_2:
+		return "February";
+	case MON_3:
+		return "March";
+	case MON_4:
+		return "April";
+	case MON_5:
+		return "May";
+	case MON_6:
+		return "June";
+	case MON_7:
+		return "July";
+	case MON_8:
+		return "August";
+	case MON_9:
+		return "September";
+	case MON_10:
+		return "October";
+	case MON_11:
+		return "November";
+	case MON_12:
+		return "December";
+	case ABMON_1:
+		return "Jan";
+	case ABMON_2:
+		return "Feb";
+	case ABMON_3:
+		return "Mar";
+	case ABMON_4:
+		return "Apr";
+	case ABMON_5:
+		return "May";
+	case ABMON_6:
+		return "Jun";
+	case ABMON_7:
+		return "Jul";
+	case ABMON_8:
+		return "Aug";
+	case ABMON_9:
+		return "Sep";
+	case ABMON_10:
+		return "Oct";
+	case ABMON_11:
+		return "Nov";
+	case ABMON_12:
+		return "Dec";
+	case ALT_DIGITS:
+		return "\0\0\0\0\0\0\0\0\0\0";
+	case CRNCYSTR:
+		return "-";
+	case YESEXPR:
+		return "^[yY]";
+	case NOEXPR:
+		return "^[nN]";
+	default:
+		return "";
+	}
+}
+
diff --git a/libblkid/linux_raid.c b/libblkid/linux_raid.c
new file mode 100644
index 0000000..a3f9d67
--- /dev/null
+++ b/libblkid/linux_raid.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct mdp0_super_block {
+	uint32_t	md_magic;
+	uint32_t	major_version;
+	uint32_t	minor_version;
+	uint32_t	patch_version;
+	uint32_t	gvalid_words;
+	uint32_t	set_uuid0;
+	uint32_t	ctime;
+	uint32_t	level;
+	uint32_t	size;
+	uint32_t	nr_disks;
+	uint32_t	raid_disks;
+	uint32_t	md_minor;
+	uint32_t	not_persistent;
+	uint32_t	set_uuid1;
+	uint32_t	set_uuid2;
+	uint32_t	set_uuid3;
+};
+
+/*
+ * Version-1, little-endian.
+ */
+struct mdp1_super_block {
+	/* constant array information - 128 bytes */
+	uint32_t	magic;		/* MD_SB_MAGIC: 0xa92b4efc - little endian */
+	uint32_t	major_version;	/* 1 */
+	uint32_t	feature_map;	/* 0 for now */
+	uint32_t	pad0;		/* always set to 0 when writing */
+
+	uint8_t		set_uuid[16];	/* user-space generated. */
+	unsigned char	set_name[32];	/* set and interpreted by user-space */
+
+	uint64_t	ctime;		/* lo 40 bits are seconds, top 24 are microseconds or 0*/
+	uint32_t	level;		/* -4 (multipath), -1 (linear), 0,1,4,5 */
+	uint32_t	layout;		/* only for raid5 currently */
+	uint64_t	size;		/* used size of component devices, in 512byte sectors */
+
+	uint32_t	chunksize;	/* in 512byte sectors */
+	uint32_t	raid_disks;
+	uint32_t	bitmap_offset;	/* sectors after start of superblock that bitmap starts
+					 * NOTE: signed, so bitmap can be before superblock
+					 * only meaningful of feature_map[0] is set.
+					 */
+
+	/* These are only valid with feature bit '4' */
+	uint32_t	new_level;	/* new level we are reshaping to		*/
+	uint64_t	reshape_position;	/* next address in array-space for reshape */
+	uint32_t	delta_disks;	/* change in number of raid_disks		*/
+	uint32_t	new_layout;	/* new layout					*/
+	uint32_t	new_chunk;	/* new chunk size (bytes)			*/
+	uint8_t		pad1[128-124];	/* set to 0 when written */
+
+	/* constant this-device information - 64 bytes */
+	uint64_t	data_offset;	/* sector start of data, often 0 */
+	uint64_t	data_size;	/* sectors in this device that can be used for data */
+	uint64_t	super_offset;	/* sector start of this superblock */
+	uint64_t	recovery_offset;/* sectors before this offset (from data_offset) have been recovered */
+	uint32_t	dev_number;	/* permanent identifier of this  device - not role in raid */
+	uint32_t	cnt_corrected_read; /* number of read errors that were corrected by re-writing */
+	uint8_t		device_uuid[16]; /* user-space setable, ignored by kernel */
+        uint8_t		devflags;        /* per-device flags.  Only one defined...*/
+	uint8_t		pad2[64-57];	/* set to 0 when writing */
+
+	/* array state information - 64 bytes */
+	uint64_t	utime;		/* 40 bits second, 24 btes microseconds */
+	uint64_t	events;		/* incremented when superblock updated */
+	uint64_t	resync_offset;	/* data before this offset (from data_offset) known to be in sync */
+	uint32_t	sb_csum;	/* checksum up to dev_roles[max_dev] */
+	uint32_t	max_dev;	/* size of dev_roles[] array to consider */
+	uint8_t		pad3[64-32];	/* set to 0 when writing */
+
+	/* device state information. Indexed by dev_number.
+	 * 2 bytes per device
+	 * Note there are no per-device state flags. State information is rolled
+	 * into the 'roles' value.  If a device is spare or faulty, then it doesn't
+	 * have a meaningful role.
+	 */
+	uint16_t	dev_roles[0];	/* role in array, or 0xffff for a spare, or 0xfffe for faulty */
+};
+
+
+#define MD_RESERVED_BYTES		0x10000
+#define MD_SB_MAGIC			0xa92b4efc
+
+static int probe_raid0(blkid_probe pr, blkid_loff_t off)
+{
+	struct mdp0_super_block *mdp0;
+	union {
+		uint32_t ints[4];
+		uint8_t bytes[16];
+	} uuid;
+	uint32_t ma, mi, pa;
+	uint64_t size;
+
+	if (pr->size < MD_RESERVED_BYTES)
+		return -1;
+	mdp0 = (struct mdp0_super_block *)
+			blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct mdp0_super_block));
+	if (!mdp0)
+		return -1;
+
+	memset(uuid.ints, 0, sizeof(uuid.ints));
+
+	if (le32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) {
+		uuid.ints[0] = swab32(mdp0->set_uuid0);
+		if (le32_to_cpu(mdp0->minor_version) >= 90) {
+			uuid.ints[1] = swab32(mdp0->set_uuid1);
+			uuid.ints[2] = swab32(mdp0->set_uuid2);
+			uuid.ints[3] = swab32(mdp0->set_uuid3);
+		}
+		ma = le32_to_cpu(mdp0->major_version);
+		mi = le32_to_cpu(mdp0->minor_version);
+		pa = le32_to_cpu(mdp0->patch_version);
+		size = le32_to_cpu(mdp0->size);
+
+	} else if (be32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) {
+		uuid.ints[0] = mdp0->set_uuid0;
+		if (be32_to_cpu(mdp0->minor_version) >= 90) {
+			uuid.ints[1] = mdp0->set_uuid1;
+			uuid.ints[2] = mdp0->set_uuid2;
+			uuid.ints[3] = mdp0->set_uuid3;
+		}
+		ma = be32_to_cpu(mdp0->major_version);
+		mi = be32_to_cpu(mdp0->minor_version);
+		pa = be32_to_cpu(mdp0->patch_version);
+		size = be32_to_cpu(mdp0->size);
+	} else
+		return 1;
+
+	size <<= 10;	/* convert KiB to bytes */
+
+	if (pr->size < 0 || (uint64_t) pr->size < size + MD_RESERVED_BYTES)
+		/* device is too small */
+		return 1;
+
+	if (off < 0 || (uint64_t) off < size)
+		/* no space before superblock */
+		return 1;
+
+	/*
+	 * Check for collisions between RAID and partition table
+	 *
+	 * For example the superblock is at the end of the last partition, it's
+	 * the same position as at the end of the disk...
+	 */
+	if ((S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) &&
+	    blkid_probe_is_covered_by_pt(pr,
+			off - size,				/* min. start  */
+			size + MD_RESERVED_BYTES)) {		/* min. length */
+
+		/* ignore this superblock, it's within any partition and
+		 * we are working with whole-disk now */
+		return 1;
+	}
+
+	if (blkid_probe_sprintf_version(pr, "%u.%u.%u", ma, mi, pa) != 0)
+		return -1;
+	if (blkid_probe_set_uuid(pr, (unsigned char *) uuid.bytes) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(mdp0->md_magic),
+				(unsigned char *) &mdp0->md_magic))
+		return -1;
+	return 0;
+}
+
+static int probe_raid1(blkid_probe pr, off_t off)
+{
+	struct mdp1_super_block *mdp1;
+
+	mdp1 = (struct mdp1_super_block *)
+			blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct mdp1_super_block));
+	if (!mdp1)
+		return -1;
+	if (le32_to_cpu(mdp1->magic) != MD_SB_MAGIC)
+		return -1;
+	if (le32_to_cpu(mdp1->major_version) != 1U)
+		return -1;
+	if (le64_to_cpu(mdp1->super_offset) != (uint64_t) off >> 9)
+		return -1;
+	if (blkid_probe_set_uuid(pr, (unsigned char *) mdp1->set_uuid) != 0)
+		return -1;
+	if (blkid_probe_set_uuid_as(pr,
+			(unsigned char *) mdp1->device_uuid, "UUID_SUB") != 0)
+		return -1;
+	if (blkid_probe_set_label(pr, mdp1->set_name,
+				sizeof(mdp1->set_name)) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(mdp1->magic),
+				(unsigned char *) &mdp1->magic))
+		return -1;
+	return 0;
+}
+
+int probe_raid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	const char *ver = NULL;
+
+	if (pr->size > MD_RESERVED_BYTES) {
+		/* version 0 at the end of the device */
+		uint64_t sboff = (pr->size & ~(MD_RESERVED_BYTES - 1))
+			         - MD_RESERVED_BYTES;
+		if (probe_raid0(pr, sboff) == 0)
+			return 0;
+
+		/* version 1.0 at the end of the device */
+		sboff = (pr->size & ~(0x1000 - 1)) - 0x2000;
+		if (probe_raid1(pr, sboff) == 0)
+			ver = "1.0";
+	}
+
+	if (!ver) {
+		/* version 1.1 at the start of the device */
+		if (probe_raid1(pr, 0) == 0)
+			ver = "1.1";
+
+		/* version 1.2 at 4k offset from the start */
+		else if (probe_raid1(pr, 0x1000) == 0)
+			ver = "1.2";
+	}
+
+	if (ver) {
+		blkid_probe_set_version(pr, ver);
+		return 0;
+	}
+	return -1;
+}
+
+
+const struct blkid_idinfo linuxraid_idinfo = {
+	.name		= "linux_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_raid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/linux_reboot.h b/libblkid/linux_reboot.h
new file mode 100644
index 0000000..9cebc67
--- /dev/null
+++ b/libblkid/linux_reboot.h
@@ -0,0 +1,72 @@
+#ifndef _LINUX_REBOOT_H
+#define _LINUX_REBOOT_H
+
+/*
+ * Magic values required to use _reboot() system call.
+ */
+
+#define	LINUX_REBOOT_MAGIC1	0xfee1dead
+#define	LINUX_REBOOT_MAGIC2	672274793
+#define	LINUX_REBOOT_MAGIC2A	85072278
+#define	LINUX_REBOOT_MAGIC2B	369367448
+
+
+/*
+ * Commands accepted by the _reboot() system call.
+ *
+ * RESTART     Restart system using default command and mode.
+ * HALT        Stop OS and give system control to ROM monitor, if any.
+ * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command.
+ * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task.
+ * POWER_OFF   Stop OS and remove all power from system, if possible.
+ * RESTART2    Restart system using given command string.
+ */
+
+#define	LINUX_REBOOT_CMD_RESTART	0x01234567
+#define	LINUX_REBOOT_CMD_HALT		0xCDEF0123
+#define	LINUX_REBOOT_CMD_CAD_ON		0x89ABCDEF
+#define	LINUX_REBOOT_CMD_CAD_OFF	0x00000000
+#define	LINUX_REBOOT_CMD_POWER_OFF	0x4321FEDC
+#define	LINUX_REBOOT_CMD_RESTART2	0xA1B2C3D4
+
+/* Including <unistd.h> makes sure that on a glibc system
+   <features.h> is included, which again defines __GLIBC__ */
+#include <unistd.h>
+#include "linux_reboot.h"
+
+#define USE_LIBC
+
+#ifdef USE_LIBC
+
+/* libc version */
+#if defined __GLIBC__ && __GLIBC__ >= 2
+#  include <sys/reboot.h>
+#  define REBOOT(cmd) reboot(cmd)
+#else
+extern int reboot(int, int, int);
+#  define REBOOT(cmd) reboot(LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,(cmd))
+#endif
+static inline int my_reboot(int cmd) {
+	return REBOOT(cmd);
+}
+
+#else /* no USE_LIBC */
+
+/* direct syscall version */
+#include <linux/unistd.h>
+
+#ifdef _syscall3
+_syscall3(int,  reboot,  int,  magic, int, magic_too, int, cmd);
+#else
+/* Let us hope we have a 3-argument reboot here */
+extern int reboot(int, int, int);
+#endif
+
+static inline int my_reboot(int cmd) {
+	return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd);
+}
+
+#endif
+
+
+#endif /*  _LINUX_REBOOT_H  */
diff --git a/libblkid/linux_version.c b/libblkid/linux_version.c
new file mode 100644
index 0000000..2bcc2cc
--- /dev/null
+++ b/libblkid/linux_version.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <sys/utsname.h>
+
+#include "linux_version.h"
+
+int get_linux_version (void)
+{
+	static int kver = -1;
+	struct utsname uts;
+	int major = 0;
+	int minor = 0;
+	int teeny = 0;
+	int n;
+
+	if (kver != -1)
+		return kver;
+	if (uname (&uts))
+		return kver = 0;
+
+	n = sscanf(uts.release, "%d.%d.%d", &major, &minor, &teeny);
+	if (n < 1 || n > 3)
+		return kver = 0;
+
+	return kver = KERNEL_VERSION(major, minor, teeny);
+}
diff --git a/libblkid/linux_version.h b/libblkid/linux_version.h
new file mode 100644
index 0000000..a6a1e99
--- /dev/null
+++ b/libblkid/linux_version.h
@@ -0,0 +1,14 @@
+#ifndef LINUX_VERSION_H
+#define LINUX_VERSION_H
+
+#ifdef HAVE_LINUX_VERSION_H
+# include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION
+# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+int get_linux_version(void);
+
+#endif /* LINUX_VERSION_H */
diff --git a/libblkid/list.h b/libblkid/list.h
new file mode 100644
index 0000000..7b60672
--- /dev/null
+++ b/libblkid/list.h
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 1999-2008 by Theodore Ts'o
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * (based on list.h from e2fsprogs)
+ * Merge sort based on kernel's implementation.
+ */
+
+#ifndef UTIL_LINUX_LIST_H
+#define UTIL_LINUX_LIST_H
+
+/* TODO: use AC_C_INLINE */
+#ifdef __GNUC__
+#define _INLINE_ static __inline__
+#else                         /* For Watcom C */
+#define _INLINE_ static inline
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+_INLINE_ void __list_add(struct list_head * add,
+	struct list_head * prev,
+	struct list_head * next)
+{
+	next->prev = add;
+	add->next = next;
+	add->prev = prev;
+	prev->next = add;
+}
+
+/**
+ * list_add - add a new entry
+ * @add:	new entry to be added
+ * @head:	list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+_INLINE_ void list_add(struct list_head *add, struct list_head *head)
+{
+	__list_add(add, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @add:	new entry to be added
+ * @head:	list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+_INLINE_ void list_add_tail(struct list_head *add, struct list_head *head)
+{
+	__list_add(add, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+_INLINE_ void __list_del(struct list_head * prev,
+				  struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry:	the element to delete from the list.
+ *
+ * list_empty() on @entry does not return true after this, @entry is
+ * in an undefined state.
+ */
+_INLINE_ void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry:	the element to delete from the list.
+ */
+_INLINE_ void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head:	the list to test.
+ */
+_INLINE_ int list_empty(struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_entry_is_last - tests whether is entry last in the list
+ * @entry:	the entry to test.
+ * @head:	the list to test.
+ */
+_INLINE_ int list_entry_is_last(struct list_head *entry, struct list_head *head)
+{
+	return head->prev == entry;
+}
+
+/**
+ * list_splice - join two lists
+ * @list:	the new list to add.
+ * @head:	the place to add it in the first list.
+ */
+_INLINE_ void list_splice(struct list_head *list, struct list_head *head)
+{
+	struct list_head *first = list->next;
+
+	if (first != list) {
+		struct list_head *last = list->prev;
+		struct list_head *at = head->next;
+
+		first->prev = head;
+		head->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) ({              \
+	const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+#define list_first_entry(head, type, member) \
+	((head) && (head)->next != (head) ? list_entry((head)->next, type, member) : NULL)
+
+#define list_last_entry(head, type, member) \
+	((head) && (head)->prev != (head) ? list_entry((head)->prev, type, member) : NULL)
+
+/**
+ * list_for_each - iterate over elements in a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_backwardly - iterate over elements in a list in reverse
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each_backwardly(pos, head) \
+	for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over elements in a list, but don't dereference
+ *                      pos after the body is done (in case it is freed)
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @pnext:	the &struct list_head to use as a pointer to the next item.
+ * @head:	the head for your list (not included in iteration).
+ */
+#define list_for_each_safe(pos, pnext, head) \
+	for (pos = (head)->next, pnext = pos->next; pos != (head); \
+	     pos = pnext, pnext = pos->next)
+
+#define MAX_LIST_LENGTH_BITS 20
+
+/*
+ * Returns a list organized in an intermediate format suited
+ * to chaining of merge() calls: null-terminated, no reserved or
+ * sentinel head node, "prev" links not maintained.
+ */
+_INLINE_ struct list_head *merge(int (*cmp)(struct list_head *a,
+					  struct list_head *b),
+			       struct list_head *a, struct list_head *b)
+{
+	struct list_head head, *tail = &head;
+
+	while (a && b) {
+		/* if equal, take 'a' -- important for sort stability */
+		if ((*cmp)(a, b) <= 0) {
+			tail->next = a;
+			a = a->next;
+		} else {
+			tail->next = b;
+			b = b->next;
+		}
+		tail = tail->next;
+	}
+	tail->next = a ? a : b;
+	return head.next;
+}
+
+/*
+ * Combine final list merge with restoration of standard doubly-linked
+ * list structure.  This approach duplicates code from merge(), but
+ * runs faster than the tidier alternatives of either a separate final
+ * prev-link restoration pass, or maintaining the prev links
+ * throughout.
+ */
+_INLINE_ void merge_and_restore_back_links(int (*cmp)(struct list_head *a,
+						    struct list_head *b),
+					 struct list_head *head,
+					 struct list_head *a, struct list_head *b)
+{
+	struct list_head *tail = head;
+
+	while (a && b) {
+		/* if equal, take 'a' -- important for sort stability */
+		if ((*cmp)(a, b) <= 0) {
+			tail->next = a;
+			a->prev = tail;
+			a = a->next;
+		} else {
+			tail->next = b;
+			b->prev = tail;
+			b = b->next;
+		}
+		tail = tail->next;
+	}
+	tail->next = a ? a : b;
+
+	do {
+		/*
+		 * In worst cases this loop may run many iterations.
+		 * Continue callbacks to the client even though no
+		 * element comparison is needed, so the client's cmp()
+		 * routine can invoke cond_resched() periodically.
+		 */
+		(*cmp)(tail->next, tail->next);
+
+		tail->next->prev = tail;
+		tail = tail->next;
+	} while (tail->next);
+
+	tail->next = head;
+	head->prev = tail;
+}
+
+
+/**
+ * list_sort - sort a list
+ * @head: the list to sort
+ * @cmp: the elements comparison function
+ *
+ * This function implements "merge sort", which has O(nlog(n))
+ * complexity.
+ *
+ * The comparison function @cmp must return a negative value if @a
+ * should sort before @b, and a positive value if @a should sort after
+ * @b. If @a and @b are equivalent, and their original relative
+ * ordering is to be preserved, @cmp must return 0.
+ */
+_INLINE_ void list_sort(struct list_head *head,
+			int (*cmp)(struct list_head *a,
+				   struct list_head *b))
+{
+	struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
+							   -- last slot is a sentinel */
+	size_t lev;  /* index into part[] */
+	size_t max_lev = 0;
+	struct list_head *list;
+
+	if (list_empty(head))
+		return;
+
+	memset(part, 0, sizeof(part));
+
+	head->prev->next = NULL;
+	list = head->next;
+
+	while (list) {
+		struct list_head *cur = list;
+		list = list->next;
+		cur->next = NULL;
+
+		for (lev = 0; part[lev]; lev++) {
+			cur = merge(cmp, part[lev], cur);
+			part[lev] = NULL;
+		}
+		if (lev > max_lev) {
+			/* list passed to list_sort() too long for efficiency */
+			if (lev >= ARRAY_SIZE(part) - 1)
+				lev--;
+			max_lev = lev;
+		}
+		part[lev] = cur;
+	}
+
+	for (lev = 0; lev < max_lev; lev++)
+		if (part[lev])
+			list = merge(cmp, part[lev], list);
+
+	merge_and_restore_back_links(cmp, head, part[max_lev], list);
+}
+
+#undef _INLINE_
+
+#endif /* UTIL_LINUX_LIST_H */
diff --git a/libblkid/llseek.c b/libblkid/llseek.c
new file mode 100644
index 0000000..be4fdd3
--- /dev/null
+++ b/libblkid/llseek.c
@@ -0,0 +1,91 @@
+/*
+ * llseek.c -- stub calling the llseek system call
+ *
+ * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef __MSDOS__
+#include <io.h>
+#endif
+
+#include "blkidP.h"
+
+#ifdef __linux__
+
+#define my_llseek lseek64
+#include <linux/unistd.h>
+
+#ifndef __NR__llseek
+#define __NR__llseek            140
+#endif
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence)
+{
+	blkid_loff_t result;
+	static int do_compat = 0;
+
+	if ((sizeof(off_t) >= sizeof(blkid_loff_t)) ||
+	    (offset < ((blkid_loff_t) 1 << ((sizeof(off_t)*8) -1))))
+		return lseek(fd, (off_t) offset, whence);
+
+	if (do_compat) {
+		errno = EOVERFLOW;
+		return -1;
+	}
+
+	if (result == -1 && errno == ENOSYS) {
+		/*
+		 * Just in case this code runs on top of an old kernel
+		 * which does not support the llseek system call
+		 */
+		do_compat++;
+		errno = EOVERFLOW;
+	}
+	return result;
+}
+
+#else /* !linux */
+
+#ifndef EOVERFLOW
+#ifdef EXT2_ET_INVALID_ARGUMENT
+#define EOVERFLOW EXT2_ET_INVALID_ARGUMENT
+#else
+#define EOVERFLOW 112
+#endif
+#endif
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int origin)
+{
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+	return lseek64 (fd, offset, origin);
+#else
+	if ((sizeof(off_t) < sizeof(blkid_loff_t)) &&
+	    (offset >= ((blkid_loff_t) 1 << ((sizeof(off_t)*8) - 1)))) {
+		errno = EOVERFLOW;
+		return -1;
+	}
+	return lseek(fd, (off_t) offset, origin);
+#endif
+}
+
+#endif	/* linux */
+
+
diff --git a/libblkid/loopdev.c b/libblkid/loopdev.c
new file mode 100644
index 0000000..a25a2fa
--- /dev/null
+++ b/libblkid/loopdev.c
@@ -0,0 +1,1559 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ *
+ * -- based on mount/losetup.c
+ *
+ * Simple library for work with loop devices.
+ *
+ *  - requires kernel 2.6.x
+ *  - reads info from /sys/block/loop<N>/loop/<attr> (new kernels)
+ *  - reads info by ioctl
+ *  - supports *unlimited* number of loop devices
+ *  - supports /dev/loop<N> as well as /dev/loop/<N>
+ *  - minimize overhead (fd, loopinfo, ... are shared for all operations)
+ *  - setup (associate device and backing file)
+ *  - delete (dis-associate file)
+ *  - old LOOP_{SET,GET}_STATUS (32bit) ioctls are unsupported
+ *  - extendible
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/sysmacros.h>
+#include <inttypes.h>
+#include <dirent.h>
+#include <linux/posix_types.h>
+
+#include "linux_version.h"
+#include "c.h"
+#include "sysfs.h"
+#include "pathnames.h"
+#include "loopdev.h"
+#include "canonicalize.h"
+#include "at.h"
+
+#define CONFIG_LOOPDEV_DEBUG
+
+#ifdef CONFIG_LOOPDEV_DEBUG
+# include <stdarg.h>
+
+# define DBG(l,x)	do { \
+				if ((l)->debug) {\
+					fprintf(stderr, "loopdev:  [%p]: ", (l)); \
+					x; \
+				} \
+			} while(0)
+
+static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+loopdev_debug(const char *mesg, ...)
+{
+	va_list ap;
+	va_start(ap, mesg);
+	vfprintf(stderr, mesg, ap);
+	va_end(ap);
+	fputc('\n', stderr);
+}
+
+#else /* !CONFIG_LOOPDEV_DEBUG */
+# define DBG(m,x) do { ; } while(0)
+#endif
+
+/*
+ * see loopcxt_init()
+ */
+#define loopcxt_ioctl_enabled(_lc)	(!((_lc)->flags & LOOPDEV_FL_NOIOCTL))
+#define loopcxt_sysfs_available(_lc)	(!((_lc)->flags & LOOPDEV_FL_NOSYSFS)) \
+					 && !loopcxt_ioctl_enabled(_lc)
+
+/*
+ * @lc: context
+ * @device: device name, absolute device path or NULL to reset the current setting
+ *
+ * Sets device, absolute paths (e.g. "/dev/loop<N>") are unchanged, device
+ * names ("loop<N>") are converted to the path (/dev/loop<N> or to
+ * /dev/loop/<N>)
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
+{
+	if (!lc)
+		return -EINVAL;
+
+	if (lc->fd >= 0) {
+		close(lc->fd);
+		DBG(lc, loopdev_debug("closing old open fd"));
+	}
+	lc->fd = -1;
+	lc->mode = 0;
+	lc->has_info = 0;
+	lc->info_failed = 0;
+	*lc->device = '\0';
+	memset(&lc->info, 0, sizeof(lc->info));
+
+	/* set new */
+	if (device) {
+		if (*device != '/') {
+			const char *dir = _PATH_DEV;
+
+			/* compose device name for /dev/loop<n> or /dev/loop/<n> */
+			if (lc->flags & LOOPDEV_FL_DEVSUBDIR) {
+				if (strlen(device) < 5)
+					return -1;
+				device += 4;
+				dir = _PATH_DEV_LOOP "/";	/* _PATH_DEV uses tailing slash */
+			}
+			snprintf(lc->device, sizeof(lc->device), "%s%s",
+				dir, device);
+		} else {
+			strncpy(lc->device, device, sizeof(lc->device));
+			lc->device[sizeof(lc->device) - 1] = '\0';
+		}
+		DBG(lc, loopdev_debug("%s successfully assigned", device));
+	}
+
+	sysfs_deinit(&lc->sysfs);
+	return 0;
+}
+
+int loopcxt_has_device(struct loopdev_cxt *lc)
+{
+	return lc && *lc->device;
+}
+
+/*
+ * @lc: context
+ * @flags: LOOPDEV_FL_* flags
+ *
+ * Initilize loop handler.
+ *
+ * We have two sets of the flags:
+ *
+ *	* LOOPDEV_FL_* flags control loopcxt_* API behavior
+ *
+ *	* LO_FLAGS_* are kernel flags used for LOOP_{SET,GET}_STAT64 ioctls
+ *
+ * Note about LOOPDEV_FL_{RDONLY,RDWR} flags. These flags are used for open(2)
+ * syscall to open loop device. By default is the device open read-only.
+ *
+ * The expection is loopcxt_setup_device(), where the device is open read-write
+ * if LO_FLAGS_READ_ONLY flags is not set (see loopcxt_set_flags()).
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int loopcxt_init(struct loopdev_cxt *lc, int flags)
+{
+	int rc;
+	struct stat st;
+	struct loopdev_cxt dummy = UL_LOOPDEVCXT_EMPTY;
+
+	if (!lc)
+		return -EINVAL;
+
+	memcpy(lc, &dummy, sizeof(dummy));
+	lc->flags = flags;
+
+	if (getenv("LOOPDEV_DEBUG"))
+		loopcxt_enable_debug(lc, TRUE);
+
+	rc = loopcxt_set_device(lc, NULL);
+	if (rc)
+		return rc;
+
+	if (stat(_PATH_SYS_BLOCK, &st) || !S_ISDIR(st.st_mode)) {
+		lc->flags |= LOOPDEV_FL_NOSYSFS;
+		lc->flags &= ~LOOPDEV_FL_NOIOCTL;
+		DBG(lc, loopdev_debug("init: disable /sys usage"));
+	}
+
+	if (!(lc->flags & LOOPDEV_FL_NOSYSFS) &&
+	    get_linux_version() >= KERNEL_VERSION(2,6,37)) {
+		/*
+		 * Use only sysfs for basic information about loop devices
+		 */
+		lc->flags |= LOOPDEV_FL_NOIOCTL;
+		DBG(lc, loopdev_debug("init: ignore ioctls"));
+	}
+
+	if (!(lc->flags & LOOPDEV_FL_CONTROL) && !stat(_PATH_DEV_LOOPCTL, &st)) {
+		lc->flags |= LOOPDEV_FL_CONTROL;
+		DBG(lc, loopdev_debug("init: loop-control detected "));
+	}
+
+	return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Deinitialize loop context
+ */
+void loopcxt_deinit(struct loopdev_cxt *lc)
+{
+	int errsv = errno;
+
+	if (!lc)
+		return;
+
+	DBG(lc, loopdev_debug("de-initialize"));
+
+	free(lc->filename);
+	lc->filename = NULL;
+
+	ignore_result( loopcxt_set_device(lc, NULL) );
+	loopcxt_deinit_iterator(lc);
+
+	errno = errsv;
+}
+
+/*
+ * @lc: context
+ * @enable: TRUE/FALSE
+ *
+ * Enabled/disables debug messages
+ */
+void loopcxt_enable_debug(struct loopdev_cxt *lc, int enable)
+{
+	if (lc)
+		lc->debug = enable ? 1 : 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns newly allocated device path.
+ */
+char *loopcxt_strdup_device(struct loopdev_cxt *lc)
+{
+	if (!lc || !lc->device || !*lc->device)
+		return NULL;
+	return strdup(lc->device);
+}
+
+/*
+ * @lc: context
+ *
+ * Returns pointer device name in the @lc struct.
+ */
+const char *loopcxt_get_device(struct loopdev_cxt *lc)
+{
+	return lc && lc->device && *lc->device ? lc->device : NULL;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns pointer to the sysfs context (see lib/sysfs.c)
+ */
+struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
+{
+	if (!lc || !*lc->device || (lc->flags & LOOPDEV_FL_NOSYSFS))
+		return NULL;
+
+	if (!lc->sysfs.devno) {
+		dev_t devno = sysfs_devname_to_devno(lc->device, NULL);
+		if (!devno) {
+			DBG(lc, loopdev_debug("sysfs: failed devname to devno"));
+			return NULL;
+		}
+		if (sysfs_init(&lc->sysfs, devno, NULL)) {
+			DBG(lc, loopdev_debug("sysfs: init failed"));
+			return NULL;
+		}
+	}
+
+	return &lc->sysfs;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: file descriptor to the open loop device or <0 on error. The mode
+ *          depends on LOOPDEV_FL_{RDWR,RDONLY} context flags. Default is
+ *          read-only.
+ */
+int loopcxt_get_fd(struct loopdev_cxt *lc)
+{
+	if (!lc || !*lc->device)
+		return -EINVAL;
+
+	if (lc->fd < 0) {
+		lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY;
+		lc->fd = open(lc->device, lc->mode);
+		DBG(lc, loopdev_debug("open %s [%s]: %s", lc->device,
+				lc->flags & LOOPDEV_FL_RDWR ? "rw" : "ro",
+				lc->fd < 0 ? "failed" : "ok"));
+	}
+	return lc->fd;
+}
+
+int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode)
+{
+	if (!lc)
+		return -EINVAL;
+
+	lc->fd = fd;
+	lc->mode = mode;
+	return 0;
+}
+
+/*
+ * @lc: context
+ * @flags: LOOPITER_FL_* flags
+ *
+ * Iterator allows to scan list of the free or used loop devices.
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags)
+{
+	struct loopdev_iter *iter;
+	struct stat st;
+
+	if (!lc)
+		return -EINVAL;
+
+	DBG(lc, loopdev_debug("iter: initialize"));
+
+	iter = &lc->iter;
+
+	/* always zeroize
+	 */
+	memset(iter, 0, sizeof(*iter));
+	iter->ncur = -1;
+	iter->flags = flags;
+	iter->default_check = 1;
+
+	if (!lc->extra_check) {
+		/*
+		 * Check for /dev/loop/<N> subdirectory
+		 */
+		if (!(lc->flags & LOOPDEV_FL_DEVSUBDIR) &&
+		    stat(_PATH_DEV_LOOP, &st) == 0 && S_ISDIR(st.st_mode))
+			lc->flags |= LOOPDEV_FL_DEVSUBDIR;
+
+		lc->extra_check = 1;
+	}
+	return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_deinit_iterator(struct loopdev_cxt *lc)
+{
+	struct loopdev_iter *iter;
+
+	if (!lc)
+		return -EINVAL;
+
+	DBG(lc, loopdev_debug("iter: de-initialize"));
+
+	iter = &lc->iter;
+
+	free(iter->minors);
+	if (iter->proc)
+		fclose(iter->proc);
+	if (iter->sysblock)
+		closedir(iter->sysblock);
+	iter->minors = NULL;
+	iter->proc = NULL;
+	iter->sysblock = NULL;
+	iter->done = 1;
+	return 0;
+}
+
+/*
+ * Same as loopcxt_set_device, but also checks if the device is
+ * associeted with any file.
+ *
+ * Returns: <0 on error, 0 on success, 1 device does not match with
+ *         LOOPITER_FL_{USED,FREE} flags.
+ */
+static int loopiter_set_device(struct loopdev_cxt *lc, const char *device)
+{
+	int rc = loopcxt_set_device(lc, device);
+	int used;
+
+	if (rc)
+		return rc;
+
+	if (!(lc->iter.flags & LOOPITER_FL_USED) &&
+	    !(lc->iter.flags & LOOPITER_FL_FREE))
+		return 0;	/* caller does not care about device status */
+
+	used = loopcxt_get_offset(lc, NULL) == 0;
+
+	if ((lc->iter.flags & LOOPITER_FL_USED) && used)
+		return 0;
+
+	if ((lc->iter.flags & LOOPITER_FL_FREE) && !used)
+		return 0;
+
+	DBG(lc, loopdev_debug("iter: unset device"));
+	ignore_result( loopcxt_set_device(lc, NULL) );
+	return 1;
+}
+
+static int cmpnum(const void *p1, const void *p2)
+{
+	return (((* (int *) p1) > (* (int *) p2)) -
+			((* (int *) p1) < (* (int *) p2)));
+}
+
+/*
+ * The classic scandir() is more expensive and less portable.
+ * We needn't full loop device names -- loop numbers (loop<N>)
+ * are enough.
+ */
+static int loop_scandir(const char *dirname, int **ary, int hasprefix)
+{
+	DIR *dir;
+	struct dirent *d;
+	unsigned int n, count = 0, arylen = 0;
+
+	if (!dirname || !ary)
+		return 0;
+	dir = opendir(dirname);
+	if (!dir)
+		return 0;
+	free(*ary);
+	*ary = NULL;
+
+	while((d = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (d->d_type != DT_BLK && d->d_type != DT_UNKNOWN &&
+		    d->d_type != DT_LNK)
+			continue;
+#endif
+		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+			continue;
+
+		if (hasprefix) {
+			/* /dev/loop<N> */
+			if (sscanf(d->d_name, "loop%u", &n) != 1)
+				continue;
+		} else {
+			/* /dev/loop/<N> */
+			char *end = NULL;
+
+			n = strtol(d->d_name, &end, 10);
+			if (d->d_name == end || (end && *end) || errno)
+				continue;
+		}
+		if (n < LOOPDEV_DEFAULT_NNODES)
+			continue;			/* ignore loop<0..7> */
+
+		if (count + 1 > arylen) {
+			int *tmp;
+
+			arylen += 1;
+
+			tmp = realloc(*ary, arylen * sizeof(int));
+			if (!tmp) {
+				free(*ary);
+				closedir(dir);
+				return -1;
+			}
+			*ary = tmp;
+		}
+		if (*ary)
+			(*ary)[count++] = n;
+	}
+	if (count && *ary)
+		qsort(*ary, count, sizeof(int), cmpnum);
+
+	closedir(dir);
+	return count;
+}
+
+/*
+ * Set the next *used* loop device according to /proc/partitions.
+ *
+ * Loop devices smaller than 512 bytes are invisible for this function.
+ */
+static int loopcxt_next_from_proc(struct loopdev_cxt *lc)
+{
+	struct loopdev_iter *iter = &lc->iter;
+	char buf[BUFSIZ];
+
+	DBG(lc, loopdev_debug("iter: scan /proc/partitions"));
+
+	if (!iter->proc)
+		iter->proc = fopen(_PATH_PROC_PARTITIONS, "r");
+	if (!iter->proc)
+		return 1;
+
+	while (fgets(buf, sizeof(buf), iter->proc)) {
+		unsigned int m;
+		char name[128 + 1];
+
+
+		if (sscanf(buf, " %u %*s %*s %128[^\n ]",
+			   &m, name) != 2 || m != LOOPDEV_MAJOR)
+			continue;
+
+		DBG(lc, loopdev_debug("iter: check %s", name));
+
+		if (loopiter_set_device(lc, name) == 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Set the next *used* loop device according to
+ * /sys/block/loopN/loop/backing_file (kernel >= 2.6.37 is required).
+ *
+ * This is preferred method.
+ */
+static int loopcxt_next_from_sysfs(struct loopdev_cxt *lc)
+{
+	struct loopdev_iter *iter = &lc->iter;
+	struct dirent *d;
+	int fd;
+
+	DBG(lc, loopdev_debug("iter: scan /sys/block"));
+
+	if (!iter->sysblock)
+		iter->sysblock = opendir(_PATH_SYS_BLOCK);
+
+	if (!iter->sysblock)
+		return 1;
+
+	fd = dirfd(iter->sysblock);
+
+	while ((d = readdir(iter->sysblock))) {
+		char name[256];
+		struct stat st;
+
+		DBG(lc, loopdev_debug("iter: check %s", d->d_name));
+
+		if (strcmp(d->d_name, ".") == 0
+		    || strcmp(d->d_name, "..") == 0
+		    || strncmp(d->d_name, "loop", 4) != 0)
+			continue;
+
+		snprintf(name, sizeof(name), "%s/loop/backing_file", d->d_name);
+		if (fstat_at(fd, _PATH_SYS_BLOCK, name, &st, 0) != 0)
+			continue;
+
+		if (loopiter_set_device(lc, d->d_name) == 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * @lc: context, has to initialized by loopcxt_init_iterator()
+ *
+ * Returns: 0 on success, -1 on error, 1 at the end of scanning. The details
+ *          about the current loop device are available by
+ *          loopcxt_get_{fd,backing_file,device,offset, ...} functions.
+ */
+int loopcxt_next(struct loopdev_cxt *lc)
+{
+	struct loopdev_iter *iter;
+
+	if (!lc)
+		return -EINVAL;
+
+	DBG(lc, loopdev_debug("iter: next"));
+
+	iter = &lc->iter;
+	if (iter->done)
+		return 1;
+
+	/* A) Look for used loop devices in /proc/partitions ("losetup -a" only)
+	 */
+	if (iter->flags & LOOPITER_FL_USED) {
+		int rc;
+
+		if (loopcxt_sysfs_available(lc))
+			rc = loopcxt_next_from_sysfs(lc);
+		else
+			rc = loopcxt_next_from_proc(lc);
+		if (rc == 0)
+			return 0;
+		goto done;
+	}
+
+	/* B) Classic way, try first eight loop devices (default number
+	 *    of loop devices). This is enough for 99% of all cases.
+	 */
+	if (iter->default_check) {
+		DBG(lc, loopdev_debug("iter: next: default check"));
+		for (++iter->ncur; iter->ncur < LOOPDEV_DEFAULT_NNODES;
+							iter->ncur++) {
+			char name[16];
+			snprintf(name, sizeof(name), "loop%d", iter->ncur);
+
+			if (loopiter_set_device(lc, name) == 0)
+				return 0;
+		}
+		iter->default_check = 0;
+	}
+
+	/* C) the worst possibility, scan whole /dev or /dev/loop/<N>
+	 */
+	if (!iter->minors) {
+		DBG(lc, loopdev_debug("iter: next: scan /dev"));
+		iter->nminors = (lc->flags & LOOPDEV_FL_DEVSUBDIR) ?
+			loop_scandir(_PATH_DEV_LOOP, &iter->minors, 0) :
+			loop_scandir(_PATH_DEV, &iter->minors, 1);
+		iter->ncur = -1;
+	}
+	for (++iter->ncur; iter->ncur < iter->nminors; iter->ncur++) {
+		char name[16];
+		snprintf(name, sizeof(name), "loop%d", iter->minors[iter->ncur]);
+
+		if (loopiter_set_device(lc, name) == 0)
+			return 0;
+	}
+done:
+	loopcxt_deinit_iterator(lc);
+	return 1;
+}
+
+/*
+ * @device: path to device
+ */
+int is_loopdev(const char *device)
+{
+	struct stat st;
+
+	if (!device)
+		return 0;
+
+	return (stat(device, &st) == 0 &&
+		S_ISBLK(st.st_mode) &&
+		major(st.st_rdev) == LOOPDEV_MAJOR);
+}
+
+/*
+ * @lc: context
+ *
+ * Returns result from LOOP_GET_STAT64 ioctl or NULL on error.
+ */
+struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc)
+{
+	int fd;
+
+	if (!lc || lc->info_failed)
+		return NULL;
+	if (lc->has_info)
+		return &lc->info;
+
+	fd = loopcxt_get_fd(lc);
+	if (fd < 0)
+		return NULL;
+
+	if (ioctl(fd, LOOP_GET_STATUS64, &lc->info) == 0) {
+		lc->has_info = 1;
+		lc->info_failed = 0;
+		DBG(lc, loopdev_debug("reading loop_info64 OK"));
+		return &lc->info;
+	} else {
+		lc->info_failed = 1;
+		DBG(lc, loopdev_debug("reading loop_info64 FAILED"));
+	}
+
+	return NULL;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns (allocated) string with path to the file assicieted
+ * with the current loop device.
+ */
+char *loopcxt_get_backing_file(struct loopdev_cxt *lc)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+	char *res = NULL;
+
+	if (sysfs)
+		/*
+		 * This is always preffered, the loop_info64
+		 * has too small buffer for the filename.
+		 */
+		res = sysfs_strdup(sysfs, "loop/backing_file");
+
+	if (!res && loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+
+		if (lo) {
+			lo->lo_file_name[LO_NAME_SIZE - 2] = '*';
+			lo->lo_file_name[LO_NAME_SIZE - 1] = '\0';
+			res = strdup((char *) lo->lo_file_name);
+		}
+	}
+
+	DBG(lc, loopdev_debug("get_backing_file [%s]", res));
+	return res;
+}
+
+/*
+ * @lc: context
+ * @offset: returns offset number for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+	int rc = -EINVAL;
+
+	if (sysfs)
+		rc = sysfs_read_u64(sysfs, "loop/offset", offset);
+
+	if (rc && loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+		if (lo) {
+			if (offset)
+				*offset = lo->lo_offset;
+			rc = 0;
+		}
+	}
+
+	DBG(lc, loopdev_debug("get_offset [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * @lc: context
+ * @sizelimit: returns size limit for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+	int rc = -EINVAL;
+
+	if (sysfs)
+		rc = sysfs_read_u64(sysfs, "loop/sizelimit", size);
+
+	if (rc && loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+		if (lo) {
+			if (size)
+				*size = lo->lo_sizelimit;
+			rc = 0;
+		}
+	}
+
+	DBG(lc, loopdev_debug("get_sizelimit [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * @lc: context
+ * @devno: returns encryption type
+ *
+ * Cryptoloop is DEPRECATED!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type)
+{
+	struct loop_info64 *lo = loopcxt_get_info(lc);
+	int rc = -EINVAL;
+
+	if (lo) {
+		if (type)
+			*type = lo->lo_encrypt_type;
+		rc = 0;
+	}
+	DBG(lc, loopdev_debug("get_encrypt_type [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * @lc: context
+ * @devno: returns crypt name
+ *
+ * Cryptoloop is DEPRECATED!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc)
+{
+	struct loop_info64 *lo = loopcxt_get_info(lc);
+
+	if (lo)
+		return (char *) lo->lo_crypt_name;
+
+	DBG(lc, loopdev_debug("get_crypt_name failed"));
+	return NULL;
+}
+
+/*
+ * @lc: context
+ * @devno: returns backing file devno
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno)
+{
+	struct loop_info64 *lo = loopcxt_get_info(lc);
+	int rc = -EINVAL;
+
+	if (lo) {
+		if (devno)
+			*devno = lo->lo_device;
+		rc = 0;
+	}
+	DBG(lc, loopdev_debug("get_backing_devno [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * @lc: context
+ * @ino: returns backing file inode
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino)
+{
+	struct loop_info64 *lo = loopcxt_get_info(lc);
+	int rc = -EINVAL;
+
+	if (lo) {
+		if (ino)
+			*ino = lo->lo_inode;
+		rc = 0;
+	}
+	DBG(lc, loopdev_debug("get_backing_inode [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * Check if the kernel supports partitioned loop devices.
+ *
+ * Notes:
+ *   - kernels < 3.2 support partitioned loop devices and PT scanning
+ *     only if max_part= module paremeter is non-zero
+ *
+ *   - kernels >= 3.2 always support partitioned loop devices
+ *
+ *   - kernels >= 3.2 always support BLKPG_{ADD,DEL}_PARTITION ioctls
+ *
+ *   - kernels >= 3.2 enable PT scanner only if max_part= is non-zero or if the
+ *     LO_FLAGS_PARTSCAN flag is set for the device. The PT scanner is disabled
+ *     by default.
+ *
+ *  See kernel commit e03c8dd14915fabc101aa495828d58598dc5af98.
+ */
+int loopmod_supports_partscan(void)
+{
+	int rc, ret = 0;
+	FILE *f;
+
+	if (get_linux_version() >= KERNEL_VERSION(3,2,0))
+		return 1;
+
+	f = fopen("/sys/module/loop/parameters/max_part", "r");
+	if (!f)
+		return 0;
+	rc = fscanf(f, "%d", &ret);
+	fclose(f);
+	return rc == 1 ? ret : 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the partscan flags is set *or* (for old kernels) partitions
+ * scannig is enabled for all loop devices.
+ */
+int loopcxt_is_partscan(struct loopdev_cxt *lc)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+	if (sysfs) {
+		/* kernel >= 3.2 */
+		int fl;
+		if (sysfs_read_int(sysfs, "loop/partscan", &fl) == 0)
+			return fl;
+	}
+
+	/* old kernels (including kernels without loopN/loop/<flags> directory */
+	return loopmod_supports_partscan();
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the autoclear flags is set.
+ */
+int loopcxt_is_autoclear(struct loopdev_cxt *lc)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+	if (sysfs) {
+		int fl;
+		if (sysfs_read_int(sysfs, "loop/autoclear", &fl) == 0)
+			return fl;
+	}
+
+	if (loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+		if (lo)
+			return lo->lo_flags & LO_FLAGS_AUTOCLEAR;
+	}
+	return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the readonly flags is set.
+ */
+int loopcxt_is_readonly(struct loopdev_cxt *lc)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+	if (sysfs) {
+		int fl;
+		if (sysfs_read_int(sysfs, "ro", &fl) == 0)
+			return fl;
+	}
+
+	if (loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+		if (lo)
+			return lo->lo_flags & LO_FLAGS_READ_ONLY;
+	}
+	return 0;
+}
+
+/*
+ * @lc: context
+ * @st: backing file stat or NULL
+ * @backing_file: filename
+ * @offset: offset
+ * @flags: LOOPDEV_FL_OFFSET if @offset should not be ignored
+ *
+ * Returns 1 if the current @lc loopdev is associated with the given backing
+ * file. Note that the preferred way is to use devno and inode number rather
+ * than filename. The @backing_file filename is poor solution usable in case
+ * that you don't have rights to call stat().
+ *
+ * Don't forget that old kernels provide very restricted (in size) backing
+ * filename by LOOP_GET_STAT64 ioctl only.
+ */
+int loopcxt_is_used(struct loopdev_cxt *lc,
+		    struct stat *st,
+		    const char *backing_file,
+		    uint64_t offset,
+		    int flags)
+{
+	ino_t ino;
+	dev_t dev;
+
+	if (!lc)
+		return 0;
+
+	DBG(lc, loopdev_debug("checking %s vs. %s",
+				loopcxt_get_device(lc),
+				backing_file));
+
+	if (st && loopcxt_get_backing_inode(lc, &ino) == 0 &&
+		  loopcxt_get_backing_devno(lc, &dev) == 0) {
+
+		if (ino == st->st_ino && dev == st->st_dev)
+			goto found;
+
+		/* don't use filename if we have devno and inode */
+		return 0;
+	}
+
+	/* poor man's solution */
+	if (backing_file) {
+		char *name = loopcxt_get_backing_file(lc);
+		int rc = name && strcmp(name, backing_file) == 0;
+
+		free(name);
+		if (rc)
+			goto found;
+	}
+
+	return 0;
+found:
+	if (flags & LOOPDEV_FL_OFFSET) {
+		uint64_t off;
+
+		return loopcxt_get_offset(lc, &off) == 0 && off == offset;
+	}
+	return 1;
+}
+
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
+int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset)
+{
+	if (!lc)
+		return -EINVAL;
+	lc->info.lo_offset = offset;
+
+	DBG(lc, loopdev_debug("set offset=%jd", offset));
+	return 0;
+}
+
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
+int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit)
+{
+	if (!lc)
+		return -EINVAL;
+	lc->info.lo_sizelimit = sizelimit;
+
+	DBG(lc, loopdev_debug("set sizelimit=%jd", sizelimit));
+	return 0;
+}
+
+/*
+ * @lc: context
+ * @flags: kernel LO_FLAGS_{READ_ONLY,USE_AOPS,AUTOCLEAR} flags
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags)
+{
+	if (!lc)
+		return -EINVAL;
+	lc->info.lo_flags = flags;
+
+	DBG(lc, loopdev_debug("set flags=%u", (unsigned) flags));
+	return 0;
+}
+
+/*
+ * @lc: context
+ * @filename: backing file path (the path will be canonicalized)
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename)
+{
+	if (!lc)
+		return -EINVAL;
+
+	lc->filename = canonicalize_path(filename);
+	if (!lc->filename)
+		return -errno;
+
+	strncpy((char *)lc->info.lo_file_name, lc->filename, LO_NAME_SIZE);
+	lc->info.lo_file_name[LO_NAME_SIZE- 1] = '\0';
+
+	DBG(lc, loopdev_debug("set backing file=%s", lc->info.lo_file_name));
+	return 0;
+}
+
+/*
+ * @cl: context
+ *
+ * Associate the current device (see loopcxt_{set,get}_device()) with
+ * a file (see loopcxt_set_backing_file()).
+ *
+ * The device is initialized read-write by default. If you want read-only
+ * device then set LO_FLAGS_READ_ONLY by loopcxt_set_flags(). The LOOPDEV_FL_*
+ * flags are ignored and modified according to LO_FLAGS_*.
+ *
+ * If the device is already open by loopcxt_get_fd() then this setup device
+ * function will re-open the device to fix read/write mode.
+ *
+ * The device is also initialized read-only if the backing file is not
+ * possible to open read-write (e.g. read-only FS).
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int loopcxt_setup_device(struct loopdev_cxt *lc)
+{
+	int file_fd, dev_fd, mode = O_RDWR, rc = -1;
+
+	if (!lc || !*lc->device || !lc->filename)
+		return -EINVAL;
+
+	DBG(lc, loopdev_debug("device setup requested"));
+
+	/*
+	 * Open backing file and device
+	 */
+	if (lc->info.lo_flags & LO_FLAGS_READ_ONLY)
+		mode = O_RDONLY;
+
+	if ((file_fd = open(lc->filename, mode)) < 0) {
+		if (mode != O_RDONLY && (errno == EROFS || errno == EACCES))
+			file_fd = open(lc->filename, mode = O_RDONLY);
+
+		if (file_fd < 0) {
+			DBG(lc, loopdev_debug("open backing file failed: %m"));
+			return -errno;
+		}
+	}
+	DBG(lc, loopdev_debug("setup: backing file open: OK"));
+
+	if (lc->fd != -1 && lc->mode != mode) {
+		DBG(lc, loopdev_debug("closing already open device (mode mismatch)"));
+		close(lc->fd);
+		lc->fd = -1;
+		lc->mode = 0;
+	}
+
+	if (mode == O_RDONLY) {
+		lc->flags |= LOOPDEV_FL_RDONLY;			/* open() mode */
+		lc->info.lo_flags |= LO_FLAGS_READ_ONLY;	/* kernel loopdev mode */
+	} else {
+		lc->flags |= LOOPDEV_FL_RDWR;			/* open() mode */
+		lc->info.lo_flags &= ~LO_FLAGS_READ_ONLY;
+		lc->flags &= ~LOOPDEV_FL_RDONLY;
+	}
+
+	dev_fd = loopcxt_get_fd(lc);
+	if (dev_fd < 0) {
+		rc = -errno;
+		goto err;
+	}
+
+	DBG(lc, loopdev_debug("setup: device open: OK"));
+
+	/*
+	 * Set FD
+	 */
+	if (ioctl(dev_fd, LOOP_SET_FD, file_fd) < 0) {
+		rc = -errno;
+		DBG(lc, loopdev_debug("LOOP_SET_FD failed: %m"));
+		goto err;
+	}
+
+	DBG(lc, loopdev_debug("setup: LOOP_SET_FD: OK"));
+
+	close(file_fd);
+	file_fd = -1;
+
+	if (ioctl(dev_fd, LOOP_SET_STATUS64, &lc->info)) {
+		DBG(lc, loopdev_debug("LOOP_SET_STATUS64 failed: %m"));
+		goto err;
+	}
+
+	DBG(lc, loopdev_debug("setup: LOOP_SET_STATUS64: OK"));
+
+	memset(&lc->info, 0, sizeof(lc->info));
+	lc->has_info = 0;
+	lc->info_failed = 0;
+
+	DBG(lc, loopdev_debug("setup success [rc=0]"));
+	return 0;
+err:
+	if (file_fd >= 0)
+		close(file_fd);
+	if (dev_fd >= 0)
+		ioctl(dev_fd, LOOP_CLR_FD, 0);
+
+	DBG(lc, loopdev_debug("setup failed [rc=%d]", rc));
+	return rc;
+}
+
+int loopcxt_delete_device(struct loopdev_cxt *lc)
+{
+	int fd = loopcxt_get_fd(lc);
+
+	if (fd < 0)
+		return -EINVAL;
+
+	if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
+		DBG(lc, loopdev_debug("LOOP_CLR_FD failed: %m"));
+		return -errno;
+	}
+
+	DBG(lc, loopdev_debug("device removed"));
+	return 0;
+}
+
+/*
+ * Note that LOOP_CTL_GET_FREE ioctl is supported since kernel 3.1. In older
+ * kernels we have to check all loop devices to found unused one.
+ *
+ * See kernel commit 770fe30a46a12b6fb6b63fbe1737654d28e8484.
+ */
+int loopcxt_find_unused(struct loopdev_cxt *lc)
+{
+	int rc = -1;
+
+	DBG(lc, loopdev_debug("find_unused requested"));
+
+	if (lc->flags & LOOPDEV_FL_CONTROL) {
+		int ctl = open(_PATH_DEV_LOOPCTL, O_RDWR);
+
+		if (ctl >= 0)
+			rc = ioctl(ctl, LOOP_CTL_GET_FREE);
+		if (rc >= 0) {
+			char name[16];
+			snprintf(name, sizeof(name), "loop%d", rc);
+
+			rc = loopiter_set_device(lc, name);
+		}
+		if (ctl >= 0)
+			close(ctl);
+		DBG(lc, loopdev_debug("find_unused by loop-control [rc=%d]", rc));
+	}
+
+	if (rc < 0) {
+		rc = loopcxt_init_iterator(lc, LOOPITER_FL_FREE);
+		if (rc)
+			return rc;
+
+		rc = loopcxt_next(lc);
+		loopcxt_deinit_iterator(lc);
+		DBG(lc, loopdev_debug("find_unused by scan [rc=%d]", rc));
+	}
+	return rc;
+}
+
+
+
+/*
+ * Return: TRUE/FALSE
+ */
+int loopdev_is_autoclear(const char *device)
+{
+	struct loopdev_cxt lc;
+	int rc;
+
+	if (!device)
+		return 0;
+
+	rc = loopcxt_init(&lc, 0);
+	if (!rc)
+		rc = loopcxt_set_device(&lc, device);
+	if (!rc)
+		rc = loopcxt_is_autoclear(&lc);
+
+	loopcxt_deinit(&lc);
+	return rc;
+}
+
+char *loopdev_get_backing_file(const char *device)
+{
+	struct loopdev_cxt lc;
+	char *res = NULL;
+
+	if (!device)
+		return NULL;
+	if (loopcxt_init(&lc, 0))
+		return NULL;
+	if (loopcxt_set_device(&lc, device) == 0)
+		res = loopcxt_get_backing_file(&lc);
+
+	loopcxt_deinit(&lc);
+	return res;
+}
+
+/*
+ * Returns: TRUE/FALSE
+ */
+int loopdev_is_used(const char *device, const char *filename,
+		    uint64_t offset, int flags)
+{
+	struct loopdev_cxt lc;
+	struct stat st;
+	int rc = 0;
+
+	if (!device || !filename)
+		return 0;
+
+	rc = loopcxt_init(&lc, 0);
+	if (!rc)
+		rc = loopcxt_set_device(&lc, device);
+	if (rc)
+		return rc;
+
+	rc = !stat(filename, &st);
+	rc = loopcxt_is_used(&lc, rc ? &st : NULL, filename, offset, flags);
+
+	loopcxt_deinit(&lc);
+	return rc;
+}
+
+int loopdev_delete(const char *device)
+{
+	struct loopdev_cxt lc;
+	int rc;
+
+	if (!device)
+		return -EINVAL;
+
+	rc = loopcxt_init(&lc, 0);
+	if (!rc)
+		rc = loopcxt_set_device(&lc, device);
+	if (!rc)
+		rc = loopcxt_delete_device(&lc);
+	loopcxt_deinit(&lc);
+	return rc;
+}
+
+/*
+ * Returns: 0 = success, < 0 error, 1 not found
+ */
+int loopcxt_find_by_backing_file(struct loopdev_cxt *lc, const char *filename,
+				 uint64_t offset, int flags)
+{
+	int rc, hasst;
+	struct stat st;
+
+	if (!filename)
+		return -EINVAL;
+
+	hasst = !stat(filename, &st);
+
+	rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED);
+	if (rc)
+		return rc;
+
+	while ((rc = loopcxt_next(lc)) == 0) {
+
+		if (loopcxt_is_used(lc, hasst ? &st : NULL,
+					filename, offset, flags))
+			break;
+	}
+
+	loopcxt_deinit_iterator(lc);
+	return rc;
+}
+
+/*
+ * Returns allocated string with device name
+ */
+char *loopdev_find_by_backing_file(const char *filename, uint64_t offset, int flags)
+{
+	struct loopdev_cxt lc;
+	char *res = NULL;
+
+	if (!filename)
+		return NULL;
+
+	if (loopcxt_init(&lc, 0))
+		return NULL;
+	if (loopcxt_find_by_backing_file(&lc, filename, offset, flags) == 0)
+		res = loopcxt_strdup_device(&lc);
+	loopcxt_deinit(&lc);
+
+	return res;
+}
+
+/*
+ * Returns number of loop devices associated with @file, if only one loop
+ * device is associeted with the given @filename and @loopdev is not NULL then
+ * @loopdev returns name of the device.
+ */
+int loopdev_count_by_backing_file(const char *filename, char **loopdev)
+{
+	struct loopdev_cxt lc;
+	int count = 0, rc;
+
+	if (!filename)
+		return -1;
+
+	rc = loopcxt_init(&lc, 0);
+	if (rc)
+		return rc;
+	if (loopcxt_init_iterator(&lc, LOOPITER_FL_USED))
+		return -1;
+
+	while(loopcxt_next(&lc) == 0) {
+		char *backing = loopcxt_get_backing_file(&lc);
+
+		if (!backing || strcmp(backing, filename)) {
+			free(backing);
+			continue;
+		}
+
+		free(backing);
+		if (loopdev && count == 0)
+			*loopdev = loopcxt_strdup_device(&lc);
+		count++;
+	}
+
+	loopcxt_deinit(&lc);
+
+	if (loopdev && count > 1) {
+		free(*loopdev);
+		*loopdev = NULL;
+	}
+	return count;
+}
+
+
+#ifdef TEST_PROGRAM_LOOPDEV
+#include <errno.h>
+#include <err.h>
+
+static void test_loop_info(const char *device, int flags, int debug)
+{
+	struct loopdev_cxt lc;
+	char *p;
+	uint64_t u64;
+
+	if (loopcxt_init(&lc, flags))
+		return;
+	loopcxt_enable_debug(&lc, debug);
+
+	if (loopcxt_set_device(&lc, device))
+		err(EXIT_FAILURE, "failed to set device");
+
+	p = loopcxt_get_backing_file(&lc);
+	printf("\tBACKING FILE: %s\n", p);
+	free(p);
+
+	if (loopcxt_get_offset(&lc, &u64) == 0)
+		printf("\tOFFSET: %jd\n", u64);
+
+	if (loopcxt_get_sizelimit(&lc, &u64) == 0)
+		printf("\tSIZE LIMIT: %jd\n", u64);
+
+	printf("\tAUTOCLEAR: %s\n", loopcxt_is_autoclear(&lc) ? "YES" : "NOT");
+
+	loopcxt_deinit(&lc);
+}
+
+static void test_loop_scan(int flags, int debug)
+{
+	struct loopdev_cxt lc;
+	int rc;
+
+	if (loopcxt_init(&lc, 0))
+		return;
+	loopcxt_enable_debug(&lc, debug);
+
+	if (loopcxt_init_iterator(&lc, flags))
+		err(EXIT_FAILURE, "iterator initlization failed");
+
+	while((rc = loopcxt_next(&lc)) == 0) {
+		const char *device = loopcxt_get_device(&lc);
+
+		if (flags & LOOPITER_FL_USED) {
+			char *backing = loopcxt_get_backing_file(&lc);
+			printf("\t%s: %s\n", device, backing);
+			free(backing);
+		} else
+			printf("\t%s\n", device);
+	}
+
+	if (rc < 0)
+		err(EXIT_FAILURE, "loopdevs scanning failed");
+
+	loopcxt_deinit(&lc);
+}
+
+static int test_loop_setup(const char *filename, const char *device, int debug)
+{
+	struct loopdev_cxt lc;
+	int rc;
+
+	rc = loopcxt_init(&lc, 0);
+	if (rc)
+		return rc;
+	loopcxt_enable_debug(&lc, debug);
+
+	if (device) {
+		rc = loopcxt_set_device(&lc, device);
+		if (rc)
+			err(EXIT_FAILURE, "failed to set device: %s", device);
+	}
+
+	do {
+		if (!device) {
+			rc = loopcxt_find_unused(&lc);
+			if (rc)
+				err(EXIT_FAILURE, "failed to find unused device");
+			printf("Trying to use '%s'\n", loopcxt_get_device(&lc));
+		}
+
+		if (loopcxt_set_backing_file(&lc, filename))
+			err(EXIT_FAILURE, "failed to set backing file");
+
+		rc = loopcxt_setup_device(&lc);
+		if (rc == 0)
+			break;		/* success */
+
+		if (device || rc != -EBUSY)
+			err(EXIT_FAILURE, "failed to setup device for %s",
+					lc.filename);
+
+		printf("device stolen...trying again\n");
+	} while (1);
+
+	loopcxt_deinit(&lc);
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	int dbg;
+
+	if (argc < 2)
+		goto usage;
+
+	dbg = getenv("LOOPDEV_DEBUG") == NULL ? 0 : 1;
+
+	if (argc == 3 && strcmp(argv[1], "--info") == 0) {
+		printf("---sysfs & ioctl:---\n");
+		test_loop_info(argv[2], 0, dbg);
+		printf("---sysfs only:---\n");
+		test_loop_info(argv[2], LOOPDEV_FL_NOIOCTL, dbg);
+		printf("---ioctl only:---\n");
+		test_loop_info(argv[2], LOOPDEV_FL_NOSYSFS, dbg);
+
+	} else if (argc == 2 && strcmp(argv[1], "--used") == 0) {
+		printf("---all used devices---\n");
+		test_loop_scan(LOOPITER_FL_USED, dbg);
+
+	} else if (argc == 2 && strcmp(argv[1], "--free") == 0) {
+		printf("---all free devices---\n");
+		test_loop_scan(LOOPITER_FL_FREE, dbg);
+
+	} else if (argc >= 3 && strcmp(argv[1], "--setup") == 0) {
+		test_loop_setup(argv[2], argv[3], dbg);
+
+	} else if (argc == 3 && strcmp(argv[1], "--delete") == 0) {
+		if (loopdev_delete(argv[2]))
+			errx(EXIT_FAILURE, "failed to deinitialize device %s", argv[2]);
+	} else
+		goto usage;
+
+	return EXIT_SUCCESS;
+
+usage:
+	errx(EXIT_FAILURE, "usage: \n"
+			   "  %1$s --info <device>\n"
+			   "  %1$s --free\n"
+			   "  %1$s --used\n"
+			   "  %1$s --setup <filename> [<device>]\n"
+			   "  %1$s --delete\n",
+			   argv[0]);
+}
+
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/loopdev.h b/libblkid/loopdev.h
new file mode 100644
index 0000000..6efa0c7
--- /dev/null
+++ b/libblkid/loopdev.h
@@ -0,0 +1,192 @@
+#ifndef UTIL_LINUX_LOOPDEV_H
+#define UTIL_LINUX_LOOPDEV_H
+
+#include "sysfs.h"
+
+/*
+ * loop_info.lo_encrypt_type
+ */
+#define LO_CRYPT_NONE	0
+#define LO_CRYPT_XOR	1
+#define LO_CRYPT_DES	2
+#define LO_CRYPT_CRYPTOAPI 18
+
+#define LOOP_SET_FD		0x4C00
+#define LOOP_CLR_FD		0x4C01
+/*
+ * Obsolete (kernel < 2.6)
+ *
+ * #define LOOP_SET_STATUS	0x4C02
+ * #define LOOP_GET_STATUS	0x4C03
+ */
+#define LOOP_SET_STATUS64	0x4C04
+#define LOOP_GET_STATUS64	0x4C05
+/* #define LOOP_CHANGE_FD	0x4C06 */
+#define LOOP_SET_CAPACITY	0x4C07
+
+/* /dev/loop-control interface */
+#ifndef LOOP_CTL_ADD
+# define LOOP_CTL_ADD		0x4C80
+# define LOOP_CTL_REMOVE	0x4C81
+# define LOOP_CTL_GET_FREE	0x4C82
+#endif
+
+/*
+ * loop_info.lo_flags
+ */
+enum {
+	LO_FLAGS_READ_ONLY  = 1,
+	LO_FLAGS_USE_AOPS   = 2,
+	LO_FLAGS_AUTOCLEAR  = 4,	/* kernel >= 2.6.25 */
+	LO_FLAGS_PARTSCAN   = 8,	/* kernel >= 3.2 */
+};
+
+#define LO_NAME_SIZE	64
+#define LO_KEY_SIZE	32
+
+/*
+ * Linux LOOP_{SET,GET}_STATUS64 ioclt struct
+ */
+struct loop_info64 {
+	uint64_t	lo_device;
+	uint64_t	lo_inode;
+	uint64_t	lo_rdevice;
+	uint64_t	lo_offset;
+	uint64_t	lo_sizelimit; /* bytes, 0 == max available */
+	uint32_t	lo_number;
+	uint32_t	lo_encrypt_type;
+	uint32_t	lo_encrypt_key_size;
+	uint32_t	lo_flags;
+	uint8_t		lo_file_name[LO_NAME_SIZE];
+	uint8_t		lo_crypt_name[LO_NAME_SIZE];
+	uint8_t		lo_encrypt_key[LO_KEY_SIZE];
+	uint64_t	lo_init[2];
+};
+
+#define LOOPDEV_MAJOR		7	/* loop major number */
+#define LOOPDEV_DEFAULT_NNODES	8	/* default number of loop devices */
+
+struct loopdev_iter {
+	FILE		*proc;		/* /proc/partitions */
+	DIR		*sysblock;	/* /sys/block */
+	int		ncur;		/* current position */
+	int		*minors;	/* ary of minor numbers (when scan whole /dev) */
+	int		nminors;	/* number of items in *minors */
+	int		ct_perm;	/* count permission problems */
+	int		ct_succ;	/* count number of detected devices */
+
+	unsigned int	done:1;		/* scanning done */
+	unsigned int	default_check:1;/* check first LOOPDEV_NLOOPS */
+	int		flags;		/* LOOPITER_FL_* flags */
+};
+
+enum {
+	LOOPITER_FL_FREE	= (1 << 0),
+	LOOPITER_FL_USED	= (1 << 1)
+};
+
+/*
+ * handler for work with loop devices
+ */
+struct loopdev_cxt {
+	char		device[128];	/* device path (e.g. /dev/loop<N>) */
+	char		*filename;	/* backing file for loopcxt_set_... */
+	int		fd;		/* open(/dev/looo<N>) */
+	int		mode;		/* fd mode O_{RDONLY,RDWR} */
+
+	int		flags;		/* LOOPDEV_FL_* flags */
+	unsigned int	has_info:1;	/* .info contains data */
+	unsigned int	extra_check:1;	/* unusual stuff for iterator */
+	unsigned int	debug:1;	/* debug mode ON/OFF */
+	unsigned int	info_failed:1;	/* LOOP_GET_STATUS ioctl failed */
+
+	struct sysfs_cxt	sysfs;	/* pointer to /sys/dev/block/<maj:min>/ */
+	struct loop_info64	info;	/* for GET/SET ioctl */
+	struct loopdev_iter	iter;	/* scans /sys or /dev for used/free devices */
+};
+
+#define UL_LOOPDEVCXT_EMPTY { .fd = -1, .sysfs = UL_SYSFSCXT_EMPTY }
+
+/*
+ * loopdev_cxt.flags
+ */
+enum {
+	LOOPDEV_FL_RDONLY	= (1 << 0),	/* open(/dev/loop) mode; default */
+	LOOPDEV_FL_RDWR		= (1 << 1),	/* necessary for loop setup only */
+	LOOPDEV_FL_OFFSET	= (1 << 4),
+	LOOPDEV_FL_NOSYSFS	= (1 << 5),
+	LOOPDEV_FL_NOIOCTL	= (1 << 6),
+	LOOPDEV_FL_DEVSUBDIR	= (1 << 7),
+	LOOPDEV_FL_CONTROL	= (1 << 8),	/* system with /dev/loop-control */
+	LOOPDEV_FL_SIZELIMIT	= (1 << 9)
+};
+
+/*
+ * High-level
+ */
+extern int loopmod_supports_partscan(void);
+
+extern int is_loopdev(const char *device);
+extern int loopdev_is_autoclear(const char *device);
+
+extern char *loopdev_get_backing_file(const char *device);
+extern int loopdev_is_used(const char *device, const char *filename,
+			   uint64_t offset, int flags);
+extern char *loopdev_find_by_backing_file(const char *filename,
+					  uint64_t offset, int flags);
+extern int loopcxt_find_unused(struct loopdev_cxt *lc);
+extern int loopdev_delete(const char *device);
+extern int loopdev_count_by_backing_file(const char *filename, char **loopdev);
+
+/*
+ * Low-level
+ */
+extern int loopcxt_init(struct loopdev_cxt *lc, int flags)
+				__attribute__ ((warn_unused_result));
+extern void loopcxt_deinit(struct loopdev_cxt *lc);
+extern void loopcxt_enable_debug(struct loopdev_cxt *lc, int enable);
+
+extern int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
+				__attribute__ ((warn_unused_result));
+extern int loopcxt_has_device(struct loopdev_cxt *lc);
+extern char *loopcxt_strdup_device(struct loopdev_cxt *lc);
+extern const char *loopcxt_get_device(struct loopdev_cxt *lc);
+extern struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc);
+extern struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc);
+
+extern int loopcxt_get_fd(struct loopdev_cxt *lc);
+extern int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode);
+
+extern int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags);
+extern int loopcxt_deinit_iterator(struct loopdev_cxt *lc);
+extern int loopcxt_next(struct loopdev_cxt *lc);
+
+extern int loopcxt_setup_device(struct loopdev_cxt *lc);
+extern int loopcxt_delete_device(struct loopdev_cxt *lc);
+
+int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset);
+int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit);
+int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags);
+int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename);
+
+extern char *loopcxt_get_backing_file(struct loopdev_cxt *lc);
+extern int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno);
+extern int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino);
+extern int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset);
+extern int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size);
+extern int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type);
+extern const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc);
+extern int loopcxt_is_autoclear(struct loopdev_cxt *lc);
+extern int loopcxt_is_readonly(struct loopdev_cxt *lc);
+extern int loopcxt_is_partscan(struct loopdev_cxt *lc);
+extern int loopcxt_find_by_backing_file(struct loopdev_cxt *lc,
+				const char *filename,
+                                uint64_t offset, int flags);
+
+extern int loopcxt_is_used(struct loopdev_cxt *lc,
+                    struct stat *st,
+                    const char *backing_file,
+                    uint64_t offset,
+                    int flags);
+
+#endif /* UTIL_LINUX_LOOPDEV_H */
diff --git a/libblkid/lsi_raid.c b/libblkid/lsi_raid.c
new file mode 100644
index 0000000..56721dd
--- /dev/null
+++ b/libblkid/lsi_raid.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct lsi_metadata {
+	uint8_t		sig[6];
+};
+
+
+#define LSI_SIGNATURE		"$XIDE$"
+
+static int probe_lsiraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct lsi_metadata *lsi;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 1) * 0x200;
+	lsi = (struct lsi_metadata *)
+		blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct lsi_metadata));
+	if (!lsi)
+		return -1;
+
+	if (memcmp(lsi->sig, LSI_SIGNATURE, sizeof(LSI_SIGNATURE)-1) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(lsi->sig),
+				(unsigned char *) lsi->sig))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo lsiraid_idinfo = {
+	.name		= "lsi_mega_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_lsiraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/luks.c b/libblkid/luks.c
new file mode 100644
index 0000000..f716e31
--- /dev/null
+++ b/libblkid/luks.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+#define LUKS_CIPHERNAME_L		32
+#define LUKS_CIPHERMODE_L		32
+#define LUKS_HASHSPEC_L			32
+#define LUKS_DIGESTSIZE			20
+#define LUKS_SALTSIZE			32
+#define LUKS_MAGIC_L			6
+#define UUID_STRING_L			40
+
+struct luks_phdr {
+	uint8_t		magic[LUKS_MAGIC_L];
+	uint16_t	version;
+	uint8_t		cipherName[LUKS_CIPHERNAME_L];
+	uint8_t		cipherMode[LUKS_CIPHERMODE_L];
+	uint8_t		hashSpec[LUKS_HASHSPEC_L];
+	uint32_t	payloadOffset;
+	uint32_t	keyBytes;
+	uint8_t		mkDigest[LUKS_DIGESTSIZE];
+	uint8_t		mkDigestSalt[LUKS_SALTSIZE];
+	uint32_t	mkDigestIterations;
+	uint8_t		uuid[UUID_STRING_L];
+} __attribute__((packed));
+
+static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct luks_phdr *header;
+
+	header = blkid_probe_get_sb(pr, mag, struct luks_phdr);
+	if (header == NULL)
+		return -1;
+
+	blkid_probe_strncpy_uuid(pr, (unsigned char *) header->uuid,
+			sizeof(header->uuid));
+	blkid_probe_sprintf_version(pr, "%u", be16_to_cpu(header->version));
+	return 0;
+}
+
+const struct blkid_idinfo luks_idinfo =
+{
+	.name		= "crypto_LUKS",
+	.usage		= BLKID_USAGE_CRYPTO,
+	.probefunc	= probe_luks,
+	.magics		=
+	{
+		{ .magic = "LUKS\xba\xbe", .len = 6 },
+		{ NULL }
+	}
+};
diff --git a/libblkid/lvm1.c b/libblkid/lvm1.c
new file mode 100644
index 0000000..632c42b
--- /dev/null
+++ b/libblkid/lvm1.c
@@ -0,0 +1,150 @@
+/*
+ * lvm topology
+ * -- this is fallback for old systems where the topology information is not
+ *    exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+#ifndef LVM_BLK_MAJOR
+# define LVM_BLK_MAJOR     58
+#endif
+
+static int is_lvm_device(dev_t devno)
+{
+	if (major(devno) == LVM_BLK_MAJOR)
+		return 1;
+	return blkid_driver_has_major("lvm", major(devno));
+}
+
+static int probe_lvm_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	const char *paths[] = {
+		"/usr/local/sbin/lvdisplay",
+		"/usr/sbin/lvdisplay",
+		"/sbin/lvdisplay"
+	};
+	int lvpipe[] = { -1, -1 }, stripes = 0, stripesize = 0;
+	FILE *stream = NULL;
+	char *cmd = NULL, *devname = NULL, buf[1024];
+	size_t i;
+	dev_t devno = blkid_probe_get_devno(pr);
+
+	if (!devno)
+		goto nothing;		/* probably not a block device */
+	if (!is_lvm_device(devno))
+		goto nothing;
+
+	for (i = 0; i < ARRAY_SIZE(paths); i++) {
+		struct stat sb;
+		if (stat(paths[i], &sb) == 0) {
+			cmd = (char *) paths[i];
+			break;
+		}
+	}
+
+	if (!cmd)
+		goto nothing;
+
+	devname = blkid_devno_to_devname(devno);
+	if (!devname)
+		goto nothing;
+
+	if (pipe(lvpipe) < 0) {
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to open pipe: errno=%d", errno));
+		goto nothing;
+	}
+
+	switch (fork()) {
+	case 0:
+	{
+		char *lvargv[3];
+
+		/* Plumbing */
+		close(lvpipe[0]);
+
+		if (lvpipe[1] != STDOUT_FILENO)
+			dup2(lvpipe[1], STDOUT_FILENO);
+
+		/* The libblkid library could linked with setuid programs */
+		if (setgid(getgid()) < 0)
+			 exit(1);
+		if (setuid(getuid()) < 0)
+			 exit(1);
+
+		lvargv[0] = cmd;
+		lvargv[1] = devname;
+		lvargv[2] = NULL;
+
+		execv(lvargv[0], lvargv);
+
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to execute %s: errno=%d", cmd, errno));
+		exit(1);
+	}
+	case -1:
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to forking: errno=%d", errno));
+		goto nothing;
+	default:
+		break;
+	}
+
+	stream = fdopen(lvpipe[0], "r");
+	if (!stream)
+		goto nothing;
+
+	while (fgets(buf, sizeof(buf), stream) != NULL) {
+		if (!strncmp(buf, "Stripes", 7))
+			sscanf(buf, "Stripes %d", &stripes);
+
+		if (!strncmp(buf, "Stripe size", 11))
+			sscanf(buf, "Stripe size (KByte) %d", &stripesize);
+	}
+
+	if (!stripes)
+		goto nothing;
+
+	blkid_topology_set_minimum_io_size(pr, stripesize << 10);
+	blkid_topology_set_optimal_io_size(pr, (stripes * stripesize) << 10);
+
+	free(devname);
+	fclose(stream);
+	close(lvpipe[1]);
+	return 0;
+
+nothing:
+	free(devname);
+	if (stream)
+		fclose(stream);
+	else if (lvpipe[0] != -1)
+		close(lvpipe[0]);
+	if (lvpipe[1] != -1)
+		close(lvpipe[1]);
+	return 1;
+}
+
+const struct blkid_idinfo lvm_tp_idinfo =
+{
+	.name		= "lvm",
+	.probefunc	= probe_lvm_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/lvm2.c b/libblkid/lvm2.c
new file mode 100644
index 0000000..0afc773
--- /dev/null
+++ b/libblkid/lvm2.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2012 Milan Broz <mbroz@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+#define LVM1_ID_LEN 128
+#define LVM2_ID_LEN 32
+
+struct lvm2_pv_label_header {
+	/* label_header */
+	uint8_t		id[8];		/* LABELONE */
+	uint64_t	sector_xl;	/* Sector number of this label */
+	uint32_t	crc_xl;		/* From next field to end of sector */
+	uint32_t	offset_xl;	/* Offset from start of struct to contents */
+	uint8_t		type[8];	/* LVM2 001 */
+	/* pv_header */
+	uint8_t		pv_uuid[LVM2_ID_LEN];
+} __attribute__ ((packed));
+
+struct lvm1_pv_label_header {
+	uint8_t id[2];			/* HM */
+	uint16_t version;		/* version 1 or 2 */
+	uint32_t _notused[10];		/* lvm1 internals */
+	uint8_t pv_uuid[LVM1_ID_LEN];
+} __attribute__ ((packed));
+
+#define LVM2_LABEL_SIZE 512
+static unsigned int lvm2_calc_crc(const void *buf, unsigned int size)
+{
+	static const unsigned int crctab[] = {
+		0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+		0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+		0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+		0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+	};
+	unsigned int i, crc = 0xf597a6cf;
+	const uint8_t *data = (const uint8_t *) buf;
+
+	for (i = 0; i < size; i++) {
+		crc ^= *data++;
+		crc = (crc >> 4) ^ crctab[crc & 0xf];
+		crc = (crc >> 4) ^ crctab[crc & 0xf];
+	}
+	return crc;
+}
+
+/* Length of real UUID is always LVM2_ID_LEN */
+static void format_lvm_uuid(char *dst_uuid, char *src_uuid)
+{
+	unsigned int i, b;
+
+	for (i = 0, b = 1; i < LVM2_ID_LEN; i++, b <<= 1) {
+		if (b & 0x4444440)
+			*dst_uuid++ = '-';
+		*dst_uuid++ = *src_uuid++;
+	}
+	*dst_uuid = '\0';
+}
+
+static int probe_lvm2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	int sector = mag->kboff << 1;
+	struct lvm2_pv_label_header *label;
+	char uuid[LVM2_ID_LEN + 7];
+	unsigned char *buf;
+
+	buf = blkid_probe_get_buffer(pr,
+			mag->kboff << 10,
+			512 + sizeof(struct lvm2_pv_label_header));
+	if (!buf)
+		return -1;
+
+	/* buf is at 0k or 1k offset; find label inside */
+	if (memcmp(buf, "LABELONE", 8) == 0) {
+		label = (struct lvm2_pv_label_header *) buf;
+	} else if (memcmp(buf + 512, "LABELONE", 8) == 0) {
+		label = (struct lvm2_pv_label_header *)(buf + 512);
+		sector++;
+	} else {
+		return 1;
+	}
+
+	if (le64_to_cpu(label->sector_xl) != (unsigned) sector)
+		return 1;
+
+	if (lvm2_calc_crc(&label->offset_xl, LVM2_LABEL_SIZE -
+			((char *) &label->offset_xl - (char *) label)) !=
+			le32_to_cpu(label->crc_xl)) {
+		DBG(DEBUG_PROBE,
+		    printf("LVM2: label checksum incorrect at sector %d\n",
+			   sector));
+		return 1;
+	}
+
+	format_lvm_uuid(uuid, (char *) label->pv_uuid);
+	blkid_probe_sprintf_uuid(pr, label->pv_uuid, sizeof(label->pv_uuid),
+			"%s", uuid);
+
+	/* the mag->magic is the same string as label->type,
+	 * but zero terminated */
+	blkid_probe_set_version(pr, mag->magic);
+
+	/* LVM (pvcreate) wipes begin of the device -- let's remember this
+	 * to resolve conflicts bettween LVM and partition tables, ...
+	 */
+	blkid_probe_set_wiper(pr, 0, 8 * 1024);
+
+	return 0;
+}
+
+static int probe_lvm1(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct lvm1_pv_label_header *label;
+	char uuid[LVM2_ID_LEN + 7];
+	unsigned int version;
+
+	label = blkid_probe_get_sb(pr, mag, struct lvm1_pv_label_header);
+	if (!label)
+		return -1;
+
+	version = le16_to_cpu(label->version);
+	if (version != 1 && version != 2)
+		return 1;
+
+	format_lvm_uuid(uuid, (char *) label->pv_uuid);
+	blkid_probe_sprintf_uuid(pr, label->pv_uuid, sizeof(label->pv_uuid),
+			"%s", uuid);
+
+	return 0;
+}
+
+struct verity_sb {
+	uint8_t  signature[8];	/* "verity\0\0" */
+	uint32_t version;	/* superblock version */
+	uint32_t hash_type;	/* 0 - Chrome OS, 1 - normal */
+	uint8_t  uuid[16];	/* UUID of hash device */
+	uint8_t  algorithm[32];/* hash algorithm name */
+	uint32_t data_block_size; /* data block in bytes */
+	uint32_t hash_block_size; /* hash block in bytes */
+	uint64_t data_blocks;	/* number of data blocks */
+	uint16_t salt_size;	/* salt size */
+	uint8_t  _pad1[6];
+	uint8_t  salt[256];	/* salt */
+	uint8_t  _pad2[168];
+} __attribute__((packed));
+
+static int probe_verity(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct verity_sb *sb;
+	unsigned int version;
+
+	sb = blkid_probe_get_sb(pr, mag, struct verity_sb);
+	if (sb == NULL)
+		return -1;
+
+	version = le32_to_cpu(sb->version);
+	if (version != 1)
+		return 1;
+
+	blkid_probe_set_uuid(pr, sb->uuid);
+	blkid_probe_sprintf_version(pr, "%u", version);
+	return 0;
+}
+
+/* NOTE: the original libblkid uses "lvm2pv" as a name */
+const struct blkid_idinfo lvm2_idinfo =
+{
+	.name		= "LVM2_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_lvm2,
+	.magics		=
+	{
+		{ .magic = "LVM2 001", .len = 8, .sboff = 0x218 },
+		{ .magic = "LVM2 001", .len = 8, .sboff = 0x018 },
+		{ .magic = "LVM2 001", .len = 8, .kboff = 1, .sboff = 0x018 },
+		{ .magic = "LVM2 001", .len = 8, .kboff = 1, .sboff = 0x218 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo lvm1_idinfo =
+{
+	.name		= "LVM1_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_lvm1,
+	.magics		=
+	{
+		{ .magic = "HM", .len = 2 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo snapcow_idinfo =
+{
+	.name		= "DM_snapshot_cow",
+	.usage		= BLKID_USAGE_OTHER,
+	.magics		=
+	{
+		{ .magic = "SnAp", .len = 4 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo verity_hash_idinfo =
+{
+	.name		= "DM_verity_hash",
+	.usage		= BLKID_USAGE_CRYPTO,
+	.probefunc	= probe_verity,
+	.magics		=
+	{
+		{ .magic = "verity\0\0", .len = 8 },
+		{ NULL }
+	}
+};
diff --git a/libblkid/mac.c b/libblkid/mac.c
new file mode 100644
index 0000000..e18896c
--- /dev/null
+++ b/libblkid/mac.c
@@ -0,0 +1,183 @@
+/*
+ * mac partitions parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+#define MAC_PARTITION_MAGIC		0x504d
+#define MAC_PARTITION_MAGIC_OLD		0x5453
+
+/*
+ * Mac partition entry
+ * http://developer.apple.com/legacy/mac/library/documentation/mac/Devices/Devices-126.html
+ */
+struct mac_partition {
+	uint16_t	signature;	/* expected to be MAC_PARTITION_MAGIC */
+	uint16_t	reserved;	/* reserved */
+	uint32_t	map_count;	/* # blocks in partition map */
+	uint32_t	start_block;	/* absolute starting block # of partition */
+	uint32_t	block_count;	/* number of blocks in partition */
+	char		name[32];	/* partition name */
+	char		type[32];	/* string type description */
+	uint32_t	data_start;	/* rel block # of first data block */
+	uint32_t	data_count;	/* number of data blocks */
+	uint32_t	status;		/* partition status bits */
+	uint32_t	boot_start;	/* first logical block of boot code */
+	uint32_t	boot_size;	/* size of boot code, in bytes */
+	uint32_t	boot_load;	/* boot code load address */
+	uint32_t	boot_load2;	/* reserved */
+	uint32_t	boot_entry;	/* boot code entry point */
+	uint32_t	boot_entry2;	/* reserved */
+	uint32_t	boot_cksum;	/* boot code checksum */
+	char		processor[16];	/* identifies ISA of boot */
+
+	/* there is more stuff after this that we don't need */
+} __attribute__((packed));
+
+/*
+ * Driver descriptor structure, in block 0
+ * http://developer.apple.com/legacy/mac/library/documentation/mac/Devices/Devices-121.html
+ */
+struct mac_driver_desc {
+	uint16_t	signature;	/* expected to be MAC_DRIVER_MAGIC */
+	uint16_t	block_size;	/* block size of the device */
+	uint32_t	block_count;	/* number of blocks on the device */
+
+	/* there is more stuff after this that we don't need */
+} __attribute__((packed));
+
+static inline unsigned char *get_mac_block(
+					blkid_probe pr,
+					uint16_t block_size,
+					uint32_t num)
+{
+	return blkid_probe_get_buffer(pr,
+			(blkid_loff_t) num * block_size, block_size);
+}
+
+static inline int has_part_signature(struct mac_partition *p)
+{
+	return	be16_to_cpu(p->signature) == MAC_PARTITION_MAGIC ||
+		be16_to_cpu(p->signature) == MAC_PARTITION_MAGIC_OLD;
+}
+
+static int probe_mac_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct mac_driver_desc *md;
+	struct mac_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	uint16_t block_size;
+	uint16_t ssf;	/* sector size fragment */
+	uint32_t nblks, i;
+
+
+	/* The driver descriptor record is always located at physical block 0,
+	 * the first block on the disk.
+	 */
+	md = (struct mac_driver_desc *) blkid_probe_get_sector(pr, 0);
+	if (!md)
+		goto nothing;
+
+	block_size = be16_to_cpu(md->block_size);
+
+	/* The partition map always begins at physical block 1,
+	 * the second block on the disk.
+	 */
+	p = (struct mac_partition *) get_mac_block(pr, block_size, 1);
+	if (!p)
+		goto nothing;
+
+	/* check the first partition signature */
+	if (!has_part_signature(p))
+		goto nothing;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "mac", 0);
+	if (!tab)
+		goto err;
+
+	ssf = block_size / 512;
+	nblks = be32_to_cpu(p->map_count);
+
+	for (i = 1; i <= nblks; ++i) {
+		blkid_partition par;
+		uint32_t start;
+		uint32_t size;
+
+		p = (struct mac_partition *) get_mac_block(pr, block_size, i);
+		if (!p)
+			goto nothing;
+		if (!has_part_signature(p))
+			goto nothing;
+
+		if (be32_to_cpu(p->map_count) != nblks) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"mac: inconsisten map_count in partition map, "
+			        "entry[0]: %d, entry[%d]: %d\n",
+				nblks, i - 1,
+				be32_to_cpu(p->map_count)));
+		}
+
+		/*
+		 * note that libparted ignores some mac partitions according to
+		 * the partition name (e.g. "Apple_Free" or "Apple_Void"). We
+		 * follows Linux kernel and all partitions are visible
+		 */
+
+		start = be32_to_cpu(p->start_block) * ssf;
+		size = be32_to_cpu(p->block_count) * ssf;
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_name(par, (unsigned char *) p->name,
+						sizeof(p->name));
+
+		blkid_partition_set_type_string(par, (unsigned char *) p->type,
+						sizeof(p->type));
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+/*
+ * Mac disk always begin with "Driver Descriptor Record"
+ * (struct mac_driver_desc) and magic 0x4552.
+ */
+const struct blkid_idinfo mac_pt_idinfo =
+{
+	.name		= "mac",
+	.probefunc	= probe_mac_pt,
+	.magics		=
+	{
+		/* big-endian magic string */
+		{ .magic = "\x45\x52", .len = 2 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/mangle.c b/libblkid/mangle.c
new file mode 100644
index 0000000..5236e97
--- /dev/null
+++ b/libblkid/mangle.c
@@ -0,0 +1,166 @@
+/*
+ * Functions for \oct encoding used in mtab/fstab/swaps/etc.
+ *
+ * Based on code from mount(8).
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "mangle.h"
+#include "c.h"
+
+#define isoctal(a)		(((a) & ~7) == '0')
+
+#define from_hex(c)		(isdigit(c) ? c - '0' : tolower(c) - 'a' + 10)
+
+#define is_unwanted_char(x)	(strchr(" \t\n\\", (unsigned int) x) != NULL)
+
+
+char *mangle(const char *s)
+{
+	char *ss, *sp;
+
+	if (!s)
+		return NULL;
+
+	ss = sp = malloc(4 * strlen(s) + 1);
+	if (!sp)
+		return NULL;
+	while(1) {
+		if (!*s) {
+			*sp = '\0';
+			break;
+		}
+		if (is_unwanted_char(*s)) {
+			*sp++ = '\\';
+			*sp++ = '0' + ((*s & 0300) >> 6);
+			*sp++ = '0' + ((*s & 070) >> 3);
+			*sp++ = '0' + (*s & 07);
+		} else
+			*sp++ = *s;
+		s++;
+	}
+	return ss;
+}
+
+
+void unmangle_to_buffer(const char *s, char *buf, size_t len)
+{
+	size_t sz = 0;
+
+	if (!s)
+		return;
+
+	while(*s && sz < len - 1) {
+		if (*s == '\\' && sz + 3 < len - 1 && isoctal(s[1]) &&
+		    isoctal(s[2]) && isoctal(s[3])) {
+
+			*buf++ = 64*(s[1] & 7) + 8*(s[2] & 7) + (s[3] & 7);
+			s += 4;
+			sz += 4;
+		} else {
+			*buf++ = *s++;
+			sz++;
+		}
+	}
+	*buf = '\0';
+}
+
+void unhexmangle_to_buffer(const char *s, char *buf, size_t len)
+{
+	size_t sz = 0;
+
+	if (!s)
+		return;
+
+	while(*s && sz < len - 1) {
+		if (*s == '\\' && sz + 3 < len - 1 && s[1] == 'x' &&
+		    isxdigit(s[2]) && isxdigit(s[3])) {
+
+			*buf++ = from_hex(s[2]) << 4 | from_hex(s[3]);
+			s += 4;
+			sz += 4;
+		} else {
+			*buf++ = *s++;
+			sz++;
+		}
+	}
+	*buf = '\0';
+}
+
+static inline char *skip_nonspaces(const char *s)
+{
+	while (*s && !(*s == ' ' || *s == '\t'))
+		s++;
+	return (char *) s;
+}
+
+/*
+ * Returns mallocated buffer or NULL in case of error.
+ */
+char *unmangle(const char *s, char **end)
+{
+	char *buf;
+	char *e;
+	size_t sz;
+
+	if (!s)
+		return NULL;
+
+	e = skip_nonspaces(s);
+	sz = e - s + 1;
+
+	if (end)
+		*end = e;
+	if (e == s)
+		return NULL;	/* empty string */
+
+	buf = malloc(sz);
+	if (!buf)
+		return NULL;
+
+	unmangle_to_buffer(s, buf, sz);
+	return buf;
+}
+
+#ifdef TEST_PROGRAM
+#include <errno.h>
+int main(int argc, char *argv[])
+{
+	char *p = NULL;
+	if (argc < 3) {
+		fprintf(stderr, "usage: %s --mangle|unmangle <string>\n",
+						program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	if (!strcmp(argv[1], "--mangle")) {
+		p = mangle(argv[2]);
+		printf("mangled: '%s'\n", p);
+		free(p);
+	}
+
+	else if (!strcmp(argv[1], "--unmangle")) {
+		char *x = unmangle(argv[2], NULL);
+
+		if (x) {
+			printf("unmangled: '%s'\n", x);
+			free(x);
+		}
+
+		x = strdup(argv[2]);
+		unmangle_to_buffer(x, x, strlen(x) + 1);
+
+		if (x) {
+			printf("self-unmangled: '%s'\n", x);
+			free(x);
+		}
+	}
+
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/mangle.h b/libblkid/mangle.h
new file mode 100644
index 0000000..ec492b5
--- /dev/null
+++ b/libblkid/mangle.h
@@ -0,0 +1,26 @@
+#ifndef UTIL_LINUX_MANGLE_H
+#define UTIL_LINUX_MANGLE_H
+
+/*
+ * Functions for \oct encoding used in mtab/fstab/swaps/etc.
+ */
+
+extern char *mangle(const char *s);
+
+extern void unmangle_to_buffer(const char *s, char *buf, size_t len);
+void unhexmangle_to_buffer(const char *s, char *buf, size_t len);
+
+extern char *unmangle(const char *s, char **end);
+
+static inline void unmangle_string(char *s)
+{
+	unmangle_to_buffer(s, s, strlen(s) + 1);
+}
+
+static inline void unhexmangle_string(char *s)
+{
+	unhexmangle_to_buffer(s, s, strlen(s) + 1);
+}
+
+#endif /* UTIL_LINUX_MANGLE_H */
+
diff --git a/libblkid/match.c b/libblkid/match.c
new file mode 100644
index 0000000..9be82b0
--- /dev/null
+++ b/libblkid/match.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <string.h>
+
+#include "match.h"
+
+/*
+ * match_fstype:
+ * @type: filesystem type
+ * @pattern: filesystem name or comma delimited list of names
+ *
+ * The @pattern list of filesystem can be prefixed with a global
+ * "no" prefix to invert matching of the whole list. The "no" could
+ * also be used for individual items in the @pattern list. So,
+ * "nofoo,bar" has the same meaning as "nofoo,nobar".
+ */
+int match_fstype(const char *type, const char *pattern)
+{
+	int no = 0;		/* negated types list */
+	int len;
+	const char *p;
+
+	if (!pattern && !type)
+		return 1;
+	if (!pattern)
+		return 0;
+
+	if (!strncmp(pattern, "no", 2)) {
+		no = 1;
+		pattern += 2;
+	}
+
+	/* Does type occur in types, separated by commas? */
+	len = strlen(type);
+	p = pattern;
+	while(1) {
+		if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) &&
+		    (p[len+2] == 0 || p[len+2] == ','))
+			return 0;
+		if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
+			return !no;
+		p = strchr(p,',');
+		if (!p)
+			break;
+		p++;
+	}
+	return no;
+}
diff --git a/libblkid/match.h b/libblkid/match.h
new file mode 100644
index 0000000..94440c2
--- /dev/null
+++ b/libblkid/match.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_MATCH_H
+#define UTIL_LINUX_MATCH_H
+
+extern int match_fstype(const char *type, const char *pattern);
+
+#endif /* UTIL_LINUX_MATCH_H */
diff --git a/libblkid/mbsalign.c b/libblkid/mbsalign.c
new file mode 100644
index 0000000..ea6851f
--- /dev/null
+++ b/libblkid/mbsalign.c
@@ -0,0 +1,291 @@
+/* Align/Truncate a string in a given screen width
+   Copyright (C) 2009-2010 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Pádraig Brady.  */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#include "c.h"
+#include "mbsalign.h"
+#include "widechar.h"
+
+
+#ifdef HAVE_WIDECHAR
+/* Replace non printable chars.
+   Note \t and \n etc. are non printable.
+   Return 1 if replacement made, 0 otherwise.  */
+
+static bool
+wc_ensure_printable (wchar_t *wchars)
+{
+  bool replaced = false;
+  wchar_t *wc = wchars;
+  while (*wc)
+    {
+      if (!iswprint ((wint_t) *wc))
+        {
+          *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
+          replaced = true;
+        }
+      wc++;
+    }
+  return replaced;
+}
+
+/* Truncate wchar string to width cells.
+ * Returns number of cells used.  */
+
+static size_t
+wc_truncate (wchar_t *wc, size_t width)
+{
+  size_t cells = 0;
+  int next_cells = 0;
+
+  while (*wc)
+    {
+      next_cells = wcwidth (*wc);
+      if (next_cells == -1) /* non printable */
+        {
+          *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
+          next_cells = 1;
+        }
+      if (cells + next_cells > width)
+        break;
+      cells += next_cells;
+      wc++;
+    }
+  *wc = L'\0';
+  return cells;
+}
+
+/* FIXME: move this function to gnulib as it's missing on:
+   OpenBSD 3.8, IRIX 5.3, Solaris 2.5.1, mingw, BeOS  */
+
+static int
+rpl_wcswidth (const wchar_t *s, size_t n)
+{
+  int ret = 0;
+
+  while (n-- > 0 && *s != L'\0')
+    {
+      int nwidth = wcwidth (*s++);
+      if (nwidth == -1)             /* non printable */
+        return -1;
+      if (ret > (INT_MAX - nwidth)) /* overflow */
+        return -1;
+      ret += nwidth;
+    }
+
+  return ret;
+}
+#endif
+
+/* Truncate multi-byte string to @width and returns number of
+ * bytes of the new string @str, and in @width returns number
+ * of cells.
+ */
+size_t
+mbs_truncate(char *str, size_t *width)
+{
+	ssize_t bytes = strlen(str);
+#ifdef HAVE_WIDECHAR
+	ssize_t sz = mbstowcs(NULL, str, 0);
+	wchar_t *wcs = NULL;
+
+	if (sz == (ssize_t) -1)
+		goto done;
+
+	wcs = malloc((sz + 1) * sizeof(wchar_t));
+	if (!wcs)
+		goto done;
+
+	if (!mbstowcs(wcs, str, sz))
+		goto done;
+	*width = wc_truncate(wcs, *width);
+	bytes = wcstombs(str, wcs, bytes);
+done:
+	free(wcs);
+#else
+	if (*width < bytes)
+		bytes = *width;
+#endif
+	if (bytes >= 0)
+		str[bytes] = '\0';
+	return bytes;
+}
+
+/* Write N_SPACES space characters to DEST while ensuring
+   nothing is written beyond DEST_END. A terminating NUL
+   is always added to DEST.
+   A pointer to the terminating NUL is returned.  */
+
+static char*
+mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces)
+{
+  /* FIXME: Should we pad with "figure space" (\u2007)
+     if non ascii data present?  */
+  while (n_spaces-- && (dest < dest_end))
+    *dest++ = ' ';
+  *dest = '\0';
+  return dest;
+}
+
+/* Align a string, SRC, in a field of *WIDTH columns, handling multi-byte
+   characters; write the result into the DEST_SIZE-byte buffer, DEST.
+   ALIGNMENT specifies whether to left- or right-justify or to center.
+   If SRC requires more than *WIDTH columns, truncate it to fit.
+   When centering, the number of trailing spaces may be one less than the
+   number of leading spaces. The FLAGS parameter is unused at present.
+   Return the length in bytes required for the final result, not counting
+   the trailing NUL.  A return value of DEST_SIZE or larger means there
+   wasn't enough space.  DEST will be NUL terminated in any case.
+   Return (size_t) -1 upon error (invalid multi-byte sequence in SRC,
+   or malloc failure), unless MBA_UNIBYTE_FALLBACK is specified.
+   Update *WIDTH to indicate how many columns were used before padding.  */
+
+size_t
+mbsalign (const char *src, char *dest, size_t dest_size,
+          size_t *width, mbs_align_t align, int flags)
+{
+  size_t ret = -1;
+  size_t src_size = strlen (src) + 1;
+  char *newstr = NULL;
+  wchar_t *str_wc = NULL;
+  const char *str_to_print = src;
+  size_t n_cols = src_size - 1;
+  size_t n_used_bytes = n_cols; /* Not including NUL */
+  size_t n_spaces = 0;
+  bool conversion = false;
+  bool wc_enabled = false;
+
+#ifdef HAVE_WIDECHAR
+  /* In multi-byte locales convert to wide characters
+     to allow easy truncation. Also determine number
+     of screen columns used.  */
+  if (MB_CUR_MAX > 1)
+    {
+      size_t src_chars = mbstowcs (NULL, src, 0);
+      if (src_chars == (size_t) -1)
+        {
+          if (flags & MBA_UNIBYTE_FALLBACK)
+            goto mbsalign_unibyte;
+          else
+            goto mbsalign_cleanup;
+        }
+      src_chars += 1; /* make space for NUL */
+      str_wc = malloc (src_chars * sizeof (wchar_t));
+      if (str_wc == NULL)
+        {
+          if (flags & MBA_UNIBYTE_FALLBACK)
+            goto mbsalign_unibyte;
+          else
+            goto mbsalign_cleanup;
+        }
+      if (mbstowcs (str_wc, src, src_chars) != 0)
+        {
+          str_wc[src_chars - 1] = L'\0';
+          wc_enabled = true;
+          conversion = wc_ensure_printable (str_wc);
+          n_cols = rpl_wcswidth (str_wc, src_chars);
+        }
+    }
+
+  /* If we transformed or need to truncate the source string
+     then create a modified copy of it.  */
+  if (wc_enabled && (conversion || (n_cols > *width)))
+    {
+        if (conversion)
+          {
+             /* May have increased the size by converting
+                \t to \uFFFD for example.  */
+            src_size = wcstombs(NULL, str_wc, 0) + 1;
+          }
+        newstr = malloc (src_size);
+        if (newstr == NULL)
+        {
+          if (flags & MBA_UNIBYTE_FALLBACK)
+            goto mbsalign_unibyte;
+          else
+            goto mbsalign_cleanup;
+        }
+        str_to_print = newstr;
+        n_cols = wc_truncate (str_wc, *width);
+        n_used_bytes = wcstombs (newstr, str_wc, src_size);
+    }
+#endif
+
+mbsalign_unibyte:
+
+  if (n_cols > *width) /* Unibyte truncation required.  */
+    {
+      n_cols = *width;
+      n_used_bytes = n_cols;
+    }
+
+  if (*width > n_cols) /* Padding required.  */
+    n_spaces = *width - n_cols;
+
+  /* indicate to caller how many cells needed (not including padding).  */
+  *width = n_cols;
+
+  /* indicate to caller how many bytes needed (not including NUL).  */
+  ret = n_used_bytes + (n_spaces * 1);
+
+  /* Write as much NUL terminated output to DEST as possible.  */
+  if (dest_size != 0)
+    {
+      char *dest_end = dest + dest_size - 1;
+      size_t start_spaces = n_spaces / 2 + n_spaces % 2;
+      size_t end_spaces = n_spaces / 2;
+
+      switch (align)
+        {
+        case MBS_ALIGN_CENTER:
+          start_spaces = n_spaces / 2 + n_spaces % 2;
+          end_spaces = n_spaces / 2;
+          break;
+        case MBS_ALIGN_LEFT:
+          start_spaces = 0;
+          end_spaces = n_spaces;
+          break;
+        case MBS_ALIGN_RIGHT:
+          start_spaces = n_spaces;
+          end_spaces = 0;
+          break;
+	default:
+	  abort();
+        }
+
+      dest = mbs_align_pad (dest, dest_end, start_spaces);
+      size_t space_left = dest_end - dest;
+      //dest = mempcpy (dest, str_to_print, min (n_used_bytes, space_left));
+      memcpy (dest, str_to_print, min (n_used_bytes, space_left));
+      mbs_align_pad (dest, dest_end, end_spaces);
+    }
+
+mbsalign_cleanup:
+
+  free (str_wc);
+  free (newstr);
+
+  return ret;
+}
diff --git a/libblkid/mbsalign.h b/libblkid/mbsalign.h
new file mode 100644
index 0000000..fd957b3
--- /dev/null
+++ b/libblkid/mbsalign.h
@@ -0,0 +1,45 @@
+/* Align/Truncate a string in a given screen width
+   Copyright (C) 2009-2010 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stddef.h>
+
+typedef enum { MBS_ALIGN_LEFT, MBS_ALIGN_RIGHT, MBS_ALIGN_CENTER } mbs_align_t;
+
+enum {
+  /* Use unibyte mode for invalid multibyte strings or
+     or when heap memory is exhausted.  */
+  MBA_UNIBYTE_FALLBACK = 0x0001,
+
+#if 0 /* Other possible options.  */
+  /* Skip invalid multibyte chars rather than failing  */
+  MBA_IGNORE_INVALID   = 0x0002,
+
+  /* Align multibyte strings using "figure space" (\u2007)  */
+  MBA_USE_FIGURE_SPACE = 0x0004,
+
+  /* Don't add any padding  */
+  MBA_TRUNCATE_ONLY    = 0x0008,
+
+  /* Don't truncate  */
+  MBA_PAD_ONLY         = 0x0010,
+#endif
+};
+
+extern size_t mbs_truncate(char *str, size_t *width);
+
+extern size_t mbsalign (const char *src, char *dest,
+			size_t dest_size,  size_t *width,
+			mbs_align_t align, int flags);
diff --git a/libblkid/md.c b/libblkid/md.c
new file mode 100644
index 0000000..5eba947
--- /dev/null
+++ b/libblkid/md.c
@@ -0,0 +1,154 @@
+/*
+ * Linux Software RAID (md) topology
+ * -- this is fallback for old systems where the topology information is not
+ *    exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+#ifndef MD_MAJOR
+#define MD_MAJOR	9
+#endif
+
+#ifndef _IOT__IOTBASE_uint32_t
+#define _IOT__IOTBASE_uint32_t IOT_SIMPLE(uint32_t)
+#endif
+#define _IOT_md_array_info _IOT (_IOTS(uint32_t), 18, 0, 0, 0, 0)
+#define GET_ARRAY_INFO          _IOR (MD_MAJOR, 0x11, struct md_array_info)
+
+struct md_array_info {
+	/*
+	 * Generic constant information
+	 */
+	uint32_t major_version;
+	uint32_t minor_version;
+	uint32_t patch_version;
+	uint32_t ctime;
+	uint32_t level;
+	uint32_t size;
+	uint32_t nr_disks;
+	uint32_t raid_disks;
+	uint32_t md_minor;
+	uint32_t not_persistent;
+
+	/*
+	 * Generic state information
+	 */
+	uint32_t utime;	  /*  0 Superblock update time		  */
+	uint32_t state;	  /*  1 State bits (clean, ...)		  */
+	uint32_t active_disks;  /*  2 Number of currently active disks  */
+	uint32_t working_disks; /*  3 Number of working disks		  */
+	uint32_t failed_disks;  /*  4 Number of failed disks		  */
+	uint32_t spare_disks;	  /*  5 Number of spare disks		  */
+
+	/*
+	 * Personality information
+	 */
+	uint32_t layout;	  /*  0 the array's physical layout	  */
+	uint32_t chunk_size;	  /*  1 chunk size in bytes		  */
+
+};
+
+static int is_md_device(dev_t devno)
+{
+	if (major(devno) == MD_MAJOR)
+		return 1;
+	return blkid_driver_has_major("md", major(devno));
+}
+
+static int probe_md_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	int fd = -1;
+	dev_t disk = 0;
+	dev_t devno = blkid_probe_get_devno(pr);
+	struct md_array_info md;
+
+	if (!devno)
+		goto nothing;		/* probably not a block device */
+
+	if (!is_md_device(devno))
+		goto nothing;
+
+	if (blkid_devno_to_wholedisk(devno, NULL, 0, &disk))
+		goto nothing;
+
+	if (disk == devno)
+		fd = pr->fd;
+	else {
+		char *diskpath = blkid_devno_to_devname(disk);
+
+		if (!diskpath)
+			goto nothing;
+
+		fd = open(diskpath, O_RDONLY|O_CLOEXEC);
+		free(diskpath);
+
+                if (fd == -1)
+			goto nothing;
+	}
+
+	memset(&md, 0, sizeof(md));
+
+	if (ioctl(fd, GET_ARRAY_INFO, &md))
+		goto nothing;
+
+	if (fd >= 0 && fd != pr->fd) {
+		close(fd);
+		fd = -1;
+	}
+
+	/*
+	 * Ignore levels we don't want aligned (e.g. linear)
+	 * and deduct disk(s) from stripe width on RAID4/5/6
+	 */
+	switch (md.level) {
+	case 6:
+		md.raid_disks--;
+		/* fallthrough */
+	case 5:
+	case 4:
+		md.raid_disks--;
+		/* fallthrough */
+	case 1:
+	case 0:
+	case 10:
+		break;
+	default:
+		goto nothing;
+	}
+
+	blkid_topology_set_minimum_io_size(pr, md.chunk_size);
+	blkid_topology_set_optimal_io_size(pr, md.chunk_size * md.raid_disks);
+
+	return 0;
+
+nothing:
+	if (fd >= 0 && fd != pr->fd)
+		close(fd);
+	return 1;
+}
+
+const struct blkid_idinfo md_tp_idinfo =
+{
+	.name		= "md",
+	.probefunc	= probe_md_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/md5.c b/libblkid/md5.c
new file mode 100644
index 0000000..488d16e
--- /dev/null
+++ b/libblkid/md5.c
@@ -0,0 +1,257 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <string.h>		/* for memcpy() */
+
+#include "md5.h"
+
+#if !defined(WORDS_BIGENDIAN)
+#define byteReverse(buf, len)	/* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32_t t;
+    do {
+	t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(uint32_t *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    memcpy(p, buf, len);
+	    return;
+	}
+	memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[MD5LENGTH], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform.
+     * Use memcpy to avoid aliasing problems.  On most systems,
+     * this will be optimized away to the same code.
+     */
+    memcpy(&ctx->in[14 * sizeof(uint32_t)], &ctx->bits[0], 4);
+    memcpy(&ctx->in[15 * sizeof(uint32_t)], &ctx->bits[1], 4);
+
+    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, MD5LENGTH);
+    memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+    register uint32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#endif
+
diff --git a/libblkid/md5.h b/libblkid/md5.h
new file mode 100644
index 0000000..d997e37
--- /dev/null
+++ b/libblkid/md5.h
@@ -0,0 +1,29 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+#endif
+
+#define MD5LENGTH 16
+
+struct MD5Context {
+	uint32_t buf[4];
+	uint32_t bits[2];
+	unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+	       unsigned len);
+void MD5Final(unsigned char digest[MD5LENGTH], struct MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+#endif /* !MD5_H */
diff --git a/libblkid/minix.h b/libblkid/minix.h
new file mode 100644
index 0000000..57be239
--- /dev/null
+++ b/libblkid/minix.h
@@ -0,0 +1,82 @@
+#ifndef UTIL_LINUX_MINIX_H
+#define UTIL_LINUX_MINIX_H
+
+#include <stdint.h>
+
+struct minix_inode {
+	uint16_t i_mode;
+	uint16_t i_uid;
+	uint32_t i_size;
+	uint32_t i_time;
+	uint8_t  i_gid;
+	uint8_t  i_nlinks;
+	uint16_t i_zone[9];
+};
+
+struct minix2_inode {
+	uint16_t i_mode;
+	uint16_t i_nlinks;
+	uint16_t i_uid;
+	uint16_t i_gid;
+	uint32_t i_size;
+	uint32_t i_atime;
+	uint32_t i_mtime;
+	uint32_t i_ctime;
+	uint32_t i_zone[10];
+};
+
+struct minix_super_block {
+	uint16_t s_ninodes;
+	uint16_t s_nzones;
+	uint16_t s_imap_blocks;
+	uint16_t s_zmap_blocks;
+	uint16_t s_firstdatazone;
+	uint16_t s_log_zone_size;
+	uint32_t s_max_size;
+	uint16_t s_magic;
+	uint16_t s_state;
+	uint32_t s_zones;
+};
+
+/* V3 minix super-block data on disk */
+struct minix3_super_block {
+	uint32_t s_ninodes;
+	uint16_t s_pad0;
+	uint16_t s_imap_blocks;
+	uint16_t s_zmap_blocks;
+	uint16_t s_firstdatazone;
+	uint16_t s_log_zone_size;
+	uint16_t s_pad1;
+	uint32_t s_max_size;
+	uint32_t s_zones;
+	uint16_t s_magic;
+	uint16_t s_pad2;
+	uint16_t s_blocksize;
+	uint8_t  s_disk_version;
+};
+
+/*
+ * Minix subpartitions are always within primary dos partition.
+ */
+#define MINIX_MAXPARTITIONS  4
+
+#define MINIX_BLOCK_SIZE_BITS 10
+#define MINIX_BLOCK_SIZE     (1 << MINIX_BLOCK_SIZE_BITS)
+
+#define MINIX_NAME_MAX       255             /* # chars in a file name */
+#define MINIX_MAX_INODES     65535
+
+#define MINIX_INODES_PER_BLOCK ((MINIX_BLOCK_SIZE)/(sizeof (struct minix_inode)))
+#define MINIX2_INODES_PER_BLOCK ((MINIX_BLOCK_SIZE)/(sizeof (struct minix2_inode)))
+
+/* minix_super_block.s_state */
+#define MINIX_VALID_FS       0x0001          /* Clean fs. */
+#define MINIX_ERROR_FS       0x0002          /* fs has errors. */
+
+#define MINIX_SUPER_MAGIC    0x137F          /* original minix fs */
+#define MINIX_SUPER_MAGIC2   0x138F          /* minix fs, 30 char names */
+#define MINIX2_SUPER_MAGIC   0x2468	     /* minix V2 fs */
+#define MINIX2_SUPER_MAGIC2  0x2478	     /* minix V2 fs, 30 char names */
+#define MINIX3_SUPER_MAGIC   0x4d5a          /* minix V3 fs (60 char names) */
+
+#endif /* UTIL_LINUX_MINIX_H */
diff --git a/libblkid/minix1.c b/libblkid/minix1.c
new file mode 100644
index 0000000..54e7139
--- /dev/null
+++ b/libblkid/minix1.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <string.h>
+#include "superblocks.h"
+#include "minix.h"
+
+static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	unsigned char *ext;
+	int version;
+
+	/* for more details see magic strings below */
+	switch(mag->magic[1]) {
+	case '\023':
+		version = 1;
+		break;
+	case '\044':
+		version = 2;
+		break;
+	case '\115':
+		version = 3;
+		break;
+	default:
+		return -1;
+		break;
+	}
+
+	if (version <= 2) {
+		struct minix_super_block *sb;
+		uint32_t zones;
+
+		sb = blkid_probe_get_sb(pr, mag, struct minix_super_block);
+		if (!sb || sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+			return -1;
+
+		zones = version == 2 ? sb->s_zones : sb->s_nzones;
+
+		/* sanity checks to be sure that the FS is really minix */
+		if (sb->s_imap_blocks * MINIX_BLOCK_SIZE * 8 < sb->s_ninodes + 1)
+			return -1;
+		if (sb->s_zmap_blocks * MINIX_BLOCK_SIZE * 8 < zones - sb->s_firstdatazone + 1)
+			return -1;
+
+	} else if (version == 3) {
+		struct minix3_super_block *sb;
+
+		sb = blkid_probe_get_sb(pr, mag, struct minix3_super_block);
+		if (!sb || sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+			return -1;
+
+	}
+
+	/* unfortunately, some parts of ext3 is sometimes possible to
+	 * interpreted as minix superblock. So check for extN magic
+	 * string. (For extN magic string and offsets see ext.c.)
+	 */
+	ext = blkid_probe_get_buffer(pr, 0x400 + 0x38, 2);
+	if (ext && memcmp(ext, "\123\357", 2) == 0)
+		return -1;
+
+	blkid_probe_sprintf_version(pr, "%d", version);
+	return 0;
+}
+
+const struct blkid_idinfo minix_idinfo =
+{
+	.name		= "minix",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_minix,
+	.magics		=
+	{
+		/* version 1 */
+		{ .magic = "\177\023", .len = 2, .kboff = 1, .sboff = 0x10 },
+		{ .magic = "\217\023", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+		/* version 2 */
+		{ .magic = "\150\044", .len = 2, .kboff = 1, .sboff = 0x10 },
+		{ .magic = "\170\044", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+		/* version 3 */
+		{ .magic = "\132\115", .len = 2, .kboff = 1, .sboff = 0x18 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/minix2.c b/libblkid/minix2.c
new file mode 100644
index 0000000..bd57a6d
--- /dev/null
+++ b/libblkid/minix2.c
@@ -0,0 +1,100 @@
+/*
+ * Minix partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+#include "dos.h"
+#include "minix.h"
+
+static int probe_minix_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct dos_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partition parent;
+	blkid_partlist ls;
+	unsigned char *data;
+	int i;
+
+	data = blkid_probe_get_sector(pr, 0);
+	if (!data)
+		goto nothing;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	/* Parent is required, because Minix uses the same PT as DOS and
+	 * difference is only in primary partition (parent) type.
+	 */
+	parent = blkid_partlist_get_parent(ls);
+	if (!parent)
+		goto nothing;
+
+	if (blkid_partition_get_type(parent) != BLKID_MINIX_PARTITION)
+		goto nothing;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	p = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+	tab = blkid_partlist_new_parttable(ls, "minix", BLKID_MSDOS_PT_OFFSET);
+	if (!tab)
+		goto err;
+
+	for (i = 0; i < MINIX_MAXPARTITIONS; i++, p++) {
+		uint32_t start, size;
+		blkid_partition par;
+
+		if (p->sys_type != BLKID_MINIX_PARTITION)
+			continue;
+
+		start = dos_partition_start(p);
+		size = dos_partition_size(p);
+
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: minix partition (%d) overflow "
+				"detected, ignore\n", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, p->sys_type);
+		blkid_partition_set_flags(par, p->boot_ind);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+/* same as DOS */
+const struct blkid_idinfo minix_pt_idinfo =
+{
+	.name		= "minix",
+	.probefunc	= probe_minix_pt,
+	.magics		=
+	{
+		{ .magic = "\x55\xAA", .len = 2, .sboff = 510 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/namespace.h b/libblkid/namespace.h
new file mode 100644
index 0000000..3a219ce
--- /dev/null
+++ b/libblkid/namespace.h
@@ -0,0 +1,42 @@
+/* Compat code so unshare and setns can be used with older libcs */
+#ifndef UTIL_LINUX_NAMESPACE_H
+# define UTIL_LINUX_NAMESPACE_H
+
+# include <sched.h>
+
+# ifndef CLONE_NEWSNS
+#  define CLONE_NEWNS 0x00020000
+# endif
+# ifndef CLONE_NEWUTS
+#  define CLONE_NEWUTS 0x04000000
+# endif
+# ifndef CLONE_NEWIPC
+#  define CLONE_NEWIPC 0x08000000
+# endif
+# ifndef CLONE_NEWNET
+#  define CLONE_NEWNET 0x40000000
+# endif
+# ifndef CLONE_NEWUSER
+#  define CLONE_NEWUSER 0x10000000
+# endif
+# ifndef CLONE_NEWPID
+#  define CLONE_NEWPID 0x20000000
+# endif
+
+# ifndef HAVE_UNSHARE
+#  include <sys/syscall.h>
+static inline int unshare(int flags)
+{
+	return syscall(SYS_unshare, flags);
+}
+# endif
+
+# ifndef HAVE_SETNS
+#  include <sys/syscall.h>
+static inline int setns(int fd, int nstype)
+{
+	return syscall(SYS_setns, fd, nstype);
+}
+# endif
+
+#endif	/* UTIL_LINUX_NAMESPACE_H */
diff --git a/libblkid/netware.c b/libblkid/netware.c
new file mode 100644
index 0000000..7ef2162
--- /dev/null
+++ b/libblkid/netware.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct netware_super_block {
+	uint8_t		SBH_Signature[4];
+	uint16_t	SBH_VersionMajor;
+	uint16_t	SBH_VersionMinor;
+	uint16_t	SBH_VersionMediaMajor;
+	uint16_t	SBH_VersionMediaMinor;
+	uint32_t	SBH_ItemsMoved;
+	uint8_t		SBH_InternalID[16];
+	uint32_t	SBH_PackedSize;
+	uint32_t	SBH_Checksum;
+	uint32_t	supersyncid;
+	int64_t		superlocation[4];
+	uint32_t	physSizeUsed;
+	uint32_t	sizeUsed;
+	uint32_t	superTimeStamp;
+	uint32_t	reserved0[1];
+	int64_t		SBH_LoggedPoolDataBlk;
+	int64_t		SBH_PoolDataBlk;
+	uint8_t		SBH_OldInternalID[16];
+	uint32_t	SBH_PoolToLVStartUTC;
+	uint32_t	SBH_PoolToLVEndUTC;
+	uint16_t	SBH_VersionMediaMajorCreate;
+	uint16_t	SBH_VersionMediaMinorCreate;
+	uint32_t	SBH_BlocksMoved;
+	uint32_t	SBH_TempBTSpBlk;
+	uint32_t	SBH_TempFTSpBlk;
+	uint32_t	SBH_TempFTSpBlk1;
+	uint32_t	SBH_TempFTSpBlk2;
+	uint32_t	nssMagicNumber;
+	uint32_t	poolClassID;
+	uint32_t	poolID;
+	uint32_t	createTime;
+	int64_t		SBH_LoggedVolumeDataBlk;
+	int64_t		SBH_VolumeDataBlk;
+	int64_t		SBH_SystemBeastBlkNum;
+	uint64_t	totalblocks;
+	uint16_t	SBH_Name[64];
+	uint8_t		SBH_VolumeID[16];
+	uint8_t		SBH_PoolID[16];
+	uint8_t		SBH_PoolInternalID[16];
+	uint64_t	SBH_Lsn;
+	uint32_t	SBH_SS_Enabled;
+	uint32_t	SBH_SS_CreateTime;
+	uint8_t		SBH_SS_OriginalPoolID[16];
+	uint8_t		SBH_SS_OriginalVolumeID[16];
+	uint8_t		SBH_SS_Guid[16];
+	uint16_t	SBH_SS_OriginalName[64];
+	uint32_t	reserved2[64-(2+46)];
+} __attribute__((__packed__));
+
+static int probe_netware(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct netware_super_block *nw;
+
+	nw = blkid_probe_get_sb(pr, mag, struct netware_super_block);
+	if (!nw)
+		return -1;
+
+	blkid_probe_set_uuid(pr, nw->SBH_PoolID);
+
+	blkid_probe_sprintf_version(pr, "%u.%02u",
+		 le16_to_cpu(nw->SBH_VersionMediaMajor),
+		 le16_to_cpu(nw->SBH_VersionMediaMinor));
+
+	return 0;
+}
+
+const struct blkid_idinfo netware_idinfo =
+{
+	.name		= "nss",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_netware,
+	.magics		=
+	{
+		{ .magic = "SPB5", .len = 4, .kboff = 4 },
+		{ NULL }
+	}
+};
+
+
diff --git a/libblkid/nilfs.c b/libblkid/nilfs.c
new file mode 100644
index 0000000..1f8f3a6
--- /dev/null
+++ b/libblkid/nilfs.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 by Jiro SEKIBA <jir@unicus.jp>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License
+ */
+#include <stddef.h>
+#include <string.h>
+
+#include "superblocks.h"
+#include "crc32.h"
+
+struct nilfs_super_block {
+	uint32_t	s_rev_level;
+	uint16_t	s_minor_rev_level;
+	uint16_t	s_magic;
+
+	uint16_t	s_bytes;
+
+	uint16_t	s_flags;
+	uint32_t	s_crc_seed;
+	uint32_t	s_sum;
+
+	uint32_t	s_log_block_size;
+
+	uint64_t	s_nsegments;
+	uint64_t	s_dev_size;
+	uint64_t	s_first_data_block;
+	uint32_t	s_blocks_per_segment;
+	uint32_t	s_r_segments_percentage;
+
+	uint64_t	s_last_cno;
+	uint64_t	s_last_pseg;
+	uint64_t	s_last_seq;
+	uint64_t	s_free_blocks_count;
+
+	uint64_t	s_ctime;
+
+	uint64_t	s_mtime;
+	uint64_t	s_wtime;
+	uint16_t	s_mnt_count;
+	uint16_t	s_max_mnt_count;
+	uint16_t	s_state;
+	uint16_t	s_errors;
+	uint64_t	s_lastcheck;
+
+	uint32_t	s_checkinterval;
+	uint32_t	s_creator_os;
+	uint16_t	s_def_resuid;
+	uint16_t	s_def_resgid;
+	uint32_t	s_first_ino;
+
+	uint16_t	s_inode_size;
+	uint16_t	s_dat_entry_size;
+	uint16_t	s_checkpoint_size;
+	uint16_t	s_segment_usage_size;
+
+	uint8_t		s_uuid[16];
+	char		s_volume_name[80];
+
+	uint32_t	s_c_interval;
+	uint32_t	s_c_block_max;
+	uint32_t	s_reserved[192];
+};
+
+/* nilfs2 magic string */
+#define NILFS_SB_MAGIC		"\x34\x34"
+/* nilfs2 super block offset */
+#define NILFS_SB_OFF		0x400
+/* nilfs2 super block offset in kB */
+#define NILFS_SB_KBOFF		(NILFS_SB_OFF >> 10)
+/* nilfs2 magic string offset within super block */
+#define NILFS_MAG_OFF		6
+
+static int probe_nilfs2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct nilfs_super_block *sb;
+	static unsigned char sum[4];
+	const int sumoff = offsetof(struct nilfs_super_block, s_sum);
+	size_t bytes;
+	uint32_t crc;
+
+	sb = blkid_probe_get_sb(pr, mag, struct nilfs_super_block);
+	if (!sb)
+		return -1;
+
+	bytes = le16_to_cpu(sb->s_bytes);
+	crc = crc32(le32_to_cpu(sb->s_crc_seed), (unsigned char *)sb, sumoff);
+	crc = crc32(crc, sum, 4);
+	crc = crc32(crc, (unsigned char *)sb + sumoff + 4, bytes - sumoff - 4);
+
+	if (crc != le32_to_cpu(sb->s_sum))
+		return -1;
+
+	if (strlen(sb->s_volume_name))
+		blkid_probe_set_label(pr, (unsigned char *) sb->s_volume_name,
+				      sizeof(sb->s_volume_name));
+
+	blkid_probe_set_uuid(pr, sb->s_uuid);
+	blkid_probe_sprintf_version(pr, "%u", le32_to_cpu(sb->s_rev_level));
+
+	return 0;
+}
+
+const struct blkid_idinfo nilfs2_idinfo =
+{
+	.name		= "nilfs2",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_nilfs2,
+	.magics		=
+	{
+		{
+			.magic = NILFS_SB_MAGIC,
+			.len = 2,
+			.kboff = NILFS_SB_KBOFF,
+			.sboff = NILFS_MAG_OFF
+		},
+		{ NULL }
+	}
+};
diff --git a/libblkid/nls.h b/libblkid/nls.h
new file mode 100644
index 0000000..3eabfe6
--- /dev/null
+++ b/libblkid/nls.h
@@ -0,0 +1,115 @@
+#ifndef UTIL_LINUX_NLS_H
+#define UTIL_LINUX_NLS_H
+
+int main(int argc, char *argv[]);
+
+#ifndef LOCALEDIR
+#define LOCALEDIR "/usr/share/locale"
+#endif
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#else
+# undef setlocale
+# define setlocale(Category, Locale) /* empty */
+struct lconv
+{
+	char *decimal_point;
+};
+# undef localeconv
+# define localeconv() NULL
+#endif
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+# ifdef gettext_noop
+#  define N_(String) gettext_noop (String)
+# else
+#  define N_(String) (String)
+# endif
+# define P_(Singular, Plural, n) ngettext (Singular, Plural, n)
+#else
+# undef bindtextdomain
+# define bindtextdomain(Domain, Directory) /* empty */
+# undef textdomain
+# define textdomain(Domain) /* empty */
+# define _(Text) (Text)
+# define N_(Text) (Text)
+# define P_(Singular, Plural, n) ((n) == 1 ? (Singular) : (Plural))
+#endif
+
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#else
+
+typedef int nl_item;
+extern char *langinfo_fallback(nl_item item);
+
+# define nl_langinfo	langinfo_fallback
+
+enum {
+	CODESET = 1,
+	RADIXCHAR,
+	THOUSEP,
+	D_T_FMT,
+	D_FMT,
+	T_FMT,
+	T_FMT_AMPM,
+	AM_STR,
+	PM_STR,
+
+	DAY_1,
+	DAY_2,
+	DAY_3,
+	DAY_4,
+	DAY_5,
+	DAY_6,
+	DAY_7,
+
+	ABDAY_1,
+	ABDAY_2,
+	ABDAY_3,
+	ABDAY_4,
+	ABDAY_5,
+	ABDAY_6,
+	ABDAY_7,
+
+	MON_1,
+	MON_2,
+	MON_3,
+	MON_4,
+	MON_5,
+	MON_6,
+	MON_7,
+	MON_8,
+	MON_9,
+	MON_10,
+	MON_11,
+	MON_12,
+
+	ABMON_1,
+	ABMON_2,
+	ABMON_3,
+	ABMON_4,
+	ABMON_5,
+	ABMON_6,
+	ABMON_7,
+	ABMON_8,
+	ABMON_9,
+	ABMON_10,
+	ABMON_11,
+	ABMON_12,
+
+	ERA_D_FMT,
+	ERA_D_T_FMT,
+	ERA_T_FMT,
+	ALT_DIGITS,
+	CRNCYSTR,
+	YESEXPR,
+	NOEXPR
+};
+
+#endif /* !HAVE_LANGINFO_H */
+
+#endif /* UTIL_LINUX_NLS_H */
diff --git a/libblkid/ntfs.c b/libblkid/ntfs.c
new file mode 100644
index 0000000..41c6b9c
--- /dev/null
+++ b/libblkid/ntfs.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "superblocks.h"
+
+struct ntfs_bios_parameters {
+	uint16_t	sector_size;	/* Size of a sector in bytes. */
+	uint8_t		sectors_per_cluster;	/* Size of a cluster in sectors. */
+	uint16_t	reserved_sectors;	/* zero */
+	uint8_t		fats;			/* zero */
+	uint16_t	root_entries;		/* zero */
+	uint16_t	sectors;		/* zero */
+	uint8_t		media_type;		/* 0xf8 = hard disk */
+	uint16_t	sectors_per_fat;	/* zero */
+	uint16_t	sectors_per_track;	/* irrelevant */
+	uint16_t	heads;			/* irrelevant */
+	uint32_t	hidden_sectors;		/* zero */
+	uint32_t	large_sectors;		/* zero */
+} __attribute__ ((__packed__));
+
+struct ntfs_super_block {
+	uint8_t		jump[3];
+	uint8_t		oem_id[8];	/* magic string */
+
+	struct ntfs_bios_parameters	bpb;
+
+	uint16_t	unused[2];
+	uint64_t	number_of_sectors;
+	uint64_t	mft_cluster_location;
+	uint64_t	mft_mirror_cluster_location;
+	int8_t		clusters_per_mft_record;
+	uint8_t		reserved1[3];
+	int8_t		cluster_per_index_record;
+	uint8_t		reserved2[3];
+	uint64_t	volume_serial;
+	uint32_t	checksum;
+} __attribute__((packed));
+
+struct master_file_table_record {
+	uint32_t	magic;
+	uint16_t	usa_ofs;
+	uint16_t	usa_count;
+	uint64_t	lsn;
+	uint16_t	sequence_number;
+	uint16_t	link_count;
+	uint16_t	attrs_offset;
+	uint16_t	flags;
+	uint32_t	bytes_in_use;
+	uint32_t	bytes_allocated;
+} __attribute__((__packed__));
+
+struct file_attribute {
+	uint32_t	type;
+	uint32_t	len;
+	uint8_t		non_resident;
+	uint8_t		name_len;
+	uint16_t	name_offset;
+	uint16_t	flags;
+	uint16_t	instance;
+	uint32_t	value_len;
+	uint16_t	value_offset;
+} __attribute__((__packed__));
+
+#define MFT_RECORD_VOLUME	3
+#define NTFS_MAX_CLUSTER_SIZE	(64 * 1024)
+
+enum {
+	MFT_RECORD_ATTR_VOLUME_NAME		= cpu_to_le32(0x60),
+	MFT_RECORD_ATTR_END			= cpu_to_le32(0xffffffff)
+};
+
+static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct ntfs_super_block *ns;
+	struct master_file_table_record *mft;
+
+	uint32_t sectors_per_cluster, mft_record_size, attr_off;
+	uint16_t sector_size;
+	uint64_t nr_clusters, off;
+	unsigned char *buf_mft;
+
+	ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block);
+	if (!ns)
+		return -1;
+
+	/*
+	 * Check bios parameters block
+	 */
+	sector_size = le16_to_cpu(ns->bpb.sector_size);
+	sectors_per_cluster = ns->bpb.sectors_per_cluster;
+
+	if (sector_size < 256 || sector_size > 4096)
+		return 1;
+
+	switch (sectors_per_cluster) {
+	case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
+		break;
+	default:
+		return 1;
+	}
+
+	if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) *
+			ns->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE)
+		return 1;
+
+	/* Unused fields must be zero */
+	if (le16_to_cpu(ns->bpb.reserved_sectors)
+	    || le16_to_cpu(ns->bpb.root_entries)
+	    || le16_to_cpu(ns->bpb.sectors)
+	    || le16_to_cpu(ns->bpb.sectors_per_fat)
+	    || le32_to_cpu(ns->bpb.large_sectors)
+	    || ns->bpb.fats)
+		return 1;
+
+	if ((uint8_t) ns->clusters_per_mft_record < 0xe1
+	    || (uint8_t) ns->clusters_per_mft_record > 0xf7) {
+
+		switch (ns->clusters_per_mft_record) {
+		case 1: case 2: case 4: case 8: case 16: case 32: case 64:
+			break;
+		default:
+			return 1;
+		}
+	}
+
+	if (ns->clusters_per_mft_record > 0)
+		mft_record_size = ns->clusters_per_mft_record *
+				  sectors_per_cluster * sector_size;
+	else
+		mft_record_size = 1 << (0 - ns->clusters_per_mft_record);
+
+	nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster;
+
+	if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) ||
+	    (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters))
+		return 1;
+
+
+	off = le64_to_cpu(ns->mft_cluster_location) * sector_size *
+		sectors_per_cluster;
+
+	DBG(DEBUG_LOWPROBE, printf("NTFS: sector_size=%d, mft_record_size=%d, "
+			"sectors_per_cluster=%d, nr_clusters=%ju "
+			"cluster_offset=%jd\n",
+			(int) sector_size, mft_record_size,
+			sectors_per_cluster, nr_clusters,
+			off));
+
+	buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
+	if (!buf_mft)
+		return 1;
+
+	if (memcmp(buf_mft, "FILE", 4))
+		return 1;
+
+	off += MFT_RECORD_VOLUME * mft_record_size;
+
+	buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
+	if (!buf_mft)
+		return 1;
+
+	if (memcmp(buf_mft, "FILE", 4))
+		return 1;
+
+	mft = (struct master_file_table_record *) buf_mft;
+	attr_off = le16_to_cpu(mft->attrs_offset);
+
+	while (attr_off < mft_record_size &&
+	       attr_off <= le32_to_cpu(mft->bytes_allocated)) {
+
+		uint32_t attr_len;
+		struct file_attribute *attr;
+
+		attr = (struct file_attribute *) (buf_mft + attr_off);
+		attr_len = le32_to_cpu(attr->len);
+		if (!attr_len)
+			break;
+
+		if (attr->type == MFT_RECORD_ATTR_END)
+			break;
+		if (attr->type == MFT_RECORD_ATTR_VOLUME_NAME) {
+			unsigned int val_off = le16_to_cpu(attr->value_offset);
+			unsigned int val_len = le32_to_cpu(attr->value_len);
+			unsigned char *val = ((uint8_t *) attr) + val_off;
+
+			blkid_probe_set_utf8label(pr, val, val_len, BLKID_ENC_UTF16LE);
+			break;
+		}
+
+		if (UINT_MAX - attr_len < attr_off)
+			break;
+		attr_off += attr_len;
+	}
+
+	blkid_probe_sprintf_uuid(pr,
+			(unsigned char *) &ns->volume_serial,
+			sizeof(ns->volume_serial),
+			"%016" PRIX64, le64_to_cpu(ns->volume_serial));
+	return 0;
+}
+
+
+const struct blkid_idinfo ntfs_idinfo =
+{
+	.name		= "ntfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ntfs,
+	.magics		=
+	{
+		{ .magic = "NTFS    ", .len = 8, .sboff = 3 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/nvidia_raid.c b/libblkid/nvidia_raid.c
new file mode 100644
index 0000000..dd86cdc
--- /dev/null
+++ b/libblkid/nvidia_raid.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct nv_metadata {
+	uint8_t		vendor[8];
+	uint32_t	size;
+	uint32_t	chksum;
+	uint16_t	version;
+} __attribute__((packed));
+
+#define NVIDIA_SIGNATURE		"NVIDIA"
+
+static int probe_nvraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct nv_metadata *nv;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 2) * 0x200;
+	nv = (struct nv_metadata *)
+		blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct nv_metadata));
+	if (!nv)
+		return -1;
+
+	if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0)
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%u", le16_to_cpu(nv->version)) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(nv->vendor),
+				(unsigned char *) nv->vendor))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo nvraid_idinfo = {
+	.name		= "nvidia_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_nvraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/ocfs.c b/libblkid/ocfs.c
new file mode 100644
index 0000000..82170ac
--- /dev/null
+++ b/libblkid/ocfs.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 1999, 2001 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct ocfs_volume_header {
+	unsigned char	minor_version[4];
+	unsigned char	major_version[4];
+	unsigned char	signature[128];
+	char		mount[128];
+	unsigned char   mount_len[2];
+} __attribute__((packed));
+
+struct ocfs_volume_label {
+	unsigned char	disk_lock[48];
+	char		label[64];
+	unsigned char	label_len[2];
+	unsigned char   vol_id[16];
+	unsigned char   vol_id_len[2];
+} __attribute__((packed));
+
+#define ocfsmajor(o) ( (uint32_t) o.major_version[0] \
+                   + (((uint32_t) o.major_version[1]) << 8) \
+                   + (((uint32_t) o.major_version[2]) << 16) \
+                   + (((uint32_t) o.major_version[3]) << 24))
+
+#define ocfsminor(o) ( (uint32_t) o.minor_version[0] \
+                   + (((uint32_t) o.minor_version[1]) << 8) \
+                   + (((uint32_t) o.minor_version[2]) << 16) \
+                   + (((uint32_t) o.minor_version[3]) << 24))
+
+#define ocfslabellen(o)	((uint32_t)o.label_len[0] + (((uint32_t) o.label_len[1]) << 8))
+#define ocfsmountlen(o)	((uint32_t)o.mount_len[0] + (((uint32_t) o.mount_len[1]) << 8))
+
+struct ocfs2_super_block {
+	uint8_t		i_signature[8];
+	uint32_t	i_generation;
+	int16_t		i_suballoc_slot;
+	uint16_t	i_suballoc_bit;
+	uint32_t	i_reserved0;
+	uint32_t	i_clusters;
+	uint32_t	i_uid;
+	uint32_t	i_gid;
+	uint64_t	i_size;
+	uint16_t	i_mode;
+	uint16_t	i_links_count;
+	uint32_t	i_flags;
+	uint64_t	i_atime;
+	uint64_t	i_ctime;
+	uint64_t	i_mtime;
+	uint64_t	i_dtime;
+	uint64_t	i_blkno;
+	uint64_t	i_last_eb_blk;
+	uint32_t	i_fs_generation;
+	uint32_t	i_atime_nsec;
+	uint32_t	i_ctime_nsec;
+	uint32_t	i_mtime_nsec;
+	uint64_t	i_reserved1[9];
+	uint64_t	i_pad1;
+	uint16_t	s_major_rev_level;
+	uint16_t	s_minor_rev_level;
+	uint16_t	s_mnt_count;
+	int16_t		s_max_mnt_count;
+	uint16_t	s_state;
+	uint16_t	s_errors;
+	uint32_t	s_checkinterval;
+	uint64_t	s_lastcheck;
+	uint32_t	s_creator_os;
+	uint32_t	s_feature_compat;
+	uint32_t	s_feature_incompat;
+	uint32_t	s_feature_ro_compat;
+	uint64_t	s_root_blkno;
+	uint64_t	s_system_dir_blkno;
+	uint32_t	s_blocksize_bits;
+	uint32_t	s_clustersize_bits;
+	uint16_t	s_max_slots;
+	uint16_t	s_reserved1;
+	uint32_t	s_reserved2;
+	uint64_t	s_first_cluster_group;
+	uint8_t		s_label[64];
+	uint8_t		s_uuid[16];
+} __attribute__((packed));
+
+struct oracle_asm_disk_label {
+	char dummy[32];
+	char dl_tag[8];
+	char dl_id[24];
+} __attribute__((packed));
+
+static int probe_ocfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	unsigned char *buf;
+	struct ocfs_volume_header ovh;
+	struct ocfs_volume_label ovl;
+	uint32_t maj, min;
+
+	/* header */
+	buf = blkid_probe_get_buffer(pr, mag->kboff << 10,
+			sizeof(struct ocfs_volume_header));
+	if (!buf)
+		return -1;
+	memcpy(&ovh, buf, sizeof(ovh));
+
+	/* label */
+	buf = blkid_probe_get_buffer(pr, (mag->kboff << 10) + 512,
+			sizeof(struct ocfs_volume_label));
+	if (!buf)
+		return -1;
+	memcpy(&ovl, buf, sizeof(ovl));
+
+	maj = ocfsmajor(ovh);
+	min = ocfsminor(ovh);
+
+	if (maj == 1)
+		blkid_probe_set_value(pr, "SEC_TYPE",
+				(unsigned char *) "ocfs1", sizeof("ocfs1"));
+	else if (maj >= 9)
+		blkid_probe_set_value(pr, "SEC_TYPE",
+				(unsigned char *) "ntocfs", sizeof("ntocfs"));
+
+	blkid_probe_set_label(pr, (unsigned char *) ovl.label,
+				ocfslabellen(ovl));
+	blkid_probe_set_value(pr, "MOUNT", (unsigned char *) ovh.mount,
+				ocfsmountlen(ovh));
+	blkid_probe_set_uuid(pr, ovl.vol_id);
+	blkid_probe_sprintf_version(pr, "%u.%u", maj, min);
+	return 0;
+}
+
+static int probe_ocfs2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct ocfs2_super_block *osb;
+
+	osb = blkid_probe_get_sb(pr, mag, struct ocfs2_super_block);
+	if (!osb)
+		return -1;
+
+	blkid_probe_set_label(pr, (unsigned char *) osb->s_label, sizeof(osb->s_label));
+	blkid_probe_set_uuid(pr, osb->s_uuid);
+
+	blkid_probe_sprintf_version(pr, "%u.%u",
+		le16_to_cpu(osb->s_major_rev_level),
+		le16_to_cpu(osb->s_minor_rev_level));
+
+	return 0;
+}
+
+static int probe_oracleasm(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct oracle_asm_disk_label *dl;
+
+	dl = blkid_probe_get_sb(pr, mag, struct oracle_asm_disk_label);
+	if (!dl)
+		return -1;
+
+	blkid_probe_set_label(pr, (unsigned char *) dl->dl_id, sizeof(dl->dl_id));
+	return 0;
+}
+
+
+const struct blkid_idinfo ocfs_idinfo =
+{
+	.name		= "ocfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ocfs,
+	.minsz		= 14000 * 1024,
+	.magics		=
+	{
+		{ .magic = "OracleCFS", .len = 9, .kboff = 8 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo ocfs2_idinfo =
+{
+	.name		= "ocfs2",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ocfs2,
+	.minsz		= 14000 * 1024,
+	.magics		=
+	{
+		{ .magic = "OCFSV2", .len = 6, .kboff = 1 },
+		{ .magic = "OCFSV2", .len = 6, .kboff = 2 },
+		{ .magic = "OCFSV2", .len = 6, .kboff = 4 },
+		{ .magic = "OCFSV2", .len = 6, .kboff = 8 },
+		{ NULL }
+	}
+};
+
+/* Oracle ASM (Automatic Storage Management) */
+const struct blkid_idinfo oracleasm_idinfo =
+{
+	.name		= "oracleasm",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_oracleasm,
+	.magics		=
+	{
+		{ .magic = "ORCLDISK", .len = 8, .sboff = 32 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/optutils.h b/libblkid/optutils.h
new file mode 100644
index 0000000..28a54b2
--- /dev/null
+++ b/libblkid/optutils.h
@@ -0,0 +1,95 @@
+#ifndef UTIL_LINUX_OPTUTILS_H
+#define UTIL_LINUX_OPTUTILS_H
+
+#include "c.h"
+#include "nls.h"
+
+static inline const char *option_to_longopt(int c, const struct option *opts)
+{
+	const struct option *o;
+
+	for (o = opts; o->name; o++)
+		if (o->val == c)
+			return o->name;
+	return NULL;
+}
+
+#ifndef OPTUTILS_EXIT_CODE
+# define OPTUTILS_EXIT_CODE EXIT_FAILURE
+#endif
+
+/*
+ * Check collisions between options.
+ *
+ * The conflicts between options are described in ul_excl_t array. The
+ * array contains groups of mutually exclusive options. For example
+ *
+ *	static const ul_excl_t excl[] = {
+ *		{ 'Z','b','c' },		// first group
+ *		{ 'b','x' },			// second group
+ *		{ 0 }
+ *	};
+ *
+ *	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+ *
+ *	while ((c = getopt_long(argc, argv, "Zbcx", longopts, NULL)) != -1) {
+ *
+ *		err_exclusive_options(c, longopts, excl, excl_st);
+ *
+ *		switch (c) {
+ *		case 'Z':
+ *		   ....
+ *		}
+ *	}
+ *
+ * The array excl[] defines two groups of the mutually exclusive options. The
+ * option '-b' is in the both groups.
+ *
+ * Note that the options in the group have to be in ASCII order (ABC..abc..) and
+ * groups have to be also in ASCII order.
+ *
+ * The current status of options is stored in excl_st array. The size of the array
+ * must be the same as number of the groups in the ul_excl_t array.
+ *
+ * If you're unsure then see sys-utils/mount.c or misc-utils/findmnt.c.
+ */
+#define UL_EXCL_STATUS_INIT	{ 0 }
+typedef int ul_excl_t[16];
+
+static inline void err_exclusive_options(
+			int c,
+			const struct option *opts,
+			const ul_excl_t *excl,
+			int *status)
+{
+	int e;
+
+	for (e = 0; excl[e][0] && excl[e][0] <= c; e++) {
+		const int *op = excl[e];
+
+		for (; *op && *op <= c; op++) {
+			if (*op != c)
+				continue;
+			if (status[e] == 0)
+				status[e] = c;
+			else if (status[e] != c) {
+				fprintf(stderr, _("%s: options "),
+						program_invocation_short_name);
+				for (op = excl[e]; *op; op++) {
+					if (opts)
+						fprintf(stderr, "--%s ",
+							option_to_longopt(*op, opts));
+					else
+						fprintf(stderr, "-%c ", *op);
+				}
+				fprintf(stderr, _("are mutually exclusive."));
+				fputc('\n', stderr);
+				exit(OPTUTILS_EXIT_CODE);
+			}
+			break;
+		}
+	}
+}
+
+#endif
+
diff --git a/libblkid/pager.c b/libblkid/pager.c
new file mode 100644
index 0000000..5cf8c03
--- /dev/null
+++ b/libblkid/pager.c
@@ -0,0 +1,220 @@
+/*
+ * Based on linux-perf/git scm
+ *
+ * Some modifications and simplifications for util-linux
+ * by Davidlohr Bueso <dave@xxxxxxx> - March 2012.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "c.h"
+#include "xalloc.h"
+#include "nls.h"
+
+#define NULL_DEVICE	"/dev/null"
+
+void setup_pager(void);
+
+static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
+
+struct child_process {
+	const char **argv;
+	pid_t pid;
+	int in;
+	int out;
+	int err;
+	unsigned no_stdin:1;
+	void (*preexec_cb)(void);
+};
+static struct child_process pager_process;
+
+static inline void close_pair(int fd[2])
+{
+	close(fd[0]);
+	close(fd[1]);
+}
+
+static inline void dup_devnull(int to)
+{
+	int fd = open(NULL_DEVICE, O_RDWR);
+
+	if (fd < 0)
+		err(EXIT_FAILURE, _("cannot open %s"), NULL_DEVICE);
+	dup2(fd, to);
+	close(fd);
+}
+
+static int start_command(struct child_process *cmd)
+{
+	int need_in;
+	int fdin[2];
+
+	/*
+	 * In case of errors we must keep the promise to close FDs
+	 * that have been passed in via ->in and ->out.
+	 */
+	need_in = !cmd->no_stdin && cmd->in < 0;
+	if (need_in) {
+		if (pipe(fdin) < 0) {
+			if (cmd->out > 0)
+				close(cmd->out);
+			return -1;
+		}
+		cmd->in = fdin[1];
+	}
+
+	fflush(NULL);
+	cmd->pid = fork();
+	if (!cmd->pid) {
+		if (need_in) {
+			dup2(fdin[0], 0);
+			close_pair(fdin);
+		} else if (cmd->in > 0) {
+			dup2(cmd->in, 0);
+			close(cmd->in);
+		}
+
+		cmd->preexec_cb();
+		execvp(cmd->argv[0], (char *const*) cmd->argv);
+		exit(127); /* cmd not found */
+	}
+
+	if (cmd->pid < 0) {
+		if (need_in)
+			close_pair(fdin);
+		else if (cmd->in)
+			close(cmd->in);
+		return -1;
+	}
+
+	if (need_in)
+		close(fdin[0]);
+	else if (cmd->in)
+		close(cmd->in);
+	return 0;
+}
+
+static int wait_or_whine(pid_t pid)
+{
+	for (;;) {
+		int status, code;
+		pid_t waiting = waitpid(pid, &status, 0);
+
+		if (waiting < 0) {
+			if (errno == EINTR)
+				continue;
+			err(EXIT_FAILURE, _("waitpid failed (%s)"), strerror(errno));
+		}
+		if (waiting != pid)
+			return -1;
+		if (WIFSIGNALED(status))
+			return -1;
+
+		if (!WIFEXITED(status))
+			return -1;
+		code = WEXITSTATUS(status);
+		switch (code) {
+		case 127:
+			return -1;
+		case 0:
+			return 0;
+		default:
+			return -1;
+		}
+	}
+}
+
+static int finish_command(struct child_process *cmd)
+{
+	return wait_or_whine(cmd->pid);
+}
+
+static void pager_preexec(void)
+{
+	/*
+	 * Work around bug in "less" by not starting it until we
+	 * have real input
+	 */
+	fd_set in;
+
+	FD_ZERO(&in);
+	FD_SET(0, &in);
+	select(1, &in, NULL, &in, NULL);
+
+	setenv("LESS", "FRSX", 0);
+}
+
+static void wait_for_pager(void)
+{
+	fflush(stdout);
+	fflush(stderr);
+	/* signal EOF to pager */
+	close(1);
+	close(2);
+	finish_command(&pager_process);
+}
+
+static void wait_for_pager_signal(int signo)
+{
+	wait_for_pager();
+	raise(signo);
+}
+
+void setup_pager(void)
+{
+	const char *pager = getenv("PAGER");
+
+	if (!isatty(1))
+		return;
+
+	if (!pager)
+		pager = "less";
+	else if (!*pager || !strcmp(pager, "cat"))
+		return;
+
+	/* spawn the pager */
+	pager_argv[2] = pager;
+	pager_process.argv = pager_argv;
+	pager_process.in = -1;
+	pager_process.preexec_cb = pager_preexec;
+
+	if (start_command(&pager_process))
+		return;
+
+	/* original process continues, but writes to the pipe */
+	dup2(pager_process.in, 1);
+	if (isatty(2))
+		dup2(pager_process.in, 2);
+	close(pager_process.in);
+
+	/* this makes sure that the parent terminates after the pager */
+	signal(SIGINT, wait_for_pager_signal);
+	signal(SIGHUP, wait_for_pager_signal);
+	signal(SIGTERM, wait_for_pager_signal);
+	signal(SIGQUIT, wait_for_pager_signal);
+	signal(SIGPIPE, wait_for_pager_signal);
+
+	atexit(wait_for_pager);
+}
+
+#ifdef TEST_PROGRAM
+
+#define MAX 255
+
+int main(int argc __attribute__ ((__unused__)),
+	 char *argv[] __attribute__ ((__unused__)))
+{
+	int i;
+
+	setup_pager();
+	for (i = 0; i < MAX; i++)
+		printf("%d\n", i);
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/pager.h b/libblkid/pager.h
new file mode 100644
index 0000000..9ca42eb
--- /dev/null
+++ b/libblkid/pager.h
@@ -0,0 +1,6 @@
+#ifndef UTIL_LINUX_PAGER
+#define UTIL_LINUX_PAGER
+
+void setup_pager(void);
+
+#endif
diff --git a/libblkid/pamfail.h b/libblkid/pamfail.h
new file mode 100644
index 0000000..e102df2
--- /dev/null
+++ b/libblkid/pamfail.h
@@ -0,0 +1,22 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_PAMFAIL_H
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+#include "c.h"
+
+static inline int
+pam_fail_check(pam_handle_t *pamh, int retcode)
+{
+	if (retcode == PAM_SUCCESS)
+		return 0;
+	warnx("%s", pam_strerror(pamh, retcode));
+	pam_end(pamh, retcode);
+	return 1;
+}
+
+#endif /* UTIL_LINUX_PAMFAIL_H */
diff --git a/libblkid/partitions.c b/libblkid/partitions.c
new file mode 100644
index 0000000..93ec4d2
--- /dev/null
+++ b/libblkid/partitions.c
@@ -0,0 +1,1385 @@
+/*
+ * partitions - partition tables parsing
+ *
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdarg.h>
+
+#include "partitions.h"
+#include "sysfs.h"
+
+/**
+ * SECTION: partitions
+ * @title: Partitions probing
+ * @short_description: partitions tables detection and parsing
+ *
+ * This chain supports binary and NAME=value interfaces, but complete PT
+ * description is provided by binary interface only. The libblkid prober is
+ * compatible with kernel partition tables parser. The parser does not return
+ * empty (size=0) partitions or special hidden partitions.
+ *
+ * NAME=value interface, supported tags:
+ *
+ * @PTTYPE: partition table type (dos, gpt, etc.).
+ *
+ * @PART_ENTRY_SCHEME: partition table type
+ *
+ * @PART_ENTRY_NAME: partition name (gpt and mac only)
+ *
+ * @PART_ENTRY_UUID: partition UUID (gpt only)
+ *
+ * @PART_ENTRY_TYPE: partition type, 0xNN (e.g 0x82) or type UUID (gpt only) or type string (mac)
+ *
+ * @PART_ENTRY_FLAGS: partition flags (e.g. boot_ind) or  attributes (e.g. gpt attributes)
+ *
+ * @PART_ENTRY_NUMBER: partition number
+ *
+ * @PART_ENTRY_OFFSET: the begin of the partition
+ *
+ * @PART_ENTRY_SIZE: size of the partition
+ *
+ * @PART_ENTRY_DISK: whole-disk maj:min
+ *
+ * Example:
+ *
+ * <informalexample>
+ *  <programlisting>
+ * blkid_probe pr;
+ * const char *ptname;
+ *
+ * pr = blkid_new_probe_from_filename(devname);
+ * if (!pr)
+ *	err("%s: faild to open device", devname);
+ *
+ * blkid_probe_enable_partitions(pr, TRUE);
+ * blkid_do_fullprobe(pr);
+ *
+ * blkid_probe_lookup_value(pr, "PTTYPE", &ptname, NULL);
+ * printf("%s partition type detected\n", pttype);
+ *
+ * blkid_free_probe(pr);
+ *
+ * // don't forget to check return codes in your code!
+ *  </programlisting>
+ * </informalexample>
+ *
+ * Binary interface:
+ *
+ * <informalexample>
+ *  <programlisting>
+ * blkid_probe pr;
+ * blkid_partlist ls;
+ * int nparts, i;
+ *
+ * pr = blkid_new_probe_from_filename(devname);
+ * if (!pr)
+ *	err("%s: faild to open device", devname);
+ *
+ * ls = blkid_probe_get_partitions(pr);
+ * nparts = blkid_partlist_numof_partitions(ls);
+ *
+ * for (i = 0; i < nparts; i++) {
+ *      blkid_partition par = blkid_partlist_get_partition(ls, i);
+ *      printf("#%d: %llu %llu  0x%x",
+ *               blkid_partition_get_partno(par),
+ *               blkid_partition_get_start(par),
+ *               blkid_partition_get_size(par),
+ *               blkid_partition_get_type(par));
+ * }
+ *
+ * blkid_free_probe(pr);
+ *
+ * // don't forget to check return codes in your code!
+ *  </programlisting>
+ * </informalexample>
+ */
+
+/*
+ * Chain driver function
+ */
+static int partitions_probe(blkid_probe pr, struct blkid_chain *chn);
+static void partitions_free_data(blkid_probe pr, void *data);
+
+/*
+ * Partitions chain probing functions
+ */
+static const struct blkid_idinfo *idinfos[] =
+{
+	&aix_pt_idinfo,
+	&sgi_pt_idinfo,
+	&sun_pt_idinfo,
+	&dos_pt_idinfo,
+	&gpt_pt_idinfo,
+	&mac_pt_idinfo,
+	&ultrix_pt_idinfo,
+	&bsd_pt_idinfo,
+	&unixware_pt_idinfo,
+	&solaris_x86_pt_idinfo,
+	&minix_pt_idinfo
+};
+
+/*
+ * Driver definition
+ */
+const struct blkid_chaindrv partitions_drv = {
+	.id           = BLKID_CHAIN_PARTS,
+	.name         = "partitions",
+	.dflt_enabled = FALSE,
+	.idinfos      = idinfos,
+	.nidinfos     = ARRAY_SIZE(idinfos),
+	.has_fltr     = TRUE,
+	.probe        = partitions_probe,
+	.safeprobe    = partitions_probe,
+	.free_data    = partitions_free_data
+};
+
+
+/*
+ * For compatibility with the rest of libblkid API (with the old high-level
+ * API) we use completely opaque typedefs for all structs. Don't forget that
+ * the final blkid_* types are pointers! See blkid.h.
+ *
+ * [Just for the record, I hate typedef for pointers --kzak]
+ */
+
+/* exported as opaque type "blkid_parttable" */
+struct blkid_struct_parttable {
+	const char	*type;		/* partition table type */
+	blkid_loff_t	offset;		/* begin of the partition table */
+	int		nparts;		/* number of partitions */
+	blkid_partition	parent;		/* parent of nested partition table */
+	char		id[37];		/* PT identifier (e.g. UUID for GPT) */
+
+	struct list_head t_tabs;	/* all tables */
+};
+
+/* exported as opaque type "blkid_partition" */
+struct blkid_struct_partition {
+	blkid_loff_t	start;		/* begin of the partition */
+	blkid_loff_t	size;		/* size of the partitions */
+
+	int		type;		/* partition type */
+	char		typestr[37];	/* partition type string (GPT and Mac) */
+
+	unsigned long long flags;	/* partition flags / attributes */
+
+	int		partno;		/* partition number */
+	char		uuid[37];	/* UUID (when supported by PT), e.g GPT */
+	unsigned char	name[128];	/* Partition in UTF8 name (when supporte by PT), e.g. Mac */
+
+	blkid_parttable	tab;		/* partition table */
+};
+
+/* exported as opaque type "blkid_partlist" */
+struct blkid_struct_partlist {
+	int		next_partno;	/* next partition number */
+	blkid_partition next_parent;	/* next parent if parsing nested PT */
+
+	int		nparts;		/* number of partitions */
+	int		nparts_max;	/* max.number of partitions */
+	blkid_partition	parts;		/* array of partitions */
+
+	struct list_head l_tabs;	/* list of partition tables */
+};
+
+static int blkid_partitions_probe_partition(blkid_probe pr);
+
+/**
+ * blkid_probe_enable_partitions:
+ * @pr: probe
+ * @enable: TRUE/FALSE
+ *
+ * Enables/disables the partitions probing for non-binary interface.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_enable_partitions(blkid_probe pr, int enable)
+{
+	if (!pr)
+		return -1;
+	pr->chains[BLKID_CHAIN_PARTS].enabled = enable;
+	return 0;
+}
+
+/**
+ * blkid_probe_set_partitions_flags:
+ * @pr: prober
+ * @flags: BLKID_PARTS_* flags
+ *
+ * Sets probing flags to the partitions prober. This function is optional.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_set_partitions_flags(blkid_probe pr, int flags)
+{
+	if (!pr)
+		return -1;
+	pr->chains[BLKID_CHAIN_PARTS].flags = flags;
+	return 0;
+}
+
+/**
+ * blkid_probe_reset_partitions_filter:
+ * @pr: prober
+ *
+ * Resets partitions probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_reset_partitions_filter(blkid_probe pr)
+{
+	return __blkid_probe_reset_filter(pr, BLKID_CHAIN_PARTS);
+}
+
+/**
+ * blkid_probe_invert_partitions_filter:
+ * @pr: prober
+ *
+ * Inverts partitions probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_invert_partitions_filter(blkid_probe pr)
+{
+	return __blkid_probe_invert_filter(pr, BLKID_CHAIN_PARTS);
+}
+
+/**
+ * blkid_probe_filter_partitions_type:
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @names: NULL terminated array of probing function names (e.g. "vfat").
+ *
+ *  %BLKID_FLTR_NOTIN  - probe for all items which are NOT IN @names
+ *
+ *  %BLKID_FLTR_ONLYIN - probe for items which are IN @names
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[])
+{
+	return __blkid_probe_filter_types(pr, BLKID_CHAIN_PARTS, flag, names);
+}
+
+/**
+ * blkid_probe_get_partitions:
+ * @pr: probe
+ *
+ * This is a binary interface for partitions. See also blkid_partlist_*
+ * functions.
+ *
+ * This function is independent on blkid_do_[safe,full]probe() and
+ * blkid_probe_enable_partitions() calls.
+ *
+ * WARNING: the returned object will be overwritten by the next
+ *          blkid_probe_get_partitions() call for the same @pr. If you want to
+ *          use more blkid_partlist objects in the same time you have to create
+ *          more blkid_probe handlers (see blkid_new_probe()).
+ *
+ * Returns: list of partitions, or NULL in case of error.
+ */
+blkid_partlist blkid_probe_get_partitions(blkid_probe pr)
+{
+	return (blkid_partlist) blkid_probe_get_binary_data(pr,
+			&pr->chains[BLKID_CHAIN_PARTS]);
+}
+
+/* for internal usage only */
+blkid_partlist blkid_probe_get_partlist(blkid_probe pr)
+{
+	return (blkid_partlist) pr->chains[BLKID_CHAIN_PARTS].data;
+}
+
+static void blkid_probe_set_partlist(blkid_probe pr, blkid_partlist ls)
+{
+	pr->chains[BLKID_CHAIN_PARTS].data = ls;
+}
+
+static void ref_parttable(blkid_parttable tab)
+{
+	tab->nparts++;
+}
+
+static void unref_parttable(blkid_parttable tab)
+{
+	tab->nparts--;
+
+	if (tab->nparts <= 0) {
+		list_del(&tab->t_tabs);
+		free(tab);
+	}
+}
+
+/* free all allocated parttables */
+static void free_parttables(blkid_partlist ls)
+{
+	if (!ls || !ls->l_tabs.next)
+		return;
+
+	/* remove unassigned partition tables */
+	while (!list_empty(&ls->l_tabs)) {
+		blkid_parttable tab = list_entry(ls->l_tabs.next,
+					struct blkid_struct_parttable, t_tabs);
+		unref_parttable(tab);
+	}
+}
+
+static void reset_partlist(blkid_partlist ls)
+{
+	if (!ls)
+		return;
+
+	free_parttables(ls);
+
+	if (ls->next_partno) {
+		/* already initialized - reset */
+		int tmp_nparts = ls->nparts_max;
+		blkid_partition tmp_parts = ls->parts;
+
+		memset(ls, 0, sizeof(struct blkid_struct_partlist));
+
+		ls->nparts_max = tmp_nparts;
+		ls->parts = tmp_parts;
+	}
+
+	ls->nparts = 0;
+	ls->next_partno = 1;
+	INIT_LIST_HEAD(&ls->l_tabs);
+
+	DBG(DEBUG_LOWPROBE, printf("partlist reset\n"));
+}
+
+static blkid_partlist partitions_init_data(struct blkid_chain *chn)
+{
+	blkid_partlist ls;
+
+	if (chn->data)
+		ls = (blkid_partlist) chn->data;
+	else {
+		/* allocate the new list of partitions */
+		ls = calloc(1, sizeof(struct blkid_struct_partlist));
+		if (!ls)
+			return NULL;
+		chn->data = (void *) ls;
+	}
+
+	reset_partlist(ls);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("parts: initialized partitions list (%p, size=%d)\n",
+		ls, ls->nparts_max));
+	return ls;
+}
+
+static void partitions_free_data(blkid_probe pr __attribute__((__unused__)),
+				 void *data)
+{
+	blkid_partlist ls = (blkid_partlist) data;
+
+	if (!ls)
+		return;
+
+	free_parttables(ls);
+
+	/* deallocate partitions and partlist */
+	free(ls->parts);
+	free(ls);
+}
+
+blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls,
+				const char *type, blkid_loff_t offset)
+{
+	blkid_parttable tab;
+
+	tab = calloc(1, sizeof(struct blkid_struct_parttable));
+	if (!tab)
+		return NULL;
+	tab->type = type;
+	tab->offset = offset;
+	tab->parent = ls->next_parent;
+
+	INIT_LIST_HEAD(&tab->t_tabs);
+	list_add_tail(&tab->t_tabs, &ls->l_tabs);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("parts: create a new partition table "
+		       "(%p, type=%s, offset=%"PRId64")\n", tab, type, offset));
+	return tab;
+}
+
+static blkid_partition new_partition(blkid_partlist ls, blkid_parttable tab)
+{
+	blkid_partition par;
+
+	if (ls->nparts + 1 > ls->nparts_max) {
+		/* Linux kernel has DISK_MAX_PARTS=256, but it's too much for
+		 * generic Linux machine -- let start with 32 partititions.
+		 */
+		ls->parts = realloc(ls->parts, (ls->nparts_max + 32) *
+					sizeof(struct blkid_struct_partition));
+		if (!ls->parts)
+			return NULL;
+		ls->nparts_max += 32;
+	}
+
+	par = &ls->parts[ls->nparts++];
+	memset(par, 0, sizeof(struct blkid_struct_partition));
+
+	ref_parttable(tab);
+	par->tab = tab;
+	par->partno = blkid_partlist_increment_partno(ls);
+
+	return par;
+}
+
+blkid_partition blkid_partlist_add_partition(blkid_partlist ls,
+					blkid_parttable tab,
+					blkid_loff_t start, blkid_loff_t size)
+{
+	blkid_partition par = new_partition(ls, tab);
+
+	if (!par)
+		return NULL;
+
+	par->start = start;
+	par->size = size;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("parts: add partition (%p start=%"
+		PRId64 ", size=%" PRId64 ", table=%p)\n",
+		par, par->start, par->size, tab));
+	return par;
+}
+
+/* allows to modify used partitions numbers (for example for logical partitions) */
+int blkid_partlist_set_partno(blkid_partlist ls, int partno)
+{
+	if (!ls)
+		return -1;
+	ls->next_partno = partno;
+	return 0;
+}
+
+int blkid_partlist_increment_partno(blkid_partlist ls)
+{
+	return ls ? ls->next_partno++ : -1;
+}
+
+/* allows to set "parent" for the next nested partition */
+int blkid_partlist_set_parent(blkid_partlist ls, blkid_partition par)
+{
+	if (!ls)
+		return -1;
+	ls->next_parent = par;
+	return 0;
+}
+
+blkid_partition blkid_partlist_get_parent(blkid_partlist ls)
+{
+	if (!ls)
+		return NULL;
+	return ls->next_parent;
+}
+
+int blkid_partitions_need_typeonly(blkid_probe pr)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	return chn && chn->data && chn->binary ? FALSE : TRUE;
+}
+
+/* get private chain flags */
+int blkid_partitions_get_flags(blkid_probe pr)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	return chn ? chn->flags : 0;
+}
+
+/* check if @start and @size are within @par partition */
+int blkid_is_nested_dimension(blkid_partition par,
+			blkid_loff_t start, blkid_loff_t size)
+{
+	blkid_loff_t pstart;
+	blkid_loff_t psize;
+
+	if (!par)
+		return 0;
+
+	pstart = blkid_partition_get_start(par);
+	psize = blkid_partition_get_size(par);
+
+	if (start < pstart || start + size > pstart + psize)
+		return 0;
+
+	return 1;
+}
+
+static int idinfo_probe(blkid_probe pr, const struct blkid_idinfo *id,
+			struct blkid_chain *chn)
+{
+	const struct blkid_idmag *mag = NULL;
+	blkid_loff_t off;
+	int rc = 1;		/* = nothing detected */
+
+	if (pr->size <= 0 || (id->minsz && id->minsz > pr->size))
+		goto nothing;	/* the device is too small */
+
+	if (blkid_probe_get_idmag(pr, id, &off, &mag))
+		goto nothing;
+
+	/* final check by probing function */
+	if (id->probefunc) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"%s: ---> call probefunc()\n", id->name));
+		rc = id->probefunc(pr, mag);
+	        if (rc == -1) {
+			/* reset after error */
+			reset_partlist(blkid_probe_get_partlist(pr));
+			if (chn && !chn->binary)
+				blkid_probe_chain_reset_vals(pr, chn);
+			DBG(DEBUG_LOWPROBE, printf(
+				"%s probefunc failed\n", id->name));
+		}
+		if (rc == 0 && mag && chn && !chn->binary)
+			rc = blkid_probe_set_magic(pr, off, mag->len,
+					(unsigned char *) mag->magic);
+
+		DBG(DEBUG_LOWPROBE, printf(
+			"%s: <--- (rc = %d)\n", id->name, rc));
+	}
+
+nothing:
+	return rc;
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int partitions_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+	int rc = 1;
+	size_t i;
+
+	if (!pr || chn->idx < -1)
+		return -1;
+	blkid_probe_chain_reset_vals(pr, chn);
+
+	if (chn->binary)
+		partitions_init_data(chn);
+
+	if (!pr->wipe_size && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_PT))
+		goto details_only;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("--> starting probing loop [PARTS idx=%d]\n",
+		chn->idx));
+
+	i = chn->idx < 0 ? 0 : chn->idx + 1U;
+
+	for ( ; i < ARRAY_SIZE(idinfos); i++) {
+		const char *name;
+
+		chn->idx = i;
+
+		/* apply filter */
+		if (chn->fltr && blkid_bmp_get_item(chn->fltr, i))
+			continue;
+
+		/* apply checks from idinfo */
+		if (idinfo_probe(pr, idinfos[i], chn) != 0)
+			continue;
+
+		name = idinfos[i]->name;
+
+		/* all checks passed */
+		if (!chn->binary)
+			blkid_probe_set_value(pr, "PTTYPE",
+						(unsigned char *) name,
+						strlen(name) + 1);
+		DBG(DEBUG_LOWPROBE,
+			printf("<-- leaving probing loop (type=%s) [PARTS idx=%d]\n",
+			name, chn->idx));
+		rc = 0;
+		break;
+	}
+
+	if (rc == 1) {
+		DBG(DEBUG_LOWPROBE,
+			printf("<-- leaving probing loop (failed) [PARTS idx=%d]\n",
+			chn->idx));
+	}
+
+details_only:
+	/*
+	 * Gather PART_ENTRY_* values if the current device is a partition.
+	 */
+	if (!chn->binary &&
+	    (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) {
+
+		if (!blkid_partitions_probe_partition(pr))
+			rc = 0;
+	}
+
+	return rc;
+}
+
+/* Probe for nested partition table within the parental partition */
+int blkid_partitions_do_subprobe(blkid_probe pr, blkid_partition parent,
+		const struct blkid_idinfo *id)
+{
+	blkid_probe prc;
+	int rc = 1;
+	blkid_partlist ls;
+	blkid_loff_t sz, off;
+
+	DBG(DEBUG_LOWPROBE, printf(
+		"parts: ----> %s subprobe requested (parent=%p)\n",
+		id->name, parent));
+
+	if (!pr || !parent || !parent->size)
+		return -1;
+
+	/* range defined by parent */
+	sz = ((blkid_loff_t) parent->size) << 9;
+	off = ((blkid_loff_t) parent->start) << 9;
+
+	if (off < pr->off || pr->off + pr->size < off + sz) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"ERROR: parts: <---- '%s' subprobe: overflow detected.\n",
+			id->name));
+		return -1;
+	}
+
+	/* create private prober */
+	prc = blkid_clone_probe(pr);
+	if (!prc)
+		return -1;
+
+	blkid_probe_set_dimension(prc, off, sz);
+
+	/* clone is always with reset chain, fix it */
+	prc->cur_chain = blkid_probe_get_chain(pr);
+
+	/*
+	 * Set 'parent' to the current list of the partitions and use the list
+	 * in cloned prober (so the cloned prober will extend the current list
+	 * of partitions rather than create a new).
+	 */
+	ls = blkid_probe_get_partlist(pr);
+	blkid_partlist_set_parent(ls, parent);
+
+	blkid_probe_set_partlist(prc, ls);
+
+	rc = idinfo_probe(prc, id, blkid_probe_get_chain(pr));
+
+	blkid_probe_set_partlist(prc, NULL);
+	blkid_partlist_set_parent(ls, NULL);
+
+	blkid_free_probe(prc);	/* free cloned prober */
+
+	DBG(DEBUG_LOWPROBE, printf(
+		"parts: <---- %s subprobe done (parent=%p, rc=%d)\n",
+		id->name, parent, rc));
+
+	return rc;
+}
+
+static int blkid_partitions_probe_partition(blkid_probe pr)
+{
+	int rc = 1;
+	blkid_probe disk_pr = NULL;
+	blkid_partlist ls;
+	blkid_partition par;
+	dev_t devno;
+
+	devno = blkid_probe_get_devno(pr);
+	if (!devno)
+		goto nothing;
+
+	disk_pr = blkid_probe_get_wholedisk_probe(pr);
+	if (!disk_pr)
+		goto nothing;
+
+	/* parse PT */
+	ls = blkid_probe_get_partitions(disk_pr);
+	if (!ls)
+		goto nothing;
+
+	par = blkid_partlist_devno_to_partition(ls, devno);
+	if (par) {
+		const char *v;
+		blkid_parttable tab = blkid_partition_get_table(par);
+		dev_t disk = blkid_probe_get_devno(disk_pr);
+
+		if (tab) {
+			v = blkid_parttable_get_type(tab);
+			if (v)
+				blkid_probe_set_value(pr, "PART_ENTRY_SCHEME",
+					(unsigned char *) v, strlen(v) + 1);
+		}
+
+		v = blkid_partition_get_name(par);
+		if (v)
+			blkid_probe_set_value(pr, "PART_ENTRY_NAME",
+				(unsigned char *) v, strlen(v) + 1);
+
+		v = blkid_partition_get_uuid(par);
+		if (v)
+			blkid_probe_set_value(pr, "PART_ENTRY_UUID",
+				(unsigned char *) v, strlen(v) + 1);
+
+		/* type */
+		v = blkid_partition_get_type_string(par);
+		if (v)
+			blkid_probe_set_value(pr, "PART_ENTRY_TYPE",
+				(unsigned char *) v, strlen(v) + 1);
+		else
+			blkid_probe_sprintf_value(pr, "PART_ENTRY_TYPE",
+				"0x%x", blkid_partition_get_type(par));
+
+		if (blkid_partition_get_flags(par))
+			blkid_probe_sprintf_value(pr, "PART_ENTRY_FLAGS",
+				"0x%llx", blkid_partition_get_flags(par));
+
+		blkid_probe_sprintf_value(pr, "PART_ENTRY_NUMBER",
+				"%d", blkid_partition_get_partno(par));
+
+		blkid_probe_sprintf_value(pr, "PART_ENTRY_OFFSET", "%jd",
+				blkid_partition_get_start(par));
+		blkid_probe_sprintf_value(pr, "PART_ENTRY_SIZE", "%jd",
+				blkid_partition_get_size(par));
+
+		blkid_probe_sprintf_value(pr, "PART_ENTRY_DISK", "%u:%u",
+				major(disk), minor(disk));
+	}
+	rc = 0;
+nothing:
+	return rc;
+}
+
+/*
+ * Returns 1 if the device is whole-disk and the area specified by @offset and
+ * @size is covered by any partition.
+ */
+int blkid_probe_is_covered_by_pt(blkid_probe pr,
+				 blkid_loff_t offset, blkid_loff_t size)
+{
+	blkid_probe prc;
+	blkid_partlist ls = NULL;
+	blkid_loff_t start, end;
+	int nparts, i, rc = 0;
+
+	DBG(DEBUG_LOWPROBE, printf(
+		"=> checking if off=%jd size=%jd covered by PT\n",
+		offset, size));
+
+	prc = blkid_clone_probe(pr);
+	if (!prc)
+		goto done;
+
+	ls = blkid_probe_get_partitions(prc);
+	if (!ls)
+		goto done;
+
+	nparts = blkid_partlist_numof_partitions(ls);
+	if (!nparts)
+		goto done;
+
+	end = (offset + size) >> 9;
+	start = offset >> 9;
+
+	/* check if the partition table fits into the device */
+	for (i = 0; i < nparts; i++) {
+		blkid_partition par = &ls->parts[i];
+
+		if (par->start + par->size > (pr->size >> 9)) {
+			DBG(DEBUG_LOWPROBE, printf("partition #%d overflows "
+				"device (off=%" PRId64 " size=%" PRId64 ")\n",
+				par->partno, par->start, par->size));
+			goto done;
+		}
+	}
+
+	/* check if the requested area is covered by PT */
+	for (i = 0; i < nparts; i++) {
+		blkid_partition par = &ls->parts[i];
+
+		if (start >= par->start && end <= par->start + par->size) {
+			rc = 1;
+			break;
+		}
+	}
+done:
+	blkid_free_probe(prc);
+
+	DBG(DEBUG_LOWPROBE, printf("<= %s covered by PT\n", rc ? "IS" : "NOT"));
+	return rc;
+}
+
+/**
+ * blkid_known_pttype:
+ * @pttype: partiton name
+ *
+ * Returns: 1 for known or 0 for unknown partition type.
+ */
+int blkid_known_pttype(const char *pttype)
+{
+	size_t i;
+
+	if (!pttype)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
+		const struct blkid_idinfo *id = idinfos[i];
+		if (strcmp(id->name, pttype) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+/**
+ * blkid_partlist_numof_partitions:
+ * @ls: partitions list
+ *
+ * Returns: number of partitions in the list or -1 in case of error.
+ */
+int blkid_partlist_numof_partitions(blkid_partlist ls)
+{
+	return ls ? ls->nparts : -1;
+}
+
+/**
+ * blkid_partlist_get_table:
+ * @ls: partitions list
+ *
+ * Returns: top-level partition table or NULL of there is not a partition table
+ * on the device.
+ */
+blkid_parttable blkid_partlist_get_table(blkid_partlist ls)
+{
+	if (!ls || list_empty(&ls->l_tabs))
+		return NULL;
+
+	return list_entry(ls->l_tabs.next,
+			struct blkid_struct_parttable, t_tabs);
+}
+
+
+/**
+ * blkid_partlist_get_partition:
+ * @ls: partitions list
+ * @n: partition number in range 0..N, where 'N' is blkid_partlist_numof_partitions().
+ *
+ * It's possible that the list of partitions is *empty*, but there is a valid
+ * partition table on the disk. This happen when on-disk details about
+ * partitions are unknown or the partition table is empty.
+ *
+ * See also blkid_partlist_get_table().
+ *
+ * Returns: partition object or NULL in case or error.
+ */
+blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n)
+{
+	if (!ls || n < 0 || n >= ls->nparts)
+		return NULL;
+
+	return &ls->parts[n];
+}
+
+/**
+ * blkid_partlist_devno_to_partition:
+ * @ls: partitions list
+ * @devno: requested partition
+ *
+ * This function tries to get start and size for @devno from sysfs and
+ * returns a partition from @ls which matches with the values from sysfs.
+ *
+ * This function is necessary when you want to make a relation between an entry
+ * in the partition table (@ls) and block devices in your system.
+ *
+ * Returns: partition object or NULL in case or error.
+ */
+blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno)
+{
+	struct sysfs_cxt sysfs;
+	uint64_t start, size;
+	int i, rc, partno = 0;
+
+	if (!ls)
+		return NULL;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("triyng to convert devno 0x%llx to partition\n",
+			(long long) devno));
+
+	if (sysfs_init(&sysfs, devno, NULL)) {
+		DBG(DEBUG_LOWPROBE, printf("failed t init sysfs context\n"));
+		return NULL;
+	}
+	rc = sysfs_read_u64(&sysfs, "size", &size);
+	if (!rc) {
+		rc = sysfs_read_u64(&sysfs, "start", &start);
+		if (rc) {
+			/* try to get partition number from DM uuid.
+			 */
+			char *uuid = sysfs_strdup(&sysfs, "dm/uuid");
+			char *tmp = uuid;
+			char *prefix = uuid ? strsep(&tmp, "-") : NULL;
+
+			if (prefix && strncasecmp(prefix, "part", 4) == 0) {
+				char *end = NULL;
+
+				partno = strtol(prefix + 4, &end, 10);
+				if (prefix == end || (end && *end))
+					partno = 0;
+				else
+					rc = 0;		/* success */
+			}
+			free(uuid);
+		}
+	}
+
+	sysfs_deinit(&sysfs);
+
+	if (rc)
+		return NULL;
+
+	if (partno) {
+		DBG(DEBUG_LOWPROBE, printf("mapped by DM, using partno %d\n", partno));
+
+		/*
+		 * Partition mapped by kpartx does not provide "start" offset
+		 * in /sys, but if we know partno and size of the partition
+		 * that we can probably make the releation bettween the device
+		 * and an entry in partition table.
+		 */
+		 for (i = 0; i < ls->nparts; i++) {
+			 blkid_partition par = &ls->parts[i];
+
+			 if (partno != blkid_partition_get_partno(par))
+				 continue;
+
+			 if ((blkid_loff_t) size == blkid_partition_get_size(par) ||
+			     (blkid_partition_is_extended(par) && size <= 1024))
+				 return par;
+
+		 }
+		 return NULL;
+	}
+
+	DBG(DEBUG_LOWPROBE, printf("searching by offset/size\n"));
+
+	for (i = 0; i < ls->nparts; i++) {
+		blkid_partition par = &ls->parts[i];
+
+		if (blkid_partition_get_start(par) == (blkid_loff_t) start &&
+		    blkid_partition_get_size(par) == (blkid_loff_t) size)
+			return par;
+
+		/* exception for extended dos partitions */
+		if (blkid_partition_get_start(par) == (blkid_loff_t) start &&
+		    blkid_partition_is_extended(par) && size <= 1024)
+			return par;
+
+	}
+
+	DBG(DEBUG_LOWPROBE, printf("not found partition for device\n"));
+	return NULL;
+}
+
+int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id)
+{
+	if (!tab)
+		return -1;
+
+	if (strcmp(tab->type, "gpt") == 0)
+		blkid_unparse_uuid(id, tab->id, sizeof(tab->id));
+	else if (strcmp(tab->type, "dos") == 0)
+		strncpy(tab->id, (const char *) id, sizeof(tab->id));
+
+	return 0;
+}
+
+/**
+ * blkid_parttable_get_id:
+ * @tab: partition table
+ *
+ * The ID is GPT disk UUID or DOS disk ID (in hex format).
+ *
+ * Returns: partition table ID (for example GPT disk UUID) or NULL
+ */
+const char *blkid_parttable_get_id(blkid_parttable tab)
+{
+	return tab && tab->id && *tab->id ? tab->id : NULL;
+}
+
+
+int blkid_partition_set_type(blkid_partition par, int type)
+{
+	if (!par)
+		return -1;
+	par->type = type;
+	return 0;
+}
+
+/**
+ * blkid_parttable_get_type:
+ * @tab: partition table
+ *
+ * Returns: partition table type (type name, e.g. "dos", "gpt", ...)
+ */
+const char *blkid_parttable_get_type(blkid_parttable tab)
+{
+	return tab ? tab->type : NULL;
+}
+
+/**
+ * blkid_parttable_get_parent:
+ * @tab: partition table
+ *
+ * Returns: parent for nexted partitition tables or NULL.
+ */
+blkid_partition blkid_parttable_get_parent(blkid_parttable tab)
+{
+	return tab ? tab->parent : NULL;
+}
+
+/**
+ * blkid_parttable_get_offset:
+ * @tab: partition table
+ *
+ * Note the position is relative to begin of the device as defined by
+ * blkid_probe_set_device() for primary partition table, and relative
+ * to parental partition for nested patition tables.
+ *
+ * <informalexample>
+ *   <programlisting>
+ * off_t offset;
+ * blkid_partition parent = blkid_parttable_get_parent(tab);
+ *
+ * offset = blkid_parttable_get_offset(tab);
+ *
+ * if (parent)
+ *      / * 'tab' is nested partition table * /
+ *	offset += blkid_partition_get_start(parent);
+ *   </programlisting>
+ * </informalexample>
+
+ * Returns: position (in bytes) of the partition table or -1 in case of error.
+ *
+ */
+blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab)
+{
+	return tab ? tab->offset : -1;
+}
+
+/**
+ * blkid_partition_get_table:
+ * @par: partition
+ *
+ * The "parttable" describes partition table. The table is usually the same for
+ * all partitions -- except nested partition tables.
+ *
+ * For example bsd, solaris, etc. use a nested partition table within
+ * standard primary dos partition:
+ *
+ * <informalexample>
+ *   <programlisting>
+ *
+ *  -- dos partition table
+ *  0: sda1     dos primary partition
+ *  1: sda2     dos primary partition
+ *     -- bsd partition table (with in sda2)
+ *  2:    sda5  bds partition
+ *  3:    sda6  bds partition
+ *
+ *   </programlisting>
+ * </informalexample>
+ *
+ * The library does not to use a separate partition table object for dos logical
+ * partitions (partitions within extended partition). It's possible to
+ * differentiate between logical, extended and primary partitions by
+ *
+ *	blkid_partition_is_{extended,primary,logical}().
+ *
+ * Returns: partition table object or NULL in case of error.
+ */
+blkid_parttable blkid_partition_get_table(blkid_partition par)
+{
+	return par ? par->tab : NULL;
+}
+
+static int partition_get_logical_type(blkid_partition par)
+{
+	blkid_parttable tab;
+
+	if (!par)
+		return -1;
+
+	tab = blkid_partition_get_table(par);
+	if (!tab || !tab->type)
+		return -1;
+
+	if (tab->parent)
+		return 'L';  /* report nested partitions as logical */
+
+	if (!strcmp(tab->type, "dos")) {
+		if (par->partno > 4)
+			return 'L';	/* logical */
+
+	        if(par->type == BLKID_DOS_EXTENDED_PARTITION ||
+                   par->type == BLKID_W95_EXTENDED_PARTITION ||
+		   par->type == BLKID_LINUX_EXTENDED_PARTITION)
+			return 'E';
+	}
+	return 'P';
+}
+
+/**
+ * blkid_partition_is_primary:
+ * @par: partition
+ *
+ * Note, this function returns FALSE for DOS extended partitions and
+ * all partitions in nested partition tables.
+ *
+ * Returns: 1 if the partitions is primary partition or 0 if not.
+ */
+int blkid_partition_is_primary(blkid_partition par)
+{
+	return partition_get_logical_type(par) == 'P' ? TRUE : FALSE;
+}
+
+/**
+ * blkid_partition_is_extended:
+ * @par: partition
+ *
+ * Returns: 1 if the partitions is extended (dos, windows or linux)
+ * partition or 0 if not.
+ */
+int blkid_partition_is_extended(blkid_partition par)
+{
+	return partition_get_logical_type(par) == 'E' ? TRUE : FALSE;
+}
+
+/**
+ * blkid_partition_is_logical:
+ * @par: partition
+ *
+ * Note that this function returns TRUE for all partitions in all
+ * nested partition tables (e.g. BSD labels).
+ *
+ * Returns: 1 if the partitions is logical partition or 0 if not.
+ */
+int blkid_partition_is_logical(blkid_partition par)
+{
+	return partition_get_logical_type(par) == 'L' ? TRUE : FALSE;
+}
+
+static void set_string(unsigned char *item, size_t max,
+				const unsigned char *data, size_t len)
+{
+	if (len >= max)
+		len = max - 1;
+
+	memcpy(item, data, len);
+	item[len] = '\0';
+
+	blkid_rtrim_whitespace(item);
+}
+
+int blkid_partition_set_name(blkid_partition par,
+		const unsigned char *name, size_t len)
+{
+	if (!par)
+		return -1;
+
+	set_string(par->name, sizeof(par->name), name, len);
+	return 0;
+}
+
+int blkid_partition_set_utf8name(blkid_partition par, const unsigned char *name,
+		size_t len, int enc)
+{
+	if (!par)
+		return -1;
+
+	blkid_encode_to_utf8(enc, par->name, sizeof(par->name), name, len);
+	blkid_rtrim_whitespace(par->name);
+	return 0;
+}
+
+int blkid_partition_set_uuid(blkid_partition par, const unsigned char *uuid)
+{
+	if (!par)
+		return -1;
+
+	blkid_unparse_uuid(uuid, par->uuid, sizeof(par->uuid));
+	return 0;
+}
+
+/**
+ * blkid_partition_get_name:
+ * @par: partition
+ *
+ * Returns: partition name string if supported by PT (e.g. Mac) or NULL.
+ */
+const char *blkid_partition_get_name(blkid_partition par)
+{
+	return par && *par->name ? (char *) par->name : NULL;
+}
+
+/**
+ * blkid_partition_get_uuid:
+ * @par: partition
+ *
+ * Returns: partition UUID string if supported by PT (e.g. GPT) or NULL.
+ */
+const char *blkid_partition_get_uuid(blkid_partition par)
+{
+	return par && *par->uuid ? par->uuid : NULL;
+}
+
+/**
+ * blkid_partition_get_partno:
+ * @par: partition
+ *
+ * Returns: proposed partitin number (e.g. 'N' from sda'N') or -1 in case of
+ * error. Note that the number is generate by library independenly on your OS.
+ */
+int blkid_partition_get_partno(blkid_partition par)
+{
+	return par ? par->partno : -1;
+}
+
+/**
+ * blkid_partition_get_start:
+ * @par: partition
+ *
+ * Be careful if you _not_ probe whole disk:
+ *
+ * 1) the offset is usully relative to begin of the disk -- but if you probe a
+ *    fragment of the disk only -- then the offset could be still relative to
+ *    the begin of the disk rather that relative to the fragment.
+ *
+ * 2) the offset for nested partitions could be releative to parent (e.g. Solaris)
+ *    _or_ relative to the begin of the whole disk (e.g. bsd).
+ *
+ * You don't have to care about such details if you proble whole disk. In such
+ * a case libblkid always returns the offset relative to the begin of the disk.
+ *
+ * Returns: start of the partition (in 512-sectors).
+ */
+blkid_loff_t blkid_partition_get_start(blkid_partition par)
+{
+	return par ? par->start : -1;
+}
+
+/**
+ * blkid_partition_get_size:
+ * @par: partition
+ *
+ * WARNING: be very careful when you work with MS-DOS extended partitions. The
+ *          library always returns full size of the partition. If you want add
+ *          the partition to the Linux system (BLKPG_ADD_PARTITION ioctl) you
+ *          need to reduce the size of the partition to 1 or 2 blocks. The
+ *          rest of the partition has to be unaccessible for mkfs or mkswap
+ *          programs, we need a small space for boot loaders only.
+ *
+ *          For some unknown reason this (safe) practice is not to used for
+ *          nested BSD, Solaris, ..., partition tables in Linux kernel.
+ *
+ * Returns: size of the partition (in 512-sectors).
+ */
+blkid_loff_t blkid_partition_get_size(blkid_partition par)
+{
+	return par ? par->size : -1;
+}
+
+/**
+ * blkid_partition_get_type:
+ * @par: partition
+ *
+ * Returns: partition type.
+ */
+int blkid_partition_get_type(blkid_partition par)
+{
+	return par->type;
+}
+
+/* Sets partition 'type' for PT where the type is defined by string rather
+ * than by number
+ */
+int blkid_partition_set_type_string(blkid_partition par,
+		const unsigned char *type, size_t len)
+{
+	if (!par)
+		return -1;
+
+	set_string((unsigned char *) par->typestr,
+			sizeof(par->typestr), type, len);
+	return 0;
+}
+
+/* Sets partition 'type' for PT where the type is defined by UUIDrather
+ * than by number
+ */
+int blkid_partition_set_type_uuid(blkid_partition par, const unsigned char *uuid)
+{
+	if (!par)
+		return -1;
+
+	blkid_unparse_uuid(uuid, par->typestr, sizeof(par->typestr));
+	return 0;
+}
+
+/**
+ * blkid_partition_get_type_string:
+ * @par: partition
+ *
+ * The type string is supported by a small subset of partition tables (e.g Mac
+ * and EFI GPT).  Note that GPT uses type UUID and this function returns this
+ * UUID as string.
+ *
+ * Returns: partition type string or NULL.
+ */
+const char *blkid_partition_get_type_string(blkid_partition par)
+{
+	return par && *par->typestr ? par->typestr : NULL;
+}
+
+
+int blkid_partition_set_flags(blkid_partition par, unsigned long long flags)
+{
+	if (!par)
+		return -1;
+	par->flags = flags;
+	return 0;
+}
+
+/**
+ * blkid_partition_get_flags
+ * @par: partition
+ *
+ * Returns: partition flags (or attributes for gpt).
+ */
+unsigned long long blkid_partition_get_flags(blkid_partition par)
+{
+	return par->flags;
+}
+
diff --git a/libblkid/partitions.h b/libblkid/partitions.h
new file mode 100644
index 0000000..496bd4a
--- /dev/null
+++ b/libblkid/partitions.h
@@ -0,0 +1,64 @@
+#ifndef BLKID_PARTITIONS_H
+#define BLKID_PARTITIONS_H
+
+#include "blkidP.h"
+#include "blkid_parttypes.h"
+
+extern int blkid_partitions_get_flags(blkid_probe pr);
+
+extern blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls,
+				const char *type, blkid_loff_t offset);
+
+extern int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id);
+
+extern blkid_partition blkid_partlist_add_partition(blkid_partlist ls,
+				blkid_parttable tab,
+				blkid_loff_t start, blkid_loff_t size);
+
+extern int blkid_partlist_set_partno(blkid_partlist ls, int partno);
+extern int blkid_partlist_increment_partno(blkid_partlist ls);
+
+extern blkid_partition blkid_partlist_get_parent(blkid_partlist ls);
+
+extern int blkid_partitions_do_subprobe(blkid_probe pr,
+			blkid_partition parent, const struct blkid_idinfo *id);
+
+extern int blkid_partitions_need_typeonly(blkid_probe pr);
+extern int blkid_is_nested_dimension(blkid_partition par,
+                        blkid_loff_t start, blkid_loff_t size);
+
+extern int blkid_partition_set_name(blkid_partition par,
+		const unsigned char *name, size_t len);
+
+extern int blkid_partition_set_utf8name(blkid_partition par,
+		const unsigned char *name, size_t len, int enc);
+
+extern int blkid_partition_set_uuid(blkid_partition par,
+		const unsigned char *uuid);
+
+extern int blkid_partition_set_type(blkid_partition par, int type);
+
+extern int blkid_partition_set_type_string(blkid_partition par,
+                const unsigned char *type, size_t len);
+
+extern int blkid_partition_set_type_uuid(blkid_partition par,
+		const unsigned char *uuid);
+
+extern int blkid_partition_set_flags(blkid_partition par, unsigned long long flags);
+
+/*
+ * partition probers
+ */
+extern const struct blkid_idinfo aix_pt_idinfo;
+extern const struct blkid_idinfo bsd_pt_idinfo;
+extern const struct blkid_idinfo unixware_pt_idinfo;
+extern const struct blkid_idinfo solaris_x86_pt_idinfo;
+extern const struct blkid_idinfo sun_pt_idinfo;
+extern const struct blkid_idinfo sgi_pt_idinfo;
+extern const struct blkid_idinfo mac_pt_idinfo;
+extern const struct blkid_idinfo dos_pt_idinfo;
+extern const struct blkid_idinfo minix_pt_idinfo;
+extern const struct blkid_idinfo gpt_pt_idinfo;
+extern const struct blkid_idinfo ultrix_pt_idinfo;
+
+#endif /* BLKID_PARTITIONS_H */
diff --git a/libblkid/path.c b/libblkid/path.c
new file mode 100644
index 0000000..4f955d9
--- /dev/null
+++ b/libblkid/path.c
@@ -0,0 +1,245 @@
+/*
+ * Simple functions to access files, paths maybe be globally prefixed by a
+ * global prefix to read data from alternative destination (e.g. /proc dump for
+ * regression tests).
+ *
+ * Taken from lscpu.c
+ *
+ * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
+ * Copyright (C) 2008-2012 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "all-io.h"
+#include "path.h"
+#include "nls.h"
+#include "c.h"
+
+static size_t prefixlen;
+static char pathbuf[PATH_MAX];
+
+static const char *
+path_vcreate(const char *path, va_list ap)
+{
+	if (prefixlen)
+		vsnprintf(pathbuf + prefixlen,
+			  sizeof(pathbuf) - prefixlen, path, ap);
+	else
+		vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
+	return pathbuf;
+}
+
+static FILE *
+path_vfopen(const char *mode, int exit_on_error, const char *path, va_list ap)
+{
+	FILE *f;
+	const char *p = path_vcreate(path, ap);
+
+	f = fopen(p, mode);
+	if (!f && exit_on_error)
+		err(EXIT_FAILURE, _("cannot open %s"), p);
+	return f;
+}
+
+static int
+path_vopen(int flags, const char *path, va_list ap)
+{
+	int fd;
+	const char *p = path_vcreate(path, ap);
+
+	fd = open(p, flags);
+	if (fd == -1)
+		err(EXIT_FAILURE, _("cannot open %s"), p);
+	return fd;
+}
+
+FILE *
+path_fopen(const char *mode, int exit_on_error, const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+
+	va_start(ap, path);
+	fd = path_vfopen(mode, exit_on_error, path, ap);
+	va_end(ap);
+
+	return fd;
+}
+
+void
+path_read_str(char *result, size_t len, const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+
+	va_start(ap, path);
+	fd = path_vfopen("r", 1, path, ap);
+	va_end(ap);
+
+	if (!fgets(result, len, fd))
+		err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
+	fclose(fd);
+
+	len = strlen(result);
+	if (result[len - 1] == '\n')
+		result[len - 1] = '\0';
+}
+
+int
+path_read_s32(const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+	int result;
+
+	va_start(ap, path);
+	fd = path_vfopen("r", 1, path, ap);
+	va_end(ap);
+
+	if (fscanf(fd, "%d", &result) != 1) {
+		if (ferror(fd))
+			err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
+		else
+			errx(EXIT_FAILURE, _("parse error: %s"), pathbuf);
+	}
+	fclose(fd);
+	return result;
+}
+
+uint64_t
+path_read_u64(const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+	uint64_t result;
+
+	va_start(ap, path);
+	fd = path_vfopen("r", 1, path, ap);
+	va_end(ap);
+
+	if (fscanf(fd, "%"SCNu64, &result) != 1) {
+		if (ferror(fd))
+			err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
+		else
+			errx(EXIT_FAILURE, _("parse error: %s"), pathbuf);
+	}
+	fclose(fd);
+	return result;
+}
+
+int
+path_write_str(const char *str, const char *path, ...)
+{
+	int fd, result;
+	va_list ap;
+
+	va_start(ap, path);
+	fd = path_vopen(O_WRONLY, path, ap);
+	va_end(ap);
+	result = write_all(fd, str, strlen(str));
+	close(fd);
+	return result;
+}
+
+int
+path_exist(const char *path, ...)
+{
+	va_list ap;
+	const char *p;
+
+	va_start(ap, path);
+	p = path_vcreate(path, ap);
+	va_end(ap);
+
+	return access(p, F_OK) == 0;
+}
+
+#ifdef HAVE_CPU_SET_T
+
+static cpu_set_t *
+path_cpuparse(int maxcpus, int islist, const char *path, va_list ap)
+{
+	FILE *fd;
+	cpu_set_t *set;
+	size_t setsize, len = maxcpus * 7;
+	char buf[len];
+
+	fd = path_vfopen("r", 1, path, ap);
+
+	if (!fgets(buf, len, fd))
+		err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
+	fclose(fd);
+
+	len = strlen(buf);
+	if (buf[len - 1] == '\n')
+		buf[len - 1] = '\0';
+
+	set = cpuset_alloc(maxcpus, &setsize, NULL);
+	if (!set)
+		err(EXIT_FAILURE, _("failed to callocate cpu set"));
+
+	if (islist) {
+		if (cpulist_parse(buf, set, setsize, 0))
+			errx(EXIT_FAILURE, _("failed to parse CPU list %s"), buf);
+	} else {
+		if (cpumask_parse(buf, set, setsize))
+			errx(EXIT_FAILURE, _("failed to parse CPU mask %s"), buf);
+	}
+	return set;
+}
+
+cpu_set_t *
+path_read_cpuset(int maxcpus, const char *path, ...)
+{
+	va_list ap;
+	cpu_set_t *set;
+
+	va_start(ap, path);
+	set = path_cpuparse(maxcpus, 0, path, ap);
+	va_end(ap);
+
+	return set;
+}
+
+cpu_set_t *
+path_read_cpulist(int maxcpus, const char *path, ...)
+{
+	va_list ap;
+	cpu_set_t *set;
+
+	va_start(ap, path);
+	set = path_cpuparse(maxcpus, 1, path, ap);
+	va_end(ap);
+
+	return set;
+}
+
+#endif /* HAVE_CPU_SET_T */
+
+void
+path_set_prefix(const char *prefix)
+{
+	prefixlen = strlen(prefix);
+	strncpy(pathbuf, prefix, sizeof(pathbuf));
+	pathbuf[sizeof(pathbuf) - 1] = '\0';
+}
diff --git a/libblkid/path.h b/libblkid/path.h
new file mode 100644
index 0000000..615d284
--- /dev/null
+++ b/libblkid/path.h
@@ -0,0 +1,31 @@
+#ifndef UTIL_LINUX_PATH_H
+#define UTIL_LINUX_PATH_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+extern FILE *path_fopen(const char *mode, int exit_on_err, const char *path, ...)
+			__attribute__ ((__format__ (__printf__, 3, 4)));
+extern void path_read_str(char *result, size_t len, const char *path, ...)
+			__attribute__ ((__format__ (__printf__, 3, 4)));
+extern int path_write_str(const char *str, const char *path, ...)
+			 __attribute__ ((__format__ (__printf__, 2, 3)));
+extern int path_read_s32(const char *path, ...)
+			__attribute__ ((__format__ (__printf__, 1, 2)));
+extern uint64_t path_read_u64(const char *path, ...)
+			__attribute__ ((__format__ (__printf__, 1, 2)));
+
+extern int path_exist(const char *path, ...)
+		      __attribute__ ((__format__ (__printf__, 1, 2)));
+
+#ifdef HAVE_CPU_SET_T
+# include "cpuset.h"
+
+extern cpu_set_t *path_read_cpuset(int, const char *path, ...)
+			      __attribute__ ((__format__ (__printf__, 2, 3)));
+extern cpu_set_t *path_read_cpulist(int, const char *path, ...)
+			       __attribute__ ((__format__ (__printf__, 2, 3)));
+extern void path_set_prefix(const char *);
+#endif /* HAVE_CPU_SET_T */
+
+#endif /* UTIL_LINUX_PATH_H */
diff --git a/libblkid/pathnames.h b/libblkid/pathnames.h
new file mode 100644
index 0000000..6d300a9
--- /dev/null
+++ b/libblkid/pathnames.h
@@ -0,0 +1,179 @@
+/*
+ * Vaguely based on
+ *	@(#)pathnames.h	5.3 (Berkeley) 5/9/89
+ * This code is in the public domain.
+ */
+#ifndef PATHNAMES_H
+#define PATHNAMES_H
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifndef __STDC__
+# error "we need an ANSI compiler"
+#endif
+
+/* used by kernel in /proc (e.g. /proc/swaps) for deleted files */
+#define PATH_DELETED_SUFFIX	"\\040(deleted)"
+#define PATH_DELETED_SUFFIX_SZ	(sizeof(PATH_DELETED_SUFFIX) - 1)
+
+/* DEFPATHs from <paths.h> don't include /usr/local */
+#undef _PATH_DEFPATH
+#define	_PATH_DEFPATH	        "/usr/local/bin:/bin:/usr/bin"
+
+#undef _PATH_DEFPATH_ROOT
+#define	_PATH_DEFPATH_ROOT	"/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
+
+#define _PATH_SECURETTY		"/etc/securetty"
+#define _PATH_WTMPLOCK		"/etc/wtmplock"
+
+#define	_PATH_HUSHLOGIN		".hushlogin"
+#define	_PATH_HUSHLOGINS	"/etc/hushlogins"
+
+#ifndef _PATH_MAILDIR
+#define	_PATH_MAILDIR		"/var/spool/mail"
+#endif
+#define	_PATH_MOTDFILE		"/etc/motd"
+#define	_PATH_NOLOGIN		"/etc/nologin"
+
+#define _PATH_LOGIN		"/bin/login"
+#define _PATH_INITTAB		"/etc/inittab"
+#define _PATH_RC		"/etc/rc"
+#define _PATH_REBOOT		"/sbin/reboot"
+#define _PATH_SHUTDOWN		"/sbin/shutdown"
+#define _PATH_SINGLE		"/etc/singleboot"
+#define _PATH_SHUTDOWN_CONF	"/etc/shutdown.conf"
+
+#define _PATH_SECURE		"/etc/securesingle"
+#define _PATH_USERTTY           "/etc/usertty"
+
+/* used in login-utils/shutdown.c */
+
+/* used in login-utils/setpwnam.h and login-utils/islocal.c */
+#define _PATH_PASSWD		"/etc/passwd"
+
+/* used in login-utils/newgrp and login-utils/setpwnam.h*/
+#define _PATH_GSHADOW		"/etc/gshadow"
+
+/* used in login-utils/setpwnam.h */
+#define _PATH_GROUP		"/etc/group"
+#define _PATH_SHADOW_PASSWD	"/etc/shadow"
+#define _PATH_SHELLS		"/etc/shells"
+
+/* used in term-utils/agetty.c */
+#define _PATH_ISSUE		"/etc/issue"
+#define _PATH_NUMLOCK_ON	_PATH_LOCALSTATEDIR "/numlock-on"
+
+#define _PATH_LOGINDEFS		"/etc/login.defs"
+
+/* used in misc-utils/look.c */
+#define _PATH_WORDS             "/usr/share/dict/words"
+#define _PATH_WORDS_ALT         "/usr/share/dict/web2"
+
+/* mount paths */
+#define _PATH_UMOUNT		"/bin/umount"
+
+#define _PATH_FILESYSTEMS	"/etc/filesystems"
+#define _PATH_PROC_SWAPS	"/proc/swaps"
+#define _PATH_PROC_FILESYSTEMS	"/proc/filesystems"
+#define _PATH_PROC_MOUNTS	"/proc/mounts"
+#define _PATH_PROC_PARTITIONS	"/proc/partitions"
+#define _PATH_PROC_DEVICES	"/proc/devices"
+#define _PATH_PROC_MOUNTINFO	"/proc/self/mountinfo"
+#define _PATH_PROC_LOCKS        "/proc/locks"
+#define _PATH_PROC_CDROMINFO	"/proc/sys/dev/cdrom/info"
+
+#define _PATH_PROC_ATTR_CURRENT	"/proc/self/attr/current"
+#define _PATH_PROC_ATTR_EXEC	"/proc/self/attr/exec"
+#define _PATH_PROC_CAPLASTCAP	"/proc/sys/kernel/cap_last_cap"
+
+
+#define _PATH_SYS_BLOCK		"/sys/block"
+#define _PATH_SYS_DEVBLOCK	"/sys/dev/block"
+#define _PATH_SYS_CLASS		"/sys/class"
+#define _PATH_SYS_SCSI		"/sys/bus/scsi"
+
+#define _PATH_SYS_SELINUX	"/sys/fs/selinux"
+#define _PATH_SYS_APPARMOR	"/sys/kernel/security/apparmor"
+
+#ifndef _PATH_MOUNTED
+# ifdef MOUNTED					/* deprecated */
+#  define _PATH_MOUNTED		MOUNTED
+# else
+#  define _PATH_MOUNTED		"/etc/mtab"
+# endif
+#endif
+
+#ifndef _PATH_MNTTAB
+# ifdef MNTTAB					/* deprecated */
+#  define _PATH_MNTTAB		MNTTAB
+# else
+#  define _PATH_MNTTAB		"/etc/fstab"
+# endif
+#endif
+
+#define _PATH_MNTTAB_DIR	_PATH_MNTTAB ".d"
+
+#define _PATH_MOUNTED_LOCK	_PATH_MOUNTED "~"
+#define _PATH_MOUNTED_TMP	_PATH_MOUNTED ".tmp"
+
+#ifndef _PATH_DEV
+  /*
+   * The tailing '/' in _PATH_DEV is there for compatibility with libc.
+   */
+# define _PATH_DEV		"/dev/"
+#endif
+
+#define _PATH_DEV_LOOP		"/dev/loop"
+#define _PATH_DEV_LOOPCTL	"/dev/loop-control"
+#define _PATH_DEV_TTY		"/dev/tty"
+
+
+/* udev paths */
+#define _PATH_DEV_BYLABEL	"/dev/disk/by-label"
+#define _PATH_DEV_BYUUID	"/dev/disk/by-uuid"
+#define _PATH_DEV_BYID		"/dev/disk/by-id"
+#define _PATH_DEV_BYPATH	"/dev/disk/by-path"
+#define _PATH_DEV_BYPARTLABEL	"/dev/disk/by-partlabel"
+#define _PATH_DEV_BYPARTUUID	"/dev/disk/by-partuuid"
+
+/* hwclock paths */
+#define _PATH_ADJPATH		"/etc/adjtime"
+#define _PATH_LASTDATE		"/var/lib/lastdate"
+#ifdef __ia64__
+# define _PATH_RTC_DEV		"/dev/efirtc"
+#else
+# define _PATH_RTC_DEV		"/dev/rtc"
+#endif
+
+#ifndef _PATH_BTMP
+#define _PATH_BTMP		"/var/log/btmp"
+#endif
+
+/* raw paths*/
+#define _PATH_RAWDEVDIR		"/dev/raw/"
+#define _PATH_RAWDEVCTL		_PATH_RAWDEVDIR "rawctl"
+/* deprecated */
+#define _PATH_RAWDEVCTL_OLD	"/dev/rawctl"
+
+/* wdctl path */
+#define _PATH_WATCHDOG_DEV	"/dev/watchdog"
+
+/* ipc paths */
+#define _PATH_PROC_SYSV_MSG	"/proc/sysvipc/msg"
+#define _PATH_PROC_SYSV_SEM	"/proc/sysvipc/sem"
+#define _PATH_PROC_SYSV_SHM	"/proc/sysvipc/shm"
+#define _PATH_PROC_IPC_MSGMAX	"/proc/sys/kernel/msgmax"
+#define _PATH_PROC_IPC_MSGMNB	"/proc/sys/kernel/msgmnb"
+#define _PATH_PROC_IPC_MSGMNI	"/proc/sys/kernel/msgmni"
+#define _PATH_PROC_IPC_SEM	"/proc/sys/kernel/sem"
+#define _PATH_PROC_IPC_SHMALL	"/proc/sys/kernel/shmall"
+#define _PATH_PROC_IPC_SHMMAX	"/proc/sys/kernel/shmmax"
+#define _PATH_PROC_IPC_SHMMNI	"/proc/sys/kernel/shmmni"
+
+/* kernel command line */
+#define _PATH_PROC_CMDLINE	"/proc/cmdline"
+
+#endif /* PATHNAMES_H */
+
diff --git a/libblkid/probe.c b/libblkid/probe.c
new file mode 100644
index 0000000..77e8b86
--- /dev/null
+++ b/libblkid/probe.c
@@ -0,0 +1,1779 @@
+/*
+ * Low-level libblkid probing API
+ *
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: lowprobe
+ * @title: Low-level probing
+ * @short_description: low-level prober initialization
+ *
+ * The low-level probing routines always and directly read information from
+ * the selected (see blkid_probe_set_device()) device.
+ *
+ * The probing routines are grouped together into separate chains. Currently,
+ * the library provides superblocks, partitions and topology chains.
+ *
+ * The probing routines is possible to filter (enable/disable) by type (e.g.
+ * fstype "vfat" or partype "gpt") or by usage flags (e.g. BLKID_USAGE_RAID).
+ * These filters are per-chain. Note that always when you touch the chain
+ * filter the current probing position is reset and probing starts from
+ * scratch.  It means that the chain filter should not be modified during
+ * probing, for example in loop where you call blkid_do_probe().
+ *
+ * For more details see the chain specific documentation.
+ *
+ * The low-level API provides two ways how access to probing results.
+ *
+ *   1. The NAME=value (tag) interface. This interface is older and returns all data
+ *      as strings. This interface is generic for all chains.
+ *
+ *   2. The binary interfaces. These interfaces return data in the native formats.
+ *      The interface is always specific to the probing chain.
+ *
+ *  Note that the previous probing result (binary or NAME=value) is always
+ *  zeroized when a chain probing function is called. For example:
+ *
+ * <informalexample>
+ *   <programlisting>
+ *     blkid_probe_enable_partitions(pr, TRUE);
+ *     blkid_probe_enable_superblocks(pr, FALSE);
+ *
+ *     blkid_do_safeprobe(pr);
+ *   </programlisting>
+ * </informalexample>
+ *
+ * overwrites the previous probing result for the partitions chain, the superblocks
+ * result is not modified.
+ */
+
+/**
+ * SECTION: lowprobe-tags
+ * @title: Low-level tags
+ * @short_description: generic NAME=value interface.
+ *
+ * The probing routines inside the chain are mutually exclusive by default --
+ * only few probing routines are marked as "tolerant". The "tolerant" probing
+ * routines are used for filesystem which can share the same device with any
+ * other filesystem. The blkid_do_safeprobe() checks for the "tolerant" flag.
+ *
+ * The SUPERBLOCKS chain is enabled by default. The all others chains is
+ * necessary to enable by blkid_probe_enable_'CHAINNAME'(). See chains specific
+ * documentation.
+ *
+ * The blkid_do_probe() function returns a result from only one probing
+ * routine, and the next call from the next probing routine. It means you need
+ * to call the function in loop, for example:
+ *
+ * <informalexample>
+ *   <programlisting>
+ *	while((blkid_do_probe(pr) == 0)
+ *		... use result ...
+ *   </programlisting>
+ * </informalexample>
+ *
+ * The blkid_do_safeprobe() is the same as blkid_do_probe(), but returns only
+ * first probing result for every enabled chain. This function checks for
+ * ambivalent results (e.g. more "intolerant" filesystems superblocks on the
+ * device).
+ *
+ * The probing result is set of NAME=value pairs (the NAME is always unique).
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_LINUX_CDROM_H
+#include <linux/cdrom.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#ifdef HAVE_LIBUUID
+# include <uuid.h>
+#endif
+
+#include "blkidP.h"
+#include "all-io.h"
+
+/* chains */
+extern const struct blkid_chaindrv superblocks_drv;
+extern const struct blkid_chaindrv topology_drv;
+extern const struct blkid_chaindrv partitions_drv;
+
+/*
+ * All supported chains
+ */
+static const struct blkid_chaindrv *chains_drvs[] = {
+	[BLKID_CHAIN_SUBLKS] = &superblocks_drv,
+	[BLKID_CHAIN_TOPLGY] = &topology_drv,
+	[BLKID_CHAIN_PARTS] = &partitions_drv
+};
+
+static void blkid_probe_reset_vals(blkid_probe pr);
+static void blkid_probe_reset_buffer(blkid_probe pr);
+
+/**
+ * blkid_new_probe:
+ *
+ * Returns: a pointer to the newly allocated probe struct or NULL in case of error.
+ */
+blkid_probe blkid_new_probe(void)
+{
+	int i;
+	blkid_probe pr;
+
+	blkid_init_debug(0);
+	pr = calloc(1, sizeof(struct blkid_struct_probe));
+	if (!pr)
+		return NULL;
+
+	DBG(DEBUG_LOWPROBE, printf("allocate a new probe %p\n", pr));
+
+	/* initialize chains */
+	for (i = 0; i < BLKID_NCHAINS; i++) {
+		pr->chains[i].driver = chains_drvs[i];
+		pr->chains[i].flags = chains_drvs[i]->dflt_flags;
+		pr->chains[i].enabled = chains_drvs[i]->dflt_enabled;
+	}
+	INIT_LIST_HEAD(&pr->buffers);
+	return pr;
+}
+
+/*
+ * Clone @parent, the new clone shares all, but except:
+ *
+ *	- probing result
+ *	- bufferes if another device (or offset) is set to the prober
+ */
+blkid_probe blkid_clone_probe(blkid_probe parent)
+{
+	blkid_probe pr;
+
+	if (!parent)
+		return NULL;
+
+	DBG(DEBUG_LOWPROBE, printf("allocate a probe clone\n"));
+
+	pr = blkid_new_probe();
+	if (!pr)
+		return NULL;
+
+	pr->fd = parent->fd;
+	pr->off = parent->off;
+	pr->size = parent->size;
+	pr->devno = parent->devno;
+	pr->disk_devno = parent->disk_devno;
+	pr->blkssz = parent->blkssz;
+	pr->flags = parent->flags;
+	pr->parent = parent;
+
+	pr->flags &= ~BLKID_FL_PRIVATE_FD;
+
+	return pr;
+}
+
+
+
+/**
+ * blkid_new_probe_from_filename:
+ * @filename: device or regular file
+ *
+ * This function is same as call open(filename), blkid_new_probe() and
+ * blkid_probe_set_device(pr, fd, 0, 0).
+ *
+ * The @filename is closed by blkid_free_probe() or by the
+ * blkid_probe_set_device() call.
+ *
+ * Returns: a pointer to the newly allocated probe struct or NULL in case of
+ * error.
+ */
+blkid_probe blkid_new_probe_from_filename(const char *filename)
+{
+	int fd = -1;
+	blkid_probe pr = NULL;
+
+	if (!filename)
+		return NULL;
+
+	fd = open(filename, O_RDONLY|O_CLOEXEC);
+	if (fd < 0)
+		return NULL;
+
+	pr = blkid_new_probe();
+	if (!pr)
+		goto err;
+
+	if (blkid_probe_set_device(pr, fd, 0, 0))
+		goto err;
+
+	pr->flags |= BLKID_FL_PRIVATE_FD;
+	return pr;
+err:
+	if (fd >= 0)
+		close(fd);
+	blkid_free_probe(pr);
+	return NULL;
+}
+
+/**
+ * blkid_free_probe:
+ * @pr: probe
+ *
+ * Deallocates the probe struct, buffers and all allocated
+ * data that are associated with this probing control struct.
+ */
+void blkid_free_probe(blkid_probe pr)
+{
+	int i;
+
+	if (!pr)
+		return;
+
+	for (i = 0; i < BLKID_NCHAINS; i++) {
+		struct blkid_chain *ch = &pr->chains[i];
+
+		if (ch->driver->free_data)
+			ch->driver->free_data(pr, ch->data);
+		free(ch->fltr);
+	}
+
+	if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0)
+		close(pr->fd);
+	blkid_probe_reset_buffer(pr);
+	blkid_free_probe(pr->disk_probe);
+
+	DBG(DEBUG_LOWPROBE, printf("free probe %p\n", pr));
+	free(pr);
+}
+
+
+/*
+ * Removes chain values from probing result.
+ */
+void blkid_probe_chain_reset_vals(blkid_probe pr, struct blkid_chain *chn)
+{
+	int nvals = pr->nvals;
+	int i, x;
+
+	for (x = 0, i = 0; i < pr->nvals; i++) {
+		struct blkid_prval *v = &pr->vals[i];
+
+		if (v->chain != chn && x == i) {
+			x++;
+			continue;
+		}
+		if (v->chain == chn) {
+			--nvals;
+			continue;
+		}
+		memcpy(&pr->vals[x++], v, sizeof(struct blkid_prval));
+	}
+	pr->nvals = nvals;
+}
+
+static void blkid_probe_chain_reset_position(struct blkid_chain *chn)
+{
+	if (chn)
+		chn->idx = -1;
+}
+
+/*
+ * Copies chain values from probing result to @vals, the max size of @vals is
+ * @nvals and returns real number of values.
+ */
+int blkid_probe_chain_copy_vals(blkid_probe pr, struct blkid_chain *chn,
+		struct blkid_prval *vals, int nvals)
+{
+	int i, x;
+
+	for (x = 0, i = 0; i < pr->nvals && x < nvals; i++) {
+		struct blkid_prval *v = &pr->vals[i];
+
+		if (v->chain != chn)
+			continue;
+		memcpy(&vals[x++], v, sizeof(struct blkid_prval));
+	}
+	return x;
+}
+
+/*
+ * Appends values from @vals to the probing result
+ */
+void blkid_probe_append_vals(blkid_probe pr, struct blkid_prval *vals, int nvals)
+{
+	int i = 0;
+
+	while (i < nvals && pr->nvals < BLKID_NVALS) {
+		memcpy(&pr->vals[pr->nvals++], &vals[i++],
+				sizeof(struct blkid_prval));
+	}
+}
+
+static void blkid_probe_reset_vals(blkid_probe pr)
+{
+	memset(pr->vals, 0, sizeof(pr->vals));
+	pr->nvals = 0;
+}
+
+struct blkid_chain *blkid_probe_get_chain(blkid_probe pr)
+{
+	return pr->cur_chain;
+}
+
+void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn)
+{
+	int rc, org_prob_flags;
+	struct blkid_chain *org_chn;
+
+	if (!pr || !chn)
+		return NULL;
+
+	/* save the current setting -- the binary API has to be completely
+	 * independent on the current probing status
+	 */
+	org_chn = pr->cur_chain;
+	org_prob_flags = pr->prob_flags;
+
+	pr->cur_chain = chn;
+	pr->prob_flags = 0;
+	chn->binary = TRUE;
+	blkid_probe_chain_reset_position(chn);
+
+	rc = chn->driver->probe(pr, chn);
+
+	chn->binary = FALSE;
+	blkid_probe_chain_reset_position(chn);
+
+	/* restore the original setting
+	 */
+	pr->cur_chain = org_chn;
+	pr->prob_flags = org_prob_flags;
+
+	if (rc != 0)
+		return NULL;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("returning %s binary data\n", chn->driver->name));
+	return chn->data;
+}
+
+
+/**
+ * blkid_reset_probe:
+ * @pr: probe
+ *
+ * Zeroize probing results and resets the current probing (this has impact to
+ * blkid_do_probe() only). This function does not touch probing filters and
+ * keeps assigned device.
+ */
+void blkid_reset_probe(blkid_probe pr)
+{
+	int i;
+
+	if (!pr)
+		return;
+
+	blkid_probe_reset_vals(pr);
+	blkid_probe_set_wiper(pr, 0, 0);
+
+	pr->cur_chain = NULL;
+
+	for (i = 0; i < BLKID_NCHAINS; i++)
+		blkid_probe_chain_reset_position(&pr->chains[i]);
+}
+
+/***
+static int blkid_probe_dump_filter(blkid_probe pr, int chain)
+{
+	struct blkid_chain *chn;
+	int i;
+
+	if (!pr || chain < 0 || chain >= BLKID_NCHAINS)
+		return -1;
+
+	chn = &pr->chains[chain];
+
+	if (!chn->fltr)
+		return -1;
+
+	for (i = 0; i < chn->driver->nidinfos; i++) {
+		const struct blkid_idinfo *id = chn->driver->idinfos[i];
+
+		DBG(DEBUG_LOWPROBE, printf("%d: %s: %s\n",
+			i,
+			id->name,
+			blkid_bmp_get_item(chn->fltr, i)
+				? "disabled" : "enabled <--"));
+	}
+	return 0;
+}
+***/
+
+/*
+ * Returns properly initialized chain filter
+ */
+unsigned long *blkid_probe_get_filter(blkid_probe pr, int chain, int create)
+{
+	struct blkid_chain *chn;
+
+	if (!pr || chain < 0 || chain >= BLKID_NCHAINS)
+		return NULL;
+
+	chn = &pr->chains[chain];
+
+	/* always when you touch the chain filter all indexes are reset and
+	 * probing starts from scratch
+	 */
+	blkid_probe_chain_reset_position(chn);
+	pr->cur_chain = NULL;
+
+	if (!chn->driver->has_fltr || (!chn->fltr && !create))
+		return NULL;
+
+	if (!chn->fltr)
+		chn->fltr = calloc(1, blkid_bmp_nbytes(chn->driver->nidinfos));
+	else
+		memset(chn->fltr, 0, blkid_bmp_nbytes(chn->driver->nidinfos));
+
+	/* blkid_probe_dump_filter(pr, chain); */
+	return chn->fltr;
+}
+
+/*
+ * Generic private functions for filter setting
+ */
+int __blkid_probe_invert_filter(blkid_probe pr, int chain)
+{
+	size_t i;
+	struct blkid_chain *chn;
+
+	if (!pr)
+		return -1;
+
+	chn = &pr->chains[chain];
+
+	if (!chn->driver->has_fltr || !chn->fltr)
+		return -1;
+
+	for (i = 0; i < blkid_bmp_nwords(chn->driver->nidinfos); i++)
+		chn->fltr[i] = ~chn->fltr[i];
+
+	DBG(DEBUG_LOWPROBE, printf("probing filter inverted\n"));
+	/* blkid_probe_dump_filter(pr, chain); */
+	return 0;
+}
+
+int __blkid_probe_reset_filter(blkid_probe pr, int chain)
+{
+	return blkid_probe_get_filter(pr, chain, FALSE) ? 0 : -1;
+}
+
+int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[])
+{
+	unsigned long *fltr;
+	struct blkid_chain *chn;
+	size_t i;
+
+	fltr = blkid_probe_get_filter(pr, chain, TRUE);
+	if (!fltr)
+		return -1;
+
+	chn = &pr->chains[chain];
+
+	for (i = 0; i < chn->driver->nidinfos; i++) {
+		int has = 0;
+		const struct blkid_idinfo *id = chn->driver->idinfos[i];
+		char **n;
+
+		for (n = names; *n; n++) {
+			if (!strcmp(id->name, *n)) {
+				has = 1;
+				break;
+			}
+		}
+		if (flag & BLKID_FLTR_ONLYIN) {
+		       if (!has)
+				blkid_bmp_set_item(fltr, i);
+		} else if (flag & BLKID_FLTR_NOTIN) {
+			if (has)
+				blkid_bmp_set_item(fltr, i);
+		}
+	}
+
+	DBG(DEBUG_LOWPROBE,
+		printf("%s: a new probing type-filter initialized\n",
+		chn->driver->name));
+	/* blkid_probe_dump_filter(pr, chain); */
+	return 0;
+}
+
+unsigned char *blkid_probe_get_buffer(blkid_probe pr,
+				blkid_loff_t off, blkid_loff_t len)
+{
+	struct list_head *p;
+	struct blkid_bufinfo *bf = NULL;
+
+	if (pr->size <= 0)
+		return NULL;
+
+	if (pr->parent &&
+	    pr->parent->devno == pr->devno &&
+	    pr->parent->off <= pr->off &&
+	    pr->parent->off + pr->parent->size >= pr->off + pr->size) {
+		/*
+		 * This is a cloned prober and points to the same area as
+		 * parent. Let's use parent's buffers.
+		 *
+		 * Note that pr->off (and pr->parent->off) is always from the
+		 * beginig of the device.
+		 */
+		return blkid_probe_get_buffer(pr->parent,
+				pr->off + off - pr->parent->off, len);
+	}
+
+	list_for_each(p, &pr->buffers) {
+		struct blkid_bufinfo *x =
+				list_entry(p, struct blkid_bufinfo, bufs);
+
+		if (x->off <= off && off + len <= x->off + x->len) {
+			DBG(DEBUG_LOWPROBE,
+				printf("\treuse buffer: off=%jd len=%jd pr=%p\n",
+							x->off, x->len, pr));
+			bf = x;
+			break;
+		}
+	}
+	if (!bf) {
+		ssize_t ret;
+
+		if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0)
+			return NULL;
+
+		/* allocate info and space for data by why call */
+		bf = calloc(1, sizeof(struct blkid_bufinfo) + len);
+		if (!bf)
+			return NULL;
+
+		bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo);
+		bf->len = len;
+		bf->off = off;
+		INIT_LIST_HEAD(&bf->bufs);
+
+		DBG(DEBUG_LOWPROBE,
+			printf("\tbuffer read: off=%jd len=%jd pr=%p\n",
+				off, len, pr));
+
+		ret = read(pr->fd, bf->data, len);
+		if (ret != (ssize_t) len) {
+			free(bf);
+			return NULL;
+		}
+		list_add_tail(&bf->bufs, &pr->buffers);
+	}
+
+	return off ? bf->data + (off - bf->off) : bf->data;
+}
+
+
+static void blkid_probe_reset_buffer(blkid_probe pr)
+{
+	uint64_t read_ct = 0, len_ct = 0;
+
+	if (!pr || list_empty(&pr->buffers))
+		return;
+
+	DBG(DEBUG_LOWPROBE, printf("reseting probing buffers pr=%p\n", pr));
+
+	while (!list_empty(&pr->buffers)) {
+		struct blkid_bufinfo *bf = list_entry(pr->buffers.next,
+						struct blkid_bufinfo, bufs);
+		read_ct++;
+		len_ct += bf->len;
+		list_del(&bf->bufs);
+		free(bf);
+	}
+
+	DBG(DEBUG_LOWPROBE,
+		printf("buffers summary: %"PRIu64" bytes "
+			"by %"PRIu64" read() call(s)\n",
+			len_ct, read_ct));
+
+	INIT_LIST_HEAD(&pr->buffers);
+}
+
+/*
+ * Small devices need a special care.
+ */
+int blkid_probe_is_tiny(blkid_probe pr)
+{
+	return pr && (pr->flags & BLKID_FL_TINY_DEV);
+}
+
+/*
+ * CDROMs may fail when probed for RAID (last sector problem)
+ */
+int blkid_probe_is_cdrom(blkid_probe pr)
+{
+	return pr && (pr->flags & BLKID_FL_CDROM_DEV);
+}
+
+/**
+ * blkid_probe_set_device:
+ * @pr: probe
+ * @fd: device file descriptor
+ * @off: begin of probing area
+ * @size: size of probing area (zero means whole device/file)
+ *
+ * Assigns the device to probe control struct, resets internal buffers and
+ * resets the current probing.
+ *
+ * Returns: -1 in case of failure, or 0 on success.
+ */
+int blkid_probe_set_device(blkid_probe pr, int fd,
+		blkid_loff_t off, blkid_loff_t size)
+{
+	struct stat sb;
+
+	if (!pr)
+		return -1;
+
+	blkid_reset_probe(pr);
+	blkid_probe_reset_buffer(pr);
+
+	if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0)
+		close(pr->fd);
+
+	pr->flags &= ~BLKID_FL_PRIVATE_FD;
+	pr->flags &= ~BLKID_FL_TINY_DEV;
+	pr->flags &= ~BLKID_FL_CDROM_DEV;
+	pr->prob_flags = 0;
+	pr->fd = fd;
+	pr->off = off;
+	pr->size = 0;
+	pr->devno = 0;
+	pr->disk_devno = 0;
+	pr->mode = 0;
+	pr->blkssz = 0;
+	pr->wipe_off = 0;
+	pr->wipe_size = 0;
+	pr->wipe_chain = NULL;
+
+#if defined(POSIX_FADV_RANDOM) && defined(HAVE_POSIX_FADVISE)
+	/* Disable read-ahead */
+	posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
+#endif
+	if (fstat(fd, &sb))
+		goto err;
+
+	if (!S_ISBLK(sb.st_mode) && !S_ISCHR(sb.st_mode) && !S_ISREG(sb.st_mode))
+		goto err;
+
+	pr->mode = sb.st_mode;
+	if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode))
+		pr->devno = sb.st_rdev;
+
+	if (size)
+		pr->size = size;
+	else {
+		if (S_ISBLK(sb.st_mode)) {
+			if (blkdev_get_size(fd, (unsigned long long *) &pr->size)) {
+				DBG(DEBUG_LOWPROBE, printf(
+					"failed to get device size\n"));
+				goto err;
+			}
+		} else if (S_ISCHR(sb.st_mode))
+			pr->size = 1;		/* UBI devices are char... */
+		else if (S_ISREG(sb.st_mode))
+			pr->size = sb.st_size;	/* regular file */
+
+		if (pr->off > pr->size)
+			goto err;
+
+		/* The probing area cannot be larger than whole device, pr->off
+		 * is offset within the device */
+		pr->size -= pr->off;
+	}
+
+	if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode))
+		pr->flags |= BLKID_FL_TINY_DEV;
+
+#ifdef CDROM_GET_CAPABILITY
+	if (S_ISBLK(sb.st_mode) &&
+	    !blkid_probe_is_tiny(pr) &&
+	    blkid_probe_is_wholedisk(pr) &&
+	    ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0)
+		pr->flags |= BLKID_FL_CDROM_DEV;
+#endif
+
+	DBG(DEBUG_LOWPROBE, printf("ready for low-probing, offset=%jd, size=%jd\n",
+				pr->off, pr->size));
+	DBG(DEBUG_LOWPROBE, printf("whole-disk: %s, regfile: %s\n",
+		blkid_probe_is_wholedisk(pr) ?"YES" : "NO",
+		S_ISREG(pr->mode) ? "YES" : "NO"));
+
+	return 0;
+err:
+	DBG(DEBUG_LOWPROBE,
+		printf("failed to prepare a device for low-probing\n"));
+	return -1;
+
+}
+
+int blkid_probe_get_dimension(blkid_probe pr,
+		blkid_loff_t *off, blkid_loff_t *size)
+{
+	if (!pr)
+		return -1;
+
+	*off = pr->off;
+	*size = pr->size;
+	return 0;
+}
+
+int blkid_probe_set_dimension(blkid_probe pr,
+		blkid_loff_t off, blkid_loff_t size)
+{
+	if (!pr)
+		return -1;
+
+	DBG(DEBUG_LOWPROBE, printf(
+		"changing probing area pr=%p: size=%llu, off=%llu "
+		"-to-> size=%llu, off=%llu\n",
+		pr,
+		(unsigned long long) pr->size,
+		(unsigned long long) pr->off,
+		(unsigned long long) size,
+		(unsigned long long) off));
+
+	pr->off = off;
+	pr->size = size;
+	pr->flags &= ~BLKID_FL_TINY_DEV;
+
+	if (pr->size <= 1440 * 1024 && !S_ISCHR(pr->mode))
+		pr->flags |= BLKID_FL_TINY_DEV;
+
+	blkid_probe_reset_buffer(pr);
+
+	return 0;
+}
+
+int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
+			blkid_loff_t *offset, const struct blkid_idmag **res)
+{
+	const struct blkid_idmag *mag = NULL;
+	blkid_loff_t off = 0;
+
+	if (id)
+		mag = &id->magics[0];
+	if (res)
+		*res = NULL;
+
+	/* try to detect by magic string */
+	while(mag && mag->magic) {
+		unsigned char *buf;
+
+		off = (mag->kboff + (mag->sboff >> 10)) << 10;
+		buf = blkid_probe_get_buffer(pr, off, 1024);
+
+		if (buf && !memcmp(mag->magic,
+				buf + (mag->sboff & 0x3ff), mag->len)) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"\tmagic sboff=%u, kboff=%ld\n",
+				mag->sboff, mag->kboff));
+			if (offset)
+				*offset = off + (mag->sboff & 0x3ff);
+			if (res)
+				*res = mag;
+			return 0;
+		}
+		mag++;
+	}
+
+	if (id && id->magics[0].magic)
+		/* magic string(s) defined, but not found */
+		return 1;
+
+	return 0;
+}
+
+static inline void blkid_probe_start(blkid_probe pr)
+{
+	if (pr) {
+		DBG(DEBUG_LOWPROBE, printf("%p: start probe\n", pr));
+		pr->cur_chain = NULL;
+		pr->prob_flags = 0;
+		blkid_probe_set_wiper(pr, 0, 0);
+	}
+}
+
+static inline void blkid_probe_end(blkid_probe pr)
+{
+	if (pr) {
+		DBG(DEBUG_LOWPROBE, printf("%p: end probe\n", pr));
+		pr->cur_chain = NULL;
+		pr->prob_flags = 0;
+		blkid_probe_set_wiper(pr, 0, 0);
+	}
+}
+
+/**
+ * blkid_do_probe:
+ * @pr: prober
+ *
+ * Calls probing functions in all enabled chains. The superblocks chain is
+ * enabled by default. The blkid_do_probe() stores result from only one
+ * probing function. It's necessary to call this routine in a loop to get
+ * results from all probing functions in all chains. The probing is reset
+ * by blkid_reset_probe() or by filter functions.
+ *
+ * This is string-based NAME=value interface only.
+ *
+ * <example>
+ *   <title>basic case - use the first result only</title>
+ *   <programlisting>
+ *
+ *	if (blkid_do_probe(pr) == 0) {
+ *		int nvals = blkid_probe_numof_values(pr);
+ *		for (n = 0; n < nvals; n++) {
+ *			if (blkid_probe_get_value(pr, n, &name, &data, &len) == 0)
+ *				printf("%s = %s\n", name, data);
+ *		}
+ *	}
+ *  </programlisting>
+ * </example>
+ *
+ * <example>
+ *   <title>advanced case - probe for all signatures</title>
+ *   <programlisting>
+ *
+ *	while (blkid_do_probe(pr) == 0) {
+ *		int nvals = blkid_probe_numof_values(pr);
+ *		...
+ *	}
+ *  </programlisting>
+ * </example>
+ *
+ * See also blkid_reset_probe().
+ *
+ * Returns: 0 on success, 1 when probing is done and -1 in case of error.
+ */
+int blkid_do_probe(blkid_probe pr)
+{
+	int rc = 1;
+
+	if (!pr)
+		return -1;
+
+	do {
+		struct blkid_chain *chn = pr->cur_chain;
+
+		if (!chn) {
+			blkid_probe_start(pr);
+			chn = pr->cur_chain = &pr->chains[0];
+		}
+		/* we go to the next chain only when the previous probing
+		 * result was nothing (rc == 1) and when the current chain is
+		 * disabled or we are at end of the current chain (chain->idx +
+		 * 1 == sizeof chain) or the current chain bailed out right at
+		 * the start (chain->idx == -1)
+		 */
+		else if (rc == 1 && (chn->enabled == FALSE ||
+				     chn->idx + 1 == (int) chn->driver->nidinfos ||
+				     chn->idx == -1)) {
+
+			size_t idx = chn->driver->id + 1;
+
+			if (idx < BLKID_NCHAINS)
+				chn = pr->cur_chain = &pr->chains[idx];
+			else {
+				blkid_probe_end(pr);
+				return 1;	/* all chains already probed */
+			}
+		}
+
+		chn->binary = FALSE;		/* for sure... */
+
+		DBG(DEBUG_LOWPROBE, printf("chain probe %s %s (idx=%d)\n",
+				chn->driver->name,
+				chn->enabled? "ENABLED" : "DISABLED",
+				chn->idx));
+
+		if (!chn->enabled)
+			continue;
+
+		/* rc: -1 = error, 0 = success, 1 = no result */
+		rc = chn->driver->probe(pr, chn);
+
+	} while (rc == 1);
+
+	return rc;
+}
+
+/**
+ * blkid_do_wipe:
+ * @pr: prober
+ * @dryrun: if TRUE then don't touch the device.
+ *
+ * This function erases the current signature detected by @pr. The @pr has to
+ * be open in O_RDWR mode, BLKID_SUBLKS_MAGIC or/and BLKID_PARTS_MAGIC flags
+ * has to be enabled.
+ *
+ * After successful signature removing the @pr prober will be moved one step
+ * back and the next blkid_do_probe() call will again call previously called
+ * probing function.
+ *
+ *  <example>
+ *  <title>wipe all filesystems or raids from the device</title>
+ *   <programlisting>
+ *      fd = open(devname, O_RDWR|O_CLOEXEC);
+ *      blkid_probe_set_device(pr, fd, 0, 0);
+ *
+ *      blkid_probe_enable_superblocks(pr, 1);
+ *      blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC);
+ *
+ *	while (blkid_do_probe(pr) == 0)
+ *		blkid_do_wipe(pr, FALSE);
+ *  </programlisting>
+ * </example>
+ *
+ * See also blkid_probe_step_back() if you cannot use this build-in wipe
+ * function, but you want to use libblkid probing as a source for wiping.
+ *
+ * Returns: 0 on success, and -1 in case of error.
+ */
+int blkid_do_wipe(blkid_probe pr, int dryrun)
+{
+	const char *off = NULL;
+	size_t len = 0;
+	loff_t offset, l;
+	char buf[BUFSIZ];
+	int fd, rc = 0;
+	struct blkid_chain *chn;
+
+	if (!pr)
+		return -1;
+
+	chn = pr->cur_chain;
+	if (!chn)
+		return -1;
+
+	switch (chn->driver->id) {
+	case BLKID_CHAIN_SUBLKS:
+		rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
+		if (!rc)
+			rc = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
+		break;
+	case BLKID_CHAIN_PARTS:
+		rc = blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL);
+		if (!rc)
+			rc = blkid_probe_lookup_value(pr, "PTMAGIC", NULL, &len);
+		break;
+	default:
+		return 0;
+	}
+
+	if (rc || len == 0 || off == NULL)
+		return 0;
+
+	offset = strtoll(off, NULL, 10);
+	fd = blkid_probe_get_fd(pr);
+	if (fd < 0)
+		return -1;
+
+	if (len > sizeof(buf))
+		len = sizeof(buf);
+
+	DBG(DEBUG_LOWPROBE, printf(
+	    "do_wipe [offset=0x%jx, len=%zd, chain=%s, idx=%d, dryrun=%s]\n",
+	    offset, len, chn->driver->name, chn->idx, dryrun ? "yes" : "not"));
+
+	l = lseek(fd, offset, SEEK_SET);
+	if (l == (off_t) -1)
+		return -1;
+
+	memset(buf, 0, len);
+
+	if (!dryrun && len) {
+		if (write_all(fd, buf, len))
+			return -1;
+		fsync(fd);
+		return blkid_probe_step_back(pr);
+	}
+
+	return 0;
+}
+
+/**
+ * blkid_probe_step_back():
+ * @pr: prober
+ *
+ * This function move pointer to the probing chain one step back -- it means
+ * that the previously used probing function will be called again in the next
+ * blkid_do_probe() call.
+ *
+ * This is necessary for example if you erase or modify on-disk superblock
+ * according to the current libblkid probing result.
+ *
+ * <example>
+ *  <title>wipe all superblock, but use libblkid only for probing</title>
+ *  <programlisting>
+ *      pr = blkid_new_probe_from_filename(devname);
+ *
+ *      blkid_probe_enable_superblocks(pr, 1);
+ *      blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC);
+ *
+ *	while (blkid_do_probe(pr) == 0) {
+ *		const char *ostr = NULL;
+ *		size_t len = 0;
+ *
+ *		// superblocks
+ *		if (blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &ostr, NULL) == 0)
+ *			blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
+ *
+ *		// partition tables
+ *		if (len == 0 && blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &ostr, NULL) == 0)
+ *			blkid_probe_lookup_value(pr, "PTMAGIC", NULL, &len);
+ *
+ *		if (!len || !str)
+ *			continue;
+ *
+ *		// convert ostr to the real offset by off = strtoll(ostr, NULL, 10);
+ *              // use your stuff to errase @len bytes at the @off
+ *              ....
+ *
+ *		// retry the last probing to check for backup superblocks ..etc.
+ *              blkid_probe_step_back(pr);
+ *	}
+ *  </programlisting>
+ * </example>
+ *
+ * Returns: 0 on success, and -1 in case of error.
+ */
+int blkid_probe_step_back(blkid_probe pr)
+{
+	struct blkid_chain *chn;
+
+	if (!pr)
+		return -1;
+
+	chn = pr->cur_chain;
+	if (!chn)
+		return -1;
+
+	blkid_probe_reset_buffer(pr);
+
+	if (chn->idx >= 0) {
+		chn->idx--;
+		DBG(DEBUG_LOWPROBE,
+			printf("step back: moving %s chain index to %d\n",
+			chn->driver->name,
+			chn->idx));
+	}
+
+	if (chn->idx == -1) {
+		/* blkid_do_probe() goes to the next chain if the index
+		 * of the current chain is -1, so we have to set the
+		 * chain pointer to the previous chain.
+		 */
+		size_t idx = chn->driver->id > 0 ? chn->driver->id - 1 : 0;
+
+		DBG(DEBUG_LOWPROBE, printf("step back: moving to previous chain\n"));
+
+		if (idx > 0)
+			pr->cur_chain = &pr->chains[idx];
+		else if (idx == 0)
+			pr->cur_chain = NULL;
+	}
+
+	return 0;
+}
+
+/**
+ * blkid_do_safeprobe:
+ * @pr: prober
+ *
+ * This function gathers probing results from all enabled chains and checks
+ * for ambivalent results (e.g. more filesystems on the device).
+ *
+ * This is string-based NAME=value interface only.
+ *
+ * Note about suberblocks chain -- the function does not check for filesystems
+ * when a RAID signature is detected.  The function also does not check for
+ * collision between RAIDs. The first detected RAID is returned. The function
+ * checks for collision between partition table and RAID signature -- it's
+ * recommended to enable partitions chain together with superblocks chain.
+ *
+ * Returns: 0 on success, 1 if nothing is detected, -2 if ambivalen result is
+ * detected and -1 on case of error.
+ */
+int blkid_do_safeprobe(blkid_probe pr)
+{
+	int i, count = 0, rc = 0;
+
+	if (!pr)
+		return -1;
+
+	blkid_probe_start(pr);
+
+	pr->prob_flags |= BLKID_PROBE_FL_IGNORE_BACKUP;
+
+	for (i = 0; i < BLKID_NCHAINS; i++) {
+		struct blkid_chain *chn;
+
+		chn = pr->cur_chain = &pr->chains[i];
+		chn->binary = FALSE;		/* for sure... */
+
+		DBG(DEBUG_LOWPROBE, printf("chain safeprobe %s %s\n",
+				chn->driver->name,
+				chn->enabled? "ENABLED" : "DISABLED"));
+
+		if (!chn->enabled)
+			continue;
+
+		blkid_probe_chain_reset_position(chn);
+
+		rc = chn->driver->safeprobe(pr, chn);
+
+		blkid_probe_chain_reset_position(chn);
+
+		/* rc: -2 ambivalent, -1 = error, 0 = success, 1 = no result */
+		if (rc < 0)
+			goto done;	/* error */
+		if (rc == 0)
+			count++;	/* success */
+	}
+
+done:
+	blkid_probe_end(pr);
+	if (rc < 0)
+		return rc;
+	return count ? 0 : 1;
+}
+
+/**
+ * blkid_do_fullprobe:
+ * @pr: prober
+ *
+ * This function gathers probing results from all enabled chains. Same as
+ * blkid_do_safeprobe() but does not check for collision between probing
+ * result.
+ *
+ * This is string-based NAME=value interface only.
+ *
+ * Returns: 0 on success, 1 if nothing is detected or -1 on case of error.
+ */
+int blkid_do_fullprobe(blkid_probe pr)
+{
+	int i, count = 0, rc = 0;
+
+	if (!pr)
+		return -1;
+
+	blkid_probe_start(pr);
+
+	for (i = 0; i < BLKID_NCHAINS; i++) {
+		struct blkid_chain *chn;
+
+		chn = pr->cur_chain = &pr->chains[i];
+		chn->binary = FALSE;		/* for sure... */
+
+		DBG(DEBUG_LOWPROBE, printf("chain fullprobe %s: %s\n",
+				chn->driver->name,
+				chn->enabled? "ENABLED" : "DISABLED"));
+
+		if (!chn->enabled)
+			continue;
+
+		blkid_probe_chain_reset_position(chn);
+
+		rc = chn->driver->probe(pr, chn);
+
+		blkid_probe_chain_reset_position(chn);
+
+		/* rc: -1 = error, 0 = success, 1 = no result */
+		if (rc < 0)
+			goto done;	/* error */
+		if (rc == 0)
+			count++;	/* success */
+	}
+
+done:
+	blkid_probe_end(pr);
+	if (rc < 0)
+		return rc;
+	return count ? 0 : 1;
+}
+
+/* same sa blkid_probe_get_buffer() but works with 512-sectors */
+unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
+{
+	return pr ? blkid_probe_get_buffer(pr,
+			((blkid_loff_t) sector) << 9, 0x200) : NULL;
+}
+
+struct blkid_prval *blkid_probe_assign_value(
+			blkid_probe pr, const char *name)
+{
+	struct blkid_prval *v;
+
+	if (!name)
+		return NULL;
+	if (pr->nvals >= BLKID_NVALS)
+		return NULL;
+
+	v = &pr->vals[pr->nvals];
+	v->name = name;
+	v->chain = pr->cur_chain;
+	pr->nvals++;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("assigning %s [%s]\n", name, v->chain->driver->name));
+	return v;
+}
+
+int blkid_probe_reset_last_value(blkid_probe pr)
+{
+	struct blkid_prval *v;
+
+	if (pr == NULL || pr->nvals == 0)
+		return -1;
+
+	v = &pr->vals[pr->nvals - 1];
+
+	DBG(DEBUG_LOWPROBE,
+		printf("un-assigning %s [%s]\n", v->name, v->chain->driver->name));
+
+	memset(v, 0, sizeof(struct blkid_prval));
+	pr->nvals--;
+
+	return 0;
+
+}
+
+int blkid_probe_set_value(blkid_probe pr, const char *name,
+		unsigned char *data, size_t len)
+{
+	struct blkid_prval *v;
+
+	if (len > BLKID_PROBVAL_BUFSIZ)
+		len = BLKID_PROBVAL_BUFSIZ;
+
+	v = blkid_probe_assign_value(pr, name);
+	if (!v)
+		return -1;
+
+	memcpy(v->data, data, len);
+	v->len = len;
+	return 0;
+}
+
+int blkid_probe_vsprintf_value(blkid_probe pr, const char *name,
+		const char *fmt, va_list ap)
+{
+	struct blkid_prval *v;
+	ssize_t len;
+
+	v = blkid_probe_assign_value(pr, name);
+	if (!v)
+		return -1;
+
+	len = vsnprintf((char *) v->data, sizeof(v->data), fmt, ap);
+
+	if (len <= 0 || (size_t) len >= sizeof(v->data)) {
+		blkid_probe_reset_last_value(pr);
+		return -1;
+	}
+	v->len = len + 1;
+	return 0;
+}
+
+int blkid_probe_sprintf_value(blkid_probe pr, const char *name,
+		const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+
+	va_start(ap, fmt);
+	rc = blkid_probe_vsprintf_value(pr, name, fmt, ap);
+	va_end(ap);
+
+	return rc;
+}
+
+int blkid_probe_set_magic(blkid_probe pr, blkid_loff_t offset,
+			size_t len, unsigned char *magic)
+{
+	int rc = 0;
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	if (!chn || !magic || !len || chn->binary)
+		return 0;
+
+	switch (chn->driver->id) {
+	case BLKID_CHAIN_SUBLKS:
+		if (!(chn->flags & BLKID_SUBLKS_MAGIC))
+			return 0;
+		rc = blkid_probe_set_value(pr, "SBMAGIC", magic, len);
+		if (!rc)
+			rc = blkid_probe_sprintf_value(pr,
+					"SBMAGIC_OFFSET", "%llu", (unsigned long long)offset);
+		break;
+	case BLKID_CHAIN_PARTS:
+		if (!(chn->flags & BLKID_PARTS_MAGIC))
+			return 0;
+		rc = blkid_probe_set_value(pr, "PTMAGIC", magic, len);
+		if (!rc)
+			rc = blkid_probe_sprintf_value(pr,
+					"PTMAGIC_OFFSET", "%llu", (unsigned long long)offset);
+		break;
+	default:
+		break;
+	}
+
+	return rc;
+}
+
+/**
+ * blkid_probe_get_devno:
+ * @pr: probe
+ *
+ * Returns: block device number, or 0 for regular files.
+ */
+dev_t blkid_probe_get_devno(blkid_probe pr)
+{
+	return pr->devno;
+}
+
+/**
+ * blkid_probe_get_wholedisk_devno:
+ * @pr: probe
+ *
+ * Returns: device number of the wholedisk, or 0 for regular files.
+ */
+dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr)
+{
+	if (!pr->disk_devno) {
+		dev_t devno, disk_devno = 0;
+
+		devno = blkid_probe_get_devno(pr);
+		if (!devno)
+			return 0;
+
+		 if (blkid_devno_to_wholedisk(devno, NULL, 0, &disk_devno) == 0)
+			pr->disk_devno = disk_devno;
+	}
+	return pr->disk_devno;
+}
+
+/**
+ * blkid_probe_is_wholedisk:
+ * @pr: probe
+ *
+ * Returns: 1 if the device is whole-disk or 0.
+ */
+int blkid_probe_is_wholedisk(blkid_probe pr)
+{
+	dev_t devno, disk_devno;
+
+	devno = blkid_probe_get_devno(pr);
+	if (!devno)
+		return 0;
+
+	disk_devno = blkid_probe_get_wholedisk_devno(pr);
+	if (!disk_devno)
+		return 0;
+
+	return devno == disk_devno;
+}
+
+blkid_probe blkid_probe_get_wholedisk_probe(blkid_probe pr)
+{
+	dev_t disk;
+
+	if (blkid_probe_is_wholedisk(pr))
+		return NULL;			/* this is not partition */
+
+	if (pr->parent)
+		/* this is cloned blkid_probe, use parent's stuff */
+		return blkid_probe_get_wholedisk_probe(pr->parent);
+
+	disk = blkid_probe_get_wholedisk_devno(pr);
+
+	if (pr->disk_probe && pr->disk_probe->devno != disk) {
+		/* we have disk prober, but for another disk... close it */
+		blkid_free_probe(pr->disk_probe);
+		pr->disk_probe = NULL;
+	}
+
+	if (!pr->disk_probe) {
+		/* Open a new disk prober */
+		char *disk_path = blkid_devno_to_devname(disk);
+
+		if (!disk_path)
+			return NULL;
+
+		DBG(DEBUG_LOWPROBE, printf("allocate a wholedisk probe\n"));
+
+		pr->disk_probe = blkid_new_probe_from_filename(disk_path);
+
+		free(disk_path);
+
+		if (!pr->disk_probe)
+			return NULL;	/* ENOMEM? */
+	}
+
+	return pr->disk_probe;
+}
+
+/**
+ * blkid_probe_get_size:
+ * @pr: probe
+ *
+ * This function returns size of probing area as defined by blkid_probe_set_device().
+ * If the size of the probing area is unrestricted then this function returns
+ * the real size of device. See also blkid_get_dev_size().
+ *
+ * Returns: size in bytes or -1 in case of error.
+ */
+blkid_loff_t blkid_probe_get_size(blkid_probe pr)
+{
+	return pr ? pr->size : -1;
+}
+
+/**
+ * blkid_probe_get_offset:
+ * @pr: probe
+ *
+ * This function returns offset of probing area as defined by blkid_probe_set_device().
+ *
+ * Returns: offset in bytes or -1 in case of error.
+ */
+blkid_loff_t blkid_probe_get_offset(blkid_probe pr)
+{
+	return pr ? pr->off : -1;
+}
+
+/**
+ * blkid_probe_get_fd:
+ * @pr: probe
+ *
+ * Returns: file descriptor for assigned device/file or -1 in case of error.
+ */
+int blkid_probe_get_fd(blkid_probe pr)
+{
+	return pr ? pr->fd : -1;
+}
+
+/**
+ * blkid_probe_get_sectorsize:
+ * @pr: probe or NULL (for NULL returns 512)
+ *
+ * Returns: block device logical sector size (BLKSSZGET ioctl, default 512).
+ */
+unsigned int blkid_probe_get_sectorsize(blkid_probe pr)
+{
+	if (!pr)
+		return DEFAULT_SECTOR_SIZE;  /*... and good luck! */
+
+	if (pr->blkssz)
+		return pr->blkssz;
+
+	if (S_ISBLK(pr->mode) &&
+	    blkdev_get_sector_size(pr->fd, (int *) &pr->blkssz) == 0)
+		return pr->blkssz;
+
+	pr->blkssz = DEFAULT_SECTOR_SIZE;
+	return pr->blkssz;
+}
+
+/**
+ * blkid_probe_get_sectors:
+ * @pr: probe
+ *
+ * Returns: 512-byte sector count or -1 in case of error.
+ */
+blkid_loff_t blkid_probe_get_sectors(blkid_probe pr)
+{
+	return pr ? pr->size >> 9 : -1;
+}
+
+/**
+ * blkid_probe_numof_values:
+ * @pr: probe
+ *
+ * Returns: number of values in probing result or -1 in case of error.
+ */
+int blkid_probe_numof_values(blkid_probe pr)
+{
+	if (!pr)
+		return -1;
+	return pr->nvals;
+}
+
+/**
+ * blkid_probe_get_value:
+ * @pr: probe
+ * @num: wanted value in range 0..N, where N is blkid_probe_numof_values() - 1
+ * @name: pointer to return value name or NULL
+ * @data: pointer to return value data or NULL
+ * @len: pointer to return value length or NULL
+ *
+ * Note, the @len returns length of the @data, including the terminating
+ * '\0' character.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_get_value(blkid_probe pr, int num, const char **name,
+			const char **data, size_t *len)
+{
+	struct blkid_prval *v = __blkid_probe_get_value(pr, num);
+
+	if (!v)
+		return -1;
+	if (name)
+		*name = v->name;
+	if (data)
+		*data = (char *) v->data;
+	if (len)
+		*len = v->len;
+
+	DBG(DEBUG_LOWPROBE, printf("returning %s value\n", v->name));
+	return 0;
+}
+
+/**
+ * blkid_probe_lookup_value:
+ * @pr: probe
+ * @name: name of value
+ * @data: pointer to return value data or NULL
+ * @len: pointer to return value length or NULL
+ *
+ * Note, the @len returns length of the @data, including the terminating
+ * '\0' character.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_lookup_value(blkid_probe pr, const char *name,
+			const char **data, size_t *len)
+{
+	struct blkid_prval *v = __blkid_probe_lookup_value(pr, name);
+
+	if (!v)
+		return -1;
+	if (data)
+		*data = (char *) v->data;
+	if (len)
+		*len = v->len;
+	return 0;
+}
+
+/**
+ * blkid_probe_has_value:
+ * @pr: probe
+ * @name: name of value
+ *
+ * Returns: 1 if value exist in probing result, otherwise 0.
+ */
+int blkid_probe_has_value(blkid_probe pr, const char *name)
+{
+	if (blkid_probe_lookup_value(pr, name, NULL, NULL) == 0)
+		return 1;
+	return 0;
+}
+
+struct blkid_prval *__blkid_probe_get_value(blkid_probe pr, int num)
+{
+	if (!pr || num < 0 || num >= pr->nvals)
+		return NULL;
+
+	return &pr->vals[num];
+}
+
+struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name)
+{
+	int i;
+
+	if (!pr || !pr->nvals || !name)
+		return NULL;
+
+	for (i = 0; i < pr->nvals; i++) {
+		struct blkid_prval *v = &pr->vals[i];
+
+		if (v->name && strcmp(name, v->name) == 0) {
+			DBG(DEBUG_LOWPROBE, printf("returning %s value\n", v->name));
+			return v;
+		}
+	}
+	return NULL;
+}
+
+
+/* converts DCE UUID (uuid[16]) to human readable string
+ * - the @len should be always 37 */
+#ifdef HAVE_LIBUUID
+void blkid_unparse_uuid(const unsigned char *uuid, char *str,
+			size_t len __attribute__((__unused__)))
+{
+	uuid_unparse(uuid, str);
+}
+#else
+void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len)
+{
+	snprintf(str, len,
+		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+		uuid[0], uuid[1], uuid[2], uuid[3],
+		uuid[4], uuid[5],
+		uuid[6], uuid[7],
+		uuid[8], uuid[9],
+		uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],uuid[15]);
+}
+#endif
+
+
+/* Removes whitespace from the right-hand side of a string (trailing
+ * whitespace).
+ *
+ * Returns size of the new string (without \0).
+ */
+size_t blkid_rtrim_whitespace(unsigned char *str)
+{
+	size_t i = strlen((char *) str);
+
+	while (i--) {
+		if (!isspace(str[i]))
+			break;
+	}
+	str[++i] = '\0';
+	return i;
+}
+
+/* Removes whitespace from the left-hand side of a string.
+ *
+ * Returns size of the new string (without \0).
+ */
+size_t blkid_ltrim_whitespace(unsigned char *str)
+{
+	size_t len;
+	unsigned char *p;
+
+	for (p = str; p && isspace(*p); p++);
+
+	len = strlen((char *) p);
+
+	if (len && p > str)
+		memmove(str, p, len + 1);
+
+	return len;
+}
+/*
+ * Some mkfs-like utils wipe some parts (usually begin) of the device.
+ * For example LVM (pvcreate) or mkswap(8). This information could be used
+ * for later resolution to conflicts between superblocks.
+ *
+ * For example we found valid LVM superblock, LVM wipes 8KiB at the begin of
+ * the device. If we found another signature (for example MBR) within the
+ * wiped area then the signature has been added later and LVM superblock
+ * should be ignore.
+ *
+ * Note that this heuristic is not 100% reliable, for example "pvcreate --zero
+ * n" allows to keep the begin of the device unmodified. It's probably better
+ * to use this heuristic for conflicts between superblocks and partition tables
+ * than for conflicts between filesystem superblocks -- existence of unwanted
+ * partition table is very unusual, because PT is pretty visible (parsed and
+ * interpreted by kernel).
+ *
+ * Note that we usually expect only one signature on the device, it means that
+ * we have to remember only one wiped area from previously successfully
+ * detected signature.
+ *
+ * blkid_probe_set_wiper() -- defines wiped area (e.g. LVM)
+ * blkid_probe_use_wiper() -- try to use area (e.g. MBR)
+ *
+ * Note that there is not relation between _wiper and blkid_to_wipe().
+ *
+ */
+void blkid_probe_set_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size)
+{
+	struct blkid_chain *chn;
+
+	if (!pr)
+		return;
+
+	if (!size) {
+		DBG(DEBUG_LOWPROBE, printf("zeroize wiper\n"));
+		pr->wipe_size = pr->wipe_off = 0;
+		pr->wipe_chain = NULL;
+		return;
+	}
+
+	chn = pr->cur_chain;
+
+	if (!chn || !chn->driver ||
+	    chn->idx < 0 || (size_t) chn->idx >= chn->driver->nidinfos)
+		return;
+
+	pr->wipe_size = size;
+	pr->wipe_off = off;
+	pr->wipe_chain = chn;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("wiper set to %s::%s off=%jd size=%jd\n",
+			chn->driver->name,
+			chn->driver->idinfos[chn->idx]->name,
+			pr->wipe_off, pr->wipe_size));
+	return;
+}
+
+/*
+ * Returns 1 if the <@off,@size> area was wiped
+ */
+int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn,
+		     blkid_loff_t off, blkid_loff_t size)
+{
+	if (!pr || !size)
+		return 0;
+
+	if (pr->wipe_off <= off && off + size <= pr->wipe_off + pr->wipe_size) {
+		if (chn)
+			*chn = pr->wipe_chain;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ *  Try to use any area -- if the area has been previously wiped then the
+ *  previous probing result should be ignored (reseted).
+ */
+void blkid_probe_use_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size)
+{
+	struct blkid_chain *chn = NULL;
+
+	if (blkid_probe_is_wiped(pr, &chn, off, size) && chn) {
+		DBG(DEBUG_LOWPROBE, printf("previously wiped area modified "
+				       " -- ignore previous results\n"));
+		blkid_probe_set_wiper(pr, 0, 0);
+		blkid_probe_chain_reset_vals(pr, chn);
+	}
+}
+
+int blkid_probe_ignore_backup(blkid_probe pr)
+{
+	return pr && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_BACKUP);
+}
diff --git a/libblkid/procutils.c b/libblkid/procutils.c
new file mode 100644
index 0000000..52e9ee3
--- /dev/null
+++ b/libblkid/procutils.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
+ *
+ * procutils.c: General purpose procfs parsing utilities
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "procutils.h"
+#include "c.h"
+
+/*
+ * @pid: process ID for which we want to obtain the threads group
+ *
+ * Returns: newly allocated tasks structure
+ */
+struct proc_tasks *proc_open_tasks(pid_t pid)
+{
+	struct proc_tasks *tasks;
+	char path[PATH_MAX];
+
+	sprintf(path, "/proc/%d/task/", pid);
+
+	tasks = malloc(sizeof(struct proc_tasks));
+	if (tasks) {
+		tasks->dir = opendir(path);
+		if (tasks->dir)
+			return tasks;
+	}
+
+	free(tasks);
+	return NULL;
+}
+
+/*
+ * @tasks: allocated tasks structure
+ *
+ * Returns: nothing
+ */
+void proc_close_tasks(struct proc_tasks *tasks)
+{
+	if (tasks && tasks->dir)
+		closedir(tasks->dir);
+	free(tasks);
+}
+
+/*
+ * @tasks: allocated task structure
+ * @tid: [output] one of the thread IDs belonging to the thread group
+ *        If when an error occurs, it is set to 0.
+ *
+ * Returns: 0 on success, 1 on end, -1 on failure or no more threads
+ */
+int proc_next_tid(struct proc_tasks *tasks, pid_t *tid)
+{
+	struct dirent *d;
+	char *end;
+
+	if (!tasks || !tid)
+		return -1;
+
+	*tid = 0;
+	errno = 0;
+
+	do {
+		d = readdir(tasks->dir);
+		if (!d)
+			return errno ? -1 : 1;		/* error or end-of-dir */
+
+		if (!isdigit((unsigned char) *d->d_name))
+			continue;
+
+		*tid = (pid_t) strtol(d->d_name, &end, 10);
+		if (errno || d->d_name == end || (end && *end))
+			return -1;
+
+	} while (!*tid);
+
+	return 0;
+}
+
+#ifdef TEST_PROGRAM
+
+int main(int argc, char *argv[])
+{
+	pid_t tid, pid;
+	struct proc_tasks *ts;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s <pid>\n", argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	pid = strtol(argv[1], (char **) NULL, 10);
+	printf("PID=%d, TIDs:", pid);
+
+	ts = proc_open_tasks(pid);
+	if (!ts)
+		err(EXIT_FAILURE, "open list of tasks failed");
+
+	while (proc_next_tid(ts, &tid) == 0)
+		printf(" %d", tid);
+
+	printf("\n");
+        proc_close_tasks(ts);
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/procutils.h b/libblkid/procutils.h
new file mode 100644
index 0000000..ca7087a
--- /dev/null
+++ b/libblkid/procutils.h
@@ -0,0 +1,14 @@
+#ifndef UTIL_LINUX_PROCUTILS
+#define UTIL_LINUX_PROCUTILS
+
+#include <dirent.h>
+
+struct proc_tasks {
+	DIR *dir;
+};
+
+extern struct proc_tasks *proc_open_tasks(pid_t pid);
+extern void proc_close_tasks(struct proc_tasks *tasks);
+extern int proc_next_tid(struct proc_tasks *tasks, pid_t *tid);
+
+#endif /* UTIL_LINUX_PROCUTILS */
diff --git a/libblkid/promise_raid.c b/libblkid/promise_raid.c
new file mode 100644
index 0000000..01e4e37
--- /dev/null
+++ b/libblkid/promise_raid.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct promise_metadata {
+	uint8_t	sig[24];
+};
+
+#define PDC_CONFIG_OFF		0x1200
+#define PDC_SIGNATURE		"Promise Technology, Inc."
+
+static int probe_pdcraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	unsigned int i;
+	static unsigned int sectors[] = {
+	  63, 255, 256, 16, 399, 591, 675, 735, 911, 974, 991, 951, 3087, 0
+	};
+
+	if (pr->size < 0x40000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	for (i = 0; sectors[i] != 0; i++) {
+		uint64_t off;
+		struct promise_metadata *pdc;
+
+		off = ((pr->size / 0x200) - sectors[i]) * 0x200;
+		pdc = (struct promise_metadata *)
+				blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct promise_metadata));
+		if (!pdc)
+			return -1;
+
+		if (memcmp(pdc->sig, PDC_SIGNATURE,
+				sizeof(PDC_SIGNATURE) - 1) == 0) {
+
+			if (blkid_probe_set_magic(pr, off, sizeof(pdc->sig),
+						(unsigned char *) pdc->sig))
+				return -1;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+const struct blkid_idinfo pdcraid_idinfo = {
+	.name		= "promise_fasttrack_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_pdcraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/randutils.c b/libblkid/randutils.c
new file mode 100644
index 0000000..80893d3
--- /dev/null
+++ b/libblkid/randutils.c
@@ -0,0 +1,125 @@
+/*
+ * General purpose random utilities
+ *
+ * Based on libuuid code.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <sys/syscall.h>
+
+#include "randutils.h"
+
+#ifdef HAVE_TLS
+#define THREAD_LOCAL static __thread
+#else
+#define THREAD_LOCAL static
+#endif
+
+#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
+#define DO_JRAND_MIX
+THREAD_LOCAL unsigned short ul_jrand_seed[3];
+#endif
+
+int random_get_fd(void)
+{
+	int i, fd;
+	struct timeval	tv;
+
+	gettimeofday(&tv, 0);
+	fd = open("/dev/urandom", O_RDONLY);
+	if (fd == -1)
+		fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+	if (fd >= 0) {
+		i = fcntl(fd, F_GETFD);
+		if (i >= 0)
+			fcntl(fd, F_SETFD, i | FD_CLOEXEC);
+	}
+	srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+
+#ifdef DO_JRAND_MIX
+	ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
+	ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
+	ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
+#endif
+	/* Crank the random number generator a few times */
+	gettimeofday(&tv, 0);
+	for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+		rand();
+	return fd;
+}
+
+
+/*
+ * Generate a stream of random nbytes into buf.
+ * Use /dev/urandom if possible, and if not,
+ * use glibc pseudo-random functions.
+ */
+void random_get_bytes(void *buf, size_t nbytes)
+{
+	size_t i, n = nbytes;
+	int fd = random_get_fd();
+	int lose_counter = 0;
+	unsigned char *cp = (unsigned char *) buf;
+
+	if (fd >= 0) {
+		while (n > 0) {
+			ssize_t x = read(fd, cp, n);
+			if (x <= 0) {
+				if (lose_counter++ > 16)
+					break;
+				continue;
+			}
+			n -= x;
+			cp += x;
+			lose_counter = 0;
+		}
+
+		close(fd);
+	}
+
+	/*
+	 * We do this all the time, but this is the only source of
+	 * randomness if /dev/random/urandom is out to lunch.
+	 */
+	for (cp = buf, i = 0; i < nbytes; i++)
+		*cp++ ^= (rand() >> 7) & 0xFF;
+
+#ifdef DO_JRAND_MIX
+	{
+		unsigned short tmp_seed[3];
+
+		memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
+		ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
+		for (cp = buf, i = 0; i < nbytes; i++)
+			*cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
+		memcpy(ul_jrand_seed, tmp_seed,
+		       sizeof(ul_jrand_seed)-sizeof(unsigned short));
+	}
+#endif
+
+	return;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc __attribute__ ((__unused__)),
+         char *argv[] __attribute__ ((__unused__)))
+{
+	unsigned int v, i;
+
+	/* generate and print 10 random numbers */
+	for (i = 0; i < 10; i++) {
+		random_get_bytes(&v, sizeof(v));
+		printf("%d\n", v);
+	}
+
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/randutils.h b/libblkid/randutils.h
new file mode 100644
index 0000000..dec5e35
--- /dev/null
+++ b/libblkid/randutils.h
@@ -0,0 +1,12 @@
+#ifndef UTIL_LINUX_RANDUTILS
+#define UTIL_LINUX_RANDUTILS
+
+#ifdef HAVE_SRANDOM
+#define srand(x)	srandom(x)
+#define rand()		random()
+#endif
+
+extern int random_get_fd(void);
+extern void random_get_bytes(void *buf, size_t nbytes);
+
+#endif
diff --git a/libblkid/read.c b/libblkid/read.c
new file mode 100644
index 0000000..8914cad
--- /dev/null
+++ b/libblkid/read.c
@@ -0,0 +1,500 @@
+/*
+ * read.c - read the blkid cache from disk, to avoid scanning all devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Y. Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "blkidP.h"
+
+#ifdef HAVE_STDLIB_H
+# ifndef _XOPEN_SOURCE
+#  define _XOPEN_SOURCE 600	/* for inclusion of strtoull */
+# endif
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRTOULL
+#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */
+#else
+/* FIXME: need to support real strtoull here */
+#define STRTOULL strtoul
+#endif
+
+#ifdef TEST_PROGRAM
+#define blkid_debug_dump_dev(dev)	(debug_dump_dev(dev))
+static void debug_dump_dev(blkid_dev dev);
+#endif
+
+/*
+ * File format:
+ *
+ *	<device [<NAME="value"> ...]>device_name</device>
+ *
+ *	The following tags are required for each entry:
+ *	<ID="id">	unique (within this file) ID number of this device
+ *	<TIME="sec.usec"> (time_t and suseconds_t) time this entry was last
+ *	                 read from disk
+ *	<TYPE="type">	(detected) type of filesystem/data for this partition
+ *
+ *	The following tags may be present, depending on the device contents
+ *	<LABEL="label">	(user supplied) label (volume name, etc)
+ *	<UUID="uuid">	(generated) universally unique identifier (serial no)
+ */
+
+static char *skip_over_blank(char *cp)
+{
+	while (*cp && isspace(*cp))
+		cp++;
+	return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+	char ch;
+
+	while ((ch = *cp)) {
+		/* If we see a backslash, skip the next character */
+		if (ch == '\\') {
+			cp++;
+			if (*cp == '\0')
+				break;
+			cp++;
+			continue;
+		}
+		if (isspace(ch) || ch == '<' || ch == '>')
+			break;
+		cp++;
+	}
+	return cp;
+}
+
+static char *strip_line(char *line)
+{
+	char	*p;
+
+	line = skip_over_blank(line);
+
+	p = line + strlen(line) - 1;
+
+	while (*line) {
+		if (isspace(*p))
+			*p-- = '\0';
+		else
+			break;
+	}
+
+	return line;
+}
+
+#if 0
+static char *parse_word(char **buf)
+{
+	char *word, *next;
+
+	word = *buf;
+	if (*word == '\0')
+		return NULL;
+
+	word = skip_over_blank(word);
+	next = skip_over_word(word);
+	if (*next) {
+		char *end = next - 1;
+		if (*end == '"' || *end == '\'')
+			*end = '\0';
+		*next++ = '\0';
+	}
+	*buf = next;
+
+	if (*word == '"' || *word == '\'')
+		word++;
+	return word;
+}
+#endif
+
+/*
+ * Start parsing a new line from the cache.
+ *
+ * line starts with "<device" return 1 -> continue parsing line
+ * line starts with "<foo", empty, or # return 0 -> skip line
+ * line starts with other, return -BLKID_ERR_CACHE -> error
+ */
+static int parse_start(char **cp)
+{
+	char *p;
+
+	p = strip_line(*cp);
+
+	/* Skip comment or blank lines.  We can't just NUL the first '#' char,
+	 * in case it is inside quotes, or escaped.
+	 */
+	if (*p == '\0' || *p == '#')
+		return 0;
+
+	if (!strncmp(p, "<device", 7)) {
+		DBG(DEBUG_READ, printf("found device header: %8s\n", p));
+		p += 7;
+
+		*cp = p;
+		return 1;
+	}
+
+	if (*p == '<')
+		return 0;
+
+	return -BLKID_ERR_CACHE;
+}
+
+/* Consume the remaining XML on the line (cosmetic only) */
+static int parse_end(char **cp)
+{
+	*cp = skip_over_blank(*cp);
+
+	if (!strncmp(*cp, "</device>", 9)) {
+		DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp));
+		*cp += 9;
+		return 0;
+	}
+
+	return -BLKID_ERR_CACHE;
+}
+
+/*
+ * Allocate a new device struct with device name filled in.  Will handle
+ * finding the device on lines of the form:
+ * <device foo=bar>devname</device>
+ * <device>devname<foo>bar</foo></device>
+ */
+static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp)
+{
+	char *start, *tmp, *end, *name;
+	int ret;
+
+	if ((ret = parse_start(cp)) <= 0)
+		return ret;
+
+	start = tmp = strchr(*cp, '>');
+	if (!start) {
+		DBG(DEBUG_READ,
+		    printf("blkid: short line parsing dev: %s\n", *cp));
+		return -BLKID_ERR_CACHE;
+	}
+	start = skip_over_blank(start + 1);
+	end = skip_over_word(start);
+
+	DBG(DEBUG_READ, printf("device should be %*s\n",
+			       (int)(end - start), start));
+
+	if (**cp == '>')
+		*cp = end;
+	else
+		(*cp)++;
+
+	*tmp = '\0';
+
+	if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) {
+		DBG(DEBUG_READ,
+		    printf("blkid: missing </device> ending: %s\n", end));
+	} else if (tmp)
+		*tmp = '\0';
+
+	if (end - start <= 1) {
+		DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp));
+		return -BLKID_ERR_CACHE;
+	}
+
+	name = strndup(start, end - start);
+	if (name == NULL)
+		return -BLKID_ERR_MEM;
+
+	DBG(DEBUG_READ, printf("found dev %s\n", name));
+
+	if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE))) {
+		free(name);
+		return -BLKID_ERR_MEM;
+	}
+
+	free(name);
+	return 1;
+}
+
+/*
+ * Extract a tag of the form NAME="value" from the line.
+ */
+static int parse_token(char **name, char **value, char **cp)
+{
+	char *end;
+
+	if (!name || !value || !cp)
+		return -BLKID_ERR_PARAM;
+
+	if (!(*value = strchr(*cp, '=')))
+		return 0;
+
+	**value = '\0';
+	*name = strip_line(*cp);
+	*value = skip_over_blank(*value + 1);
+
+	if (**value == '"') {
+		end = strchr(*value + 1, '"');
+		if (!end) {
+			DBG(DEBUG_READ,
+			    printf("unbalanced quotes at: %s\n", *value));
+			*cp = *value;
+			return -BLKID_ERR_CACHE;
+		}
+		(*value)++;
+		*end = '\0';
+		end++;
+	} else {
+		end = skip_over_word(*value);
+		if (*end) {
+			*end = '\0';
+			end++;
+		}
+	}
+	*cp = end;
+
+	return 1;
+}
+
+/*
+ * Extract a tag of the form <NAME>value</NAME> from the line.
+ */
+/*
+static int parse_xml(char **name, char **value, char **cp)
+{
+	char *end;
+
+	if (!name || !value || !cp)
+		return -BLKID_ERR_PARAM;
+
+	*name = strip_line(*cp);
+
+	if ((*name)[0] != '<' || (*name)[1] == '/')
+		return 0;
+
+	FIXME: finish this.
+}
+*/
+
+/*
+ * Extract a tag from the line.
+ *
+ * Return 1 if a valid tag was found.
+ * Return 0 if no tag found.
+ * Return -ve error code.
+ */
+static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp)
+{
+	char *name = NULL;
+	char *value = NULL;
+	int ret;
+
+	if (!cache || !dev)
+		return -BLKID_ERR_PARAM;
+
+	if ((ret = parse_token(&name, &value, cp)) <= 0 /* &&
+	    (ret = parse_xml(&name, &value, cp)) <= 0 */)
+		return ret;
+
+	/* Some tags are stored directly in the device struct */
+	if (!strcmp(name, "DEVNO"))
+		dev->bid_devno = STRTOULL(value, 0, 0);
+	else if (!strcmp(name, "PRI"))
+		dev->bid_pri = strtol(value, 0, 0);
+	else if (!strcmp(name, "TIME")) {
+		char *end = NULL;
+		dev->bid_time = STRTOULL(value, &end, 0);
+		if (end && *end == '.')
+			dev->bid_utime = STRTOULL(end + 1, 0, 0);
+	} else
+		ret = blkid_set_tag(dev, name, value, strlen(value));
+
+	DBG(DEBUG_READ, printf("    tag: %s=\"%s\"\n", name, value));
+
+	return ret < 0 ? ret : 1;
+}
+
+/*
+ * Parse a single line of data, and return a newly allocated dev struct.
+ * Add the new device to the cache struct, if one was read.
+ *
+ * Lines are of the form <device [TAG="value" ...]>/dev/foo</device>
+ *
+ * Returns -ve value on error.
+ * Returns 0 otherwise.
+ * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL
+ * (e.g. comment lines, unknown XML content, etc).
+ */
+static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp)
+{
+	blkid_dev dev;
+	int ret;
+
+	if (!cache || !dev_p)
+		return -BLKID_ERR_PARAM;
+
+	*dev_p = NULL;
+
+	DBG(DEBUG_READ, printf("line: %s\n", cp));
+
+	if ((ret = parse_dev(cache, dev_p, &cp)) <= 0)
+		return ret;
+
+	dev = *dev_p;
+
+	while ((ret = parse_tag(cache, dev, &cp)) > 0) {
+		;
+	}
+
+	if (dev->bid_type == NULL) {
+		DBG(DEBUG_READ,
+		    printf("blkid: device %s has no TYPE\n",dev->bid_name));
+		blkid_free_dev(dev);
+		goto done;
+	}
+
+	DBG(DEBUG_READ, blkid_debug_dump_dev(dev));
+
+done:
+	return ret;
+}
+
+/*
+ * Parse the specified filename, and return the data in the supplied or
+ * a newly allocated cache struct.  If the file doesn't exist, return a
+ * new empty cache struct.
+ */
+void blkid_read_cache(blkid_cache cache)
+{
+	FILE *file;
+	char buf[4096];
+	int fd, lineno = 0;
+	struct stat st;
+
+	if (!cache)
+		return;
+
+	/*
+	 * If the file doesn't exist, then we just return an empty
+	 * struct so that the cache can be populated.
+	 */
+	if ((fd = open(cache->bic_filename, O_RDONLY|O_CLOEXEC)) < 0)
+		return;
+	if (fstat(fd, &st) < 0)
+		goto errout;
+	if ((st.st_mtime == cache->bic_ftime) ||
+	    (cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+		DBG(DEBUG_CACHE, printf("skipping re-read of %s\n",
+					cache->bic_filename));
+		goto errout;
+	}
+
+	DBG(DEBUG_CACHE, printf("reading cache file %s\n",
+				cache->bic_filename));
+
+	file = fdopen(fd, "r");
+	if (!file)
+		goto errout;
+
+	while (fgets(buf, sizeof(buf), file)) {
+		blkid_dev dev;
+		unsigned int end;
+
+		lineno++;
+		if (buf[0] == 0)
+			continue;
+		end = strlen(buf) - 1;
+		/* Continue reading next line if it ends with a backslash */
+		while (end < (sizeof(buf) - 2) && buf[end] == '\\' &&
+		       fgets(buf + end, sizeof(buf) - end, file)) {
+			end = strlen(buf) - 1;
+			lineno++;
+		}
+
+		if (blkid_parse_line(cache, &dev, buf) < 0) {
+			DBG(DEBUG_READ,
+			    printf("blkid: bad format on line %d\n", lineno));
+			continue;
+		}
+	}
+	fclose(file);
+
+	/*
+	 * Initially we do not need to write out the cache file.
+	 */
+	cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+	cache->bic_ftime = st.st_mtime;
+
+	return;
+errout:
+	close(fd);
+	return;
+}
+
+#ifdef TEST_PROGRAM
+static void debug_dump_dev(blkid_dev dev)
+{
+	struct list_head *p;
+
+	if (!dev) {
+		printf("  dev: NULL\n");
+		return;
+	}
+
+	printf("  dev: name = %s\n", dev->bid_name);
+	printf("  dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno);
+	printf("  dev: TIME=\"%ld.%ld\"\n", (long)dev->bid_time, (long)dev->bid_utime);
+	printf("  dev: PRI=\"%d\"\n", dev->bid_pri);
+	printf("  dev: flags = 0x%08X\n", dev->bid_flags);
+
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+		if (tag)
+			printf("    tag: %s=\"%s\"\n", tag->bit_name,
+			       tag->bit_val);
+		else
+			printf("    tag: NULL\n");
+	}
+	printf("\n");
+}
+
+int main(int argc, char**argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(DEBUG_ALL);
+	if (argc > 2) {
+		fprintf(stderr, "Usage: %s [filename]\n"
+			"Test parsing of the cache (filename)\n", argv[0]);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, argv[1])) < 0)
+		fprintf(stderr, "error %d reading cache file %s\n", ret,
+			argv[1] ? argv[1] : blkid_get_cache_filename(NULL));
+
+	blkid_put_cache(cache);
+
+	return ret;
+}
+#endif
diff --git a/libblkid/reiserfs.c b/libblkid/reiserfs.c
new file mode 100644
index 0000000..152571f
--- /dev/null
+++ b/libblkid/reiserfs.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 1999, 2001 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct reiserfs_super_block {
+	uint32_t	rs_blocks_count;
+	uint32_t	rs_free_blocks;
+	uint32_t	rs_root_block;
+	uint32_t	rs_journal_block;
+	uint32_t	rs_journal_dev;
+	uint32_t	rs_orig_journal_size;
+	uint32_t	rs_dummy2[5];
+	uint16_t	rs_blocksize;
+	uint16_t	rs_dummy3[3];
+	unsigned char	rs_magic[12];
+	uint32_t	rs_dummy4[5];
+	unsigned char	rs_uuid[16];
+	char		rs_label[16];
+} __attribute__((packed));
+
+struct reiser4_super_block {
+	unsigned char	rs4_magic[16];
+	uint16_t	rs4_dummy[2];
+	unsigned char	rs4_uuid[16];
+	unsigned char	rs4_label[16];
+	uint64_t	rs4_dummy2;
+} __attribute__((packed));
+
+static int probe_reiser(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct reiserfs_super_block *rs;
+	unsigned int blocksize;
+
+	rs = blkid_probe_get_sb(pr, mag, struct reiserfs_super_block);
+	if (!rs)
+		return -1;
+
+	blocksize = le16_to_cpu(rs->rs_blocksize);
+
+	/* The blocksize must be at least 512B */
+	if ((blocksize >> 9) == 0)
+		return -BLKID_ERR_PARAM;
+
+	/* If the superblock is inside the journal, we have the wrong one */
+	if (mag->kboff / (blocksize >> 9) > le32_to_cpu(rs->rs_journal_block) / 2)
+		return -BLKID_ERR_BIG;
+
+	/* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */
+	if (mag->magic[6] == '2' || mag->magic[6] == '3') {
+		if (*rs->rs_label)
+			blkid_probe_set_label(pr,
+					(unsigned char *) rs->rs_label,
+					sizeof(rs->rs_label));
+		blkid_probe_set_uuid(pr, rs->rs_uuid);
+	}
+
+	if (mag->magic[6] == '3')
+		blkid_probe_set_version(pr, "JR");
+	else if (mag->magic[6] == '2')
+		blkid_probe_set_version(pr, "3.6");
+	else
+		blkid_probe_set_version(pr, "3.5");
+
+	return 0;
+}
+
+static int probe_reiser4(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct reiser4_super_block *rs4;
+
+	rs4 = blkid_probe_get_sb(pr, mag, struct reiser4_super_block);
+	if (!rs4)
+		return -1;
+
+	if (*rs4->rs4_label)
+		blkid_probe_set_label(pr, rs4->rs4_label, sizeof(rs4->rs4_label));
+	blkid_probe_set_uuid(pr, rs4->rs4_uuid);
+	blkid_probe_set_version(pr, "4");
+
+	return 0;
+}
+
+
+const struct blkid_idinfo reiser_idinfo =
+{
+	.name		= "reiserfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_reiser,
+	.minsz		= 128 * 1024,
+	.magics		=
+	{
+		{ .magic = "ReIsErFs",  .len = 8, .kboff = 8,  .sboff = 0x34 },
+		{ .magic = "ReIsEr2Fs", .len = 9, .kboff = 64, .sboff = 0x34 },
+		{ .magic = "ReIsEr3Fs", .len = 9, .kboff = 64, .sboff = 0x34 },
+		{ .magic = "ReIsErFs",  .len = 8, .kboff = 64, .sboff = 0x34 },
+		{ .magic = "ReIsErFs",  .len = 8, .kboff =  8, .sboff = 20   },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo reiser4_idinfo =
+{
+	.name		= "reiser4",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_reiser4,
+	.minsz		= 128 * 1024,
+	.magics		=
+	{
+		{ .magic = "ReIsEr4", .len = 7, .kboff = 64 },
+		{ NULL }
+	}
+};
+
+
+
+
diff --git a/libblkid/resolve.c b/libblkid/resolve.c
new file mode 100644
index 0000000..96749b3
--- /dev/null
+++ b/libblkid/resolve.c
@@ -0,0 +1,131 @@
+/*
+ * resolve.c - resolve names and tags into specific devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Ts'o.
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "blkidP.h"
+
+/*
+ * Find a tagname (e.g. LABEL or UUID) on a specific device.
+ */
+char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+			  const char *devname)
+{
+	blkid_tag found;
+	blkid_dev dev;
+	blkid_cache c = cache;
+	char *ret = NULL;
+
+	DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname));
+
+	if (!devname)
+		return NULL;
+	if (!cache && blkid_get_cache(&c, NULL) < 0)
+		return NULL;
+
+	if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) &&
+	    (found = blkid_find_tag_dev(dev, tagname)))
+		ret = found->bit_val ? strdup(found->bit_val) : NULL;
+
+	if (!cache)
+		blkid_put_cache(c);
+
+	return ret;
+}
+
+/*
+ * Locate a device name from a token (NAME=value string), or (name, value)
+ * pair.  In the case of a token, value is ignored.  If the "token" is not
+ * of the form "NAME=value" and there is no value given, then it is assumed
+ * to be the actual devname and a copy is returned.
+ */
+char *blkid_get_devname(blkid_cache cache, const char *token,
+			const char *value)
+{
+	blkid_dev dev;
+	blkid_cache c = cache;
+	char *t = 0, *v = 0;
+	char *ret = NULL;
+
+	if (!token)
+		return NULL;
+	if (!cache && blkid_get_cache(&c, NULL) < 0)
+		return NULL;
+
+	DBG(DEBUG_RESOLVE,
+	    printf("looking for %s%s%s %s\n", token, value ? "=" : "",
+		   value ? value : "", cache ? "in cache" : "from disk"));
+
+	if (!value) {
+		if (!strchr(token, '=')) {
+			ret = strdup(token);
+			goto out;
+		}
+		blkid_parse_tag_string(token, &t, &v);
+		if (!t || !v)
+			goto out;
+		token = t;
+		value = v;
+	}
+
+	dev = blkid_find_dev_with_tag(c, token, value);
+	if (!dev)
+		goto out;
+
+	ret = dev->bid_name ? strdup(dev->bid_name) : NULL;
+out:
+	free(t);
+	free(v);
+	if (!cache)
+		blkid_put_cache(c);
+	return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	char *value;
+	blkid_cache cache;
+
+	blkid_init_debug(DEBUG_ALL);
+	if (argc != 2 && argc != 3) {
+		fprintf(stderr, "Usage:\t%s tagname=value\n"
+			"\t%s tagname devname\n"
+			"Find which device holds a given token or\n"
+			"Find what the value of a tag is in a device\n",
+			argv[0], argv[0]);
+		exit(1);
+	}
+	if (blkid_get_cache(&cache, "/dev/null") < 0) {
+		fprintf(stderr, "Couldn't get blkid cache\n");
+		exit(1);
+	}
+
+	if (argv[2]) {
+		value = blkid_get_tag_value(cache, argv[1], argv[2]);
+		printf("%s has tag %s=%s\n", argv[2], argv[1],
+		       value ? value : "<missing>");
+	} else {
+		value = blkid_get_devname(cache, argv[1], NULL);
+		printf("%s has tag %s\n", value ? value : "<none>", argv[1]);
+	}
+	blkid_put_cache(cache);
+	return value ? 0 : 1;
+}
+#endif
diff --git a/libblkid/romfs.c b/libblkid/romfs.c
new file mode 100644
index 0000000..91ef996
--- /dev/null
+++ b/libblkid/romfs.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 1999, 2001 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct romfs_super_block {
+	unsigned char	ros_magic[8];
+	uint32_t	ros_dummy1[2];
+	unsigned char	ros_volume[16];
+} __attribute__((packed));
+
+static int probe_romfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct romfs_super_block *ros;
+
+	ros = blkid_probe_get_sb(pr, mag, struct romfs_super_block);
+	if (!ros)
+		return -1;
+
+	if (strlen((char *) ros->ros_volume))
+		blkid_probe_set_label(pr, ros->ros_volume,
+				sizeof(ros->ros_volume));
+	return 0;
+}
+
+const struct blkid_idinfo romfs_idinfo =
+{
+	.name		= "romfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_romfs,
+	.magics		=
+	{
+		{ .magic = "-rom1fs-", .len = 8 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/rpmatch.h b/libblkid/rpmatch.h
new file mode 100644
index 0000000..d62634b
--- /dev/null
+++ b/libblkid/rpmatch.h
@@ -0,0 +1,9 @@
+#ifndef UTIL_LINUX_RPMATCH_H
+#define UTIL_LINUX_RPMATCH_H
+
+#ifndef HAVE_RPMATCH
+#define rpmatch(r) \
+	(*r == 'y' || *r == 'Y' ? 1 : *r == 'n' || *r == 'N' ? 0 : -1)
+#endif
+
+#endif /* UTIL_LINUX_RPMATCH_H */
diff --git a/libblkid/save.c b/libblkid/save.c
new file mode 100644
index 0000000..c94cc2a
--- /dev/null
+++ b/libblkid/save.c
@@ -0,0 +1,227 @@
+/*
+ * save.c - write the cache struct to disk
+ *
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+
+static int save_dev(blkid_dev dev, FILE *file)
+{
+	struct list_head *p;
+
+	if (!dev || dev->bid_name[0] != '/')
+		return 0;
+
+	DBG(DEBUG_SAVE,
+	    printf("device %s, type %s\n", dev->bid_name, dev->bid_type ?
+		   dev->bid_type : "(null)"));
+
+	fprintf(file, "<device DEVNO=\"0x%04lx\" TIME=\"%ld.%ld\"",
+			(unsigned long) dev->bid_devno,
+			(long) dev->bid_time,
+			(long) dev->bid_utime);
+
+	if (dev->bid_pri)
+		fprintf(file, " PRI=\"%d\"", dev->bid_pri);
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+		fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val);
+	}
+	fprintf(file, ">%s</device>\n", dev->bid_name);
+
+	return 0;
+}
+
+/*
+ * Write out the cache struct to the cache file on disk.
+ */
+int blkid_flush_cache(blkid_cache cache)
+{
+	struct list_head *p;
+	char *tmp = NULL;
+	char *opened = NULL;
+	char *filename;
+	FILE *file = NULL;
+	int fd, ret = 0;
+	struct stat st;
+
+	if (!cache)
+		return -BLKID_ERR_PARAM;
+
+	if (list_empty(&cache->bic_devs) ||
+	    !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+		DBG(DEBUG_SAVE, printf("skipping cache file write\n"));
+		return 0;
+	}
+
+	filename = cache->bic_filename ? cache->bic_filename :
+					 blkid_get_cache_filename(NULL);
+	if (!filename)
+		return -BLKID_ERR_PARAM;
+
+	if (strncmp(filename,
+	    BLKID_RUNTIME_DIR "/", sizeof(BLKID_RUNTIME_DIR)) == 0) {
+
+		/* default destination, create the directory if necessary */
+		if (stat(BLKID_RUNTIME_DIR, &st)
+		    && errno == ENOENT
+		    && mkdir(BLKID_RUNTIME_DIR, S_IWUSR|
+						S_IRUSR|S_IRGRP|S_IROTH|
+						S_IXUSR|S_IXGRP|S_IXOTH) != 0
+		    && errno != EEXIST) {
+			DBG(DEBUG_SAVE,
+				printf("can't create %s directory for cache file\n",
+					BLKID_RUNTIME_DIR));
+			return 0;
+		}
+	}
+
+	/* If we can't write to the cache file, then don't even try */
+	if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) ||
+	    (ret == 0 && access(filename, W_OK) < 0)) {
+		DBG(DEBUG_SAVE,
+		    printf("can't write to cache file %s\n", filename));
+		return 0;
+	}
+
+	/*
+	 * Try and create a temporary file in the same directory so
+	 * that in case of error we don't overwrite the cache file.
+	 * If the cache file doesn't yet exist, it isn't a regular
+	 * file (e.g. /dev/null or a socket), or we couldn't create
+	 * a temporary file then we open it directly.
+	 */
+	if (ret == 0 && S_ISREG(st.st_mode)) {
+		tmp = malloc(strlen(filename) + 8);
+		if (tmp) {
+			sprintf(tmp, "%s-XXXXXX", filename);
+			fd = mkstemp(tmp);
+			if (fd >= 0) {
+				if (fchmod(fd, 0644) != 0)
+					DBG(DEBUG_SAVE,	printf("%s: fchmod failed\n", filename));
+				else if ((file = fdopen(fd, "w")))
+					opened = tmp;
+				if (!file)
+					close(fd);
+			}
+		}
+	}
+
+	if (!file) {
+		file = fopen(filename, "w");
+		opened = filename;
+	}
+
+	DBG(DEBUG_SAVE,
+	    printf("writing cache file %s (really %s)\n",
+		   filename, opened));
+
+	if (!file) {
+		ret = errno;
+		goto errout;
+	}
+
+	list_for_each(p, &cache->bic_devs) {
+		blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+		if (!dev->bid_type || (dev->bid_flags & BLKID_BID_FL_REMOVABLE))
+			continue;
+		if ((ret = save_dev(dev, file)) < 0)
+			break;
+	}
+
+	if (ret >= 0) {
+		cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+		ret = 1;
+	}
+
+	fclose(file);
+	if (opened != filename) {
+		if (ret < 0) {
+			unlink(opened);
+			DBG(DEBUG_SAVE,
+			    printf("unlinked temp cache %s\n", opened));
+		} else {
+			char *backup;
+
+			backup = malloc(strlen(filename) + 5);
+			if (backup) {
+				sprintf(backup, "%s.old", filename);
+				unlink(backup);
+				if (link(filename, backup)) {
+					DBG(DEBUG_SAVE,
+						printf("can't link %s to %s\n",
+							filename, backup));
+				}
+				free(backup);
+			}
+			if (rename(opened, filename)) {
+				ret = errno;
+				DBG(DEBUG_SAVE,
+					printf("can't rename %s to %s\n",
+						opened, filename));
+			} else {
+				DBG(DEBUG_SAVE,
+				    printf("moved temp cache %s\n", opened));
+			}
+		}
+	}
+
+errout:
+	free(tmp);
+	if (filename != cache->bic_filename)
+		free(filename);
+	return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(DEBUG_ALL);
+	if (argc > 2) {
+		fprintf(stderr, "Usage: %s [filename]\n"
+			"Test loading/saving a cache (filename)\n", argv[0]);
+		exit(1);
+	}
+
+	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	if ((ret = blkid_probe_all(cache)) < 0) {
+		fprintf(stderr, "error (%d) probing devices\n", ret);
+		exit(1);
+	}
+	cache->bic_filename = strdup(argv[1]);
+
+	if ((ret = blkid_flush_cache(cache)) < 0) {
+		fprintf(stderr, "error (%d) saving cache\n", ret);
+		exit(1);
+	}
+
+	blkid_put_cache(cache);
+
+	return ret;
+}
+#endif
diff --git a/libblkid/setproctitle.c b/libblkid/setproctitle.c
new file mode 100644
index 0000000..4bcf8c8
--- /dev/null
+++ b/libblkid/setproctitle.c
@@ -0,0 +1,74 @@
+/*
+ *  set process title for ps (from sendmail)
+ *
+ *  Clobbers argv of our main procedure so ps(1) will display the title.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "setproctitle.h"
+
+#ifndef SPT_BUFSIZE
+# define SPT_BUFSIZE     2048
+#endif
+
+extern char **environ;
+
+static char **argv0;
+static int argv_lth;
+
+void initproctitle (int argc, char **argv)
+{
+	int i;
+	char **envp = environ;
+
+	/*
+	 * Move the environment so we can reuse the memory.
+	 * (Code borrowed from sendmail.)
+	 * WARNING: ugly assumptions on memory layout here;
+	 *          if this ever causes problems, #undef DO_PS_FIDDLING
+	 */
+	for (i = 0; envp[i] != NULL; i++)
+		continue;
+
+	environ = (char **) malloc(sizeof(char *) * (i + 1));
+	if (environ == NULL)
+		return;
+
+	for (i = 0; envp[i] != NULL; i++)
+		if ((environ[i] = strdup(envp[i])) == NULL)
+			return;
+	environ[i] = NULL;
+
+	argv0 = argv;
+	if (i > 0)
+		argv_lth = envp[i-1] + strlen(envp[i-1]) - argv0[0];
+	else
+		argv_lth = argv0[argc-1] + strlen(argv0[argc-1]) - argv0[0];
+}
+
+void setproctitle (const char *prog, const char *txt)
+{
+        int i;
+        char buf[SPT_BUFSIZE];
+
+        if (!argv0)
+                return;
+
+	if (strlen(prog) + strlen(txt) + 5 > SPT_BUFSIZE)
+		return;
+
+	sprintf(buf, "%s -- %s", prog, txt);
+
+        i = strlen(buf);
+        if (i > argv_lth - 2) {
+                i = argv_lth - 2;
+                buf[i] = '\0';
+        }
+	memset(argv0[0], '\0', argv_lth);       /* clear the memory area */
+        strcpy(argv0[0], buf);
+
+        argv0[1] = NULL;
+}
diff --git a/libblkid/setproctitle.h b/libblkid/setproctitle.h
new file mode 100644
index 0000000..70a9efa
--- /dev/null
+++ b/libblkid/setproctitle.h
@@ -0,0 +1,7 @@
+#ifndef UTIL_LINUX_SETPROCTITLE_H
+#define UTIL_LINUX_SETPROCTITLE_H
+
+extern void initproctitle (int argc, char **argv);
+extern void setproctitle (const char *prog, const char *txt);
+
+#endif
diff --git a/libblkid/sgi.c b/libblkid/sgi.c
new file mode 100644
index 0000000..b89e463
--- /dev/null
+++ b/libblkid/sgi.c
@@ -0,0 +1,159 @@
+/*
+ * sgi partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+#define SGI_MAXPARTITIONS	16
+
+/* partition type */
+#define SGI_TYPE_VOLHDR		0x00
+#define SGI_TYPE_VOLULME	0x06	/* entire disk */
+
+struct sgi_device_parameter {
+	unsigned char skew;
+	unsigned char gap1;
+	unsigned char gap2;
+	unsigned char sparecyl;
+
+	uint16_t pcylcount;
+	uint16_t head_vol0;
+	uint16_t ntrks;		/* tracks in cyl 0 or vol 0 */
+
+	unsigned char cmd_tag_queue_depth;
+	unsigned char unused0;
+
+	uint16_t unused1;
+	uint16_t nsect;		/* sectors/tracks in cyl 0 or vol 0 */
+	uint16_t bytes;
+	uint16_t ilfact;
+	uint32_t flags;		/* controller flags */
+	uint32_t datarate;
+	uint32_t retries_on_error;
+	uint32_t ms_per_word;
+	uint16_t xylogics_gap1;
+	uint16_t xylogics_syncdelay;
+	uint16_t xylogics_readdelay;
+	uint16_t xylogics_gap2;
+	uint16_t xylogics_readgate;
+	uint16_t xylogics_writecont;
+} __attribute__((packed));
+
+struct sgi_disklabel {
+	uint32_t magic;			/* magic number */
+	uint16_t root_part_num;		/* # root partition */
+	uint16_t swap_part_num;		/* # swap partition */
+	unsigned char boot_file[16];	/* name of boot file */
+
+	struct sgi_device_parameter	devparam;	/* not used now */
+
+	struct sgi_volume {
+		unsigned char name[8];	/* name of volume */
+		uint32_t block_num;	/* logical block number */
+		uint32_t num_bytes;	/* how big, in bytes */
+	} __attribute__((packed)) volume[15];
+
+	struct sgi_partition {
+		uint32_t num_blocks;	/* size in logical blocks */
+		uint32_t first_block;	/* first logical block */
+		uint32_t type;		/* type of this partition */
+	} __attribute__((packed)) partitions[SGI_MAXPARTITIONS];
+
+	/* checksum is the 32bit 2's complement sum of the disklabel */
+	uint32_t csum;			/* disk label checksum */
+	uint32_t padding;		/* padding */
+} __attribute__((packed));
+
+static uint32_t count_checksum(struct sgi_disklabel *label)
+{
+	int i;
+	uint32_t *ptr = (uint32_t *) label;
+	uint32_t sum = 0;
+
+	i = sizeof(*label) / sizeof(*ptr);
+
+	while (i--)
+		sum += be32_to_cpu(ptr[i]);
+
+	return sum;
+}
+
+
+static int probe_sgi_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct sgi_disklabel *l;
+	struct sgi_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	int i;
+
+	l = (struct sgi_disklabel *) blkid_probe_get_sector(pr, 0);
+	if (!l)
+		goto nothing;
+
+	if (count_checksum(l)) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"detected corrupted sgi disk label -- ignore\n"));
+		goto nothing;
+	}
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "sgi", 0);
+	if (!tab)
+		goto err;
+
+	for(i = 0, p = &l->partitions[0]; i < SGI_MAXPARTITIONS; i++, p++) {
+		uint32_t size = be32_to_cpu(p->num_blocks);
+		uint32_t start = be32_to_cpu(p->first_block);
+		uint32_t type = be32_to_cpu(p->type);
+		blkid_partition par;
+
+		if (size == 0 || type == SGI_TYPE_VOLULME ||
+			         type == SGI_TYPE_VOLHDR) {
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, type);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+const struct blkid_idinfo sgi_pt_idinfo =
+{
+	.name		= "sgi",
+	.probefunc	= probe_sgi_pt,
+	.magics		=
+	{
+		{ .magic = "\x0B\xE5\xA9\x41", .len = 4	},
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/silicon_raid.c b/libblkid/silicon_raid.c
new file mode 100644
index 0000000..496a3e7
--- /dev/null
+++ b/libblkid/silicon_raid.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+struct silicon_metadata {
+	uint8_t		unknown0[0x2E];
+	uint8_t		ascii_version[0x36 - 0x2E];
+	int8_t		diskname[0x56 - 0x36];
+	int8_t		unknown1[0x60 - 0x56];
+	uint32_t	magic;
+	int8_t		unknown1a[0x6C - 0x64];
+	uint32_t	array_sectors_low;
+	uint32_t	array_sectors_high;
+	int8_t		unknown2[0x78 - 0x74];
+	uint32_t	thisdisk_sectors;
+	int8_t		unknown3[0x100 - 0x7C];
+	int8_t		unknown4[0x104 - 0x100];
+	uint16_t	product_id;
+	uint16_t	vendor_id;
+	uint16_t	minor_ver;
+	uint16_t	major_ver;
+	uint8_t		seconds;
+	uint8_t		minutes;
+	uint8_t		hour;
+	uint8_t		day;
+	uint8_t		month;
+	uint8_t		year;
+	uint16_t	raid0_stride;
+	int8_t		unknown6[0x116 - 0x114];
+	uint8_t		disk_number;
+	uint8_t		type;			/* SILICON_TYPE_* */
+	int8_t		drives_per_striped_set;
+	int8_t		striped_set_number;
+	int8_t		drives_per_mirrored_set;
+	int8_t		mirrored_set_number;
+	uint32_t	rebuild_ptr_low;
+	uint32_t	rebuild_ptr_high;
+	uint32_t	incarnation_no;
+	uint8_t		member_status;
+	uint8_t		mirrored_set_state;	/* SILICON_MIRROR_* */
+	uint8_t		reported_device_location;
+	uint8_t		idechannel;
+	uint8_t		auto_rebuild;
+	uint8_t		unknown8;
+	uint8_t		text_type[0x13E - 0x12E];
+	uint16_t	checksum1;
+	int8_t		assumed_zeros[0x1FE - 0x140];
+	uint16_t	checksum2;
+} __attribute__((packed));
+
+#define SILICON_MAGIC		0x2F000000
+
+static int checksum(struct silicon_metadata *sil)
+{
+	int sum = 0;
+	unsigned short count = offsetof(struct silicon_metadata, checksum1) / 2;
+	uint16_t *p = (uint16_t *) sil;
+
+	while (count--)
+		sum += *p++;
+
+	return (-sum & 0xFFFF) == le16_to_cpu(sil->checksum1);
+}
+
+static int probe_silraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct silicon_metadata *sil;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 1) * 0x200;
+
+	sil = (struct silicon_metadata *)
+			blkid_probe_get_buffer(pr, off,
+				sizeof(struct silicon_metadata));
+	if (!sil)
+		return -1;
+
+	if (le32_to_cpu(sil->magic) != SILICON_MAGIC)
+		return -1;
+	if (sil->disk_number >= 8)
+		return -1;
+	if (!checksum(sil)) {
+		DBG(DEBUG_LOWPROBE, printf("silicon raid: incorrect checksum\n"));
+		return -1;
+	}
+
+	if (blkid_probe_sprintf_version(pr, "%u.%u",
+				le16_to_cpu(sil->major_ver),
+				le16_to_cpu(sil->minor_ver)) != 0)
+		return -1;
+
+	if (blkid_probe_set_magic(pr,
+			off + offsetof(struct silicon_metadata, magic),
+			sizeof(sil->magic),
+			(unsigned char *) &sil->magic))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo silraid_idinfo = {
+	.name		= "silicon_medley_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_silraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/solaris_x86.c b/libblkid/solaris_x86.c
new file mode 100644
index 0000000..7824f4e
--- /dev/null
+++ b/libblkid/solaris_x86.c
@@ -0,0 +1,151 @@
+/*
+ * Solaris x86 partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+/*
+ * Solaris-x86 is always within primary dos partition (nested PT table).  The
+ * solaris-x86 vtoc allows to split the entire partition to "slices". The
+ * offset (start) of the slice is always relatively to the primary dos
+ * partition.
+ *
+ * Note that Solaris-SPARC uses entire disk with a different partitionning
+ * scheme.
+ */
+
+/* some other implementation than Linux kernel assume 8 partitions only */
+#define SOLARIS_MAXPARTITIONS	16
+
+/* disklabel (vtoc) location  */
+#define SOLARIS_SECTOR		1			/* in 512-sectors */
+#define SOLARIS_OFFSET		(SOLARIS_SECTOR << 9)	/* in bytes */
+#define SOLARIS_MAGICOFFSET	(SOLARIS_OFFSET + 12)	/* v_sanity offset in bytes */
+
+/* slice tags */
+#define SOLARIS_TAG_WHOLEDISK	5
+
+struct solaris_slice {
+	uint16_t s_tag;      /* ID tag of partition */
+	uint16_t s_flag;     /* permission flags */
+	uint32_t s_start;    /* start sector no of partition */
+	uint32_t s_size;     /* # of blocks in partition */
+} __attribute__((packed));
+
+struct solaris_vtoc {
+	unsigned int v_bootinfo[3];     /* info needed by mboot (unsupported) */
+
+	uint32_t     v_sanity;          /* to verify vtoc sanity */
+	uint32_t     v_version;         /* layout version */
+	char         v_volume[8];       /* volume name */
+	uint16_t     v_sectorsz;        /* sector size in bytes */
+	uint16_t     v_nparts;          /* number of partitions */
+	unsigned int v_reserved[10];    /* free space */
+
+	struct solaris_slice v_slice[SOLARIS_MAXPARTITIONS]; /* slices */
+
+	unsigned int timestamp[SOLARIS_MAXPARTITIONS]; /* timestamp (unsupported) */
+	char         v_asciilabel[128];	/* for compatibility */
+} __attribute__((packed));
+
+static int probe_solaris_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct solaris_vtoc *l;	/* disk label */
+	struct solaris_slice *p;	/* partitsion */
+	blkid_parttable tab = NULL;
+	blkid_partition parent;
+	blkid_partlist ls;
+	int i;
+	uint16_t nparts;
+
+	l = (struct solaris_vtoc *) blkid_probe_get_sector(pr, SOLARIS_SECTOR);
+	if (!l)
+		goto nothing;
+
+	if (le32_to_cpu(l->v_version) != 1) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"WARNING: unsupported solaris x86 version %d, ignore\n",
+			le32_to_cpu(l->v_version)));
+		goto nothing;
+	}
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	parent = blkid_partlist_get_parent(ls);
+
+	tab = blkid_partlist_new_parttable(ls, "solaris", SOLARIS_OFFSET);
+	if (!tab)
+		goto err;
+
+	nparts = le16_to_cpu(l->v_nparts);
+	if (nparts > SOLARIS_MAXPARTITIONS)
+		nparts = SOLARIS_MAXPARTITIONS;
+
+	for (i = 1, p = &l->v_slice[0];	i < nparts; i++, p++) {
+
+		uint32_t start = le32_to_cpu(p->s_start);
+		uint32_t size = le32_to_cpu(p->s_size);
+		blkid_partition par;
+
+		if (size == 0 || le16_to_cpu(p->s_tag) == SOLARIS_TAG_WHOLEDISK)
+			continue;
+
+		if (parent)
+			/* Solaris slices are relative to the parent (primary
+			 * DOS partition) */
+			start += blkid_partition_get_start(parent);
+
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: solaris partition (%d) overflow "
+				"detected, ignore\n", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, le16_to_cpu(p->s_tag));
+		blkid_partition_set_flags(par, le16_to_cpu(p->s_flag));
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+const struct blkid_idinfo solaris_x86_pt_idinfo =
+{
+	.name		= "solaris",
+	.probefunc	= probe_solaris_pt,
+	.magics		=
+	{
+		{
+		  .magic = "\xEE\xDE\x0D\x60",	/* little-endian magic string */
+		  .len = 4,			/* v_sanity size in bytes */
+		  .sboff = SOLARIS_MAGICOFFSET	/* offset of v_sanity */
+		},
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/squashfs.c b/libblkid/squashfs.c
new file mode 100644
index 0000000..45f1029
--- /dev/null
+++ b/libblkid/squashfs.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "bitops.h"	/* swab16() */
+#include "superblocks.h"
+
+struct sqsh_super_block {
+	uint32_t	s_magic;
+	uint32_t	inodes;
+	uint32_t	bytes_used_2;
+	uint32_t	uid_start_2;
+	uint32_t	guid_start_2;
+	uint32_t	inode_table_start_2;
+	uint32_t	directory_table_start_2;
+	uint16_t	s_major;
+	uint16_t	s_minor;
+} __attribute__((packed));
+
+static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct sqsh_super_block *sq;
+
+	sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block);
+	if (!sq)
+		return -1;
+
+	if (strcmp(mag->magic, "sqsh") == 0 ||
+	    strcmp(mag->magic, "qshs") == 0)
+		blkid_probe_sprintf_version(pr, "%u.%u",
+				sq->s_major,
+				sq->s_minor);
+	else
+		blkid_probe_sprintf_version(pr, "%u.%u",
+				swab16(sq->s_major),
+				swab16(sq->s_minor));
+	return 0;
+}
+
+const struct blkid_idinfo squashfs_idinfo =
+{
+	.name		= "squashfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_squashfs,
+	.magics		=
+	{
+		{ .magic = "sqsh", .len = 4 },
+		{ .magic = "hsqs", .len = 4 }, /* swap */
+
+		/* LZMA version */
+		{ .magic = "qshs", .len = 4 },
+		{ .magic = "shsq", .len = 4 }, /* swap */
+		{ NULL }
+	}
+};
+
+
diff --git a/libblkid/strutils.c b/libblkid/strutils.c
new file mode 100644
index 0000000..2337b07
--- /dev/null
+++ b/libblkid/strutils.c
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "bitops.h"
+
+static int do_scale_by_power (uintmax_t *x, int base, int power)
+{
+	while (power--) {
+		if (UINTMAX_MAX / base < *x)
+			return -2;
+		*x *= base;
+	}
+	return 0;
+}
+
+/*
+ * strtosize() - convert string to size (uintmax_t).
+ *
+ * Supported suffixes:
+ *
+ * XiB or X for 2^N
+ *     where X = {K,M,G,T,P,E,Y,Z}
+ *        or X = {k,m,g,t,p,e}  (undocumented for backward compatibility only)
+ * for example:
+ *		10KiB	= 10240
+ *		10K	= 10240
+ *
+ * XB for 10^N
+ *     where X = {K,M,G,T,P,E,Y,Z}
+ * for example:
+ *		10KB	= 10000
+ *
+ * Note that the function does not accept numbers with '-' (negative sign)
+ * prefix.
+ */
+int strtosize(const char *str, uintmax_t *res)
+{
+	char *p;
+	uintmax_t x;
+	int base = 1024, rc = 0;
+
+	*res = 0;
+
+	if (!str || !*str)
+		goto err;
+
+	/* Only positive numbers are acceptable
+	 *
+	 * Note that this check is not perfect, it would be better to
+	 * use lconv->negative_sign. But coreutils use the same solution,
+	 * so it's probably good enough...
+	 */
+	p = (char *) str;
+	while (isspace((unsigned char) *p))
+		p++;
+	if (*p == '-')
+		goto err;
+	p = NULL;
+
+	errno = 0;
+	x = strtoumax(str, &p, 0);
+
+	if (p == str ||
+	    (errno != 0 && (x == UINTMAX_MAX || x == 0)))
+		goto err;
+
+	if (!p || !*p)
+		goto done;			/* without suffix */
+
+	/*
+	 * Check size suffixes
+	 */
+	if (*(p + 1) == 'i' && *(p + 2) == 'B' && !*(p + 3))
+		base = 1024;			/* XiB, 2^N */
+	else if (*(p + 1) == 'B' && !*(p + 2))
+		base = 1000;			/* XB, 10^N */
+	else if (*(p + 1))
+		goto err;			/* unexpected suffix */
+
+	switch(*p) {
+	case 'K':
+	case 'k':
+		rc = do_scale_by_power(&x, base, 1);
+		break;
+	case 'M':
+	case 'm':
+		rc = do_scale_by_power(&x, base, 2);
+		break;
+	case 'G':
+	case 'g':
+		rc = do_scale_by_power(&x, base, 3);
+		break;
+	case 'T':
+	case 't':
+		rc = do_scale_by_power(&x, base, 4);
+		break;
+	case 'P':
+	case 'p':
+		rc = do_scale_by_power(&x, base, 5);
+		break;
+	case 'E':
+	case 'e':
+		rc = do_scale_by_power(&x, base, 6);
+		break;
+	case 'Z':
+		rc = do_scale_by_power(&x, base, 7);
+		break;
+	case 'Y':
+		rc = do_scale_by_power(&x, base, 8);
+		break;
+	default:
+		goto err;
+	}
+
+done:
+	*res = x;
+	return rc;
+err:
+	return -1;
+}
+
+#ifndef HAVE_MEMPCPY
+void *mempcpy(void *restrict dest, const void *restrict src, size_t n)
+{
+    return ((char *)memcpy(dest, src, n)) + n;
+}
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *s, size_t maxlen)
+{
+        int i;
+
+        for (i = 0; i < maxlen; i++) {
+                if (s[i] == '\0')
+                        return i + 1;
+        }
+        return maxlen;
+}
+#endif
+
+#ifndef HAVE_STRNCHR
+char *strnchr(const char *s, size_t maxlen, int c)
+{
+	for (; maxlen-- && *s != '\0'; ++s)
+		if (*s == (char)c)
+			return (char *)s;
+	return NULL;
+}
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n)
+{
+	size_t len = strnlen(s, n);
+	char *new = (char *) malloc((len + 1) * sizeof(char));
+	if (!new)
+		return NULL;
+	new[len] = '\0';
+	return (char *) memcpy(new, s, len);
+}
+#endif
+
+int16_t strtos16_or_err(const char *str, const char *errmesg)
+{
+	int32_t num = strtos32_or_err(str, errmesg);
+
+	if (num < INT16_MIN || num > INT16_MAX)
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	return num;
+}
+
+uint16_t strtou16_or_err(const char *str, const char *errmesg)
+{
+	uint32_t num = strtou32_or_err(str, errmesg);
+
+	if (num > UINT16_MAX)
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	return num;
+}
+
+int32_t strtos32_or_err(const char *str, const char *errmesg)
+{
+	int64_t num = strtos64_or_err(str, errmesg);
+
+	if (num < INT32_MIN || num > INT32_MAX)
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	return num;
+}
+
+uint32_t strtou32_or_err(const char *str, const char *errmesg)
+{
+	uint64_t num = strtou64_or_err(str, errmesg);
+
+	if (num > UINT32_MAX)
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	return num;
+}
+
+int64_t strtos64_or_err(const char *str, const char *errmesg)
+{
+	int64_t num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtoimax(str, &end, 10);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+uint64_t strtou64_or_err(const char *str, const char *errmesg)
+{
+	uintmax_t num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtoumax(str, &end, 10);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+
+double strtod_or_err(const char *str, const char *errmesg)
+{
+	double num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtod(str, &end);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+long strtol_or_err(const char *str, const char *errmesg)
+{
+	long num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtol(str, &end, 10);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+unsigned long strtoul_or_err(const char *str, const char *errmesg)
+{
+	unsigned long num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtoul(str, &end, 10);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+uintmax_t strtosize_or_err(const char *str, const char *errmesg)
+{
+	uintmax_t num;
+
+	if (strtosize(str, &num) == 0)
+		return num;
+
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+/*
+ * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
+ * be 10 bytes.
+ */
+void strmode(mode_t mode, char *str)
+{
+	if (S_ISDIR(mode))
+		str[0] = 'd';
+	else if (S_ISLNK(mode))
+		str[0] = 'l';
+	else if (S_ISCHR(mode))
+		str[0] = 'c';
+	else if (S_ISBLK(mode))
+		str[0] = 'b';
+	else if (S_ISSOCK(mode))
+		str[0] = 's';
+	else if (S_ISFIFO(mode))
+		str[0] = 'p';
+	else if (S_ISREG(mode))
+		str[0] = '-';
+
+	str[1] = mode & S_IRUSR ? 'r' : '-';
+	str[2] = mode & S_IWUSR ? 'w' : '-';
+	str[3] = (mode & S_ISUID
+		? (mode & S_IXUSR ? 's' : 'S')
+		: (mode & S_IXUSR ? 'x' : '-'));
+	str[4] = mode & S_IRGRP ? 'r' : '-';
+	str[5] = mode & S_IWGRP ? 'w' : '-';
+	str[6] = (mode & S_ISGID
+		? (mode & S_IXGRP ? 's' : 'S')
+		: (mode & S_IXGRP ? 'x' : '-'));
+	str[7] = mode & S_IROTH ? 'r' : '-';
+	str[8] = mode & S_IWOTH ? 'w' : '-';
+	str[9] = (mode & S_ISVTX
+		? (mode & S_IXOTH ? 't' : 'T')
+		: (mode & S_IXOTH ? 'x' : '-'));
+	str[10] = '\0';
+}
+
+/*
+ * returns exponent (2^x=n) in range KiB..PiB
+ */
+static int get_exp(uint64_t n)
+{
+	int shft;
+
+	for (shft = 10; shft <= 60; shft += 10) {
+		if (n < (1ULL << shft))
+			break;
+	}
+	return shft - 10;
+}
+
+char *size_to_human_string(int options, uint64_t bytes)
+{
+	char buf[32];
+	int dec, exp;
+	uint64_t frac;
+	const char *letters = "BKMGTPE";
+	char suffix[sizeof(" KiB")], *psuf = suffix;
+	char c;
+
+	if (options & SIZE_SUFFIX_SPACE)
+		*psuf++ = ' ';
+
+	exp  = get_exp(bytes);
+	c    = *(letters + (exp ? exp / 10 : 0));
+	dec  = exp ? bytes / (1ULL << exp) : bytes;
+	frac = exp ? bytes % (1ULL << exp) : 0;
+
+	*psuf++ = c;
+
+	if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
+		*psuf++ = 'i';
+		*psuf++ = 'B';
+	}
+
+	*psuf = '\0';
+
+	/* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
+	 *                 exp, suffix[0], dec, frac);
+	 */
+
+	if (frac) {
+		/* round */
+		frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
+		if (frac == 10)
+			dec++, frac = 0;
+	}
+
+	if (frac) {
+		struct lconv const *l = localeconv();
+		char *dp = l ? l->decimal_point : NULL;
+
+		if (!dp || !*dp)
+			dp = ".";
+		snprintf(buf, sizeof(buf), "%d%s%jd%s", dec, dp, frac, suffix);
+	} else
+		snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
+
+	return strdup(buf);
+}
+
+/*
+ * Parses comma delimited list to array with IDs, for example:
+ *
+ * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
+ *                   ary[1] = FOO_BBB;
+ *                   ary[3] = FOO_CCC;
+ *
+ * The function name2id() provides conversion from string to ID.
+ *
+ * Returns: >= 0  : number of items added to ary[]
+ *            -1  : parse error or unknown item
+ *            -2  : arysz reached
+ */
+int string_to_idarray(const char *list, int ary[], size_t arysz,
+			int (name2id)(const char *, size_t))
+{
+	const char *begin = NULL, *p;
+	size_t n = 0;
+
+	if (!list || !*list || !ary || !arysz || !name2id)
+		return -1;
+
+	for (p = list; p && *p; p++) {
+		const char *end = NULL;
+		int id;
+
+		if (n >= arysz)
+			return -2;
+		if (!begin)
+			begin = p;		/* begin of the column name */
+		if (*p == ',')
+			end = p;		/* terminate the name */
+		if (*(p + 1) == '\0')
+			end = p + 1;		/* end of string */
+		if (!begin || !end)
+			continue;
+		if (end <= begin)
+			return -1;
+
+		id = name2id(begin, end - begin);
+		if (id == -1)
+			return -1;
+		ary[ n++ ] = id;
+		begin = NULL;
+		if (end && !*end)
+			break;
+	}
+	return n;
+}
+
+/*
+ * Parses the array like string_to_idarray but if format is "+aaa,bbb"
+ * it adds fields to array instead of replacing them.
+ */
+int string_add_to_idarray(const char *list, int ary[], size_t arysz,
+			int *ary_pos, int (name2id)(const char *, size_t))
+{
+	const char *list_add;
+	int r;
+
+	if (!list || !*list || !ary_pos ||
+	    *ary_pos < 0 || (size_t) *ary_pos > arysz)
+		return -1;
+
+	if (list[0] == '+')
+		list_add = &list[1];
+	else {
+		list_add = list;
+		*ary_pos = 0;
+	}
+
+	r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
+	if (r > 0)
+		*ary_pos += r;
+	return r;
+}
+
+/*
+ * LIST ::= <item> [, <item>]
+ *
+ * The <item> is translated to 'id' by name2id() function and the 'id' is used
+ * as a position in the 'ary' bit array. It means that the 'id' has to be in
+ * range <0..N> where N < sizeof(ary) * NBBY.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int string_to_bitarray(const char *list,
+		     char *ary,
+		     int (*name2bit)(const char *, size_t))
+{
+	const char *begin = NULL, *p;
+
+	if (!list || !name2bit || !ary)
+		return -EINVAL;
+
+	for (p = list; p && *p; p++) {
+		const char *end = NULL;
+		int bit;
+
+		if (!begin)
+			begin = p;		/* begin of the level name */
+		if (*p == ',')
+			end = p;		/* terminate the name */
+		if (*(p + 1) == '\0')
+			end = p + 1;		/* end of string */
+		if (!begin || !end)
+			continue;
+		if (end <= begin)
+			return -1;
+
+		bit = name2bit(begin, end - begin);
+		if (bit < 0)
+			return bit;
+		setbit(ary, bit);
+		begin = NULL;
+		if (end && !*end)
+			break;
+	}
+	return 0;
+}
+
+/*
+ * LIST ::= <item> [, <item>]
+ *
+ * The <item> is translated to 'id' by name2flag() function and the flags is
+ * set to the 'mask'
+*
+ * Returns: 0 on success, <0 on error.
+ */
+int string_to_bitmask(const char *list,
+		     unsigned long *mask,
+		     long (*name2flag)(const char *, size_t))
+{
+	const char *begin = NULL, *p;
+
+	if (!list || !name2flag || !mask)
+		return -EINVAL;
+
+	for (p = list; p && *p; p++) {
+		const char *end = NULL;
+		long flag;
+
+		if (!begin)
+			begin = p;		/* begin of the level name */
+		if (*p == ',')
+			end = p;		/* terminate the name */
+		if (*(p + 1) == '\0')
+			end = p + 1;		/* end of string */
+		if (!begin || !end)
+			continue;
+		if (end <= begin)
+			return -1;
+
+		flag = name2flag(begin, end - begin);
+		if (flag < 0)
+			return flag;	/* error */
+		*mask |= flag;
+		begin = NULL;
+		if (end && !*end)
+			break;
+	}
+	return 0;
+}
+
+/*
+ * Parse the lower and higher values in a string containing
+ * "lower:higher" or "lower-higher" format. Note that either
+ * the lower or the higher values may be missing, and the def
+ * value will be assigned to it by default.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int parse_range(const char *str, int *lower, int *upper, int def)
+{
+	char *end = NULL;
+
+	if (!str)
+		return 0;
+
+	*upper = *lower = def;
+	errno = 0;
+
+	if (*str == ':') {				/* <:N> */
+		str++;
+		*upper = strtol(str, &end, 10);
+		if (errno || !end || *end || end == str)
+			return -1;
+	} else {
+		*upper = *lower = strtol(str, &end, 10);
+		if (errno || !end || end == str)
+			return -1;
+
+		if (*end == ':' && !*(end + 1))		/* <M:> */
+			*upper = 0;
+		else if (*end == '-' || *end == ':') {	/* <M:N> <M-N> */
+			str = end + 1;
+			end = NULL;
+			errno = 0;
+			*upper = strtol(str, &end, 10);
+
+			if (errno || !end || *end || end == str)
+				return -1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Compare two strings for equality, ignoring at most one trailing
+ * slash.
+ */
+int streq_except_trailing_slash(const char *s1, const char *s2)
+{
+	int equal;
+
+	if (!s1 && !s2)
+		return 1;
+	if (!s1 || !s2)
+		return 0;
+
+	equal = !strcmp(s1, s2);
+
+	if (!equal) {
+		size_t len1 = strlen(s1);
+		size_t len2 = strlen(s2);
+
+		if (len1 && *(s1 + len1 - 1) == '/')
+			len1--;
+		if (len2 && *(s2 + len2 - 1) == '/')
+			len2--;
+		if (len1 != len2)
+			return 0;
+
+		equal = !strncmp(s1, s2, len1);
+	}
+
+	return equal;
+}
+
+
+#ifdef TEST_PROGRAM
+
+int main(int argc, char *argv[])
+{
+	uintmax_t size = 0;
+	char *hum, *hum2;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <number>[suffix]\n",	argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if (strtosize(argv[1], &size))
+		errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
+
+	hum = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
+	hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
+				    SIZE_SUFFIX_SPACE, size);
+
+	printf("%25s : %20ju : %8s : %12s\n", argv[1], size, hum, hum2);
+	free(hum);
+	free(hum2);
+
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/strutils.h b/libblkid/strutils.h
new file mode 100644
index 0000000..00598cf
--- /dev/null
+++ b/libblkid/strutils.h
@@ -0,0 +1,79 @@
+#ifndef UTIL_LINUX_STRUTILS
+#define UTIL_LINUX_STRUTILS
+
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+
+/* default strtoxx_or_err() exit code */
+#ifndef STRTOXX_EXIT_CODE
+# define STRTOXX_EXIT_CODE EXIT_FAILURE
+#endif
+
+
+extern int strtosize(const char *str, uintmax_t *res);
+extern uintmax_t strtosize_or_err(const char *str, const char *errmesg);
+
+extern int16_t strtos16_or_err(const char *str, const char *errmesg);
+extern uint16_t strtou16_or_err(const char *str, const char *errmesg);
+
+extern int32_t strtos32_or_err(const char *str, const char *errmesg);
+extern uint32_t strtou32_or_err(const char *str, const char *errmesg);
+
+extern int64_t strtos64_or_err(const char *str, const char *errmesg);
+extern uint64_t strtou64_or_err(const char *str, const char *errmesg);
+
+extern double strtod_or_err(const char *str, const char *errmesg);
+
+extern long strtol_or_err(const char *str, const char *errmesg);
+extern unsigned long strtoul_or_err(const char *str, const char *errmesg);
+
+#ifndef HAVE_MEMPCPY
+extern void *mempcpy(void *restrict dest, const void *restrict src, size_t n);
+#endif
+#ifndef HAVE_STRNLEN
+extern size_t strnlen(const char *s, size_t maxlen);
+#endif
+#ifndef HAVE_STRNDUP
+extern char *strndup(const char *s, size_t n);
+#endif
+#ifndef HAVE_STRNCHR
+extern char *strnchr(const char *s, size_t maxlen, int c);
+#endif
+
+/* caller guarantees n > 0 */
+static inline void xstrncpy(char *dest, const char *src, size_t n)
+{
+	strncpy(dest, src, n-1);
+	dest[n-1] = 0;
+}
+
+extern void strmode(mode_t mode, char *str);
+
+/* Options for size_to_human_string() */
+enum
+{
+        SIZE_SUFFIX_1LETTER = 0,
+        SIZE_SUFFIX_3LETTER = 1,
+        SIZE_SUFFIX_SPACE   = 2
+};
+
+extern char *size_to_human_string(int options, uint64_t bytes);
+
+extern int string_to_idarray(const char *list, int ary[], size_t arysz,
+			   int (name2id)(const char *, size_t));
+extern int string_add_to_idarray(const char *list, int ary[],
+				 size_t arysz, int *ary_pos,
+				 int (name2id)(const char *, size_t));
+
+extern int string_to_bitarray(const char *list, char *ary,
+			    int (*name2bit)(const char *, size_t));
+
+extern int string_to_bitmask(const char *list,
+			     unsigned long *mask,
+			     long (*name2flag)(const char *, size_t));
+extern int parse_range(const char *str, int *lower, int *upper, int def);
+
+extern int streq_except_trailing_slash(const char *s1, const char *s2);
+
+#endif
diff --git a/libblkid/sun.c b/libblkid/sun.c
new file mode 100644
index 0000000..f151f46
--- /dev/null
+++ b/libblkid/sun.c
@@ -0,0 +1,188 @@
+/*
+ * sun (solaris-sparc) partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "partitions.h"
+
+/* Supported VTOC setting */
+#define SUN_VTOC_SANITY		0x600DDEEE	/* magic number */
+#define SUN_VTOC_VERSION	1
+
+#define SUN_MAXPARTITIONS	8
+
+/* Partition IDs */
+#define SUN_TAG_WHOLEDISK          0x05
+
+struct sun_disklabel {
+	unsigned char info[128];   /* Informative text string */
+
+	struct sun_vtoc {
+		uint32_t version;     /* version */
+		char	 volume[8];   /* volume name */
+		uint16_t nparts;      /* num of partitions */
+
+		struct sun_info {     /* partition information */
+			uint16_t id;  /* tag */
+			uint16_t flags;
+		} __attribute__ ((packed)) infos[8];
+
+		uint16_t padding;      /* padding */
+		uint32_t bootinfo[3];  /* info needed by mboot */
+		uint32_t sanity;       /* magic number */
+		uint32_t reserved[10]; /* padding */
+		uint32_t timestamp[8]; /* partition timestamp */
+	} __attribute__ ((packed)) vtoc;
+
+	uint32_t write_reinstruct;     /* sectors to skip, writes */
+	uint32_t read_reinstruct;      /* sectors to skip, reads */
+	unsigned char spare[148];      /* padding */
+	uint16_t rspeed;               /* disk rotational speed */
+	uint16_t pcylcount;            /* physical cylinder count */
+	uint16_t sparecyl;             /* extra sects per cylinder */
+	uint16_t obs1;
+	uint16_t obs2;
+	uint16_t ilfact;               /* interleave factor */
+	uint16_t ncyl;                 /* data cylinder count */
+	uint16_t nacyl;                /* alt. cylinder count */
+	uint16_t ntrks;                /* tracks per cylinder   <---- */
+	uint16_t nsect;                /* sectors per track     <---- */
+	uint16_t obs3;
+	uint16_t obs4;
+
+	struct sun_partition {         /* partitions */
+		uint32_t start_cylinder;
+		uint32_t num_sectors;
+	} __attribute__ ((packed)) partitions[8];
+
+	uint16_t magic;                /* magic number */
+	uint16_t csum;                 /* label xor'd checksum */
+} __attribute__ ((packed));
+
+
+uint16_t count_checksum(struct sun_disklabel *label)
+{
+	uint16_t *ptr = ((uint16_t *) (label + 1)) - 1;
+	uint16_t sum;
+
+	for (sum = 0; ptr >= ((uint16_t *) label);)
+		sum ^= *ptr--;
+
+	return sum;
+}
+
+static int probe_sun_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct sun_disklabel *l;
+	struct sun_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	uint16_t nparts;
+	blkid_loff_t spc;
+	int i, use_vtoc;
+
+	l = (struct sun_disklabel *) blkid_probe_get_sector(pr, 0);
+	if (!l)
+		goto nothing;
+
+	if (count_checksum(l)) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"detected corrupted sun disk label -- ignore\n"));
+		goto nothing;
+	}
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "sun", 0);
+	if (!tab)
+		goto err;
+
+	/* sectors per cylinder (partition offset is in cylinders...) */
+	spc = be16_to_cpu(l->ntrks) * be16_to_cpu(l->nsect);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("Sun VTOC sanity=%u version=%u nparts=%u\n",
+			be32_to_cpu(l->vtoc.sanity),
+			be32_to_cpu(l->vtoc.version),
+			be16_to_cpu(l->vtoc.nparts)));
+
+	/* Check to see if we can use the VTOC table */
+	use_vtoc = ((be32_to_cpu(l->vtoc.sanity) == SUN_VTOC_SANITY) &&
+		    (be32_to_cpu(l->vtoc.version) == SUN_VTOC_VERSION) &&
+		    (be16_to_cpu(l->vtoc.nparts) <= SUN_MAXPARTITIONS));
+
+	/* Use 8 partition entries if not specified in validated VTOC */
+	nparts = use_vtoc ? be16_to_cpu(l->vtoc.nparts) : SUN_MAXPARTITIONS;
+
+	/*
+	 * So that old Linux-Sun partitions continue to work,
+	 * alow the VTOC to be used under the additional condition ...
+	 */
+	use_vtoc = use_vtoc || !(l->vtoc.sanity || l->vtoc.version || l->vtoc.nparts);
+
+	for (i = 0, p = l->partitions; i < nparts; i++, p++) {
+
+		blkid_loff_t start, size;
+		uint16_t type = 0, flags = 0;
+		blkid_partition par;
+
+                start = be32_to_cpu(p->start_cylinder) * spc;
+		size = be32_to_cpu(p->num_sectors);
+		if (use_vtoc) {
+			type = be16_to_cpu(l->vtoc.infos[i].id);
+			flags = be16_to_cpu(l->vtoc.infos[i].flags);
+		}
+
+		if (type == SUN_TAG_WHOLEDISK || !size) {
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		if (type)
+			blkid_partition_set_type(par, type);
+		if (flags)
+			blkid_partition_set_flags(par, flags);
+	}
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+const struct blkid_idinfo sun_pt_idinfo =
+{
+	.name		= "sun",
+	.probefunc	= probe_sun_pt,
+	.magics		=
+	{
+		{
+		  .magic = "\xDA\xBE",		/* big-endian magic string */
+		  .len = 2,
+		  .sboff = offsetof(struct sun_disklabel, magic)
+		},
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/superblocks.c b/libblkid/superblocks.c
new file mode 100644
index 0000000..384d2ca
--- /dev/null
+++ b/libblkid/superblocks.c
@@ -0,0 +1,804 @@
+/*
+ * superblocks.c - reads information from filesystem and raid superblocks
+ *
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include "superblocks.h"
+
+/**
+ * SECTION:superblocks
+ * @title: Superblocks probing
+ * @short_description: filesystems and raids superblocks probing.
+ *
+ * The library API has been originally designed for superblocks probing only.
+ * This is reason why some *deprecated* superblock specific functions don't use
+ * '_superblocks_' namespace in the function name. Please, don't use these
+ * functions in new code.
+ *
+ * The 'superblocks' probers support NAME=value (tags) interface only. The
+ * superblocks probing is enabled by default (and controlled by
+ * blkid_probe_enable_superblocks()).
+ *
+ * Currently supported tags:
+ *
+ * @TYPE: filesystem type
+ *
+ * @SEC_TYPE: secondary filesystem type
+ *
+ * @LABEL: filesystem label
+ *
+ * @LABEL_RAW: raw label from FS superblock
+ *
+ * @UUID: filesystem UUID (lower case)
+ *
+ * @UUID_SUB: subvolume uuid (e.g. btrfs)
+ *
+ * @UUID_RAW: raw UUID from FS superblock
+ *
+ * @EXT_JOURNAL: external journal UUID
+ *
+ * @USAGE:  usage string: "raid", "filesystem", ...
+ *
+ * @VERSION: filesystem version
+ *
+ * @MOUNT: cluster mount name (?) -- ocfs only
+ *
+ * @SBMAGIC: super block magic string
+ *
+ * @SBMAGIC_OFFSET: offset of SBMAGIC
+ *
+ * @FSSIZE: size of filessystem [not-implemented yet]
+ *
+ * @SYSTEM_ID: ISO9660 system identifier
+ *
+ * @PUBLISHER_ID: ISO9660 publisher identifier
+ *
+ * @APPLICATION_ID: ISO9660 application identifier
+ *
+ * @BOOT_SYSTEM_ID: ISO9660 boot system identifier
+ */
+
+static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn);
+static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn);
+
+static int blkid_probe_set_usage(blkid_probe pr, int usage);
+
+
+/*
+ * Superblocks chains probing functions
+ */
+static const struct blkid_idinfo *idinfos[] =
+{
+	/* RAIDs */
+	&linuxraid_idinfo,
+	&ddfraid_idinfo,
+	&iswraid_idinfo,
+	&lsiraid_idinfo,
+	&viaraid_idinfo,
+	&silraid_idinfo,
+	&nvraid_idinfo,
+	&pdcraid_idinfo,
+	&highpoint45x_idinfo,
+	&highpoint37x_idinfo,
+	&adraid_idinfo,
+	&jmraid_idinfo,
+
+	&drbd_idinfo,
+	&drbdproxy_datalog_idinfo,
+	&lvm2_idinfo,
+	&lvm1_idinfo,
+	&snapcow_idinfo,
+	&verity_hash_idinfo,
+	&luks_idinfo,
+	&vmfs_volume_idinfo,
+
+	/* Filesystems */
+	&vfat_idinfo,
+	&swsuspend_idinfo,
+	&swap_idinfo,
+	&xfs_idinfo,
+	&ext4dev_idinfo,
+	&ext4_idinfo,
+	&ext3_idinfo,
+	&ext2_idinfo,
+	&jbd_idinfo,
+	&reiser_idinfo,
+	&reiser4_idinfo,
+	&jfs_idinfo,
+	&udf_idinfo,
+	&iso9660_idinfo,
+	&zfs_idinfo,
+	&hfsplus_idinfo,
+	&hfs_idinfo,
+	&ufs_idinfo,
+	&hpfs_idinfo,
+	&sysv_idinfo,
+        &xenix_idinfo,
+	&ntfs_idinfo,
+	&cramfs_idinfo,
+	&romfs_idinfo,
+	&minix_idinfo,
+	&gfs_idinfo,
+	&gfs2_idinfo,
+	&ocfs_idinfo,
+	&ocfs2_idinfo,
+	&oracleasm_idinfo,
+	&vxfs_idinfo,
+	&squashfs_idinfo,
+	&netware_idinfo,
+	&btrfs_idinfo,
+	&ubifs_idinfo,
+	&bfs_idinfo,
+	&vmfs_fs_idinfo,
+	&befs_idinfo,
+	&nilfs2_idinfo,
+	&exfat_idinfo,
+	&f2fs_idinfo
+};
+
+/*
+ * Driver definition
+ */
+const struct blkid_chaindrv superblocks_drv = {
+	.id           = BLKID_CHAIN_SUBLKS,
+	.name         = "superblocks",
+	.dflt_enabled = TRUE,
+	.dflt_flags   = BLKID_SUBLKS_DEFAULT,
+	.idinfos      = idinfos,
+	.nidinfos     = ARRAY_SIZE(idinfos),
+	.has_fltr     = TRUE,
+	.probe        = superblocks_probe,
+	.safeprobe    = superblocks_safeprobe,
+};
+
+/**
+ * blkid_probe_enable_superblocks:
+ * @pr: probe
+ * @enable: TRUE/FALSE
+ *
+ * Enables/disables the superblocks probing for non-binary interface.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_enable_superblocks(blkid_probe pr, int enable)
+{
+	if (!pr)
+		return -1;
+	pr->chains[BLKID_CHAIN_SUBLKS].enabled = enable;
+	return 0;
+}
+
+/**
+ * blkid_probe_set_superblocks_flags:
+ * @pr: prober
+ * @flags: BLKID_SUBLKS_* flags
+ *
+ * Sets probing flags to the superblocks prober. This function is optional, the
+ * default are BLKID_SUBLKS_DEFAULTS flags.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_set_superblocks_flags(blkid_probe pr, int flags)
+{
+	if (!pr)
+		return -1;
+
+	pr->chains[BLKID_CHAIN_SUBLKS].flags = flags;
+	return 0;
+}
+
+/**
+ * blkid_probe_reset_superblocks_filter:
+ * @pr: prober
+ *
+ * Resets superblocks probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_reset_superblocks_filter(blkid_probe pr)
+{
+	return __blkid_probe_reset_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_invert_superblocks_filter:
+ * @pr: prober
+ *
+ * Inverts superblocks probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_invert_superblocks_filter(blkid_probe pr)
+{
+	return __blkid_probe_invert_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_filter_superblocks_type:
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @names: NULL terminated array of probing function names (e.g. "vfat").
+ *
+ *  %BLKID_FLTR_NOTIN  - probe for all items which are NOT IN @names;
+ *
+ *  %BLKID_FLTR_ONLYIN - probe for items which are IN @names
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_filter_superblocks_type(blkid_probe pr, int flag, char *names[])
+{
+	return __blkid_probe_filter_types(pr, BLKID_CHAIN_SUBLKS, flag, names);
+}
+
+/**
+ * blkid_probe_filter_superblocks_usage:
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @usage: BLKID_USAGE_* flags
+ *
+ *  %BLKID_FLTR_NOTIN  - probe for all items which are NOT IN @usage;
+ *
+ *  %BLKID_FLTR_ONLYIN - probe for items which are IN @usage
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_filter_superblocks_usage(blkid_probe pr, int flag, int usage)
+{
+	unsigned long *fltr;
+	struct blkid_chain *chn;
+	size_t i;
+
+	fltr = blkid_probe_get_filter(pr, BLKID_CHAIN_SUBLKS, TRUE);
+	if (!fltr)
+		return -1;
+
+	chn = &pr->chains[BLKID_CHAIN_SUBLKS];
+
+	for (i = 0; i < chn->driver->nidinfos; i++) {
+		const struct blkid_idinfo *id = chn->driver->idinfos[i];
+
+		if (id->usage & usage) {
+			if (flag & BLKID_FLTR_NOTIN)
+				blkid_bmp_set_item(chn->fltr, i);
+		} else if (flag & BLKID_FLTR_ONLYIN)
+			blkid_bmp_set_item(chn->fltr, i);
+	}
+	DBG(DEBUG_LOWPROBE, printf("a new probing usage-filter initialized\n"));
+	return 0;
+}
+
+/**
+ * blkid_known_fstype:
+ * @fstype: filesystem name
+ *
+ * Returns: 1 for known filesytems, or 0 for unknown filesystem.
+ */
+int blkid_known_fstype(const char *fstype)
+{
+	size_t i;
+
+	if (!fstype)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
+		const struct blkid_idinfo *id = idinfos[i];
+		if (strcmp(id->name, fstype) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+/**
+ * blkid_superblocks_get_name:
+ * @idx: number >= 0
+ * @name: returns name of supported filesystem/raid (optional)
+ * @usage: returns BLKID_USAGE_* flags, (optional)
+ *
+ * Returns: -1 if @idx is out of range, or 0 on success.
+ */
+int blkid_superblocks_get_name(size_t idx, const char **name, int *usage)
+{
+	if (idx < ARRAY_SIZE(idinfos)) {
+		if (name)
+			*name = idinfos[idx]->name;
+		if (usage)
+			*usage = idinfos[idx]->usage;
+		return 0;
+	}
+	return -1;
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+	size_t i;
+
+	if (!pr || chn->idx < -1)
+		return -1;
+	blkid_probe_chain_reset_vals(pr, chn);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("--> starting probing loop [SUBLKS idx=%d]\n",
+		chn->idx));
+
+	if (pr->size <= 0 || (pr->size <= 1024 && !S_ISCHR(pr->mode)))
+		/* Ignore very very small block devices or regular files (e.g.
+		 * extended partitions). Note that size of the UBI char devices
+		 * is 1 byte */
+		goto nothing;
+
+	i = chn->idx < 0 ? 0 : chn->idx + 1U;
+
+	for ( ; i < ARRAY_SIZE(idinfos); i++) {
+		const struct blkid_idinfo *id;
+		const struct blkid_idmag *mag = NULL;
+		blkid_loff_t off = 0;
+		int rc = 0;
+
+		chn->idx = i;
+		id = idinfos[i];
+
+		if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) {
+			DBG(DEBUG_LOWPROBE, printf("filter out: %s\n", id->name));
+			continue;
+		}
+
+		if (id->minsz && id->minsz > pr->size)
+			continue;	/* the device is too small */
+
+		/* don't probe for RAIDs, swap or journal on CD/DVDs */
+		if ((id->usage & (BLKID_USAGE_RAID | BLKID_USAGE_OTHER)) &&
+		    blkid_probe_is_cdrom(pr))
+			continue;
+
+		/* don't probe for RAIDs on floppies */
+		if ((id->usage & BLKID_USAGE_RAID) && blkid_probe_is_tiny(pr))
+			continue;
+
+		DBG(DEBUG_LOWPROBE, printf("[%zd] %s:\n", i, id->name));
+
+		if (blkid_probe_get_idmag(pr, id, &off, &mag))
+			continue;
+
+		/* final check by probing function */
+		if (id->probefunc) {
+			DBG(DEBUG_LOWPROBE, printf("\tcall probefunc()\n"));
+			if (id->probefunc(pr, mag) != 0) {
+				blkid_probe_chain_reset_vals(pr, chn);
+				continue;
+			}
+		}
+
+		/* all cheks passed */
+		if (chn->flags & BLKID_SUBLKS_TYPE)
+			rc = blkid_probe_set_value(pr, "TYPE",
+				(unsigned char *) id->name,
+				strlen(id->name) + 1);
+
+		if (!rc)
+			rc = blkid_probe_set_usage(pr, id->usage);
+
+		if (!rc && mag)
+			rc = blkid_probe_set_magic(pr, off, mag->len,
+					(unsigned char *) mag->magic);
+		if (rc) {
+			blkid_probe_chain_reset_vals(pr, chn);
+			DBG(DEBUG_LOWPROBE, printf("failed to set result -- ingnore\n"));
+			continue;
+		}
+
+		DBG(DEBUG_LOWPROBE,
+			printf("<-- leaving probing loop (type=%s) [SUBLKS idx=%d]\n",
+			id->name, chn->idx));
+		return 0;
+	}
+
+nothing:
+	DBG(DEBUG_LOWPROBE,
+		printf("<-- leaving probing loop (failed) [SUBLKS idx=%d]\n",
+		chn->idx));
+	return 1;
+}
+
+/*
+ * This is the same function as blkid_do_probe(), but returns only one result
+ * (cannot be used in while()) and checks for ambivalen results (more
+ * filesystems on the device) -- in such case returns -2.
+ *
+ * The function does not check for filesystems when a RAID or crypto signature
+ * is detected.  The function also does not check for collision between RAIDs
+ * and crypto devices. The first detected RAID or crypto device is returned.
+ *
+ * The function does not probe for ambivalent results on very small devices
+ * (e.g. floppies), on small devices the first detected filesystem is returned.
+ */
+static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn)
+{
+	struct blkid_prval vals[BLKID_NVALS_SUBLKS];
+	int nvals = BLKID_NVALS_SUBLKS;
+	int idx = -1;
+	int count = 0;
+	int intol = 0;
+	int rc;
+
+	while ((rc = superblocks_probe(pr, chn)) == 0) {
+
+		if (blkid_probe_is_tiny(pr) && !count)
+			/* floppy or so -- returns the first result. */
+			return 0;
+
+		count++;
+
+		if (chn->idx >= 0 &&
+		    idinfos[chn->idx]->usage & (BLKID_USAGE_RAID | BLKID_USAGE_CRYPTO))
+			break;
+
+		if (chn->idx >= 0 &&
+		    !(idinfos[chn->idx]->flags & BLKID_IDINFO_TOLERANT))
+			intol++;
+
+		if (count == 1) {
+			/* save the first result */
+			nvals = blkid_probe_chain_copy_vals(pr, chn, vals, nvals);
+			idx = chn->idx;
+		}
+	}
+
+	if (rc < 0)
+		return rc;		/* error */
+
+	if (count > 1 && intol) {
+		DBG(DEBUG_LOWPROBE,
+			printf("ERROR: superblocks chain: "
+			       "ambivalent result detected (%d filesystems)!\n",
+			       count));
+		return -2;		/* error, ambivalent result (more FS) */
+	}
+	if (!count)
+		return 1;		/* nothing detected */
+
+	if (idx != -1) {
+		/* restore the first result */
+		blkid_probe_chain_reset_vals(pr, chn);
+		blkid_probe_append_vals(pr, vals, nvals);
+		chn->idx = idx;
+	}
+
+	/*
+	 * The RAID device could be partitioned. The problem are RAID1 devices
+	 * where the partition table is visible from underlaying devices. We
+	 * have to ignore such partition tables.
+	 */
+	if (chn->idx >= 0 && idinfos[chn->idx]->usage & BLKID_USAGE_RAID)
+		pr->prob_flags |= BLKID_PROBE_FL_IGNORE_PT;
+
+	return 0;
+}
+
+int blkid_probe_set_version(blkid_probe pr, const char *version)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	if (chn->flags & BLKID_SUBLKS_VERSION)
+		return blkid_probe_set_value(pr, "VERSION",
+			   (unsigned char *) version, strlen(version) + 1);
+	return 0;
+}
+
+
+int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	int rc = 0;
+
+	if (chn->flags & BLKID_SUBLKS_VERSION) {
+		va_list ap;
+
+		va_start(ap, fmt);
+		rc = blkid_probe_vsprintf_value(pr, "VERSION", fmt, ap);
+		va_end(ap);
+	}
+	return rc;
+}
+
+static int blkid_probe_set_usage(blkid_probe pr, int usage)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	char *u = NULL;
+
+	if (!(chn->flags & BLKID_SUBLKS_USAGE))
+		return 0;
+
+	if (usage & BLKID_USAGE_FILESYSTEM)
+		u = "filesystem";
+	else if (usage & BLKID_USAGE_RAID)
+		u = "raid";
+	else if (usage & BLKID_USAGE_CRYPTO)
+		u = "crypto";
+	else if (usage & BLKID_USAGE_OTHER)
+		u = "other";
+	else
+		u = "unknown";
+
+	return blkid_probe_set_value(pr, "USAGE", (unsigned char *) u, strlen(u) + 1);
+}
+
+int blkid_probe_set_id_label(blkid_probe pr, const char *name,
+			     unsigned char *data, size_t len)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+
+	if (!(chn->flags & BLKID_SUBLKS_LABEL))
+		return 0;
+
+	v = blkid_probe_assign_value(pr, name);
+	if (!v)
+		return -1;
+
+	if (len >= BLKID_PROBVAL_BUFSIZ)
+		len = BLKID_PROBVAL_BUFSIZ - 1;			/* make a space for \0 */
+
+	memcpy(v->data, data, len);
+	v->data[len] = '\0';
+
+	/* remove white spaces */
+	v->len = blkid_rtrim_whitespace(v->data) + 1;
+	if (v->len > 1)
+		v->len = blkid_ltrim_whitespace(v->data) + 1;
+
+	if (v->len <= 1)
+		blkid_probe_reset_last_value(pr);		/* ignore empty */
+	return 0;
+}
+
+int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+	if (len > BLKID_PROBVAL_BUFSIZ)
+		len = BLKID_PROBVAL_BUFSIZ;
+
+	if ((chn->flags & BLKID_SUBLKS_LABELRAW) &&
+	    blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0)
+		return -1;
+	if (!(chn->flags & BLKID_SUBLKS_LABEL))
+		return 0;
+	v = blkid_probe_assign_value(pr, "LABEL");
+	if (!v)
+		return -1;
+
+	if (len == BLKID_PROBVAL_BUFSIZ)
+		len--;				/* make a space for \0 */
+
+	memcpy(v->data, label, len);
+	v->data[len] = '\0';
+
+	v->len = blkid_rtrim_whitespace(v->data) + 1;
+	if (v->len == 1)
+		blkid_probe_reset_last_value(pr);
+	return 0;
+}
+
+int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label,
+				size_t len, int enc)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+
+	if ((chn->flags & BLKID_SUBLKS_LABELRAW) &&
+	    blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0)
+		return -1;
+	if (!(chn->flags & BLKID_SUBLKS_LABEL))
+		return 0;
+	v = blkid_probe_assign_value(pr, "LABEL");
+	if (!v)
+		return -1;
+
+	blkid_encode_to_utf8(enc, v->data, sizeof(v->data), label, len);
+	v->len = blkid_rtrim_whitespace(v->data) + 1;
+	if (v->len == 1)
+		blkid_probe_reset_last_value(pr);
+	return 0;
+}
+
+/* like uuid_is_null() from libuuid, but works with arbitrary size of UUID */
+static int uuid_is_empty(const unsigned char *buf, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		if (buf[i])
+			return 0;
+	return 1;
+}
+
+int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid,
+				size_t len, const char *fmt, ...)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	int rc = -1;
+	va_list ap;
+
+	if (len > BLKID_PROBVAL_BUFSIZ)
+		len = BLKID_PROBVAL_BUFSIZ;
+
+	if (uuid_is_empty(uuid, len))
+		return 0;
+
+	if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+	    blkid_probe_set_value(pr, "UUID_RAW", uuid, len) < 0)
+		return -1;
+	if (!(chn->flags & BLKID_SUBLKS_UUID))
+		return 0;
+
+	va_start(ap, fmt);
+	rc = blkid_probe_vsprintf_value(pr, "UUID", fmt, ap);
+	va_end(ap);
+
+	/* convert to lower case (..be paranoid) */
+	if (!rc) {
+		size_t i;
+		struct blkid_prval *v = __blkid_probe_get_value(pr,
+						blkid_probe_numof_values(pr));
+		if (v) {
+			for (i = 0; i < v->len; i++)
+				if (v->data[i] >= 'A' && v->data[i] <= 'F')
+					v->data[i] = (v->data[i] - 'A') + 'a';
+		}
+	}
+	return rc;
+}
+
+/* function to set UUIDs that are in suberblocks stored as strings */
+int blkid_probe_strncpy_uuid(blkid_probe pr, unsigned char *str, size_t len)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+
+	if (str == NULL || *str == '\0')
+		return -1;
+	if (!len)
+		len = strlen((char *) str);
+	if (len > BLKID_PROBVAL_BUFSIZ)
+		len = BLKID_PROBVAL_BUFSIZ;
+
+	if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+	    blkid_probe_set_value(pr, "UUID_RAW", str, len) < 0)
+		return -1;
+	if (!(chn->flags & BLKID_SUBLKS_UUID))
+		return 0;
+
+	v = blkid_probe_assign_value(pr, "UUID");
+	if (v) {
+		if (len == BLKID_PROBVAL_BUFSIZ)
+			len--;		/* make a space for \0 */
+
+		memcpy((char *) v->data, str, len);
+		v->data[len] = '\0';
+		v->len = len + 1;
+		return 0;
+	}
+	return -1;
+}
+
+/* default _set_uuid function to set DCE UUIDs */
+int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+
+	if (uuid_is_empty(uuid, 16))
+		return 0;
+
+	if (!name) {
+		if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+		    blkid_probe_set_value(pr, "UUID_RAW", uuid, 16) < 0)
+			return -1;
+		if (!(chn->flags & BLKID_SUBLKS_UUID))
+			return 0;
+
+		v = blkid_probe_assign_value(pr, "UUID");
+	} else
+		v = blkid_probe_assign_value(pr, name);
+
+	blkid_unparse_uuid(uuid, (char *) v->data, sizeof(v->data));
+	v->len = 37;
+
+	return 0;
+}
+
+int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid)
+{
+	return blkid_probe_set_uuid_as(pr, uuid, NULL);
+}
+
+/**
+ * blkid_probe_set_request:
+ * @pr: probe
+ * @flags: BLKID_PROBREQ_* (deprecated) or BLKID_SUBLKS_* flags
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_set_superblocks_flags().
+ */
+int blkid_probe_set_request(blkid_probe pr, int flags)
+{
+	return blkid_probe_set_superblocks_flags(pr, flags);
+}
+
+/**
+ * blkid_probe_reset_filter:
+ * @pr: prober
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_reset_superblocks_filter().
+ */
+int blkid_probe_reset_filter(blkid_probe pr)
+{
+	return __blkid_probe_reset_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_invert_filter:
+ * @pr: prober
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_invert_superblocks_filter().
+ */
+int blkid_probe_invert_filter(blkid_probe pr)
+{
+	return __blkid_probe_invert_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_filter_types
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @names: NULL terminated array of probing function names (e.g. "vfat").
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_filter_superblocks_types().
+ */
+int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[])
+{
+	return __blkid_probe_filter_types(pr, BLKID_CHAIN_SUBLKS, flag, names);
+}
+
+/**
+ * blkid_probe_filter_usage
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @usage: BLKID_USAGE_* flags
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_filter_superblocks_usage().
+ */
+int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage)
+{
+	return blkid_probe_filter_superblocks_usage(pr, flag, usage);
+}
+
+
diff --git a/libblkid/superblocks.h b/libblkid/superblocks.h
new file mode 100644
index 0000000..e45dc6a
--- /dev/null
+++ b/libblkid/superblocks.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef _BLKID_SUPERBLOCKS_H
+#define _BLKID_SUPERBLOCKS_H
+
+#include "blkidP.h"
+
+extern const struct blkid_idinfo cramfs_idinfo;
+extern const struct blkid_idinfo swap_idinfo;
+extern const struct blkid_idinfo swsuspend_idinfo;
+extern const struct blkid_idinfo adraid_idinfo;
+extern const struct blkid_idinfo ddfraid_idinfo;
+extern const struct blkid_idinfo iswraid_idinfo;
+extern const struct blkid_idinfo jmraid_idinfo;
+extern const struct blkid_idinfo lsiraid_idinfo;
+extern const struct blkid_idinfo nvraid_idinfo;
+extern const struct blkid_idinfo pdcraid_idinfo;
+extern const struct blkid_idinfo silraid_idinfo;
+extern const struct blkid_idinfo viaraid_idinfo;
+extern const struct blkid_idinfo linuxraid_idinfo;
+extern const struct blkid_idinfo ext4dev_idinfo;
+extern const struct blkid_idinfo ext4_idinfo;
+extern const struct blkid_idinfo ext3_idinfo;
+extern const struct blkid_idinfo ext2_idinfo;
+extern const struct blkid_idinfo jbd_idinfo;
+extern const struct blkid_idinfo jfs_idinfo;
+extern const struct blkid_idinfo xfs_idinfo;
+extern const struct blkid_idinfo gfs_idinfo;
+extern const struct blkid_idinfo gfs2_idinfo;
+extern const struct blkid_idinfo romfs_idinfo;
+extern const struct blkid_idinfo ocfs_idinfo;
+extern const struct blkid_idinfo ocfs2_idinfo;
+extern const struct blkid_idinfo oracleasm_idinfo;
+extern const struct blkid_idinfo reiser_idinfo;
+extern const struct blkid_idinfo reiser4_idinfo;
+extern const struct blkid_idinfo hfs_idinfo;
+extern const struct blkid_idinfo hfsplus_idinfo;
+extern const struct blkid_idinfo ntfs_idinfo;
+extern const struct blkid_idinfo iso9660_idinfo;
+extern const struct blkid_idinfo udf_idinfo;
+extern const struct blkid_idinfo vxfs_idinfo;
+extern const struct blkid_idinfo minix_idinfo;
+extern const struct blkid_idinfo vfat_idinfo;
+extern const struct blkid_idinfo ufs_idinfo;
+extern const struct blkid_idinfo hpfs_idinfo;
+extern const struct blkid_idinfo lvm2_idinfo;
+extern const struct blkid_idinfo lvm1_idinfo;
+extern const struct blkid_idinfo snapcow_idinfo;
+extern const struct blkid_idinfo verity_hash_idinfo;
+extern const struct blkid_idinfo luks_idinfo;
+extern const struct blkid_idinfo highpoint37x_idinfo;
+extern const struct blkid_idinfo highpoint45x_idinfo;
+extern const struct blkid_idinfo squashfs_idinfo;
+extern const struct blkid_idinfo netware_idinfo;
+extern const struct blkid_idinfo sysv_idinfo;
+extern const struct blkid_idinfo xenix_idinfo;
+extern const struct blkid_idinfo btrfs_idinfo;
+extern const struct blkid_idinfo ubifs_idinfo;
+extern const struct blkid_idinfo zfs_idinfo;
+extern const struct blkid_idinfo bfs_idinfo;
+extern const struct blkid_idinfo vmfs_volume_idinfo;
+extern const struct blkid_idinfo vmfs_fs_idinfo;
+extern const struct blkid_idinfo drbd_idinfo;
+extern const struct blkid_idinfo drbdproxy_datalog_idinfo;
+extern const struct blkid_idinfo befs_idinfo;
+extern const struct blkid_idinfo nilfs2_idinfo;
+extern const struct blkid_idinfo exfat_idinfo;
+extern const struct blkid_idinfo f2fs_idinfo;
+
+/*
+ * superblock functions
+ */
+extern int blkid_probe_set_version(blkid_probe pr, const char *version);
+extern int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...)
+		__attribute__ ((__format__ (__printf__, 2, 3)));
+
+extern int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len);
+extern int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label,
+                size_t len, int enc);
+extern int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid,
+                size_t len, const char *fmt, ...)
+		__attribute__ ((__format__ (__printf__, 4, 5)));
+extern int blkid_probe_strncpy_uuid(blkid_probe pr, unsigned char *str, size_t len);
+
+extern int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid);
+extern int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name);
+
+extern int blkid_probe_set_id_label(blkid_probe pr, const char *name,
+			     unsigned char *data, size_t len);
+
+#endif /* _BLKID_SUPERBLOCKS_H */
diff --git a/libblkid/swap.c b/libblkid/swap.c
new file mode 100644
index 0000000..7ac119b
--- /dev/null
+++ b/libblkid/swap.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/* linux-2.6/include/linux/swap.h */
+struct swap_header_v1_2 {
+     /*	char		bootbits[1024];	*/ /* Space for disklabel etc. */
+	uint32_t	version;
+	uint32_t	lastpage;
+	uint32_t	nr_badpages;
+	unsigned char	uuid[16];
+	unsigned char	volume[16];
+	uint32_t	padding[117];
+	uint32_t	badpages[1];
+} __attribute__((packed));
+
+#define PAGESIZE_MIN	0xff6	/* 4086 (arm, i386, ...) */
+#define PAGESIZE_MAX	0xfff6	/* 65526 (ia64) */
+
+#define TOI_MAGIC_STRING	"\xed\xc3\x02\xe9\x98\x56\xe5\x0c"
+#define TOI_MAGIC_STRLEN	(sizeof(TOI_MAGIC_STRING) - 1)
+
+static int swap_set_info(blkid_probe pr, const char *version)
+{
+	struct swap_header_v1_2 *hdr;
+
+	/* Swap header always located at offset of 1024 bytes */
+	hdr = (struct swap_header_v1_2 *) blkid_probe_get_buffer(pr, 1024,
+				sizeof(struct swap_header_v1_2));
+	if (!hdr)
+		return -1;
+
+	/* SWAPSPACE2 - check for wrong version or zeroed pagecount */
+	if (strcmp(version, "2") == 0 &&
+	    (hdr->version != 1 || hdr->lastpage == 0))
+		return -1;
+
+	/* arbitrary sanity check.. is there any garbage down there? */
+	if (hdr->padding[32] == 0 && hdr->padding[33] == 0) {
+		if (hdr->volume[0] && blkid_probe_set_label(pr, hdr->volume,
+				sizeof(hdr->volume)) < 0)
+			return -1;
+		if (blkid_probe_set_uuid(pr, hdr->uuid) < 0)
+			return -1;
+	}
+
+	blkid_probe_set_version(pr, version);
+	return 0;
+}
+
+static int probe_swap(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	unsigned char *buf;
+
+	if (!mag)
+		return -1;
+
+	/* TuxOnIce keeps valid swap header at the end of the 1st page */
+	buf = blkid_probe_get_buffer(pr, 0, TOI_MAGIC_STRLEN);
+	if (!buf)
+		return -1;
+
+	if (memcmp(buf, TOI_MAGIC_STRING, TOI_MAGIC_STRLEN) == 0)
+		return 1;	/* Ignore swap signature, it's TuxOnIce */
+
+	if (!memcmp(mag->magic, "SWAP-SPACE", mag->len)) {
+		/* swap v0 doesn't support LABEL or UUID */
+		blkid_probe_set_version(pr, "1");
+		return 0;
+
+	} else if (!memcmp(mag->magic, "SWAPSPACE2", mag->len))
+		return swap_set_info(pr, "2");
+
+	return -1;
+}
+
+static int probe_swsuspend(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	if (!mag)
+		return -1;
+	if (!memcmp(mag->magic, "S1SUSPEND", mag->len))
+		return swap_set_info(pr, "s1suspend");
+	if (!memcmp(mag->magic, "S2SUSPEND", mag->len))
+		return swap_set_info(pr, "s2suspend");
+	if (!memcmp(mag->magic, "ULSUSPEND", mag->len))
+		return swap_set_info(pr, "ulsuspend");
+	if (!memcmp(mag->magic, TOI_MAGIC_STRING, mag->len))
+		return swap_set_info(pr, "tuxonice");
+	if (!memcmp(mag->magic, "LINHIB0001", mag->len))
+		return swap_set_info(pr, "linhib0001");
+
+	return -1;	/* no signature detected */
+}
+
+const struct blkid_idinfo swap_idinfo =
+{
+	.name		= "swap",
+	.usage		= BLKID_USAGE_OTHER,
+	.probefunc	= probe_swap,
+	.minsz		= 10 * 4096,	/* 10 pages */
+	.magics		=
+	{
+		{ "SWAP-SPACE", 10, 0,  0xff6 },
+		{ "SWAPSPACE2", 10, 0,  0xff6 },
+		{ "SWAP-SPACE", 10, 0, 0x1ff6 },
+		{ "SWAPSPACE2", 10, 0, 0x1ff6 },
+		{ "SWAP-SPACE", 10, 0, 0x3ff6 },
+		{ "SWAPSPACE2", 10, 0, 0x3ff6 },
+		{ "SWAP-SPACE", 10, 0, 0x7ff6 },
+		{ "SWAPSPACE2", 10, 0, 0x7ff6 },
+		{ "SWAP-SPACE", 10, 0, 0xfff6 },
+		{ "SWAPSPACE2", 10, 0, 0xfff6 },
+		{ NULL }
+	}
+};
+
+
+const struct blkid_idinfo swsuspend_idinfo =
+{
+	.name		= "swsuspend",
+	.usage		= BLKID_USAGE_OTHER,
+	.probefunc	= probe_swsuspend,
+	.minsz		= 10 * 4096,	/* 10 pages */
+	.magics		=
+	{
+		{ TOI_MAGIC_STRING, TOI_MAGIC_STRLEN, 0, 0 },
+		{ "S1SUSPEND", 9, 0, 0xff6 },
+		{ "S2SUSPEND", 9, 0, 0xff6 },
+		{ "ULSUSPEND", 9, 0, 0xff6 },
+		{ "LINHIB0001",10,0, 0xff6 },
+
+		{ "S1SUSPEND", 9, 0, 0x1ff6 },
+		{ "S2SUSPEND", 9, 0, 0x1ff6 },
+		{ "ULSUSPEND", 9, 0, 0x1ff6 },
+		{ "LINHIB0001",10,0, 0x1ff6 },
+
+		{ "S1SUSPEND", 9, 0, 0x3ff6 },
+		{ "S2SUSPEND", 9, 0, 0x3ff6 },
+		{ "ULSUSPEND", 9, 0, 0x3ff6 },
+		{ "LINHIB0001",10,0, 0x3ff6 },
+
+		{ "S1SUSPEND", 9, 0, 0x7ff6 },
+		{ "S2SUSPEND", 9, 0, 0x7ff6 },
+		{ "ULSUSPEND", 9, 0, 0x7ff6 },
+		{ "LINHIB0001",10,0, 0x7ff6 },
+
+		{ "S1SUSPEND", 9, 0, 0xfff6 },
+		{ "S2SUSPEND", 9, 0, 0xfff6 },
+		{ "ULSUSPEND", 9, 0, 0xfff6 },
+		{ "LINHIB0001",10,0, 0xfff6 },
+
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/swapheader.h b/libblkid/swapheader.h
new file mode 100644
index 0000000..42d521a
--- /dev/null
+++ b/libblkid/swapheader.h
@@ -0,0 +1,28 @@
+#ifndef _SWAPHEADER_H
+#define _SWAPHEADER_H
+
+struct swap_header_v1 {
+        char         bootbits[1024];    /* Space for disklabel etc. */
+	unsigned int version;
+	unsigned int last_page;
+	unsigned int nr_badpages;
+	unsigned int padding[125];
+	unsigned int badpages[1];
+};
+
+
+#define SWAP_UUID_LENGTH 16
+#define SWAP_LABEL_LENGTH 16
+
+struct swap_header_v1_2 {
+	char	      bootbits[1024];    /* Space for disklabel etc. */
+	unsigned int  version;
+	unsigned int  last_page;
+	unsigned int  nr_badpages;
+	unsigned char uuid[SWAP_UUID_LENGTH];
+	char	      volume_name[SWAP_LABEL_LENGTH];
+	unsigned int  padding[117];
+	unsigned int  badpages[1];
+};
+
+#endif /* _SWAPHEADER_H */
diff --git a/libblkid/sysfs.h b/libblkid/sysfs.h
new file mode 100644
index 0000000..739f9de
--- /dev/null
+++ b/libblkid/sysfs.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_SYSFS_H
+#define UTIL_LINUX_SYSFS_H
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <inttypes.h>
+#include <dirent.h>
+
+struct sysfs_cxt {
+	dev_t	devno;
+	int	dir_fd;		/* /sys/block/<name> */
+	char	*dir_path;
+	struct sysfs_cxt *parent;
+
+	unsigned int	scsi_host,
+			scsi_channel,
+			scsi_target,
+			scsi_lun;
+
+	unsigned int	has_hctl : 1;
+};
+
+#define UL_SYSFSCXT_EMPTY { 0, -1, NULL, NULL, 0, 0, 0, 0, 0 }
+
+extern char *sysfs_devno_attribute_path(dev_t devno, char *buf,
+                                 size_t bufsiz, const char *attr);
+extern int sysfs_devno_has_attribute(dev_t devno, const char *attr);
+extern char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz);
+extern char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz);
+extern dev_t sysfs_devname_to_devno(const char *name, const char *parent);
+
+extern int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
+					__attribute__ ((warn_unused_result));
+extern void sysfs_deinit(struct sysfs_cxt *cxt);
+
+extern DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr);
+
+extern int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st);
+extern ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
+	                   char *buf, size_t bufsiz);
+extern int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr);
+
+extern int sysfs_scanf(struct sysfs_cxt *cxt,  const char *attr,
+		       const char *fmt, ...)
+		        __attribute__ ((format (scanf, 3, 4)));
+
+extern int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res);
+extern int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res);
+extern int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res);
+
+extern char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz);
+
+extern char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr);
+
+extern int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr);
+extern int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname);
+extern dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno);
+extern char *sysfs_get_slave(struct sysfs_cxt *cxt);
+
+extern int sysfs_is_partition_dirent(DIR *dir, struct dirent *d,
+			const char *parent_name);
+
+extern int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
+            size_t len, dev_t *diskdevno);
+
+extern int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h,
+			       int *c, int *t, int *l);
+extern char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
+                const char *type, const char *attr);
+extern int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type);
+extern int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr);
+extern int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern);
+
+#endif /* UTIL_LINUX_SYSFS_H */
diff --git a/libblkid/sysfs1.c b/libblkid/sysfs1.c
new file mode 100644
index 0000000..fd4ec62
--- /dev/null
+++ b/libblkid/sysfs1.c
@@ -0,0 +1,845 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "c.h"
+#include "at.h"
+#include "pathnames.h"
+#include "sysfs.h"
+
+char *sysfs_devno_attribute_path(dev_t devno, char *buf,
+				 size_t bufsiz, const char *attr)
+{
+	int len;
+
+	if (attr)
+		len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s",
+			major(devno), minor(devno), attr);
+	else
+		len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d",
+			major(devno), minor(devno));
+
+	return (len < 0 || (size_t) len + 1 > bufsiz) ? NULL : buf;
+}
+
+int sysfs_devno_has_attribute(dev_t devno, const char *attr)
+{
+	char path[PATH_MAX];
+	struct stat info;
+
+	if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr))
+		return 0;
+	if (stat(path, &info) == 0)
+		return 1;
+	return 0;
+}
+
+char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz)
+{
+	return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL);
+}
+
+dev_t sysfs_devname_to_devno(const char *name, const char *parent)
+{
+	char buf[PATH_MAX], *path = NULL;
+	dev_t dev = 0;
+
+	if (strncmp("/dev/", name, 5) == 0) {
+		/*
+		 * Read from /dev
+		 */
+		struct stat st;
+
+		if (stat(name, &st) == 0)
+			dev = st.st_rdev;
+		else
+			name += 5;	/* unaccesible, or not node in /dev */
+	}
+
+	if (!dev && parent && strncmp("dm-", name, 3)) {
+		/*
+		 * Create path to /sys/block/<parent>/<name>/dev
+		 */
+		int len = snprintf(buf, sizeof(buf),
+				_PATH_SYS_BLOCK "/%s/%s/dev", parent, name);
+		if (len < 0 || (size_t) len + 1 > sizeof(buf))
+			return 0;
+		path = buf;
+
+	} else if (!dev) {
+		/*
+		 * Create path to /sys/block/<name>/dev
+		 */
+		int len = snprintf(buf, sizeof(buf),
+				_PATH_SYS_BLOCK "/%s/dev", name);
+		if (len < 0 || (size_t) len + 1 > sizeof(buf))
+			return 0;
+		path = buf;
+	}
+
+	if (path) {
+		/*
+		 * read devno from sysfs
+		 */
+		FILE *f;
+		int maj = 0, min = 0;
+
+		f = fopen(path, "r");
+		if (!f)
+			return 0;
+
+		if (fscanf(f, "%d:%d", &maj, &min) == 2)
+			dev = makedev(maj, min);
+		fclose(f);
+	}
+	return dev;
+}
+
+/*
+ * Returns devname (e.g. "/dev/sda1") for the given devno.
+ *
+ * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
+ * symlinks.
+ *
+ * Please, use more robust blkid_devno_to_devname() in your applications.
+ */
+char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
+{
+	struct sysfs_cxt cxt;
+	char *name;
+	size_t sz;
+	struct stat st;
+
+	if (sysfs_init(&cxt, devno, NULL))
+		return NULL;
+
+	name = sysfs_get_devname(&cxt, buf, bufsiz);
+	sysfs_deinit(&cxt);
+
+	if (!name)
+		return NULL;
+
+	sz = strlen(name);
+
+	if (sz + sizeof("/dev/") > bufsiz)
+		return NULL;
+
+	/* create the final "/dev/<name>" string */
+	memmove(buf + 5, name, sz + 1);
+	memcpy(buf, "/dev/", 5);
+
+	if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno)
+		return buf;
+
+	return NULL;
+}
+
+int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
+{
+	char path[PATH_MAX];
+	int fd, rc;
+
+	memset(cxt, 0, sizeof(*cxt));
+	cxt->dir_fd = -1;
+
+	if (!sysfs_devno_path(devno, path, sizeof(path)))
+		goto err;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		goto err;
+	cxt->dir_fd = fd;
+
+	cxt->dir_path = strdup(path);
+	if (!cxt->dir_path)
+		goto err;
+	cxt->devno = devno;
+	cxt->parent = parent;
+	return 0;
+err:
+	rc = errno > 0 ? -errno : -1;
+	sysfs_deinit(cxt);
+	return rc;
+}
+
+void sysfs_deinit(struct sysfs_cxt *cxt)
+{
+	if (!cxt)
+		return;
+
+	if (cxt->dir_fd >= 0)
+	       close(cxt->dir_fd);
+	free(cxt->dir_path);
+
+	memset(cxt, 0, sizeof(*cxt));
+
+	cxt->dir_fd = -1;
+}
+
+int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st)
+{
+	int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0);
+
+	if (rc != 0 && errno == ENOENT &&
+	    strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
+
+		/* Exception for "queue/<attr>". These attributes are available
+		 * for parental devices only
+		 */
+		return fstat_at(cxt->parent->dir_fd,
+				cxt->parent->dir_path, attr, st, 0);
+	}
+	return rc;
+}
+
+int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
+{
+	struct stat st;
+
+	return sysfs_stat(cxt, attr, &st) == 0;
+}
+
+static int sysfs_open(struct sysfs_cxt *cxt, const char *attr)
+{
+	int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY);
+
+	if (fd == -1 && errno == ENOENT &&
+	    strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
+
+		/* Exception for "queue/<attr>". These attributes are available
+		 * for parental devices only
+		 */
+		fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr, O_RDONLY);
+	}
+	return fd;
+}
+
+ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
+		   char *buf, size_t bufsiz)
+{
+	if (!cxt->dir_path)
+		return -1;
+
+	if (attr)
+		return readlink_at(cxt->dir_fd, cxt->dir_path, attr, buf, bufsiz);
+
+	/* read /sys/dev/block/<maj:min> link */
+	return readlink(cxt->dir_path, buf, bufsiz);
+}
+
+DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
+{
+	DIR *dir;
+	int fd = -1;
+
+	if (attr)
+		fd = sysfs_open(cxt, attr);
+
+	else if (cxt->dir_fd >= 0)
+		/* request to open root of device in sysfs (/sys/block/<dev>)
+		 * -- we cannot use cxt->sysfs_fd directly, because closedir()
+		 * will close this our persistent file descriptor.
+		 */
+		fd = dup(cxt->dir_fd);
+
+	if (fd < 0)
+		return NULL;
+
+	dir = fdopendir(fd);
+	if (!dir) {
+		close(fd);
+		return NULL;
+	}
+	if (!attr)
+		 rewinddir(dir);
+	return dir;
+}
+
+
+static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
+{
+	int fd = sysfs_open(cxt, attr);
+
+	return fd < 0 ? NULL : fdopen(fd, "r");
+}
+
+
+static struct dirent *xreaddir(DIR *dp)
+{
+	struct dirent *d;
+
+	while ((d = readdir(dp))) {
+		if (!strcmp(d->d_name, ".") ||
+		    !strcmp(d->d_name, ".."))
+			continue;
+
+		/* blacklist here? */
+		break;
+	}
+	return d;
+}
+
+int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
+{
+	char path[256];
+
+#ifdef _DIRENT_HAVE_D_TYPE
+	if (d->d_type != DT_DIR &&
+	    d->d_type != DT_LNK)
+		return 0;
+#endif
+	if (parent_name) {
+		const char *p = parent_name;
+		size_t len;
+
+		/* /dev/sda --> "sda" */
+		if (*parent_name == '/') {
+			p = strrchr(parent_name, '/');
+			if (!p)
+				return 0;
+			p++;
+		}
+
+		len = strlen(p);
+		if (strlen(d->d_name) <= len)
+			return 0;
+
+		/* partitions subdir name is
+		 *	"<parent>[:digit:]" or "<parent>p[:digit:]"
+		 */
+		return strncmp(p, d->d_name, len) == 0 &&
+		       ((*(d->d_name + len) == 'p' && isdigit(*(d->d_name + len + 1)))
+			|| isdigit(*(d->d_name + len)));
+	}
+
+	/* Cannot use /partition file, not supported on old sysfs */
+	snprintf(path, sizeof(path), "%s/start", d->d_name);
+
+	return access(path, R_OK) == 0;
+}
+
+/*
+ * Converts @partno (partition number) to devno of the partition.
+ * The @cxt handles wholedisk device.
+ *
+ * Note that this code does not expect any special format of the
+ * partitions devnames.
+ */
+dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno)
+{
+	DIR *dir;
+	struct dirent *d;
+	char path[256];
+	dev_t devno = 0;
+
+	dir = sysfs_opendir(cxt, NULL);
+	if (!dir)
+		return 0;
+
+	while ((d = xreaddir(dir))) {
+		int n, maj, min;
+
+		if (!sysfs_is_partition_dirent(dir, d, NULL))
+			continue;
+
+		snprintf(path, sizeof(path), "%s/partition", d->d_name);
+		if (sysfs_read_int(cxt, path, &n))
+			continue;
+
+		if (n == partno) {
+			snprintf(path, sizeof(path), "%s/dev", d->d_name);
+			if (sysfs_scanf(cxt, path, "%d:%d", &maj, &min) == 2)
+				devno = makedev(maj, min);
+			break;
+		}
+	}
+
+	closedir(dir);
+	return devno;
+}
+
+
+int sysfs_scanf(struct sysfs_cxt *cxt,  const char *attr, const char *fmt, ...)
+{
+	FILE *f = sysfs_fopen(cxt, attr);
+	va_list ap;
+	int rc;
+
+	if (!f)
+		return -EINVAL;
+	va_start(ap, fmt);
+	rc = vfscanf(f, fmt, ap);
+	va_end(ap);
+
+	fclose(f);
+	return rc;
+}
+
+
+int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
+{
+	int64_t x = 0;
+
+	if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) {
+		if (res)
+			*res = x;
+		return 0;
+	}
+	return -1;
+}
+
+int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res)
+{
+	uint64_t x = 0;
+
+	if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) {
+		if (res)
+			*res = x;
+		return 0;
+	}
+	return -1;
+}
+
+int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
+{
+	int x = 0;
+
+	if (sysfs_scanf(cxt, attr, "%d", &x) == 1) {
+		if (res)
+			*res = x;
+		return 0;
+	}
+	return -1;
+}
+
+char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
+{
+	char buf[1024];
+	return sysfs_scanf(cxt, attr, "%1023[^\n]", buf) == 1 ?
+						strdup(buf) : NULL;
+}
+
+int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr)
+{
+	DIR *dir;
+	int r = 0;
+
+	if (!(dir = sysfs_opendir(cxt, attr)))
+		return 0;
+
+	while (xreaddir(dir)) r++;
+
+	closedir(dir);
+	return r;
+}
+
+int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname)
+{
+	DIR *dir;
+	struct dirent *d;
+	int r = 0;
+
+	if (!(dir = sysfs_opendir(cxt, NULL)))
+		return 0;
+
+	while ((d = xreaddir(dir))) {
+		if (sysfs_is_partition_dirent(dir, d, devname))
+			r++;
+	}
+
+	closedir(dir);
+	return r;
+}
+
+/*
+ * Returns slave name if there is only one slave, otherwise returns NULL.
+ * The result should be deallocated by free().
+ */
+char *sysfs_get_slave(struct sysfs_cxt *cxt)
+{
+	DIR *dir;
+	struct dirent *d;
+	char *name = NULL;
+
+	if (!(dir = sysfs_opendir(cxt, "slaves")))
+		return NULL;
+
+	while ((d = xreaddir(dir))) {
+		if (name)
+			goto err;	/* more slaves */
+
+		name = strdup(d->d_name);
+	}
+
+	closedir(dir);
+	return name;
+err:
+	free(name);
+	closedir(dir);
+	return NULL;
+}
+
+/*
+ * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
+ * symlinks.
+ */
+char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz)
+{
+	char *name = NULL;
+	ssize_t sz;
+
+	sz = sysfs_readlink(cxt, NULL, buf, bufsiz - 1);
+	if (sz < 0)
+		return NULL;
+
+	buf[sz] = '\0';
+	name = strrchr(buf, '/');
+	if (!name)
+		return NULL;
+
+	name++;
+	sz = strlen(name);
+
+	memmove(buf, name, sz + 1);
+	return buf;
+}
+
+/* returns basename and keeps dirname in the @path */
+static char *stripoff_last_component(char *path)
+{
+    char *p = strrchr(path, '/');
+
+    if (!p)
+        return NULL;
+    *p = '\0';
+    return ++p;
+}
+
+static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname,
+                size_t len, dev_t *diskdevno)
+{
+    int rc = 0;
+    char *name;
+
+    /* Note, sysfs_get_slave() returns the first slave only,
+     * if there is more slaves, then return NULL
+     */
+    name = sysfs_get_slave(cxt);
+    if (!name)
+        return -1;
+
+    if (diskname && len) {
+        strncpy(diskname, name, len);
+        diskname[len - 1] = '\0';
+    }
+
+    if (diskdevno) {
+        *diskdevno = sysfs_devname_to_devno(name, NULL);
+        if (!*diskdevno)
+            rc = -1;
+    }
+
+    free(name);
+    return rc;
+}
+
+int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
+            size_t len, dev_t *diskdevno)
+{
+    struct sysfs_cxt cxt;
+    int is_part = 0;
+
+    if (!dev || sysfs_init(&cxt, dev, NULL) != 0)
+        return -1;
+
+    is_part = sysfs_has_attribute(&cxt, "partition");
+    if (!is_part) {
+        /*
+         * Extra case for partitions mapped by device-mapper.
+         *
+         * All regualar partitions (added by BLKPG ioctl or kernel PT
+         * parser) have the /sys/.../partition file. The partitions
+         * mapped by DM don't have such file, but they have "part"
+         * prefix in DM UUID.
+         */
+        char *uuid = sysfs_strdup(&cxt, "dm/uuid");
+        char *tmp = uuid;
+        char *prefix = uuid ? strsep(&tmp, "-") : NULL;
+
+        if (prefix && strncasecmp(prefix, "part", 4) == 0)
+            is_part = 1;
+        free(uuid);
+
+        if (is_part &&
+            get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0)
+            /*
+             * partitioned device, mapped by DM
+             */
+            goto done;
+
+        is_part = 0;
+    }
+
+    if (!is_part) {
+        /*
+         * unpartitioned device
+         */
+        if (diskname && len) {
+            if (!sysfs_get_devname(&cxt, diskname, len))
+                goto err;
+        }
+        if (diskdevno)
+            *diskdevno = dev;
+
+    } else {
+        /*
+         * partitioned device
+         *  - readlink /sys/dev/block/8:1   = ../../block/sda/sda1
+         *  - dirname  ../../block/sda/sda1 = ../../block/sda
+         *  - basename ../../block/sda      = sda
+         */
+        char linkpath[PATH_MAX];
+        char *name;
+        int linklen;
+
+        linklen = sysfs_readlink(&cxt, NULL,
+                linkpath, sizeof(linkpath) - 1);
+        if (linklen < 0)
+            goto err;
+        linkpath[linklen] = '\0';
+
+        stripoff_last_component(linkpath);      /* dirname */
+        name = stripoff_last_component(linkpath);   /* basename */
+        if (!name)
+            goto err;
+
+        if (diskname && len) {
+            strncpy(diskname, name, len);
+            diskname[len - 1] = '\0';
+        }
+
+        if (diskdevno) {
+            *diskdevno = sysfs_devname_to_devno(name, NULL);
+            if (!*diskdevno)
+                goto err;
+        }
+    }
+
+done:
+    sysfs_deinit(&cxt);
+    return 0;
+err:
+    sysfs_deinit(&cxt);
+    return -1;
+}
+
+
+int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l)
+{
+	char buf[PATH_MAX], *hctl;
+	ssize_t len;
+
+	if (!cxt)
+		return -EINVAL;
+	if (cxt->has_hctl)
+		goto done;
+
+	len = sysfs_readlink(cxt, "device", buf, sizeof(buf) - 1);
+	if (len < 0)
+		return len;
+
+	buf[len] = '\0';
+	hctl = strrchr(buf, '/') + 1;
+	if (!hctl)
+		return -1;
+
+	if (sscanf(hctl, "%d:%d:%d:%d", &cxt->scsi_host, &cxt->scsi_channel,
+				&cxt->scsi_target, &cxt->scsi_lun) != 4)
+		return -1;
+
+	cxt->has_hctl = 1;
+done:
+	if (h)
+		*h = cxt->scsi_host;
+	if (c)
+		*c = cxt->scsi_channel;
+	if (t)
+		*t = cxt->scsi_target;
+	if (l)
+		*l = cxt->scsi_lun;
+	return 0;
+}
+
+
+static char *sysfs_scsi_host_attribute_path(struct sysfs_cxt *cxt,
+		const char *type, char *buf, size_t bufsz, const char *attr)
+{
+	int len;
+	int host;
+
+	if (sysfs_scsi_get_hctl(cxt, &host, NULL, NULL, NULL))
+		return NULL;
+
+	if (attr)
+		len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d/%s",
+				type, host, attr);
+	else
+		len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d",
+				type, host);
+
+	return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
+}
+
+char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
+		const char *type, const char *attr)
+{
+	char buf[1024];
+	int rc;
+	FILE *f;
+
+	if (!attr || !type ||
+	    !sysfs_scsi_host_attribute_path(cxt, type, buf, sizeof(buf), attr))
+		return NULL;
+
+	if (!(f = fopen(buf, "r")))
+                return NULL;
+
+	rc = fscanf(f, "%1023[^\n]", buf);
+	fclose(f);
+
+	return rc == 1 ? strdup(buf) : NULL;
+}
+
+int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type)
+{
+	char buf[PATH_MAX];
+	struct stat st;
+
+	if (!type || !sysfs_scsi_host_attribute_path(cxt, type,
+				buf, sizeof(buf), NULL))
+		return 0;
+
+	return stat(buf, &st) == 0 && S_ISDIR(st.st_mode);
+}
+
+static char *sysfs_scsi_attribute_path(struct sysfs_cxt *cxt,
+		char *buf, size_t bufsz, const char *attr)
+{
+	int len, h, c, t, l;
+
+	if (sysfs_scsi_get_hctl(cxt, &h, &c, &t, &l) != 0)
+		return NULL;
+
+	if (attr)
+		len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d/%s",
+				h,c,t,l, attr);
+	else
+		len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d",
+				h,c,t,l);
+	return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
+}
+
+int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr)
+{
+	char path[PATH_MAX];
+	struct stat st;
+
+	if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), attr))
+		return 0;
+
+	return stat(path, &st) == 0;
+}
+
+int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern)
+{
+	char path[PATH_MAX], linkc[PATH_MAX];
+	struct stat st;
+	ssize_t len;
+
+	if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), NULL))
+		return 0;
+
+	if (stat(path, &st) != 0)
+		return 0;
+
+	len = readlink(path, linkc, sizeof(linkc) - 1);
+	if (len < 0)
+		return 0;
+
+	linkc[len] = '\0';
+	return strstr(linkc, pattern) != NULL;
+}
+
+#ifdef TEST_PROGRAM_SYSFS
+#include <errno.h>
+#include <err.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+	struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
+	char *devname;
+	dev_t devno;
+	char path[PATH_MAX];
+	int i, is_part;
+	uint64_t u64;
+	ssize_t len;
+
+	if (argc != 2)
+		errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
+
+	devname = argv[1];
+	devno = sysfs_devname_to_devno(devname, NULL);
+
+	if (!devno)
+		err(EXIT_FAILURE, "failed to read devno");
+
+	is_part = sysfs_devno_has_attribute(devno, "partition");
+
+	printf("NAME: %s\n", devname);
+	printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
+	printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
+	printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
+	printf("PARTITION: %s\n", is_part ? "YES" : "NOT");
+
+	if (sysfs_init(&cxt, devno, NULL))
+		return EXIT_FAILURE;
+
+	len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
+	if (len > 0) {
+		path[len] = '\0';
+		printf("DEVNOLINK: %s\n", path);
+	}
+
+	if (!is_part) {
+		printf("First 5 partitions:\n");
+		for (i = 1; i <= 5; i++) {
+			dev_t dev = sysfs_partno_to_devno(&cxt, i);
+			if (dev)
+				printf("\t#%d %d:%d\n", i, major(dev), minor(dev));
+		}
+	}
+
+	printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
+
+	if (sysfs_read_u64(&cxt, "size", &u64))
+		printf("read SIZE failed\n");
+	else
+		printf("SIZE: %jd\n", u64);
+
+	if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
+		printf("read SECTOR failed\n");
+	else
+		printf("SECTOR: %d\n", i);
+
+	printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));
+
+	sysfs_deinit(&cxt);
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/sysfs2.c b/libblkid/sysfs2.c
new file mode 100644
index 0000000..a04b20a
--- /dev/null
+++ b/libblkid/sysfs2.c
@@ -0,0 +1,119 @@
+/*
+ * sysfs based topology -- gathers topology information from Linux sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * For more information see Linux kernel Documentation/ABI/testing/sysfs-block.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "sysfs.h"
+#include "topology.h"
+
+/*
+ * Sysfs topology values (since 2.6.31, May 2009).
+ */
+static struct topology_val {
+
+	/* /sys/dev/block/<maj>:<min>/<ATTR> */
+	const char *attr;
+
+	/* functions to set probing resut */
+	int (*set_ulong)(blkid_probe, unsigned long);
+	int (*set_int)(blkid_probe, int);
+
+} topology_vals[] = {
+	{ "alignment_offset", NULL, blkid_topology_set_alignment_offset },
+	{ "queue/minimum_io_size", blkid_topology_set_minimum_io_size },
+	{ "queue/optimal_io_size", blkid_topology_set_optimal_io_size },
+	{ "queue/physical_block_size", blkid_topology_set_physical_sector_size },
+};
+
+static int probe_sysfs_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	dev_t dev, disk = 0;
+	int rc;
+	struct sysfs_cxt sysfs = UL_SYSFSCXT_EMPTY,
+			 parent = UL_SYSFSCXT_EMPTY;
+	size_t i, count = 0;
+
+	dev = blkid_probe_get_devno(pr);
+	if (!dev || sysfs_init(&sysfs, dev, NULL) != 0)
+		return 1;
+
+	rc = 1;		/* nothing (default) */
+
+	for (i = 0; i < ARRAY_SIZE(topology_vals); i++) {
+		struct topology_val *val = &topology_vals[i];
+		int ok = sysfs_has_attribute(&sysfs, val->attr);
+
+		rc = 1;	/* nothing */
+
+		if (!ok) {
+			if (!disk) {
+				/*
+				 * Read atrributes from "disk" if the current
+				 * device is a partition.
+				 */
+				disk = blkid_probe_get_wholedisk_devno(pr);
+				if (disk && disk != dev) {
+					if (sysfs_init(&parent, disk, NULL) != 0)
+						goto done;
+
+					sysfs.parent = &parent;
+					ok = sysfs_has_attribute(&sysfs,
+								 val->attr);
+				}
+			}
+			if (!ok)
+				continue;	/* attribute does not exist */
+		}
+
+		if (val->set_ulong) {
+			uint64_t data;
+
+			if (sysfs_read_u64(&sysfs, val->attr, &data) != 0)
+				continue;
+			rc = val->set_ulong(pr, (unsigned long) data);
+
+		} else if (val->set_int) {
+			int64_t data;
+
+			if (sysfs_read_s64(&sysfs, val->attr, &data) != 0)
+				continue;
+			rc = val->set_int(pr, (int) data);
+		}
+
+		if (rc < 0)
+			goto done;	/* error */
+		if (rc == 0)
+			count++;
+	}
+
+done:
+	sysfs_deinit(&sysfs);
+	sysfs_deinit(&parent);
+
+	if (count)
+		return 0;		/* success */
+	return rc;			/* error or nothing */
+}
+
+const struct blkid_idinfo sysfs_tp_idinfo =
+{
+	.name		= "sysfs",
+	.probefunc	= probe_sysfs_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/sysv.c b/libblkid/sysv.c
new file mode 100644
index 0000000..80b0cc5
--- /dev/null
+++ b/libblkid/sysv.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This is written from sratch according to Linux kernel fs/sysv/super.c file.
+ * It seems that sysv probing code in libvolume_id and also in the original
+ * blkid is useless.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+#define XENIX_NICINOD				100
+#define XENIX_NICFREE				100
+
+struct xenix_super_block {
+	uint16_t	s_isize;
+	uint32_t	s_fsize;
+	uint16_t	s_nfree;
+	uint32_t	s_free[XENIX_NICFREE];
+	uint16_t	s_ninode;
+	uint16_t	s_inode[XENIX_NICINOD];
+	uint8_t		s_flock;
+	uint8_t		s_ilock;
+	uint8_t		s_fmod;
+	uint8_t		s_ronly;
+	uint32_t	s_time;
+	uint32_t	s_tfree;
+	uint16_t	s_tinode;
+	uint16_t	s_dinfo[4];
+	uint8_t		s_fname[6];
+	uint8_t		s_fpack[6];
+	uint8_t		s_clean;
+	uint8_t		s_fill[371];
+	uint32_t	s_magic;
+	uint32_t	s_type;
+} __attribute__((packed));
+
+
+#define SYSV_NICINOD			100
+#define SYSV_NICFREE			50
+
+struct sysv_super_block
+{
+	uint16_t	s_isize;
+	uint16_t	s_pad0;
+	uint32_t	s_fsize;
+	uint16_t	s_nfree;
+	uint16_t	s_pad1;
+	uint32_t	s_free[SYSV_NICFREE];
+	uint16_t	s_ninode;
+	uint16_t	s_pad2;
+	uint16_t	s_inode[SYSV_NICINOD];
+	uint8_t		s_flock;
+	uint8_t		s_ilock;
+	uint8_t		s_fmod;
+	uint8_t		s_ronly;
+	uint32_t	s_time;
+	uint16_t	s_dinfo[4];
+	uint32_t	s_tfree;
+	uint16_t	s_tinode;
+	uint16_t	s_pad3;
+	uint8_t		s_fname[6];
+	uint8_t		s_fpack[6];
+	uint32_t	s_fill[12];
+	uint32_t	s_state;
+	uint32_t	s_magic;
+	uint32_t	s_type;
+};
+
+static int probe_xenix(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct xenix_super_block *sb;
+
+	sb = blkid_probe_get_sb(pr, mag, struct xenix_super_block);
+	if (!sb)
+		return -1;
+	blkid_probe_set_label(pr, sb->s_fname, sizeof(sb->s_fname));
+	return 0;
+}
+
+#define SYSV_BLOCK_SIZE	1024
+
+/* Note that we don't probe for Coherent FS, this FS does not have
+ * magic string. (It requires to probe fname/fpack field..)
+ */
+static int probe_sysv(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct sysv_super_block *sb;
+	int blocks[] = {0, 9, 15, 18};
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(blocks); i++) {
+		int off = blocks[i] * SYSV_BLOCK_SIZE + SYSV_BLOCK_SIZE/2;
+
+		sb = (struct sysv_super_block *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct sysv_super_block));
+		if (!sb)
+			return -1;
+
+		if (sb->s_magic == cpu_to_le32(0xfd187e20) ||
+		    sb->s_magic == cpu_to_be32(0xfd187e20)) {
+
+			if (blkid_probe_set_label(pr, sb->s_fname,
+						sizeof(sb->s_fname)))
+				return -1;
+
+			if (blkid_probe_set_magic(pr,
+					off + offsetof(struct sysv_super_block,
+								s_magic),
+					sizeof(sb->s_magic),
+					(unsigned char *) &sb->s_magic))
+				return -1;
+
+			return 0;
+		}
+	}
+	return 1;
+}
+
+const struct blkid_idinfo xenix_idinfo =
+{
+	.name		= "xenix",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_xenix,
+	.magics		=
+	{
+		{ .magic = "\x2b\x55\x44", .len = 3, .kboff = 1, .sboff = 0x400 },
+		{ .magic = "\x44\x55\x2b", .len = 3, .kboff = 1, .sboff = 0x400 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo sysv_idinfo =
+{
+	.name		= "sysv",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_sysv,
+
+	/* SYSV is BE and LE and superblock could be on four positions. It's
+	 * simpler to probe for the magic string by .probefunc().
+	 */
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/tag.c b/libblkid/tag.c
new file mode 100644
index 0000000..9dbacef
--- /dev/null
+++ b/libblkid/tag.c
@@ -0,0 +1,476 @@
+/*
+ * tag.c - allocation/initialization/free routines for tag structs
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blkidP.h"
+
+static blkid_tag blkid_new_tag(void)
+{
+	blkid_tag tag;
+
+	if (!(tag = (blkid_tag) calloc(1, sizeof(struct blkid_struct_tag))))
+		return NULL;
+
+	INIT_LIST_HEAD(&tag->bit_tags);
+	INIT_LIST_HEAD(&tag->bit_names);
+
+	return tag;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_tag(blkid_tag tag)
+{
+	if (!tag) {
+		printf("    tag: NULL\n");
+		return;
+	}
+
+	printf("    tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
+}
+#endif
+
+void blkid_free_tag(blkid_tag tag)
+{
+	if (!tag)
+		return;
+
+	DBG(DEBUG_TAG, printf("    freeing tag %s=%s\n", tag->bit_name,
+		   tag->bit_val ? tag->bit_val : "(NULL)"));
+	DBG(DEBUG_TAG, blkid_debug_dump_tag(tag));
+
+	list_del(&tag->bit_tags);	/* list of tags for this device */
+	list_del(&tag->bit_names);	/* list of tags with this type */
+
+	free(tag->bit_name);
+	free(tag->bit_val);
+
+	free(tag);
+}
+
+/*
+ * Find the desired tag on a device.  If value is NULL, then the
+ * first such tag is returned, otherwise return only exact tag if found.
+ */
+blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type)
+{
+	struct list_head *p;
+
+	if (!dev || !type)
+		return NULL;
+
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+					   bit_tags);
+
+		if (!strcmp(tmp->bit_name, type))
+			return tmp;
+	}
+	return NULL;
+}
+
+extern int blkid_dev_has_tag(blkid_dev dev, const char *type,
+			     const char *value)
+{
+	blkid_tag		tag;
+
+	tag = blkid_find_tag_dev(dev, type);
+	if (!value)
+		return (tag != NULL);
+	if (!tag || strcmp(tag->bit_val, value))
+		return 0;
+	return 1;
+}
+
+/*
+ * Find the desired tag type in the cache.
+ * We return the head tag for this tag type.
+ */
+static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type)
+{
+	blkid_tag head = NULL, tmp;
+	struct list_head *p;
+
+	if (!cache || !type)
+		return NULL;
+
+	list_for_each(p, &cache->bic_tags) {
+		tmp = list_entry(p, struct blkid_struct_tag, bit_tags);
+		if (!strcmp(tmp->bit_name, type)) {
+			DBG(DEBUG_TAG,
+			    printf("    found cache tag head %s\n", type));
+			head = tmp;
+			break;
+		}
+	}
+	return head;
+}
+
+/*
+ * Set a tag on an existing device.
+ *
+ * If value is NULL, then delete the tagsfrom the device.
+ */
+int blkid_set_tag(blkid_dev dev, const char *name,
+		  const char *value, const int vlength)
+{
+	blkid_tag	t = 0, head = 0;
+	char		*val = 0;
+	char		**dev_var = 0;
+
+	if (!dev || !name)
+		return -BLKID_ERR_PARAM;
+
+	if (value && !(val = strndup(value, vlength)))
+		return -BLKID_ERR_MEM;
+
+	/*
+	 * Certain common tags are linked directly to the device struct
+	 * We need to know what they are before we do anything else because
+	 * the function name parameter might get freed later on.
+	 */
+	if (!strcmp(name, "TYPE"))
+		dev_var = &dev->bid_type;
+	else if (!strcmp(name, "LABEL"))
+		dev_var = &dev->bid_label;
+	else if (!strcmp(name, "UUID"))
+		dev_var = &dev->bid_uuid;
+
+	t = blkid_find_tag_dev(dev, name);
+	if (!value) {
+		if (t)
+			blkid_free_tag(t);
+	} else if (t) {
+		if (!strcmp(t->bit_val, val)) {
+			/* Same thing, exit */
+			free(val);
+			return 0;
+		}
+		free(t->bit_val);
+		t->bit_val = val;
+	} else {
+		/* Existing tag not present, add to device */
+		if (!(t = blkid_new_tag()))
+			goto errout;
+		t->bit_name = name ? strdup(name) : NULL;
+		t->bit_val = val;
+		t->bit_dev = dev;
+
+		list_add_tail(&t->bit_tags, &dev->bid_tags);
+
+		if (dev->bid_cache) {
+			head = blkid_find_head_cache(dev->bid_cache,
+						     t->bit_name);
+			if (!head) {
+				head = blkid_new_tag();
+				if (!head)
+					goto errout;
+
+				DBG(DEBUG_TAG,
+				    printf("    creating new cache tag head %s\n", name));
+				head->bit_name = name ? strdup(name) : NULL;
+				if (!head->bit_name)
+					goto errout;
+				list_add_tail(&head->bit_tags,
+					      &dev->bid_cache->bic_tags);
+			}
+			list_add_tail(&t->bit_names, &head->bit_names);
+		}
+	}
+
+	/* Link common tags directly to the device struct */
+	if (dev_var)
+		*dev_var = val;
+
+	if (dev->bid_cache)
+		dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+	return 0;
+
+errout:
+	if (t)
+		blkid_free_tag(t);
+	else
+		free(val);
+	if (head)
+		blkid_free_tag(head);
+	return -BLKID_ERR_MEM;
+}
+
+
+/*
+ * Parse a "NAME=value" string.  This is slightly different than
+ * parse_token, because that will end an unquoted value at a space, while
+ * this will assume that an unquoted value is the rest of the token (e.g.
+ * if we are passed an already quoted string from the command-line we don't
+ * have to both quote and escape quote so that the quotes make it to
+ * us).
+ *
+ * Returns 0 on success, and -1 on failure.
+ */
+int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val)
+{
+	char *name, *value, *cp;
+
+	DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token));
+
+	if (!token || !(cp = strchr(token, '=')))
+		return -1;
+
+	name = strdup(token);
+	if (!name)
+		return -1;
+	value = name + (cp - token);
+	*value++ = '\0';
+	if (*value == '"' || *value == '\'') {
+		char c = *value++;
+		if (!(cp = strrchr(value, c)))
+			goto errout; /* missing closing quote */
+		*cp = '\0';
+	}
+	if (value && *value)
+		value = strdup(value);
+	if (!value)
+		goto errout;
+
+	if (ret_type)
+		*ret_type = name;
+	if (ret_val)
+		*ret_val = value;
+
+	return 0;
+
+errout:
+	free(name);
+	return -1;
+}
+
+/*
+ * Tag iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implemenation.  I'm not convinced I want
+ * to keep list.h in the long term, anyway.  It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application.  [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all tags in a device
+ */
+#define TAG_ITERATE_MAGIC	0x01a5284c
+
+struct blkid_struct_tag_iterate {
+	int			magic;
+	blkid_dev		dev;
+	struct list_head	*p;
+};
+
+extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
+{
+	blkid_tag_iterate	iter;
+
+	iter = malloc(sizeof(struct blkid_struct_tag_iterate));
+	if (iter) {
+		iter->magic = TAG_ITERATE_MAGIC;
+		iter->dev = dev;
+		iter->p	= dev->bid_tags.next;
+	}
+	return (iter);
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_tag_next(blkid_tag_iterate iter,
+			  const char **type, const char **value)
+{
+	blkid_tag tag;
+
+	if (!type || !value ||
+	    !iter || iter->magic != TAG_ITERATE_MAGIC ||
+	    iter->p == &iter->dev->bid_tags)
+		return -1;
+
+	*type = 0;
+	*value = 0;
+	tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags);
+	*type = tag->bit_name;
+	*value = tag->bit_val;
+	iter->p = iter->p->next;
+	return 0;
+}
+
+extern void blkid_tag_iterate_end(blkid_tag_iterate iter)
+{
+	if (!iter || iter->magic != TAG_ITERATE_MAGIC)
+		return;
+	iter->magic = 0;
+	free(iter);
+}
+
+/*
+ * This function returns a device which matches a particular
+ * type/value pair.  If there is more than one device that matches the
+ * search specification, it returns the one with the highest priority
+ * value.  This allows us to give preference to EVMS or LVM devices.
+ */
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+					 const char *type,
+					 const char *value)
+{
+	blkid_tag	head;
+	blkid_dev	dev;
+	int		pri;
+	struct list_head *p;
+	int		probe_new = 0;
+
+	if (!cache || !type || !value)
+		return NULL;
+
+	blkid_read_cache(cache);
+
+	DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value));
+
+try_again:
+	pri = -1;
+	dev = 0;
+	head = blkid_find_head_cache(cache, type);
+
+	if (head) {
+		list_for_each(p, &head->bit_names) {
+			blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+						   bit_names);
+
+			if (!strcmp(tmp->bit_val, value) &&
+			    (tmp->bit_dev->bid_pri > pri) &&
+			    !access(tmp->bit_dev->bid_name, F_OK)) {
+				dev = tmp->bit_dev;
+				pri = dev->bid_pri;
+			}
+		}
+	}
+	if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+		dev = blkid_verify(cache, dev);
+		if (!dev || (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED)))
+			goto try_again;
+	}
+
+	if (!dev && !probe_new) {
+		if (blkid_probe_all_new(cache) < 0)
+			return NULL;
+		probe_new++;
+		goto try_again;
+	}
+
+	if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) {
+		if (blkid_probe_all(cache) < 0)
+			return NULL;
+		goto try_again;
+	}
+	return dev;
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+void __attribute__((__noreturn__)) usage(char *prog)
+{
+	fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device "
+		"[type value]\n",
+		prog);
+	fprintf(stderr, "\tList all tags for a device and exit\n");
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	blkid_tag_iterate	iter;
+	blkid_cache 		cache = NULL;
+	blkid_dev		dev;
+	int			c, ret, found;
+	int			flags = BLKID_DEV_FIND;
+	char			*tmp;
+	char			*file = NULL;
+	char			*devname = NULL;
+	char			*search_type = NULL;
+	char			*search_value = NULL;
+	const char		*type, *value;
+
+	while ((c = getopt (argc, argv, "m:f:")) != EOF)
+		switch (c) {
+		case 'f':
+			file = optarg;
+			break;
+		case 'm':
+		{
+			int mask = strtoul (optarg, &tmp, 0);
+			if (*tmp) {
+				fprintf(stderr, "Invalid debug mask: %s\n",
+					optarg);
+				exit(1);
+			}
+			blkid_init_debug(mask);
+			break;
+		}
+		case '?':
+			usage(argv[0]);
+		}
+	if (argc > optind)
+		devname = argv[optind++];
+	if (argc > optind)
+		search_type = argv[optind++];
+	if (argc > optind)
+		search_value = argv[optind++];
+	if (!devname || (argc != optind))
+		usage(argv[0]);
+
+	if ((ret = blkid_get_cache(&cache, file)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+
+	dev = blkid_get_dev(cache, devname, flags);
+	if (!dev) {
+		fprintf(stderr, "%s: Can not find device in blkid cache\n",
+			devname);
+		exit(1);
+	}
+	if (search_type) {
+		found = blkid_dev_has_tag(dev, search_type, search_value);
+		printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev),
+		       search_type, search_value ? search_value : "NULL",
+		       found ? "FOUND" : "NOT FOUND");
+		return(!found);
+	}
+	printf("Device %s...\n", blkid_dev_devname(dev));
+
+	iter = blkid_tag_iterate_begin(dev);
+	while (blkid_tag_next(iter, &type, &value) == 0) {
+		printf("\tTag %s has value %s\n", type, value);
+	}
+	blkid_tag_iterate_end(iter);
+
+	blkid_put_cache(cache);
+	return (0);
+}
+#endif
diff --git a/libblkid/topology.c b/libblkid/topology.c
new file mode 100644
index 0000000..9d4e7bd
--- /dev/null
+++ b/libblkid/topology.c
@@ -0,0 +1,366 @@
+/*
+ * topology - gathers information about device topology
+ *
+ * Copyright 2009 Red Hat, Inc.  All rights reserved.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include "topology.h"
+
+/**
+ * SECTION:topology
+ * @title: Topology information
+ * @short_description: block device topology information.
+ *
+ * The topology chain provides details about Linux block devices, for more
+ * information see:
+ *
+ *      Linux kernel Documentation/ABI/testing/sysfs-block
+ *
+ * NAME=value (tags) interface is enabled by blkid_probe_enable_topology(),
+ * and provides:
+ *
+ * @LOGICAL_SECTOR_SIZE: this is the smallest unit the storage device can
+ *                       address. It is typically 512 bytes.
+ *
+ * @PHYSICAL_SECTOR_SIZE: this is the smallest unit a physical storage device
+ *                        can write atomically. It is usually the same as the
+ *                        logical sector size but may be bigger.
+ *
+ * @MINIMUM_IO_SIZE: minimum size which is the device's preferred unit of I/O.
+ *                   For RAID arrays it is often the stripe chunk size.
+ *
+ * @OPTIMAL_IO_SIZE: usually the stripe width for RAID or zero. For RAID arrays
+ *                   it is usually the stripe width or the internal track size.
+ *
+ * @ALIGNMENT_OFFSET: indicates how many bytes the beginning of the device is
+ *                    offset from the disk's natural alignment.
+ *
+ * The NAME=value tags are not defined when the corresponding topology value
+ * is zero. The MINIMUM_IO_SIZE should be always defined if kernel provides
+ * topology information.
+ *
+ * Binary interface:
+ *
+ * blkid_probe_get_topology()
+ *
+ * blkid_topology_get_'VALUENAME'()
+ */
+static int topology_probe(blkid_probe pr, struct blkid_chain *chn);
+static void topology_free(blkid_probe pr, void *data);
+static int topology_is_complete(blkid_probe pr);
+static int topology_set_logical_sector_size(blkid_probe pr);
+
+/*
+ * Binary interface
+ */
+struct blkid_struct_topology {
+	unsigned long	alignment_offset;
+	unsigned long	minimum_io_size;
+	unsigned long	optimal_io_size;
+	unsigned long	logical_sector_size;
+	unsigned long	physical_sector_size;
+};
+
+/*
+ * Topology chain probing functions
+ */
+static const struct blkid_idinfo *idinfos[] =
+{
+#ifdef __linux__
+	&ioctl_tp_idinfo,
+	&sysfs_tp_idinfo,
+	&md_tp_idinfo,
+	&dm_tp_idinfo,
+	&lvm_tp_idinfo,
+	&evms_tp_idinfo
+#endif
+};
+
+
+/*
+ * Driver definition
+ */
+const struct blkid_chaindrv topology_drv = {
+	.id           = BLKID_CHAIN_TOPLGY,
+	.name         = "topology",
+	.dflt_enabled = FALSE,
+	.idinfos      = idinfos,
+	.nidinfos     = ARRAY_SIZE(idinfos),
+	.probe        = topology_probe,
+	.safeprobe    = topology_probe,
+	.free_data    = topology_free
+};
+
+/**
+ * blkid_probe_enable_topology:
+ * @pr: probe
+ * @enable: TRUE/FALSE
+ *
+ * Enables/disables the topology probing for non-binary interface.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_enable_topology(blkid_probe pr, int enable)
+{
+	if (!pr)
+		return -1;
+	pr->chains[BLKID_CHAIN_TOPLGY].enabled = enable;
+	return 0;
+}
+
+/**
+ * blkid_probe_get_topology:
+ * @pr: probe
+ *
+ * This is a binary interface for topology values. See also blkid_topology_*
+ * functions.
+ *
+ * This function is independent on blkid_do_[safe,full]probe() and
+ * blkid_probe_enable_topology() calls.
+ *
+ * WARNING: the returned object will be overwritten by the next
+ *          blkid_probe_get_topology() call for the same @pr. If you want to
+ *          use more blkid_topopogy objects in the same time you have to create
+ *          more blkid_probe handlers (see blkid_new_probe()).
+ *
+ * Returns: blkid_topopogy, or NULL in case of error.
+ */
+blkid_topology blkid_probe_get_topology(blkid_probe pr)
+{
+	return (blkid_topology) blkid_probe_get_binary_data(pr,
+			&pr->chains[BLKID_CHAIN_TOPLGY]);
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int topology_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+	size_t i;
+
+	if (!pr || chn->idx < -1)
+		return -1;
+
+	if (!S_ISBLK(pr->mode))
+		return -1;	/* nothing, works with block devices only */
+
+	if (chn->binary) {
+		DBG(DEBUG_LOWPROBE, printf("initialize topology binary data\n"));
+
+		if (chn->data)
+			/* reset binary data */
+			memset(chn->data, 0,
+					sizeof(struct blkid_struct_topology));
+		else {
+			chn->data = calloc(1,
+					sizeof(struct blkid_struct_topology));
+			if (!chn->data)
+				return -1;
+		}
+	}
+
+	blkid_probe_chain_reset_vals(pr, chn);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("--> starting probing loop [TOPOLOGY idx=%d]\n",
+		chn->idx));
+
+	i = chn->idx < 0 ? 0 : chn->idx + 1U;
+
+	for ( ; i < ARRAY_SIZE(idinfos); i++) {
+		const struct blkid_idinfo *id = idinfos[i];
+
+		chn->idx = i;
+
+		if (id->probefunc) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"%s: call probefunc()\n", id->name));
+			if (id->probefunc(pr, NULL) != 0)
+				continue;
+		}
+
+		if (!topology_is_complete(pr))
+			continue;
+
+		/* generic for all probing drivers */
+		topology_set_logical_sector_size(pr);
+
+		DBG(DEBUG_LOWPROBE,
+			printf("<-- leaving probing loop (type=%s) [TOPOLOGY idx=%d]\n",
+			id->name, chn->idx));
+		return 0;
+	}
+
+	DBG(DEBUG_LOWPROBE,
+		printf("<-- leaving probing loop (failed) [TOPOLOGY idx=%d]\n",
+		chn->idx));
+	return 1;
+}
+
+static void topology_free(blkid_probe pr __attribute__((__unused__)),
+			  void *data)
+{
+	free(data);
+}
+
+static int topology_set_value(blkid_probe pr, const char *name,
+				size_t structoff, unsigned long data)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	if (!chn)
+		return -1;
+	if (!data)
+		return 0;	/* ignore zeros */
+
+	if (chn->binary) {
+		memcpy(chn->data + structoff, &data, sizeof(data));
+		return 0;
+	}
+	return blkid_probe_sprintf_value(pr, name, "%lu", data);
+}
+
+
+/* the topology info is complete when we have at least "minimum_io_size" which
+ * is provided by all blkid topology drivers */
+static int topology_is_complete(blkid_probe pr)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	if (!chn)
+		return FALSE;
+
+	if (chn->binary && chn->data) {
+		blkid_topology tp = (blkid_topology) chn->data;
+		if (tp->minimum_io_size)
+			return TRUE;
+	}
+
+	return __blkid_probe_lookup_value(pr, "MINIMUM_IO_SIZE") ? TRUE : FALSE;
+}
+
+int blkid_topology_set_alignment_offset(blkid_probe pr, int val)
+{
+	unsigned long xval;
+
+	/* Welcome to Hell. The kernel is able to return -1 as an
+	 * alignment_offset if no compatible sizes and alignments
+	 * exist for stacked devices.
+	 *
+	 * There is no way how libblkid caller can respond to the value -1, so
+	 * we will hide this corner case...
+	 *
+	 * (TODO: maybe we can export an extra boolean value 'misaligned' rather
+	 *  then complete hide this problem.)
+	 */
+	xval = val < 0 ? 0 : val;
+
+	return topology_set_value(pr,
+			"ALIGNMENT_OFFSET",
+			offsetof(struct blkid_struct_topology, alignment_offset),
+			xval);
+}
+
+int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val)
+{
+	return topology_set_value(pr,
+			"MINIMUM_IO_SIZE",
+			offsetof(struct blkid_struct_topology, minimum_io_size),
+			val);
+}
+
+int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val)
+{
+	return topology_set_value(pr,
+			"OPTIMAL_IO_SIZE",
+			offsetof(struct blkid_struct_topology, optimal_io_size),
+			val);
+}
+
+/* BLKSSZGET is provided on all systems since 2.3.3 -- so we don't have to
+ * waste time with sysfs.
+ */
+static int topology_set_logical_sector_size(blkid_probe pr)
+{
+	unsigned long val = blkid_probe_get_sectorsize(pr);
+
+	if (!val)
+		return -1;
+
+	return topology_set_value(pr,
+			"LOGICAL_SECTOR_SIZE",
+			offsetof(struct blkid_struct_topology, logical_sector_size),
+			val);
+}
+
+int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val)
+{
+	return topology_set_value(pr,
+			"PHYSICAL_SECTOR_SIZE",
+			offsetof(struct blkid_struct_topology, physical_sector_size),
+			val);
+}
+
+/**
+ * blkid_topology_get_alignment_offset:
+ * @tp: topology
+ *
+ * Returns: alignment offset in bytes or 0.
+ */
+unsigned long blkid_topology_get_alignment_offset(blkid_topology tp)
+{
+	return tp->alignment_offset;
+}
+
+/**
+ * blkid_topology_get_minimum_io_size:
+ * @tp: topology
+ *
+ * Returns: minimum io size in bytes or 0.
+ */
+unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp)
+{
+	return tp->minimum_io_size;
+}
+
+/**
+ * blkid_topology_get_optimal_io_size
+ * @tp: topology
+ *
+ * Returns: optimal io size in bytes or 0.
+ */
+unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp)
+{
+	return tp->optimal_io_size;
+}
+
+/**
+ * blkid_topology_get_logical_sector_size
+ * @tp: topology
+ *
+ * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0.
+ */
+unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp)
+{
+	return tp->logical_sector_size;
+}
+
+/**
+ * blkid_topology_get_physical_sector_size
+ * @tp: topology
+ *
+ * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0.
+ */
+unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp)
+{
+	return tp->physical_sector_size;
+}
+
diff --git a/libblkid/topology.h b/libblkid/topology.h
new file mode 100644
index 0000000..6d2f433
--- /dev/null
+++ b/libblkid/topology.h
@@ -0,0 +1,24 @@
+#ifndef BLKID_TOPOLOGY_H
+#define BLKID_TOPOLOGY_H
+
+#include "blkidP.h"
+
+extern int blkid_topology_set_alignment_offset(blkid_probe pr, int val);
+extern int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val);
+extern int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val);
+extern int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val);
+
+/*
+ * topology probers
+ */
+#ifdef __linux__
+extern const struct blkid_idinfo ioctl_tp_idinfo;
+extern const struct blkid_idinfo md_tp_idinfo;
+extern const struct blkid_idinfo evms_tp_idinfo;
+extern const struct blkid_idinfo sysfs_tp_idinfo;
+extern const struct blkid_idinfo dm_tp_idinfo;
+extern const struct blkid_idinfo lvm_tp_idinfo;
+#endif
+
+#endif /* BLKID_TOPOLOGY_H */
+
diff --git a/libblkid/tt.c b/libblkid/tt.c
new file mode 100644
index 0000000..cbe4e3b
--- /dev/null
+++ b/libblkid/tt.c
@@ -0,0 +1,1005 @@
+/*
+ * TT - Table or Tree, features:
+ * - column width could be defined as absolute or relative to the terminal width
+ * - allows to truncate or wrap data in columns
+ * - prints tree if parent->child relation is defined
+ * - draws the tree by ASCII or UTF8 lines (depends on terminal setting)
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <ctype.h>
+
+#include "c.h"
+#include "nls.h"
+#include "widechar.h"
+#include "tt.h"
+#include "mbsalign.h"
+#include "ttyutils.h"
+
+struct tt_symbols {
+	const char *branch;
+	const char *vert;
+	const char *right;
+};
+
+static const struct tt_symbols ascii_tt_symbols = {
+	.branch = "|-",
+	.vert	= "| ",
+	.right	= "`-",
+};
+
+#ifdef HAVE_WIDECHAR
+#define UTF_V	"\342\224\202"	/* U+2502, Vertical line drawing char */
+#define UTF_VR	"\342\224\234"	/* U+251C, Vertical and right */
+#define UTF_H	"\342\224\200"	/* U+2500, Horizontal */
+#define UTF_UR	"\342\224\224"	/* U+2514, Up and right */
+
+static const struct tt_symbols utf8_tt_symbols = {
+	.branch = UTF_VR UTF_H,
+	.vert   = UTF_V " ",
+	.right	= UTF_UR UTF_H,
+};
+#endif /* !HAVE_WIDECHAR */
+
+#define is_last_column(_tb, _cl) \
+		list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns)
+
+/*
+ * Counts number of cells in multibyte string. For all control and
+ * non-printable chars is the result width enlarged to store \x?? hex
+ * sequence. See mbs_safe_encode().
+ */
+static size_t mbs_safe_width(const char *s)
+{
+	mbstate_t st;
+	const char *p = s;
+	size_t width = 0;
+
+	memset(&st, 0, sizeof(st));
+
+	while (p && *p) {
+		if (iscntrl((unsigned char) *p)) {
+			width += 4;			/* *p encoded to \x?? */
+			p++;
+		}
+#ifdef HAVE_WIDECHAR
+		else {
+			wchar_t wc;
+			size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+
+			if (len == 0)
+				break;
+
+			if (len == (size_t) -1 || len == (size_t) -2) {
+				len = 1;
+				width += (isprint((unsigned char) *p) ? 1 : 4);
+
+			} if (!iswprint(wc))
+				width += len * 4;	/* hex encode whole sequence */
+			else
+				width += wcwidth(wc);	/* number of cells */
+			p += len;
+		}
+#else
+		else if (!isprint((unsigned char) *p)) {
+			width += 4;			/* *p encoded to \x?? */
+			p++;
+		} else {
+			width++;
+			p++;
+		}
+#endif
+	}
+
+	return width;
+}
+
+/*
+ * Returns allocated string where all control and non-printable chars are
+ * replaced with \x?? hex sequence.
+ */
+static char *mbs_safe_encode(const char *s, size_t *width)
+{
+	mbstate_t st;
+	const char *p = s;
+	char *res, *r;
+	size_t sz = s ? strlen(s) : 0;
+
+
+	if (!sz)
+		return NULL;
+
+	memset(&st, 0, sizeof(st));
+
+	res = malloc((sz * 4) + 1);
+	if (!res)
+		return NULL;
+
+	r = res;
+	*width = 0;
+
+	while (p && *p) {
+		if (iscntrl((unsigned char) *p)) {
+			sprintf(r, "\\x%02x", (unsigned char) *p);
+			r += 4;
+			*width += 4;
+			p++;
+		}
+#ifdef HAVE_WIDECHAR
+		else {
+			wchar_t wc;
+			size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+
+			if (len == 0)
+				break;		/* end of string */
+
+			if (len == (size_t) -1 || len == (size_t) -2) {
+				len = 1;
+				/*
+				 * Not valid multibyte sequence -- maybe it's
+				 * printable char according to the current locales.
+				 */
+				if (!isprint((unsigned char) *p)) {
+					sprintf(r, "\\x%02x", (unsigned char) *p);
+					r += 4;
+					*width += 4;
+				} else {
+					width++;
+					*r++ = *p;
+				}
+			} else if (!iswprint(wc)) {
+				size_t i;
+				for (i = 0; i < len; i++) {
+					sprintf(r, "\\x%02x", (unsigned char) *p);
+					r += 4;
+					*width += 4;
+				}
+			} else {
+				memcpy(r, p, len);
+				r += len;
+				*width += wcwidth(wc);
+			}
+			p += len;
+		}
+#else
+		else if (!isprint((unsigned char) *p)) {
+			sprintf(r, "\\x%02x", (unsigned char) *p);
+			p++;
+			r += 4;
+			*width += 4;
+		} else {
+			*r++ = *p++;
+			*width++;
+		}
+#endif
+	}
+
+	*r = '\0';
+
+	return res;
+}
+
+/*
+ * @flags: TT_FL_* flags (usually TT_FL_{ASCII,RAW})
+ *
+ * Returns: newly allocated table
+ */
+struct tt *tt_new_table(int flags)
+{
+	struct tt *tb;
+
+	tb = calloc(1, sizeof(struct tt));
+	if (!tb)
+		return NULL;
+
+	tb->flags = flags;
+	INIT_LIST_HEAD(&tb->tb_lines);
+	INIT_LIST_HEAD(&tb->tb_columns);
+
+#if defined(HAVE_WIDECHAR)
+	if (!(flags & TT_FL_ASCII) && !strcmp(nl_langinfo(CODESET), "UTF-8"))
+		tb->symbols = &utf8_tt_symbols;
+	else
+#endif
+		tb->symbols = &ascii_tt_symbols;
+
+	tb->first_run = TRUE;
+	return tb;
+}
+
+void tt_remove_lines(struct tt *tb)
+{
+	if (!tb)
+		return;
+
+	while (!list_empty(&tb->tb_lines)) {
+		struct tt_line *ln = list_entry(tb->tb_lines.next,
+						struct tt_line, ln_lines);
+		list_del(&ln->ln_lines);
+		free(ln->data);
+		free(ln);
+	}
+}
+
+void tt_free_table(struct tt *tb)
+{
+	if (!tb)
+		return;
+
+	tt_remove_lines(tb);
+
+	while (!list_empty(&tb->tb_columns)) {
+		struct tt_column *cl = list_entry(tb->tb_columns.next,
+						struct tt_column, cl_columns);
+		list_del(&cl->cl_columns);
+		free(cl);
+	}
+	free(tb);
+}
+
+
+/*
+ * @tb: table
+ * @name: column header
+ * @whint: column width hint (absolute width: N > 1; relative width: N < 1)
+ * @flags: usually TT_FL_{TREE,TRUNCATE}
+ *
+ * The column width is possible to define by three ways:
+ *
+ *  @whint = 0..1    : relative width, percent of terminal width
+ *
+ *  @whint = 1..N    : absolute width, empty colum will be truncated to
+ *                     the column header width
+ *
+ *  @whint = 1..N
+ *  @flags = TT_FL_STRICTWIDTH
+ *                   : absolute width, empty colum won't be truncated
+ *
+ * The column is necessary to address (for example for tt_line_set_data()) by
+ * sequential number. The first defined column has the colnum = 0. For example:
+ *
+ *	tt_define_column(tab, "FOO", 0.5, 0);		// colnum = 0
+ *	tt_define_column(tab, "BAR", 0.5, 0);		// colnum = 1
+ *      .
+ *      .
+ *	tt_line_set_data(line, 0, "foo-data");		// FOO column
+ *	tt_line_set_data(line, 1, "bar-data");		// BAR column
+ *
+ * Returns: newly allocated column definition
+ */
+struct tt_column *tt_define_column(struct tt *tb, const char *name,
+					double whint, int flags)
+{
+	struct tt_column *cl;
+
+	if (!tb)
+		return NULL;
+	cl = calloc(1, sizeof(*cl));
+	if (!cl)
+		return NULL;
+
+	cl->name = name;
+	cl->width_hint = whint;
+	cl->flags = flags;
+	cl->seqnum = tb->ncols++;
+
+	if (flags & TT_FL_TREE)
+		tb->flags |= TT_FL_TREE;
+
+	INIT_LIST_HEAD(&cl->cl_columns);
+	list_add_tail(&cl->cl_columns, &tb->tb_columns);
+	return cl;
+}
+
+/*
+ * @tb: table
+ * @parent: parental line or NULL
+ *
+ * Returns: newly allocate line
+ */
+struct tt_line *tt_add_line(struct tt *tb, struct tt_line *parent)
+{
+	struct tt_line *ln = NULL;
+
+	if (!tb || !tb->ncols)
+		goto err;
+	ln = calloc(1, sizeof(*ln));
+	if (!ln)
+		goto err;
+	ln->data = calloc(tb->ncols, sizeof(char *));
+	if (!ln->data)
+		goto err;
+
+	ln->table = tb;
+	ln->parent = parent;
+	INIT_LIST_HEAD(&ln->ln_lines);
+	INIT_LIST_HEAD(&ln->ln_children);
+	INIT_LIST_HEAD(&ln->ln_branch);
+
+	list_add_tail(&ln->ln_lines, &tb->tb_lines);
+
+	if (parent)
+		list_add_tail(&ln->ln_children, &parent->ln_branch);
+	return ln;
+err:
+	free(ln);
+	return NULL;
+}
+
+/*
+ * @tb: table
+ * @colnum: number of column (0..N)
+ *
+ * Returns: pointer to column or NULL
+ */
+struct tt_column *tt_get_column(struct tt *tb, size_t colnum)
+{
+	struct list_head *p;
+
+	list_for_each(p, &tb->tb_columns) {
+		struct tt_column *cl =
+				list_entry(p, struct tt_column, cl_columns);
+		if (cl->seqnum == colnum)
+			return cl;
+	}
+	return NULL;
+}
+
+/*
+ * @ln: line
+ * @colnum: number of column (0..N)
+ * @data: printable data
+ *
+ * Stores data that will be printed to the table cell.
+ */
+int tt_line_set_data(struct tt_line *ln, int colnum, const char *data)
+{
+	struct tt_column *cl;
+
+	if (!ln)
+		return -1;
+	cl = tt_get_column(ln->table, colnum);
+	if (!cl)
+		return -1;
+
+	if (ln->data[cl->seqnum]) {
+		size_t sz = strlen(ln->data[cl->seqnum]);;
+		ln->data_sz = ln->data_sz > sz ? ln->data_sz - sz : 0;
+	}
+
+	ln->data[cl->seqnum] = data;
+	if (data)
+		ln->data_sz += strlen(data);
+	return 0;
+}
+
+int tt_line_set_userdata(struct tt_line *ln, void *data)
+{
+	if (!ln)
+		return -1;
+	ln->userdata = data;
+	return 0;
+}
+
+static char *line_get_ascii_art(struct tt_line *ln, char *buf, size_t *bufsz)
+{
+	const char *art;
+	size_t len;
+
+	if (!ln->parent)
+		return buf;
+
+	buf = line_get_ascii_art(ln->parent, buf, bufsz);
+	if (!buf)
+		return NULL;
+
+	if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
+		art = "  ";
+	else
+		art = ln->table->symbols->vert;
+
+	len = strlen(art);
+	if (*bufsz < len)
+		return NULL;	/* no space, internal error */
+
+	memcpy(buf, art, len);
+	*bufsz -= len;
+	return buf + len;
+}
+
+static char *line_get_data(struct tt_line *ln, struct tt_column *cl,
+				char *buf, size_t bufsz)
+{
+	const char *data = ln->data[cl->seqnum];
+	const struct tt_symbols *sym;
+	char *p = buf;
+
+	memset(buf, 0, bufsz);
+
+	if (!data)
+		return NULL;
+	if (!(cl->flags & TT_FL_TREE)) {
+		strncpy(buf, data, bufsz);
+		buf[bufsz - 1] = '\0';
+		return buf;
+	}
+
+	/*
+	 * Tree stuff
+	 */
+	if (ln->parent) {
+		p = line_get_ascii_art(ln->parent, buf, &bufsz);
+		if (!p)
+			return NULL;
+	}
+
+	sym = ln->table->symbols;
+
+	if (!ln->parent)
+		snprintf(p, bufsz, "%s", data);			/* root node */
+	else if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
+		snprintf(p, bufsz, "%s%s", sym->right, data);	/* last chaild */
+	else
+		snprintf(p, bufsz, "%s%s", sym->branch, data);	/* any child */
+
+	return buf;
+}
+
+/*
+ * This function counts column width.
+ *
+ * For the TT_FL_NOEXTREMES columns is possible to call this function two
+ * times.  The first pass counts width and average width. If the column
+ * contains too large fields (width greater than 2 * average) then the column
+ * is marked as "extreme". In the second pass all extreme fields are ignored
+ * and column width is counted from non-extreme fields only.
+ */
+static void count_column_width(struct tt *tb, struct tt_column *cl,
+				 char *buf, size_t bufsz)
+{
+	struct list_head *lp;
+	int count = 0;
+	size_t sum = 0;
+
+	cl->width = 0;
+
+	list_for_each(lp, &tb->tb_lines) {
+		struct tt_line *ln = list_entry(lp, struct tt_line, ln_lines);
+		char *data = line_get_data(ln, cl, buf, bufsz);
+		size_t len = data ? mbs_safe_width(data) : 0;
+
+		if (len == (size_t) -1)		/* ignore broken multibyte strings */
+			len = 0;
+
+		if (len > cl->width_max)
+			cl->width_max = len;
+
+		if (cl->is_extreme && len > cl->width_avg * 2)
+			continue;
+		else if (cl->flags & TT_FL_NOEXTREMES) {
+			sum += len;
+			count++;
+		}
+		if (len > cl->width)
+			cl->width = len;
+	}
+
+	if (count && cl->width_avg == 0) {
+		cl->width_avg = sum / count;
+
+		if (cl->width_max > cl->width_avg * 2)
+			cl->is_extreme = 1;
+	}
+
+	/* check and set minimal column width */
+	if (cl->name)
+		cl->width_min = mbs_safe_width(cl->name);
+
+	/* enlarge to minimal width */
+	if (cl->width < cl->width_min && !(cl->flags & TT_FL_STRICTWIDTH))
+		cl->width = cl->width_min;
+
+	/* use relative size for large columns */
+	else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint &&
+		 cl->width_min < (size_t) cl->width_hint)
+
+		cl->width = (size_t) cl->width_hint;
+}
+
+/*
+ * This is core of the tt_* voodo...
+ */
+static void recount_widths(struct tt *tb, char *buf, size_t bufsz)
+{
+	struct list_head *p;
+	size_t width = 0;	/* output width */
+	int trunc_only;
+	int extremes = 0;
+
+	/* set basic columns width
+	 */
+	list_for_each(p, &tb->tb_columns) {
+		struct tt_column *cl =
+				list_entry(p, struct tt_column, cl_columns);
+
+		count_column_width(tb, cl, buf, bufsz);
+		width += cl->width + (is_last_column(tb, cl) ? 0 : 1);
+		extremes += cl->is_extreme;
+	}
+
+	if (!tb->is_term)
+		return;
+
+	/* reduce columns with extreme fields
+	 */
+	if (width > tb->termwidth && extremes) {
+		list_for_each(p, &tb->tb_columns) {
+			struct tt_column *cl = list_entry(p, struct tt_column, cl_columns);
+			size_t org_width;
+
+			if (!cl->is_extreme)
+				continue;
+
+			org_width = cl->width;
+			count_column_width(tb, cl, buf, bufsz);
+
+			if (org_width > cl->width)
+				width -= org_width - cl->width;
+			else
+				extremes--;	/* hmm... nothing reduced */
+		}
+	}
+
+	if (width < tb->termwidth) {
+		/* try to found extreme column which fits into available space
+		 */
+		if (extremes) {
+			/* enlarge the first extreme column */
+			list_for_each(p, &tb->tb_columns) {
+				struct tt_column *cl =
+					list_entry(p, struct tt_column, cl_columns);
+				size_t add;
+
+				if (!cl->is_extreme)
+					continue;
+
+				/* this column is tooo large, ignore?
+				if (cl->width_max - cl->width >
+						(tb->termwidth - width))
+					continue;
+				*/
+
+				add = tb->termwidth - width;
+				if (add && cl->width + add > cl->width_max)
+					add = cl->width_max - cl->width;
+
+				cl->width += add;
+				width += add;
+
+				if (width == tb->termwidth)
+					break;
+			}
+		}
+		if (width < tb->termwidth) {
+			/* enalarge the last column */
+			struct tt_column *cl = list_entry(
+				tb->tb_columns.prev, struct tt_column, cl_columns);
+
+			if (!(cl->flags & TT_FL_RIGHT) && tb->termwidth - width > 0) {
+				cl->width += tb->termwidth - width;
+				width = tb->termwidth;
+			}
+		}
+	}
+
+	/* bad, we have to reduce output width, this is done in two steps:
+	 * 1/ reduce columns with a relative width and with truncate flag
+	 * 2) reduce columns with a relative width without truncate flag
+	 */
+	trunc_only = 1;
+	while (width > tb->termwidth) {
+		size_t org = width;
+
+		list_for_each(p, &tb->tb_columns) {
+			struct tt_column *cl =
+				list_entry(p, struct tt_column, cl_columns);
+
+			if (width <= tb->termwidth)
+				break;
+			if (cl->width_hint > 1 && !(cl->flags & TT_FL_TRUNC))
+				continue;	/* never truncate columns with absolute sizes */
+			if (cl->flags & TT_FL_TREE)
+				continue;	/* never truncate the tree */
+			if (trunc_only && !(cl->flags & TT_FL_TRUNC))
+				continue;
+			if (cl->width == cl->width_min)
+				continue;
+
+			/* truncate column with relative sizes */
+			if (cl->width_hint < 1 && cl->width > 0 && width > 0 &&
+			    cl->width > cl->width_hint * tb->termwidth) {
+				cl->width--;
+				width--;
+			}
+			/* truncate column with absolute size */
+			if (cl->width_hint > 1 && cl->width > 0 && width > 0 &&
+			    !trunc_only) {
+				cl->width--;
+				width--;
+			}
+
+		}
+		if (org == width) {
+			if (trunc_only)
+				trunc_only = 0;
+			else
+				break;
+		}
+	}
+
+/*
+	fprintf(stderr, "terminal: %d, output: %d\n", tb->termwidth, width);
+
+	list_for_each(p, &tb->tb_columns) {
+		struct tt_column *cl =
+			list_entry(p, struct tt_column, cl_columns);
+
+		fprintf(stderr, "width: %s=%zd [hint=%d, avg=%zd, max=%zd, extreme=%s]\n",
+			cl->name, cl->width,
+			cl->width_hint > 1 ? (int) cl->width_hint :
+					     (int) (cl->width_hint * tb->termwidth),
+			cl->width_avg,
+			cl->width_max,
+			cl->is_extreme ? "yes" : "not");
+	}
+*/
+	return;
+}
+
+void tt_fputs_quoted(const char *data, FILE *out)
+{
+	const char *p;
+
+	fputc('"', out);
+	for (p = data; p && *p; p++) {
+		if ((unsigned char) *p == 0x22 ||		/* " */
+		    (unsigned char) *p == 0x5c ||		/* \ */
+		    !isprint((unsigned char) *p) ||
+		    iscntrl((unsigned char) *p)) {
+
+			fprintf(out, "\\x%02x", (unsigned char) *p);
+		} else
+			fputc(*p, out);
+	}
+	fputc('"', out);
+}
+
+void tt_fputs_nonblank(const char *data, FILE *out)
+{
+	const char *p;
+
+	for (p = data; p && *p; p++) {
+		if (isblank((unsigned char) *p) ||
+		    (unsigned char) *p == 0x5c ||		/* \ */
+		    !isprint((unsigned char) *p) ||
+		    iscntrl((unsigned char) *p)) {
+
+			fprintf(out, "\\x%02x", (unsigned char) *p);
+
+		} else
+			fputc(*p, out);
+	}
+}
+
+/*
+ * Prints data, data maybe be printed in more formats (raw, NAME=xxx pairs) and
+ * control and non-printable chars maybe encoded in \x?? hex encoding.
+ */
+static void print_data(struct tt *tb, struct tt_column *cl, char *data)
+{
+	size_t len = 0, i, width;
+	char *buf;
+
+	if (!data)
+		data = "";
+
+	/* raw mode */
+	if (tb->flags & TT_FL_RAW) {
+		tt_fputs_nonblank(data, stdout);
+		if (!is_last_column(tb, cl))
+			fputc(' ', stdout);
+		return;
+	}
+
+	/* NAME=value mode */
+	if (tb->flags & TT_FL_EXPORT) {
+		fprintf(stdout, "%s=", cl->name);
+		tt_fputs_quoted(data, stdout);
+		if (!is_last_column(tb, cl))
+			fputc(' ', stdout);
+		return;
+	}
+
+	/* note that 'len' and 'width' are number of cells, not bytes */
+	buf = mbs_safe_encode(data, &len);
+	data = buf;
+	if (!data)
+		data = "";
+
+	if (!len || len == (size_t) -1) {
+		len = 0;
+		data = NULL;
+	}
+	width = cl->width;
+
+	if (is_last_column(tb, cl) && len < width)
+		width = len;
+
+	/* truncate data */
+	if (len > width && (cl->flags & TT_FL_TRUNC)) {
+		if (data)
+			len = mbs_truncate(data, &width);
+		if (!data || len == (size_t) -1) {
+			len = 0;
+			data = NULL;
+		}
+	}
+	if (data) {
+		if (!(tb->flags & TT_FL_RAW) && (cl->flags & TT_FL_RIGHT)) {
+			size_t xw = cl->width;
+			fprintf(stdout, "%*s", (int) xw, data);
+			if (len < xw)
+				len = xw;
+		}
+		else
+			fputs(data, stdout);
+	}
+	for (i = len; i < width; i++)
+		fputc(' ', stdout);		/* padding */
+
+	if (!is_last_column(tb, cl)) {
+		if (len > width && !(cl->flags & TT_FL_TRUNC)) {
+			fputc('\n', stdout);
+			for (i = 0; i <= (size_t) cl->seqnum; i++) {
+				struct tt_column *x = tt_get_column(tb, i);
+				printf("%*s ", -((int)x->width), " ");
+			}
+		} else
+			fputc(' ', stdout);	/* columns separator */
+	}
+
+	free(buf);
+}
+
+static void print_line(struct tt_line *ln, char *buf, size_t bufsz)
+{
+	struct list_head *p;
+
+	/* set width according to the size of data
+	 */
+	list_for_each(p, &ln->table->tb_columns) {
+		struct tt_column *cl =
+				list_entry(p, struct tt_column, cl_columns);
+
+		print_data(ln->table, cl, line_get_data(ln, cl, buf, bufsz));
+	}
+	fputc('\n', stdout);
+}
+
+static void print_header(struct tt *tb, char *buf, size_t bufsz)
+{
+	struct list_head *p;
+
+	if (!tb->first_run ||
+	    (tb->flags & TT_FL_NOHEADINGS) ||
+	    (tb->flags & TT_FL_EXPORT) ||
+	    list_empty(&tb->tb_lines))
+		return;
+
+	/* set width according to the size of data
+	 */
+	list_for_each(p, &tb->tb_columns) {
+		struct tt_column *cl =
+				list_entry(p, struct tt_column, cl_columns);
+
+		strncpy(buf, cl->name, bufsz);
+		buf[bufsz - 1] = '\0';
+		print_data(tb, cl, buf);
+	}
+	fputc('\n', stdout);
+}
+
+static void print_table(struct tt *tb, char *buf, size_t bufsz)
+{
+	struct list_head *p;
+
+	print_header(tb, buf, bufsz);
+
+	list_for_each(p, &tb->tb_lines) {
+		struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
+
+		print_line(ln, buf, bufsz);
+	}
+}
+
+static void print_tree_line(struct tt_line *ln, char *buf, size_t bufsz)
+{
+	struct list_head *p;
+
+	print_line(ln, buf, bufsz);
+
+	if (list_empty(&ln->ln_branch))
+		return;
+
+	/* print all children */
+	list_for_each(p, &ln->ln_branch) {
+		struct tt_line *chld =
+				list_entry(p, struct tt_line, ln_children);
+		print_tree_line(chld, buf, bufsz);
+	}
+}
+
+static void print_tree(struct tt *tb, char *buf, size_t bufsz)
+{
+	struct list_head *p;
+
+	print_header(tb, buf, bufsz);
+
+	list_for_each(p, &tb->tb_lines) {
+		struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
+
+		if (ln->parent)
+			continue;
+
+		print_tree_line(ln, buf, bufsz);
+	}
+}
+
+/*
+ * @tb: table
+ *
+ * Prints the table to stdout
+ */
+int tt_print_table(struct tt *tb)
+{
+	char *line;
+	size_t line_sz;
+	struct list_head *p;
+
+	if (!tb)
+		return -1;
+
+	if (tb->first_run) {
+		tb->is_term = isatty(STDOUT_FILENO);
+
+		if (tb->is_term && !tb->termwidth)
+			tb->termwidth = get_terminal_width();
+		if (tb->termwidth <= 0)
+			tb->termwidth = 80;
+	}
+
+	line_sz = tb->termwidth;
+
+	list_for_each(p, &tb->tb_lines) {
+		struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
+		if (ln->data_sz > line_sz)
+			line_sz = ln->data_sz;
+	}
+
+	line_sz++;			/* make a space for \0 */
+	line = malloc(line_sz);
+	if (!line)
+		return -1;
+
+	if (tb->first_run &&
+	    !((tb->flags & TT_FL_RAW) || (tb->flags & TT_FL_EXPORT)))
+		recount_widths(tb, line, line_sz);
+
+	if (tb->flags & TT_FL_TREE)
+		print_tree(tb, line, line_sz);
+	else
+		print_table(tb, line, line_sz);
+
+	free(line);
+
+	tb->first_run = FALSE;
+	return 0;
+}
+
+#ifdef TEST_PROGRAM
+#include <errno.h>
+
+enum { MYCOL_NAME, MYCOL_FOO, MYCOL_BAR, MYCOL_PATH };
+
+int main(int argc, char *argv[])
+{
+	struct tt *tb;
+	struct tt_line *ln, *pr, *root;
+	int flags = 0, notree = 0, i;
+
+	if (argc == 2 && !strcmp(argv[1], "--help")) {
+		printf("%s [--ascii | --raw | --list]\n",
+				program_invocation_short_name);
+		return EXIT_SUCCESS;
+	} else if (argc == 2 && !strcmp(argv[1], "--ascii")) {
+		flags |= TT_FL_ASCII;
+	} else if (argc == 2 && !strcmp(argv[1], "--raw")) {
+		flags |= TT_FL_RAW;
+		notree = 1;
+	} else if (argc == 2 && !strcmp(argv[1], "--export")) {
+		flags |= TT_FL_EXPORT;
+		notree = 1;
+	} else if (argc == 2 && !strcmp(argv[1], "--list"))
+		notree = 1;
+
+	setlocale(LC_ALL, "");
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+
+	tb = tt_new_table(flags);
+	if (!tb)
+		err(EXIT_FAILURE, "table initialization failed");
+
+	tt_define_column(tb, "NAME", 0.3, notree ? 0 : TT_FL_TREE);
+	tt_define_column(tb, "FOO", 0.3, TT_FL_TRUNC);
+	tt_define_column(tb, "BAR", 0.3, 0);
+	tt_define_column(tb, "PATH", 0.3, 0);
+
+	for (i = 0; i < 2; i++) {
+		root = ln = tt_add_line(tb, NULL);
+		tt_line_set_data(ln, MYCOL_NAME, "AAA");
+		tt_line_set_data(ln, MYCOL_FOO, "a-foo-foo");
+		tt_line_set_data(ln, MYCOL_BAR, "barBar-A");
+		tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA");
+
+		pr = ln = tt_add_line(tb, ln);
+		tt_line_set_data(ln, MYCOL_NAME, "AAA.A");
+		tt_line_set_data(ln, MYCOL_FOO, "a.a-foo-foo");
+		tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A");
+		tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A");
+
+		ln = tt_add_line(tb, pr);
+		tt_line_set_data(ln, MYCOL_NAME, "AAA.A.AAA");
+		tt_line_set_data(ln, MYCOL_FOO, "a.a.a-foo-foo");
+		tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A.A");
+		tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A/AAA");
+
+		ln = tt_add_line(tb, root);
+		tt_line_set_data(ln, MYCOL_NAME, "AAA.B");
+		tt_line_set_data(ln, MYCOL_FOO, "a.b-foo-foo");
+		tt_line_set_data(ln, MYCOL_BAR, "barBar-A.B");
+		tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/B");
+
+		ln = tt_add_line(tb, pr);
+		tt_line_set_data(ln, MYCOL_NAME, "AAA.A.BBB");
+		tt_line_set_data(ln, MYCOL_FOO, "a.a.b-foo-foo");
+		tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A.BBB");
+		tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A/BBB");
+
+		ln = tt_add_line(tb, pr);
+		tt_line_set_data(ln, MYCOL_NAME, "AAA.A.CCC");
+		tt_line_set_data(ln, MYCOL_FOO, "a.a.c-foo-foo");
+		tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A.CCC");
+		tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A/CCC");
+
+		ln = tt_add_line(tb, root);
+		tt_line_set_data(ln, MYCOL_NAME, "AAA.C");
+		tt_line_set_data(ln, MYCOL_FOO, "a.c-foo-foo");
+		tt_line_set_data(ln, MYCOL_BAR, "barBar-A.C");
+		tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/C");
+	}
+
+	tt_print_table(tb);
+	tt_free_table(tb);
+
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/tt.h b/libblkid/tt.h
new file mode 100644
index 0000000..603844f
--- /dev/null
+++ b/libblkid/tt.h
@@ -0,0 +1,94 @@
+/*
+ * Prints table or tree. See lib/table.c for more details and example.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_TT_H
+#define UTIL_LINUX_TT_H
+
+#include "list.h"
+
+enum {
+	/*
+	 * Global flags
+	 */
+	TT_FL_RAW         = (1 << 1),
+	TT_FL_ASCII       = (1 << 2),
+	TT_FL_NOHEADINGS  = (1 << 3),
+	TT_FL_EXPORT      = (1 << 4),
+
+	/*
+	 * Column flags
+	 */
+	TT_FL_TRUNC       = (1 << 5),	/* truncate fields data if necessary */
+	TT_FL_TREE        = (1 << 6),	/* use tree "ascii art" */
+	TT_FL_RIGHT	  = (1 << 7),	/* align to the right */
+	TT_FL_STRICTWIDTH = (1 << 8),	/* don't reduce width if column is empty */
+	TT_FL_NOEXTREMES  = (1 << 9)    /* ignore extreme fields when count column width*/
+};
+
+struct tt {
+	size_t	ncols;		/* number of columns */
+	size_t	termwidth;	/* terminal width */
+	int	is_term;	/* is a tty? */
+	int	flags;
+	int	first_run;
+
+	struct list_head	tb_columns;
+	struct list_head	tb_lines;
+
+	const struct tt_symbols	*symbols;
+};
+
+struct tt_column {
+	const char *name;	/* header */
+	size_t	seqnum;
+
+	size_t	width;		/* real column width */
+	size_t	width_min;	/* minimal width (usually header width) */
+	size_t  width_max;	/* maximal width */
+	size_t  width_avg;	/* average width, used to detect extreme fields */
+	double	width_hint;	/* hint (N < 1 is in percent of termwidth) */
+
+	int	flags;
+	int	is_extreme;
+
+	struct list_head	cl_columns;
+};
+
+struct tt_line {
+	struct tt	*table;
+	char const	**data;
+	void		*userdata;
+	size_t		data_sz;		/* strlen of all data */
+
+	struct list_head	ln_lines;	/* table lines */
+
+	struct list_head	ln_branch;	/* begin of branch (head of ln_children) */
+	struct list_head	ln_children;
+
+	struct tt_line	*parent;
+};
+
+extern struct tt *tt_new_table(int flags);
+extern void tt_free_table(struct tt *tb);
+extern void tt_remove_lines(struct tt *tb);
+extern int tt_print_table(struct tt *tb);
+
+extern struct tt_column *tt_define_column(struct tt *tb, const char *name,
+						double whint, int flags);
+
+extern struct tt_column *tt_get_column(struct tt *tb, size_t colnum);
+
+extern struct tt_line *tt_add_line(struct tt *tb, struct tt_line *parent);
+
+extern int tt_line_set_data(struct tt_line *ln, int colnum, const char *data);
+extern int tt_line_set_userdata(struct tt_line *ln, void *data);
+
+extern void tt_fputs_quoted(const char *data, FILE *out);
+extern void tt_fputs_nonblank(const char *data, FILE *out);
+
+#endif /* UTIL_LINUX_TT_H */
diff --git a/libblkid/ttyutils.c b/libblkid/ttyutils.c
new file mode 100644
index 0000000..3b5a68c
--- /dev/null
+++ b/libblkid/ttyutils.c
@@ -0,0 +1,94 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <ctype.h>
+
+#include "c.h"
+#include "ttyutils.h"
+
+int get_terminal_width(void)
+{
+#ifdef TIOCGSIZE
+	struct ttysize	t_win;
+#endif
+#ifdef TIOCGWINSZ
+	struct winsize	w_win;
+#endif
+        const char	*cp;
+
+#ifdef TIOCGSIZE
+	if (ioctl (0, TIOCGSIZE, &t_win) == 0)
+		return t_win.ts_cols;
+#endif
+#ifdef TIOCGWINSZ
+	if (ioctl (0, TIOCGWINSZ, &w_win) == 0)
+		return w_win.ws_col;
+#endif
+        cp = getenv("COLUMNS");
+	if (cp) {
+		char *end = NULL;
+		long c;
+
+		errno = 0;
+		c = strtol(cp, &end, 10);
+
+		if (errno == 0 && end && *end == '\0' && end > cp &&
+		    c > 0 && c <= INT_MAX)
+			return c;
+	}
+	return 0;
+}
+
+int get_terminal_name(const char **path,
+		      const char **name,
+		      const char **number)
+{
+	const char *tty;
+	const char *p;
+
+	if (name)
+		*name = NULL;
+	if (path)
+		*path = NULL;
+	if (number)
+		*number = NULL;
+
+	tty = ttyname(STDERR_FILENO);
+	if (!tty)
+		return -1;
+	if (path)
+		*path = tty;
+	tty = strncmp(tty, "/dev/", 5) == 0 ? tty + 5 : tty;
+	if (name)
+		*name = tty;
+	if (number) {
+		for (p = tty; p && *p; p++) {
+			if (isdigit(*p)) {
+				*number = p;
+				break;
+			}
+		}
+	}
+	return 0;
+}
+
+
+#ifdef TEST_PROGRAM
+# include <stdlib.h>
+int main(void)
+{
+	const char *path, *name, *num;
+
+	if (get_terminal_name(&path, &name, &num) == 0) {
+		fprintf(stderr, "tty path:   %s\n", path);
+		fprintf(stderr, "tty name:   %s\n", name);
+		fprintf(stderr, "tty number: %s\n", num);
+	}
+	fprintf(stderr,         "tty width:  %d\n", get_terminal_width());
+
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/ttyutils.h b/libblkid/ttyutils.h
new file mode 100644
index 0000000..021156d
--- /dev/null
+++ b/libblkid/ttyutils.h
@@ -0,0 +1,126 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_TTYUTILS_H
+#define UTIL_LINUX_TTYUTILS_H
+
+#include <stdlib.h>
+#include <termios.h>
+#include <limits.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+/* Some shorthands for control characters. */
+#define CTL(x)		((x) ^ 0100)	/* Assumes ASCII dialect */
+#define CR		CTL('M')	/* carriage return */
+#define NL		CTL('J')	/* line feed */
+#define BS		CTL('H')	/* back space */
+#define DEL		CTL('?')	/* delete */
+
+/* Defaults for line-editing etc. characters; you may want to change these. */
+#define DEF_ERASE	DEL		/* default erase character */
+#define DEF_INTR	CTL('C')	/* default interrupt character */
+#define DEF_QUIT	CTL('\\')	/* default quit char */
+#define DEF_KILL	CTL('U')	/* default kill char */
+#define DEF_EOF		CTL('D')	/* default EOF char */
+#define DEF_EOL		0
+#define DEF_SWITCH	0		/* default switch char */
+
+/* Storage for things detected while the login name was read. */
+struct chardata {
+	int erase;		/* erase character */
+	int kill;		/* kill character */
+	int eol;		/* end-of-line character */
+	int parity;		/* what parity did we see */
+	int capslock;		/* upper case without lower case */
+};
+
+#define INIT_CHARDATA(ptr) do {              \
+		(ptr)->erase    = DEF_ERASE; \
+		(ptr)->kill     = DEF_KILL;  \
+		(ptr)->eol      = CTRL('r'); \
+	        (ptr)->parity   = 0;         \
+	        (ptr)->capslock = 0;         \
+	} while (0)
+
+extern int get_terminal_width(void);
+extern int get_terminal_name(const char **path, const char **name, const char **number);
+
+#define UL_TTY_KEEPCFLAGS	(1 << 1)
+#define UL_TTY_UTF8		(1 << 2)
+
+static inline void reset_virtual_console(struct termios *tp, int flags)
+{
+	/* Use defaults of <sys/ttydefaults.h> for base settings */
+	tp->c_iflag |= TTYDEF_IFLAG;
+	tp->c_oflag |= TTYDEF_OFLAG;
+	tp->c_lflag |= TTYDEF_LFLAG;
+
+	if ((flags & UL_TTY_KEEPCFLAGS) == 0) {
+#ifdef CBAUD
+		tp->c_lflag &= ~CBAUD;
+#endif
+		tp->c_cflag |= (B38400 | TTYDEF_CFLAG);
+	}
+
+	/* Sane setting, allow eight bit characters, no carriage return delay
+	 * the same result as `stty sane cr0 pass8'
+	 */
+	tp->c_iflag |=  (BRKINT | ICRNL | IMAXBEL);
+	tp->c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | ISTRIP);
+	tp->c_oflag |=  (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0);
+	tp->c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | \
+			    NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
+	tp->c_lflag |=  (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOKE);
+	tp->c_lflag &= ~(ECHONL|ECHOCTL|ECHOPRT | NOFLSH | TOSTOP);
+
+	if ((flags & UL_TTY_KEEPCFLAGS) == 0) {
+		tp->c_cflag |=  (CREAD | CS8 | HUPCL);
+		tp->c_cflag &= ~(PARODD | PARENB);
+	}
+#ifdef OFDEL
+	tp->c_oflag &= ~OFDEL;
+#endif
+#ifdef XCASE
+	tp->c_lflag &= ~XCASE;
+#endif
+#ifdef IUTF8
+	if (flags & UL_TTY_UTF8)
+		tp->c_iflag |= IUTF8;	    /* Set UTF-8 input flag */
+	else
+		tp->c_iflag &= ~IUTF8;
+#endif
+	/* VTIME and VMIN can overlap with VEOF and VEOL since they are
+	 * only used for non-canonical mode. We just set the at the
+	 * beginning, so nothing bad should happen.
+	 */
+	tp->c_cc[VTIME]    = 0;
+	tp->c_cc[VMIN]     = 1;
+	tp->c_cc[VINTR]    = CINTR;
+	tp->c_cc[VQUIT]    = CQUIT;
+	tp->c_cc[VERASE]   = CERASE; /* ASCII DEL (0177) */
+	tp->c_cc[VKILL]    = CKILL;
+	tp->c_cc[VEOF]     = CEOF;
+#ifdef VSWTC
+	tp->c_cc[VSWTC]    = _POSIX_VDISABLE;
+#elif defined(VSWTCH)
+	tp->c_cc[VSWTCH]   = _POSIX_VDISABLE;
+#endif
+	tp->c_cc[VSTART]   = CSTART;
+	tp->c_cc[VSTOP]    = CSTOP;
+	tp->c_cc[VSUSP]    = CSUSP;
+	tp->c_cc[VEOL]     = _POSIX_VDISABLE;
+	tp->c_cc[VREPRINT] = CREPRINT;
+	tp->c_cc[VDISCARD] = CDISCARD;
+	tp->c_cc[VWERASE]  = CWERASE;
+	tp->c_cc[VLNEXT]   = CLNEXT;
+	tp->c_cc[VEOL2]    = _POSIX_VDISABLE;
+}
+
+
+
+#endif /* UTIL_LINUX_TTYUTILS_H */
diff --git a/libblkid/ubifs.c b/libblkid/ubifs.c
new file mode 100644
index 0000000..2d69c2b
--- /dev/null
+++ b/libblkid/ubifs.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2009 Corentin Chary <corentincj@iksaif.net>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/*
+ * struct ubifs_ch - common header node.
+ * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
+ * @crc: CRC-32 checksum of the node header
+ * @sqnum: sequence number
+ * @len: full node length
+ * @node_type: node type
+ * @group_type: node group type
+ * @padding: reserved for future, zeroes
+ *
+ * Every UBIFS node starts with this common part. If the node has a key, the
+ * key always goes next.
+ */
+struct ubifs_ch {
+	uint32_t magic;
+	uint32_t crc;
+	uint64_t sqnum;
+	uint32_t len;
+	uint8_t node_type;
+	uint8_t group_type;
+	uint8_t padding[2];
+} __attribute__ ((packed));
+
+/*
+ * struct ubifs_sb_node - superblock node.
+ * @ch: common header
+ * @padding: reserved for future, zeroes
+ * @key_hash: type of hash function used in keys
+ * @key_fmt: format of the key
+ * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc)
+ * @min_io_size: minimal input/output unit size
+ * @leb_size: logical eraseblock size in bytes
+ * @leb_cnt: count of LEBs used by file-system
+ * @max_leb_cnt: maximum count of LEBs used by file-system
+ * @max_bud_bytes: maximum amount of data stored in buds
+ * @log_lebs: log size in logical eraseblocks
+ * @lpt_lebs: number of LEBs used for lprops table
+ * @orph_lebs: number of LEBs used for recording orphans
+ * @jhead_cnt: count of journal heads
+ * @fanout: tree fanout (max. number of links per indexing node)
+ * @lsave_cnt: number of LEB numbers in LPT's save table
+ * @fmt_version: UBIFS on-flash format version
+ * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
+ * @padding1: reserved for future, zeroes
+ * @rp_uid: reserve pool UID
+ * @rp_gid: reserve pool GID
+ * @rp_size: size of the reserved pool in bytes
+ * @padding2: reserved for future, zeroes
+ * @time_gran: time granularity in nanoseconds
+ * @uuid: UUID generated when the file system image was created
+ * @ro_compat_version: UBIFS R/O compatibility version
+ */
+struct ubifs_sb_node {
+	struct ubifs_ch ch;
+	uint8_t padding[2];
+	uint8_t key_hash;
+	uint8_t key_fmt;
+	uint32_t flags;
+	uint32_t min_io_size;
+	uint32_t leb_size;
+	uint32_t leb_cnt;
+	uint32_t max_leb_cnt;
+	uint64_t max_bud_bytes;
+	uint32_t log_lebs;
+	uint32_t lpt_lebs;
+	uint32_t orph_lebs;
+	uint32_t jhead_cnt;
+	uint32_t fanout;
+	uint32_t lsave_cnt;
+	uint32_t fmt_version;
+	uint16_t default_compr;
+	uint8_t padding1[2];
+	uint32_t rp_uid;
+	uint32_t rp_gid;
+	uint64_t rp_size;
+	uint32_t time_gran;
+	uint8_t uuid[16];
+	uint32_t ro_compat_version;
+	uint8_t padding2[3968];
+} __attribute__ ((packed));
+
+static int probe_ubifs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct ubifs_sb_node *sb;
+
+	sb = blkid_probe_get_sb(pr, mag, struct ubifs_sb_node);
+	if (!sb)
+		return -1;
+
+	blkid_probe_set_uuid(pr, sb->uuid);
+	blkid_probe_sprintf_version(pr, "w%dr%d",
+				    sb->fmt_version, sb->ro_compat_version);
+	return 0;
+}
+
+const struct blkid_idinfo ubifs_idinfo =
+{
+	.name		= "ubifs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ubifs,
+	.magics		=
+	{
+		{ .magic = "\x31\x18\x10\x06", .len = 4 },
+		{ NULL }
+	}
+};
diff --git a/libblkid/udf.c b/libblkid/udf.c
new file mode 100644
index 0000000..2cb471d
--- /dev/null
+++ b/libblkid/udf.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+#include "iso9660.h"
+
+struct volume_descriptor {
+	struct descriptor_tag {
+		uint16_t	id;
+		uint16_t	version;
+		uint8_t		checksum;
+		uint8_t		reserved;
+		uint16_t	serial;
+		uint16_t	crc;
+		uint16_t	crc_len;
+		uint32_t	location;
+	} __attribute__((packed)) tag;
+
+	union {
+		struct anchor_descriptor {
+			uint32_t	length;
+			uint32_t	location;
+		} __attribute__((packed)) anchor;
+
+		struct primary_descriptor {
+			uint32_t	seq_num;
+			uint32_t	desc_num;
+			struct dstring {
+				uint8_t	clen;
+				uint8_t	c[31];
+			} __attribute__((packed)) ident;
+		} __attribute__((packed)) primary;
+
+	} __attribute__((packed)) type;
+
+} __attribute__((packed));
+
+struct volume_structure_descriptor {
+	uint8_t		type;
+	uint8_t		id[5];
+	uint8_t		version;
+} __attribute__((packed));
+
+#define UDF_VSD_OFFSET			0x8000LL
+
+static int probe_udf(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct volume_descriptor *vd;
+	struct volume_structure_descriptor *vsd;
+	unsigned int bs;
+	unsigned int b;
+	unsigned int type;
+	unsigned int count;
+	unsigned int loc;
+
+	/* search Volume Sequence Descriptor (VSD) to get the logical
+	 * block size of the volume */
+	for (bs = 0x800; bs < 0x8000; bs += 0x800) {
+		vsd = (struct volume_structure_descriptor *)
+			blkid_probe_get_buffer(pr,
+					UDF_VSD_OFFSET + bs,
+					sizeof(*vsd));
+		if (!vsd)
+			return 1;
+		if (vsd->id[0] != '\0')
+			goto nsr;
+	}
+	return -1;
+
+nsr:
+	/* search the list of VSDs for a NSR descriptor */
+	for (b = 0; b < 64; b++) {
+		vsd = (struct volume_structure_descriptor *)
+			blkid_probe_get_buffer(pr,
+					UDF_VSD_OFFSET + ((blkid_loff_t) b * bs),
+					sizeof(*vsd));
+		if (!vsd)
+			return -1;
+		if (vsd->id[0] == '\0')
+			return -1;
+		if (memcmp(vsd->id, "NSR02", 5) == 0)
+			goto anchor;
+		if (memcmp(vsd->id, "NSR03", 5) == 0)
+			goto anchor;
+	}
+	return -1;
+
+anchor:
+	/* read Anchor Volume Descriptor (AVDP) */
+	vd = (struct volume_descriptor *)
+		blkid_probe_get_buffer(pr, 256 * bs, sizeof(*vd));
+	if (!vd)
+		return -1;
+
+	type = le16_to_cpu(vd->tag.id);
+	if (type != 2) /* TAG_ID_AVDP */
+		return 0;
+
+	/* get desriptor list address and block count */
+	count = le32_to_cpu(vd->type.anchor.length) / bs;
+	loc = le32_to_cpu(vd->type.anchor.location);
+
+	/* check if the list is usable */
+	for (b = 0; b < count; b++) {
+		vd = (struct volume_descriptor *)
+			blkid_probe_get_buffer(pr,
+					(blkid_loff_t) (loc + b) * bs,
+					sizeof(*vd));
+		if (!vd)
+			return -1;
+	}
+
+	/* Try extract all possible ISO9660 information -- if there is
+	 * usable LABEL in ISO header then use it, otherwise read UDF
+	 * specific LABEL */
+	if (probe_iso9660(pr, mag) == 0 &&
+	    __blkid_probe_lookup_value(pr, "LABEL") != NULL)
+		return 0;
+
+	/* Read UDF label */
+	for (b = 0; b < count; b++) {
+		vd = (struct volume_descriptor *)
+			blkid_probe_get_buffer(pr,
+					(blkid_loff_t) (loc + b) * bs,
+					sizeof(*vd));
+
+		type = le16_to_cpu(vd->tag.id);
+		if (type == 0)
+			break;
+		if (le32_to_cpu(vd->tag.location) != loc + b)
+			break;
+		if (type == 1) { /* TAG_ID_PVD */
+			uint8_t clen = vd->type.primary.ident.clen;
+
+			if (clen == 8)
+				blkid_probe_set_label(pr,
+						vd->type.primary.ident.c, 31);
+			else if (clen == 16)
+				blkid_probe_set_utf8label(pr,
+						vd->type.primary.ident.c,
+						31, BLKID_ENC_UTF16BE);
+
+			if (clen == 8 || clen == 16)
+				break;
+		}
+	}
+
+	return 0;
+}
+
+
+const struct blkid_idinfo udf_idinfo =
+{
+	.name		= "udf",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_udf,
+	.flags		= BLKID_IDINFO_TOLERANT,
+	.magics		=
+	{
+		{ .magic = "BEA01", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "CDW02", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "NSR02", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "NSR03", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "TEA01", .len = 5, .kboff = 32, .sboff = 1 },
+		{ NULL }
+	}
+};
diff --git a/libblkid/ufs.c b/libblkid/ufs.c
new file mode 100644
index 0000000..673a528
--- /dev/null
+++ b/libblkid/ufs.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+struct ufs_super_block {
+	uint32_t	fs_link;
+	uint32_t	fs_rlink;
+	uint32_t	fs_sblkno;
+	uint32_t	fs_cblkno;
+	uint32_t	fs_iblkno;
+	uint32_t	fs_dblkno;
+	uint32_t	fs_cgoffset;
+	uint32_t	fs_cgmask;
+	uint32_t	fs_time;
+	uint32_t	fs_size;
+	uint32_t	fs_dsize;
+	uint32_t	fs_ncg;
+	uint32_t	fs_bsize;
+	uint32_t	fs_fsize;
+	uint32_t	fs_frag;
+	uint32_t	fs_minfree;
+	uint32_t	fs_rotdelay;
+	uint32_t	fs_rps;
+	uint32_t	fs_bmask;
+	uint32_t	fs_fmask;
+	uint32_t	fs_bshift;
+	uint32_t	fs_fshift;
+	uint32_t	fs_maxcontig;
+	uint32_t	fs_maxbpg;
+	uint32_t	fs_fragshift;
+	uint32_t	fs_fsbtodb;
+	uint32_t	fs_sbsize;
+	uint32_t	fs_csmask;
+	uint32_t	fs_csshift;
+	uint32_t	fs_nindir;
+	uint32_t	fs_inopb;
+	uint32_t	fs_nspf;
+	uint32_t	fs_optim;
+	uint32_t	fs_npsect_state;
+	uint32_t	fs_interleave;
+	uint32_t	fs_trackskew;
+	uint32_t	fs_id[2];
+	uint32_t	fs_csaddr;
+	uint32_t	fs_cssize;
+	uint32_t	fs_cgsize;
+	uint32_t	fs_ntrak;
+	uint32_t	fs_nsect;
+	uint32_t	fs_spc;
+	uint32_t	fs_ncyl;
+	uint32_t	fs_cpg;
+	uint32_t	fs_ipg;
+	uint32_t	fs_fpg;
+	struct ufs_csum {
+		uint32_t	cs_ndir;
+		uint32_t	cs_nbfree;
+		uint32_t	cs_nifree;
+		uint32_t	cs_nffree;
+	} fs_cstotal;
+	int8_t		fs_fmod;
+	int8_t		fs_clean;
+	int8_t		fs_ronly;
+	int8_t		fs_flags;
+	union {
+		struct {
+			int8_t	fs_fsmnt[512];
+			uint32_t	fs_cgrotor;
+			uint32_t	fs_csp[31];
+			uint32_t	fs_maxcluster;
+			uint32_t	fs_cpc;
+			uint16_t	fs_opostbl[16][8];
+		} fs_u1;
+		struct {
+			int8_t		fs_fsmnt[468];
+			uint8_t		fs_volname[32];
+			uint64_t	fs_swuid;
+			int32_t		fs_pad;
+			uint32_t	fs_cgrotor;
+			uint32_t	fs_ocsp[28];
+			uint32_t	fs_contigdirs;
+			uint32_t	fs_csp;
+			uint32_t	fs_maxcluster;
+			uint32_t	fs_active;
+			int32_t		fs_old_cpc;
+			int32_t		fs_maxbsize;
+			int64_t		fs_sparecon64[17];
+			int64_t		fs_sblockloc;
+			struct ufs2_csum_total {
+				uint64_t	cs_ndir;
+				uint64_t	cs_nbfree;
+				uint64_t	cs_nifree;
+				uint64_t	cs_nffree;
+				uint64_t	cs_numclusters;
+				uint64_t	cs_spare[3];
+			} fs_cstotal;
+			struct ufs_timeval {
+				int32_t		tv_sec;
+				int32_t		tv_usec;
+			} fs_time;
+			int64_t		fs_size;
+			int64_t		fs_dsize;
+			uint64_t	fs_csaddr;
+			int64_t		fs_pendingblocks;
+			int32_t		fs_pendinginodes;
+		} __attribute__((packed)) fs_u2;
+	}  fs_u11;
+	union {
+		struct {
+			int32_t		fs_sparecon[53];
+			int32_t		fs_reclaim;
+			int32_t		fs_sparecon2[1];
+			int32_t		fs_state;
+			uint32_t	fs_qbmask[2];
+			uint32_t	fs_qfmask[2];
+		} fs_sun;
+		struct {
+			int32_t		fs_sparecon[53];
+			int32_t		fs_reclaim;
+			int32_t		fs_sparecon2[1];
+			uint32_t	fs_npsect;
+			uint32_t	fs_qbmask[2];
+			uint32_t	fs_qfmask[2];
+		} fs_sunx86;
+		struct {
+			int32_t		fs_sparecon[50];
+			int32_t		fs_contigsumsize;
+			int32_t		fs_maxsymlinklen;
+			int32_t		fs_inodefmt;
+			uint32_t	fs_maxfilesize[2];
+			uint32_t	fs_qbmask[2];
+			uint32_t	fs_qfmask[2];
+			int32_t		fs_state;
+		} fs_44;
+	} fs_u2;
+	int32_t		fs_postblformat;
+	int32_t		fs_nrpos;
+	int32_t		fs_postbloff;
+	int32_t		fs_rotbloff;
+	uint32_t	fs_magic;
+	uint8_t		fs_space[1];
+} __attribute__((packed));
+
+#define UFS_MAGIC			0x00011954
+#define UFS2_MAGIC			0x19540119
+#define UFS_MAGIC_FEA			0x00195612
+#define UFS_MAGIC_LFN			0x00095014
+#define UFS_MAGIC_SEC			0x00612195
+#define UFS_MAGIC_4GB			0x05231994
+
+static int probe_ufs(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	int offsets[] = { 0, 8, 64, 256 };
+	uint32_t mags[] = {
+		UFS2_MAGIC, UFS_MAGIC, UFS_MAGIC_FEA, UFS_MAGIC_LFN,
+		UFS_MAGIC_SEC, UFS_MAGIC_4GB
+	};
+	size_t i;
+	uint32_t magic;
+	struct ufs_super_block *ufs;
+	int is_be;
+
+	for (i = 0; i < ARRAY_SIZE(offsets); i++) {
+		uint32_t magLE, magBE;
+		size_t y;
+
+		ufs = (struct ufs_super_block *)
+				blkid_probe_get_buffer(pr,
+					offsets[i] * 1024,
+					sizeof(struct ufs_super_block));
+		if (!ufs)
+			return -1;
+
+		magBE = be32_to_cpu(ufs->fs_magic);
+		magLE = le32_to_cpu(ufs->fs_magic);
+
+		for (y = 0; y < ARRAY_SIZE(mags); y++) {
+			if (magLE == mags[y] || magBE == mags[y]) {
+				magic = mags[y];
+				is_be = (magBE == mags[y]);
+				goto found;
+			}
+		}
+	}
+
+	return 1;
+
+found:
+	if (magic == UFS2_MAGIC) {
+		blkid_probe_set_version(pr, "2");
+		blkid_probe_set_label(pr, ufs->fs_u11.fs_u2.fs_volname,
+				sizeof(ufs->fs_u11.fs_u2.fs_volname));
+	} else
+		blkid_probe_set_version(pr, "1");
+	if (ufs->fs_id[0] || ufs->fs_id[1])
+	{
+		if (is_be)
+			blkid_probe_sprintf_uuid(pr,
+					 (unsigned char *) &ufs->fs_id,
+						 sizeof(ufs->fs_id),
+						 "%08x%08x",
+						 be32_to_cpu(ufs->fs_id[0]),
+						 be32_to_cpu(ufs->fs_id[1]));
+		else
+			blkid_probe_sprintf_uuid(pr,
+					 (unsigned char *) &ufs->fs_id,
+						 sizeof(ufs->fs_id),
+						 "%08x%08x",
+						 le32_to_cpu(ufs->fs_id[0]),
+						 le32_to_cpu(ufs->fs_id[1]));
+	}
+
+	if (blkid_probe_set_magic(pr,
+			(offsets[i] * 1024) +
+				offsetof(struct ufs_super_block, fs_magic),
+			sizeof(ufs->fs_magic),
+			(unsigned char *) &ufs->fs_magic))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * According to libvolume_id the UFS superblock could be on four positions.
+ * The original libblkid has checked one position (.kboff=8) only.
+ *
+ * We know four UFS magic strings and UFS could be both little-endian and
+ * big-endian. ... so we have:
+ *
+ *	4 position * 4 string * 2 version = 32 magic strings
+ *
+ * It seems simpler to check for these string in probing function that hardcode
+ * all in the .magic array.
+ */
+const struct blkid_idinfo ufs_idinfo =
+{
+	.name		= "ufs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ufs,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/ultrix.c b/libblkid/ultrix.c
new file mode 100644
index 0000000..853ae6e
--- /dev/null
+++ b/libblkid/ultrix.c
@@ -0,0 +1,96 @@
+/*
+ * uktrix partition parsing code
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+#define ULTRIX_MAXPARTITIONS	8
+
+#define ULTRIX_MAGIC		0x032957
+#define ULTRIX_MAGIC_STR	"\x02\x29\x57"
+
+/* sector with partition table */
+#define ULTRIX_SECTOR		((16384 - sizeof(struct ultrix_disklabel)) >> 9)
+/* position of partition table within ULTRIX_SECTOR */
+#define ULTRIX_OFFSET		(512 - sizeof(struct ultrix_disklabel))
+
+struct ultrix_disklabel {
+	int32_t	pt_magic;	/* magic no. indicating part. info exits */
+	int32_t	pt_valid;	/* set by driver if pt is current */
+	struct  pt_info {
+		int32_t		pi_nblocks; /* no. of sectors */
+		uint32_t	pi_blkoff;  /* block offset for start */
+	} pt_part[ULTRIX_MAXPARTITIONS];
+} __attribute__((packed));
+
+
+static int probe_ultrix_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	unsigned char *data;
+	struct ultrix_disklabel *l;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	int i;
+
+	data = blkid_probe_get_sector(pr, ULTRIX_SECTOR);
+	if (!data)
+		goto nothing;
+
+	l = (struct ultrix_disklabel *) (data + ULTRIX_OFFSET);
+
+	if (l->pt_magic != ULTRIX_MAGIC || l->pt_valid != 1)
+		goto nothing;
+
+	if (blkid_probe_set_magic(pr, (ULTRIX_SECTOR << 9) + ULTRIX_OFFSET,
+			sizeof(ULTRIX_MAGIC_STR) - 1,
+			(unsigned char *) ULTRIX_MAGIC_STR))
+		goto err;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "ultrix", 0);
+	if (!tab)
+		goto err;
+
+	for (i = 0; i < ULTRIX_MAXPARTITIONS; i++) {
+		if (!l->pt_part[i].pi_nblocks)
+			 blkid_partlist_increment_partno(ls);
+		else {
+			if (!blkid_partlist_add_partition(ls, tab,
+						l->pt_part[i].pi_blkoff,
+						l->pt_part[i].pi_nblocks))
+				goto err;
+		}
+	}
+
+	return 0;
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+const struct blkid_idinfo ultrix_pt_idinfo =
+{
+	.name		= "ultrix",
+	.probefunc	= probe_ultrix_pt,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/unixware.c b/libblkid/unixware.c
new file mode 100644
index 0000000..e9bcba3
--- /dev/null
+++ b/libblkid/unixware.c
@@ -0,0 +1,194 @@
+/*
+ * unixware partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ *
+ * The intersting information about unixware PT:
+ *   - Linux kernel / partx
+ *   - vtoc(7) SCO UNIX command man page
+ *   - evms source code (http://evms.sourceforge.net/)
+ *   - vxtools source code (http://martin.hinner.info/fs/vxfs/)
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+/* disklabel location */
+#define UNIXWARE_SECTOR		29
+#define UNIXWARE_OFFSET		(UNIXWARE_SECTOR << 9)	/* offset in bytes */
+#define UNIXWARE_KBOFFSET	(UNIXWARE_OFFSET >> 10)	/* offset in 1024-blocks */
+
+/* disklabel->d_magic offset within the last 1024 block */
+#define UNIXWARE_MAGICOFFSET	(UNIXWARE_OFFSET - UNIXWARE_KBOFFSET + 4)
+
+#define UNIXWARE_VTOCMAGIC	0x600DDEEEUL
+#define UNIXWARE_MAXPARTITIONS	16
+
+/* unixware_partition->s_label flags */
+#define UNIXWARE_TAG_UNUSED       0x0000  /* unused partition */
+#define UNIXWARE_TAG_BOOT         0x0001  /* boot fs */
+#define UNIXWARE_TAG_ROOT         0x0002  /* root fs */
+#define UNIXWARE_TAG_SWAP         0x0003  /* swap fs */
+#define UNIXWARE_TAG_USER         0x0004  /* user fs */
+#define UNIXWARE_TAG_ENTIRE_DISK  0x0005  /* whole disk */
+#define UNIXWARE_TAG_ALT_S        0x0006  /* alternate sector space */
+#define UNIXWARE_TAG_OTHER        0x0007  /* non unix */
+#define UNIXWARE_TAG_ALT_T        0x0008  /* alternate track space */
+#define UNIXWARE_TAG_STAND        0x0009  /* stand partition */
+#define UNIXWARE_TAG_VAR          0x000a  /* var partition */
+#define UNIXWARE_TAG_HOME         0x000b  /* home partition */
+#define UNIXWARE_TAG_DUMP         0x000c  /* dump partition */
+#define UNIXWARE_TAG_ALT_ST       0x000d  /* alternate sector track */
+#define UNIXWARE_TAG_VM_PUBLIC    0x000e  /* volume mgt public partition */
+#define UNIXWARE_TAG_VM_PRIVATE   0x000f  /* volume mgt private partition */
+
+
+/* unixware_partition->s_flags flags */
+#define UNIXWARE_FLAG_VALID	0x0200
+
+struct unixware_partition {
+	uint16_t	s_label;	/* partition label (tag) */
+	uint16_t	s_flags;	/* permission flags */
+	uint32_t	start_sect;	/* starting sector */
+	uint32_t	nr_sects;	/* number of sectors */
+} __attribute__((packed));
+
+struct unixware_disklabel {
+	uint32_t	d_type;		/* drive type */
+	uint32_t	d_magic;	/* the magic number */
+	uint32_t	d_version;	/* version number */
+	char		d_serial[12];	/* serial number of the device */
+	uint32_t	d_ncylinders;	/* # of data cylinders per device */
+	uint32_t	d_ntracks;	/* # of tracks per cylinder */
+	uint32_t	d_nsectors;	/* # of data sectors per track */
+	uint32_t	d_secsize;	/* # of bytes per sector */
+	uint32_t	d_part_start;	/* # of first sector of this partition */
+	uint32_t	d_unknown1[12];	/* ? */
+	uint32_t	d_alt_tbl;	/* byte offset of alternate table */
+	uint32_t	d_alt_len;	/* byte length of alternate table */
+	uint32_t	d_phys_cyl;	/* # of physical cylinders per device */
+	uint32_t	d_phys_trk;	/* # of physical tracks per cylinder */
+	uint32_t	d_phys_sec;	/* # of physical sectors per track */
+	uint32_t	d_phys_bytes;	/* # of physical bytes per sector */
+	uint32_t	d_unknown2;	/* ? */
+	uint32_t	d_unknown3;	/* ? */
+	uint32_t	d_pad[8];	/* pad */
+
+	struct unixware_vtoc {
+		uint32_t	v_magic;	/* the magic number */
+		uint32_t	v_version;	/* version number */
+		char		v_name[8];	/* volume name */
+		uint16_t	v_nslices;	/* # of partitions */
+		uint16_t	v_unknown1;	/* ? */
+		uint32_t	v_reserved[10];	/* reserved */
+
+		struct unixware_partition
+			v_slice[UNIXWARE_MAXPARTITIONS]; /* partition */
+	} __attribute__((packed)) vtoc;
+};
+
+static int probe_unixware_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct unixware_disklabel *l;
+	struct unixware_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partition parent;
+	blkid_partlist ls;
+	int i;
+
+	l = (struct unixware_disklabel *)
+			blkid_probe_get_sector(pr, UNIXWARE_SECTOR);
+	if (!l)
+		goto nothing;
+
+	if (le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_VTOCMAGIC)
+		goto nothing;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	parent = blkid_partlist_get_parent(ls);
+
+	tab = blkid_partlist_new_parttable(ls, "unixware", UNIXWARE_OFFSET);
+	if (!tab)
+		goto err;
+
+	/* Skip the first partition that describe whole disk
+	 */
+	for (i = 1, p = &l->vtoc.v_slice[1];
+			i < UNIXWARE_MAXPARTITIONS; i++, p++) {
+
+		uint32_t start, size;
+		uint16_t tag, flg;
+		blkid_partition par;
+
+		tag = le16_to_cpu(p->s_label);
+		flg = le16_to_cpu(p->s_flags);
+
+		if (tag == UNIXWARE_TAG_UNUSED ||
+		    tag == UNIXWARE_TAG_ENTIRE_DISK ||
+		    flg != UNIXWARE_FLAG_VALID)
+			continue;
+
+		start = le32_to_cpu(p->start_sect);
+		size = le32_to_cpu(p->nr_sects);
+
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: unixware partition (%d) overflow "
+				"detected, ignore\n", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, tag);
+		blkid_partition_set_flags(par, flg);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+/*
+ * The unixware partition table is within primary DOS partition.  The PT is
+ * located on 29 sector, PT magic string is d_magic member of 'struct
+ * unixware_disklabel'.
+ */
+const struct blkid_idinfo unixware_pt_idinfo =
+{
+	.name		= "unixware",
+	.probefunc	= probe_unixware_pt,
+	.minsz		= 1024 * 1440 + 1,		/* ignore floppies */
+	.magics		=
+	{
+		{
+		  .magic = "\x0D\x60\xE5\xCA",	/* little-endian magic string */
+		  .len = 4,			/* d_magic size in bytes */
+		  .kboff = UNIXWARE_KBOFFSET,
+		  .sboff = UNIXWARE_MAGICOFFSET
+		},
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/verify.c b/libblkid/verify.c
new file mode 100644
index 0000000..bd756e7
--- /dev/null
+++ b/libblkid/verify.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+
+static void blkid_probe_to_tags(blkid_probe pr, blkid_dev dev)
+{
+	const char *data;
+	const char *name;
+	int nvals, n;
+	size_t len;
+
+	nvals = blkid_probe_numof_values(pr);
+
+	for (n = 0; n < nvals; n++) {
+		if (blkid_probe_get_value(pr, n, &name, &data, &len) != 0)
+			continue;
+		if (strncmp(name, "PART_ENTRY_", 11) == 0) {
+			if (strcmp(name, "PART_ENTRY_UUID") == 0)
+				blkid_set_tag(dev, "PARTUUID", data, len);
+			else if (strcmp(name, "PART_ENTRY_NAME") == 0)
+				blkid_set_tag(dev, "PARTLABEL", data, len);
+
+		} else if (!strstr(name, "_ID")) {
+			/* superblock UUID, LABEL, ...
+			 * but not {SYSTEM,APPLICATION,..._ID} */
+			blkid_set_tag(dev, name, data, len);
+		}
+	}
+}
+
+/*
+ * Verify that the data in dev is consistent with what is on the actual
+ * block device (using the devname field only).  Normally this will be
+ * called when finding items in the cache, but for long running processes
+ * is also desirable to revalidate an item before use.
+ *
+ * If we are unable to revalidate the data, we return the old data and
+ * do not set the BLKID_BID_FL_VERIFIED flag on it.
+ */
+blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev)
+{
+	blkid_tag_iterate iter;
+	const char *type, *value;
+	struct stat st;
+	time_t diff, now;
+	int fd;
+
+	if (!dev || !cache)
+		return NULL;
+
+	now = time(0);
+	diff = now - dev->bid_time;
+
+	if (stat(dev->bid_name, &st) < 0) {
+		DBG(DEBUG_PROBE,
+		    printf("blkid_verify: error %m (%d) while "
+			   "trying to stat %s\n", errno,
+			   dev->bid_name));
+	open_err:
+		if ((errno == EPERM) || (errno == EACCES) || (errno == ENOENT)) {
+			/* We don't have read permission, just return cache data. */
+			DBG(DEBUG_PROBE, printf("returning unverified data for %s\n",
+						dev->bid_name));
+			return dev;
+		}
+		blkid_free_dev(dev);
+		return NULL;
+	}
+
+	if (now >= dev->bid_time &&
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	    (st.st_mtime < dev->bid_time ||
+	        (st.st_mtime == dev->bid_time &&
+		 st.st_mtim.tv_nsec / 1000 <= dev->bid_utime)) &&
+#else
+	    st.st_mtime <= dev->bid_time &&
+#endif
+	    (diff < BLKID_PROBE_MIN ||
+		(dev->bid_flags & BLKID_BID_FL_VERIFIED &&
+		 diff < BLKID_PROBE_INTERVAL)))
+		return dev;
+
+#ifndef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	DBG(DEBUG_PROBE,
+	    printf("need to revalidate %s (cache time %lu, stat time %lu,\n\t"
+		   "time since last check %lu)\n",
+		   dev->bid_name, (unsigned long)dev->bid_time,
+		   (unsigned long)st.st_mtime, (unsigned long)diff));
+#else
+	DBG(DEBUG_PROBE,
+	    printf("need to revalidate %s (cache time %lu.%lu, stat time %lu.%lu,\n\t"
+		   "time since last check %lu)\n",
+		   dev->bid_name,
+		   (unsigned long)dev->bid_time, (unsigned long)dev->bid_utime,
+		   (unsigned long)st.st_mtime, (unsigned long)st.st_mtim.tv_nsec / 1000,
+		   (unsigned long)diff));
+#endif
+
+	if (!cache->probe) {
+		cache->probe = blkid_new_probe();
+		if (!cache->probe) {
+			blkid_free_dev(dev);
+			return NULL;
+		}
+	}
+
+	fd = open(dev->bid_name, O_RDONLY|O_CLOEXEC);
+	if (fd < 0) {
+		DBG(DEBUG_PROBE, printf("blkid_verify: error %m (%d) while "
+					"opening %s\n", errno,
+					dev->bid_name));
+		goto open_err;
+	}
+
+	if (blkid_probe_set_device(cache->probe, fd, 0, 0)) {
+		/* failed to read the device */
+		close(fd);
+		blkid_free_dev(dev);
+		return NULL;
+	}
+
+	/* remove old cache info */
+	iter = blkid_tag_iterate_begin(dev);
+	while (blkid_tag_next(iter, &type, &value) == 0)
+		blkid_set_tag(dev, type, NULL, 0);
+	blkid_tag_iterate_end(iter);
+
+	/* enable superblocks probing */
+	blkid_probe_enable_superblocks(cache->probe, TRUE);
+	blkid_probe_set_superblocks_flags(cache->probe,
+		BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
+		BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE);
+
+	/* enable partitions probing */
+	blkid_probe_enable_partitions(cache->probe, TRUE);
+	blkid_probe_set_partitions_flags(cache->probe, BLKID_PARTS_ENTRY_DETAILS);
+
+	/* probe */
+	if (blkid_do_safeprobe(cache->probe)) {
+		/* found nothing or error */
+		blkid_free_dev(dev);
+		dev = NULL;
+	}
+
+	if (dev) {
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+		struct timeval tv;
+		if (!gettimeofday(&tv, NULL)) {
+			dev->bid_time = tv.tv_sec;
+			dev->bid_utime = tv.tv_usec;
+		} else
+#endif
+			dev->bid_time = time(0);
+
+		dev->bid_devno = st.st_rdev;
+		dev->bid_flags |= BLKID_BID_FL_VERIFIED;
+		cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+
+		blkid_probe_to_tags(cache->probe, dev);
+
+		DBG(DEBUG_PROBE, printf("%s: devno 0x%04llx, type %s\n",
+			   dev->bid_name, (long long)st.st_rdev, dev->bid_type));
+	}
+
+	blkid_reset_probe(cache->probe);
+	blkid_probe_reset_superblocks_filter(cache->probe);
+	close(fd);
+	return dev;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_dev dev;
+	blkid_cache cache;
+	int ret;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s device\n"
+			"Probe a single device to determine type\n", argv[0]);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL);
+	if (!dev) {
+		printf("%s: %s has an unsupported type\n", argv[0], argv[1]);
+		return (1);
+	}
+	printf("TYPE='%s'\n", dev->bid_type ? dev->bid_type : "(null)");
+	if (dev->bid_label)
+		printf("LABEL='%s'\n", dev->bid_label);
+	if (dev->bid_uuid)
+		printf("UUID='%s'\n", dev->bid_uuid);
+
+	blkid_free_dev(dev);
+	return (0);
+}
+#endif
diff --git a/libblkid/version.c b/libblkid/version.c
new file mode 100644
index 0000000..9659b4c
--- /dev/null
+++ b/libblkid/version.c
@@ -0,0 +1,63 @@
+/*
+ * version.c --- Return the version of the blkid library
+ *
+ * Copyright (C) 2004 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Lesser General
+ * Public License.
+ * %End-Header%
+ */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "blkid.h"
+#include "config.h"
+
+/* LIBBLKID_* defined in the global config.h */
+static const char *lib_version = LIBBLKID_VERSION;	/* release version */
+static const char *lib_date = LIBBLKID_DATE;
+
+/**
+ * blkid_parse_version_string:
+ * @ver_string:  version string (e.g. "2.16.0")
+ *
+ * Returns: release version code.
+ */
+int blkid_parse_version_string(const char *ver_string)
+{
+	const char *cp;
+	int version = 0;
+
+	for (cp = ver_string; *cp; cp++) {
+		if (*cp == '.')
+			continue;
+		if (!isdigit(*cp))
+			break;
+		version = (version * 10) + (*cp - '0');
+	}
+	return version;
+}
+
+/**
+ * blkid_get_library_version:
+ * @ver_string: returns relese version (!= SONAME version)
+ * @date_string: returns date
+ *
+ * Returns: release version code.
+ */
+int blkid_get_library_version(const char **ver_string,
+			       const char **date_string)
+{
+	if (ver_string)
+		*ver_string = lib_version;
+	if (date_string)
+		*date_string = lib_date;
+
+	return blkid_parse_version_string(lib_version);
+}
diff --git a/libblkid/vfat.c b/libblkid/vfat.c
new file mode 100644
index 0000000..2feb818
--- /dev/null
+++ b/libblkid/vfat.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/* Yucky misaligned values */
+struct vfat_super_block {
+/* 00*/	unsigned char	vs_ignored[3];
+/* 03*/	unsigned char	vs_sysid[8];
+/* 0b*/	unsigned char	vs_sector_size[2];
+/* 0d*/	uint8_t		vs_cluster_size;
+/* 0e*/	uint16_t	vs_reserved;
+/* 10*/	uint8_t		vs_fats;
+/* 11*/	unsigned char	vs_dir_entries[2];
+/* 13*/	unsigned char	vs_sectors[2];
+/* 15*/	unsigned char	vs_media;
+/* 16*/	uint16_t	vs_fat_length;
+/* 18*/	uint16_t	vs_secs_track;
+/* 1a*/	uint16_t	vs_heads;
+/* 1c*/	uint32_t	vs_hidden;
+/* 20*/	uint32_t	vs_total_sect;
+/* 24*/	uint32_t	vs_fat32_length;
+/* 28*/	uint16_t	vs_flags;
+/* 2a*/	uint8_t		vs_version[2];
+/* 2c*/	uint32_t	vs_root_cluster;
+/* 30*/	uint16_t	vs_fsinfo_sector;
+/* 32*/	uint16_t	vs_backup_boot;
+/* 34*/	uint16_t	vs_reserved2[6];
+/* 40*/	unsigned char	vs_unknown[3];
+/* 43*/	unsigned char	vs_serno[4];
+/* 47*/	unsigned char	vs_label[11];
+/* 52*/	unsigned char   vs_magic[8];
+/* 5a*/	unsigned char	vs_dummy2[0x1fe - 0x5a];
+/*1fe*/	unsigned char	vs_pmagic[2];
+} __attribute__((packed));
+
+/* Yucky misaligned values */
+struct msdos_super_block {
+/* 00*/	unsigned char	ms_ignored[3];
+/* 03*/	unsigned char	ms_sysid[8];
+/* 0b*/	unsigned char	ms_sector_size[2];
+/* 0d*/	uint8_t		ms_cluster_size;
+/* 0e*/	uint16_t	ms_reserved;
+/* 10*/	uint8_t		ms_fats;
+/* 11*/	unsigned char	ms_dir_entries[2];
+/* 13*/	unsigned char	ms_sectors[2]; /* =0 iff V3 or later */
+/* 15*/	unsigned char	ms_media;
+/* 16*/	uint16_t	ms_fat_length; /* Sectors per FAT */
+/* 18*/	uint16_t	ms_secs_track;
+/* 1a*/	uint16_t	ms_heads;
+/* 1c*/	uint32_t	ms_hidden;
+/* V3 BPB */
+/* 20*/	uint32_t	ms_total_sect; /* iff ms_sectors == 0 */
+/* V4 BPB */
+/* 24*/	unsigned char	ms_unknown[3]; /* Phys drive no., resvd, V4 sig (0x29) */
+/* 27*/	unsigned char	ms_serno[4];
+/* 2b*/	unsigned char	ms_label[11];
+/* 36*/	unsigned char   ms_magic[8];
+/* 3e*/	unsigned char	ms_dummy2[0x1fe - 0x3e];
+/*1fe*/	unsigned char	ms_pmagic[2];
+} __attribute__((packed));
+
+struct vfat_dir_entry {
+	uint8_t		name[11];
+	uint8_t		attr;
+	uint16_t	time_creat;
+	uint16_t	date_creat;
+	uint16_t	time_acc;
+	uint16_t	date_acc;
+	uint16_t	cluster_high;
+	uint16_t	time_write;
+	uint16_t	date_write;
+	uint16_t	cluster_low;
+	uint32_t	size;
+} __attribute__((packed));
+
+struct fat32_fsinfo {
+	uint8_t signature1[4];
+	uint32_t reserved1[120];
+	uint8_t signature2[4];
+	uint32_t free_clusters;
+	uint32_t next_cluster;
+	uint32_t reserved2[4];
+} __attribute__((packed));
+
+/* maximum number of clusters */
+#define FAT12_MAX 0xFF4
+#define FAT16_MAX 0xFFF4
+#define FAT32_MAX 0x0FFFFFF6
+
+#define FAT_ATTR_VOLUME_ID		0x08
+#define FAT_ATTR_DIR			0x10
+#define FAT_ATTR_LONG_NAME		0x0f
+#define FAT_ATTR_MASK			0x3f
+#define FAT_ENTRY_FREE			0xe5
+
+static const char *no_name = "NO NAME    ";
+
+#define unaligned_le16(x) \
+		(((unsigned char *) x)[0] + (((unsigned char *) x)[1] << 8))
+
+/*
+ * Look for LABEL (name) in the FAT root directory.
+ */
+static unsigned char *search_fat_label(blkid_probe pr,
+				uint64_t offset, uint32_t entries)
+{
+	struct vfat_dir_entry *ent, *dir = NULL;
+	uint32_t i;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("\tlook for label in root-dir "
+			"(entries: %d, offset: %jd)\n", entries, offset));
+
+	if (!blkid_probe_is_tiny(pr)) {
+		/* large disk, read whole root directory */
+		dir = (struct vfat_dir_entry *)
+			blkid_probe_get_buffer(pr,
+					offset,
+					(blkid_loff_t) entries *
+						sizeof(struct vfat_dir_entry));
+		if (!dir)
+			return NULL;
+	}
+
+	for (i = 0; i < entries; i++) {
+		/*
+		 * The root directory could be relatively large (4-16kB).
+		 * Fortunately, the LABEL is usually the first entry in the
+		 * directory. On tiny disks we call read() per entry.
+		 */
+		if (!dir)
+			ent = (struct vfat_dir_entry *)
+				blkid_probe_get_buffer(pr,
+					(blkid_loff_t) offset + (i *
+						sizeof(struct vfat_dir_entry)),
+					sizeof(struct vfat_dir_entry));
+		else
+			ent = &dir[i];
+
+		if (!ent || ent->name[0] == 0x00)
+			break;
+
+		if ((ent->name[0] == FAT_ENTRY_FREE) ||
+		    (ent->cluster_high != 0 || ent->cluster_low != 0) ||
+		    ((ent->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME))
+			continue;
+
+		if ((ent->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) ==
+		    FAT_ATTR_VOLUME_ID) {
+			DBG(DEBUG_LOWPROBE,
+				printf("\tfound fs LABEL at entry %d\n", i));
+			return ent->name;
+		}
+	}
+	return NULL;
+}
+
+static int fat_valid_superblock(const struct blkid_idmag *mag,
+			struct msdos_super_block *ms,
+			struct vfat_super_block *vs,
+			uint32_t *cluster_count, uint32_t *fat_size)
+{
+	uint16_t sector_size, dir_entries, reserved;
+	uint32_t sect_count, __fat_size, dir_size, __cluster_count, fat_length;
+	uint32_t max_count;
+
+	/* extra check for FATs without magic strings */
+	if (mag->len <= 2) {
+		/* Old floppies have a valid MBR signature */
+		if (ms->ms_pmagic[0] != 0x55 || ms->ms_pmagic[1] != 0xAA)
+			return 0;
+
+		/*
+		 * OS/2 and apparently DFSee will place a FAT12/16-like
+		 * pseudo-superblock in the first 512 bytes of non-FAT
+		 * filesystems --- at least JFS and HPFS, and possibly others.
+		 * So we explicitly check for those filesystems at the
+		 * FAT12/16 filesystem magic field identifier, and if they are
+		 * present, we rule this out as a FAT filesystem, despite the
+		 * FAT-like pseudo-header.
+		 */
+		if ((memcmp(ms->ms_magic, "JFS     ", 8) == 0) ||
+		    (memcmp(ms->ms_magic, "HPFS    ", 8) == 0))
+			return 0;
+	}
+
+	/* fat counts(Linux kernel expects at least 1 FAT table) */
+	if (!ms->ms_fats)
+		return 0;
+	if (!ms->ms_reserved)
+		return 0;
+	if (!(0xf8 <= ms->ms_media || ms->ms_media == 0xf0))
+		return 0;
+	if (!is_power_of_2(ms->ms_cluster_size))
+		return 0;
+
+	sector_size = unaligned_le16(&ms->ms_sector_size);
+	if (!is_power_of_2(sector_size) ||
+	    sector_size < 512 || sector_size > 4096)
+		return 0;
+
+	dir_entries = unaligned_le16(&ms->ms_dir_entries);
+	reserved =  le16_to_cpu(ms->ms_reserved);
+	sect_count = unaligned_le16(&ms->ms_sectors);
+
+	if (sect_count == 0)
+		sect_count = le32_to_cpu(ms->ms_total_sect);
+
+	fat_length = le16_to_cpu(ms->ms_fat_length);
+	if (fat_length == 0)
+		fat_length = le32_to_cpu(vs->vs_fat32_length);
+
+	__fat_size = fat_length * ms->ms_fats;
+	dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) +
+					(sector_size-1)) / sector_size;
+
+	__cluster_count = (sect_count - (reserved + __fat_size + dir_size)) /
+							ms->ms_cluster_size;
+	if (!ms->ms_fat_length && vs->vs_fat32_length)
+		max_count = FAT32_MAX;
+	else
+		max_count = __cluster_count > FAT12_MAX ? FAT16_MAX : FAT12_MAX;
+
+	if (__cluster_count > max_count)
+		return 0;
+
+	if (fat_size)
+		*fat_size = __fat_size;
+	if (cluster_count)
+		*cluster_count = __cluster_count;
+
+	return 1;	/* valid */
+}
+
+/*
+ * This function is used by MBR partition table parser to avoid
+ * misinterpretation of FAT filesystem.
+ */
+int blkid_probe_is_vfat(blkid_probe pr)
+{
+	struct vfat_super_block *vs;
+	struct msdos_super_block *ms;
+	const struct blkid_idmag *mag = NULL;
+
+	if (blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag) || !mag)
+		return 0;
+
+	ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
+	if (!ms)
+		return 0;
+	vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
+	if (!vs)
+		return 0;
+
+	return fat_valid_superblock(mag, ms, vs, NULL, NULL);
+}
+
+/* FAT label extraction from the root directory taken from Kay
+ * Sievers's volume_id library */
+static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct vfat_super_block *vs;
+	struct msdos_super_block *ms;
+	const unsigned char *vol_label = 0;
+	unsigned char *vol_serno = NULL, vol_label_buf[11];
+	uint16_t sector_size = 0, reserved;
+	uint32_t cluster_count, fat_size;
+	const char *version = NULL;
+
+	ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
+	if (!ms)
+		return 0;
+	vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
+	if (!vs)
+		return 0;
+	if (!fat_valid_superblock(mag, ms, vs, &cluster_count, &fat_size))
+		return 1;
+
+	sector_size = unaligned_le16(&ms->ms_sector_size);
+	reserved =  le16_to_cpu(ms->ms_reserved);
+
+	if (ms->ms_fat_length) {
+		/* the label may be an attribute in the root directory */
+		uint32_t root_start = (reserved + fat_size) * sector_size;
+		uint32_t root_dir_entries = unaligned_le16(&vs->vs_dir_entries);
+
+		vol_label = search_fat_label(pr, root_start, root_dir_entries);
+		if (vol_label) {
+			memcpy(vol_label_buf, vol_label, 11);
+			vol_label = vol_label_buf;
+		}
+
+		if (!vol_label || !memcmp(vol_label, no_name, 11))
+			vol_label = ms->ms_label;
+		vol_serno = ms->ms_serno;
+
+		blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos",
+                              sizeof("msdos"));
+
+		if (cluster_count < FAT12_MAX)
+			version = "FAT12";
+		else if (cluster_count < FAT16_MAX)
+			version = "FAT16";
+
+	} else if (vs->vs_fat32_length) {
+		unsigned char *buf;
+		uint16_t fsinfo_sect;
+		int maxloop = 100;
+
+		/* Search the FAT32 root dir for the label attribute */
+		uint32_t buf_size = vs->vs_cluster_size * sector_size;
+		uint32_t start_data_sect = reserved + fat_size;
+		uint32_t entries = le32_to_cpu(vs->vs_fat32_length) *
+					sector_size / sizeof(uint32_t);
+		uint32_t next = le32_to_cpu(vs->vs_root_cluster);
+
+		while (next && next < entries && --maxloop) {
+			uint32_t next_sect_off;
+			uint64_t next_off, fat_entry_off;
+			int count;
+
+			next_sect_off = (next - 2) * vs->vs_cluster_size;
+			next_off = (uint64_t)(start_data_sect + next_sect_off) *
+				sector_size;
+
+			count = buf_size / sizeof(struct vfat_dir_entry);
+
+			vol_label = search_fat_label(pr, next_off, count);
+			if (vol_label) {
+				memcpy(vol_label_buf, vol_label, 11);
+				vol_label = vol_label_buf;
+				break;
+			}
+
+			/* get FAT entry */
+			fat_entry_off = ((uint64_t) reserved * sector_size) +
+				(next * sizeof(uint32_t));
+			buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size);
+			if (buf == NULL)
+				break;
+
+			/* set next cluster */
+			next = le32_to_cpu(*((uint32_t *) buf)) & 0x0fffffff;
+		}
+
+		version = "FAT32";
+
+		if (!vol_label || !memcmp(vol_label, no_name, 11))
+			vol_label = vs->vs_label;
+		vol_serno = vs->vs_serno;
+
+		/*
+		 * FAT32 should have a valid signature in the fsinfo block,
+		 * but also allow all bytes set to '\0', because some volumes
+		 * do not set the signature at all.
+		 */
+		fsinfo_sect = le16_to_cpu(vs->vs_fsinfo_sector);
+		if (fsinfo_sect) {
+			struct fat32_fsinfo *fsinfo;
+
+			buf = blkid_probe_get_buffer(pr,
+					(blkid_loff_t) fsinfo_sect * sector_size,
+					sizeof(struct fat32_fsinfo));
+			if (buf == NULL)
+				return -1;
+
+			fsinfo = (struct fat32_fsinfo *) buf;
+			if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 &&
+			    memcmp(fsinfo->signature1, "\x52\x52\x64\x41", 4) != 0 &&
+			    memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0)
+				return -1;
+			if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 &&
+			    memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0)
+				return -1;
+		}
+	}
+
+	if (vol_label && memcmp(vol_label, no_name, 11))
+		blkid_probe_set_label(pr, (unsigned char *) vol_label, 11);
+
+	/* We can't just print them as %04X, because they are unaligned */
+	if (vol_serno)
+		blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02X%02X-%02X%02X",
+			vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]);
+	if (version)
+		blkid_probe_set_version(pr, version);
+
+	return 0;
+}
+
+
+const struct blkid_idinfo vfat_idinfo =
+{
+	.name		= "vfat",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_vfat,
+	.magics		=
+	{
+		{ .magic = "MSWIN",    .len = 5, .sboff = 0x52 },
+		{ .magic = "FAT32   ", .len = 8, .sboff = 0x52 },
+		{ .magic = "MSDOS",    .len = 5, .sboff = 0x36 },
+		{ .magic = "FAT16   ", .len = 8, .sboff = 0x36 },
+		{ .magic = "FAT12   ", .len = 8, .sboff = 0x36 },
+		{ .magic = "FAT     ", .len = 8, .sboff = 0x36 },
+		{ .magic = "\353",     .len = 1, },
+		{ .magic = "\351",     .len = 1, },
+		{ .magic = "\125\252", .len = 2, .sboff = 0x1fe },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/via_raid.c b/libblkid/via_raid.c
new file mode 100644
index 0000000..eba7e4b
--- /dev/null
+++ b/libblkid/via_raid.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct via_metadata {
+	uint16_t	signature;
+	uint8_t		version_number;
+	struct via_array {
+		uint16_t	disk_bit_mask;
+		uint8_t		disk_array_ex;
+		uint32_t	capacity_low;
+		uint32_t	capacity_high;
+		uint32_t	serial_checksum;
+	} __attribute__((packed)) array;
+	uint32_t	serial_checksum[8];
+	uint8_t		checksum;
+} __attribute__((packed));
+
+#define VIA_SIGNATURE		0xAA55
+
+/* 8 bit checksum on first 50 bytes of metadata. */
+static uint8_t via_checksum(struct via_metadata *v)
+{
+	uint8_t i = 50, cs = 0;
+
+	while (i--)
+		cs += ((uint8_t*) v)[i];
+
+	return cs == v->checksum;
+}
+
+static int probe_viaraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct via_metadata *v;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200)-1) * 0x200;
+
+	v = (struct via_metadata *)
+			blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct via_metadata));
+	if (!v)
+		return -1;
+	if (le16_to_cpu(v->signature) != VIA_SIGNATURE)
+		return -1;
+	if (v->version_number > 2)
+		return -1;
+	if (!via_checksum(v))
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%u", v->version_number) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off,
+				sizeof(v->signature),
+				(unsigned char *) &v->signature))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo viaraid_idinfo = {
+	.name		= "via_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_viaraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/vmfs.c b/libblkid/vmfs.c
new file mode 100644
index 0000000..ead09a8
--- /dev/null
+++ b/libblkid/vmfs.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 Mike Hommey <mh@glandium.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include "superblocks.h"
+
+struct vmfs_fs_info {
+	uint32_t magic;
+	uint32_t volume_version;
+	uint8_t version;
+	uint8_t uuid[16];
+	uint32_t mode;
+	char label[128];
+} __attribute__ ((__packed__));
+
+struct vmfs_volume_info {
+	uint32_t magic;
+	uint32_t ver;
+	uint8_t irrelevant[122];
+	uint8_t uuid[16];
+} __attribute__ ((__packed__));
+
+static int probe_vmfs_fs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct vmfs_fs_info *header;
+
+	header = blkid_probe_get_sb(pr, mag, struct vmfs_fs_info);
+	if (header == NULL)
+		return -1;
+
+	blkid_probe_sprintf_uuid(pr, (unsigned char *) header->uuid, 16,
+		"%02x%02x%02x%02x-%02x%02x%02x%02x-"
+		"%02x%02x-%02x%02x%02x%02x%02x%02x",
+		header->uuid[3], header->uuid[2], header->uuid[1],
+		header->uuid[0], header->uuid[7], header->uuid[6],
+		header->uuid[5], header->uuid[4], header->uuid[9],
+		header->uuid[8], header->uuid[10], header->uuid[11],
+		header->uuid[12], header->uuid[13], header->uuid[14],
+		header->uuid[15]);
+
+	blkid_probe_set_label(pr, (unsigned char *) header->label,
+					sizeof(header->label));
+	blkid_probe_sprintf_version(pr, "%u", header->version);
+	return 0;
+}
+
+static int probe_vmfs_volume(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct vmfs_volume_info *header;
+	unsigned char *lvm_uuid;
+
+	header = blkid_probe_get_sb(pr, mag, struct vmfs_volume_info);
+	if (header == NULL)
+		return -1;
+
+	blkid_probe_sprintf_value(pr, "UUID_SUB",
+		"%02x%02x%02x%02x-%02x%02x%02x%02x-"
+		"%02x%02x-%02x%02x%02x%02x%02x%02x",
+		header->uuid[3], header->uuid[2], header->uuid[1],
+		header->uuid[0], header->uuid[7], header->uuid[6],
+		header->uuid[5], header->uuid[4], header->uuid[9],
+		header->uuid[8], header->uuid[10], header->uuid[11],
+		header->uuid[12], header->uuid[13], header->uuid[14],
+		header->uuid[15]);
+	blkid_probe_sprintf_version(pr, "%u", le32_to_cpu(header->ver));
+
+	lvm_uuid = blkid_probe_get_buffer(pr,
+				1024 * 1024 /* Start of the volume info */
+				+ 512 /* Offset to lvm info */
+				+ 20 /* Offset in lvm info */, 35);
+	if (lvm_uuid)
+		blkid_probe_strncpy_uuid(pr, lvm_uuid, 35);
+
+	return 0;
+}
+
+const struct blkid_idinfo vmfs_fs_idinfo =
+{
+	.name		= "VMFS",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_vmfs_fs,
+	.magics		=
+	{
+		{ .magic = "\x5e\xf1\xab\x2f", .len = 4, .kboff = 2048 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo vmfs_volume_idinfo =
+{
+	.name		= "VMFS_volume_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_vmfs_volume,
+	.magics		=
+	{
+		{ .magic = "\x0d\xd0\x01\xc0", .len = 4, .kboff = 1024 },
+		{ NULL }
+	}
+};
diff --git a/libblkid/vxfs.c b/libblkid/vxfs.c
new file mode 100644
index 0000000..fdab85a
--- /dev/null
+++ b/libblkid/vxfs.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+
+#include "superblocks.h"
+
+struct vxfs_super_block {
+	uint32_t		vs_magic;
+	int32_t			vs_version;
+};
+
+static int probe_vxfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct vxfs_super_block *vxs;
+
+	vxs = blkid_probe_get_sb(pr, mag, struct vxfs_super_block);
+	if (!vxs)
+		return -1;
+
+	blkid_probe_sprintf_version(pr, "%u", (unsigned int) vxs->vs_version);
+	return 0;
+}
+
+
+const struct blkid_idinfo vxfs_idinfo =
+{
+	.name		= "vxfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_vxfs,
+	.magics		=
+	{
+		{ .magic = "\365\374\001\245", .len = 4, .kboff = 1 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/wholedisk.c b/libblkid/wholedisk.c
new file mode 100644
index 0000000..1dbb90c
--- /dev/null
+++ b/libblkid/wholedisk.c
@@ -0,0 +1,62 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "blkdev.h"
+#include "wholedisk.h"
+
+int is_whole_disk_fd(int fd, const char *name)
+{
+#ifdef HDIO_GETGEO
+	if (fd != -1) {
+		struct hd_geometry geometry;
+		int i = ioctl(fd, HDIO_GETGEO, &geometry);
+		if (i == 0)
+			return geometry.start == 0;
+	}
+#endif
+	/*
+	 * The "silly heuristic" is still sexy for us, because
+	 * for example Xen doesn't implement HDIO_GETGEO for virtual
+	 * block devices (/dev/xvda).
+	 *
+	 * -- kzak@redhat.com (23-Feb-2006)
+	 */
+	while (*name)
+		name++;
+	return !isdigit(name[-1]);
+}
+
+int is_whole_disk(const char *name)
+{
+	int fd = -1, res = 0;
+#ifdef HDIO_GETGEO
+	fd = open(name, O_RDONLY);
+	if (fd != -1)
+#endif
+		res = is_whole_disk_fd(fd, name);
+
+	if (fd != -1)
+		close(fd);
+	return res;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	printf("%s: is%s whole disk\n", argv[1],
+			is_whole_disk(argv[1]) ? "" : " NOT");
+	exit(EXIT_SUCCESS);
+}
+#endif
diff --git a/libblkid/wholedisk.h b/libblkid/wholedisk.h
new file mode 100644
index 0000000..251479e
--- /dev/null
+++ b/libblkid/wholedisk.h
@@ -0,0 +1,8 @@
+#ifndef WHOLEDISK_H
+#define WHOLEDISK_H
+
+extern int is_whole_disk(const char *name);
+extern int is_whole_disk_fd(int fd, const char *name);
+
+#endif /* WHOLEDISK_H */
+
diff --git a/libblkid/widechar.h b/libblkid/widechar.h
new file mode 100644
index 0000000..b023b5f
--- /dev/null
+++ b/libblkid/widechar.h
@@ -0,0 +1,38 @@
+/* Declarations for wide characters */
+/* This file must be included last because the redefinition of wchar_t may
+   cause conflicts when system include files were included after it. */
+
+#ifdef HAVE_WIDECHAR
+
+# include <wchar.h>
+# include <wctype.h>
+
+#else /* !HAVE_WIDECHAR */
+
+# include <ctype.h>
+  /* Fallback for types */
+# define wchar_t char
+# define wint_t int
+# define WEOF EOF
+  /* Fallback for input operations */
+# define fgetwc fgetc
+# define getwc getc
+# define getwchar getchar
+# define fgetws fgets
+  /* Fallback for output operations */
+# define fputwc fputc
+# define putwc putc
+# define putwchar putchar
+# define fputws fputs
+  /* Fallback for character classification */
+# define iswgraph isgraph
+# define iswprint isprint
+# define iswspace isspace
+  /* Fallback for string functions */
+# define wcschr strchr
+# define wcsdup strdup
+# define wcslen strlen
+
+# define wcwidth(c) 1
+
+#endif /* HAVE_WIDECHAR */
diff --git a/libblkid/xalloc.h b/libblkid/xalloc.h
new file mode 100644
index 0000000..7b685e7
--- /dev/null
+++ b/libblkid/xalloc.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * General memory allocation wrappers for malloc, realloc, calloc and strdup
+ */
+
+#ifndef UTIL_LINUX_XALLOC_H
+#define UTIL_LINUX_XALLOC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "c.h"
+
+#ifndef XALLOC_EXIT_CODE
+# define XALLOC_EXIT_CODE EXIT_FAILURE
+#endif
+
+static inline __ul_alloc_size(1)
+void *xmalloc(const size_t size)
+{
+        void *ret = malloc(size);
+
+        if (!ret && size)
+                err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+        return ret;
+}
+
+static inline __ul_alloc_size(2)
+void *xrealloc(void *ptr, const size_t size)
+{
+        void *ret = realloc(ptr, size);
+
+        if (!ret && size)
+                err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+        return ret;
+}
+
+static inline __ul_calloc_size(1, 2)
+void *xcalloc(const size_t nelems, const size_t size)
+{
+        void *ret = calloc(nelems, size);
+
+        if (!ret && size && nelems)
+                err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+        return ret;
+}
+
+static inline char *xstrdup(const char *str)
+{
+        char *ret;
+
+        if (!str)
+                return NULL;
+
+        ret = strdup(str);
+
+        if (!ret)
+                err(XALLOC_EXIT_CODE, "cannot duplicate string");
+        return ret;
+}
+
+static inline int __attribute__ ((__format__(printf, 2, 3)))
+    xasprintf(char **strp, const char *fmt, ...)
+{
+	int ret;
+	va_list args;
+	va_start(args, fmt);
+	ret = vasprintf(&(*strp), fmt, args);
+	va_end(args);
+	if (ret < 0)
+		err(XALLOC_EXIT_CODE, "cannot allocate string");
+	return ret;
+}
+
+
+static inline char *xgethostname(void)
+{
+	char *name;
+	size_t sz = get_hostname_max() + 1;
+
+	name = xmalloc(sizeof(char) * sz);
+	if (gethostname(name, sz) != 0)
+		return NULL;
+
+	name[sz - 1] = '\0';
+	return name;
+}
+
+#endif
diff --git a/libblkid/xfs.c b/libblkid/xfs.c
new file mode 100644
index 0000000..1399fe1
--- /dev/null
+++ b/libblkid/xfs.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct xfs_super_block {
+	unsigned char	xs_magic[4];
+	uint32_t	xs_blocksize;
+	uint64_t	xs_dblocks;
+	uint64_t	xs_rblocks;
+	uint32_t	xs_dummy1[2];
+	unsigned char	xs_uuid[16];
+	uint32_t	xs_dummy2[15];
+	char		xs_fname[12];
+	uint32_t	xs_dummy3[2];
+	uint64_t	xs_icount;
+	uint64_t	xs_ifree;
+	uint64_t	xs_fdblocks;
+} __attribute__((packed));
+
+static int probe_xfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct xfs_super_block *xs;
+
+	xs = blkid_probe_get_sb(pr, mag, struct xfs_super_block);
+	if (!xs)
+		return -1;
+
+	if (strlen(xs->xs_fname))
+		blkid_probe_set_label(pr, (unsigned char *) xs->xs_fname,
+				sizeof(xs->xs_fname));
+	blkid_probe_set_uuid(pr, xs->xs_uuid);
+	return 0;
+}
+
+const struct blkid_idinfo xfs_idinfo =
+{
+	.name		= "xfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_xfs,
+	.magics		=
+	{
+		{ .magic = "XFSB", .len = 4 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/xgetpass.c b/libblkid/xgetpass.c
new file mode 100644
index 0000000..ba20894
--- /dev/null
+++ b/libblkid/xgetpass.c
@@ -0,0 +1,46 @@
+/*
+ * A function to read the passphrase either from the terminal or from
+ * an open file descriptor.
+ *
+ * Public domain.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include "c.h"
+#include "xgetpass.h"
+
+char *xgetpass(int pfd, const char *prompt)
+{
+	char *pass = NULL;
+	int len = 0, i;
+
+        if (pfd < 0) /* terminal */
+		return getpass(prompt);
+
+	for (i=0; ; i++) {
+		if (i >= len-1) {
+			char *tmppass = pass;
+			len += 128;
+
+			pass = realloc(tmppass, len);
+			if (!pass) {
+				pass = tmppass; /* the old buffer hasn't changed */
+				break;
+			}
+		}
+		if (pass && (read(pfd, pass + i, 1) != 1 ||
+			     pass[i] == '\n' || pass[i] == 0))
+			break;
+	}
+
+	if (pass)
+		pass[i] = '\0';
+	return pass;
+}
+
diff --git a/libblkid/xgetpass.h b/libblkid/xgetpass.h
new file mode 100644
index 0000000..b5a3c87
--- /dev/null
+++ b/libblkid/xgetpass.h
@@ -0,0 +1,6 @@
+#ifndef UTIL_LINUX_XGETPASS_H
+#define UTIL_LINUX_XGETPASS_H
+
+extern char *xgetpass(int pfd, const char *prompt);
+
+#endif /* UTIL_LINUX_XGETPASS_H */
diff --git a/libblkid/zfs.c b/libblkid/zfs.c
new file mode 100644
index 0000000..b96c5df
--- /dev/null
+++ b/libblkid/zfs.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2009-2010 by Andreas Dilger <adilger@sun.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "superblocks.h"
+
+#define VDEV_LABEL_UBERBLOCK	(128 * 1024ULL)
+#define VDEV_LABEL_NVPAIR	( 16 * 1024ULL)
+#define VDEV_LABEL_SIZE		(256 * 1024ULL)
+
+/* #include <sys/uberblock_impl.h> */
+#define UBERBLOCK_MAGIC         0x00bab10c              /* oo-ba-bloc!  */
+struct zfs_uberblock {
+	uint64_t	ub_magic;	/* UBERBLOCK_MAGIC		*/
+	uint64_t	ub_version;	/* SPA_VERSION			*/
+	uint64_t	ub_txg;		/* txg of last sync		*/
+	uint64_t	ub_guid_sum;	/* sum of all vdev guids	*/
+	uint64_t	ub_timestamp;	/* UTC time of last sync	*/
+	char		ub_rootbp;	/* MOS objset_phys_t		*/
+} __attribute__((packed));
+
+#define ZFS_TRIES	64
+#define ZFS_WANT	 4
+
+#define DATA_TYPE_UINT64 8
+#define DATA_TYPE_STRING 9
+
+struct nvpair {
+	uint32_t	nvp_size;
+	uint32_t	nvp_unkown;
+	uint32_t	nvp_namelen;
+	char		nvp_name[0]; /* aligned to 4 bytes */
+	/* aligned ptr array for string arrays */
+	/* aligned array of data for value */
+};
+
+struct nvstring {
+	uint32_t	nvs_type;
+	uint32_t	nvs_elem;
+	uint32_t	nvs_strlen;
+	unsigned char	nvs_string[0];
+};
+
+struct nvuint64 {
+	uint32_t	nvu_type;
+	uint32_t	nvu_elem;
+	uint64_t	nvu_value;
+};
+
+struct nvlist {
+	uint32_t	nvl_unknown[3];
+	struct nvpair	nvl_nvpair;
+};
+
+#define nvdebug(fmt, ...)	do { } while(0)
+/*#define nvdebug(fmt, a...)	printf(fmt, ##a)*/
+
+static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
+{
+	struct nvlist *nvl;
+	struct nvpair *nvp;
+	size_t left = 4096;
+	int found = 0;
+
+	offset = (offset & ~(VDEV_LABEL_SIZE - 1)) + VDEV_LABEL_NVPAIR;
+
+	/* Note that we currently assume that the desired fields are within
+	 * the first 4k (left) of the nvlist.  This is true for all pools
+	 * I've seen, and simplifies this code somewhat, because we don't
+	 * have to handle an nvpair crossing a buffer boundary. */
+	nvl = (struct nvlist *)blkid_probe_get_buffer(pr, offset, left);
+	if (nvl == NULL)
+		return;
+
+	nvdebug("zfs_extract: nvlist offset %llu\n", offset);
+
+	nvp = &nvl->nvl_nvpair;
+	while (left > sizeof(*nvp) && nvp->nvp_size != 0 && found < 3) {
+		int avail;   /* tracks that name/value data fits in nvp_size */
+		int namesize;
+
+		nvp->nvp_size = be32_to_cpu(nvp->nvp_size);
+		nvp->nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
+		avail = nvp->nvp_size - nvp->nvp_namelen - sizeof(*nvp);
+
+		nvdebug("left %zd nvp_size %u\n", left, nvp->nvp_size);
+		if (left < nvp->nvp_size || avail < 0)
+			break;
+
+		namesize = (nvp->nvp_namelen + 3) & ~3;
+
+		nvdebug("nvlist: size %u, namelen %u, name %*s\n",
+			nvp->nvp_size, nvp->nvp_namelen, nvp->nvp_namelen,
+			nvp->nvp_name);
+		if (strncmp(nvp->nvp_name, "name", nvp->nvp_namelen) == 0) {
+			struct nvstring *nvs = (void *)(nvp->nvp_name+namesize);
+
+			nvs->nvs_type = be32_to_cpu(nvs->nvs_type);
+			nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
+			avail -= nvs->nvs_strlen + sizeof(*nvs);
+			nvdebug("nvstring: type %u string %*s\n", nvs->nvs_type,
+				nvs->nvs_strlen, nvs->nvs_string);
+			if (nvs->nvs_type == DATA_TYPE_STRING && avail >= 0)
+				blkid_probe_set_label(pr, nvs->nvs_string,
+						      nvs->nvs_strlen);
+			found++;
+		} else if (strncmp(nvp->nvp_name, "guid",
+				   nvp->nvp_namelen) == 0) {
+			struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
+			uint64_t nvu_value;
+
+			memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+			nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
+			nvu_value = be64_to_cpu(nvu_value);
+			avail -= sizeof(*nvu);
+			nvdebug("nvuint64: type %u value %"PRIu64"\n",
+				nvu->nvu_type, nvu_value);
+			if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
+				blkid_probe_sprintf_value(pr, "UUID_SUB",
+							  "%"PRIu64, nvu_value);
+			found++;
+		} else if (strncmp(nvp->nvp_name, "pool_guid",
+				   nvp->nvp_namelen) == 0) {
+			struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
+			uint64_t nvu_value;
+
+			memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+			nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
+			nvu_value = be64_to_cpu(nvu_value);
+			avail -= sizeof(*nvu);
+			nvdebug("nvuint64: type %u value %"PRIu64"\n",
+				nvu->nvu_type, nvu_value);
+			if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
+				blkid_probe_sprintf_uuid(pr, (unsigned char *)
+							 &nvu_value,
+							 sizeof(nvu_value),
+							 "%"PRIu64, nvu_value);
+			found++;
+		}
+		if (left > nvp->nvp_size)
+			left -= nvp->nvp_size;
+		else
+			left = 0;
+		nvp = (struct nvpair *)((char *)nvp + nvp->nvp_size);
+	}
+}
+
+#define zdebug(fmt, ...)	do {} while(0)
+/*#define zdebug(fmt, a...)	printf(fmt, ##a)*/
+
+/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start
+ * of the disk, and 2 areas at the end of the disk.  Check only some of them...
+ * #4 (@ 132kB) is the first one written on a new filesystem. */
+static int probe_zfs(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t swab_magic = swab64(UBERBLOCK_MAGIC);
+	struct zfs_uberblock *ub;
+	int swab_endian;
+	loff_t offset;
+	int tried;
+	int found;
+
+	zdebug("probe_zfs\n");
+	/* Look for at least 4 uberblocks to ensure a positive match */
+	for (tried = found = 0, offset = VDEV_LABEL_UBERBLOCK;
+	     tried < ZFS_TRIES && found < ZFS_WANT;
+	     tried++, offset += 4096) {
+		/* also try the second uberblock copy */
+		if (tried == (ZFS_TRIES / 2))
+			offset = VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK;
+
+		ub = (struct zfs_uberblock *)
+			blkid_probe_get_buffer(pr, offset,
+					       sizeof(struct zfs_uberblock));
+		if (ub == NULL)
+			return -1;
+
+		if (ub->ub_magic == UBERBLOCK_MAGIC)
+			found++;
+
+		if ((swab_endian = (ub->ub_magic == swab_magic)))
+			found++;
+
+		zdebug("probe_zfs: found %s-endian uberblock at %llu\n",
+		       swab_endian ? "big" : "little", offset >> 10);
+	}
+
+	if (found < 4)
+		return -1;
+
+	/* If we found the 4th uberblock, then we will have exited from the
+	 * scanning loop immediately, and ub will be a valid uberblock. */
+	blkid_probe_sprintf_version(pr, "%" PRIu64, swab_endian ?
+				    swab64(ub->ub_version) : ub->ub_version);
+
+	zfs_extract_guid_name(pr, offset);
+
+	if (blkid_probe_set_magic(pr, offset,
+				sizeof(ub->ub_magic),
+				(unsigned char *) &ub->ub_magic))
+		return -1;
+
+	return 0;
+}
+
+const struct blkid_idinfo zfs_idinfo =
+{
+	.name		= "zfs_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_zfs,
+	.minsz		= 64 * 1024 * 1024,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libcrecovery/Android.mk b/libcrecovery/Android.mk
new file mode 100644
index 0000000..d948dd1
--- /dev/null
+++ b/libcrecovery/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := system.c popen.c
+LOCAL_MODULE := libcrecovery
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_STATIC_LIBRARY)
+
+endif
diff --git a/libcrecovery/common.h b/libcrecovery/common.h
new file mode 100644
index 0000000..59af22e
--- /dev/null
+++ b/libcrecovery/common.h
@@ -0,0 +1,10 @@
+#ifndef LIBCRECOVERY_COMMON_H
+#define LIBCRECOVERY_COMMON_H
+
+#include <stdio.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/libmincrypt/Android.mk b/libmincrypt/Android.mk
new file mode 100644
index 0000000..1c21324
--- /dev/null
+++ b/libmincrypt/Android.mk
@@ -0,0 +1,16 @@
+# Copyright 2008 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libmincrypttwrp
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)/libmincrypt/includes
+LOCAL_SRC_FILES := rsa.c sha.c sha256.c
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libmincrypttwrp
+LOCAL_SRC_FILES := rsa.c sha.c sha256.c
+include $(BUILD_HOST_STATIC_LIBRARY)
+
diff --git a/libmincrypt/NOTICE b/libmincrypt/NOTICE
new file mode 100644
index 0000000..430d3d6
--- /dev/null
+++ b/libmincrypt/NOTICE
@@ -0,0 +1,23 @@
+ Copyright 2008, The Android Open Source Project
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+     * 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.
+     * Neither the name of Google Inc. 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 Google Inc. ``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 Google Inc. 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.
diff --git a/libmincrypt/includes/mincrypt/hash-internal.h b/libmincrypt/includes/mincrypt/hash-internal.h
new file mode 100644
index 0000000..96806f7
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/hash-internal.h
@@ -0,0 +1,40 @@
+// Copyright 2007 Google Inc. All Rights Reserved.
+// Author: mschilder@google.com (Marius Schilder)
+
+#ifndef SECURITY_UTIL_LITE_HASH_INTERNAL_H__
+#define SECURITY_UTIL_LITE_HASH_INTERNAL_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+struct HASH_CTX;  // forward decl
+
+typedef struct HASH_VTAB {
+  void (* const init)(struct HASH_CTX*);
+  void (* const update)(struct HASH_CTX*, const void*, int);
+  const uint8_t* (* const final)(struct HASH_CTX*);
+  const uint8_t* (* const hash)(const void*, int, uint8_t*);
+  int size;
+} HASH_VTAB;
+
+typedef struct HASH_CTX {
+  const HASH_VTAB * f;
+  uint64_t count;
+  uint8_t buf[64];
+  uint32_t state[8];  // upto SHA2
+} HASH_CTX;
+
+#define HASH_init(ctx) (ctx)->f->init(ctx)
+#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len)
+#define HASH_final(ctx) (ctx)->f->final(ctx)
+#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest)
+#define HASH_size(ctx) (ctx)->f->size
+
+#ifdef __cplusplus
+}
+#endif  // __cplusplus
+
+#endif  // SECURITY_UTIL_LITE_HASH_INTERNAL_H__
diff --git a/libmincrypt/includes/mincrypt/rsa.h b/libmincrypt/includes/mincrypt/rsa.h
new file mode 100644
index 0000000..cc0e800
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/rsa.h
@@ -0,0 +1,58 @@
+/* rsa.h
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * 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.
+**     * Neither the name of Google Inc. 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 Google Inc. ``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 Google Inc. 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.
+*/
+
+#ifndef _EMBEDDED_RSA_H_
+#define _EMBEDDED_RSA_H_
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RSANUMBYTES 256           /* 2048 bit key length */
+#define RSANUMWORDS (RSANUMBYTES / sizeof(uint32_t))
+
+typedef struct RSAPublicKey {
+    int len;                  /* Length of n[] in number of uint32_t */
+    uint32_t n0inv;           /* -1 / n[0] mod 2^32 */
+    uint32_t n[RSANUMWORDS];  /* modulus as little endian array */
+    uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */
+    int exponent;             /* 3 or 65537 */
+} RSAPublicKey;
+
+int RSA_verify(const RSAPublicKey *key,
+               const uint8_t* signature,
+               const int len,
+               const uint8_t* hash,
+               const int hash_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libmincrypt/includes/mincrypt/sha.h b/libmincrypt/includes/mincrypt/sha.h
new file mode 100644
index 0000000..120ddcb
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/sha.h
@@ -0,0 +1,30 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+// Author: mschilder@google.com (Marius Schilder)
+
+#ifndef SECURITY_UTIL_LITE_SHA1_H__
+#define SECURITY_UTIL_LITE_SHA1_H__
+
+#include <stdint.h>
+#include "hash-internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+typedef HASH_CTX SHA_CTX;
+
+void SHA_init(SHA_CTX* ctx);
+void SHA_update(SHA_CTX* ctx, const void* data, int len);
+const uint8_t* SHA_final(SHA_CTX* ctx);
+
+// Convenience method. Returns digest address.
+// NOTE: *digest needs to hold SHA_DIGEST_SIZE bytes.
+const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest);
+
+#define SHA_DIGEST_SIZE 20
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif  // SECURITY_UTIL_LITE_SHA1_H__
diff --git a/libmincrypt/includes/mincrypt/sha256.h b/libmincrypt/includes/mincrypt/sha256.h
new file mode 100644
index 0000000..0f3efb7
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/sha256.h
@@ -0,0 +1,29 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: mschilder@google.com (Marius Schilder)
+
+#ifndef SECURITY_UTIL_LITE_SHA256_H__
+#define SECURITY_UTIL_LITE_SHA256_H__
+
+#include <stdint.h>
+#include "hash-internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+typedef HASH_CTX SHA256_CTX;
+
+void SHA256_init(SHA256_CTX* ctx);
+void SHA256_update(SHA256_CTX* ctx, const void* data, int len);
+const uint8_t* SHA256_final(SHA256_CTX* ctx);
+
+// Convenience method. Returns digest address.
+const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest);
+
+#define SHA256_DIGEST_SIZE 32
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif  // SECURITY_UTIL_LITE_SHA256_H__
diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c
new file mode 100644
index 0000000..9061b3a
--- /dev/null
+++ b/libmincrypt/rsa.c
@@ -0,0 +1,308 @@
+/* rsa.c
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * 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.
+**     * Neither the name of Google Inc. 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 Google Inc. ``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 Google Inc. 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 "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+#include "mincrypt/sha256.h"
+
+// a[] -= mod
+static void subM(const RSAPublicKey* key,
+                 uint32_t* a) {
+    int64_t A = 0;
+    int i;
+    for (i = 0; i < key->len; ++i) {
+        A += (uint64_t)a[i] - key->n[i];
+        a[i] = (uint32_t)A;
+        A >>= 32;
+    }
+}
+
+// return a[] >= mod
+static int geM(const RSAPublicKey* key,
+               const uint32_t* a) {
+    int i;
+    for (i = key->len; i;) {
+        --i;
+        if (a[i] < key->n[i]) return 0;
+        if (a[i] > key->n[i]) return 1;
+    }
+    return 1;  // equal
+}
+
+// montgomery c[] += a * b[] / R % mod
+static void montMulAdd(const RSAPublicKey* key,
+                       uint32_t* c,
+                       const uint32_t a,
+                       const uint32_t* b) {
+    uint64_t A = (uint64_t)a * b[0] + c[0];
+    uint32_t d0 = (uint32_t)A * key->n0inv;
+    uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
+    int i;
+
+    for (i = 1; i < key->len; ++i) {
+        A = (A >> 32) + (uint64_t)a * b[i] + c[i];
+        B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
+        c[i - 1] = (uint32_t)B;
+    }
+
+    A = (A >> 32) + (B >> 32);
+
+    c[i - 1] = (uint32_t)A;
+
+    if (A >> 32) {
+        subM(key, c);
+    }
+}
+
+// montgomery c[] = a[] * b[] / R % mod
+static void montMul(const RSAPublicKey* key,
+                    uint32_t* c,
+                    const uint32_t* a,
+                    const uint32_t* b) {
+    int i;
+    for (i = 0; i < key->len; ++i) {
+        c[i] = 0;
+    }
+    for (i = 0; i < key->len; ++i) {
+        montMulAdd(key, c, a[i], b);
+    }
+}
+
+// In-place public exponentiation.
+// Input and output big-endian byte array in inout.
+static void modpow(const RSAPublicKey* key,
+                   uint8_t* inout) {
+    uint32_t a[RSANUMWORDS];
+    uint32_t aR[RSANUMWORDS];
+    uint32_t aaR[RSANUMWORDS];
+    uint32_t* aaa = 0;
+    int i;
+
+    // Convert from big endian byte array to little endian word array.
+    for (i = 0; i < key->len; ++i) {
+        uint32_t tmp =
+            (inout[((key->len - 1 - i) * 4) + 0] << 24) |
+            (inout[((key->len - 1 - i) * 4) + 1] << 16) |
+            (inout[((key->len - 1 - i) * 4) + 2] << 8) |
+            (inout[((key->len - 1 - i) * 4) + 3] << 0);
+        a[i] = tmp;
+    }
+
+    if (key->exponent == 65537) {
+        aaa = aaR;  // Re-use location.
+        montMul(key, aR, a, key->rr);  // aR = a * RR / R mod M
+        for (i = 0; i < 16; i += 2) {
+            montMul(key, aaR, aR, aR);  // aaR = aR * aR / R mod M
+            montMul(key, aR, aaR, aaR);  // aR = aaR * aaR / R mod M
+        }
+        montMul(key, aaa, aR, a);  // aaa = aR * a / R mod M
+    } else if (key->exponent == 3) {
+        aaa = aR;  // Re-use location.
+        montMul(key, aR, a, key->rr);  /* aR = a * RR / R mod M   */
+        montMul(key, aaR, aR, aR);     /* aaR = aR * aR / R mod M */
+        montMul(key, aaa, aaR, a);     /* aaa = aaR * a / R mod M */
+    }
+
+    // Make sure aaa < mod; aaa is at most 1x mod too large.
+    if (geM(key, aaa)) {
+        subM(key, aaa);
+    }
+
+    // Convert to bigendian byte array
+    for (i = key->len - 1; i >= 0; --i) {
+        uint32_t tmp = aaa[i];
+        *inout++ = tmp >> 24;
+        *inout++ = tmp >> 16;
+        *inout++ = tmp >> 8;
+        *inout++ = tmp >> 0;
+    }
+}
+
+// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature.
+// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the
+// other flavor which omits the optional parameter entirely). This code does not
+// accept signatures without the optional parameter.
+
+/*
+static const uint8_t sha_padding[RSANUMBYTES] = {
+    0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30,
+    0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
+    0x05, 0x00, 0x04, 0x14,
+
+    // 20 bytes of hash go here.
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+*/
+
+// SHA-1 of PKCS1.5 signature sha_padding for 2048 bit, as above.
+// At the location of the bytes of the hash all 00 are hashed.
+static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = {
+    0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e,
+    0x6e, 0xfc, 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68,
+    0x7c, 0xfb, 0xf1, 0x67
+};
+
+/*
+static const uint8_t sha256_padding[RSANUMBYTES] = {
+    0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x31, 0x30,
+    0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+    0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20,
+
+    // 32 bytes of hash go here.
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+};
+*/
+
+// SHA-256 of PKCS1.5 signature sha256_padding for 2048 bit, as above.
+// At the location of the bytes of the hash all 00 are hashed.
+static const uint8_t kExpectedPadSha256Rsa2048[SHA256_DIGEST_SIZE] = {
+    0xab, 0x28, 0x8d, 0x8a, 0xd7, 0xd9, 0x59, 0x92,
+    0xba, 0xcc, 0xf8, 0x67, 0x20, 0xe1, 0x15, 0x2e,
+    0x39, 0x8d, 0x80, 0x36, 0xd6, 0x6f, 0xf0, 0xfd,
+    0x90, 0xe8, 0x7d, 0x8b, 0xe1, 0x7c, 0x87, 0x59,
+};
+
+// Verify a 2048-bit RSA PKCS1.5 signature against an expected hash.
+// Both e=3 and e=65537 are supported.  hash_len may be
+// SHA_DIGEST_SIZE (== 20) to indicate a SHA-1 hash, or
+// SHA256_DIGEST_SIZE (== 32) to indicate a SHA-256 hash.  No other
+// values are supported.
+//
+// Returns 1 on successful verification, 0 on failure.
+int RSA_verify(const RSAPublicKey *key,
+               const uint8_t *signature,
+               const int len,
+               const uint8_t *hash,
+               const int hash_len) {
+    uint8_t buf[RSANUMBYTES];
+    int i;
+    const uint8_t* padding_hash;
+
+    if (key->len != RSANUMWORDS) {
+        return 0;  // Wrong key passed in.
+    }
+
+    if (len != sizeof(buf)) {
+        return 0;  // Wrong input length.
+    }
+
+    if (hash_len != SHA_DIGEST_SIZE &&
+        hash_len != SHA256_DIGEST_SIZE) {
+        return 0;  // Unsupported hash.
+    }
+
+    if (key->exponent != 3 && key->exponent != 65537) {
+        return 0;  // Unsupported exponent.
+    }
+
+    for (i = 0; i < len; ++i) {  // Copy input to local workspace.
+        buf[i] = signature[i];
+    }
+
+    modpow(key, buf);  // In-place exponentiation.
+
+    // Xor sha portion, so it all becomes 00 iff equal.
+    for (i = len - hash_len; i < len; ++i) {
+        buf[i] ^= *hash++;
+    }
+
+    // Hash resulting buf, in-place.
+    switch (hash_len) {
+        case SHA_DIGEST_SIZE:
+            padding_hash = kExpectedPadShaRsa2048;
+            SHA_hash(buf, len, buf);
+            break;
+        case SHA256_DIGEST_SIZE:
+            padding_hash = kExpectedPadSha256Rsa2048;
+            SHA256_hash(buf, len, buf);
+            break;
+        default:
+            return 0;
+    }
+
+    // Compare against expected hash value.
+    for (i = 0; i < hash_len; ++i) {
+        if (buf[i] != padding_hash[i]) {
+            return 0;
+        }
+    }
+
+    return 1;  // All checked out OK.
+}
diff --git a/libmincrypt/sha.c b/libmincrypt/sha.c
new file mode 100644
index 0000000..5bef32e
--- /dev/null
+++ b/libmincrypt/sha.c
@@ -0,0 +1,155 @@
+/* sha.c
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * 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.
+**     * Neither the name of Google Inc. 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 Google Inc. ``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 Google Inc. 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.
+*/
+
+// Optimized for minimal code size.
+
+#include "mincrypt/sha.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+static void SHA1_Transform(SHA_CTX* ctx) {
+    uint32_t W[80];
+    uint32_t A, B, C, D, E;
+    uint8_t* p = ctx->buf;
+    int t;
+
+    for(t = 0; t < 16; ++t) {
+        uint32_t tmp =  *p++ << 24;
+        tmp |= *p++ << 16;
+        tmp |= *p++ << 8;
+        tmp |= *p++;
+        W[t] = tmp;
+    }
+
+    for(; t < 80; t++) {
+        W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+    }
+
+    A = ctx->state[0];
+    B = ctx->state[1];
+    C = ctx->state[2];
+    D = ctx->state[3];
+    E = ctx->state[4];
+
+    for(t = 0; t < 80; t++) {
+        uint32_t tmp = rol(5,A) + E + W[t];
+
+        if (t < 20)
+            tmp += (D^(B&(C^D))) + 0x5A827999;
+        else if ( t < 40)
+            tmp += (B^C^D) + 0x6ED9EBA1;
+        else if ( t < 60)
+            tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC;
+        else
+            tmp += (B^C^D) + 0xCA62C1D6;
+
+        E = D;
+        D = C;
+        C = rol(30,B);
+        B = A;
+        A = tmp;
+    }
+
+    ctx->state[0] += A;
+    ctx->state[1] += B;
+    ctx->state[2] += C;
+    ctx->state[3] += D;
+    ctx->state[4] += E;
+}
+
+static const HASH_VTAB SHA_VTAB = {
+    SHA_init,
+    SHA_update,
+    SHA_final,
+    SHA_hash,
+    SHA_DIGEST_SIZE
+};
+
+void SHA_init(SHA_CTX* ctx) {
+    ctx->f = &SHA_VTAB;
+    ctx->state[0] = 0x67452301;
+    ctx->state[1] = 0xEFCDAB89;
+    ctx->state[2] = 0x98BADCFE;
+    ctx->state[3] = 0x10325476;
+    ctx->state[4] = 0xC3D2E1F0;
+    ctx->count = 0;
+}
+
+
+void SHA_update(SHA_CTX* ctx, const void* data, int len) {
+    int i = (int) (ctx->count & 63);
+    const uint8_t* p = (const uint8_t*)data;
+
+    ctx->count += len;
+
+    while (len--) {
+        ctx->buf[i++] = *p++;
+        if (i == 64) {
+            SHA1_Transform(ctx);
+            i = 0;
+        }
+    }
+}
+
+
+const uint8_t* SHA_final(SHA_CTX* ctx) {
+    uint8_t *p = ctx->buf;
+    uint64_t cnt = ctx->count * 8;
+    int i;
+
+    SHA_update(ctx, (uint8_t*)"\x80", 1);
+    while ((ctx->count & 63) != 56) {
+        SHA_update(ctx, (uint8_t*)"\0", 1);
+    }
+    for (i = 0; i < 8; ++i) {
+        uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
+        SHA_update(ctx, &tmp, 1);
+    }
+
+    for (i = 0; i < 5; i++) {
+        uint32_t tmp = ctx->state[i];
+        *p++ = tmp >> 24;
+        *p++ = tmp >> 16;
+        *p++ = tmp >> 8;
+        *p++ = tmp >> 0;
+    }
+
+    return ctx->buf;
+}
+
+/* Convenience function */
+const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest) {
+    SHA_CTX ctx;
+    SHA_init(&ctx);
+    SHA_update(&ctx, data, len);
+    memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE);
+    return digest;
+}
diff --git a/libmincrypt/sha256.c b/libmincrypt/sha256.c
new file mode 100644
index 0000000..eb6e308
--- /dev/null
+++ b/libmincrypt/sha256.c
@@ -0,0 +1,184 @@
+/* sha256.c
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * 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.
+**     * Neither the name of Google Inc. 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 Google Inc. ``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 Google Inc. 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.
+*/
+
+// Optimized for minimal code size.
+
+#include "mincrypt/sha256.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits))))
+#define shr(value, bits) ((value) >> (bits))
+
+static const uint32_t K[64] = {
+    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
+
+static void SHA256_Transform(SHA256_CTX* ctx) {
+    uint32_t W[64];
+    uint32_t A, B, C, D, E, F, G, H;
+    uint8_t* p = ctx->buf;
+    int t;
+
+    for(t = 0; t < 16; ++t) {
+        uint32_t tmp =  *p++ << 24;
+        tmp |= *p++ << 16;
+        tmp |= *p++ << 8;
+        tmp |= *p++;
+        W[t] = tmp;
+    }
+
+    for(; t < 64; t++) {
+        uint32_t s0 = ror(W[t-15], 7) ^ ror(W[t-15], 18) ^ shr(W[t-15], 3);
+        uint32_t s1 = ror(W[t-2], 17) ^ ror(W[t-2], 19) ^ shr(W[t-2], 10);
+        W[t] = W[t-16] + s0 + W[t-7] + s1;
+    }
+
+    A = ctx->state[0];
+    B = ctx->state[1];
+    C = ctx->state[2];
+    D = ctx->state[3];
+    E = ctx->state[4];
+    F = ctx->state[5];
+    G = ctx->state[6];
+    H = ctx->state[7];
+
+    for(t = 0; t < 64; t++) {
+        uint32_t s0 = ror(A, 2) ^ ror(A, 13) ^ ror(A, 22);
+        uint32_t maj = (A & B) ^ (A & C) ^ (B & C);
+        uint32_t t2 = s0 + maj;
+        uint32_t s1 = ror(E, 6) ^ ror(E, 11) ^ ror(E, 25);
+        uint32_t ch = (E & F) ^ ((~E) & G);
+        uint32_t t1 = H + s1 + ch + K[t] + W[t];
+
+        H = G;
+        G = F;
+        F = E;
+        E = D + t1;
+        D = C;
+        C = B;
+        B = A;
+        A = t1 + t2;
+    }
+
+    ctx->state[0] += A;
+    ctx->state[1] += B;
+    ctx->state[2] += C;
+    ctx->state[3] += D;
+    ctx->state[4] += E;
+    ctx->state[5] += F;
+    ctx->state[6] += G;
+    ctx->state[7] += H;
+}
+
+static const HASH_VTAB SHA256_VTAB = {
+    SHA256_init,
+    SHA256_update,
+    SHA256_final,
+    SHA256_hash,
+    SHA256_DIGEST_SIZE
+};
+
+void SHA256_init(SHA256_CTX* ctx) {
+    ctx->f = &SHA256_VTAB;
+    ctx->state[0] = 0x6a09e667;
+    ctx->state[1] = 0xbb67ae85;
+    ctx->state[2] = 0x3c6ef372;
+    ctx->state[3] = 0xa54ff53a;
+    ctx->state[4] = 0x510e527f;
+    ctx->state[5] = 0x9b05688c;
+    ctx->state[6] = 0x1f83d9ab;
+    ctx->state[7] = 0x5be0cd19;
+    ctx->count = 0;
+}
+
+
+void SHA256_update(SHA256_CTX* ctx, const void* data, int len) {
+    int i = (int) (ctx->count & 63);
+    const uint8_t* p = (const uint8_t*)data;
+
+    ctx->count += len;
+
+    while (len--) {
+        ctx->buf[i++] = *p++;
+        if (i == 64) {
+            SHA256_Transform(ctx);
+            i = 0;
+        }
+    }
+}
+
+
+const uint8_t* SHA256_final(SHA256_CTX* ctx) {
+    uint8_t *p = ctx->buf;
+    uint64_t cnt = ctx->count * 8;
+    int i;
+
+    SHA256_update(ctx, (uint8_t*)"\x80", 1);
+    while ((ctx->count & 63) != 56) {
+        SHA256_update(ctx, (uint8_t*)"\0", 1);
+    }
+    for (i = 0; i < 8; ++i) {
+        uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
+        SHA256_update(ctx, &tmp, 1);
+    }
+
+    for (i = 0; i < 8; i++) {
+        uint32_t tmp = ctx->state[i];
+        *p++ = tmp >> 24;
+        *p++ = tmp >> 16;
+        *p++ = tmp >> 8;
+        *p++ = tmp >> 0;
+    }
+
+    return ctx->buf;
+}
+
+/* Convenience function */
+const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest) {
+    SHA256_CTX ctx;
+    SHA256_init(&ctx);
+    SHA256_update(&ctx, data, len);
+    memcpy(digest, SHA256_final(&ctx), SHA256_DIGEST_SIZE);
+    return digest;
+}
diff --git a/libtar/Android.mk b/libtar/Android.mk
new file mode 100644
index 0000000..838b441
--- /dev/null
+++ b/libtar/Android.mk
@@ -0,0 +1,39 @@
+LOCAL_PATH := $(call my-dir)
+
+# Build shared library
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtar
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_CFLAGS :=
+LOCAL_SRC_FILES = append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+					external/zlib
+LOCAL_SHARED_LIBRARIES += libz libc
+
+ifeq ($(TWHAVE_SELINUX), true)
+	LOCAL_C_INCLUDES += external/libselinux/include
+	LOCAL_SHARED_LIBRARIES += libselinux
+	LOCAL_CFLAGS += -DHAVE_SELINUX
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Build static library
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtar_static
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_CFLAGS = 
+LOCAL_SRC_FILES = append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+					external/zlib
+LOCAL_STATIC_LIBRARIES += libz libc
+
+ifeq ($(TWHAVE_SELINUX), true)
+	LOCAL_C_INCLUDES += external/libselinux/include
+	LOCAL_STATIC_LIBRARIES += libselinux
+	LOCAL_CFLAGS += -DHAVE_SELINUX
+endif
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libtar/COPYRIGHT b/libtar/COPYRIGHT
new file mode 100644
index 0000000..2471ec0
--- /dev/null
+++ b/libtar/COPYRIGHT
@@ -0,0 +1,35 @@
+Copyright (c) 1998-2003  University of Illinois Board of Trustees
+Copyright (c) 1998-2003  Mark D. Roth
+All rights reserved.
+
+Developed by: Campus Information Technologies and Educational Services,
+              University of Illinois at Urbana-Champaign
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+``Software''), to deal with the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimers.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimers in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the names of Campus Information Technologies and Educational
+  Services, University of Illinois at Urbana-Champaign, nor the names
+  of its contributors may be used to endorse or promote products derived
+  from this Software without specific prior written permission.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+
diff --git a/libtar/ChangeLog b/libtar/ChangeLog
new file mode 100644
index 0000000..cde7675
--- /dev/null
+++ b/libtar/ChangeLog
@@ -0,0 +1,15 @@
+2002-12-09	added list_empty() and hash_empty() functions
+
+2002-09-12	fixed list_iterate function to return -1 if it gets
+		an invalid argument
+
+		include <config.h> and <compat.h> from source files, not
+		from header file, since header file is sometimes
+		installed as part of a user-visible API
+		(those APIs should eventually be redesigned without the
+		listhash code being publicly visible, but for now we
+		need to accomodate this)
+
+2002-07-07	modified list iterate function to return int
+		(returns -1 if plugin function returns -1)
+
diff --git a/libtar/ChangeLog-1.0.x b/libtar/ChangeLog-1.0.x
new file mode 100644
index 0000000..23b06b3
--- /dev/null
+++ b/libtar/ChangeLog-1.0.x
@@ -0,0 +1,141 @@
+libtar 1.0.2 - 6/21/00
+------------
+
+- tar_set_file_perms() now calls chown() only if the effective user ID is 0
+  (workaround for IRIX and HP-UX, which allow file giveaways)
+
+- tar_set_file_perms() now calls chmod() or lchmod() after chown()
+  (this fixes a problem with extracting setuid files under Linux)
+
+- removed calls to fchown() and fchmod() from tar_extract_regfile()
+
+- fixed bugs in th_read() which didn't set errno properly
+
+- removed various unused variables
+
+----------------------------------------------------------------------
+
+libtar 1.0.1 - 4/1/00
+------------
+
+- removed libgen.h include from dirname and basename compat code
+
+- added lib/fnmatch.c compatability module from OpenBSD
+
+- fixed several objdirs bugs in libtar/Makefile.in
+
+- misc Makefile changes (added $CPPFLAGS support, added -o flag to compile
+  commands, use $CFLAGS on link line, etc)
+
+- removed "inline" keyword from all source files to prevent portability
+  problems
+
+- updated README
+
+----------------------------------------------------------------------
+
+libtar 1.0 - 1/2/00
+----------
+
+- various portability fixes
+
+- "make install" now runs mkencap and epkg if they're available
+
+- libmisc is now integrated into libtar
+
+----------------------------------------------------------------------
+
+libtar 0.5.6 beta - 12/16/99
+-----------------
+
+- changed API to allow better error reporting via errno
+
+- added manpages to document libtar API
+
+- replaced symbolic_mode() call with strmode() compatibility code
+
+----------------------------------------------------------------------
+
+libtar 0.5.5 beta - 11/16/99
+-----------------
+
+- fixed conditional expression in extract.c to check if we're overwriting
+  a pre-existing file
+
+- many improvements to libtar.c driver program (better error checking,
+  added -C and -v options, etc)
+
+- changed API to include list of canned file types, instead of passing
+  function pointers to tar_open()
+
+- fixed tar_set_file_perms() to not complain about chown() if not root
+  and not to call utime() on a symlink
+
+- added hash code for extracting hard links in other directory paths
+
+- fixed tar_extract_glob() to only print filenames if TAR_VERBOSE option
+  is set
+
+- replaced GNU basename(), dirname(), and strdup() compatibility code
+  with OpenBSD versions
+
+- configure performs super-anal checking of basename() and dirname()
+
+----------------------------------------------------------------------
+
+libtar 0.5.4 beta - 11/13/99
+-----------------
+
+- portability fix: use ranlib instead of ar -s
+
+- misc fixes in append.c, extract.c, and wrapper.c to do error checking
+
+- fixed a bug in tar_append_file() in append.c which added some garbage
+  characters to encoded symlink names (wasn't NULL-terminating the result
+  of readlink())
+
+- fixed a bug in symbolic_mode() in output.c concerning setuid and setgid
+  bit displaying
+
+- fixed tar_extract_all() in wrapper.c to only call print_long_ls() if
+  the TAR_VERBOSE option is set
+
+- added libtar_version constant string to handle.c for external configure
+  scripts to detect what version of libtar is installed
+
+----------------------------------------------------------------------
+
+libtar 0.5.3 beta - 09/27/99
+-----------------
+
+- fixed mk_dirs_for_file() to avoid broken dirname() implementations
+
+- misc portability fixes
+
+- merged old "compat" and "libds" directories into new "misc" directory
+  and cleaned up Makefiles
+
+----------------------------------------------------------------------
+
+libtar 0.5.2 beta - 09/10/99
+-----------------
+
+- use calloc() instead of malloc() in tar_open() to fix a bounds-checking
+  bug in tar_extract_all()
+
+- fix tar_extract_all() to properly honor the prefix argument
+
+----------------------------------------------------------------------
+
+libtar 0.5.1 beta - 08/27/99
+-----------------
+
+- misc portability fixes
+
+----------------------------------------------------------------------
+
+libtar 0.5 beta - 07/05/99
+---------------
+
+- first public release
+
diff --git a/libtar/INSTALL b/libtar/INSTALL
new file mode 100644
index 0000000..50dbe43
--- /dev/null
+++ b/libtar/INSTALL
@@ -0,0 +1,183 @@
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/libtar/README b/libtar/README
new file mode 100644
index 0000000..91f466c
--- /dev/null
+++ b/libtar/README
@@ -0,0 +1,121 @@
+libtar - C library for manipulating tar files
+======
+
+libtar is a library for manipulating tar files from within C programs.
+Here are some of its features:
+
+  * Handles both POSIX tar file format and the GNU extensions.
+  * API provides functions for easy use, such as tar_extract_all().
+  * Also provides functions for more granular use, such as 
+    tar_append_regfile().
+
+
+Installation
+------------
+
+To build libtar, you should be able to simply run these commands:
+
+   ./configure
+   make
+   make install
+
+
+Encap Package Support
+---------------------
+
+To build this software as an Encap package, you can pass the
+--enable-encap option to configure.  This will be automatically
+enabled if the epkg or mkencap programs are detected on the system,
+but can be overridden by the --disable-encap option.
+
+When building an Encap package, the configure script will automatically
+adjust the installation prefix to use an appropriate Encap package
+directory.  It does this using a heuristic algorithm which examines the
+values of the ${ENCAP_SOURCE} and ${ENCAP_TARGET} environment variables
+and the argument to configure's --prefix option.
+
+If mkencap was detected on the system, it will be automatically run during
+"make install".  By default, epkg will also be run, but this can be
+inhibited with the --disable-epkg-install configure option.
+
+For information on the Encap package management system, see the WSG
+Encap Archive:
+
+   http://www.encap.org/
+
+
+zlib Support
+------------
+
+The configure script will attempt to find the zlib library on your system
+for use with the libtar driver program.  The zlib package is available from:
+
+   http://www.gzip.org/zlib/
+
+If zlib is installed on your system, but you do not wish to use it,
+specify the --without-zlib option when you invoke configure.
+
+
+More Information
+----------------
+
+For documentation of the libtar API, see the enclosed manpages.  For more
+information on the libtar package, see:
+
+   http://www-dev.cites.uiuc.edu/libtar/
+
+Source code for the latest version of libtar will be available there, as
+well as Encap binary distributions for many common platforms.
+
+
+Supported Platforms
+-------------------
+
+I develop and test libtar on the following platforms:
+
+   AIX 4.3.3 and 5.1
+   HP-UX 11.00
+   IRIX 6.5
+   RedHat Linux 7.2
+   Solaris 8 and 9
+
+It should also build on the following platforms, but I do not actively
+support them:
+
+   AIX 3.2.5
+   AIX 4.2.1
+   Cygwin
+   FreeBSD
+   HP-UX 10.20
+   Linux/libc5
+   OpenBSD
+   Solaris 2.5
+   Solaris 2.6
+   Solaris 7
+
+If you successfully build libtar on another platform, please email me a
+patch and/or configuration information.
+
+
+Compatibility Code
+------------------
+
+libtar depends on some library calls which are not available or not
+usable on some platforms.  To accomodate these systems, I've included
+a version of these calls in the compat subdirectory.
+
+I've slightly modified these functions for integration into this source
+tree, but the functionality has not been modified from the original
+source.  Please note that while this code should work for you, I didn't
+write it, so please don't send me bug reports on it.
+
+
+Author
+------
+
+Feedback and bug reports are welcome.
+
+Mark D. Roth <roth@uiuc.edu>
+Campus Information Technologies and Educational Services
+University of Illinois at Urbana-Champaign
+
diff --git a/libtar/TODO b/libtar/TODO
new file mode 100644
index 0000000..f2f859f
--- /dev/null
+++ b/libtar/TODO
@@ -0,0 +1,21 @@
+Functionality:
+--------------
+
+* add list mode to allow nodes to be inserted in any arbitrary location
+* add "*_hash_iterate()" function
+* add flags argument to *_list_del() that allows the listptr to be set
+  to the previous or next element
+* add a generic pointer type to replace *_listptr_t and *_hashptr_t ???
+
+
+Code Cleanup:
+-------------
+
+* rename functions:
+	*_list_next	=> *_listptr_next()
+	*_list_prev	=> *_listptr_prev()
+	*_hash_next	=> *_hashptr_next()
+* start using "*_list_t" and "*_hash_t" instead of "*_list_t *" and
+  "*_hash_t *" ?
+* add prefixes to structure member field names
+
diff --git a/libtar/append.c b/libtar/append.c
new file mode 100644
index 0000000..514cf54
--- /dev/null
+++ b/libtar/append.c
@@ -0,0 +1,277 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  append.c - libtar code to append files to a tar archive
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_SELINUX
+#include "selinux/selinux.h"
+#endif
+
+struct tar_dev
+{
+	dev_t td_dev;
+	libtar_hash_t *td_h;
+};
+typedef struct tar_dev tar_dev_t;
+
+struct tar_ino
+{
+	ino_t ti_ino;
+	char ti_name[MAXPATHLEN];
+};
+typedef struct tar_ino tar_ino_t;
+
+
+/* free memory associated with a tar_dev_t */
+void
+tar_dev_free(tar_dev_t *tdp)
+{
+	libtar_hash_free(tdp->td_h, free);
+	free(tdp);
+}
+
+
+/* appends a file to the tar archive */
+int
+tar_append_file(TAR *t, char *realname, char *savename)
+{
+	struct stat s;
+	int i;
+	libtar_hashptr_t hp;
+	tar_dev_t *td = NULL;
+	tar_ino_t *ti = NULL;
+	char path[MAXPATHLEN];
+
+#ifdef DEBUG
+	printf("==> tar_append_file(TAR=0x%lx (\"%s\"), realname=\"%s\", "
+	       "savename=\"%s\")\n", t, t->pathname, realname,
+	       (savename ? savename : "[NULL]"));
+#endif
+
+	if (lstat(realname, &s) != 0)
+	{
+#ifdef DEBUG
+		perror("lstat()");
+#endif
+		return -1;
+	}
+
+	/* set header block */
+#ifdef DEBUG
+	puts("    tar_append_file(): setting header block...");
+#endif
+	memset(&(t->th_buf), 0, sizeof(struct tar_header));
+	th_set_from_stat(t, &s);
+
+	/* set the header path */
+#ifdef DEBUG
+	puts("    tar_append_file(): setting header path...");
+#endif
+	th_set_path(t, (savename ? savename : realname));
+
+#ifdef HAVE_SELINUX
+	/* get selinux context */
+	if(t->options & TAR_STORE_SELINUX) {
+		if(t->th_buf.selinux_context != NULL) {
+			free(t->th_buf.selinux_context);
+			t->th_buf.selinux_context = NULL;
+		}
+
+		security_context_t selinux_context = NULL;
+		if (lgetfilecon(realname, &selinux_context) >= 0) {
+			t->th_buf.selinux_context = strdup(selinux_context);
+			printf("setting selinux context: %s\n", selinux_context);
+			freecon(selinux_context);
+		}
+		else
+			perror("Failed to get selinux context");
+	}
+#endif
+	/* check if it's a hardlink */
+#ifdef DEBUG
+	puts("    tar_append_file(): checking inode cache for hardlink...");
+#endif
+	libtar_hashptr_reset(&hp);
+	if (libtar_hash_getkey(t->h, &hp, &(s.st_dev),
+			       (libtar_matchfunc_t)dev_match) != 0)
+		td = (tar_dev_t *)libtar_hashptr_data(&hp);
+	else
+	{
+#ifdef DEBUG
+		printf("+++ adding hash for device (0x%lx, 0x%lx)...\n",
+		       major(s.st_dev), minor(s.st_dev));
+#endif
+		td = (tar_dev_t *)calloc(1, sizeof(tar_dev_t));
+		td->td_dev = s.st_dev;
+		td->td_h = libtar_hash_new(256, (libtar_hashfunc_t)ino_hash);
+		if (td->td_h == NULL)
+			return -1;
+		if (libtar_hash_add(t->h, td) == -1)
+			return -1;
+	}
+	libtar_hashptr_reset(&hp);
+	if (libtar_hash_getkey(td->td_h, &hp, &(s.st_ino),
+			       (libtar_matchfunc_t)ino_match) != 0)
+	{
+		ti = (tar_ino_t *)libtar_hashptr_data(&hp);
+#ifdef DEBUG
+		printf("    tar_append_file(): encoding hard link \"%s\" "
+		       "to \"%s\"...\n", realname, ti->ti_name);
+#endif
+		t->th_buf.typeflag = LNKTYPE;
+		th_set_link(t, ti->ti_name);
+	}
+	else
+	{
+#ifdef DEBUG
+		printf("+++ adding entry: device (0x%lx,0x%lx), inode %ld "
+		       "(\"%s\")...\n", major(s.st_dev), minor(s.st_dev),
+		       s.st_ino, realname);
+#endif
+		ti = (tar_ino_t *)calloc(1, sizeof(tar_ino_t));
+		if (ti == NULL)
+			return -1;
+		ti->ti_ino = s.st_ino;
+		snprintf(ti->ti_name, sizeof(ti->ti_name), "%s",
+			 savename ? savename : realname);
+		libtar_hash_add(td->td_h, ti);
+	}
+
+	/* check if it's a symlink */
+	if (TH_ISSYM(t))
+	{
+		i = readlink(realname, path, sizeof(path));
+		if (i == -1)
+			return -1;
+		if (i >= MAXPATHLEN)
+			i = MAXPATHLEN - 1;
+		path[i] = '\0';
+#ifdef DEBUG
+		printf("    tar_append_file(): encoding symlink \"%s\" -> "
+		       "\"%s\"...\n", realname, path);
+#endif
+		th_set_link(t, path);
+	}
+
+	/* print file info */
+	if (t->options & TAR_VERBOSE)
+		th_print_long_ls(t);
+
+#ifdef DEBUG
+	puts("    tar_append_file(): writing header");
+#endif
+	/* write header */
+	if (th_write(t) != 0)
+	{
+#ifdef DEBUG
+		printf("t->fd = %d\n", t->fd);
+#endif
+		return -1;
+	}
+#ifdef DEBUG
+	puts("    tar_append_file(): back from th_write()");
+#endif
+
+	/* if it's a regular file, write the contents as well */
+	if (TH_ISREG(t) && tar_append_regfile(t, realname) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+/* write EOF indicator */
+int
+tar_append_eof(TAR *t)
+{
+	int i, j;
+	char block[T_BLOCKSIZE];
+
+	memset(&block, 0, T_BLOCKSIZE);
+	for (j = 0; j < 2; j++)
+	{
+		i = tar_block_write(t, &block);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* add file contents to a tarchive */
+int
+tar_append_regfile(TAR *t, char *realname)
+{
+	char block[T_BLOCKSIZE];
+	int filefd;
+	int i, j;
+	size_t size;
+
+	filefd = open(realname, O_RDONLY);
+	if (filefd == -1)
+	{
+#ifdef DEBUG
+		perror("open()");
+#endif
+		return -1;
+	}
+
+	size = th_get_size(t);
+	for (i = size; i > T_BLOCKSIZE; i -= T_BLOCKSIZE)
+	{
+		j = read(filefd, &block, T_BLOCKSIZE);
+		if (j != T_BLOCKSIZE)
+		{
+			if (j != -1)
+				errno = EINVAL;
+			return -1;
+		}
+		if (tar_block_write(t, &block) == -1)
+			return -1;
+	}
+
+	if (i > 0)
+	{
+		j = read(filefd, &block, i);
+		if (j == -1)
+			return -1;
+		memset(&(block[i]), 0, T_BLOCKSIZE - i);
+		if (tar_block_write(t, &block) == -1)
+			return -1;
+	}
+
+	close(filefd);
+
+	return 0;
+}
+
+
diff --git a/libtar/basename.c b/libtar/basename.c
new file mode 100644
index 0000000..2ac1e13
--- /dev/null
+++ b/libtar/basename.c
@@ -0,0 +1,74 @@
+/*	$OpenBSD: basename.c,v 1.4 1999/05/30 17:10:30 espie Exp $	*/
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$OpenBSD: basename.c,v 1.4 1999/05/30 17:10:30 espie Exp $";
+#endif /* not lint */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+
+char *
+openbsd_basename(path)
+	const char *path;
+{
+	static char bname[MAXPATHLEN];
+	register const char *endp, *startp;
+
+	/* Empty or NULL string gets treated as "." */
+	if (path == NULL || *path == '\0') {
+		(void)strcpy(bname, ".");
+		return(bname);
+	}
+
+	/* Strip trailing slashes */
+	endp = path + strlen(path) - 1;
+	while (endp > path && *endp == '/')
+		endp--;
+
+	/* All slashes becomes "/" */
+	if (endp == path && *endp == '/') {
+		(void)strcpy(bname, "/");
+		return(bname);
+	}
+
+	/* Find the start of the base */
+	startp = endp;
+	while (startp > path && *(startp - 1) != '/')
+		startp--;
+
+	if (endp - startp + 1 > sizeof(bname)) {
+		errno = ENAMETOOLONG;
+		return(NULL);
+	}
+	(void)strncpy(bname, startp, endp - startp + 1);
+	bname[endp - startp + 1] = '\0';
+	return(bname);
+}
diff --git a/libtar/block.c b/libtar/block.c
new file mode 100644
index 0000000..1cfc0e4
--- /dev/null
+++ b/libtar/block.c
@@ -0,0 +1,496 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  block.c - libtar code to handle tar archive header blocks
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
+
+// Used to identify selinux_context in extended ('x')
+// metadata. From RedHat implementation.
+#define SELINUX_TAG "RHT.security.selinux="
+#define SELINUX_TAG_LEN 21
+
+/* read a header block */
+int
+th_read_internal(TAR *t)
+{
+	int i;
+	int num_zero_blocks = 0;
+
+#ifdef DEBUG
+	printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
+#endif
+
+	while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
+	{
+		/* two all-zero blocks mark EOF */
+		if (t->th_buf.name[0] == '\0')
+		{
+			num_zero_blocks++;
+			if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
+			    && num_zero_blocks >= 2)
+				return 0;	/* EOF */
+			else
+				continue;
+		}
+
+		/* verify magic and version */
+		if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
+		    && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
+		{
+#ifdef DEBUG
+			puts("!!! unknown magic value in tar header");
+#endif
+			return -2;
+		}
+
+		if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
+		    && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
+		{
+#ifdef DEBUG
+			puts("!!! unknown version value in tar header");
+#endif
+			return -2;
+		}
+
+		/* check chksum */
+		if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
+		    && !th_crc_ok(t))
+		{
+#ifdef DEBUG
+			puts("!!! tar header checksum error");
+#endif
+			return -2;
+		}
+
+		break;
+	}
+
+#ifdef DEBUG
+	printf("<== th_read_internal(): returning %d\n", i);
+#endif
+	return i;
+}
+
+
+/* wrapper function for th_read_internal() to handle GNU extensions */
+int
+th_read(TAR *t)
+{
+	int i, j;
+	size_t sz;
+	char *ptr;
+
+#ifdef DEBUG
+	printf("==> th_read(t=0x%lx)\n", t);
+#endif
+
+	if (t->th_buf.gnu_longname != NULL)
+		free(t->th_buf.gnu_longname);
+	if (t->th_buf.gnu_longlink != NULL)
+		free(t->th_buf.gnu_longlink);
+#ifdef HAVE_SELINUX
+	if (t->th_buf.selinux_context != NULL)
+		free(t->th_buf.selinux_context);
+#endif
+
+	memset(&(t->th_buf), 0, sizeof(struct tar_header));
+
+	i = th_read_internal(t);
+	if (i == 0)
+		return 1;
+	else if (i != T_BLOCKSIZE)
+	{
+		if (i != -1)
+			errno = EINVAL;
+		return -1;
+	}
+
+	/* check for GNU long link extention */
+	if (TH_ISLONGLINK(t))
+	{
+		sz = th_get_size(t);
+		j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+#ifdef DEBUG
+		printf("    th_read(): GNU long linkname detected "
+		       "(%ld bytes, %d blocks)\n", sz, j);
+#endif
+		t->th_buf.gnu_longlink = (char *)malloc(j * T_BLOCKSIZE);
+		if (t->th_buf.gnu_longlink == NULL)
+			return -1;
+
+		for (ptr = t->th_buf.gnu_longlink; j > 0;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+#ifdef DEBUG
+			printf("    th_read(): reading long linkname "
+			       "(%d blocks left, ptr == %ld)\n", j, ptr);
+#endif
+			i = tar_block_read(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+#ifdef DEBUG
+			printf("    th_read(): read block == \"%s\"\n", ptr);
+#endif
+		}
+#ifdef DEBUG
+		printf("    th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
+		       t->th_buf.gnu_longlink);
+#endif
+
+		i = th_read_internal(t);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+	/* check for GNU long name extention */
+	if (TH_ISLONGNAME(t))
+	{
+		sz = th_get_size(t);
+		j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+#ifdef DEBUG
+		printf("    th_read(): GNU long filename detected "
+		       "(%ld bytes, %d blocks)\n", sz, j);
+#endif
+		t->th_buf.gnu_longname = (char *)malloc(j * T_BLOCKSIZE);
+		if (t->th_buf.gnu_longname == NULL)
+			return -1;
+
+		for (ptr = t->th_buf.gnu_longname; j > 0;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+#ifdef DEBUG
+			printf("    th_read(): reading long filename "
+			       "(%d blocks left, ptr == %ld)\n", j, ptr);
+#endif
+			i = tar_block_read(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+#ifdef DEBUG
+			printf("    th_read(): read block == \"%s\"\n", ptr);
+#endif
+		}
+#ifdef DEBUG
+		printf("    th_read(): t->th_buf.gnu_longname == \"%s\"\n",
+		       t->th_buf.gnu_longname);
+#endif
+
+		i = th_read_internal(t);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+#ifdef HAVE_SELINUX
+	if(TH_ISEXTHEADER(t))
+	{
+		sz = th_get_size(t);
+
+		if(sz >= T_BLOCKSIZE) // Not supported
+		{
+#ifdef DEBUG
+			printf("    th_read(): Extended header is too long!\n");
+#endif
+		}
+		else
+		{
+			char buf[T_BLOCKSIZE];
+			i = tar_block_read(t, buf);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+
+			// To be sure
+			buf[T_BLOCKSIZE-1] = 0;
+
+			int len = strlen(buf);
+			char *start = strstr(buf, SELINUX_TAG);
+			if(start && start+SELINUX_TAG_LEN < buf+len)
+			{
+				start += SELINUX_TAG_LEN;
+				char *end = strchr(start, '\n');
+				if(end)
+				{
+					t->th_buf.selinux_context = strndup(start, end-start);
+#ifdef DEBUG
+					printf("    th_read(): SELinux context xattr detected: %s\n", t->th_buf.selinux_context);
+#endif
+				}
+			}
+		}
+
+		i = th_read_internal(t);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+#endif
+
+#if 0
+	/*
+	** work-around for old archive files with broken typeflag fields
+	** NOTE: I fixed this in the TH_IS*() macros instead
+	*/
+
+	/*
+	** (directories are signified with a trailing '/')
+	*/
+	if (t->th_buf.typeflag == AREGTYPE
+	    && t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
+		t->th_buf.typeflag = DIRTYPE;
+
+	/*
+	** fallback to using mode bits
+	*/
+	if (t->th_buf.typeflag == AREGTYPE)
+	{
+		mode = (mode_t)oct_to_int(t->th_buf.mode);
+
+		if (S_ISREG(mode))
+			t->th_buf.typeflag = REGTYPE;
+		else if (S_ISDIR(mode))
+			t->th_buf.typeflag = DIRTYPE;
+		else if (S_ISFIFO(mode))
+			t->th_buf.typeflag = FIFOTYPE;
+		else if (S_ISCHR(mode))
+			t->th_buf.typeflag = CHRTYPE;
+		else if (S_ISBLK(mode))
+			t->th_buf.typeflag = BLKTYPE;
+		else if (S_ISLNK(mode))
+			t->th_buf.typeflag = SYMTYPE;
+	}
+#endif
+
+	return 0;
+}
+
+
+/* write a header block */
+int
+th_write(TAR *t)
+{
+	int i, j;
+	char type2;
+	size_t sz, sz2;
+	char *ptr;
+	char buf[T_BLOCKSIZE];
+
+#ifdef DEBUG
+	printf("==> th_write(TAR=\"%s\")\n", t->pathname);
+	th_print(t);
+#endif
+
+	if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
+	{
+#ifdef DEBUG
+		printf("th_write(): using gnu_longlink (\"%s\")\n",
+		       t->th_buf.gnu_longlink);
+#endif
+		/* save old size and type */
+		type2 = t->th_buf.typeflag;
+		sz2 = th_get_size(t);
+
+		/* write out initial header block with fake size and type */
+		t->th_buf.typeflag = GNU_LONGLINK_TYPE;
+		sz = strlen(t->th_buf.gnu_longlink);
+		th_set_size(t, sz);
+		th_finish(t);
+		i = tar_block_write(t, &(t->th_buf));
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* write out extra blocks containing long name */
+		for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
+		     ptr = t->th_buf.gnu_longlink; j > 1;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+			i = tar_block_write(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+		}
+		memset(buf, 0, T_BLOCKSIZE);
+		strncpy(buf, ptr, T_BLOCKSIZE);
+		i = tar_block_write(t, &buf);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* reset type and size to original values */
+		t->th_buf.typeflag = type2;
+		th_set_size(t, sz2);
+	}
+
+	if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
+	{
+#ifdef DEBUG
+		printf("th_write(): using gnu_longname (\"%s\")\n",
+		       t->th_buf.gnu_longname);
+#endif
+		/* save old size and type */
+		type2 = t->th_buf.typeflag;
+		sz2 = th_get_size(t);
+
+		/* write out initial header block with fake size and type */
+		t->th_buf.typeflag = GNU_LONGNAME_TYPE;
+		sz = strlen(t->th_buf.gnu_longname);
+		th_set_size(t, sz);
+		th_finish(t);
+		i = tar_block_write(t, &(t->th_buf));
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* write out extra blocks containing long name */
+		for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
+		     ptr = t->th_buf.gnu_longname; j > 1;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+			i = tar_block_write(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+		}
+		memset(buf, 0, T_BLOCKSIZE);
+		strncpy(buf, ptr, T_BLOCKSIZE);
+		i = tar_block_write(t, &buf);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* reset type and size to original values */
+		t->th_buf.typeflag = type2;
+		th_set_size(t, sz2);
+	}
+
+#ifdef HAVE_SELINUX
+	if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
+	{
+#ifdef DEBUG
+		printf("th_write(): using selinux_context (\"%s\")\n",
+		       t->th_buf.selinux_context);
+#endif
+		/* save old size and type */
+		type2 = t->th_buf.typeflag;
+		sz2 = th_get_size(t);
+
+		/* write out initial header block with fake size and type */
+		t->th_buf.typeflag = TH_EXT_TYPE;
+
+		/* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */
+		//                                                       size   newline
+		sz = SELINUX_TAG_LEN + strlen(t->th_buf.selinux_context) + 3  +    1;
+
+		if(sz >= 100) // another ascci digit for size
+			++sz;
+
+		if(sz >= T_BLOCKSIZE) // impossible
+		{
+			errno = EINVAL;
+			return -1;
+		}
+
+		th_set_size(t, sz);
+		th_finish(t);
+		i = tar_block_write(t, &(t->th_buf));
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		memset(buf, 0, T_BLOCKSIZE);
+		snprintf(buf, T_BLOCKSIZE, "%d "SELINUX_TAG"%s\n", sz, t->th_buf.selinux_context);
+		i = tar_block_write(t, &buf);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* reset type and size to original values */
+		t->th_buf.typeflag = type2;
+		th_set_size(t, sz2);
+	}
+#endif
+
+	th_finish(t);
+
+#ifdef DEBUG
+	/* print tar header */
+	th_print(t);
+#endif
+
+	i = tar_block_write(t, &(t->th_buf));
+	if (i != T_BLOCKSIZE)
+	{
+		if (i != -1)
+			errno = EINVAL;
+		return -1;
+	}
+
+#ifdef DEBUG
+	puts("th_write(): returning 0");
+#endif
+	return 0;
+}
+
+
diff --git a/libtar/compat.h b/libtar/compat.h
new file mode 100644
index 0000000..d086294
--- /dev/null
+++ b/libtar/compat.h
@@ -0,0 +1,260 @@
+/* prototypes for borrowed "compatibility" code */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifdef HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+#ifdef HAVE_SELINUX
+#include "selinux/selinux.h"
+#endif
+
+
+#if defined(NEED_BASENAME) && !defined(HAVE_BASENAME)
+
+# ifdef basename
+#  undef basename		/* fix glibc brokenness */
+# endif
+
+char *openbsd_basename(const char *);
+# define basename openbsd_basename
+
+#endif /* NEED_BASENAME && ! HAVE_BASENAME */
+
+
+#if defined(NEED_DIRNAME) && !defined(HAVE_DIRNAME)
+
+char *openbsd_dirname(const char *);
+# define dirname openbsd_dirname
+
+#endif /* NEED_DIRNAME && ! HAVE_DIRNAME */
+
+
+#ifdef NEED_FNMATCH
+# ifndef HAVE_FNMATCH
+
+#  define FNM_NOMATCH	1	/* Match failed. */
+
+#  define FNM_NOESCAPE	0x01	/* Disable backslash escaping. */
+#  define FNM_PATHNAME	0x02	/* Slash must be matched by slash. */
+#  define FNM_PERIOD	0x04	/* Period must be matched by period. */
+
+#  define FNM_LEADING_DIR 0x08	/* Ignore /<tail> after Imatch. */
+#  define FNM_CASEFOLD	0x10	/* Case insensitive search. */
+#  define FNM_IGNORECASE FNM_CASEFOLD
+#  define FNM_FILE_NAME FNM_PATHNAME
+
+int openbsd_fnmatch(const char *, const char *, int);
+#  define fnmatch openbsd_fnmatch
+
+# else /* HAVE_FNMATCH */
+
+#  ifdef HAVE_FNMATCH_H
+#   include <fnmatch.h>
+#  endif
+
+# endif /* ! HAVE_FNMATCH */
+#endif /* NEED_FNMATCH */
+
+
+#ifdef NEED_GETHOSTBYNAME_R
+
+# include <netdb.h>
+
+# if GETHOSTBYNAME_R_NUM_ARGS != 6
+
+int compat_gethostbyname_r(const char *, struct hostent *,
+			   char *, size_t, struct hostent **, int *);
+
+#  define gethostbyname_r compat_gethostbyname_r
+
+# endif /* GETHOSTBYNAME_R_NUM_ARGS != 6 */
+
+#endif /* NEED_GETHOSTBYNAME_R */
+
+
+#if defined(NEED_GETHOSTNAME) && !defined(HAVE_GETHOSTNAME)
+
+int gethostname(char *, size_t);
+
+#endif /* NEED_GETHOSTNAME && ! HAVE_GETHOSTNAME */
+
+
+#ifdef NEED_GETSERVBYNAME_R
+
+# include <netdb.h>
+
+# if GETSERVBYNAME_R_NUM_ARGS != 6
+
+int compat_getservbyname_r(const char *, const char *, struct servent *,
+			   char *, size_t, struct servent **);
+
+#  define getservbyname_r compat_getservbyname_r
+
+# endif /* GETSERVBYNAME_R_NUM_ARGS != 6 */
+
+#endif /* NEED_GETSERVBYNAME_R */
+
+
+
+#ifdef NEED_GLOB
+# ifndef HAVE_GLOB
+
+typedef struct {
+	int gl_pathc;		/* Count of total paths so far. */
+	int gl_matchc;		/* Count of paths matching pattern. */
+	int gl_offs;		/* Reserved at beginning of gl_pathv. */
+	int gl_flags;		/* Copy of flags parameter to glob. */
+	char **gl_pathv;	/* List of paths matching pattern. */
+				/* Copy of errfunc parameter to glob. */
+	int (*gl_errfunc)(const char *, int);
+
+	/*
+	 * Alternate filesystem access methods for glob; replacement
+	 * versions of closedir(3), readdir(3), opendir(3), stat(2)
+	 * and lstat(2).
+	 */
+	void (*gl_closedir)(void *);
+	struct dirent *(*gl_readdir)(void *);
+	void *(*gl_opendir)(const char *);
+	int (*gl_lstat)(const char *, struct stat *);
+	int (*gl_stat)(const char *, struct stat *);
+} glob_t;
+
+/* Flags */
+#  define GLOB_APPEND	0x0001	/* Append to output from previous call. */
+#  define GLOB_DOOFFS	0x0002	/* Use gl_offs. */
+#  define GLOB_ERR	0x0004	/* Return on error. */
+#  define GLOB_MARK	0x0008	/* Append / to matching directories. */
+#  define GLOB_NOCHECK	0x0010	/* Return pattern itself if nothing matches. */
+#  define GLOB_NOSORT	0x0020	/* Don't sort. */
+
+#  define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */
+#  define GLOB_BRACE	0x0080	/* Expand braces ala csh. */
+#  define GLOB_MAGCHAR	0x0100	/* Pattern had globbing characters. */
+#  define GLOB_NOMAGIC	0x0200	/* GLOB_NOCHECK without magic chars (csh). */
+#  define GLOB_QUOTE	0x0400	/* Quote special chars with \. */
+#  define GLOB_TILDE	0x0800	/* Expand tilde names from the passwd file. */
+#  define GLOB_NOESCAPE	0x1000	/* Disable backslash escaping. */
+
+/* Error values returned by glob(3) */
+#  define GLOB_NOSPACE	(-1)	/* Malloc call failed. */
+#  define GLOB_ABORTED	(-2)	/* Unignored error. */
+#  define GLOB_NOMATCH	(-3)	/* No match and GLOB_NOCHECK not set. */
+#  define GLOB_NOSYS	(-4)	/* Function not supported. */
+#  define GLOB_ABEND	GLOB_ABORTED
+
+int openbsd_glob(const char *, int, int (*)(const char *, int), glob_t *);
+void openbsd_globfree(glob_t *);
+#  define glob openbsd_glob
+#  define globfree openbsd_globfree
+
+# else /* HAVE_GLOB */
+
+#  ifdef HAVE_GLOB_H
+#   include <glob.h>
+#  endif
+
+# endif /* ! HAVE_GLOB */
+#endif /* NEED_GLOB */
+
+
+#if defined(NEED_INET_ATON) && !defined(HAVE_INET_ATON)
+
+int inet_aton(const char *, struct in_addr *);
+
+#endif /* NEED_INET_ATON && ! HAVE_INET_ATON */
+
+
+#ifdef NEED_MAKEDEV
+
+# ifdef MAJOR_IN_MKDEV
+#  include <sys/mkdev.h>
+# else
+#  ifdef MAJOR_IN_SYSMACROS
+#   include <sys/sysmacros.h>
+#  endif
+# endif
+
+/*
+** On most systems makedev() has two args.
+** Some weird systems, like QNX6, have makedev() functions that expect
+** an extra first argument for "node", which can be 0 for a local
+** machine.
+*/
+
+# ifdef MAKEDEV_THREE_ARGS
+#  define compat_makedev(maj, min)	makedev(0, maj, min)
+# else
+#  define compat_makedev		makedev
+# endif
+
+#endif /* NEED_MAKEDEV */
+
+
+#if defined(NEED_SNPRINTF) && !defined(HAVE_SNPRINTF)
+
+int mutt_snprintf(char *, size_t, const char *, ...);
+int mutt_vsnprintf(char *, size_t, const char *, va_list);
+#define snprintf mutt_snprintf
+#define vsnprintf mutt_vsnprintf
+
+#endif /* NEED_SNPRINTF && ! HAVE_SNPRINTF */
+
+
+#if defined(NEED_STRLCAT) && !defined(HAVE_STRLCAT)
+
+size_t strlcat(char *, const char *, size_t);
+
+#endif /* NEED_STRLCAT && ! HAVE_STRLCAT */
+
+
+#if defined(NEED_STRLCPY) && !defined(HAVE_STRLCPY)
+
+size_t strlcpy(char *, const char *, size_t);
+
+#endif /* NEED_STRLCPY && ! HAVE_STRLCPY */
+
+
+#if defined(NEED_STRDUP) && !defined(HAVE_STRDUP)
+
+char *openbsd_strdup(const char *);
+# define strdup openbsd_strdup
+
+#endif /* NEED_STRDUP && ! HAVE_STRDUP */
+
+
+#if defined(NEED_STRMODE) && !defined(HAVE_STRMODE)
+
+void strmode(register mode_t, register char *);
+
+#endif /* NEED_STRMODE && ! HAVE_STRMODE */
+
+
+#if defined(NEED_STRRSTR) && !defined(HAVE_STRRSTR)
+
+char *strrstr(char *, char *);
+
+#endif /* NEED_STRRSTR && ! HAVE_STRRSTR */
+
+
+#ifdef NEED_STRSEP
+
+# ifdef HAVE_STRSEP
+#  define _LINUX_SOURCE_COMPAT		/* needed on AIX 4.3.3 */
+# else
+
+char *strsep(register char **, register const char *);
+
+# endif
+
+#endif /* NEED_STRSEP */
+
+
diff --git a/libtar/config.h b/libtar/config.h
new file mode 100644
index 0000000..c658020
--- /dev/null
+++ b/libtar/config.h
@@ -0,0 +1,188 @@
+/* config.h.  Generated by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if your system has a working basename */
+/* #undef HAVE_BASENAME */
+
+/* Define to 1 if you have the <ctype.h> header file. */
+/* #undef HAVE_CTYPE_H */
+
+/* Define to 1 if the system has the type `dev_t'. */
+#define HAVE_DEV_T 1
+
+/* Define if your system has a working dirname */
+/* #undef HAVE_DIRNAME */
+
+/* Define to 1 if your system has a working POSIX `fnmatch' function. */
+#define HAVE_FNMATCH 1
+
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#define HAVE_FNMATCH_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#define HAVE_LIBGEN_H 1
+
+/* Define to 1 if you have the `z' library (-lz). */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if the system has the type `major_t'. */
+/* #undef HAVE_MAJOR_T */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if the system has the type `minor_t'. */
+/* #undef HAVE_MINOR_T */
+
+/* Define to 1 if the system has the type `nlink_t'. */
+#define HAVE_NLINK_T 1
+
+/* Define if your system has a working snprintf */
+#define HAVE_SNPRINTF 1
+
+/* Define to 1 if the system has the type `socklen_t'. */
+#define HAVE_SOCKLEN_T 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the strdup function */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the strlcpy function */
+/* #undef HAVE_STRLCPY */
+
+/* Define if you have the strmode function */
+/* #undef HAVE_STRMODE */
+
+/* Define if you have the strsep function */
+#define HAVE_STRSEP 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if the system has the type `uint64_t'. */
+#define HAVE_UINT64_T 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+   */
+/* #undef MAJOR_IN_MKDEV */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+   <sysmacros.h>. */
+/* #undef MAJOR_IN_SYSMACROS */
+
+/* Define as 1 if makedev expects three arguments */
+/* #undef MAKEDEV_THREE_ARGS */
+
+/* Define if you want to use the basename function */
+#define NEED_BASENAME 1
+
+/* Define if you want to use the dirname function */
+#define NEED_DIRNAME 1
+
+/* Define if you want to use the fnmatch function */
+#define NEED_FNMATCH 1
+
+/* Define if you want to use the makedev function */
+#define NEED_MAKEDEV 1
+
+/* Define if you want to use the snprintf function */
+#define NEED_SNPRINTF 1
+
+/* Define if you want to use the strdup function */
+#define NEED_STRDUP 1
+
+/* Define if you want to use the strlcpy function */
+#define NEED_STRLCPY 1
+
+/* Define if you want to use the strmode function */
+#define NEED_STRMODE 1
+
+/* Define if you want to use the strsep function */
+#define NEED_STRSEP 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libtar"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libtar 1.2.11"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libtar"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.2.11"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+/* # undef _ALL_SOURCE */
+#endif
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `unsigned long' if not defined in system header files. */
+/* #undef dev_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define to `unsigned int' if not defined in system header files. */
+#define major_t unsigned int
+
+/* Define to `unsigned int' if not defined in system header files. */
+#define minor_t unsigned int
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `unsigned short' if not defined in system header files. */
+/* #undef nlink_t */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `unsigned long' if not defined in system header files. */
+/* #undef socklen_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define to `long long' if not defined in system header files. */
+/* #undef uint64_t */
diff --git a/libtar/decode.c b/libtar/decode.c
new file mode 100644
index 0000000..383306d
--- /dev/null
+++ b/libtar/decode.c
@@ -0,0 +1,122 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  decode.c - libtar code to decode tar header blocks
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+
+/* determine full path name */
+char *
+th_get_pathname(TAR *t)
+{
+	char filename[MAXPATHLEN];
+
+	if (t->th_buf.gnu_longname) {
+		printf("returning gnu longname\n");
+		return t->th_buf.gnu_longname;
+	}
+
+	if (t->th_buf.prefix[0] != '\0')
+	{
+		snprintf(filename, sizeof(filename), "%.155s/%.100s",
+			 t->th_buf.prefix, t->th_buf.name);
+		return strdup(filename);
+	}
+
+	snprintf(filename, sizeof(filename), "%.100s", t->th_buf.name);
+	return strdup(filename);
+}
+
+
+uid_t
+th_get_uid(TAR *t)
+{
+	int uid;
+	struct passwd *pw;
+
+	pw = getpwnam(t->th_buf.uname);
+	if (pw != NULL)
+		return pw->pw_uid;
+
+	/* if the password entry doesn't exist */
+	sscanf(t->th_buf.uid, "%o", &uid);
+	return uid;
+}
+
+
+gid_t
+th_get_gid(TAR *t)
+{
+	int gid;
+	struct group *gr;
+
+	gr = getgrnam(t->th_buf.gname);
+	if (gr != NULL)
+		return gr->gr_gid;
+
+	/* if the group entry doesn't exist */
+	sscanf(t->th_buf.gid, "%o", &gid);
+	return gid;
+}
+
+
+mode_t
+th_get_mode(TAR *t)
+{
+	mode_t mode;
+
+	mode = (mode_t)oct_to_int(t->th_buf.mode);
+	if (! (mode & S_IFMT))
+	{
+		switch (t->th_buf.typeflag)
+		{
+		case SYMTYPE:
+			mode |= S_IFLNK;
+			break;
+		case CHRTYPE:
+			mode |= S_IFCHR;
+			break;
+		case BLKTYPE:
+			mode |= S_IFBLK;
+			break;
+		case DIRTYPE:
+			mode |= S_IFDIR;
+			break;
+		case FIFOTYPE:
+			mode |= S_IFIFO;
+			break;
+		case AREGTYPE:
+			if (t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
+			{
+				mode |= S_IFDIR;
+				break;
+			}
+			/* FALLTHROUGH */
+		case LNKTYPE:
+		case REGTYPE:
+		default:
+			mode |= S_IFREG;
+		}
+	}
+
+	return mode;
+}
+
+
diff --git a/libtar/dirname.c b/libtar/dirname.c
new file mode 100644
index 0000000..986db4a
--- /dev/null
+++ b/libtar/dirname.c
@@ -0,0 +1,77 @@
+/*	$OpenBSD: dirname.c,v 1.4 1999/05/30 17:10:30 espie Exp $	*/
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$OpenBSD: dirname.c,v 1.4 1999/05/30 17:10:30 espie Exp $";
+#endif /* not lint */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+
+char *
+openbsd_dirname(path)
+	const char *path;
+{
+	static char bname[MAXPATHLEN];
+	register const char *endp;
+
+	/* Empty or NULL string gets treated as "." */
+	if (path == NULL || *path == '\0') {
+		(void)strcpy(bname, ".");
+		return(bname);
+	}
+
+	/* Strip trailing slashes */
+	endp = path + strlen(path) - 1;
+	while (endp > path && *endp == '/')
+		endp--;
+
+	/* Find the start of the dir */
+	while (endp > path && *endp != '/')
+		endp--;
+
+	/* Either the dir is "/" or there are no slashes */
+	if (endp == path) {
+		(void)strcpy(bname, *endp == '/' ? "/" : ".");
+		return(bname);
+	} else {
+		do {
+			endp--;
+		} while (endp > path && *endp == '/');
+	}
+
+	if (endp - path + 1 > sizeof(bname)) {
+		errno = ENAMETOOLONG;
+		return(NULL);
+	}
+	(void)strncpy(bname, path, endp - path + 1);
+	bname[endp - path + 1] = '\0';
+	return(bname);
+}
diff --git a/libtar/encode.c b/libtar/encode.c
new file mode 100644
index 0000000..662eff5
--- /dev/null
+++ b/libtar/encode.c
@@ -0,0 +1,213 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  encode.c - libtar code to encode tar header blocks
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+
+/* magic, version, and checksum */
+void
+th_finish(TAR *t)
+{
+	if (t->options & TAR_GNU)
+	{
+		/* we're aiming for this result, but must do it in
+		 * two calls to avoid FORTIFY segfaults on some Linux
+		 * systems:
+		 *      strncpy(t->th_buf.magic, "ustar  ", 8);
+		 */
+		strncpy(t->th_buf.magic, "ustar ", 6);
+		strncpy(t->th_buf.version, " ", 2);
+	}
+	else
+	{
+		strncpy(t->th_buf.version, TVERSION, TVERSLEN);
+		strncpy(t->th_buf.magic, TMAGIC, TMAGLEN);
+	}
+
+	int_to_oct(th_crc_calc(t), t->th_buf.chksum, 8);
+}
+
+
+/* map a file mode to a typeflag */
+void
+th_set_type(TAR *t, mode_t mode)
+{
+	if (S_ISLNK(mode))
+		t->th_buf.typeflag = SYMTYPE;
+	if (S_ISREG(mode))
+		t->th_buf.typeflag = REGTYPE;
+	if (S_ISDIR(mode))
+		t->th_buf.typeflag = DIRTYPE;
+	if (S_ISCHR(mode))
+		t->th_buf.typeflag = CHRTYPE;
+	if (S_ISBLK(mode))
+		t->th_buf.typeflag = BLKTYPE;
+	if (S_ISFIFO(mode) || S_ISSOCK(mode))
+		t->th_buf.typeflag = FIFOTYPE;
+}
+
+
+/* encode file path */
+void
+th_set_path(TAR *t, char *pathname)
+{
+	char suffix[2] = "";
+	char *tmp;
+
+#ifdef DEBUG
+	printf("in th_set_path(th, pathname=\"%s\")\n", pathname);
+#endif
+
+	if (t->th_buf.gnu_longname != NULL)
+		free(t->th_buf.gnu_longname);
+	t->th_buf.gnu_longname = NULL;
+
+	if (pathname[strlen(pathname) - 1] != '/' && TH_ISDIR(t))
+		strcpy(suffix, "/");
+
+	if (strlen(pathname) > T_NAMELEN-1 && (t->options & TAR_GNU))
+	{
+		/* GNU-style long name */
+		t->th_buf.gnu_longname = strdup(pathname);
+		strncpy(t->th_buf.name, t->th_buf.gnu_longname, T_NAMELEN);
+	}
+	else if (strlen(pathname) > T_NAMELEN)
+	{
+		/* POSIX-style prefix field */
+		tmp = strchr(&(pathname[strlen(pathname) - T_NAMELEN - 1]), '/');
+		if (tmp == NULL)
+		{
+			printf("!!! '/' not found in \"%s\"\n", pathname);
+			return;
+		}
+		snprintf(t->th_buf.name, 100, "%s%s", &(tmp[1]), suffix);
+		snprintf(t->th_buf.prefix,
+			 ((tmp - pathname + 1) <
+			  155 ? (tmp - pathname + 1) : 155), "%s", pathname);
+	}
+	else
+		/* classic tar format */
+		snprintf(t->th_buf.name, 100, "%s%s", pathname, suffix);
+
+#ifdef DEBUG
+	puts("returning from th_set_path()...");
+#endif
+}
+
+
+/* encode link path */
+void
+th_set_link(TAR *t, char *linkname)
+{
+#ifdef DEBUG
+	printf("==> th_set_link(th, linkname=\"%s\")\n", linkname);
+#endif
+
+	if (strlen(linkname) > T_NAMELEN-1 && (t->options & TAR_GNU))
+	{
+		/* GNU longlink format */
+		t->th_buf.gnu_longlink = strdup(linkname);
+		strcpy(t->th_buf.linkname, "././@LongLink");
+	}
+	else
+	{
+		/* classic tar format */
+		strlcpy(t->th_buf.linkname, linkname,
+			sizeof(t->th_buf.linkname));
+		if (t->th_buf.gnu_longlink != NULL)
+			free(t->th_buf.gnu_longlink);
+		t->th_buf.gnu_longlink = NULL;
+	}
+}
+
+
+/* encode device info */
+void
+th_set_device(TAR *t, dev_t device)
+{
+#ifdef DEBUG
+	printf("th_set_device(): major = %d, minor = %d\n",
+	       major(device), minor(device));
+#endif
+	int_to_oct(major(device), t->th_buf.devmajor, 8);
+	int_to_oct(minor(device), t->th_buf.devminor, 8);
+}
+
+
+/* encode user info */
+void
+th_set_user(TAR *t, uid_t uid)
+{
+	struct passwd *pw;
+
+	pw = getpwuid(uid);
+	if (pw != NULL)
+		strlcpy(t->th_buf.uname, pw->pw_name, sizeof(t->th_buf.uname));
+
+	int_to_oct(uid, t->th_buf.uid, 8);
+}
+
+
+/* encode group info */
+void
+th_set_group(TAR *t, gid_t gid)
+{
+	struct group *gr;
+
+	gr = getgrgid(gid);
+	if (gr != NULL)
+		strlcpy(t->th_buf.gname, gr->gr_name, sizeof(t->th_buf.gname));
+
+	int_to_oct(gid, t->th_buf.gid, 8);
+}
+
+
+/* encode file mode */
+void
+th_set_mode(TAR *t, mode_t fmode)
+{
+	if (S_ISSOCK(fmode))
+	{
+		fmode &= ~S_IFSOCK;
+		fmode |= S_IFIFO;
+	}
+	int_to_oct(fmode, (t)->th_buf.mode, 8);
+}
+
+
+void
+th_set_from_stat(TAR *t, struct stat *s)
+{
+	th_set_type(t, s->st_mode);
+	if (S_ISCHR(s->st_mode) || S_ISBLK(s->st_mode))
+		th_set_device(t, s->st_rdev);
+	th_set_user(t, s->st_uid);
+	th_set_group(t, s->st_gid);
+	th_set_mode(t, s->st_mode);
+	th_set_mtime(t, s->st_mtime);
+	if (S_ISREG(s->st_mode))
+		th_set_size(t, s->st_size);
+	else
+		th_set_size(t, 0);
+}
+
+
diff --git a/libtar/extract.c b/libtar/extract.c
new file mode 100644
index 0000000..e605aca
--- /dev/null
+++ b/libtar/extract.c
@@ -0,0 +1,569 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  extract.c - libtar code to extract a file from a tar archive
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+
+#define DEBUG
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#define DEBUG
+
+static int
+tar_set_file_perms(TAR *t, char *realname)
+{
+	mode_t mode;
+	uid_t uid;
+	gid_t gid;
+	struct utimbuf ut;
+	char *filename;
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+	uid = th_get_uid(t);
+	gid = th_get_gid(t);
+	ut.modtime = ut.actime = th_get_mtime(t);
+
+#ifdef DEBUG
+	printf("   ==> setting perms: %s (mode %04o, uid %d, gid %d)\n",
+	       filename, mode, uid, gid);
+#endif
+
+	/* change owner/group */
+	if (geteuid() == 0)
+#ifdef HAVE_LCHOWN
+		if (lchown(filename, uid, gid) == -1)
+		{
+# ifdef DEBUG
+			fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
+				filename, uid, gid, strerror(errno));
+# endif
+#else /* ! HAVE_LCHOWN */
+		if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
+		{
+# ifdef DEBUG
+			fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
+				filename, uid, gid, strerror(errno));
+# endif
+#endif /* HAVE_LCHOWN */
+			return -1;
+		}
+
+	/* change access/modification time */
+	if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
+	{
+#ifdef DEBUG
+		perror("utime()");
+#endif
+		return -1;
+	}
+
+	/* change permissions */
+	if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
+	{
+#ifdef DEBUG
+		perror("chmod()");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* switchboard */
+int
+tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd)
+{
+	int i;
+	char *lnp;
+	int pathname_len;
+	int realname_len;
+
+	if (t->options & TAR_NOOVERWRITE)
+	{
+		struct stat s;
+
+		if (lstat(realname, &s) == 0 || errno != ENOENT)
+		{
+			errno = EEXIST;
+			return -1;
+		}
+	}
+
+	if (TH_ISDIR(t))
+	{
+		printf("dir\n");
+		i = tar_extract_dir(t, realname);
+		if (i == 1)
+			i = 0;
+	}
+	else if (TH_ISLNK(t)) {
+		printf("link\n");
+		i = tar_extract_hardlink(t, realname, prefix);
+	}
+	else if (TH_ISSYM(t)) {
+		printf("sym\n");
+		i = tar_extract_symlink(t, realname);
+	}
+	else if (TH_ISCHR(t)) {
+		printf("chr\n");
+		i = tar_extract_chardev(t, realname);
+	}
+	else if (TH_ISBLK(t)) {
+		printf("blk\n");
+		i = tar_extract_blockdev(t, realname);
+	}
+	else if (TH_ISFIFO(t)) {
+		printf("fifo\n");
+		i = tar_extract_fifo(t, realname);
+	}
+	else /* if (TH_ISREG(t)) */ {
+		printf("reg\n");
+		i = tar_extract_regfile(t, realname, progress_fd);
+	}
+
+	if (i != 0) {
+		printf("FAILED RESTORE OF FILE i: %s\n", realname);
+		return i;
+	}
+
+	i = tar_set_file_perms(t, realname);
+	if (i != 0) {
+		printf("FAILED SETTING PERMS: %d\n", i);
+		return i;
+	}
+
+#ifdef HAVE_SELINUX
+	if((t->options & TAR_STORE_SELINUX) && t->th_buf.selinux_context != NULL)
+	{
+#ifdef DEBUG
+		printf("   Restoring SELinux context %s to file %s\n", t->th_buf.selinux_context, realname);
+#endif
+		if (lsetfilecon(realname, t->th_buf.selinux_context) < 0) {
+			fprintf(stderr, "Failed to restore SELinux context %s!\n", strerror(errno));
+		}
+	}
+#endif
+
+/*
+	pathname_len = strlen(th_get_pathname(t)) + 1;
+	realname_len = strlen(realname) + 1;
+	lnp = (char *)calloc(1, pathname_len + realname_len);
+	if (lnp == NULL)
+		return -1;
+	strcpy(&lnp[0], th_get_pathname(t));
+	strcpy(&lnp[pathname_len], realname);
+#ifdef DEBUG
+	printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
+	       "value=\"%s\"\n", th_get_pathname(t), realname);
+#endif
+	if (libtar_hash_add(t->h, lnp) != 0)
+		return -1;
+	free(lnp);
+*/
+	return 0;
+}
+
+
+/* extract regular file */
+int
+tar_extract_regfile(TAR *t, char *realname, const int *progress_fd)
+{
+	//mode_t mode;
+	size_t size;
+	//uid_t uid;
+	//gid_t gid;
+	int fdout;
+	int i, k;
+	char buf[T_BLOCKSIZE];
+	char *filename;
+
+	fflush(NULL);
+#ifdef DEBUG
+	printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
+	       realname);
+#endif
+
+	if (!TH_ISREG(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	//mode = th_get_mode(t);
+	size = th_get_size(t);
+	//uid = th_get_uid(t);
+	//gid = th_get_gid(t);
+
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+
+#ifdef DEBUG
+	//printf("  ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
+	//       filename, mode, uid, gid, size);
+	printf("  ==> extracting: %s (file size %d bytes)\n",
+	       filename, size);
+#endif
+	fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
+#ifdef O_BINARY
+		     | O_BINARY
+#endif
+		    , 0666);
+	if (fdout == -1)
+	{
+#ifdef DEBUG
+		perror("open()");
+#endif
+		return -1;
+	}
+
+#if 0
+	/* change the owner.  (will only work if run as root) */
+	if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
+	{
+#ifdef DEBUG
+		perror("fchown()");
+#endif
+		return -1;
+	}
+
+	/* make sure the mode isn't inheritted from a file we're overwriting */
+	if (fchmod(fdout, mode & 07777) == -1)
+	{
+#ifdef DEBUG
+		perror("fchmod()");
+#endif
+		return -1;
+	}
+#endif
+
+	/* extract the file */
+	for (i = size; i > 0; i -= T_BLOCKSIZE)
+	{
+		k = tar_block_read(t, buf);
+		if (k != T_BLOCKSIZE)
+		{
+			if (k != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* write block to output file */
+		if (write(fdout, buf,
+			  ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
+			return -1;
+	}
+
+	/* close output file */
+	if (close(fdout) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("### done extracting %s\n", filename);
+#endif
+
+	if (*progress_fd != 0) {
+		unsigned long long file_size = (unsigned long long)(size);
+		write(*progress_fd, &file_size, sizeof(file_size));
+	}
+
+	return 0;
+}
+
+
+/* skip regfile */
+int
+tar_skip_regfile(TAR *t)
+{
+	int i, k;
+	size_t size;
+	char buf[T_BLOCKSIZE];
+
+	if (!TH_ISREG(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	size = th_get_size(t);
+	for (i = size; i > 0; i -= T_BLOCKSIZE)
+	{
+		k = tar_block_read(t, buf);
+		if (k != T_BLOCKSIZE)
+		{
+			if (k != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* hardlink */
+int
+tar_extract_hardlink(TAR * t, char *realname, char *prefix)
+{
+	char *filename;
+	char *linktgt = NULL;
+	char *lnp;
+	libtar_hashptr_t hp;
+
+	if (!TH_ISLNK(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+	libtar_hashptr_reset(&hp);
+	if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
+			       (libtar_matchfunc_t)libtar_str_match) != 0)
+	{
+		lnp = (char *)libtar_hashptr_data(&hp);
+		linktgt = &lnp[strlen(lnp) + 1];
+	}
+	else
+		linktgt = th_get_linkname(t);
+	char *newtgt = strdup(linktgt);
+	sprintf(linktgt, "%s/%s", prefix, newtgt);
+#ifdef DEBUG
+	printf("  ==> extracting: %s (link to %s)\n", filename, linktgt);
+#endif
+	if (link(linktgt, filename) == -1)
+	{
+#ifdef DEBUG
+		perror("link()");
+#endif
+		printf("Failed restore of hardlink '%s' but returning as if nothing bad happened anyway\n", filename);
+		return 0; // Used to be -1
+	}
+
+	return 0;
+}
+
+
+/* symlink */
+int
+tar_extract_symlink(TAR *t, char *realname)
+{
+	char *filename;
+
+	if (!TH_ISSYM(t))
+	{
+		printf("not a sym\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	printf("file: %s\n", filename);
+	if (mkdirhier(dirname(filename)) == -1) {
+		printf("mkdirhier\n");
+		return -1;
+	}
+
+	if (unlink(filename) == -1 && errno != ENOENT) {
+		printf("unlink\n");
+		return -1;
+	}
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (symlink to %s)\n",
+	       filename, th_get_linkname(t));
+#endif
+	if (symlink(th_get_linkname(t), filename) == -1)
+	{
+#ifdef DEBUG
+		perror("symlink()");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* character device */
+int
+tar_extract_chardev(TAR *t, char *realname)
+{
+	mode_t mode;
+	unsigned long devmaj, devmin;
+	char *filename;
+
+	if (!TH_ISCHR(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+	devmaj = th_get_devmajor(t);
+	devmin = th_get_devminor(t);
+
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (character device %ld,%ld)\n",
+	       filename, devmaj, devmin);
+#endif
+	if (mknod(filename, mode | S_IFCHR,
+		  compat_makedev(devmaj, devmin)) == -1)
+	{
+#ifdef DEBUG
+		printf("mknod() failed, returning good anyway");
+#endif
+		return 0;
+	}
+
+	return 0;
+}
+
+
+/* block device */
+int
+tar_extract_blockdev(TAR *t, char *realname)
+{
+	mode_t mode;
+	unsigned long devmaj, devmin;
+	char *filename;
+
+	if (!TH_ISBLK(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+	devmaj = th_get_devmajor(t);
+	devmin = th_get_devminor(t);
+
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (block device %ld,%ld)\n",
+	       filename, devmaj, devmin);
+#endif
+	if (mknod(filename, mode | S_IFBLK,
+		  compat_makedev(devmaj, devmin)) == -1)
+	{
+#ifdef DEBUG
+		printf("mknod() failed but returning anyway");
+#endif
+		return 0;
+	}
+
+	return 0;
+}
+
+
+/* directory */
+int
+tar_extract_dir(TAR *t, char *realname)
+{
+	mode_t mode;
+	char *filename;
+	if (!TH_ISDIR(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+
+	if (mkdirhier(dirname(filename)) == -1) {
+		printf("tar_extract_dir mkdirhier failed\n");
+		return -1;
+	}
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (mode %04o, directory)\n", filename,
+	       mode);
+#endif
+	if (mkdir(filename, mode) == -1)
+	{
+		if (errno == EEXIST)
+		{
+#ifdef DEBUG
+			printf("  *** using existing directory");
+#endif
+		}
+		else
+		{
+#ifdef DEBUG
+			perror("mkdir()");
+#endif
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* FIFO */
+int
+tar_extract_fifo(TAR *t, char *realname)
+{
+	mode_t mode;
+	char *filename;
+
+	if (!TH_ISFIFO(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (fifo)\n", filename);
+#endif
+	if (mkfifo(filename, mode) == -1)
+	{
+#ifdef DEBUG
+		perror("mkfifo()");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+
diff --git a/libtar/fnmatch.c b/libtar/fnmatch.c
new file mode 100644
index 0000000..fe75f0e
--- /dev/null
+++ b/libtar/fnmatch.c
@@ -0,0 +1,237 @@
+/*	$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $	*/
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)fnmatch.c	8.2 (Berkeley) 4/16/94";
+#else
+static char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+# include <ctype.h>
+#endif
+
+#include <compat.h>
+
+
+#define	EOS	'\0'
+
+#define	RANGE_MATCH	1
+#define	RANGE_NOMATCH	0
+#define	RANGE_ERROR	(-1)
+
+#ifdef NO_IBM_COMPILER_HORKAGE
+static int rangematch (const char *, char, int, char **);
+#else
+static int rangematch ();
+#endif
+
+int
+fnmatch(pattern, string, flags)
+	const char *pattern, *string;
+	int flags;
+{
+	const char *stringstart;
+	char *newp;
+	char c, test;
+
+	for (stringstart = string;;)
+		switch (c = *pattern++) {
+		case EOS:
+			if ((flags & FNM_LEADING_DIR) && *string == '/')
+				return (0);
+			return (*string == EOS ? 0 : FNM_NOMATCH);
+		case '?':
+			if (*string == EOS)
+				return (FNM_NOMATCH);
+			if (*string == '/' && (flags & FNM_PATHNAME))
+				return (FNM_NOMATCH);
+			if (*string == '.' && (flags & FNM_PERIOD) &&
+			    (string == stringstart ||
+			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+				return (FNM_NOMATCH);
+			++string;
+			break;
+		case '*':
+			c = *pattern;
+			/* Collapse multiple stars. */
+			while (c == '*')
+				c = *++pattern;
+
+			if (*string == '.' && (flags & FNM_PERIOD) &&
+			    (string == stringstart ||
+			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+				return (FNM_NOMATCH);
+
+			/* Optimize for pattern with * at end or before /. */
+			if (c == EOS) {
+				if (flags & FNM_PATHNAME)
+					return ((flags & FNM_LEADING_DIR) ||
+					    strchr(string, '/') == NULL ?
+					    0 : FNM_NOMATCH);
+				else
+					return (0);
+			} else if (c == '/' && (flags & FNM_PATHNAME)) {
+				if ((string = strchr(string, '/')) == NULL)
+					return (FNM_NOMATCH);
+				break;
+			}
+
+			/* General case, use recursion. */
+			while ((test = *string) != EOS) {
+				if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
+					return (0);
+				if (test == '/' && (flags & FNM_PATHNAME))
+					break;
+				++string;
+			}
+			return (FNM_NOMATCH);
+		case '[':
+			if (*string == EOS)
+				return (FNM_NOMATCH);
+			if (*string == '/' && (flags & FNM_PATHNAME))
+				return (FNM_NOMATCH);
+			if (*string == '.' && (flags & FNM_PERIOD) &&
+			    (string == stringstart ||
+			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+				return (FNM_NOMATCH);
+
+			switch (rangematch(pattern, *string, flags, &newp)) {
+			case RANGE_ERROR:
+				/* not a good range, treat as normal text */
+				goto normal;
+			case RANGE_MATCH:
+				pattern = newp;
+				break;
+			case RANGE_NOMATCH:
+				return (FNM_NOMATCH);
+			}
+			++string;
+			break;
+		case '\\':
+			if (!(flags & FNM_NOESCAPE)) {
+				if ((c = *pattern++) == EOS) {
+					c = '\\';
+					--pattern;
+				}
+			}
+			/* FALLTHROUGH */
+		default:
+		normal:
+			if (c != *string && !((flags & FNM_CASEFOLD) &&
+				 (tolower((unsigned char)c) ==
+				 tolower((unsigned char)*string))))
+				return (FNM_NOMATCH);
+			++string;
+			break;
+		}
+	/* NOTREACHED */
+}
+
+static int
+rangematch(pattern, test, flags, newp)
+	const char *pattern;
+	char test;
+	int flags;
+	char **newp;
+{
+	int negate, ok;
+	char c, c2;
+
+	/*
+	 * A bracket expression starting with an unquoted circumflex
+	 * character produces unspecified results (IEEE 1003.2-1992,
+	 * 3.13.2).  This implementation treats it like '!', for
+	 * consistency with the regular expression syntax.
+	 * J.T. Conklin (conklin@ngai.kaleida.com)
+	 */
+	if ((negate = (*pattern == '!' || *pattern == '^')))
+		++pattern;
+
+	if (flags & FNM_CASEFOLD)
+		test = tolower((unsigned char)test);
+
+	/*
+	 * A right bracket shall lose its special meaning and represent
+	 * itself in a bracket expression if it occurs first in the list.
+	 * -- POSIX.2 2.8.3.2
+	 */
+	ok = 0;
+	c = *pattern++;
+	do {
+		if (c == '\\' && !(flags & FNM_NOESCAPE))
+			c = *pattern++;
+		if (c == EOS)
+			return (RANGE_ERROR);
+		if (c == '/' && (flags & FNM_PATHNAME))
+			return (RANGE_NOMATCH);
+		if ((flags & FNM_CASEFOLD))
+			c = tolower((unsigned char)c);
+		if (*pattern == '-'
+		    && (c2 = *(pattern+1)) != EOS && c2 != ']') {
+			pattern += 2;
+			if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+				c2 = *pattern++;
+			if (c2 == EOS)
+				return (RANGE_ERROR);
+			if (flags & FNM_CASEFOLD)
+				c2 = tolower((unsigned char)c2);
+			if (c <= test && test <= c2)
+				ok = 1;
+		} else if (c == test)
+			ok = 1;
+	} while ((c = *pattern++) != ']');
+
+	*newp = (char *)pattern;
+	return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
+}
diff --git a/libtar/gethostbyname_r.c b/libtar/gethostbyname_r.c
new file mode 100644
index 0000000..5264b84
--- /dev/null
+++ b/libtar/gethostbyname_r.c
@@ -0,0 +1,41 @@
+/*
+**  Copyright 2002 University of Illinois Board of Trustees
+**  Copyright 2002 Mark D. Roth
+**  All rights reserved.
+**
+**  gethostbyname_r.c - gethostbyname_r() function for compatibility library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+
+int
+compat_gethostbyname_r(const char *name, struct hostent *hp,
+		       char *buf, size_t buflen,
+		       struct hostent **hpp, int *herr)
+{
+#if GETHOSTBYNAME_R_NUM_ARGS == 5
+	*hpp = gethostbyname_r(name, hp, buf, buflen, herr);
+
+	if (*hpp == NULL)
+		return -1;
+	return 0;
+#elif GETHOSTBYNAME_R_NUM_ARGS == 3
+	struct hostent_data hdata;
+
+	if (gethostbyname_r(name, hp, &hdata) == -1)
+		return -1;
+	*hpp = hp;
+	return 0;
+#endif /* GETHOSTBYNAME_R_NUM_ARGS == 5 */
+}
+
+
diff --git a/libtar/gethostname.c b/libtar/gethostname.c
new file mode 100644
index 0000000..1abaae1
--- /dev/null
+++ b/libtar/gethostname.c
@@ -0,0 +1,36 @@
+/* gethostname.c: minimal substitute for missing gethostname() function
+ * created 2000-Mar-02 jmk
+ * requires SVR4 uname() and -lc
+ *
+ * by Jim Knoble <jmknoble@pobox.com>
+ * Copyright ? 2000 Jim Knoble
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This software is provided "as is", without warranty of any kind,
+ * express or implied, including but not limited to the warranties of
+ * merchantability, fitness for a particular purpose and
+ * noninfringement. In no event shall the author(s) be liable for any
+ * claim, damages or other liability, whether in an action of contract,
+ * tort or otherwise, arising from, out of or in connection with the
+ * software or the use or other dealings in the software.
+ */
+
+#include <string.h>
+#include <sys/utsname.h>
+
+int gethostname(char *name, size_t len)
+{
+   struct utsname u;
+   int status = uname(&u);
+   if (-1 != status) {
+      strncpy(name, u.nodename, len);
+      name[len - 1] = '\0';
+   }
+   return(status);
+}
+
diff --git a/libtar/getservbyname_r.c b/libtar/getservbyname_r.c
new file mode 100644
index 0000000..e386bc9
--- /dev/null
+++ b/libtar/getservbyname_r.c
@@ -0,0 +1,41 @@
+/*
+**  Copyright 2002 University of Illinois Board of Trustees
+**  Copyright 2002 Mark D. Roth
+**  All rights reserved.
+**
+**  getservbyname_r.c - getservbyname_r() function for compatibility library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+
+int
+compat_getservbyname_r(const char *name, const char *proto,
+		       struct servent *sp, char *buf, size_t buflen,
+		       struct servent **spp)
+{
+#if GETSERVBYNAME_R_NUM_ARGS == 5
+	*spp = getservbyname_r(name, proto, sp, buf, buflen);
+
+	if (*spp == NULL)
+		return -1;
+	return 0;
+#elif GETSERVBYNAME_R_NUM_ARGS == 4
+	struct servent_data sdata;
+
+	if (getservbyname_r(name, proto, sp, &sdata) == -1)
+		return -1;
+	*spp = sp;
+	return 0;
+#endif /* GETSERVBYNAME_R_NUM_ARGS == 5 */
+}
+
+
diff --git a/libtar/glob.c b/libtar/glob.c
new file mode 100644
index 0000000..9ee235a
--- /dev/null
+++ b/libtar/glob.c
@@ -0,0 +1,873 @@
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)glob.c	8.3 (Berkeley) 10/13/93";
+#else
+static char rcsid[] = "$OpenBSD: glob.c,v 1.8 1998/08/14 21:39:30 deraadt Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * glob(3) -- a superset of the one defined in POSIX 1003.2.
+ *
+ * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
+ *
+ * Optional extra services, controlled by flags not defined by POSIX:
+ *
+ * GLOB_QUOTE:
+ *	Escaping convention: \ inhibits any special meaning the following
+ *	character might have (except \ at end of string is retained).
+ * GLOB_MAGCHAR:
+ *	Set in gl_flags if pattern contained a globbing character.
+ * GLOB_NOMAGIC:
+ *	Same as GLOB_NOCHECK, but it will only append pattern if it did
+ *	not contain any magic characters.  [Used in csh style globbing]
+ * GLOB_ALTDIRFUNC:
+ *	Use alternately specified directory access functions.
+ * GLOB_TILDE:
+ *	expand ~user/foo to the /home/dir/of/user/foo
+ * GLOB_BRACE:
+ *	expand {1,2}{a,b} to 1a 1b 2a 2b
+ * gl_matchc:
+ *	Number of matches in the current invocation of glob.
+ */
+
+#include <config.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <compat.h>
+
+
+#define	DOLLAR		'$'
+#define	DOT		'.'
+#define	EOS		'\0'
+#define	LBRACKET	'['
+#define	NOT		'!'
+#define	QUESTION	'?'
+#define	QUOTE		'\\'
+#define	RANGE		'-'
+#define	RBRACKET	']'
+#define	SEP		'/'
+#define	STAR		'*'
+#define	TILDE		'~'
+#define	UNDERSCORE	'_'
+#define	LBRACE		'{'
+#define	RBRACE		'}'
+#define	SLASH		'/'
+#define	COMMA		','
+
+#ifndef DEBUG
+
+#define	M_QUOTE		0x8000
+#define	M_PROTECT	0x4000
+#define	M_MASK		0xffff
+#define	M_ASCII		0x00ff
+
+typedef u_short Char;
+
+#else
+
+#define	M_QUOTE		0x80
+#define	M_PROTECT	0x40
+#define	M_MASK		0xff
+#define	M_ASCII		0x7f
+
+typedef char Char;
+
+#endif
+
+
+#define	CHAR(c)		((Char)((c)&M_ASCII))
+#define	META(c)		((Char)((c)|M_QUOTE))
+#define	M_ALL		META('*')
+#define	M_END		META(']')
+#define	M_NOT		META('!')
+#define	M_ONE		META('?')
+#define	M_RNG		META('-')
+#define	M_SET		META('[')
+#define	ismeta(c)	(((c)&M_QUOTE) != 0)
+
+
+static int	 compare (const void *, const void *);
+static void	 g_Ctoc (const Char *, char *);
+static int	 g_lstat (Char *, struct stat *, glob_t *);
+static DIR	*g_opendir (Char *, glob_t *);
+static Char	*g_strchr (Char *, int);
+#ifdef notdef
+static Char	*g_strcat (Char *, const Char *);
+#endif
+static int	 g_stat (Char *, struct stat *, glob_t *);
+static int	 glob0 (const Char *, glob_t *);
+static int	 glob1 (Char *, glob_t *);
+static int	 glob2 (Char *, Char *, Char *, glob_t *);
+static int	 glob3 (Char *, Char *, Char *, Char *, glob_t *);
+static int	 globextend (const Char *, glob_t *);
+static const Char *	globtilde (const Char *, Char *, size_t, glob_t *);
+static int	 globexp1 (const Char *, glob_t *);
+static int	 globexp2 (const Char *, const Char *, glob_t *, int *);
+static int	 match (Char *, Char *, Char *);
+#ifdef DEBUG
+static void	 qprintf (const char *, Char *);
+#endif
+
+int
+openbsd_glob(pattern, flags, errfunc, pglob)
+	const char *pattern;
+	int flags, (*errfunc) __P((const char *, int));
+	glob_t *pglob;
+{
+	const u_char *patnext;
+	int c;
+	Char *bufnext, *bufend, patbuf[MAXPATHLEN+1];
+
+	patnext = (u_char *) pattern;
+	if (!(flags & GLOB_APPEND)) {
+		pglob->gl_pathc = 0;
+		pglob->gl_pathv = NULL;
+		if (!(flags & GLOB_DOOFFS))
+			pglob->gl_offs = 0;
+	}
+	pglob->gl_flags = flags & ~GLOB_MAGCHAR;
+	pglob->gl_errfunc = errfunc;
+	pglob->gl_matchc = 0;
+
+	bufnext = patbuf;
+	bufend = bufnext + MAXPATHLEN;
+	if (flags & GLOB_NOESCAPE)
+	    while (bufnext < bufend && (c = *patnext++) != EOS)
+		    *bufnext++ = c;
+	else {
+		/* Protect the quoted characters. */
+		while (bufnext < bufend && (c = *patnext++) != EOS)
+			if (c == QUOTE) {
+				if ((c = *patnext++) == EOS) {
+					c = QUOTE;
+					--patnext;
+				}
+				*bufnext++ = c | M_PROTECT;
+			}
+			else
+				*bufnext++ = c;
+	}
+	*bufnext = EOS;
+
+	if (flags & GLOB_BRACE)
+		return globexp1(patbuf, pglob);
+	else
+		return glob0(patbuf, pglob);
+}
+
+/*
+ * Expand recursively a glob {} pattern. When there is no more expansion
+ * invoke the standard globbing routine to glob the rest of the magic
+ * characters
+ */
+static int globexp1(pattern, pglob)
+	const Char *pattern;
+	glob_t *pglob;
+{
+	const Char* ptr = pattern;
+	int rv;
+
+	/* Protect a single {}, for find(1), like csh */
+	if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
+		return glob0(pattern, pglob);
+
+	while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
+		if (!globexp2(ptr, pattern, pglob, &rv))
+			return rv;
+
+	return glob0(pattern, pglob);
+}
+
+
+/*
+ * Recursive brace globbing helper. Tries to expand a single brace.
+ * If it succeeds then it invokes globexp1 with the new pattern.
+ * If it fails then it tries to glob the rest of the pattern and returns.
+ */
+static int globexp2(ptr, pattern, pglob, rv)
+	const Char *ptr, *pattern;
+	glob_t *pglob;
+	int *rv;
+{
+	int     i;
+	Char   *lm, *ls;
+	const Char *pe, *pm, *pl;
+	Char    patbuf[MAXPATHLEN + 1];
+
+	/* copy part up to the brace */
+	for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
+		continue;
+	ls = lm;
+
+	/* Find the balanced brace */
+	for (i = 0, pe = ++ptr; *pe; pe++)
+		if (*pe == LBRACKET) {
+			/* Ignore everything between [] */
+			for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
+				continue;
+			if (*pe == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pe = pm;
+			}
+		}
+		else if (*pe == LBRACE)
+			i++;
+		else if (*pe == RBRACE) {
+			if (i == 0)
+				break;
+			i--;
+		}
+
+	/* Non matching braces; just glob the pattern */
+	if (i != 0 || *pe == EOS) {
+		*rv = glob0(patbuf, pglob);
+		return 0;
+	}
+
+	for (i = 0, pl = pm = ptr; pm <= pe; pm++)
+		switch (*pm) {
+		case LBRACKET:
+			/* Ignore everything between [] */
+			for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
+				continue;
+			if (*pm == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pm = pl;
+			}
+			break;
+
+		case LBRACE:
+			i++;
+			break;
+
+		case RBRACE:
+			if (i) {
+			    i--;
+			    break;
+			}
+			/* FALLTHROUGH */
+		case COMMA:
+			if (i && *pm == COMMA)
+				break;
+			else {
+				/* Append the current string */
+				for (lm = ls; (pl < pm); *lm++ = *pl++)
+					continue;
+				/*
+				 * Append the rest of the pattern after the
+				 * closing brace
+				 */
+				for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
+					continue;
+
+				/* Expand the current pattern */
+#ifdef DEBUG
+				qprintf("globexp2:", patbuf);
+#endif
+				*rv = globexp1(patbuf, pglob);
+
+				/* move after the comma, to the next string */
+				pl = pm + 1;
+			}
+			break;
+
+		default:
+			break;
+		}
+	*rv = 0;
+	return 0;
+}
+
+
+
+/*
+ * expand tilde from the passwd file.
+ */
+static const Char *
+globtilde(pattern, patbuf, patbuf_len, pglob)
+	const Char *pattern;
+	Char *patbuf;
+	size_t patbuf_len;
+	glob_t *pglob;
+{
+	struct passwd *pwd;
+	char *h;
+	const Char *p;
+	Char *b, *eb;
+
+	if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
+		return pattern;
+
+	/* Copy up to the end of the string or / */
+	eb = &patbuf[patbuf_len - 1];
+	for (p = pattern + 1, h = (char *) patbuf;
+	    h < (char *)eb && *p && *p != SLASH; *h++ = *p++)
+		continue;
+
+	*h = EOS;
+
+	if (((char *) patbuf)[0] == EOS) {
+		/*
+		 * handle a plain ~ or ~/ by expanding $HOME
+		 * first and then trying the password file
+		 */
+#ifdef HAVE_ISSETUGID
+		if (issetugid() != 0 || (h = getenv("HOME")) == NULL) {
+#endif
+			if ((pwd = getpwuid(getuid())) == NULL)
+				return pattern;
+			else
+				h = pwd->pw_dir;
+#ifdef HAVE_ISSETUGID
+		}
+#endif
+	}
+	else {
+		/*
+		 * Expand a ~user
+		 */
+		if ((pwd = getpwnam((char*) patbuf)) == NULL)
+			return pattern;
+		else
+			h = pwd->pw_dir;
+	}
+
+	/* Copy the home directory */
+	for (b = patbuf; b < eb && *h; *b++ = *h++)
+		continue;
+
+	/* Append the rest of the pattern */
+	while (b < eb && (*b++ = *p++) != EOS)
+		continue;
+	*b = EOS;
+
+	return patbuf;
+}
+
+
+/*
+ * The main glob() routine: compiles the pattern (optionally processing
+ * quotes), calls glob1() to do the real pattern matching, and finally
+ * sorts the list (unless unsorted operation is requested).  Returns 0
+ * if things went well, nonzero if errors occurred.  It is not an error
+ * to find no matches.
+ */
+static int
+glob0(pattern, pglob)
+	const Char *pattern;
+	glob_t *pglob;
+{
+	const Char *qpatnext;
+	int c, err, oldpathc;
+	Char *bufnext, patbuf[MAXPATHLEN+1];
+
+	qpatnext = globtilde(pattern, patbuf, sizeof(patbuf) / sizeof(Char),
+	    pglob);
+	oldpathc = pglob->gl_pathc;
+	bufnext = patbuf;
+
+	/* We don't need to check for buffer overflow any more. */
+	while ((c = *qpatnext++) != EOS) {
+		switch (c) {
+		case LBRACKET:
+			c = *qpatnext;
+			if (c == NOT)
+				++qpatnext;
+			if (*qpatnext == EOS ||
+			    g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) {
+				*bufnext++ = LBRACKET;
+				if (c == NOT)
+					--qpatnext;
+				break;
+			}
+			*bufnext++ = M_SET;
+			if (c == NOT)
+				*bufnext++ = M_NOT;
+			c = *qpatnext++;
+			do {
+				*bufnext++ = CHAR(c);
+				if (*qpatnext == RANGE &&
+				    (c = qpatnext[1]) != RBRACKET) {
+					*bufnext++ = M_RNG;
+					*bufnext++ = CHAR(c);
+					qpatnext += 2;
+				}
+			} while ((c = *qpatnext++) != RBRACKET);
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			*bufnext++ = M_END;
+			break;
+		case QUESTION:
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			*bufnext++ = M_ONE;
+			break;
+		case STAR:
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			/* collapse adjacent stars to one,
+			 * to avoid exponential behavior
+			 */
+			if (bufnext == patbuf || bufnext[-1] != M_ALL)
+			    *bufnext++ = M_ALL;
+			break;
+		default:
+			*bufnext++ = CHAR(c);
+			break;
+		}
+	}
+	*bufnext = EOS;
+#ifdef DEBUG
+	qprintf("glob0:", patbuf);
+#endif
+
+	if ((err = glob1(patbuf, pglob)) != 0)
+		return(err);
+
+	/*
+	 * If there was no match we are going to append the pattern
+	 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
+	 * and the pattern did not contain any magic characters
+	 * GLOB_NOMAGIC is there just for compatibility with csh.
+	 */
+	if (pglob->gl_pathc == oldpathc) {
+		if ((pglob->gl_flags & GLOB_NOCHECK) ||
+		    ((pglob->gl_flags & GLOB_NOMAGIC) &&
+		    !(pglob->gl_flags & GLOB_MAGCHAR)))
+			return(globextend(pattern, pglob));
+		else
+			return(GLOB_NOMATCH);
+	}
+	if (!(pglob->gl_flags & GLOB_NOSORT))
+		qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
+		    pglob->gl_pathc - oldpathc, sizeof(char *), compare);
+	return(0);
+}
+
+static int
+compare(p, q)
+	const void *p, *q;
+{
+	return(strcmp(*(char **)p, *(char **)q));
+}
+
+static int
+glob1(pattern, pglob)
+	Char *pattern;
+	glob_t *pglob;
+{
+	Char pathbuf[MAXPATHLEN+1];
+
+	/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
+	if (*pattern == EOS)
+		return(0);
+	return(glob2(pathbuf, pathbuf, pattern, pglob));
+}
+
+/*
+ * The functions glob2 and glob3 are mutually recursive; there is one level
+ * of recursion for each segment in the pattern that contains one or more
+ * meta characters.
+ */
+static int
+glob2(pathbuf, pathend, pattern, pglob)
+	Char *pathbuf, *pathend, *pattern;
+	glob_t *pglob;
+{
+	struct stat sb;
+	Char *p, *q;
+	int anymeta;
+
+	/*
+	 * Loop over pattern segments until end of pattern or until
+	 * segment with meta character found.
+	 */
+	for (anymeta = 0;;) {
+		if (*pattern == EOS) {		/* End of pattern? */
+			*pathend = EOS;
+			if (g_lstat(pathbuf, &sb, pglob))
+				return(0);
+
+			if (((pglob->gl_flags & GLOB_MARK) &&
+			    pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
+			    || (S_ISLNK(sb.st_mode) &&
+			    (g_stat(pathbuf, &sb, pglob) == 0) &&
+			    S_ISDIR(sb.st_mode)))) {
+				*pathend++ = SEP;
+				*pathend = EOS;
+			}
+			++pglob->gl_matchc;
+			return(globextend(pathbuf, pglob));
+		}
+
+		/* Find end of next segment, copy tentatively to pathend. */
+		q = pathend;
+		p = pattern;
+		while (*p != EOS && *p != SEP) {
+			if (ismeta(*p))
+				anymeta = 1;
+			*q++ = *p++;
+		}
+
+		if (!anymeta) {		/* No expansion, do next segment. */
+			pathend = q;
+			pattern = p;
+			while (*pattern == SEP)
+				*pathend++ = *pattern++;
+		} else			/* Need expansion, recurse. */
+			return(glob3(pathbuf, pathend, pattern, p, pglob));
+	}
+	/* NOTREACHED */
+}
+
+static int
+glob3(pathbuf, pathend, pattern, restpattern, pglob)
+	Char *pathbuf, *pathend, *pattern, *restpattern;
+	glob_t *pglob;
+{
+	register struct dirent *dp;
+	DIR *dirp;
+	int err;
+	char buf[MAXPATHLEN];
+
+	/*
+	 * The readdirfunc declaration can't be prototyped, because it is
+	 * assigned, below, to two functions which are prototyped in glob.h
+	 * and dirent.h as taking pointers to differently typed opaque
+	 * structures.
+	 */
+	struct dirent *(*readdirfunc)();
+
+	*pathend = EOS;
+	errno = 0;
+
+	if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
+		/* TODO: don't call for ENOENT or ENOTDIR? */
+		if (pglob->gl_errfunc) {
+			g_Ctoc(pathbuf, buf);
+			if (pglob->gl_errfunc(buf, errno) ||
+			    pglob->gl_flags & GLOB_ERR)
+				return (GLOB_ABORTED);
+		}
+		return(0);
+	}
+
+	err = 0;
+
+	/* Search directory for matching names. */
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		readdirfunc = pglob->gl_readdir;
+	else
+		readdirfunc = readdir;
+	while ((dp = (*readdirfunc)(dirp))) {
+		register u_char *sc;
+		register Char *dc;
+
+		/* Initial DOT must be matched literally. */
+		if (dp->d_name[0] == DOT && *pattern != DOT)
+			continue;
+		for (sc = (u_char *) dp->d_name, dc = pathend;
+		     (*dc++ = *sc++) != EOS;)
+			continue;
+		if (!match(pathend, pattern, restpattern)) {
+			*pathend = EOS;
+			continue;
+		}
+		err = glob2(pathbuf, --dc, restpattern, pglob);
+		if (err)
+			break;
+	}
+
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		(*pglob->gl_closedir)(dirp);
+	else
+		closedir(dirp);
+	return(err);
+}
+
+
+/*
+ * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
+ * add the new item, and update gl_pathc.
+ *
+ * This assumes the BSD realloc, which only copies the block when its size
+ * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
+ * behavior.
+ *
+ * Return 0 if new item added, error code if memory couldn't be allocated.
+ *
+ * Invariant of the glob_t structure:
+ *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
+ *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
+ */
+static int
+globextend(path, pglob)
+	const Char *path;
+	glob_t *pglob;
+{
+	register char **pathv;
+	register int i;
+	u_int newsize;
+	char *copy;
+	const Char *p;
+
+	newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
+	pathv = pglob->gl_pathv ?
+		    realloc((char *)pglob->gl_pathv, newsize) :
+		    malloc(newsize);
+	if (pathv == NULL) {
+		if (pglob->gl_pathv)
+			free(pglob->gl_pathv);
+		return(GLOB_NOSPACE);
+	}
+
+	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
+		/* first time around -- clear initial gl_offs items */
+		pathv += pglob->gl_offs;
+		for (i = pglob->gl_offs; --i >= 0; )
+			*--pathv = NULL;
+	}
+	pglob->gl_pathv = pathv;
+
+	for (p = path; *p++;)
+		continue;
+	if ((copy = malloc(p - path)) != NULL) {
+		g_Ctoc(path, copy);
+		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
+	}
+	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+	return(copy == NULL ? GLOB_NOSPACE : 0);
+}
+
+
+/*
+ * pattern matching function for filenames.  Each occurrence of the *
+ * pattern causes a recursion level.
+ */
+static int
+match(name, pat, patend)
+	register Char *name, *pat, *patend;
+{
+	int ok, negate_range;
+	Char c, k;
+
+	while (pat < patend) {
+		c = *pat++;
+		switch (c & M_MASK) {
+		case M_ALL:
+			if (pat == patend)
+				return(1);
+			do
+			    if (match(name, pat, patend))
+				    return(1);
+			while (*name++ != EOS);
+			return(0);
+		case M_ONE:
+			if (*name++ == EOS)
+				return(0);
+			break;
+		case M_SET:
+			ok = 0;
+			if ((k = *name++) == EOS)
+				return(0);
+			if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
+				++pat;
+			while (((c = *pat++) & M_MASK) != M_END)
+				if ((*pat & M_MASK) == M_RNG) {
+					if (c <= k && k <= pat[1])
+						ok = 1;
+					pat += 2;
+				} else if (c == k)
+					ok = 1;
+			if (ok == negate_range)
+				return(0);
+			break;
+		default:
+			if (*name++ != c)
+				return(0);
+			break;
+		}
+	}
+	return(*name == EOS);
+}
+
+/* Free allocated data belonging to a glob_t structure. */
+void
+openbsd_globfree(pglob)
+	glob_t *pglob;
+{
+	register int i;
+	register char **pp;
+
+	if (pglob->gl_pathv != NULL) {
+		pp = pglob->gl_pathv + pglob->gl_offs;
+		for (i = pglob->gl_pathc; i--; ++pp)
+			if (*pp)
+				free(*pp);
+		free(pglob->gl_pathv);
+	}
+}
+
+static DIR *
+g_opendir(str, pglob)
+	register Char *str;
+	glob_t *pglob;
+{
+	char buf[MAXPATHLEN];
+
+	if (!*str)
+		strcpy(buf, ".");
+	else
+		g_Ctoc(str, buf);
+
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return((*pglob->gl_opendir)(buf));
+
+	return(opendir(buf));
+}
+
+static int
+g_lstat(fn, sb, pglob)
+	register Char *fn;
+	struct stat *sb;
+	glob_t *pglob;
+{
+	char buf[MAXPATHLEN];
+
+	g_Ctoc(fn, buf);
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return((*pglob->gl_lstat)(buf, sb));
+	return(lstat(buf, sb));
+}
+
+static int
+g_stat(fn, sb, pglob)
+	register Char *fn;
+	struct stat *sb;
+	glob_t *pglob;
+{
+	char buf[MAXPATHLEN];
+
+	g_Ctoc(fn, buf);
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return((*pglob->gl_stat)(buf, sb));
+	return(stat(buf, sb));
+}
+
+static Char *
+g_strchr(str, ch)
+	Char *str;
+	int ch;
+{
+	do {
+		if (*str == ch)
+			return (str);
+	} while (*str++);
+	return (NULL);
+}
+
+#ifdef notdef
+static Char *
+g_strcat(dst, src)
+	Char *dst;
+	const Char* src;
+{
+	Char *sdst = dst;
+
+	while (*dst++)
+		continue;
+	--dst;
+	while((*dst++ = *src++) != EOS)
+	    continue;
+
+	return (sdst);
+}
+#endif
+
+static void
+g_Ctoc(str, buf)
+	register const Char *str;
+	char *buf;
+{
+	register char *dc;
+
+	for (dc = buf; (*dc++ = *str++) != EOS;)
+		continue;
+}
+
+#ifdef DEBUG
+static void
+qprintf(str, s)
+	const char *str;
+	register Char *s;
+{
+	register Char *p;
+
+	(void)printf("%s:\n", str);
+	for (p = s; *p; p++)
+		(void)printf("%c", CHAR(*p));
+	(void)printf("\n");
+	for (p = s; *p; p++)
+		(void)printf("%c", *p & M_PROTECT ? '"' : ' ');
+	(void)printf("\n");
+	for (p = s; *p; p++)
+		(void)printf("%c", ismeta(*p) ? '_' : ' ');
+	(void)printf("\n");
+}
+#endif
diff --git a/libtar/handle.c b/libtar/handle.c
new file mode 100644
index 0000000..ae974b9
--- /dev/null
+++ b/libtar/handle.c
@@ -0,0 +1,129 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  handle.c - libtar code for initializing a TAR handle
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+
+const char libtar_version[] = PACKAGE_VERSION;
+
+static tartype_t default_type = { open, close, read, write };
+
+
+static int
+tar_init(TAR **t, char *pathname, tartype_t *type,
+	 int oflags, int mode, int options)
+{
+	if ((oflags & O_ACCMODE) == O_RDWR)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	*t = (TAR *)calloc(1, sizeof(TAR));
+	if (*t == NULL)
+		return -1;
+
+	(*t)->pathname = pathname;
+	(*t)->options = options;
+	(*t)->type = (type ? type : &default_type);
+	(*t)->oflags = oflags;
+
+	if ((oflags & O_ACCMODE) == O_RDONLY)
+		(*t)->h = libtar_hash_new(256,
+					  (libtar_hashfunc_t)path_hashfunc);
+	else
+		(*t)->h = libtar_hash_new(16, (libtar_hashfunc_t)dev_hash);
+	if ((*t)->h == NULL)
+	{
+		free(*t);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* open a new tarfile handle */
+int
+tar_open(TAR **t, char *pathname, tartype_t *type,
+	 int oflags, int mode, int options)
+{
+	if (tar_init(t, pathname, type, oflags, mode, options) == -1)
+		return -1;
+
+	if ((options & TAR_NOOVERWRITE) && (oflags & O_CREAT))
+		oflags |= O_EXCL;
+
+#ifdef O_BINARY
+	oflags |= O_BINARY;
+#endif
+
+	(*t)->fd = (*((*t)->type->openfunc))(pathname, oflags, mode);
+	if ((*t)->fd == -1)
+	{
+		free(*t);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int
+tar_fdopen(TAR **t, int fd, char *pathname, tartype_t *type,
+	   int oflags, int mode, int options)
+{
+	if (tar_init(t, pathname, type, oflags, mode, options) == -1)
+		return -1;
+
+	(*t)->fd = fd;
+	return 0;
+}
+
+
+int
+tar_fd(TAR *t)
+{
+	return t->fd;
+}
+
+
+/* close tarfile handle */
+int
+tar_close(TAR *t)
+{
+	int i;
+
+	i = (*(t->type->closefunc))(t->fd);
+
+	if (t->h != NULL)
+		libtar_hash_free(t->h, ((t->oflags & O_ACCMODE) == O_RDONLY
+					? free
+					: (libtar_freefunc_t)tar_dev_free));
+	free(t);
+
+	return i;
+}
+
+
diff --git a/libtar/inet_aton.c b/libtar/inet_aton.c
new file mode 100644
index 0000000..a935d5a
--- /dev/null
+++ b/libtar/inet_aton.c
@@ -0,0 +1,27 @@
+/*
+**  Copyright 2002 University of Illinois Board of Trustees
+**  Copyright 2002 Mark D. Roth
+**  All rights reserved.
+**
+**  inet_aton.c - inet_aton() function for compatibility library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+int
+inet_aton(const char *cp, struct in_addr *inp)
+{
+	inp->s_addr = inet_addr(cp);
+	if (inp->s_addr == -1)
+		return 0;
+	return 1;
+}
+
+
diff --git a/libtar/internal.h b/libtar/internal.h
new file mode 100644
index 0000000..23243d2
--- /dev/null
+++ b/libtar/internal.h
@@ -0,0 +1,17 @@
+/*
+**  Copyright 2002-2003 University of Illinois Board of Trustees
+**  Copyright 2002-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  internal.h - internal header file for libtar
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+#include <compat.h>
+
+#include <libtar.h>
+
diff --git a/libtar/libtar.h b/libtar/libtar.h
new file mode 100644
index 0000000..e3154ae
--- /dev/null
+++ b/libtar/libtar.h
@@ -0,0 +1,310 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  libtar.h - header file for libtar library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#ifndef LIBTAR_H
+#define LIBTAR_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "tar.h"
+
+#include "libtar_listhash.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+/* useful constants */
+#define T_BLOCKSIZE		512
+#define T_NAMELEN		100
+#define T_PREFIXLEN		155
+#define T_MAXPATHLEN		(T_NAMELEN + T_PREFIXLEN)
+
+/* GNU extensions for typeflag */
+#define GNU_LONGNAME_TYPE	'L'
+#define GNU_LONGLINK_TYPE	'K'
+
+/* extended metadata for next file - used to store selinux_context */
+#define TH_EXT_TYPE		'x'
+
+/* our version of the tar header structure */
+struct tar_header
+{
+	char name[100];
+	char mode[8];
+	char uid[8];
+	char gid[8];
+	char size[12];
+	char mtime[12];
+	char chksum[8];
+	char typeflag;
+	char linkname[100];
+	char magic[6];
+	char version[2];
+	char uname[32];
+	char gname[32];
+	char devmajor[8];
+	char devminor[8];
+	char prefix[155];
+	char padding[12];
+	char *gnu_longname;
+	char *gnu_longlink;
+#ifdef HAVE_SELINUX
+	char *selinux_context;
+#endif
+};
+
+
+/***** handle.c ************************************************************/
+
+typedef int (*openfunc_t)(const char *, int, ...);
+typedef int (*closefunc_t)(int);
+typedef ssize_t (*readfunc_t)(int, void *, size_t);
+typedef ssize_t (*writefunc_t)(int, const void *, size_t);
+
+typedef struct
+{
+	openfunc_t openfunc;
+	closefunc_t closefunc;
+	readfunc_t readfunc;
+	writefunc_t writefunc;
+}
+tartype_t;
+
+typedef struct
+{
+	tartype_t *type;
+	char *pathname;
+	long fd;
+	int oflags;
+	int options;
+	struct tar_header th_buf;
+	libtar_hash_t *h;
+}
+TAR;
+
+/* constant values for the TAR options field */
+#define TAR_GNU			 1	/* use GNU extensions */
+#define TAR_VERBOSE		 2	/* output file info to stdout */
+#define TAR_NOOVERWRITE		 4	/* don't overwrite existing files */
+#define TAR_IGNORE_EOT		 8	/* ignore double zero blocks as EOF */
+#define TAR_CHECK_MAGIC		16	/* check magic in file header */
+#define TAR_CHECK_VERSION	32	/* check version in file header */
+#define TAR_IGNORE_CRC		64	/* ignore CRC in file header */
+#define TAR_STORE_SELINUX	128	/* store selinux context */
+
+/* this is obsolete - it's here for backwards-compatibility only */
+#define TAR_IGNORE_MAGIC	0
+
+extern const char libtar_version[];
+
+
+/* open a new tarfile handle */
+int tar_open(TAR **t, char *pathname, tartype_t *type,
+	     int oflags, int mode, int options);
+
+/* make a tarfile handle out of a previously-opened descriptor */
+int tar_fdopen(TAR **t, int fd, char *pathname, tartype_t *type,
+	       int oflags, int mode, int options);
+
+/* returns the descriptor associated with t */
+int tar_fd(TAR *t);
+
+/* close tarfile handle */
+int tar_close(TAR *t);
+
+
+/***** append.c ************************************************************/
+
+/* forward declaration to appease the compiler */
+struct tar_dev;
+
+/* cleanup function */
+void tar_dev_free(struct tar_dev *tdp);
+
+/* Appends a file to the tar archive.
+ * Arguments:
+ *    t        = TAR handle to append to
+ *    realname = path of file to append
+ *    savename = name to save the file under in the archive
+ */
+int tar_append_file(TAR *t, char *realname, char *savename);
+
+/* write EOF indicator */
+int tar_append_eof(TAR *t);
+
+/* add file contents to a tarchive */
+int tar_append_regfile(TAR *t, char *realname);
+
+
+/***** block.c *************************************************************/
+
+/* macros for reading/writing tarchive blocks */
+#define tar_block_read(t, buf) \
+	(*((t)->type->readfunc))((t)->fd, (char *)(buf), T_BLOCKSIZE)
+#define tar_block_write(t, buf) \
+	(*((t)->type->writefunc))((t)->fd, (char *)(buf), T_BLOCKSIZE)
+
+/* read/write a header block */
+int th_read(TAR *t);
+int th_write(TAR *t);
+
+
+/***** decode.c ************************************************************/
+
+/* determine file type */
+#define TH_ISREG(t)	((t)->th_buf.typeflag == REGTYPE \
+			 || (t)->th_buf.typeflag == AREGTYPE \
+			 || (t)->th_buf.typeflag == CONTTYPE \
+			 || (S_ISREG((mode_t)oct_to_int((t)->th_buf.mode)) \
+			     && (t)->th_buf.typeflag != LNKTYPE))
+#define TH_ISLNK(t)	((t)->th_buf.typeflag == LNKTYPE)
+#define TH_ISSYM(t)	((t)->th_buf.typeflag == SYMTYPE \
+			 || S_ISLNK((mode_t)oct_to_int((t)->th_buf.mode)))
+#define TH_ISCHR(t)	((t)->th_buf.typeflag == CHRTYPE \
+			 || S_ISCHR((mode_t)oct_to_int((t)->th_buf.mode)))
+#define TH_ISBLK(t)	((t)->th_buf.typeflag == BLKTYPE \
+			 || S_ISBLK((mode_t)oct_to_int((t)->th_buf.mode)))
+#define TH_ISDIR(t)	((t)->th_buf.typeflag == DIRTYPE \
+			 || S_ISDIR((mode_t)oct_to_int((t)->th_buf.mode)) \
+			 || ((t)->th_buf.typeflag == AREGTYPE \
+			     && ((t)->th_buf.name[strlen((t)->th_buf.name) - 1] == '/')))
+#define TH_ISFIFO(t)	((t)->th_buf.typeflag == FIFOTYPE \
+			 || S_ISFIFO((mode_t)oct_to_int((t)->th_buf.mode)))
+#define TH_ISLONGNAME(t)	((t)->th_buf.typeflag == GNU_LONGNAME_TYPE)
+#define TH_ISLONGLINK(t)	((t)->th_buf.typeflag == GNU_LONGLINK_TYPE)
+#define TH_ISEXTHEADER(t)	((t)->th_buf.typeflag == TH_EXT_TYPE)
+
+/* decode tar header info */
+#define th_get_crc(t) oct_to_int((t)->th_buf.chksum)
+#define th_get_size(t) oct_to_int((t)->th_buf.size)
+#define th_get_mtime(t) oct_to_int((t)->th_buf.mtime)
+#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor)
+#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor)
+#define th_get_linkname(t) ((t)->th_buf.gnu_longlink \
+                            ? (t)->th_buf.gnu_longlink \
+                            : (t)->th_buf.linkname)
+char *th_get_pathname(TAR *t);
+mode_t th_get_mode(TAR *t);
+uid_t th_get_uid(TAR *t);
+gid_t th_get_gid(TAR *t);
+
+
+/***** encode.c ************************************************************/
+
+/* encode file info in th_header */
+void th_set_type(TAR *t, mode_t mode);
+void th_set_path(TAR *t, char *pathname);
+void th_set_link(TAR *t, char *linkname);
+void th_set_device(TAR *t, dev_t device);
+void th_set_user(TAR *t, uid_t uid);
+void th_set_group(TAR *t, gid_t gid);
+void th_set_mode(TAR *t, mode_t fmode);
+#define th_set_mtime(t, fmtime) \
+	int_to_oct_nonull((fmtime), (t)->th_buf.mtime, 12)
+#define th_set_size(t, fsize) \
+	int_to_oct_nonull((fsize), (t)->th_buf.size, 12)
+
+/* encode everything at once (except the pathname and linkname) */
+void th_set_from_stat(TAR *t, struct stat *s);
+
+/* encode magic, version, and crc - must be done after everything else is set */
+void th_finish(TAR *t);
+
+
+/***** extract.c ***********************************************************/
+
+/* sequentially extract next file from t */
+int tar_extract_file(TAR *t, char *realname, char *prefix, const int *progress_fd);
+
+/* extract different file types */
+int tar_extract_dir(TAR *t, char *realname);
+int tar_extract_hardlink(TAR *t, char *realname, char *prefix);
+int tar_extract_symlink(TAR *t, char *realname);
+int tar_extract_chardev(TAR *t, char *realname);
+int tar_extract_blockdev(TAR *t, char *realname);
+int tar_extract_fifo(TAR *t, char *realname);
+
+/* for regfiles, we need to extract the content blocks as well */
+int tar_extract_regfile(TAR *t, char *realname, const int *progress_fd);
+int tar_skip_regfile(TAR *t);
+
+
+/***** output.c ************************************************************/
+
+/* print the tar header */
+void th_print(TAR *t);
+
+/* print "ls -l"-like output for the file described by th */
+void th_print_long_ls(TAR *t);
+
+
+/***** util.c *************************************************************/
+
+/* hashing function for pathnames */
+int path_hashfunc(char *key, int numbuckets);
+
+/* matching function for dev_t's */
+int dev_match(dev_t *dev1, dev_t *dev2);
+
+/* matching function for ino_t's */
+int ino_match(ino_t *ino1, ino_t *ino2);
+
+/* hashing function for dev_t's */
+int dev_hash(dev_t *dev);
+
+/* hashing function for ino_t's */
+int ino_hash(ino_t *inode);
+
+/* create any necessary dirs */
+int mkdirhier(char *path);
+
+/* calculate header checksum */
+int th_crc_calc(TAR *t);
+
+/* calculate a signed header checksum */
+int th_signed_crc_calc(TAR *t);
+
+/* compare checksums in a forgiving way */
+#define th_crc_ok(t) (th_get_crc(t) == th_crc_calc(t) || th_get_crc(t) == th_signed_crc_calc(t))
+
+/* string-octal to integer conversion */
+int oct_to_int(char *oct);
+
+/* integer to NULL-terminated string-octal conversion */
+#define int_to_oct(num, oct, octlen) \
+	snprintf((oct), (octlen), "%*lo ", (octlen) - 2, (unsigned long)(num))
+
+/* integer to string-octal conversion, no NULL */
+void int_to_oct_nonull(int num, char *oct, size_t octlen);
+
+
+/***** wrapper.c **********************************************************/
+
+/* extract groups of files */
+int tar_extract_glob(TAR *t, char *globname, char *prefix);
+int tar_extract_all(TAR *t, char *prefix, const int *progress_fd);
+
+/* add a whole tree of files */
+int tar_append_tree(TAR *t, char *realdir, char *savedir, char *exclude);
+
+/* find an entry */
+int tar_find(TAR *t, char *searchstr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ! LIBTAR_H */
+
diff --git a/libtar/libtar_hash.c b/libtar/libtar_hash.c
new file mode 100644
index 0000000..796ebbe
--- /dev/null
+++ b/libtar/libtar_hash.c
@@ -0,0 +1,344 @@
+/* listhash/libtar_hash.c.  Generated from hash.c.in by configure. */
+
+/*
+**  Copyright 1998-2002 University of Illinois Board of Trustees
+**  Copyright 1998-2002 Mark D. Roth
+**  All rights reserved. 
+**
+**  libtar_hash.c - hash table routines
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+#include <compat.h>
+
+#include <libtar_listhash.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+
+/*
+** libtar_hashptr_reset() - reset a hash pointer
+*/
+void
+libtar_hashptr_reset(libtar_hashptr_t *hp)
+{
+	libtar_listptr_reset(&(hp->node));
+	hp->bucket = -1;
+}
+
+
+/*
+** libtar_hashptr_data() - retrieve the data being pointed to
+*/
+void *
+libtar_hashptr_data(libtar_hashptr_t *hp)
+{
+	return libtar_listptr_data(&(hp->node));
+}
+
+
+/*
+** libtar_str_hashfunc() - default hash function, optimized for
+**				      7-bit strings
+*/
+unsigned int
+libtar_str_hashfunc(char *key, unsigned int num_buckets)
+{
+#if 0
+	register unsigned result = 0;
+	register int i;
+
+	if (key == NULL)
+		return 0;
+
+	for (i = 0; *key != '\0' && i < 32; i++)
+		result = result * 33U + *key++;
+
+	return (result % num_buckets);
+#else
+	if (key == NULL)
+		return 0;
+
+	return (key[0] % num_buckets);
+#endif
+}
+
+
+/*
+** libtar_hash_nents() - return number of elements from hash
+*/
+unsigned int
+libtar_hash_nents(libtar_hash_t *h)
+{
+	return h->nents;
+}
+
+
+/*
+** libtar_hash_new() - create a new hash
+*/
+libtar_hash_t *
+libtar_hash_new(int num, libtar_hashfunc_t hashfunc)
+{
+	libtar_hash_t *hash;
+
+	hash = (libtar_hash_t *)calloc(1, sizeof(libtar_hash_t));
+	if (hash == NULL)
+		return NULL;
+	hash->numbuckets = num;
+	if (hashfunc != NULL)
+		hash->hashfunc = hashfunc;
+	else
+		hash->hashfunc = (libtar_hashfunc_t)libtar_str_hashfunc;
+
+	hash->table = (libtar_list_t **)calloc(num, sizeof(libtar_list_t *));
+	if (hash->table == NULL)
+	{
+		free(hash);
+		return NULL;
+	}
+
+	return hash;
+}
+
+
+/*
+** libtar_hash_next() - get next element in hash
+** returns:
+**	1			data found
+**	0			end of list
+*/
+int
+libtar_hash_next(libtar_hash_t *h,
+			    libtar_hashptr_t *hp)
+{
+#ifdef DS_DEBUG
+	printf("==> libtar_hash_next(h=0x%lx, hp={%d,0x%lx})\n",
+	       h, hp->bucket, hp->node);
+#endif
+
+	if (hp->bucket >= 0 && hp->node != NULL &&
+	    libtar_list_next(h->table[hp->bucket], &(hp->node)) != 0)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_next(): found additional "
+		       "data in current bucket (%d), returing 1\n",
+		       hp->bucket);
+#endif
+		return 1;
+	}
+
+#ifdef DS_DEBUG
+	printf("    libtar_hash_next(): done with bucket %d\n",
+	       hp->bucket);
+#endif
+
+	for (hp->bucket++; hp->bucket < h->numbuckets; hp->bucket++)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_next(): "
+		       "checking bucket %d\n", hp->bucket);
+#endif
+		hp->node = NULL;
+		if (h->table[hp->bucket] != NULL &&
+		    libtar_list_next(h->table[hp->bucket],
+		    				&(hp->node)) != 0)
+		{
+#ifdef DS_DEBUG
+			printf("    libtar_hash_next(): "
+			       "found data in bucket %d, returing 1\n",
+			       hp->bucket);
+#endif
+			return 1;
+		}
+	}
+
+	if (hp->bucket == h->numbuckets)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_next(): hash pointer "
+		       "wrapped to 0\n");
+#endif
+		hp->bucket = -1;
+		hp->node = NULL;
+	}
+
+#ifdef DS_DEBUG
+	printf("<== libtar_hash_next(): no more data, "
+	       "returning 0\n");
+#endif
+	return 0;
+}
+
+
+/*
+** libtar_hash_del() - delete an entry from the hash
+** returns:
+**	0			success
+**	-1 (and sets errno)	failure
+*/
+int
+libtar_hash_del(libtar_hash_t *h,
+			   libtar_hashptr_t *hp)
+{
+	if (hp->bucket < 0
+	    || hp->bucket >= h->numbuckets
+	    || h->table[hp->bucket] == NULL
+	    || hp->node == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	libtar_list_del(h->table[hp->bucket], &(hp->node));
+	h->nents--;
+	return 0;
+}
+
+
+/*
+** libtar_hash_empty() - empty the hash
+*/
+void
+libtar_hash_empty(libtar_hash_t *h, libtar_freefunc_t freefunc)
+{
+	int i;
+
+	for (i = 0; i < h->numbuckets; i++)
+		if (h->table[i] != NULL)
+			libtar_list_empty(h->table[i], freefunc);
+
+	h->nents = 0;
+}
+
+
+/*
+** libtar_hash_free() - delete all of the nodes in the hash
+*/
+void
+libtar_hash_free(libtar_hash_t *h, libtar_freefunc_t freefunc)
+{
+	int i;
+
+	for (i = 0; i < h->numbuckets; i++)
+		if (h->table[i] != NULL)
+			libtar_list_free(h->table[i], freefunc);
+
+	free(h->table);
+	free(h);
+}
+
+
+/*
+** libtar_hash_search() - iterative search for an element in a hash
+** returns:
+**	1			match found
+**	0			no match
+*/
+int
+libtar_hash_search(libtar_hash_t *h,
+			      libtar_hashptr_t *hp, void *data,
+			      libtar_matchfunc_t matchfunc)
+{
+	while (libtar_hash_next(h, hp) != 0)
+		if ((*matchfunc)(data, libtar_listptr_data(&(hp->node))) != 0)
+			return 1;
+
+	return 0;
+}
+
+
+/*
+** libtar_hash_getkey() - hash-based search for an element in a hash
+** returns:
+**	1			match found
+**	0			no match
+*/
+int
+libtar_hash_getkey(libtar_hash_t *h,
+			      libtar_hashptr_t *hp, void *key,
+			      libtar_matchfunc_t matchfunc)
+{
+#ifdef DS_DEBUG
+	printf("==> libtar_hash_getkey(h=0x%lx, hp={%d,0x%lx}, "
+	       "key=0x%lx, matchfunc=0x%lx)\n",
+	       h, hp->bucket, hp->node, key, matchfunc);
+#endif
+
+	if (hp->bucket == -1)
+	{
+		hp->bucket = (*(h->hashfunc))(key, h->numbuckets);
+#ifdef DS_DEBUG
+		printf("    libtar_hash_getkey(): hp->bucket "
+		       "set to %d\n", hp->bucket);
+#endif
+	}
+
+	if (h->table[hp->bucket] == NULL)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_getkey(): no list "
+		       "for bucket %d, returning 0\n", hp->bucket);
+#endif
+		hp->bucket = -1;
+		return 0;
+	}
+
+#ifdef DS_DEBUG
+	printf("<== libtar_hash_getkey(): "
+	       "returning libtar_list_search()\n");
+#endif
+	return libtar_list_search(h->table[hp->bucket], &(hp->node),
+					     key, matchfunc);
+}
+
+
+/*
+** libtar_hash_add() - add an element to the hash
+** returns:
+**	0			success
+**	-1 (and sets errno)	failure
+*/
+int
+libtar_hash_add(libtar_hash_t *h, void *data)
+{
+	int bucket, i;
+
+#ifdef DS_DEBUG
+	printf("==> libtar_hash_add(h=0x%lx, data=0x%lx)\n",
+	       h, data);
+#endif
+
+	bucket = (*(h->hashfunc))(data, h->numbuckets);
+#ifdef DS_DEBUG
+	printf("    libtar_hash_add(): inserting in bucket %d\n",
+	       bucket);
+#endif
+	if (h->table[bucket] == NULL)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_add(): creating new list\n");
+#endif
+		h->table[bucket] = libtar_list_new(LIST_QUEUE, NULL);
+	}
+
+#ifdef DS_DEBUG
+	printf("<== libtar_hash_add(): "
+	       "returning libtar_list_add()\n");
+#endif
+	i = libtar_list_add(h->table[bucket], data);
+	if (i == 0)
+		h->nents++;
+	return i;
+}
+
+
diff --git a/libtar/libtar_list.c b/libtar/libtar_list.c
new file mode 100644
index 0000000..2c5febb
--- /dev/null
+++ b/libtar/libtar_list.c
@@ -0,0 +1,458 @@
+/* listhash/libtar_list.c.  Generated from list.c.in by configure. */
+
+/*
+**  Copyright 1998-2002 University of Illinois Board of Trustees
+**  Copyright 1998-2002 Mark D. Roth
+**  All rights reserved.
+**
+**  libtar_list.c - linked list routines
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+#include <compat.h>
+
+#include <libtar_listhash.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+
+/*
+** libtar_listptr_reset() - reset a list pointer
+*/
+void
+libtar_listptr_reset(libtar_listptr_t *lp)
+{
+	*lp = NULL;
+}
+
+
+/*
+** libtar_listptr_data() - retrieve the data pointed to by lp
+*/
+void *
+libtar_listptr_data(libtar_listptr_t *lp)
+{
+	return (*lp)->data;
+}
+
+
+/*
+** libtar_list_new() - create a new, empty list
+*/
+libtar_list_t *
+libtar_list_new(int flags, libtar_cmpfunc_t cmpfunc)
+{
+	libtar_list_t *newlist;
+
+#ifdef DS_DEBUG
+	printf("in libtar_list_new(%d, 0x%lx)\n", flags, cmpfunc);
+#endif
+
+	if (flags != LIST_USERFUNC
+	    && flags != LIST_STACK
+	    && flags != LIST_QUEUE)
+	{
+		errno = EINVAL;
+		return NULL;
+	}
+
+	newlist = (libtar_list_t *)calloc(1, sizeof(libtar_list_t));
+	if (cmpfunc != NULL)
+		newlist->cmpfunc = cmpfunc;
+	else
+		newlist->cmpfunc = (libtar_cmpfunc_t)strcmp;
+	newlist->flags = flags;
+
+	return newlist;
+}
+
+
+/*
+** libtar_list_iterate() - call a function for every element
+**				      in a list
+*/
+int
+libtar_list_iterate(libtar_list_t *l,
+			       libtar_iterate_func_t plugin,
+			       void *state)
+{
+	libtar_listptr_t n;
+
+	if (l == NULL)
+		return -1;
+
+	for (n = l->first; n != NULL; n = n->next)
+	{
+		if ((*plugin)(n->data, state) == -1)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+** libtar_list_empty() - empty the list
+*/
+void
+libtar_list_empty(libtar_list_t *l, libtar_freefunc_t freefunc)
+{
+	libtar_listptr_t n;
+
+	for (n = l->first; n != NULL; n = l->first)
+	{
+		l->first = n->next;
+		if (freefunc != NULL)
+			(*freefunc)(n->data);
+		free(n);
+	}
+
+	l->nents = 0;
+}
+
+
+/*
+** libtar_list_free() - remove and free() the whole list
+*/
+void
+libtar_list_free(libtar_list_t *l, libtar_freefunc_t freefunc)
+{
+	libtar_list_empty(l, freefunc);
+	free(l);
+}
+
+
+/*
+** libtar_list_nents() - return number of elements in the list
+*/
+unsigned int
+libtar_list_nents(libtar_list_t *l)
+{
+	return l->nents;
+}
+
+
+/*
+** libtar_list_add() - adds an element to the list
+** returns:
+**	0			success
+**	-1 (and sets errno)	failure
+*/
+int
+libtar_list_add(libtar_list_t *l, void *data)
+{
+	libtar_listptr_t n, m;
+
+#ifdef DS_DEBUG
+	printf("==> libtar_list_add(\"%s\")\n", (char *)data);
+#endif
+
+	n = (libtar_listptr_t)malloc(sizeof(struct libtar_node));
+	if (n == NULL)
+		return -1;
+	n->data = data;
+	l->nents++;
+
+#ifdef DS_DEBUG
+	printf("    libtar_list_add(): allocated data\n");
+#endif
+
+	/* if the list is empty */
+	if (l->first == NULL)
+	{
+		l->last = l->first = n;
+		n->next = n->prev = NULL;
+#ifdef DS_DEBUG
+		printf("<== libtar_list_add(): list was empty; "
+		       "added first element and returning 0\n");
+#endif
+		return 0;
+	}
+
+#ifdef DS_DEBUG
+	printf("    libtar_list_add(): list not empty\n");
+#endif
+
+	if (l->flags == LIST_STACK)
+	{
+		n->prev = NULL;
+		n->next = l->first;
+		if (l->first != NULL)
+			l->first->prev = n;
+		l->first = n;
+#ifdef DS_DEBUG
+		printf("<== libtar_list_add(): LIST_STACK set; "
+		       "added in front\n");
+#endif
+		return 0;
+	}
+
+	if (l->flags == LIST_QUEUE)
+	{
+		n->prev = l->last;
+		n->next = NULL;
+		if (l->last != NULL)
+			l->last->next = n;
+		l->last = n;
+#ifdef DS_DEBUG
+		printf("<== libtar_list_add(): LIST_QUEUE set; "
+		       "added at end\n");
+#endif
+		return 0;
+	}
+
+	for (m = l->first; m != NULL; m = m->next)
+		if ((*(l->cmpfunc))(data, m->data) < 0)
+		{
+			/*
+			** if we find one that's bigger,
+			** insert data before it
+			*/
+#ifdef DS_DEBUG
+			printf("    libtar_list_add(): gotcha..."
+			       "inserting data\n");
+#endif
+			if (m == l->first)
+			{
+				l->first = n;
+				n->prev = NULL;
+				m->prev = n;
+				n->next = m;
+#ifdef DS_DEBUG
+				printf("<== libtar_list_add(): "
+				       "added first, returning 0\n");
+#endif
+				return 0;
+			}
+			m->prev->next = n;
+			n->prev = m->prev;
+			m->prev = n;
+			n->next = m;
+#ifdef DS_DEBUG
+			printf("<== libtar_list_add(): added middle,"
+			       " returning 0\n");
+#endif
+			return 0;
+		}
+
+#ifdef DS_DEBUG
+	printf("    libtar_list_add(): new data larger than current "
+	       "list elements\n");
+#endif
+
+	/* if we get here, data is bigger than everything in the list */
+	l->last->next = n;
+	n->prev = l->last;
+	l->last = n;
+	n->next = NULL;
+#ifdef DS_DEBUG
+	printf("<== libtar_list_add(): added end, returning 0\n");
+#endif
+	return 0;
+}
+
+
+/*
+** libtar_list_del() - remove the element pointed to by n
+**				  from the list l
+*/
+void
+libtar_list_del(libtar_list_t *l, libtar_listptr_t *n)
+{
+	libtar_listptr_t m;
+
+#ifdef DS_DEBUG
+	printf("==> libtar_list_del()\n");
+#endif
+
+	l->nents--;
+
+	m = (*n)->next;
+
+	if ((*n)->prev)
+		(*n)->prev->next = (*n)->next;
+	else
+		l->first = (*n)->next;
+	if ((*n)->next)
+		(*n)->next->prev = (*n)->prev;
+	else
+		l->last = (*n)->prev;
+
+	free(*n);
+	*n = m;
+}
+
+
+/*
+** libtar_list_next() - get the next element in the list
+** returns:
+**	1			success
+**	0			end of list
+*/
+int
+libtar_list_next(libtar_list_t *l,
+			    libtar_listptr_t *n)
+{
+	if (*n == NULL)
+		*n = l->first;
+	else
+		*n = (*n)->next;
+
+	return (*n != NULL ? 1 : 0);
+}
+
+
+/*
+** libtar_list_prev() - get the previous element in the list
+** returns:
+**	1			success
+**	0			end of list
+*/
+int
+libtar_list_prev(libtar_list_t *l,
+			    libtar_listptr_t *n)
+{
+	if (*n == NULL)
+		*n = l->last;
+	else
+		*n = (*n)->prev;
+
+	return (*n != NULL ? 1 : 0);
+}
+
+
+/*
+** libtar_str_match() - string matching function
+** returns:
+**	1			match
+**	0			no match
+*/
+int
+libtar_str_match(char *check, char *data)
+{
+	return !strcmp(check, data);
+}
+
+
+/*
+** libtar_list_add_str() - splits string str into delim-delimited
+**				      elements and adds them to list l
+** returns:
+**	0			success
+**	-1 (and sets errno)	failure
+*/
+int
+libtar_list_add_str(libtar_list_t *l,
+			       char *str, char *delim)
+{
+	char tmp[10240];
+	char *tokp, *nextp = tmp;
+
+	strlcpy(tmp, str, sizeof(tmp));
+	while ((tokp = strsep(&nextp, delim)) != NULL)
+	{
+		if (*tokp == '\0')
+			continue;
+		if (libtar_list_add(l, strdup(tokp)))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+** libtar_list_search() - find an entry in a list
+** returns:
+**	1			match found
+**	0			no match
+*/
+int
+libtar_list_search(libtar_list_t *l,
+			      libtar_listptr_t *n, void *data,
+			      libtar_matchfunc_t matchfunc)
+{
+#ifdef DS_DEBUG
+	printf("==> libtar_list_search(l=0x%lx, n=0x%lx, \"%s\")\n",
+	       l, n, (char *)data);
+#endif
+
+	if (matchfunc == NULL)
+		matchfunc = (libtar_matchfunc_t)libtar_str_match;
+
+	if (*n == NULL)
+		*n = l->first;
+	else
+		*n = (*n)->next;
+
+	for (; *n != NULL; *n = (*n)->next)
+	{
+#ifdef DS_DEBUG
+		printf("checking against \"%s\"\n", (char *)(*n)->data);
+#endif
+		if ((*(matchfunc))(data, (*n)->data) != 0)
+			return 1;
+	}
+
+#ifdef DS_DEBUG
+	printf("no matches found\n");
+#endif
+	return 0;
+}
+
+
+/*
+** libtar_list_dup() - copy an existing list
+*/
+libtar_list_t *
+libtar_list_dup(libtar_list_t *l)
+{
+	libtar_list_t *newlist;
+	libtar_listptr_t n;
+
+	newlist = libtar_list_new(l->flags, l->cmpfunc);
+	for (n = l->first; n != NULL; n = n->next)
+		libtar_list_add(newlist, n->data);
+
+#ifdef DS_DEBUG
+	printf("returning from libtar_list_dup()\n");
+#endif
+	return newlist;
+}
+
+
+/*
+** libtar_list_merge() - merge two lists into a new list
+*/
+libtar_list_t *
+libtar_list_merge(libtar_cmpfunc_t cmpfunc, int flags,
+			     libtar_list_t *list1,
+			     libtar_list_t *list2)
+{
+	libtar_list_t *newlist;
+	libtar_listptr_t n;
+
+	newlist = libtar_list_new(flags, cmpfunc);
+
+	n = NULL;
+	while (libtar_list_next(list1, &n) != 0)
+		libtar_list_add(newlist, n->data);
+	n = NULL;
+	while (libtar_list_next(list2, &n) != 0)
+		libtar_list_add(newlist, n->data);
+
+	return newlist;
+}
+
+
diff --git a/libtar/libtar_listhash.h b/libtar/libtar_listhash.h
new file mode 100644
index 0000000..fa33cfd
--- /dev/null
+++ b/libtar/libtar_listhash.h
@@ -0,0 +1,196 @@
+/* listhash/libtar_listhash.h.  Generated from listhash.h.in by configure. */
+
+/*
+**  Copyright 1998-2002 University of Illinois Board of Trustees
+**  Copyright 1998-2002 Mark D. Roth
+**  All rights reserved.
+**
+**  libtar_listhash.h - header file for listhash module
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#ifndef libtar_LISTHASH_H
+#define libtar_LISTHASH_H
+
+
+/***** list.c **********************************************************/
+
+/*
+** Comparison function (used to determine order of elements in a list)
+** returns less than, equal to, or greater than 0
+** if data1 is less than, equal to, or greater than data2
+*/
+typedef int (*libtar_cmpfunc_t)(void *, void *);
+
+/*
+** Free function (for freeing allocated memory in each element)
+*/
+typedef void (*libtar_freefunc_t)(void *);
+
+/*
+** Plugin function for libtar_list_iterate()
+*/
+typedef int (*libtar_iterate_func_t)(void *, void *);
+
+/*
+** Matching function (used to find elements in a list)
+** first argument is the data to search for
+** second argument is the list element it's being compared to
+** returns 0 if no match is found, non-zero otherwise
+*/
+typedef int (*libtar_matchfunc_t)(void *, void *);
+
+
+struct libtar_node
+{
+	void *data;
+	struct libtar_node *next;
+	struct libtar_node *prev;
+};
+typedef struct libtar_node *libtar_listptr_t;
+
+struct libtar_list
+{
+	libtar_listptr_t first;
+	libtar_listptr_t last;
+	libtar_cmpfunc_t cmpfunc;
+	int flags;
+	unsigned int nents;
+};
+typedef struct libtar_list libtar_list_t;
+
+
+/* values for flags */
+#define LIST_USERFUNC	0	/* use cmpfunc() to order */
+#define LIST_STACK	1	/* new elements go in front */
+#define LIST_QUEUE	2	/* new elements go at the end */
+
+
+/* reset a list pointer */
+void libtar_listptr_reset(libtar_listptr_t *);
+
+/* retrieve the data being pointed to */
+void *libtar_listptr_data(libtar_listptr_t *);
+
+/* creates a new, empty list */
+libtar_list_t *libtar_list_new(int, libtar_cmpfunc_t);
+
+/* call a function for every element in a list */
+int libtar_list_iterate(libtar_list_t *,
+				   libtar_iterate_func_t, void *);
+
+/* empty the list */
+void libtar_list_empty(libtar_list_t *,
+				  libtar_freefunc_t);
+
+/* remove and free() the entire list */
+void libtar_list_free(libtar_list_t *,
+				 libtar_freefunc_t);
+
+/* add elements */
+int libtar_list_add(libtar_list_t *, void *);
+
+/* removes an element from the list - returns -1 on error */
+void libtar_list_del(libtar_list_t *,
+				libtar_listptr_t *);
+
+/* returns 1 when valid data is returned, or 0 at end of list */
+int libtar_list_next(libtar_list_t *,
+				libtar_listptr_t *);
+
+/* returns 1 when valid data is returned, or 0 at end of list */
+int libtar_list_prev(libtar_list_t *,
+				libtar_listptr_t *);
+
+/* return 1 if the data matches a list entry, 0 otherwise */
+int libtar_list_search(libtar_list_t *,
+				  libtar_listptr_t *, void *,
+				  libtar_matchfunc_t);
+
+/* return number of elements from list */
+unsigned int libtar_list_nents(libtar_list_t *);
+
+/* adds elements from a string delimited by delim */
+int libtar_list_add_str(libtar_list_t *, char *, char *);
+
+/* string matching function */
+int libtar_str_match(char *, char *);
+
+
+/***** hash.c **********************************************************/
+
+/*
+** Hashing function (determines which bucket the given key hashes into)
+** first argument is the key to hash
+** second argument is the total number of buckets
+** returns the bucket number
+*/
+typedef unsigned int (*libtar_hashfunc_t)(void *, unsigned int);
+
+
+struct libtar_hashptr
+{
+	int bucket;
+	libtar_listptr_t node;
+};
+typedef struct libtar_hashptr libtar_hashptr_t;
+
+struct libtar_hash
+{
+	int numbuckets;
+	libtar_list_t **table;
+	libtar_hashfunc_t hashfunc;
+	unsigned int nents;
+};
+typedef struct libtar_hash libtar_hash_t;
+
+
+/* reset a hash pointer */
+void libtar_hashptr_reset(libtar_hashptr_t *);
+
+/* retrieve the data being pointed to */
+void *libtar_hashptr_data(libtar_hashptr_t *);
+
+/* default hash function, optimized for 7-bit strings */
+unsigned int libtar_str_hashfunc(char *, unsigned int);
+
+/* return number of elements from hash */
+unsigned int libtar_hash_nents(libtar_hash_t *);
+
+/* create a new hash */
+libtar_hash_t *libtar_hash_new(int, libtar_hashfunc_t);
+
+/* empty the hash */
+void libtar_hash_empty(libtar_hash_t *,
+				  libtar_freefunc_t);
+
+/* delete all the libtar_nodes of the hash and clean up */
+void libtar_hash_free(libtar_hash_t *,
+				 libtar_freefunc_t);
+
+/* returns 1 when valid data is returned, or 0 at end of list */
+int libtar_hash_next(libtar_hash_t *,
+				libtar_hashptr_t *);
+
+/* return 1 if the data matches a list entry, 0 otherwise */
+int libtar_hash_search(libtar_hash_t *,
+				  libtar_hashptr_t *, void *,
+				  libtar_matchfunc_t);
+
+/* return 1 if the key matches a list entry, 0 otherwise */
+int libtar_hash_getkey(libtar_hash_t *,
+				  libtar_hashptr_t *, void *,
+				  libtar_matchfunc_t);
+
+/* inserting data */
+int libtar_hash_add(libtar_hash_t *, void *);
+
+/* delete an entry */
+int libtar_hash_del(libtar_hash_t *,
+			       libtar_hashptr_t *);
+
+#endif /* ! libtar_LISTHASH_H */
+
diff --git a/libtar/output.c b/libtar/output.c
new file mode 100644
index 0000000..a2db929
--- /dev/null
+++ b/libtar/output.c
@@ -0,0 +1,134 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  output.c - libtar code to print out tar header blocks
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/param.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+
+#ifndef _POSIX_LOGIN_NAME_MAX
+# define _POSIX_LOGIN_NAME_MAX	9
+#endif
+
+
+void
+th_print(TAR *t)
+{
+	puts("\nPrinting tar header:");
+	printf("  name     = \"%.100s\"\n", t->th_buf.name);
+	printf("  mode     = \"%.8s\"\n", t->th_buf.mode);
+	printf("  uid      = \"%.8s\"\n", t->th_buf.uid);
+	printf("  gid      = \"%.8s\"\n", t->th_buf.gid);
+	printf("  size     = \"%.12s\"\n", t->th_buf.size);
+	printf("  mtime    = \"%.12s\"\n", t->th_buf.mtime);
+	printf("  chksum   = \"%.8s\"\n", t->th_buf.chksum);
+	printf("  typeflag = \'%c\'\n", t->th_buf.typeflag);
+	printf("  linkname = \"%.100s\"\n", t->th_buf.linkname);
+	printf("  magic    = \"%.6s\"\n", t->th_buf.magic);
+	/*printf("  version  = \"%.2s\"\n", t->th_buf.version); */
+	printf("  version[0] = \'%c\',version[1] = \'%c\'\n",
+	       t->th_buf.version[0], t->th_buf.version[1]);
+	printf("  uname    = \"%.32s\"\n", t->th_buf.uname);
+	printf("  gname    = \"%.32s\"\n", t->th_buf.gname);
+	printf("  devmajor = \"%.8s\"\n", t->th_buf.devmajor);
+	printf("  devminor = \"%.8s\"\n", t->th_buf.devminor);
+	printf("  prefix   = \"%.155s\"\n", t->th_buf.prefix);
+	printf("  padding  = \"%.12s\"\n", t->th_buf.padding);
+	printf("  gnu_longname = \"%s\"\n",
+	       (t->th_buf.gnu_longname ? t->th_buf.gnu_longname : "[NULL]"));
+	printf("  gnu_longlink = \"%s\"\n",
+	       (t->th_buf.gnu_longlink ? t->th_buf.gnu_longlink : "[NULL]"));
+}
+
+
+void
+th_print_long_ls(TAR *t)
+{
+	char modestring[12];
+	struct passwd *pw;
+	struct group *gr;
+	uid_t uid;
+	gid_t gid;
+	char username[_POSIX_LOGIN_NAME_MAX];
+	char groupname[_POSIX_LOGIN_NAME_MAX];
+	time_t mtime;
+	struct tm *mtm;
+
+#ifdef HAVE_STRFTIME
+	char timebuf[18];
+#else
+	const char *months[] = {
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+	};
+#endif
+
+	uid = th_get_uid(t);
+	pw = getpwuid(uid);
+	if (pw == NULL)
+		snprintf(username, sizeof(username), "%d", uid);
+	else
+		strlcpy(username, pw->pw_name, sizeof(username));
+
+	gid = th_get_gid(t);
+	gr = getgrgid(gid);
+	if (gr == NULL)
+		snprintf(groupname, sizeof(groupname), "%d", gid);
+	else
+		strlcpy(groupname, gr->gr_name, sizeof(groupname));
+
+	strmode(th_get_mode(t), modestring);
+	printf("%.10s %-8.8s %-8.8s ", modestring, username, groupname);
+
+	if (TH_ISCHR(t) || TH_ISBLK(t))
+		printf(" %3d, %3d ", th_get_devmajor(t), th_get_devminor(t));
+	else
+		printf("%9ld ", (long)th_get_size(t));
+
+	mtime = th_get_mtime(t);
+	mtm = localtime(&mtime);
+#ifdef HAVE_STRFTIME
+	strftime(timebuf, sizeof(timebuf), "%h %e %H:%M %Y", mtm);
+	printf("%s", timebuf);
+#else
+	printf("%.3s %2d %2d:%02d %4d",
+	       months[mtm->tm_mon],
+	       mtm->tm_mday, mtm->tm_hour, mtm->tm_min, mtm->tm_year + 1900);
+#endif
+
+	printf(" %s", th_get_pathname(t));
+
+	if (TH_ISSYM(t) || TH_ISLNK(t))
+	{
+		if (TH_ISSYM(t))
+			printf(" -> ");
+		else
+			printf(" link to ");
+		if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
+			printf("%s", t->th_buf.gnu_longlink);
+		else
+			printf("%.100s", t->th_buf.linkname);
+	}
+
+	putchar('\n');
+}
+
+
diff --git a/libtar/snprintf.c b/libtar/snprintf.c
new file mode 100644
index 0000000..8c228d4
--- /dev/null
+++ b/libtar/snprintf.c
@@ -0,0 +1,761 @@
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh.  This sort of thing is always nasty do deal with.  Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length.  This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ *  This was ugly.  It is still ugly.  I opted out of floating point
+ *  numbers, but the formatter understands just about everything
+ *  from the normal C string format, at least as far as I can tell from
+ *  the Solaris 2.5 printf(3S) man page.
+ *
+ *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ *    Ok, added some minimal floating point support, which means this
+ *    probably requires libm on most operating systems.  Don't yet
+ *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
+ *    was pretty badly broken, it just wasn't being exercised in ways
+ *    which showed it, so that's been fixed.  Also, formated the code
+ *    to mutt conventions, and removed dead code left over from the
+ *    original.  Also, there is now a builtin-test, just compile with:
+ *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ *    and run snprintf for results.
+ * 
+ *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ *    The PGP code was using unsigned hexadecimal formats. 
+ *    Unfortunately, unsigned formats simply didn't work.
+ *
+ *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ *    The original code assumed that both snprintf() and vsnprintf() were
+ *    missing.  Some systems only have snprintf() but not vsnprintf(), so
+ *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ *  Chris Frey <cdfrey@foursquare.net> 2012/09/05
+ *    Removed varargs.h and all dependency on it.
+ *
+ **************************************************************/
+
+#include <config.h>
+
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
+
+#include <string.h>
+# include <ctype.h>
+#include <sys/types.h>
+
+#include <stdarg.h>
+#define VA_LOCAL_DECL   va_list ap
+#define VA_START(f)     va_start(ap, f)
+#define VA_SHIFT(v,t)  ;   /* no-op for ANSI */
+#define VA_END          va_end(ap)
+
+/*int snprintf (char *str, size_t count, const char *fmt, ...);*/
+/*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
+
+static void dopr (char *buffer, size_t maxlen, const char *format, 
+                  va_list args);
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+		    char *value, int flags, int min, int max);
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+		    long value, int base, int min, int max, int flags);
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+		   long double fvalue, int min, int max, int flags);
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS   1
+#define DP_S_MIN     2
+#define DP_S_DOT     3
+#define DP_S_MAX     4
+#define DP_S_MOD     5
+#define DP_S_CONV    6
+#define DP_S_DONE    7
+
+/* format flags - Bits */
+#define DP_F_MINUS 	(1 << 0)
+#define DP_F_PLUS  	(1 << 1)
+#define DP_F_SPACE 	(1 << 2)
+#define DP_F_NUM   	(1 << 3)
+#define DP_F_ZERO  	(1 << 4)
+#define DP_F_UP    	(1 << 5)
+#define DP_F_UNSIGNED 	(1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT   1
+#define DP_C_LONG    2
+#define DP_C_LDOUBLE 3
+
+#define char_to_int(p) (p - '0')
+#define MAX(p,q) ((p >= q) ? p : q)
+
+static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
+{
+  char ch;
+  long value;
+  long double fvalue;
+  char *strvalue;
+  int min;
+  int max;
+  int state;
+  int flags;
+  int cflags;
+  size_t currlen;
+  
+  state = DP_S_DEFAULT;
+  currlen = flags = cflags = min = 0;
+  max = -1;
+  ch = *format++;
+
+  while (state != DP_S_DONE)
+  {
+    if ((ch == '\0') || (currlen >= maxlen)) 
+      state = DP_S_DONE;
+
+    switch(state) 
+    {
+    case DP_S_DEFAULT:
+      if (ch == '%') 
+	state = DP_S_FLAGS;
+      else 
+	dopr_outch (buffer, &currlen, maxlen, ch);
+      ch = *format++;
+      break;
+    case DP_S_FLAGS:
+      switch (ch) 
+      {
+      case '-':
+	flags |= DP_F_MINUS;
+        ch = *format++;
+	break;
+      case '+':
+	flags |= DP_F_PLUS;
+        ch = *format++;
+	break;
+      case ' ':
+	flags |= DP_F_SPACE;
+        ch = *format++;
+	break;
+      case '#':
+	flags |= DP_F_NUM;
+        ch = *format++;
+	break;
+      case '0':
+	flags |= DP_F_ZERO;
+        ch = *format++;
+	break;
+      default:
+	state = DP_S_MIN;
+	break;
+      }
+      break;
+    case DP_S_MIN:
+      if (isdigit((unsigned char)ch)) 
+      {
+	min = 10*min + char_to_int (ch);
+	ch = *format++;
+      } 
+      else if (ch == '*') 
+      {
+	min = va_arg (args, int);
+	ch = *format++;
+	state = DP_S_DOT;
+      } 
+      else 
+	state = DP_S_DOT;
+      break;
+    case DP_S_DOT:
+      if (ch == '.') 
+      {
+	state = DP_S_MAX;
+	ch = *format++;
+      } 
+      else 
+	state = DP_S_MOD;
+      break;
+    case DP_S_MAX:
+      if (isdigit((unsigned char)ch)) 
+      {
+	if (max < 0)
+	  max = 0;
+	max = 10*max + char_to_int (ch);
+	ch = *format++;
+      } 
+      else if (ch == '*') 
+      {
+	max = va_arg (args, int);
+	ch = *format++;
+	state = DP_S_MOD;
+      } 
+      else 
+	state = DP_S_MOD;
+      break;
+    case DP_S_MOD:
+      /* Currently, we don't support Long Long, bummer */
+      switch (ch) 
+      {
+      case 'h':
+	cflags = DP_C_SHORT;
+	ch = *format++;
+	break;
+      case 'l':
+	cflags = DP_C_LONG;
+	ch = *format++;
+	break;
+      case 'L':
+	cflags = DP_C_LDOUBLE;
+	ch = *format++;
+	break;
+      default:
+	break;
+      }
+      state = DP_S_CONV;
+      break;
+    case DP_S_CONV:
+      switch (ch) 
+      {
+      case 'd':
+      case 'i':
+	if (cflags == DP_C_SHORT) 
+	  value = va_arg (args, int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, long int);
+	else
+	  value = va_arg (args, int);
+	fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+	break;
+      case 'o':
+	flags |= DP_F_UNSIGNED;
+	if (cflags == DP_C_SHORT)
+	  value = va_arg (args, unsigned int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, unsigned long int);
+	else
+	  value = va_arg (args, unsigned int);
+	fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+	break;
+      case 'u':
+	flags |= DP_F_UNSIGNED;
+	if (cflags == DP_C_SHORT)
+	  value = va_arg (args, unsigned int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, unsigned long int);
+	else
+	  value = va_arg (args, unsigned int);
+	fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+	break;
+      case 'X':
+	flags |= DP_F_UP;
+      case 'x':
+	flags |= DP_F_UNSIGNED;
+	if (cflags == DP_C_SHORT)
+	  value = va_arg (args, unsigned int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, unsigned long int);
+	else
+	  value = va_arg (args, unsigned int);
+	fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+	break;
+      case 'f':
+	if (cflags == DP_C_LDOUBLE)
+	  fvalue = va_arg (args, long double);
+	else
+	  fvalue = va_arg (args, double);
+	/* um, floating point? */
+	fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+	break;
+      case 'E':
+	flags |= DP_F_UP;
+      case 'e':
+	if (cflags == DP_C_LDOUBLE)
+	  fvalue = va_arg (args, long double);
+	else
+	  fvalue = va_arg (args, double);
+	break;
+      case 'G':
+	flags |= DP_F_UP;
+      case 'g':
+	if (cflags == DP_C_LDOUBLE)
+	  fvalue = va_arg (args, long double);
+	else
+	  fvalue = va_arg (args, double);
+	break;
+      case 'c':
+	dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+	break;
+      case 's':
+	strvalue = va_arg (args, char *);
+	if (max < 0) 
+	  max = maxlen; /* ie, no max */
+	fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+	break;
+      case 'p':
+	strvalue = va_arg (args, void *);
+	fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
+	break;
+      case 'n':
+	if (cflags == DP_C_SHORT) 
+	{
+	  short int *num;
+	  num = va_arg (args, short int *);
+	  *num = currlen;
+        } 
+	else if (cflags == DP_C_LONG) 
+	{
+	  long int *num;
+	  num = va_arg (args, long int *);
+	  *num = currlen;
+        } 
+	else 
+	{
+	  int *num;
+	  num = va_arg (args, int *);
+	  *num = currlen;
+        }
+	break;
+      case '%':
+	dopr_outch (buffer, &currlen, maxlen, ch);
+	break;
+      case 'w':
+	/* not supported yet, treat as next char */
+	ch = *format++;
+	break;
+      default:
+	/* Unknown, skip */
+	break;
+      }
+      ch = *format++;
+      state = DP_S_DEFAULT;
+      flags = cflags = min = 0;
+      max = -1;
+      break;
+    case DP_S_DONE:
+      break;
+    default:
+      /* hmm? */
+      break; /* some picky compilers need this */
+    }
+  }
+  if (currlen < maxlen - 1) 
+    buffer[currlen] = '\0';
+  else 
+    buffer[maxlen - 1] = '\0';
+}
+
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+		    char *value, int flags, int min, int max)
+{
+  int padlen, strln;     /* amount to pad */
+  int cnt = 0;
+  
+  if (value == 0)
+  {
+    value = "<NULL>";
+  }
+
+  for (strln = 0; value[strln]; ++strln); /* strlen */
+  padlen = min - strln;
+  if (padlen < 0) 
+    padlen = 0;
+  if (flags & DP_F_MINUS) 
+    padlen = -padlen; /* Left Justify */
+
+  while ((padlen > 0) && (cnt < max)) 
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    --padlen;
+    ++cnt;
+  }
+  while (*value && (cnt < max)) 
+  {
+    dopr_outch (buffer, currlen, maxlen, *value++);
+    ++cnt;
+  }
+  while ((padlen < 0) && (cnt < max)) 
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    ++padlen;
+    ++cnt;
+  }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+		    long value, int base, int min, int max, int flags)
+{
+  int signvalue = 0;
+  unsigned long uvalue;
+  char convert[20];
+  int place = 0;
+  int spadlen = 0; /* amount to space pad */
+  int zpadlen = 0; /* amount to zero pad */
+  int caps = 0;
+  
+  if (max < 0)
+    max = 0;
+
+  uvalue = value;
+
+  if(!(flags & DP_F_UNSIGNED))
+  {
+    if( value < 0 ) {
+      signvalue = '-';
+      uvalue = -value;
+    }
+    else
+      if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
+	signvalue = '+';
+    else
+      if (flags & DP_F_SPACE)
+	signvalue = ' ';
+  }
+  
+  if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+  do {
+    convert[place++] =
+      (caps? "0123456789ABCDEF":"0123456789abcdef")
+      [uvalue % (unsigned)base  ];
+    uvalue = (uvalue / (unsigned)base );
+  } while(uvalue && (place < 20));
+  if (place == 20) place--;
+  convert[place] = 0;
+
+  zpadlen = max - place;
+  spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+  if (zpadlen < 0) zpadlen = 0;
+  if (spadlen < 0) spadlen = 0;
+  if (flags & DP_F_ZERO)
+  {
+    zpadlen = MAX(zpadlen, spadlen);
+    spadlen = 0;
+  }
+  if (flags & DP_F_MINUS) 
+    spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+  dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+      zpadlen, spadlen, min, max, place));
+#endif
+
+  /* Spaces */
+  while (spadlen > 0) 
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    --spadlen;
+  }
+
+  /* Sign */
+  if (signvalue) 
+    dopr_outch (buffer, currlen, maxlen, signvalue);
+
+  /* Zeros */
+  if (zpadlen > 0) 
+  {
+    while (zpadlen > 0)
+    {
+      dopr_outch (buffer, currlen, maxlen, '0');
+      --zpadlen;
+    }
+  }
+
+  /* Digits */
+  while (place > 0) 
+    dopr_outch (buffer, currlen, maxlen, convert[--place]);
+  
+  /* Left Justified spaces */
+  while (spadlen < 0) {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    ++spadlen;
+  }
+}
+
+static long double abs_val (long double value)
+{
+  long double result = value;
+
+  if (value < 0)
+    result = -value;
+
+  return result;
+}
+
+static long double pow10_ (int exp)
+{
+  long double result = 1;
+
+  while (exp)
+  {
+    result *= 10;
+    exp--;
+  }
+  
+  return result;
+}
+
+static long round_ (long double value)
+{
+  long intpart;
+
+  intpart = value;
+  value = value - intpart;
+  if (value >= 0.5)
+    intpart++;
+
+  return intpart;
+}
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+		   long double fvalue, int min, int max, int flags)
+{
+  int signvalue = 0;
+  long double ufvalue;
+  char iconvert[20];
+  char fconvert[20];
+  int iplace = 0;
+  int fplace = 0;
+  int padlen = 0; /* amount to pad */
+  int zpadlen = 0; 
+  int caps = 0;
+  long intpart;
+  long fracpart;
+  
+  /* 
+   * AIX manpage says the default is 0, but Solaris says the default
+   * is 6, and sprintf on AIX defaults to 6
+   */
+  if (max < 0)
+    max = 6;
+
+  ufvalue = abs_val (fvalue);
+
+  if (fvalue < 0)
+    signvalue = '-';
+  else
+    if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
+      signvalue = '+';
+    else
+      if (flags & DP_F_SPACE)
+	signvalue = ' ';
+
+#if 0
+  if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+  intpart = ufvalue;
+
+  /* 
+   * Sorry, we only support 9 digits past the decimal because of our 
+   * conversion method
+   */
+  if (max > 9)
+    max = 9;
+
+  /* We "cheat" by converting the fractional part to integer by
+   * multiplying by a factor of 10
+   */
+  fracpart = round_ ((pow10_ (max)) * (ufvalue - intpart));
+
+  if (fracpart >= pow10_ (max))
+  {
+    intpart++;
+    fracpart -= pow10_ (max);
+  }
+
+#ifdef DEBUG_SNPRINTF
+  dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
+#endif
+
+  /* Convert integer part */
+  do {
+    iconvert[iplace++] =
+      (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
+    intpart = (intpart / 10);
+  } while(intpart && (iplace < 20));
+  if (iplace == 20) iplace--;
+  iconvert[iplace] = 0;
+
+  /* Convert fractional part */
+  do {
+    fconvert[fplace++] =
+      (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
+    fracpart = (fracpart / 10);
+  } while(fracpart && (fplace < 20));
+  if (fplace == 20) fplace--;
+  fconvert[fplace] = 0;
+
+  /* -1 for decimal point, another -1 if we are printing a sign */
+  padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
+  zpadlen = max - fplace;
+  if (zpadlen < 0)
+    zpadlen = 0;
+  if (padlen < 0) 
+    padlen = 0;
+  if (flags & DP_F_MINUS) 
+    padlen = -padlen; /* Left Justifty */
+
+  if ((flags & DP_F_ZERO) && (padlen > 0)) 
+  {
+    if (signvalue) 
+    {
+      dopr_outch (buffer, currlen, maxlen, signvalue);
+      --padlen;
+      signvalue = 0;
+    }
+    while (padlen > 0)
+    {
+      dopr_outch (buffer, currlen, maxlen, '0');
+      --padlen;
+    }
+  }
+  while (padlen > 0)
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    --padlen;
+  }
+  if (signvalue) 
+    dopr_outch (buffer, currlen, maxlen, signvalue);
+
+  while (iplace > 0) 
+    dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+  /*
+   * Decimal point.  This should probably use locale to find the correct
+   * char to print out.
+   */
+  dopr_outch (buffer, currlen, maxlen, '.');
+
+  while (fplace > 0) 
+    dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+
+  while (zpadlen > 0)
+  {
+    dopr_outch (buffer, currlen, maxlen, '0');
+    --zpadlen;
+  }
+
+  while (padlen < 0) 
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    ++padlen;
+  }
+}
+
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+  if (*currlen < maxlen)
+    buffer[(*currlen)++] = c;
+}
+#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
+
+#ifndef HAVE_VSNPRINTF
+int mutt_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+  str[0] = 0;
+  dopr(str, count, fmt, args);
+  return(strlen(str));
+}
+#endif /* !HAVE_VSNPRINTF */
+
+#ifndef HAVE_SNPRINTF
+int mutt_snprintf (char *str,size_t count,const char *fmt,...)
+{
+  VA_LOCAL_DECL;
+
+  VA_START (fmt);
+  VA_SHIFT (str, char *);
+  VA_SHIFT (count, size_t );
+  VA_SHIFT (fmt, char *);
+  (void) mutt_vsnprintf(str, count, fmt, ap);
+  VA_END;
+  return(strlen(str));
+}
+
+#ifdef TEST_SNPRINTF
+#ifndef LONG_STRING
+#define LONG_STRING 1024
+#endif
+int main (void)
+{
+  char buf1[LONG_STRING];
+  char buf2[LONG_STRING];
+  char *fp_fmt[] = {
+    "%-1.5f",
+    "%1.5f",
+    "%123.9f",
+    "%10.5f",
+    "% 10.5f",
+    "%+22.9f",
+    "%+4.9f",
+    "%01.3f",
+    "%4f",
+    "%3.1f",
+    "%3.2f",
+    NULL
+  };
+  double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
+    0.9996, 1.996, 4.136, 0};
+  char *int_fmt[] = {
+    "%-1.5d",
+    "%1.5d",
+    "%123.9d",
+    "%5.5d",
+    "%10.5d",
+    "% 10.5d",
+    "%+22.33d",
+    "%01.3d",
+    "%4d",
+    NULL
+  };
+  long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+  int x, y;
+  int fail = 0;
+  int num = 0;
+
+  printf ("Testing snprintf format codes against system sprintf...\n");
+
+  for (x = 0; fp_fmt[x] != NULL ; x++)
+    for (y = 0; fp_nums[y] != 0 ; y++)
+    {
+      mutt_snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
+      sprintf (buf2, fp_fmt[x], fp_nums[y]);
+      if (strcmp (buf1, buf2))
+      {
+	printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
+	    fp_fmt[x], buf1, buf2);
+	fail++;
+      }
+      num++;
+    }
+
+  for (x = 0; int_fmt[x] != NULL ; x++)
+    for (y = 0; int_nums[y] != 0 ; y++)
+    {
+      mutt_snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
+      sprintf (buf2, int_fmt[x], int_nums[y]);
+      if (strcmp (buf1, buf2))
+      {
+	printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
+	    int_fmt[x], buf1, buf2);
+	fail++;
+      }
+      num++;
+    }
+  printf ("%d tests failed out of %d.\n", fail, num);
+}
+#endif /* SNPRINTF_TEST */
+
+#endif /* !HAVE_SNPRINTF */
diff --git a/libtar/strdup.c b/libtar/strdup.c
new file mode 100644
index 0000000..75e9af5
--- /dev/null
+++ b/libtar/strdup.c
@@ -0,0 +1,62 @@
+/*	$OpenBSD: strdup.c,v 1.3 1997/08/20 04:18:52 millert Exp $	*/
+
+/*
+ * Copyright (c) 1988, 1993
+ *	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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)strdup.c	8.1 (Berkeley) 6/4/93";
+#else
+static char *rcsid = "$OpenBSD: strdup.c,v 1.3 1997/08/20 04:18:52 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *
+openbsd_strdup(str)
+	const char *str;
+{
+	size_t siz;
+	char *copy;
+
+	siz = strlen(str) + 1;
+	if ((copy = malloc(siz)) == NULL)
+		return(NULL);
+	(void)memcpy(copy, str, siz);
+	return(copy);
+}
diff --git a/libtar/strlcat.c b/libtar/strlcat.c
new file mode 100644
index 0000000..3912539
--- /dev/null
+++ b/libtar/strlcat.c
@@ -0,0 +1,72 @@
+/*	$OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(initial dst) + strlen(src); if retval >= siz,
+ * truncation occurred.
+ */
+size_t strlcat(dst, src, siz)
+	char *dst;
+	const char *src;
+	size_t siz;
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}
diff --git a/libtar/strlcpy.c b/libtar/strlcpy.c
new file mode 100644
index 0000000..300a28b
--- /dev/null
+++ b/libtar/strlcpy.c
@@ -0,0 +1,68 @@
+/*	$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t strlcpy(dst, src, siz)
+	char *dst;
+	const char *src;
+	size_t siz;
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0 && --n != 0) {
+		do {
+			if ((*d++ = *s++) == 0)
+				break;
+		} while (--n != 0);
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
diff --git a/libtar/strmode.c b/libtar/strmode.c
new file mode 100644
index 0000000..5e7f15e
--- /dev/null
+++ b/libtar/strmode.c
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 1990 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strmode.c,v 1.3 1997/06/13 13:57:20 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+void
+strmode(mode, p)
+	register mode_t mode;
+	register char *p;
+{
+	 /* print type */
+	switch (mode & S_IFMT) {
+	case S_IFDIR:			/* directory */
+		*p++ = 'd';
+		break;
+	case S_IFCHR:			/* character special */
+		*p++ = 'c';
+		break;
+	case S_IFBLK:			/* block special */
+		*p++ = 'b';
+		break;
+	case S_IFREG:			/* regular */
+		*p++ = '-';
+		break;
+	case S_IFLNK:			/* symbolic link */
+		*p++ = 'l';
+		break;
+	case S_IFSOCK:			/* socket */
+		*p++ = 's';
+		break;
+#ifdef S_IFIFO
+	case S_IFIFO:			/* fifo */
+		*p++ = 'p';
+		break;
+#endif
+#ifdef S_IFWHT
+	case S_IFWHT:			/* whiteout */
+		*p++ = 'w';
+		break;
+#endif
+	default:			/* unknown */
+		*p++ = '?';
+		break;
+	}
+	/* usr */
+	if (mode & S_IRUSR)
+		*p++ = 'r';
+	else
+		*p++ = '-';
+	if (mode & S_IWUSR)
+		*p++ = 'w';
+	else
+		*p++ = '-';
+	switch (mode & (S_IXUSR | S_ISUID)) {
+	case 0:
+		*p++ = '-';
+		break;
+	case S_IXUSR:
+		*p++ = 'x';
+		break;
+	case S_ISUID:
+		*p++ = 'S';
+		break;
+	case S_IXUSR | S_ISUID:
+		*p++ = 's';
+		break;
+	}
+	/* group */
+	if (mode & S_IRGRP)
+		*p++ = 'r';
+	else
+		*p++ = '-';
+	if (mode & S_IWGRP)
+		*p++ = 'w';
+	else
+		*p++ = '-';
+	switch (mode & (S_IXGRP | S_ISGID)) {
+	case 0:
+		*p++ = '-';
+		break;
+	case S_IXGRP:
+		*p++ = 'x';
+		break;
+	case S_ISGID:
+		*p++ = 'S';
+		break;
+	case S_IXGRP | S_ISGID:
+		*p++ = 's';
+		break;
+	}
+	/* other */
+	if (mode & S_IROTH)
+		*p++ = 'r';
+	else
+		*p++ = '-';
+	if (mode & S_IWOTH)
+		*p++ = 'w';
+	else
+		*p++ = '-';
+	switch (mode & (S_IXOTH | S_ISVTX)) {
+	case 0:
+		*p++ = '-';
+		break;
+	case S_IXOTH:
+		*p++ = 'x';
+		break;
+	case S_ISVTX:
+		*p++ = 'T';
+		break;
+	case S_IXOTH | S_ISVTX:
+		*p++ = 't';
+		break;
+	}
+	*p++ = ' ';		/* will be a '+' if ACL's implemented */
+	*p = '\0';
+}
diff --git a/libtar/strrstr.c b/libtar/strrstr.c
new file mode 100644
index 0000000..097ef91
--- /dev/null
+++ b/libtar/strrstr.c
@@ -0,0 +1,40 @@
+/*
+**  Copyright 1998-2002 University of Illinois Board of Trustees
+**  Copyright 1998-2002 Mark D. Roth
+**  All rights reserved.
+**
+**  strrstr.c - strrstr() function for compatibility library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <string.h>
+
+
+/*
+** find the last occurrance of find in string
+*/
+char *
+strrstr(char *string, char *find)
+{
+	size_t stringlen, findlen;
+	char *cp;
+
+	findlen = strlen(find);
+	stringlen = strlen(string);
+	if (findlen > stringlen)
+		return NULL;
+
+	for (cp = string + stringlen - findlen; cp >= string; cp--)
+		if (strncmp(cp, find, findlen) == 0)
+			return cp;
+
+	return NULL;
+}
+
+
diff --git a/libtar/strsep.c b/libtar/strsep.c
new file mode 100644
index 0000000..3132962
--- /dev/null
+++ b/libtar/strsep.c
@@ -0,0 +1,87 @@
+/*	$OpenBSD: strsep.c,v 1.3 1997/08/20 04:28:14 millert Exp $	*/
+
+/*-
+ * Copyright (c) 1990, 1993
+ *	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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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 <string.h>
+#include <stdio.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)strsep.c	8.1 (Berkeley) 6/4/93";
+#else
+static char *rcsid = "$OpenBSD: strsep.c,v 1.3 1997/08/20 04:28:14 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef HAVE_STRSEP
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.  
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *
+strsep(stringp, delim)
+	register char **stringp;
+	register const char *delim;
+{
+	register char *s;
+	register const char *spanp;
+	register int c, sc;
+	char *tok;
+
+	if ((s = *stringp) == NULL)
+		return (NULL);
+	for (tok = s;;) {
+		c = *s++;
+		spanp = delim;
+		do {
+			if ((sc = *spanp++) == c) {
+				if (c == 0)
+					s = NULL;
+				else
+					s[-1] = 0;
+				*stringp = s;
+				return (tok);
+			}
+		} while (sc != 0);
+	}
+	/* NOTREACHED */
+}
+#endif /* ! HAVE_STRSEP */
diff --git a/libtar/tar.h b/libtar/tar.h
new file mode 100644
index 0000000..ddfef75
--- /dev/null
+++ b/libtar/tar.h
@@ -0,0 +1,108 @@
+/* Extended tar format from POSIX.1.
+   Copyright (C) 1992, 1996 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by David J. MacKenzie.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef	_TAR_H
+#define	_TAR_H	1
+
+/* A tar archive consists of 512-byte blocks.
+   Each file in the archive has a header block followed by 0+ data blocks.
+   Two blocks of NUL bytes indicate the end of the archive.  */
+
+/* The fields of header blocks:
+   All strings are stored as ISO 646 (approximately ASCII) strings.
+
+   Fields are numeric unless otherwise noted below; numbers are ISO 646
+   representations of octal numbers, with leading zeros as needed.
+
+   linkname is only valid when typeflag==LNKTYPE.  It doesn't use prefix;
+   files that are links to pathnames >100 chars long can not be stored
+   in a tar archive.
+
+   If typeflag=={LNKTYPE,SYMTYPE,DIRTYPE} then size must be 0.
+
+   devmajor and devminor are only valid for typeflag=={BLKTYPE,CHRTYPE}.
+
+   chksum contains the sum of all 512 bytes in the header block,
+   treating each byte as an 8-bit unsigned value and treating the
+   8 bytes of chksum as blank characters.
+
+   uname and gname are used in preference to uid and gid, if those
+   names exist locally.
+
+   Field Name	Byte Offset	Length in Bytes	Field Type
+   name		0		100		NUL-terminated if NUL fits
+   mode		100		8
+   uid		108		8
+   gid		116		8
+   size		124		12
+   mtime	136		12
+   chksum	148		8
+   typeflag	156		1		see below
+   linkname	157		100		NUL-terminated if NUL fits
+   magic	257		6		must be TMAGIC (NUL term.)
+   version	263		2		must be TVERSION
+   uname	265		32		NUL-terminated
+   gname	297		32		NUL-terminated
+   devmajor	329		8
+   devminor	337		8
+   prefix	345		155		NUL-terminated if NUL fits
+
+   If the first character of prefix is '\0', the file name is name;
+   otherwise, it is prefix/name.  Files whose pathnames don't fit in that
+   length can not be stored in a tar archive.  */
+
+/* The bits in mode: */
+#define TSUID	04000
+#define TSGID	02000
+#define TSVTX	01000
+#define TUREAD	00400
+#define TUWRITE	00200
+#define TUEXEC	00100
+#define TGREAD	00040
+#define TGWRITE	00020
+#define TGEXEC	00010
+#define TOREAD	00004
+#define TOWRITE	00002
+#define TOEXEC	00001
+
+/* The values for typeflag:
+   Values 'A'-'Z' are reserved for custom implementations.
+   All other values are reserved for future POSIX.1 revisions.  */
+
+#define REGTYPE		'0'	/* Regular file (preferred code).  */
+#define AREGTYPE	'\0'	/* Regular file (alternate code).  */
+#define LNKTYPE		'1'	/* Hard link.  */
+#define SYMTYPE		'2'	/* Symbolic link (hard if not supported).  */
+#define CHRTYPE		'3'	/* Character special.  */
+#define BLKTYPE		'4'	/* Block special.  */
+#define DIRTYPE		'5'	/* Directory.  */
+#define FIFOTYPE	'6'	/* Named pipe.  */
+#define CONTTYPE	'7'	/* Contiguous file */
+ /* (regular file if not supported).  */
+
+/* Contents of magic field and its length.  */
+#define TMAGIC	"ustar"
+#define TMAGLEN	6
+
+/* Contents of the version field and its length.  */
+#define TVERSION	"00"
+#define TVERSLEN	2
+
+#endif /* tar.h */
diff --git a/libtar/util.c b/libtar/util.c
new file mode 100644
index 0000000..31e8315
--- /dev/null
+++ b/libtar/util.c
@@ -0,0 +1,165 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  util.c - miscellaneous utility code for libtar
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+
+/* hashing function for pathnames */
+int
+path_hashfunc(char *key, int numbuckets)
+{
+	char buf[MAXPATHLEN];
+	char *p;
+
+	strcpy(buf, key);
+	p = basename(buf);
+
+	return (((unsigned int)p[0]) % numbuckets);
+}
+
+
+/* matching function for dev_t's */
+int
+dev_match(dev_t *dev1, dev_t *dev2)
+{
+	return !memcmp(dev1, dev2, sizeof(dev_t));
+}
+
+
+/* matching function for ino_t's */
+int
+ino_match(ino_t *ino1, ino_t *ino2)
+{
+	return !memcmp(ino1, ino2, sizeof(ino_t));
+}
+
+
+/* hashing function for dev_t's */
+int
+dev_hash(dev_t *dev)
+{
+	return *dev % 16;
+}
+
+
+/* hashing function for ino_t's */
+int
+ino_hash(ino_t *inode)
+{
+	return *inode % 256;
+}
+
+
+/*
+** mkdirhier() - create all directories in a given path
+** returns:
+**	0			success
+**	1			all directories already exist
+**	-1 (and sets errno)	error
+*/
+int
+mkdirhier(char *path)
+{
+	char src[MAXPATHLEN], dst[MAXPATHLEN] = "";
+	char *dirp, *nextp = src;
+	int retval = 1;
+
+	if (strlcpy(src, path, sizeof(src)) > sizeof(src))
+	{
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+
+	if (path[0] == '/')
+		strcpy(dst, "/");
+
+	while ((dirp = strsep(&nextp, "/")) != NULL)
+	{
+		if (*dirp == '\0')
+			continue;
+
+		if (dst[0] != '\0')
+			strcat(dst, "/");
+		strcat(dst, dirp);
+
+		if (mkdir(dst, 0777) == -1)
+		{
+			if (errno != EEXIST)
+				return -1;
+		}
+		else
+			retval = 0;
+	}
+
+	return retval;
+}
+
+
+/* calculate header checksum */
+int
+th_crc_calc(TAR *t)
+{
+	int i, sum = 0;
+
+	for (i = 0; i < T_BLOCKSIZE; i++)
+		sum += ((unsigned char *)(&(t->th_buf)))[i];
+	for (i = 0; i < 8; i++)
+		sum += (' ' - (unsigned char)t->th_buf.chksum[i]);
+
+	return sum;
+}
+
+
+/* calculate a signed header checksum */
+int
+th_signed_crc_calc(TAR *t)
+{
+	int i, sum = 0;
+
+	for (i = 0; i < T_BLOCKSIZE; i++)
+		sum += ((signed char *)(&(t->th_buf)))[i];
+	for (i = 0; i < 8; i++)
+		sum += (' ' - (signed char)t->th_buf.chksum[i]);
+
+	return sum;
+}
+
+
+/* string-octal to integer conversion */
+int
+oct_to_int(char *oct)
+{
+	int i;
+
+	sscanf(oct, "%o", &i);
+
+	return i;
+}
+
+
+/* integer to string-octal conversion, no NULL */
+void
+int_to_oct_nonull(int num, char *oct, size_t octlen)
+{
+	snprintf(oct, octlen, "%*lo", octlen - 1, (unsigned long)num);
+	oct[octlen - 1] = ' ';
+}
+
+
diff --git a/libtar/wrapper.c b/libtar/wrapper.c
new file mode 100644
index 0000000..82f045f
--- /dev/null
+++ b/libtar/wrapper.c
@@ -0,0 +1,213 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  wrapper.c - libtar high-level wrapper code
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#define DEBUG
+#include <internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+int
+tar_extract_glob(TAR *t, char *globname, char *prefix)
+{
+	char *filename;
+	char buf[MAXPATHLEN];
+	int i, fd = 0;
+
+	while ((i = th_read(t)) == 0)
+	{
+		filename = th_get_pathname(t);
+		if (fnmatch(globname, filename, FNM_PATHNAME | FNM_PERIOD))
+		{
+			if (TH_ISREG(t) && tar_skip_regfile(t))
+				return -1;
+			continue;
+		}
+		if (t->options & TAR_VERBOSE)
+			th_print_long_ls(t);
+		if (prefix != NULL)
+			snprintf(buf, sizeof(buf), "%s/%s", prefix, filename);
+		else
+			strlcpy(buf, filename, sizeof(buf));
+		if (tar_extract_file(t, filename, prefix, &fd) != 0)
+			return -1;
+	}
+
+	return (i == 1 ? 0 : -1);
+}
+
+
+int
+tar_extract_all(TAR *t, char *prefix, const int *progress_fd)
+{
+	char *filename;
+	char buf[MAXPATHLEN];
+	int i;
+	printf("prefix: %s\n", prefix);
+#ifdef DEBUG
+	printf("==> tar_extract_all(TAR *t, \"%s\")\n",
+	       (prefix ? prefix : "(null)"));
+#endif
+	while ((i = th_read(t)) == 0)
+	{
+#ifdef DEBUG
+		puts("    tar_extract_all(): calling th_get_pathname()");
+#endif
+		filename = th_get_pathname(t);
+		if (t->options & TAR_VERBOSE)
+			th_print_long_ls(t);
+		if (prefix != NULL)
+			snprintf(buf, sizeof(buf), "%s/%s", prefix, filename);
+		else
+			strlcpy(buf, filename, sizeof(buf));
+#ifdef DEBUG
+		printf("    tar_extract_all(): calling tar_extract_file(t, "
+		       "\"%s\")\n", buf);
+#endif
+		printf("item name: '%s'\n", filename);
+		if (tar_extract_file(t, buf, prefix, progress_fd) != 0)
+			return -1;
+	}
+	return (i == 1 ? 0 : -1);
+}
+
+
+int
+tar_append_tree(TAR *t, char *realdir, char *savedir, char *exclude)
+{
+#ifdef DEBUG
+	printf("==> tar_append_tree(0x%lx, \"%s\", \"%s\")\n",
+	       (long unsigned int)t, realdir, (savedir ? savedir : "[NULL]"));
+#endif
+
+	char temp[1024];
+	int skip = 0, i, n_spaces = 0;
+	char ** excluded = NULL;
+	char * p = NULL;
+	if (exclude) {
+		strcpy(temp, exclude);
+		p = strtok(exclude, " ");
+		if (p == NULL) {
+			excluded = realloc(excluded, sizeof(char*) * (++n_spaces));
+			excluded[0] = temp;
+		} else {
+			while (p) {
+				excluded = realloc(excluded, sizeof(char*) * (++n_spaces));
+				excluded[n_spaces-1] = p;
+				p = strtok(NULL, " ");
+			}
+		}
+		excluded = realloc(excluded, sizeof(char*) * (n_spaces+1));
+		excluded[n_spaces] = 0;
+		for (i = 0; i < (n_spaces+1); i++) {
+			if (realdir == excluded[i]) {
+				printf("    excluding '%s'\n", excluded[i]);
+				skip = 1;
+				break;
+			}
+		}
+	}
+	if (skip == 0) {
+		if (tar_append_file(t, realdir, savedir) != 0)
+			return -1;
+	}
+
+	char realpath[MAXPATHLEN];
+	char savepath[MAXPATHLEN];
+	struct dirent *dent;
+	DIR *dp;
+	struct stat s;
+
+	dp = opendir(realdir);
+	if (dp == NULL) {
+		if (errno == ENOTDIR)
+			return 0;
+		return -1;
+	}
+	while ((dent = readdir(dp)) != NULL) {
+		if(strcmp(dent->d_name, ".") == 0
+		|| strcmp(dent->d_name, "..") == 0)
+			continue;
+
+		if (exclude) {
+			int omit = 0;
+			for (i = 0; i < (n_spaces+1); i++) {
+				if (excluded[i] != NULL) {
+						if (strcmp(dent->d_name, excluded[i]) == 0 || strcmp(excluded[i], realdir) == 0) {
+							printf("    excluding '%s'\n", excluded[i]);
+							omit = 1;
+							break;
+						}
+				}
+			}
+			if (omit)
+				continue;
+		}
+
+		snprintf(realpath, MAXPATHLEN, "%s/%s", realdir, dent->d_name);
+		if (savedir)
+			snprintf(savepath, MAXPATHLEN, "%s/%s", savedir, dent->d_name);
+
+		if (lstat(realpath, &s) != 0)
+			return -1;
+
+		if (S_ISDIR(s.st_mode)) {
+			if (tar_append_tree(t, realpath, (savedir ? savepath : NULL), (exclude ? exclude : NULL)) != 0)
+				return -1;
+			continue;
+		} else {
+			if (tar_append_file(t, realpath, (savedir ? savepath : NULL)) != 0)
+				return -1;
+			continue;
+		}
+	}
+	closedir(dp);
+	free(excluded);
+
+	return 0;
+}
+
+
+int
+tar_find(TAR *t, char *searchstr)
+{
+	if (!searchstr)
+		return 0;
+
+	char *filename;
+	int i, entryfound = 0;
+#ifdef DEBUG
+	printf("==> tar_find(0x%lx, %s)\n", (long unsigned int)t, searchstr);
+#endif
+	while ((i = th_read(t)) == 0) {
+		filename = th_get_pathname(t);
+		if (fnmatch(searchstr, filename, FNM_FILE_NAME | FNM_PERIOD) == 0) {
+			entryfound++;
+#ifdef DEBUG
+			printf("Found matching entry: %s\n", filename);
+#endif
+			break;
+		}
+	}
+#ifdef DEBUG
+	if (!entryfound)
+		printf("No matching entry found.\n");
+#endif
+
+	return entryfound;
+}
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index 04956d8..4430a2b 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -19,14 +19,13 @@
 	sockets.c \
 	services.c \
 	usb_linux_client.c \
-	utils.c
+	utils.c \
+       ../../../system/core/adb/transport_local.c
 
 LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
 LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
-LOCAL_C_INCLUDES += bootable/recovery
-
+LOCAL_MODULE_TAGS := eng
 LOCAL_MODULE := libminadbd
 
-LOCAL_STATIC_LIBRARIES := libfusesideload libcutils libc
-
-include $(BUILD_STATIC_LIBRARY)
+LOCAL_SHARED_LIBRARIES := libfusesideload libcutils libc
+include $(BUILD_SHARED_LIBRARY)
diff --git a/minadbd/adb.c b/minadbd/adb.c
index 127d072..c35e830 100644
--- a/minadbd/adb.c
+++ b/minadbd/adb.c
@@ -38,6 +38,8 @@
 
 static const char *adb_device_banner = "sideload";
 
+char ADB_SIDELOAD_FILENAME[255];
+
 void fatal(const char *fmt, ...)
 {
     va_list ap;
@@ -376,8 +378,9 @@
     usb_cleanup();
 }
 
-int adb_main()
+int adb_main(const char* path)
 {
+	strcpy(ADB_SIDELOAD_FILENAME, path);
     atexit(adb_cleanup);
 #if defined(HAVE_FORKEXEC)
     // No SIGCHLD. Let the service subproc handle its children.
@@ -392,6 +395,19 @@
         usb_init();
     }
 
+/* Remove this so that perms work properly
+    if (setgid(AID_SHELL) != 0) {
+        fprintf(stderr, "failed to setgid to shell\n");
+        exit(1);
+    }
+    if (setuid(AID_SHELL) != 0) {
+        fprintf(stderr, "failed to setuid to shell\n");
+        exit(1);
+    }
+
+    fprintf(stderr, "userid is %d\n", getuid());
+*/
+
     D("Event loop starting\n");
 
     fdevent_loop();
diff --git a/minadbd/adb.h b/minadbd/adb.h
index 714868f..08ee989 100644
--- a/minadbd/adb.h
+++ b/minadbd/adb.h
@@ -217,7 +217,7 @@
 
 void get_my_path(char *s, size_t maxLen);
 int launch_server(int server_port);
-int adb_main();
+int adb_main(const char* path);
 
 
 /* transports are ref-counted
@@ -421,4 +421,7 @@
 int sendfailmsg(int fd, const char *reason);
 int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
 
+//#define ADB_SIDELOAD_FILENAME "/tmp/update.zip"
+extern char ADB_SIDELOAD_FILENAME[255];
+
 #endif
diff --git a/minui/Android.mk b/minui/Android.mk
index df4aac1..7b40156 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -1,13 +1,30 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := graphics.c graphics_adf.c graphics_fbdev.c events.c \
-	resources.c
+LOCAL_SRC_FILES := graphics_adf.c graphics_fbdev.c graphics_overlay.c events.c resources.c
+ifneq ($(BOARD_CUSTOM_GRAPHICS),)
+  LOCAL_SRC_FILES += $(BOARD_CUSTOM_GRAPHICS)
+else
+  LOCAL_SRC_FILES += graphics.c
+endif
 
 LOCAL_C_INCLUDES +=\
     external/libpng\
     external/zlib
 
+ifeq ($(TW_TARGET_USES_QCOM_BSP), true)
+  LOCAL_CFLAGS += -DMSM_BSP
+  ifeq ($(TARGET_PREBUILT_KERNEL),)
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
+    LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
+  else
+    LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include
+  endif
+else
+  LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include
+endif
+
+LOCAL_STATIC_LIBRARY := libpng
 LOCAL_WHOLE_STATIC_LIBRARIES += libadf
 
 LOCAL_MODULE := libminui
@@ -29,4 +46,79 @@
   LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0
 endif
 
+ifneq ($(TW_BRIGHTNESS_PATH),)
+  LOCAL_CFLAGS += -DTW_BRIGHTNESS_PATH=\"$(TW_BRIGHTNESS_PATH)\"
+endif
+ifneq ($(TW_MAX_BRIGHTNESS),)
+  LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=$(TW_MAX_BRIGHTNESS)
+else
+  LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=255
+endif
+ifneq ($(TW_NO_SCREEN_BLANK),)
+  LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK
+endif
+
 include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := graphics_adf.c graphics_fbdev.c graphics_overlay.c events.c resources.c
+ifneq ($(BOARD_CUSTOM_GRAPHICS),)
+  LOCAL_SRC_FILES += $(BOARD_CUSTOM_GRAPHICS)
+else
+  LOCAL_SRC_FILES += graphics.c
+endif
+
+ifeq ($(TW_TARGET_USES_QCOM_BSP), true)
+  LOCAL_CFLAGS += -DMSM_BSP
+  ifeq ($(TARGET_PREBUILT_KERNEL),)
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
+    LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
+  else
+    LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include
+  endif
+else
+  LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include
+endif
+
+LOCAL_C_INCLUDES +=\
+    external/libpng\
+    external/zlib
+
+LOCAL_MODULE := libminui
+
+LOCAL_ARM_MODE:= arm
+LOCAL_SHARED_LIBRARIES := libpng libpixelflinger
+LOCAL_WHOLE_STATIC_LIBRARIES += libadf
+# This used to compare against values in double-quotes (which are just
+# ordinary characters in this context).  Strip double-quotes from the
+# value so that either will work.
+
+ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888)
+  LOCAL_CFLAGS += -DRECOVERY_RGBX
+endif
+ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),BGRA_8888)
+  LOCAL_CFLAGS += -DRECOVERY_BGRA
+endif
+
+ifneq ($(TARGET_RECOVERY_OVERSCAN_PERCENT),)
+  LOCAL_CFLAGS += -DOVERSCAN_PERCENT=$(TARGET_RECOVERY_OVERSCAN_PERCENT)
+else
+  LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0
+endif
+
+ifneq ($(TW_BRIGHTNESS_PATH),)
+  LOCAL_CFLAGS += -DTW_BRIGHTNESS_PATH=\"$(TW_BRIGHTNESS_PATH)\"
+endif
+ifneq ($(TW_MAX_BRIGHTNESS),)
+  LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=$(TW_MAX_BRIGHTNESS)
+else
+  LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=255
+endif
+ifneq ($(TW_NO_SCREEN_BLANK),)
+  LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK
+endif
+
+LOCAL_CFLAGS += -DFASTMMI_FEATURE
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/minui/graphics.c b/minui/graphics.c
index 6049d85..470e735 100644
--- a/minui/graphics.c
+++ b/minui/graphics.c
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 
@@ -56,6 +57,170 @@
 
 static GRSurface* gr_draw = NULL;
 
+static struct fb_var_screeninfo vi;
+static struct fb_fix_screeninfo fi;
+
+static bool has_overlay = false;
+
+bool target_has_overlay(char *version);
+int free_ion_mem(void);
+int alloc_ion_mem(unsigned int size);
+int allocate_overlay(int fd, GGLSurface gr_fb[]);
+int free_overlay(int fd);
+int overlay_display_frame(int fd, GGLubyte* data, size_t size);
+
+static int get_framebuffer(GGLSurface *fb)
+{
+    int fd;
+    void *bits;
+
+    fd = open("/dev/graphics/fb0", O_RDWR);
+    if (fd < 0) {
+        perror("cannot open fb0");
+        return -1;
+    }
+
+    if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
+        perror("failed to get fb0 info");
+        close(fd);
+        return -1;
+    }
+
+    vi.bits_per_pixel = PIXEL_SIZE * 8;
+    if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_BGRA_8888) {
+      vi.red.offset     = 8;
+      vi.red.length     = 8;
+      vi.green.offset   = 16;
+      vi.green.length   = 8;
+      vi.blue.offset    = 24;
+      vi.blue.length    = 8;
+      vi.transp.offset  = 0;
+      vi.transp.length  = 8;
+    } else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGBX_8888) {
+      vi.red.offset     = 24;
+      vi.red.length     = 8;
+      vi.green.offset   = 16;
+      vi.green.length   = 8;
+      vi.blue.offset    = 8;
+      vi.blue.length    = 8;
+      vi.transp.offset  = 0;
+      vi.transp.length  = 8;
+    } else { /* RGB565*/
+      vi.red.offset     = 11;
+      vi.red.length     = 5;
+      vi.green.offset   = 5;
+      vi.green.length   = 6;
+      vi.blue.offset    = 0;
+      vi.blue.length    = 5;
+      vi.transp.offset  = 0;
+      vi.transp.length  = 0;
+    }
+    if (ioctl(fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
+        perror("failed to put fb0 info");
+        close(fd);
+        return -1;
+    }
+
+    if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
+        perror("failed to get fb0 info");
+        close(fd);
+        return -1;
+    }
+
+    has_overlay = target_has_overlay(fi.id);
+
+    if (!has_overlay) {
+        bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+        if (bits == MAP_FAILED) {
+            perror("failed to mmap framebuffer");
+            close(fd);
+            return -1;
+        }
+    }
+
+    overscan_offset_x = vi.xres * overscan_percent / 100;
+    overscan_offset_y = vi.yres * overscan_percent / 100;
+
+    fb->version = sizeof(*fb);
+    fb->width = vi.xres;
+    fb->height = vi.yres;
+    fb->stride = fi.line_length/PIXEL_SIZE;
+    fb->format = PIXEL_FORMAT;
+    if (!has_overlay) {
+        fb->data = bits;
+        memset(fb->data, 0, vi.yres * fi.line_length);
+    }
+
+    fb++;
+
+    /* check if we can use double buffering */
+    if (vi.yres * fi.line_length * 2 > fi.smem_len)
+        return fd;
+
+    fb->version = sizeof(*fb);
+    fb->width = vi.xres;
+    fb->height = vi.yres;
+    fb->stride = fi.line_length/PIXEL_SIZE;
+    fb->format = PIXEL_FORMAT;
+    if (!has_overlay) {
+        fb->data = (void*) (((unsigned) bits) + vi.yres * fi.line_length);
+        memset(fb->data, 0, vi.yres * fi.line_length);
+    }
+
+    return fd;
+}
+
+static void get_memory_surface(GGLSurface* ms) {
+  ms->version = sizeof(*ms);
+  ms->width = vi.xres;
+  ms->height = vi.yres;
+  ms->stride = fi.line_length/PIXEL_SIZE;
+  ms->data = malloc(fi.line_length * vi.yres);
+  ms->format = PIXEL_FORMAT;
+}
+
+static void set_active_framebuffer(unsigned n)
+{
+    if (n > 1 || !double_buffering) return;
+    vi.yres_virtual = vi.yres * NUM_BUFFERS;
+    vi.yoffset = n * vi.yres;
+    vi.bits_per_pixel = PIXEL_SIZE * 8;
+    if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
+        perror("active fb swap failed");
+    }
+}
+
+void gr_flip(void)
+{
+    if (-EINVAL == overlay_display_frame(gr_fb_fd, gr_mem_surface.data,
+                                         (fi.line_length * vi.yres))) {
+        GGLContext *gl = gr_context;
+
+        /* swap front and back buffers */
+        if (double_buffering)
+            gr_active_fb = (gr_active_fb + 1) & 1;
+
+        /* copy data from the in-memory surface to the buffer we're about
+         * to make active. */
+        memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data,
+               fi.line_length * vi.yres);
+
+        /* inform the display driver */
+        set_active_framebuffer(gr_active_fb);
+    }
+}
+
+void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+    GGLContext *gl = gr_context;
+    GGLint color[4];
+    color[0] = ((r << 8) | r) + 1;
+    color[1] = ((g << 8) | g) + 1;
+    color[2] = ((b << 8) | b) + 1;
+    color[3] = ((a << 8) | a) + 1;
+    gl->color4xv(gl, color);
+}
+
 static bool outside(int x, int y)
 {
     return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height;
@@ -72,6 +237,11 @@
     *y = gr_font->cheight;
 }
 
+int gr_text(int x, int y, const char *s, ...)
+{
+    return gr_text_impl(x, y, s, 0);
+}
+
 static void text_blend(unsigned char* src_p, int src_row_bytes,
                        unsigned char* dst_p, int dst_row_bytes,
                        int width, int height)
@@ -106,7 +276,7 @@
 }
 
 
-void gr_text(int x, int y, const char *s, int bold)
+int gr_text_impl(int x, int y, const char *s, int bold)
 {
     GRFont *font = gr_font;
     unsigned off;
@@ -377,6 +547,17 @@
         }
     }
 
+    get_memory_surface(&gr_mem_surface);
+
+    fprintf(stderr, "framebuffer: fd %d (%d x %d)\n",
+            gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height);
+
+    /* start with 0 as front (displayed) and 1 as back (drawing) */
+    gr_active_fb = 0;
+    if (!has_overlay)
+        set_active_framebuffer(0);
+    gl->colorBuffer(gl, &gr_mem_surface);
+
     if (!gr_draw) {
         gr_backend = open_fbdev();
         gr_draw = gr_backend->init(gr_backend);
@@ -391,11 +572,22 @@
     gr_flip();
     gr_flip();
 
+    if (!alloc_ion_mem(fi.line_length * vi.yres))
+        allocate_overlay(gr_fb_fd, gr_framebuffer);
+
     return 0;
 }
 
 void gr_exit(void)
 {
+    free_overlay(gr_fb_fd);
+    free_ion_mem();
+
+    close(gr_fb_fd);
+    gr_fb_fd = -1;
+
+    free(gr_mem_surface.data);
+
     gr_backend->exit(gr_backend);
 
     ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT);
@@ -415,5 +607,30 @@
 
 void gr_fb_blank(bool blank)
 {
+#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
+    int fd;
+    char brightness[4];
+    snprintf(brightness, 4, "%03d", TW_MAX_BRIGHTNESS/2);
+
+    fd = open(TW_BRIGHTNESS_PATH, O_RDWR);
+    if (fd < 0) {
+        perror("cannot open LCD backlight");
+        return;
+    }
+    write(fd, blank ? "000" : brightness, 3);
+    close(fd);
+#else
     gr_backend->blank(gr_backend, blank);
+
+    if (blank)
+        free_overlay(gr_fb_fd);
+
+    if (!blank)
+        allocate_overlay(gr_fb_fd, gr_framebuffer);
+#endif
+}
+
+void gr_get_memory_surface(gr_surface surface)
+{
+    get_memory_surface( (GGLSurface*) surface);
 }
diff --git a/minui/graphics_overlay.c b/minui/graphics_overlay.c
new file mode 100644
index 0000000..5677ad7
--- /dev/null
+++ b/minui/graphics_overlay.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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 <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <linux/fb.h>
+#include <linux/kd.h>
+
+#ifdef MSM_BSP
+#include <linux/msm_mdp.h>
+#include <linux/msm_ion.h>
+#endif
+
+#include <pixelflinger/pixelflinger.h>
+
+#include "minui.h"
+
+#define MDP_V4_0 400
+
+#ifdef MSM_BSP
+#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1))
+
+typedef struct {
+    unsigned char *mem_buf;
+    int size;
+    int ion_fd;
+    int mem_fd;
+    struct ion_handle_data handle_data;
+} memInfo;
+
+static int overlay_id = MSMFB_NEW_REQUEST;
+static memInfo mem_info;
+
+static int map_mdp_pixel_format()
+{
+    int format = MDP_RGB_565;
+#if defined(RECOVERY_BGRA)
+    format = MDP_BGRA_8888;
+#elif defined(RECOVERY_RGBX)
+    format = MDP_RGBA_8888;
+#endif
+    return format;
+}
+
+static bool overlay_supported = false;
+
+bool target_has_overlay(char *version)
+{
+    int ret;
+    int mdp_version;
+
+    if (strlen(version) >= 8) {
+        if(!strncmp(version, "msmfb", strlen("msmfb"))) {
+            char str_ver[4];
+            memcpy(str_ver, version + strlen("msmfb"), 3);
+            str_ver[3] = '\0';
+            mdp_version = atoi(str_ver);
+            if (mdp_version >= MDP_V4_0) {
+                overlay_supported = true;
+            }
+        } else if (!strncmp(version, "mdssfb", strlen("mdssfb"))) {
+            overlay_supported = true;
+        }
+    }
+
+    return overlay_supported;
+}
+
+int free_ion_mem(void) {
+    if (!overlay_supported)
+        return -EINVAL;
+
+    int ret = 0;
+
+    if (mem_info.mem_buf)
+        munmap(mem_info.mem_buf, mem_info.size);
+
+    if (mem_info.ion_fd >= 0) {
+        ret = ioctl(mem_info.ion_fd, ION_IOC_FREE, &mem_info.handle_data);
+        if (ret < 0)
+            perror("free_mem failed ");
+    }
+
+    if (mem_info.mem_fd >= 0)
+        close(mem_info.mem_fd);
+    if (mem_info.ion_fd >= 0)
+        close(mem_info.ion_fd);
+
+    memset(&mem_info, 0, sizeof(mem_info));
+    mem_info.mem_fd = -1;
+    mem_info.ion_fd = -1;
+    return 0;
+}
+
+int alloc_ion_mem(unsigned int size)
+{
+    if (!overlay_supported)
+        return -EINVAL;
+    int result;
+    struct ion_fd_data fd_data;
+    struct ion_allocation_data ionAllocData;
+
+    mem_info.ion_fd = open("/dev/ion", O_RDWR|O_DSYNC);
+    if (mem_info.ion_fd < 0) {
+        perror("ERROR: Can't open ion ");
+        return -errno;
+    }
+
+    ionAllocData.flags = 0;
+    ionAllocData.len = size;
+    ionAllocData.align = sysconf(_SC_PAGESIZE);
+    ionAllocData.heap_mask =
+            ION_HEAP(ION_IOMMU_HEAP_ID) |
+            ION_HEAP(ION_SYSTEM_CONTIG_HEAP_ID);
+
+    result = ioctl(mem_info.ion_fd, ION_IOC_ALLOC,  &ionAllocData);
+    if(result){
+        perror("ION_IOC_ALLOC Failed ");
+        close(mem_info.ion_fd);
+        return result;
+    }
+
+    fd_data.handle = ionAllocData.handle;
+    mem_info.handle_data.handle = ionAllocData.handle;
+    result = ioctl(mem_info.ion_fd, ION_IOC_MAP, &fd_data);
+    if (result) {
+        perror("ION_IOC_MAP Failed ");
+        free_ion_mem();
+        return result;
+    }
+    mem_info.mem_buf = (unsigned char *)mmap(NULL, size, PROT_READ |
+                PROT_WRITE, MAP_SHARED, fd_data.fd, 0);
+    mem_info.mem_fd = fd_data.fd;
+
+    if (!mem_info.mem_buf) {
+        perror("ERROR: mem_buf MAP_FAILED ");
+        free_ion_mem();
+        return -ENOMEM;
+    }
+
+    return 0;
+}
+
+int allocate_overlay(int fd, GGLSurface gr_fb[])
+{
+    if (!overlay_supported)
+        return -EINVAL;
+
+    struct mdp_overlay overlay;
+    int ret = 0;
+
+    memset(&overlay, 0 , sizeof (struct mdp_overlay));
+
+    /* Fill Overlay Data */
+
+    overlay.src.width  = ALIGN(gr_fb[0].width, 32);
+    overlay.src.height = gr_fb[0].height;
+    overlay.src.format = map_mdp_pixel_format();
+    overlay.src_rect.w = gr_fb[0].width;
+    overlay.src_rect.h = gr_fb[0].height;
+    overlay.dst_rect.w = gr_fb[0].width;
+    overlay.dst_rect.h = gr_fb[0].height;
+    overlay.alpha = 0xFF;
+    overlay.transp_mask = MDP_TRANSP_NOP;
+    overlay.id = MSMFB_NEW_REQUEST;
+    ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlay);
+    if (ret < 0) {
+        perror("Overlay Set Failed");
+        return ret;
+    }
+
+    overlay_id = overlay.id;
+    return 0;
+}
+
+int free_overlay(int fd)
+{
+    if (!overlay_supported)
+        return -EINVAL;
+
+    int ret = 0;
+    struct mdp_display_commit ext_commit;
+
+    if (overlay_id != MSMFB_NEW_REQUEST) {
+        ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlay_id);
+        if (ret) {
+            perror("Overlay Unset Failed");
+            overlay_id = MSMFB_NEW_REQUEST;
+            return ret;
+        }
+
+        memset(&ext_commit, 0, sizeof(struct mdp_display_commit));
+        ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
+        ext_commit.wait_for_finish = 1;
+        ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit);
+        if (ret < 0) {
+            perror("ERROR: Clear MSMFB_DISPLAY_COMMIT failed!");
+            overlay_id = MSMFB_NEW_REQUEST;
+            return ret;
+        }
+
+        overlay_id = MSMFB_NEW_REQUEST;
+    }
+    return 0;
+}
+
+int overlay_display_frame(int fd, GGLubyte* data, size_t size)
+{
+    if (!overlay_supported)
+        return -EINVAL;
+    int ret = 0;
+    struct msmfb_overlay_data ovdata;
+    struct mdp_display_commit ext_commit;
+
+    if (overlay_id == MSMFB_NEW_REQUEST) {
+        perror("display_frame failed, no overlay\n");
+        return 0;
+    }
+
+    memcpy(mem_info.mem_buf, data, size);
+
+    memset(&ovdata, 0, sizeof(struct msmfb_overlay_data));
+
+    ovdata.id = overlay_id;
+    ovdata.data.flags = 0;
+    ovdata.data.offset = 0;
+    ovdata.data.memory_id = mem_info.mem_fd;
+    ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdata);
+    if (ret < 0) {
+        perror("overlay_display_frame failed, overlay play Failed\n");
+        return 0;
+    }
+
+    memset(&ext_commit, 0, sizeof(struct mdp_display_commit));
+    ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
+    ext_commit.wait_for_finish = 1;
+    ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit);
+    if (ret < 0) {
+        perror("overlay_display_frame failed, overlay commit Failed\n!");
+        return 0;
+    }
+
+    return 0;
+}
+
+#else
+
+bool target_has_overlay(char *version) {
+    return false;
+}
+
+int free_ion_mem(void) {
+    return -EINVAL;
+}
+
+int alloc_ion_mem(unsigned int size)
+{
+    return -EINVAL;
+}
+
+int allocate_overlay(int fd, GGLSurface gr_fb[])
+{
+    return -EINVAL;
+}
+
+int free_overlay(int fd)
+{
+    return -EINVAL;
+}
+
+int overlay_display_frame(int fd, GGLubyte* data, size_t size)
+{
+    return -EINVAL;
+}
+
+#endif //#ifdef MSM_BSP
diff --git a/minui/include/linux/msm_ion.h b/minui/include/linux/msm_ion.h
new file mode 100644
index 0000000..121a5a7
--- /dev/null
+++ b/minui/include/linux/msm_ion.h
@@ -0,0 +1,157 @@
+#ifndef _LINUX_MSM_ION_H
+#define _LINUX_MSM_ION_H
+
+#include <linux/ion.h>
+
+/*enum msm_ion_heap_types {
+	ION_HEAP_TYPE_MSM_START = ION_HEAP_TYPE_CUSTOM + 1,
+	ION_HEAP_TYPE_IOMMU = ION_HEAP_TYPE_MSM_START,
+	ION_HEAP_TYPE_DMA,
+	ION_HEAP_TYPE_CP,
+	ION_HEAP_TYPE_SECURE_DMA,
+	ION_HEAP_TYPE_REMOVED,
+};*/
+
+/**
+ * These are the only ids that should be used for Ion heap ids.
+ * The ids listed are the order in which allocation will be attempted
+ * if specified. Don't swap the order of heap ids unless you know what
+ * you are doing!
+ * Id's are spaced by purpose to allow new Id's to be inserted in-between (for
+ * possible fallbacks)
+ */
+
+enum ion_heap_ids {
+	INVALID_HEAP_ID = -1,
+	ION_CP_MM_HEAP_ID = 8,
+	ION_CP_MFC_HEAP_ID = 12,
+	ION_CP_WB_HEAP_ID = 16, /* 8660 only */
+	ION_CAMERA_HEAP_ID = 20, /* 8660 only */
+	ION_SYSTEM_CONTIG_HEAP_ID = 21,
+	ION_ADSP_HEAP_ID = 22,
+	ION_PIL1_HEAP_ID = 23, /* Currently used for other PIL images */
+	ION_SF_HEAP_ID = 24,
+	ION_IOMMU_HEAP_ID = 25,
+	ION_PIL2_HEAP_ID = 26, /* Currently used for modem firmware images */
+	ION_QSECOM_HEAP_ID = 27,
+	ION_AUDIO_HEAP_ID = 28,
+
+	ION_MM_FIRMWARE_HEAP_ID = 29,
+	ION_SYSTEM_HEAP_ID = 30,
+
+	ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_FLAG_SECURE flag */
+};
+
+/*enum ion_fixed_position {
+	NOT_FIXED,
+	FIXED_LOW,
+	FIXED_MIDDLE,
+	FIXED_HIGH,
+};*/
+
+/*enum cp_mem_usage {
+	VIDEO_BITSTREAM = 0x1,
+	VIDEO_PIXEL = 0x2,
+	VIDEO_NONPIXEL = 0x3,
+	MAX_USAGE = 0x4,
+	UNKNOWN = 0x7FFFFFFF,
+};*/
+
+#define ION_HEAP_CP_MASK		(1 << ION_HEAP_TYPE_CP)
+#define ION_HEAP_TYPE_DMA_MASK         (1 << ION_HEAP_TYPE_DMA)
+
+/**
+ * Flag to use when allocating to indicate that a heap is secure.
+ */
+#define ION_FLAG_SECURE (1 << ION_HEAP_ID_RESERVED)
+
+/**
+ * Flag for clients to force contiguous memort allocation
+ *
+ * Use of this flag is carefully monitored!
+ */
+#define ION_FLAG_FORCE_CONTIGUOUS (1 << 30)
+
+/*
+ * Used in conjunction with heap which pool memory to force an allocation
+ * to come from the page allocator directly instead of from the pool allocation
+ */
+#define ION_FLAG_POOL_FORCE_ALLOC (1 << 16)
+
+/**
+* Deprecated! Please use the corresponding ION_FLAG_*
+*/
+#define ION_SECURE ION_FLAG_SECURE
+#define ION_FORCE_CONTIGUOUS ION_FLAG_FORCE_CONTIGUOUS
+
+/**
+ * Macro should be used with ion_heap_ids defined above.
+ */
+#define ION_HEAP(bit) (1 << (bit))
+
+#define ION_ADSP_HEAP_NAME	"adsp"
+#define ION_VMALLOC_HEAP_NAME	"vmalloc"
+#define ION_KMALLOC_HEAP_NAME	"kmalloc"
+#define ION_AUDIO_HEAP_NAME	"audio"
+#define ION_SF_HEAP_NAME	"sf"
+#define ION_MM_HEAP_NAME	"mm"
+#define ION_CAMERA_HEAP_NAME	"camera_preview"
+#define ION_IOMMU_HEAP_NAME	"iommu"
+#define ION_MFC_HEAP_NAME	"mfc"
+#define ION_WB_HEAP_NAME	"wb"
+#define ION_MM_FIRMWARE_HEAP_NAME	"mm_fw"
+#define ION_PIL1_HEAP_NAME  "pil_1"
+#define ION_PIL2_HEAP_NAME  "pil_2"
+#define ION_QSECOM_HEAP_NAME	"qsecom"
+
+#define ION_SET_CACHED(__cache)		(__cache | ION_FLAG_CACHED)
+#define ION_SET_UNCACHED(__cache)	(__cache & ~ION_FLAG_CACHED)
+
+#define ION_IS_CACHED(__flags)	((__flags) & ION_FLAG_CACHED)
+
+
+/* struct ion_flush_data - data passed to ion for flushing caches
+ *
+ * @handle:	handle with data to flush
+ * @fd:		fd to flush
+ * @vaddr:	userspace virtual address mapped with mmap
+ * @offset:	offset into the handle to flush
+ * @length:	length of handle to flush
+ *
+ * Performs cache operations on the handle. If p is the start address
+ * of the handle, p + offset through p + offset + length will have
+ * the cache operations performed
+ */
+struct ion_flush_data {
+	struct ion_handle *handle;
+	int fd;
+	void *vaddr;
+	unsigned int offset;
+	unsigned int length;
+};
+
+#define ION_IOC_MSM_MAGIC 'M'
+
+/**
+ * DOC: ION_IOC_CLEAN_CACHES - clean the caches
+ *
+ * Clean the caches of the handle specified.
+ */
+#define ION_IOC_CLEAN_CACHES	_IOWR(ION_IOC_MSM_MAGIC, 0, \
+						struct ion_flush_data)
+/**
+ * DOC: ION_IOC_INV_CACHES - invalidate the caches
+ *
+ * Invalidate the caches of the handle specified.
+ */
+#define ION_IOC_INV_CACHES	_IOWR(ION_IOC_MSM_MAGIC, 1, \
+						struct ion_flush_data)
+/**
+ * DOC: ION_IOC_CLEAN_INV_CACHES - clean and invalidate the caches
+ *
+ * Clean and invalidate the caches of the handle specified.
+ */
+#define ION_IOC_CLEAN_INV_CACHES	_IOWR(ION_IOC_MSM_MAGIC, 2, \
+						struct ion_flush_data)
+
+#endif
diff --git a/minui/include/linux/msm_mdp.h b/minui/include/linux/msm_mdp.h
new file mode 100644
index 0000000..abdcd29
--- /dev/null
+++ b/minui/include/linux/msm_mdp.h
@@ -0,0 +1,992 @@
+/* include/linux/msm_mdp.h
+ *
+ * Copyright (C) 2007 Google Incorporated
+ * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_MDP_H_
+#define _MSM_MDP_H_
+
+#include <linux/types.h>
+#include <linux/fb.h>
+
+#define MSMFB_IOCTL_MAGIC 'm'
+#define MSMFB_GRP_DISP          _IOW(MSMFB_IOCTL_MAGIC, 1, unsigned int)
+#define MSMFB_BLIT              _IOW(MSMFB_IOCTL_MAGIC, 2, unsigned int)
+#define MSMFB_SUSPEND_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 128, unsigned int)
+#define MSMFB_RESUME_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 129, unsigned int)
+#define MSMFB_CURSOR _IOW(MSMFB_IOCTL_MAGIC, 130, struct fb_cursor)
+#define MSMFB_SET_LUT _IOW(MSMFB_IOCTL_MAGIC, 131, struct fb_cmap)
+#define MSMFB_HISTOGRAM _IOWR(MSMFB_IOCTL_MAGIC, 132, struct mdp_histogram_data)
+/* new ioctls's for set/get ccs matrix */
+#define MSMFB_GET_CCS_MATRIX  _IOWR(MSMFB_IOCTL_MAGIC, 133, struct mdp_ccs)
+#define MSMFB_SET_CCS_MATRIX  _IOW(MSMFB_IOCTL_MAGIC, 134, struct mdp_ccs)
+#define MSMFB_OVERLAY_SET       _IOWR(MSMFB_IOCTL_MAGIC, 135, \
+						struct mdp_overlay)
+#define MSMFB_OVERLAY_UNSET     _IOW(MSMFB_IOCTL_MAGIC, 136, unsigned int)
+
+#define MSMFB_OVERLAY_PLAY      _IOW(MSMFB_IOCTL_MAGIC, 137, \
+						struct msmfb_overlay_data)
+#define MSMFB_OVERLAY_QUEUE	MSMFB_OVERLAY_PLAY
+
+#define MSMFB_GET_PAGE_PROTECTION _IOR(MSMFB_IOCTL_MAGIC, 138, \
+					struct mdp_page_protection)
+#define MSMFB_SET_PAGE_PROTECTION _IOW(MSMFB_IOCTL_MAGIC, 139, \
+					struct mdp_page_protection)
+#define MSMFB_OVERLAY_GET      _IOR(MSMFB_IOCTL_MAGIC, 140, \
+						struct mdp_overlay)
+#define MSMFB_OVERLAY_PLAY_ENABLE     _IOW(MSMFB_IOCTL_MAGIC, 141, unsigned int)
+#define MSMFB_OVERLAY_BLT       _IOWR(MSMFB_IOCTL_MAGIC, 142, \
+						struct msmfb_overlay_blt)
+#define MSMFB_OVERLAY_BLT_OFFSET     _IOW(MSMFB_IOCTL_MAGIC, 143, unsigned int)
+#define MSMFB_HISTOGRAM_START	_IOR(MSMFB_IOCTL_MAGIC, 144, \
+						struct mdp_histogram_start_req)
+#define MSMFB_HISTOGRAM_STOP	_IOR(MSMFB_IOCTL_MAGIC, 145, unsigned int)
+#define MSMFB_NOTIFY_UPDATE	_IOWR(MSMFB_IOCTL_MAGIC, 146, unsigned int)
+
+#define MSMFB_OVERLAY_3D       _IOWR(MSMFB_IOCTL_MAGIC, 147, \
+						struct msmfb_overlay_3d)
+
+#define MSMFB_MIXER_INFO       _IOWR(MSMFB_IOCTL_MAGIC, 148, \
+						struct msmfb_mixer_info_req)
+#define MSMFB_OVERLAY_PLAY_WAIT _IOWR(MSMFB_IOCTL_MAGIC, 149, \
+						struct msmfb_overlay_data)
+#define MSMFB_WRITEBACK_INIT _IO(MSMFB_IOCTL_MAGIC, 150)
+#define MSMFB_WRITEBACK_START _IO(MSMFB_IOCTL_MAGIC, 151)
+#define MSMFB_WRITEBACK_STOP _IO(MSMFB_IOCTL_MAGIC, 152)
+#define MSMFB_WRITEBACK_QUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 153, \
+						struct msmfb_data)
+#define MSMFB_WRITEBACK_DEQUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 154, \
+						struct msmfb_data)
+#define MSMFB_WRITEBACK_TERMINATE _IO(MSMFB_IOCTL_MAGIC, 155)
+#define MSMFB_MDP_PP _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp)
+#define MSMFB_OVERLAY_VSYNC_CTRL _IOW(MSMFB_IOCTL_MAGIC, 160, unsigned int)
+#define MSMFB_VSYNC_CTRL  _IOW(MSMFB_IOCTL_MAGIC, 161, unsigned int)
+#define MSMFB_BUFFER_SYNC  _IOW(MSMFB_IOCTL_MAGIC, 162, struct mdp_buf_sync)
+#define MSMFB_OVERLAY_COMMIT      _IO(MSMFB_IOCTL_MAGIC, 163)
+#define MSMFB_DISPLAY_COMMIT      _IOW(MSMFB_IOCTL_MAGIC, 164, \
+						struct mdp_display_commit)
+#define MSMFB_METADATA_SET  _IOW(MSMFB_IOCTL_MAGIC, 165, struct msmfb_metadata)
+#define MSMFB_METADATA_GET  _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata)
+#define MSMFB_WRITEBACK_SET_MIRRORING_HINT _IOW(MSMFB_IOCTL_MAGIC, 167, \
+						unsigned int)
+#define MSMFB_ASYNC_BLIT              _IOW(MSMFB_IOCTL_MAGIC, 168, unsigned int)
+
+#define FB_TYPE_3D_PANEL 0x10101010
+#define MDP_IMGTYPE2_START 0x10000
+#define MSMFB_DRIVER_VERSION	0xF9E8D701
+
+enum {
+	NOTIFY_UPDATE_START,
+	NOTIFY_UPDATE_STOP,
+	NOTIFY_UPDATE_POWER_OFF,
+};
+
+enum {
+	NOTIFY_TYPE_NO_UPDATE,
+	NOTIFY_TYPE_SUSPEND,
+	NOTIFY_TYPE_UPDATE,
+};
+
+enum {
+	MDP_RGB_565,      /* RGB 565 planer */
+	MDP_XRGB_8888,    /* RGB 888 padded */
+	MDP_Y_CBCR_H2V2,  /* Y and CbCr, pseudo planer w/ Cb is in MSB */
+	MDP_Y_CBCR_H2V2_ADRENO,
+	MDP_ARGB_8888,    /* ARGB 888 */
+	MDP_RGB_888,      /* RGB 888 planer */
+	MDP_Y_CRCB_H2V2,  /* Y and CrCb, pseudo planer w/ Cr is in MSB */
+	MDP_YCRYCB_H2V1,  /* YCrYCb interleave */
+	MDP_CBYCRY_H2V1,  /* CbYCrY interleave */
+	MDP_Y_CRCB_H2V1,  /* Y and CrCb, pseduo planer w/ Cr is in MSB */
+	MDP_Y_CBCR_H2V1,   /* Y and CrCb, pseduo planer w/ Cr is in MSB */
+	MDP_Y_CRCB_H1V2,
+	MDP_Y_CBCR_H1V2,
+	MDP_RGBA_8888,    /* ARGB 888 */
+	MDP_BGRA_8888,	  /* ABGR 888 */
+	MDP_RGBX_8888,	  /* RGBX 888 */
+	MDP_Y_CRCB_H2V2_TILE,  /* Y and CrCb, pseudo planer tile */
+	MDP_Y_CBCR_H2V2_TILE,  /* Y and CbCr, pseudo planer tile */
+	MDP_Y_CR_CB_H2V2,  /* Y, Cr and Cb, planar */
+	MDP_Y_CR_CB_GH2V2,  /* Y, Cr and Cb, planar aligned to Android YV12 */
+	MDP_Y_CB_CR_H2V2,  /* Y, Cb and Cr, planar */
+	MDP_Y_CRCB_H1V1,  /* Y and CrCb, pseduo planer w/ Cr is in MSB */
+	MDP_Y_CBCR_H1V1,  /* Y and CbCr, pseduo planer w/ Cb is in MSB */
+	MDP_YCRCB_H1V1,   /* YCrCb interleave */
+	MDP_YCBCR_H1V1,   /* YCbCr interleave */
+	MDP_BGR_565,      /* BGR 565 planer */
+	MDP_BGR_888,      /* BGR 888 */
+	MDP_Y_CBCR_H2V2_VENUS,
+	MDP_BGRX_8888,   /* BGRX 8888 */
+	MDP_RGBA_8888_TILE,	/* RGBA 8888 in tile format */
+	MDP_ARGB_8888_TILE,	/* ARGB 8888 in tile format */
+	MDP_ABGR_8888_TILE,	/* ABGR 8888 in tile format */
+	MDP_BGRA_8888_TILE,	/* BGRA 8888 in tile format */
+	MDP_RGBX_8888_TILE,	/* RGBX 8888 in tile format */
+	MDP_XRGB_8888_TILE,	/* XRGB 8888 in tile format */
+	MDP_XBGR_8888_TILE,	/* XBGR 8888 in tile format */
+	MDP_BGRX_8888_TILE,	/* BGRX 8888 in tile format */
+	MDP_YCBYCR_H2V1,  /* YCbYCr interleave */
+	MDP_IMGTYPE_LIMIT,
+	MDP_RGB_BORDERFILL,	/* border fill pipe */
+	MDP_FB_FORMAT = MDP_IMGTYPE2_START,    /* framebuffer format */
+	MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */
+};
+
+enum {
+	PMEM_IMG,
+	FB_IMG,
+};
+
+enum {
+	HSIC_HUE = 0,
+	HSIC_SAT,
+	HSIC_INT,
+	HSIC_CON,
+	NUM_HSIC_PARAM,
+};
+
+#define MDSS_MDP_ROT_ONLY		0x80
+#define MDSS_MDP_RIGHT_MIXER		0x100
+#define MDSS_MDP_DUAL_PIPE		0x200
+
+/* mdp_blit_req flag values */
+#define MDP_ROT_NOP 0
+#define MDP_FLIP_LR 0x1
+#define MDP_FLIP_UD 0x2
+#define MDP_ROT_90 0x4
+#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR)
+#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR)
+#define MDP_DITHER 0x8
+#define MDP_BLUR 0x10
+#define MDP_BLEND_FG_PREMULT 0x20000
+#define MDP_IS_FG 0x40000
+#define MDP_SOLID_FILL 0x0000100
+#define MDP_DEINTERLACE 0x80000000
+#define MDP_SHARPENING  0x40000000
+#define MDP_NO_DMA_BARRIER_START	0x20000000
+#define MDP_NO_DMA_BARRIER_END		0x10000000
+#define MDP_NO_BLIT			0x08000000
+#define MDP_BLIT_WITH_DMA_BARRIERS	0x000
+#define MDP_BLIT_WITH_NO_DMA_BARRIERS    \
+	(MDP_NO_DMA_BARRIER_START | MDP_NO_DMA_BARRIER_END)
+#define MDP_BLIT_SRC_GEM                0x04000000
+#define MDP_BLIT_DST_GEM                0x02000000
+#define MDP_BLIT_NON_CACHED		0x01000000
+#define MDP_OV_PIPE_SHARE		0x00800000
+#define MDP_DEINTERLACE_ODD		0x00400000
+#define MDP_OV_PLAY_NOWAIT		0x00200000
+#define MDP_SOURCE_ROTATED_90		0x00100000
+#define MDP_OVERLAY_PP_CFG_EN		0x00080000
+#define MDP_BACKEND_COMPOSITION		0x00040000
+#define MDP_BORDERFILL_SUPPORTED	0x00010000
+#define MDP_SECURE_OVERLAY_SESSION      0x00008000
+#define MDP_SECURE_DISPLAY_OVERLAY_SESSION	0x00002000
+#define MDP_OV_PIPE_FORCE_DMA		0x00004000
+#define MDP_MEMORY_ID_TYPE_FB		0x00001000
+#define MDP_BWC_EN			0x00000400
+#define MDP_DECIMATION_EN		0x00000800
+#define MDP_TRANSP_NOP 0xffffffff
+#define MDP_ALPHA_NOP 0xff
+
+#define MDP_FB_PAGE_PROTECTION_NONCACHED         (0)
+#define MDP_FB_PAGE_PROTECTION_WRITECOMBINE      (1)
+#define MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE (2)
+#define MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE    (3)
+#define MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE  (4)
+/* Sentinel: Don't use! */
+#define MDP_FB_PAGE_PROTECTION_INVALID           (5)
+/* Count of the number of MDP_FB_PAGE_PROTECTION_... values. */
+#define MDP_NUM_FB_PAGE_PROTECTION_VALUES        (5)
+
+struct mdp_rect {
+	uint32_t x;
+	uint32_t y;
+	uint32_t w;
+	uint32_t h;
+};
+
+struct mdp_img {
+	uint32_t width;
+	uint32_t height;
+	uint32_t format;
+	uint32_t offset;
+	int memory_id;		/* the file descriptor */
+	uint32_t priv;
+};
+
+/*
+ * {3x3} + {3} ccs matrix
+ */
+
+#define MDP_CCS_RGB2YUV 	0
+#define MDP_CCS_YUV2RGB 	1
+
+#define MDP_CCS_SIZE	9
+#define MDP_BV_SIZE	3
+
+struct mdp_ccs {
+	int direction;			/* MDP_CCS_RGB2YUV or YUV2RGB */
+	uint16_t ccs[MDP_CCS_SIZE];	/* 3x3 color coefficients */
+	uint16_t bv[MDP_BV_SIZE];	/* 1x3 bias vector */
+};
+
+struct mdp_csc {
+	int id;
+	uint32_t csc_mv[9];
+	uint32_t csc_pre_bv[3];
+	uint32_t csc_post_bv[3];
+	uint32_t csc_pre_lv[6];
+	uint32_t csc_post_lv[6];
+};
+
+/* The version of the mdp_blit_req structure so that
+ * user applications can selectively decide which functionality
+ * to include
+ */
+
+#define MDP_BLIT_REQ_VERSION 2
+
+struct color {
+	uint32_t r;
+	uint32_t g;
+	uint32_t b;
+	uint32_t alpha;
+};
+
+struct mdp_blit_req {
+	struct mdp_img src;
+	struct mdp_img dst;
+	struct mdp_rect src_rect;
+	struct mdp_rect dst_rect;
+	struct color const_color;
+	uint32_t alpha;
+	uint32_t transp_mask;
+	uint32_t flags;
+	int sharpening_strength;  /* -127 <--> 127, default 64 */
+};
+
+struct mdp_blit_req_list {
+	uint32_t count;
+	struct mdp_blit_req req[];
+};
+
+#define MSMFB_DATA_VERSION 2
+
+struct msmfb_data {
+	uint32_t offset;
+	int memory_id;
+	int id;
+	uint32_t flags;
+	uint32_t priv;
+	uint32_t iova;
+};
+
+#define MSMFB_NEW_REQUEST -1
+
+struct msmfb_overlay_data {
+	uint32_t id;
+	struct msmfb_data data;
+	uint32_t version_key;
+	struct msmfb_data plane1_data;
+	struct msmfb_data plane2_data;
+	struct msmfb_data dst_data;
+};
+
+struct msmfb_img {
+	uint32_t width;
+	uint32_t height;
+	uint32_t format;
+};
+
+#define MSMFB_WRITEBACK_DEQUEUE_BLOCKING 0x1
+struct msmfb_writeback_data {
+	struct msmfb_data buf_info;
+	struct msmfb_img img;
+};
+
+#define MDP_PP_OPS_ENABLE 0x1
+#define MDP_PP_OPS_READ 0x2
+#define MDP_PP_OPS_WRITE 0x4
+#define MDP_PP_OPS_DISABLE 0x8
+#define MDP_PP_IGC_FLAG_ROM0	0x10
+#define MDP_PP_IGC_FLAG_ROM1	0x20
+
+#define MDSS_PP_DSPP_CFG	0x000
+#define MDSS_PP_SSPP_CFG	0x100
+#define MDSS_PP_LM_CFG	0x200
+#define MDSS_PP_WB_CFG	0x300
+
+#define MDSS_PP_ARG_MASK	0x3C00
+#define MDSS_PP_ARG_NUM		4
+#define MDSS_PP_ARG_SHIFT	10
+#define MDSS_PP_LOCATION_MASK	0x0300
+#define MDSS_PP_LOGICAL_MASK	0x00FF
+
+#define MDSS_PP_ADD_ARG(var, arg) ((var) | (0x1 << (MDSS_PP_ARG_SHIFT + (arg))))
+#define PP_ARG(x, var) ((var) & (0x1 << (MDSS_PP_ARG_SHIFT + (x))))
+#define PP_LOCAT(var) ((var) & MDSS_PP_LOCATION_MASK)
+#define PP_BLOCK(var) ((var) & MDSS_PP_LOGICAL_MASK)
+
+
+struct mdp_qseed_cfg {
+	uint32_t table_num;
+	uint32_t ops;
+	uint32_t len;
+	uint32_t *data;
+};
+
+struct mdp_sharp_cfg {
+	uint32_t flags;
+	uint32_t strength;
+	uint32_t edge_thr;
+	uint32_t smooth_thr;
+	uint32_t noise_thr;
+};
+
+struct mdp_qseed_cfg_data {
+	uint32_t block;
+	struct mdp_qseed_cfg qseed_data;
+};
+
+#define MDP_OVERLAY_PP_CSC_CFG         0x1
+#define MDP_OVERLAY_PP_QSEED_CFG       0x2
+#define MDP_OVERLAY_PP_PA_CFG          0x4
+#define MDP_OVERLAY_PP_IGC_CFG         0x8
+#define MDP_OVERLAY_PP_SHARP_CFG       0x10
+#define MDP_OVERLAY_PP_HIST_CFG        0x20
+#define MDP_OVERLAY_PP_HIST_LUT_CFG    0x40
+
+#define MDP_CSC_FLAG_ENABLE	0x1
+#define MDP_CSC_FLAG_YUV_IN	0x2
+#define MDP_CSC_FLAG_YUV_OUT	0x4
+
+struct mdp_csc_cfg {
+	/* flags for enable CSC, toggling RGB,YUV input/output */
+	uint32_t flags;
+	uint32_t csc_mv[9];
+	uint32_t csc_pre_bv[3];
+	uint32_t csc_post_bv[3];
+	uint32_t csc_pre_lv[6];
+	uint32_t csc_post_lv[6];
+};
+
+struct mdp_csc_cfg_data {
+	uint32_t block;
+	struct mdp_csc_cfg csc_data;
+};
+
+struct mdp_pa_cfg {
+	uint32_t flags;
+	uint32_t hue_adj;
+	uint32_t sat_adj;
+	uint32_t val_adj;
+	uint32_t cont_adj;
+};
+
+struct mdp_igc_lut_data {
+	uint32_t block;
+	uint32_t len, ops;
+	uint32_t *c0_c1_data;
+	uint32_t *c2_data;
+};
+
+struct mdp_histogram_cfg {
+	uint32_t ops;
+	uint32_t block;
+	uint8_t frame_cnt;
+	uint8_t bit_mask;
+	uint16_t num_bins;
+};
+
+struct mdp_hist_lut_data {
+	uint32_t block;
+	uint32_t ops;
+	uint32_t len;
+	uint32_t *data;
+};
+
+struct mdp_overlay_pp_params {
+	uint32_t config_ops;
+	struct mdp_csc_cfg csc_cfg;
+	struct mdp_qseed_cfg qseed_cfg[2];
+	struct mdp_pa_cfg pa_cfg;
+	struct mdp_igc_lut_data igc_cfg;
+	struct mdp_sharp_cfg sharp_cfg;
+	struct mdp_histogram_cfg hist_cfg;
+	struct mdp_hist_lut_data hist_lut_cfg;
+};
+
+/**
+ * enum mdss_mdp_blend_op - Different blend operations set by userspace
+ *
+ * @BLEND_OP_NOT_DEFINED:    No blend operation defined for the layer.
+ * @BLEND_OP_OPAQUE:         Apply a constant blend operation. The layer
+ *                           would appear opaque in case fg plane alpha is
+ *                           0xff.
+ * @BLEND_OP_PREMULTIPLIED:  Apply source over blend rule. Layer already has
+ *                           alpha pre-multiplication done. If fg plane alpha
+ *                           is less than 0xff, apply modulation as well. This
+ *                           operation is intended on layers having alpha
+ *                           channel.
+ * @BLEND_OP_COVERAGE:       Apply source over blend rule. Layer is not alpha
+ *                           pre-multiplied. Apply pre-multiplication. If fg
+ *                           plane alpha is less than 0xff, apply modulation as
+ *                           well.
+ * @BLEND_OP_MAX:            Used to track maximum blend operation possible by
+ *                           mdp.
+ */
+enum mdss_mdp_blend_op {
+	BLEND_OP_NOT_DEFINED = 0,
+	BLEND_OP_OPAQUE,
+	BLEND_OP_PREMULTIPLIED,
+	BLEND_OP_COVERAGE,
+	BLEND_OP_MAX,
+};
+
+#define MAX_PLANES	4
+struct mdp_scale_data {
+	uint8_t enable_pxl_ext;
+
+	int init_phase_x[MAX_PLANES];
+	int phase_step_x[MAX_PLANES];
+	int init_phase_y[MAX_PLANES];
+	int phase_step_y[MAX_PLANES];
+
+	int num_ext_pxls_left[MAX_PLANES];
+	int num_ext_pxls_right[MAX_PLANES];
+	int num_ext_pxls_top[MAX_PLANES];
+	int num_ext_pxls_btm[MAX_PLANES];
+
+	int left_ftch[MAX_PLANES];
+	int left_rpt[MAX_PLANES];
+	int right_ftch[MAX_PLANES];
+	int right_rpt[MAX_PLANES];
+
+	int top_rpt[MAX_PLANES];
+	int btm_rpt[MAX_PLANES];
+	int top_ftch[MAX_PLANES];
+	int btm_ftch[MAX_PLANES];
+
+	uint32_t roi_w[MAX_PLANES];
+};
+
+/**
+ * struct mdp_overlay - overlay surface structure
+ * @src:	Source image information (width, height, format).
+ * @src_rect:	Source crop rectangle, portion of image that will be fetched.
+ *		This should always be within boundaries of source image.
+ * @dst_rect:	Destination rectangle, the position and size of image on screen.
+ *		This should always be within panel boundaries.
+ * @z_order:	Blending stage to occupy in display, if multiple layers are
+ *		present, highest z_order usually means the top most visible
+ *		layer. The range acceptable is from 0-3 to support blending
+ *		up to 4 layers.
+ * @is_fg:	This flag is used to disable blending of any layers with z_order
+ *		less than this overlay. It means that any layers with z_order
+ *		less than this layer will not be blended and will be replaced
+ *		by the background border color.
+ * @alpha:	Used to set plane opacity. The range can be from 0-255, where
+ *		0 means completely transparent and 255 means fully opaque.
+ * @transp_mask: Color used as color key for transparency. Any pixel in fetched
+ *		image matching this color will be transparent when blending.
+ *		The color should be in same format as the source image format.
+ * @flags:	This is used to customize operation of overlay. See MDP flags
+ *		for more information.
+ * @user_data:	DEPRECATED* Used to store user application specific information.
+ * @bg_color:	Solid color used to fill the overlay surface when no source
+ *		buffer is provided.
+ * @horz_deci:	Horizontal decimation value, this indicates the amount of pixels
+ *		dropped for each pixel that is fetched from a line. The value
+ *		given should be power of two of decimation amount.
+ *		0: no decimation
+ *		1: decimate by 2 (drop 1 pixel for each pixel fetched)
+ *		2: decimate by 4 (drop 3 pixels for each pixel fetched)
+ *		3: decimate by 8 (drop 7 pixels for each pixel fetched)
+ *		4: decimate by 16 (drop 15 pixels for each pixel fetched)
+ * @vert_deci:	Vertical decimation value, this indicates the amount of lines
+ *		dropped for each line that is fetched from overlay. The value
+ *		given should be power of two of decimation amount.
+ *		0: no decimation
+ *		1: decimation by 2 (drop 1 line for each line fetched)
+ *		2: decimation by 4 (drop 3 lines for each line fetched)
+ *		3: decimation by 8 (drop 7 lines for each line fetched)
+ *		4: decimation by 16 (drop 15 lines for each line fetched)
+ * @overlay_pp_cfg: Overlay post processing configuration, for more information
+ *		see struct mdp_overlay_pp_params.
+ */
+struct mdp_overlay {
+	struct msmfb_img src;
+	struct mdp_rect src_rect;
+	struct mdp_rect dst_rect;
+	uint32_t z_order;	/* stage number */
+	uint32_t is_fg;		/* control alpha & transp */
+	uint32_t alpha;
+	uint32_t blend_op;
+	uint32_t transp_mask;
+	uint32_t flags;
+	uint32_t id;
+	uint32_t user_data[6];
+	uint32_t bg_color;
+	uint8_t horz_deci;
+	uint8_t vert_deci;
+	struct mdp_overlay_pp_params overlay_pp_cfg;
+	struct mdp_scale_data scale;
+};
+
+struct msmfb_overlay_3d {
+	uint32_t is_3d;
+	uint32_t width;
+	uint32_t height;
+};
+
+
+struct msmfb_overlay_blt {
+	uint32_t enable;
+	uint32_t offset;
+	uint32_t width;
+	uint32_t height;
+	uint32_t bpp;
+};
+
+struct mdp_histogram {
+	uint32_t frame_cnt;
+	uint32_t bin_cnt;
+	uint32_t *r;
+	uint32_t *g;
+	uint32_t *b;
+};
+
+enum {
+	DISPLAY_MISR_EDP,
+	DISPLAY_MISR_DSI0,
+	DISPLAY_MISR_DSI1,
+	DISPLAY_MISR_HDMI,
+	DISPLAY_MISR_LCDC,
+	DISPLAY_MISR_ATV,
+	DISPLAY_MISR_DSI_CMD,
+	DISPLAY_MISR_MAX
+};
+
+enum {
+	MISR_OP_NONE,
+	MISR_OP_SFM,
+	MISR_OP_MFM,
+	MISR_OP_BM,
+	MISR_OP_MAX
+};
+
+struct mdp_misr {
+	uint32_t block_id;
+	uint32_t frame_count;
+	uint32_t crc_op_mode;
+	uint32_t crc_value[32];
+};
+
+/*
+
+	mdp_block_type defines the identifiers for pipes in MDP 4.3 and up
+
+	MDP_BLOCK_RESERVED is provided for backward compatibility and is
+	deprecated. It corresponds to DMA_P. So MDP_BLOCK_DMA_P should be used
+	instead.
+
+	MDP_LOGICAL_BLOCK_DISP_0 identifies the display pipe which fb0 uses,
+	same for others.
+
+*/
+
+enum {
+	MDP_BLOCK_RESERVED = 0,
+	MDP_BLOCK_OVERLAY_0,
+	MDP_BLOCK_OVERLAY_1,
+	MDP_BLOCK_VG_1,
+	MDP_BLOCK_VG_2,
+	MDP_BLOCK_RGB_1,
+	MDP_BLOCK_RGB_2,
+	MDP_BLOCK_DMA_P,
+	MDP_BLOCK_DMA_S,
+	MDP_BLOCK_DMA_E,
+	MDP_BLOCK_OVERLAY_2,
+	MDP_LOGICAL_BLOCK_DISP_0 = 0x10,
+	MDP_LOGICAL_BLOCK_DISP_1,
+	MDP_LOGICAL_BLOCK_DISP_2,
+	MDP_BLOCK_MAX,
+};
+
+/*
+ * mdp_histogram_start_req is used to provide the parameters for
+ * histogram start request
+ */
+
+struct mdp_histogram_start_req {
+	uint32_t block;
+	uint8_t frame_cnt;
+	uint8_t bit_mask;
+	uint16_t num_bins;
+};
+
+/*
+ * mdp_histogram_data is used to return the histogram data, once
+ * the histogram is done/stopped/cance
+ */
+
+struct mdp_histogram_data {
+	uint32_t block;
+	uint32_t bin_cnt;
+	uint32_t *c0;
+	uint32_t *c1;
+	uint32_t *c2;
+	uint32_t *extra_info;
+};
+
+struct mdp_pcc_coeff {
+	uint32_t c, r, g, b, rr, gg, bb, rg, gb, rb, rgb_0, rgb_1;
+};
+
+struct mdp_pcc_cfg_data {
+	uint32_t block;
+	uint32_t ops;
+	struct mdp_pcc_coeff r, g, b;
+};
+
+#define MDP_GAMUT_TABLE_NUM		8
+
+enum {
+	mdp_lut_igc,
+	mdp_lut_pgc,
+	mdp_lut_hist,
+	mdp_lut_max,
+};
+
+struct mdp_ar_gc_lut_data {
+	uint32_t x_start;
+	uint32_t slope;
+	uint32_t offset;
+};
+
+struct mdp_pgc_lut_data {
+	uint32_t block;
+	uint32_t flags;
+	uint8_t num_r_stages;
+	uint8_t num_g_stages;
+	uint8_t num_b_stages;
+	struct mdp_ar_gc_lut_data *r_data;
+	struct mdp_ar_gc_lut_data *g_data;
+	struct mdp_ar_gc_lut_data *b_data;
+};
+
+
+struct mdp_lut_cfg_data {
+	uint32_t lut_type;
+	union {
+		struct mdp_igc_lut_data igc_lut_data;
+		struct mdp_pgc_lut_data pgc_lut_data;
+		struct mdp_hist_lut_data hist_lut_data;
+	} data;
+};
+
+struct mdp_bl_scale_data {
+	uint32_t min_lvl;
+	uint32_t scale;
+};
+
+struct mdp_pa_cfg_data {
+	uint32_t block;
+	struct mdp_pa_cfg pa_data;
+};
+
+struct mdp_dither_cfg_data {
+	uint32_t block;
+	uint32_t flags;
+	uint32_t g_y_depth;
+	uint32_t r_cr_depth;
+	uint32_t b_cb_depth;
+};
+
+struct mdp_gamut_cfg_data {
+	uint32_t block;
+	uint32_t flags;
+	uint32_t gamut_first;
+	uint32_t tbl_size[MDP_GAMUT_TABLE_NUM];
+	uint16_t *r_tbl[MDP_GAMUT_TABLE_NUM];
+	uint16_t *g_tbl[MDP_GAMUT_TABLE_NUM];
+	uint16_t *b_tbl[MDP_GAMUT_TABLE_NUM];
+};
+
+struct mdp_calib_config_data {
+	uint32_t ops;
+	uint32_t addr;
+	uint32_t data;
+};
+
+struct mdp_calib_config_buffer {
+	uint32_t ops;
+	uint32_t size;
+	uint32_t *buffer;
+};
+
+struct mdp_calib_dcm_state {
+	uint32_t ops;
+	uint32_t dcm_state;
+};
+
+enum {
+	DCM_UNINIT,
+	DCM_UNBLANK,
+	DCM_ENTER,
+	DCM_EXIT,
+	DCM_BLANK,
+};
+
+#define MDSS_MAX_BL_BRIGHTNESS 255
+#define AD_BL_LIN_LEN (MDSS_MAX_BL_BRIGHTNESS + 1)
+
+#define MDSS_AD_MODE_AUTO_BL	0x0
+#define MDSS_AD_MODE_AUTO_STR	0x1
+#define MDSS_AD_MODE_TARG_STR	0x3
+#define MDSS_AD_MODE_MAN_STR	0x7
+#define MDSS_AD_MODE_CALIB	0xF
+
+#define MDP_PP_AD_INIT	0x10
+#define MDP_PP_AD_CFG	0x20
+
+struct mdss_ad_init {
+	uint32_t asym_lut[33];
+	uint32_t color_corr_lut[33];
+	uint8_t i_control[2];
+	uint16_t black_lvl;
+	uint16_t white_lvl;
+	uint8_t var;
+	uint8_t limit_ampl;
+	uint8_t i_dither;
+	uint8_t slope_max;
+	uint8_t slope_min;
+	uint8_t dither_ctl;
+	uint8_t format;
+	uint8_t auto_size;
+	uint16_t frame_w;
+	uint16_t frame_h;
+	uint8_t logo_v;
+	uint8_t logo_h;
+	uint32_t bl_lin_len;
+	uint32_t *bl_lin;
+	uint32_t *bl_lin_inv;
+};
+
+#define MDSS_AD_BL_CTRL_MODE_EN 1
+#define MDSS_AD_BL_CTRL_MODE_DIS 0
+struct mdss_ad_cfg {
+	uint32_t mode;
+	uint32_t al_calib_lut[33];
+	uint16_t backlight_min;
+	uint16_t backlight_max;
+	uint16_t backlight_scale;
+	uint16_t amb_light_min;
+	uint16_t filter[2];
+	uint16_t calib[4];
+	uint8_t strength_limit;
+	uint8_t t_filter_recursion;
+	uint16_t stab_itr;
+	uint32_t bl_ctrl_mode;
+};
+
+/* ops uses standard MDP_PP_* flags */
+struct mdss_ad_init_cfg {
+	uint32_t ops;
+	union {
+		struct mdss_ad_init init;
+		struct mdss_ad_cfg cfg;
+	} params;
+};
+
+/* mode uses MDSS_AD_MODE_* flags */
+struct mdss_ad_input {
+	uint32_t mode;
+	union {
+		uint32_t amb_light;
+		uint32_t strength;
+		uint32_t calib_bl;
+	} in;
+	uint32_t output;
+};
+
+#define MDSS_CALIB_MODE_BL	0x1
+struct mdss_calib_cfg {
+	uint32_t ops;
+	uint32_t calib_mask;
+};
+
+enum {
+	mdp_op_pcc_cfg,
+	mdp_op_csc_cfg,
+	mdp_op_lut_cfg,
+	mdp_op_qseed_cfg,
+	mdp_bl_scale_cfg,
+	mdp_op_pa_cfg,
+	mdp_op_dither_cfg,
+	mdp_op_gamut_cfg,
+	mdp_op_calib_cfg,
+	mdp_op_ad_cfg,
+	mdp_op_ad_input,
+	mdp_op_calib_mode,
+	mdp_op_calib_buffer,
+	mdp_op_calib_dcm_state,
+	mdp_op_max,
+};
+
+enum {
+	WB_FORMAT_NV12,
+	WB_FORMAT_RGB_565,
+	WB_FORMAT_RGB_888,
+	WB_FORMAT_xRGB_8888,
+	WB_FORMAT_ARGB_8888,
+	WB_FORMAT_BGRA_8888,
+	WB_FORMAT_BGRX_8888,
+	WB_FORMAT_ARGB_8888_INPUT_ALPHA /* Need to support */
+};
+
+struct msmfb_mdp_pp {
+	uint32_t op;
+	union {
+		struct mdp_pcc_cfg_data pcc_cfg_data;
+		struct mdp_csc_cfg_data csc_cfg_data;
+		struct mdp_lut_cfg_data lut_cfg_data;
+		struct mdp_qseed_cfg_data qseed_cfg_data;
+		struct mdp_bl_scale_data bl_scale_data;
+		struct mdp_pa_cfg_data pa_cfg_data;
+		struct mdp_dither_cfg_data dither_cfg_data;
+		struct mdp_gamut_cfg_data gamut_cfg_data;
+		struct mdp_calib_config_data calib_cfg;
+		struct mdss_ad_init_cfg ad_init_cfg;
+		struct mdss_calib_cfg mdss_calib_cfg;
+		struct mdss_ad_input ad_input;
+		struct mdp_calib_config_buffer calib_buffer;
+		struct mdp_calib_dcm_state calib_dcm;
+	} data;
+};
+
+#define FB_METADATA_VIDEO_INFO_CODE_SUPPORT 1
+enum {
+	metadata_op_none,
+	metadata_op_base_blend,
+	metadata_op_frame_rate,
+	metadata_op_vic,
+	metadata_op_wb_format,
+	metadata_op_get_caps,
+	metadata_op_crc,
+	metadata_op_max
+};
+
+struct mdp_blend_cfg {
+	uint32_t is_premultiplied;
+};
+
+struct mdp_mixer_cfg {
+	uint32_t writeback_format;
+	uint32_t alpha;
+};
+
+struct mdss_hw_caps {
+	uint32_t mdp_rev;
+	uint8_t rgb_pipes;
+	uint8_t vig_pipes;
+	uint8_t dma_pipes;
+	uint32_t features;
+};
+
+struct msmfb_metadata {
+	uint32_t op;
+	uint32_t flags;
+	union {
+		struct mdp_misr misr_request;
+		struct mdp_blend_cfg blend_cfg;
+		struct mdp_mixer_cfg mixer_cfg;
+		uint32_t panel_frame_rate;
+		uint32_t video_info_code;
+		struct mdss_hw_caps caps;
+	} data;
+};
+
+#define MDP_MAX_FENCE_FD	32
+#define MDP_BUF_SYNC_FLAG_WAIT	1
+
+struct mdp_buf_sync {
+	uint32_t flags;
+	uint32_t acq_fen_fd_cnt;
+	uint32_t session_id;
+	int *acq_fen_fd;
+	int *rel_fen_fd;
+};
+
+struct mdp_async_blit_req_list {
+	struct mdp_buf_sync sync;
+	uint32_t count;
+	struct mdp_blit_req req[];
+};
+
+#define MDP_DISPLAY_COMMIT_OVERLAY	1
+struct mdp_buf_fence {
+	uint32_t flags;
+	uint32_t acq_fen_fd_cnt;
+	int acq_fen_fd[MDP_MAX_FENCE_FD];
+	int rel_fen_fd[MDP_MAX_FENCE_FD];
+};
+
+
+struct mdp_display_commit {
+	uint32_t flags;
+	uint32_t wait_for_finish;
+	struct fb_var_screeninfo var;
+	struct mdp_buf_fence buf_fence;
+	struct mdp_rect roi;
+};
+
+struct mdp_page_protection {
+	uint32_t page_protection;
+};
+
+
+struct mdp_mixer_info {
+	int pndx;
+	int pnum;
+	int ptype;
+	int mixer_num;
+	int z_order;
+};
+
+#define MAX_PIPE_PER_MIXER  4
+
+struct msmfb_mixer_info_req {
+	int mixer_num;
+	int cnt;
+	struct mdp_mixer_info info[MAX_PIPE_PER_MIXER];
+};
+
+enum {
+	DISPLAY_SUBSYSTEM_ID,
+	ROTATOR_SUBSYSTEM_ID,
+};
+
+enum {
+	MDP_IOMMU_DOMAIN_CP,
+	MDP_IOMMU_DOMAIN_NS,
+};
+
+enum {
+	MDP_WRITEBACK_MIRROR_OFF,
+	MDP_WRITEBACK_MIRROR_ON,
+	MDP_WRITEBACK_MIRROR_PAUSE,
+	MDP_WRITEBACK_MIRROR_RESUME,
+};
+
+
+#endif /*_MSM_MDP_H_*/
diff --git a/minui/minui.h b/minui/minui.h
index 733b675..8228248 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -47,10 +47,16 @@
 void gr_clear();  // clear entire surface to current color
 void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
 void gr_fill(int x1, int y1, int x2, int y2);
-void gr_text(int x, int y, const char *s, int bold);
+
+// system/core/charger uses different gr_print signatures in diferent
+// Android versions, either with or without int bold.
+int gr_text(int x, int y, const char *s, ...);
+int gr_text_impl(int x, int y, const char *s, int bold);
+
 void gr_texticon(int x, int y, gr_surface icon);
 int gr_measure(const char *s);
 void gr_font_size(int *x, int *y);
+void gr_get_memory_surface(gr_surface);
 
 void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);
 unsigned int gr_get_width(gr_surface surface);
@@ -116,6 +122,9 @@
 // Free a surface allocated by any of the res_create_*_surface()
 // functions.
 void res_free_surface(gr_surface surface);
+static inline int res_create_display_surface(const char* name, gr_surface* pSurface) {
+    return res_create_surface(name, pSurface);
+}
 
 #ifdef __cplusplus
 }
diff --git a/minui/resources.c b/minui/resources.c
index 2bae4de..92a3d31 100644
--- a/minui/resources.c
+++ b/minui/resources.c
@@ -31,7 +31,11 @@
 
 #include "minui.h"
 
+#ifdef FASTMMI_FEATURE
+char *locale = NULL;
+#else
 extern char* locale;
+#endif
 
 #define SURFACE_DATA_ALIGNMENT 8
 
diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk
new file mode 100644
index 0000000..bc4e054
--- /dev/null
+++ b/minuitwrp/Android.mk
@@ -0,0 +1,123 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := events.c resources.c graphics_overlay.c graphics_utils.c
+
+ifneq ($(TW_BOARD_CUSTOM_GRAPHICS),)
+    LOCAL_SRC_FILES += $(TW_BOARD_CUSTOM_GRAPHICS)
+else
+    LOCAL_SRC_FILES += graphics.c
+endif
+
+ifeq ($(TW_TARGET_USES_QCOM_BSP), true)
+  LOCAL_CFLAGS += -DMSM_BSP
+  ifeq ($(TARGET_PREBUILT_KERNEL),)
+    LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
+    LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
+  else
+    LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minuitwrp/include
+  endif
+else
+  LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minuitwrp/include
+endif
+
+LOCAL_C_INCLUDES += \
+    external/libpng \
+    external/zlib \
+    system/core/include \
+    external/jpeg
+
+ifeq ($(RECOVERY_TOUCHSCREEN_SWAP_XY), true)
+LOCAL_CFLAGS += -DRECOVERY_TOUCHSCREEN_SWAP_XY
+endif
+
+ifeq ($(RECOVERY_TOUCHSCREEN_FLIP_X), true)
+LOCAL_CFLAGS += -DRECOVERY_TOUCHSCREEN_FLIP_X
+endif
+
+ifeq ($(RECOVERY_TOUCHSCREEN_FLIP_Y), true)
+LOCAL_CFLAGS += -DRECOVERY_TOUCHSCREEN_FLIP_Y
+endif
+
+ifeq ($(RECOVERY_GRAPHICS_USE_LINELENGTH), true)
+LOCAL_CFLAGS += -DRECOVERY_GRAPHICS_USE_LINELENGTH
+endif
+
+#Remove the # from the line below to enable event logging
+#TWRP_EVENT_LOGGING := true
+ifeq ($(TWRP_EVENT_LOGGING), true)
+LOCAL_CFLAGS += -D_EVENT_LOGGING
+endif
+
+ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888)
+  LOCAL_CFLAGS += -DRECOVERY_RGBX
+endif
+ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),BGRA_8888)
+  LOCAL_CFLAGS += -DRECOVERY_BGRA
+endif
+ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGB_565)
+  LOCAL_CFLAGS += -DRECOVERY_RGB_565
+endif
+
+ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"RGBX_8888")
+  LOCAL_CFLAGS += -DRECOVERY_RGBX
+endif
+ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"BGRA_8888")
+  LOCAL_CFLAGS += -DRECOVERY_BGRA
+endif
+ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"RGB_565")
+  LOCAL_CFLAGS += -DRECOVERY_RGB_565
+endif
+ifeq ($(TW_SCREEN_BLANK_ON_BOOT), true)
+    LOCAL_CFLAGS += -DTW_SCREEN_BLANK_ON_BOOT
+endif
+
+ifeq ($(BOARD_HAS_FLIPPED_SCREEN), true)
+LOCAL_CFLAGS += -DBOARD_HAS_FLIPPED_SCREEN
+endif
+
+ifeq ($(TW_IGNORE_MAJOR_AXIS_0), true)
+LOCAL_CFLAGS += -DTW_IGNORE_MAJOR_AXIS_0
+endif
+
+ifneq ($(TW_INPUT_BLACKLIST),)
+  LOCAL_CFLAGS += -DTW_INPUT_BLACKLIST=$(TW_INPUT_BLACKLIST)
+endif
+
+ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),)
+  LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=$(BOARD_USE_CUSTOM_RECOVERY_FONT)
+else
+  FONT_7x16 := 240x240 280x280 320x320 320x480
+  ROBOTO_10x18 := 480x800 480x854 540x960 800x480 1024x600 1024x768 1280x800
+  ROBOTO_15x24 := 720x1280 800x1280
+  ROBOTO_23x41 := 1080x1920 1200x1920 1440x2560 1600x2560 1920x1200 2560x1600
+  ifneq ($(filter $(DEVICE_RESOLUTION), $(FONT_7x16)),)
+    LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=\"font_7x16.h\"
+  else ifneq ($(filter $(DEVICE_RESOLUTION), $(ROBOTO_10x18)),)
+    LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=\"roboto_10x18.h\"
+  else ifneq ($(filter $(DEVICE_RESOLUTION), $(ROBOTO_15x24)),)
+    LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=\"roboto_15x24.h\"
+  else ifneq ($(filter $(DEVICE_RESOLUTION), $(ROBOTO_23x41)),)
+    LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=\"roboto_23x41.h\"
+  endif
+endif
+
+ifneq ($(TW_WHITELIST_INPUT),)
+  LOCAL_CFLAGS += -DWHITELIST_INPUT=$(TW_WHITELIST_INPUT)
+endif
+
+ifeq ($(TW_DISABLE_TTF), true)
+    LOCAL_CFLAGS += -DTW_DISABLE_TTF
+else
+    LOCAL_SHARED_LIBRARIES += libft2
+    LOCAL_C_INCLUDES += external/freetype/include
+    LOCAL_SRC_FILES += truetype.c
+endif
+
+LOCAL_SHARED_LIBRARIES += libz libc libcutils libjpeg libpng
+LOCAL_STATIC_LIBRARIES += libpixelflinger_static
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := libminuitwrp
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/minuitwrp/events.c b/minuitwrp/events.c
new file mode 100644
index 0000000..61fcf95
--- /dev/null
+++ b/minuitwrp/events.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/poll.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+#include "../common.h"
+
+#include "minui.h"
+
+//#define _EVENT_LOGGING
+
+#define MAX_DEVICES         32
+
+#define VIBRATOR_TIMEOUT_FILE	"/sys/class/timed_output/vibrator/enable"
+#define VIBRATOR_TIME_MS    50
+
+#ifndef SYN_REPORT
+#define SYN_REPORT          0x00
+#endif
+#ifndef SYN_CONFIG
+#define SYN_CONFIG          0x01
+#endif
+#ifndef SYN_MT_REPORT
+#define SYN_MT_REPORT       0x02
+#endif
+
+#define ABS_MT_POSITION     0x2a /* Group a set of X and Y */
+#define ABS_MT_AMPLITUDE    0x2b /* Group a set of Z and W */
+#define ABS_MT_SLOT         0x2f
+#define ABS_MT_TOUCH_MAJOR  0x30
+#define ABS_MT_TOUCH_MINOR  0x31
+#define ABS_MT_WIDTH_MAJOR  0x32
+#define ABS_MT_WIDTH_MINOR  0x33
+#define ABS_MT_ORIENTATION  0x34
+#define ABS_MT_POSITION_X   0x35
+#define ABS_MT_POSITION_Y   0x36
+#define ABS_MT_TOOL_TYPE    0x37
+#define ABS_MT_BLOB_ID      0x38
+#define ABS_MT_TRACKING_ID  0x39
+#define ABS_MT_PRESSURE     0x3a
+#define ABS_MT_DISTANCE     0x3b
+
+enum {
+    DOWN_NOT,
+    DOWN_SENT,
+    DOWN_RELEASED,
+};
+
+struct virtualkey {
+    int scancode;
+    int centerx, centery;
+    int width, height;
+};
+
+struct position {
+    int x, y;
+    int synced;
+    struct input_absinfo xi, yi;
+};
+
+struct ev {
+    struct pollfd *fd;
+
+    struct virtualkey *vks;
+    int vk_count;
+
+    char deviceName[64];
+
+    int ignored;
+
+    struct position p, mt_p;
+    int down;
+};
+
+static struct pollfd ev_fds[MAX_DEVICES];
+static struct ev evs[MAX_DEVICES];
+static unsigned ev_count = 0;
+static struct timeval lastInputStat;
+static unsigned long lastInputMTime;
+static int has_mouse = 0;
+
+static inline int ABS(int x) {
+    return x<0?-x:x;
+}
+
+int vibrate(int timeout_ms)
+{
+    char str[20];
+    int fd;
+    int ret;
+
+    if (timeout_ms > 10000) timeout_ms = 1000;
+
+    fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY);
+    if (fd < 0)
+        return -1;
+
+    ret = snprintf(str, sizeof(str), "%d", timeout_ms);
+    ret = write(fd, str, ret);
+    close(fd);
+
+    if (ret < 0)
+       return -1;
+
+    return 0;
+}
+
+/* Returns empty tokens */
+static char *vk_strtok_r(char *str, const char *delim, char **save_str)
+{
+    if(!str)
+    {
+        if(!*save_str)
+            return NULL;
+
+        str = (*save_str) + 1;
+    }
+    *save_str = strpbrk(str, delim);
+
+    if (*save_str)
+        **save_str = '\0';
+
+    return str;
+}
+
+static int vk_init(struct ev *e)
+{
+    char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys.";
+    char vks[2048], *ts = NULL;
+    ssize_t len;
+    int vk_fd;
+    int i;
+
+    e->vk_count = 0;
+
+    len = strlen(vk_path);
+    len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(e->deviceName)), e->deviceName);
+    if (len <= 0)
+    {
+        printf("Unable to query event object.\n");
+        return -1;
+    }
+#ifdef _EVENT_LOGGING
+    printf("Event object: %s\n", e->deviceName);
+#endif
+
+#ifdef WHITELIST_INPUT
+    if (strcmp(e->deviceName, EXPAND(WHITELIST_INPUT)) != 0)
+    {
+        e->ignored = 1;
+    }
+#else
+#ifndef TW_INPUT_BLACKLIST
+    // Blacklist these "input" devices
+    if (strcmp(e->deviceName, "bma250") == 0 || strcmp(e->deviceName, "bma150") == 0)
+    {
+        printf("blacklisting %s input device\n", e->deviceName);
+        e->ignored = 1;
+    }
+#else
+    char* bl = strdup(EXPAND(TW_INPUT_BLACKLIST));
+    char* blacklist = strtok(bl, "\n");
+
+    while (blacklist != NULL) {
+        if (strcmp(e->deviceName, blacklist) == 0) {
+            printf("blacklisting %s input device\n", blacklist);
+            e->ignored = 1;
+        }
+        blacklist = strtok(NULL, "\n");
+    }
+    free(bl);
+#endif
+#endif
+
+    strcat(vk_path, e->deviceName);
+
+    // Some devices split the keys from the touchscreen
+    e->vk_count = 0;
+    vk_fd = open(vk_path, O_RDONLY);
+    if (vk_fd >= 0)
+    {
+        len = read(vk_fd, vks, sizeof(vks)-1);
+        close(vk_fd);
+        if (len <= 0)
+            return -1;
+
+        vks[len] = '\0';
+
+        /* Parse a line like:
+            keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:...
+        */
+        for (ts = vks, e->vk_count = 1; *ts; ++ts) {
+            if (*ts == ':')
+                ++e->vk_count;
+        }
+
+        if (e->vk_count % 6) {
+            printf("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6);
+        }
+        e->vk_count /= 6;
+        if (e->vk_count <= 0)
+            return -1;
+
+        e->down = DOWN_NOT;
+    }
+
+    ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi);
+    ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi);
+    e->p.synced = 0;
+#ifdef _EVENT_LOGGING
+    printf("EV: ST minX: %d  maxX: %d  minY: %d  maxY: %d\n", e->p.xi.minimum, e->p.xi.maximum, e->p.yi.minimum, e->p.yi.maximum);
+#endif
+
+    ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi);
+    ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi);
+    e->mt_p.synced = 0;
+#ifdef _EVENT_LOGGING
+    printf("EV: MT minX: %d  maxX: %d  minY: %d  maxY: %d\n", e->mt_p.xi.minimum, e->mt_p.xi.maximum, e->mt_p.yi.minimum, e->mt_p.yi.maximum);
+#endif
+
+    e->vks = malloc(sizeof(*e->vks) * e->vk_count);
+
+    for (i = 0; i < e->vk_count; ++i) {
+        char *token[6];
+        int j;
+
+        for (j = 0; j < 6; ++j) {
+            token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts);
+        }
+
+        if (strcmp(token[0], "0x01") != 0) {
+            /* Java does string compare, so we do too. */
+            printf("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]);
+            continue;
+        }
+
+        e->vks[i].scancode = strtol(token[1], NULL, 0);
+        e->vks[i].centerx = strtol(token[2], NULL, 0);
+        e->vks[i].centery = strtol(token[3], NULL, 0);
+        e->vks[i].width = strtol(token[4], NULL, 0);
+        e->vks[i].height = strtol(token[5], NULL, 0);
+    }
+
+    return 0;
+}
+
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define OFF(x)  ((x)%BITS_PER_LONG)
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define test_bit(bit, array)	((array[LONG(bit)] >> OFF(bit)) & 1)
+
+// Check for EV_REL (REL_X and REL_Y) and, because touchscreens can have those too,
+// check also for EV_KEY (BTN_LEFT and BTN_RIGHT)
+static void check_mouse(int fd)
+{
+	if(has_mouse)
+		return;
+
+	unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
+	memset(bit, 0, sizeof(bit));
+	ioctl(fd, EVIOCGBIT(0, EV_MAX), bit[0]);
+
+	if(!test_bit(EV_REL, bit[0]) || !test_bit(EV_KEY, bit[0]))
+		return;
+
+	ioctl(fd, EVIOCGBIT(EV_REL, KEY_MAX), bit[EV_REL]);
+	if(!test_bit(REL_X, bit[EV_REL]) || !test_bit(REL_Y, bit[EV_REL]))
+		return;
+
+	ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), bit[EV_KEY]);
+	if(!test_bit(BTN_LEFT, bit[EV_KEY]) || !test_bit(BTN_RIGHT, bit[EV_KEY]))
+		return;
+
+	has_mouse = 1;
+}
+
+int ev_has_mouse(void)
+{
+	return has_mouse;
+}
+
+int ev_init(void)
+{
+    DIR *dir;
+    struct dirent *de;
+    int fd;
+
+    has_mouse = 0;
+
+	dir = opendir("/dev/input");
+    if(dir != 0) {
+        while((de = readdir(dir))) {
+//            fprintf(stderr,"/dev/input/%s\n", de->d_name);
+            if(strncmp(de->d_name,"event",5)) continue;
+            fd = openat(dirfd(dir), de->d_name, O_RDONLY);
+            if(fd < 0) continue;
+
+			ev_fds[ev_count].fd = fd;
+            ev_fds[ev_count].events = POLLIN;
+            evs[ev_count].fd = &ev_fds[ev_count];
+
+            /* Load virtualkeys if there are any */
+			vk_init(&evs[ev_count]);
+
+            check_mouse(fd);
+
+            ev_count++;
+            if(ev_count == MAX_DEVICES) break;
+        }
+        closedir(dir);
+    }
+
+    struct stat st;
+    if(stat("/dev/input", &st) >= 0)
+        lastInputMTime = st.st_mtime;
+    gettimeofday(&lastInputStat, NULL);
+
+    return 0;
+}
+
+void ev_exit(void)
+{
+	while (ev_count-- > 0) {
+		if (evs[ev_count].vk_count) {
+			free(evs[ev_count].vks);
+			evs[ev_count].vk_count = 0;
+		}
+		close(ev_fds[ev_count].fd);
+	}
+	ev_count = 0;
+}
+
+static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size)
+{
+    int screen_pos;
+
+    if (info->minimum == info->maximum)
+        return 0;
+
+    screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum);
+    return (screen_pos >= 0 && screen_pos < screen_size);
+}
+
+static int vk_tp_to_screen(struct position *p, int *x, int *y)
+{
+    if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum)
+    {
+        // In this case, we assume the screen dimensions are the same.
+        *x = p->x;
+        *y = p->y;
+        return 0;
+    }
+
+#ifdef _EVENT_LOGGING
+    printf("EV: p->x=%d  x-range=%d,%d  fb-width=%d\n", p->x, p->xi.minimum, p->xi.maximum, gr_fb_width());
+#endif
+
+#ifndef RECOVERY_TOUCHSCREEN_SWAP_XY
+    int fb_width = gr_fb_width();
+    int fb_height = gr_fb_height();
+#else
+    // We need to swap the scaling sizes, too
+    int fb_width = gr_fb_height();
+    int fb_height = gr_fb_width();
+#endif
+
+    *x = (p->x - p->xi.minimum) * (fb_width - 1) / (p->xi.maximum - p->xi.minimum);
+    *y = (p->y - p->yi.minimum) * (fb_height - 1) / (p->yi.maximum - p->yi.minimum);
+
+    if (*x >= 0 && *x < fb_width &&
+        *y >= 0 && *y < fb_height)
+    {
+        return 0;
+    }
+
+    return 1;
+}
+
+/* Translate a virtual key in to a real key event, if needed */
+/* Returns non-zero when the event should be consumed */
+static int vk_modify(struct ev *e, struct input_event *ev)
+{
+    static int downX = -1, downY = -1;
+    static int discard = 0;
+    static int last_virt_key = 0;
+    static int lastWasSynReport = 0;
+    static int touchReleaseOnNextSynReport = 0;
+	static int use_tracking_id_negative_as_touch_release = 0; // On some devices, type: 3  code: 39  value: -1, aka EV_ABS ABS_MT_TRACKING_ID -1 indicates a true touch release
+    int i;
+    int x, y;
+
+    // This is used to ditch useless event handlers, like an accelerometer
+    if (e->ignored)     return 1;
+
+    if (ev->type == EV_REL && ev->code == REL_Z)
+    {
+        // This appears to be an accelerometer or another strange input device. It's not the touchscreen.
+#ifdef _EVENT_LOGGING
+        printf("EV: Device disabled due to non-touchscreen messages.\n");
+#endif
+        e->ignored = 1;
+        return 1;
+    }
+
+#ifdef _EVENT_LOGGING
+    printf("EV: %s => type: %x  code: %x  value: %d\n", e->deviceName, ev->type, ev->code, ev->value);
+#endif
+
+	// Handle keyboard events, value of 1 indicates key down, 0 indicates key up
+	if (ev->type == EV_KEY) {
+		return 0;
+	}
+
+    if (ev->type == EV_ABS) {
+        switch (ev->code) {
+
+        case ABS_X: //00
+            e->p.synced |= 0x01;
+            e->p.x = ev->value;
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS  ABS_X  %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+        case ABS_Y: //01
+            e->p.synced |= 0x02;
+            e->p.y = ev->value;
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS  ABS_Y  %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+        case ABS_MT_POSITION: //2a
+            e->mt_p.synced = 0x03;
+            if (ev->value == (1 << 31))
+            {
+                e->mt_p.x = 0;
+                e->mt_p.y = 0;
+                lastWasSynReport = 1;
+            }
+            else
+            {
+                lastWasSynReport = 0;
+                e->mt_p.x = (ev->value & 0x7FFF0000) >> 16;
+                e->mt_p.y = (ev->value & 0xFFFF);
+            }
+            break;
+
+        case ABS_MT_TOUCH_MAJOR: //30
+            if (ev->value == 0)
+            {
+#ifndef TW_IGNORE_MAJOR_AXIS_0
+                // We're in a touch release, although some devices will still send positions as well
+                e->mt_p.x = 0;
+                e->mt_p.y = 0;
+                touchReleaseOnNextSynReport = 1;
+#endif
+            }
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS  ABS_MT_TOUCH_MAJOR  %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+		case ABS_MT_PRESSURE: //3a
+                    if (ev->value == 0)
+            {
+                // We're in a touch release, although some devices will still send positions as well
+                e->mt_p.x = 0;
+                e->mt_p.y = 0;
+                touchReleaseOnNextSynReport = 1;
+            }
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS  ABS_MT_PRESSURE  %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+		case ABS_MT_POSITION_X: //35
+            e->mt_p.synced |= 0x01;
+            e->mt_p.x = ev->value;
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS  ABS_MT_POSITION_X  %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+        case ABS_MT_POSITION_Y: //36
+            e->mt_p.synced |= 0x02;
+            e->mt_p.y = ev->value;
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS  ABS_MT_POSITION_Y  %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+        case ABS_MT_TOUCH_MINOR: //31
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS ABS_MT_TOUCH_MINOR %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+        case ABS_MT_WIDTH_MAJOR: //32
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS ABS_MT_WIDTH_MAJOR %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+        case ABS_MT_WIDTH_MINOR: //33
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS ABS_MT_WIDTH_MINOR %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+        case ABS_MT_TRACKING_ID: //39
+            if (ev->value < 0) {
+                e->mt_p.x = 0;
+                e->mt_p.y = 0;
+                touchReleaseOnNextSynReport = 2;
+                use_tracking_id_negative_as_touch_release = 1;
+#ifdef _EVENT_LOGGING
+                if (use_tracking_id_negative_as_touch_release)
+                    printf("using ABS_MT_TRACKING_ID value -1 to indicate touch releases\n");
+#endif
+            }
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS ABS_MT_TRACKING_ID %d\n", e->deviceName, ev->value);
+#endif
+            break;
+
+#ifdef _EVENT_LOGGING
+        // These are for touch logging purposes only
+        case ABS_MT_ORIENTATION: //34
+            printf("EV: %s => EV_ABS ABS_MT_ORIENTATION %d\n", e->deviceName, ev->value);
+			return 1;
+            break;
+
+		case ABS_MT_TOOL_TYPE: //37
+            LOGI("EV: %s => EV_ABS ABS_MT_TOOL_TYPE %d\n", e->deviceName, ev->value);
+			return 1;
+            break;
+
+        case ABS_MT_BLOB_ID: //38
+            printf("EV: %s => EV_ABS ABS_MT_BLOB_ID %d\n", e->deviceName, ev->value);
+			return 1;
+            break;
+
+		case ABS_MT_DISTANCE: //3b
+            printf("EV: %s => EV_ABS ABS_MT_DISTANCE %d\n", e->deviceName, ev->value);
+			return 1;
+            break;
+#endif
+
+        default:
+            // This is an unhandled message, just skip it
+            return 1;
+        }
+
+        if (ev->code != ABS_MT_POSITION)
+        {
+            lastWasSynReport = 0;
+            return 1;
+        }
+    }
+
+    // Check if we should ignore the message
+    if (ev->code != ABS_MT_POSITION && (ev->type != EV_SYN || (ev->code != SYN_REPORT && ev->code != SYN_MT_REPORT)))
+    {
+        lastWasSynReport = 0;
+        return 0;
+    }
+
+#ifdef _EVENT_LOGGING
+    if (ev->type == EV_SYN && ev->code == SYN_REPORT)       printf("EV: %s => EV_SYN  SYN_REPORT\n", e->deviceName);
+    if (ev->type == EV_SYN && ev->code == SYN_MT_REPORT)    printf("EV: %s => EV_SYN  SYN_MT_REPORT\n", e->deviceName);
+#endif
+
+    // Discard the MT versions
+    if (ev->code == SYN_MT_REPORT)      return 0;
+
+    if (((lastWasSynReport == 1 || touchReleaseOnNextSynReport == 1) && !use_tracking_id_negative_as_touch_release) || (use_tracking_id_negative_as_touch_release && touchReleaseOnNextSynReport == 2))
+    {
+        // Reset the value
+        touchReleaseOnNextSynReport = 0;
+
+        // We are a finger-up state
+        if (!discard)
+        {
+            // Report the key up
+            ev->type = EV_ABS;
+            ev->code = 0;
+            ev->value = (downX << 16) | downY;
+        }
+        downX = -1;
+        downY = -1;
+        if (discard)
+        {
+            discard = 0;
+
+            // Send the keyUp event
+            ev->type = EV_KEY;
+            ev->code = last_virt_key;
+            ev->value = 0;
+        }
+        return 0;
+    }
+    lastWasSynReport = 1;
+
+    // Retrieve where the x,y position is
+    if (e->p.synced & 0x03)
+    {
+        vk_tp_to_screen(&e->p, &x, &y);
+    }
+    else if (e->mt_p.synced & 0x03)
+    {
+        vk_tp_to_screen(&e->mt_p, &x, &y);
+    }
+    else
+    {
+        // We don't have useful information to convey
+        return 1;
+    }
+
+#ifdef RECOVERY_TOUCHSCREEN_SWAP_XY
+    x ^= y;
+    y ^= x;
+    x ^= y;
+#endif
+#ifdef RECOVERY_TOUCHSCREEN_FLIP_X
+    x = gr_fb_width() - x;
+#endif
+#ifdef RECOVERY_TOUCHSCREEN_FLIP_Y
+    y = gr_fb_height() - y;
+#endif
+
+#ifdef _EVENT_LOGGING
+    printf("EV: x: %d  y: %d\n", x, y);
+#endif
+
+    // Clear the current sync states
+    e->p.synced = e->mt_p.synced = 0;
+
+    // If we have nothing useful to report, skip it
+    if (x == -1 || y == -1)     return 1;
+
+    // On first touch, see if we're at a virtual key
+    if (downX == -1)
+    {
+        // Attempt mapping to virtual key
+        for (i = 0; i < e->vk_count; ++i)
+        {
+            int xd = ABS(e->vks[i].centerx - x);
+            int yd = ABS(e->vks[i].centery - y);
+
+            if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2)
+            {
+                ev->type = EV_KEY;
+                ev->code = e->vks[i].scancode;
+                ev->value = 1;
+
+                last_virt_key = e->vks[i].scancode;
+
+                vibrate(VIBRATOR_TIME_MS);
+
+                // Mark that all further movement until lift is discard,
+                // and make sure we don't come back into this area
+                discard = 1;
+                downX = 0;
+                return 0;
+            }
+        }
+    }
+
+    // If we were originally a button press, discard this event
+    if (discard)
+    {
+        return 1;
+    }
+
+    // Record where we started the touch for deciding if this is a key or a scroll
+    downX = x;
+    downY = y;
+
+    ev->type = EV_ABS;
+    ev->code = 1;
+    ev->value = (x << 16) | y;
+    return 0;
+}
+
+int ev_get(struct input_event *ev, unsigned dont_wait)
+{
+    int r;
+    unsigned n;
+    struct timeval curr;
+
+    do {
+        gettimeofday(&curr, NULL);
+        if(curr.tv_sec - lastInputStat.tv_sec >= 2)
+        {
+            struct stat st;
+            stat("/dev/input", &st);
+            if (st.st_mtime > lastInputMTime)
+            {
+                LOGI("Reloading input devices\n");
+                ev_exit();
+                ev_init();
+                lastInputMTime = st.st_mtime;
+            }
+            lastInputStat = curr;
+        }
+
+        r = poll(ev_fds, ev_count, 0);
+
+        if(r > 0) {
+            for(n = 0; n < ev_count; n++) {
+                if(ev_fds[n].revents & POLLIN) {
+                    r = read(ev_fds[n].fd, ev, sizeof(*ev));
+                    if(r == sizeof(*ev)) {
+                        if (!vk_modify(&evs[n], ev))
+                            return 0;
+                    }
+                }
+            }
+        }
+
+        usleep(1000);
+    } while(dont_wait == 0);
+
+    return -1;
+}
+
+int ev_wait(int timeout)
+{
+    return -1;
+}
+
+void ev_dispatch(void)
+{
+    return;
+}
+
+int ev_get_input(int fd, short revents, struct input_event *ev)
+{
+    return -1;
+}
diff --git a/minuitwrp/font_10x18.h b/minuitwrp/font_10x18.h
new file mode 100644
index 0000000..7f96465
--- /dev/null
+++ b/minuitwrp/font_10x18.h
@@ -0,0 +1,214 @@
+struct {
+  unsigned width;
+  unsigned height;
+  unsigned cwidth;
+  unsigned cheight;
+  unsigned char rundata[];
+} font = {
+  .width = 960,
+  .height = 18,
+  .cwidth = 10,
+  .cheight = 18,
+  .rundata = {
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x55,0x82,0x06,0x82,0x02,0x82,0x10,0x82,
+0x11,0x83,0x08,0x82,0x0a,0x82,0x04,0x82,0x46,0x82,0x08,0x82,0x07,0x84,0x06,
+0x84,0x0a,0x81,0x03,0x88,0x04,0x84,0x04,0x88,0x04,0x84,0x06,0x84,0x1e,0x81,
+0x0e,0x81,0x0a,0x84,0x06,0x84,0x07,0x82,0x05,0x85,0x07,0x84,0x04,0x86,0x04,
+0x88,0x02,0x88,0x04,0x84,0x04,0x82,0x04,0x82,0x02,0x88,0x05,0x86,0x01,0x82,
+0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04,
+0x86,0x06,0x84,0x04,0x86,0x06,0x84,0x04,0x88,0x02,0x82,0x04,0x82,0x02,0x82,
+0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,
+0x88,0x03,0x86,0x0e,0x86,0x06,0x82,0x11,0x82,0x10,0x82,0x18,0x82,0x0f,0x84,
+0x0d,0x82,0x1c,0x82,0x09,0x84,0x7f,0x16,0x84,0x05,0x82,0x05,0x84,0x07,0x83,
+0x02,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x03,0x86,0x04,
+0x83,0x02,0x82,0x03,0x82,0x01,0x82,0x07,0x82,0x09,0x82,0x06,0x82,0x3e,0x82,
+0x04,0x84,0x06,0x83,0x06,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x03,
+0x82,0x09,0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,
+0x1c,0x82,0x0e,0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x05,0x84,0x04,
+0x82,0x02,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,
+0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x04,
+0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x03,0x82,0x02,0x82,
+0x03,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x02,
+0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
+0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x03,0x82,0x08,0x82,0x0c,
+0x82,0x05,0x84,0x11,0x82,0x0f,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82,
+0x1c,0x82,0x0b,0x82,0x7f,0x15,0x82,0x08,0x82,0x08,0x82,0x05,0x82,0x01,0x82,
+0x01,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x02,0x82,0x01,
+0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x07,0x82,
+0x08,0x82,0x08,0x82,0x3d,0x82,0x03,0x82,0x02,0x82,0x04,0x84,0x05,0x82,0x04,
+0x82,0x02,0x82,0x04,0x82,0x06,0x83,0x03,0x82,0x08,0x82,0x04,0x81,0x09,0x82,
+0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x1a,0x82,0x10,0x82,0x06,0x82,0x04,
+0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,
+0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,
+0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83,
+0x02,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,
+0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,
+0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04,
+0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x08,0x82,0x0c,0x82,0x04,0x82,0x02,0x82,
+0x11,0x82,0x0e,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82,0x0b,0x82,0x0b,
+0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x05,0x82,
+0x02,0x83,0x1a,0x82,0x07,0x81,0x02,0x81,0x07,0x82,0x01,0x82,0x02,0x82,0x01,
+0x82,0x05,0x82,0x01,0x84,0x04,0x82,0x01,0x82,0x07,0x82,0x08,0x82,0x08,0x82,
+0x06,0x82,0x02,0x82,0x06,0x82,0x28,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01,
+0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x84,0x03,0x82,0x08,0x82,
+0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x19,0x82,0x12,0x82,0x05,
+0x82,0x04,0x82,0x02,0x82,0x02,0x84,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82,
+0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04,
+0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83,0x02,0x83,
+0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
+0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82,0x05,0x82,0x04,0x82,0x02,0x82,
+0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,
+0x82,0x04,0x82,0x09,0x82,0x0b,0x82,0x03,0x82,0x04,0x82,0x20,0x82,0x18,0x82,
+0x0e,0x82,0x10,0x82,0x0b,0x82,0x0b,0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45,
+0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x88,0x01,0x82,0x01,0x82,0x06,0x83,
+0x01,0x82,0x04,0x84,0x08,0x81,0x08,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06,
+0x82,0x28,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x08,0x82,0x04,0x82,
+0x01,0x82,0x03,0x82,0x08,0x82,0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,
+0x82,0x18,0x82,0x06,0x88,0x06,0x82,0x04,0x82,0x04,0x82,0x02,0x82,0x01,0x85,
+0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02,
+0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,
+0x02,0x82,0x04,0x82,0x08,0x88,0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02,
+0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82,
+0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x84,0x06,
+0x84,0x08,0x82,0x05,0x82,0x09,0x82,0x0b,0x82,0x2b,0x82,0x18,0x82,0x0e,0x82,
+0x10,0x82,0x1c,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x26,
+0x82,0x11,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x09,0x82,0x06,0x82,0x12,0x82,
+0x0a,0x82,0x06,0x84,0x07,0x82,0x27,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0b,
+0x82,0x07,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01,0x83,0x04,0x82,0x01,0x83,
+0x08,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x83,0x07,0x83,0x05,
+0x82,0x16,0x82,0x08,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,
+0x02,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,
+0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,
+0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,
+0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,
+0x0a,0x82,0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,
+0x82,0x04,0x84,0x06,0x84,0x08,0x82,0x05,0x82,0x0a,0x82,0x0a,0x82,0x23,0x85,
+0x03,0x82,0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x84,0x04,0x86,0x05,
+0x85,0x01,0x81,0x02,0x82,0x01,0x83,0x05,0x84,0x09,0x84,0x02,0x82,0x03,0x82,
+0x06,0x82,0x05,0x81,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x83,0x06,0x84,0x04,
+0x82,0x01,0x83,0x06,0x83,0x01,0x82,0x02,0x82,0x01,0x84,0x04,0x86,0x03,0x86,
+0x04,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
+0x82,0x02,0x82,0x04,0x82,0x03,0x87,0x05,0x82,0x08,0x82,0x08,0x82,0x26,0x82,
+0x11,0x82,0x01,0x82,0x04,0x86,0x07,0x82,0x05,0x83,0x12,0x82,0x0a,0x82,0x04,
+0x88,0x02,0x88,0x0c,0x88,0x10,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,
+0x06,0x83,0x04,0x82,0x03,0x82,0x03,0x83,0x02,0x82,0x03,0x83,0x02,0x82,0x07,
+0x82,0x06,0x84,0x05,0x82,0x02,0x83,0x05,0x83,0x07,0x83,0x04,0x82,0x18,0x82,
+0x06,0x82,0x04,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x86,0x04,
+0x82,0x08,0x82,0x04,0x82,0x02,0x86,0x04,0x86,0x04,0x82,0x02,0x84,0x02,0x88,
+0x05,0x82,0x0a,0x82,0x03,0x85,0x05,0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02,
+0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,
+0x04,0x82,0x02,0x82,0x03,0x82,0x05,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x03,
+0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x08,0x82,0x08,0x82,
+0x06,0x82,0x0a,0x82,0x0a,0x82,0x22,0x82,0x03,0x82,0x02,0x83,0x02,0x82,0x04,
+0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x02,0x82,0x05,0x82,0x06,0x82,
+0x03,0x83,0x02,0x83,0x02,0x82,0x06,0x82,0x0b,0x82,0x02,0x82,0x02,0x82,0x07,
+0x82,0x05,0x88,0x02,0x83,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82,
+0x04,0x82,0x02,0x83,0x03,0x83,0x02,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06,
+0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,
+0x03,0x82,0x04,0x82,0x08,0x82,0x02,0x84,0x09,0x82,0x09,0x84,0x23,0x82,0x11,
+0x82,0x01,0x82,0x06,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x01,0x82,0x11,0x82,
+0x0a,0x82,0x06,0x84,0x07,0x82,0x26,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x08,
+0x83,0x09,0x82,0x03,0x82,0x03,0x82,0x09,0x82,0x02,0x82,0x04,0x82,0x05,0x82,
+0x06,0x82,0x02,0x82,0x05,0x83,0x01,0x82,0x17,0x82,0x16,0x82,0x06,0x82,0x05,
+0x82,0x01,0x82,0x01,0x82,0x02,0x88,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82,
+0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,
+0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82,
+0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x86,0x04,0x82,0x04,0x82,0x02,
+0x86,0x09,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82,
+0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09,0x82,0x27,
+0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
+0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,
+0x82,0x01,0x82,0x08,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,
+0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x07,
+0x82,0x0a,0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82,
+0x04,0x84,0x04,0x82,0x04,0x82,0x07,0x82,0x06,0x82,0x08,0x82,0x08,0x82,0x26,
+0x82,0x0f,0x88,0x05,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x02,0x82,0x01,0x82,
+0x0d,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06,0x82,0x26,0x82,0x05,0x82,0x04,
+0x82,0x05,0x82,0x07,0x82,0x0c,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82,
+0x05,0x82,0x05,0x82,0x04,0x82,0x08,0x82,0x18,0x82,0x14,0x82,0x07,0x82,0x05,
+0x82,0x01,0x84,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,
+0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,
+0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82,
+0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,
+0x82,0x02,0x82,0x0a,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82,
+0x01,0x82,0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09,
+0x82,0x22,0x87,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x88,
+0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,
+0x84,0x09,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,
+0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x08,0x86,0x05,
+0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82,
+0x05,0x82,0x05,0x82,0x04,0x82,0x06,0x82,0x07,0x82,0x08,0x82,0x08,0x82,0x26,
+0x82,0x10,0x82,0x01,0x82,0x07,0x82,0x01,0x82,0x04,0x82,0x01,0x83,0x02,0x82,
+0x03,0x83,0x0f,0x82,0x08,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x25,0x82,0x07,
+0x82,0x02,0x82,0x06,0x82,0x06,0x82,0x07,0x82,0x04,0x82,0x07,0x82,0x09,0x82,
+0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04,0x82,0x08,0x82,0x19,0x82,0x05,
+0x88,0x05,0x82,0x08,0x82,0x05,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x02,0x82,
+0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,
+0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82,0x03,0x82,0x03,0x82,
+0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02,
+0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x03,0x82,0x09,0x82,0x05,0x82,
+0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x06,
+0x82,0x06,0x82,0x08,0x82,0x0c,0x82,0x08,0x82,0x21,0x82,0x04,0x82,0x02,0x82,
+0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a,0x82,0x06,0x82,0x03,
+0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x85,0x08,0x82,0x05,0x82,
+0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
+0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82,0x06,0x82,0x04,0x82,
+0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05,
+0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x38,0x82,0x01,0x82,0x04,0x82,0x01,0x82,
+0x01,0x82,0x04,0x84,0x01,0x82,0x01,0x82,0x03,0x82,0x10,0x82,0x08,0x82,0x30,
+0x83,0x06,0x82,0x07,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x08,0x82,0x04,0x82,
+0x07,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04,
+0x82,0x03,0x81,0x04,0x82,0x1a,0x82,0x10,0x82,0x10,0x82,0x08,0x82,0x04,0x82,
+0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,
+0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82,
+0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,
+0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x02,0x84,0x02,0x82,0x03,0x82,0x03,0x82,
+0x04,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x05,0x83,0x02,0x83,0x03,
+0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x09,0x82,0x0c,0x82,0x08,0x82,0x21,0x82,
+0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a,
+0x82,0x07,0x85,0x04,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x02,0x82,
+0x07,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
+0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82,
+0x06,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x04,0x84,0x04,
+0x82,0x04,0x82,0x04,0x82,0x09,0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x82,
+0x01,0x82,0x05,0x86,0x04,0x82,0x01,0x82,0x01,0x82,0x01,0x83,0x01,0x84,0x10,
+0x82,0x06,0x82,0x1d,0x83,0x11,0x83,0x05,0x82,0x09,0x84,0x07,0x82,0x05,0x82,
+0x09,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
+0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x06,0x83,0x07,0x83,0x09,0x82,
+0x0e,0x82,0x0a,0x82,0x06,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03,
+0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x09,0x82,
+0x02,0x83,0x02,0x82,0x04,0x82,0x05,0x82,0x06,0x82,0x01,0x82,0x04,0x82,0x04,
+0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,
+0x03,0x82,0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x06,
+0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
+0x05,0x82,0x05,0x82,0x09,0x82,0x0d,0x82,0x07,0x82,0x21,0x82,0x04,0x82,0x02,
+0x83,0x02,0x82,0x04,0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x03,0x82,
+0x04,0x82,0x06,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x03,
+0x82,0x06,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x03,0x82,
+0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x07,0x82,0x04,
+0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x02,0x83,0x05,0x82,0x05,0x88,0x03,0x82,
+0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x0a,0x82,0x08,0x82,0x08,0x82,0x26,
+0x82,0x1c,0x82,0x06,0x82,0x02,0x83,0x03,0x84,0x02,0x82,0x10,0x82,0x04,0x82,
+0x1e,0x83,0x11,0x83,0x05,0x82,0x0a,0x82,0x05,0x88,0x02,0x88,0x04,0x84,0x09,
+0x82,0x05,0x84,0x06,0x84,0x05,0x82,0x09,0x84,0x06,0x84,0x07,0x83,0x07,0x83,
+0x0a,0x81,0x0e,0x81,0x0b,0x82,0x07,0x85,0x03,0x82,0x04,0x82,0x02,0x86,0x06,
+0x84,0x04,0x86,0x04,0x88,0x02,0x82,0x0a,0x84,0x01,0x81,0x02,0x82,0x04,0x82,
+0x02,0x88,0x04,0x83,0x05,0x82,0x04,0x82,0x02,0x88,0x02,0x82,0x04,0x82,0x02,
+0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x0a,0x85,0x03,0x82,0x04,0x82,0x04,0x84,
+0x07,0x82,0x07,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,
+0x82,0x05,0x88,0x03,0x86,0x09,0x82,0x03,0x86,0x22,0x85,0x01,0x81,0x02,0x82,
+0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x85,0x05,0x82,0x07,0x86,0x03,
+0x82,0x04,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x88,0x02,0x82,
+0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x83,0x06,
+0x83,0x01,0x82,0x03,0x82,0x08,0x86,0x06,0x84,0x05,0x83,0x01,0x82,0x05,0x82,
+0x06,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x83,0x01,0x82,0x03,0x87,0x06,
+0x84,0x05,0x82,0x05,0x84,0x7f,0x15,0x83,0x7f,0x14,0x83,0x7f,0x5e,0x82,0x7f,
+0x05,0x89,0x47,0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x4e,
+0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a,0x82,0x04,0x82,0x17,0x82,0x03,0x82,
+0x34,0x82,0x0e,0x82,0x48,0x82,0x04,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a,
+0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x49,0x82,0x02,0x82,
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0c,0x86,0x19,0x85,0x35,0x82,0x0e,0x82,0x4a,
+0x84,0x3f,
+0x00,
+  }
+};
diff --git a/minuitwrp/font_7x16.h b/minuitwrp/font_7x16.h
new file mode 100644
index 0000000..0f72b53
--- /dev/null
+++ b/minuitwrp/font_7x16.h
@@ -0,0 +1,15 @@
+struct {
+  unsigned width;
+  unsigned height;
+  unsigned cwidth;
+  unsigned cheight;
+  unsigned char rundata[];
+} font = {
+  .width = 668,
+  .height = 16,
+  .cwidth = 7,
+  .cheight = 16,
+  .rundata = {
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7e,0x82,0x03,0x82,0x7f,0x7f,0x5f,0x82,0x0b,0x82,0x14,0x81,0x0b,0x81,0x11,0x81,0x0c,0x82,0x09,0x81,0x08,0x81,0x07,0x81,0x03,0x81,0x06,0x83,0x68,0x83,0x04,0x81,0x04,0x83,0x17,0x81,0x05,0x81,0x01,0x81,0x0c,0x81,0x04,0x82,0x07,0x83,0x04,0x81,0x07,0x81,0x05,0x81,0x06,0x81,0x25,0x81,0x02,0x84,0x02,0x83,0x05,0x84,0x03,0x84,0x05,0x82,0x02,0x85,0x04,0x83,0x02,0x86,0x02,0x84,0x03,0x84,0x27,0x83,0x0b,0x82,0x03,0x85,0x04,0x83,0x02,0x84,0x03,0x86,0x01,0x86,0x03,0x83,0x02,0x81,0x04,0x81,0x01,0x85,0x04,0x83,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x82,0x03,0x81,0x02,0x84,0x02,0x85,0x03,0x84,0x02,0x85,0x03,0x84,0x01,0x87,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x82,0x05,0x81,0x01,0x81,0x04,0x82,0x05,0x81,0x01,0x86,0x03,0x81,0x04,0x81,0x08,0x81,0x05,0x82,0x0e,0x81,0x0a,0x81,0x11,0x81,0x0b,0x81,0x0b,0x81,0x14,0x81,0x08,0x81,0x37,0x81,0x30,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x05,0x81,0x01,0x81,0x05,0x81,0x01,0x81,0x03,0x83,0x02,0x81,0x02,0x81,0x05,0x81,0x07,0x81,0x07,0x81,0x05,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x22,0x81,0x03,0x81,0x02,0x81,0x04,0x81,0x04,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x04,0x82,0x02,0x81,0x07,0x81,0x03,0x81,0x05,0x82,0x01,0x81,0x04,0x81,0x01,0x82,0x02,0x81,0x26,0x81,0x03,0x81,0x03,0x83,0x04,0x82,0x03,0x81,0x04,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x82,0x02,0x82,0x01,0x82,0x03,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x01,0x81,0x04,0x82,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x06,0x82,0x03,0x81,0x05,0x81,0x07,0x81,0x04,0x81,0x02,0x81,0x18,0x81,0x11,0x81,0x0b,0x81,0x0b,0x81,0x14,0x81,0x08,0x81,0x37,0x81,0x30,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x05,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x05,0x81,0x07,0x81,0x06,0x81,0x07,0x81,0x04,0x83,0x05,0x81,0x1d,0x81,0x02,0x81,0x04,0x81,0x03,0x81,0x09,0x81,0x06,0x81,0x03,0x81,0x01,0x81,0x02,0x81,0x06,0x81,0x0a,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x14,0x81,0x08,0x81,0x0b,0x81,0x02,0x81,0x02,0x82,0x03,0x82,0x03,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x06,0x82,0x02,0x82,0x01,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x08,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x07,0x81,0x04,0x81,0x05,0x81,0x07,0x81,0x03,0x81,0x04,0x81,0x11,0x83,0x03,0x84,0x04,0x83,0x04,0x84,0x03,0x83,0x03,0x85,0x03,0x84,0x02,0x81,0x01,0x82,0x03,0x83,0x05,0x83,0x03,0x81,0x03,0x81,0x04,0x81,0x04,0x85,0x02,0x81,0x01,0x82,0x04,0x83,0x03,0x84,0x04,0x84,0x03,0x84,0x03,0x83,0x03,0x85,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x05,0x81,0x01,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x85,0x04,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0b,0x86,0x01,0x81,0x01,0x81,0x04,0x82,0x02,0x81,0x03,0x82,0x0d,0x81,0x07,0x81,0x04,0x83,0x05,0x81,0x1c,0x81,0x03,0x81,0x04,0x81,0x03,0x81,0x09,0x81,0x06,0x81,0x02,0x82,0x01,0x81,0x02,0x85,0x02,0x81,0x01,0x83,0x06,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x06,0x81,0x06,0x83,0x0a,0x83,0x06,0x82,0x02,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x01,0x81,0x04,0x81,0x06,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x82,0x07,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x03,0x82,0x04,0x81,0x01,0x81,0x06,0x81,0x05,0x81,0x06,0x81,0x06,0x81,0x19,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x82,0x05,0x81,0x03,0x81,0x02,0x82,0x02,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x82,0x02,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x02,0x81,0x05,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x82,0x02,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x82,0x02,0x81,0x01,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x05,0x81,0x02,0x81,0x01,0x81,0x03,0x81,0x03,0x81,0x06,0x81,0x04,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0c,0x81,0x01,0x81,0x03,0x83,0x06,0x82,0x04,0x82,0x0d,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x1c,0x81,0x03,0x81,0x02,0x81,0x01,0x81,0x03,0x81,0x08,0x81,0x04,0x83,0x03,0x81,0x02,0x81,0x06,0x82,0x01,0x82,0x02,0x82,0x04,0x81,0x04,0x84,0x02,0x81,0x03,0x82,0x03,0x81,0x06,0x81,0x04,0x82,0x05,0x86,0x05,0x82,0x03,0x82,0x03,0x81,0x02,0x83,0x02,0x81,0x02,0x81,0x02,0x85,0x02,0x81,0x06,0x81,0x04,0x81,0x01,0x86,0x01,0x86,0x01,0x81,0x03,0x82,0x01,0x86,0x03,0x81,0x08,0x81,0x02,0x83,0x04,0x81,0x06,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x04,0x81,0x01,0x85,0x02,0x81,0x04,0x81,0x01,0x85,0x03,0x84,0x04,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x03,0x82,0x05,0x81,0x06,0x82,0x05,0x81,0x06,0x81,0x06,0x81,0x1d,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x05,0x81,0x08,0x81,0x04,0x81,0x03,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x03,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x03,0x82,0x07,0x81,0x07,0x82,0x02,0x83,0x10,0x81,0x0c,0x81,0x01,0x81,0x05,0x83,0x02,0x82,0x01,0x82,0x02,0x81,0x02,0x81,0x01,0x81,0x0a,0x81,0x07,0x81,0x05,0x81,0x03,0x87,0x09,0x83,0x0c,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x07,0x81,0x08,0x81,0x01,0x81,0x03,0x81,0x07,0x81,0x01,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x02,0x83,0x01,0x81,0x0f,0x82,0x10,0x82,0x03,0x81,0x04,0x81,0x01,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x06,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x03,0x81,0x07,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x03,0x82,0x05,0x81,0x06,0x81,0x06,0x81,0x07,0x81,0x05,0x81,0x1a,0x84,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x03,0x81,0x02,0x85,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x82,0x07,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x06,0x83,0x05,0x81,0x04,0x81,0x03,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x05,0x81,0x01,0x81,0x05,0x81,0x06,0x81,0x06,0x81,0x06,0x81,0x07,0x83,0x18,0x86,0x04,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x01,0x81,0x02,0x83,0x0a,0x81,0x07,0x81,0x0c,0x81,0x1b,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x06,0x81,0x09,0x81,0x01,0x86,0x06,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x06,0x81,0x11,0x83,0x02,0x86,0x02,0x83,0x0a,0x81,0x01,0x81,0x02,0x81,0x02,0x84,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x02,0x82,0x02,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x06,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x82,0x03,0x82,0x01,0x82,0x03,0x81,0x02,0x81,0x04,0x81,0x05,0x81,0x07,0x81,0x07,0x81,0x05,0x81,0x19,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x03,0x81,0x02,0x81,0x08,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x09,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x03,0x81,0x01,0x81,0x03,0x82,0x01,0x82,0x03,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x07,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0b,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x01,0x82,0x02,0x81,0x0c,0x81,0x05,0x81,0x0d,0x81,0x06,0x81,0x0d,0x81,0x05,0x81,0x06,0x81,0x02,0x81,0x04,0x81,0x05,0x81,0x05,0x81,0x04,0x81,0x05,0x81,0x02,0x81,0x03,0x82,0x02,0x81,0x02,0x82,0x03,0x81,0x04,0x81,0x04,0x81,0x01,0x81,0x03,0x81,0x04,0x81,0x06,0x81,0x09,0x81,0x08,0x81,0x08,0x81,0x04,0x81,0x02,0x83,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x03,0x82,0x02,0x81,0x02,0x81,0x02,0x81,0x07,0x81,0x02,0x82,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x82,0x03,0x81,0x03,0x81,0x03,0x81,0x02,0x81,0x04,0x81,0x04,0x82,0x07,0x81,0x08,0x81,0x04,0x81,0x19,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x82,0x05,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x02,0x81,0x05,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x05,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x05,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x82,0x04,0x81,0x08,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0b,0x81,0x01,0x81,0x05,0x83,0x06,0x82,0x03,0x83,0x01,0x81,0x0b,0x81,0x05,0x81,0x0d,0x81,0x06,0x81,0x0d,0x81,0x05,0x81,0x06,0x84,0x02,0x85,0x02,0x86,0x02,0x84,0x06,0x81,0x03,0x84,0x03,0x84,0x03,0x81,0x06,0x84,0x03,0x83,0x05,0x81,0x06,0x81,0x1b,0x81,0x04,0x82,0x05,0x81,0x04,0x81,0x01,0x85,0x04,0x83,0x02,0x84,0x03,0x86,0x01,0x81,0x08,0x83,0x02,0x81,0x04,0x81,0x01,0x85,0x03,0x83,0x03,0x81,0x04,0x81,0x01,0x86,0x01,0x81,0x04,0x81,0x01,0x81,0x03,0x82,0x02,0x84,0x02,0x81,0x07,0x84,0x02,0x81,0x05,0x81,0x01,0x84,0x04,0x81,0x05,0x84,0x04,0x82,0x03,0x81,0x03,0x81,0x02,0x81,0x04,0x81,0x03,0x81,0x04,0x86,0x03,0x81,0x08,0x81,0x04,0x81,0x1a,0x84,0x02,0x84,0x04,0x83,0x04,0x84,0x03,0x83,0x05,0x81,0x05,0x84,0x02,0x81,0x03,0x81,0x02,0x85,0x05,0x81,0x03,0x81,0x03,0x81,0x05,0x82,0x02,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x03,0x83,0x03,0x84,0x04,0x84,0x03,0x81,0x06,0x83,0x05,0x83,0x03,0x84,0x04,0x81,0x05,0x81,0x01,0x81,0x03,0x81,0x03,0x81,0x04,0x81,0x04,0x85,0x04,0x81,0x06,0x81,0x06,0x81,0x2c,0x81,0x1c,0x82,0x03,0x82,0x13,0x81,0x13,0x81,0x54,0x81,0x22,0x81,0x79,0x81,0x43,0x82,0x08,0x81,0x02,0x82,0x47,0x81,0x13,0x81,0x26,0x81,0x0a,0x81,0x35,0x81,0x0d,0x83,0x04,0x81,0x04,0x83,0x2c,0x81,0x7f,0x44,0x83,0x76,0x81,0x7f,0x17,0x81,0x02,0x81,0x13,0x81,0x26,0x81,0x0a,0x81,0x34,0x81,0x15,0x81,0x7f,0x7f,0x7f,0x50,0x87,0x34,0x82,0x12,0x82,0x27,0x81,0x0a,0x81,0x33,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x4b,0x00,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x00,0x49,0x44,0x41,0x54,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0x00,0x00,0x00,0x00,0x50,0x4c,0x54,0x45,0x00,0x00,0x00,0x00,0x62,0x4b,0x47,0x44,0x00,0x00,0x00,0x00,0x63,0x48,0x52,0x4d,0x00,0x00,0x00,0x00,0x67,0x41,0x4d,0x41,0x00,0x00,0x00,0x00,0x68,0x49,0x53,0x54,0x00,0x00,0x00,0x00,0x69,0x43,0x43,0x50,0x00,0x00,0x00,0x00,0x69,0x54,0x58,0x74,0x00,0x00,0x00,0x00,0x6f,0x46,0x46,0x73,0x00,0x00,0x00,0x00,0x70,0x43,0x41,0x4c,0x00,0x00,0x00,0x00,0x73,0x43,0x41,0x4c,0x00,0x00,0x00,0x00,0x70,0x48,0x59,0x73,0x00,0x00,0x00,0x00,0x73,0x42,0x49,0x54,0x00,0x00,0x00,0x00,0x73,0x50,0x4c,0x54,0x00,0x00,0x00,0x00,0x73,0x52,0x47,0x42,0x00,0x00,0x00,0x00,0x74,0x45,0x58,0x74,0x00,0x00,0x00,0x00,0x74,0x49,0x4d,0x45,0x00,0x00,0x00,0x00,0x74,0x52,0x4e,0x53,0x00,0x00,0x00,0x00,0x7a,0x54,0x58,0x74,0x00,
+  }
+};
diff --git a/minuitwrp/graphics.c b/minuitwrp/graphics.c
new file mode 100644
index 0000000..9926904
--- /dev/null
+++ b/minuitwrp/graphics.c
@@ -0,0 +1,861 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <linux/fb.h>
+#include <linux/kd.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+#include "minui.h"
+
+#ifdef BOARD_USE_CUSTOM_RECOVERY_FONT
+#include BOARD_USE_CUSTOM_RECOVERY_FONT
+#else
+#include "font_10x18.h"
+#endif
+
+#ifdef RECOVERY_BGRA
+#define PIXEL_FORMAT GGL_PIXEL_FORMAT_BGRA_8888
+#define PIXEL_SIZE 4
+#endif
+#ifdef RECOVERY_RGBX
+#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888
+#define PIXEL_SIZE 4
+#endif
+#ifndef PIXEL_FORMAT
+#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565
+#define PIXEL_SIZE 2
+#endif
+
+#define NUM_BUFFERS 2
+#define MAX_DISPLAY_DIM  2048
+
+// #define PRINT_SCREENINFO 1 // Enables printing of screen info to log
+
+typedef struct {
+    int type;
+    GGLSurface texture;
+    unsigned offset[97];
+    unsigned cheight;
+    unsigned ascent;
+} GRFont;
+
+static GRFont *gr_font = 0;
+static GGLContext *gr_context = 0;
+static GGLSurface gr_font_texture;
+static GGLSurface gr_framebuffer[NUM_BUFFERS];
+GGLSurface gr_mem_surface;
+static unsigned gr_active_fb = 0;
+static unsigned double_buffering = 0;
+static int gr_is_curr_clr_opaque = 0;
+
+static int gr_fb_fd = -1;
+static int gr_vt_fd = -1;
+
+struct fb_var_screeninfo vi;
+static struct fb_fix_screeninfo fi;
+
+static bool has_overlay = false;
+static int leftSplit = 0;
+static int rightSplit = 0;
+
+bool target_has_overlay(char *version);
+int free_ion_mem(void);
+int alloc_ion_mem(unsigned int size);
+int allocate_overlay(int fd, GGLSurface gr_fb[]);
+int free_overlay(int fd);
+int overlay_display_frame(int fd, GGLubyte* data, size_t size);
+
+#ifdef PRINT_SCREENINFO
+static void print_fb_var_screeninfo()
+{
+	printf("vi.xres: %d\n", vi.xres);
+	printf("vi.yres: %d\n", vi.yres);
+	printf("vi.xres_virtual: %d\n", vi.xres_virtual);
+	printf("vi.yres_virtual: %d\n", vi.yres_virtual);
+	printf("vi.xoffset: %d\n", vi.xoffset);
+	printf("vi.yoffset: %d\n", vi.yoffset);
+	printf("vi.bits_per_pixel: %d\n", vi.bits_per_pixel);
+	printf("vi.grayscale: %d\n", vi.grayscale);
+}
+#endif
+
+#ifdef MSM_BSP
+int getLeftSplit(void) {
+   //Default even split for all displays with high res
+   int lSplit = vi.xres / 2;
+
+   //Override if split published by driver
+   if (leftSplit)
+       lSplit = leftSplit;
+
+   return lSplit;
+}
+
+int getRightSplit(void) {
+   return rightSplit;
+}
+
+
+void setDisplaySplit(void) {
+    char split[64] = {0};
+    FILE* fp = fopen("/sys/class/graphics/fb0/msm_fb_split", "r");
+    if (fp) {
+        //Format "left right" space as delimiter
+        if(fread(split, sizeof(char), 64, fp)) {
+            leftSplit = atoi(split);
+            printf("Left Split=%d\n",leftSplit);
+            char *rght = strpbrk(split, " ");
+            if (rght)
+                rightSplit = atoi(rght + 1);
+            printf("Right Split=%d\n", rightSplit);
+        }
+    } else {
+        printf("Failed to open mdss_fb_split node\n");
+    }
+    if (fp)
+        fclose(fp);
+}
+
+bool isDisplaySplit(void) {
+    if (vi.xres > MAX_DISPLAY_DIM)
+        return true;
+    //check if right split is set by driver
+    if (getRightSplit())
+        return true;
+
+    return false;
+}
+
+int getFbXres(void) {
+    return vi.xres;
+}
+
+int getFbYres (void) {
+    return vi.yres;
+}
+#endif // MSM_BSP
+
+static int get_framebuffer(GGLSurface *fb)
+{
+    int fd;
+    void *bits;
+
+    fd = open("/dev/graphics/fb0", O_RDWR);
+    if (fd < 0) {
+        perror("cannot open fb0");
+        return -1;
+    }
+
+    if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
+        perror("failed to get fb0 info");
+        close(fd);
+        return -1;
+    }
+
+    fprintf(stderr, "Pixel format: %dx%d @ %dbpp\n", vi.xres, vi.yres, vi.bits_per_pixel);
+
+    vi.bits_per_pixel = PIXEL_SIZE * 8;
+    if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_BGRA_8888) {
+        fprintf(stderr, "Pixel format: BGRA_8888\n");
+        if (PIXEL_SIZE != 4)    fprintf(stderr, "E: Pixel Size mismatch!\n");
+        vi.red.offset     = 8;
+        vi.red.length     = 8;
+        vi.green.offset   = 16;
+        vi.green.length   = 8;
+        vi.blue.offset    = 24;
+        vi.blue.length    = 8;
+        vi.transp.offset  = 0;
+        vi.transp.length  = 8;
+    } else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGBX_8888) {
+        fprintf(stderr, "Pixel format: RGBX_8888\n");
+        if (PIXEL_SIZE != 4)    fprintf(stderr, "E: Pixel Size mismatch!\n");
+        vi.red.offset     = 24;
+        vi.red.length     = 8;
+        vi.green.offset   = 16;
+        vi.green.length   = 8;
+        vi.blue.offset    = 8;
+        vi.blue.length    = 8;
+        vi.transp.offset  = 0;
+        vi.transp.length  = 8;
+    } else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGB_565) {
+#ifdef RECOVERY_RGB_565
+		fprintf(stderr, "Pixel format: RGB_565\n");
+		vi.blue.offset    = 0;
+		vi.green.offset   = 5;
+		vi.red.offset     = 11;
+#else
+        fprintf(stderr, "Pixel format: BGR_565\n");
+		vi.blue.offset    = 11;
+		vi.green.offset   = 5;
+		vi.red.offset     = 0;
+#endif
+		if (PIXEL_SIZE != 2)    fprintf(stderr, "E: Pixel Size mismatch!\n");
+		vi.blue.length    = 5;
+		vi.green.length   = 6;
+		vi.red.length     = 5;
+        vi.blue.msb_right = 0;
+        vi.green.msb_right = 0;
+        vi.red.msb_right = 0;
+        vi.transp.offset  = 0;
+        vi.transp.length  = 0;
+    }
+    else
+    {
+        perror("unknown pixel format");
+        close(fd);
+        return -1;
+    }
+
+    vi.vmode = FB_VMODE_NONINTERLACED;
+    vi.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+
+    if (ioctl(fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
+        perror("failed to put fb0 info");
+        close(fd);
+        return -1;
+    }
+
+    if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
+        perror("failed to get fb0 info");
+        close(fd);
+        return -1;
+    }
+
+#ifdef MSM_BSP
+    has_overlay = target_has_overlay(fi.id);
+
+    if (isTargetMdp5())
+        setDisplaySplit();
+#else
+    has_overlay = false;
+#endif
+
+    if (!has_overlay) {
+        printf("Not using qualcomm overlay, '%s'\n", fi.id);
+        bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+        if (bits == MAP_FAILED) {
+            perror("failed to mmap framebuffer");
+            close(fd);
+            return -1;
+        }
+    } else {
+        printf("Using qualcomm overlay\n");
+    }
+
+#ifdef RECOVERY_GRAPHICS_USE_LINELENGTH
+    vi.xres_virtual = fi.line_length / PIXEL_SIZE;
+#endif
+
+    fb->version = sizeof(*fb);
+    fb->width = vi.xres;
+    fb->height = vi.yres;
+#ifdef BOARD_HAS_JANKY_BACKBUFFER
+    printf("setting JANKY BACKBUFFER\n");
+    fb->stride = fi.line_length/2;
+#else
+    fb->stride = vi.xres_virtual;
+#endif
+    fb->format = PIXEL_FORMAT;
+    if (!has_overlay) {
+        fb->data = bits;
+        memset(fb->data, 0, vi.yres * fb->stride * PIXEL_SIZE);
+    }
+
+    fb++;
+
+    /* check if we can use double buffering */
+    if (vi.yres * fi.line_length * 2 > fi.smem_len)
+        return fd;
+
+    double_buffering = 1;
+
+    fb->version = sizeof(*fb);
+    fb->width = vi.xres;
+    fb->height = vi.yres;
+#ifdef BOARD_HAS_JANKY_BACKBUFFER
+    fb->stride = fi.line_length/2;
+    fb->data = (void*) (((unsigned) bits) + vi.yres * fi.line_length);
+#else
+    fb->stride = vi.xres_virtual;
+    fb->data = (void*) (((unsigned) bits) + vi.yres * fb->stride * PIXEL_SIZE);
+#endif
+    fb->format = PIXEL_FORMAT;
+    if (!has_overlay) {
+        memset(fb->data, 0, vi.yres * fb->stride * PIXEL_SIZE);
+    }
+
+#ifdef PRINT_SCREENINFO
+	print_fb_var_screeninfo();
+#endif
+
+    return fd;
+}
+
+static void get_memory_surface(GGLSurface* ms) {
+  ms->version = sizeof(*ms);
+  ms->width = vi.xres;
+  ms->height = vi.yres;
+  ms->stride = vi.xres_virtual;
+  ms->data = malloc(vi.xres_virtual * vi.yres * PIXEL_SIZE);
+  ms->format = PIXEL_FORMAT;
+}
+
+static void set_active_framebuffer(unsigned n)
+{
+    if (n > 1  || !double_buffering) return;
+    vi.yres_virtual = vi.yres * NUM_BUFFERS;
+    vi.yoffset = n * vi.yres;
+//    vi.bits_per_pixel = PIXEL_SIZE * 8;
+    if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
+        perror("active fb swap failed");
+    }
+}
+
+void gr_flip(void)
+{
+    if (-EINVAL == overlay_display_frame(gr_fb_fd, gr_mem_surface.data,
+                                         (fi.line_length * vi.yres))) {
+        GGLContext *gl = gr_context;
+
+        /* swap front and back buffers */
+        if (double_buffering)
+            gr_active_fb = (gr_active_fb + 1) & 1;
+
+#ifdef BOARD_HAS_FLIPPED_SCREEN
+        /* flip buffer 180 degrees for devices with physicaly inverted screens */
+        unsigned int i;
+        unsigned int j;
+        uint8_t tmp;
+        for (i = 0; i < ((vi.xres_virtual * vi.yres)/2); i++) {
+            for (j = 0; j < PIXEL_SIZE; j++) {
+                tmp = gr_mem_surface.data[i * PIXEL_SIZE + j];
+                gr_mem_surface.data[i * PIXEL_SIZE + j] = gr_mem_surface.data[(vi.xres_virtual * vi.yres * PIXEL_SIZE) - ((i+1) * PIXEL_SIZE) + j];
+                gr_mem_surface.data[(vi.xres_virtual * vi.yres * PIXEL_SIZE) - ((i+1) * PIXEL_SIZE) + j] = tmp;
+            }
+        }
+#endif
+
+        /* copy data from the in-memory surface to the buffer we're about
+         * to make active. */
+        memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data,
+               vi.xres_virtual * vi.yres * PIXEL_SIZE);
+
+        /* inform the display driver */
+        set_active_framebuffer(gr_active_fb);
+    }
+}
+
+void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+    GGLContext *gl = gr_context;
+    GGLint color[4];
+    color[0] = ((r << 8) | r) + 1;
+    color[1] = ((g << 8) | g) + 1;
+    color[2] = ((b << 8) | b) + 1;
+    color[3] = ((a << 8) | a) + 1;
+    gl->color4xv(gl, color);
+
+    gr_is_curr_clr_opaque = (a == 255);
+}
+
+int gr_measureEx(const char *s, void* font)
+{
+    GRFont* fnt = (GRFont*) font;
+    int total = 0;
+    unsigned pos;
+    unsigned off;
+
+    if (!fnt)   fnt = gr_font;
+
+#ifndef TW_DISABLE_TTF
+    if(fnt->type == FONT_TYPE_TTF)
+        return gr_ttf_measureEx(s, font);
+#endif
+
+    while ((off = *s++))
+    {
+        off -= 32;
+        if (off < 96)
+            total += (fnt->offset[off+1] - fnt->offset[off]);
+    }
+    return total;
+}
+
+int gr_maxExW(const char *s, void* font, int max_width)
+{
+    GRFont* fnt = (GRFont*) font;
+    int total = 0;
+    unsigned pos;
+    unsigned off;
+
+    if (!fnt)   fnt = gr_font;
+
+#ifndef TW_DISABLE_TTF
+    if(fnt->type == FONT_TYPE_TTF)
+        return gr_ttf_maxExW(s, font, max_width);
+#endif
+
+    while ((off = *s++))
+    {
+        off -= 32;
+        if (off < 96) {
+            max_width -= (fnt->offset[off+1] - fnt->offset[off]);
+			if (max_width > 0) {
+				total++;
+			} else {
+				return total;
+			}
+		}
+    }
+    return total;
+}
+
+int gr_textEx(int x, int y, const char *s, void* pFont)
+{
+    GGLContext *gl = gr_context;
+    GRFont *font = (GRFont*) pFont;
+    unsigned off;
+    unsigned cwidth;
+
+    /* Handle default font */
+    if (!font)  font = gr_font;
+
+#ifndef TW_DISABLE_TTF
+    if(font->type == FONT_TYPE_TTF)
+        return gr_ttf_textExWH(gl, x, y, s, pFont, -1, -1);
+#endif
+
+    gl->bindTexture(gl, &font->texture);
+    gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
+    gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->enable(gl, GGL_TEXTURE_2D);
+
+    while((off = *s++)) {
+        off -= 32;
+        cwidth = 0;
+        if (off < 96) {
+            cwidth = font->offset[off+1] - font->offset[off];
+			gl->texCoord2i(gl, (font->offset[off]) - x, 0 - y);
+			gl->recti(gl, x, y, x + cwidth, y + font->cheight);
+			x += cwidth;
+        }
+    }
+
+    return x;
+}
+
+int gr_textExW(int x, int y, const char *s, void* pFont, int max_width)
+{
+    GGLContext *gl = gr_context;
+    GRFont *font = (GRFont*) pFont;
+    unsigned off;
+    unsigned cwidth;
+
+    /* Handle default font */
+    if (!font)  font = gr_font;
+
+#ifndef TW_DISABLE_TTF
+    if(font->type == FONT_TYPE_TTF)
+        return gr_ttf_textExWH(gl, x, y, s, pFont, max_width, -1);
+#endif
+
+    gl->bindTexture(gl, &font->texture);
+    gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
+    gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->enable(gl, GGL_TEXTURE_2D);
+
+    while((off = *s++)) {
+        off -= 32;
+        cwidth = 0;
+        if (off < 96) {
+            cwidth = font->offset[off+1] - font->offset[off];
+			if ((x + (int)cwidth) < max_width) {
+				gl->texCoord2i(gl, (font->offset[off]) - x, 0 - y);
+				gl->recti(gl, x, y, x + cwidth, y + font->cheight);
+				x += cwidth;
+			} else {
+				gl->texCoord2i(gl, (font->offset[off]) - x, 0 - y);
+				gl->recti(gl, x, y, max_width, y + font->cheight);
+				x = max_width;
+				return x;
+			}
+        }
+    }
+
+    return x;
+}
+
+int gr_textExWH(int x, int y, const char *s, void* pFont, int max_width, int max_height)
+{
+    GGLContext *gl = gr_context;
+    GRFont *font = (GRFont*) pFont;
+    unsigned off;
+    unsigned cwidth;
+	int rect_x, rect_y;
+
+    /* Handle default font */
+    if (!font)  font = gr_font;
+
+#ifndef TW_DISABLE_TTF
+    if(font->type == FONT_TYPE_TTF)
+        return gr_ttf_textExWH(gl, x, y, s, pFont, max_width, max_height);
+#endif
+
+    gl->bindTexture(gl, &font->texture);
+    gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
+    gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->enable(gl, GGL_TEXTURE_2D);
+
+    while((off = *s++)) {
+        off -= 32;
+        cwidth = 0;
+        if (off < 96) {
+            cwidth = font->offset[off+1] - font->offset[off];
+			if ((x + (int)cwidth) < max_width)
+				rect_x = x + cwidth;
+			else
+				rect_x = max_width;
+			if (y + font->cheight < (unsigned int)(max_height))
+				rect_y = y + font->cheight;
+			else
+				rect_y = max_height;
+
+			gl->texCoord2i(gl, (font->offset[off]) - x, 0 - y);
+			gl->recti(gl, x, y, rect_x, rect_y);
+			x += cwidth;
+			if (x > max_width)
+				return x;
+        }
+    }
+
+    return x;
+}
+
+void gr_fill(int x, int y, int w, int h)
+{
+    GGLContext *gl = gr_context;
+
+    if(gr_is_curr_clr_opaque)
+        gl->disable(gl, GGL_BLEND);
+
+    gl->disable(gl, GGL_TEXTURE_2D);
+    gl->recti(gl, x, y, x + w, y + h);
+
+    if(gr_is_curr_clr_opaque)
+        gl->enable(gl, GGL_BLEND);
+}
+
+void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
+    if (gr_context == NULL) {
+        return;
+    }
+
+    GGLContext *gl = gr_context;
+    GGLSurface *surface = (GGLSurface*)source;
+
+    if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
+        gl->disable(gl, GGL_BLEND);
+
+    gl->bindTexture(gl, surface);
+    gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
+    gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->enable(gl, GGL_TEXTURE_2D);
+    gl->texCoord2i(gl, sx - dx, sy - dy);
+    gl->recti(gl, dx, dy, dx + w, dy + h);
+
+    if(surface->format == GGL_PIXEL_FORMAT_RGBX_8888)
+        gl->enable(gl, GGL_BLEND);
+}
+
+unsigned int gr_get_width(gr_surface surface) {
+    if (surface == NULL) {
+        return 0;
+    }
+    return ((GGLSurface*) surface)->width;
+}
+
+unsigned int gr_get_height(gr_surface surface) {
+    if (surface == NULL) {
+        return 0;
+    }
+    return ((GGLSurface*) surface)->height;
+}
+
+void* gr_loadFont(const char* fontName)
+{
+    int fd;
+    GRFont *font = 0;
+    GGLSurface *ftex;
+    unsigned char *bits, *rle;
+    unsigned char *in, data;
+    unsigned width, height;
+    unsigned element;
+
+    fd = open(fontName, O_RDONLY);
+    if (fd == -1)
+    {
+        char tmp[128];
+
+        sprintf(tmp, "/res/fonts/%s.dat", fontName);
+        fd = open(tmp, O_RDONLY);
+        if (fd == -1)
+            return NULL;
+    }
+
+    font = calloc(sizeof(*font), 1);
+    ftex = &font->texture;
+
+    read(fd, &width, sizeof(unsigned));
+    read(fd, &height, sizeof(unsigned));
+    read(fd, font->offset, sizeof(unsigned) * 96);
+    font->offset[96] = width;
+
+    bits = malloc(width * height);
+    memset(bits, 0, width * height);
+
+    unsigned pos = 0;
+    while (pos < width * height)
+    {
+        int bit;
+
+        read(fd, &data, 1);
+        for (bit = 0; bit < 8; bit++)
+        {
+            if (data & (1 << (7-bit)))  bits[pos++] = 255;
+            else                        bits[pos++] = 0;
+
+            if (pos == width * height)  break;
+        }
+    }
+    close(fd);
+
+    ftex->version = sizeof(*ftex);
+    ftex->width = width;
+    ftex->height = height;
+    ftex->stride = width;
+    ftex->data = (void*) bits;
+    ftex->format = GGL_PIXEL_FORMAT_A_8;
+    font->type = FONT_TYPE_TWRP;
+    font->cheight = height;
+    font->ascent = height - 2;
+    return (void*) font;
+}
+
+void gr_freeFont(void *font)
+{
+    GRFont *f = font;
+    free(f->texture.data);
+    free(f);
+}
+
+int gr_getMaxFontHeight(void *font)
+{
+    GRFont *fnt = (GRFont*) font;
+
+    if (!fnt)   fnt = gr_font;
+    if (!fnt)   return -1;
+
+#ifndef TW_DISABLE_TTF
+    if(fnt->type == FONT_TYPE_TTF)
+        return gr_ttf_getMaxFontHeight(font);
+#endif
+
+    return fnt->cheight;
+}
+
+static void gr_init_font(void)
+{
+    int fontRes;
+    GGLSurface *ftex;
+    unsigned char *bits, *rle;
+    unsigned char *in, data;
+    unsigned width, height;
+    unsigned element;
+
+    gr_font = calloc(sizeof(*gr_font), 1);
+    ftex = &gr_font->texture;
+
+    width = font.width;
+    height = font.height;
+
+    bits = malloc(width * height);
+    rle = bits;
+
+    in = font.rundata;
+    while((data = *in++))
+    {
+        memset(rle, (data & 0x80) ? 255 : 0, data & 0x7f);
+        rle += (data & 0x7f);
+    }
+    for (element = 0; element < 97; element++)
+    {
+        gr_font->offset[element] = (element * font.cwidth);
+    }
+
+    ftex->version = sizeof(*ftex);
+    ftex->width = width;
+    ftex->height = height;
+    ftex->stride = width;
+    ftex->data = (void*) bits;
+    ftex->format = GGL_PIXEL_FORMAT_A_8;
+    gr_font->type = FONT_TYPE_TWRP;
+    gr_font->cheight = height;
+    gr_font->ascent = height - 2;
+    return;
+}
+
+int gr_init(void)
+{
+    gglInit(&gr_context);
+    GGLContext *gl = gr_context;
+
+    gr_init_font();
+    gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC);
+    if (gr_vt_fd < 0) {
+        // This is non-fatal; post-Cupcake kernels don't have tty0.
+    } else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) {
+        // However, if we do open tty0, we expect the ioctl to work.
+        perror("failed KDSETMODE to KD_GRAPHICS on tty0");
+        gr_exit();
+        return -1;
+    }
+
+    gr_fb_fd = get_framebuffer(gr_framebuffer);
+    if (gr_fb_fd < 0) {
+        perror("Unable to get framebuffer.\n");
+        gr_exit();
+        return -1;
+    }
+
+    get_memory_surface(&gr_mem_surface);
+
+    fprintf(stderr, "framebuffer: fd %d (%d x %d)\n",
+            gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height);
+
+    /* start with 0 as front (displayed) and 1 as back (drawing) */
+    gr_active_fb = 0;
+    if (!has_overlay)
+        set_active_framebuffer(0);
+    gl->colorBuffer(gl, &gr_mem_surface);
+
+    gl->activeTexture(gl, 0);
+    gl->enable(gl, GGL_BLEND);
+    gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
+
+#ifdef TW_SCREEN_BLANK_ON_BOOT
+    printf("TW_SCREEN_BLANK_ON_BOOT := true\n");
+    gr_fb_blank(true);
+    gr_fb_blank(false);
+#endif
+
+    if (!alloc_ion_mem(fi.line_length * vi.yres))
+        allocate_overlay(gr_fb_fd, gr_framebuffer);
+
+    return 0;
+}
+
+void gr_exit(void)
+{
+    free_overlay(gr_fb_fd);
+    free_ion_mem();
+
+    close(gr_fb_fd);
+    gr_fb_fd = -1;
+
+    free(gr_mem_surface.data);
+
+    ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT);
+    close(gr_vt_fd);
+    gr_vt_fd = -1;
+}
+
+int gr_fb_width(void)
+{
+    return gr_framebuffer[0].width;
+}
+
+int gr_fb_height(void)
+{
+    return gr_framebuffer[0].height;
+}
+
+gr_pixel *gr_fb_data(void)
+{
+    return (unsigned short *) gr_mem_surface.data;
+}
+
+int gr_fb_blank(int blank)
+{
+    int ret;
+    //if (blank)
+        //free_overlay(gr_fb_fd);
+
+    ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
+    if (ret < 0)
+        perror("ioctl(): blank");
+
+    //if (!blank)
+        //allocate_overlay(gr_fb_fd, gr_framebuffer);
+    return ret;
+}
+
+int gr_get_surface(gr_surface* surface)
+{
+    GGLSurface* ms = malloc(sizeof(GGLSurface));
+    if (!ms)    return -1;
+
+    // Allocate the data
+    get_memory_surface(ms);
+
+    // Now, copy the data
+    memcpy(ms->data, gr_mem_surface.data, vi.xres * vi.yres * vi.bits_per_pixel / 8);
+
+    *surface = (gr_surface*) ms;
+    return 0;
+}
+
+int gr_free_surface(gr_surface surface)
+{
+    if (!surface)
+        return -1;
+
+    GGLSurface* ms = (GGLSurface*) surface;
+    free(ms->data);
+    free(ms);
+    return 0;
+}
+
+void gr_write_frame_to_file(int fd)
+{
+    write(fd, gr_mem_surface.data, vi.xres * vi.yres * vi.bits_per_pixel / 8);
+}
diff --git a/minuitwrp/graphics_overlay.c b/minuitwrp/graphics_overlay.c
new file mode 100644
index 0000000..05c5472
--- /dev/null
+++ b/minuitwrp/graphics_overlay.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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 <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <linux/fb.h>
+#include <linux/kd.h>
+
+#ifdef MSM_BSP
+#include <linux/msm_mdp.h>
+#include <linux/msm_ion.h>
+#endif
+
+#include <pixelflinger/pixelflinger.h>
+
+#include "minui.h"
+
+#define MDP_V4_0 400
+
+#ifdef MSM_BSP
+#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1))
+
+typedef struct {
+    unsigned char *mem_buf;
+    int size;
+    int ion_fd;
+    int mem_fd;
+    struct ion_handle_data handle_data;
+} memInfo;
+
+//Left and right overlay id
+static int overlayL_id = MSMFB_NEW_REQUEST;
+static int overlayR_id = MSMFB_NEW_REQUEST;
+
+static memInfo mem_info;
+
+static int map_mdp_pixel_format()
+{
+    int format = MDP_RGB_565;
+#if defined(RECOVERY_BGRA)
+    format = MDP_BGRA_8888;
+#elif defined(RECOVERY_RGBX)
+    format = MDP_RGBA_8888;
+#endif
+    return format;
+}
+
+static bool overlay_supported = false;
+static bool isMDP5 = false;
+
+bool target_has_overlay(char *version)
+{
+    int ret;
+    int mdp_version;
+
+    if (strlen(version) >= 8) {
+        if(!strncmp(version, "msmfb", strlen("msmfb"))) {
+            char str_ver[4];
+            memcpy(str_ver, version + strlen("msmfb"), 3);
+            str_ver[3] = '\0';
+            mdp_version = atoi(str_ver);
+            if (mdp_version >= MDP_V4_0) {
+                overlay_supported = true;
+            }
+        } else if (!strncmp(version, "mdssfb", strlen("mdssfb"))) {
+            overlay_supported = true;
+            isMDP5 = true;
+        }
+    }
+    if (overlay_supported) printf("Using qcomm overlay\n");
+    return overlay_supported;
+}
+
+bool isTargetMdp5()
+{
+    if (isMDP5)
+        return true;
+
+    return false;
+}
+
+int free_ion_mem(void) {
+    if (!overlay_supported)
+        return -EINVAL;
+
+    int ret = 0;
+
+    if (mem_info.mem_buf)
+        munmap(mem_info.mem_buf, mem_info.size);
+
+    if (mem_info.ion_fd >= 0) {
+        ret = ioctl(mem_info.ion_fd, ION_IOC_FREE, &mem_info.handle_data);
+        if (ret < 0)
+            perror("free_mem failed ");
+    }
+
+    if (mem_info.mem_fd >= 0)
+        close(mem_info.mem_fd);
+    if (mem_info.ion_fd >= 0)
+        close(mem_info.ion_fd);
+
+    memset(&mem_info, 0, sizeof(mem_info));
+    mem_info.mem_fd = -1;
+    mem_info.ion_fd = -1;
+    return 0;
+}
+
+int alloc_ion_mem(unsigned int size)
+{
+    if (!overlay_supported)
+        return -EINVAL;
+    int result;
+    struct ion_fd_data fd_data;
+    struct ion_allocation_data ionAllocData;
+
+    mem_info.ion_fd = open("/dev/ion", O_RDWR|O_DSYNC);
+    if (mem_info.ion_fd < 0) {
+        perror("ERROR: Can't open ion ");
+        return -errno;
+    }
+
+    ionAllocData.flags = 0;
+    ionAllocData.len = size;
+    ionAllocData.align = sysconf(_SC_PAGESIZE);
+    ionAllocData.heap_mask =
+            ION_HEAP(ION_IOMMU_HEAP_ID) |
+            ION_HEAP(ION_SYSTEM_CONTIG_HEAP_ID);
+
+    result = ioctl(mem_info.ion_fd, ION_IOC_ALLOC,  &ionAllocData);
+    if(result){
+        perror("ION_IOC_ALLOC Failed ");
+        close(mem_info.ion_fd);
+        return result;
+    }
+
+    fd_data.handle = ionAllocData.handle;
+    mem_info.handle_data.handle = ionAllocData.handle;
+    result = ioctl(mem_info.ion_fd, ION_IOC_MAP, &fd_data);
+    if (result) {
+        perror("ION_IOC_MAP Failed ");
+        free_ion_mem();
+        return result;
+    }
+    mem_info.mem_buf = (unsigned char *)mmap(NULL, size, PROT_READ |
+                PROT_WRITE, MAP_SHARED, fd_data.fd, 0);
+    mem_info.mem_fd = fd_data.fd;
+
+    if (!mem_info.mem_buf) {
+        perror("ERROR: mem_buf MAP_FAILED ");
+        free_ion_mem();
+        return -ENOMEM;
+    }
+
+    return 0;
+}
+
+int allocate_overlay(int fd, GGLSurface gr_fb[])
+{
+    int ret = 0;
+
+    if (!overlay_supported)
+        return -EINVAL;
+
+    if (!isDisplaySplit()) {
+        // Check if overlay is already allocated
+        if (MSMFB_NEW_REQUEST == overlayL_id) {
+            struct mdp_overlay overlayL;
+
+            memset(&overlayL, 0 , sizeof (struct mdp_overlay));
+
+            /* Fill Overlay Data */
+            overlayL.src.width  = ALIGN(gr_fb[0].width, 32);
+            overlayL.src.height = gr_fb[0].height;
+            overlayL.src.format = map_mdp_pixel_format();
+            overlayL.src_rect.w = gr_fb[0].width;
+            overlayL.src_rect.h = gr_fb[0].height;
+            overlayL.dst_rect.w = gr_fb[0].width;
+            overlayL.dst_rect.h = gr_fb[0].height;
+            overlayL.alpha = 0xFF;
+            overlayL.transp_mask = MDP_TRANSP_NOP;
+            overlayL.id = MSMFB_NEW_REQUEST;
+            ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL);
+            if (ret < 0) {
+                perror("Overlay Set Failed");
+                return ret;
+            }
+            overlayL_id = overlayL.id;
+        }
+    } else {
+        float xres = getFbXres();
+        int lSplit = getLeftSplit();
+        float lSplitRatio = lSplit / xres;
+        float lCropWidth = gr_fb[0].width * lSplitRatio;
+        int lWidth = lSplit;
+        int rWidth = gr_fb[0].width - lSplit;
+        int height = gr_fb[0].height;
+
+        if (MSMFB_NEW_REQUEST == overlayL_id) {
+
+            struct mdp_overlay overlayL;
+
+            memset(&overlayL, 0 , sizeof (struct mdp_overlay));
+
+            /* Fill OverlayL Data */
+            overlayL.src.width  = ALIGN(gr_fb[0].width, 32);
+            overlayL.src.height = gr_fb[0].height;
+            overlayL.src.format = map_mdp_pixel_format();
+            overlayL.src_rect.x = 0;
+            overlayL.src_rect.y = 0;
+            overlayL.src_rect.w = lCropWidth;
+            overlayL.src_rect.h = gr_fb[0].height;
+            overlayL.dst_rect.x = 0;
+            overlayL.dst_rect.y = 0;
+            overlayL.dst_rect.w = lWidth;
+            overlayL.dst_rect.h = height;
+            overlayL.alpha = 0xFF;
+            overlayL.transp_mask = MDP_TRANSP_NOP;
+            overlayL.id = MSMFB_NEW_REQUEST;
+            ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayL);
+            if (ret < 0) {
+                perror("OverlayL Set Failed");
+                return ret;
+            }
+            overlayL_id = overlayL.id;
+        }
+        if (MSMFB_NEW_REQUEST == overlayR_id) {
+            struct mdp_overlay overlayR;
+
+            memset(&overlayR, 0 , sizeof (struct mdp_overlay));
+
+            /* Fill OverlayR Data */
+            overlayR.src.width  = ALIGN(gr_fb[0].width, 32);
+            overlayR.src.height = gr_fb[0].height;
+            overlayR.src.format = map_mdp_pixel_format();
+            overlayR.src_rect.x = lCropWidth;
+            overlayR.src_rect.y = 0;
+            overlayR.src_rect.w = gr_fb[0].width - lCropWidth;
+            overlayR.src_rect.h = gr_fb[0].height;
+            overlayR.dst_rect.x = 0;
+            overlayR.dst_rect.y = 0;
+            overlayR.dst_rect.w = rWidth;
+            overlayR.dst_rect.h = height;
+            overlayR.alpha = 0xFF;
+            overlayR.flags = MDSS_MDP_RIGHT_MIXER;
+            overlayR.transp_mask = MDP_TRANSP_NOP;
+            overlayR.id = MSMFB_NEW_REQUEST;
+            ret = ioctl(fd, MSMFB_OVERLAY_SET, &overlayR);
+            if (ret < 0) {
+                perror("OverlayR Set Failed");
+                return ret;
+            }
+            overlayR_id = overlayR.id;
+        }
+
+    }
+    return 0;
+}
+
+int free_overlay(int fd)
+{
+    if (!overlay_supported)
+        return -EINVAL;
+
+    int ret = 0;
+    struct mdp_display_commit ext_commit;
+
+    if (!isDisplaySplit()) {
+        if (overlayL_id != MSMFB_NEW_REQUEST) {
+            ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayL_id);
+            if (ret) {
+                perror("Overlay Unset Failed");
+                overlayL_id = MSMFB_NEW_REQUEST;
+                return ret;
+            }
+        }
+    } else {
+
+        if (overlayL_id != MSMFB_NEW_REQUEST) {
+            ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayL_id);
+            if (ret) {
+                perror("OverlayL Unset Failed");
+                overlayL_id = MSMFB_NEW_REQUEST;
+                return ret;
+            }
+        }
+
+        if (overlayR_id != MSMFB_NEW_REQUEST) {
+            ret = ioctl(fd, MSMFB_OVERLAY_UNSET, &overlayR_id);
+            if (ret) {
+                perror("OverlayR Unset Failed");
+                overlayR_id = MSMFB_NEW_REQUEST;
+                return ret;
+            }
+        }
+    }
+    memset(&ext_commit, 0, sizeof(struct mdp_display_commit));
+    ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
+    ext_commit.wait_for_finish = 1;
+    ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit);
+    if (ret < 0) {
+        perror("ERROR: Clear MSMFB_DISPLAY_COMMIT failed!");
+        overlayL_id = MSMFB_NEW_REQUEST;
+        overlayR_id = MSMFB_NEW_REQUEST;
+        return ret;
+    }
+    overlayL_id = MSMFB_NEW_REQUEST;
+    overlayR_id = MSMFB_NEW_REQUEST;
+
+    return 0;
+}
+
+int overlay_display_frame(int fd, GGLubyte* data, size_t size)
+{
+    if (!overlay_supported)
+        return -EINVAL;
+
+    int ret = 0;
+    struct msmfb_overlay_data ovdataL, ovdataR;
+    struct mdp_display_commit ext_commit;
+
+    if (!isDisplaySplit()) {
+        if (overlayL_id == MSMFB_NEW_REQUEST) {
+            perror("display_frame failed, no overlay\n");
+            return -EINVAL;
+        }
+
+        memcpy(mem_info.mem_buf, data, size);
+
+        memset(&ovdataL, 0, sizeof(struct msmfb_overlay_data));
+
+        ovdataL.id = overlayL_id;
+        ovdataL.data.flags = 0;
+        ovdataL.data.offset = 0;
+        ovdataL.data.memory_id = mem_info.mem_fd;
+        ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataL);
+        if (ret < 0) {
+            perror("overlay_display_frame failed, overlay play Failed\n");
+            return ret;
+        }
+    } else {
+
+        if (overlayL_id == MSMFB_NEW_REQUEST) {
+            perror("display_frame failed, no overlayL \n");
+            return -EINVAL;
+        }
+
+        memcpy(mem_info.mem_buf, data, size);
+
+        memset(&ovdataL, 0, sizeof(struct msmfb_overlay_data));
+
+        ovdataL.id = overlayL_id;
+        ovdataL.data.flags = 0;
+        ovdataL.data.offset = 0;
+        ovdataL.data.memory_id = mem_info.mem_fd;
+        ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataL);
+        if (ret < 0) {
+            perror("overlay_display_frame failed, overlayL play Failed\n");
+            return ret;
+        }
+
+        if (overlayR_id == MSMFB_NEW_REQUEST) {
+            perror("display_frame failed, no overlayR \n");
+            return -EINVAL;
+        }
+        memset(&ovdataR, 0, sizeof(struct msmfb_overlay_data));
+
+        ovdataR.id = overlayR_id;
+        ovdataR.data.flags = 0;
+        ovdataR.data.offset = 0;
+        ovdataR.data.memory_id = mem_info.mem_fd;
+        ret = ioctl(fd, MSMFB_OVERLAY_PLAY, &ovdataR);
+        if (ret < 0) {
+            perror("overlay_display_frame failed, overlayR play Failed\n");
+            return ret;
+        }
+    }
+    memset(&ext_commit, 0, sizeof(struct mdp_display_commit));
+    ext_commit.flags = MDP_DISPLAY_COMMIT_OVERLAY;
+    ext_commit.wait_for_finish = 1;
+    ret = ioctl(fd, MSMFB_DISPLAY_COMMIT, &ext_commit);
+    if (ret < 0) {
+        perror("overlay_display_frame failed, overlay commit Failed\n!");
+    }
+
+    return ret;
+}
+
+#else
+
+bool target_has_overlay(char *version) {
+    return false;
+}
+
+bool isTargetMdp5() {
+    return false;
+}
+
+int free_ion_mem(void) {
+    return -EINVAL;
+}
+
+int alloc_ion_mem(unsigned int size)
+{
+    return -EINVAL;
+}
+
+int allocate_overlay(int fd, GGLSurface gr_fb[])
+{
+    return -EINVAL;
+}
+
+int free_overlay(int fd)
+{
+    return -EINVAL;
+}
+
+int overlay_display_frame(int fd, GGLubyte* data, size_t size)
+{
+    return -EINVAL;
+}
+
+#endif //#ifdef MSM_BSP
diff --git a/minuitwrp/graphics_utils.c b/minuitwrp/graphics_utils.c
new file mode 100644
index 0000000..96f6449
--- /dev/null
+++ b/minuitwrp/graphics_utils.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <png.h>
+#include <pixelflinger/pixelflinger.h>
+#include <linux/fb.h>
+
+#include "minui.h"
+
+struct fb_var_screeninfo vi;
+GGLSurface gr_mem_surface;
+
+int gr_save_screenshot(const char *dest)
+{
+    uint32_t y, stride_bytes;
+    int res = -1;
+    GGLContext *gl = NULL;
+    GGLSurface surface;
+    uint8_t * volatile img_data = NULL;
+    uint8_t *ptr;
+    FILE *fp = NULL;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+
+    fp = fopen(dest, "wb");
+    if(!fp)
+        goto exit;
+
+    img_data = malloc(vi.xres * vi.yres * 3);
+    surface.version = sizeof(surface);
+    surface.width = gr_mem_surface.width;
+    surface.height = gr_mem_surface.height;
+    surface.stride = gr_mem_surface.width;
+    surface.data = img_data;
+    surface.format = GGL_PIXEL_FORMAT_RGB_888;
+
+    gglInit(&gl);
+    gl->colorBuffer(gl, &surface);
+    gl->activeTexture(gl, 0);
+
+    gl->bindTexture(gl, &gr_mem_surface);
+    gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
+    gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->enable(gl, GGL_TEXTURE_2D);
+    gl->texCoord2i(gl, 0, 0);
+    gl->recti(gl, 0, 0, gr_mem_surface.width, gr_mem_surface.height);
+
+    gglUninit(gl);
+    gl = NULL;
+
+    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!png_ptr)
+        goto exit;
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (info_ptr == NULL)
+        goto exit;
+
+    if (setjmp(png_jmpbuf(png_ptr)))
+        goto exit;
+
+    png_init_io(png_ptr, fp);
+    png_set_IHDR(png_ptr, info_ptr, surface.width, surface.height,
+         8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+         PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+    png_write_info(png_ptr, info_ptr);
+
+    ptr = img_data;
+    stride_bytes = surface.width*3;
+    for(y = 0; y < surface.height; ++y)
+    {
+        png_write_row(png_ptr, ptr);
+        ptr += stride_bytes;
+    }
+
+    png_write_end(png_ptr, NULL);
+
+    res = 0;
+exit:
+    if(info_ptr)
+        png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
+    if(png_ptr)
+        png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+    if(gl)
+        gglUninit(gl);
+    if(img_data)
+        free(img_data);
+    if(fp)
+        fclose(fp);
+    return res;
+}
diff --git a/minuitwrp/include/linux/msm_ion.h b/minuitwrp/include/linux/msm_ion.h
new file mode 100644
index 0000000..121a5a7
--- /dev/null
+++ b/minuitwrp/include/linux/msm_ion.h
@@ -0,0 +1,157 @@
+#ifndef _LINUX_MSM_ION_H
+#define _LINUX_MSM_ION_H
+
+#include <linux/ion.h>
+
+/*enum msm_ion_heap_types {
+	ION_HEAP_TYPE_MSM_START = ION_HEAP_TYPE_CUSTOM + 1,
+	ION_HEAP_TYPE_IOMMU = ION_HEAP_TYPE_MSM_START,
+	ION_HEAP_TYPE_DMA,
+	ION_HEAP_TYPE_CP,
+	ION_HEAP_TYPE_SECURE_DMA,
+	ION_HEAP_TYPE_REMOVED,
+};*/
+
+/**
+ * These are the only ids that should be used for Ion heap ids.
+ * The ids listed are the order in which allocation will be attempted
+ * if specified. Don't swap the order of heap ids unless you know what
+ * you are doing!
+ * Id's are spaced by purpose to allow new Id's to be inserted in-between (for
+ * possible fallbacks)
+ */
+
+enum ion_heap_ids {
+	INVALID_HEAP_ID = -1,
+	ION_CP_MM_HEAP_ID = 8,
+	ION_CP_MFC_HEAP_ID = 12,
+	ION_CP_WB_HEAP_ID = 16, /* 8660 only */
+	ION_CAMERA_HEAP_ID = 20, /* 8660 only */
+	ION_SYSTEM_CONTIG_HEAP_ID = 21,
+	ION_ADSP_HEAP_ID = 22,
+	ION_PIL1_HEAP_ID = 23, /* Currently used for other PIL images */
+	ION_SF_HEAP_ID = 24,
+	ION_IOMMU_HEAP_ID = 25,
+	ION_PIL2_HEAP_ID = 26, /* Currently used for modem firmware images */
+	ION_QSECOM_HEAP_ID = 27,
+	ION_AUDIO_HEAP_ID = 28,
+
+	ION_MM_FIRMWARE_HEAP_ID = 29,
+	ION_SYSTEM_HEAP_ID = 30,
+
+	ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_FLAG_SECURE flag */
+};
+
+/*enum ion_fixed_position {
+	NOT_FIXED,
+	FIXED_LOW,
+	FIXED_MIDDLE,
+	FIXED_HIGH,
+};*/
+
+/*enum cp_mem_usage {
+	VIDEO_BITSTREAM = 0x1,
+	VIDEO_PIXEL = 0x2,
+	VIDEO_NONPIXEL = 0x3,
+	MAX_USAGE = 0x4,
+	UNKNOWN = 0x7FFFFFFF,
+};*/
+
+#define ION_HEAP_CP_MASK		(1 << ION_HEAP_TYPE_CP)
+#define ION_HEAP_TYPE_DMA_MASK         (1 << ION_HEAP_TYPE_DMA)
+
+/**
+ * Flag to use when allocating to indicate that a heap is secure.
+ */
+#define ION_FLAG_SECURE (1 << ION_HEAP_ID_RESERVED)
+
+/**
+ * Flag for clients to force contiguous memort allocation
+ *
+ * Use of this flag is carefully monitored!
+ */
+#define ION_FLAG_FORCE_CONTIGUOUS (1 << 30)
+
+/*
+ * Used in conjunction with heap which pool memory to force an allocation
+ * to come from the page allocator directly instead of from the pool allocation
+ */
+#define ION_FLAG_POOL_FORCE_ALLOC (1 << 16)
+
+/**
+* Deprecated! Please use the corresponding ION_FLAG_*
+*/
+#define ION_SECURE ION_FLAG_SECURE
+#define ION_FORCE_CONTIGUOUS ION_FLAG_FORCE_CONTIGUOUS
+
+/**
+ * Macro should be used with ion_heap_ids defined above.
+ */
+#define ION_HEAP(bit) (1 << (bit))
+
+#define ION_ADSP_HEAP_NAME	"adsp"
+#define ION_VMALLOC_HEAP_NAME	"vmalloc"
+#define ION_KMALLOC_HEAP_NAME	"kmalloc"
+#define ION_AUDIO_HEAP_NAME	"audio"
+#define ION_SF_HEAP_NAME	"sf"
+#define ION_MM_HEAP_NAME	"mm"
+#define ION_CAMERA_HEAP_NAME	"camera_preview"
+#define ION_IOMMU_HEAP_NAME	"iommu"
+#define ION_MFC_HEAP_NAME	"mfc"
+#define ION_WB_HEAP_NAME	"wb"
+#define ION_MM_FIRMWARE_HEAP_NAME	"mm_fw"
+#define ION_PIL1_HEAP_NAME  "pil_1"
+#define ION_PIL2_HEAP_NAME  "pil_2"
+#define ION_QSECOM_HEAP_NAME	"qsecom"
+
+#define ION_SET_CACHED(__cache)		(__cache | ION_FLAG_CACHED)
+#define ION_SET_UNCACHED(__cache)	(__cache & ~ION_FLAG_CACHED)
+
+#define ION_IS_CACHED(__flags)	((__flags) & ION_FLAG_CACHED)
+
+
+/* struct ion_flush_data - data passed to ion for flushing caches
+ *
+ * @handle:	handle with data to flush
+ * @fd:		fd to flush
+ * @vaddr:	userspace virtual address mapped with mmap
+ * @offset:	offset into the handle to flush
+ * @length:	length of handle to flush
+ *
+ * Performs cache operations on the handle. If p is the start address
+ * of the handle, p + offset through p + offset + length will have
+ * the cache operations performed
+ */
+struct ion_flush_data {
+	struct ion_handle *handle;
+	int fd;
+	void *vaddr;
+	unsigned int offset;
+	unsigned int length;
+};
+
+#define ION_IOC_MSM_MAGIC 'M'
+
+/**
+ * DOC: ION_IOC_CLEAN_CACHES - clean the caches
+ *
+ * Clean the caches of the handle specified.
+ */
+#define ION_IOC_CLEAN_CACHES	_IOWR(ION_IOC_MSM_MAGIC, 0, \
+						struct ion_flush_data)
+/**
+ * DOC: ION_IOC_INV_CACHES - invalidate the caches
+ *
+ * Invalidate the caches of the handle specified.
+ */
+#define ION_IOC_INV_CACHES	_IOWR(ION_IOC_MSM_MAGIC, 1, \
+						struct ion_flush_data)
+/**
+ * DOC: ION_IOC_CLEAN_INV_CACHES - clean and invalidate the caches
+ *
+ * Clean and invalidate the caches of the handle specified.
+ */
+#define ION_IOC_CLEAN_INV_CACHES	_IOWR(ION_IOC_MSM_MAGIC, 2, \
+						struct ion_flush_data)
+
+#endif
diff --git a/minuitwrp/include/linux/msm_mdp.h b/minuitwrp/include/linux/msm_mdp.h
new file mode 100644
index 0000000..abdcd29
--- /dev/null
+++ b/minuitwrp/include/linux/msm_mdp.h
@@ -0,0 +1,992 @@
+/* include/linux/msm_mdp.h
+ *
+ * Copyright (C) 2007 Google Incorporated
+ * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_MDP_H_
+#define _MSM_MDP_H_
+
+#include <linux/types.h>
+#include <linux/fb.h>
+
+#define MSMFB_IOCTL_MAGIC 'm'
+#define MSMFB_GRP_DISP          _IOW(MSMFB_IOCTL_MAGIC, 1, unsigned int)
+#define MSMFB_BLIT              _IOW(MSMFB_IOCTL_MAGIC, 2, unsigned int)
+#define MSMFB_SUSPEND_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 128, unsigned int)
+#define MSMFB_RESUME_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 129, unsigned int)
+#define MSMFB_CURSOR _IOW(MSMFB_IOCTL_MAGIC, 130, struct fb_cursor)
+#define MSMFB_SET_LUT _IOW(MSMFB_IOCTL_MAGIC, 131, struct fb_cmap)
+#define MSMFB_HISTOGRAM _IOWR(MSMFB_IOCTL_MAGIC, 132, struct mdp_histogram_data)
+/* new ioctls's for set/get ccs matrix */
+#define MSMFB_GET_CCS_MATRIX  _IOWR(MSMFB_IOCTL_MAGIC, 133, struct mdp_ccs)
+#define MSMFB_SET_CCS_MATRIX  _IOW(MSMFB_IOCTL_MAGIC, 134, struct mdp_ccs)
+#define MSMFB_OVERLAY_SET       _IOWR(MSMFB_IOCTL_MAGIC, 135, \
+						struct mdp_overlay)
+#define MSMFB_OVERLAY_UNSET     _IOW(MSMFB_IOCTL_MAGIC, 136, unsigned int)
+
+#define MSMFB_OVERLAY_PLAY      _IOW(MSMFB_IOCTL_MAGIC, 137, \
+						struct msmfb_overlay_data)
+#define MSMFB_OVERLAY_QUEUE	MSMFB_OVERLAY_PLAY
+
+#define MSMFB_GET_PAGE_PROTECTION _IOR(MSMFB_IOCTL_MAGIC, 138, \
+					struct mdp_page_protection)
+#define MSMFB_SET_PAGE_PROTECTION _IOW(MSMFB_IOCTL_MAGIC, 139, \
+					struct mdp_page_protection)
+#define MSMFB_OVERLAY_GET      _IOR(MSMFB_IOCTL_MAGIC, 140, \
+						struct mdp_overlay)
+#define MSMFB_OVERLAY_PLAY_ENABLE     _IOW(MSMFB_IOCTL_MAGIC, 141, unsigned int)
+#define MSMFB_OVERLAY_BLT       _IOWR(MSMFB_IOCTL_MAGIC, 142, \
+						struct msmfb_overlay_blt)
+#define MSMFB_OVERLAY_BLT_OFFSET     _IOW(MSMFB_IOCTL_MAGIC, 143, unsigned int)
+#define MSMFB_HISTOGRAM_START	_IOR(MSMFB_IOCTL_MAGIC, 144, \
+						struct mdp_histogram_start_req)
+#define MSMFB_HISTOGRAM_STOP	_IOR(MSMFB_IOCTL_MAGIC, 145, unsigned int)
+#define MSMFB_NOTIFY_UPDATE	_IOWR(MSMFB_IOCTL_MAGIC, 146, unsigned int)
+
+#define MSMFB_OVERLAY_3D       _IOWR(MSMFB_IOCTL_MAGIC, 147, \
+						struct msmfb_overlay_3d)
+
+#define MSMFB_MIXER_INFO       _IOWR(MSMFB_IOCTL_MAGIC, 148, \
+						struct msmfb_mixer_info_req)
+#define MSMFB_OVERLAY_PLAY_WAIT _IOWR(MSMFB_IOCTL_MAGIC, 149, \
+						struct msmfb_overlay_data)
+#define MSMFB_WRITEBACK_INIT _IO(MSMFB_IOCTL_MAGIC, 150)
+#define MSMFB_WRITEBACK_START _IO(MSMFB_IOCTL_MAGIC, 151)
+#define MSMFB_WRITEBACK_STOP _IO(MSMFB_IOCTL_MAGIC, 152)
+#define MSMFB_WRITEBACK_QUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 153, \
+						struct msmfb_data)
+#define MSMFB_WRITEBACK_DEQUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 154, \
+						struct msmfb_data)
+#define MSMFB_WRITEBACK_TERMINATE _IO(MSMFB_IOCTL_MAGIC, 155)
+#define MSMFB_MDP_PP _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp)
+#define MSMFB_OVERLAY_VSYNC_CTRL _IOW(MSMFB_IOCTL_MAGIC, 160, unsigned int)
+#define MSMFB_VSYNC_CTRL  _IOW(MSMFB_IOCTL_MAGIC, 161, unsigned int)
+#define MSMFB_BUFFER_SYNC  _IOW(MSMFB_IOCTL_MAGIC, 162, struct mdp_buf_sync)
+#define MSMFB_OVERLAY_COMMIT      _IO(MSMFB_IOCTL_MAGIC, 163)
+#define MSMFB_DISPLAY_COMMIT      _IOW(MSMFB_IOCTL_MAGIC, 164, \
+						struct mdp_display_commit)
+#define MSMFB_METADATA_SET  _IOW(MSMFB_IOCTL_MAGIC, 165, struct msmfb_metadata)
+#define MSMFB_METADATA_GET  _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata)
+#define MSMFB_WRITEBACK_SET_MIRRORING_HINT _IOW(MSMFB_IOCTL_MAGIC, 167, \
+						unsigned int)
+#define MSMFB_ASYNC_BLIT              _IOW(MSMFB_IOCTL_MAGIC, 168, unsigned int)
+
+#define FB_TYPE_3D_PANEL 0x10101010
+#define MDP_IMGTYPE2_START 0x10000
+#define MSMFB_DRIVER_VERSION	0xF9E8D701
+
+enum {
+	NOTIFY_UPDATE_START,
+	NOTIFY_UPDATE_STOP,
+	NOTIFY_UPDATE_POWER_OFF,
+};
+
+enum {
+	NOTIFY_TYPE_NO_UPDATE,
+	NOTIFY_TYPE_SUSPEND,
+	NOTIFY_TYPE_UPDATE,
+};
+
+enum {
+	MDP_RGB_565,      /* RGB 565 planer */
+	MDP_XRGB_8888,    /* RGB 888 padded */
+	MDP_Y_CBCR_H2V2,  /* Y and CbCr, pseudo planer w/ Cb is in MSB */
+	MDP_Y_CBCR_H2V2_ADRENO,
+	MDP_ARGB_8888,    /* ARGB 888 */
+	MDP_RGB_888,      /* RGB 888 planer */
+	MDP_Y_CRCB_H2V2,  /* Y and CrCb, pseudo planer w/ Cr is in MSB */
+	MDP_YCRYCB_H2V1,  /* YCrYCb interleave */
+	MDP_CBYCRY_H2V1,  /* CbYCrY interleave */
+	MDP_Y_CRCB_H2V1,  /* Y and CrCb, pseduo planer w/ Cr is in MSB */
+	MDP_Y_CBCR_H2V1,   /* Y and CrCb, pseduo planer w/ Cr is in MSB */
+	MDP_Y_CRCB_H1V2,
+	MDP_Y_CBCR_H1V2,
+	MDP_RGBA_8888,    /* ARGB 888 */
+	MDP_BGRA_8888,	  /* ABGR 888 */
+	MDP_RGBX_8888,	  /* RGBX 888 */
+	MDP_Y_CRCB_H2V2_TILE,  /* Y and CrCb, pseudo planer tile */
+	MDP_Y_CBCR_H2V2_TILE,  /* Y and CbCr, pseudo planer tile */
+	MDP_Y_CR_CB_H2V2,  /* Y, Cr and Cb, planar */
+	MDP_Y_CR_CB_GH2V2,  /* Y, Cr and Cb, planar aligned to Android YV12 */
+	MDP_Y_CB_CR_H2V2,  /* Y, Cb and Cr, planar */
+	MDP_Y_CRCB_H1V1,  /* Y and CrCb, pseduo planer w/ Cr is in MSB */
+	MDP_Y_CBCR_H1V1,  /* Y and CbCr, pseduo planer w/ Cb is in MSB */
+	MDP_YCRCB_H1V1,   /* YCrCb interleave */
+	MDP_YCBCR_H1V1,   /* YCbCr interleave */
+	MDP_BGR_565,      /* BGR 565 planer */
+	MDP_BGR_888,      /* BGR 888 */
+	MDP_Y_CBCR_H2V2_VENUS,
+	MDP_BGRX_8888,   /* BGRX 8888 */
+	MDP_RGBA_8888_TILE,	/* RGBA 8888 in tile format */
+	MDP_ARGB_8888_TILE,	/* ARGB 8888 in tile format */
+	MDP_ABGR_8888_TILE,	/* ABGR 8888 in tile format */
+	MDP_BGRA_8888_TILE,	/* BGRA 8888 in tile format */
+	MDP_RGBX_8888_TILE,	/* RGBX 8888 in tile format */
+	MDP_XRGB_8888_TILE,	/* XRGB 8888 in tile format */
+	MDP_XBGR_8888_TILE,	/* XBGR 8888 in tile format */
+	MDP_BGRX_8888_TILE,	/* BGRX 8888 in tile format */
+	MDP_YCBYCR_H2V1,  /* YCbYCr interleave */
+	MDP_IMGTYPE_LIMIT,
+	MDP_RGB_BORDERFILL,	/* border fill pipe */
+	MDP_FB_FORMAT = MDP_IMGTYPE2_START,    /* framebuffer format */
+	MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */
+};
+
+enum {
+	PMEM_IMG,
+	FB_IMG,
+};
+
+enum {
+	HSIC_HUE = 0,
+	HSIC_SAT,
+	HSIC_INT,
+	HSIC_CON,
+	NUM_HSIC_PARAM,
+};
+
+#define MDSS_MDP_ROT_ONLY		0x80
+#define MDSS_MDP_RIGHT_MIXER		0x100
+#define MDSS_MDP_DUAL_PIPE		0x200
+
+/* mdp_blit_req flag values */
+#define MDP_ROT_NOP 0
+#define MDP_FLIP_LR 0x1
+#define MDP_FLIP_UD 0x2
+#define MDP_ROT_90 0x4
+#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR)
+#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR)
+#define MDP_DITHER 0x8
+#define MDP_BLUR 0x10
+#define MDP_BLEND_FG_PREMULT 0x20000
+#define MDP_IS_FG 0x40000
+#define MDP_SOLID_FILL 0x0000100
+#define MDP_DEINTERLACE 0x80000000
+#define MDP_SHARPENING  0x40000000
+#define MDP_NO_DMA_BARRIER_START	0x20000000
+#define MDP_NO_DMA_BARRIER_END		0x10000000
+#define MDP_NO_BLIT			0x08000000
+#define MDP_BLIT_WITH_DMA_BARRIERS	0x000
+#define MDP_BLIT_WITH_NO_DMA_BARRIERS    \
+	(MDP_NO_DMA_BARRIER_START | MDP_NO_DMA_BARRIER_END)
+#define MDP_BLIT_SRC_GEM                0x04000000
+#define MDP_BLIT_DST_GEM                0x02000000
+#define MDP_BLIT_NON_CACHED		0x01000000
+#define MDP_OV_PIPE_SHARE		0x00800000
+#define MDP_DEINTERLACE_ODD		0x00400000
+#define MDP_OV_PLAY_NOWAIT		0x00200000
+#define MDP_SOURCE_ROTATED_90		0x00100000
+#define MDP_OVERLAY_PP_CFG_EN		0x00080000
+#define MDP_BACKEND_COMPOSITION		0x00040000
+#define MDP_BORDERFILL_SUPPORTED	0x00010000
+#define MDP_SECURE_OVERLAY_SESSION      0x00008000
+#define MDP_SECURE_DISPLAY_OVERLAY_SESSION	0x00002000
+#define MDP_OV_PIPE_FORCE_DMA		0x00004000
+#define MDP_MEMORY_ID_TYPE_FB		0x00001000
+#define MDP_BWC_EN			0x00000400
+#define MDP_DECIMATION_EN		0x00000800
+#define MDP_TRANSP_NOP 0xffffffff
+#define MDP_ALPHA_NOP 0xff
+
+#define MDP_FB_PAGE_PROTECTION_NONCACHED         (0)
+#define MDP_FB_PAGE_PROTECTION_WRITECOMBINE      (1)
+#define MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE (2)
+#define MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE    (3)
+#define MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE  (4)
+/* Sentinel: Don't use! */
+#define MDP_FB_PAGE_PROTECTION_INVALID           (5)
+/* Count of the number of MDP_FB_PAGE_PROTECTION_... values. */
+#define MDP_NUM_FB_PAGE_PROTECTION_VALUES        (5)
+
+struct mdp_rect {
+	uint32_t x;
+	uint32_t y;
+	uint32_t w;
+	uint32_t h;
+};
+
+struct mdp_img {
+	uint32_t width;
+	uint32_t height;
+	uint32_t format;
+	uint32_t offset;
+	int memory_id;		/* the file descriptor */
+	uint32_t priv;
+};
+
+/*
+ * {3x3} + {3} ccs matrix
+ */
+
+#define MDP_CCS_RGB2YUV 	0
+#define MDP_CCS_YUV2RGB 	1
+
+#define MDP_CCS_SIZE	9
+#define MDP_BV_SIZE	3
+
+struct mdp_ccs {
+	int direction;			/* MDP_CCS_RGB2YUV or YUV2RGB */
+	uint16_t ccs[MDP_CCS_SIZE];	/* 3x3 color coefficients */
+	uint16_t bv[MDP_BV_SIZE];	/* 1x3 bias vector */
+};
+
+struct mdp_csc {
+	int id;
+	uint32_t csc_mv[9];
+	uint32_t csc_pre_bv[3];
+	uint32_t csc_post_bv[3];
+	uint32_t csc_pre_lv[6];
+	uint32_t csc_post_lv[6];
+};
+
+/* The version of the mdp_blit_req structure so that
+ * user applications can selectively decide which functionality
+ * to include
+ */
+
+#define MDP_BLIT_REQ_VERSION 2
+
+struct color {
+	uint32_t r;
+	uint32_t g;
+	uint32_t b;
+	uint32_t alpha;
+};
+
+struct mdp_blit_req {
+	struct mdp_img src;
+	struct mdp_img dst;
+	struct mdp_rect src_rect;
+	struct mdp_rect dst_rect;
+	struct color const_color;
+	uint32_t alpha;
+	uint32_t transp_mask;
+	uint32_t flags;
+	int sharpening_strength;  /* -127 <--> 127, default 64 */
+};
+
+struct mdp_blit_req_list {
+	uint32_t count;
+	struct mdp_blit_req req[];
+};
+
+#define MSMFB_DATA_VERSION 2
+
+struct msmfb_data {
+	uint32_t offset;
+	int memory_id;
+	int id;
+	uint32_t flags;
+	uint32_t priv;
+	uint32_t iova;
+};
+
+#define MSMFB_NEW_REQUEST -1
+
+struct msmfb_overlay_data {
+	uint32_t id;
+	struct msmfb_data data;
+	uint32_t version_key;
+	struct msmfb_data plane1_data;
+	struct msmfb_data plane2_data;
+	struct msmfb_data dst_data;
+};
+
+struct msmfb_img {
+	uint32_t width;
+	uint32_t height;
+	uint32_t format;
+};
+
+#define MSMFB_WRITEBACK_DEQUEUE_BLOCKING 0x1
+struct msmfb_writeback_data {
+	struct msmfb_data buf_info;
+	struct msmfb_img img;
+};
+
+#define MDP_PP_OPS_ENABLE 0x1
+#define MDP_PP_OPS_READ 0x2
+#define MDP_PP_OPS_WRITE 0x4
+#define MDP_PP_OPS_DISABLE 0x8
+#define MDP_PP_IGC_FLAG_ROM0	0x10
+#define MDP_PP_IGC_FLAG_ROM1	0x20
+
+#define MDSS_PP_DSPP_CFG	0x000
+#define MDSS_PP_SSPP_CFG	0x100
+#define MDSS_PP_LM_CFG	0x200
+#define MDSS_PP_WB_CFG	0x300
+
+#define MDSS_PP_ARG_MASK	0x3C00
+#define MDSS_PP_ARG_NUM		4
+#define MDSS_PP_ARG_SHIFT	10
+#define MDSS_PP_LOCATION_MASK	0x0300
+#define MDSS_PP_LOGICAL_MASK	0x00FF
+
+#define MDSS_PP_ADD_ARG(var, arg) ((var) | (0x1 << (MDSS_PP_ARG_SHIFT + (arg))))
+#define PP_ARG(x, var) ((var) & (0x1 << (MDSS_PP_ARG_SHIFT + (x))))
+#define PP_LOCAT(var) ((var) & MDSS_PP_LOCATION_MASK)
+#define PP_BLOCK(var) ((var) & MDSS_PP_LOGICAL_MASK)
+
+
+struct mdp_qseed_cfg {
+	uint32_t table_num;
+	uint32_t ops;
+	uint32_t len;
+	uint32_t *data;
+};
+
+struct mdp_sharp_cfg {
+	uint32_t flags;
+	uint32_t strength;
+	uint32_t edge_thr;
+	uint32_t smooth_thr;
+	uint32_t noise_thr;
+};
+
+struct mdp_qseed_cfg_data {
+	uint32_t block;
+	struct mdp_qseed_cfg qseed_data;
+};
+
+#define MDP_OVERLAY_PP_CSC_CFG         0x1
+#define MDP_OVERLAY_PP_QSEED_CFG       0x2
+#define MDP_OVERLAY_PP_PA_CFG          0x4
+#define MDP_OVERLAY_PP_IGC_CFG         0x8
+#define MDP_OVERLAY_PP_SHARP_CFG       0x10
+#define MDP_OVERLAY_PP_HIST_CFG        0x20
+#define MDP_OVERLAY_PP_HIST_LUT_CFG    0x40
+
+#define MDP_CSC_FLAG_ENABLE	0x1
+#define MDP_CSC_FLAG_YUV_IN	0x2
+#define MDP_CSC_FLAG_YUV_OUT	0x4
+
+struct mdp_csc_cfg {
+	/* flags for enable CSC, toggling RGB,YUV input/output */
+	uint32_t flags;
+	uint32_t csc_mv[9];
+	uint32_t csc_pre_bv[3];
+	uint32_t csc_post_bv[3];
+	uint32_t csc_pre_lv[6];
+	uint32_t csc_post_lv[6];
+};
+
+struct mdp_csc_cfg_data {
+	uint32_t block;
+	struct mdp_csc_cfg csc_data;
+};
+
+struct mdp_pa_cfg {
+	uint32_t flags;
+	uint32_t hue_adj;
+	uint32_t sat_adj;
+	uint32_t val_adj;
+	uint32_t cont_adj;
+};
+
+struct mdp_igc_lut_data {
+	uint32_t block;
+	uint32_t len, ops;
+	uint32_t *c0_c1_data;
+	uint32_t *c2_data;
+};
+
+struct mdp_histogram_cfg {
+	uint32_t ops;
+	uint32_t block;
+	uint8_t frame_cnt;
+	uint8_t bit_mask;
+	uint16_t num_bins;
+};
+
+struct mdp_hist_lut_data {
+	uint32_t block;
+	uint32_t ops;
+	uint32_t len;
+	uint32_t *data;
+};
+
+struct mdp_overlay_pp_params {
+	uint32_t config_ops;
+	struct mdp_csc_cfg csc_cfg;
+	struct mdp_qseed_cfg qseed_cfg[2];
+	struct mdp_pa_cfg pa_cfg;
+	struct mdp_igc_lut_data igc_cfg;
+	struct mdp_sharp_cfg sharp_cfg;
+	struct mdp_histogram_cfg hist_cfg;
+	struct mdp_hist_lut_data hist_lut_cfg;
+};
+
+/**
+ * enum mdss_mdp_blend_op - Different blend operations set by userspace
+ *
+ * @BLEND_OP_NOT_DEFINED:    No blend operation defined for the layer.
+ * @BLEND_OP_OPAQUE:         Apply a constant blend operation. The layer
+ *                           would appear opaque in case fg plane alpha is
+ *                           0xff.
+ * @BLEND_OP_PREMULTIPLIED:  Apply source over blend rule. Layer already has
+ *                           alpha pre-multiplication done. If fg plane alpha
+ *                           is less than 0xff, apply modulation as well. This
+ *                           operation is intended on layers having alpha
+ *                           channel.
+ * @BLEND_OP_COVERAGE:       Apply source over blend rule. Layer is not alpha
+ *                           pre-multiplied. Apply pre-multiplication. If fg
+ *                           plane alpha is less than 0xff, apply modulation as
+ *                           well.
+ * @BLEND_OP_MAX:            Used to track maximum blend operation possible by
+ *                           mdp.
+ */
+enum mdss_mdp_blend_op {
+	BLEND_OP_NOT_DEFINED = 0,
+	BLEND_OP_OPAQUE,
+	BLEND_OP_PREMULTIPLIED,
+	BLEND_OP_COVERAGE,
+	BLEND_OP_MAX,
+};
+
+#define MAX_PLANES	4
+struct mdp_scale_data {
+	uint8_t enable_pxl_ext;
+
+	int init_phase_x[MAX_PLANES];
+	int phase_step_x[MAX_PLANES];
+	int init_phase_y[MAX_PLANES];
+	int phase_step_y[MAX_PLANES];
+
+	int num_ext_pxls_left[MAX_PLANES];
+	int num_ext_pxls_right[MAX_PLANES];
+	int num_ext_pxls_top[MAX_PLANES];
+	int num_ext_pxls_btm[MAX_PLANES];
+
+	int left_ftch[MAX_PLANES];
+	int left_rpt[MAX_PLANES];
+	int right_ftch[MAX_PLANES];
+	int right_rpt[MAX_PLANES];
+
+	int top_rpt[MAX_PLANES];
+	int btm_rpt[MAX_PLANES];
+	int top_ftch[MAX_PLANES];
+	int btm_ftch[MAX_PLANES];
+
+	uint32_t roi_w[MAX_PLANES];
+};
+
+/**
+ * struct mdp_overlay - overlay surface structure
+ * @src:	Source image information (width, height, format).
+ * @src_rect:	Source crop rectangle, portion of image that will be fetched.
+ *		This should always be within boundaries of source image.
+ * @dst_rect:	Destination rectangle, the position and size of image on screen.
+ *		This should always be within panel boundaries.
+ * @z_order:	Blending stage to occupy in display, if multiple layers are
+ *		present, highest z_order usually means the top most visible
+ *		layer. The range acceptable is from 0-3 to support blending
+ *		up to 4 layers.
+ * @is_fg:	This flag is used to disable blending of any layers with z_order
+ *		less than this overlay. It means that any layers with z_order
+ *		less than this layer will not be blended and will be replaced
+ *		by the background border color.
+ * @alpha:	Used to set plane opacity. The range can be from 0-255, where
+ *		0 means completely transparent and 255 means fully opaque.
+ * @transp_mask: Color used as color key for transparency. Any pixel in fetched
+ *		image matching this color will be transparent when blending.
+ *		The color should be in same format as the source image format.
+ * @flags:	This is used to customize operation of overlay. See MDP flags
+ *		for more information.
+ * @user_data:	DEPRECATED* Used to store user application specific information.
+ * @bg_color:	Solid color used to fill the overlay surface when no source
+ *		buffer is provided.
+ * @horz_deci:	Horizontal decimation value, this indicates the amount of pixels
+ *		dropped for each pixel that is fetched from a line. The value
+ *		given should be power of two of decimation amount.
+ *		0: no decimation
+ *		1: decimate by 2 (drop 1 pixel for each pixel fetched)
+ *		2: decimate by 4 (drop 3 pixels for each pixel fetched)
+ *		3: decimate by 8 (drop 7 pixels for each pixel fetched)
+ *		4: decimate by 16 (drop 15 pixels for each pixel fetched)
+ * @vert_deci:	Vertical decimation value, this indicates the amount of lines
+ *		dropped for each line that is fetched from overlay. The value
+ *		given should be power of two of decimation amount.
+ *		0: no decimation
+ *		1: decimation by 2 (drop 1 line for each line fetched)
+ *		2: decimation by 4 (drop 3 lines for each line fetched)
+ *		3: decimation by 8 (drop 7 lines for each line fetched)
+ *		4: decimation by 16 (drop 15 lines for each line fetched)
+ * @overlay_pp_cfg: Overlay post processing configuration, for more information
+ *		see struct mdp_overlay_pp_params.
+ */
+struct mdp_overlay {
+	struct msmfb_img src;
+	struct mdp_rect src_rect;
+	struct mdp_rect dst_rect;
+	uint32_t z_order;	/* stage number */
+	uint32_t is_fg;		/* control alpha & transp */
+	uint32_t alpha;
+	uint32_t blend_op;
+	uint32_t transp_mask;
+	uint32_t flags;
+	uint32_t id;
+	uint32_t user_data[6];
+	uint32_t bg_color;
+	uint8_t horz_deci;
+	uint8_t vert_deci;
+	struct mdp_overlay_pp_params overlay_pp_cfg;
+	struct mdp_scale_data scale;
+};
+
+struct msmfb_overlay_3d {
+	uint32_t is_3d;
+	uint32_t width;
+	uint32_t height;
+};
+
+
+struct msmfb_overlay_blt {
+	uint32_t enable;
+	uint32_t offset;
+	uint32_t width;
+	uint32_t height;
+	uint32_t bpp;
+};
+
+struct mdp_histogram {
+	uint32_t frame_cnt;
+	uint32_t bin_cnt;
+	uint32_t *r;
+	uint32_t *g;
+	uint32_t *b;
+};
+
+enum {
+	DISPLAY_MISR_EDP,
+	DISPLAY_MISR_DSI0,
+	DISPLAY_MISR_DSI1,
+	DISPLAY_MISR_HDMI,
+	DISPLAY_MISR_LCDC,
+	DISPLAY_MISR_ATV,
+	DISPLAY_MISR_DSI_CMD,
+	DISPLAY_MISR_MAX
+};
+
+enum {
+	MISR_OP_NONE,
+	MISR_OP_SFM,
+	MISR_OP_MFM,
+	MISR_OP_BM,
+	MISR_OP_MAX
+};
+
+struct mdp_misr {
+	uint32_t block_id;
+	uint32_t frame_count;
+	uint32_t crc_op_mode;
+	uint32_t crc_value[32];
+};
+
+/*
+
+	mdp_block_type defines the identifiers for pipes in MDP 4.3 and up
+
+	MDP_BLOCK_RESERVED is provided for backward compatibility and is
+	deprecated. It corresponds to DMA_P. So MDP_BLOCK_DMA_P should be used
+	instead.
+
+	MDP_LOGICAL_BLOCK_DISP_0 identifies the display pipe which fb0 uses,
+	same for others.
+
+*/
+
+enum {
+	MDP_BLOCK_RESERVED = 0,
+	MDP_BLOCK_OVERLAY_0,
+	MDP_BLOCK_OVERLAY_1,
+	MDP_BLOCK_VG_1,
+	MDP_BLOCK_VG_2,
+	MDP_BLOCK_RGB_1,
+	MDP_BLOCK_RGB_2,
+	MDP_BLOCK_DMA_P,
+	MDP_BLOCK_DMA_S,
+	MDP_BLOCK_DMA_E,
+	MDP_BLOCK_OVERLAY_2,
+	MDP_LOGICAL_BLOCK_DISP_0 = 0x10,
+	MDP_LOGICAL_BLOCK_DISP_1,
+	MDP_LOGICAL_BLOCK_DISP_2,
+	MDP_BLOCK_MAX,
+};
+
+/*
+ * mdp_histogram_start_req is used to provide the parameters for
+ * histogram start request
+ */
+
+struct mdp_histogram_start_req {
+	uint32_t block;
+	uint8_t frame_cnt;
+	uint8_t bit_mask;
+	uint16_t num_bins;
+};
+
+/*
+ * mdp_histogram_data is used to return the histogram data, once
+ * the histogram is done/stopped/cance
+ */
+
+struct mdp_histogram_data {
+	uint32_t block;
+	uint32_t bin_cnt;
+	uint32_t *c0;
+	uint32_t *c1;
+	uint32_t *c2;
+	uint32_t *extra_info;
+};
+
+struct mdp_pcc_coeff {
+	uint32_t c, r, g, b, rr, gg, bb, rg, gb, rb, rgb_0, rgb_1;
+};
+
+struct mdp_pcc_cfg_data {
+	uint32_t block;
+	uint32_t ops;
+	struct mdp_pcc_coeff r, g, b;
+};
+
+#define MDP_GAMUT_TABLE_NUM		8
+
+enum {
+	mdp_lut_igc,
+	mdp_lut_pgc,
+	mdp_lut_hist,
+	mdp_lut_max,
+};
+
+struct mdp_ar_gc_lut_data {
+	uint32_t x_start;
+	uint32_t slope;
+	uint32_t offset;
+};
+
+struct mdp_pgc_lut_data {
+	uint32_t block;
+	uint32_t flags;
+	uint8_t num_r_stages;
+	uint8_t num_g_stages;
+	uint8_t num_b_stages;
+	struct mdp_ar_gc_lut_data *r_data;
+	struct mdp_ar_gc_lut_data *g_data;
+	struct mdp_ar_gc_lut_data *b_data;
+};
+
+
+struct mdp_lut_cfg_data {
+	uint32_t lut_type;
+	union {
+		struct mdp_igc_lut_data igc_lut_data;
+		struct mdp_pgc_lut_data pgc_lut_data;
+		struct mdp_hist_lut_data hist_lut_data;
+	} data;
+};
+
+struct mdp_bl_scale_data {
+	uint32_t min_lvl;
+	uint32_t scale;
+};
+
+struct mdp_pa_cfg_data {
+	uint32_t block;
+	struct mdp_pa_cfg pa_data;
+};
+
+struct mdp_dither_cfg_data {
+	uint32_t block;
+	uint32_t flags;
+	uint32_t g_y_depth;
+	uint32_t r_cr_depth;
+	uint32_t b_cb_depth;
+};
+
+struct mdp_gamut_cfg_data {
+	uint32_t block;
+	uint32_t flags;
+	uint32_t gamut_first;
+	uint32_t tbl_size[MDP_GAMUT_TABLE_NUM];
+	uint16_t *r_tbl[MDP_GAMUT_TABLE_NUM];
+	uint16_t *g_tbl[MDP_GAMUT_TABLE_NUM];
+	uint16_t *b_tbl[MDP_GAMUT_TABLE_NUM];
+};
+
+struct mdp_calib_config_data {
+	uint32_t ops;
+	uint32_t addr;
+	uint32_t data;
+};
+
+struct mdp_calib_config_buffer {
+	uint32_t ops;
+	uint32_t size;
+	uint32_t *buffer;
+};
+
+struct mdp_calib_dcm_state {
+	uint32_t ops;
+	uint32_t dcm_state;
+};
+
+enum {
+	DCM_UNINIT,
+	DCM_UNBLANK,
+	DCM_ENTER,
+	DCM_EXIT,
+	DCM_BLANK,
+};
+
+#define MDSS_MAX_BL_BRIGHTNESS 255
+#define AD_BL_LIN_LEN (MDSS_MAX_BL_BRIGHTNESS + 1)
+
+#define MDSS_AD_MODE_AUTO_BL	0x0
+#define MDSS_AD_MODE_AUTO_STR	0x1
+#define MDSS_AD_MODE_TARG_STR	0x3
+#define MDSS_AD_MODE_MAN_STR	0x7
+#define MDSS_AD_MODE_CALIB	0xF
+
+#define MDP_PP_AD_INIT	0x10
+#define MDP_PP_AD_CFG	0x20
+
+struct mdss_ad_init {
+	uint32_t asym_lut[33];
+	uint32_t color_corr_lut[33];
+	uint8_t i_control[2];
+	uint16_t black_lvl;
+	uint16_t white_lvl;
+	uint8_t var;
+	uint8_t limit_ampl;
+	uint8_t i_dither;
+	uint8_t slope_max;
+	uint8_t slope_min;
+	uint8_t dither_ctl;
+	uint8_t format;
+	uint8_t auto_size;
+	uint16_t frame_w;
+	uint16_t frame_h;
+	uint8_t logo_v;
+	uint8_t logo_h;
+	uint32_t bl_lin_len;
+	uint32_t *bl_lin;
+	uint32_t *bl_lin_inv;
+};
+
+#define MDSS_AD_BL_CTRL_MODE_EN 1
+#define MDSS_AD_BL_CTRL_MODE_DIS 0
+struct mdss_ad_cfg {
+	uint32_t mode;
+	uint32_t al_calib_lut[33];
+	uint16_t backlight_min;
+	uint16_t backlight_max;
+	uint16_t backlight_scale;
+	uint16_t amb_light_min;
+	uint16_t filter[2];
+	uint16_t calib[4];
+	uint8_t strength_limit;
+	uint8_t t_filter_recursion;
+	uint16_t stab_itr;
+	uint32_t bl_ctrl_mode;
+};
+
+/* ops uses standard MDP_PP_* flags */
+struct mdss_ad_init_cfg {
+	uint32_t ops;
+	union {
+		struct mdss_ad_init init;
+		struct mdss_ad_cfg cfg;
+	} params;
+};
+
+/* mode uses MDSS_AD_MODE_* flags */
+struct mdss_ad_input {
+	uint32_t mode;
+	union {
+		uint32_t amb_light;
+		uint32_t strength;
+		uint32_t calib_bl;
+	} in;
+	uint32_t output;
+};
+
+#define MDSS_CALIB_MODE_BL	0x1
+struct mdss_calib_cfg {
+	uint32_t ops;
+	uint32_t calib_mask;
+};
+
+enum {
+	mdp_op_pcc_cfg,
+	mdp_op_csc_cfg,
+	mdp_op_lut_cfg,
+	mdp_op_qseed_cfg,
+	mdp_bl_scale_cfg,
+	mdp_op_pa_cfg,
+	mdp_op_dither_cfg,
+	mdp_op_gamut_cfg,
+	mdp_op_calib_cfg,
+	mdp_op_ad_cfg,
+	mdp_op_ad_input,
+	mdp_op_calib_mode,
+	mdp_op_calib_buffer,
+	mdp_op_calib_dcm_state,
+	mdp_op_max,
+};
+
+enum {
+	WB_FORMAT_NV12,
+	WB_FORMAT_RGB_565,
+	WB_FORMAT_RGB_888,
+	WB_FORMAT_xRGB_8888,
+	WB_FORMAT_ARGB_8888,
+	WB_FORMAT_BGRA_8888,
+	WB_FORMAT_BGRX_8888,
+	WB_FORMAT_ARGB_8888_INPUT_ALPHA /* Need to support */
+};
+
+struct msmfb_mdp_pp {
+	uint32_t op;
+	union {
+		struct mdp_pcc_cfg_data pcc_cfg_data;
+		struct mdp_csc_cfg_data csc_cfg_data;
+		struct mdp_lut_cfg_data lut_cfg_data;
+		struct mdp_qseed_cfg_data qseed_cfg_data;
+		struct mdp_bl_scale_data bl_scale_data;
+		struct mdp_pa_cfg_data pa_cfg_data;
+		struct mdp_dither_cfg_data dither_cfg_data;
+		struct mdp_gamut_cfg_data gamut_cfg_data;
+		struct mdp_calib_config_data calib_cfg;
+		struct mdss_ad_init_cfg ad_init_cfg;
+		struct mdss_calib_cfg mdss_calib_cfg;
+		struct mdss_ad_input ad_input;
+		struct mdp_calib_config_buffer calib_buffer;
+		struct mdp_calib_dcm_state calib_dcm;
+	} data;
+};
+
+#define FB_METADATA_VIDEO_INFO_CODE_SUPPORT 1
+enum {
+	metadata_op_none,
+	metadata_op_base_blend,
+	metadata_op_frame_rate,
+	metadata_op_vic,
+	metadata_op_wb_format,
+	metadata_op_get_caps,
+	metadata_op_crc,
+	metadata_op_max
+};
+
+struct mdp_blend_cfg {
+	uint32_t is_premultiplied;
+};
+
+struct mdp_mixer_cfg {
+	uint32_t writeback_format;
+	uint32_t alpha;
+};
+
+struct mdss_hw_caps {
+	uint32_t mdp_rev;
+	uint8_t rgb_pipes;
+	uint8_t vig_pipes;
+	uint8_t dma_pipes;
+	uint32_t features;
+};
+
+struct msmfb_metadata {
+	uint32_t op;
+	uint32_t flags;
+	union {
+		struct mdp_misr misr_request;
+		struct mdp_blend_cfg blend_cfg;
+		struct mdp_mixer_cfg mixer_cfg;
+		uint32_t panel_frame_rate;
+		uint32_t video_info_code;
+		struct mdss_hw_caps caps;
+	} data;
+};
+
+#define MDP_MAX_FENCE_FD	32
+#define MDP_BUF_SYNC_FLAG_WAIT	1
+
+struct mdp_buf_sync {
+	uint32_t flags;
+	uint32_t acq_fen_fd_cnt;
+	uint32_t session_id;
+	int *acq_fen_fd;
+	int *rel_fen_fd;
+};
+
+struct mdp_async_blit_req_list {
+	struct mdp_buf_sync sync;
+	uint32_t count;
+	struct mdp_blit_req req[];
+};
+
+#define MDP_DISPLAY_COMMIT_OVERLAY	1
+struct mdp_buf_fence {
+	uint32_t flags;
+	uint32_t acq_fen_fd_cnt;
+	int acq_fen_fd[MDP_MAX_FENCE_FD];
+	int rel_fen_fd[MDP_MAX_FENCE_FD];
+};
+
+
+struct mdp_display_commit {
+	uint32_t flags;
+	uint32_t wait_for_finish;
+	struct fb_var_screeninfo var;
+	struct mdp_buf_fence buf_fence;
+	struct mdp_rect roi;
+};
+
+struct mdp_page_protection {
+	uint32_t page_protection;
+};
+
+
+struct mdp_mixer_info {
+	int pndx;
+	int pnum;
+	int ptype;
+	int mixer_num;
+	int z_order;
+};
+
+#define MAX_PIPE_PER_MIXER  4
+
+struct msmfb_mixer_info_req {
+	int mixer_num;
+	int cnt;
+	struct mdp_mixer_info info[MAX_PIPE_PER_MIXER];
+};
+
+enum {
+	DISPLAY_SUBSYSTEM_ID,
+	ROTATOR_SUBSYSTEM_ID,
+};
+
+enum {
+	MDP_IOMMU_DOMAIN_CP,
+	MDP_IOMMU_DOMAIN_NS,
+};
+
+enum {
+	MDP_WRITEBACK_MIRROR_OFF,
+	MDP_WRITEBACK_MIRROR_ON,
+	MDP_WRITEBACK_MIRROR_PAUSE,
+	MDP_WRITEBACK_MIRROR_RESUME,
+};
+
+
+#endif /*_MSM_MDP_H_*/
diff --git a/minuitwrp/minui.h b/minuitwrp/minui.h
new file mode 100644
index 0000000..cb9f8a3
--- /dev/null
+++ b/minuitwrp/minui.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MINUI_H_
+#define _MINUI_H_
+
+typedef void* gr_surface;
+typedef unsigned short gr_pixel;
+
+#define FONT_TYPE_TWRP 0
+
+#ifndef TW_DISABLE_TTF
+#define FONT_TYPE_TTF  1
+#endif
+
+int gr_init(void);
+void gr_exit(void);
+
+int gr_fb_width(void);
+int gr_fb_height(void);
+gr_pixel *gr_fb_data(void);
+void gr_flip(void);
+int gr_fb_blank(int blank);
+
+void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
+void gr_fill(int x, int y, int w, int h);
+
+int gr_textEx(int x, int y, const char *s, void* font);
+int gr_textExW(int x, int y, const char *s, void* font, int max_width);
+int gr_textExWH(int x, int y, const char *s, void* pFont, int max_width, int max_height);
+static inline int gr_text(int x, int y, const char *s)     { return gr_textEx(x, y, s, NULL); }
+int gr_measureEx(const char *s, void* font);
+static inline int gr_measure(const char *s)                { return gr_measureEx(s, NULL); }
+int gr_maxExW(const char *s, void* font, int max_width);
+
+int gr_getMaxFontHeight(void *font);
+
+void* gr_loadFont(const char* fontName);
+void gr_freeFont(void *font);
+
+#ifndef TW_DISABLE_TTF
+void *gr_ttf_loadFont(const char *filename, int size, int dpi);
+void gr_ttf_freeFont(void *font);
+int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int max_width, int max_height);
+int gr_ttf_measureEx(const char *s, void *font);
+int gr_ttf_maxExW(const char *s, void *font, int max_width);
+int gr_ttf_getMaxFontHeight(void *font);
+void gr_ttf_dump_stats(void);
+#endif
+
+void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);
+unsigned int gr_get_width(gr_surface surface);
+unsigned int gr_get_height(gr_surface surface);
+int gr_get_surface(gr_surface* surface);
+int gr_free_surface(gr_surface surface);
+
+int gr_save_screenshot(const char *dest);
+
+// input event structure, include <linux/input.h> for the definition.
+// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
+struct input_event;
+
+int ev_init(void);
+void ev_exit(void);
+int ev_get(struct input_event *ev, unsigned dont_wait);
+int ev_has_mouse(void);
+
+// Resources
+
+// Returns 0 if no error, else negative.
+int res_create_surface(const char* name, gr_surface* pSurface);
+void res_free_surface(gr_surface surface);
+
+// Needed for AOSP:
+int ev_wait(int timeout);
+void ev_dispatch(void);
+int ev_get_input(int fd, short revents, struct input_event *ev);
+
+int vibrate(int timeout_ms);
+
+#endif
diff --git a/minuitwrp/mkfont.c b/minuitwrp/mkfont.c
new file mode 100644
index 0000000..409316c
--- /dev/null
+++ b/minuitwrp/mkfont.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv)
+{
+    unsigned n;
+    unsigned char *x;
+    unsigned m;
+    unsigned run_val;
+    unsigned run_count;
+
+    n = gimp_image.width * gimp_image.height;
+    m = 0;
+    x = gimp_image.pixel_data;
+
+    printf("struct {\n");
+    printf("  unsigned width;\n");
+    printf("  unsigned height;\n");
+    printf("  unsigned cwidth;\n");
+    printf("  unsigned cheight;\n");
+    printf("  unsigned char rundata[];\n");
+    printf("} font = {\n");
+    printf("  .width = %d,\n  .height = %d,\n  .cwidth = %d,\n  .cheight = %d,\n", gimp_image.width, gimp_image.height,
+           gimp_image.width / 96, gimp_image.height);
+    printf("  .rundata = {\n");
+
+    run_val = (*x ? 0 : 255);
+    run_count = 1;
+    n--;
+    x+=3;
+
+    while(n-- > 0) {
+        unsigned val = (*x ? 0 : 255);
+        x+=3;
+        if((val == run_val) && (run_count < 127)) {
+            run_count++;
+        } else {
+eject:
+            printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00));
+            run_val = val;
+            run_count = 1;
+            m += 5;
+            if(m >= 75) {
+                printf("\n");
+                m = 0;
+            }
+        }
+    }
+    printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00));
+    printf("\n0x00,");
+    printf("\n");
+    printf("  }\n};\n");
+    return 0;
+}
diff --git a/minuitwrp/resources.c b/minuitwrp/resources.c
new file mode 100644
index 0000000..c589c9d
--- /dev/null
+++ b/minuitwrp/resources.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <linux/fb.h>
+#include <linux/kd.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+#include <png.h>
+#include "jpeglib.h"
+
+#include "minui.h"
+
+// libpng gives "undefined reference to 'pow'" errors, and I have no
+// idea how to convince the build system to link with -lm.  We don't
+// need this functionality (it's used for gamma adjustment) so provide
+// a dummy implementation to satisfy the linker.
+double pow(double x, double y) {
+    return x;
+}
+
+int res_create_surface_png(const char* name, gr_surface* pSurface) {
+    GGLSurface* surface = NULL;
+    int result = 0;
+    unsigned char header[8];
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+
+    FILE* fp = fopen(name, "rb");
+    if (fp == NULL) {
+        char resPath[256];
+
+        snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
+        resPath[sizeof(resPath)-1] = '\0';
+        fp = fopen(resPath, "rb");
+        if (fp == NULL)
+        {
+            result = -1;
+            goto exit;
+        }
+    }
+
+    size_t bytesRead = fread(header, 1, sizeof(header), fp);
+    if (bytesRead != sizeof(header)) {
+        result = -2;
+        goto exit;
+    }
+
+    if (png_sig_cmp(header, 0, sizeof(header))) {
+        result = -3;
+        goto exit;
+    }
+
+    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!png_ptr) {
+        result = -4;
+        goto exit;
+    }
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        result = -5;
+        goto exit;
+    }
+
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        result = -6;
+        goto exit;
+    }
+
+    png_set_packing(png_ptr);
+
+    png_init_io(png_ptr, fp);
+    png_set_sig_bytes(png_ptr, sizeof(header));
+    png_read_info(png_ptr, info_ptr);
+
+    size_t width = info_ptr->width;
+    size_t height = info_ptr->height;
+    size_t stride = 4 * width;
+    size_t pixelSize = stride * height;
+
+    int color_type = info_ptr->color_type;
+    int bit_depth = info_ptr->bit_depth;
+    int channels = info_ptr->channels;
+    if (!(bit_depth == 8 &&
+          ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
+           (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) ||
+           (channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE)))) {
+        return -7;
+        goto exit;
+    }
+
+    surface = malloc(sizeof(GGLSurface) + pixelSize);
+    if (surface == NULL) {
+        result = -8;
+        goto exit;
+    }
+    unsigned char* pData = (unsigned char*) (surface + 1);
+    surface->version = sizeof(GGLSurface);
+    surface->width = width;
+    surface->height = height;
+    surface->stride = width; /* Yes, pixels, not bytes */
+    surface->data = pData;
+    surface->format = (channels == 3) ?
+            GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888;
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        png_set_palette_to_rgb(png_ptr);
+    }
+
+    int y;
+    if (channels < 4) {
+        for (y = 0; y < (int) height; ++y) {
+            unsigned char* pRow = pData + y * stride;
+            png_read_row(png_ptr, pRow, NULL);
+
+            int x;
+            for(x = width - 1; x >= 0; x--) {
+                int sx = x * 3;
+                int dx = x * 4;
+                unsigned char r = pRow[sx];
+                unsigned char g = pRow[sx + 1];
+                unsigned char b = pRow[sx + 2];
+                unsigned char a = 0xff;
+                pRow[dx    ] = r; // r
+                pRow[dx + 1] = g; // g
+                pRow[dx + 2] = b; // b
+                pRow[dx + 3] = a;
+            }
+        }
+    } else {
+        for (y = 0; y < (int) height; ++y) {
+            unsigned char* pRow = pData + y * stride;
+            png_read_row(png_ptr, pRow, NULL);
+        }
+    }
+
+    *pSurface = (gr_surface) surface;
+
+exit:
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+    if (fp != NULL) {
+        fclose(fp);
+    }
+    if (result < 0) {
+        if (surface) {
+            free(surface);
+        }
+    }
+    return result;
+}
+
+int res_create_surface_jpg(const char* name, gr_surface* pSurface) {
+    GGLSurface* surface = NULL;
+    int result = 0;
+    struct jpeg_decompress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+
+    FILE* fp = fopen(name, "rb");
+    if (fp == NULL) {
+        char resPath[256];
+
+        snprintf(resPath, sizeof(resPath)-1, "/res/images/%s", name);
+        resPath[sizeof(resPath)-1] = '\0';
+        fp = fopen(resPath, "rb");
+        if (fp == NULL) {
+            result = -1;
+            goto exit;
+        }
+    }
+
+    cinfo.err = jpeg_std_error(&jerr);
+    jpeg_create_decompress(&cinfo);
+
+    /* Specify data source for decompression */
+    jpeg_stdio_src(&cinfo, fp);
+
+    /* Read file header, set default decompression parameters */
+    if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
+        goto exit;
+
+    /* Start decompressor */
+    (void) jpeg_start_decompress(&cinfo);
+
+    size_t width = cinfo.image_width;
+    size_t height = cinfo.image_height;
+    size_t stride = 4 * width;
+    size_t pixelSize = stride * height;
+
+    surface = malloc(sizeof(GGLSurface) + pixelSize);
+    if (surface == NULL) {
+        result = -8;
+        goto exit;
+    }
+
+    unsigned char* pData = (unsigned char*) (surface + 1);
+    surface->version = sizeof(GGLSurface);
+    surface->width = width;
+    surface->height = height;
+    surface->stride = width; /* Yes, pixels, not bytes */
+    surface->data = pData;
+    surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
+
+    int y;
+    for (y = 0; y < (int) height; ++y) {
+        unsigned char* pRow = pData + y * stride;
+        jpeg_read_scanlines(&cinfo, &pRow, 1);
+
+        int x;
+        for(x = width - 1; x >= 0; x--) {
+            int sx = x * 3;
+            int dx = x * 4;
+            unsigned char r = pRow[sx];
+            unsigned char g = pRow[sx + 1];
+            unsigned char b = pRow[sx + 2];
+            unsigned char a = 0xff;
+            pRow[dx    ] = r; // r
+            pRow[dx + 1] = g; // g
+            pRow[dx + 2] = b; // b
+            pRow[dx + 3] = a;
+        }
+    }
+    *pSurface = (gr_surface) surface;
+
+exit:
+    if (fp != NULL)
+    {
+        if (surface)
+        {
+            (void) jpeg_finish_decompress(&cinfo);
+            if (result < 0)
+            {
+                free(surface);
+            }
+        }
+        jpeg_destroy_decompress(&cinfo);
+        fclose(fp);
+    }
+    return result;
+}
+
+int res_create_surface(const char* name, gr_surface* pSurface) {
+    int ret;
+
+    if (!name)      return -1;
+
+    if (strlen(name) > 4 && strcmp(name + strlen(name) - 4, ".jpg") == 0)
+		return res_create_surface_jpg(name,pSurface);
+
+    ret = res_create_surface_png(name,pSurface);
+    if (ret < 0)
+        ret = res_create_surface_jpg(name,pSurface);
+
+    return ret;
+}
+
+void res_free_surface(gr_surface surface) {
+    GGLSurface* pSurface = (GGLSurface*) surface;
+    if (pSurface) {
+        free(pSurface);
+    }
+}
diff --git a/minuitwrp/roboto_10x18.h b/minuitwrp/roboto_10x18.h
new file mode 100644
index 0000000..3119c81
--- /dev/null
+++ b/minuitwrp/roboto_10x18.h
@@ -0,0 +1,197 @@
+struct {
+  unsigned width;
+  unsigned height;
+  unsigned cwidth;
+  unsigned cheight;
+  unsigned char rundata[];
+} font = {
+  .width = 960,
+  .height = 18,
+  .cwidth = 10,
+  .cheight = 18,
+  .rundata = {
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x3b,0x81,0x29,0x81,0x06,0x81,0x3f,0x81,0x7f,0x7f,0x7f,0x37,0x83,0x05,0x81,
+0x0a,0x83,0x7f,0x7f,0x2f,0x81,0x0e,0x81,0x29,0x82,0x07,0x81,0x01,0x82,0x07,
+0x81,0x02,0x81,0x05,0x83,0x05,0x82,0x0a,0x82,0x09,0x82,0x09,0x81,0x08,0x81,
+0x3e,0x81,0x05,0x84,0x06,0x84,0x06,0x83,0x06,0x84,0x09,0x82,0x05,0x85,0x06,
+0x84,0x04,0x87,0x04,0x84,0x07,0x84,0x39,0x83,0x06,0x84,0x07,0x81,0x06,0x86,
+0x06,0x84,0x04,0x86,0x04,0x87,0x04,0x87,0x04,0x84,0x04,0x82,0x04,0x82,0x03,
+0x86,0x08,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x07,0x82,0x05,0x82,0x02,0x82,
+0x05,0x81,0x04,0x84,0x04,0x86,0x06,0x84,0x04,0x86,0x06,0x84,0x04,0x88,0x02,
+0x82,0x05,0x81,0x01,0x82,0x06,0x81,0x01,0x81,0x07,0x81,0x02,0x81,0x05,0x82,
+0x02,0x82,0x04,0x82,0x02,0x87,0x06,0x81,0x07,0x82,0x0b,0x81,0x08,0x81,0x12,
+0x82,0x10,0x82,0x18,0x82,0x10,0x83,0x0d,0x82,0x0b,0x82,0x08,0x82,0x06,0x82,
+0x09,0x83,0x4c,0x82,0x48,0x81,0x07,0x82,0x07,0x81,0x28,0x82,0x07,0x81,0x01,
+0x82,0x07,0x81,0x02,0x81,0x04,0x82,0x01,0x82,0x03,0x81,0x02,0x81,0x08,0x81,
+0x02,0x81,0x08,0x82,0x08,0x82,0x08,0x82,0x3c,0x82,0x04,0x82,0x02,0x82,0x04,
+0x85,0x05,0x82,0x01,0x82,0x04,0x82,0x02,0x82,0x07,0x83,0x05,0x85,0x05,0x82,
+0x02,0x81,0x09,0x82,0x03,0x82,0x02,0x82,0x05,0x82,0x02,0x82,0x37,0x82,0x01,
+0x82,0x04,0x81,0x03,0x82,0x06,0x82,0x05,0x82,0x03,0x82,0x04,0x83,0x01,0x82,
+0x03,0x82,0x03,0x82,0x03,0x82,0x09,0x82,0x08,0x82,0x02,0x82,0x03,0x82,0x04,
+0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x07,0x82,0x05,0x82,
+0x02,0x82,0x05,0x81,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x02,
+0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x05,0x81,
+0x02,0x81,0x05,0x82,0x01,0x81,0x07,0x81,0x02,0x82,0x04,0x81,0x03,0x82,0x04,
+0x82,0x07,0x82,0x06,0x81,0x08,0x81,0x0b,0x81,0x07,0x82,0x13,0x81,0x10,0x82,
+0x18,0x82,0x0f,0x84,0x0d,0x82,0x1d,0x82,0x0a,0x82,0x4c,0x82,0x47,0x82,0x07,
+0x82,0x07,0x82,0x27,0x82,0x07,0x81,0x01,0x82,0x06,0x81,0x02,0x81,0x04,0x82,
+0x03,0x82,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x82,0x02,0x82,0x08,0x81,0x08,
+0x81,0x0a,0x81,0x12,0x82,0x28,0x81,0x05,0x81,0x04,0x81,0x07,0x82,0x04,0x82,
+0x03,0x82,0x03,0x81,0x04,0x81,0x07,0x83,0x05,0x81,0x08,0x82,0x0c,0x82,0x04,
+0x81,0x04,0x82,0x04,0x81,0x04,0x81,0x36,0x82,0x03,0x81,0x03,0x81,0x05,0x82,
+0x04,0x83,0x05,0x82,0x04,0x81,0x03,0x83,0x03,0x82,0x02,0x82,0x04,0x82,0x02,
+0x82,0x09,0x82,0x07,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,
+0x03,0x82,0x02,0x82,0x04,0x82,0x07,0x83,0x03,0x83,0x02,0x83,0x04,0x81,0x02,
+0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x81,
+0x03,0x82,0x04,0x81,0x06,0x82,0x05,0x82,0x05,0x81,0x02,0x81,0x05,0x82,0x01,
+0x82,0x02,0x81,0x02,0x82,0x03,0x82,0x02,0x82,0x04,0x81,0x03,0x82,0x08,0x81,
+0x07,0x81,0x08,0x81,0x0b,0x81,0x07,0x83,0x13,0x81,0x0f,0x82,0x18,0x82,0x0e,
+0x82,0x10,0x82,0x1d,0x82,0x0a,0x82,0x4c,0x82,0x47,0x81,0x08,0x82,0x08,0x81,
+0x27,0x82,0x07,0x81,0x01,0x81,0x05,0x88,0x02,0x82,0x07,0x81,0x02,0x81,0x01,
+0x82,0x05,0x81,0x02,0x81,0x08,0x81,0x08,0x82,0x0a,0x82,0x07,0x81,0x09,0x82,
+0x27,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x0a,0x81,0x08,0x81,0x06,0x81,0x01,
+0x82,0x05,0x81,0x08,0x82,0x0c,0x81,0x05,0x81,0x04,0x82,0x04,0x81,0x04,0x81,
+0x05,0x82,0x08,0x82,0x2a,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x01,
+0x82,0x04,0x82,0x04,0x81,0x03,0x82,0x04,0x82,0x02,0x82,0x05,0x81,0x02,0x82,
+0x09,0x82,0x07,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,
+0x82,0x02,0x81,0x05,0x82,0x07,0x83,0x03,0x83,0x02,0x84,0x03,0x81,0x02,0x82,
+0x05,0x81,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,
+0x82,0x0b,0x82,0x05,0x82,0x05,0x81,0x02,0x82,0x04,0x81,0x03,0x81,0x02,0x82,
+0x01,0x82,0x04,0x81,0x01,0x82,0x05,0x82,0x01,0x82,0x08,0x82,0x07,0x81,0x08,
+0x82,0x0a,0x81,0x06,0x82,0x01,0x81,0x1a,0x85,0x04,0x82,0x01,0x83,0x06,0x84,
+0x06,0x83,0x01,0x82,0x04,0x84,0x06,0x82,0x07,0x85,0x04,0x82,0x01,0x83,0x06,
+0x83,0x08,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x05,0x81,0x01,0x82,0x01,0x82,
+0x03,0x82,0x01,0x84,0x05,0x84,0x04,0x82,0x01,0x83,0x06,0x83,0x01,0x82,0x03,
+0x82,0x01,0x84,0x04,0x84,0x04,0x86,0x04,0x82,0x04,0x82,0x02,0x82,0x04,0x81,
+0x02,0x82,0x06,0x81,0x02,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x03,0x86,0x07,
+0x81,0x08,0x82,0x08,0x81,0x27,0x82,0x11,0x81,0x02,0x81,0x05,0x82,0x06,0x84,
+0x01,0x81,0x06,0x83,0x12,0x82,0x0a,0x82,0x05,0x81,0x01,0x81,0x01,0x81,0x07,
+0x82,0x27,0x81,0x05,0x82,0x04,0x82,0x06,0x82,0x09,0x82,0x07,0x82,0x05,0x82,
+0x01,0x82,0x04,0x85,0x05,0x81,0x01,0x83,0x08,0x82,0x06,0x81,0x02,0x82,0x05,
+0x81,0x04,0x81,0x05,0x82,0x08,0x82,0x0a,0x83,0x04,0x86,0x04,0x82,0x0c,0x82,
+0x02,0x81,0x02,0x81,0x01,0x81,0x02,0x81,0x04,0x81,0x01,0x82,0x04,0x82,0x03,
+0x82,0x03,0x82,0x08,0x82,0x05,0x81,0x02,0x82,0x09,0x82,0x07,0x81,0x09,0x82,
+0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x01,0x82,0x05,0x82,0x07,0x84,0x02,
+0x83,0x02,0x82,0x01,0x81,0x03,0x81,0x02,0x82,0x05,0x81,0x02,0x82,0x04,0x82,
+0x02,0x81,0x06,0x81,0x02,0x82,0x03,0x82,0x04,0x82,0x0a,0x82,0x05,0x82,0x05,
+0x81,0x03,0x81,0x03,0x82,0x03,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x05,0x83,
+0x07,0x84,0x07,0x82,0x08,0x81,0x09,0x81,0x0a,0x81,0x06,0x81,0x02,0x81,0x19,
+0x82,0x02,0x83,0x03,0x83,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x83,
+0x03,0x82,0x02,0x82,0x04,0x85,0x04,0x83,0x01,0x83,0x03,0x83,0x02,0x82,0x06,
+0x82,0x08,0x82,0x06,0x82,0x02,0x81,0x07,0x82,0x05,0x82,0x01,0x82,0x01,0x82,
+0x02,0x83,0x03,0x82,0x03,0x82,0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02,
+0x83,0x03,0x83,0x03,0x81,0x03,0x82,0x02,0x82,0x04,0x82,0x07,0x82,0x04,0x82,
+0x03,0x81,0x03,0x82,0x02,0x82,0x02,0x82,0x02,0x81,0x03,0x82,0x02,0x81,0x05,
+0x81,0x04,0x82,0x07,0x81,0x08,0x81,0x08,0x82,0x08,0x81,0x27,0x82,0x11,0x81,
+0x02,0x81,0x06,0x83,0x08,0x81,0x07,0x82,0x13,0x82,0x0a,0x82,0x05,0x85,0x04,
+0x88,0x23,0x82,0x05,0x82,0x04,0x82,0x06,0x82,0x08,0x82,0x06,0x83,0x06,0x81,
+0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x07,0x81,0x08,0x83,0x06,
+0x82,0x02,0x82,0x19,0x83,0x11,0x83,0x09,0x82,0x03,0x81,0x01,0x81,0x02,0x81,
+0x02,0x81,0x03,0x82,0x02,0x81,0x04,0x86,0x04,0x82,0x08,0x82,0x05,0x81,0x02,
+0x86,0x05,0x86,0x03,0x81,0x09,0x88,0x05,0x82,0x0a,0x82,0x03,0x84,0x06,0x82,
+0x07,0x82,0x01,0x81,0x01,0x81,0x01,0x82,0x02,0x82,0x01,0x82,0x02,0x81,0x02,
+0x82,0x05,0x81,0x02,0x82,0x03,0x82,0x03,0x81,0x06,0x81,0x02,0x86,0x06,0x83,
+0x08,0x82,0x05,0x82,0x05,0x81,0x03,0x82,0x02,0x81,0x04,0x81,0x01,0x81,0x01,
+0x81,0x01,0x81,0x06,0x82,0x08,0x82,0x08,0x81,0x09,0x81,0x09,0x82,0x09,0x81,
+0x06,0x81,0x02,0x82,0x1d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x81,0x03,
+0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,
+0x04,0x82,0x05,0x82,0x08,0x82,0x06,0x82,0x01,0x81,0x08,0x82,0x05,0x82,0x01,
+0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
+0x02,0x82,0x04,0x82,0x03,0x82,0x08,0x81,0x09,0x82,0x07,0x82,0x04,0x82,0x03,
+0x82,0x02,0x81,0x04,0x81,0x02,0x82,0x02,0x81,0x04,0x81,0x01,0x82,0x05,0x82,
+0x02,0x82,0x07,0x82,0x08,0x81,0x08,0x82,0x08,0x81,0x07,0x83,0x03,0x81,0x19,
+0x82,0x11,0x81,0x02,0x81,0x08,0x82,0x07,0x81,0x01,0x82,0x03,0x82,0x01,0x81,
+0x02,0x82,0x0e,0x81,0x0c,0x81,0x06,0x82,0x06,0x88,0x0d,0x86,0x10,0x81,0x06,
+0x82,0x04,0x82,0x06,0x82,0x07,0x82,0x09,0x82,0x04,0x82,0x02,0x82,0x09,0x82,
+0x03,0x81,0x04,0x82,0x05,0x82,0x06,0x82,0x03,0x81,0x06,0x85,0x18,0x82,0x15,
+0x83,0x06,0x82,0x04,0x81,0x01,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x03,0x82,
+0x03,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x05,0x81,0x02,0x82,0x09,0x82,0x07,
+0x81,0x03,0x84,0x02,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x01,0x82,
+0x05,0x82,0x07,0x82,0x01,0x81,0x01,0x81,0x01,0x82,0x02,0x82,0x02,0x82,0x01,
+0x81,0x02,0x82,0x05,0x81,0x02,0x86,0x04,0x81,0x06,0x81,0x02,0x82,0x03,0x82,
+0x07,0x82,0x07,0x82,0x05,0x82,0x05,0x81,0x03,0x82,0x02,0x81,0x04,0x83,0x01,
+0x83,0x05,0x83,0x08,0x82,0x07,0x82,0x09,0x81,0x0a,0x81,0x09,0x81,0x25,0x85,
+0x03,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x88,0x04,0x82,0x06,
+0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x08,0x82,0x06,0x84,0x08,0x82,
+0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,
+0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x08,0x83,0x07,0x82,0x07,0x82,
+0x04,0x82,0x04,0x81,0x02,0x81,0x04,0x81,0x02,0x82,0x02,0x81,0x05,0x82,0x06,
+0x82,0x02,0x82,0x06,0x82,0x07,0x82,0x09,0x82,0x09,0x82,0x04,0x82,0x01,0x82,
+0x01,0x82,0x2a,0x87,0x08,0x82,0x05,0x81,0x01,0x81,0x02,0x81,0x02,0x81,0x02,
+0x82,0x01,0x82,0x0e,0x81,0x0c,0x81,0x05,0x82,0x01,0x82,0x07,0x82,0x26,0x81,
+0x06,0x82,0x04,0x82,0x06,0x82,0x06,0x82,0x0b,0x82,0x03,0x87,0x08,0x82,0x03,
+0x81,0x04,0x82,0x05,0x82,0x06,0x81,0x04,0x82,0x09,0x81,0x18,0x82,0x08,0x86,
+0x08,0x82,0x06,0x82,0x04,0x81,0x01,0x81,0x02,0x81,0x02,0x81,0x03,0x86,0x03,
+0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x05,0x81,0x02,0x82,0x09,0x82,0x07,0x82,
+0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,
+0x82,0x07,0x82,0x01,0x83,0x01,0x82,0x02,0x82,0x03,0x81,0x01,0x81,0x02,0x82,
+0x05,0x81,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x81,0x08,0x82,0x06,
+0x82,0x05,0x82,0x05,0x81,0x04,0x81,0x01,0x82,0x05,0x82,0x02,0x82,0x05,0x81,
+0x01,0x82,0x07,0x82,0x06,0x82,0x0a,0x81,0x0a,0x81,0x09,0x81,0x24,0x82,0x02,
+0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a,0x82,
+0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x08,0x82,0x06,0x84,0x08,
+0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
+0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0b,0x83,0x04,0x82,0x07,
+0x82,0x04,0x82,0x04,0x81,0x01,0x82,0x04,0x81,0x01,0x81,0x01,0x81,0x01,0x82,
+0x05,0x82,0x07,0x81,0x02,0x82,0x06,0x81,0x0a,0x81,0x08,0x82,0x08,0x81,0x06,
+0x81,0x03,0x83,0x2c,0x81,0x02,0x81,0x05,0x81,0x04,0x82,0x04,0x81,0x02,0x81,
+0x02,0x81,0x01,0x82,0x03,0x83,0x0f,0x82,0x0a,0x82,0x11,0x82,0x25,0x82,0x07,
+0x81,0x04,0x81,0x07,0x82,0x05,0x82,0x07,0x81,0x04,0x82,0x03,0x87,0x03,0x81,
+0x04,0x82,0x03,0x81,0x04,0x82,0x05,0x81,0x07,0x81,0x04,0x82,0x08,0x82,0x19,
+0x84,0x10,0x83,0x0e,0x81,0x01,0x81,0x02,0x81,0x01,0x82,0x02,0x82,0x04,0x81,
+0x03,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x09,
+0x82,0x07,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x81,0x04,0x82,
+0x03,0x82,0x03,0x81,0x04,0x82,0x07,0x82,0x02,0x81,0x02,0x82,0x02,0x82,0x03,
+0x83,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
+0x02,0x81,0x05,0x81,0x06,0x82,0x05,0x83,0x03,0x82,0x04,0x83,0x06,0x82,0x02,
+0x82,0x04,0x82,0x02,0x81,0x07,0x82,0x06,0x81,0x0b,0x81,0x0a,0x82,0x08,0x81,
+0x23,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x81,0x03,0x82,0x04,
+0x82,0x02,0x82,0x0a,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,
+0x08,0x82,0x06,0x82,0x01,0x82,0x07,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,
+0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
+0x03,0x82,0x0d,0x81,0x04,0x82,0x07,0x82,0x03,0x83,0x04,0x83,0x06,0x82,0x02,
+0x82,0x05,0x81,0x01,0x82,0x06,0x84,0x06,0x81,0x0b,0x81,0x08,0x82,0x08,0x81,
+0x27,0x82,0x10,0x81,0x02,0x81,0x05,0x82,0x02,0x83,0x04,0x81,0x02,0x81,0x02,
+0x81,0x02,0x81,0x04,0x82,0x0f,0x82,0x0a,0x82,0x11,0x82,0x07,0x82,0x12,0x82,
+0x08,0x81,0x08,0x82,0x02,0x82,0x07,0x82,0x05,0x81,0x08,0x82,0x03,0x81,0x08,
+0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x06,0x81,0x07,0x82,0x02,0x82,
+0x08,0x82,0x06,0x82,0x08,0x82,0x0b,0x82,0x0e,0x82,0x0a,0x82,0x04,0x81,0x01,
+0x83,0x01,0x82,0x03,0x81,0x05,0x82,0x02,0x82,0x04,0x82,0x03,0x83,0x01,0x82,
+0x03,0x82,0x03,0x82,0x03,0x82,0x09,0x82,0x08,0x82,0x03,0x82,0x02,0x82,0x04,
+0x82,0x05,0x82,0x05,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x03,0x82,0x07,0x82,
+0x05,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x09,0x82,0x02,
+0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x06,0x82,0x06,0x82,0x03,0x82,
+0x05,0x82,0x06,0x82,0x02,0x81,0x04,0x82,0x03,0x82,0x06,0x82,0x05,0x82,0x0b,
+0x81,0x0b,0x81,0x08,0x81,0x23,0x82,0x02,0x83,0x03,0x83,0x02,0x82,0x04,0x82,
+0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x03,0x82,0x04,0x82,0x06,0x83,0x01,
+0x83,0x03,0x82,0x04,0x82,0x05,0x82,0x08,0x82,0x06,0x82,0x02,0x82,0x06,0x82,
+0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,
+0x83,0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x08,0x82,0x02,0x82,0x04,0x82,
+0x03,0x81,0x03,0x83,0x01,0x84,0x05,0x82,0x06,0x82,0x02,0x82,0x04,0x82,0x02,
+0x81,0x07,0x83,0x05,0x82,0x0b,0x81,0x08,0x82,0x08,0x81,0x27,0x82,0x10,0x81,
+0x02,0x81,0x06,0x85,0x09,0x82,0x04,0x84,0x01,0x82,0x0f,0x81,0x0a,0x81,0x1b,
+0x82,0x12,0x82,0x08,0x81,0x09,0x84,0x08,0x82,0x04,0x87,0x04,0x84,0x09,0x82,
+0x05,0x84,0x06,0x84,0x07,0x81,0x08,0x84,0x06,0x84,0x07,0x82,0x08,0x82,0x27,
+0x82,0x04,0x82,0x09,0x81,0x06,0x81,0x02,0x87,0x05,0x84,0x04,0x86,0x04,0x87,
+0x04,0x82,0x09,0x85,0x03,0x82,0x04,0x82,0x03,0x86,0x04,0x84,0x05,0x82,0x04,
+0x82,0x02,0x87,0x02,0x82,0x05,0x82,0x02,0x82,0x05,0x81,0x04,0x84,0x04,0x82,
+0x0a,0x86,0x02,0x82,0x04,0x82,0x03,0x85,0x07,0x82,0x07,0x85,0x06,0x82,0x06,
+0x82,0x02,0x81,0x04,0x82,0x04,0x82,0x05,0x82,0x05,0x87,0x06,0x81,0x0b,0x82,
+0x07,0x81,0x23,0x84,0x01,0x82,0x03,0x82,0x01,0x83,0x06,0x84,0x06,0x83,0x01,
+0x82,0x04,0x85,0x05,0x82,0x07,0x83,0x01,0x82,0x03,0x82,0x04,0x82,0x03,0x86,
+0x06,0x82,0x06,0x82,0x03,0x81,0x04,0x86,0x03,0x82,0x01,0x82,0x01,0x82,0x02,
+0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x83,0x06,0x83,0x01,0x82,0x03,0x82,
+0x09,0x84,0x06,0x84,0x05,0x84,0x01,0x82,0x05,0x82,0x06,0x81,0x03,0x82,0x03,
+0x82,0x03,0x82,0x07,0x82,0x05,0x86,0x07,0x81,0x08,0x82,0x08,0x81,0x45,0x81,
+0x27,0x81,0x0a,0x81,0x1c,0x81,0x1b,0x82,0x78,0x81,0x2e,0x82,0x7f,0x30,0x82,
+0x5e,0x81,0x0c,0x81,0x07,0x81,0x0f,0x88,0x4d,0x82,0x1a,0x82,0x37,0x82,0x0e,
+0x82,0x4b,0x82,0x13,0x82,0x07,0x82,0x07,0x82,0x45,0x81,0x28,0x81,0x08,0x81,
+0x1c,0x81,0x1c,0x81,0x78,0x81,0x30,0x84,0x7f,0x7f,0x0e,0x83,0x0a,0x81,0x05,
+0x83,0x64,0x82,0x1a,0x82,0x37,0x82,0x0e,0x82,0x4b,0x82,0x14,0x81,0x07,0x82,
+0x07,0x81,0x70,0x81,0x06,0x81,0x7f,0x7f,0x7f,0x7f,0x6e,0x85,0x1a,0x82,0x38,
+0x82,0x0e,0x82,0x4a,0x82,0x16,0x81,0x0e,0x81,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x62,
+0x00,
+  }
+};
diff --git a/minuitwrp/roboto_15x24.h b/minuitwrp/roboto_15x24.h
new file mode 100644
index 0000000..7271d74
--- /dev/null
+++ b/minuitwrp/roboto_15x24.h
@@ -0,0 +1,281 @@
+struct {
+  unsigned width;
+  unsigned height;
+  unsigned cwidth;
+  unsigned cheight;
+  unsigned char rundata[];
+} font = {
+  .width = 1440,
+  .height = 24,
+  .cwidth = 15,
+  .cheight = 24,
+  .rundata = {
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x6e,0x81,0x3e,0x81,
+0x07,0x81,0x7f,0x7f,0x7f,0x7f,0x7f,0x76,0x84,0x17,0x84,0x7f,0x7f,0x7f,0x7f,
+0x4c,0x81,0x3d,0x82,0x07,0x82,0x5f,0x81,0x7f,0x7f,0x7f,0x7f,0x7f,0x15,0x84,
+0x07,0x82,0x0e,0x84,0x45,0x82,0x24,0x82,0x1a,0x83,0x16,0x82,0x10,0x82,0x0e,
+0x82,0x0a,0x82,0x0d,0x84,0x7f,0x62,0x82,0x0b,0x82,0x0b,0x82,0x3b,0x82,0x0c,
+0x82,0x02,0x82,0x0a,0x82,0x03,0x81,0x0a,0x84,0x08,0x83,0x0e,0x84,0x0b,0x82,
+0x10,0x82,0x09,0x82,0x5e,0x81,0x09,0x84,0x0b,0x84,0x0b,0x84,0x0b,0x84,0x0e,
+0x83,0x08,0x88,0x09,0x84,0x07,0x8a,0x08,0x84,0x0b,0x84,0x56,0x84,0x1c,0x82,
+0x08,0x87,0x0b,0x85,0x07,0x87,0x09,0x89,0x06,0x8a,0x07,0x85,0x06,0x82,0x08,
+0x82,0x06,0x86,0x0f,0x82,0x06,0x82,0x07,0x82,0x04,0x82,0x0a,0x83,0x09,0x82,
+0x03,0x82,0x08,0x82,0x06,0x85,0x07,0x87,0x0b,0x85,0x07,0x87,0x0c,0x84,0x06,
+0x8d,0x03,0x82,0x07,0x82,0x02,0x83,0x08,0x83,0x01,0x82,0x0a,0x82,0x02,0x82,
+0x08,0x82,0x03,0x82,0x08,0x82,0x04,0x8b,0x09,0x82,0x09,0x82,0x10,0x82,0x0d,
+0x81,0x1d,0x82,0x18,0x82,0x24,0x82,0x19,0x84,0x16,0x82,0x10,0x82,0x0e,0x82,
+0x0a,0x82,0x0d,0x84,0x7f,0x61,0x82,0x0c,0x82,0x0c,0x82,0x3a,0x82,0x0c,0x82,
+0x02,0x82,0x0a,0x82,0x03,0x81,0x08,0x87,0x06,0x82,0x01,0x82,0x0c,0x86,0x0a,
+0x82,0x0f,0x82,0x0b,0x82,0x5c,0x82,0x08,0x87,0x07,0x86,0x0a,0x87,0x08,0x87,
+0x0c,0x83,0x08,0x88,0x07,0x87,0x06,0x8a,0x07,0x87,0x07,0x87,0x54,0x87,0x08,
+0x86,0x0c,0x82,0x08,0x89,0x07,0x88,0x06,0x89,0x07,0x89,0x06,0x8a,0x05,0x88,
+0x05,0x82,0x08,0x82,0x06,0x86,0x0f,0x82,0x06,0x82,0x06,0x82,0x05,0x82,0x0a,
+0x83,0x08,0x83,0x03,0x83,0x07,0x82,0x04,0x88,0x06,0x89,0x07,0x88,0x06,0x89,
+0x08,0x88,0x04,0x8d,0x03,0x82,0x07,0x82,0x03,0x82,0x08,0x82,0x02,0x82,0x04,
+0x81,0x05,0x82,0x03,0x82,0x06,0x82,0x04,0x82,0x07,0x83,0x04,0x8b,0x09,0x82,
+0x0a,0x82,0x0f,0x82,0x0c,0x83,0x1d,0x82,0x17,0x82,0x24,0x82,0x18,0x83,0x18,
+0x82,0x2c,0x82,0x0f,0x82,0x7f,0x60,0x83,0x0c,0x82,0x0d,0x82,0x39,0x82,0x0c,
+0x82,0x02,0x82,0x0a,0x82,0x02,0x82,0x07,0x83,0x03,0x83,0x04,0x82,0x03,0x81,
+0x04,0x81,0x06,0x83,0x03,0x82,0x09,0x82,0x0f,0x82,0x0b,0x82,0x5c,0x82,0x07,
+0x82,0x04,0x82,0x0a,0x83,0x09,0x82,0x04,0x83,0x06,0x82,0x04,0x83,0x0a,0x84,
+0x08,0x82,0x0d,0x82,0x13,0x82,0x06,0x82,0x04,0x83,0x06,0x82,0x04,0x82,0x52,
+0x82,0x04,0x82,0x07,0x82,0x05,0x82,0x09,0x84,0x07,0x82,0x05,0x83,0x06,0x82,
+0x05,0x82,0x05,0x82,0x05,0x83,0x06,0x82,0x0d,0x82,0x0d,0x82,0x05,0x82,0x04,
+0x82,0x08,0x82,0x08,0x82,0x11,0x82,0x06,0x82,0x05,0x82,0x06,0x82,0x0a,0x83,
+0x08,0x83,0x03,0x83,0x07,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x06,0x82,0x06,
+0x82,0x05,0x82,0x05,0x82,0x05,0x83,0x06,0x83,0x04,0x83,0x08,0x83,0x08,0x82,
+0x07,0x82,0x03,0x82,0x08,0x82,0x02,0x82,0x03,0x83,0x04,0x82,0x03,0x83,0x04,
+0x83,0x05,0x82,0x06,0x82,0x0d,0x82,0x0a,0x82,0x0a,0x82,0x0f,0x82,0x0c,0x83,
+0x1e,0x81,0x17,0x82,0x24,0x82,0x18,0x82,0x19,0x82,0x2c,0x82,0x0f,0x82,0x74,
+0x82,0x69,0x82,0x0d,0x82,0x0d,0x82,0x39,0x82,0x0c,0x82,0x02,0x82,0x0a,0x82,
+0x02,0x82,0x07,0x82,0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x06,0x82,0x04,
+0x82,0x09,0x82,0x0e,0x82,0x0d,0x82,0x1c,0x82,0x3c,0x82,0x08,0x82,0x05,0x82,
+0x09,0x83,0x09,0x82,0x05,0x82,0x05,0x83,0x05,0x82,0x09,0x85,0x07,0x82,0x0d,
+0x82,0x13,0x82,0x07,0x82,0x05,0x82,0x05,0x82,0x05,0x83,0x51,0x82,0x04,0x82,
+0x06,0x82,0x07,0x82,0x08,0x84,0x07,0x82,0x06,0x82,0x05,0x82,0x07,0x82,0x04,
+0x82,0x06,0x83,0x05,0x82,0x0d,0x82,0x0c,0x82,0x07,0x82,0x03,0x82,0x08,0x82,
+0x08,0x82,0x11,0x82,0x06,0x82,0x04,0x83,0x06,0x82,0x0a,0x84,0x06,0x84,0x03,
+0x84,0x06,0x82,0x03,0x82,0x07,0x82,0x04,0x82,0x07,0x82,0x04,0x82,0x07,0x82,
+0x04,0x82,0x06,0x82,0x06,0x82,0x06,0x82,0x08,0x83,0x08,0x82,0x07,0x82,0x03,
+0x83,0x06,0x83,0x02,0x82,0x03,0x83,0x03,0x82,0x05,0x82,0x04,0x82,0x06,0x82,
+0x05,0x83,0x0c,0x83,0x0a,0x82,0x0b,0x82,0x0e,0x82,0x0c,0x81,0x01,0x82,0x35,
+0x82,0x24,0x82,0x18,0x82,0x19,0x82,0x2c,0x82,0x0f,0x82,0x74,0x82,0x69,0x82,
+0x0d,0x82,0x0d,0x83,0x38,0x82,0x0c,0x82,0x02,0x81,0x0b,0x81,0x03,0x82,0x07,
+0x82,0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x81,0x07,0x82,0x04,0x82,0x09,0x82,
+0x0e,0x82,0x0d,0x82,0x0d,0x82,0x0d,0x82,0x3c,0x82,0x07,0x82,0x06,0x82,0x09,
+0x83,0x08,0x83,0x05,0x82,0x0d,0x82,0x09,0x85,0x07,0x82,0x0d,0x82,0x12,0x83,
+0x07,0x82,0x05,0x82,0x05,0x82,0x06,0x82,0x57,0x83,0x04,0x82,0x09,0x81,0x08,
+0x84,0x07,0x82,0x06,0x82,0x05,0x82,0x07,0x82,0x04,0x82,0x07,0x82,0x05,0x82,
+0x0d,0x82,0x0c,0x82,0x07,0x82,0x03,0x82,0x08,0x82,0x08,0x82,0x11,0x82,0x06,
+0x82,0x03,0x83,0x07,0x82,0x0a,0x84,0x06,0x84,0x03,0x85,0x05,0x82,0x03,0x82,
+0x07,0x82,0x04,0x82,0x07,0x82,0x04,0x82,0x07,0x82,0x04,0x82,0x06,0x83,0x05,
+0x82,0x06,0x82,0x08,0x83,0x08,0x82,0x07,0x82,0x04,0x82,0x06,0x82,0x03,0x82,
+0x03,0x83,0x03,0x82,0x05,0x83,0x02,0x83,0x07,0x82,0x04,0x82,0x0d,0x82,0x0b,
+0x82,0x0b,0x82,0x0e,0x82,0x0b,0x82,0x01,0x82,0x29,0x84,0x08,0x82,0x01,0x84,
+0x0a,0x86,0x0a,0x83,0x02,0x82,0x08,0x84,0x0a,0x86,0x0a,0x83,0x02,0x82,0x06,
+0x82,0x01,0x84,0x09,0x84,0x0e,0x82,0x0a,0x82,0x04,0x83,0x08,0x82,0x07,0x81,
+0x02,0x83,0x03,0x83,0x05,0x81,0x02,0x84,0x0b,0x84,0x09,0x81,0x02,0x84,0x0a,
+0x83,0x02,0x82,0x08,0x81,0x02,0x84,0x08,0x84,0x08,0x88,0x07,0x82,0x06,0x82,
+0x05,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x04,
+0x83,0x05,0x83,0x06,0x89,0x09,0x82,0x0d,0x82,0x0d,0x83,0x38,0x82,0x0c,0x82,
+0x02,0x81,0x08,0x8b,0x05,0x82,0x0b,0x82,0x03,0x81,0x02,0x82,0x07,0x83,0x02,
+0x82,0x0a,0x81,0x0f,0x82,0x0d,0x82,0x0d,0x82,0x0d,0x82,0x3c,0x82,0x07,0x82,
+0x06,0x82,0x09,0x83,0x10,0x82,0x0d,0x82,0x08,0x82,0x01,0x83,0x07,0x82,0x0d,
+0x82,0x12,0x82,0x08,0x82,0x05,0x82,0x05,0x82,0x06,0x82,0x08,0x82,0x0d,0x82,
+0x13,0x81,0x06,0x89,0x06,0x81,0x14,0x82,0x05,0x81,0x04,0x84,0x02,0x82,0x06,
+0x82,0x02,0x82,0x06,0x82,0x06,0x82,0x04,0x82,0x0e,0x82,0x07,0x82,0x05,0x82,
+0x0d,0x82,0x0b,0x82,0x0d,0x82,0x08,0x82,0x08,0x82,0x11,0x82,0x06,0x82,0x03,
+0x82,0x08,0x82,0x0a,0x82,0x01,0x82,0x05,0x84,0x03,0x82,0x01,0x82,0x05,0x82,
+0x02,0x82,0x09,0x82,0x03,0x82,0x07,0x82,0x03,0x82,0x09,0x82,0x03,0x82,0x06,
+0x83,0x05,0x82,0x10,0x83,0x08,0x82,0x07,0x82,0x04,0x82,0x06,0x82,0x03,0x82,
+0x03,0x81,0x01,0x82,0x02,0x82,0x06,0x82,0x02,0x82,0x08,0x82,0x03,0x83,0x0c,
+0x82,0x0c,0x82,0x0b,0x82,0x0e,0x82,0x0b,0x82,0x02,0x81,0x28,0x87,0x06,0x88,
+0x08,0x88,0x08,0x88,0x07,0x86,0x09,0x86,0x09,0x88,0x06,0x88,0x08,0x84,0x0e,
+0x82,0x0a,0x82,0x03,0x83,0x09,0x82,0x07,0x87,0x01,0x85,0x04,0x88,0x09,0x87,
+0x07,0x81,0x01,0x86,0x08,0x88,0x08,0x87,0x06,0x87,0x07,0x88,0x07,0x82,0x06,
+0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x03,0x82,0x05,0x82,0x04,0x83,0x03,0x83,
+0x05,0x82,0x05,0x82,0x07,0x89,0x09,0x82,0x0d,0x82,0x0d,0x83,0x38,0x82,0x1b,
+0x82,0x03,0x81,0x08,0x83,0x0b,0x85,0x01,0x82,0x09,0x85,0x1a,0x83,0x0d,0x83,
+0x09,0x81,0x02,0x82,0x01,0x81,0x0b,0x82,0x3b,0x82,0x08,0x82,0x06,0x82,0x09,
+0x83,0x0f,0x82,0x0d,0x82,0x09,0x82,0x01,0x83,0x07,0x87,0x07,0x83,0x01,0x84,
+0x0c,0x82,0x09,0x83,0x03,0x82,0x06,0x82,0x06,0x82,0x08,0x82,0x0d,0x82,0x11,
+0x83,0x06,0x89,0x06,0x83,0x12,0x82,0x05,0x81,0x03,0x82,0x02,0x82,0x02,0x81,
+0x06,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x05,0x82,0x0e,0x82,0x07,0x83,0x04,
+0x82,0x0d,0x82,0x0b,0x82,0x0d,0x82,0x08,0x82,0x08,0x82,0x11,0x82,0x06,0x82,
+0x02,0x82,0x09,0x82,0x0a,0x82,0x01,0x82,0x04,0x82,0x01,0x82,0x03,0x82,0x02,
+0x82,0x04,0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x07,0x82,0x03,0x82,0x09,0x82,
+0x03,0x82,0x06,0x82,0x06,0x83,0x0f,0x83,0x08,0x82,0x07,0x82,0x05,0x82,0x04,
+0x82,0x05,0x81,0x02,0x82,0x01,0x82,0x02,0x82,0x07,0x84,0x0a,0x82,0x02,0x82,
+0x0c,0x83,0x0c,0x82,0x0c,0x82,0x0d,0x82,0x0a,0x82,0x03,0x82,0x26,0x82,0x04,
+0x82,0x06,0x83,0x03,0x83,0x07,0x82,0x05,0x82,0x06,0x82,0x04,0x83,0x06,0x82,
+0x04,0x82,0x0a,0x82,0x0a,0x83,0x03,0x83,0x06,0x82,0x04,0x83,0x09,0x82,0x0e,
+0x82,0x0a,0x82,0x03,0x82,0x0a,0x82,0x07,0x82,0x03,0x84,0x02,0x82,0x04,0x82,
+0x04,0x83,0x07,0x82,0x04,0x83,0x06,0x82,0x04,0x83,0x06,0x82,0x04,0x83,0x08,
+0x83,0x0a,0x82,0x04,0x82,0x08,0x82,0x0b,0x82,0x06,0x82,0x05,0x82,0x05,0x82,
+0x04,0x82,0x03,0x83,0x03,0x82,0x06,0x82,0x03,0x82,0x06,0x82,0x05,0x82,0x0d,
+0x82,0x0a,0x82,0x0d,0x82,0x0d,0x83,0x38,0x82,0x1b,0x82,0x02,0x82,0x09,0x85,
+0x0e,0x81,0x0b,0x83,0x1b,0x82,0x0f,0x82,0x09,0x87,0x0b,0x82,0x3b,0x82,0x08,
+0x82,0x06,0x82,0x09,0x83,0x0e,0x83,0x09,0x85,0x09,0x82,0x02,0x83,0x07,0x88,
+0x06,0x89,0x0b,0x82,0x0a,0x86,0x07,0x82,0x06,0x82,0x27,0x85,0x17,0x84,0x0f,
+0x82,0x05,0x82,0x03,0x81,0x03,0x82,0x02,0x81,0x06,0x82,0x02,0x82,0x06,0x88,
+0x06,0x82,0x0e,0x82,0x07,0x83,0x04,0x88,0x07,0x88,0x05,0x82,0x0d,0x8c,0x08,
+0x82,0x11,0x82,0x06,0x85,0x0a,0x82,0x0a,0x82,0x01,0x82,0x04,0x82,0x01,0x82,
+0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x06,0x82,0x04,
+0x82,0x09,0x82,0x03,0x82,0x04,0x83,0x08,0x85,0x0c,0x83,0x08,0x82,0x07,0x82,
+0x05,0x82,0x04,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x81,0x08,0x84,0x0a,
+0x86,0x0c,0x82,0x0d,0x82,0x0c,0x82,0x0d,0x82,0x0a,0x82,0x03,0x82,0x26,0x82,
+0x04,0x83,0x05,0x82,0x05,0x82,0x06,0x83,0x05,0x82,0x05,0x83,0x05,0x82,0x05,
+0x83,0x04,0x82,0x0a,0x82,0x0a,0x82,0x05,0x82,0x06,0x82,0x05,0x82,0x09,0x82,
+0x0e,0x82,0x0a,0x82,0x02,0x82,0x0b,0x82,0x07,0x82,0x03,0x83,0x03,0x83,0x03,
+0x82,0x05,0x82,0x06,0x83,0x05,0x82,0x06,0x82,0x05,0x82,0x05,0x83,0x05,0x82,
+0x08,0x82,0x0b,0x82,0x04,0x82,0x08,0x82,0x0b,0x82,0x06,0x82,0x06,0x82,0x04,
+0x82,0x04,0x82,0x02,0x84,0x03,0x82,0x07,0x82,0x01,0x82,0x07,0x83,0x03,0x83,
+0x0c,0x83,0x0a,0x82,0x0d,0x82,0x0e,0x82,0x0a,0x82,0x2c,0x82,0x1b,0x82,0x02,
+0x82,0x0b,0x85,0x0b,0x82,0x09,0x85,0x1b,0x82,0x0f,0x82,0x0b,0x83,0x09,0x8b,
+0x36,0x81,0x09,0x82,0x06,0x82,0x09,0x83,0x0e,0x82,0x0a,0x86,0x07,0x82,0x03,
+0x83,0x07,0x82,0x04,0x83,0x05,0x84,0x03,0x83,0x09,0x82,0x0b,0x87,0x07,0x82,
+0x04,0x83,0x25,0x85,0x1b,0x85,0x0b,0x82,0x06,0x82,0x02,0x82,0x03,0x82,0x02,
+0x81,0x05,0x82,0x04,0x82,0x05,0x8a,0x04,0x82,0x0e,0x82,0x07,0x83,0x04,0x88,
+0x07,0x88,0x05,0x82,0x04,0x86,0x03,0x8c,0x08,0x82,0x11,0x82,0x06,0x86,0x09,
+0x82,0x0a,0x82,0x02,0x82,0x02,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,
+0x02,0x82,0x09,0x82,0x03,0x8a,0x04,0x82,0x09,0x82,0x03,0x89,0x0a,0x85,0x0a,
+0x83,0x08,0x82,0x07,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x01,0x82,0x02,0x81,
+0x01,0x82,0x09,0x82,0x0c,0x84,0x0c,0x82,0x0e,0x82,0x0d,0x82,0x0c,0x82,0x3d,
+0x83,0x05,0x82,0x05,0x82,0x06,0x82,0x0d,0x82,0x06,0x82,0x05,0x82,0x06,0x82,
+0x09,0x82,0x09,0x82,0x06,0x82,0x06,0x82,0x05,0x82,0x09,0x82,0x0e,0x82,0x0a,
+0x82,0x01,0x83,0x0b,0x82,0x07,0x82,0x04,0x82,0x04,0x82,0x03,0x82,0x05,0x82,
+0x06,0x82,0x06,0x82,0x06,0x82,0x05,0x82,0x05,0x82,0x06,0x82,0x08,0x82,0x0b,
+0x82,0x0e,0x82,0x0b,0x82,0x06,0x82,0x06,0x82,0x03,0x82,0x06,0x82,0x01,0x82,
+0x01,0x81,0x03,0x82,0x07,0x82,0x01,0x82,0x08,0x82,0x03,0x82,0x0c,0x83,0x09,
+0x83,0x0e,0x82,0x0e,0x83,0x07,0x86,0x04,0x82,0x24,0x82,0x1b,0x81,0x03,0x82,
+0x0d,0x84,0x0a,0x81,0x02,0x84,0x03,0x83,0x01,0x83,0x03,0x82,0x15,0x82,0x0f,
+0x82,0x0b,0x84,0x08,0x8b,0x15,0x86,0x1a,0x82,0x09,0x82,0x06,0x82,0x09,0x83,
+0x0d,0x82,0x0f,0x83,0x06,0x82,0x03,0x83,0x0e,0x82,0x05,0x83,0x05,0x82,0x09,
+0x82,0x0a,0x82,0x04,0x83,0x06,0x89,0x24,0x83,0x21,0x84,0x08,0x82,0x07,0x81,
+0x03,0x82,0x03,0x81,0x03,0x81,0x05,0x82,0x04,0x82,0x05,0x82,0x06,0x83,0x03,
+0x82,0x0e,0x82,0x07,0x83,0x04,0x82,0x0d,0x82,0x0b,0x82,0x04,0x86,0x03,0x82,
+0x08,0x82,0x08,0x82,0x11,0x82,0x06,0x82,0x02,0x82,0x09,0x82,0x0a,0x82,0x02,
+0x82,0x02,0x82,0x02,0x82,0x03,0x82,0x03,0x83,0x02,0x82,0x02,0x82,0x09,0x82,
+0x03,0x88,0x06,0x82,0x09,0x82,0x03,0x82,0x05,0x83,0x0c,0x84,0x08,0x83,0x08,
+0x82,0x07,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x81,0x03,0x84,0x08,0x84,
+0x0b,0x83,0x0c,0x83,0x0e,0x82,0x0d,0x82,0x0c,0x82,0x39,0x87,0x05,0x82,0x05,
+0x82,0x06,0x82,0x0d,0x82,0x06,0x82,0x05,0x8a,0x09,0x82,0x09,0x82,0x06,0x82,
+0x06,0x82,0x05,0x82,0x09,0x82,0x0e,0x82,0x0a,0x85,0x0c,0x82,0x07,0x82,0x04,
+0x82,0x04,0x82,0x03,0x82,0x05,0x82,0x06,0x82,0x06,0x82,0x06,0x82,0x05,0x82,
+0x05,0x82,0x06,0x82,0x08,0x82,0x0b,0x85,0x0b,0x82,0x0b,0x82,0x06,0x82,0x06,
+0x82,0x03,0x82,0x06,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x08,0x83,0x09,0x82,
+0x03,0x82,0x0c,0x82,0x0a,0x82,0x0f,0x82,0x0f,0x83,0x06,0x82,0x02,0x83,0x02,
+0x82,0x25,0x82,0x18,0x8b,0x0d,0x82,0x09,0x82,0x01,0x82,0x02,0x82,0x02,0x82,
+0x03,0x83,0x02,0x82,0x15,0x82,0x0f,0x82,0x0a,0x82,0x01,0x82,0x0c,0x82,0x3a,
+0x82,0x09,0x82,0x06,0x82,0x09,0x83,0x0c,0x82,0x11,0x82,0x05,0x82,0x04,0x83,
+0x0e,0x82,0x05,0x83,0x05,0x82,0x09,0x82,0x09,0x82,0x06,0x82,0x08,0x83,0x02,
+0x82,0x24,0x83,0x0c,0x89,0x0d,0x83,0x08,0x82,0x07,0x81,0x03,0x82,0x03,0x81,
+0x03,0x81,0x04,0x89,0x05,0x82,0x07,0x82,0x03,0x82,0x0e,0x82,0x07,0x82,0x05,
+0x82,0x0d,0x82,0x0b,0x82,0x08,0x82,0x03,0x82,0x08,0x82,0x08,0x82,0x11,0x82,
+0x06,0x82,0x03,0x82,0x08,0x82,0x0a,0x82,0x03,0x81,0x02,0x81,0x03,0x82,0x03,
+0x82,0x04,0x82,0x02,0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x0c,0x82,0x09,0x82,
+0x03,0x82,0x06,0x82,0x0e,0x82,0x08,0x83,0x08,0x82,0x07,0x82,0x06,0x82,0x02,
+0x82,0x06,0x84,0x03,0x84,0x07,0x86,0x0b,0x82,0x0c,0x82,0x0f,0x82,0x0d,0x82,
+0x0c,0x82,0x37,0x89,0x05,0x82,0x05,0x82,0x06,0x82,0x0d,0x82,0x06,0x82,0x05,
+0x8a,0x09,0x82,0x09,0x82,0x06,0x82,0x06,0x82,0x05,0x82,0x09,0x82,0x0e,0x82,
+0x0a,0x85,0x0c,0x82,0x07,0x82,0x04,0x82,0x04,0x82,0x03,0x82,0x05,0x82,0x06,
+0x82,0x06,0x82,0x06,0x82,0x05,0x82,0x05,0x82,0x06,0x82,0x08,0x82,0x0d,0x85,
+0x09,0x82,0x0b,0x82,0x06,0x82,0x07,0x82,0x02,0x81,0x07,0x82,0x01,0x81,0x02,
+0x82,0x01,0x82,0x09,0x83,0x0a,0x82,0x01,0x83,0x0b,0x82,0x0c,0x82,0x0e,0x82,
+0x0e,0x82,0x07,0x82,0x04,0x86,0x3f,0x8b,0x05,0x82,0x06,0x82,0x08,0x82,0x02,
+0x81,0x04,0x81,0x02,0x82,0x04,0x85,0x16,0x82,0x0f,0x82,0x0e,0x81,0x0c,0x82,
+0x39,0x82,0x0a,0x82,0x06,0x82,0x09,0x83,0x0b,0x83,0x11,0x82,0x05,0x8b,0x0c,
+0x82,0x06,0x82,0x05,0x82,0x08,0x83,0x09,0x82,0x06,0x82,0x0d,0x82,0x25,0x85,
+0x09,0x89,0x0a,0x85,0x09,0x82,0x07,0x81,0x03,0x82,0x03,0x81,0x02,0x82,0x04,
+0x8a,0x04,0x82,0x07,0x82,0x03,0x83,0x07,0x82,0x04,0x82,0x07,0x82,0x05,0x82,
+0x0d,0x82,0x0b,0x83,0x07,0x82,0x03,0x82,0x08,0x82,0x08,0x82,0x11,0x82,0x06,
+0x82,0x03,0x83,0x07,0x82,0x0a,0x82,0x03,0x84,0x03,0x82,0x03,0x82,0x05,0x82,
+0x01,0x82,0x02,0x83,0x07,0x83,0x03,0x82,0x0c,0x83,0x07,0x83,0x03,0x82,0x06,
+0x83,0x04,0x82,0x07,0x82,0x08,0x83,0x08,0x82,0x07,0x82,0x06,0x82,0x01,0x83,
+0x07,0x83,0x03,0x84,0x07,0x82,0x02,0x82,0x0b,0x82,0x0b,0x82,0x10,0x82,0x0e,
+0x82,0x0b,0x82,0x36,0x83,0x04,0x83,0x05,0x82,0x05,0x82,0x06,0x82,0x0d,0x82,
+0x06,0x82,0x05,0x82,0x11,0x82,0x09,0x82,0x06,0x82,0x06,0x82,0x05,0x82,0x09,
+0x82,0x0e,0x82,0x0a,0x82,0x02,0x82,0x0b,0x82,0x07,0x82,0x04,0x82,0x04,0x82,
+0x03,0x82,0x05,0x82,0x06,0x82,0x06,0x82,0x06,0x82,0x05,0x82,0x05,0x82,0x06,
+0x82,0x08,0x82,0x10,0x83,0x08,0x82,0x0b,0x82,0x06,0x82,0x07,0x82,0x01,0x82,
+0x08,0x83,0x03,0x81,0x01,0x82,0x08,0x85,0x09,0x82,0x01,0x82,0x0b,0x83,0x0d,
+0x82,0x0d,0x82,0x0e,0x82,0x0f,0x82,0x43,0x82,0x02,0x82,0x08,0x83,0x05,0x82,
+0x08,0x81,0x03,0x81,0x04,0x81,0x02,0x82,0x05,0x84,0x16,0x82,0x0f,0x82,0x1b,
+0x82,0x39,0x82,0x0a,0x83,0x05,0x82,0x09,0x83,0x0a,0x83,0x0a,0x82,0x06,0x82,
+0x0b,0x83,0x07,0x82,0x05,0x82,0x06,0x82,0x05,0x82,0x08,0x82,0x0a,0x82,0x06,
+0x82,0x0c,0x83,0x27,0x85,0x18,0x84,0x15,0x82,0x02,0x82,0x02,0x82,0x02,0x81,
+0x05,0x82,0x06,0x82,0x04,0x82,0x07,0x82,0x04,0x82,0x07,0x82,0x04,0x82,0x06,
+0x83,0x05,0x82,0x0d,0x82,0x0c,0x82,0x07,0x82,0x03,0x82,0x08,0x82,0x08,0x82,
+0x0a,0x82,0x05,0x82,0x06,0x82,0x04,0x83,0x06,0x82,0x0a,0x82,0x03,0x84,0x03,
+0x82,0x03,0x82,0x05,0x85,0x03,0x82,0x07,0x82,0x04,0x82,0x0d,0x82,0x05,0x81,
+0x01,0x82,0x04,0x82,0x06,0x83,0x04,0x83,0x06,0x82,0x08,0x83,0x08,0x82,0x07,
+0x82,0x07,0x84,0x08,0x83,0x04,0x82,0x07,0x82,0x04,0x82,0x0a,0x82,0x0a,0x83,
+0x10,0x82,0x0e,0x82,0x0b,0x82,0x36,0x82,0x05,0x83,0x05,0x82,0x05,0x82,0x06,
+0x83,0x05,0x82,0x05,0x82,0x06,0x82,0x05,0x83,0x10,0x82,0x09,0x83,0x05,0x82,
+0x06,0x82,0x05,0x82,0x09,0x82,0x0e,0x82,0x0a,0x82,0x02,0x83,0x0a,0x82,0x07,
+0x82,0x04,0x82,0x04,0x82,0x03,0x82,0x05,0x82,0x06,0x83,0x05,0x82,0x06,0x82,
+0x05,0x82,0x05,0x82,0x06,0x82,0x08,0x82,0x0a,0x82,0x05,0x82,0x08,0x82,0x0b,
+0x82,0x06,0x82,0x08,0x81,0x01,0x82,0x08,0x83,0x03,0x84,0x08,0x82,0x01,0x82,
+0x09,0x82,0x01,0x82,0x0b,0x82,0x0e,0x82,0x0d,0x82,0x0d,0x83,0x54,0x82,0x02,
+0x82,0x09,0x82,0x04,0x83,0x07,0x82,0x03,0x81,0x04,0x81,0x02,0x82,0x06,0x82,
+0x18,0x82,0x0d,0x82,0x1c,0x82,0x39,0x81,0x0c,0x82,0x04,0x83,0x09,0x83,0x09,
+0x83,0x0c,0x82,0x05,0x82,0x0b,0x83,0x07,0x82,0x04,0x83,0x06,0x83,0x04,0x82,
+0x08,0x82,0x0a,0x83,0x05,0x82,0x0c,0x82,0x2b,0x83,0x15,0x84,0x18,0x81,0x03,
+0x83,0x01,0x83,0x05,0x82,0x07,0x83,0x03,0x82,0x06,0x83,0x04,0x83,0x05,0x82,
+0x05,0x82,0x06,0x82,0x06,0x82,0x0d,0x82,0x0c,0x83,0x06,0x82,0x03,0x82,0x08,
+0x82,0x08,0x82,0x0a,0x82,0x05,0x82,0x06,0x82,0x05,0x83,0x05,0x82,0x0a,0x82,
+0x04,0x82,0x04,0x82,0x03,0x82,0x06,0x84,0x03,0x83,0x05,0x83,0x04,0x82,0x0d,
+0x83,0x05,0x83,0x04,0x82,0x06,0x83,0x05,0x82,0x06,0x82,0x08,0x83,0x08,0x83,
+0x05,0x83,0x07,0x84,0x08,0x82,0x05,0x82,0x06,0x83,0x04,0x83,0x09,0x82,0x0a,
+0x82,0x11,0x82,0x0e,0x82,0x0b,0x82,0x36,0x82,0x05,0x83,0x05,0x83,0x04,0x82,
+0x07,0x82,0x05,0x82,0x06,0x82,0x04,0x83,0x06,0x82,0x10,0x82,0x0a,0x82,0x04,
+0x83,0x06,0x82,0x05,0x82,0x09,0x82,0x0e,0x82,0x0a,0x82,0x03,0x82,0x0a,0x82,
+0x07,0x82,0x04,0x82,0x04,0x82,0x03,0x82,0x05,0x82,0x07,0x82,0x04,0x83,0x06,
+0x82,0x05,0x82,0x06,0x82,0x04,0x83,0x08,0x82,0x0a,0x83,0x04,0x82,0x08,0x82,
+0x03,0x82,0x06,0x83,0x04,0x83,0x08,0x83,0x09,0x82,0x04,0x83,0x08,0x82,0x03,
+0x82,0x09,0x83,0x0b,0x82,0x0f,0x82,0x0d,0x82,0x0d,0x83,0x38,0x82,0x1a,0x81,
+0x03,0x82,0x09,0x88,0x08,0x81,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x85,0x17,
+0x82,0x0d,0x82,0x29,0x82,0x1d,0x82,0x0c,0x82,0x0c,0x83,0x02,0x83,0x0a,0x83,
+0x09,0x89,0x06,0x84,0x01,0x83,0x0c,0x83,0x07,0x84,0x01,0x83,0x08,0x83,0x01,
+0x83,0x09,0x82,0x0b,0x84,0x01,0x83,0x07,0x82,0x02,0x84,0x09,0x82,0x0d,0x82,
+0x13,0x81,0x15,0x82,0x10,0x82,0x08,0x81,0x0f,0x82,0x08,0x82,0x03,0x8a,0x06,
+0x83,0x02,0x84,0x05,0x89,0x07,0x89,0x06,0x82,0x0d,0x84,0x02,0x84,0x03,0x82,
+0x08,0x82,0x06,0x86,0x09,0x83,0x01,0x83,0x07,0x82,0x06,0x83,0x04,0x8a,0x02,
+0x82,0x04,0x82,0x04,0x82,0x03,0x82,0x07,0x83,0x04,0x89,0x05,0x82,0x0e,0x8a,
+0x04,0x82,0x07,0x82,0x05,0x84,0x02,0x83,0x09,0x83,0x09,0x84,0x01,0x84,0x08,
+0x83,0x09,0x82,0x05,0x82,0x06,0x82,0x06,0x82,0x09,0x82,0x09,0x8b,0x09,0x82,
+0x0f,0x82,0x0a,0x82,0x37,0x89,0x05,0x88,0x08,0x88,0x07,0x89,0x06,0x84,0x02,
+0x82,0x0a,0x82,0x0a,0x89,0x06,0x82,0x05,0x82,0x06,0x88,0x0b,0x82,0x0a,0x82,
+0x04,0x82,0x06,0x88,0x04,0x82,0x04,0x82,0x04,0x82,0x03,0x82,0x05,0x82,0x07,
+0x84,0x01,0x83,0x07,0x84,0x01,0x83,0x07,0x83,0x02,0x84,0x08,0x82,0x0b,0x83,
+0x02,0x83,0x08,0x87,0x06,0x8a,0x08,0x83,0x09,0x82,0x05,0x82,0x07,0x83,0x03,
+0x83,0x08,0x83,0x0a,0x89,0x09,0x82,0x0d,0x82,0x0d,0x83,0x38,0x82,0x1a,0x81,
+0x03,0x82,0x0a,0x86,0x0f,0x84,0x05,0x86,0x01,0x83,0x16,0x82,0x0d,0x82,0x29,
+0x82,0x1d,0x82,0x0c,0x82,0x0d,0x86,0x0b,0x83,0x09,0x89,0x07,0x86,0x0d,0x83,
+0x08,0x86,0x0a,0x85,0x0a,0x82,0x0c,0x86,0x08,0x86,0x0b,0x82,0x0d,0x82,0x3b,
+0x82,0x09,0x81,0x0e,0x82,0x08,0x82,0x03,0x89,0x08,0x86,0x07,0x88,0x08,0x89,
+0x06,0x82,0x0e,0x87,0x05,0x82,0x08,0x82,0x06,0x86,0x0a,0x85,0x08,0x82,0x07,
+0x82,0x04,0x8a,0x02,0x82,0x0a,0x82,0x03,0x82,0x07,0x83,0x05,0x86,0x07,0x82,
+0x0f,0x86,0x01,0x83,0x03,0x82,0x07,0x82,0x07,0x86,0x0a,0x83,0x0a,0x87,0x0a,
+0x82,0x09,0x82,0x06,0x81,0x05,0x83,0x06,0x83,0x08,0x82,0x09,0x8b,0x09,0x82,
+0x0f,0x82,0x0a,0x82,0x37,0x85,0x02,0x82,0x05,0x81,0x01,0x85,0x0b,0x85,0x09,
+0x85,0x01,0x82,0x08,0x86,0x0a,0x82,0x0b,0x85,0x01,0x82,0x06,0x82,0x05,0x82,
+0x06,0x88,0x0b,0x82,0x0a,0x82,0x04,0x83,0x05,0x88,0x04,0x82,0x04,0x82,0x04,
+0x82,0x03,0x82,0x05,0x82,0x09,0x85,0x08,0x87,0x09,0x85,0x01,0x82,0x08,0x82,
+0x0c,0x86,0x0a,0x84,0x09,0x86,0x01,0x82,0x09,0x82,0x0a,0x81,0x05,0x82,0x07,
+0x82,0x05,0x82,0x08,0x83,0x0a,0x89,0x09,0x82,0x0d,0x82,0x0d,0x82,0x68,0x81,
+0x3b,0x82,0x0b,0x82,0x2a,0x82,0x2a,0x82,0x7f,0x35,0x82,0x46,0x83,0x7f,0x7f,
+0x08,0x82,0x7f,0x10,0x82,0x10,0x82,0x09,0x82,0x18,0x8a,0x76,0x82,0x28,0x82,
+0x54,0x82,0x14,0x82,0x71,0x82,0x1d,0x82,0x0d,0x82,0x0d,0x82,0x68,0x81,0x3b,
+0x82,0x0b,0x82,0x2a,0x82,0x2a,0x82,0x7f,0x35,0x82,0x48,0x86,0x7f,0x7f,0x7f,
+0x15,0x84,0x0e,0x82,0x07,0x84,0x18,0x8a,0x76,0x82,0x28,0x82,0x54,0x82,0x14,
+0x82,0x71,0x82,0x1d,0x83,0x0c,0x82,0x0d,0x82,0x7f,0x26,0x82,0x09,0x82,0x2b,
+0x81,0x7f,0x62,0x81,0x7f,0x7f,0x7f,0x64,0x84,0x17,0x84,0x7f,0x12,0x82,0x04,
+0x82,0x28,0x83,0x54,0x82,0x14,0x82,0x70,0x83,0x1e,0x83,0x0b,0x82,0x0c,0x82,
+0x7f,0x28,0x82,0x07,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x27,0x88,0x26,
+0x84,0x55,0x82,0x14,0x82,0x6e,0x84,0x21,0x81,0x0b,0x82,0x0c,0x81,0x7f,0x2a,
+0x81,0x07,0x81,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x2a,0x84,0x28,0x83,0x7f,
+0x5d,0x83,0x64,
+0x00,
+  }
+};
diff --git a/minuitwrp/roboto_23x41.h b/minuitwrp/roboto_23x41.h
new file mode 100644
index 0000000..6e7566e
--- /dev/null
+++ b/minuitwrp/roboto_23x41.h
@@ -0,0 +1,461 @@
+struct {
+  unsigned width;
+  unsigned height;
+  unsigned cwidth;
+  unsigned cheight;
+  unsigned char rundata[];
+} font = {
+  .width = 2208,
+  .height = 41,
+  .cwidth = 23,
+  .cheight = 41,
+  .rundata = {
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x7f,0x7f,0x17,0x83,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x2e,0x83,0x60,0x82,0x0a,0x81,0x7f,0x7f,
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0b,0x87,0x23,0x86,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x7f,0x7f,0x05,0x83,0x5f,0x83,0x09,0x83,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x7f,0x7f,0x0a,0x87,0x23,0x86,0x7f,0x7f,0x7f,0x7f,0x7f,0x39,0x82,0x26,
+0x82,0x7f,0x21,0x83,0x5e,0x83,0x0b,0x83,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x7f,0x09,0x87,0x23,0x86,0x40,0x84,0x27,0x83,0x37,0x83,0x27,0x86,0x20,
+0x83,0x1a,0x83,0x17,0x83,0x0c,0x83,0x16,0x86,0x7f,0x7f,0x5a,0x85,0x10,0x83,
+0x11,0x85,0x7f,0x1d,0x87,0x5c,0x83,0x0b,0x83,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x7f,0x7f,0x09,0x83,0x2a,0x83,0x41,0x83,0x27,0x83,0x37,0x83,0x26,0x86,
+0x21,0x83,0x1a,0x83,0x17,0x83,0x0c,0x83,0x16,0x86,0x7f,0x7f,0x59,0x84,0x12,
+0x83,0x13,0x84,0x58,0x83,0x11,0x83,0x03,0x83,0x10,0x83,0x04,0x83,0x0a,0x8a,
+0x0c,0x84,0x16,0x85,0x14,0x83,0x18,0x83,0x0d,0x83,0x7f,0x11,0x83,0x0d,0x86,
+0x13,0x84,0x11,0x86,0x11,0x86,0x16,0x83,0x0d,0x8c,0x0e,0x86,0x0b,0x91,0x0c,
+0x87,0x0f,0x86,0x7f,0x05,0x86,0x2a,0x83,0x0d,0x8c,0x10,0x87,0x0a,0x8c,0x0c,
+0x90,0x07,0x90,0x0d,0x86,0x0a,0x83,0x0d,0x83,0x09,0x89,0x16,0x83,0x08,0x83,
+0x0b,0x84,0x06,0x83,0x10,0x84,0x0e,0x84,0x03,0x83,0x0d,0x83,0x0a,0x86,0x0c,
+0x8d,0x0f,0x86,0x0c,0x8c,0x10,0x87,0x09,0x94,0x04,0x83,0x0d,0x83,0x03,0x83,
+0x0e,0x84,0x01,0x83,0x10,0x83,0x03,0x84,0x0b,0x84,0x03,0x84,0x0d,0x84,0x04,
+0x92,0x0c,0x83,0x0e,0x83,0x19,0x83,0x14,0x83,0x2b,0x83,0x26,0x83,0x37,0x83,
+0x25,0x87,0x21,0x83,0x1a,0x83,0x17,0x83,0x0c,0x83,0x19,0x83,0x7f,0x7f,0x59,
+0x83,0x13,0x83,0x14,0x83,0x58,0x83,0x11,0x83,0x03,0x83,0x10,0x83,0x04,0x83,
+0x09,0x8c,0x09,0x88,0x12,0x89,0x12,0x83,0x17,0x83,0x0f,0x83,0x7f,0x0f,0x83,
+0x0c,0x8a,0x0e,0x87,0x0f,0x8a,0x0d,0x8a,0x13,0x84,0x0d,0x8c,0x0c,0x8a,0x09,
+0x91,0x0a,0x8b,0x0b,0x8a,0x7f,0x01,0x8a,0x28,0x83,0x0d,0x8e,0x0c,0x8b,0x08,
+0x8e,0x0a,0x90,0x07,0x90,0x0a,0x8c,0x07,0x83,0x0d,0x83,0x09,0x89,0x16,0x83,
+0x08,0x83,0x0a,0x84,0x07,0x83,0x10,0x85,0x0d,0x84,0x03,0x84,0x0c,0x83,0x08,
+0x8a,0x0a,0x8f,0x0b,0x8a,0x0a,0x8e,0x0c,0x8b,0x07,0x94,0x04,0x83,0x0d,0x83,
+0x03,0x84,0x0d,0x84,0x01,0x83,0x08,0x81,0x07,0x83,0x03,0x84,0x0b,0x84,0x04,
+0x84,0x0b,0x84,0x05,0x92,0x0c,0x83,0x0e,0x84,0x18,0x83,0x14,0x83,0x2b,0x84,
+0x25,0x83,0x37,0x83,0x24,0x84,0x25,0x83,0x43,0x83,0x19,0x83,0x7f,0x33,0x83,
+0x7f,0x22,0x84,0x13,0x83,0x14,0x83,0x58,0x83,0x11,0x83,0x03,0x83,0x10,0x83,
+0x04,0x83,0x09,0x84,0x05,0x84,0x08,0x83,0x02,0x83,0x11,0x8b,0x11,0x83,0x17,
+0x83,0x0f,0x83,0x7f,0x0f,0x83,0x0b,0x8c,0x0b,0x89,0x0e,0x8c,0x0b,0x8c,0x11,
+0x85,0x0c,0x8d,0x0b,0x8b,0x09,0x91,0x09,0x8d,0x09,0x8c,0x7e,0x8c,0x0e,0x87,
+0x11,0x85,0x0c,0x8f,0x0a,0x8e,0x06,0x8f,0x09,0x90,0x07,0x90,0x09,0x8e,0x06,
+0x83,0x0d,0x83,0x0c,0x83,0x19,0x83,0x08,0x83,0x09,0x84,0x08,0x83,0x10,0x85,
+0x0c,0x85,0x03,0x85,0x0b,0x83,0x06,0x8e,0x08,0x90,0x08,0x8e,0x08,0x8f,0x0a,
+0x8d,0x06,0x94,0x04,0x83,0x0d,0x83,0x03,0x84,0x0d,0x83,0x02,0x83,0x08,0x81,
+0x07,0x83,0x04,0x84,0x09,0x84,0x05,0x84,0x0b,0x84,0x05,0x92,0x0c,0x83,0x0f,
+0x83,0x18,0x83,0x13,0x85,0x2b,0x83,0x25,0x83,0x37,0x83,0x24,0x83,0x26,0x83,
+0x43,0x83,0x19,0x83,0x7f,0x33,0x83,0x7f,0x22,0x83,0x14,0x83,0x15,0x83,0x57,
+0x83,0x11,0x83,0x03,0x83,0x10,0x82,0x05,0x83,0x08,0x84,0x07,0x83,0x07,0x83,
+0x04,0x83,0x05,0x82,0x08,0x85,0x04,0x84,0x10,0x83,0x16,0x83,0x11,0x83,0x7f,
+0x0e,0x83,0x0a,0x85,0x04,0x85,0x09,0x86,0x01,0x83,0x0d,0x84,0x05,0x84,0x0a,
+0x84,0x05,0x84,0x11,0x85,0x0c,0x84,0x13,0x84,0x06,0x82,0x16,0x84,0x09,0x84,
+0x05,0x84,0x08,0x85,0x04,0x85,0x7d,0x84,0x04,0x84,0x0c,0x8b,0x0f,0x85,0x0c,
+0x83,0x08,0x85,0x08,0x85,0x06,0x84,0x06,0x83,0x08,0x85,0x08,0x83,0x14,0x83,
+0x15,0x85,0x06,0x85,0x05,0x83,0x0d,0x83,0x0c,0x83,0x19,0x83,0x08,0x83,0x08,
+0x84,0x09,0x83,0x10,0x86,0x0b,0x85,0x03,0x85,0x0b,0x83,0x05,0x85,0x06,0x85,
+0x07,0x83,0x09,0x85,0x06,0x85,0x06,0x85,0x07,0x83,0x08,0x85,0x08,0x85,0x05,
+0x85,0x0e,0x83,0x0c,0x83,0x0d,0x83,0x04,0x83,0x0c,0x84,0x02,0x83,0x07,0x82,
+0x07,0x83,0x05,0x84,0x07,0x84,0x07,0x84,0x09,0x84,0x14,0x83,0x0d,0x83,0x0f,
+0x83,0x18,0x83,0x13,0x85,0x53,0x83,0x37,0x83,0x24,0x83,0x26,0x83,0x43,0x83,
+0x19,0x83,0x7f,0x33,0x83,0x7f,0x22,0x83,0x14,0x83,0x15,0x83,0x57,0x83,0x11,
+0x83,0x03,0x83,0x10,0x82,0x05,0x82,0x09,0x83,0x08,0x84,0x06,0x83,0x04,0x83,
+0x05,0x82,0x08,0x83,0x07,0x83,0x10,0x83,0x16,0x83,0x11,0x83,0x2c,0x83,0x5d,
+0x83,0x0b,0x83,0x08,0x83,0x09,0x83,0x04,0x83,0x0d,0x83,0x07,0x84,0x08,0x84,
+0x07,0x84,0x0f,0x86,0x0c,0x83,0x13,0x84,0x1e,0x84,0x09,0x84,0x07,0x84,0x07,
+0x83,0x08,0x83,0x7c,0x84,0x06,0x84,0x0a,0x83,0x06,0x84,0x0e,0x86,0x0b,0x83,
+0x0a,0x84,0x07,0x84,0x08,0x84,0x05,0x83,0x0a,0x84,0x07,0x83,0x14,0x83,0x15,
+0x84,0x09,0x83,0x05,0x83,0x0d,0x83,0x0c,0x83,0x19,0x83,0x08,0x83,0x08,0x83,
+0x0a,0x83,0x10,0x86,0x0b,0x85,0x03,0x86,0x0a,0x83,0x05,0x84,0x08,0x84,0x07,
+0x83,0x0b,0x83,0x06,0x84,0x08,0x84,0x07,0x83,0x0a,0x84,0x06,0x84,0x09,0x83,
+0x0e,0x83,0x0c,0x83,0x0d,0x83,0x04,0x84,0x0b,0x84,0x03,0x82,0x07,0x83,0x06,
+0x82,0x06,0x84,0x07,0x84,0x08,0x83,0x09,0x84,0x13,0x84,0x0d,0x83,0x0f,0x84,
+0x17,0x83,0x13,0x82,0x01,0x83,0x52,0x83,0x37,0x83,0x24,0x83,0x26,0x83,0x43,
+0x83,0x19,0x83,0x7f,0x33,0x83,0x7f,0x22,0x83,0x14,0x83,0x15,0x83,0x57,0x83,
+0x11,0x83,0x03,0x83,0x0f,0x83,0x04,0x83,0x09,0x83,0x09,0x83,0x06,0x83,0x04,
+0x83,0x04,0x82,0x09,0x83,0x07,0x83,0x10,0x83,0x15,0x83,0x13,0x83,0x2b,0x83,
+0x5d,0x83,0x0b,0x83,0x08,0x83,0x10,0x83,0x0c,0x83,0x09,0x83,0x08,0x83,0x09,
+0x83,0x0f,0x86,0x0c,0x83,0x13,0x83,0x1e,0x84,0x0a,0x83,0x09,0x83,0x06,0x84,
+0x08,0x83,0x7c,0x83,0x08,0x83,0x09,0x83,0x09,0x83,0x0c,0x87,0x0b,0x83,0x0b,
+0x83,0x06,0x84,0x0a,0x84,0x04,0x83,0x0b,0x84,0x06,0x83,0x14,0x83,0x14,0x84,
+0x0a,0x84,0x04,0x83,0x0d,0x83,0x0c,0x83,0x19,0x83,0x08,0x83,0x07,0x84,0x0a,
+0x83,0x10,0x86,0x0a,0x86,0x03,0x87,0x09,0x83,0x04,0x84,0x0a,0x84,0x06,0x83,
+0x0b,0x84,0x04,0x84,0x0a,0x84,0x06,0x83,0x0b,0x83,0x06,0x83,0x0a,0x84,0x0d,
+0x83,0x0c,0x83,0x0d,0x83,0x04,0x84,0x0b,0x83,0x04,0x83,0x06,0x83,0x06,0x82,
+0x07,0x84,0x05,0x84,0x09,0x84,0x07,0x84,0x13,0x84,0x0e,0x83,0x10,0x83,0x17,
+0x83,0x12,0x83,0x01,0x83,0x52,0x83,0x37,0x83,0x24,0x83,0x26,0x83,0x43,0x83,
+0x19,0x83,0x7f,0x33,0x83,0x7f,0x22,0x83,0x14,0x83,0x15,0x83,0x57,0x83,0x11,
+0x82,0x04,0x82,0x10,0x83,0x04,0x83,0x09,0x83,0x09,0x83,0x06,0x83,0x04,0x83,
+0x04,0x82,0x09,0x83,0x07,0x83,0x10,0x82,0x16,0x83,0x13,0x83,0x14,0x83,0x14,
+0x83,0x5d,0x82,0x0b,0x83,0x0a,0x83,0x0f,0x83,0x0c,0x83,0x09,0x83,0x08,0x83,
+0x09,0x83,0x0e,0x83,0x01,0x83,0x0c,0x83,0x13,0x83,0x1e,0x83,0x0b,0x83,0x09,
+0x83,0x06,0x83,0x0a,0x83,0x0d,0x83,0x14,0x83,0x5f,0x83,0x08,0x83,0x0b,0x82,
+0x0c,0x83,0x01,0x83,0x0b,0x83,0x0b,0x83,0x06,0x83,0x0c,0x83,0x04,0x83,0x0c,
+0x83,0x06,0x83,0x14,0x83,0x14,0x83,0x0c,0x83,0x04,0x83,0x0d,0x83,0x0c,0x83,
+0x19,0x83,0x08,0x83,0x06,0x84,0x0b,0x83,0x10,0x87,0x09,0x86,0x03,0x87,0x09,
+0x83,0x04,0x83,0x0c,0x83,0x06,0x83,0x0c,0x83,0x04,0x83,0x0c,0x83,0x06,0x83,
+0x0b,0x83,0x06,0x83,0x0b,0x83,0x0d,0x83,0x0c,0x83,0x0d,0x83,0x05,0x83,0x0a,
+0x84,0x04,0x83,0x06,0x83,0x05,0x83,0x07,0x84,0x05,0x84,0x0a,0x83,0x07,0x84,
+0x13,0x83,0x0f,0x83,0x10,0x83,0x17,0x83,0x12,0x83,0x01,0x83,0x40,0x86,0x0c,
+0x83,0x03,0x85,0x11,0x86,0x10,0x85,0x03,0x83,0x0d,0x85,0x0f,0x8a,0x0f,0x85,
+0x04,0x82,0x08,0x83,0x03,0x85,0x0f,0x86,0x17,0x83,0x0c,0x83,0x07,0x84,0x0e,
+0x83,0x0a,0x83,0x02,0x83,0x07,0x83,0x09,0x83,0x03,0x85,0x10,0x86,0x0d,0x82,
+0x04,0x85,0x10,0x85,0x04,0x82,0x09,0x83,0x02,0x85,0x11,0x86,0x0c,0x8d,0x0a,
+0x83,0x09,0x83,0x08,0x84,0x09,0x83,0x03,0x84,0x0f,0x83,0x05,0x84,0x08,0x84,
+0x07,0x83,0x0a,0x83,0x07,0x8f,0x0e,0x83,0x14,0x83,0x15,0x83,0x57,0x83,0x11,
+0x82,0x04,0x82,0x0b,0x92,0x06,0x83,0x12,0x83,0x04,0x83,0x03,0x82,0x0a,0x83,
+0x06,0x84,0x10,0x82,0x16,0x83,0x13,0x83,0x14,0x83,0x14,0x83,0x5c,0x83,0x0b,
+0x83,0x0a,0x83,0x0f,0x83,0x0c,0x83,0x09,0x83,0x14,0x83,0x0d,0x84,0x01,0x83,
+0x0c,0x83,0x12,0x83,0x1e,0x83,0x0c,0x83,0x09,0x83,0x06,0x83,0x0a,0x83,0x0d,
+0x83,0x14,0x83,0x5f,0x83,0x07,0x83,0x0d,0x82,0x0b,0x83,0x01,0x84,0x0a,0x83,
+0x0b,0x83,0x05,0x84,0x0c,0x83,0x04,0x83,0x0c,0x83,0x06,0x83,0x14,0x83,0x13,
+0x83,0x0d,0x83,0x04,0x83,0x0d,0x83,0x0c,0x83,0x19,0x83,0x08,0x83,0x05,0x84,
+0x0c,0x83,0x10,0x83,0x01,0x83,0x08,0x83,0x01,0x83,0x03,0x83,0x01,0x84,0x08,
+0x83,0x03,0x84,0x0c,0x84,0x05,0x83,0x0c,0x83,0x03,0x84,0x0c,0x84,0x05,0x83,
+0x0b,0x83,0x06,0x83,0x0b,0x83,0x0d,0x83,0x0c,0x83,0x0d,0x83,0x05,0x84,0x09,
+0x84,0x04,0x83,0x06,0x83,0x05,0x83,0x08,0x84,0x03,0x84,0x0b,0x84,0x05,0x84,
+0x13,0x84,0x0f,0x83,0x10,0x84,0x16,0x83,0x11,0x83,0x03,0x83,0x3d,0x8a,0x0a,
+0x83,0x01,0x89,0x0d,0x89,0x0d,0x89,0x01,0x83,0x0b,0x89,0x0d,0x8a,0x0d,0x89,
+0x01,0x83,0x08,0x83,0x01,0x89,0x0d,0x86,0x17,0x83,0x0c,0x83,0x06,0x84,0x0f,
+0x83,0x0a,0x83,0x01,0x86,0x03,0x87,0x07,0x83,0x01,0x89,0x0d,0x89,0x0b,0x83,
+0x01,0x89,0x0c,0x89,0x01,0x83,0x09,0x83,0x01,0x88,0x0d,0x8a,0x0a,0x8d,0x0a,
+0x83,0x09,0x83,0x08,0x84,0x09,0x83,0x03,0x84,0x06,0x82,0x06,0x84,0x06,0x84,
+0x06,0x84,0x08,0x84,0x08,0x84,0x07,0x8f,0x0e,0x83,0x14,0x83,0x15,0x83,0x57,
+0x83,0x11,0x82,0x04,0x82,0x0b,0x92,0x06,0x84,0x12,0x83,0x02,0x83,0x03,0x83,
+0x0a,0x84,0x04,0x84,0x11,0x82,0x16,0x83,0x13,0x83,0x14,0x83,0x14,0x83,0x5c,
+0x83,0x0b,0x83,0x0a,0x83,0x0f,0x83,0x18,0x83,0x13,0x84,0x0d,0x83,0x02,0x83,
+0x0c,0x83,0x12,0x83,0x1d,0x84,0x0c,0x84,0x07,0x84,0x06,0x83,0x0a,0x83,0x0d,
+0x83,0x14,0x83,0x1a,0x82,0x0a,0x8f,0x08,0x81,0x21,0x83,0x07,0x82,0x06,0x84,
+0x04,0x83,0x09,0x83,0x03,0x83,0x0a,0x83,0x0b,0x83,0x05,0x83,0x0d,0x83,0x04,
+0x83,0x0d,0x83,0x05,0x83,0x14,0x83,0x13,0x83,0x14,0x83,0x0d,0x83,0x0c,0x83,
+0x19,0x83,0x08,0x83,0x04,0x84,0x0d,0x83,0x10,0x83,0x01,0x84,0x07,0x83,0x01,
+0x83,0x03,0x83,0x01,0x85,0x07,0x83,0x03,0x83,0x0e,0x83,0x05,0x83,0x0c,0x83,
+0x03,0x83,0x0e,0x83,0x05,0x83,0x0b,0x83,0x06,0x83,0x1b,0x83,0x0c,0x83,0x0d,
+0x83,0x05,0x84,0x09,0x83,0x06,0x82,0x06,0x83,0x05,0x82,0x0a,0x83,0x02,0x84,
+0x0d,0x83,0x05,0x83,0x13,0x84,0x10,0x83,0x11,0x83,0x16,0x83,0x11,0x83,0x03,
+0x83,0x3c,0x8c,0x09,0x8e,0x0b,0x8b,0x0b,0x8e,0x0a,0x8b,0x0c,0x8a,0x0c,0x8e,
+0x08,0x8e,0x0f,0x83,0x17,0x83,0x0c,0x83,0x06,0x84,0x0f,0x83,0x0a,0x8b,0x01,
+0x89,0x06,0x8e,0x0a,0x8c,0x0a,0x8e,0x0a,0x8e,0x09,0x8d,0x0b,0x8c,0x09,0x8d,
+0x0a,0x83,0x09,0x83,0x08,0x84,0x08,0x84,0x03,0x84,0x05,0x83,0x06,0x84,0x06,
+0x84,0x06,0x83,0x09,0x84,0x08,0x84,0x07,0x8f,0x0e,0x83,0x14,0x83,0x15,0x83,
+0x57,0x83,0x24,0x92,0x07,0x83,0x12,0x88,0x03,0x82,0x0c,0x83,0x03,0x84,0x29,
+0x83,0x15,0x83,0x0f,0x81,0x03,0x83,0x03,0x81,0x10,0x83,0x5b,0x83,0x0c,0x83,
+0x0a,0x83,0x0f,0x83,0x17,0x83,0x13,0x84,0x0d,0x83,0x03,0x83,0x0c,0x83,0x01,
+0x85,0x0c,0x83,0x03,0x85,0x15,0x83,0x0e,0x84,0x05,0x84,0x07,0x83,0x0a,0x83,
+0x3f,0x84,0x0a,0x8f,0x08,0x83,0x1e,0x83,0x07,0x82,0x06,0x86,0x04,0x82,0x09,
+0x83,0x03,0x83,0x0a,0x83,0x0a,0x83,0x06,0x83,0x14,0x83,0x0d,0x83,0x05,0x83,
+0x14,0x83,0x13,0x83,0x14,0x83,0x0d,0x83,0x0c,0x83,0x19,0x83,0x08,0x83,0x03,
+0x84,0x0e,0x83,0x10,0x83,0x02,0x83,0x06,0x84,0x01,0x83,0x03,0x83,0x02,0x84,
+0x07,0x83,0x03,0x83,0x0e,0x83,0x05,0x83,0x0c,0x83,0x03,0x83,0x0e,0x83,0x05,
+0x83,0x0b,0x83,0x07,0x83,0x1a,0x83,0x0c,0x83,0x0d,0x83,0x06,0x83,0x08,0x84,
+0x06,0x83,0x04,0x85,0x04,0x82,0x0a,0x84,0x01,0x84,0x0d,0x84,0x03,0x84,0x12,
+0x84,0x11,0x83,0x11,0x84,0x15,0x83,0x11,0x83,0x03,0x84,0x3b,0x84,0x04,0x84,
+0x09,0x85,0x05,0x84,0x0b,0x84,0x04,0x84,0x0a,0x84,0x05,0x85,0x09,0x84,0x05,
+0x84,0x0e,0x83,0x10,0x84,0x05,0x85,0x08,0x85,0x05,0x84,0x0f,0x83,0x17,0x83,
+0x0c,0x83,0x05,0x84,0x10,0x83,0x0a,0x85,0x02,0x84,0x01,0x83,0x02,0x84,0x06,
+0x85,0x05,0x84,0x0a,0x84,0x05,0x84,0x09,0x85,0x05,0x84,0x0a,0x84,0x05,0x85,
+0x09,0x85,0x05,0x84,0x09,0x84,0x05,0x84,0x0c,0x83,0x11,0x83,0x09,0x83,0x09,
+0x83,0x08,0x83,0x05,0x83,0x05,0x83,0x06,0x83,0x08,0x84,0x04,0x84,0x0a,0x83,
+0x08,0x83,0x12,0x84,0x0f,0x83,0x14,0x83,0x15,0x83,0x57,0x83,0x28,0x83,0x05,
+0x82,0x0b,0x85,0x12,0x84,0x04,0x82,0x0d,0x89,0x2a,0x83,0x15,0x83,0x0e,0x88,
+0x01,0x83,0x10,0x83,0x5b,0x83,0x0c,0x83,0x0a,0x83,0x0f,0x83,0x17,0x83,0x0e,
+0x88,0x0e,0x83,0x03,0x83,0x0c,0x8b,0x0a,0x83,0x01,0x89,0x12,0x83,0x10,0x8b,
+0x08,0x83,0x0a,0x83,0x3c,0x87,0x0a,0x8f,0x08,0x86,0x1a,0x84,0x07,0x82,0x05,
+0x83,0x02,0x82,0x04,0x82,0x08,0x84,0x03,0x84,0x09,0x83,0x09,0x84,0x06,0x83,
+0x14,0x83,0x0d,0x83,0x05,0x83,0x14,0x83,0x13,0x83,0x14,0x83,0x0d,0x83,0x0c,
+0x83,0x19,0x83,0x08,0x83,0x02,0x84,0x0f,0x83,0x10,0x83,0x02,0x83,0x06,0x83,
+0x02,0x83,0x03,0x83,0x03,0x84,0x06,0x83,0x03,0x83,0x0e,0x83,0x05,0x83,0x0b,
+0x84,0x03,0x83,0x0e,0x83,0x05,0x83,0x0a,0x83,0x08,0x84,0x19,0x83,0x0c,0x83,
+0x0d,0x83,0x06,0x84,0x07,0x84,0x06,0x83,0x04,0x85,0x03,0x83,0x0b,0x87,0x0f,
+0x83,0x03,0x83,0x13,0x84,0x11,0x83,0x12,0x83,0x15,0x83,0x10,0x83,0x05,0x83,
+0x3a,0x83,0x07,0x84,0x08,0x84,0x07,0x84,0x09,0x84,0x06,0x84,0x08,0x84,0x07,
+0x84,0x08,0x84,0x07,0x83,0x0e,0x83,0x0f,0x84,0x07,0x84,0x08,0x84,0x07,0x84,
+0x0e,0x83,0x17,0x83,0x0c,0x83,0x04,0x84,0x11,0x83,0x0a,0x84,0x04,0x85,0x05,
+0x84,0x05,0x84,0x07,0x84,0x08,0x84,0x07,0x84,0x08,0x84,0x07,0x84,0x08,0x84,
+0x07,0x84,0x09,0x84,0x08,0x82,0x09,0x83,0x07,0x84,0x0b,0x83,0x11,0x83,0x09,
+0x83,0x09,0x84,0x07,0x83,0x05,0x83,0x05,0x83,0x06,0x83,0x09,0x83,0x04,0x83,
+0x0b,0x84,0x06,0x84,0x12,0x84,0x0f,0x83,0x14,0x83,0x15,0x83,0x57,0x83,0x28,
+0x83,0x04,0x83,0x0c,0x86,0x17,0x83,0x0e,0x86,0x2c,0x83,0x15,0x83,0x0e,0x8d,
+0x0f,0x83,0x5b,0x82,0x0d,0x83,0x0a,0x83,0x0f,0x83,0x16,0x83,0x0f,0x87,0x0e,
+0x83,0x04,0x83,0x0c,0x8c,0x09,0x8e,0x11,0x83,0x12,0x87,0x0a,0x84,0x09,0x83,
+0x3a,0x88,0x22,0x88,0x18,0x83,0x08,0x82,0x04,0x83,0x03,0x82,0x04,0x83,0x07,
+0x83,0x05,0x83,0x09,0x8f,0x07,0x83,0x14,0x83,0x0d,0x83,0x05,0x8e,0x09,0x8f,
+0x07,0x83,0x14,0x93,0x0c,0x83,0x19,0x83,0x08,0x88,0x10,0x83,0x10,0x83,0x02,
+0x84,0x05,0x83,0x02,0x83,0x03,0x83,0x03,0x85,0x05,0x83,0x03,0x83,0x0e,0x83,
+0x05,0x83,0x0b,0x83,0x04,0x83,0x0e,0x83,0x05,0x83,0x09,0x84,0x09,0x86,0x16,
+0x83,0x0c,0x83,0x0d,0x83,0x07,0x83,0x07,0x83,0x07,0x83,0x04,0x82,0x01,0x82,
+0x03,0x83,0x0c,0x85,0x10,0x84,0x01,0x84,0x12,0x84,0x12,0x83,0x12,0x83,0x15,
+0x83,0x10,0x83,0x05,0x83,0x3a,0x83,0x08,0x83,0x08,0x83,0x09,0x83,0x09,0x83,
+0x08,0x83,0x08,0x83,0x09,0x83,0x08,0x83,0x09,0x83,0x0d,0x83,0x0f,0x83,0x09,
+0x83,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,0x83,0x0c,0x83,0x04,0x84,0x11,0x83,
+0x0a,0x83,0x06,0x84,0x06,0x83,0x05,0x83,0x09,0x83,0x08,0x83,0x09,0x83,0x08,
+0x83,0x09,0x83,0x08,0x83,0x09,0x83,0x09,0x83,0x14,0x83,0x08,0x83,0x0b,0x83,
+0x11,0x83,0x09,0x83,0x0a,0x83,0x06,0x84,0x05,0x84,0x04,0x84,0x05,0x83,0x09,
+0x84,0x02,0x84,0x0b,0x84,0x06,0x84,0x11,0x84,0x0f,0x83,0x15,0x83,0x16,0x83,
+0x56,0x83,0x28,0x83,0x04,0x83,0x0d,0x88,0x14,0x82,0x0f,0x85,0x2d,0x83,0x15,
+0x83,0x11,0x88,0x0a,0x92,0x52,0x83,0x0d,0x83,0x0a,0x83,0x0f,0x83,0x15,0x84,
+0x0f,0x89,0x0b,0x83,0x05,0x83,0x0b,0x85,0x04,0x85,0x08,0x85,0x05,0x85,0x0f,
+0x83,0x11,0x8b,0x09,0x83,0x08,0x84,0x38,0x88,0x26,0x88,0x15,0x83,0x08,0x82,
+0x05,0x83,0x03,0x82,0x05,0x82,0x07,0x83,0x05,0x83,0x09,0x8f,0x07,0x83,0x14,
+0x83,0x0d,0x83,0x05,0x8e,0x09,0x8f,0x07,0x83,0x07,0x89,0x04,0x93,0x0c,0x83,
+0x19,0x83,0x08,0x88,0x10,0x83,0x10,0x83,0x03,0x83,0x04,0x83,0x03,0x83,0x03,
+0x83,0x04,0x84,0x05,0x83,0x03,0x83,0x0e,0x83,0x05,0x83,0x09,0x85,0x04,0x83,
+0x0e,0x83,0x05,0x8f,0x0c,0x87,0x13,0x83,0x0c,0x83,0x0d,0x83,0x07,0x83,0x06,
+0x84,0x08,0x82,0x04,0x82,0x01,0x83,0x02,0x82,0x0d,0x85,0x11,0x83,0x01,0x83,
+0x12,0x84,0x13,0x83,0x12,0x84,0x14,0x83,0x0f,0x84,0x06,0x83,0x44,0x83,0x08,
+0x83,0x09,0x84,0x07,0x83,0x09,0x83,0x07,0x84,0x09,0x83,0x07,0x83,0x0a,0x83,
+0x0d,0x83,0x0e,0x84,0x09,0x83,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,0x83,0x0c,
+0x83,0x03,0x84,0x12,0x83,0x0a,0x83,0x06,0x83,0x07,0x83,0x05,0x83,0x09,0x83,
+0x07,0x84,0x09,0x84,0x07,0x83,0x09,0x84,0x06,0x84,0x09,0x83,0x09,0x83,0x14,
+0x83,0x16,0x83,0x11,0x83,0x09,0x83,0x0a,0x83,0x06,0x83,0x06,0x84,0x03,0x85,
+0x04,0x83,0x0b,0x88,0x0d,0x83,0x06,0x83,0x11,0x84,0x0f,0x84,0x15,0x83,0x16,
+0x84,0x0c,0x85,0x44,0x83,0x28,0x82,0x05,0x83,0x0e,0x88,0x12,0x82,0x0f,0x87,
+0x2c,0x83,0x15,0x83,0x12,0x85,0x0c,0x92,0x1f,0x8d,0x26,0x83,0x0d,0x83,0x0a,
+0x83,0x0f,0x83,0x15,0x83,0x16,0x84,0x0a,0x83,0x05,0x83,0x0b,0x84,0x07,0x83,
+0x08,0x84,0x08,0x83,0x0f,0x83,0x10,0x84,0x04,0x85,0x08,0x85,0x04,0x86,0x36,
+0x87,0x2c,0x87,0x12,0x83,0x09,0x82,0x04,0x83,0x04,0x82,0x05,0x82,0x06,0x83,
+0x06,0x84,0x08,0x90,0x06,0x83,0x14,0x83,0x0d,0x83,0x05,0x8e,0x09,0x8f,0x07,
+0x83,0x07,0x89,0x04,0x93,0x0c,0x83,0x19,0x83,0x08,0x89,0x0f,0x83,0x10,0x83,
+0x03,0x84,0x03,0x83,0x03,0x83,0x03,0x83,0x05,0x84,0x04,0x83,0x03,0x83,0x0e,
+0x83,0x05,0x90,0x05,0x83,0x0e,0x83,0x05,0x8e,0x0f,0x87,0x11,0x83,0x0c,0x83,
+0x0d,0x83,0x07,0x84,0x05,0x83,0x09,0x83,0x02,0x83,0x02,0x82,0x02,0x82,0x0d,
+0x85,0x11,0x87,0x12,0x84,0x13,0x83,0x13,0x83,0x14,0x83,0x60,0x83,0x08,0x83,
+0x0a,0x83,0x07,0x83,0x13,0x83,0x0a,0x83,0x07,0x83,0x0a,0x83,0x0d,0x83,0x0e,
+0x83,0x0a,0x83,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,0x83,0x0c,0x83,0x02,0x84,
+0x13,0x83,0x0a,0x83,0x06,0x83,0x07,0x83,0x05,0x83,0x09,0x83,0x07,0x83,0x0b,
+0x83,0x07,0x83,0x0a,0x83,0x06,0x83,0x0a,0x83,0x09,0x83,0x14,0x84,0x15,0x83,
+0x11,0x83,0x09,0x83,0x0a,0x84,0x05,0x83,0x07,0x83,0x03,0x85,0x04,0x83,0x0c,
+0x86,0x0e,0x84,0x05,0x83,0x10,0x84,0x0f,0x84,0x16,0x83,0x17,0x85,0x09,0x88,
+0x07,0x83,0x38,0x83,0x28,0x82,0x05,0x82,0x12,0x86,0x11,0x82,0x03,0x84,0x07,
+0x84,0x01,0x84,0x06,0x83,0x22,0x83,0x15,0x83,0x11,0x87,0x0b,0x92,0x1f,0x8d,
+0x26,0x82,0x0e,0x83,0x0a,0x83,0x0f,0x83,0x14,0x83,0x18,0x83,0x09,0x83,0x06,
+0x83,0x16,0x84,0x07,0x83,0x09,0x84,0x0d,0x83,0x10,0x83,0x08,0x83,0x09,0x8e,
+0x34,0x87,0x30,0x87,0x0f,0x83,0x0a,0x82,0x04,0x83,0x04,0x82,0x05,0x82,0x06,
+0x83,0x07,0x83,0x08,0x83,0x09,0x85,0x05,0x83,0x14,0x83,0x0d,0x83,0x05,0x83,
+0x14,0x83,0x13,0x83,0x07,0x89,0x04,0x83,0x0d,0x83,0x0c,0x83,0x19,0x83,0x08,
+0x83,0x03,0x84,0x0e,0x83,0x10,0x83,0x04,0x83,0x02,0x84,0x03,0x83,0x03,0x83,
+0x05,0x85,0x03,0x83,0x03,0x83,0x0e,0x83,0x05,0x8f,0x06,0x83,0x0e,0x83,0x05,
+0x8f,0x11,0x86,0x0f,0x83,0x0c,0x83,0x0d,0x83,0x08,0x83,0x05,0x83,0x09,0x83,
+0x02,0x82,0x03,0x82,0x01,0x83,0x0d,0x86,0x11,0x85,0x12,0x84,0x14,0x83,0x13,
+0x83,0x14,0x83,0x59,0x8a,0x08,0x83,0x0a,0x83,0x07,0x83,0x13,0x83,0x0a,0x83,
+0x07,0x90,0x0d,0x83,0x0e,0x83,0x0a,0x83,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,
+0x83,0x0c,0x88,0x14,0x83,0x0a,0x83,0x06,0x83,0x07,0x83,0x05,0x83,0x09,0x83,
+0x07,0x83,0x0b,0x83,0x07,0x83,0x0a,0x83,0x06,0x83,0x0a,0x83,0x09,0x83,0x15,
+0x85,0x13,0x83,0x11,0x83,0x09,0x83,0x0b,0x83,0x04,0x84,0x07,0x83,0x03,0x86,
+0x03,0x83,0x0c,0x86,0x0f,0x83,0x04,0x84,0x10,0x84,0x0f,0x83,0x17,0x83,0x18,
+0x84,0x08,0x8a,0x06,0x83,0x38,0x83,0x27,0x83,0x05,0x82,0x14,0x85,0x0f,0x82,
+0x03,0x86,0x05,0x84,0x03,0x84,0x05,0x83,0x22,0x83,0x15,0x83,0x10,0x84,0x01,
+0x83,0x12,0x83,0x27,0x8d,0x25,0x83,0x0e,0x83,0x0a,0x83,0x0f,0x83,0x13,0x83,
+0x1a,0x83,0x07,0x84,0x06,0x83,0x17,0x83,0x07,0x83,0x0a,0x83,0x0d,0x83,0x0f,
+0x84,0x08,0x84,0x09,0x89,0x01,0x83,0x34,0x85,0x35,0x85,0x0d,0x84,0x0a,0x82,
+0x04,0x83,0x04,0x82,0x05,0x82,0x06,0x83,0x07,0x83,0x08,0x83,0x0b,0x83,0x05,
+0x83,0x14,0x83,0x0d,0x83,0x05,0x83,0x14,0x83,0x13,0x83,0x0d,0x83,0x04,0x83,
+0x0d,0x83,0x0c,0x83,0x19,0x83,0x08,0x83,0x03,0x85,0x0d,0x83,0x10,0x83,0x04,
+0x84,0x01,0x83,0x04,0x83,0x03,0x83,0x06,0x84,0x03,0x83,0x03,0x83,0x0e,0x83,
+0x05,0x8d,0x08,0x83,0x0e,0x83,0x05,0x83,0x09,0x84,0x13,0x84,0x0e,0x83,0x0c,
+0x83,0x0d,0x83,0x08,0x83,0x04,0x84,0x09,0x83,0x02,0x82,0x03,0x82,0x01,0x83,
+0x0c,0x87,0x11,0x85,0x11,0x84,0x15,0x83,0x13,0x84,0x13,0x83,0x56,0x8d,0x08,
+0x83,0x0a,0x83,0x07,0x83,0x13,0x83,0x0a,0x83,0x07,0x90,0x0d,0x83,0x0e,0x83,
+0x0a,0x83,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,0x83,0x0c,0x88,0x14,0x83,0x0a,
+0x83,0x06,0x83,0x07,0x83,0x05,0x83,0x09,0x83,0x07,0x83,0x0b,0x83,0x07,0x83,
+0x0a,0x83,0x06,0x83,0x0a,0x83,0x09,0x83,0x16,0x88,0x0f,0x83,0x11,0x83,0x09,
+0x83,0x0b,0x83,0x04,0x83,0x08,0x84,0x01,0x83,0x01,0x83,0x03,0x83,0x0d,0x84,
+0x10,0x83,0x04,0x83,0x10,0x84,0x10,0x84,0x16,0x83,0x17,0x85,0x07,0x84,0x04,
+0x84,0x04,0x84,0x38,0x83,0x23,0x92,0x12,0x84,0x0d,0x83,0x02,0x83,0x02,0x83,
+0x03,0x84,0x05,0x84,0x04,0x83,0x22,0x83,0x15,0x83,0x10,0x83,0x03,0x83,0x11,
+0x83,0x59,0x82,0x0f,0x83,0x0a,0x83,0x0f,0x83,0x12,0x84,0x1a,0x83,0x07,0x83,
+0x07,0x83,0x17,0x83,0x07,0x83,0x0a,0x83,0x0d,0x83,0x0f,0x83,0x0a,0x83,0x0b,
+0x85,0x03,0x83,0x34,0x85,0x14,0x8f,0x13,0x84,0x0d,0x83,0x0b,0x82,0x04,0x83,
+0x04,0x82,0x05,0x82,0x05,0x8f,0x07,0x83,0x0c,0x83,0x04,0x83,0x14,0x83,0x0d,
+0x83,0x05,0x83,0x14,0x83,0x13,0x83,0x0d,0x83,0x04,0x83,0x0d,0x83,0x0c,0x83,
+0x19,0x83,0x08,0x83,0x04,0x85,0x0c,0x83,0x10,0x83,0x04,0x84,0x01,0x83,0x04,
+0x83,0x03,0x83,0x07,0x84,0x02,0x83,0x03,0x83,0x0e,0x83,0x05,0x83,0x12,0x83,
+0x0e,0x83,0x05,0x83,0x0a,0x84,0x13,0x83,0x0e,0x83,0x0c,0x83,0x0d,0x83,0x08,
+0x84,0x03,0x83,0x0a,0x83,0x01,0x83,0x03,0x82,0x01,0x83,0x0b,0x84,0x01,0x84,
+0x11,0x83,0x12,0x83,0x16,0x83,0x14,0x83,0x13,0x83,0x55,0x8e,0x08,0x83,0x0a,
+0x83,0x07,0x83,0x13,0x83,0x0a,0x83,0x07,0x90,0x0d,0x83,0x0e,0x83,0x0a,0x83,
+0x08,0x83,0x09,0x83,0x0e,0x83,0x17,0x83,0x0c,0x88,0x14,0x83,0x0a,0x83,0x06,
+0x83,0x07,0x83,0x05,0x83,0x09,0x83,0x07,0x83,0x0b,0x83,0x07,0x83,0x0a,0x83,
+0x06,0x83,0x0a,0x83,0x09,0x83,0x19,0x87,0x0d,0x83,0x11,0x83,0x09,0x83,0x0b,
+0x84,0x03,0x83,0x09,0x83,0x01,0x83,0x01,0x83,0x02,0x83,0x0e,0x84,0x10,0x84,
+0x03,0x83,0x0f,0x84,0x12,0x84,0x15,0x83,0x16,0x84,0x09,0x83,0x06,0x8a,0x39,
+0x83,0x23,0x92,0x12,0x84,0x0d,0x82,0x02,0x83,0x04,0x83,0x02,0x83,0x07,0x84,
+0x02,0x84,0x22,0x83,0x15,0x83,0x11,0x81,0x04,0x82,0x12,0x83,0x58,0x83,0x0f,
+0x83,0x0a,0x83,0x0f,0x83,0x11,0x84,0x1b,0x83,0x06,0x92,0x13,0x83,0x07,0x83,
+0x0a,0x83,0x0d,0x83,0x0f,0x83,0x0a,0x83,0x13,0x83,0x35,0x87,0x11,0x8f,0x10,
+0x87,0x0d,0x83,0x0b,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x05,0x8f,0x07,0x83,
+0x0c,0x83,0x04,0x83,0x0d,0x83,0x04,0x83,0x0d,0x83,0x05,0x83,0x14,0x83,0x13,
+0x83,0x0d,0x83,0x04,0x83,0x0d,0x83,0x0c,0x83,0x19,0x83,0x08,0x83,0x05,0x84,
+0x0c,0x83,0x10,0x83,0x05,0x86,0x05,0x83,0x03,0x83,0x07,0x85,0x01,0x83,0x03,
+0x83,0x0e,0x83,0x05,0x83,0x12,0x83,0x0e,0x83,0x05,0x83,0x0b,0x83,0x14,0x83,
+0x0d,0x83,0x0c,0x83,0x0d,0x83,0x09,0x83,0x03,0x83,0x0b,0x82,0x01,0x82,0x04,
+0x86,0x0b,0x83,0x02,0x84,0x11,0x83,0x11,0x84,0x16,0x83,0x14,0x84,0x12,0x83,
+0x55,0x84,0x07,0x83,0x08,0x83,0x0a,0x83,0x07,0x83,0x13,0x83,0x0a,0x83,0x07,
+0x83,0x1a,0x83,0x0e,0x83,0x0a,0x83,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,0x83,
+0x0c,0x83,0x02,0x84,0x13,0x83,0x0a,0x83,0x06,0x83,0x07,0x83,0x05,0x83,0x09,
+0x83,0x07,0x83,0x0b,0x83,0x07,0x83,0x0a,0x83,0x06,0x83,0x0a,0x83,0x09,0x83,
+0x1c,0x85,0x0c,0x83,0x11,0x83,0x09,0x83,0x0c,0x83,0x02,0x84,0x09,0x83,0x01,
+0x83,0x01,0x83,0x02,0x83,0x0d,0x86,0x10,0x83,0x02,0x84,0x0e,0x84,0x14,0x83,
+0x15,0x83,0x16,0x83,0x0a,0x83,0x07,0x88,0x60,0x92,0x13,0x83,0x0c,0x82,0x03,
+0x83,0x04,0x83,0x02,0x83,0x07,0x85,0x01,0x83,0x23,0x83,0x15,0x83,0x2a,0x83,
+0x58,0x83,0x0f,0x83,0x0a,0x83,0x0f,0x83,0x10,0x84,0x0f,0x83,0x0a,0x83,0x06,
+0x92,0x13,0x83,0x07,0x83,0x0a,0x83,0x0c,0x83,0x10,0x83,0x0a,0x83,0x13,0x83,
+0x37,0x87,0x0f,0x8f,0x0e,0x87,0x0f,0x83,0x0b,0x82,0x04,0x82,0x05,0x82,0x05,
+0x82,0x05,0x8f,0x07,0x83,0x0c,0x83,0x04,0x84,0x0c,0x83,0x04,0x83,0x0c,0x84,
+0x05,0x83,0x14,0x83,0x13,0x84,0x0c,0x83,0x04,0x83,0x0d,0x83,0x0c,0x83,0x0d,
+0x83,0x09,0x83,0x08,0x83,0x06,0x84,0x0b,0x83,0x10,0x83,0x05,0x86,0x05,0x83,
+0x03,0x83,0x08,0x84,0x01,0x83,0x03,0x84,0x0c,0x84,0x05,0x83,0x12,0x84,0x0c,
+0x84,0x05,0x83,0x0b,0x83,0x05,0x83,0x0c,0x83,0x0d,0x83,0x0c,0x83,0x0d,0x83,
+0x09,0x84,0x01,0x84,0x0b,0x82,0x01,0x82,0x05,0x84,0x0b,0x84,0x03,0x84,0x10,
+0x83,0x10,0x84,0x17,0x83,0x15,0x83,0x12,0x83,0x54,0x84,0x08,0x83,0x08,0x83,
+0x0a,0x83,0x07,0x83,0x13,0x83,0x0a,0x83,0x07,0x83,0x1a,0x83,0x0e,0x83,0x0a,
+0x83,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,0x83,0x0c,0x83,0x03,0x84,0x12,0x83,
+0x0a,0x83,0x06,0x83,0x07,0x83,0x05,0x83,0x09,0x83,0x07,0x83,0x0b,0x83,0x07,
+0x83,0x0a,0x83,0x06,0x83,0x0a,0x83,0x09,0x83,0x1e,0x84,0x0b,0x83,0x11,0x83,
+0x09,0x83,0x0c,0x83,0x02,0x83,0x0a,0x83,0x01,0x82,0x03,0x83,0x01,0x83,0x0d,
+0x87,0x0f,0x83,0x02,0x83,0x0f,0x83,0x16,0x83,0x14,0x83,0x15,0x83,0x17,0x85,
+0x65,0x82,0x05,0x83,0x0a,0x83,0x0a,0x83,0x0c,0x82,0x03,0x83,0x04,0x83,0x02,
+0x83,0x08,0x88,0x23,0x83,0x15,0x83,0x2a,0x83,0x58,0x82,0x10,0x83,0x0a,0x83,
+0x0f,0x83,0x0f,0x84,0x10,0x83,0x0a,0x83,0x06,0x92,0x07,0x83,0x09,0x83,0x07,
+0x84,0x09,0x83,0x0c,0x83,0x10,0x83,0x0a,0x83,0x12,0x84,0x39,0x87,0x27,0x88,
+0x1f,0x82,0x04,0x82,0x05,0x82,0x04,0x82,0x05,0x83,0x0a,0x84,0x06,0x83,0x0c,
+0x83,0x05,0x83,0x0c,0x83,0x04,0x83,0x0c,0x83,0x06,0x83,0x14,0x83,0x14,0x83,
+0x0c,0x83,0x04,0x83,0x0d,0x83,0x0c,0x83,0x0d,0x83,0x09,0x83,0x08,0x83,0x07,
+0x84,0x0a,0x83,0x10,0x83,0x06,0x85,0x05,0x83,0x03,0x83,0x09,0x87,0x04,0x83,
+0x0c,0x83,0x06,0x83,0x13,0x83,0x0c,0x83,0x06,0x83,0x0b,0x83,0x05,0x83,0x0c,
+0x83,0x0d,0x83,0x0c,0x83,0x0c,0x84,0x0a,0x83,0x01,0x83,0x0c,0x85,0x05,0x84,
+0x0a,0x84,0x05,0x84,0x0f,0x83,0x10,0x83,0x18,0x83,0x15,0x83,0x12,0x83,0x54,
+0x83,0x09,0x83,0x08,0x83,0x0a,0x83,0x07,0x83,0x09,0x83,0x07,0x83,0x0a,0x83,
+0x07,0x83,0x1a,0x83,0x0e,0x83,0x0a,0x83,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,
+0x83,0x0c,0x83,0x04,0x84,0x11,0x83,0x0a,0x83,0x06,0x83,0x07,0x83,0x05,0x83,
+0x09,0x83,0x07,0x84,0x09,0x84,0x07,0x83,0x0a,0x83,0x06,0x83,0x0a,0x83,0x09,
+0x83,0x1f,0x83,0x0b,0x83,0x11,0x83,0x09,0x83,0x0d,0x83,0x01,0x83,0x0a,0x86,
+0x03,0x83,0x01,0x82,0x0d,0x83,0x02,0x83,0x0f,0x84,0x01,0x83,0x0e,0x84,0x16,
+0x83,0x14,0x83,0x15,0x83,0x7f,0x02,0x82,0x05,0x82,0x0b,0x83,0x0a,0x83,0x0b,
+0x82,0x04,0x83,0x04,0x83,0x02,0x83,0x09,0x86,0x24,0x83,0x15,0x83,0x2a,0x83,
+0x57,0x83,0x11,0x83,0x08,0x83,0x10,0x83,0x0f,0x83,0x11,0x84,0x08,0x84,0x11,
+0x83,0x0b,0x83,0x08,0x84,0x08,0x83,0x08,0x84,0x0c,0x83,0x10,0x84,0x08,0x84,
+0x12,0x83,0x3c,0x88,0x22,0x88,0x21,0x82,0x04,0x83,0x03,0x83,0x04,0x82,0x05,
+0x83,0x0b,0x83,0x06,0x83,0x0c,0x83,0x05,0x84,0x0a,0x83,0x05,0x83,0x0b,0x84,
+0x06,0x83,0x14,0x83,0x14,0x84,0x0b,0x83,0x04,0x83,0x0d,0x83,0x0c,0x83,0x0d,
+0x83,0x08,0x84,0x08,0x83,0x07,0x85,0x09,0x83,0x10,0x83,0x07,0x83,0x06,0x83,
+0x03,0x83,0x09,0x87,0x04,0x84,0x0a,0x84,0x06,0x83,0x13,0x84,0x0a,0x84,0x06,
+0x83,0x0b,0x83,0x05,0x84,0x0b,0x83,0x0d,0x83,0x0d,0x83,0x0b,0x83,0x0b,0x83,
+0x01,0x83,0x0c,0x85,0x05,0x84,0x0a,0x84,0x05,0x84,0x0f,0x83,0x0f,0x84,0x18,
+0x83,0x15,0x84,0x11,0x83,0x54,0x83,0x09,0x83,0x08,0x83,0x09,0x83,0x09,0x83,
+0x08,0x83,0x07,0x84,0x09,0x83,0x08,0x83,0x19,0x83,0x0e,0x84,0x09,0x83,0x08,
+0x83,0x09,0x83,0x0e,0x83,0x17,0x83,0x0c,0x83,0x04,0x84,0x11,0x83,0x0a,0x83,
+0x06,0x83,0x07,0x83,0x05,0x83,0x09,0x83,0x08,0x83,0x09,0x83,0x08,0x83,0x09,
+0x83,0x07,0x84,0x09,0x83,0x09,0x83,0x13,0x83,0x09,0x83,0x0b,0x83,0x11,0x83,
+0x09,0x83,0x0d,0x86,0x0c,0x85,0x03,0x83,0x01,0x82,0x0c,0x84,0x02,0x84,0x0f,
+0x87,0x0d,0x84,0x17,0x83,0x14,0x83,0x15,0x83,0x7f,0x01,0x83,0x05,0x82,0x0b,
+0x83,0x0a,0x83,0x0a,0x83,0x04,0x83,0x04,0x83,0x02,0x84,0x09,0x84,0x26,0x83,
+0x13,0x83,0x2b,0x83,0x57,0x83,0x11,0x83,0x08,0x83,0x10,0x83,0x0e,0x83,0x13,
+0x83,0x08,0x83,0x12,0x83,0x0b,0x84,0x07,0x83,0x09,0x84,0x07,0x83,0x0d,0x83,
+0x11,0x83,0x08,0x83,0x12,0x84,0x25,0x83,0x16,0x86,0x21,0x86,0x25,0x82,0x03,
+0x83,0x02,0x85,0x02,0x82,0x05,0x84,0x0b,0x83,0x06,0x83,0x0b,0x84,0x06,0x83,
+0x09,0x84,0x05,0x83,0x0a,0x84,0x07,0x83,0x14,0x83,0x15,0x84,0x09,0x84,0x04,
+0x83,0x0d,0x83,0x0c,0x83,0x0d,0x84,0x07,0x83,0x09,0x83,0x08,0x85,0x08,0x83,
+0x10,0x83,0x07,0x83,0x06,0x83,0x03,0x83,0x0a,0x86,0x05,0x83,0x09,0x84,0x07,
+0x83,0x14,0x83,0x09,0x84,0x07,0x83,0x0b,0x83,0x06,0x84,0x09,0x84,0x0d,0x83,
+0x0d,0x84,0x09,0x84,0x0b,0x87,0x0c,0x84,0x07,0x83,0x09,0x84,0x07,0x84,0x0e,
+0x83,0x0e,0x84,0x19,0x83,0x16,0x83,0x11,0x83,0x54,0x83,0x08,0x84,0x08,0x84,
+0x08,0x83,0x09,0x83,0x07,0x84,0x08,0x83,0x08,0x84,0x08,0x83,0x0a,0x81,0x0e,
+0x83,0x0f,0x83,0x08,0x84,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,0x83,0x0c,0x83,
+0x05,0x84,0x10,0x83,0x0a,0x83,0x06,0x83,0x07,0x83,0x05,0x83,0x09,0x83,0x08,
+0x84,0x07,0x84,0x08,0x84,0x07,0x84,0x08,0x84,0x07,0x84,0x09,0x83,0x13,0x84,
+0x08,0x83,0x0b,0x83,0x11,0x84,0x07,0x84,0x0d,0x86,0x0c,0x84,0x05,0x85,0x0c,
+0x83,0x04,0x83,0x0f,0x86,0x0d,0x84,0x18,0x83,0x14,0x83,0x15,0x83,0x7f,0x01,
+0x83,0x04,0x83,0x0c,0x83,0x08,0x84,0x0a,0x82,0x05,0x83,0x04,0x83,0x03,0x84,
+0x06,0x87,0x25,0x83,0x13,0x83,0x2b,0x83,0x57,0x82,0x12,0x85,0x04,0x85,0x10,
+0x83,0x0d,0x83,0x14,0x85,0x04,0x85,0x12,0x83,0x0c,0x84,0x05,0x84,0x0a,0x84,
+0x04,0x85,0x0d,0x83,0x11,0x85,0x04,0x85,0x09,0x81,0x07,0x84,0x26,0x83,0x18,
+0x84,0x21,0x84,0x18,0x83,0x0c,0x82,0x04,0x85,0x01,0x86,0x06,0x83,0x0c,0x84,
+0x05,0x83,0x0a,0x84,0x07,0x85,0x06,0x84,0x06,0x83,0x08,0x85,0x08,0x83,0x14,
+0x83,0x15,0x85,0x07,0x85,0x04,0x83,0x0d,0x83,0x0c,0x83,0x0e,0x84,0x05,0x84,
+0x09,0x83,0x09,0x84,0x08,0x83,0x10,0x83,0x10,0x83,0x03,0x83,0x0b,0x85,0x05,
+0x85,0x06,0x85,0x07,0x83,0x14,0x85,0x06,0x85,0x07,0x83,0x0b,0x83,0x06,0x85,
+0x06,0x85,0x0e,0x83,0x0e,0x85,0x05,0x85,0x0d,0x85,0x0d,0x84,0x07,0x83,0x08,
+0x84,0x09,0x84,0x0d,0x83,0x0d,0x84,0x1a,0x83,0x16,0x83,0x11,0x83,0x54,0x84,
+0x05,0x86,0x08,0x85,0x05,0x85,0x0a,0x84,0x04,0x84,0x09,0x85,0x05,0x85,0x09,
+0x84,0x06,0x83,0x0e,0x83,0x0f,0x85,0x05,0x85,0x08,0x83,0x09,0x83,0x0e,0x83,
+0x17,0x83,0x0c,0x83,0x06,0x84,0x0f,0x83,0x0a,0x83,0x06,0x83,0x07,0x83,0x05,
+0x83,0x09,0x83,0x09,0x84,0x05,0x84,0x09,0x85,0x05,0x85,0x08,0x85,0x05,0x85,
+0x09,0x83,0x14,0x84,0x05,0x85,0x0b,0x84,0x05,0x83,0x09,0x84,0x05,0x85,0x0e,
+0x85,0x0c,0x84,0x05,0x85,0x0b,0x84,0x04,0x84,0x0e,0x86,0x0d,0x83,0x19,0x83,
+0x14,0x83,0x15,0x83,0x57,0x83,0x26,0x83,0x04,0x83,0x0c,0x85,0x05,0x84,0x13,
+0x83,0x02,0x83,0x05,0x91,0x24,0x83,0x13,0x83,0x40,0x83,0x2c,0x83,0x12,0x83,
+0x13,0x8c,0x11,0x83,0x0c,0x90,0x09,0x8c,0x13,0x83,0x0c,0x8c,0x0b,0x8c,0x0e,
+0x83,0x12,0x8c,0x0a,0x8c,0x0f,0x83,0x14,0x83,0x1a,0x82,0x21,0x82,0x1a,0x83,
+0x0c,0x83,0x04,0x83,0x03,0x84,0x07,0x83,0x0d,0x83,0x05,0x90,0x09,0x8e,0x06,
+0x8f,0x09,0x90,0x07,0x83,0x16,0x8f,0x05,0x83,0x0d,0x83,0x0c,0x83,0x0e,0x8c,
+0x0a,0x83,0x0a,0x84,0x07,0x90,0x03,0x83,0x10,0x83,0x03,0x83,0x0b,0x85,0x06,
+0x8e,0x08,0x83,0x15,0x90,0x06,0x83,0x0b,0x83,0x07,0x8e,0x0f,0x83,0x0e,0x8e,
+0x0e,0x85,0x0e,0x83,0x07,0x83,0x08,0x84,0x09,0x84,0x0d,0x83,0x0d,0x92,0x0c,
+0x83,0x16,0x84,0x10,0x83,0x55,0x8a,0x01,0x83,0x08,0x8e,0x0b,0x8b,0x0b,0x8e,
+0x09,0x8e,0x0d,0x83,0x10,0x8e,0x08,0x83,0x09,0x83,0x0e,0x83,0x17,0x83,0x0c,
+0x83,0x06,0x84,0x0f,0x83,0x0a,0x83,0x06,0x83,0x07,0x83,0x05,0x83,0x09,0x83,
+0x09,0x8d,0x09,0x8e,0x0a,0x8e,0x09,0x83,0x14,0x8d,0x0c,0x8c,0x09,0x8e,0x0e,
+0x84,0x0e,0x83,0x05,0x84,0x0b,0x84,0x06,0x84,0x0e,0x85,0x0c,0x8f,0x0e,0x83,
+0x14,0x83,0x15,0x83,0x57,0x83,0x26,0x82,0x05,0x83,0x0d,0x8c,0x15,0x86,0x07,
+0x8b,0x02,0x84,0x23,0x83,0x13,0x83,0x40,0x83,0x2c,0x83,0x12,0x82,0x15,0x8a,
+0x12,0x83,0x0c,0x90,0x0a,0x8a,0x14,0x83,0x0d,0x8a,0x0e,0x89,0x0f,0x83,0x13,
+0x8a,0x0b,0x8a,0x11,0x83,0x14,0x83,0x59,0x83,0x0d,0x82,0x14,0x84,0x0d,0x83,
+0x05,0x8f,0x0b,0x8b,0x08,0x8e,0x0a,0x90,0x07,0x83,0x18,0x8b,0x07,0x83,0x0d,
+0x83,0x09,0x89,0x0c,0x8a,0x0b,0x83,0x0b,0x84,0x06,0x90,0x03,0x83,0x10,0x83,
+0x03,0x83,0x0c,0x84,0x07,0x8b,0x0a,0x83,0x17,0x8a,0x01,0x84,0x05,0x83,0x0b,
+0x83,0x09,0x8b,0x10,0x83,0x10,0x8b,0x0f,0x84,0x0f,0x82,0x09,0x82,0x07,0x84,
+0x0b,0x84,0x0c,0x83,0x0d,0x92,0x0c,0x83,0x17,0x83,0x10,0x83,0x56,0x88,0x02,
+0x83,0x08,0x83,0x01,0x89,0x0d,0x89,0x0d,0x89,0x01,0x83,0x0b,0x8a,0x0f,0x83,
+0x11,0x89,0x01,0x83,0x08,0x83,0x09,0x83,0x0a,0x8b,0x13,0x83,0x0c,0x83,0x07,
+0x84,0x0a,0x8b,0x06,0x83,0x06,0x83,0x07,0x83,0x05,0x83,0x09,0x83,0x0b,0x89,
+0x0b,0x83,0x01,0x89,0x0c,0x89,0x01,0x83,0x09,0x83,0x16,0x8a,0x0e,0x8a,0x0b,
+0x89,0x01,0x83,0x0e,0x84,0x0e,0x83,0x06,0x83,0x0b,0x84,0x06,0x84,0x0e,0x84,
+0x0d,0x8f,0x0e,0x83,0x14,0x83,0x15,0x83,0x57,0x83,0x25,0x83,0x05,0x82,0x0f,
+0x8a,0x17,0x84,0x0a,0x87,0x04,0x84,0x24,0x83,0x11,0x83,0x41,0x83,0x2c,0x83,
+0x11,0x83,0x17,0x86,0x14,0x83,0x0c,0x90,0x0c,0x86,0x16,0x83,0x0f,0x86,0x11,
+0x86,0x11,0x83,0x15,0x86,0x0e,0x87,0x13,0x83,0x14,0x82,0x5a,0x83,0x0d,0x83,
+0x13,0x83,0x0e,0x84,0x04,0x8d,0x0f,0x87,0x0a,0x8c,0x0c,0x90,0x07,0x83,0x1a,
+0x87,0x09,0x83,0x0d,0x83,0x09,0x89,0x0e,0x86,0x0d,0x83,0x0b,0x85,0x05,0x90,
+0x03,0x83,0x10,0x83,0x03,0x83,0x0d,0x83,0x0a,0x86,0x0c,0x83,0x19,0x86,0x04,
+0x84,0x04,0x83,0x0c,0x83,0x0a,0x87,0x12,0x83,0x12,0x87,0x12,0x83,0x10,0x81,
+0x09,0x81,0x07,0x85,0x0b,0x85,0x0b,0x83,0x0d,0x92,0x0c,0x83,0x17,0x84,0x0f,
+0x83,0x57,0x86,0x03,0x83,0x08,0x82,0x04,0x85,0x11,0x86,0x10,0x85,0x04,0x82,
+0x0d,0x86,0x11,0x83,0x13,0x85,0x03,0x83,0x08,0x83,0x09,0x83,0x0a,0x8b,0x13,
+0x83,0x0c,0x83,0x08,0x84,0x09,0x8b,0x06,0x83,0x06,0x83,0x07,0x83,0x05,0x83,
+0x09,0x83,0x0c,0x87,0x0c,0x83,0x03,0x85,0x10,0x85,0x03,0x83,0x09,0x83,0x17,
+0x87,0x11,0x87,0x0f,0x85,0x03,0x83,0x0f,0x83,0x0e,0x82,0x07,0x83,0x0a,0x84,
+0x08,0x84,0x0d,0x84,0x0d,0x8f,0x0e,0x83,0x14,0x83,0x15,0x83,0x7f,0x1c,0x85,
+0x5a,0x83,0x11,0x83,0x41,0x83,0x40,0x83,0x7f,0x7f,0x16,0x83,0x6b,0x83,0x7f,
+0x7f,0x7f,0x17,0x83,0x7f,0x5b,0x83,0x18,0x83,0x0f,0x83,0x25,0x90,0x7f,0x36,
+0x83,0x3f,0x83,0x7e,0x83,0x20,0x83,0x7f,0x30,0x83,0x2b,0x83,0x14,0x83,0x15,
+0x83,0x7f,0x1d,0x83,0x5c,0x83,0x0f,0x83,0x42,0x82,0x41,0x82,0x7f,0x7f,0x17,
+0x82,0x6d,0x84,0x06,0x81,0x7f,0x7f,0x7f,0x0f,0x81,0x7f,0x5c,0x83,0x18,0x83,
+0x0f,0x83,0x25,0x90,0x7f,0x36,0x83,0x3f,0x83,0x7e,0x83,0x20,0x83,0x7f,0x30,
+0x83,0x2b,0x84,0x13,0x83,0x14,0x83,0x7f,0x1e,0x83,0x5c,0x83,0x0f,0x83,0x41,
+0x83,0x7f,0x7f,0x5a,0x82,0x6e,0x8b,0x7f,0x7f,0x7f,0x7f,0x6b,0x83,0x2a,0x83,
+0x25,0x90,0x7f,0x36,0x83,0x3f,0x83,0x7e,0x83,0x20,0x83,0x7f,0x2f,0x84,0x2c,
+0x83,0x13,0x83,0x14,0x83,0x7f,0x1e,0x83,0x5d,0x83,0x0d,0x83,0x42,0x82,0x7f,
+0x7f,0x7f,0x4e,0x86,0x7f,0x7f,0x7f,0x7f,0x6e,0x87,0x23,0x86,0x7f,0x6a,0x83,
+0x3f,0x84,0x7e,0x83,0x20,0x83,0x7f,0x2f,0x83,0x2d,0x84,0x12,0x83,0x13,0x84,
+0x7f,0x7f,0x82,0x0d,0x82,0x43,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x43,0x87,0x23,0x86,0x7f,0x60,0x82,0x07,0x84,0x3e,0x84,0x7f,0x83,0x20,0x83,
+0x7f,0x2e,0x84,0x2e,0x84,0x11,0x83,0x11,0x85,0x7f,0x7f,0x01,0x83,0x0b,0x83,
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x09,0x87,0x23,0x86,0x7f,0x60,
+0x8c,0x3b,0x88,0x7f,0x83,0x20,0x83,0x7f,0x2b,0x86,0x31,0x82,0x11,0x83,0x12,
+0x82,0x7f,0x7f,0x04,0x83,0x09,0x83,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x7f,0x7f,0x1b,0x8b,0x3c,0x87,0x7f,0x01,0x83,0x20,0x83,0x7f,0x2b,0x86,
+0x7f,0x7f,0x60,0x81,0x0b,0x81,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x7f,0x1e,0x87,0x3e,0x85,0x7f,0x03,0x83,0x20,0x83,0x7f,0x2b,0x84,0x7f,
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+0x7f,0x7f,0x4a,
+0x00,
+  }
+};
diff --git a/minuitwrp/truetype.c b/minuitwrp/truetype.c
new file mode 100644
index 0000000..8e0df42
--- /dev/null
+++ b/minuitwrp/truetype.c
@@ -0,0 +1,731 @@
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "minui.h"
+
+#include <cutils/hashmap.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+#include <pixelflinger/pixelflinger.h>
+#include <pthread.h>
+
+#define STRING_CACHE_MAX_ENTRIES 400
+#define STRING_CACHE_TRUNCATE_ENTRIES 150
+
+typedef struct
+{
+    int size;
+    int dpi;
+    char *path;
+} TrueTypeFontKey;
+
+typedef struct
+{
+    int type;
+    int refcount;
+    int size;
+    int dpi;
+    int max_height;
+    int base;
+    FT_Face face;
+    Hashmap *glyph_cache;
+    Hashmap *string_cache;
+    struct StringCacheEntry *string_cache_head;
+    struct StringCacheEntry *string_cache_tail;
+    pthread_mutex_t mutex;
+    TrueTypeFontKey *key;
+} TrueTypeFont;
+
+typedef struct
+{
+    FT_BBox bbox;
+    FT_BitmapGlyph glyph;
+} TrueTypeCacheEntry;
+
+typedef struct
+{
+    char *text;
+    int max_width;
+} StringCacheKey;
+
+struct StringCacheEntry
+{
+    GGLSurface surface;
+    int rendered_len;
+    StringCacheKey *key;
+    struct StringCacheEntry *prev;
+    struct StringCacheEntry *next;
+};
+
+typedef struct StringCacheEntry StringCacheEntry;
+
+typedef struct 
+{
+    FT_Library ft_library;
+    Hashmap *fonts;
+    pthread_mutex_t mutex;
+} FontData;
+
+static FontData font_data = {
+    .ft_library = NULL,
+    .fonts = NULL,
+    .mutex = PTHREAD_MUTEX_INITIALIZER,
+};
+
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+
+// 32bit FNV-1a hash algorithm
+// http://isthe.com/chongo/tech/comp/fnv/#FNV-1a
+static const uint32_t FNV_prime = 16777619U;
+static const uint32_t offset_basis = 2166136261U;
+
+static uint32_t fnv_hash(void *data, uint32_t len)
+{
+    uint8_t *d8 = data;
+    uint32_t *d32 = data;
+    uint32_t i, max;
+    uint32_t hash = offset_basis;
+
+    max = len/4;
+
+    // 32 bit data
+    for(i = 0; i < max; ++i)
+    {
+        hash ^= *d32++;
+        hash *= FNV_prime;
+    }
+
+    // last bits
+    for(i *= 4; i < len; ++i)
+    {
+        hash ^= (uint32_t) d8[i];
+        hash *= FNV_prime;
+    }
+    return hash;
+}
+
+static inline uint32_t fnv_hash_add(uint32_t cur_hash, uint32_t word)
+{
+    cur_hash ^= word;
+    cur_hash *= FNV_prime;
+    return cur_hash;
+}
+
+static bool gr_ttf_string_cache_equals(void *keyA, void *keyB)
+{
+    StringCacheKey *a = keyA;
+    StringCacheKey *b = keyB;
+    return a->max_width == b->max_width && strcmp(a->text, b->text) == 0;
+}
+
+static int gr_ttf_string_cache_hash(void *key)
+{
+    StringCacheKey *k = key;
+    return fnv_hash(k->text, strlen(k->text));
+}
+
+static bool gr_ttf_font_cache_equals(void *keyA, void *keyB)
+{
+    TrueTypeFontKey *a = keyA;
+    TrueTypeFontKey *b = keyB;
+    return (a->size == b->size) && (a->dpi == b->dpi) && !strcmp(a->path, b->path);
+}
+
+static int gr_ttf_font_cache_hash(void *key)
+{
+    TrueTypeFontKey *k = key;
+
+    uint32_t hash = fnv_hash(k->path, strlen(k->path));
+    hash = fnv_hash_add(hash, k->size);
+    hash = fnv_hash_add(hash, k->dpi);
+    return hash;
+}
+
+void *gr_ttf_loadFont(const char *filename, int size, int dpi)
+{
+    int error;
+    TrueTypeFont *res = NULL;
+
+    pthread_mutex_lock(&font_data.mutex);
+
+    if(font_data.fonts)
+    {
+        TrueTypeFontKey k = {
+            .size = size,
+            .dpi = dpi,
+            .path = (char*)filename
+        };
+
+        res = hashmapGet(font_data.fonts, &k);
+        if(res)
+        {
+            ++res->refcount;
+            goto exit;
+        }
+    }
+
+    if(!font_data.ft_library)
+    {
+        error = FT_Init_FreeType(&font_data.ft_library);
+        if(error)
+        {
+            fprintf(stderr, "Failed to init libfreetype! %d\n", error);
+            goto exit;
+        }
+    }
+
+    FT_Face face;
+    error = FT_New_Face(font_data.ft_library, filename, 0, &face);
+    if(error)
+    {
+        fprintf(stderr, "Failed to load truetype face %s: %d\n", filename, error);
+        goto exit;
+    }
+
+    error = FT_Set_Char_Size(face, 0, size*16, dpi, dpi);
+    if(error)
+    {
+         fprintf(stderr, "Failed to set truetype face size to %d, dpi %d: %d\n", size, dpi, error);
+         FT_Done_Face(face);
+         goto exit;
+    }
+
+    res = malloc(sizeof(TrueTypeFont));
+    memset(res, 0, sizeof(TrueTypeFont));
+    res->type = FONT_TYPE_TTF;
+    res->size = size;
+    res->dpi = dpi;
+    res->face = face;
+    res->max_height = -1;
+    res->base = -1;
+    res->refcount = 1;
+    res->glyph_cache = hashmapCreate(32, hashmapIntHash, hashmapIntEquals);
+    res->string_cache = hashmapCreate(128, gr_ttf_string_cache_hash, gr_ttf_string_cache_equals);
+    pthread_mutex_init(&res->mutex, 0);
+
+    if(!font_data.fonts)
+        font_data.fonts = hashmapCreate(4, gr_ttf_font_cache_hash, gr_ttf_font_cache_equals);
+
+    TrueTypeFontKey *key = malloc(sizeof(TrueTypeFontKey));
+    memset(key, 0, sizeof(TrueTypeFontKey));
+    key->path = strdup(filename);
+    key->size = size;
+    key->dpi = dpi;
+
+    res->key = key;
+
+    hashmapPut(font_data.fonts, key, res);
+
+exit:
+    pthread_mutex_unlock(&font_data.mutex);
+    return res;
+}
+
+static bool gr_ttf_freeFontCache(void *key, void *value, void *context)
+{
+    TrueTypeCacheEntry *e = value;
+    FT_Done_Glyph((FT_Glyph)e->glyph);
+    free(e);
+    free(key);
+    return true;
+}
+
+static bool gr_ttf_freeStringCache(void *key, void *value, void *context)
+{
+    StringCacheKey *k = key;
+    free(k->text);
+    free(k);
+
+    StringCacheEntry *e = value;
+    free(e->surface.data);
+    free(e);
+    return true;
+}
+
+void gr_ttf_freeFont(void *font)
+{
+    pthread_mutex_lock(&font_data.mutex);
+
+    TrueTypeFont *d = font;
+
+    if(--d->refcount == 0)
+    {
+        hashmapRemove(font_data.fonts, d->key);
+
+        if(hashmapSize(font_data.fonts) == 0)
+        {
+            hashmapFree(font_data.fonts);
+            font_data.fonts = NULL;
+        }
+
+        free(d->key->path);
+        free(d->key);
+
+        FT_Done_Face(d->face);
+        hashmapForEach(d->string_cache, gr_ttf_freeStringCache, NULL);
+        hashmapFree(d->string_cache);
+        hashmapForEach(d->glyph_cache, gr_ttf_freeFontCache, NULL);
+        hashmapFree(d->glyph_cache);
+        pthread_mutex_destroy(&d->mutex);
+        free(d);
+    }
+
+    pthread_mutex_unlock(&font_data.mutex);
+}
+
+static TrueTypeCacheEntry *gr_ttf_glyph_cache_peek(TrueTypeFont *font, int char_index)
+{
+    return hashmapGet(font->glyph_cache, &char_index);
+}
+
+static TrueTypeCacheEntry *gr_ttf_glyph_cache_get(TrueTypeFont *font, int char_index)
+{
+    TrueTypeCacheEntry *res = hashmapGet(font->glyph_cache, &char_index);
+    if(!res)
+    {
+        int error = FT_Load_Glyph(font->face, char_index, FT_LOAD_RENDER);
+        if(error)
+        {
+            fprintf(stderr, "Failed to load glyph idx %d: %d\n", char_index, error);
+            return NULL;
+        }
+
+        FT_BitmapGlyph glyph;
+        error = FT_Get_Glyph(font->face->glyph, (FT_Glyph*)&glyph);
+        if(error)
+        {
+            fprintf(stderr, "Failed to copy glyph %d: %d\n", char_index, error);
+            return NULL;
+        }
+
+        res = malloc(sizeof(TrueTypeCacheEntry));
+        memset(res, 0, sizeof(TrueTypeCacheEntry));
+        res->glyph = glyph;
+        FT_Glyph_Get_CBox((FT_Glyph)glyph, FT_GLYPH_BBOX_PIXELS, &res->bbox);
+
+        int *key = malloc(sizeof(int));
+        *key = char_index;
+
+        hashmapPut(font->glyph_cache, key, res);
+    }
+
+    return res;
+}
+
+static int gr_ttf_copy_glyph_to_surface(GGLSurface *dest, FT_BitmapGlyph glyph, int offX, int offY, int base)
+{
+    int y;
+    uint8_t *src_itr = glyph->bitmap.buffer;
+    uint8_t *dest_itr = dest->data;
+
+    if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
+    {
+        fprintf(stderr, "Unsupported pixel mode in FT_BitmapGlyph %d\n", glyph->bitmap.pixel_mode);
+        return -1;
+    }
+
+    dest_itr += (offY + base - glyph->top)*dest->stride + (offX + glyph->left);
+
+    for(y = 0; y < glyph->bitmap.rows; ++y)
+    {
+        memcpy(dest_itr, src_itr, glyph->bitmap.width);
+        src_itr += glyph->bitmap.pitch;
+        dest_itr += dest->stride;
+    }
+    return 0;
+}
+
+static int gr_ttf_render_text(TrueTypeFont *font, GGLSurface *surface, const char *text, int max_width)
+{
+    TrueTypeFont *f = font;
+    TrueTypeCacheEntry *ent;
+    int max_len = 0, total_w = 0;
+    char c;
+    int i, x, diff, char_idx, prev_idx = 0;
+    int height, base;
+    FT_Vector delta;
+    uint8_t *data = NULL;
+    const char *text_itr = text;
+
+    while((c = *text_itr++))
+    {
+        char_idx = FT_Get_Char_Index(f->face, c);
+        ent = gr_ttf_glyph_cache_get(f, char_idx);
+        if(ent)
+        {
+            diff = ent->glyph->root.advance.x >> 16;
+
+            if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
+            {
+                FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
+                diff += delta.x >> 6;
+            }
+
+            if(max_width != -1 && total_w + diff > max_width)
+                break;
+
+            total_w += diff;
+        }
+        prev_idx = char_idx;
+        ++max_len;
+    }
+
+    if(font->max_height == -1)
+        gr_ttf_getMaxFontHeight(font);
+
+    if(font->max_height == -1)
+        return -1;
+
+    height = font->max_height;
+
+    data = malloc(total_w*height);
+    memset(data, 0, total_w*height);
+    x = 0;
+    prev_idx = 0;
+
+    surface->version = sizeof(*surface);
+    surface->width = total_w;
+    surface->height = height;
+    surface->stride = total_w;
+    surface->data = (void*)data;
+    surface->format = GGL_PIXEL_FORMAT_A_8;
+
+    for(i = 0; i < max_len; ++i)
+    {
+        char_idx = FT_Get_Char_Index(f->face, text[i]);
+        if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
+        {
+            FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
+            x += delta.x >> 6;
+        }
+
+        ent = gr_ttf_glyph_cache_get(f, char_idx);
+        if(ent)
+        {
+            gr_ttf_copy_glyph_to_surface(surface, ent->glyph, x, 0, font->base);
+            x += ent->glyph->root.advance.x >> 16;
+        }
+
+        prev_idx = char_idx;
+    }
+
+    return max_len;
+}
+
+static StringCacheEntry *gr_ttf_string_cache_peek(TrueTypeFont *font, const char *text, int max_width)
+{
+    StringCacheEntry *res;
+    StringCacheKey k = {
+        .text = (char*)text,
+        .max_width = max_width
+    };
+
+    return hashmapGet(font->string_cache, &k);
+}
+
+static StringCacheEntry *gr_ttf_string_cache_get(TrueTypeFont *font, const char *text, int max_width)
+{
+    StringCacheEntry *res;
+    StringCacheKey k = {
+        .text = (char*)text,
+        .max_width = max_width
+    };
+
+    res = hashmapGet(font->string_cache, &k);
+    if(!res)
+    {
+        res = malloc(sizeof(StringCacheEntry));
+        memset(res, 0, sizeof(StringCacheEntry));
+        res->rendered_len = gr_ttf_render_text(font, &res->surface, text, max_width);
+        if(res->rendered_len < 0)
+        {
+            free(res);
+            return NULL;
+        }
+
+        StringCacheKey *new_key = malloc(sizeof(StringCacheKey));
+        memset(new_key, 0, sizeof(StringCacheKey));
+        new_key->max_width = max_width;
+        new_key->text = strdup(text);
+
+        res->key = new_key;
+
+        if(font->string_cache_tail)
+        {
+            res->prev = font->string_cache_tail;
+            res->prev->next = res;
+        }
+        else
+            font->string_cache_head = res;
+        font->string_cache_tail = res;
+
+        hashmapPut(font->string_cache, new_key, res);
+    }
+    else if(res->next)
+    {
+        // move this entry to the tail of the linked list
+        // if it isn't already there
+        if(res->prev)
+            res->prev->next = res->next;
+
+        res->next->prev = res->prev;
+
+        if(!res->prev)
+            font->string_cache_head = res->next;
+
+        res->next = NULL;
+        res->prev = font->string_cache_tail;
+        res->prev->next = res;
+        font->string_cache_tail = res;
+
+        // truncate old entries
+        if(hashmapSize(font->string_cache) >= STRING_CACHE_MAX_ENTRIES)
+        {
+            printf("Truncating string cache entries.\n");
+            int i;
+            StringCacheEntry *ent;
+            for(i = 0; i < STRING_CACHE_TRUNCATE_ENTRIES; ++i)
+            {
+                ent = font->string_cache_head;
+                font->string_cache_head = ent->next;
+                font->string_cache_head->prev = NULL;
+
+                hashmapRemove(font->string_cache, ent->key);
+
+                gr_ttf_freeStringCache(ent->key, ent, NULL);
+            }
+        }
+    }
+    return res;
+}
+
+int gr_ttf_measureEx(const char *s, void *font)
+{
+    TrueTypeFont *f = font;
+    int res = -1;
+
+    pthread_mutex_lock(&f->mutex);
+    StringCacheEntry *e = gr_ttf_string_cache_get(font, s, -1);
+    if(e)
+        res = e->surface.width;
+    pthread_mutex_unlock(&f->mutex);
+
+    return res;
+}
+
+int gr_ttf_maxExW(const char *s, void *font, int max_width)
+{
+    TrueTypeFont *f = font;
+    TrueTypeCacheEntry *ent;
+    int max_len = 0, total_w = 0;
+    char c;
+    int char_idx, prev_idx = 0;
+    FT_Vector delta;
+    StringCacheEntry *e;
+
+    pthread_mutex_lock(&f->mutex);
+
+    e = gr_ttf_string_cache_peek(font, s, max_width);
+    if(e)
+    {
+        max_len = e->rendered_len;
+        pthread_mutex_unlock(&f->mutex);
+        return max_len;
+    }
+
+    for(; (c = *s++); ++max_len)
+    {
+        char_idx = FT_Get_Char_Index(f->face, c);
+        if(FT_HAS_KERNING(f->face) && prev_idx && char_idx)
+        {
+            FT_Get_Kerning(f->face, prev_idx, char_idx, FT_KERNING_DEFAULT, &delta);
+            total_w += delta.x >> 6;
+        }
+        prev_idx = char_idx;
+
+        if(total_w > max_width)
+            break;
+
+        ent = gr_ttf_glyph_cache_get(f, char_idx);
+        if(!ent)
+            continue;
+
+        total_w += ent->glyph->root.advance.x >> 16;
+    }
+    pthread_mutex_unlock(&f->mutex);
+    return max_len > 0 ? max_len - 1 : 0;
+}
+
+int gr_ttf_textExWH(void *context, int x, int y, const char *s, void *pFont, int max_width, int max_height)
+{
+    GGLContext *gl = context;
+    TrueTypeFont *font = pFont;
+
+    // not actualy max width, but max_width + x
+    if(max_width != -1)
+    {
+        max_width -= x;
+        if(max_width <= 0)
+            return 0;
+    }
+
+    pthread_mutex_lock(&font->mutex);
+
+    StringCacheEntry *e = gr_ttf_string_cache_get(font, s, max_width);
+    if(!e)
+    {
+        pthread_mutex_unlock(&font->mutex);
+        return -1;
+    }
+
+    int y_bottom = y + e->surface.height;
+    int res = e->rendered_len;
+
+    if(max_height != -1 && max_height < y_bottom)
+    {
+        y_bottom = max_height;
+        if(y_bottom <= y)
+        {
+            pthread_mutex_unlock(&font->mutex);
+            return 0;
+        }
+    }
+
+    gl->bindTexture(gl, &e->surface);
+    gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
+    gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+    gl->enable(gl, GGL_TEXTURE_2D);
+
+    gl->texCoord2i(gl, -x, -y);
+    gl->recti(gl, x, y, x + e->surface.width, y_bottom);
+
+    pthread_mutex_unlock(&font->mutex);
+    return res;
+}
+
+int gr_ttf_getMaxFontHeight(void *font)
+{
+    int res;
+    TrueTypeFont *f = font;
+
+    pthread_mutex_lock(&f->mutex);
+
+    if(f->max_height == -1)
+    {
+        char c;
+        int char_idx;
+        int error;
+        FT_Glyph glyph;
+        FT_BBox bbox;
+        FT_BBox bbox_glyph;
+        TrueTypeCacheEntry *ent;
+
+        bbox.yMin = bbox_glyph.yMin = LONG_MAX;
+        bbox.yMax = bbox_glyph.yMax = LONG_MIN;
+
+        for(c = '!'; c <= '~'; ++c)
+        {
+            char_idx = FT_Get_Char_Index(f->face, c);
+            ent = gr_ttf_glyph_cache_peek(f, char_idx);
+            if(ent)
+            {
+                bbox.yMin = MIN(bbox.yMin, ent->bbox.yMin);
+                bbox.yMax = MAX(bbox.yMax, ent->bbox.yMax);
+            }
+            else
+            {
+                error = FT_Load_Glyph(f->face, char_idx, 0);
+                if(error)
+                    continue;
+
+                error = FT_Get_Glyph(f->face->glyph, &glyph);
+                if(error)
+                    continue;
+
+                FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox_glyph);
+                bbox.yMin = MIN(bbox.yMin, bbox_glyph.yMin);
+                bbox.yMax = MAX(bbox.yMax, bbox_glyph.yMax);
+
+                FT_Done_Glyph(glyph);
+            }
+        }
+
+        if(bbox.yMin > bbox.yMax)
+            bbox.yMin = bbox.yMax = 0;
+
+        f->max_height = bbox.yMax - bbox.yMin;
+        f->base = bbox.yMax;
+
+        // FIXME: twrp fonts have some padding on top, I'll add it here
+        // Should be fixed in the themes
+        f->max_height += f->size / 4;
+        f->base += f->size / 4;
+    }
+
+    res = f->max_height;
+
+    pthread_mutex_unlock(&f->mutex);
+    return res;
+}
+
+static bool gr_ttf_dump_stats_count_string_cache(void *key, void *value, void *context)
+{
+    int *string_cache_size = context;
+    StringCacheEntry *e = value;
+    *string_cache_size += e->surface.height*e->surface.width + sizeof(StringCacheEntry);
+    return true;
+}
+
+static bool gr_ttf_dump_stats_font(void *key, void *value, void *context)
+{
+    TrueTypeFontKey *k = key;
+    TrueTypeFont *f = value;
+    int *total_string_cache_size = context;
+    int string_cache_size = 0;
+
+    pthread_mutex_lock(&f->mutex);
+
+    hashmapForEach(f->string_cache, gr_ttf_dump_stats_count_string_cache, &string_cache_size);
+
+    printf("  Font %s (size %d, dpi %d):\n"
+            "    refcount: %d\n"
+            "    max_height: %d\n"
+            "    base: %d\n"
+            "    glyph_cache: %d entries\n"
+            "    string_cache: %d entries (%.2f kB)\n",
+            k->path, k->size, k->dpi,
+            f->refcount, f->max_height, f->base,
+            hashmapSize(f->glyph_cache),
+            hashmapSize(f->string_cache), ((double)string_cache_size)/1024);
+
+    pthread_mutex_unlock(&f->mutex);
+
+    *total_string_cache_size += string_cache_size;
+    return true;
+}
+
+void gr_ttf_dump_stats(void)
+{
+    pthread_mutex_lock(&font_data.mutex);
+
+    printf("TrueType fonts system stats: ");
+    if(!font_data.fonts)
+        printf("no truetype fonts loaded.\n");
+    else
+    {
+        int total_string_cache_size = 0;
+        printf("%d fonts loaded.\n", hashmapSize(font_data.fonts));
+        hashmapForEach(font_data.fonts, gr_ttf_dump_stats_font, &total_string_cache_size);
+        printf("  Total string cache size: %.2f kB\n", ((double)total_string_cache_size)/1024);
+    }
+
+    pthread_mutex_unlock(&font_data.mutex);
+}
diff --git a/minzip/Android.mk b/minzip/Android.mk
index 045f355..68485ab 100644
--- a/minzip/Android.mk
+++ b/minzip/Android.mk
@@ -17,5 +17,33 @@
 LOCAL_MODULE := libminzip
 
 LOCAL_CFLAGS += -Wall
+LOCAL_SHARED_LIBRARIES := libz
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	Hash.c \
+	SysUtil.c \
+	DirUtil.c \
+	Inlines.c \
+	Zip.c
+
+LOCAL_C_INCLUDES += \
+	external/zlib \
+	external/safe-iop/include
+
+ifeq ($(TWHAVE_SELINUX),true)
+LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_STATIC_LIBRARIES += libselinux
+LOCAL_CFLAGS += -DHAVE_SELINUX
+endif
+
+LOCAL_MODULE := libminzip
+
+LOCAL_CFLAGS += -Wall
+LOCAL_STATIC_LIBRARIES := libz
 
 include $(BUILD_STATIC_LIBRARY)
diff --git a/minzipold/Android.mk b/minzipold/Android.mk
new file mode 100644
index 0000000..0435c6a
--- /dev/null
+++ b/minzipold/Android.mk
@@ -0,0 +1,53 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	Hash.c \
+	SysUtil.c \
+	DirUtil.c \
+	Inlines.c \
+	Zip.c
+
+LOCAL_C_INCLUDES += \
+	external/zlib \
+	external/safe-iop/include
+
+ifeq ($(HAVE_SELINUX),true)
+LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_STATIC_LIBRARIES += libselinux
+LOCAL_CFLAGS += -DHAVE_SELINUX
+endif
+
+LOCAL_MODULE := libminzip
+
+LOCAL_CFLAGS += -Wall
+LOCAL_SHARED_LIBRARIES := libz
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	Hash.c \
+	SysUtil.c \
+	DirUtil.c \
+	Inlines.c \
+	Zip.c
+
+LOCAL_C_INCLUDES += \
+	external/zlib \
+	external/safe-iop/include
+
+ifeq ($(HAVE_SELINUX),true)
+LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_STATIC_LIBRARIES += libselinux
+LOCAL_CFLAGS += -DHAVE_SELINUX
+endif
+
+LOCAL_MODULE := libminzip
+
+LOCAL_CFLAGS += -Wall
+LOCAL_STATIC_LIBRARIES := libz
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/minzipold/Bits.h b/minzipold/Bits.h
new file mode 100644
index 0000000..f96e6c4
--- /dev/null
+++ b/minzipold/Bits.h
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Some handy functions for manipulating bits and bytes.
+ */
+#ifndef _MINZIP_BITS
+#define _MINZIP_BITS
+
+#include "inline_magic.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Get 1 byte.  (Included to make the code more legible.)
+ */
+INLINE unsigned char get1(unsigned const char* pSrc)
+{
+    return *pSrc;
+}
+
+/*
+ * Get 2 big-endian bytes.
+ */
+INLINE unsigned short get2BE(unsigned char const* pSrc)
+{
+    unsigned short result;
+
+    result = *pSrc++ << 8;
+    result |= *pSrc++;
+
+    return result;
+}
+
+/*
+ * Get 4 big-endian bytes.
+ */
+INLINE unsigned int get4BE(unsigned char const* pSrc)
+{
+    unsigned int result;
+
+    result = *pSrc++ << 24;
+    result |= *pSrc++ << 16;
+    result |= *pSrc++ << 8;
+    result |= *pSrc++;
+
+    return result;
+}
+
+/*
+ * Get 8 big-endian bytes.
+ */
+INLINE unsigned long long get8BE(unsigned char const* pSrc)
+{
+    unsigned long long result;
+
+    result = (unsigned long long) *pSrc++ << 56;
+    result |= (unsigned long long) *pSrc++ << 48;
+    result |= (unsigned long long) *pSrc++ << 40;
+    result |= (unsigned long long) *pSrc++ << 32;
+    result |= (unsigned long long) *pSrc++ << 24;
+    result |= (unsigned long long) *pSrc++ << 16;
+    result |= (unsigned long long) *pSrc++ << 8;
+    result |= (unsigned long long) *pSrc++;
+
+    return result;
+}
+
+/*
+ * Get 2 little-endian bytes.
+ */
+INLINE unsigned short get2LE(unsigned char const* pSrc)
+{
+    unsigned short result;
+
+    result = *pSrc++;
+    result |= *pSrc++ << 8;
+
+    return result;
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+INLINE unsigned int get4LE(unsigned char const* pSrc)
+{
+    unsigned int result;
+
+    result = *pSrc++;
+    result |= *pSrc++ << 8;
+    result |= *pSrc++ << 16;
+    result |= *pSrc++ << 24;
+
+    return result;
+}
+
+/*
+ * Get 8 little-endian bytes.
+ */
+INLINE unsigned long long get8LE(unsigned char const* pSrc)
+{
+    unsigned long long result;
+
+    result = (unsigned long long) *pSrc++;
+    result |= (unsigned long long) *pSrc++ << 8;
+    result |= (unsigned long long) *pSrc++ << 16;
+    result |= (unsigned long long) *pSrc++ << 24;
+    result |= (unsigned long long) *pSrc++ << 32;
+    result |= (unsigned long long) *pSrc++ << 40;
+    result |= (unsigned long long) *pSrc++ << 48;
+    result |= (unsigned long long) *pSrc++ << 56;
+
+    return result;
+}
+
+/*
+ * Grab 1 byte and advance the data pointer.
+ */
+INLINE unsigned char read1(unsigned const char** ppSrc)
+{
+    return *(*ppSrc)++;
+}
+
+/*
+ * Grab 2 big-endian bytes and advance the data pointer.
+ */
+INLINE unsigned short read2BE(unsigned char const** ppSrc)
+{
+    unsigned short result;
+
+    result = *(*ppSrc)++ << 8;
+    result |= *(*ppSrc)++;
+
+    return result;
+}
+
+/*
+ * Grab 4 big-endian bytes and advance the data pointer.
+ */
+INLINE unsigned int read4BE(unsigned char const** ppSrc)
+{
+    unsigned int result;
+
+    result = *(*ppSrc)++ << 24;
+    result |= *(*ppSrc)++ << 16;
+    result |= *(*ppSrc)++ << 8;
+    result |= *(*ppSrc)++;
+
+    return result;
+}
+
+/*
+ * Get 8 big-endian bytes.
+ */
+INLINE unsigned long long read8BE(unsigned char const** ppSrc)
+{
+    unsigned long long result;
+
+    result = (unsigned long long) *(*ppSrc)++ << 56;
+    result |= (unsigned long long) *(*ppSrc)++ << 48;
+    result |= (unsigned long long) *(*ppSrc)++ << 40;
+    result |= (unsigned long long) *(*ppSrc)++ << 32;
+    result |= (unsigned long long) *(*ppSrc)++ << 24;
+    result |= (unsigned long long) *(*ppSrc)++ << 16;
+    result |= (unsigned long long) *(*ppSrc)++ << 8;
+    result |= (unsigned long long) *(*ppSrc)++;
+
+    return result;
+}
+
+/*
+ * Grab 2 little-endian bytes and advance the data pointer.
+ */
+INLINE unsigned short read2LE(unsigned char const** ppSrc)
+{
+    unsigned short result;
+
+    result = *(*ppSrc)++;
+    result |= *(*ppSrc)++ << 8;
+
+    return result;
+}
+
+/*
+ * Grab 4 little-endian bytes and advance the data pointer.
+ */
+INLINE unsigned int read4LE(unsigned char const** ppSrc)
+{
+    unsigned int result;
+
+    result = *(*ppSrc)++;
+    result |= *(*ppSrc)++ << 8;
+    result |= *(*ppSrc)++ << 16;
+    result |= *(*ppSrc)++ << 24;
+
+    return result;
+}
+
+/*
+ * Get 8 little-endian bytes.
+ */
+INLINE unsigned long long read8LE(unsigned char const** ppSrc)
+{
+    unsigned long long result;
+
+    result = (unsigned long long) *(*ppSrc)++;
+    result |= (unsigned long long) *(*ppSrc)++ << 8;
+    result |= (unsigned long long) *(*ppSrc)++ << 16;
+    result |= (unsigned long long) *(*ppSrc)++ << 24;
+    result |= (unsigned long long) *(*ppSrc)++ << 32;
+    result |= (unsigned long long) *(*ppSrc)++ << 40;
+    result |= (unsigned long long) *(*ppSrc)++ << 48;
+    result |= (unsigned long long) *(*ppSrc)++ << 56;
+
+    return result;
+}
+
+/*
+ * Skip over a UTF-8 string.
+ */
+INLINE void skipUtf8String(unsigned char const** ppSrc)
+{
+    unsigned int length = read4BE(ppSrc);
+
+    (*ppSrc) += length;
+}
+
+/*
+ * Read a UTF-8 string into a fixed-size buffer, and null-terminate it.
+ *
+ * Returns the length of the original string.
+ */
+INLINE int readUtf8String(unsigned char const** ppSrc, char* buf, size_t bufLen)
+{
+    unsigned int length = read4BE(ppSrc);
+    size_t copyLen = (length < bufLen) ? length : bufLen-1;
+
+    memcpy(buf, *ppSrc, copyLen);
+    buf[copyLen] = '\0';
+
+    (*ppSrc) += length;
+    return length;
+}
+
+/*
+ * Read a UTF-8 string into newly-allocated storage, and null-terminate it.
+ *
+ * Returns the string and its length.  (The latter is probably unnecessary
+ * for the way we're using UTF8.)
+ */
+INLINE char* readNewUtf8String(unsigned char const** ppSrc, size_t* pLength)
+{
+    unsigned int length = read4BE(ppSrc);
+    char* buf;
+
+    buf = (char*) malloc(length+1);
+
+    memcpy(buf, *ppSrc, length);
+    buf[length] = '\0';
+
+    (*ppSrc) += length;
+
+    *pLength = length;
+    return buf;
+}
+
+
+/*
+ * Set 1 byte.  (Included to make the code more legible.)
+ */
+INLINE void set1(unsigned char* buf, unsigned char val)
+{
+    *buf = (unsigned char)(val);
+}
+
+/*
+ * Set 2 big-endian bytes.
+ */
+INLINE void set2BE(unsigned char* buf, unsigned short val)
+{
+    *buf++ = (unsigned char)(val >> 8);
+    *buf = (unsigned char)(val);
+}
+
+/*
+ * Set 4 big-endian bytes.
+ */
+INLINE void set4BE(unsigned char* buf, unsigned int val)
+{
+    *buf++ = (unsigned char)(val >> 24);
+    *buf++ = (unsigned char)(val >> 16);
+    *buf++ = (unsigned char)(val >> 8);
+    *buf = (unsigned char)(val);
+}
+
+/*
+ * Set 8 big-endian bytes.
+ */
+INLINE void set8BE(unsigned char* buf, unsigned long long val)
+{
+    *buf++ = (unsigned char)(val >> 56);
+    *buf++ = (unsigned char)(val >> 48);
+    *buf++ = (unsigned char)(val >> 40);
+    *buf++ = (unsigned char)(val >> 32);
+    *buf++ = (unsigned char)(val >> 24);
+    *buf++ = (unsigned char)(val >> 16);
+    *buf++ = (unsigned char)(val >> 8);
+    *buf = (unsigned char)(val);
+}
+
+/*
+ * Set 2 little-endian bytes.
+ */
+INLINE void set2LE(unsigned char* buf, unsigned short val)
+{
+    *buf++ = (unsigned char)(val);
+    *buf = (unsigned char)(val >> 8);
+}
+
+/*
+ * Set 4 little-endian bytes.
+ */
+INLINE void set4LE(unsigned char* buf, unsigned int val)
+{
+    *buf++ = (unsigned char)(val);
+    *buf++ = (unsigned char)(val >> 8);
+    *buf++ = (unsigned char)(val >> 16);
+    *buf = (unsigned char)(val >> 24);
+}
+
+/*
+ * Set 8 little-endian bytes.
+ */
+INLINE void set8LE(unsigned char* buf, unsigned long long val)
+{
+    *buf++ = (unsigned char)(val);
+    *buf++ = (unsigned char)(val >> 8);
+    *buf++ = (unsigned char)(val >> 16);
+    *buf++ = (unsigned char)(val >> 24);
+    *buf++ = (unsigned char)(val >> 32);
+    *buf++ = (unsigned char)(val >> 40);
+    *buf++ = (unsigned char)(val >> 48);
+    *buf = (unsigned char)(val >> 56);
+}
+
+/*
+ * Stuff a UTF-8 string into the buffer.
+ */
+INLINE void setUtf8String(unsigned char* buf, const unsigned char* str)
+{
+    unsigned int strLen = strlen((const char*)str);
+
+    set4BE(buf, strLen);
+    memcpy(buf + sizeof(unsigned int), str, strLen);
+}
+
+#endif /*_MINZIP_BITS*/
diff --git a/minzipold/DirUtil.c b/minzipold/DirUtil.c
new file mode 100644
index 0000000..0d49b57
--- /dev/null
+++ b/minzipold/DirUtil.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include "DirUtil.h"
+
+typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
+
+static DirStatus
+getPathDirStatus(const char *path)
+{
+    struct stat st;
+    int err;
+
+    err = stat(path, &st);
+    if (err == 0) {
+        /* Something's there; make sure it's a directory.
+         */
+        if (S_ISDIR(st.st_mode)) {
+            return DDIR;
+        }
+        errno = ENOTDIR;
+        return DILLEGAL;
+    } else if (errno != ENOENT) {
+        /* Something went wrong, or something in the path
+         * is bad.  Can't do anything in this situation.
+         */
+        return DILLEGAL;
+    }
+    return DMISSING;
+}
+
+int
+dirCreateHierarchy(const char *path, int mode,
+        const struct utimbuf *timestamp, bool stripFileName,
+        struct selabel_handle *sehnd)
+{
+    DirStatus ds;
+
+    /* Check for an empty string before we bother
+     * making any syscalls.
+     */
+    if (path[0] == '\0') {
+        errno = ENOENT;
+        return -1;
+    }
+
+    /* Allocate a path that we can modify; stick a slash on
+     * the end to make things easier.
+     */
+    size_t pathLen = strlen(path);
+    char *cpath = (char *)malloc(pathLen + 2);
+    if (cpath == NULL) {
+        errno = ENOMEM;
+        return -1;
+    }
+    memcpy(cpath, path, pathLen);
+    if (stripFileName) {
+        /* Strip everything after the last slash.
+         */
+        char *c = cpath + pathLen - 1;
+        while (c != cpath && *c != '/') {
+            c--;
+        }
+        if (c == cpath) {
+//xxx test this path
+            /* No directory component.  Act like the path was empty.
+             */
+            errno = ENOENT;
+            free(cpath);
+            return -1;
+        }
+        c[1] = '\0';    // Terminate after the slash we found.
+    } else {
+        /* Make sure that the path ends in a slash.
+         */
+        cpath[pathLen] = '/';
+        cpath[pathLen + 1] = '\0';
+    }
+
+    /* See if it already exists.
+     */
+    ds = getPathDirStatus(cpath);
+    if (ds == DDIR) {
+        return 0;
+    } else if (ds == DILLEGAL) {
+        return -1;
+    }
+
+    /* Walk up the path from the root and make each level.
+     * If a directory already exists, no big deal.
+     */
+    char *p = cpath;
+    while (*p != '\0') {
+        /* Skip any slashes, watching out for the end of the string.
+         */
+        while (*p != '\0' && *p == '/') {
+            p++;
+        }
+        if (*p == '\0') {
+            break;
+        }
+
+        /* Find the end of the next path component.
+         * We know that we'll see a slash before the NUL,
+         * because we added it, above.
+         */
+        while (*p != '/') {
+            p++;
+        }
+        *p = '\0';
+
+        /* Check this part of the path and make a new directory
+         * if necessary.
+         */
+        ds = getPathDirStatus(cpath);
+        if (ds == DILLEGAL) {
+            /* Could happen if some other process/thread is
+             * messing with the filesystem.
+             */
+            free(cpath);
+            return -1;
+        } else if (ds == DMISSING) {
+            int err;
+
+#ifdef HAVE_SELINUX
+            char *secontext = NULL;
+
+            if (sehnd) {
+                selabel_lookup(sehnd, &secontext, cpath, mode);
+                setfscreatecon(secontext);
+            }
+#endif
+
+            err = mkdir(cpath, mode);
+
+#ifdef HAVE_SELINUX
+
+            if (secontext) {
+                freecon(secontext);
+                setfscreatecon(NULL);
+            }
+#endif
+
+            if (err != 0) {
+                free(cpath);
+                return -1;
+            }
+            if (timestamp != NULL && utime(cpath, timestamp)) {
+                free(cpath);
+                return -1;
+            }
+        }
+        // else, this directory already exists.
+        
+        /* Repair the path and continue.
+         */
+        *p = '/';
+    }
+    free(cpath);
+
+    return 0;
+}
+
+int
+dirUnlinkHierarchy(const char *path)
+{
+    struct stat st;
+    DIR *dir;
+    struct dirent *de;
+    int fail = 0;
+
+    /* is it a file or directory? */
+    if (lstat(path, &st) < 0) {
+        return -1;
+    }
+
+    /* a file, so unlink it */
+    if (!S_ISDIR(st.st_mode)) {
+        return unlink(path);
+    }
+
+    /* a directory, so open handle */
+    dir = opendir(path);
+    if (dir == NULL) {
+        return -1;
+    }
+
+    /* recurse over components */
+    errno = 0;
+    while ((de = readdir(dir)) != NULL) {
+//TODO: don't blow the stack
+        char dn[PATH_MAX];
+        if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
+            continue;
+        }
+        snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
+        if (dirUnlinkHierarchy(dn) < 0) {
+            fail = 1;
+            break;
+        }
+        errno = 0;
+    }
+    /* in case readdir or unlink_recursive failed */
+    if (fail || errno < 0) {
+        int save = errno;
+        closedir(dir);
+        errno = save;
+        return -1;
+    }
+
+    /* close directory handle */
+    if (closedir(dir) < 0) {
+        return -1;
+    }
+
+    /* delete target directory */
+    return rmdir(path);
+}
+
+int
+dirSetHierarchyPermissions(const char *path,
+        int uid, int gid, int dirMode, int fileMode)
+{
+    struct stat st;
+    if (lstat(path, &st)) {
+        return -1;
+    }
+
+    /* ignore symlinks */
+    if (S_ISLNK(st.st_mode)) {
+        return 0;
+    }
+
+    /* directories and files get different permissions */
+    if (chown(path, uid, gid) ||
+        chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) {
+        return -1;
+    }
+
+    /* recurse over directory components */
+    if (S_ISDIR(st.st_mode)) {
+        DIR *dir = opendir(path);
+        if (dir == NULL) {
+            return -1;
+        }
+
+        errno = 0;
+        const struct dirent *de;
+        while (errno == 0 && (de = readdir(dir)) != NULL) {
+            if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
+                continue;
+            }
+
+            char dn[PATH_MAX];
+            snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
+            if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) {
+                errno = 0;
+            } else if (errno == 0) {
+                errno = -1;
+            }
+        }
+
+        if (errno != 0) {
+            int save = errno;
+            closedir(dir);
+            errno = save;
+            return -1;
+        }
+
+        if (closedir(dir)) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
diff --git a/minzipold/DirUtil.h b/minzipold/DirUtil.h
new file mode 100644
index 0000000..f8be640
--- /dev/null
+++ b/minzipold/DirUtil.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MINZIP_DIRUTIL_H_
+#define MINZIP_DIRUTIL_H_
+
+#include <stdbool.h>
+#include <utime.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#else
+struct selabel_handle;
+#endif
+
+/* Like "mkdir -p", try to guarantee that all directories
+ * specified in path are present, creating as many directories
+ * as necessary.  The specified mode is passed to all mkdir
+ * calls;  no modifications are made to umask.
+ *
+ * If stripFileName is set, everything after the final '/'
+ * is stripped before creating the directory hierarchy.
+ *
+ * If timestamp is non-NULL, new directories will be timestamped accordingly.
+ *
+ * Returns 0 on success; returns -1 (and sets errno) on failure
+ * (usually if some element of path is not a directory).
+ */
+int dirCreateHierarchy(const char *path, int mode,
+        const struct utimbuf *timestamp, bool stripFileName,
+        struct selabel_handle* sehnd);
+
+/* rm -rf <path>
+ */
+int dirUnlinkHierarchy(const char *path);
+
+/* chown -R <uid>:<gid> <path>
+ * chmod -R <mode> <path>
+ *
+ * Sets directories to <dirMode> and files to <fileMode>.  Skips symlinks.
+ */
+int dirSetHierarchyPermissions(const char *path,
+         int uid, int gid, int dirMode, int fileMode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // MINZIP_DIRUTIL_H_
diff --git a/minzipold/Hash.c b/minzipold/Hash.c
new file mode 100644
index 0000000..8c6ca9b
--- /dev/null
+++ b/minzipold/Hash.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Hash table.  The dominant calls are add and lookup, with removals
+ * happening very infrequently.  We use probing, and don't worry much
+ * about tombstone removal.
+ */
+#include <stdlib.h>
+#include <assert.h>
+
+#define LOG_TAG "minzip"
+#include "Log.h"
+#include "Hash.h"
+
+/* table load factor, i.e. how full can it get before we resize */
+//#define LOAD_NUMER  3       // 75%
+//#define LOAD_DENOM  4
+#define LOAD_NUMER  5       // 62.5%
+#define LOAD_DENOM  8
+//#define LOAD_NUMER  1       // 50%
+//#define LOAD_DENOM  2
+
+/*
+ * Compute the capacity needed for a table to hold "size" elements.
+ */
+size_t mzHashSize(size_t size) {
+    return (size * LOAD_DENOM) / LOAD_NUMER +1;
+}
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+unsigned int roundUpPower2(unsigned int val)
+{
+    val--;
+    val |= val >> 1;
+    val |= val >> 2;
+    val |= val >> 4;
+    val |= val >> 8;
+    val |= val >> 16;
+    val++;
+
+    return val;
+}
+
+/*
+ * Create and initialize a hash table.
+ */
+HashTable* mzHashTableCreate(size_t initialSize, HashFreeFunc freeFunc)
+{
+    HashTable* pHashTable;
+
+    assert(initialSize > 0);
+
+    pHashTable = (HashTable*) malloc(sizeof(*pHashTable));
+    if (pHashTable == NULL)
+        return NULL;
+
+    pHashTable->tableSize = roundUpPower2(initialSize);
+    pHashTable->numEntries = pHashTable->numDeadEntries = 0;
+    pHashTable->freeFunc = freeFunc;
+    pHashTable->pEntries =
+        (HashEntry*) calloc((size_t)pHashTable->tableSize, sizeof(HashTable));
+    if (pHashTable->pEntries == NULL) {
+        free(pHashTable);
+        return NULL;
+    }
+
+    return pHashTable;
+}
+
+/*
+ * Clear out all entries.
+ */
+void mzHashTableClear(HashTable* pHashTable)
+{
+    HashEntry* pEnt;
+    int i;
+
+    pEnt = pHashTable->pEntries;
+    for (i = 0; i < pHashTable->tableSize; i++, pEnt++) {
+        if (pEnt->data == HASH_TOMBSTONE) {
+            // nuke entry
+            pEnt->data = NULL;
+        } else if (pEnt->data != NULL) {
+            // call free func then nuke entry
+            if (pHashTable->freeFunc != NULL)
+                (*pHashTable->freeFunc)(pEnt->data);
+            pEnt->data = NULL;
+        }
+    }
+
+    pHashTable->numEntries = 0;
+    pHashTable->numDeadEntries = 0;
+}
+
+/*
+ * Free the table.
+ */
+void mzHashTableFree(HashTable* pHashTable)
+{
+    if (pHashTable == NULL)
+        return;
+    mzHashTableClear(pHashTable);
+    free(pHashTable->pEntries);
+    free(pHashTable);
+}
+
+#ifndef NDEBUG
+/*
+ * Count up the number of tombstone entries in the hash table.
+ */
+static int countTombStones(HashTable* pHashTable)
+{
+    int i, count;
+
+    for (count = i = 0; i < pHashTable->tableSize; i++) {
+        if (pHashTable->pEntries[i].data == HASH_TOMBSTONE)
+            count++;
+    }
+    return count;
+}
+#endif
+
+/*
+ * Resize a hash table.  We do this when adding an entry increased the
+ * size of the table beyond its comfy limit.
+ *
+ * This essentially requires re-inserting all elements into the new storage.
+ *
+ * If multiple threads can access the hash table, the table's lock should
+ * have been grabbed before issuing the "lookup+add" call that led to the
+ * resize, so we don't have a synchronization problem here.
+ */
+static bool resizeHash(HashTable* pHashTable, int newSize)
+{
+    HashEntry* pNewEntries;
+    int i;
+
+    assert(countTombStones(pHashTable) == pHashTable->numDeadEntries);
+    //LOGI("before: dead=%d\n", pHashTable->numDeadEntries);
+
+    pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashTable));
+    if (pNewEntries == NULL)
+        return false;
+
+    for (i = 0; i < pHashTable->tableSize; i++) {
+        void* data = pHashTable->pEntries[i].data;
+        if (data != NULL && data != HASH_TOMBSTONE) {
+            int hashValue = pHashTable->pEntries[i].hashValue;
+            int newIdx;
+
+            /* probe for new spot, wrapping around */
+            newIdx = hashValue & (newSize-1);
+            while (pNewEntries[newIdx].data != NULL)
+                newIdx = (newIdx + 1) & (newSize-1);
+
+            pNewEntries[newIdx].hashValue = hashValue;
+            pNewEntries[newIdx].data = data;
+        }
+    }
+
+    free(pHashTable->pEntries);
+    pHashTable->pEntries = pNewEntries;
+    pHashTable->tableSize = newSize;
+    pHashTable->numDeadEntries = 0;
+
+    assert(countTombStones(pHashTable) == 0);
+    return true;
+}
+
+/*
+ * Look up an entry.
+ *
+ * We probe on collisions, wrapping around the table.
+ */
+void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item,
+    HashCompareFunc cmpFunc, bool doAdd)
+{
+    HashEntry* pEntry;
+    HashEntry* pEnd;
+    void* result = NULL;
+
+    assert(pHashTable->tableSize > 0);
+    assert(item != HASH_TOMBSTONE);
+    assert(item != NULL);
+
+    /* jump to the first entry and probe for a match */
+    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+    while (pEntry->data != NULL) {
+        if (pEntry->data != HASH_TOMBSTONE &&
+            pEntry->hashValue == itemHash &&
+            (*cmpFunc)(pEntry->data, item) == 0)
+        {
+            /* match */
+            //LOGD("+++ match on entry %d\n", pEntry - pHashTable->pEntries);
+            break;
+        }
+
+        pEntry++;
+        if (pEntry == pEnd) {     /* wrap around to start */
+            if (pHashTable->tableSize == 1)
+                break;      /* edge case - single-entry table */
+            pEntry = pHashTable->pEntries;
+        }
+
+        //LOGI("+++ look probing %d...\n", pEntry - pHashTable->pEntries);
+    }
+
+    if (pEntry->data == NULL) {
+        if (doAdd) {
+            pEntry->hashValue = itemHash;
+            pEntry->data = item;
+            pHashTable->numEntries++;
+
+            /*
+             * We've added an entry.  See if this brings us too close to full.
+             */
+            if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM
+                > pHashTable->tableSize * LOAD_NUMER)
+            {
+                if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) {
+                    /* don't really have a way to indicate failure */
+                    LOGE("Dalvik hash resize failure\n");
+                    abort();
+                }
+                /* note "pEntry" is now invalid */
+            } else {
+                //LOGW("okay %d/%d/%d\n",
+                //    pHashTable->numEntries, pHashTable->tableSize,
+                //    (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM);
+            }
+
+            /* full table is bad -- search for nonexistent never halts */
+            assert(pHashTable->numEntries < pHashTable->tableSize);
+            result = item;
+        } else {
+            assert(result == NULL);
+        }
+    } else {
+        result = pEntry->data;
+    }
+
+    return result;
+}
+
+/*
+ * Remove an entry from the table.
+ *
+ * Does NOT invoke the "free" function on the item.
+ */
+bool mzHashTableRemove(HashTable* pHashTable, unsigned int itemHash, void* item)
+{
+    HashEntry* pEntry;
+    HashEntry* pEnd;
+
+    assert(pHashTable->tableSize > 0);
+
+    /* jump to the first entry and probe for a match */
+    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+    while (pEntry->data != NULL) {
+        if (pEntry->data == item) {
+            //LOGI("+++ stepping on entry %d\n", pEntry - pHashTable->pEntries);
+            pEntry->data = HASH_TOMBSTONE;
+            pHashTable->numEntries--;
+            pHashTable->numDeadEntries++;
+            return true;
+        }
+
+        pEntry++;
+        if (pEntry == pEnd) {     /* wrap around to start */
+            if (pHashTable->tableSize == 1)
+                break;      /* edge case - single-entry table */
+            pEntry = pHashTable->pEntries;
+        }
+
+        //LOGI("+++ del probing %d...\n", pEntry - pHashTable->pEntries);
+    }
+
+    return false;
+}
+
+/*
+ * Execute a function on every entry in the hash table.
+ *
+ * If "func" returns a nonzero value, terminate early and return the value.
+ */
+int mzHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg)
+{
+    int i, val;
+
+    for (i = 0; i < pHashTable->tableSize; i++) {
+        HashEntry* pEnt = &pHashTable->pEntries[i];
+
+        if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) {
+            val = (*func)(pEnt->data, arg);
+            if (val != 0)
+                return val;
+        }
+    }
+
+    return 0;
+}
+
+
+/*
+ * Look up an entry, counting the number of times we have to probe.
+ *
+ * Returns -1 if the entry wasn't found.
+ */
+int countProbes(HashTable* pHashTable, unsigned int itemHash, const void* item,
+    HashCompareFunc cmpFunc)
+{
+    HashEntry* pEntry;
+    HashEntry* pEnd;
+    int count = 0;
+
+    assert(pHashTable->tableSize > 0);
+    assert(item != HASH_TOMBSTONE);
+    assert(item != NULL);
+
+    /* jump to the first entry and probe for a match */
+    pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+    pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+    while (pEntry->data != NULL) {
+        if (pEntry->data != HASH_TOMBSTONE &&
+            pEntry->hashValue == itemHash &&
+            (*cmpFunc)(pEntry->data, item) == 0)
+        {
+            /* match */
+            break;
+        }
+
+        pEntry++;
+        if (pEntry == pEnd) {     /* wrap around to start */
+            if (pHashTable->tableSize == 1)
+                break;      /* edge case - single-entry table */
+            pEntry = pHashTable->pEntries;
+        }
+
+        count++;
+    }
+    if (pEntry->data == NULL)
+        return -1;
+
+    return count;
+}
+
+/*
+ * Evaluate the amount of probing required for the specified hash table.
+ *
+ * We do this by running through all entries in the hash table, computing
+ * the hash value and then doing a lookup.
+ *
+ * The caller should lock the table before calling here.
+ */
+void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
+    HashCompareFunc cmpFunc)
+{
+    int numEntries, minProbe, maxProbe, totalProbe;
+    HashIter iter;
+
+    numEntries = maxProbe = totalProbe = 0;
+    minProbe = 65536*32767;
+
+    for (mzHashIterBegin(pHashTable, &iter); !mzHashIterDone(&iter);
+        mzHashIterNext(&iter))
+    {
+        const void* data = (const void*)mzHashIterData(&iter);
+        int count;
+            
+        count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc);
+
+        numEntries++;
+
+        if (count < minProbe)
+            minProbe = count;
+        if (count > maxProbe)
+            maxProbe = count;
+        totalProbe += count;
+    }
+
+    LOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n",
+        minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize,
+        (float) totalProbe / (float) numEntries);
+}
diff --git a/minzipold/Hash.h b/minzipold/Hash.h
new file mode 100644
index 0000000..8194537
--- /dev/null
+++ b/minzipold/Hash.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * General purpose hash table, used for finding classes, methods, etc.
+ *
+ * When the number of elements reaches 3/4 of the table's capacity, the
+ * table will be resized.
+ */
+#ifndef _MINZIP_HASH
+#define _MINZIP_HASH
+
+#include "inline_magic.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+
+/* compute the hash of an item with a specific type */
+typedef unsigned int (*HashCompute)(const void* item);
+
+/*
+ * Compare a hash entry with a "loose" item after their hash values match.
+ * Returns { <0, 0, >0 } depending on ordering of items (same semantics
+ * as strcmp()).
+ */
+typedef int (*HashCompareFunc)(const void* tableItem, const void* looseItem);
+
+/*
+ * This function will be used to free entries in the table.  This can be
+ * NULL if no free is required, free(), or a custom function.
+ */
+typedef void (*HashFreeFunc)(void* ptr);
+
+/*
+ * Used by mzHashForeach().
+ */
+typedef int (*HashForeachFunc)(void* data, void* arg);
+
+/*
+ * One entry in the hash table.  "data" values are expected to be (or have
+ * the same characteristics as) valid pointers.  In particular, a NULL
+ * value for "data" indicates an empty slot, and HASH_TOMBSTONE indicates
+ * a no-longer-used slot that must be stepped over during probing.
+ *
+ * Attempting to add a NULL or tombstone value is an error.
+ *
+ * When an entry is released, we will call (HashFreeFunc)(entry->data).
+ */
+typedef struct HashEntry {
+    unsigned int hashValue;
+    void* data;
+} HashEntry;
+
+#define HASH_TOMBSTONE ((void*) 0xcbcacccd)     // invalid ptr value
+
+/*
+ * Expandable hash table.
+ *
+ * This structure should be considered opaque.
+ */
+typedef struct HashTable {
+    int         tableSize;          /* must be power of 2 */
+    int         numEntries;         /* current #of "live" entries */
+    int         numDeadEntries;     /* current #of tombstone entries */
+    HashEntry*  pEntries;           /* array on heap */
+    HashFreeFunc freeFunc;
+} HashTable;
+
+/*
+ * Create and initialize a HashTable structure, using "initialSize" as
+ * a basis for the initial capacity of the table.  (The actual initial
+ * table size may be adjusted upward.)  If you know exactly how many
+ * elements the table will hold, pass the result from mzHashSize() in.)
+ *
+ * Returns "false" if unable to allocate the table.
+ */
+HashTable* mzHashTableCreate(size_t initialSize, HashFreeFunc freeFunc);
+
+/*
+ * Compute the capacity needed for a table to hold "size" elements.  Use
+ * this when you know ahead of time how many elements the table will hold.
+ * Pass this value into mzHashTableCreate() to ensure that you can add
+ * all elements without needing to reallocate the table.
+ */
+size_t mzHashSize(size_t size);
+
+/*
+ * Clear out a hash table, freeing the contents of any used entries.
+ */
+void mzHashTableClear(HashTable* pHashTable);
+
+/*
+ * Free a hash table.
+ */
+void mzHashTableFree(HashTable* pHashTable);
+
+/*
+ * Get #of entries in hash table.
+ */
+INLINE int mzHashTableNumEntries(HashTable* pHashTable) {
+    return pHashTable->numEntries;
+}
+
+/*
+ * Get total size of hash table (for memory usage calculations).
+ */
+INLINE int mzHashTableMemUsage(HashTable* pHashTable) {
+    return sizeof(HashTable) + pHashTable->tableSize * sizeof(HashEntry);
+}
+
+/*
+ * Look up an entry in the table, possibly adding it if it's not there.
+ *
+ * If "item" is not found, and "doAdd" is false, NULL is returned.
+ * Otherwise, a pointer to the found or added item is returned.  (You can
+ * tell the difference by seeing if return value == item.)
+ *
+ * An "add" operation may cause the entire table to be reallocated.
+ */
+void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item,
+    HashCompareFunc cmpFunc, bool doAdd);
+
+/*
+ * Remove an item from the hash table, given its "data" pointer.  Does not
+ * invoke the "free" function; just detaches it from the table.
+ */
+bool mzHashTableRemove(HashTable* pHashTable, unsigned int hash, void* item);
+
+/*
+ * Execute "func" on every entry in the hash table.
+ *
+ * If "func" returns a nonzero value, terminate early and return the value.
+ */
+int mzHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg);
+
+/*
+ * An alternative to mzHashForeach(), using an iterator.
+ *
+ * Use like this:
+ *   HashIter iter;
+ *   for (mzHashIterBegin(hashTable, &iter); !mzHashIterDone(&iter);
+ *       mzHashIterNext(&iter))
+ *   {
+ *       MyData* data = (MyData*)mzHashIterData(&iter);
+ *   }
+ */
+typedef struct HashIter {
+    void*       data;
+    HashTable*  pHashTable;
+    int         idx;
+} HashIter;
+INLINE void mzHashIterNext(HashIter* pIter) {
+    int i = pIter->idx +1;
+    int lim = pIter->pHashTable->tableSize;
+    for ( ; i < lim; i++) {
+        void* data = pIter->pHashTable->pEntries[i].data;
+        if (data != NULL && data != HASH_TOMBSTONE)
+            break;
+    }
+    pIter->idx = i;
+}
+INLINE void mzHashIterBegin(HashTable* pHashTable, HashIter* pIter) {
+    pIter->pHashTable = pHashTable;
+    pIter->idx = -1;
+    mzHashIterNext(pIter);
+}
+INLINE bool mzHashIterDone(HashIter* pIter) {
+    return (pIter->idx >= pIter->pHashTable->tableSize);
+}
+INLINE void* mzHashIterData(HashIter* pIter) {
+    assert(pIter->idx >= 0 && pIter->idx < pIter->pHashTable->tableSize);
+    return pIter->pHashTable->pEntries[pIter->idx].data;
+}
+
+
+/*
+ * Evaluate hash table performance by examining the number of times we
+ * have to probe for an entry.
+ *
+ * The caller should lock the table beforehand.
+ */
+typedef unsigned int (*HashCalcFunc)(const void* item);
+void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
+    HashCompareFunc cmpFunc);
+
+#endif /*_MINZIP_HASH*/
diff --git a/minzipold/Inlines.c b/minzipold/Inlines.c
new file mode 100644
index 0000000..91f8775
--- /dev/null
+++ b/minzipold/Inlines.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Make sure that non-inlined versions of INLINED-marked functions
+ * exist so that debug builds (which don't generally do inlining)
+ * don't break.
+ */
+#define MINZIP_GENERATE_INLINES 1
+#include "Bits.h"
+#include "Hash.h"
+#include "SysUtil.h"
+#include "Zip.h"
diff --git a/minzipold/Log.h b/minzipold/Log.h
new file mode 100644
index 0000000..36e62f5
--- /dev/null
+++ b/minzipold/Log.h
@@ -0,0 +1,207 @@
+//
+// Copyright 2005 The Android Open Source Project
+//
+// C/C++ logging functions.  See the logging documentation for API details.
+//
+// We'd like these to be available from C code (in case we import some from
+// somewhere), so this has a C interface.
+//
+// The output will be correct when the log file is shared between multiple
+// threads and/or multiple processes so long as the operating system
+// supports O_APPEND.  These calls have mutex-protected data structures
+// and so are NOT reentrant.  Do not use LOG in a signal handler.
+//
+#ifndef _MINZIP_LOG_H
+#define _MINZIP_LOG_H
+
+#include <stdio.h>
+
+// ---------------------------------------------------------------------
+
+/*
+ * Normally we strip LOGV (VERBOSE messages) from release builds.
+ * You can modify this (for example with "#define LOG_NDEBUG 0"
+ * at the top of your source file) to change that behavior.
+ */
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * This is the local tag used for the following simplified
+ * logging macros.  You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#ifndef LOGV
+#if LOG_NDEBUG
+#define LOGV(...)   ((void)0)
+#else
+#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond)     (__builtin_expect((cond)!=0, 0))
+
+#ifndef LOGV_IF
+#if LOG_NDEBUG
+#define LOGV_IF(cond, ...)   ((void)0)
+#else
+#define LOGV_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+#endif
+
+#define LOGVV LOGV
+#define LOGVV_IF LOGV_IF
+
+/*
+ * Simplified macro to send a debug log message using the current LOG_TAG.
+ */
+#ifndef LOGD
+#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGD_IF
+#define LOGD_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current LOG_TAG.
+ */
+#ifndef LOGI
+#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGI_IF
+#define LOGI_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current LOG_TAG.
+ */
+#ifndef LOGW
+#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGW_IF
+#define LOGW_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current LOG_TAG.
+ */
+#ifndef LOGE
+#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGE_IF
+#define LOGE_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * verbose priority.
+ */
+#ifndef IF_LOGV
+#if LOG_NDEBUG
+#define IF_LOGV() if (false)
+#else
+#define IF_LOGV() IF_LOG(LOG_VERBOSE, LOG_TAG)
+#endif
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * debug priority.
+ */
+#ifndef IF_LOGD
+#define IF_LOGD() IF_LOG(LOG_DEBUG, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * info priority.
+ */
+#ifndef IF_LOGI
+#define IF_LOGI() IF_LOG(LOG_INFO, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * warn priority.
+ */
+#ifndef IF_LOGW
+#define IF_LOGW() IF_LOG(LOG_WARN, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * error priority.
+ */
+#ifndef IF_LOGE
+#define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG)
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ *  LOG(LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ *
+ * Non-gcc probably won't have __FUNCTION__.  It's not vital.  gcc also
+ * offers __PRETTY_FUNCTION__, which is rather more than we need.
+ */
+#ifndef LOG
+#define LOG(priority, tag, ...) \
+    LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef LOG_PRI
+#define LOG_PRI(priority, tag, ...) \
+    printf(tag ": " __VA_ARGS__)
+#endif
+
+/*
+ * Conditional given a desired logging priority and tag.
+ */
+#ifndef IF_LOG
+#define IF_LOG(priority, tag) \
+    if (1)
+#endif
+
+#endif // _MINZIP_LOG_H
diff --git a/minzipold/SysUtil.c b/minzipold/SysUtil.c
new file mode 100644
index 0000000..49a2522
--- /dev/null
+++ b/minzipold/SysUtil.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * System utilities.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+
+#define LOG_TAG "minzip"
+#include "Log.h"
+#include "SysUtil.h"
+
+/*
+ * Having trouble finding a portable way to get this.  sysconf(_SC_PAGE_SIZE)
+ * seems appropriate, but we don't have that on the device.  Some systems
+ * have getpagesize(2), though the linux man page has some odd cautions.
+ */
+#define DEFAULT_PAGE_SIZE   4096
+
+
+/*
+ * Create an anonymous shared memory segment large enough to hold "length"
+ * bytes.  The actual segment may be larger because mmap() operates on
+ * page boundaries (usually 4K).
+ */
+static void* sysCreateAnonShmem(size_t length)
+{
+    void* ptr;
+
+    ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
+            MAP_SHARED | MAP_ANON, -1, 0);
+    if (ptr == MAP_FAILED) {
+        LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
+            strerror(errno));
+        return NULL;
+    }
+
+    return ptr;
+}
+
+static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
+{
+    off_t start, end;
+    size_t length;
+
+    assert(start_ != NULL);
+    assert(length_ != NULL);
+
+    start = lseek(fd, 0L, SEEK_CUR);
+    end = lseek(fd, 0L, SEEK_END);
+    (void) lseek(fd, start, SEEK_SET);
+
+    if (start == (off_t) -1 || end == (off_t) -1) {
+        LOGE("could not determine length of file\n");
+        return -1;
+    }
+
+    length = end - start;
+    if (length == 0) {
+        LOGE("file is empty\n");
+        return -1;
+    }
+
+    *start_ = start;
+    *length_ = length;
+
+    return 0;
+}
+
+/*
+ * Pull the contents of a file into an new shared memory segment.  We grab
+ * everything from fd's current offset on.
+ *
+ * We need to know the length ahead of time so we can allocate a segment
+ * of sufficient size.
+ */
+int sysLoadFileInShmem(int fd, MemMapping* pMap)
+{
+    off_t start;
+    size_t length, actual;
+    void* memPtr;
+
+    assert(pMap != NULL);
+
+    if (getFileStartAndLength(fd, &start, &length) < 0)
+        return -1;
+
+    memPtr = sysCreateAnonShmem(length);
+    if (memPtr == NULL)
+        return -1;
+
+    actual = read(fd, memPtr, length);
+    if (actual != length) {
+        LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
+        sysReleaseShmem(pMap);
+        return -1;
+    }
+
+    pMap->baseAddr = pMap->addr = memPtr;
+    pMap->baseLength = pMap->length = length;
+
+    return 0;
+}
+
+/*
+ * Map a file (from fd's current offset) into a shared, read-only memory
+ * segment.  The file offset must be a multiple of the page size.
+ *
+ * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileInShmem(int fd, MemMapping* pMap)
+{
+    off_t start;
+    size_t length;
+    void* memPtr;
+
+    assert(pMap != NULL);
+
+    if (getFileStartAndLength(fd, &start, &length) < 0)
+        return -1;
+
+    memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
+    if (memPtr == MAP_FAILED) {
+        LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
+            fd, (int) start, strerror(errno));
+        return -1;
+    }
+
+    pMap->baseAddr = pMap->addr = memPtr;
+    pMap->baseLength = pMap->length = length;
+
+    return 0;
+}
+
+/*
+ * Map part of a file (from fd's current offset) into a shared, read-only
+ * memory segment.
+ *
+ * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileSegmentInShmem(int fd, off_t start, long length,
+    MemMapping* pMap)
+{
+    off_t dummy;
+    size_t fileLength, actualLength;
+    off_t actualStart;
+    int adjust;
+    void* memPtr;
+
+    assert(pMap != NULL);
+
+    if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
+        return -1;
+
+    if (start + length > (long)fileLength) {
+        LOGW("bad segment: st=%d len=%ld flen=%d\n",
+            (int) start, length, (int) fileLength);
+        return -1;
+    }
+
+    /* adjust to be page-aligned */
+    adjust = start % DEFAULT_PAGE_SIZE;
+    actualStart = start - adjust;
+    actualLength = length + adjust;
+
+    memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
+                fd, actualStart);
+    if (memPtr == MAP_FAILED) {
+        LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
+            (int) actualLength, fd, (int) actualStart, strerror(errno));
+        return -1;
+    }
+
+    pMap->baseAddr = memPtr;
+    pMap->baseLength = actualLength;
+    pMap->addr = (char*)memPtr + adjust;
+    pMap->length = length;
+
+    LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
+        (int) start, (int) length,
+        pMap->baseAddr, (int) pMap->baseLength,
+        pMap->addr, (int) pMap->length);
+
+    return 0;
+}
+
+/*
+ * Release a memory mapping.
+ */
+void sysReleaseShmem(MemMapping* pMap)
+{
+    if (pMap->baseAddr == NULL && pMap->baseLength == 0)
+        return;
+
+    if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
+        LOGW("munmap(%p, %d) failed: %s\n",
+            pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
+    } else {
+        LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
+        pMap->baseAddr = NULL;
+        pMap->baseLength = 0;
+    }
+}
+
diff --git a/minzipold/SysUtil.h b/minzipold/SysUtil.h
new file mode 100644
index 0000000..ec3a4bc
--- /dev/null
+++ b/minzipold/SysUtil.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * System utilities.
+ */
+#ifndef _MINZIP_SYSUTIL
+#define _MINZIP_SYSUTIL
+
+#include "inline_magic.h"
+
+#include <sys/types.h>
+
+/*
+ * Use this to keep track of mapped segments.
+ */
+typedef struct MemMapping {
+    void*   addr;           /* start of data */
+    size_t  length;         /* length of data */
+
+    void*   baseAddr;       /* page-aligned base address */
+    size_t  baseLength;     /* length of mapping */
+} MemMapping;
+
+/* copy a map */
+INLINE void sysCopyMap(MemMapping* dst, const MemMapping* src) {
+    *dst = *src;
+}
+
+/*
+ * Load a file into a new shared memory segment.  All data from the current
+ * offset to the end of the file is pulled in.
+ *
+ * The segment is read-write, allowing VM fixups.  (It should be modified
+ * to support .gz/.zip compressed data.)
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysLoadFileInShmem(int fd, MemMapping* pMap);
+
+/*
+ * Map a file (from fd's current offset) into a shared,
+ * read-only memory segment.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFileInShmem(int fd, MemMapping* pMap);
+
+/*
+ * Like sysMapFileInShmem, but on only part of a file.
+ */
+int sysMapFileSegmentInShmem(int fd, off_t start, long length,
+    MemMapping* pMap);
+
+/*
+ * Release the pages associated with a shared memory segment.
+ *
+ * This does not free "pMap"; it just releases the memory.
+ */
+void sysReleaseShmem(MemMapping* pMap);
+
+#endif /*_MINZIP_SYSUTIL*/
diff --git a/minzipold/Zip.c b/minzipold/Zip.c
new file mode 100644
index 0000000..54d5d55
--- /dev/null
+++ b/minzipold/Zip.c
@@ -0,0 +1,1168 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Simple Zip file support.
+ */
+#include "safe_iop.h"
+#include "zlib.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>     // for uintptr_t
+#include <stdlib.h>
+#include <sys/stat.h>   // for S_ISLNK()
+#include <unistd.h>
+
+#define LOG_TAG "minzip"
+#include "Zip.h"
+#include "Bits.h"
+#include "Log.h"
+#include "DirUtil.h"
+
+#undef NDEBUG   // do this after including Log.h
+#include <assert.h>
+
+#define SORT_ENTRIES 1
+
+/*
+ * Offset and length constants (java.util.zip naming convention).
+ */
+enum {
+    CENSIG = 0x02014b50,      // PK12
+    CENHDR = 46,
+
+    CENVEM =  4,
+    CENVER =  6,
+    CENFLG =  8,
+    CENHOW = 10,
+    CENTIM = 12,
+    CENCRC = 16,
+    CENSIZ = 20,
+    CENLEN = 24,
+    CENNAM = 28,
+    CENEXT = 30,
+    CENCOM = 32,
+    CENDSK = 34,
+    CENATT = 36,
+    CENATX = 38,
+    CENOFF = 42,
+
+    ENDSIG = 0x06054b50,     // PK56
+    ENDHDR = 22,
+
+    ENDSUB =  8,
+    ENDTOT = 10,
+    ENDSIZ = 12,
+    ENDOFF = 16,
+    ENDCOM = 20,
+
+    EXTSIG = 0x08074b50,     // PK78
+    EXTHDR = 16,
+
+    EXTCRC =  4,
+    EXTSIZ =  8,
+    EXTLEN = 12,
+
+    LOCSIG = 0x04034b50,      // PK34
+    LOCHDR = 30,
+
+    LOCVER =  4,
+    LOCFLG =  6,
+    LOCHOW =  8,
+    LOCTIM = 10,
+    LOCCRC = 14,
+    LOCSIZ = 18,
+    LOCLEN = 22,
+    LOCNAM = 26,
+    LOCEXT = 28,
+
+    STORED = 0,
+    DEFLATED = 8,
+
+    CENVEM_UNIX = 3 << 8,   // the high byte of CENVEM
+};
+
+
+/*
+ * For debugging, dump the contents of a ZipEntry.
+ */
+#if 0
+static void dumpEntry(const ZipEntry* pEntry)
+{
+    LOGI(" %p '%.*s'\n", pEntry->fileName,pEntry->fileNameLen,pEntry->fileName);
+    LOGI("   off=%ld comp=%ld uncomp=%ld how=%d\n", pEntry->offset,
+        pEntry->compLen, pEntry->uncompLen, pEntry->compression);
+}
+#endif
+
+/*
+ * (This is a mzHashTableLookup callback.)
+ *
+ * Compare two ZipEntry structs, by name.
+ */
+static int hashcmpZipEntry(const void* ventry1, const void* ventry2)
+{
+    const ZipEntry* entry1 = (const ZipEntry*) ventry1;
+    const ZipEntry* entry2 = (const ZipEntry*) ventry2;
+
+    if (entry1->fileNameLen != entry2->fileNameLen)
+        return entry1->fileNameLen - entry2->fileNameLen;
+    return memcmp(entry1->fileName, entry2->fileName, entry1->fileNameLen);
+}
+
+/*
+ * (This is a mzHashTableLookup callback.)
+ *
+ * find a ZipEntry struct by name.
+ */
+static int hashcmpZipName(const void* ventry, const void* vname)
+{
+    const ZipEntry* entry = (const ZipEntry*) ventry;
+    const char* name = (const char*) vname;
+    unsigned int nameLen = strlen(name);
+
+    if (entry->fileNameLen != nameLen)
+        return entry->fileNameLen - nameLen;
+    return memcmp(entry->fileName, name, nameLen);
+}
+
+/*
+ * Compute the hash code for a ZipEntry filename.
+ *
+ * Not expected to be compatible with any other hash function, so we init
+ * to 2 to ensure it doesn't happen to match.
+ */
+static unsigned int computeHash(const char* name, int nameLen)
+{
+    unsigned int hash = 2;
+
+    while (nameLen--)
+        hash = hash * 31 + *name++;
+
+    return hash;
+}
+
+static void addEntryToHashTable(HashTable* pHash, ZipEntry* pEntry)
+{
+    unsigned int itemHash = computeHash(pEntry->fileName, pEntry->fileNameLen);
+    const ZipEntry* found;
+
+    found = (const ZipEntry*)mzHashTableLookup(pHash,
+                itemHash, pEntry, hashcmpZipEntry, true);
+    if (found != pEntry) {
+        LOGW("WARNING: duplicate entry '%.*s' in Zip\n",
+            found->fileNameLen, found->fileName);
+        /* keep going */
+    }
+}
+
+static int validFilename(const char *fileName, unsigned int fileNameLen)
+{
+    // Forbid super long filenames.
+    if (fileNameLen >= PATH_MAX) {
+        LOGW("Filename too long (%d chatacters)\n", fileNameLen);
+        return 0;
+    }
+
+    // Require all characters to be printable ASCII (no NUL, no UTF-8, etc).
+    unsigned int i;
+    for (i = 0; i < fileNameLen; ++i) {
+        if (fileName[i] < 32 || fileName[i] >= 127) {
+            LOGW("Filename contains invalid character '\%03o'\n", fileName[i]);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/*
+ * Parse the contents of a Zip archive.  After confirming that the file
+ * is in fact a Zip, we scan out the contents of the central directory and
+ * store it in a hash table.
+ *
+ * Returns "true" on success.
+ */
+static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
+{
+    bool result = false;
+    const unsigned char* ptr;
+    unsigned int i, numEntries, cdOffset;
+    unsigned int val;
+
+    /*
+     * The first 4 bytes of the file will either be the local header
+     * signature for the first file (LOCSIG) or, if the archive doesn't
+     * have any files in it, the end-of-central-directory signature (ENDSIG).
+     */
+    val = get4LE(pMap->addr);
+    if (val == ENDSIG) {
+        LOGI("Found Zip archive, but it looks empty\n");
+        goto bail;
+    } else if (val != LOCSIG) {
+        LOGV("Not a Zip archive (found 0x%08x)\n", val);
+        goto bail;
+    }
+
+    /*
+     * Find the EOCD.  We'll find it immediately unless they have a file
+     * comment.
+     */
+    ptr = pMap->addr + pMap->length - ENDHDR;
+
+    while (ptr >= (const unsigned char*) pMap->addr) {
+        if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG)
+            break;
+        ptr--;
+    }
+    if (ptr < (const unsigned char*) pMap->addr) {
+        LOGI("Could not find end-of-central-directory in Zip\n");
+        goto bail;
+    }
+
+    /*
+     * There are two interesting items in the EOCD block: the number of
+     * entries in the file, and the file offset of the start of the
+     * central directory.
+     */
+    numEntries = get2LE(ptr + ENDSUB);
+    cdOffset = get4LE(ptr + ENDOFF);
+
+    LOGVV("numEntries=%d cdOffset=%d\n", numEntries, cdOffset);
+    if (numEntries == 0 || cdOffset >= pMap->length) {
+        LOGW("Invalid entries=%d offset=%d (len=%zd)\n",
+            numEntries, cdOffset, pMap->length);
+        goto bail;
+    }
+
+    /*
+     * Create data structures to hold entries.
+     */
+    pArchive->numEntries = numEntries;
+    pArchive->pEntries = (ZipEntry*) calloc(numEntries, sizeof(ZipEntry));
+    pArchive->pHash = mzHashTableCreate(mzHashSize(numEntries), NULL);
+    if (pArchive->pEntries == NULL || pArchive->pHash == NULL)
+        goto bail;
+
+    ptr = pMap->addr + cdOffset;
+    for (i = 0; i < numEntries; i++) {
+        ZipEntry* pEntry;
+        unsigned int fileNameLen, extraLen, commentLen, localHdrOffset;
+        const unsigned char* localHdr;
+        const char *fileName;
+
+        if (ptr + CENHDR > (const unsigned char*)pMap->addr + pMap->length) {
+            LOGW("Ran off the end (at %d)\n", i);
+            goto bail;
+        }
+        if (get4LE(ptr) != CENSIG) {
+            LOGW("Missed a central dir sig (at %d)\n", i);
+            goto bail;
+        }
+
+        localHdrOffset = get4LE(ptr + CENOFF);
+        fileNameLen = get2LE(ptr + CENNAM);
+        extraLen = get2LE(ptr + CENEXT);
+        commentLen = get2LE(ptr + CENCOM);
+        fileName = (const char*)ptr + CENHDR;
+        if (fileName + fileNameLen > (const char*)pMap->addr + pMap->length) {
+            LOGW("Filename ran off the end (at %d)\n", i);
+            goto bail;
+        }
+        if (!validFilename(fileName, fileNameLen)) {
+            LOGW("Invalid filename (at %d)\n", i);
+            goto bail;
+        }
+
+#if SORT_ENTRIES
+        /* Figure out where this entry should go (binary search).
+         */
+        if (i > 0) {
+            int low, high;
+
+            low = 0;
+            high = i - 1;
+            while (low <= high) {
+                int mid;
+                int diff;
+                int diffLen;
+
+                mid = low + ((high - low) / 2); // avoid overflow
+
+                if (pArchive->pEntries[mid].fileNameLen < fileNameLen) {
+                    diffLen = pArchive->pEntries[mid].fileNameLen;
+                } else {
+                    diffLen = fileNameLen;
+                }
+                diff = strncmp(pArchive->pEntries[mid].fileName, fileName,
+                        diffLen);
+                if (diff == 0) {
+                    diff = pArchive->pEntries[mid].fileNameLen - fileNameLen;
+                }
+                if (diff < 0) {
+                    low = mid + 1;
+                } else if (diff > 0) {
+                    high = mid - 1;
+                } else {
+                    high = mid;
+                    break;
+                }
+            }
+
+            unsigned int target = high + 1;
+            assert(target <= i);
+            if (target != i) {
+                /* It belongs somewhere other than at the end of
+                 * the list.  Make some room at [target].
+                 */
+                memmove(pArchive->pEntries + target + 1,
+                        pArchive->pEntries + target,
+                        (i - target) * sizeof(ZipEntry));
+            }
+            pEntry = &pArchive->pEntries[target];
+        } else {
+            pEntry = &pArchive->pEntries[0];
+        }
+#else
+        pEntry = &pArchive->pEntries[i];
+#endif
+
+        //LOGI("%d: localHdr=%d fnl=%d el=%d cl=%d\n",
+        //    i, localHdrOffset, fileNameLen, extraLen, commentLen);
+
+        pEntry->fileNameLen = fileNameLen;
+        pEntry->fileName = fileName;
+
+        pEntry->compLen = get4LE(ptr + CENSIZ);
+        pEntry->uncompLen = get4LE(ptr + CENLEN);
+        pEntry->compression = get2LE(ptr + CENHOW);
+        pEntry->modTime = get4LE(ptr + CENTIM);
+        pEntry->crc32 = get4LE(ptr + CENCRC);
+
+        /* These two are necessary for finding the mode of the file.
+         */
+        pEntry->versionMadeBy = get2LE(ptr + CENVEM);
+        if ((pEntry->versionMadeBy & 0xff00) != 0 &&
+                (pEntry->versionMadeBy & 0xff00) != CENVEM_UNIX)
+        {
+            LOGW("Incompatible \"version made by\": 0x%02x (at %d)\n",
+                    pEntry->versionMadeBy >> 8, i);
+            goto bail;
+        }
+        pEntry->externalFileAttributes = get4LE(ptr + CENATX);
+
+        // Perform pMap->addr + localHdrOffset, ensuring that it won't
+        // overflow. This is needed because localHdrOffset is untrusted.
+        if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pMap->addr,
+            (uintptr_t)localHdrOffset)) {
+            LOGW("Integer overflow adding in parseZipArchive\n");
+            goto bail;
+        }
+        if ((uintptr_t)localHdr + LOCHDR >
+            (uintptr_t)pMap->addr + pMap->length) {
+            LOGW("Bad offset to local header: %d (at %d)\n", localHdrOffset, i);
+            goto bail;
+        }
+        if (get4LE(localHdr) != LOCSIG) {
+            LOGW("Missed a local header sig (at %d)\n", i);
+            goto bail;
+        }
+        pEntry->offset = localHdrOffset + LOCHDR
+            + get2LE(localHdr + LOCNAM) + get2LE(localHdr + LOCEXT);
+        if (!safe_add(NULL, pEntry->offset, pEntry->compLen)) {
+            LOGW("Integer overflow adding in parseZipArchive\n");
+            goto bail;
+        }
+        if ((size_t)pEntry->offset + pEntry->compLen > pMap->length) {
+            LOGW("Data ran off the end (at %d)\n", i);
+            goto bail;
+        }
+
+#if !SORT_ENTRIES
+        /* Add to hash table; no need to lock here.
+         * Can't do this now if we're sorting, because entries
+         * will move around.
+         */
+        addEntryToHashTable(pArchive->pHash, pEntry);
+#endif
+
+        //dumpEntry(pEntry);
+        ptr += CENHDR + fileNameLen + extraLen + commentLen;
+    }
+
+#if SORT_ENTRIES
+    /* If we're sorting, we have to wait until all entries
+     * are in their final places, otherwise the pointers will
+     * probably point to the wrong things.
+     */
+    for (i = 0; i < numEntries; i++) {
+        /* Add to hash table; no need to lock here.
+         */
+        addEntryToHashTable(pArchive->pHash, &pArchive->pEntries[i]);
+    }
+#endif
+
+    result = true;
+
+bail:
+    if (!result) {
+        mzHashTableFree(pArchive->pHash);
+        pArchive->pHash = NULL;
+    }
+    return result;
+}
+
+/*
+ * Open a Zip archive and scan out the contents.
+ *
+ * The easiest way to do this is to mmap() the whole thing and do the
+ * traditional backward scan for central directory.  Since the EOCD is
+ * a relatively small bit at the end, we should end up only touching a
+ * small set of pages.
+ *
+ * This will be called on non-Zip files, especially during startup, so
+ * we don't want to be too noisy about failures.  (Do we want a "quiet"
+ * flag?)
+ *
+ * On success, we fill out the contents of "pArchive".
+ */
+int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive)
+{
+    MemMapping map;
+    int err;
+
+    LOGV("Opening archive '%s' %p\n", fileName, pArchive);
+
+    map.addr = NULL;
+    memset(pArchive, 0, sizeof(*pArchive));
+
+    pArchive->fd = open(fileName, O_RDONLY, 0);
+    if (pArchive->fd < 0) {
+        err = errno ? errno : -1;
+        LOGV("Unable to open '%s': %s\n", fileName, strerror(err));
+        goto bail;
+    }
+
+    if (sysMapFileInShmem(pArchive->fd, &map) != 0) {
+        err = -1;
+        LOGW("Map of '%s' failed\n", fileName);
+        goto bail;
+    }
+
+    if (map.length < ENDHDR) {
+        err = -1;
+        LOGV("File '%s' too small to be zip (%zd)\n", fileName, map.length);
+        goto bail;
+    }
+
+    if (!parseZipArchive(pArchive, &map)) {
+        err = -1;
+        LOGV("Parsing '%s' failed\n", fileName);
+        goto bail;
+    }
+
+    err = 0;
+    sysCopyMap(&pArchive->map, &map);
+    map.addr = NULL;
+
+bail:
+    if (err != 0)
+        mzCloseZipArchive(pArchive);
+    if (map.addr != NULL)
+        sysReleaseShmem(&map);
+    return err;
+}
+
+/*
+ * Close a ZipArchive, closing the file and freeing the contents.
+ *
+ * NOTE: the ZipArchive may not have been fully created.
+ */
+void mzCloseZipArchive(ZipArchive* pArchive)
+{
+    LOGV("Closing archive %p\n", pArchive);
+
+    if (pArchive->fd >= 0)
+        close(pArchive->fd);
+    if (pArchive->map.addr != NULL)
+        sysReleaseShmem(&pArchive->map);
+
+    free(pArchive->pEntries);
+
+    mzHashTableFree(pArchive->pHash);
+
+    pArchive->fd = -1;
+    pArchive->pHash = NULL;
+    pArchive->pEntries = NULL;
+}
+
+/*
+ * Find a matching entry.
+ *
+ * Returns NULL if no matching entry found.
+ */
+const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive,
+        const char* entryName)
+{
+    unsigned int itemHash = computeHash(entryName, strlen(entryName));
+
+    return (const ZipEntry*)mzHashTableLookup(pArchive->pHash,
+                itemHash, (char*) entryName, hashcmpZipName, false);
+}
+
+/*
+ * Return true if the entry is a symbolic link.
+ */
+bool mzIsZipEntrySymlink(const ZipEntry* pEntry)
+{
+    if ((pEntry->versionMadeBy & 0xff00) == CENVEM_UNIX) {
+        return S_ISLNK(pEntry->externalFileAttributes >> 16);
+    }
+    return false;
+}
+
+/* Call processFunction on the uncompressed data of a STORED entry.
+ */
+static bool processStoredEntry(const ZipArchive *pArchive,
+    const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
+    void *cookie)
+{
+    size_t bytesLeft = pEntry->compLen;
+    while (bytesLeft > 0) {
+        unsigned char buf[32 * 1024];
+        ssize_t n;
+        size_t count;
+        bool ret;
+
+        count = bytesLeft;
+        if (count > sizeof(buf)) {
+            count = sizeof(buf);
+        }
+        n = read(pArchive->fd, buf, count);
+        if (n < 0 || (size_t)n != count) {
+            LOGE("Can't read %zu bytes from zip file: %ld\n", count, n);
+            return false;
+        }
+        ret = processFunction(buf, n, cookie);
+        if (!ret) {
+            return false;
+        }
+        bytesLeft -= count;
+    }
+    return true;
+}
+
+static bool processDeflatedEntry(const ZipArchive *pArchive,
+    const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
+    void *cookie)
+{
+    long result = -1;
+    unsigned char readBuf[32 * 1024];
+    unsigned char procBuf[32 * 1024];
+    z_stream zstream;
+    int zerr;
+    long compRemaining;
+
+    compRemaining = pEntry->compLen;
+
+    /*
+     * Initialize the zlib stream.
+     */
+    memset(&zstream, 0, sizeof(zstream));
+    zstream.zalloc = Z_NULL;
+    zstream.zfree = Z_NULL;
+    zstream.opaque = Z_NULL;
+    zstream.next_in = NULL;
+    zstream.avail_in = 0;
+    zstream.next_out = (Bytef*) procBuf;
+    zstream.avail_out = sizeof(procBuf);
+    zstream.data_type = Z_UNKNOWN;
+
+    /*
+     * Use the undocumented "negative window bits" feature to tell zlib
+     * that there's no zlib header waiting for it.
+     */
+    zerr = inflateInit2(&zstream, -MAX_WBITS);
+    if (zerr != Z_OK) {
+        if (zerr == Z_VERSION_ERROR) {
+            LOGE("Installed zlib is not compatible with linked version (%s)\n",
+                ZLIB_VERSION);
+        } else {
+            LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+        }
+        goto bail;
+    }
+
+    /*
+     * Loop while we have data.
+     */
+    do {
+        /* read as much as we can */
+        if (zstream.avail_in == 0) {
+            long getSize = (compRemaining > (long)sizeof(readBuf)) ?
+                        (long)sizeof(readBuf) : compRemaining;
+            LOGVV("+++ reading %ld bytes (%ld left)\n",
+                getSize, compRemaining);
+
+            int cc = read(pArchive->fd, readBuf, getSize);
+            if (cc != (int) getSize) {
+                LOGW("inflate read failed (%d vs %ld)\n", cc, getSize);
+                goto z_bail;
+            }
+
+            compRemaining -= getSize;
+
+            zstream.next_in = readBuf;
+            zstream.avail_in = getSize;
+        }
+
+        /* uncompress the data */
+        zerr = inflate(&zstream, Z_NO_FLUSH);
+        if (zerr != Z_OK && zerr != Z_STREAM_END) {
+            LOGD("zlib inflate call failed (zerr=%d)\n", zerr);
+            goto z_bail;
+        }
+
+        /* write when we're full or when we're done */
+        if (zstream.avail_out == 0 ||
+            (zerr == Z_STREAM_END && zstream.avail_out != sizeof(procBuf)))
+        {
+            long procSize = zstream.next_out - procBuf;
+            LOGVV("+++ processing %d bytes\n", (int) procSize);
+            bool ret = processFunction(procBuf, procSize, cookie);
+            if (!ret) {
+                LOGW("Process function elected to fail (in inflate)\n");
+                goto z_bail;
+            }
+
+            zstream.next_out = procBuf;
+            zstream.avail_out = sizeof(procBuf);
+        }
+    } while (zerr == Z_OK);
+
+    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
+
+    // success!
+    result = zstream.total_out;
+
+z_bail:
+    inflateEnd(&zstream);        /* free up any allocated structures */
+
+bail:
+    if (result != pEntry->uncompLen) {
+        if (result != -1)        // error already shown?
+            LOGW("Size mismatch on inflated file (%ld vs %ld)\n",
+                result, pEntry->uncompLen);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Stream the uncompressed data through the supplied function,
+ * passing cookie to it each time it gets called.  processFunction
+ * may be called more than once.
+ *
+ * If processFunction returns false, the operation is abandoned and
+ * mzProcessZipEntryContents() immediately returns false.
+ *
+ * This is useful for calculating the hash of an entry's uncompressed contents.
+ */
+bool mzProcessZipEntryContents(const ZipArchive *pArchive,
+    const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
+    void *cookie)
+{
+    bool ret = false;
+    off_t oldOff;
+
+    /* save current offset */
+    oldOff = lseek(pArchive->fd, 0, SEEK_CUR);
+
+    /* Seek to the beginning of the entry's compressed data. */
+    lseek(pArchive->fd, pEntry->offset, SEEK_SET);
+
+    switch (pEntry->compression) {
+    case STORED:
+        ret = processStoredEntry(pArchive, pEntry, processFunction, cookie);
+        break;
+    case DEFLATED:
+        ret = processDeflatedEntry(pArchive, pEntry, processFunction, cookie);
+        break;
+    default:
+        LOGE("Unsupported compression type %d for entry '%s'\n",
+                pEntry->compression, pEntry->fileName);
+        break;
+    }
+
+    /* restore file offset */
+    lseek(pArchive->fd, oldOff, SEEK_SET);
+    return ret;
+}
+
+static bool crcProcessFunction(const unsigned char *data, int dataLen,
+        void *crc)
+{
+    *(unsigned long *)crc = crc32(*(unsigned long *)crc, data, dataLen);
+    return true;
+}
+
+/*
+ * Check the CRC on this entry; return true if it is correct.
+ * May do other internal checks as well.
+ */
+bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry)
+{
+    unsigned long crc;
+    bool ret;
+
+    crc = crc32(0L, Z_NULL, 0);
+    ret = mzProcessZipEntryContents(pArchive, pEntry, crcProcessFunction,
+            (void *)&crc);
+    if (!ret) {
+        LOGE("Can't calculate CRC for entry\n");
+        return false;
+    }
+    if (crc != (unsigned long)pEntry->crc32) {
+        LOGW("CRC for entry %.*s (0x%08lx) != expected (0x%08lx)\n",
+                pEntry->fileNameLen, pEntry->fileName, crc, pEntry->crc32);
+        return false;
+    }
+    return true;
+}
+
+typedef struct {
+    char *buf;
+    int bufLen;
+} CopyProcessArgs;
+
+static bool copyProcessFunction(const unsigned char *data, int dataLen,
+        void *cookie)
+{
+    CopyProcessArgs *args = (CopyProcessArgs *)cookie;
+    if (dataLen <= args->bufLen) {
+        memcpy(args->buf, data, dataLen);
+        args->buf += dataLen;
+        args->bufLen -= dataLen;
+        return true;
+    }
+    return false;
+}
+
+/*
+ * Read an entry into a buffer allocated by the caller.
+ */
+bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
+        char *buf, int bufLen)
+{
+    CopyProcessArgs args;
+    bool ret;
+
+    args.buf = buf;
+    args.bufLen = bufLen;
+    ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction,
+            (void *)&args);
+    if (!ret) {
+        LOGE("Can't extract entry to buffer.\n");
+        return false;
+    }
+    return true;
+}
+
+static bool writeProcessFunction(const unsigned char *data, int dataLen,
+                                 void *cookie)
+{
+    int fd = (int)cookie;
+
+    ssize_t soFar = 0;
+    while (true) {
+        ssize_t n = write(fd, data+soFar, dataLen-soFar);
+        if (n <= 0) {
+            LOGE("Error writing %ld bytes from zip file from %p: %s\n",
+                 dataLen-soFar, data+soFar, strerror(errno));
+            if (errno != EINTR) {
+              return false;
+            }
+        } else if (n > 0) {
+            soFar += n;
+            if (soFar == dataLen) return true;
+            if (soFar > dataLen) {
+                LOGE("write overrun?  (%ld bytes instead of %d)\n",
+                     soFar, dataLen);
+                return false;
+            }
+        }
+    }
+}
+
+/*
+ * Uncompress "pEntry" in "pArchive" to "fd" at the current offset.
+ */
+bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
+    const ZipEntry *pEntry, int fd)
+{
+    bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction,
+                                         (void*)fd);
+    if (!ret) {
+        LOGE("Can't extract entry to file.\n");
+        return false;
+    }
+    return true;
+}
+
+typedef struct {
+    unsigned char* buffer;
+    long len;
+} BufferExtractCookie;
+
+static bool bufferProcessFunction(const unsigned char *data, int dataLen,
+    void *cookie) {
+    BufferExtractCookie *bec = (BufferExtractCookie*)cookie;
+
+    memmove(bec->buffer, data, dataLen);
+    bec->buffer += dataLen;
+    bec->len -= dataLen;
+
+    return true;
+}
+
+/*
+ * Uncompress "pEntry" in "pArchive" to buffer, which must be large
+ * enough to hold mzGetZipEntryUncomplen(pEntry) bytes.
+ */
+bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive,
+    const ZipEntry *pEntry, unsigned char *buffer)
+{
+    BufferExtractCookie bec;
+    bec.buffer = buffer;
+    bec.len = mzGetZipEntryUncompLen(pEntry);
+
+    bool ret = mzProcessZipEntryContents(pArchive, pEntry,
+        bufferProcessFunction, (void*)&bec);
+    if (!ret || bec.len != 0) {
+        LOGE("Can't extract entry to memory buffer.\n");
+        return false;
+    }
+    return true;
+}
+
+
+/* Helper state to make path translation easier and less malloc-happy.
+ */
+typedef struct {
+    const char *targetDir;
+    const char *zipDir;
+    char *buf;
+    int targetDirLen;
+    int zipDirLen;
+    int bufLen;
+} MzPathHelper;
+
+/* Given the values of targetDir and zipDir in the helper,
+ * return the target filename of the provided entry.
+ * The helper must be initialized first.
+ */
+static const char *targetEntryPath(MzPathHelper *helper, ZipEntry *pEntry)
+{
+    int needLen;
+    bool firstTime = (helper->buf == NULL);
+
+    /* target file <-- targetDir + / + entry[zipDirLen:]
+     */
+    needLen = helper->targetDirLen + 1 +
+            pEntry->fileNameLen - helper->zipDirLen + 1;
+    if (needLen > helper->bufLen) {
+        char *newBuf;
+
+        needLen *= 2;
+        newBuf = (char *)realloc(helper->buf, needLen);
+        if (newBuf == NULL) {
+            return NULL;
+        }
+        helper->buf = newBuf;
+        helper->bufLen = needLen;
+    }
+
+    /* Every path will start with the target path and a slash.
+     */
+    if (firstTime) {
+        char *p = helper->buf;
+        memcpy(p, helper->targetDir, helper->targetDirLen);
+        p += helper->targetDirLen;
+        if (p == helper->buf || p[-1] != '/') {
+            helper->targetDirLen += 1;
+            *p++ = '/';
+        }
+    }
+
+    /* Replace the custom part of the path with the appropriate
+     * part of the entry's path.
+     */
+    char *epath = helper->buf + helper->targetDirLen;
+    memcpy(epath, pEntry->fileName + helper->zipDirLen,
+            pEntry->fileNameLen - helper->zipDirLen);
+    epath += pEntry->fileNameLen - helper->zipDirLen;
+    *epath = '\0';
+
+    return helper->buf;
+}
+
+/*
+ * Inflate all entries under zipDir to the directory specified by
+ * targetDir, which must exist and be a writable directory.
+ *
+ * The immediate children of zipDir will become the immediate
+ * children of targetDir; e.g., if the archive contains the entries
+ *
+ *     a/b/c/one
+ *     a/b/c/two
+ *     a/b/c/d/three
+ *
+ * and mzExtractRecursive(a, "a/b/c", "/tmp") is called, the resulting
+ * files will be
+ *
+ *     /tmp/one
+ *     /tmp/two
+ *     /tmp/d/three
+ *
+ * Returns true on success, false on failure.
+ */
+bool mzExtractRecursive(const ZipArchive *pArchive,
+                        const char *zipDir, const char *targetDir,
+                        int flags, const struct utimbuf *timestamp,
+                        void (*callback)(const char *fn, void *), void *cookie,
+                        struct selabel_handle *sehnd)
+{
+    if (zipDir[0] == '/') {
+        LOGE("mzExtractRecursive(): zipDir must be a relative path.\n");
+        return false;
+    }
+    if (targetDir[0] != '/') {
+        LOGE("mzExtractRecursive(): targetDir must be an absolute path.\n");
+        return false;
+    }
+
+    unsigned int zipDirLen;
+    char *zpath;
+
+    zipDirLen = strlen(zipDir);
+    zpath = (char *)malloc(zipDirLen + 2);
+    if (zpath == NULL) {
+        LOGE("Can't allocate %d bytes for zip path\n", zipDirLen + 2);
+        return false;
+    }
+    /* If zipDir is empty, we'll extract the entire zip file.
+     * Otherwise, canonicalize the path.
+     */
+    if (zipDirLen > 0) {
+        /* Make sure there's (hopefully, exactly one) slash at the
+         * end of the path.  This way we don't need to worry about
+         * accidentally extracting "one/twothree" when a path like
+         * "one/two" is specified.
+         */
+        memcpy(zpath, zipDir, zipDirLen);
+        if (zpath[zipDirLen-1] != '/') {
+            zpath[zipDirLen++] = '/';
+        }
+    }
+    zpath[zipDirLen] = '\0';
+
+    /* Set up the helper structure that we'll use to assemble paths.
+     */
+    MzPathHelper helper;
+    helper.targetDir = targetDir;
+    helper.targetDirLen = strlen(helper.targetDir);
+    helper.zipDir = zpath;
+    helper.zipDirLen = strlen(helper.zipDir);
+    helper.buf = NULL;
+    helper.bufLen = 0;
+
+    /* Walk through the entries and extract anything whose path begins
+     * with zpath.
+//TODO: since the entries are sorted, binary search for the first match
+//      and stop after the first non-match.
+     */
+    unsigned int i;
+    bool seenMatch = false;
+    int ok = true;
+    for (i = 0; i < pArchive->numEntries; i++) {
+        ZipEntry *pEntry = pArchive->pEntries + i;
+        if (pEntry->fileNameLen < zipDirLen) {
+//TODO: look out for a single empty directory entry that matches zpath, but
+//      missing the trailing slash.  Most zip files seem to include
+//      the trailing slash, but I think it's legal to leave it off.
+//      e.g., zpath "a/b/", entry "a/b", with no children of the entry.
+            /* No chance of matching.
+             */
+#if SORT_ENTRIES
+            if (seenMatch) {
+                /* Since the entries are sorted, we can give up
+                 * on the first mismatch after the first match.
+                 */
+                break;
+            }
+#endif
+            continue;
+        }
+        /* If zpath is empty, this strncmp() will match everything,
+         * which is what we want.
+         */
+        if (strncmp(pEntry->fileName, zpath, zipDirLen) != 0) {
+#if SORT_ENTRIES
+            if (seenMatch) {
+                /* Since the entries are sorted, we can give up
+                 * on the first mismatch after the first match.
+                 */
+                break;
+            }
+#endif
+            continue;
+        }
+        /* This entry begins with zipDir, so we'll extract it.
+         */
+        seenMatch = true;
+
+        /* Find the target location of the entry.
+         */
+        const char *targetFile = targetEntryPath(&helper, pEntry);
+        if (targetFile == NULL) {
+            LOGE("Can't assemble target path for \"%.*s\"\n",
+                    pEntry->fileNameLen, pEntry->fileName);
+            ok = false;
+            break;
+        }
+
+        /* With DRY_RUN set, invoke the callback but don't do anything else.
+         */
+        if (flags & MZ_EXTRACT_DRY_RUN) {
+            if (callback != NULL) callback(targetFile, cookie);
+            continue;
+        }
+
+        /* Create the file or directory.
+         */
+#define UNZIP_DIRMODE 0755
+#define UNZIP_FILEMODE 0644
+        if (pEntry->fileName[pEntry->fileNameLen-1] == '/') {
+            if (!(flags & MZ_EXTRACT_FILES_ONLY)) {
+                int ret = dirCreateHierarchy(
+                        targetFile, UNZIP_DIRMODE, timestamp, false, sehnd);
+                if (ret != 0) {
+                    LOGE("Can't create containing directory for \"%s\": %s\n",
+                            targetFile, strerror(errno));
+                    ok = false;
+                    break;
+                }
+                LOGD("Extracted dir \"%s\"\n", targetFile);
+            }
+        } else {
+            /* This is not a directory.  First, make sure that
+             * the containing directory exists.
+             */
+            int ret = dirCreateHierarchy(
+                    targetFile, UNZIP_DIRMODE, timestamp, true, sehnd);
+            if (ret != 0) {
+                LOGE("Can't create containing directory for \"%s\": %s\n",
+                        targetFile, strerror(errno));
+                ok = false;
+                break;
+            }
+
+            /* With FILES_ONLY set, we need to ignore metadata entirely,
+             * so treat symlinks as regular files.
+             */
+            if (!(flags & MZ_EXTRACT_FILES_ONLY) && mzIsZipEntrySymlink(pEntry)) {
+                /* The entry is a symbolic link.
+                 * The relative target of the symlink is in the
+                 * data section of this entry.
+                 */
+                if (pEntry->uncompLen == 0) {
+                    LOGE("Symlink entry \"%s\" has no target\n",
+                            targetFile);
+                    ok = false;
+                    break;
+                }
+                char *linkTarget = malloc(pEntry->uncompLen + 1);
+                if (linkTarget == NULL) {
+                    ok = false;
+                    break;
+                }
+                ok = mzReadZipEntry(pArchive, pEntry, linkTarget,
+                        pEntry->uncompLen);
+                if (!ok) {
+                    LOGE("Can't read symlink target for \"%s\"\n",
+                            targetFile);
+                    free(linkTarget);
+                    break;
+                }
+                linkTarget[pEntry->uncompLen] = '\0';
+
+                /* Make the link.
+                 */
+                ret = symlink(linkTarget, targetFile);
+                if (ret != 0) {
+                    LOGE("Can't symlink \"%s\" to \"%s\": %s\n",
+                            targetFile, linkTarget, strerror(errno));
+                    free(linkTarget);
+                    ok = false;
+                    break;
+                }
+                LOGD("Extracted symlink \"%s\" -> \"%s\"\n",
+                        targetFile, linkTarget);
+                free(linkTarget);
+            } else {
+                /* The entry is a regular file.
+                 * Open the target for writing.
+                 */
+
+#ifdef HAVE_SELINUX
+                char *secontext = NULL;
+
+                if (sehnd) {
+                    selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE);
+                    setfscreatecon(secontext);
+                }
+#endif
+
+                int fd = creat(targetFile, UNZIP_FILEMODE);
+
+#ifdef HAVE_SELINUX
+                if (secontext) {
+                    freecon(secontext);
+                    setfscreatecon(NULL);
+                }
+#endif
+
+                if (fd < 0) {
+                    LOGE("Can't create target file \"%s\": %s\n",
+                            targetFile, strerror(errno));
+                    ok = false;
+                    break;
+                }
+
+                bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd);
+                close(fd);
+                if (!ok) {
+                    LOGE("Error extracting \"%s\"\n", targetFile);
+                    ok = false;
+                    break;
+                }
+
+                if (timestamp != NULL && utime(targetFile, timestamp)) {
+                    LOGE("Error touching \"%s\"\n", targetFile);
+                    ok = false;
+                    break;
+                }
+
+                LOGD("Extracted file \"%s\"\n", targetFile);
+            }
+        }
+
+        if (callback != NULL) callback(targetFile, cookie);
+    }
+
+    free(helper.buf);
+    free(zpath);
+
+    return ok;
+}
diff --git a/minzipold/Zip.h b/minzipold/Zip.h
new file mode 100644
index 0000000..4bb9ef6
--- /dev/null
+++ b/minzipold/Zip.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Simple Zip archive support.
+ */
+#ifndef _MINZIP_ZIP
+#define _MINZIP_ZIP
+
+#include "inline_magic.h"
+
+#include <stdlib.h>
+#include <utime.h>
+
+#include "Hash.h"
+#include "SysUtil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#else
+struct selabel_handle;
+#endif
+
+/*
+ * One entry in the Zip archive.  Treat this as opaque -- use accessors below.
+ *
+ * TODO: we're now keeping the pages mapped so we don't have to copy the
+ * filename.  We can change the accessors to retrieve the various pieces
+ * directly from the source file instead of copying them out, for a very
+ * slight speed hit and a modest reduction in memory usage.
+ */
+typedef struct ZipEntry {
+    unsigned int fileNameLen;
+    const char*  fileName;       // not null-terminated
+    long         offset;
+    long         compLen;
+    long         uncompLen;
+    int          compression;
+    long         modTime;
+    long         crc32;
+    int          versionMadeBy;
+    long         externalFileAttributes;
+} ZipEntry;
+
+/*
+ * One Zip archive.  Treat as opaque.
+ */
+typedef struct ZipArchive {
+    int         fd;
+    unsigned int numEntries;
+    ZipEntry*   pEntries;
+    HashTable*  pHash;          // maps file name to ZipEntry
+    MemMapping  map;
+} ZipArchive;
+
+/*
+ * Represents a non-NUL-terminated string,
+ * which is how entry names are stored.
+ */
+typedef struct {
+    const char *str;
+    size_t len;
+} UnterminatedString;
+
+/*
+ * Open a Zip archive.
+ *
+ * On success, returns 0 and populates "pArchive".  Returns nonzero errno
+ * value on failure.
+ */
+int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive);
+
+/*
+ * Close archive, releasing resources associated with it.
+ *
+ * Depending on the implementation this could unmap pages used by classes
+ * stored in a Jar.  This should only be done after unloading classes.
+ */
+void mzCloseZipArchive(ZipArchive* pArchive);
+
+
+/*
+ * Find an entry in the Zip archive, by name.
+ */
+const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive,
+        const char* entryName);
+
+/*
+ * Get the number of entries in the Zip archive.
+ */
+INLINE unsigned int mzZipEntryCount(const ZipArchive* pArchive) {
+    return pArchive->numEntries;
+}
+
+/*
+ * Get an entry by index.  Returns NULL if the index is out-of-bounds.
+ */
+INLINE const ZipEntry*
+mzGetZipEntryAt(const ZipArchive* pArchive, unsigned int index)
+{
+    if (index < pArchive->numEntries) {
+        return pArchive->pEntries + index;
+    }
+    return NULL;
+}
+
+/*
+ * Get the index number of an entry in the archive.
+ */
+INLINE unsigned int
+mzGetZipEntryIndex(const ZipArchive *pArchive, const ZipEntry *pEntry) {
+    return pEntry - pArchive->pEntries;
+}
+
+/*
+ * Simple accessors.
+ */
+INLINE UnterminatedString mzGetZipEntryFileName(const ZipEntry* pEntry) {
+    UnterminatedString ret;
+    ret.str = pEntry->fileName;
+    ret.len = pEntry->fileNameLen;
+    return ret;
+}
+INLINE long mzGetZipEntryOffset(const ZipEntry* pEntry) {
+    return pEntry->offset;
+}
+INLINE long mzGetZipEntryUncompLen(const ZipEntry* pEntry) {
+    return pEntry->uncompLen;
+}
+INLINE long mzGetZipEntryModTime(const ZipEntry* pEntry) {
+    return pEntry->modTime;
+}
+INLINE long mzGetZipEntryCrc32(const ZipEntry* pEntry) {
+    return pEntry->crc32;
+}
+bool mzIsZipEntrySymlink(const ZipEntry* pEntry);
+
+
+/*
+ * Type definition for the callback function used by
+ * mzProcessZipEntryContents().
+ */
+typedef bool (*ProcessZipEntryContentsFunction)(const unsigned char *data,
+    int dataLen, void *cookie);
+
+/*
+ * Stream the uncompressed data through the supplied function,
+ * passing cookie to it each time it gets called.  processFunction
+ * may be called more than once.
+ *
+ * If processFunction returns false, the operation is abandoned and
+ * mzProcessZipEntryContents() immediately returns false.
+ *
+ * This is useful for calculating the hash of an entry's uncompressed contents.
+ */
+bool mzProcessZipEntryContents(const ZipArchive *pArchive,
+    const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
+    void *cookie);
+
+/*
+ * Read an entry into a buffer allocated by the caller.
+ */
+bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
+        char* buf, int bufLen);
+
+/*
+ * Check the CRC on this entry; return true if it is correct.
+ * May do other internal checks as well.
+ */
+bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry);
+
+/*
+ * Inflate and write an entry to a file.
+ */
+bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
+    const ZipEntry *pEntry, int fd);
+
+/*
+ * Inflate and write an entry to a memory buffer, which must be long
+ * enough to hold mzGetZipEntryUncomplen(pEntry) bytes.
+ */
+bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive,
+    const ZipEntry *pEntry, unsigned char* buffer);
+
+/*
+ * Inflate all entries under zipDir to the directory specified by
+ * targetDir, which must exist and be a writable directory.
+ *
+ * The immediate children of zipDir will become the immediate
+ * children of targetDir; e.g., if the archive contains the entries
+ *
+ *     a/b/c/one
+ *     a/b/c/two
+ *     a/b/c/d/three
+ *
+ * and mzExtractRecursive(a, "a/b/c", "/tmp", ...) is called, the resulting
+ * files will be
+ *
+ *     /tmp/one
+ *     /tmp/two
+ *     /tmp/d/three
+ *
+ * flags is zero or more of the following:
+ *
+ *     MZ_EXTRACT_FILES_ONLY - only unpack files, not directories or symlinks
+ *     MZ_EXTRACT_DRY_RUN - don't do anything, but do invoke the callback
+ *
+ * If timestamp is non-NULL, file timestamps will be set accordingly.
+ *
+ * If callback is non-NULL, it will be invoked with each unpacked file.
+ *
+ * Returns true on success, false on failure.
+ */
+enum { MZ_EXTRACT_FILES_ONLY = 1, MZ_EXTRACT_DRY_RUN = 2 };
+bool mzExtractRecursive(const ZipArchive *pArchive,
+        const char *zipDir, const char *targetDir,
+        int flags, const struct utimbuf *timestamp,
+        void (*callback)(const char *fn, void*), void *cookie,
+        struct selabel_handle *sehnd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_MINZIP_ZIP*/
diff --git a/minzipold/inline_magic.h b/minzipold/inline_magic.h
new file mode 100644
index 0000000..59c659f
--- /dev/null
+++ b/minzipold/inline_magic.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MINZIP_INLINE_MAGIC_H_
+#define MINZIP_INLINE_MAGIC_H_
+
+#ifndef MINZIP_GENERATE_INLINES
+#define INLINE extern inline __attribute((__gnu_inline__))
+#else
+#define INLINE
+#endif
+
+#endif  // MINZIP_INLINE_MAGIC_H_
diff --git a/mmcutils/Android.mk b/mmcutils/Android.mk
new file mode 100644
index 0000000..1f905d4
--- /dev/null
+++ b/mmcutils/Android.mk
@@ -0,0 +1,28 @@
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	mmcutils.c
+
+LOCAL_MODULE := libmmcutils
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_STATIC_LIBRARY)
+
+#Added for TWRP building dynamic:
+include $(CLEAR_VARS)
+ifeq ($(BOARD_HAS_LARGE_FILESYSTEM),true)
+LOCAL_CFLAGS += -DBOARD_HAS_LARGE_FILESYSTEM
+endif
+
+LOCAL_SRC_FILES := \
+mmcutils.c
+
+LOCAL_MODULE := libmmcutils
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif	# !TARGET_SIMULATOR
diff --git a/mmcutils/mmcutils.c b/mmcutils/mmcutils.c
new file mode 100644
index 0000000..f9c7b70
--- /dev/null
+++ b/mmcutils/mmcutils.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * 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.
+ *   * Neither the name of Code Aurora Forum, Inc. 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>  // for _IOW, _IOR, mount()
+
+#include "mmcutils.h"
+
+unsigned ext3_count = 0;
+char *ext3_partitions[] = {"system", "userdata", "cache", "NONE"};
+
+unsigned vfat_count = 0;
+char *vfat_partitions[] = {"modem", "NONE"};
+
+struct MmcPartition {
+    char *device_index;
+    char *filesystem;
+    char *name;
+    unsigned dstatus;
+    unsigned dtype ;
+    unsigned dfirstsec;
+    unsigned dsize;
+};
+
+typedef struct {
+    MmcPartition *partitions;
+    int partitions_allocd;
+    int partition_count;
+} MmcState;
+
+static MmcState g_mmc_state = {
+    NULL,   // partitions
+    0,      // partitions_allocd
+    -1      // partition_count
+};
+
+#define MMC_DEVICENAME "/dev/block/mmcblk0"
+
+static void
+mmc_partition_name (MmcPartition *mbr, unsigned int type) {
+    switch(type)
+    {
+        char name[64];
+        case MMC_BOOT_TYPE:
+            sprintf(name,"boot");
+            mbr->name = strdup(name);
+            break;
+        case MMC_RECOVERY_TYPE:
+            sprintf(name,"recovery");
+            mbr->name = strdup(name);
+            break;
+        case MMC_EXT3_TYPE:
+            if (strcmp("NONE", ext3_partitions[ext3_count])) {
+                strcpy((char *)name,(const char *)ext3_partitions[ext3_count]);
+                mbr->name = strdup(name);
+                ext3_count++;
+            }
+            mbr->filesystem = strdup("ext3");
+            break;
+        case MMC_VFAT_TYPE:
+            if (strcmp("NONE", vfat_partitions[vfat_count])) {
+                strcpy((char *)name,(const char *)vfat_partitions[vfat_count]);
+                mbr->name = strdup(name);
+                vfat_count++;
+            }
+            mbr->filesystem = strdup("vfat");
+            break;
+    };
+}
+
+static int
+mmc_read_mbr (const char *device, MmcPartition *mbr) {
+    FILE *fd;
+    unsigned char buffer[512];
+    int idx, i;
+    unsigned mmc_partition_count = 0;
+    unsigned int dtype;
+    unsigned int dfirstsec;
+    unsigned int EBR_first_sec;
+    unsigned int EBR_current_sec;
+    int ret = -1;
+
+    fd = fopen(device, "r");
+    if(fd == NULL)
+    {
+        printf("Can't open device: \"%s\"\n", device);
+        goto ERROR2;
+    }
+    if ((fread(buffer, 512, 1, fd)) != 1)
+    {
+        printf("Can't read device: \"%s\"\n", device);
+        goto ERROR1;
+    }
+    /* Check to see if signature exists */
+    if ((buffer[TABLE_SIGNATURE] != 0x55) || \
+        (buffer[TABLE_SIGNATURE + 1] != 0xAA))
+    {
+        printf("Incorrect mbr signatures!\n");
+        goto ERROR1;
+    }
+    idx = TABLE_ENTRY_0;
+    for (i = 0; i < 4; i++)
+    {
+        char device_index[128];
+
+        mbr[mmc_partition_count].dstatus = \
+                    buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS];
+        mbr[mmc_partition_count].dtype   = \
+                    buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];
+        mbr[mmc_partition_count].dfirstsec = \
+                    GET_LWORD_FROM_BYTE(&buffer[idx + \
+                                        i * TABLE_ENTRY_SIZE + \
+                                        OFFSET_FIRST_SEC]);
+        mbr[mmc_partition_count].dsize  = \
+                    GET_LWORD_FROM_BYTE(&buffer[idx + \
+                                        i * TABLE_ENTRY_SIZE + \
+                                        OFFSET_SIZE]);
+        dtype  = mbr[mmc_partition_count].dtype;
+        dfirstsec = mbr[mmc_partition_count].dfirstsec;
+        mmc_partition_name(&mbr[mmc_partition_count], \
+                        mbr[mmc_partition_count].dtype);
+
+        sprintf(device_index, "%sp%d", device, (mmc_partition_count+1));
+        mbr[mmc_partition_count].device_index = strdup(device_index);
+
+        mmc_partition_count++;
+        if (mmc_partition_count == MAX_PARTITIONS)
+            goto SUCCESS;
+    }
+
+    /* See if the last partition is EBR, if not, parsing is done */
+    if (dtype != 0x05)
+    {
+        goto SUCCESS;
+    }
+
+    EBR_first_sec = dfirstsec;
+    EBR_current_sec = dfirstsec;
+
+    fseek (fd, (EBR_first_sec * 512), SEEK_SET);
+    if ((fread(buffer, 512, 1, fd)) != 1)
+        goto ERROR1;
+
+    /* Loop to parse the EBR */
+    for (i = 0;; i++)
+    {
+        char device_index[128];
+
+        if ((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE + 1] != 0xAA))
+        {
+            break;
+        }
+        mbr[mmc_partition_count].dstatus = \
+                    buffer[TABLE_ENTRY_0 + OFFSET_STATUS];
+        mbr[mmc_partition_count].dtype   = \
+                    buffer[TABLE_ENTRY_0 + OFFSET_TYPE];
+        mbr[mmc_partition_count].dfirstsec = \
+                    GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
+                                        OFFSET_FIRST_SEC])    + \
+                                        EBR_current_sec;
+        mbr[mmc_partition_count].dsize = \
+                    GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
+                                        OFFSET_SIZE]);
+        mmc_partition_name(&mbr[mmc_partition_count], \
+                        mbr[mmc_partition_count].dtype);
+
+        sprintf(device_index, "%sp%d", device, (mmc_partition_count+1));
+        mbr[mmc_partition_count].device_index = strdup(device_index);
+
+        mmc_partition_count++;
+        if (mmc_partition_count == MAX_PARTITIONS)
+            goto SUCCESS;
+
+        dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]);
+        if(dfirstsec == 0)
+        {
+            /* Getting to the end of the EBR tables */
+            break;
+        }
+        /* More EBR to follow - read in the next EBR sector */
+        fseek (fd,  ((EBR_first_sec + dfirstsec) * 512), SEEK_SET);
+        if ((fread(buffer, 512, 1, fd)) != 1)
+            goto ERROR1;
+
+        EBR_current_sec = EBR_first_sec + dfirstsec;
+    }
+
+SUCCESS:
+    ret = mmc_partition_count;
+ERROR1:
+    fclose(fd);
+ERROR2:
+    return ret;
+}
+
+int
+mmc_scan_partitions() {
+    int i;
+    ssize_t nbytes;
+
+    if (g_mmc_state.partitions == NULL) {
+        const int nump = MAX_PARTITIONS;
+        MmcPartition *partitions = malloc(nump * sizeof(*partitions));
+        if (partitions == NULL) {
+            errno = ENOMEM;
+            return -1;
+        }
+        g_mmc_state.partitions = partitions;
+        g_mmc_state.partitions_allocd = nump;
+        memset(partitions, 0, nump * sizeof(*partitions));
+    }
+    g_mmc_state.partition_count = 0;
+    ext3_count = 0;
+    vfat_count = 0;
+
+    /* Initialize all of the entries to make things easier later.
+     * (Lets us handle sparsely-numbered partitions, which
+     * may not even be possible.)
+     */
+    for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
+        MmcPartition *p = &g_mmc_state.partitions[i];
+        if (p->device_index != NULL) {
+            free(p->device_index);
+            p->device_index = NULL;
+        }
+        if (p->name != NULL) {
+            free(p->name);
+            p->name = NULL;
+        }
+        if (p->filesystem != NULL) {
+            free(p->filesystem);
+            p->filesystem = NULL;
+        }
+    }
+
+    g_mmc_state.partition_count = mmc_read_mbr(MMC_DEVICENAME, g_mmc_state.partitions);
+    if(g_mmc_state.partition_count == -1)
+    {
+        printf("Error in reading mbr!\n");
+        // keep "partitions" around so we can free the names on a rescan.
+        g_mmc_state.partition_count = -1;
+    }
+    return g_mmc_state.partition_count;
+}
+
+static const MmcPartition *
+mmc_find_partition_by_device_index(const char *device_index)
+{
+    if (g_mmc_state.partitions != NULL) {
+        int i;
+        for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
+            MmcPartition *p = &g_mmc_state.partitions[i];
+            if (p->device_index !=NULL && p->name != NULL) {
+                if (strcmp(p->device_index, device_index) == 0) {
+                    return p;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+const MmcPartition *
+mmc_find_partition_by_name(const char *name)
+{
+    if (name[0] == '/') {
+        return mmc_find_partition_by_device_index(name);
+    }
+
+    if (g_mmc_state.partitions != NULL) {
+        int i;
+        for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
+            MmcPartition *p = &g_mmc_state.partitions[i];
+            if (p->device_index !=NULL && p->name != NULL) {
+                if (strcmp(p->name, name) == 0) {
+                    return p;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+#define MKE2FS_BIN      "/sbin/mke2fs"
+#define TUNE2FS_BIN     "/sbin/tune2fs"
+#define E2FSCK_BIN      "/sbin/e2fsck"
+
+int
+run_exec_process ( char **argv) {
+    pid_t pid;
+    int status;
+    pid = fork();
+    if (pid == 0) {
+        execv(argv[0], argv);
+        fprintf(stderr, "E:Can't run (%s)\n",strerror(errno));
+        _exit(-1);
+    }
+
+    waitpid(pid, &status, 0);
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+        return 1;
+    }
+    return 0;
+}
+
+int
+format_ext3_device (const char *device) {
+    char *const mke2fs[] = {MKE2FS_BIN, "-j", "-q", device, NULL};
+    char *const tune2fs[] = {TUNE2FS_BIN, "-C", "1", device, NULL};
+    // Run mke2fs
+    if(run_exec_process(mke2fs)) {
+        printf("failure while running mke2fs\n");
+        return -1;
+    }
+
+    // Run tune2fs
+    if(run_exec_process(tune2fs)) {
+        printf("failure while running mke2fs\n");
+        return -1;
+    }
+
+    // Run e2fsck
+    char *const e2fsck[] = {E2FSCK_BIN, "-fy", device, NULL};
+    if(run_exec_process(e2fsck)) {
+        printf("failure while running e2fsck\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+format_ext2_device (const char *device) {
+    // Run mke2fs
+    char *const mke2fs[] = {MKE2FS_BIN, device, NULL};
+    if(run_exec_process(mke2fs))
+        return -1;
+
+    // Run tune2fs
+    char *const tune2fs[] = {TUNE2FS_BIN, "-C", "1", device, NULL};
+    if(run_exec_process(tune2fs))
+        return -1;
+
+    // Run e2fsck
+    char *const e2fsck[] = {E2FSCK_BIN, "-fy", device, NULL};
+    if(run_exec_process(e2fsck))
+        return -1;
+
+    return 0;
+}
+
+int
+mmc_format_ext3 (MmcPartition *partition) {
+    char device[128];
+    strcpy(device, partition->device_index);
+    return format_ext3_device(device);
+}
+
+int
+mmc_mount_partition(const MmcPartition *partition, const char *mount_point,
+        int read_only)
+{
+    const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
+    char devname[128];
+    int rv = -1;
+    strcpy(devname, partition->device_index);
+    if (partition->filesystem == NULL) {
+        printf("Null filesystem!\n");
+        return rv;
+    }
+    if (!read_only) {
+        rv = mount(devname, mount_point, partition->filesystem, flags, NULL);
+    }
+    if (read_only || rv < 0) {
+        rv = mount(devname, mount_point, partition->filesystem, flags | MS_RDONLY, 0);
+        if (rv < 0) {
+            printf("Failed to mount %s on %s: %s\n",
+                    devname, mount_point, strerror(errno));
+        } else {
+            printf("Mount %s on %s read-only\n", devname, mount_point);
+        }
+    }
+    return rv;
+}
+
+int
+mmc_raw_copy (const MmcPartition *partition, char *in_file) {
+    int ch;
+    FILE *in;
+    FILE *out;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    char *out_file = partition->device_index;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    out = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR2;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    if (sz % 512)
+    {
+        while ( ( ch = fgetc ( in ) ) != EOF )
+            fputc ( ch, out );
+    }
+    else
+    {
+        for (i=0; i< (sz/512); i++)
+        {
+            if ((fread(buf, 512, 1, in)) != 1)
+                goto ERROR1;
+            if ((fwrite(buf, 512, 1, out)) != 1)
+                goto ERROR1;
+        }
+    }
+
+    fsync(out);
+    ret = 0;
+ERROR1:
+    fclose ( out );
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+
+}
+
+
+int
+mmc_raw_dump_internal (const char* in_file, const char *out_file) {
+    int ch;
+    FILE *in;
+    FILE *out;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    out = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR2;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    if (sz % 512)
+    {
+        while ( ( ch = fgetc ( in ) ) != EOF )
+            fputc ( ch, out );
+    }
+    else
+    {
+        for (i=0; i< (sz/512); i++)
+        {
+            if ((fread(buf, 512, 1, in)) != 1)
+                goto ERROR1;
+            if ((fwrite(buf, 512, 1, out)) != 1)
+                goto ERROR1;
+        }
+    }
+
+    fsync(out);
+    ret = 0;
+ERROR1:
+    fclose ( out );
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+
+}
+
+// TODO: refactor this to not be a giant copy paste mess
+int
+mmc_raw_dump (const MmcPartition *partition, char *out_file) {
+    return mmc_raw_dump_internal(partition->device_index, out_file);
+}
+
+
+int
+mmc_raw_read (const MmcPartition *partition, char *data, int data_size) {
+    int ch;
+    FILE *in;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    char *in_file = partition->device_index;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    fread(data, data_size, 1, in);
+
+    ret = 0;
+ERROR1:
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+
+}
+
+int
+mmc_raw_write (const MmcPartition *partition, char *data, int data_size) {
+    int ch;
+    FILE *out;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    char *out_file = partition->device_index;
+
+    out  = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR3;
+
+    fwrite(data, data_size, 1, out);
+
+    ret = 0;
+ERROR1:
+ERROR2:
+    fclose ( out );
+ERROR3:
+    return ret;
+
+}
+
+int cmd_mmc_restore_raw_partition(const char *partition, const char *filename)
+{
+    if (partition[0] != '/') {
+        mmc_scan_partitions();
+        const MmcPartition *p;
+        p = mmc_find_partition_by_name(partition);
+        if (p == NULL)
+            return -1;
+        return mmc_raw_copy(p, filename);
+    }
+    else {
+        return mmc_raw_dump_internal(filename, partition);
+    }
+}
+
+int cmd_mmc_backup_raw_partition(const char *partition, const char *filename)
+{
+    if (partition[0] != '/') {
+        mmc_scan_partitions();
+        const MmcPartition *p;
+        p = mmc_find_partition_by_name(partition);
+        if (p == NULL)
+            return -1;
+        return mmc_raw_dump(p, filename);
+    }
+    else {
+        return mmc_raw_dump_internal(partition, filename);
+    }
+}
+
+int cmd_mmc_erase_raw_partition(const char *partition)
+{
+    return 0;
+}
+
+int cmd_mmc_erase_partition(const char *partition, const char *filesystem)
+{
+    mmc_scan_partitions();
+    const MmcPartition *p;
+    p = mmc_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    return mmc_format_ext3 (p);
+}
+
+int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
+{
+    mmc_scan_partitions();
+    const MmcPartition *p;
+    p = mmc_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    return mmc_mount_partition(p, mount_point, read_only);
+}
+
+int cmd_mmc_get_partition_device(const char *partition, char *device)
+{
+    mmc_scan_partitions();
+    const MmcPartition *p;
+    p = mmc_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    strcpy(device, p->device_index);
+    return 0;
+}
diff --git a/mmcutils/mmcutils.h b/mmcutils/mmcutils.h
new file mode 100644
index 0000000..5b10fdc
--- /dev/null
+++ b/mmcutils/mmcutils.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * 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.
+ *   * Neither the name of Code Aurora Forum, Inc. 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
+ */
+
+#ifndef MMCUTILS_H_
+#define MMCUTILS_H_
+
+/* Some useful define used to access the MBR/EBR table */
+#define BLOCK_SIZE                0x200
+#define TABLE_ENTRY_0             0x1BE
+#define TABLE_ENTRY_1             0x1CE
+#define TABLE_ENTRY_2             0x1DE
+#define TABLE_ENTRY_3             0x1EE
+#define TABLE_SIGNATURE           0x1FE
+#define TABLE_ENTRY_SIZE          0x010
+
+#define OFFSET_STATUS             0x00
+#define OFFSET_TYPE               0x04
+#define OFFSET_FIRST_SEC          0x08
+#define OFFSET_SIZE               0x0C
+#define COPYBUFF_SIZE             (1024 * 16)
+#define BINARY_IN_TABLE_SIZE      (16 * 512)
+#define MAX_FILE_ENTRIES          20
+
+#define MMC_BOOT_TYPE 0x48
+#define MMC_SYSTEM_TYPE 0x82
+#define MMC_USERDATA_TYPE 0x83
+#define MMC_RECOVERY_TYPE 0x71
+
+#define MMC_RCA 2
+
+#define MAX_PARTITIONS 64
+
+#define GET_LWORD_FROM_BYTE(x)    ((unsigned)*(x) | \
+        ((unsigned)*((x)+1) << 8) | \
+        ((unsigned)*((x)+2) << 16) | \
+        ((unsigned)*((x)+3) << 24))
+
+#define PUT_LWORD_TO_BYTE(x, y)   do{*(x) = (y) & 0xff;     \
+    *((x)+1) = ((y) >> 8) & 0xff;     \
+    *((x)+2) = ((y) >> 16) & 0xff;     \
+    *((x)+3) = ((y) >> 24) & 0xff; }while(0)
+
+#define GET_PAR_NUM_FROM_POS(x) ((((x) & 0x0000FF00) >> 8) + ((x) & 0x000000FF))
+
+#define MMC_BOOT_TYPE 0x48
+#define MMC_EXT3_TYPE 0x83
+#define MMC_VFAT_TYPE 0xC
+typedef struct MmcPartition MmcPartition;
+
+/* Functions */
+int mmc_scan_partitions();
+const MmcPartition *mmc_find_partition_by_name(const char *name);
+int mmc_format_ext3 (MmcPartition *partition);
+int mmc_mount_partition(const MmcPartition *partition, const char *mount_point, \
+                        int read_only);
+int mmc_raw_copy (const MmcPartition *partition, char *in_file);
+int mmc_raw_read (const MmcPartition *partition, char *data, int data_size);
+int mmc_raw_write (const MmcPartition *partition, char *data, int data_size);
+
+int format_ext2_device(const char *device);
+int format_ext3_device(const char *device);
+
+#endif  // MMCUTILS_H_
+
+
diff --git a/mounts.c b/mounts.c
new file mode 100644
index 0000000..e8b69e8
--- /dev/null
+++ b/mounts.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mount.h>
+
+#include "mounts.h"
+
+typedef struct {
+    MountedVolume *volumes;
+    int volumes_allocd;
+    int volume_count;
+} MountsState;
+
+static MountsState g_mounts_state = {
+    NULL,   // volumes
+    0,      // volumes_allocd
+    0       // volume_count
+};
+
+static inline void
+free_volume_internals(const MountedVolume *volume, int zero)
+{
+    free((char *)volume->device);
+    free((char *)volume->mount_point);
+    free((char *)volume->filesystem);
+    free((char *)volume->flags);
+    if (zero) {
+        memset((void *)volume, 0, sizeof(*volume));
+    }
+}
+
+#define PROC_MOUNTS_FILENAME   "/proc/mounts"
+
+int
+scan_mounted_volumes()
+{
+    char buf[2048];
+    const char *bufp;
+    int fd;
+    ssize_t nbytes;
+
+    if (g_mounts_state.volumes == NULL) {
+        const int numv = 32;
+        MountedVolume *volumes = malloc(numv * sizeof(*volumes));
+        if (volumes == NULL) {
+            errno = ENOMEM;
+            return -1;
+        }
+        g_mounts_state.volumes = volumes;
+        g_mounts_state.volumes_allocd = numv;
+        memset(volumes, 0, numv * sizeof(*volumes));
+    } else {
+        /* Free the old volume strings.
+         */
+        int i;
+        for (i = 0; i < g_mounts_state.volume_count; i++) {
+            free_volume_internals(&g_mounts_state.volumes[i], 1);
+        }
+    }
+    g_mounts_state.volume_count = 0;
+
+    /* Open and read the file contents.
+     */
+    fd = open(PROC_MOUNTS_FILENAME, O_RDONLY);
+    if (fd < 0) {
+        goto bail;
+    }
+    nbytes = read(fd, buf, sizeof(buf) - 1);
+    close(fd);
+    if (nbytes < 0) {
+        goto bail;
+    }
+    buf[nbytes] = '\0';
+
+    /* Parse the contents of the file, which looks like:
+     *
+     *     # cat /proc/mounts
+     *     rootfs / rootfs rw 0 0
+     *     /dev/pts /dev/pts devpts rw 0 0
+     *     /proc /proc proc rw 0 0
+     *     /sys /sys sysfs rw 0 0
+     *     /dev/block/mtdblock4 /system yaffs2 rw,nodev,noatime,nodiratime 0 0
+     *     /dev/block/mtdblock5 /data yaffs2 rw,nodev,noatime,nodiratime 0 0
+     *     /dev/block/mmcblk0p1 /sdcard vfat rw,sync,dirsync,fmask=0000,dmask=0000,codepage=cp437,iocharset=iso8859-1,utf8 0 0
+     *
+     * The zeroes at the end are dummy placeholder fields to make the
+     * output match Linux's /etc/mtab, but don't represent anything here.
+     */
+    bufp = buf;
+    while (nbytes > 0) {
+        char device[64];
+        char mount_point[64];
+        char filesystem[64];
+        char flags[128];
+        int matches;
+
+        /* %as is a gnu extension that malloc()s a string for each field.
+         */
+        matches = sscanf(bufp, "%63s %63s %63s %127s",
+                device, mount_point, filesystem, flags);
+
+        if (matches == 4) {
+            device[sizeof(device)-1] = '\0';
+            mount_point[sizeof(mount_point)-1] = '\0';
+            filesystem[sizeof(filesystem)-1] = '\0';
+            flags[sizeof(flags)-1] = '\0';
+
+            MountedVolume *v =
+                    &g_mounts_state.volumes[g_mounts_state.volume_count++];
+            v->device = strdup(device);
+            v->mount_point = strdup(mount_point);
+            v->filesystem = strdup(filesystem);
+            v->flags = strdup(flags);
+        } else {
+printf("matches was %d on <<%.40s>>\n", matches, bufp);
+        }
+
+        /* Eat the line.
+         */
+        while (nbytes > 0 && *bufp != '\n') {
+            bufp++;
+            nbytes--;
+        }
+        if (nbytes > 0) {
+            bufp++;
+            nbytes--;
+        }
+    }
+
+    return 0;
+
+bail:
+//TODO: free the strings we've allocated.
+    g_mounts_state.volume_count = 0;
+    return -1;
+}
+
+const MountedVolume *
+find_mounted_volume_by_device(const char *device)
+{
+    if (g_mounts_state.volumes != NULL) {
+        int i;
+        for (i = 0; i < g_mounts_state.volume_count; i++) {
+            MountedVolume *v = &g_mounts_state.volumes[i];
+            /* May be null if it was unmounted and we haven't rescanned.
+             */
+            if (v->device != NULL) {
+                if (strcmp(v->device, device) == 0) {
+                    return v;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+const MountedVolume *
+find_mounted_volume_by_mount_point(const char *mount_point)
+{
+    if (g_mounts_state.volumes != NULL) {
+        int i;
+        for (i = 0; i < g_mounts_state.volume_count; i++) {
+            MountedVolume *v = &g_mounts_state.volumes[i];
+            /* May be null if it was unmounted and we haven't rescanned.
+             */
+            if (v->mount_point != NULL) {
+                if (strcmp(v->mount_point, mount_point) == 0) {
+                    return v;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+int
+unmount_mounted_volume(const MountedVolume *volume)
+{
+    /* Intentionally pass NULL to umount if the caller tries
+     * to unmount a volume they already unmounted using this
+     * function.
+     */
+    int ret = umount(volume->mount_point);
+    if (ret == 0) {
+        free_volume_internals(volume, 1);
+        return 0;
+    }
+    return ret;
+}
+
+int
+remount_read_only(const MountedVolume* volume)
+{
+    return mount(volume->device, volume->mount_point, volume->filesystem,
+                 MS_NOATIME | MS_NODEV | MS_NODIRATIME |
+                 MS_RDONLY | MS_REMOUNT, 0);
+}
diff --git a/mounts.h b/mounts.h
new file mode 100644
index 0000000..ed7fb5f
--- /dev/null
+++ b/mounts.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MTDUTILS_MOUNTS_H_
+#define MTDUTILS_MOUNTS_H_
+
+typedef struct {
+ const char *device;
+ const char *mount_point;
+ const char *filesystem;
+ const char *flags;
+} MountedVolume;
+
+int scan_mounted_volumes(void);
+
+const MountedVolume *find_mounted_volume_by_device(const char *device);
+
+const MountedVolume *
+find_mounted_volume_by_mount_point(const char *mount_point);
+
+int unmount_mounted_volume(const MountedVolume *volume);
+
+int remount_read_only(const MountedVolume* volume);
+
+#endif  // MTDUTILS_MOUNTS_H_
diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk
index f04355b..cdde25f 100644
--- a/mtdutils/Android.mk
+++ b/mtdutils/Android.mk
@@ -1,18 +1,61 @@
+ifneq ($(TARGET_SIMULATOR),true)
+
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
 	mtdutils.c \
-	mounts.c
+	mounts.c 
+
+ifneq ($(filter rk30xx rk3188,$(TARGET_BOARD_PLATFORM)),)
+LOCAL_SRC_FILES += rk3xhack.c
+LOCAL_CFLAGS += -DRK3X
+endif
 
 LOCAL_MODULE := libmtdutils
+LOCAL_STATIC_LIBRARIES := libcutils libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
 
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := flash_image.c
-LOCAL_MODULE := flash_image
+
+LOCAL_SRC_FILES := \
+	mtdutils.c \
+	mounts.c 
+
+ifneq ($(filter rk30xx rk3188,$(TARGET_BOARD_PLATFORM)),)
+LOCAL_SRC_FILES += rk3xhack.c
+LOCAL_CFLAGS += -DRK3X
+endif
+
+LOCAL_MODULE := libmtdutils
+LOCAL_STATIC_LIBRARIES := libcutils libc
+
+include $(BUILD_SHARED_LIBRARY)
+
+ifeq ($(BOARD_USES_BML_OVER_MTD),true)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := bml_over_mtd.c
+LOCAL_C_INCLUDES += $(commands_recovery_local_path)/mtdutils
+LOCAL_MODULE := libbml_over_mtd
 LOCAL_MODULE_TAGS := eng
+LOCAL_CFLAGS += -Dmain=bml_over_mtd_main
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := bml_over_mtd.c
+LOCAL_MODULE := bml_over_mtd
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE_STEM := bml_over_mtd
+LOCAL_C_INCLUDES += $(commands_recovery_local_path)/mtdutils
 LOCAL_STATIC_LIBRARIES := libmtdutils
 LOCAL_SHARED_LIBRARIES := libcutils liblog libc
 include $(BUILD_EXECUTABLE)
+endif
+
+endif	# !TARGET_SIMULATOR
+
diff --git a/mtdutils/bml_over_mtd.c b/mtdutils/bml_over_mtd.c
new file mode 100644
index 0000000..12ca109
--- /dev/null
+++ b/mtdutils/bml_over_mtd.c
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+
+#include "cutils/log.h"
+
+#include <mtd/mtd-user.h>
+
+#include "mtdutils.h"
+
+#ifdef RK3X
+    #include "rk3xhack.h"
+#endif
+
+typedef struct BmlOverMtdReadContext {
+	const MtdPartition *partition;
+	char *buffer;
+	size_t consumed;
+	int fd;
+} BmlOverMtdReadContext;
+
+typedef struct BmlOverMtdWriteContext {
+	const MtdPartition *partition;
+	char *buffer;
+	size_t stored;
+	int fd;
+
+	off_t* bad_block_offsets;
+	int bad_block_alloc;
+	int bad_block_count;
+} BmlOverMtdWriteContext;
+
+
+static BmlOverMtdReadContext *bml_over_mtd_read_partition(const MtdPartition *partition)
+{
+	BmlOverMtdReadContext *ctx = (BmlOverMtdReadContext*) malloc(sizeof(BmlOverMtdReadContext));
+	if (ctx == NULL) return NULL;
+
+	ctx->buffer = malloc(partition->erase_size);
+	if (ctx->buffer == NULL) {
+		free(ctx);
+		return NULL;
+	}
+
+	char mtddevname[32];
+	sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
+	ctx->fd = open(mtddevname, O_RDONLY);
+	if (ctx->fd < 0) {
+		free(ctx);
+		free(ctx->buffer);
+		return NULL;
+	}
+
+	ctx->partition = partition;
+	ctx->consumed = partition->erase_size;
+	return ctx;
+}
+
+static void bml_over_mtd_read_close(BmlOverMtdReadContext *ctx)
+{
+	close(ctx->fd);
+	free(ctx->buffer);
+	free(ctx);
+}
+
+static BmlOverMtdWriteContext *bml_over_mtd_write_partition(const MtdPartition *partition)
+{
+	BmlOverMtdWriteContext *ctx = (BmlOverMtdWriteContext*) malloc(sizeof(BmlOverMtdWriteContext));
+	if (ctx == NULL) return NULL;
+
+	ctx->bad_block_offsets = NULL;
+	ctx->bad_block_alloc = 0;
+	ctx->bad_block_count = 0;
+
+	ctx->buffer = malloc(partition->erase_size);
+	if (ctx->buffer == NULL) {
+		free(ctx);
+		return NULL;
+	}
+
+	char mtddevname[32];
+	sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
+	ctx->fd = open(mtddevname, O_RDWR);
+	if (ctx->fd < 0) {
+		free(ctx->buffer);
+		free(ctx);
+		return NULL;
+	}
+
+	ctx->partition = partition;
+	ctx->stored = 0;
+	return ctx;
+}
+
+static int bml_over_mtd_write_close(BmlOverMtdWriteContext *ctx)
+{
+	int r = 0;
+	if (close(ctx->fd)) r = -1;
+	free(ctx->bad_block_offsets);
+	free(ctx->buffer);
+	free(ctx);
+	return r;
+}
+
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "bml_over_mtd"
+
+#define BLOCK_SIZE    2048
+#define SPARE_SIZE    (BLOCK_SIZE >> 5)
+
+#define EXIT_CODE_BAD_BLOCKS 15
+
+static int die(const char *msg, ...) {
+	int err = errno;
+	va_list args;
+	va_start(args, msg);
+	char buf[1024];
+	vsnprintf(buf, sizeof(buf), msg, args);
+	va_end(args);
+
+	if (err != 0) {
+		strlcat(buf, ": ", sizeof(buf));
+		strlcat(buf, strerror(err), sizeof(buf));
+	}
+
+	fprintf(stderr, "%s\n", buf);
+	return 1;
+}
+
+static unsigned short* CreateEmptyBlockMapping(const MtdPartition* pSrcPart)
+{
+	size_t srcTotal, srcErase, srcWrite;
+	if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0)
+	{
+		fprintf(stderr, "Failed to access partition.\n");
+		return NULL;
+	}
+
+	int numSrcBlocks = srcTotal/srcErase;
+
+	unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short));
+	if (pMapping == NULL)
+	{
+		fprintf(stderr, "Failed to allocate block mapping memory.\n");
+		return NULL;
+	}
+	memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short));
+	return pMapping;
+}
+
+static const unsigned short* CreateBlockMapping(const MtdPartition* pSrcPart, int srcPartStartBlock,
+		const MtdPartition *pReservoirPart, int reservoirPartStartBlock)
+{
+	size_t srcTotal, srcErase, srcWrite;
+	if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0)
+	{
+		fprintf(stderr, "Failed to access partition.\n");
+		return NULL;
+	}
+
+	int numSrcBlocks = srcTotal/srcErase;
+
+	unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short));
+	if (pMapping == NULL)
+	{
+		fprintf(stderr, "Failed to allocate block mapping memory.\n");
+		return NULL;
+	}
+	memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short));
+
+	size_t total, erase, write;
+	if (mtd_partition_info(pReservoirPart, &total, &erase, &write) != 0)
+	{
+		fprintf(stderr, "Failed to access reservoir partition.\n");
+		free(pMapping);
+		return NULL;
+	}
+
+	if (erase != srcErase || write != srcWrite)
+	{
+		fprintf(stderr, "Source partition and reservoir partition differ in size properties.\n");
+		free(pMapping);
+		return NULL;
+	}
+
+	printf("Partition info: Total %d, Erase %d, write %d\n", total, erase, write);
+
+	BmlOverMtdReadContext *readctx = bml_over_mtd_read_partition(pReservoirPart);
+	if (readctx == NULL)
+	{
+		fprintf(stderr, "Failed to open reservoir partition for reading.\n");
+		free(pMapping);
+		return NULL;
+	}
+
+	if (total < erase || total > INT_MAX)
+	{
+		fprintf(stderr, "Unsuitable reservoir partition properties.\n");
+		free(pMapping);
+		bml_over_mtd_read_close(readctx);
+		return NULL;
+	}
+
+	int foundMappingTable = 0;
+
+	int currOffset = total; //Offset *behind* the last byte
+	while (currOffset > 0)
+	{
+		currOffset -= erase;
+		loff_t pos = lseek64(readctx->fd, currOffset, SEEK_SET);
+		int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos);
+		if (mgbb != 0)
+		{
+			printf("Bad block %d in reservoir area, skipping.\n", currOffset/erase);
+			continue;
+		}
+		ssize_t readBytes = read(readctx->fd, readctx->buffer, erase);
+		if (readBytes != (ssize_t)erase)
+		{
+			fprintf(stderr, "Failed to read good block in reservoir area (%s).\n",
+					strerror(errno));
+			free(pMapping);
+			bml_over_mtd_read_close(readctx);
+			return NULL;
+		}
+		if (readBytes >= 0x2000)
+		{
+			char* buf = readctx->buffer;
+			if (buf[0]=='U' && buf[1]=='P' && buf[2]=='C' && buf[3]=='H')
+			{
+				printf ("Found mapping block mark at 0x%x (block %d).\n", currOffset, currOffset/erase);
+
+				unsigned short* mappings = (unsigned short*) &buf[0x1000];
+				if (mappings[0]==0 && mappings[1]==0xffff)
+				{
+					printf("Found start of mapping table.\n");
+					foundMappingTable = 1;
+					//Skip first entry (dummy)
+					unsigned short* mappingEntry = mappings + 2;
+					while (mappingEntry - mappings < 100
+							&& mappingEntry[0] != 0xffff)
+					{
+						unsigned short rawSrcBlk = mappingEntry[0];
+						unsigned short rawDstBlk = mappingEntry[1];
+
+						printf("Found raw block mapping %d -> %d\n", rawSrcBlk,
+								rawDstBlk);
+
+						unsigned int srcAbsoluteStartAddress = srcPartStartBlock * erase;
+						unsigned int resAbsoluteStartAddress = reservoirPartStartBlock * erase;
+
+						int reservoirLastBlock = reservoirPartStartBlock + numSrcBlocks - 1;
+						if (rawDstBlk < reservoirPartStartBlock
+								|| rawDstBlk*erase >= resAbsoluteStartAddress+currOffset)
+						{
+							fprintf(stderr, "Mapped block not within reasonable reservoir area.\n");
+							foundMappingTable = 0;
+							break;
+						}
+
+						int srcLastBlock = srcPartStartBlock + numSrcBlocks - 1;
+						if (rawSrcBlk >= srcPartStartBlock && rawSrcBlk <= srcLastBlock)
+						{
+
+							unsigned short relSrcBlk = rawSrcBlk - srcPartStartBlock;
+							unsigned short relDstBlk = rawDstBlk - reservoirPartStartBlock;
+							printf("Partition relative block mapping %d -> %d\n",relSrcBlk, relDstBlk);
+
+							printf("Absolute mapped start addresses 0x%x -> 0x%x\n",
+									srcAbsoluteStartAddress+relSrcBlk*erase,
+									resAbsoluteStartAddress+relDstBlk*erase);
+							printf("Partition relative mapped start addresses 0x%x -> 0x%x\n",
+									relSrcBlk*erase, relDstBlk*erase);
+
+							//Set mapping entry. For duplicate entries, later entries replace former ones.
+							//*Assumption*: Bad blocks in reservoir area will not be mapped themselves in
+							//the mapping table. User partition blocks will not be mapped to bad blocks
+							//(only) in the reservoir area. This has to be confirmed on a wider range of
+							//devices.
+							pMapping[relSrcBlk] = relDstBlk;
+
+						}
+						mappingEntry+=2;
+					}
+					break; //We found the mapping table, no need to search further
+				}
+
+
+			}
+		}
+
+	}
+	bml_over_mtd_read_close(readctx);
+
+	if (foundMappingTable == 0)
+	{
+		fprintf(stderr, "Cannot find mapping table in reservoir partition.\n");
+		free(pMapping);
+		return NULL;
+	}
+
+	//Consistency and validity check
+	int mappingValid = 1;
+	readctx = bml_over_mtd_read_partition(pSrcPart);
+	if (readctx == NULL)
+	{
+		fprintf(stderr, "Cannot open source partition for reading.\n");
+		free(pMapping);
+		return NULL;
+	}
+	int currBlock = 0;
+	for (;currBlock < numSrcBlocks; ++currBlock)
+	{
+		loff_t pos = lseek64(readctx->fd, currBlock*erase, SEEK_SET);
+		int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos);
+		if (mgbb == 0)
+		{
+			if (pMapping[currBlock]!=0xffff)
+			{
+				fprintf(stderr, "Consistency error: Good block has mapping entry %d -> %d\n", currBlock, pMapping[currBlock]);
+				mappingValid = 0;
+			}
+		} else
+		{
+			//Bad block!
+			if (pMapping[currBlock]==0xffff)
+			{
+				fprintf(stderr, "Consistency error: Bad block has no mapping entry \n");
+				mappingValid = 0;
+			} else
+			{
+				BmlOverMtdReadContext* reservoirReadCtx = bml_over_mtd_read_partition(pReservoirPart);
+				if (reservoirReadCtx == 0)
+				{
+					fprintf(stderr, "Reservoir partition cannot be opened for reading in consistency check.\n");
+					mappingValid = 0;
+				} else
+				{
+					pos = lseek64(reservoirReadCtx->fd, pMapping[currBlock]*erase, SEEK_SET);
+					mgbb = ioctl(reservoirReadCtx->fd, MEMGETBADBLOCK, &pos);
+					if (mgbb == 0)
+					{
+						printf("Bad block has properly mapped reservoir block %d -> %d\n",currBlock, pMapping[currBlock]);
+					}
+					else
+					{
+						fprintf(stderr, "Consistency error: Mapped block is bad, too. (%d -> %d)\n",currBlock, pMapping[currBlock]);
+						mappingValid = 0;
+					}
+
+				}
+				bml_over_mtd_read_close(reservoirReadCtx);
+			}
+
+		}
+
+	}
+	bml_over_mtd_read_close(readctx);
+
+
+	if (!mappingValid)
+	{
+		free(pMapping);
+		return NULL;
+	}
+
+	return pMapping;
+}
+
+static void ReleaseBlockMapping(const unsigned short* blockMapping)
+{
+	free((void*)blockMapping);
+}
+
+static int dump_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart,
+		const unsigned short* blockMapping, const char* filename)
+{
+	int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+	if (fd < 0)
+	{
+		fprintf(stderr, "error opening %s", filename);
+		return -1;
+	}
+	BmlOverMtdReadContext* pSrcRead = bml_over_mtd_read_partition(pSrcPart);
+	if (pSrcRead == NULL)
+	{
+		close(fd);
+		fprintf(stderr, "dump_bml_partition: Error opening src part for reading.\n");
+		return -1;
+	}
+
+	BmlOverMtdReadContext* pResRead = bml_over_mtd_read_partition(pReservoirPart);
+	if (pResRead == NULL)
+	{
+		close(fd);
+		bml_over_mtd_read_close(pSrcRead);
+		fprintf(stderr, "dump_bml_partition: Error opening reservoir part for reading.\n");
+		return -1;
+	}
+
+
+	int numBlocks = pSrcPart->size / pSrcPart->erase_size;
+	int currblock = 0;
+	for (;currblock < numBlocks; ++currblock)
+	{
+		int srcFd = -1;
+		if (blockMapping[currblock] == 0xffff)
+		{
+			//Good block, use src partition
+			srcFd = pSrcRead->fd;
+			if (lseek64(pSrcRead->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1)
+			{
+				close(fd);
+				bml_over_mtd_read_close(pSrcRead);
+				bml_over_mtd_read_close(pResRead);
+				fprintf(stderr, "dump_bml_partition: lseek in src partition failed\n");
+				return -1;
+			}
+		} else
+		{
+			//Bad block, use mapped block in reservoir partition
+			srcFd = pResRead->fd;
+			if (lseek64(pResRead->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1)
+			{
+				close(fd);
+				bml_over_mtd_read_close(pSrcRead);
+				bml_over_mtd_read_close(pResRead);
+				fprintf(stderr, "dump_bml_partition: lseek in reservoir partition failed\n");
+				return -1;
+			}
+		}
+		size_t blockBytesRead = 0;
+		while (blockBytesRead < pSrcPart->erase_size)
+		{
+			ssize_t len = read(srcFd, pSrcRead->buffer + blockBytesRead,
+					pSrcPart->erase_size - blockBytesRead);
+			if (len <= 0)
+			{
+				close(fd);
+				bml_over_mtd_read_close(pSrcRead);
+				bml_over_mtd_read_close(pResRead);
+				fprintf(stderr, "dump_bml_partition: reading partition failed\n");
+				return -1;
+			}
+			blockBytesRead += len;
+		}
+
+		size_t blockBytesWritten = 0;
+		while (blockBytesWritten < pSrcPart->erase_size)
+		{
+			ssize_t len = write(fd, pSrcRead->buffer + blockBytesWritten,
+					pSrcPart->erase_size - blockBytesWritten);
+			if (len <= 0)
+			{
+				close(fd);
+				bml_over_mtd_read_close(pSrcRead);
+				bml_over_mtd_read_close(pResRead);
+				fprintf(stderr, "dump_bml_partition: writing partition dump file failed\n");
+				return -1;
+			}
+			blockBytesWritten += len;
+		}
+
+	}
+
+	bml_over_mtd_read_close(pSrcRead);
+	bml_over_mtd_read_close(pResRead);
+
+	if (close(fd)) {
+		unlink(filename);
+		printf("error closing %s", filename);
+		return -1;
+	}
+
+	return 0;
+}
+
+static ssize_t bml_over_mtd_write_block(int fd, ssize_t erase_size, char* data)
+{
+	off_t pos = lseek(fd, 0, SEEK_CUR);
+	if (pos == (off_t) -1) return -1;
+
+	ssize_t size = erase_size;
+	loff_t bpos = pos;
+	int ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
+	if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) {
+		fprintf(stderr,
+				"Mapping failure: Trying to write bad block at 0x%08lx (ret %d errno %d)\n",
+				pos, ret, errno);
+		return -1;
+	}
+
+	struct erase_info_user erase_info;
+	erase_info.start = pos;
+	erase_info.length = size;
+	int retry;
+	for (retry = 0; retry < 2; ++retry) {
+#ifdef RK3X
+		if (rk30_zero_out(fd, pos, size) < 0) {
+			fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+			continue;
+		}
+#else
+		if (ioctl(fd, MEMERASE, &erase_info) < 0) {
+			fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+			continue;
+		}
+#endif
+		if (lseek(fd, pos, SEEK_SET) != pos ||
+				write(fd, data, size) != size) {
+			fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+		}
+
+		char verify[size];
+		if (lseek(fd, pos, SEEK_SET) != pos ||
+				read(fd, verify, size) != size) {
+			fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+			continue;
+		}
+		if (memcmp(data, verify, size) != 0) {
+			fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+			continue;
+		}
+
+		if (retry > 0) {
+			fprintf(stderr, "mtd: wrote block after %d retries\n", retry);
+		}
+		fprintf(stderr, "mtd: successfully wrote block at %llx\n", pos);
+		return size;  // Success!
+	}
+
+
+	fprintf(stderr, "mtd: Block at %llx could not be properly written.\n", pos);
+	// Ran out of space on the device
+	errno = ENOSPC;
+	return -1;
+}
+
+static int flash_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart,
+		const unsigned short* blockMapping, const char* filename)
+{
+	int fd = open(filename, O_RDONLY);
+	if (fd < 0)
+	{
+		fprintf(stderr, "error opening %s", filename);
+		return -1;
+	}
+	BmlOverMtdWriteContext* pSrcWrite = bml_over_mtd_write_partition(pSrcPart);
+	if (pSrcWrite == NULL)
+	{
+		close(fd);
+		fprintf(stderr, "flash_bml_partition: Error opening src part for writing.\n");
+		return -1;
+	}
+
+#ifdef DUMMY_WRITING
+	close(pSrcWrite->fd);
+	pSrcWrite->fd = open("/sdcard/srcPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666);
+#endif
+
+	BmlOverMtdWriteContext* pResWrite = bml_over_mtd_write_partition(pReservoirPart);
+	if (pResWrite == NULL)
+	{
+		close(fd);
+		bml_over_mtd_write_close(pSrcWrite);
+		fprintf(stderr, "flash_bml_partition: Error opening reservoir part for writing.\n");
+		return -1;
+	}
+#ifdef DUMMY_WRITING
+	close(pResWrite->fd);
+	pResWrite->fd = open("/sdcard/resPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666);
+#endif
+
+	struct stat fileStat;
+	if (fstat(fd, &fileStat) != 0)
+	{
+		close(fd);
+		bml_over_mtd_write_close(pSrcWrite);
+		bml_over_mtd_write_close(pResWrite);
+		fprintf(stderr, "flash_bml_partition: Failed to stat source file.\n");
+		return -1;
+
+	}
+	if (fileStat.st_size > pSrcPart->size)
+	{
+		close(fd);
+		bml_over_mtd_write_close(pSrcWrite);
+		bml_over_mtd_write_close(pResWrite);
+		fprintf(stderr, "flash_bml_partition: Source file too large for target partition.\n");
+		return -1;
+	}
+
+	int numBlocks = (fileStat.st_size +  pSrcPart->erase_size - 1) / pSrcPart->erase_size;
+	int currblock;
+	for (currblock = 0 ;currblock < numBlocks; ++currblock)
+	{
+		memset(pSrcWrite->buffer, 0xFF, pSrcPart->erase_size);
+		size_t blockBytesRead = 0;
+		while (blockBytesRead < pSrcPart->erase_size)
+		{
+			ssize_t len = read(fd, pSrcWrite->buffer + blockBytesRead,
+					pSrcPart->erase_size - blockBytesRead);
+			if (len < 0)
+			{
+				close(fd);
+				bml_over_mtd_write_close(pSrcWrite);
+				bml_over_mtd_write_close(pResWrite);
+				fprintf(stderr, "flash_bml_partition: read source file failed\n");
+				return -1;
+			}
+			if (len == 0)
+			{
+				//End of file
+				break;
+			}
+
+			blockBytesRead += len;
+		}
+
+
+
+		int srcFd = -1;
+		if (blockMapping[currblock] == 0xffff)
+		{
+			//Good block, use src partition
+			srcFd = pSrcWrite->fd;
+			if (lseek64(pSrcWrite->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1)
+			{
+				close(fd);
+				bml_over_mtd_write_close(pSrcWrite);
+				bml_over_mtd_write_close(pResWrite);
+				fprintf(stderr, "flash_bml_partition: lseek in src partition failed\n");
+				return -1;
+			}
+		} else
+		{
+			//Bad block, use mapped block in reservoir partition
+			srcFd = pResWrite->fd;
+			if (lseek64(pResWrite->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1)
+			{
+				close(fd);
+				bml_over_mtd_write_close(pSrcWrite);
+				bml_over_mtd_write_close(pResWrite);
+				fprintf(stderr, "flash_bml_partition: lseek in reservoir partition failed\n");
+				return -1;
+			}
+		}
+		size_t blockBytesWritten = 0;
+		while (blockBytesWritten < pSrcPart->erase_size)
+		{
+#ifdef DUMMY_WRITING
+			ssize_t len = write(srcFd, pSrcWrite->buffer + blockBytesWritten,
+					pSrcPart->erase_size - blockBytesWritten);
+#else
+			ssize_t len = bml_over_mtd_write_block(srcFd, pSrcPart->erase_size, pSrcWrite->buffer);
+#endif
+			if (len <= 0)
+			{
+				close(fd);
+				bml_over_mtd_write_close(pSrcWrite);
+				bml_over_mtd_write_close(pResWrite);
+				fprintf(stderr, "flash_bml_partition: writing to partition failed\n");
+				return -1;
+			}
+			blockBytesWritten += len;
+		}
+
+
+	}
+
+	bml_over_mtd_write_close(pSrcWrite);
+	bml_over_mtd_write_close(pResWrite);
+
+	if (close(fd)) {
+		printf("error closing %s", filename);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int scan_partition(const MtdPartition* pPart)
+{
+	BmlOverMtdReadContext* readCtx = bml_over_mtd_read_partition(pPart);
+	if (readCtx == NULL)
+	{
+		fprintf(stderr, "Failed to open partition for reading.\n");
+		return -1;
+	}
+
+	int numBadBlocks = 0;
+	size_t numBlocks = pPart->size / pPart->erase_size;
+	size_t currBlock;
+	for (currBlock = 0; currBlock < numBlocks; ++currBlock)
+	{
+
+		loff_t pos = currBlock * pPart->erase_size;
+		int mgbb = ioctl(readCtx->fd, MEMGETBADBLOCK, &pos);
+		if (mgbb != 0)
+		{
+			printf("Bad block %d at 0x%x.\n", currBlock, (unsigned int)pos);
+			numBadBlocks++;
+		}
+	}
+
+	bml_over_mtd_read_close(readCtx);
+	if (numBadBlocks == 0)
+	{
+		printf("No bad blocks.\n");
+		return 0;
+	}
+	return -1 ;
+}
+
+int main(int argc, char **argv)
+{
+	if (argc != 7 && (argc != 3 || (argc == 3 && strcmp(argv[1],"scan"))!=0)
+			&& (argc != 6 || (argc == 6 && strcmp(argv[1],"scan"))!=0))
+		return die("Usage: %s dump|flash <partition> <partition_start_block> <reservoirpartition> <reservoir_start_block> <file>\n"
+				"E.g. %s dump boot 72 reservoir 2004 file.bin\n"
+				"Usage: %s scan <partition> [<partition_start_block> <reservoirpartition> <reservoir_start_block>]\n"
+				,argv[0], argv[0], argv[0]);
+	int num_partitions = mtd_scan_partitions();
+	const MtdPartition *pSrcPart = mtd_find_partition_by_name(argv[2]);
+	if (pSrcPart == NULL)
+		return die("Cannot find partition %s", argv[2]);
+
+	int scanResult = scan_partition(pSrcPart);
+
+	if (argc == 3 && strcmp(argv[1],"scan")==0)
+	{
+		return (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS);
+	}
+
+	int retVal = 0;
+	const MtdPartition* pReservoirPart = mtd_find_partition_by_name(argv[4]);
+	if (pReservoirPart == NULL)
+		return die("Cannot find partition %s", argv[4]);
+
+	int srcPartStartBlock = atoi(argv[3]);
+	int reservoirPartStartBlock = atoi(argv[5]);
+	const unsigned short* pMapping = CreateBlockMapping(pSrcPart, srcPartStartBlock,
+			pReservoirPart, reservoirPartStartBlock);
+
+	if (pMapping == NULL && scanResult == 0)
+	{
+		printf("Generating empty block mapping table for error-free partition.\n");
+		pMapping = CreateEmptyBlockMapping(pSrcPart);
+	}
+
+	if (argc == 6 && strcmp(argv[1],"scan")==0)
+	{
+		retVal = (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS);
+	}
+
+	if (pMapping == NULL)
+		return die("Failed to create block mapping table");
+
+	if (strcmp(argv[1],"dump")==0)
+	{
+		retVal = dump_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]);
+		if (retVal == 0)
+			printf("Successfully dumped partition to %s\n", argv[6]);
+	}
+
+	if (strcmp(argv[1],"flash")==0)
+	{
+		retVal = flash_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]);
+		if (retVal == 0)
+			printf("Successfully wrote %s to partition\n", argv[6]);
+
+	}
+
+
+	ReleaseBlockMapping(pMapping);
+	return retVal;
+}
+
diff --git a/mtdutils/mounts.h b/mtdutils/mounts.h
index d721355..30b2927 100644
--- a/mtdutils/mounts.h
+++ b/mtdutils/mounts.h
@@ -17,10 +17,6 @@
 #ifndef MTDUTILS_MOUNTS_H_
 #define MTDUTILS_MOUNTS_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef struct MountedVolume MountedVolume;
 
 int scan_mounted_volumes(void);
@@ -34,8 +30,4 @@
 
 int remount_read_only(const MountedVolume* volume);
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif  // MTDUTILS_MOUNTS_H_
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index d04b26e..158c880 100644
--- a/mtdutils/mtdutils.c
+++ b/mtdutils/mtdutils.c
@@ -28,12 +28,9 @@
 
 #include "mtdutils.h"
 
-struct MtdPartition {
-    int device_index;
-    unsigned int size;
-    unsigned int erase_size;
-    char *name;
-};
+#ifdef RK3X
+    #include "rk3xhack.h"
+#endif
 
 struct MtdReadContext {
     const MtdPartition *partition;
@@ -328,8 +325,8 @@
 
 ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
 {
-    size_t read = 0;
-    while (read < len) {
+    ssize_t read = 0;
+    while (read < (int) len) {
         if (ctx->consumed < ctx->partition->erase_size) {
             size_t avail = ctx->partition->erase_size - ctx->consumed;
             size_t copy = len - read < avail ? len - read : avail;
@@ -345,12 +342,12 @@
             read += ctx->partition->erase_size;
         }
 
-        if (read >= len) {
+        if (read >= (int)len) {
             return read;
         }
 
         // Read the next block into the buffer
-        if (ctx->consumed == ctx->partition->erase_size && read < len) {
+        if (ctx->consumed == ctx->partition->erase_size && read < (int) len) {
             if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
             ctx->consumed = 0;
         }
@@ -430,11 +427,19 @@
         erase_info.length = size;
         int retry;
         for (retry = 0; retry < 2; ++retry) {
+#ifdef RK3X
+            if (rk30_zero_out(fd, pos, size) < 0) {
+                fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+                        pos, strerror(errno));
+                continue;
+            }
+#else
             if (ioctl(fd, MEMERASE, &erase_info) < 0) {
                 printf("mtd: erase failure at 0x%08lx (%s)\n",
                         pos, strerror(errno));
                 continue;
             }
+#endif
             if (lseek(fd, pos, SEEK_SET) != pos ||
                 write(fd, data, size) != size) {
                 printf("mtd: write error at 0x%08lx (%s)\n",
@@ -464,7 +469,12 @@
         // Try to erase it once more as we give up on this block
         add_bad_block_offset(ctx, pos);
         printf("mtd: skipping write block at 0x%08lx\n", pos);
+#ifdef RK3X
+        rk30_zero_out(fd, pos, size);
+#else
+
         ioctl(fd, MEMERASE, &erase_info);
+#endif
         pos += partition->erase_size;
     }
 
@@ -534,9 +544,15 @@
         struct erase_info_user erase_info;
         erase_info.start = pos;
         erase_info.length = ctx->partition->erase_size;
+#ifdef RK3X
+        if (rk30_zero_out(ctx->fd, pos, ctx->partition->erase_size) < 0) {
+            fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
+        }
+#else
         if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) {
             printf("mtd: erase failure at 0x%08lx\n", pos);
         }
+#endif
         pos += ctx->partition->erase_size;
     }
 
@@ -569,3 +585,209 @@
     }
     return pos;
 }
+
+#define BLOCK_SIZE    2048
+#define SPARE_SIZE    (BLOCK_SIZE >> 5)
+#define HEADER_SIZE 2048
+
+int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
+{
+    const MtdPartition *ptn;
+    MtdWriteContext *write;
+    void *data;
+
+    FILE* f = fopen(filename, "rb");
+    if (f == NULL) {
+        fprintf(stderr, "error opening %s", filename);
+        return -1;
+    }
+
+    if (mtd_scan_partitions() <= 0)
+    {
+        fprintf(stderr, "error scanning partitions");
+        return -1;
+    }
+    const MtdPartition *mtd = mtd_find_partition_by_name(partition_name);
+    if (mtd == NULL)
+    {
+        fprintf(stderr, "can't find %s partition", partition_name);
+        return -1;
+    }
+
+    int fd = open(filename, O_RDONLY);
+    if (fd < 0)
+    {
+        printf("error opening %s", filename);
+        return -1;
+    }
+    
+    MtdWriteContext* ctx = mtd_write_partition(mtd);
+    if (ctx == NULL) {
+        printf("error writing %s", partition_name);
+        return -1;
+    }
+
+    int success = 1;
+    char* buffer = malloc(BUFSIZ);
+    int read;
+    while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
+        int wrote = mtd_write_data(ctx, buffer, read);
+        success = success && (wrote == read);
+    }
+    free(buffer);
+    fclose(f);
+
+    if (!success) {
+        fprintf(stderr, "error writing %s", partition_name);
+        return -1;
+    }
+
+    if (mtd_erase_blocks(ctx, -1) == -1) {
+        fprintf(stderr, "error erasing blocks of %s\n", partition_name);
+    }
+    if (mtd_write_close(ctx) != 0) {
+        fprintf(stderr, "error closing write of %s\n", partition_name);
+    }
+    printf("%s %s partition\n", success ? "wrote" : "failed to write", partition_name);
+    return 0;
+}
+
+
+int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
+{
+    MtdReadContext *in;
+    const MtdPartition *partition;
+    char buf[BLOCK_SIZE + SPARE_SIZE];
+    size_t partition_size;
+    size_t read_size;
+    size_t total;
+    int fd;
+    int wrote;
+    int len;
+
+    if (mtd_scan_partitions() <= 0)
+    {
+        printf("error scanning partitions");
+        return -1;
+    }
+
+    partition = mtd_find_partition_by_name(partition_name);
+    if (partition == NULL)
+    {
+        printf("can't find %s partition", partition_name);
+        return -1;
+    }
+
+    if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
+        printf("can't get info of partition %s", partition_name);
+        return -1;
+    }
+
+    if (!strcmp(filename, "-")) {
+        fd = fileno(stdout);
+    }
+    else {
+        fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+    }
+
+    if (fd < 0)
+    {
+       printf("error opening %s", filename);
+       return -1;
+    }
+
+    in = mtd_read_partition(partition);
+    if (in == NULL) {
+        close(fd);
+        unlink(filename);
+        printf("error opening %s: %s\n", partition_name, strerror(errno));
+        return -1;
+    }
+
+    total = 0;
+    while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
+        wrote = write(fd, buf, len);
+        if (wrote != len) {
+            close(fd);
+            unlink(filename);
+            printf("error writing %s", filename);
+            return -1;
+        }
+        total += BLOCK_SIZE;
+    }
+
+    mtd_read_close(in);
+
+    if (close(fd)) {
+        unlink(filename);
+        printf("error closing %s", filename);
+        return -1;
+    }
+    return 0;
+}
+
+int cmd_mtd_erase_raw_partition(const char *partition_name)
+{
+    MtdWriteContext *out;
+    size_t erased;
+    size_t total_size;
+    size_t erase_size;
+
+    if (mtd_scan_partitions() <= 0)
+    {
+        printf("error scanning partitions");
+        return -1;
+    }
+    const MtdPartition *p = mtd_find_partition_by_name(partition_name);
+    if (p == NULL)
+    {
+        printf("can't find %s partition", partition_name);
+        return -1;
+    }
+
+    out = mtd_write_partition(p);
+    if (out == NULL)
+    {
+        printf("could not estabilish write context for %s", partition_name);
+        return -1;
+    }
+
+    // do the actual erase, -1 = full partition erase
+    erased = mtd_erase_blocks(out, -1);
+
+    // erased = bytes erased, if zero, something borked
+    if (!erased)
+    {
+        printf("error erasing %s", partition_name);
+        return -1;
+    }
+
+    return 0;
+}
+
+int cmd_mtd_erase_partition(const char *partition, const char *filesystem)
+{
+    return cmd_mtd_erase_raw_partition(partition);
+}
+
+
+int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
+{
+    mtd_scan_partitions();
+    const MtdPartition *p;
+    p = mtd_find_partition_by_name(partition);
+    if (p == NULL) {
+        return -1;
+    }
+    return mtd_mount_partition(p, mount_point, filesystem, read_only);
+}
+
+int cmd_mtd_get_partition_device(const char *partition, char *device)
+{
+    mtd_scan_partitions();
+    MtdPartition *p = mtd_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    sprintf(device, "/dev/block/mtdblock%d", p->device_index);
+    return 0;
+}
diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h
index 2708c43..c57d45d 100644
--- a/mtdutils/mtdutils.h
+++ b/mtdutils/mtdutils.h
@@ -19,10 +19,6 @@
 
 #include <sys/types.h>  // for size_t, etc.
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef struct MtdPartition MtdPartition;
 
 int mtd_scan_partitions(void);
@@ -57,8 +53,11 @@
 off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos);
 int mtd_write_close(MtdWriteContext *);
 
-#ifdef __cplusplus
-}
-#endif
+struct MtdPartition {
+    int device_index;
+    unsigned int size;
+    unsigned int erase_size;
+    char *name;
+};
 
 #endif  // MTDUTILS_H_
diff --git a/mtdutils/rk3xhack.c b/mtdutils/rk3xhack.c
new file mode 100644
index 0000000..930f605
--- /dev/null
+++ b/mtdutils/rk3xhack.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, Sergey 'Jin' Bostandzhyan
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This is a hack for Rockchip rk3x based devices. The problem is that
+ * the MEMERASE ioctl is failing (hangs and never returns) in their kernel.
+ * The sources are not fully available, so fixing it in the rk30xxnand_ko driver
+ * is not possible.
+ *
+ * I straced the stock recovery application and it seems to avoid this
+ * particular ioctl, instead it is simply writing zeroes using the write() call.
+ *
+ * This workaround does the same and will replace all MEMERASE occurances in
+ * the recovery code.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "rk3xhack.h"
+
+int rk30_zero_out(int fd, off_t pos, ssize_t size)
+{
+    if (lseek(fd, pos, SEEK_SET) != pos) {
+        fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+                pos, strerror(errno));
+        return -1;
+    }
+
+    unsigned char *zb = (unsigned char *)calloc(1, size);
+    if (zb == NULL) {
+        fprintf(stderr, "mtd: erase failure, could not allocate memory\n");
+        return -1;
+    }
+
+    if (write(fd, zb, size) != size) {
+        fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+                pos, strerror(errno));
+        free(zb);
+        return -1;
+    }
+
+    free(zb);
+    return 0;
+}
diff --git a/mtdutils/rk3xhack.h b/mtdutils/rk3xhack.h
new file mode 100644
index 0000000..3cc16e4
--- /dev/null
+++ b/mtdutils/rk3xhack.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013 Sergey 'Jin' Bostandzhyan
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This is a hack for Rockchip rk3x based devices. The problem is that
+ * the MEMERASE ioctl is failing (hangs and never returns) in their kernel.
+ * The sources are not fully available, so fixing it in the rk30xxnand_ko driver
+ * is not possible.
+ *
+ * I straced the stock recovery application and it seems to avoid this
+ * particular ioctl, instead it is simply writing zeroes using the write() call.
+ *
+ * This workaround does the same and will replace all MEMERASE occurances in
+ * the recovery code.
+ */
+
+#ifndef __RK3X_HACK_H__
+#define __RK3X_HACK_H__
+
+#include <sys/types.h>  // for size_t, etc.
+
+// write zeroes to fd at position pos
+int zero_out(int fd, off_t pos, ssize_t length);
+
+#endif//__RK3X_HACK_H__
diff --git a/mtp/Android.mk b/mtp/Android.mk
new file mode 100755
index 0000000..809a29a
--- /dev/null
+++ b/mtp/Android.mk
@@ -0,0 +1,70 @@
+LOCAL_PATH := $(call my-dir)
+
+# Build libtwrpmtp library
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtwrpmtp
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -fno-strict-aliasing
+LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic external/stlport/stlport frameworks/base/include system/core/include bionic/libc/private/
+LOCAL_SRC_FILES = \
+    btree.cpp \
+    MtpDataPacket.cpp \
+    MtpDebug.cpp \
+    MtpDevice.cpp \
+    MtpDeviceInfo.cpp \
+    MtpEventPacket.cpp \
+    MtpObjectInfo.cpp \
+    MtpPacket.cpp \
+    MtpProperty.cpp \
+    MtpRequestPacket.cpp \
+    MtpResponsePacket.cpp \
+    MtpServer.cpp \
+    MtpStorage.cpp \
+    MtpStorageInfo.cpp \
+    MtpStringBuffer.cpp \
+    MtpUtils.cpp \
+    mtp_MtpServer.cpp \
+    twrpMtp.cpp \
+    mtp_MtpDatabase.cpp \
+    node.cpp
+LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libstlport libdl libcutils libutils
+
+ifneq ($(TW_MTP_DEVICE),)
+	LOCAL_CFLAGS += -DUSB_MTP_DEVICE=$(TW_MTP_DEVICE)
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Build twrpmtp binary / executable
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := twrpmtp
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -DTWRPMTP
+LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic external/stlport/stlport frameworks/base/include system/core/include bionic/libc/private/
+LOCAL_SRC_FILES = \
+    btree.cpp \
+    MtpDataPacket.cpp \
+    MtpDebug.cpp \
+    MtpDevice.cpp \
+    MtpDeviceInfo.cpp \
+    MtpEventPacket.cpp \
+    MtpObjectInfo.cpp \
+    MtpPacket.cpp \
+    MtpProperty.cpp \
+    MtpRequestPacket.cpp \
+    MtpResponsePacket.cpp \
+    MtpServer.cpp \
+    MtpStorage.cpp \
+    MtpStorageInfo.cpp \
+    MtpStringBuffer.cpp \
+    MtpUtils.cpp \
+    mtp_MtpServer.cpp \
+    twrpMtp.cpp \
+    mtp_MtpDatabase.cpp \
+    node.cpp
+LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libstlport libdl libcutils libutils
+include $(BUILD_EXECUTABLE)
diff --git a/mtp/MtpDataPacket.cpp b/mtp/MtpDataPacket.cpp
new file mode 100755
index 0000000..2c51725
--- /dev/null
+++ b/mtp/MtpDataPacket.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to c++
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <usbhost/usbhost.h>
+
+#include "MtpDataPacket.h"
+#include "MtpStringBuffer.h"
+#include "MtpDebug.h"
+
+#define MTP_BUFFER_SIZE 16384
+
+
+MtpDataPacket::MtpDataPacket()
+	:   MtpPacket(MTP_BUFFER_SIZE),   // MAX_USBFS_BUFFER_SIZE
+		mOffset(MTP_CONTAINER_HEADER_SIZE)
+{
+}
+
+MtpDataPacket::~MtpDataPacket() {
+}
+
+void MtpDataPacket::reset() {
+	MtpPacket::reset();
+	mOffset = MTP_CONTAINER_HEADER_SIZE;
+}
+
+void MtpDataPacket::setOperationCode(MtpOperationCode code) {
+	MtpPacket::putUInt16(MTP_CONTAINER_CODE_OFFSET, code);
+}
+
+void MtpDataPacket::setTransactionID(MtpTransactionID id) {
+	MtpPacket::putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
+}
+
+uint16_t MtpDataPacket::getUInt16() {
+	int offset = mOffset;
+	uint16_t result = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8);
+	mOffset += 2;
+	return result;
+}
+
+uint32_t MtpDataPacket::getUInt32() {
+	int offset = mOffset;
+	uint32_t result = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) |
+		   ((uint32_t)mBuffer[offset + 2] << 16)  | ((uint32_t)mBuffer[offset + 3] << 24);
+	mOffset += 4;
+	return result;
+}
+
+uint64_t MtpDataPacket::getUInt64() {
+	int offset = mOffset;
+	uint64_t result = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) |
+		   ((uint64_t)mBuffer[offset + 2] << 16) | ((uint64_t)mBuffer[offset + 3] << 24) |
+		   ((uint64_t)mBuffer[offset + 4] << 32) | ((uint64_t)mBuffer[offset + 5] << 40) |
+		   ((uint64_t)mBuffer[offset + 6] << 48)  | ((uint64_t)mBuffer[offset + 7] << 56);
+	mOffset += 8;
+	return result;
+}
+
+void MtpDataPacket::getUInt128(uint128_t& value) {
+	value[0] = getUInt32();
+	value[1] = getUInt32();
+	value[2] = getUInt32();
+	value[3] = getUInt32();
+}
+
+void MtpDataPacket::getString(MtpStringBuffer& string)
+{
+	string.readFromPacket(this);
+}
+
+Int8List* MtpDataPacket::getAInt8() {
+	Int8List* result = new Int8List;
+	int count = getUInt32();
+	for (int i = 0; i < count; i++)
+		result->push(getInt8());
+	return result;
+}
+
+UInt8List* MtpDataPacket::getAUInt8() {
+	UInt8List* result = new UInt8List;
+	int count = getUInt32();
+	for (int i = 0; i < count; i++)
+		result->push(getUInt8());
+	return result;
+}
+
+Int16List* MtpDataPacket::getAInt16() {
+	Int16List* result = new Int16List;
+	int count = getUInt32();
+	for (int i = 0; i < count; i++)
+		result->push(getInt16());
+	return result;
+}
+
+UInt16List* MtpDataPacket::getAUInt16() {
+	UInt16List* result = new UInt16List;
+	int count = getUInt32();
+	for (int i = 0; i < count; i++)
+		result->push(getUInt16());
+	return result;
+}
+
+Int32List* MtpDataPacket::getAInt32() {
+	Int32List* result = new Int32List;
+	int count = getUInt32();
+	for (int i = 0; i < count; i++)
+		result->push(getInt32());
+	return result;
+}
+
+UInt32List* MtpDataPacket::getAUInt32() {
+	UInt32List* result = new UInt32List;
+	int count = getUInt32();
+	for (int i = 0; i < count; i++)
+		result->push(getUInt32());
+	return result;
+}
+
+Int64List* MtpDataPacket::getAInt64() {
+	Int64List* result = new Int64List;
+	int count = getUInt32();
+	for (int i = 0; i < count; i++)
+		result->push(getInt64());
+	return result;
+}
+
+UInt64List* MtpDataPacket::getAUInt64() {
+	UInt64List* result = new UInt64List;
+	int count = getUInt32();
+	for (int i = 0; i < count; i++)
+		result->push(getUInt64());
+	return result;
+}
+
+void MtpDataPacket::putInt8(int8_t value) {
+	allocate(mOffset + 1);
+	mBuffer[mOffset++] = (uint8_t)value;
+	if (mPacketSize < mOffset)
+		mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt8(uint8_t value) {
+	allocate(mOffset + 1);
+	mBuffer[mOffset++] = (uint8_t)value;
+	if (mPacketSize < mOffset)
+		mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt16(int16_t value) {
+	allocate(mOffset + 2);
+	mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+	if (mPacketSize < mOffset)
+		mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt16(uint16_t value) {
+	allocate(mOffset + 2);
+	mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+	if (mPacketSize < mOffset)
+		mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt32(int32_t value) {
+	allocate(mOffset + 4);
+	mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+	if (mPacketSize < mOffset)
+		mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt32(uint32_t value) {
+	allocate(mOffset + 4);
+	mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+	if (mPacketSize < mOffset)
+		mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt64(int64_t value) {
+	allocate(mOffset + 8);
+	mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF);
+	if (mPacketSize < mOffset)
+		mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt64(uint64_t value) {
+	allocate(mOffset + 8);
+	mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF);
+	mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF);
+	if (mPacketSize < mOffset)
+		mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt128(const int128_t& value) {
+	putInt32(value[0]);
+	putInt32(value[1]);
+	putInt32(value[2]);
+	putInt32(value[3]);
+}
+
+void MtpDataPacket::putUInt128(const uint128_t& value) {
+	putUInt32(value[0]);
+	putUInt32(value[1]);
+	putUInt32(value[2]);
+	putUInt32(value[3]);
+}
+
+void MtpDataPacket::putInt128(int64_t value) {
+	putInt64(value);
+	putInt64(value < 0 ? -1 : 0);
+}
+
+void MtpDataPacket::putUInt128(uint64_t value) {
+	putUInt64(value);
+	putUInt64(0);
+}
+
+void MtpDataPacket::putAInt8(const int8_t* values, int count) {
+	putUInt32(count);
+	for (int i = 0; i < count; i++)
+		putInt8(*values++);
+}
+
+void MtpDataPacket::putAUInt8(const uint8_t* values, int count) {
+	putUInt32(count);
+	for (int i = 0; i < count; i++)
+		putUInt8(*values++);
+}
+
+void MtpDataPacket::putAInt16(const int16_t* values, int count) {
+	putUInt32(count);
+	for (int i = 0; i < count; i++)
+		putInt16(*values++);
+}
+
+void MtpDataPacket::putAUInt16(const uint16_t* values, int count) {
+	putUInt32(count);
+	for (int i = 0; i < count; i++)
+		putUInt16(*values++);
+}
+
+void MtpDataPacket::putAUInt16(const UInt16List* values) {
+	size_t count = (values ? values->size() : 0);
+	putUInt32(count);
+	for (size_t i = 0; i < count; i++)
+		putUInt16((*values)[i]);
+}
+
+void MtpDataPacket::putAInt32(const int32_t* values, int count) {
+	putUInt32(count);
+	for (int i = 0; i < count; i++)
+		putInt32(*values++);
+}
+
+void MtpDataPacket::putAUInt32(const uint32_t* values, int count) {
+	putUInt32(count);
+	for (int i = 0; i < count; i++)
+		putUInt32(*values++);
+}
+
+void MtpDataPacket::putAUInt32(const UInt32List* list) {
+	if (!list) {
+		putEmptyArray();
+	} else {
+		size_t size = list->size();
+		putUInt32(size);
+		for (size_t i = 0; i < size; i++)
+			putUInt32((*list)[i]);
+	}
+}
+
+void MtpDataPacket::putAInt64(const int64_t* values, int count) {
+	putUInt32(count);
+	for (int i = 0; i < count; i++)
+		putInt64(*values++);
+}
+
+void MtpDataPacket::putAUInt64(const uint64_t* values, int count) {
+	putUInt32(count);
+	for (int i = 0; i < count; i++)
+		putUInt64(*values++);
+}
+
+void MtpDataPacket::putString(const MtpStringBuffer& string) {
+	string.writeToPacket(this);
+}
+
+void MtpDataPacket::putString(const char* s) {
+	MtpStringBuffer string(s);
+	string.writeToPacket(this);
+}
+
+void MtpDataPacket::putString(const uint16_t* string) {
+	int count = 0;
+	for (int i = 0; i < 256; i++) {
+		if (string[i])
+			count++;
+		else
+			break;
+	}
+	putUInt8(count > 0 ? count + 1 : 0);
+	for (int i = 0; i < count; i++)
+		putUInt16(string[i]);
+	// only terminate with zero if string is not empty
+	if (count > 0)
+		putUInt16(0);
+}
+
+#ifdef MTP_DEVICE 
+int MtpDataPacket::read(int fd) {
+	int ret = ::read(fd, mBuffer, MTP_BUFFER_SIZE);
+	if (ret < MTP_CONTAINER_HEADER_SIZE)
+		return -1;
+	mPacketSize = ret;
+	mOffset = MTP_CONTAINER_HEADER_SIZE;
+	return ret;
+}
+
+int MtpDataPacket::write(int fd) {
+	MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+	MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+	int ret = ::write(fd, mBuffer, mPacketSize);
+	return (ret < 0 ? ret : 0);
+}
+
+int MtpDataPacket::writeData(int fd, void* data, uint32_t length) {
+	allocate(length);
+	memcpy(mBuffer + MTP_CONTAINER_HEADER_SIZE, data, length);
+	length += MTP_CONTAINER_HEADER_SIZE;
+	MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
+	MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+	int ret = ::write(fd, mBuffer, length);
+	return (ret < 0 ? ret : 0);
+}
+
+#endif // MTP_DEVICE
+
+#ifdef MTP_HOST
+int MtpDataPacket::read(struct usb_request *request) {
+	// first read the header
+	request->buffer = mBuffer;
+	request->buffer_length = mBufferSize;
+	int32_t length = transfer(request);
+	if (length >= MTP_CONTAINER_HEADER_SIZE) {
+		// look at the length field to see if the data spans multiple packets
+		uint32_t totalLength = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET);
+		allocate(totalLength);
+		while (totalLength > (uint32_t)length) {
+			request->buffer = mBuffer + length;
+			request->buffer_length = totalLength - length;
+			int ret = transfer(request);
+			if (ret >= 0)
+				length += ret;
+			else {
+				length = ret;
+				break;
+			}
+		}
+	}
+	if (length >= 0)
+		mPacketSize = length;
+	return length;
+}
+
+int MtpDataPacket::readData(struct usb_request *request, void* buffer, int length) {
+	int read = 0;
+	while (read < length) {
+		request->buffer = (char *)buffer + read;
+		request->buffer_length = length - read;
+		int ret = transfer(request);
+		if (ret < 0) {
+			return ret;
+		}
+		read += ret;
+	}
+	return read;
+}
+
+// Queue a read request.  Call readDataWait to wait for result
+int MtpDataPacket::readDataAsync(struct usb_request *req) {
+	if (usb_request_queue(req)) {
+		MTPE("usb_endpoint_queue failed, errno: %d", errno);
+		return -1;
+	}
+	return 0;
+}
+
+// Wait for result of readDataAsync
+int MtpDataPacket::readDataWait(struct usb_device *device) {
+	struct usb_request *req = usb_request_wait(device);
+	return (req ? req->actual_length : -1);
+}
+
+int MtpDataPacket::readDataHeader(struct usb_request *request) {
+	request->buffer = mBuffer;
+	request->buffer_length = request->max_packet_size;
+	int length = transfer(request);
+	if (length >= 0)
+		mPacketSize = length;
+	return length;
+}
+
+int MtpDataPacket::writeDataHeader(struct usb_request *request, uint32_t length) {
+	MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
+	MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+	request->buffer = mBuffer;
+	request->buffer_length = MTP_CONTAINER_HEADER_SIZE;
+	int ret = transfer(request);
+	return (ret < 0 ? ret : 0);
+}
+int MtpDataPacket::write(struct usb_request *request) {
+	MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+	MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+
+	// send header separately from data
+	request->buffer = mBuffer;
+	request->buffer_length = MTP_CONTAINER_HEADER_SIZE;
+	int ret = transfer(request);
+	if (ret == MTP_CONTAINER_HEADER_SIZE) {
+		request->buffer = mBuffer + MTP_CONTAINER_HEADER_SIZE;
+		request->buffer_length = mPacketSize - MTP_CONTAINER_HEADER_SIZE;
+		ret = transfer(request);
+	}
+	return (ret < 0 ? ret : 0);
+}
+
+int MtpDataPacket::write(struct usb_request *request, void* buffer, uint32_t length) {
+	request->buffer = buffer;
+	request->buffer_length = length;
+	int ret = transfer(request);
+	return (ret < 0 ? ret : 0);
+}
+
+#endif // MTP_HOST
+void* MtpDataPacket::getData(int& outLength) const {
+	int length = mPacketSize - MTP_CONTAINER_HEADER_SIZE;
+	if (length > 0) {
+		void* result = malloc(length);
+		if (result) {
+			memcpy(result, mBuffer + MTP_CONTAINER_HEADER_SIZE, length);
+			outLength = length;
+			return result;
+		}
+	}
+	outLength = 0;
+	return NULL;
+}
+
diff --git a/mtp/MtpDataPacket.h b/mtp/MtpDataPacket.h
new file mode 100755
index 0000000..0e7a873
--- /dev/null
+++ b/mtp/MtpDataPacket.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to c++
+ *
+ */
+
+#ifndef _MTP_DATA_PACKET_H
+#define _MTP_DATA_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+struct usb_device;
+struct usb_request;
+
+
+class MtpStringBuffer;
+
+class MtpDataPacket : public MtpPacket {
+private:
+    // current offset for get/put methods
+    uint64_t            mOffset;
+
+public:
+                        MtpDataPacket();
+    virtual             ~MtpDataPacket();
+
+    virtual void        reset();
+
+    void                setOperationCode(MtpOperationCode code);
+    void                setTransactionID(MtpTransactionID id);
+
+    inline const uint8_t*     getData() const { return mBuffer + MTP_CONTAINER_HEADER_SIZE; }
+    inline uint8_t      getUInt8() { return (uint8_t)mBuffer[mOffset++]; }
+    inline int8_t       getInt8() { return (int8_t)mBuffer[mOffset++]; }
+    uint16_t            getUInt16();
+    inline int16_t      getInt16() { return (int16_t)getUInt16(); }
+    uint32_t            getUInt32();
+    inline int32_t      getInt32() { return (int32_t)getUInt32(); }
+    uint64_t            getUInt64();
+    inline int64_t      getInt64() { return (int64_t)getUInt64(); }
+    void                getUInt128(uint128_t& value);
+    inline void         getInt128(int128_t& value) { getUInt128((uint128_t&)value); }
+    void                getString(MtpStringBuffer& string);
+
+    Int8List*           getAInt8();
+    UInt8List*          getAUInt8();
+    Int16List*          getAInt16();
+    UInt16List*         getAUInt16();
+    Int32List*          getAInt32();
+    UInt32List*         getAUInt32();
+    Int64List*          getAInt64();
+    UInt64List*         getAUInt64();
+
+    void                putInt8(int8_t value);
+    void                putUInt8(uint8_t value);
+    void                putInt16(int16_t value);
+    void                putUInt16(uint16_t value);
+    void                putInt32(int32_t value);
+    void                putUInt32(uint32_t value);
+    void                putInt64(int64_t value);
+    void                putUInt64(uint64_t value);
+    void                putInt128(const int128_t& value);
+    void                putUInt128(const uint128_t& value);
+    void                putInt128(int64_t value);
+    void                putUInt128(uint64_t value);
+
+    void                putAInt8(const int8_t* values, int count);
+    void                putAUInt8(const uint8_t* values, int count);
+    void                putAInt16(const int16_t* values, int count);
+    void                putAUInt16(const uint16_t* values, int count);
+    void                putAUInt16(const UInt16List* values);
+    void                putAInt32(const int32_t* values, int count);
+    void                putAUInt32(const uint32_t* values, int count);
+    void                putAUInt32(const UInt32List* list);
+    void                putAInt64(const int64_t* values, int count);
+    void                putAUInt64(const uint64_t* values, int count);
+    void                putString(const MtpStringBuffer& string);
+    void                putString(const char* string);
+    void                putString(const uint16_t* string);
+    inline void         putEmptyString() { putUInt8(0); }
+    inline void         putEmptyArray() { putUInt32(0); }
+
+
+#ifdef MTP_DEVICE
+    // fill our buffer with data from the given file descriptor
+    int                 read(int fd);
+
+    // write our data to the given file descriptor
+    int                 write(int fd);
+    int                 writeData(int fd, void* data, uint32_t length);
+#endif
+#ifdef MTP_HOST
+    int                 read(struct usb_request *request);
+    int                 readData(struct usb_request *request, void* buffer, int length);
+    int                 readDataAsync(struct usb_request *req);
+    int                 readDataWait(struct usb_device *device);
+    int                 readDataHeader(struct usb_request *ep);
+
+    int                 writeDataHeader(struct usb_request *ep, uint32_t length);
+    int                 write(struct usb_request *ep);
+    int                 write(struct usb_request *ep, void* buffer, uint32_t length);
+#endif
+    inline bool         hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; }
+    inline uint32_t     getContainerLength() const { return MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET); }
+    void*               getData(int& outLength) const;
+};
+
+
+#endif // _MTP_DATA_PACKET_H
diff --git a/mtp/MtpDatabase.h b/mtp/MtpDatabase.h
new file mode 100755
index 0000000..c25e9b2
--- /dev/null
+++ b/mtp/MtpDatabase.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to c++
+ */
+
+#ifndef _MTP_DATABASE_H
+#define _MTP_DATABASE_H
+
+#include "MtpTypes.h"
+
+class MtpDataPacket;
+class MtpProperty;
+class MtpObjectInfo;
+
+class MtpDatabase {
+public:
+    virtual ~MtpDatabase() {}
+
+    // called from SendObjectInfo to reserve a database entry for the incoming file
+    virtual MtpObjectHandle         beginSendObject(const char* path,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent,
+                                            MtpStorageID storage,
+                                            uint64_t size,
+                                            time_t modified) = 0;
+
+    // called to report success or failure of the SendObject file transfer
+    // success should signal a notification of the new object's creation,
+    // failure should remove the database entry created in beginSendObject
+    virtual void                    endSendObject(const char* path,
+                                            MtpObjectHandle handle,
+                                            MtpObjectFormat format,
+                                            bool succeeded) = 0;
+
+    virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent) = 0;
+
+    virtual int                     getNumObjects(MtpStorageID storageID,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent) = 0;
+
+    // callee should delete[] the results from these
+    // results can be NULL
+    virtual MtpObjectFormatList*    getSupportedPlaybackFormats() = 0;
+    virtual MtpObjectFormatList*    getSupportedCaptureFormats() = 0;
+    virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format) = 0;
+    virtual MtpDevicePropertyList*  getSupportedDeviceProperties() = 0;
+
+	virtual void 					createDB(MtpStorage* storage, MtpStorageID storageID) = 0;
+
+    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property) = 0;
+
+    virtual MtpResponseCode         getObjectPropertyList(MtpObjectHandle handle,
+                                            uint32_t format, uint32_t property,
+                                            int groupCode, int depth,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
+                                            MtpObjectInfo& info) = 0;
+
+    virtual void*                   getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) = 0;
+
+    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
+                                            MtpString& outFilePath,
+                                            int64_t& outFileLength,
+                                            MtpObjectFormat& outFormat) = 0;
+
+    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle) = 0;
+
+    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle) = 0;
+
+    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
+                                            MtpObjectHandleList* references) = 0;
+
+    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format) = 0;
+
+    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property) = 0;
+
+    virtual void                    sessionStarted() = 0;
+
+    virtual void                    sessionEnded() = 0;
+    virtual void                    lockMutex() = 0;
+    virtual void                    unlockMutex() = 0;
+};
+
+#endif // _MTP_DATABASE_H
diff --git a/mtp/MtpDebug.cpp b/mtp/MtpDebug.cpp
new file mode 100755
index 0000000..47834ab
--- /dev/null
+++ b/mtp/MtpDebug.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ */
+
+#include "MtpDebug.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+#define MTP_DEBUG_BUFFER_SIZE 2048
+static int debug_enabled = 0;
+
+extern "C" void mtpdebug(const char *fmt, ...)
+{
+	if (debug_enabled) {
+		char buf[MTP_DEBUG_BUFFER_SIZE];		// We're going to limit a single request to 512 bytes
+
+		va_list ap;
+		va_start(ap, fmt);
+		vsnprintf(buf, MTP_DEBUG_BUFFER_SIZE, fmt, ap);
+		va_end(ap);
+
+		fputs(buf, stdout);
+	}
+}
+
+struct CodeEntry {
+	const char* name;
+	uint16_t code;
+};
+
+static const CodeEntry sOperationCodes[] = {
+	{ "MTP_OPERATION_GET_DEVICE_INFO",              0x1001 },
+	{ "MTP_OPERATION_OPEN_SESSION",                 0x1002 },
+	{ "MTP_OPERATION_CLOSE_SESSION",                0x1003 },
+	{ "MTP_OPERATION_GET_STORAGE_IDS",              0x1004 },
+	{ "MTP_OPERATION_GET_STORAGE_INFO",             0x1005 },
+	{ "MTP_OPERATION_GET_NUM_OBJECTS",              0x1006 },
+	{ "MTP_OPERATION_GET_OBJECT_HANDLES",           0x1007 },
+	{ "MTP_OPERATION_GET_OBJECT_INFO",              0x1008 },
+	{ "MTP_OPERATION_GET_OBJECT",                   0x1009 },
+	{ "MTP_OPERATION_GET_THUMB",                    0x100A },
+	{ "MTP_OPERATION_DELETE_OBJECT",                0x100B },
+	{ "MTP_OPERATION_SEND_OBJECT_INFO",             0x100C },
+	{ "MTP_OPERATION_SEND_OBJECT",                  0x100D },
+	{ "MTP_OPERATION_INITIATE_CAPTURE",             0x100E },
+	{ "MTP_OPERATION_FORMAT_STORE",                 0x100F },
+	{ "MTP_OPERATION_RESET_DEVICE",                 0x1010 },
+	{ "MTP_OPERATION_SELF_TEST",                    0x1011 },
+	{ "MTP_OPERATION_SET_OBJECT_PROTECTION",        0x1012 },
+	{ "MTP_OPERATION_POWER_DOWN",                   0x1013 },
+	{ "MTP_OPERATION_GET_DEVICE_PROP_DESC",         0x1014 },
+	{ "MTP_OPERATION_GET_DEVICE_PROP_VALUE",        0x1015 },
+	{ "MTP_OPERATION_SET_DEVICE_PROP_VALUE",        0x1016 },
+	{ "MTP_OPERATION_RESET_DEVICE_PROP_VALUE",      0x1017 },
+	{ "MTP_OPERATION_TERMINATE_OPEN_CAPTURE",       0x1018 },
+	{ "MTP_OPERATION_MOVE_OBJECT",                  0x1019 },
+	{ "MTP_OPERATION_COPY_OBJECT",                  0x101A },
+	{ "MTP_OPERATION_GET_PARTIAL_OBJECT",           0x101B },
+	{ "MTP_OPERATION_INITIATE_OPEN_CAPTURE",        0x101C },
+	{ "MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED",   0x9801 },
+	{ "MTP_OPERATION_GET_OBJECT_PROP_DESC",         0x9802 },
+	{ "MTP_OPERATION_GET_OBJECT_PROP_VALUE",        0x9803 },
+	{ "MTP_OPERATION_SET_OBJECT_PROP_VALUE",        0x9804 },
+	{ "MTP_OPERATION_GET_OBJECT_PROP_LIST",         0x9805 },
+	{ "MTP_OPERATION_SET_OBJECT_PROP_LIST",         0x9806 },
+	{ "MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC", 0x9807 },
+	{ "MTP_OPERATION_SEND_OBJECT_PROP_LIST",        0x9808 },
+	{ "MTP_OPERATION_GET_OBJECT_REFERENCES",        0x9810 },
+	{ "MTP_OPERATION_SET_OBJECT_REFERENCES",        0x9811 },
+	{ "MTP_OPERATION_SKIP",                         0x9820 },
+    // android extensions
+	{ "MTP_OPERATION_GET_PARTIAL_OBJECT_64",        0x95C1 },
+	{ "MTP_OPERATION_SEND_PARTIAL_OBJECT",          0x95C2 },
+	{ "MTP_OPERATION_TRUNCATE_OBJECT",              0x95C3 },
+	{ "MTP_OPERATION_BEGIN_EDIT_OBJECT",            0x95C4 },
+	{ "MTP_OPERATION_END_EDIT_OBJECT",              0x95C5 },
+	{ 0,                                            0      },
+};
+
+static const CodeEntry sFormatCodes[] = {
+	{ "MTP_FORMAT_UNDEFINED",                       0x3000 },
+	{ "MTP_FORMAT_ASSOCIATION",                     0x3001 },
+	{ "MTP_FORMAT_SCRIPT",                          0x3002 },
+	{ "MTP_FORMAT_EXECUTABLE",                      0x3003 },
+	{ "MTP_FORMAT_TEXT",                            0x3004 },
+	{ "MTP_FORMAT_HTML",                            0x3005 },
+	{ "MTP_FORMAT_DPOF",                            0x3006 },
+	{ "MTP_FORMAT_AIFF",                            0x3007 },
+	{ "MTP_FORMAT_WAV",                             0x3008 },
+	{ "MTP_FORMAT_MP3",                             0x3009 },
+	{ "MTP_FORMAT_AVI",                             0x300A },
+	{ "MTP_FORMAT_MPEG",                            0x300B },
+	{ "MTP_FORMAT_ASF",                             0x300C },
+	{ "MTP_FORMAT_DEFINED",                         0x3800 },
+	{ "MTP_FORMAT_EXIF_JPEG",                       0x3801 },
+	{ "MTP_FORMAT_TIFF_EP",                         0x3802 },
+	{ "MTP_FORMAT_FLASHPIX",                        0x3803 },
+	{ "MTP_FORMAT_BMP",                             0x3804 },
+	{ "MTP_FORMAT_CIFF",                            0x3805 },
+	{ "MTP_FORMAT_GIF",                             0x3807 },
+	{ "MTP_FORMAT_JFIF",                            0x3808 },
+	{ "MTP_FORMAT_CD",                              0x3809 },
+	{ "MTP_FORMAT_PICT",                            0x380A },
+	{ "MTP_FORMAT_PNG",                             0x380B },
+	{ "MTP_FORMAT_TIFF",                            0x380D },
+	{ "MTP_FORMAT_TIFF_IT",                         0x380E },
+	{ "MTP_FORMAT_JP2",                             0x380F },
+	{ "MTP_FORMAT_JPX",                             0x3810 },
+	{ "MTP_FORMAT_UNDEFINED_FIRMWARE",              0xB802 },
+	{ "MTP_FORMAT_WINDOWS_IMAGE_FORMAT",            0xB881 },
+	{ "MTP_FORMAT_UNDEFINED_AUDIO",                 0xB900 },
+	{ "MTP_FORMAT_WMA",                             0xB901 },
+	{ "MTP_FORMAT_OGG",                             0xB902 },
+	{ "MTP_FORMAT_AAC",                             0xB903 },
+	{ "MTP_FORMAT_AUDIBLE",                         0xB904 },
+	{ "MTP_FORMAT_FLAC",                            0xB906 },
+	{ "MTP_FORMAT_UNDEFINED_VIDEO",                 0xB980 },
+	{ "MTP_FORMAT_WMV",                             0xB981 },
+	{ "MTP_FORMAT_MP4_CONTAINER",                   0xB982 },
+	{ "MTP_FORMAT_MP2",                             0xB983 },
+	{ "MTP_FORMAT_3GP_CONTAINER",                   0xB984 },
+	{ "MTP_FORMAT_UNDEFINED_COLLECTION",            0xBA00 },
+	{ "MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM",       0xBA01 },
+	{ "MTP_FORMAT_ABSTRACT_IMAGE_ALBUM",            0xBA02 },
+	{ "MTP_FORMAT_ABSTRACT_AUDIO_ALBUM",            0xBA03 },
+	{ "MTP_FORMAT_ABSTRACT_VIDEO_ALBUM",            0xBA04 },
+	{ "MTP_FORMAT_ABSTRACT_AV_PLAYLIST",            0xBA05 },
+	{ "MTP_FORMAT_ABSTRACT_CONTACT_GROUP",          0xBA06 },
+	{ "MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER",         0xBA07 },
+	{ "MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION",   0xBA08 },
+	{ "MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST",         0xBA09 },
+	{ "MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST",         0xBA0A },
+	{ "MTP_FORMAT_ABSTRACT_MEDIACAST",              0xBA0B },
+	{ "MTP_FORMAT_WPL_PLAYLIST",                    0xBA10 },
+	{ "MTP_FORMAT_M3U_PLAYLIST",                    0xBA11 },
+	{ "MTP_FORMAT_MPL_PLAYLIST",                    0xBA12 },
+	{ "MTP_FORMAT_ASX_PLAYLIST",                    0xBA13 },
+	{ "MTP_FORMAT_PLS_PLAYLIST",                    0xBA14 },
+	{ "MTP_FORMAT_UNDEFINED_DOCUMENT",              0xBA80 },
+	{ "MTP_FORMAT_ABSTRACT_DOCUMENT",               0xBA81 },
+	{ "MTP_FORMAT_XML_DOCUMENT",                    0xBA82 },
+	{ "MTP_FORMAT_MS_WORD_DOCUMENT",                0xBA83 },
+	{ "MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT",      0xBA84 },
+	{ "MTP_FORMAT_MS_EXCEL_SPREADSHEET",            0xBA85 },
+	{ "MTP_FORMAT_MS_POWERPOINT_PRESENTATION",      0xBA86 },
+	{ "MTP_FORMAT_UNDEFINED_MESSAGE",               0xBB00 },
+	{ "MTP_FORMAT_ABSTRACT_MESSSAGE",               0xBB01 },
+	{ "MTP_FORMAT_UNDEFINED_CONTACT",               0xBB80 },
+	{ "MTP_FORMAT_ABSTRACT_CONTACT",                0xBB81 },
+	{ "MTP_FORMAT_VCARD_2",                         0xBB82 },
+	{ 0,                                            0      },
+};
+
+static const CodeEntry sObjectPropCodes[] = {
+	{ "MTP_PROPERTY_STORAGE_ID",                             0xDC01 },
+	{ "MTP_PROPERTY_OBJECT_FORMAT",                          0xDC02 },
+	{ "MTP_PROPERTY_PROTECTION_STATUS",                      0xDC03 },
+	{ "MTP_PROPERTY_OBJECT_SIZE",                            0xDC04 },
+	{ "MTP_PROPERTY_ASSOCIATION_TYPE",                       0xDC05 },
+	{ "MTP_PROPERTY_ASSOCIATION_DESC",                       0xDC06 },
+	{ "MTP_PROPERTY_OBJECT_FILE_NAME",                       0xDC07 },
+	{ "MTP_PROPERTY_DATE_CREATED",                           0xDC08 },
+	{ "MTP_PROPERTY_DATE_MODIFIED",                          0xDC09 },
+	{ "MTP_PROPERTY_KEYWORDS",                               0xDC0A },
+	{ "MTP_PROPERTY_PARENT_OBJECT",                          0xDC0B },
+	{ "MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS",                0xDC0C },
+	{ "MTP_PROPERTY_HIDDEN",                                 0xDC0D },
+	{ "MTP_PROPERTY_SYSTEM_OBJECT",                          0xDC0E },
+	{ "MTP_PROPERTY_PERSISTENT_UID",                         0xDC41 },
+	{ "MTP_PROPERTY_SYNC_ID",                                0xDC42 },
+	{ "MTP_PROPERTY_PROPERTY_BAG",                           0xDC43 },
+	{ "MTP_PROPERTY_NAME",                                   0xDC44 },
+	{ "MTP_PROPERTY_CREATED_BY",                             0xDC45 },
+	{ "MTP_PROPERTY_ARTIST",                                 0xDC46 },
+	{ "MTP_PROPERTY_DATE_AUTHORED",                          0xDC47 },
+	{ "MTP_PROPERTY_DESCRIPTION",                            0xDC48 },
+	{ "MTP_PROPERTY_URL_REFERENCE",                          0xDC49 },
+	{ "MTP_PROPERTY_LANGUAGE_LOCALE",                        0xDC4A },
+	{ "MTP_PROPERTY_COPYRIGHT_INFORMATION",                  0xDC4B },
+	{ "MTP_PROPERTY_SOURCE",                                 0xDC4C },
+	{ "MTP_PROPERTY_ORIGIN_LOCATION",                        0xDC4D },
+	{ "MTP_PROPERTY_DATE_ADDED",                             0xDC4E },
+	{ "MTP_PROPERTY_NON_CONSUMABLE",                         0xDC4F },
+	{ "MTP_PROPERTY_CORRUPT_UNPLAYABLE",                     0xDC50 },
+	{ "MTP_PROPERTY_PRODUCER_SERIAL_NUMBER",                 0xDC51 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT",           0xDC81 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE",             0xDC82 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT",           0xDC83 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH",            0xDC84 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION",         0xDC85 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA",             0xDC86 },
+	{ "MTP_PROPERTY_WIDTH",                                  0xDC87 },
+	{ "MTP_PROPERTY_HEIGHT",                                 0xDC88 },
+	{ "MTP_PROPERTY_DURATION",                               0xDC89 },
+	{ "MTP_PROPERTY_RATING",                                 0xDC8A },
+	{ "MTP_PROPERTY_TRACK",                                  0xDC8B },
+	{ "MTP_PROPERTY_GENRE",                                  0xDC8C },
+	{ "MTP_PROPERTY_CREDITS",                                0xDC8D },
+	{ "MTP_PROPERTY_LYRICS",                                 0xDC8E },
+	{ "MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID",                0xDC8F },
+	{ "MTP_PROPERTY_PRODUCED_BY",                            0xDC90 },
+	{ "MTP_PROPERTY_USE_COUNT",                              0xDC91 },
+	{ "MTP_PROPERTY_SKIP_COUNT",                             0xDC92 },
+	{ "MTP_PROPERTY_LAST_ACCESSED",                          0xDC93 },
+	{ "MTP_PROPERTY_PARENTAL_RATING",                        0xDC94 },
+	{ "MTP_PROPERTY_META_GENRE",                             0xDC95 },
+	{ "MTP_PROPERTY_COMPOSER",                               0xDC96 },
+	{ "MTP_PROPERTY_EFFECTIVE_RATING",                       0xDC97 },
+	{ "MTP_PROPERTY_SUBTITLE",                               0xDC98 },
+	{ "MTP_PROPERTY_ORIGINAL_RELEASE_DATE",                  0xDC99 },
+	{ "MTP_PROPERTY_ALBUM_NAME",                             0xDC9A },
+	{ "MTP_PROPERTY_ALBUM_ARTIST",                           0xDC9B },
+	{ "MTP_PROPERTY_MOOD",                                   0xDC9C },
+	{ "MTP_PROPERTY_DRM_STATUS",                             0xDC9D },
+	{ "MTP_PROPERTY_SUB_DESCRIPTION",                        0xDC9E },
+	{ "MTP_PROPERTY_IS_CROPPED",                             0xDCD1 },
+	{ "MTP_PROPERTY_IS_COLOUR_CORRECTED",                    0xDCD2 },
+	{ "MTP_PROPERTY_IMAGE_BIT_DEPTH",                        0xDCD3 },
+	{ "MTP_PROPERTY_F_NUMBER",                               0xDCD4 },
+	{ "MTP_PROPERTY_EXPOSURE_TIME",                          0xDCD5 },
+	{ "MTP_PROPERTY_EXPOSURE_INDEX",                         0xDCD6 },
+	{ "MTP_PROPERTY_TOTAL_BITRATE",                          0xDE91 },
+	{ "MTP_PROPERTY_BITRATE_TYPE",                           0xDE92 },
+	{ "MTP_PROPERTY_SAMPLE_RATE",                            0xDE93 },
+	{ "MTP_PROPERTY_NUMBER_OF_CHANNELS",                     0xDE94 },
+	{ "MTP_PROPERTY_AUDIO_BIT_DEPTH",                        0xDE95 },
+	{ "MTP_PROPERTY_SCAN_TYPE",                              0xDE97 },
+	{ "MTP_PROPERTY_AUDIO_WAVE_CODEC",                       0xDE99 },
+	{ "MTP_PROPERTY_AUDIO_BITRATE",                          0xDE9A },
+	{ "MTP_PROPERTY_VIDEO_FOURCC_CODEC",                     0xDE9B },
+	{ "MTP_PROPERTY_VIDEO_BITRATE",                          0xDE9C },
+	{ "MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS",            0xDE9D },
+	{ "MTP_PROPERTY_KEYFRAME_DISTANCE",                      0xDE9E },
+	{ "MTP_PROPERTY_BUFFER_SIZE",                            0xDE9F },
+	{ "MTP_PROPERTY_ENCODING_QUALITY",                       0xDEA0 },
+	{ "MTP_PROPERTY_ENCODING_PROFILE",                       0xDEA1 },
+	{ "MTP_PROPERTY_DISPLAY_NAME",                           0xDCE0 },
+	{ "MTP_PROPERTY_BODY_TEXT",                              0xDCE1 },
+	{ "MTP_PROPERTY_SUBJECT",                                0xDCE2 },
+	{ "MTP_PROPERTY_PRIORITY",                               0xDCE3 },
+	{ "MTP_PROPERTY_GIVEN_NAME",                             0xDD00 },
+	{ "MTP_PROPERTY_MIDDLE_NAMES",                           0xDD01 },
+	{ "MTP_PROPERTY_FAMILY_NAME",                            0xDD02 },
+	{ "MTP_PROPERTY_PREFIX",                                 0xDD03 },
+	{ "MTP_PROPERTY_SUFFIX",                                 0xDD04 },
+	{ "MTP_PROPERTY_PHONETIC_GIVEN_NAME",                    0xDD05 },
+	{ "MTP_PROPERTY_PHONETIC_FAMILY_NAME",                   0xDD06 },
+	{ "MTP_PROPERTY_EMAIL_PRIMARY",                          0xDD07 },
+	{ "MTP_PROPERTY_EMAIL_PERSONAL_1",                       0xDD08 },
+	{ "MTP_PROPERTY_EMAIL_PERSONAL_2",                       0xDD09 },
+	{ "MTP_PROPERTY_EMAIL_BUSINESS_1",                       0xDD0A },
+	{ "MTP_PROPERTY_EMAIL_BUSINESS_2",                       0xDD0B },
+	{ "MTP_PROPERTY_EMAIL_OTHERS",                           0xDD0C },
+	{ "MTP_PROPERTY_PHONE_NUMBER_PRIMARY",                   0xDD0D },
+	{ "MTP_PROPERTY_PHONE_NUMBER_PERSONAL",                  0xDD0E },
+	{ "MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2",                0xDD0F },
+	{ "MTP_PROPERTY_PHONE_NUMBER_BUSINESS",                  0xDD10 },
+	{ "MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2",                0xDD11 },
+	{ "MTP_PROPERTY_PHONE_NUMBER_MOBILE",                    0xDD12 },
+	{ "MTP_PROPERTY_PHONE_NUMBER_MOBILE_2",                  0xDD13 },
+	{ "MTP_PROPERTY_FAX_NUMBER_PRIMARY",                     0xDD14 },
+	{ "MTP_PROPERTY_FAX_NUMBER_PERSONAL",                    0xDD15 },
+	{ "MTP_PROPERTY_FAX_NUMBER_BUSINESS",                    0xDD16 },
+	{ "MTP_PROPERTY_PAGER_NUMBER",                           0xDD17 },
+	{ "MTP_PROPERTY_PHONE_NUMBER_OTHERS",                    0xDD18 },
+	{ "MTP_PROPERTY_PRIMARY_WEB_ADDRESS",                    0xDD19 },
+	{ "MTP_PROPERTY_PERSONAL_WEB_ADDRESS",                   0xDD1A },
+	{ "MTP_PROPERTY_BUSINESS_WEB_ADDRESS",                   0xDD1B },
+	{ "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS",              0xDD1C },
+	{ "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2",            0xDD1D },
+	{ "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3",            0xDD1E },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL",           0xDD1F },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1",         0xDD20 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2",         0xDD21 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY",           0xDD22 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION",         0xDD23 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE",    0xDD24 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY",        0xDD25 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL",           0xDD26 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1",         0xDD27 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2",         0xDD28 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY",           0xDD29 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION",         0xDD2A },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE",    0xDD2B },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY",        0xDD2C },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL",              0xDD2D },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1",            0xDD2E },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2",            0xDD2F },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY",              0xDD30 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION",            0xDD31 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE",       0xDD32 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY",           0xDD33 },
+	{ "MTP_PROPERTY_ORGANIZATION_NAME",                      0xDD34 },
+	{ "MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME",             0xDD35 },
+	{ "MTP_PROPERTY_ROLE",                                   0xDD36 },
+	{ "MTP_PROPERTY_BIRTHDATE",                              0xDD37 },
+	{ "MTP_PROPERTY_MESSAGE_TO",                             0xDD40 },
+	{ "MTP_PROPERTY_MESSAGE_CC",                             0xDD41 },
+	{ "MTP_PROPERTY_MESSAGE_BCC",                            0xDD42 },
+	{ "MTP_PROPERTY_MESSAGE_READ",                           0xDD43 },
+	{ "MTP_PROPERTY_MESSAGE_RECEIVED_TIME",                  0xDD44 },
+	{ "MTP_PROPERTY_MESSAGE_SENDER",                         0xDD45 },
+	{ "MTP_PROPERTY_ACTIVITY_BEGIN_TIME",                    0xDD50 },
+	{ "MTP_PROPERTY_ACTIVITY_END_TIME",                      0xDD51 },
+	{ "MTP_PROPERTY_ACTIVITY_LOCATION",                      0xDD52 },
+	{ "MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES",            0xDD54 },
+	{ "MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES",            0xDD55 },
+	{ "MTP_PROPERTY_ACTIVITY_RESOURCES",                     0xDD56 },
+	{ "MTP_PROPERTY_ACTIVITY_ACCEPTED",                      0xDD57 },
+	{ "MTP_PROPERTY_ACTIVITY_TENTATIVE",                     0xDD58 },
+	{ "MTP_PROPERTY_ACTIVITY_DECLINED",                      0xDD59 },
+	{ "MTP_PROPERTY_ACTIVITY_REMAINDER_TIME",                0xDD5A },
+	{ "MTP_PROPERTY_ACTIVITY_OWNER",                         0xDD5B },
+	{ "MTP_PROPERTY_ACTIVITY_STATUS",                        0xDD5C },
+	{ "MTP_PROPERTY_OWNER",                                  0xDD5D },
+	{ "MTP_PROPERTY_EDITOR",                                 0xDD5E },
+	{ "MTP_PROPERTY_WEBMASTER",                              0xDD5F },
+	{ "MTP_PROPERTY_URL_SOURCE",                             0xDD60 },
+	{ "MTP_PROPERTY_URL_DESTINATION",                        0xDD61 },
+	{ "MTP_PROPERTY_TIME_BOOKMARK",                          0xDD62 },
+	{ "MTP_PROPERTY_OBJECT_BOOKMARK",                        0xDD63 },
+	{ "MTP_PROPERTY_BYTE_BOOKMARK",                          0xDD64 },
+	{ "MTP_PROPERTY_LAST_BUILD_DATE",                        0xDD70 },
+	{ "MTP_PROPERTY_TIME_TO_LIVE",                           0xDD71 },
+	{ "MTP_PROPERTY_MEDIA_GUID",                             0xDD72 },
+	{ 0,                                                     0      },
+};
+
+static const CodeEntry sDevicePropCodes[] = {
+	{ "MTP_DEVICE_PROPERTY_UNDEFINED",                       0x5000 },
+	{ "MTP_DEVICE_PROPERTY_BATTERY_LEVEL",                   0x5001 },
+	{ "MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE",                 0x5002 },
+	{ "MTP_DEVICE_PROPERTY_IMAGE_SIZE",                      0x5003 },
+	{ "MTP_DEVICE_PROPERTY_COMPRESSION_SETTING",             0x5004 },
+	{ "MTP_DEVICE_PROPERTY_WHITE_BALANCE",                   0x5005 },
+	{ "MTP_DEVICE_PROPERTY_RGB_GAIN",                        0x5006 },
+	{ "MTP_DEVICE_PROPERTY_F_NUMBER",                        0x5007 },
+	{ "MTP_DEVICE_PROPERTY_FOCAL_LENGTH",                    0x5008 },
+	{ "MTP_DEVICE_PROPERTY_FOCUS_DISTANCE",                  0x5009 },
+	{ "MTP_DEVICE_PROPERTY_FOCUS_MODE",                      0x500A },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE",          0x500B },
+	{ "MTP_DEVICE_PROPERTY_FLASH_MODE",                      0x500C },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_TIME",                   0x500D },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE",           0x500E },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_INDEX",                  0x500F },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION",      0x5010 },
+	{ "MTP_DEVICE_PROPERTY_DATETIME",                        0x5011 },
+	{ "MTP_DEVICE_PROPERTY_CAPTURE_DELAY",                   0x5012 },
+	{ "MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE",              0x5013 },
+	{ "MTP_DEVICE_PROPERTY_CONTRAST",                        0x5014 },
+	{ "MTP_DEVICE_PROPERTY_SHARPNESS",                       0x5015 },
+	{ "MTP_DEVICE_PROPERTY_DIGITAL_ZOOM",                    0x5016 },
+	{ "MTP_DEVICE_PROPERTY_EFFECT_MODE",                     0x5017 },
+	{ "MTP_DEVICE_PROPERTY_BURST_NUMBER",                    0x5018 },
+	{ "MTP_DEVICE_PROPERTY_BURST_INTERVAL",                  0x5019 },
+	{ "MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER",                0x501A },
+	{ "MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL",              0x501B },
+	{ "MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE",             0x501C },
+	{ "MTP_DEVICE_PROPERTY_UPLOAD_URL",                      0x501D },
+	{ "MTP_DEVICE_PROPERTY_ARTIST",                          0x501E },
+	{ "MTP_DEVICE_PROPERTY_COPYRIGHT_INFO",                  0x501F },
+	{ "MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER",         0xD401 },
+	{ "MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME",            0xD402 },
+	{ "MTP_DEVICE_PROPERTY_VOLUME",                          0xD403 },
+	{ "MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED",       0xD404 },
+	{ "MTP_DEVICE_PROPERTY_DEVICE_ICON",                     0xD405 },
+	{ "MTP_DEVICE_PROPERTY_PLAYBACK_RATE",                   0xD410 },
+	{ "MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT",                 0xD411 },
+	{ "MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX",        0xD412 },
+	{ "MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO",  0xD406 },
+	{ "MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE",           0xD407 },
+	{ 0,                                                     0      },
+};
+
+static const char* getCodeName(uint16_t code, const CodeEntry* table) {
+	const CodeEntry* entry = table;
+	while (entry->name) {
+		if (entry->code == code)
+			return entry->name;
+		entry++;
+	}
+	return "UNKNOWN";
+}
+
+const char* MtpDebug::getOperationCodeName(MtpOperationCode code) {
+	return getCodeName(code, sOperationCodes);
+}
+
+const char* MtpDebug::getFormatCodeName(MtpObjectFormat code) {
+	if (code == 0)
+		return "NONE";
+	return getCodeName(code, sFormatCodes);
+}
+
+const char* MtpDebug::getObjectPropCodeName(MtpPropertyCode code) {
+	if (code == 0)
+		return "NONE";
+	return getCodeName(code, sObjectPropCodes);
+}
+
+const char* MtpDebug::getDevicePropCodeName(MtpPropertyCode code) {
+	if (code == 0)
+		return "NONE";
+	return getCodeName(code, sDevicePropCodes);
+}
+
+void MtpDebug::enableDebug(void) {
+	debug_enabled = 1;
+	MTPD("MTP debug logging enabled\n");
+}
diff --git a/mtp/MtpDebug.h b/mtp/MtpDebug.h
new file mode 100755
index 0000000..b572b3b
--- /dev/null
+++ b/mtp/MtpDebug.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_DEBUG_H
+#define _MTP_DEBUG_H
+
+// #define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "MtpTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void mtpdebug(const char *fmt, ...);
+
+#define MTPI(...) fprintf(stdout, __VA_ARGS__)
+#define MTPD(...) mtpdebug(__VA_ARGS__)
+#define MTPE(...) fprintf(stdout, "E:" __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+class MtpDebug {
+public:
+	static const char* getOperationCodeName(MtpOperationCode code);
+	static const char* getFormatCodeName(MtpObjectFormat code);
+	static const char* getObjectPropCodeName(MtpPropertyCode code);
+	static const char* getDevicePropCodeName(MtpPropertyCode code);
+	static void enableDebug();
+};
+
+
+#endif // _MTP_DEBUG_H
diff --git a/mtp/MtpDevice.cpp b/mtp/MtpDevice.cpp
new file mode 100755
index 0000000..53f8b2e
--- /dev/null
+++ b/mtp/MtpDevice.cpp
@@ -0,0 +1,833 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDevice.h"
+#include "MtpDebug.h"
+#include "MtpDeviceInfo.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpStorageInfo.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+#include "MtpDataPacket.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <endian.h>
+
+#include <usbhost/usbhost.h>
+
+
+#if 0
+static bool isMtpDevice(uint16_t vendor, uint16_t product) {
+	// Sandisk Sansa Fuze
+	if (vendor == 0x0781 && product == 0x74c2)
+		return true;
+	// Samsung YP-Z5
+	if (vendor == 0x04e8 && product == 0x503c)
+		return true;
+	return false;
+}
+#endif
+
+MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
+	struct usb_device *device = usb_device_new(deviceName, fd);
+	if (!device) {
+		MTPE("usb_device_new failed for %s", deviceName);
+		return NULL;
+	}
+
+	struct usb_descriptor_header* desc;
+	struct usb_descriptor_iter iter;
+
+	usb_descriptor_iter_init(device, &iter);
+
+	while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+		if (desc->bDescriptorType == USB_DT_INTERFACE) {
+			struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+
+			if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
+				interface->bInterfaceSubClass == 1 && // Still Image Capture
+				interface->bInterfaceProtocol == 1)	 // Picture Transfer Protocol (PIMA 15470)
+			{
+				char* manufacturerName = usb_device_get_manufacturer_name(device);
+				char* productName = usb_device_get_product_name(device);
+				MTPD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
+				free(manufacturerName);
+				free(productName);
+			} else if (interface->bInterfaceClass == 0xFF &&
+					interface->bInterfaceSubClass == 0xFF &&
+					interface->bInterfaceProtocol == 0) {
+				char* interfaceName = usb_device_get_string(device, interface->iInterface);
+				if (!interfaceName) {
+					continue;
+				} else if (strcmp(interfaceName, "MTP")) {
+					free(interfaceName);
+					continue;
+				}
+				free(interfaceName);
+
+				// Looks like an android style MTP device
+				char* manufacturerName = usb_device_get_manufacturer_name(device);
+				char* productName = usb_device_get_product_name(device);
+				MTPI("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
+				free(manufacturerName);
+				free(productName);
+			}
+#if 0
+			 else {
+				// look for special cased devices based on vendor/product ID
+				// we are doing this mainly for testing purposes
+				uint16_t vendor = usb_device_get_vendor_id(device);
+				uint16_t product = usb_device_get_product_id(device);
+				if (!isMtpDevice(vendor, product)) {
+					// not an MTP or PTP device
+					continue;
+				}
+				// request MTP OS string and descriptor
+				// some music players need to see this before entering MTP mode.
+				char buffer[256];
+				memset(buffer, 0, sizeof(buffer));
+				int ret = usb_device_control_transfer(device,
+						USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
+						USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
+						0, buffer, sizeof(buffer), 0);
+				MTPE("usb_device_control_transfer returned %d errno: %d\n", ret, errno);
+				if (ret > 0) {
+					MTPI("got MTP string %s\n", buffer);
+					ret = usb_device_control_transfer(device,
+							USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
+							0, 4, buffer, sizeof(buffer), 0);
+					MTPI("OS descriptor got %d\n", ret);
+				} else {
+					MTPI("no MTP string\n");
+				}
+			}
+#endif
+			// if we got here, then we have a likely MTP or PTP device
+
+			// interface should be followed by three endpoints
+			struct usb_endpoint_descriptor *ep;
+			struct usb_endpoint_descriptor *ep_in_desc = NULL;
+			struct usb_endpoint_descriptor *ep_out_desc = NULL;
+			struct usb_endpoint_descriptor *ep_intr_desc = NULL;
+			for (int i = 0; i < 3; i++) {
+				ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
+				if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
+					MTPE("endpoints not found\n");
+					usb_device_close(device);
+					return NULL;
+				}
+				if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+					if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+						ep_in_desc = ep;
+					else
+						ep_out_desc = ep;
+				} else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
+					ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+					ep_intr_desc = ep;
+				}
+			}
+			if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
+				MTPE("endpoints not found\n");
+				usb_device_close(device);
+				return NULL;
+			}
+
+			if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
+				MTPE("usb_device_claim_interface failed errno: %d\n", errno);
+				usb_device_close(device);
+				return NULL;
+			}
+
+			MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
+						ep_in_desc, ep_out_desc, ep_intr_desc);
+			mtpDevice->initialize();
+			return mtpDevice;
+		}
+	}
+
+	usb_device_close(device);
+	MTPE("device not found");
+	return NULL;
+}
+
+MtpDevice::MtpDevice(struct usb_device* device, int interface,
+			const struct usb_endpoint_descriptor *ep_in,
+			const struct usb_endpoint_descriptor *ep_out,
+			const struct usb_endpoint_descriptor *ep_intr)
+	:   mDevice(device),
+		mInterface(interface),
+		mRequestIn1(NULL),
+		mRequestIn2(NULL),
+		mRequestOut(NULL),
+		mRequestIntr(NULL),
+		mDeviceInfo(NULL),
+		mSessionID(0),
+		mTransactionID(0),
+		mReceivedResponse(false)
+{
+	mRequestIn1 = usb_request_new(device, ep_in);
+	mRequestIn2 = usb_request_new(device, ep_in);
+	mRequestOut = usb_request_new(device, ep_out);
+	mRequestIntr = usb_request_new(device, ep_intr);
+}
+
+MtpDevice::~MtpDevice() {
+	close();
+	for (size_t i = 0; i < mDeviceProperties.size(); i++)
+		delete mDeviceProperties[i];
+	usb_request_free(mRequestIn1);
+	usb_request_free(mRequestIn2);
+	usb_request_free(mRequestOut);
+	usb_request_free(mRequestIntr);
+}
+
+void MtpDevice::initialize() {
+	openSession();
+	mDeviceInfo = getDeviceInfo();
+	if (mDeviceInfo) {
+		if (mDeviceInfo->mDeviceProperties) {
+			int count = mDeviceInfo->mDeviceProperties->size();
+			for (int i = 0; i < count; i++) {
+				MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
+				MtpProperty* property = getDevicePropDesc(propCode);
+				if (property)
+					mDeviceProperties.push(property);
+			}
+		}
+	}
+}
+
+void MtpDevice::close() {
+	if (mDevice) {
+		usb_device_release_interface(mDevice, mInterface);
+		usb_device_close(mDevice);
+		mDevice = NULL;
+	}
+}
+
+void MtpDevice::print() {
+	if (mDeviceInfo) {
+		mDeviceInfo->print();
+
+		if (mDeviceInfo->mDeviceProperties) {
+			MTPI("***** DEVICE PROPERTIES *****\n");
+			int count = mDeviceInfo->mDeviceProperties->size();
+			for (int i = 0; i < count; i++) {
+				MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
+				MtpProperty* property = getDevicePropDesc(propCode);
+				if (property) {
+					property->print();
+					delete property;
+				}
+			}
+		}
+	}
+
+	if (mDeviceInfo->mPlaybackFormats) {
+			MTPI("***** OBJECT PROPERTIES *****\n");
+		int count = mDeviceInfo->mPlaybackFormats->size();
+		for (int i = 0; i < count; i++) {
+			MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i];
+			MTPI("*** FORMAT: %s\n", MtpDebug::getFormatCodeName(format));
+			MtpObjectPropertyList* props = getObjectPropsSupported(format);
+			if (props) {
+				for (size_t j = 0; j < props->size(); j++) {
+					MtpObjectProperty prop = (*props)[j];
+					MtpProperty* property = getObjectPropDesc(prop, format);
+					if (property) {
+						property->print();
+						delete property;
+					} else {
+						MTPI("could not fetch property: %s",
+								MtpDebug::getObjectPropCodeName(prop));
+					}
+				}
+			}
+		}
+	}
+}
+
+const char* MtpDevice::getDeviceName() {
+	if (mDevice)
+		return usb_device_get_name(mDevice);
+	else
+		return "???";
+}
+
+bool MtpDevice::openSession() {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mSessionID = 0;
+	mTransactionID = 0;
+	MtpSessionID newSession = 1;
+	mRequest.reset();
+	mRequest.setParameter(1, newSession);
+	if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
+		return false;
+	MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
+		newSession = mResponse.getParameter(1);
+	else if (ret != MTP_RESPONSE_OK)
+		return false;
+
+	mSessionID = newSession;
+	mTransactionID = 1;
+	return true;
+}
+
+bool MtpDevice::closeSession() {
+	// FIXME
+	return true;
+}
+
+MtpDeviceInfo* MtpDevice::getDeviceInfo() {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
+		return NULL;
+	if (!readData())
+		return NULL;
+	MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_OK) {
+		MtpDeviceInfo* info = new MtpDeviceInfo;
+		info->read(mData);
+		return info;
+	}
+	return NULL;
+}
+
+MtpStorageIDList* MtpDevice::getStorageIDs() {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
+		return NULL;
+	if (!readData())
+		return NULL;
+	MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_OK) {
+		return mData.getAUInt32();
+	}
+	return NULL;
+}
+
+MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, storageID);
+	if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
+		return NULL;
+	if (!readData())
+		return NULL;
+	MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_OK) {
+		MtpStorageInfo* info = new MtpStorageInfo(storageID);
+		info->read(mData);
+		return info;
+	}
+	return NULL;
+}
+
+MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
+			MtpObjectFormat format, MtpObjectHandle parent) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, storageID);
+	mRequest.setParameter(2, format);
+	mRequest.setParameter(3, parent);
+	if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
+		return NULL;
+	if (!readData())
+		return NULL;
+	MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_OK) {
+		return mData.getAUInt32();
+	}
+	return NULL;
+}
+
+MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	// FIXME - we might want to add some caching here
+
+	mRequest.reset();
+	mRequest.setParameter(1, handle);
+	if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
+		return NULL;
+	if (!readData())
+		return NULL;
+	MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_OK) {
+		MtpObjectInfo* info = new MtpObjectInfo(handle);
+		info->read(mData);
+		return info;
+	}
+	return NULL;
+}
+
+void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, handle);
+	if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
+		MtpResponseCode ret = readResponse();
+		if (ret == MTP_RESPONSE_OK) {
+			return mData.getData(outLength);
+		}
+	}
+	outLength = 0;
+	return NULL;
+}
+
+MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	MtpObjectHandle parent = info->mParent;
+	if (parent == 0)
+		parent = MTP_PARENT_ROOT;
+
+	mRequest.setParameter(1, info->mStorageID);
+	mRequest.setParameter(2, info->mParent);
+
+	mData.putUInt32(info->mStorageID);
+	mData.putUInt16(info->mFormat);
+	mData.putUInt16(info->mProtectionStatus);
+	mData.putUInt32(info->mCompressedSize);
+	mData.putUInt16(info->mThumbFormat);
+	mData.putUInt32(info->mThumbCompressedSize);
+	mData.putUInt32(info->mThumbPixWidth);
+	mData.putUInt32(info->mThumbPixHeight);
+	mData.putUInt32(info->mImagePixWidth);
+	mData.putUInt32(info->mImagePixHeight);
+	mData.putUInt32(info->mImagePixDepth);
+	mData.putUInt32(info->mParent);
+	mData.putUInt16(info->mAssociationType);
+	mData.putUInt32(info->mAssociationDesc);
+	mData.putUInt32(info->mSequenceNumber);
+	mData.putString(info->mName);
+
+	char created[100], modified[100];
+	formatDateTime(info->mDateCreated, created, sizeof(created));
+	formatDateTime(info->mDateModified, modified, sizeof(modified));
+
+	mData.putString(created);
+	mData.putString(modified);
+	if (info->mKeywords)
+		mData.putString(info->mKeywords);
+	else
+		mData.putEmptyString();
+
+   if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
+		MtpResponseCode ret = readResponse();
+		if (ret == MTP_RESPONSE_OK) {
+			info->mStorageID = mResponse.getParameter(1);
+			info->mParent = mResponse.getParameter(2);
+			info->mHandle = mResponse.getParameter(3);
+			return info->mHandle;
+		}
+	}
+	return (MtpObjectHandle)-1;
+}
+
+bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	int remaining = info->mCompressedSize;
+	mRequest.reset();
+	mRequest.setParameter(1, info->mHandle);
+	if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
+		// send data header
+		writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
+
+		char buffer[65536];
+		while (remaining > 0) {
+			int count = read(srcFD, buffer, sizeof(buffer));
+			if (count > 0) {
+				int written = mData.write(mRequestOut, buffer, count);
+				// FIXME check error
+				remaining -= count;
+			} else {
+				break;
+			}
+		}
+	}
+	MtpResponseCode ret = readResponse();
+	return (remaining == 0 && ret == MTP_RESPONSE_OK);
+}
+
+bool MtpDevice::deleteObject(MtpObjectHandle handle) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, handle);
+	if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
+		MtpResponseCode ret = readResponse();
+		if (ret == MTP_RESPONSE_OK)
+			return true;
+	}
+	return false;
+}
+
+MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
+	MtpObjectInfo* info = getObjectInfo(handle);
+	if (info) {
+		MtpObjectHandle parent = info->mParent;
+		delete info;
+		return parent;
+	} else {
+		return -1;
+	}
+}
+
+MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
+	MtpObjectInfo* info = getObjectInfo(handle);
+	if (info) {
+		MtpObjectHandle storageId = info->mStorageID;
+		delete info;
+		return storageId;
+	} else {
+		return -1;
+	}
+}
+
+MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, format);
+	if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
+		return NULL;
+	if (!readData())
+		return NULL;
+	MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_OK) {
+		return mData.getAUInt16();
+	}
+	return NULL;
+
+}
+
+MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, code);
+	if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
+		return NULL;
+	if (!readData())
+		return NULL;
+	MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_OK) {
+		MtpProperty* property = new MtpProperty;
+		property->read(mData);
+		return property;
+	}
+	return NULL;
+}
+
+MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, code);
+	mRequest.setParameter(2, format);
+	if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
+		return NULL;
+	if (!readData())
+		return NULL;
+	MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_OK) {
+		MtpProperty* property = new MtpProperty;
+		property->read(mData);
+		return property;
+	}
+	return NULL;
+}
+
+bool MtpDevice::readObject(MtpObjectHandle handle,
+		bool (* callback)(void* data, int offset, int length, void* clientData),
+		int objectSize, void* clientData) {
+	android::Mutex::Autolock autoLock(mMutex);
+	bool result = false;
+
+	mRequest.reset();
+	mRequest.setParameter(1, handle);
+	if (sendRequest(MTP_OPERATION_GET_OBJECT)
+			&& mData.readDataHeader(mRequestIn1)) {
+		uint32_t length = mData.getContainerLength();
+		if ((int)length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
+			MTPE("readObject error objectSize: %d, length: %d",
+					objectSize, length);
+			goto fail;
+		}
+		length -= MTP_CONTAINER_HEADER_SIZE;
+		uint32_t remaining = length;
+		int offset = 0;
+
+		int initialDataLength = 0;
+		void* initialData = mData.getData(initialDataLength);
+		if (initialData) {
+			if (initialDataLength > 0) {
+				if (!callback(initialData, 0, initialDataLength, clientData))
+					goto fail;
+				remaining -= initialDataLength;
+				offset += initialDataLength;
+			}
+			free(initialData);
+		}
+
+		// USB reads greater than 16K don't work
+		char buffer1[16384], buffer2[16384];
+		mRequestIn1->buffer = buffer1;
+		mRequestIn2->buffer = buffer2;
+		struct usb_request* req = mRequestIn1;
+		void* writeBuffer = NULL;
+		int writeLength = 0;
+
+		while (remaining > 0 || writeBuffer) {
+			if (remaining > 0) {
+				// queue up a read request
+				req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
+				if (mData.readDataAsync(req)) {
+					MTPE("readDataAsync failed");
+					goto fail;
+				}
+			} else {
+				req = NULL;
+			}
+
+			if (writeBuffer) {
+				// write previous buffer
+				if (!callback(writeBuffer, offset, writeLength, clientData)) {
+					MTPE("write failed");
+					// wait for pending read before failing
+					if (req)
+						mData.readDataWait(mDevice);
+					goto fail;
+				}
+				offset += writeLength;
+				writeBuffer = NULL;
+			}
+
+			// wait for read to complete
+			if (req) {
+				int read = mData.readDataWait(mDevice);
+				if (read < 0)
+					goto fail;
+
+				if (read > 0) {
+					writeBuffer = req->buffer;
+					writeLength = read;
+					remaining -= read;
+					req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+				} else {
+					writeBuffer = NULL;
+				}
+			}
+		}
+
+		MtpResponseCode response = readResponse();
+		if (response == MTP_RESPONSE_OK)
+			result = true;
+	}
+
+fail:
+	return result;
+}
+
+
+// reads the object's data and writes it to the specified file path
+bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
+	MTPI("readObject: %s", destPath);
+	int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, 0640);
+	if (fd < 0) {
+		MTPE("open failed for %s", destPath);
+		return false;
+	}
+
+	fchown(fd, getuid(), group);
+	// set permissions
+	int mask = umask(0);
+	fchmod(fd, perm);
+	umask(mask);
+
+	android::Mutex::Autolock autoLock(mMutex);
+	bool result = false;
+
+	mRequest.reset();
+	mRequest.setParameter(1, handle);
+	if (sendRequest(MTP_OPERATION_GET_OBJECT)
+			&& mData.readDataHeader(mRequestIn1)) {
+		uint32_t length = mData.getContainerLength();
+		if (length < MTP_CONTAINER_HEADER_SIZE)
+			goto fail;
+		length -= MTP_CONTAINER_HEADER_SIZE;
+		uint32_t remaining = length;
+
+		int initialDataLength = 0;
+		void* initialData = mData.getData(initialDataLength);
+		if (initialData) {
+			if (initialDataLength > 0) {
+				if (write(fd, initialData, initialDataLength) != initialDataLength) {
+					free(initialData);
+					goto fail;
+				}
+				remaining -= initialDataLength;
+			}
+			free(initialData);
+		}
+
+		// USB reads greater than 16K don't work
+		char buffer1[16384], buffer2[16384];
+		mRequestIn1->buffer = buffer1;
+		mRequestIn2->buffer = buffer2;
+		struct usb_request* req = mRequestIn1;
+		void* writeBuffer = NULL;
+		int writeLength = 0;
+
+		while (remaining > 0 || writeBuffer) {
+			if (remaining > 0) {
+				// queue up a read request
+				req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
+				if (mData.readDataAsync(req)) {
+					MTPE("readDataAsync failed");
+					goto fail;
+				}
+			} else {
+				req = NULL;
+			}
+
+			if (writeBuffer) {
+				// write previous buffer
+				if (write(fd, writeBuffer, writeLength) != writeLength) {
+					MTPE("write failed");
+					// wait for pending read before failing
+					if (req)
+						mData.readDataWait(mDevice);
+					goto fail;
+				}
+				writeBuffer = NULL;
+			}
+
+			// wait for read to complete
+			if (req) {
+				int read = mData.readDataWait(mDevice);
+				if (read < 0)
+					goto fail;
+
+				if (read > 0) {
+					writeBuffer = req->buffer;
+					writeLength = read;
+					remaining -= read;
+					req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+				} else {
+					writeBuffer = NULL;
+				}
+			}
+		}
+
+		MtpResponseCode response = readResponse();
+		if (response == MTP_RESPONSE_OK)
+			result = true;
+	}
+
+fail:
+	::close(fd);
+	return result;
+}
+
+bool MtpDevice::sendRequest(MtpOperationCode operation) {
+	MTPD("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
+	mReceivedResponse = false;
+	mRequest.setOperationCode(operation);
+	if (mTransactionID > 0)
+		mRequest.setTransactionID(mTransactionID++);
+	int ret = mRequest.write(mRequestOut);
+	mRequest.dump();
+	return (ret > 0);
+}
+
+bool MtpDevice::sendData() {
+	MTPD("sendData\n");
+	mData.setOperationCode(mRequest.getOperationCode());
+	mData.setTransactionID(mRequest.getTransactionID());
+	int ret = mData.write(mRequestOut);
+	mData.dump();
+	return (ret > 0);
+}
+
+bool MtpDevice::readData() {
+	mData.reset();
+	int ret = mData.read(mRequestIn1);
+	MTPD("readData returned %d\n", ret);
+	if (ret >= MTP_CONTAINER_HEADER_SIZE) {
+		if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
+			MTPD("got response packet instead of data packet");
+			// we got a response packet rather than data
+			// copy it to mResponse
+			mResponse.copyFrom(mData);
+			mReceivedResponse = true;
+			return false;
+		}
+		mData.dump();
+		return true;
+	}
+	else {
+		MTPE("readResponse failed\n");
+		return false;
+	}
+}
+
+bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
+	mData.setOperationCode(operation);
+	mData.setTransactionID(mRequest.getTransactionID());
+	return (!mData.writeDataHeader(mRequestOut, dataLength));
+}
+
+MtpResponseCode MtpDevice::readResponse() {
+	MTPD("readResponse\n");
+	if (mReceivedResponse) {
+		mReceivedResponse = false;
+		return mResponse.getResponseCode();
+	}
+	int ret = mResponse.read(mRequestIn1);
+	// handle zero length packets, which might occur if the data transfer
+	// ends on a packet boundary
+	if (ret == 0)
+		ret = mResponse.read(mRequestIn1);
+	if (ret >= MTP_CONTAINER_HEADER_SIZE) {
+		mResponse.dump();
+		return mResponse.getResponseCode();
+	} else {
+		MTPE("readResponse failed\n");
+		return -1;
+	}
+}
diff --git a/mtp/MtpDevice.h b/mtp/MtpDevice.h
new file mode 100755
index 0000000..d90b0c0
--- /dev/null
+++ b/mtp/MtpDevice.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_DEVICE_H
+#define _MTP_DEVICE_H
+
+#include "MtpRequestPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpResponsePacket.h"
+#include "MtpTypes.h"
+
+#include <utils/threads.h>
+
+struct usb_device;
+struct usb_request;
+struct usb_endpoint_descriptor;
+
+
+class MtpDeviceInfo;
+class MtpObjectInfo;
+class MtpStorageInfo;
+
+class MtpDevice {
+private:
+    struct usb_device*      mDevice;
+    int                     mInterface;
+    struct usb_request*     mRequestIn1;
+    struct usb_request*     mRequestIn2;
+    struct usb_request*     mRequestOut;
+    struct usb_request*     mRequestIntr;
+    MtpDeviceInfo*          mDeviceInfo;
+    MtpPropertyList         mDeviceProperties;
+
+    // current session ID
+    MtpSessionID            mSessionID;
+    // current transaction ID
+    MtpTransactionID        mTransactionID;
+
+    MtpRequestPacket        mRequest;
+    MtpDataPacket           mData;
+    MtpResponsePacket       mResponse;
+    // set to true if we received a response packet instead of a data packet
+    bool                    mReceivedResponse;
+
+    // to ensure only one MTP transaction at a time
+    android::Mutex                   mMutex;
+
+public:
+                            MtpDevice(struct usb_device* device, int interface,
+                                    const struct usb_endpoint_descriptor *ep_in,
+                                    const struct usb_endpoint_descriptor *ep_out,
+                                    const struct usb_endpoint_descriptor *ep_intr);
+
+    static MtpDevice*       open(const char* deviceName, int fd);
+
+    virtual                 ~MtpDevice();
+
+    void                    initialize();
+    void                    close();
+    void                    print();
+    const char*             getDeviceName();
+
+    bool                    openSession();
+    bool                    closeSession();
+
+    MtpDeviceInfo*          getDeviceInfo();
+    MtpStorageIDList*       getStorageIDs();
+    MtpStorageInfo*         getStorageInfo(MtpStorageID storageID);
+    MtpObjectHandleList*    getObjectHandles(MtpStorageID storageID, MtpObjectFormat format,
+                                    MtpObjectHandle parent);
+    MtpObjectInfo*          getObjectInfo(MtpObjectHandle handle);
+    void*                   getThumbnail(MtpObjectHandle handle, int& outLength);
+    MtpObjectHandle         sendObjectInfo(MtpObjectInfo* info);
+    bool                    sendObject(MtpObjectInfo* info, int srcFD);
+    bool                    deleteObject(MtpObjectHandle handle);
+    MtpObjectHandle         getParent(MtpObjectHandle handle);
+    MtpObjectHandle         getStorageID(MtpObjectHandle handle);
+
+    MtpObjectPropertyList*  getObjectPropsSupported(MtpObjectFormat format);
+
+    MtpProperty*            getDevicePropDesc(MtpDeviceProperty code);
+    MtpProperty*            getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format);
+
+    bool                    readObject(MtpObjectHandle handle,
+                                    bool (* callback)(void* data, int offset,
+                                            int length, void* clientData),
+                                    int objectSize, void* clientData);
+    bool                    readObject(MtpObjectHandle handle, const char* destPath, int group,
+                                    int perm);
+
+private:
+    bool                    sendRequest(MtpOperationCode operation);
+    bool                    sendData();
+    bool                    readData();
+    bool                    writeDataHeader(MtpOperationCode operation, int dataLength);
+    MtpResponseCode         readResponse();
+
+};
+
+
+#endif // _MTP_DEVICE_H
diff --git a/mtp/MtpDeviceInfo.cpp b/mtp/MtpDeviceInfo.cpp
new file mode 100755
index 0000000..337cc13
--- /dev/null
+++ b/mtp/MtpDeviceInfo.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpDeviceInfo.h"
+#include "MtpStringBuffer.h"
+
+MtpDeviceInfo::MtpDeviceInfo()
+	:	mStandardVersion(0),
+		mVendorExtensionID(0),
+		mVendorExtensionVersion(0),
+		mVendorExtensionDesc(NULL),
+		mFunctionalCode(0),
+		mOperations(NULL),
+		mEvents(NULL),
+		mDeviceProperties(NULL),
+		mCaptureFormats(NULL),
+		mPlaybackFormats(NULL),
+		mManufacturer(NULL),
+		mModel(NULL),
+		mVersion(NULL),
+		mSerial(NULL)
+{
+}
+
+MtpDeviceInfo::~MtpDeviceInfo() {
+	if (mVendorExtensionDesc)
+		free(mVendorExtensionDesc);
+	delete mOperations;
+	delete mEvents;
+	delete mDeviceProperties;
+	delete mCaptureFormats;
+	delete mPlaybackFormats;
+	if (mManufacturer)
+		free(mManufacturer);
+	if (mModel)
+		free(mModel);
+	if (mVersion)
+		free(mVersion);
+	if (mSerial)
+		free(mSerial);
+}
+
+void MtpDeviceInfo::read(MtpDataPacket& packet) {
+	MtpStringBuffer string;
+
+	// read the device info
+	mStandardVersion = packet.getUInt16();
+	mVendorExtensionID = packet.getUInt32();
+	mVendorExtensionVersion = packet.getUInt16();
+
+	packet.getString(string);
+	mVendorExtensionDesc = strdup((const char *)string);
+
+	mFunctionalCode = packet.getUInt16();
+	mOperations = packet.getAUInt16();
+	mEvents = packet.getAUInt16();
+	mDeviceProperties = packet.getAUInt16();
+	mCaptureFormats = packet.getAUInt16();
+	mPlaybackFormats = packet.getAUInt16();
+
+	packet.getString(string);
+	mManufacturer = strdup((const char *)string);
+	packet.getString(string);
+	mModel = strdup((const char *)string);
+	packet.getString(string);
+	mVersion = strdup((const char *)string);
+	packet.getString(string);
+	mSerial = strdup((const char *)string);
+}
+
+void MtpDeviceInfo::print() {
+	MTPI("Device Info:\n\tmStandardVersion: %d\n\tmVendorExtensionID: %d\n\tmVendorExtensionVersiony: %d\n",
+			mStandardVersion, mVendorExtensionID, mVendorExtensionVersion);
+	MTPI("\tmVendorExtensionDesc: %s\n\tmFunctionalCode: %d\n\tmManufacturer: %s\n\tmModel: %s\n\tmVersion: %s\n\tmSerial: %s\n",
+			mVendorExtensionDesc, mFunctionalCode, mManufacturer, mModel, mVersion, mSerial);
+}
+
diff --git a/mtp/MtpDeviceInfo.h b/mtp/MtpDeviceInfo.h
new file mode 100755
index 0000000..b316371
--- /dev/null
+++ b/mtp/MtpDeviceInfo.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_DEVICE_INFO_H
+#define _MTP_DEVICE_INFO_H
+
+struct stat;
+
+
+class MtpDataPacket;
+
+class MtpDeviceInfo {
+public:
+    uint16_t                mStandardVersion;
+    uint32_t                mVendorExtensionID;
+    uint16_t                mVendorExtensionVersion;
+    char*                   mVendorExtensionDesc;
+    uint16_t                mFunctionalCode;
+    UInt16List*             mOperations;
+    UInt16List*             mEvents;
+    MtpDevicePropertyList*  mDeviceProperties;
+    MtpObjectFormatList*    mCaptureFormats;
+    MtpObjectFormatList*    mPlaybackFormats;
+    char*                   mManufacturer;
+    char*                   mModel;
+    char*                   mVersion;
+    char*                   mSerial;
+
+public:
+                            MtpDeviceInfo();
+    virtual                 ~MtpDeviceInfo();
+
+    void                    read(MtpDataPacket& packet);
+
+    void                    print();
+};
+
+
+#endif // _MTP_DEVICE_INFO_H
diff --git a/mtp/MtpEventPacket.cpp b/mtp/MtpEventPacket.cpp
new file mode 100755
index 0000000..1119f7d
--- /dev/null
+++ b/mtp/MtpEventPacket.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#ifdef MTP_DEVICE
+#include <linux/usb/f_mtp.h>
+#endif
+
+#include "MtpEventPacket.h"
+
+#include <usbhost/usbhost.h>
+
+
+MtpEventPacket::MtpEventPacket()
+	:	MtpPacket(512)
+{
+}
+
+MtpEventPacket::~MtpEventPacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpEventPacket::write(int fd) {
+	struct mtp_event	event;
+
+	putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+	putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_EVENT);
+
+	event.data = mBuffer;
+	event.length = mPacketSize;
+	int ret = ::ioctl(fd, MTP_SEND_EVENT, (unsigned long)&event);
+	return (ret < 0 ? ret : 0);
+}
+#endif
+
+#ifdef MTP_HOST
+int MtpEventPacket::read(struct usb_request *request) {
+	request->buffer = mBuffer;
+	request->buffer_length = mBufferSize;
+	int ret = transfer(request);
+	 if (ret >= 0)
+		mPacketSize = ret;
+	else
+		mPacketSize = 0;
+	return ret;
+}
+#endif
+
+
diff --git a/mtp/MtpEventPacket.h b/mtp/MtpEventPacket.h
new file mode 100755
index 0000000..b42abce
--- /dev/null
+++ b/mtp/MtpEventPacket.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_EVENT_PACKET_H
+#define _MTP_EVENT_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+class MtpEventPacket : public MtpPacket {
+
+public:
+                        MtpEventPacket();
+    virtual             ~MtpEventPacket();
+
+#ifdef MTP_DEVICE
+    // write our data to the given file descriptor
+    int                 write(int fd);
+#endif
+
+#ifdef MTP_HOST
+    // read our buffer with the given request
+    int                 read(struct usb_request *request);
+#endif
+
+    inline MtpEventCode     getEventCode() const { return getContainerCode(); }
+    inline void             setEventCode(MtpEventCode code)
+                                                     { return setContainerCode(code); }
+};
+
+#endif // _MTP_EVENT_PACKET_H
diff --git a/mtp/MtpObjectInfo.cpp b/mtp/MtpObjectInfo.cpp
new file mode 100755
index 0000000..50192d7
--- /dev/null
+++ b/mtp/MtpObjectInfo.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpObjectInfo.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+
+MtpObjectInfo::MtpObjectInfo(MtpObjectHandle handle)
+	:   mHandle(handle),
+		mStorageID(0),
+		mFormat(0),
+		mProtectionStatus(0),
+		mCompressedSize(0),
+		mThumbFormat(0),
+		mThumbCompressedSize(0),
+		mThumbPixWidth(0),
+		mThumbPixHeight(0),
+		mImagePixWidth(0),
+		mImagePixHeight(0),
+		mImagePixDepth(0),
+		mParent(0),
+		mAssociationType(0),
+		mAssociationDesc(0),
+		mSequenceNumber(0),
+		mName(NULL),
+		mDateCreated(0),
+		mDateModified(0),
+		mKeywords(NULL)
+{
+}
+
+MtpObjectInfo::~MtpObjectInfo() {
+	if (mName)
+		free(mName);
+	if (mKeywords)
+		free(mKeywords);
+}
+
+void MtpObjectInfo::read(MtpDataPacket& packet) {
+	MtpStringBuffer string;
+	time_t time;
+
+	mStorageID = packet.getUInt32();
+	mFormat = packet.getUInt16();
+	mProtectionStatus = packet.getUInt16();
+	mCompressedSize = packet.getUInt32();
+	mThumbFormat = packet.getUInt16();
+	mThumbCompressedSize = packet.getUInt32();
+	mThumbPixWidth = packet.getUInt32();
+	mThumbPixHeight = packet.getUInt32();
+	mImagePixWidth = packet.getUInt32();
+	mImagePixHeight = packet.getUInt32();
+	mImagePixDepth = packet.getUInt32();
+	mParent = packet.getUInt32();
+	mAssociationType = packet.getUInt16();
+	mAssociationDesc = packet.getUInt32();
+	mSequenceNumber = packet.getUInt32();
+
+	packet.getString(string);
+	mName = strdup((const char *)string);
+
+	packet.getString(string);
+	if (parseDateTime((const char*)string, time))
+		mDateCreated = time;
+
+	packet.getString(string);
+	if (parseDateTime((const char*)string, time))
+		mDateModified = time;
+
+	packet.getString(string);
+	mKeywords = strdup((const char *)string);
+}
+
+void MtpObjectInfo::print() {
+	MTPI("MtpObject Info %08X: %s\n", mHandle, mName);
+	MTPI("  mStorageID: %08X mFormat: %04X mProtectionStatus: %d\n",
+			mStorageID, mFormat, mProtectionStatus);
+	MTPI("  mCompressedSize: %d mThumbFormat: %04X mThumbCompressedSize: %d\n",
+			mCompressedSize, mFormat, mThumbCompressedSize);
+	MTPI("  mThumbPixWidth: %d mThumbPixHeight: %d\n", mThumbPixWidth, mThumbPixHeight);
+	MTPI("  mImagePixWidth: %d mImagePixHeight: %d mImagePixDepth: %d\n",
+			mImagePixWidth, mImagePixHeight, mImagePixDepth);
+	MTPI("  mParent: %08X mAssociationType: %04X mAssociationDesc: %04X\n",
+			mParent, mAssociationType, mAssociationDesc);
+	MTPI("  mSequenceNumber: %d mDateCreated: %ld mDateModified: %ld mKeywords: %s\n",
+			mSequenceNumber, mDateCreated, mDateModified, mKeywords);
+}
+
diff --git a/mtp/MtpObjectInfo.h b/mtp/MtpObjectInfo.h
new file mode 100755
index 0000000..406c3f4
--- /dev/null
+++ b/mtp/MtpObjectInfo.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_OBJECT_INFO_H
+#define _MTP_OBJECT_INFO_H
+
+#include "MtpTypes.h"
+
+
+class MtpDataPacket;
+
+class MtpObjectInfo {
+public:
+    MtpObjectHandle     mHandle;
+    MtpStorageID        mStorageID;
+    MtpObjectFormat     mFormat;
+    uint16_t            mProtectionStatus;
+    uint32_t            mCompressedSize;
+    MtpObjectFormat     mThumbFormat;
+    uint32_t            mThumbCompressedSize;
+    uint32_t            mThumbPixWidth;
+    uint32_t            mThumbPixHeight;
+    uint32_t            mImagePixWidth;
+    uint32_t            mImagePixHeight;
+    uint32_t            mImagePixDepth;
+    MtpObjectHandle     mParent;
+    uint16_t            mAssociationType;
+    uint32_t            mAssociationDesc;
+    uint32_t            mSequenceNumber;
+    char*               mName;
+    time_t              mDateCreated;
+    time_t              mDateModified;
+    char*               mKeywords;
+
+public:
+                        MtpObjectInfo(MtpObjectHandle handle);
+    virtual             ~MtpObjectInfo();
+
+    void                read(MtpDataPacket& packet);
+
+    void                print();
+};
+
+
+#endif // _MTP_OBJECT_INFO_H
diff --git a/mtp/MtpPacket.cpp b/mtp/MtpPacket.cpp
new file mode 100755
index 0000000..2f9e438
--- /dev/null
+++ b/mtp/MtpPacket.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpPacket.h"
+#include "mtp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <usbhost/usbhost.h>
+
+
+MtpPacket::MtpPacket(int bufferSize)
+	:   mBuffer(NULL),
+		mBufferSize(bufferSize),
+		mAllocationIncrement(bufferSize),
+		mPacketSize(0)
+{
+	mBuffer = (uint8_t *)malloc(bufferSize);
+	if (!mBuffer) {
+		MTPE("out of memory!");
+		abort();
+	}
+}
+
+MtpPacket::~MtpPacket() {
+	if (mBuffer)
+		free(mBuffer);
+}
+
+void MtpPacket::reset() {
+	allocate(MTP_CONTAINER_HEADER_SIZE);
+	mPacketSize = MTP_CONTAINER_HEADER_SIZE;
+	memset(mBuffer, 0, mBufferSize);
+}
+
+void MtpPacket::allocate(int length) {
+	if (length > mBufferSize) {
+		int newLength = length + mAllocationIncrement;
+		mBuffer = (uint8_t *)realloc(mBuffer, newLength);
+		if (!mBuffer) {
+			MTPE("out of memory!");
+			abort();
+		}
+		mBufferSize = newLength;
+	}
+}
+
+void MtpPacket::dump() {
+#define DUMP_BYTES_PER_ROW  16
+	char buffer[500];
+	char* bufptr = buffer;
+
+	for (size_t i = 0; i < mPacketSize; i++) {
+		sprintf(bufptr, "%02X ", mBuffer[i]);
+		bufptr += strlen(bufptr);
+		if (i % DUMP_BYTES_PER_ROW == (DUMP_BYTES_PER_ROW - 1)) {
+			MTPD("%s", buffer);
+			bufptr = buffer;
+		}
+	}
+	if (bufptr != buffer) {
+		// print last line
+		MTPD("%s", buffer);
+	}
+	MTPD("\n");
+}
+
+void MtpPacket::copyFrom(const MtpPacket& src) {
+	int length = src.mPacketSize;
+	allocate(length);
+	mPacketSize = length;
+	memcpy(mBuffer, src.mBuffer, length);
+}
+
+uint16_t MtpPacket::getUInt16(int offset) const {
+	return ((uint16_t)mBuffer[offset + 1] << 8) | (uint16_t)mBuffer[offset];
+}
+
+uint32_t MtpPacket::getUInt32(int offset) const {
+	return ((uint32_t)mBuffer[offset + 3] << 24) | ((uint32_t)mBuffer[offset + 2] << 16) |
+		   ((uint32_t)mBuffer[offset + 1] << 8)  | (uint32_t)mBuffer[offset];
+}
+
+void MtpPacket::putUInt16(int offset, uint16_t value) {
+	mBuffer[offset++] = (uint8_t)(value & 0xFF);
+	mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
+}
+
+void MtpPacket::putUInt32(int offset, uint32_t value) {
+	mBuffer[offset++] = (uint8_t)(value & 0xFF);
+	mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
+	mBuffer[offset++] = (uint8_t)((value >> 16) & 0xFF);
+	mBuffer[offset++] = (uint8_t)((value >> 24) & 0xFF);
+}
+
+uint16_t MtpPacket::getContainerCode() const {
+	return getUInt16(MTP_CONTAINER_CODE_OFFSET);
+}
+
+void MtpPacket::setContainerCode(uint16_t code) {
+	putUInt16(MTP_CONTAINER_CODE_OFFSET, code);
+}
+
+uint16_t MtpPacket::getContainerType() const {
+	return getUInt16(MTP_CONTAINER_TYPE_OFFSET);
+}
+
+MtpTransactionID MtpPacket::getTransactionID() const {
+	return getUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET);
+}
+
+void MtpPacket::setTransactionID(MtpTransactionID id) {
+	putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
+}
+
+uint32_t MtpPacket::getParameter(int index) const {
+	if (index < 1 || index > 5) {
+		MTPE("index %d out of range in MtpPacket::getParameter", index);
+		return 0;
+	}
+	return getUInt32(MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t));
+}
+
+void MtpPacket::setParameter(int index, uint32_t value) {
+	if (index < 1 || index > 5) {
+		MTPE("index %d out of range in MtpPacket::setParameter", index);
+		return;
+	}
+	int offset = MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t);
+	if (mPacketSize < offset + sizeof(uint32_t))
+		mPacketSize = offset + sizeof(uint32_t);
+	putUInt32(offset, value);
+}
+
+#ifdef MTP_HOST
+int MtpPacket::transfer(struct usb_request* request) {
+	int result = usb_device_bulk_transfer(request->dev,
+							request->endpoint,
+							request->buffer,
+							request->buffer_length,
+							0);
+	request->actual_length = result;
+	return result;
+}
+#endif
+
diff --git a/mtp/MtpPacket.h b/mtp/MtpPacket.h
new file mode 100755
index 0000000..ec763c8
--- /dev/null
+++ b/mtp/MtpPacket.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_PACKET_H
+#define _MTP_PACKET_H
+
+#include "MtpTypes.h"
+
+struct usb_request;
+
+
+class MtpPacket {
+
+protected:
+    uint8_t*            mBuffer;
+    // current size of the buffer
+    int                 mBufferSize;
+    // number of bytes to add when resizing the buffer
+    int                 mAllocationIncrement;
+    // size of the data in the packet
+    unsigned            mPacketSize;
+
+public:
+                        MtpPacket(int bufferSize);
+    virtual             ~MtpPacket();
+
+    // sets packet size to the default container size and sets buffer to zero
+    virtual void        reset();
+
+    void                allocate(int length);
+    void                dump();
+    void                copyFrom(const MtpPacket& src);
+
+    uint16_t            getContainerCode() const;
+    void                setContainerCode(uint16_t code);
+
+    uint16_t            getContainerType() const;
+
+    MtpTransactionID    getTransactionID() const;
+    void                setTransactionID(MtpTransactionID id);
+
+    uint32_t            getParameter(int index) const;
+    void                setParameter(int index, uint32_t value);
+
+#ifdef MTP_HOST
+    int                 transfer(struct usb_request* request);
+#endif
+
+protected:
+    uint16_t            getUInt16(int offset) const;
+    uint32_t            getUInt32(int offset) const;
+    void                putUInt16(int offset, uint16_t value);
+    void                putUInt32(int offset, uint32_t value);
+};
+
+
+#endif // _MTP_PACKET_H
diff --git a/mtp/MtpProperty.cpp b/mtp/MtpProperty.cpp
new file mode 100755
index 0000000..e105f24
--- /dev/null
+++ b/mtp/MtpProperty.cpp
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDataPacket.h"
+#include "MtpDebug.h"
+#include "MtpProperty.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+
+MtpProperty::MtpProperty()
+	:	mCode(0),
+		mType(0),
+		mWriteable(false),
+		mDefaultArrayLength(0),
+		mDefaultArrayValues(NULL),
+		mCurrentArrayLength(0),
+		mCurrentArrayValues(NULL),
+		mGroupCode(0),
+		mFormFlag(kFormNone),
+		mEnumLength(0),
+		mEnumValues(NULL)
+{
+	memset(&mDefaultValue, 0, sizeof(mDefaultValue));
+	memset(&mCurrentValue, 0, sizeof(mCurrentValue));
+	memset(&mMinimumValue, 0, sizeof(mMinimumValue));
+	memset(&mMaximumValue, 0, sizeof(mMaximumValue));
+}
+
+MtpProperty::MtpProperty(MtpPropertyCode propCode,
+						 MtpDataType type,
+						 bool writeable,
+						 int defaultValue)
+	:   mCode(propCode),
+		mType(type),
+		mWriteable(writeable),
+		mDefaultArrayLength(0),
+		mDefaultArrayValues(NULL),
+		mCurrentArrayLength(0),
+		mCurrentArrayValues(NULL),
+		mGroupCode(0),
+		mFormFlag(kFormNone),
+		mEnumLength(0),
+		mEnumValues(NULL)
+{
+	memset(&mDefaultValue, 0, sizeof(mDefaultValue));
+	memset(&mCurrentValue, 0, sizeof(mCurrentValue));
+	memset(&mMinimumValue, 0, sizeof(mMinimumValue));
+	memset(&mMaximumValue, 0, sizeof(mMaximumValue));
+
+	if (defaultValue) {
+		switch (type) {
+			case MTP_TYPE_INT8:
+				mDefaultValue.u.i8 = defaultValue;
+				break;
+			case MTP_TYPE_UINT8:
+				mDefaultValue.u.u8 = defaultValue;
+				break;
+			case MTP_TYPE_INT16:
+				mDefaultValue.u.i16 = defaultValue;
+				break;
+			case MTP_TYPE_UINT16:
+				mDefaultValue.u.u16 = defaultValue;
+				break;
+			case MTP_TYPE_INT32:
+				mDefaultValue.u.i32 = defaultValue;
+				break;
+			case MTP_TYPE_UINT32:
+				mDefaultValue.u.u32 = defaultValue;
+				break;
+			case MTP_TYPE_INT64:
+				mDefaultValue.u.i64 = defaultValue;
+				break;
+			case MTP_TYPE_UINT64:
+				mDefaultValue.u.u64 = defaultValue;
+				break;
+			default:
+				MTPE("unknown type %04X in MtpProperty::MtpProperty", type);
+		}
+	}
+}
+
+MtpProperty::~MtpProperty() {
+	if (mType == MTP_TYPE_STR) {
+		// free all strings
+		free(mDefaultValue.str);
+		free(mCurrentValue.str);
+		free(mMinimumValue.str);
+		free(mMaximumValue.str);
+		if (mDefaultArrayValues) {
+			for (int i = 0; i < mDefaultArrayLength; i++)
+				free(mDefaultArrayValues[i].str);
+		}
+		if (mCurrentArrayValues) {
+			for (int i = 0; i < mCurrentArrayLength; i++)
+				free(mCurrentArrayValues[i].str);
+		}
+		if (mEnumValues) {
+			for (int i = 0; i < mEnumLength; i++)
+				free(mEnumValues[i].str);
+		}
+	}
+	delete[] mDefaultArrayValues;
+	delete[] mCurrentArrayValues;
+	delete[] mEnumValues;
+}
+
+void MtpProperty::read(MtpDataPacket& packet) {
+	mCode = packet.getUInt16();
+	bool deviceProp = isDeviceProperty();
+	mType = packet.getUInt16();
+	mWriteable = (packet.getUInt8() == 1);
+	switch (mType) {
+		case MTP_TYPE_AINT8:
+		case MTP_TYPE_AUINT8:
+		case MTP_TYPE_AINT16:
+		case MTP_TYPE_AUINT16:
+		case MTP_TYPE_AINT32:
+		case MTP_TYPE_AUINT32:
+		case MTP_TYPE_AINT64:
+		case MTP_TYPE_AUINT64:
+		case MTP_TYPE_AINT128:
+		case MTP_TYPE_AUINT128:
+			mDefaultArrayValues = readArrayValues(packet, mDefaultArrayLength);
+			if (deviceProp)
+				mCurrentArrayValues = readArrayValues(packet, mCurrentArrayLength);
+			break;
+		default:
+			readValue(packet, mDefaultValue);
+			if (deviceProp)
+				readValue(packet, mCurrentValue);
+	}
+	if (!deviceProp)
+		mGroupCode = packet.getUInt32();
+	mFormFlag = packet.getUInt8();
+
+	if (mFormFlag == kFormRange) {
+			readValue(packet, mMinimumValue);
+			readValue(packet, mMaximumValue);
+			readValue(packet, mStepSize);
+	} else if (mFormFlag == kFormEnum) {
+		mEnumLength = packet.getUInt16();
+		mEnumValues = new MtpPropertyValue[mEnumLength];
+		for (int i = 0; i < mEnumLength; i++)
+			readValue(packet, mEnumValues[i]);
+	}
+}
+
+void MtpProperty::write(MtpDataPacket& packet) {
+	bool deviceProp = isDeviceProperty();
+
+	packet.putUInt16(mCode);
+	packet.putUInt16(mType);
+	packet.putUInt8(mWriteable ? 1 : 0);
+
+	switch (mType) {
+		case MTP_TYPE_AINT8:
+		case MTP_TYPE_AUINT8:
+		case MTP_TYPE_AINT16:
+		case MTP_TYPE_AUINT16:
+		case MTP_TYPE_AINT32:
+		case MTP_TYPE_AUINT32:
+		case MTP_TYPE_AINT64:
+		case MTP_TYPE_AUINT64:
+		case MTP_TYPE_AINT128:
+		case MTP_TYPE_AUINT128:
+			writeArrayValues(packet, mDefaultArrayValues, mDefaultArrayLength);
+			if (deviceProp)
+				writeArrayValues(packet, mCurrentArrayValues, mCurrentArrayLength);
+			break;
+		default:
+			writeValue(packet, mDefaultValue);
+			if (deviceProp)
+				writeValue(packet, mCurrentValue);
+	}
+	packet.putUInt32(mGroupCode);
+	if (!deviceProp)
+		packet.putUInt8(mFormFlag);
+	if (mFormFlag == kFormRange) {
+			writeValue(packet, mMinimumValue);
+			writeValue(packet, mMaximumValue);
+			writeValue(packet, mStepSize);
+	} else if (mFormFlag == kFormEnum) {
+		packet.putUInt16(mEnumLength);
+		for (int i = 0; i < mEnumLength; i++)
+			writeValue(packet, mEnumValues[i]);
+	}
+}
+
+void MtpProperty::setDefaultValue(const uint16_t* string) {
+	free(mDefaultValue.str);
+	if (string) {
+		MtpStringBuffer buffer(string);
+		mDefaultValue.str = strdup(buffer);
+	}
+	else
+		mDefaultValue.str = NULL;
+}
+
+void MtpProperty::setCurrentValue(const uint16_t* string) {
+	free(mCurrentValue.str);
+	if (string) {
+		MtpStringBuffer buffer(string);
+		mCurrentValue.str = strdup(buffer);
+	}
+	else
+		mCurrentValue.str = NULL;
+}
+
+void MtpProperty::setFormRange(int min, int max, int step) {
+	mFormFlag = kFormRange;
+	switch (mType) {
+		case MTP_TYPE_INT8:
+			mMinimumValue.u.i8 = min;
+			mMaximumValue.u.i8 = max;
+			mStepSize.u.i8 = step;
+			break;
+		case MTP_TYPE_UINT8:
+			mMinimumValue.u.u8 = min;
+			mMaximumValue.u.u8 = max;
+			mStepSize.u.u8 = step;
+			break;
+		case MTP_TYPE_INT16:
+			mMinimumValue.u.i16 = min;
+			mMaximumValue.u.i16 = max;
+			mStepSize.u.i16 = step;
+			break;
+		case MTP_TYPE_UINT16:
+			mMinimumValue.u.u16 = min;
+			mMaximumValue.u.u16 = max;
+			mStepSize.u.u16 = step;
+			break;
+		case MTP_TYPE_INT32:
+			mMinimumValue.u.i32 = min;
+			mMaximumValue.u.i32 = max;
+			mStepSize.u.i32 = step;
+			break;
+		case MTP_TYPE_UINT32:
+			mMinimumValue.u.u32 = min;
+			mMaximumValue.u.u32 = max;
+			mStepSize.u.u32 = step;
+			break;
+		case MTP_TYPE_INT64:
+			mMinimumValue.u.i64 = min;
+			mMaximumValue.u.i64 = max;
+			mStepSize.u.i64 = step;
+			break;
+		case MTP_TYPE_UINT64:
+			mMinimumValue.u.u64 = min;
+			mMaximumValue.u.u64 = max;
+			mStepSize.u.u64 = step;
+			break;
+		default:
+			MTPE("unsupported type for MtpProperty::setRange");
+			break;
+	}
+}
+
+void MtpProperty::setFormEnum(const int* values, int count) {
+	 mFormFlag = kFormEnum;
+	 delete[] mEnumValues;
+	 mEnumValues = new MtpPropertyValue[count];
+	 mEnumLength = count;
+
+	for (int i = 0; i < count; i++) {
+		int value = *values++;
+			switch (mType) {
+				case MTP_TYPE_INT8:
+					mEnumValues[i].u.i8 = value;
+					break;
+				case MTP_TYPE_UINT8:
+					mEnumValues[i].u.u8 = value;
+					break;
+				case MTP_TYPE_INT16:
+					mEnumValues[i].u.i16 = value;
+					break;
+				case MTP_TYPE_UINT16:
+					mEnumValues[i].u.u16 = value;
+					break;
+				case MTP_TYPE_INT32:
+					mEnumValues[i].u.i32 = value;
+					break;
+				case MTP_TYPE_UINT32:
+					mEnumValues[i].u.u32 = value;
+					break;
+				case MTP_TYPE_INT64:
+					mEnumValues[i].u.i64 = value;
+					break;
+				case MTP_TYPE_UINT64:
+					mEnumValues[i].u.u64 = value;
+					break;
+				default:
+					MTPE("unsupported type for MtpProperty::setEnum");
+					break;
+		}
+	}
+}
+
+void MtpProperty::setFormDateTime() {
+	 mFormFlag = kFormDateTime;
+}
+
+void MtpProperty::print() {
+	MtpString buffer;
+	bool deviceProp = isDeviceProperty();
+	if (deviceProp)
+		MTPI("	%s (%04X)", MtpDebug::getDevicePropCodeName(mCode), mCode);
+	else
+		MTPI("	%s (%04X)", MtpDebug::getObjectPropCodeName(mCode), mCode);
+	MTPI("	type %04X", mType);
+	MTPI("	writeable %s", (mWriteable ? "true" : "false"));
+	buffer = "	default value: ";
+	print(mDefaultValue, buffer);
+	MTPI("%s", (const char *)buffer);
+	if (deviceProp) {
+		buffer = "	current value: ";
+		print(mCurrentValue, buffer);
+		MTPI("%s", (const char *)buffer);
+	}
+	switch (mFormFlag) {
+		case kFormNone:
+			break;
+		case kFormRange:
+			buffer = "	Range (";
+			print(mMinimumValue, buffer);
+			buffer += ", ";
+			print(mMaximumValue, buffer);
+			buffer += ", ";
+			print(mStepSize, buffer);
+			buffer += ")";
+			MTPI("%s", (const char *)buffer);
+			break;
+		case kFormEnum:
+			buffer = "	Enum { ";
+			for (int i = 0; i < mEnumLength; i++) {
+				print(mEnumValues[i], buffer);
+				buffer += " ";
+			}
+			buffer += "}";
+			MTPI("%s", (const char *)buffer);
+			break;
+		case kFormDateTime:
+			MTPI("	DateTime\n");
+			break;
+		default:
+			MTPI("	form %d\n", mFormFlag);
+			break;
+	}
+}
+
+void MtpProperty::print(MtpPropertyValue& value, MtpString& buffer) {
+	switch (mType) {
+		case MTP_TYPE_INT8:
+			buffer.appendFormat("%d", value.u.i8);
+			break;
+		case MTP_TYPE_UINT8:
+			buffer.appendFormat("%d", value.u.u8);
+			break;
+		case MTP_TYPE_INT16:
+			buffer.appendFormat("%d", value.u.i16);
+			break;
+		case MTP_TYPE_UINT16:
+			buffer.appendFormat("%d", value.u.u16);
+			break;
+		case MTP_TYPE_INT32:
+			buffer.appendFormat("%d", value.u.i32);
+			break;
+		case MTP_TYPE_UINT32:
+			buffer.appendFormat("%d", value.u.u32);
+			break;
+		case MTP_TYPE_INT64:
+			buffer.appendFormat("%lld", value.u.i64);
+			break;
+		case MTP_TYPE_UINT64:
+			buffer.appendFormat("%lld", value.u.u64);
+			break;
+		case MTP_TYPE_INT128:
+			buffer.appendFormat("%08X%08X%08X%08X", value.u.i128[0], value.u.i128[1],
+					value.u.i128[2], value.u.i128[3]);
+			break;
+		case MTP_TYPE_UINT128:
+			buffer.appendFormat("%08X%08X%08X%08X", value.u.u128[0], value.u.u128[1],
+					value.u.u128[2], value.u.u128[3]);
+			break;
+		case MTP_TYPE_STR:
+			buffer.appendFormat("%s", value.str);
+			break;
+		default:
+			MTPE("unsupported type for MtpProperty::print\n");
+			break;
+	}
+}
+
+void MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) {
+	MtpStringBuffer stringBuffer;
+
+	switch (mType) {
+		case MTP_TYPE_INT8:
+		case MTP_TYPE_AINT8:
+			value.u.i8 = packet.getInt8();
+			break;
+		case MTP_TYPE_UINT8:
+		case MTP_TYPE_AUINT8:
+			value.u.u8 = packet.getUInt8();
+			break;
+		case MTP_TYPE_INT16:
+		case MTP_TYPE_AINT16:
+			value.u.i16 = packet.getInt16();
+			break;
+		case MTP_TYPE_UINT16:
+		case MTP_TYPE_AUINT16:
+			value.u.u16 = packet.getUInt16();
+			break;
+		case MTP_TYPE_INT32:
+		case MTP_TYPE_AINT32:
+			value.u.i32 = packet.getInt32();
+			break;
+		case MTP_TYPE_UINT32:
+		case MTP_TYPE_AUINT32:
+			value.u.u32 = packet.getUInt32();
+			break;
+		case MTP_TYPE_INT64:
+		case MTP_TYPE_AINT64:
+			value.u.i64 = packet.getInt64();
+			break;
+		case MTP_TYPE_UINT64:
+		case MTP_TYPE_AUINT64:
+			value.u.u64 = packet.getUInt64();
+			break;
+		case MTP_TYPE_INT128:
+		case MTP_TYPE_AINT128:
+			packet.getInt128(value.u.i128);
+			break;
+		case MTP_TYPE_UINT128:
+		case MTP_TYPE_AUINT128:
+			packet.getUInt128(value.u.u128);
+			break;
+		case MTP_TYPE_STR:
+			packet.getString(stringBuffer);
+			value.str = strdup(stringBuffer);
+			break;
+		default:
+			MTPE("unknown type %04X in MtpProperty::readValue", mType);
+	}
+}
+
+void MtpProperty::writeValue(MtpDataPacket& packet, MtpPropertyValue& value) {
+	MtpStringBuffer stringBuffer;
+
+	switch (mType) {
+		case MTP_TYPE_INT8:
+		case MTP_TYPE_AINT8:
+			packet.putInt8(value.u.i8);
+			break;
+		case MTP_TYPE_UINT8:
+		case MTP_TYPE_AUINT8:
+			packet.putUInt8(value.u.u8);
+			break;
+		case MTP_TYPE_INT16:
+		case MTP_TYPE_AINT16:
+			packet.putInt16(value.u.i16);
+			break;
+		case MTP_TYPE_UINT16:
+		case MTP_TYPE_AUINT16:
+			packet.putUInt16(value.u.u16);
+			break;
+		case MTP_TYPE_INT32:
+		case MTP_TYPE_AINT32:
+			packet.putInt32(value.u.i32);
+			break;
+		case MTP_TYPE_UINT32:
+		case MTP_TYPE_AUINT32:
+			packet.putUInt32(value.u.u32);
+			break;
+		case MTP_TYPE_INT64:
+		case MTP_TYPE_AINT64:
+			packet.putInt64(value.u.i64);
+			break;
+		case MTP_TYPE_UINT64:
+		case MTP_TYPE_AUINT64:
+			packet.putUInt64(value.u.u64);
+			break;
+		case MTP_TYPE_INT128:
+		case MTP_TYPE_AINT128:
+			packet.putInt128(value.u.i128);
+			break;
+		case MTP_TYPE_UINT128:
+		case MTP_TYPE_AUINT128:
+			packet.putUInt128(value.u.u128);
+			break;
+		case MTP_TYPE_STR:
+			if (value.str)
+				packet.putString(value.str);
+			else
+				packet.putEmptyString();
+			break;
+		default:
+			MTPE("unknown type %04X in MtpProperty::writeValue", mType);
+	}
+}
+
+MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, int& length) {
+	length = packet.getUInt32();
+	if (length == 0)
+		return NULL;
+	MtpPropertyValue* result = new MtpPropertyValue[length];
+	for (int i = 0; i < length; i++)
+		readValue(packet, result[i]);
+	return result;
+}
+
+void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, int length) {
+	packet.putUInt32(length);
+	for (int i = 0; i < length; i++)
+		writeValue(packet, values[i]);
+}
+
diff --git a/mtp/MtpProperty.h b/mtp/MtpProperty.h
new file mode 100755
index 0000000..017d875
--- /dev/null
+++ b/mtp/MtpProperty.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_PROPERTY_H
+#define _MTP_PROPERTY_H
+
+#include "MtpTypes.h"
+
+
+class MtpDataPacket;
+
+struct MtpPropertyValue {
+    union {
+        int8_t          i8;
+        uint8_t         u8;
+        int16_t         i16;
+        uint16_t        u16;
+        int32_t         i32;
+        uint32_t        u32;
+        int64_t         i64;
+        uint64_t        u64;
+        int128_t        i128;
+        uint128_t       u128;
+    } u;
+    // string in UTF8 format
+    char*               str;
+};
+
+class MtpProperty {
+public:
+    MtpPropertyCode     mCode;
+    MtpDataType         mType;
+    bool                mWriteable;
+    MtpPropertyValue    mDefaultValue;
+    MtpPropertyValue    mCurrentValue;
+
+    // for array types
+    int                 mDefaultArrayLength;
+    MtpPropertyValue*   mDefaultArrayValues;
+    int                 mCurrentArrayLength;
+    MtpPropertyValue*   mCurrentArrayValues;
+
+    enum {
+        kFormNone = 0,
+        kFormRange = 1,
+        kFormEnum = 2,
+        kFormDateTime = 3,
+    };
+
+    uint32_t            mGroupCode;
+    uint8_t             mFormFlag;
+
+    // for range form
+    MtpPropertyValue    mMinimumValue;
+    MtpPropertyValue    mMaximumValue;
+    MtpPropertyValue    mStepSize;
+
+    // for enum form
+    int                 mEnumLength;
+    MtpPropertyValue*   mEnumValues;
+
+public:
+                        MtpProperty();
+                        MtpProperty(MtpPropertyCode propCode,
+                                     MtpDataType type,
+                                     bool writeable = false,
+                                     int defaultValue = 0);
+    virtual             ~MtpProperty();
+
+    inline MtpPropertyCode getPropertyCode() const { return mCode; }
+
+    void                read(MtpDataPacket& packet);
+    void                write(MtpDataPacket& packet);
+
+    void                setDefaultValue(const uint16_t* string);
+    void                setCurrentValue(const uint16_t* string);
+
+    void                setFormRange(int min, int max, int step);
+    void                setFormEnum(const int* values, int count);
+    void                setFormDateTime();
+
+    void                print();
+    void                print(MtpPropertyValue& value, MtpString& buffer);
+
+    inline bool         isDeviceProperty() const {
+                            return (   ((mCode & 0xF000) == 0x5000)
+                                    || ((mCode & 0xF800) == 0xD000));
+                        }
+
+private:
+    void                readValue(MtpDataPacket& packet, MtpPropertyValue& value);
+    void                writeValue(MtpDataPacket& packet, MtpPropertyValue& value);
+    MtpPropertyValue*   readArrayValues(MtpDataPacket& packet, int& length);
+    void                writeArrayValues(MtpDataPacket& packet,
+                                            MtpPropertyValue* values, int length);
+};
+
+
+#endif // _MTP_PROPERTY_H
diff --git a/mtp/MtpRequestPacket.cpp b/mtp/MtpRequestPacket.cpp
new file mode 100755
index 0000000..754e205
--- /dev/null
+++ b/mtp/MtpRequestPacket.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "MtpRequestPacket.h"
+#include "MtpDebug.h"
+
+#include <usbhost/usbhost.h>
+
+
+MtpRequestPacket::MtpRequestPacket()
+	:	MtpPacket(512)
+{
+}
+
+MtpRequestPacket::~MtpRequestPacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpRequestPacket::read(int fd) {
+	MTPD("block1 fd: %d\n", fd);
+	int ret = ::read(fd, mBuffer, mBufferSize);
+	MTPD("block2\n");
+	if (ret >= 0)
+		mPacketSize = ret;
+	else
+		mPacketSize = 0;
+	return ret;
+}
+#endif
+
+#ifdef MTP_HOST
+	// write our buffer to the given endpoint (host mode)
+int MtpRequestPacket::write(struct usb_request *request)
+{
+	putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+	putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_COMMAND);
+	request->buffer = mBuffer;
+	request->buffer_length = mPacketSize;
+	return transfer(request);
+}
+#endif
+
diff --git a/mtp/MtpRequestPacket.h b/mtp/MtpRequestPacket.h
new file mode 100755
index 0000000..8551dde
--- /dev/null
+++ b/mtp/MtpRequestPacket.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_REQUEST_PACKET_H
+#define _MTP_REQUEST_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+struct usb_request;
+
+
+class MtpRequestPacket : public MtpPacket {
+
+public:
+                        MtpRequestPacket();
+    virtual             ~MtpRequestPacket();
+#ifdef MTP_DEVICE
+    // fill our buffer with data from the given file descriptor
+    int                 read(int fd);
+#endif
+
+#ifdef MTP_HOST
+    // write our buffer to the given endpoint
+    int                 write(struct usb_request *request);
+#endif
+
+    inline MtpOperationCode    getOperationCode() const { return getContainerCode(); }
+    inline void                setOperationCode(MtpOperationCode code)
+                                                    { return setContainerCode(code); }
+};
+
+
+#endif // _MTP_REQUEST_PACKET_H
diff --git a/mtp/MtpResponsePacket.cpp b/mtp/MtpResponsePacket.cpp
new file mode 100755
index 0000000..8eed13a
--- /dev/null
+++ b/mtp/MtpResponsePacket.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use 	 file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "MtpResponsePacket.h"
+
+#include <usbhost/usbhost.h>
+
+
+MtpResponsePacket::MtpResponsePacket()
+	:   MtpPacket(512)
+{
+}
+
+MtpResponsePacket::~MtpResponsePacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpResponsePacket::write(int fd) {
+	putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+	putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_RESPONSE);
+	int ret = ::write(fd, mBuffer, mPacketSize);
+	return (ret < 0 ? ret : 0);
+}
+#endif
+
+#ifdef MTP_HOST
+int MtpResponsePacket::read(struct usb_request *request) {
+	request->buffer = mBuffer;
+	request->buffer_length = mBufferSize;
+	int ret = transfer(request);
+	 if (ret >= 0)
+		mPacketSize = ret;
+	else
+		mPacketSize = 0;
+	return ret;
+}
+#endif
+
+
diff --git a/mtp/MtpResponsePacket.h b/mtp/MtpResponsePacket.h
new file mode 100755
index 0000000..749b534
--- /dev/null
+++ b/mtp/MtpResponsePacket.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_RESPONSE_PACKET_H
+#define _MTP_RESPONSE_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+
+class MtpResponsePacket : public MtpPacket {
+
+public:
+                        MtpResponsePacket();
+    virtual             ~MtpResponsePacket();
+
+#ifdef MTP_DEVICE
+    // write our data to the given file descriptor
+    int                 write(int fd);
+#endif
+
+#ifdef MTP_HOST
+    // read our buffer with the given request
+    int                 read(struct usb_request *request);
+#endif
+
+    inline MtpResponseCode      getResponseCode() const { return getContainerCode(); }
+    inline void                 setResponseCode(MtpResponseCode code)
+                                                     { return setContainerCode(code); }
+};
+
+
+#endif // _MTP_RESPONSE_PACKET_H
diff --git a/mtp/MtpServer.cpp b/mtp/MtpServer.cpp
new file mode 100755
index 0000000..c044cad
--- /dev/null
+++ b/mtp/MtpServer.cpp
@@ -0,0 +1,1342 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include "../twcommon.h"
+#include <cutils/properties.h>
+
+#include "MtpTypes.h"
+#include "MtpDebug.h"
+#include "MtpDatabase.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpStringBuffer.h"
+
+#include <linux/usb/f_mtp.h>
+
+static const MtpOperationCode kSupportedOperationCodes[] = {
+	MTP_OPERATION_GET_DEVICE_INFO,
+	MTP_OPERATION_OPEN_SESSION,
+	MTP_OPERATION_CLOSE_SESSION,
+	MTP_OPERATION_GET_STORAGE_IDS,
+	MTP_OPERATION_GET_STORAGE_INFO,
+	MTP_OPERATION_GET_NUM_OBJECTS,
+	MTP_OPERATION_GET_OBJECT_HANDLES,
+	MTP_OPERATION_GET_OBJECT_INFO,
+	MTP_OPERATION_GET_OBJECT,
+	MTP_OPERATION_GET_THUMB,
+	MTP_OPERATION_DELETE_OBJECT,
+	MTP_OPERATION_SEND_OBJECT_INFO,
+	MTP_OPERATION_SEND_OBJECT,
+//	MTP_OPERATION_INITIATE_CAPTURE,
+//	MTP_OPERATION_FORMAT_STORE,
+//	MTP_OPERATION_RESET_DEVICE,
+//	MTP_OPERATION_SELF_TEST,
+//	MTP_OPERATION_SET_OBJECT_PROTECTION,
+//	MTP_OPERATION_POWER_DOWN,
+	MTP_OPERATION_GET_DEVICE_PROP_DESC,
+	MTP_OPERATION_GET_DEVICE_PROP_VALUE,
+	MTP_OPERATION_SET_DEVICE_PROP_VALUE,
+	MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
+//	MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
+//	MTP_OPERATION_MOVE_OBJECT,
+//	MTP_OPERATION_COPY_OBJECT,
+	MTP_OPERATION_GET_PARTIAL_OBJECT,
+//	MTP_OPERATION_INITIATE_OPEN_CAPTURE,
+	MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
+	MTP_OPERATION_GET_OBJECT_PROP_DESC,
+	MTP_OPERATION_GET_OBJECT_PROP_VALUE,
+	MTP_OPERATION_SET_OBJECT_PROP_VALUE,
+	MTP_OPERATION_GET_OBJECT_PROP_LIST,
+//	MTP_OPERATION_SET_OBJECT_PROP_LIST,
+//	MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
+//	MTP_OPERATION_SEND_OBJECT_PROP_LIST,
+	MTP_OPERATION_GET_OBJECT_REFERENCES,
+	MTP_OPERATION_SET_OBJECT_REFERENCES,
+//	MTP_OPERATION_SKIP,
+	// Android extension for direct file IO
+	MTP_OPERATION_GET_PARTIAL_OBJECT_64,
+	MTP_OPERATION_SEND_PARTIAL_OBJECT,
+	MTP_OPERATION_TRUNCATE_OBJECT,
+	MTP_OPERATION_BEGIN_EDIT_OBJECT,
+	MTP_OPERATION_END_EDIT_OBJECT,
+};
+
+static const MtpEventCode kSupportedEventCodes[] = {
+	MTP_EVENT_OBJECT_ADDED,
+	MTP_EVENT_OBJECT_REMOVED,
+	MTP_EVENT_STORE_ADDED,
+	MTP_EVENT_STORE_REMOVED,
+	MTP_EVENT_OBJECT_PROP_CHANGED,
+};
+
+MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp,
+					int fileGroup, int filePerm, int directoryPerm)
+	:	mFD(fd),
+		mDatabase(database),
+		mPtp(ptp),
+		mFileGroup(fileGroup),
+		mFilePermission(filePerm),
+		mDirectoryPermission(directoryPerm),
+		mSessionID(0),
+		mSessionOpen(false),
+		mSendObjectHandle(kInvalidObjectHandle),
+		mSendObjectFormat(0),
+		mSendObjectFileSize(0)
+{
+}
+
+MtpServer::~MtpServer() {
+}
+
+void MtpServer::addStorage(MtpStorage* storage) {
+	MTPD("addStorage(): storage: %x\n", storage);
+	mDatabase->createDB(storage, storage->getStorageID());
+	android::Mutex::Autolock autoLock(mMutex);
+	mStorages.push(storage);
+	sendStoreAdded(storage->getStorageID());
+}
+
+void MtpServer::removeStorage(MtpStorage* storage) {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	for (size_t i = 0; i < mStorages.size(); i++) {
+		if (mStorages[i] == storage) {
+			mStorages.removeAt(i);
+			sendStoreRemoved(storage->getStorageID());
+			break;
+		}
+	}
+}
+
+MtpStorage* MtpServer::getStorage(MtpStorageID id) {
+	MTPD("getStorage\n");
+	if (id == 0) {
+		MTPD("mStorages\n");
+		return mStorages[0];
+	}
+	for (size_t i = 0; i < mStorages.size(); i++) {
+		MtpStorage* storage = mStorages[i];
+		MTPD("id: %d\n", id);
+		MTPD("storage: %d\n", storage->getStorageID());
+		if (storage->getStorageID() == id) {
+			return storage;
+		}
+	}
+	return NULL;
+}
+
+bool MtpServer::hasStorage(MtpStorageID id) {
+	MTPD("in hasStorage\n");
+	if (id == 0 || id == 0xFFFFFFFF)
+		return mStorages.size() > 0;
+	return (getStorage(id) != NULL);
+}
+
+void MtpServer::run() {
+	int fd = mFD;
+
+	MTPI("MtpServer::run fd: %d\n", fd);
+
+	while (1) {
+		MTPD("About to read device...\n");
+		int ret = mRequest.read(fd);
+		if (ret < 0) {
+			MTPD("request read returned %d, errno: %d", ret, errno);
+			if (errno == ECANCELED) {
+				// return to top of loop and wait for next command
+				continue;
+			}
+			break;
+		}
+		MtpOperationCode operation = mRequest.getOperationCode();
+		MtpTransactionID transaction = mRequest.getTransactionID();
+
+		MTPD("operation: %s", MtpDebug::getOperationCodeName(operation));
+		mRequest.dump();
+
+		// FIXME need to generalize this
+		bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
+					|| operation == MTP_OPERATION_SET_OBJECT_REFERENCES
+					|| operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
+					|| operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
+		if (dataIn) {
+			int ret = mData.read(fd);
+			if (ret < 0) {
+				MTPD("data read returned %d, errno: %d", ret, errno);
+				if (errno == ECANCELED) {
+					// return to top of loop and wait for next command
+					continue;
+				}
+				break;
+			}
+			MTPD("received data:");
+			mData.dump();
+		} else {
+			mData.reset();
+		}
+
+		if (handleRequest()) {
+			if (!dataIn && mData.hasData()) {
+				mData.setOperationCode(operation);
+				mData.setTransactionID(transaction);
+				MTPD("sending data:");
+				mData.dump();
+				ret = mData.write(fd);
+				if (ret < 0) {
+					MTPD("request write returned %d, errno: %d", ret, errno);
+					if (errno == ECANCELED) {
+						// return to top of loop and wait for next command
+						continue;
+					}
+					break;
+				}
+			}
+
+			mResponse.setTransactionID(transaction);
+			MTPD("sending response %04X\n", mResponse.getResponseCode());
+			ret = mResponse.write(fd);
+			MTPD("ret: %d\n", ret);
+			mResponse.dump();
+			if (ret < 0) {
+				MTPD("request write returned %d, errno: %d", ret, errno);
+				if (errno == ECANCELED) {
+					// return to top of loop and wait for next command
+					continue;
+				}
+				break;
+			}
+		} else {
+			MTPD("skipping response\n");
+		}
+	}
+
+	// commit any open edits
+	int count = mObjectEditList.size();
+	for (int i = 0; i < count; i++) {
+		ObjectEdit* edit = mObjectEditList[i];
+		commitEdit(edit);
+		delete edit;
+	}
+	mObjectEditList.clear();
+
+	if (mSessionOpen)
+		mDatabase->sessionEnded();
+	close(fd);
+	mFD = -1;
+}
+
+void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
+	MTPD("sendObjectAdded %d\n", handle);
+	sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
+}
+
+void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
+	MTPD("sendObjectRemoved %d\n", handle);
+	sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
+}
+
+void MtpServer::sendObjectUpdated(MtpObjectHandle handle) {
+	MTPD("sendObjectUpdated %d\n", handle);
+	sendEvent(MTP_EVENT_OBJECT_PROP_CHANGED, handle);
+}
+
+void MtpServer::sendStoreAdded(MtpStorageID id) {
+	MTPD("sendStoreAdded %08X\n", id);
+	sendEvent(MTP_EVENT_STORE_ADDED, id);
+}
+
+void MtpServer::sendStoreRemoved(MtpStorageID id) {
+	MTPD("sendStoreRemoved %08X\n", id);
+	sendEvent(MTP_EVENT_STORE_REMOVED, id);
+}
+
+void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
+	MTPD("MtpServer::sendEvent sending event code: %x\n", code);
+	if (mSessionOpen) {
+		mEvent.setEventCode(code);
+		mEvent.setTransactionID(mRequest.getTransactionID());
+		mEvent.setParameter(1, param1);
+		int ret = mEvent.write(mFD);
+		MTPD("mEvent.write returned %d\n", ret);
+	}
+}
+
+void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
+		uint64_t size, MtpObjectFormat format, int fd) {
+	ObjectEdit*  edit = new ObjectEdit(handle, path, size, format, fd);
+	mObjectEditList.add(edit);
+}
+
+MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
+	int count = mObjectEditList.size();
+	for (int i = 0; i < count; i++) {
+		ObjectEdit* edit = mObjectEditList[i];
+		if (edit->mHandle == handle) return edit;
+	}
+	return NULL;
+}
+
+void MtpServer::removeEditObject(MtpObjectHandle handle) {
+	int count = mObjectEditList.size();
+	for (int i = 0; i < count; i++) {
+		ObjectEdit* edit = mObjectEditList[i];
+		if (edit->mHandle == handle) {
+			delete edit;
+			mObjectEditList.removeAt(i);
+			return;
+		}
+	}
+	MTPE("ObjectEdit not found in removeEditObject");
+}
+
+void MtpServer::commitEdit(ObjectEdit* edit) {
+	mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
+}
+
+
+bool MtpServer::handleRequest() {
+	android::Mutex::Autolock autoLock(mMutex);
+
+	MtpOperationCode operation = mRequest.getOperationCode();
+	MtpResponseCode response;
+
+	mResponse.reset();
+
+	if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
+		// FIXME - need to delete mSendObjectHandle from the database
+		MTPE("expected SendObject after SendObjectInfo");
+		mSendObjectHandle = kInvalidObjectHandle;
+	}
+
+	switch (operation) {
+		case MTP_OPERATION_GET_DEVICE_INFO:
+				MTPD("doGetDeviceInfo()\n");
+				response = doGetDeviceInfo();
+				break;
+			case MTP_OPERATION_OPEN_SESSION:
+				MTPD("doOpenSesion()\n");
+				response = doOpenSession();
+				break;
+			case MTP_OPERATION_CLOSE_SESSION:
+				MTPD("doCloseSession()\n");
+				response = doCloseSession();
+				break;
+			case MTP_OPERATION_GET_STORAGE_IDS:
+				MTPD("doGetStorageIDs()\n");
+				response = doGetStorageIDs();
+				break;
+		 	 case MTP_OPERATION_GET_STORAGE_INFO:
+				MTPD("about to call doGetStorageInfo()\n");
+				response = doGetStorageInfo();
+				break;
+			case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
+				MTPD("about to call doGetObjectPropsSupported()\n");
+				response = doGetObjectPropsSupported();
+				break;
+			case MTP_OPERATION_GET_OBJECT_HANDLES:
+				MTPD("about to call doGetObjectHandles()\n");
+				response = doGetObjectHandles();
+				break;
+			case MTP_OPERATION_GET_NUM_OBJECTS:
+				MTPD("about to call doGetNumbObjects()\n");
+				response = doGetNumObjects();
+				break;
+			case MTP_OPERATION_GET_OBJECT_REFERENCES:
+				MTPD("about to call doGetObjectReferences()\n");
+				response = doGetObjectReferences();
+				break;
+			case MTP_OPERATION_SET_OBJECT_REFERENCES:
+				MTPD("about to call doSetObjectReferences()\n");
+				response = doSetObjectReferences();
+				break;
+			case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
+				MTPD("about to call doGetObjectPropValue()\n");
+				response = doGetObjectPropValue();
+				break;
+			case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
+				MTPD("about to call doSetObjectPropValue()\n");
+				response = doSetObjectPropValue();
+				break;
+			case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
+				MTPD("about to call doGetDevicPropValue()\n");
+				response = doGetDevicePropValue();
+				break;
+			case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
+				MTPD("about to call doSetDevicePropVaue()\n");
+				response = doSetDevicePropValue();
+				break;
+			case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
+				MTPD("about to call doResetDevicePropValue()\n");
+				response = doResetDevicePropValue();
+				break;
+			case MTP_OPERATION_GET_OBJECT_PROP_LIST:
+				MTPD("calling doGetObjectPropList()\n");
+				response = doGetObjectPropList();
+				break;
+			case MTP_OPERATION_GET_OBJECT_INFO:
+				MTPD("calling doGetObjectInfo()\n");
+				response = doGetObjectInfo();
+				break;
+			case MTP_OPERATION_GET_OBJECT:
+				MTPD("about to call doGetObject()\n");
+				response = doGetObject();
+				break;
+			case MTP_OPERATION_GET_THUMB:
+				response = doGetThumb();
+				break;
+			case MTP_OPERATION_GET_PARTIAL_OBJECT:
+			case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
+				response = doGetPartialObject(operation);
+				break;
+			case MTP_OPERATION_SEND_OBJECT_INFO:
+				MTPD("about to call doSendObjectInfo()\n");
+				response = doSendObjectInfo();
+				break;
+			case MTP_OPERATION_SEND_OBJECT:
+				MTPD("about to call doSendObject()\n");
+				response = doSendObject();
+				break;
+			case MTP_OPERATION_DELETE_OBJECT:
+				response = doDeleteObject();
+				break;
+			case MTP_OPERATION_GET_OBJECT_PROP_DESC:
+				MTPD("about to call doGetObjectPropDesc()\n");
+				response = doGetObjectPropDesc();
+				break;
+			case MTP_OPERATION_GET_DEVICE_PROP_DESC:
+				MTPD("about to call doGetDevicePropDesc()\n");
+				response = doGetDevicePropDesc();
+				break;
+			case MTP_OPERATION_SEND_PARTIAL_OBJECT:
+				response = doSendPartialObject();
+				break;
+			case MTP_OPERATION_TRUNCATE_OBJECT:
+				response = doTruncateObject();
+				break;
+			case MTP_OPERATION_BEGIN_EDIT_OBJECT:
+				response = doBeginEditObject();
+				break;
+			case MTP_OPERATION_END_EDIT_OBJECT:
+				response = doEndEditObject();
+				break;
+			default:
+				MTPE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
+				response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
+				break;
+		}
+
+		if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
+			return false;
+		mResponse.setResponseCode(response);
+		return true;
+}
+
+MtpResponseCode MtpServer::doGetDeviceInfo() {
+	MtpStringBuffer   string;
+	char prop_value[PROPERTY_VALUE_MAX];
+
+	MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
+	MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
+	MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
+
+	// fill in device info
+	mData.putUInt16(MTP_STANDARD_VERSION);
+	if (mPtp) {
+		MTPD("doGetDeviceInfo putting 0\n");
+		mData.putUInt32(0);
+	} else {
+		// MTP Vendor Extension ID
+		MTPD("doGetDeviceInfo putting 6\n");
+		mData.putUInt32(6);
+	}
+	mData.putUInt16(MTP_STANDARD_VERSION);
+	if (mPtp) {
+		// no extensions
+		MTPD("doGetDeviceInfo no extensions\n");
+		string.set("");
+	} else {
+		// MTP extensions
+		MTPD("doGetDeviceInfo microsoft.com: 1.0; android.com: 1.0;\n");
+		string.set("microsoft.com: 1.0; android.com: 1.0;");
+	}
+	mData.putString(string); // MTP Extensions
+	mData.putUInt16(0); //Functional Mode
+	MTPD("doGetDeviceInfo opcodes, %i\n", sizeof(kSupportedOperationCodes) / sizeof(uint16_t));
+	MTPD("doGetDeviceInfo eventcodes, %i\n", sizeof(kSupportedEventCodes) / sizeof(uint16_t));
+	mData.putAUInt16(kSupportedOperationCodes,
+			sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
+	mData.putAUInt16(kSupportedEventCodes,
+			sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
+	mData.putAUInt16(deviceProperties); // Device Properties Supported
+	mData.putAUInt16(captureFormats); // Capture Formats
+	mData.putAUInt16(playbackFormats);  // Playback Formats
+
+	property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
+	MTPD("prop: %s\n", prop_value);
+	string.set(prop_value);
+	mData.putString(string);   // Manufacturer
+
+	property_get("ro.product.model", prop_value, "MTP Device");
+	string.set(prop_value);
+	mData.putString(string);   // Model
+	string.set("1.0");
+	mData.putString(string);   // Device Version
+
+	property_get("ro.serialno", prop_value, "????????");
+	MTPD("sn: %s\n", prop_value);
+	string.set(prop_value);
+	mData.putString(string);   // Serial Number
+
+	delete playbackFormats;
+	delete captureFormats;
+	delete deviceProperties;
+
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doOpenSession() {
+	if (mSessionOpen) {
+		mResponse.setParameter(1, mSessionID);
+		return MTP_RESPONSE_SESSION_ALREADY_OPEN;
+	}
+	mSessionID = mRequest.getParameter(1);
+	mSessionOpen = true;
+
+	mDatabase->sessionStarted();
+
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doCloseSession() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	mSessionID = 0;
+	mSessionOpen = false;
+	mDatabase->sessionEnded();
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetStorageIDs() {
+	MTPD("doGetStorageIDs()\n");
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	int count = mStorages.size();
+	mData.putUInt32(count);
+	for (int i = 0; i < count; i++) {
+		MTPD("getting storageid %d\n", mStorages[i]->getStorageID());
+		mData.putUInt32(mStorages[i]->getStorageID());
+	}
+
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetStorageInfo() {
+	MtpStringBuffer   string;
+	MTPD("doGetStorageInfo()\n");
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	MtpStorageID id = mRequest.getParameter(1);
+	MtpStorage* storage = getStorage(id);
+	if (!storage) {
+		MTPE("invalid storage id\n");
+		return MTP_RESPONSE_INVALID_STORAGE_ID;
+	}
+
+	mData.putUInt16(storage->getType());
+	mData.putUInt16(storage->getFileSystemType());
+	mData.putUInt16(storage->getAccessCapability());
+	mData.putUInt64(storage->getMaxCapacity());
+	mData.putUInt64(storage->getFreeSpace());
+	mData.putUInt32(1024*1024*1024); // Free Space in Objects
+	string.set(storage->getDescription());
+	mData.putString(string);
+	mData.putEmptyString();   // Volume Identifier
+
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropsSupported() {
+	MTPD("doGetObjectPropsSupported()\n");
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	MtpObjectFormat format = mRequest.getParameter(1);
+	mDatabase->lockMutex();
+	MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
+	mData.putAUInt16(properties);
+	delete properties;
+	mDatabase->unlockMutex();
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetObjectHandles() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	MtpStorageID storageID = mRequest.getParameter(1);	  // 0xFFFFFFFF for all storage
+	MtpObjectFormat format = mRequest.getParameter(2);	  // 0 for all formats
+	MtpObjectHandle parent = mRequest.getParameter(3);	  // 0xFFFFFFFF for objects with no parent
+															// 0x00000000 for all objects
+
+	if (!hasStorage(storageID))
+		return MTP_RESPONSE_INVALID_STORAGE_ID;
+
+	MTPD("calling MtpDatabase->getObjectList()\n");
+	mDatabase->lockMutex();
+	MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
+	mData.putAUInt32(handles);
+	delete handles;
+	mDatabase->unlockMutex();
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetNumObjects() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	MtpStorageID storageID = mRequest.getParameter(1);	  // 0xFFFFFFFF for all storage
+	MtpObjectFormat format = mRequest.getParameter(2);	  // 0 for all formats
+	MtpObjectHandle parent = mRequest.getParameter(3);	  // 0xFFFFFFFF for objects with no parent
+															// 0x00000000 for all objects
+	if (!hasStorage(storageID))
+		return MTP_RESPONSE_INVALID_STORAGE_ID;
+
+	mDatabase->lockMutex();
+	int count = mDatabase->getNumObjects(storageID, format, parent);
+	mDatabase->unlockMutex();
+	if (count >= 0) {
+		mResponse.setParameter(1, count);
+		return MTP_RESPONSE_OK;
+	} else {
+		mResponse.setParameter(1, 0);
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	}
+}
+
+MtpResponseCode MtpServer::doGetObjectReferences() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+
+	// FIXME - check for invalid object handle
+	mDatabase->lockMutex();
+	MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
+	if (handles) {
+		mData.putAUInt32(handles);
+		delete handles;
+	} else {
+		MTPD("MtpServer::doGetObjectReferences putEmptyArray\n");
+		mData.putEmptyArray();
+	}
+	mDatabase->unlockMutex();
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSetObjectReferences() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpStorageID handle = mRequest.getParameter(1);
+
+	MtpObjectHandleList* references = mData.getAUInt32();
+	mDatabase->lockMutex();
+	MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
+	mDatabase->unlockMutex();
+	delete references;
+	return result;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropValue() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpObjectProperty property = mRequest.getParameter(2);
+	MTPD("GetObjectPropValue %d %s\n", handle,
+			MtpDebug::getObjectPropCodeName(property));
+
+	mDatabase->lockMutex();
+	MtpResponseCode res = mDatabase->getObjectPropertyValue(handle, property, mData);
+	mDatabase->unlockMutex();
+	return res;
+}
+
+MtpResponseCode MtpServer::doSetObjectPropValue() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpObjectProperty property = mRequest.getParameter(2);
+	MTPD("SetObjectPropValue %d %s\n", handle,
+			MtpDebug::getObjectPropCodeName(property));
+
+	mDatabase->lockMutex();
+	MtpResponseCode res = mDatabase->setObjectPropertyValue(handle, property, mData);
+	mDatabase->unlockMutex();
+	return res;
+}
+
+MtpResponseCode MtpServer::doGetDevicePropValue() {
+	MtpDeviceProperty property = mRequest.getParameter(1);
+	MTPD("GetDevicePropValue %s\n",
+			MtpDebug::getDevicePropCodeName(property));
+
+	mDatabase->lockMutex();
+	MtpResponseCode res = mDatabase->getDevicePropertyValue(property, mData);
+	mDatabase->unlockMutex();
+	return res;
+}
+
+MtpResponseCode MtpServer::doSetDevicePropValue() {
+	MtpDeviceProperty property = mRequest.getParameter(1);
+	MTPD("SetDevicePropValue %s\n",
+			MtpDebug::getDevicePropCodeName(property));
+
+	mDatabase->lockMutex();
+	MtpResponseCode res = mDatabase->setDevicePropertyValue(property, mData);
+	mDatabase->unlockMutex();
+	return res;
+}
+
+MtpResponseCode MtpServer::doResetDevicePropValue() {
+	MtpDeviceProperty property = mRequest.getParameter(1);
+	MTPD("ResetDevicePropValue %s\n",
+			MtpDebug::getDevicePropCodeName(property));
+
+	mDatabase->lockMutex();
+	MtpResponseCode res = mDatabase->resetDeviceProperty(property);
+	mDatabase->unlockMutex();
+	return res;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropList() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	// use uint32_t so we can support 0xFFFFFFFF
+	uint32_t format = mRequest.getParameter(2);
+	uint32_t property = mRequest.getParameter(3);
+	int groupCode = mRequest.getParameter(4);
+	int depth = mRequest.getParameter(5);
+	MTPD("GetObjectPropList %d format: %s property: %x group: %d depth: %d\n",
+			handle, MtpDebug::getFormatCodeName(format),
+			property, groupCode, depth);
+
+	mDatabase->lockMutex();
+	MtpResponseCode res = mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
+	mDatabase->unlockMutex();
+	return res;
+}
+
+MtpResponseCode MtpServer::doGetObjectInfo() {
+	MTPD("inside doGetObjectInfo()\n");
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpObjectInfo info(handle);
+	MTPD("calling mtpdatabase getObjectInfo()\n");
+	mDatabase->lockMutex();
+	MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
+	mDatabase->unlockMutex();
+	if (result == MTP_RESPONSE_OK) {
+		char	date[20];
+
+		mData.putUInt32(info.mStorageID);
+		mData.putUInt16(info.mFormat);
+		mData.putUInt16(info.mProtectionStatus);
+
+		// if object is being edited the database size may be out of date
+		uint32_t size = info.mCompressedSize;
+		ObjectEdit* edit = getEditObject(handle);
+		if (edit)
+			size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
+		mData.putUInt32(size);
+
+		mData.putUInt16(info.mThumbFormat);
+		mData.putUInt32(info.mThumbCompressedSize);
+		mData.putUInt32(info.mThumbPixWidth);
+		mData.putUInt32(info.mThumbPixHeight);
+		mData.putUInt32(info.mImagePixWidth);
+		mData.putUInt32(info.mImagePixHeight);
+		mData.putUInt32(info.mImagePixDepth);
+		mData.putUInt32(info.mParent);
+		mData.putUInt16(info.mAssociationType);
+		mData.putUInt32(info.mAssociationDesc);
+		mData.putUInt32(info.mSequenceNumber);
+		MTPD("info.mName: %s\n", info.mName);
+		mData.putString(info.mName);
+		mData.putEmptyString();	// date created
+		formatDateTime(info.mDateModified, date, sizeof(date));
+		mData.putString(date);   // date modified
+		mData.putEmptyString();   // keywords
+	}
+	return result;
+}
+
+MtpResponseCode MtpServer::doGetObject() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpString pathBuf;
+	int64_t fileLength;
+	MtpObjectFormat format;
+	MTPD("MtpServer::doGetObject calling getObjectFilePath\n");
+	mDatabase->lockMutex();
+	int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
+	mDatabase->unlockMutex();
+	if (result != MTP_RESPONSE_OK)
+		return result;
+
+	const char* filePath = (const char *)pathBuf;
+	MTPD("filePath: %s\n", filePath);
+	mtp_file_range  mfr;
+	mfr.fd = open(filePath, O_RDONLY);
+	if (mfr.fd < 0) {
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+	mfr.offset = 0;
+	mfr.length = fileLength;
+	MTPD("mfr.length: %lld\n", mfr.length);
+	mfr.command = mRequest.getOperationCode();
+	mfr.transaction_id = mRequest.getTransactionID();
+
+	// then transfer the file
+	int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
+	MTPD("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
+	close(mfr.fd);
+	if (ret < 0) {
+		if (errno == ECANCELED)
+			return MTP_RESPONSE_TRANSACTION_CANCELLED;
+		else
+			return MTP_RESPONSE_GENERAL_ERROR;
+	}
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetThumb() {
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	size_t thumbSize;
+	mDatabase->lockMutex();
+	void* thumb = mDatabase->getThumbnail(handle, thumbSize);
+	mDatabase->unlockMutex();
+	if (thumb) {
+		// send data
+		mData.setOperationCode(mRequest.getOperationCode());
+		mData.setTransactionID(mRequest.getTransactionID());
+		mData.writeData(mFD, thumb, thumbSize);
+		free(thumb);
+		return MTP_RESPONSE_OK;
+	} else {
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+}
+
+MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	uint64_t offset;
+	uint32_t length;
+	offset = mRequest.getParameter(2);
+	if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
+		// android extension with 64 bit offset
+		uint64_t offset2 = mRequest.getParameter(3);
+		offset = offset | (offset2 << 32);
+		length = mRequest.getParameter(4);
+	} else {
+		// standard GetPartialObject
+		length = mRequest.getParameter(3);
+	}
+	MtpString pathBuf;
+	int64_t fileLength;
+	MtpObjectFormat format;
+	MTPD("MtpServer::doGetPartialObject calling getObjectFilePath\n");
+	mDatabase->lockMutex();
+	int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
+	mDatabase->unlockMutex();
+	if (result != MTP_RESPONSE_OK) {
+		return result;
+	}
+	if (offset + length > (uint64_t)fileLength)
+		length = fileLength - offset;
+
+	const char* filePath = (const char *)pathBuf;
+	mtp_file_range  mfr;
+	mfr.fd = open(filePath, O_RDONLY);
+	if (mfr.fd < 0) {
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+	mfr.offset = offset;
+	mfr.length = length;
+	mfr.command = mRequest.getOperationCode();
+	mfr.transaction_id = mRequest.getTransactionID();
+	mResponse.setParameter(1, length);
+
+	// transfer the file
+	int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
+	MTPD("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
+	close(mfr.fd);
+	if (ret < 0) {
+		if (errno == ECANCELED)
+			return MTP_RESPONSE_TRANSACTION_CANCELLED;
+		else
+			return MTP_RESPONSE_GENERAL_ERROR;
+	}
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSendObjectInfo() {
+	MTPD("MtpServer::doSendObjectInfo starting\n");
+	MtpString path;
+	MtpStorageID storageID = mRequest.getParameter(1);
+	MtpStorage* storage = getStorage(storageID);
+	MtpObjectHandle parent = mRequest.getParameter(2);
+	if (!storage)
+		return MTP_RESPONSE_INVALID_STORAGE_ID;
+
+	// special case the root
+	if (parent == MTP_PARENT_ROOT) {
+		MTPD("MtpServer::doSendObjectInfo special case root\n");
+		path = storage->getPath();
+		parent = 0;
+	} else {
+		int64_t length;
+		MtpObjectFormat format;
+		MTPD("MtpServer::doSendObjectInfo calling getObjectFilePath\n");
+		mDatabase->lockMutex();
+		int result = mDatabase->getObjectFilePath(parent, path, length, format);
+		mDatabase->unlockMutex();
+		if (result != MTP_RESPONSE_OK) {
+			return result;
+		}
+		if (format != MTP_FORMAT_ASSOCIATION)
+			return MTP_RESPONSE_INVALID_PARENT_OBJECT;
+	}
+
+	// read only the fields we need
+	mData.getUInt32();  // storage ID
+	MtpObjectFormat format = mData.getUInt16();
+	mData.getUInt16();  // protection status
+	mSendObjectFileSize = mData.getUInt32();
+	mData.getUInt16();  // thumb format
+	mData.getUInt32();  // thumb compressed size
+	mData.getUInt32();  // thumb pix width
+	mData.getUInt32();  // thumb pix height
+	mData.getUInt32();  // image pix width
+	mData.getUInt32();  // image pix height
+	mData.getUInt32();  // image bit depth
+	mData.getUInt32();  // parent
+	uint16_t associationType = mData.getUInt16();
+	uint32_t associationDesc = mData.getUInt32();   // association desc
+	mData.getUInt32();  // sequence number
+	MtpStringBuffer name, created, modified;
+	mData.getString(name);	// file name
+	mData.getString(created);	  // date created
+	mData.getString(modified);	 // date modified
+	// keywords follow
+
+	MTPD("name: %s format: %04X\n", (const char *)name, format);
+	time_t modifiedTime;
+	if (!parseDateTime(modified, modifiedTime)) {
+		modifiedTime = 0;
+	}
+	if (path[path.size() - 1] != '/') {
+		path += "/";
+	}
+	path += (const char *)name;
+
+	// check space first
+	if (mSendObjectFileSize > storage->getFreeSpace())
+		return MTP_RESPONSE_STORAGE_FULL;
+	uint64_t maxFileSize = storage->getMaxFileSize();
+	// check storage max file size
+	if (maxFileSize != 0) {
+		// if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
+		// is >= 0xFFFFFFFF
+		if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
+			return MTP_RESPONSE_OBJECT_TOO_LARGE;
+	}
+
+	MTPD("MtpServer::doSendObjectInfo path: %s parent: %d storageID: %08X\n", (const char*)path, parent, storageID);
+	mDatabase->lockMutex();
+	MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
+			format, parent, storageID, mSendObjectFileSize, modifiedTime);
+	mDatabase->unlockMutex();
+	if (handle == kInvalidObjectHandle) {
+		MTPE("MtpServer::doSendObjectInfo returning MTP_RESPONSE_GENERAL_ERROR, handle == kInvalidObjectHandle\n");
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+  if (format == MTP_FORMAT_ASSOCIATION) {
+		mode_t mask = umask(0);
+		MTPD("MtpServer::doSendObjectInfo mkdir '%s'\n", (const char *)path);
+		int ret = mkdir((const char *)path, mDirectoryPermission);
+		umask(mask);
+		if (ret && ret != -EEXIST) {
+			MTPE("MtpServer::doSendObjectInfo returning MTP_RESPONSE_GENERAL_ERROR, ret && ret != -EEXIST\n");
+			return MTP_RESPONSE_GENERAL_ERROR;
+		}
+		chown((const char *)path, getuid(), mFileGroup);
+
+		// SendObject does not get sent for directories, so call endSendObject here instead
+		mDatabase->lockMutex();
+		mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
+		mDatabase->unlockMutex();
+	} else {
+		mSendObjectFilePath = path;
+		// save the handle for the SendObject call, which should follow
+		mSendObjectHandle = handle;
+		mSendObjectFormat = format;
+	}
+
+	mResponse.setParameter(1, storageID);
+	mResponse.setParameter(2, parent);
+	mResponse.setParameter(3, handle);
+	MTPD("MtpServer::doSendObjectInfo returning MTP_RESPONSE_OK\n");
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSendObject() {
+	if (!hasStorage())
+		return MTP_RESPONSE_GENERAL_ERROR;
+	MtpResponseCode result = MTP_RESPONSE_OK;
+	mode_t mask;
+	int ret = 0, initialData;
+
+	if (mSendObjectHandle == kInvalidObjectHandle) {
+		MTPE("Expected SendObjectInfo before SendObject");
+		result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
+		goto done;
+	}
+
+	// read the header, and possibly some data
+	ret = mData.read(mFD);
+	if (ret < MTP_CONTAINER_HEADER_SIZE) {
+		MTPE("MTP_RESPONSE_GENERAL_ERROR\n");
+		result = MTP_RESPONSE_GENERAL_ERROR;
+		goto done;
+	}
+	initialData = ret - MTP_CONTAINER_HEADER_SIZE;
+
+	mtp_file_range  mfr;
+	mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, 0640);
+	if (mfr.fd < 0) {
+		result = MTP_RESPONSE_GENERAL_ERROR;
+		MTPE("fd error\n");
+		goto done;
+	}
+	fchown(mfr.fd, getuid(), mFileGroup);
+	// set permissions
+	mask = umask(0);
+	fchmod(mfr.fd, mFilePermission);
+	umask(mask);
+
+	if (initialData > 0)
+		ret = write(mfr.fd, mData.getData(), initialData);
+
+	if (mSendObjectFileSize - initialData > 0) {
+		mfr.offset = initialData;
+		if (mSendObjectFileSize == 0xFFFFFFFF) {
+			// tell driver to read until it receives a short packet
+			mfr.length = 0xFFFFFFFF;
+		} else {
+			mfr.length = mSendObjectFileSize - initialData;
+		}
+
+		MTPD("receiving %s\n", (const char *)mSendObjectFilePath);
+		// transfer the file
+		ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+	}
+	close(mfr.fd);
+
+	if (ret < 0) {
+		unlink(mSendObjectFilePath);
+		if (errno == ECANCELED)
+			result = MTP_RESPONSE_TRANSACTION_CANCELLED;
+		else
+			result = MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+done:
+	// reset so we don't attempt to send the data back
+	MTPD("MTP_RECEIVE_FILE returned %d\n", ret);
+	mData.reset();
+	mDatabase->lockMutex();
+	mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
+			result == MTP_RESPONSE_OK);
+	mDatabase->unlockMutex();
+	mSendObjectHandle = kInvalidObjectHandle;
+	MTPD("result: %d\n", result);
+	mSendObjectFormat = 0;
+	return MTP_RESPONSE_OK;
+}
+
+static void deleteRecursive(const char* path) {
+	char pathbuf[PATH_MAX];
+	size_t pathLength = strlen(path);
+	if (pathLength >= sizeof(pathbuf) - 1) {
+		MTPE("path too long: %s\n", path);
+	}
+	strcpy(pathbuf, path);
+	if (pathbuf[pathLength - 1] != '/') {
+		pathbuf[pathLength++] = '/';
+	}
+	char* fileSpot = pathbuf + pathLength;
+	int pathRemaining = sizeof(pathbuf) - pathLength - 1;
+
+	DIR* dir = opendir(path);
+	if (!dir) {
+		MTPE("opendir %s failed: %s", path, strerror(errno));
+		return;
+	}
+
+	struct dirent* entry;
+	while ((entry = readdir(dir))) {
+		const char* name = entry->d_name;
+
+		// ignore "." and ".."
+		if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+			continue;
+		}
+
+		int nameLength = strlen(name);
+		if (nameLength > pathRemaining) {
+			MTPE("path %s/%s too long\n", path, name);
+			continue;
+		}
+		strcpy(fileSpot, name);
+
+		int type = entry->d_type;
+		struct stat st;
+		if (lstat(pathbuf, &st)) {
+			MTPE("Failed to lstat '%s'\n", pathbuf);
+			continue;
+		}
+		if (st.st_mode & S_IFDIR) {
+			deleteRecursive(pathbuf);
+			rmdir(pathbuf);
+		} else {
+			unlink(pathbuf);
+		}
+	}
+	closedir(dir);
+}
+
+static void deletePath(const char* path) {
+	struct stat statbuf;
+	if (stat(path, &statbuf) == 0) {
+		if (S_ISDIR(statbuf.st_mode)) {
+			deleteRecursive(path);
+			rmdir(path);
+		} else {
+			unlink(path);
+		}
+	} else {
+		MTPE("deletePath stat failed for %s: %s", path, strerror(errno));
+	}
+}
+
+MtpResponseCode MtpServer::doDeleteObject() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpObjectFormat format = mRequest.getParameter(2);
+	// FIXME - support deleting all objects if handle is 0xFFFFFFFF
+	// FIXME - implement deleting objects by format
+
+	MtpString filePath;
+	int64_t fileLength;
+	MTPD("MtpServer::doDeleteObject calling getObjectFilePath\n");
+	mDatabase->lockMutex();
+	int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
+	if (result == MTP_RESPONSE_OK) {
+		MTPD("deleting %s", (const char *)filePath);
+		result = mDatabase->deleteFile(handle);
+		// Don't delete the actual files unless the database deletion is allowed
+		if (result == MTP_RESPONSE_OK) {
+			deletePath((const char *)filePath);
+		}
+	}
+	mDatabase->unlockMutex();
+	return result;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropDesc() {
+	MtpObjectProperty propCode = mRequest.getParameter(1);
+	MtpObjectFormat format = mRequest.getParameter(2);
+	MTPD("MtpServer::doGetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
+										MtpDebug::getFormatCodeName(format));
+	mDatabase->lockMutex();
+	MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
+	mDatabase->unlockMutex();
+	if (!property) {
+		MTPE("MtpServer::doGetObjectPropDesc propery not supported\n");
+		return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+	}
+	property->write(mData);
+	delete property;
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetDevicePropDesc() {
+	MtpDeviceProperty propCode = mRequest.getParameter(1);
+	MTPD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
+	mDatabase->lockMutex();
+	MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
+	mDatabase->unlockMutex();
+	if (!property) {
+		MTPE("MtpServer::doGetDevicePropDesc property not supported\n");
+		return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+	}
+	property->write(mData);
+	delete property;
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSendPartialObject() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	uint64_t offset = mRequest.getParameter(2);
+	uint64_t offset2 = mRequest.getParameter(3);
+	offset = offset | (offset2 << 32);
+	uint32_t length = mRequest.getParameter(4);
+
+	ObjectEdit* edit = getEditObject(handle);
+	if (!edit) {
+		MTPE("object not open for edit in doSendPartialObject");
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	// can't start writing past the end of the file
+	if (offset > edit->mSize) {
+		MTPE("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	const char* filePath = (const char *)edit->mPath;
+	MTPD("receiving partial %s %lld %lld\n", filePath, offset, length);
+
+	// read the header, and possibly some data
+	int ret = mData.read(mFD);
+	if (ret < MTP_CONTAINER_HEADER_SIZE)
+		return MTP_RESPONSE_GENERAL_ERROR;
+	int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
+
+	if (initialData > 0) {
+		ret = write(edit->mFD, mData.getData(), initialData);
+		offset += initialData;
+		length -= initialData;
+	}
+
+	if (length > 0) {
+		mtp_file_range  mfr;
+		mfr.fd = edit->mFD;
+		mfr.offset = offset;
+		mfr.length = length;
+
+		// transfer the file
+		ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+		MTPD("MTP_RECEIVE_FILE returned %d", ret);
+	}
+	if (ret < 0) {
+		mResponse.setParameter(1, 0);
+		if (errno == ECANCELED)
+			return MTP_RESPONSE_TRANSACTION_CANCELLED;
+		else
+			return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	// reset so we don't attempt to send this back
+	mData.reset();
+	mResponse.setParameter(1, length);
+	uint64_t end = offset + length;
+	if (end > edit->mSize) {
+		edit->mSize = end;
+	}
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doTruncateObject() {
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	ObjectEdit* edit = getEditObject(handle);
+	if (!edit) {
+		MTPE("object not open for edit in doTruncateObject");
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	uint64_t offset = mRequest.getParameter(2);
+	uint64_t offset2 = mRequest.getParameter(3);
+	offset |= (offset2 << 32);
+	if (ftruncate(edit->mFD, offset) != 0) {
+		return MTP_RESPONSE_GENERAL_ERROR;
+	} else {
+		edit->mSize = offset;
+		return MTP_RESPONSE_OK;
+	}
+}
+
+MtpResponseCode MtpServer::doBeginEditObject() {
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	if (getEditObject(handle)) {
+		MTPE("object already open for edit in doBeginEditObject");
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	MtpString path;
+	int64_t fileLength;
+	MtpObjectFormat format;
+	MTPD("MtpServer::doBeginEditObject calling getObjectFilePath\n");
+	mDatabase->lockMutex();
+	int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
+	mDatabase->unlockMutex();
+	if (result != MTP_RESPONSE_OK)
+		return result;
+
+	int fd = open((const char *)path, O_RDWR | O_EXCL);
+	if (fd < 0) {
+		MTPE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	addEditObject(handle, path, fileLength, format, fd);
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doEndEditObject() {
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	ObjectEdit* edit = getEditObject(handle);
+	if (!edit) {
+		MTPE("object not open for edit in doEndEditObject");
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	commitEdit(edit);
+	removeEditObject(handle);
+	return MTP_RESPONSE_OK;
+}
diff --git a/mtp/MtpServer.h b/mtp/MtpServer.h
new file mode 100755
index 0000000..61f5ccf
--- /dev/null
+++ b/mtp/MtpServer.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_SERVER_H
+#define _MTP_SERVER_H
+
+#include <utils/threads.h>
+#include <utils/Vector.h>
+#include "MtpRequestPacket.h"
+#include "MtpDatabase.h"
+#include "MtpDataPacket.h"
+#include "MtpResponsePacket.h"
+#include "MtpEventPacket.h"
+#include "mtp.h"
+#include "MtpUtils.h"
+
+
+class MtpDatabase;
+class MtpStorage;
+
+class MtpServer {
+
+private:
+    // file descriptor for MTP kernel driver
+    int                 mFD;
+	android::Mutex                   mMutex;
+    MtpDatabase*        mDatabase;
+
+    // appear as a PTP device
+    bool                mPtp;
+
+    // group to own new files and folders
+    int                 mFileGroup;
+    // permissions for new files and directories
+    int                 mFilePermission;
+    int                 mDirectoryPermission;
+
+    // current session ID
+    MtpSessionID        mSessionID;
+    // true if we have an open session and mSessionID is valid
+    bool                mSessionOpen;
+
+    MtpRequestPacket    mRequest;
+    MtpDataPacket       mData;
+    MtpResponsePacket   mResponse;
+    MtpEventPacket      mEvent;
+
+    MtpStorageList      mStorages;
+
+    // handle for new object, set by SendObjectInfo and used by SendObject
+    MtpObjectHandle     mSendObjectHandle;
+    MtpObjectFormat     mSendObjectFormat;
+    MtpString           mSendObjectFilePath;
+    size_t              mSendObjectFileSize;
+
+	pthread_mutex_t mtpMutex;
+
+    // represents an MTP object that is being edited using the android extensions
+    // for direct editing (BeginEditObject, SendPartialObject, TruncateObject and EndEditObject)
+    class ObjectEdit {
+        public:
+        MtpObjectHandle     mHandle;
+        MtpString           mPath;
+        uint64_t            mSize;
+        MtpObjectFormat     mFormat;
+        int                 mFD;
+
+        ObjectEdit(MtpObjectHandle handle, const char* path, uint64_t size,
+            MtpObjectFormat format, int fd)
+                : mHandle(handle), mPath(path), mSize(size), mFormat(format), mFD(fd) {
+            }
+
+        virtual ~ObjectEdit() {
+            close(mFD);
+        }
+    };
+    android::Vector<ObjectEdit*>  mObjectEditList;
+
+public:
+                        MtpServer(int fd, MtpDatabase* database, bool ptp,
+                                    int fileGroup, int filePerm, int directoryPerm);
+    virtual             ~MtpServer();
+
+    MtpStorage*         getStorage(MtpStorageID id);
+    inline bool         hasStorage() { return mStorages.size() > 0; }
+    bool                hasStorage(MtpStorageID id);
+    void                addStorage(MtpStorage* storage);
+    void                removeStorage(MtpStorage* storage);
+
+    void                run();
+
+    void                sendObjectAdded(MtpObjectHandle handle);
+    void                sendObjectRemoved(MtpObjectHandle handle);
+    void                sendObjectUpdated(MtpObjectHandle handle);
+
+private:
+    void                sendStoreAdded(MtpStorageID id);
+    void                sendStoreRemoved(MtpStorageID id);
+    void                sendEvent(MtpEventCode code, uint32_t param1);
+
+    void                addEditObject(MtpObjectHandle handle, MtpString& path,
+                                uint64_t size, MtpObjectFormat format, int fd);
+    ObjectEdit*         getEditObject(MtpObjectHandle handle);
+    void                removeEditObject(MtpObjectHandle handle);
+    void                commitEdit(ObjectEdit* edit);
+
+    bool                handleRequest();
+
+    MtpResponseCode     doGetDeviceInfo();
+    MtpResponseCode     doOpenSession();
+    MtpResponseCode     doCloseSession();
+    MtpResponseCode     doGetStorageIDs();
+    MtpResponseCode     doGetStorageInfo();
+    MtpResponseCode     doGetObjectPropsSupported();
+    MtpResponseCode     doGetObjectHandles();
+    MtpResponseCode     doGetNumObjects();
+    MtpResponseCode     doGetObjectReferences();
+    MtpResponseCode     doSetObjectReferences();
+    MtpResponseCode     doGetObjectPropValue();
+    MtpResponseCode     doSetObjectPropValue();
+    MtpResponseCode     doGetDevicePropValue();
+    MtpResponseCode     doSetDevicePropValue();
+    MtpResponseCode     doResetDevicePropValue();
+    MtpResponseCode     doGetObjectPropList();
+    MtpResponseCode     doGetObjectInfo();
+    MtpResponseCode     doGetObject();
+    MtpResponseCode     doGetThumb();
+    MtpResponseCode     doGetPartialObject(MtpOperationCode operation);
+    MtpResponseCode     doSendObjectInfo();
+    MtpResponseCode     doSendObject();
+    MtpResponseCode     doDeleteObject();
+    MtpResponseCode     doGetObjectPropDesc();
+    MtpResponseCode     doGetDevicePropDesc();
+    MtpResponseCode     doSendPartialObject();
+    MtpResponseCode     doTruncateObject();
+    MtpResponseCode     doBeginEditObject();
+    MtpResponseCode     doEndEditObject();
+};
+
+#endif // _MTP_SERVER_H
diff --git a/mtp/MtpStorage.cpp b/mtp/MtpStorage.cpp
new file mode 100755
index 0000000..821a788
--- /dev/null
+++ b/mtp/MtpStorage.cpp
@@ -0,0 +1,818 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpStorage.h"
+#include "MtpDataPacket.h"
+#include "MtpServer.h"
+#include "MtpEventPacket.h"
+#include "MtpDatabase.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/inotify.h>
+#include <fcntl.h>
+
+#define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY )
+
+MtpStorage::MtpStorage(MtpStorageID id, const char* filePath,
+		const char* description, uint64_t reserveSpace,
+		bool removable, uint64_t maxFileSize, MtpServer* refserver)
+	:	mStorageID(id),
+		mFilePath(filePath),
+		mDescription(description),
+		mMaxCapacity(0),
+		mMaxFileSize(maxFileSize),
+		mReserveSpace(reserveSpace),
+		mRemovable(removable),
+		mServer(refserver)
+{
+	MTPI("MtpStorage id: %d path: %s\n", id, filePath);
+	inotify_thread = 0;
+	sendEvents = false;
+	handleCurrentlySending = 0;
+	use_mutex = true;
+	if (pthread_mutex_init(&mtpMutex, NULL) != 0) {
+		MTPE("Failed to init mtpMutex\n");
+		use_mutex = false;
+	}
+	if (pthread_mutex_init(&inMutex, NULL) != 0) {
+		MTPE("Failed to init inMutex\n");
+		use_mutex = false;
+	}
+	
+}
+
+MtpStorage::~MtpStorage() {
+	if (inotify_thread) {
+		// TODO: what does this do? manpage says it does not kill the thread
+		pthread_kill(inotify_thread, 0);
+		for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+			inotify_rm_watch(inotify_fd, i->first);
+		}
+		close(inotify_fd);
+	}
+	for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+		delete i->second;
+	}
+	if (use_mutex) {
+		use_mutex = false;
+		pthread_mutex_destroy(&mtpMutex);
+		pthread_mutex_destroy(&inMutex);
+	}
+}
+
+int MtpStorage::getType() const {
+	return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM :  MTP_STORAGE_FIXED_RAM);
+}
+
+int MtpStorage::getFileSystemType() const {
+	return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
+}
+
+int MtpStorage::getAccessCapability() const {
+	return MTP_STORAGE_READ_WRITE;
+}
+
+uint64_t MtpStorage::getMaxCapacity() {
+	if (mMaxCapacity == 0) {
+		struct statfs   stat;
+		if (statfs(getPath(), &stat))
+			return -1;
+		mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
+	}
+	return mMaxCapacity;
+}
+
+uint64_t MtpStorage::getFreeSpace() {
+	struct statfs   stat;
+	if (statfs(getPath(), &stat))
+		return -1;
+	uint64_t freeSpace = (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
+	return (freeSpace > mReserveSpace ? freeSpace - mReserveSpace : 0);
+}
+
+const char* MtpStorage::getDescription() const {
+	return (const char *)mDescription;
+}
+
+int MtpStorage::createDB() {
+	std::string mtpParent = "";
+	mtpstorageparent = getPath();
+	// root directory is special: handle 0, parent 0, and empty path
+	mtpmap[0] = new Tree(0, 0, "");
+	MTPD("MtpStorage::createDB DONE\n");
+	if (use_mutex) {
+		MTPD("Starting inotify thread\n");
+		sendEvents = true;
+		inotify_thread = inotify();
+	} else {
+		MTPD("NOT starting inotify thread\n");
+	}
+	// for debugging and caching purposes, read the root dir already now
+	readDir(mtpstorageparent, mtpmap[0]);
+	// all other dirs are read on demand
+	return 0;
+}
+
+MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) {
+	MTPD("MtpStorage::getObjectList, parent: %u\n", parent);
+	//append object id  (numerical #s) of database to int array
+	MtpObjectHandleList* list = new MtpObjectHandleList();
+	if (parent == MTP_PARENT_ROOT) {
+		MTPD("parent == MTP_PARENT_ROOT\n");
+		parent = 0;
+	}
+
+	if (mtpmap.find(parent) == mtpmap.end()) {
+		MTPE("parent handle not found, returning empty list\n");
+		return list;
+	}
+
+	Tree* tree = mtpmap[parent];
+	if (!tree->wasAlreadyRead())
+	{
+		std::string path = getNodePath(tree);
+		MTPD("reading directory on demand for tree %p (%u), path: %s\n", tree, tree->Mtpid(), path.c_str());
+		readDir(path, tree);
+	}
+
+	mtpmap[parent]->getmtpids(list);
+	MTPD("returning %u objects in %s.\n", list->size(), tree->getName().c_str());
+	return list;
+}
+
+int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
+	struct stat st;
+	uint64_t size = 0;
+	MTPD("MtpStorage::getObjectInfo, handle: %u\n", handle);
+	Node* node = findNode(handle);
+	if (!node) {
+		// Item is not on this storage device
+		return -1;
+	}
+	
+	info.mStorageID = getStorageID();
+	MTPD("info.mStorageID: %u\n", info.mStorageID);
+	info.mParent = node->getMtpParentId();
+	MTPD("mParent: %u\n", info.mParent);
+	// TODO: do we want to lstat again here, or read from the node properties?
+	if (lstat(getNodePath(node).c_str(), &st) == 0)
+		size = st.st_size;
+	MTPD("size is: %llu\n", size);
+	info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
+	info.mDateModified = st.st_mtime;
+	if (S_ISDIR(st.st_mode)) {
+		info.mFormat = MTP_FORMAT_ASSOCIATION;
+	}
+	else {
+		info.mFormat = MTP_FORMAT_UNDEFINED;
+	}
+	info.mName = strdup(node->getName().c_str());
+	MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n");
+	return 0;
+}
+
+MtpObjectHandle MtpStorage::beginSendObject(const char* path,
+											MtpObjectFormat format,
+											MtpObjectHandle parent,
+											uint64_t size,
+											time_t modified) {
+	MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %u, format: %04x\n", path, parent, format);
+	iter it = mtpmap.find(parent);
+	if (it == mtpmap.end()) {
+		MTPE("parent node not found, returning error\n");
+		return kInvalidObjectHandle;
+	}
+	Tree* tree = it->second;
+
+	std::string pathstr(path);
+	size_t slashpos = pathstr.find_last_of('/');
+	if (slashpos == std::string::npos) {
+		MTPE("path has no slash, returning error\n");
+		return kInvalidObjectHandle;
+	}
+	std::string parentdir = pathstr.substr(0, slashpos);
+	std::string basename = pathstr.substr(slashpos + 1);
+	if (parent != 0 && parentdir != getNodePath(tree)) {
+		MTPE("beginSendObject into path '%s' but parent tree has path '%s', returning error\n", parentdir.c_str(), getNodePath(tree).c_str());
+		return kInvalidObjectHandle;
+	}
+
+	MTPD("MtpStorage::beginSendObject() parentdir: %s basename: %s\n", parentdir.c_str(), basename.c_str());
+	// note: for directories, the mkdir call is done later in MtpServer, here we just reserve a handle
+	bool isDir = format == MTP_FORMAT_ASSOCIATION;
+	Node* node = addNewNode(isDir, tree, basename);
+	handleCurrentlySending = node->Mtpid();	// suppress inotify for this node while sending
+
+	return node->Mtpid();
+}
+
+void MtpStorage::endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded)
+{
+	Node* node = findNode(handle);
+	if (!node)
+		return;	// just ignore if this is for another storage
+
+	node->addProperties(path, mStorageID);
+	handleCurrentlySending = 0;
+	// TODO: are we supposed to send an event about an upload by the initiator?
+	if (sendEvents)
+		mServer->sendObjectAdded(node->Mtpid());
+}
+
+int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
+	MTPD("MtpStorage::getObjectFilePath handle: %u\n", handle);
+	Node* node = findNode(handle);
+	if (!node)
+	{
+		// Item is not on this storage device
+		return -1;
+	}
+	// TODO: do we want to lstat here, or just read the info from the node?
+	struct stat st;
+	if (lstat(getNodePath(node).c_str(), &st) == 0)
+		outFileLength = st.st_size;
+	else
+		outFileLength = 0;
+	outFilePath = getNodePath(node).c_str();
+	MTPD("outFilePath: %s\n", outFilePath.string());
+	outFormat = node->isDir() ? MTP_FORMAT_ASSOCIATION : MTP_FORMAT_UNDEFINED;
+	return 0;
+}
+
+int MtpStorage::readDir(const std::string& path, Tree* tree)
+{
+	struct dirent *de;
+	int storageID = getStorageID();
+	MtpObjectHandle parent = tree->Mtpid();
+
+	DIR *d = opendir(path.c_str());
+	MTPD("reading dir '%s', parent handle %u\n", path.c_str(), parent);
+	if (d == NULL) {
+		MTPE("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno));
+		return -1;
+	}
+	// TODO: for refreshing dirs: capture old entries here
+	while ((de = readdir(d)) != NULL) {
+		// Because exfat-fuse causes issues with dirent, we will use stat
+		// for some things that dirent should be able to do
+		std::string item = path + "/" + de->d_name;
+		struct stat st;
+		if (lstat(item.c_str(), &st)) {
+			MTPE("Error running lstat on '%s'\n", item.c_str());
+			return -1;
+		}
+		// TODO: if we want to use this for refreshing dirs too, first find existing name and overwrite
+		if (strcmp(de->d_name, ".") == 0)
+			continue;
+		if (strcmp(de->d_name, "..") == 0)
+			continue;
+		Node* node = addNewNode(st.st_mode & S_IFDIR, tree, de->d_name);
+		node->addProperties(item, storageID);
+		//if (sendEvents)
+		//	mServer->sendObjectAdded(node->Mtpid());
+		//	sending events here makes simple-mtpfs very slow, and it is probably the wrong thing to do anyway
+	}
+	closedir(d);
+	// TODO: for refreshing dirs: remove entries that no longer exist (with their nodes)
+	tree->setAlreadyRead(true);
+	addInotify(tree);
+	return 0;
+}
+
+int MtpStorage::deleteFile(MtpObjectHandle handle) {
+	MTPD("MtpStorage::deleteFile handle: %u\n", handle);
+	Node* node = findNode(handle);
+	if (!node) {
+		// Item is not on this storage device
+		return -1;
+	}
+	MtpObjectHandle parent = node->getMtpParentId();
+	Tree* tree = mtpmap[parent];
+	if (!tree) {
+		MTPE("parent tree for handle %u not found\n", parent);
+		return -1;
+	}
+	if (node->isDir()) {
+		MTPD("deleting tree from mtpmap: %u\n", handle);
+		mtpmap.erase(handle);
+	}
+
+	MTPD("deleting handle: %u\n", handle);
+	tree->deleteNode(handle);
+	MTPD("deleted\n");
+	return 0;
+}
+
+void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID)
+{
+	MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str());
+	PropEntry pe;
+	pe.handle = node->Mtpid();
+	pe.property = property;
+
+	if (property == 0xffffffff)
+	{
+		// add all properties
+		MTPD("MtpStorage::queryNodeProperties for all properties\n");
+		std::vector<Node::mtpProperty> mtpprop = node->getMtpProps();
+		for (size_t i = 0; i < mtpprop.size(); ++i) {
+			pe.property = mtpprop[i].property;
+			pe.datatype = mtpprop[i].dataType;
+			pe.intvalue = mtpprop[i].valueInt;
+			pe.strvalue = mtpprop[i].valueStr;
+			results.push_back(pe);
+		}
+		return;
+	}
+	else if (property == 0)
+	{
+		// TODO: use groupCode
+	}
+
+	// single property
+	// TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue
+	switch (property) {
+//		case MTP_PROPERTY_OBJECT_FORMAT:
+//			pe.datatype = MTP_TYPE_UINT16;
+//			pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT);
+//			break;
+
+		case MTP_PROPERTY_STORAGE_ID:
+			pe.datatype = MTP_TYPE_UINT32;
+			pe.intvalue = storageID;
+			break;
+
+		case MTP_PROPERTY_PROTECTION_STATUS:
+			pe.datatype = MTP_TYPE_UINT16;
+			pe.intvalue = 0;
+			break;
+
+		case MTP_PROPERTY_OBJECT_SIZE:
+		{
+			pe.datatype = MTP_TYPE_UINT64;
+			struct stat st;
+			pe.intvalue = 0;
+			if (lstat(getNodePath(node).c_str(), &st) == 0)
+				pe.intvalue = st.st_size;
+			break;
+		}
+
+		default:
+		{
+			const Node::mtpProperty& prop = node->getProperty(property);
+			if (prop.property != property)
+			{
+				MTPD("queryNodeProperties: unknown property %x\n", property);
+				return;
+			}
+			pe.datatype = prop.dataType;
+			pe.intvalue = prop.valueInt;
+			pe.strvalue = prop.valueStr;
+			// TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here
+		}
+
+	}
+	results.push_back(pe);
+}
+
+int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) {
+	MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property);
+	if (groupCode != 0)
+	{
+		MTPE("getObjectPropertyList: groupCode unsupported\n");
+		return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED
+	}
+	// TODO: support all the special stuff, like:
+	// handle == 0 -> all objects at the root level
+	// handle == 0xffffffff -> all objects (on all storages? how could we support that?)
+	// format == 0 -> all formats, otherwise filter by ObjectFormatCode
+	// property == 0xffffffff -> all properties except those with group code 0xffffffff
+	// if property == 0 then use groupCode
+	//   groupCode == 0 -> return Specification_By_Group_Unsupported
+	// depth == 0xffffffff -> all objects incl. and below handle
+
+	std::vector<PropEntry> results;
+
+	if (handle == 0xffffffff) {
+		// TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage)
+	} else if (handle == 0)	{
+		// all objects at the root level
+		Tree* root = mtpmap[0];
+		MtpObjectHandleList list;
+		root->getmtpids(&list);
+		for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) {
+			Node* node = root->findNode(*it);
+			if (!node) {
+				MTPE("BUG: node not found for root entry with handle %u\n", *it);
+				break;
+			}
+			queryNodeProperties(results, node, property, groupCode, mStorageID);
+		}
+	} else {
+		// single object
+		Node* node = findNode(handle);
+		if (!node) {
+			// Item is not on this storage device
+			return -1;
+		}
+		queryNodeProperties(results, node, property, groupCode, mStorageID);
+	}
+
+	MTPD("count: %u\n", results.size());
+	packet.putUInt32(results.size());
+
+	for (size_t i = 0; i < results.size(); ++i) {
+		PropEntry& p = results[i];
+		MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n",
+				p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property),
+				p.datatype, p.intvalue);
+		packet.putUInt32(p.handle);
+		packet.putUInt16(p.property);
+		packet.putUInt16(p.datatype);
+		switch (p.datatype) {
+			case MTP_TYPE_INT8:
+				MTPD("MTP_TYPE_INT8\n");
+				packet.putInt8(p.intvalue);
+				break;
+			case MTP_TYPE_UINT8:
+				MTPD("MTP_TYPE_UINT8\n");
+				packet.putUInt8(p.intvalue);
+				break;
+			case MTP_TYPE_INT16:
+				MTPD("MTP_TYPE_INT16\n");
+				packet.putInt16(p.intvalue);
+				break;
+			case MTP_TYPE_UINT16:
+				MTPD("MTP_TYPE_UINT16\n");
+				packet.putUInt16(p.intvalue);
+				break;
+			case MTP_TYPE_INT32:
+				MTPD("MTP_TYPE_INT32\n");
+				packet.putInt32(p.intvalue);
+				break;
+			case MTP_TYPE_UINT32:
+				MTPD("MTP_TYPE_UINT32\n");
+				packet.putUInt32(p.intvalue);
+				break;
+			case MTP_TYPE_INT64:
+				MTPD("MTP_TYPE_INT64\n");
+				packet.putInt64(p.intvalue);
+				break;
+			case MTP_TYPE_UINT64:
+				MTPD("MTP_TYPE_UINT64\n");
+				packet.putUInt64(p.intvalue);
+				break;
+			case MTP_TYPE_INT128:
+				MTPD("MTP_TYPE_INT128\n");
+				packet.putInt128(p.intvalue);
+				break;
+			case MTP_TYPE_UINT128:
+				MTPD("MTP_TYPE_UINT128\n");
+				packet.putUInt128(p.intvalue);
+				break;
+			case MTP_TYPE_STR:
+				MTPD("MTP_TYPE_STR: %s\n", p.strvalue.c_str());
+				packet.putString(p.strvalue.c_str());
+				break;
+			default:
+				MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype);
+				break;
+		}
+	}
+	return 0;
+}
+
+int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) {
+	MTPD("MtpStorage::renameObject, handle: %u, new name: '%s'\n", handle, newName.c_str());
+	if (handle == MTP_PARENT_ROOT) {
+		MTPE("parent == MTP_PARENT_ROOT, cannot rename root\n");
+		return -1;
+	} else {
+		for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+			Node* node = i->second->findNode(handle);
+			if (node != NULL) {
+				std::string oldName = getNodePath(node);
+				std::string parentdir = oldName.substr(0, oldName.find_last_of('/'));
+				std::string newFullName = parentdir + "/" + newName;
+				MTPD("old: '%s', new: '%s'\n", oldName.c_str(), newFullName.c_str());
+				if (rename(oldName.c_str(), newFullName.c_str()) == 0) {
+					node->rename(newName);
+					return 0;
+				} else {
+					MTPE("MtpStorage::renameObject failed, handle: %u, new name: '%s'\n", handle, newName.c_str());
+					return -1;
+				}
+			}
+		}
+	}
+	// handle not found on this storage
+	return -1;
+}
+
+int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpStorage::PropEntry& pe) {
+	Node *node;
+	for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+		node = i->second->findNode(handle);
+		if (node != NULL) {
+			const Node::mtpProperty& prop = node->getProperty(property);
+			if (prop.property != property) {
+				MTPD("getObjectPropertyValue: unknown property %x for handle %u\n", property, handle);
+				return -1;
+			}
+			pe.datatype = prop.dataType;
+			pe.intvalue = prop.valueInt;
+			pe.strvalue = prop.valueStr;
+			pe.handle = handle;
+			pe.property = property;
+			return 0;
+		}
+	}
+	// handle not found on this storage
+	return -1;
+}
+
+pthread_t MtpStorage::inotify(void) {
+	pthread_t thread;
+	ThreadPtr inotifyptr = &MtpStorage::inotify_t;
+	PThreadPtr p = *(PThreadPtr*)&inotifyptr;
+	pthread_create(&thread, NULL, p, this);
+	return thread;
+}
+
+int MtpStorage::addInotify(Tree* tree) {
+	std::string path = getNodePath(tree);
+	MTPD("adding inotify for tree %x, dir: %s\n", tree, path.c_str());
+	int wd = inotify_add_watch(inotify_fd, path.c_str(), WATCH_FLAGS);
+	if (wd < 0) {
+		MTPE("inotify_add_watch failed: %s\n", strerror(errno));
+		return -1;
+	}
+	inotifymap[wd] = tree;
+	return 0;
+}
+
+void MtpStorage::handleInotifyEvent(struct inotify_event* event)
+{
+	std::map<int, Tree*>::iterator it = inotifymap.find(event->wd);
+	if (it == inotifymap.end()) {
+		MTPE("Unable to locate inotify_wd: %i\n", event->wd);
+		return;
+	}
+	Tree* tree = it->second;
+	MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str());
+	Node* node = tree->findEntryByName(basename(event->name));
+	if (node && node->Mtpid() == handleCurrentlySending) {
+		MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid());
+		return;
+	}
+	if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
+		if (event->mask & IN_ISDIR) {
+			MTPD("inotify_t create is dir\n");
+		} else {
+			MTPD("inotify_t create is file\n");
+		}
+		if (node == NULL) {
+			node = addNewNode(event->mask & IN_ISDIR, tree, event->name);
+			std::string item = getNodePath(tree) + "/" + event->name;
+			node->addProperties(item, getStorageID());
+			mServer->sendObjectAdded(node->Mtpid());
+		} else {
+			MTPD("inotify_t item already exists.\n");
+		}
+		if (event->mask & IN_ISDIR) {
+			// TODO: do we need to do anything here? probably not until someone reads from the dir...
+		}
+	} else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
+		if (event->mask & IN_ISDIR) {
+			MTPD("inotify_t Directory %s deleted\n", event->name);
+		} else {
+			MTPD("inotify_t File %s deleted\n", event->name);
+		}
+		if (node)
+		{
+			if (event->mask & IN_ISDIR) {
+				for (std::map<int, Tree*>::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) {
+					if (it->second == node) {
+						inotify_rm_watch(inotify_fd, it->first);
+						MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str());
+						inotifymap.erase(it->first);
+						break;
+					}
+
+				}
+			}
+			MtpObjectHandle handle = node->Mtpid();
+			deleteFile(handle);
+			mServer->sendObjectRemoved(handle);
+		} else {
+			MTPD("inotify_t already removed.\n");
+		}
+	} else if (event->mask & IN_MODIFY) {
+		MTPD("inotify_t item %s modified.\n", event->name);
+		if (node != NULL) {
+			uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt;
+			struct stat st;
+			uint64_t new_size = 0;
+			if (lstat(getNodePath(node).c_str(), &st) == 0)
+				new_size = (uint64_t)st.st_size;
+			if (orig_size != new_size) {
+				MTPD("size changed from %llu to %llu on mtpid: %u\n", orig_size, new_size, node->Mtpid());
+				node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64);
+				mServer->sendObjectUpdated(node->Mtpid());
+			}
+		} else {
+			MTPE("inotify_t modified item not found\n");
+		}
+	} else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
+		// TODO: is this always already handled by IN_DELETE for the parent dir?
+	}
+}
+
+int MtpStorage::inotify_t(void) {
+	#define EVENT_SIZE ( sizeof(struct inotify_event) )
+	#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) )
+	char buf[EVENT_BUF_LEN];
+
+	MTPD("inotify thread: inotify_init\n");
+	inotify_fd = inotify_init();
+
+	if (inotify_fd < 0) {
+		MTPE("Can't run inotify for mtp server: %s\n", strerror(errno));
+		return -1;
+	}
+
+	while (true) {
+		int i = 0;
+		int len = read(inotify_fd, buf, EVENT_BUF_LEN);
+
+		if (len < 0) {
+			if (errno == EINTR)
+				continue;
+			MTPE("inotify_t Can't read inotify events\n");
+		}
+
+		while (i < len) {
+			struct inotify_event *event = (struct inotify_event *) &buf[i];
+			if (event->len) {
+				MTPD("inotify event: wd: %i, mask: %x, name: %s\n", event->wd, event->mask, event->name);
+				lockMutex(1);
+				handleInotifyEvent(event);
+				unlockMutex(1);
+			}
+			i += EVENT_SIZE + event->len;
+		}
+	}
+
+	for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+		inotify_rm_watch(inotify_fd, i->first);
+	}
+	close(inotify_fd);
+	return 0;
+}
+
+Node* MtpStorage::findNodeByPath(const std::string& path) {
+	MTPD("findNodeByPath: %s\n", path.c_str());
+	std::string match = path.substr(0, mtpstorageparent.size());
+	if (match != mtpstorageparent) {
+		// not on this device
+		MTPD("no match: %s is not on storage %s\n", match.c_str(), mtpstorageparent.c_str());
+		return NULL;
+	}
+
+	// TODO: fix and test this
+	std::string p = path.substr(mtpstorageparent.size()+1);	// cut off "/" after storage root too
+	Tree* tree = mtpmap[0]; // start at storage root
+
+	Node* node = NULL;
+	while (!p.empty()) {
+		size_t slashpos = p.find('/');
+		std::string e;
+		if (slashpos != std::string::npos) {
+			e = p;
+			p.clear();
+		} else {
+			e = p.substr(0, slashpos);
+			p = p.substr(slashpos + 1);
+		}
+		MTPD("path element: %s, rest: %s\n", e.c_str(), p.c_str());
+		node = tree->findEntryByName(e);
+		if (!node) {
+			MTPE("path element of %s not found: %s\n", path.c_str(), e.c_str());
+			return NULL;
+		}
+		if (node->isDir())
+			tree = static_cast<Tree*>(node);
+		else if (!p.empty()) {
+			MTPE("path element of %s is not a directory: %s node: %p\n", path.c_str(), e.c_str(), node);
+			return NULL;
+		}
+	}
+	MTPD("findNodeByPath: found node %p, handle: %u, name: %s\n", node, node->Mtpid(), node->getName().c_str());
+	return node;
+}
+
+Node* MtpStorage::addNewNode(bool isDir, Tree* tree, const std::string& name)
+{
+	// global counter for new object handles
+	static MtpObjectHandle mtpid = 0;
+
+	++mtpid;
+	MTPD("adding new %s node for %s, new handle: %u\n", isDir ? "dir" : "file", name.c_str(), mtpid);
+	MtpObjectHandle parent = tree->Mtpid();
+	MTPD("parent tree: %x, handle: %u, name: %s\n", tree, parent, tree->getName().c_str());
+	Node* node;
+	if (isDir)
+		node = mtpmap[mtpid] = new Tree(mtpid, parent, name);
+	else
+		node = new Node(mtpid, parent, name);
+	tree->addEntry(node);
+	return node;
+}
+
+Node* MtpStorage::findNode(MtpObjectHandle handle) {
+	for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+		Node* node = i->second->findNode(handle);
+		if (node != NULL) {
+			MTPD("findNode: found node %p for handle %u, name: %s\n", node, handle, node->getName().c_str());
+			if (node->Mtpid() != handle)
+			{
+				MTPE("BUG: entry for handle %u points to node with handle %u\n", handle, node->Mtpid());
+			}
+			return node;
+		}
+	}
+	// Item is not on this storage device
+	MTPD("MtpStorage::findNode: no node found for handle %u on storage %u, searched %u trees\n", handle, mStorageID, mtpmap.size());
+	return NULL;
+}
+
+std::string MtpStorage::getNodePath(Node* node) {
+	std::string path;
+	MTPD("getNodePath: node %p, handle %u\n", node, node->Mtpid());
+	while (node)
+	{
+		path = "/" + node->getName() + path;
+		MtpObjectHandle parent = node->getMtpParentId();
+		if (parent == 0)	// root
+			break;
+		node = findNode(parent);
+	}
+	path = mtpstorageparent + path;
+	MTPD("getNodePath: path %s\n", path.c_str());
+	return path;
+}
+
+void MtpStorage::lockMutex(int thread_type) {
+	if (!use_mutex)
+		return; // mutex is disabled
+	if (thread_type) {
+		// inotify thread
+		pthread_mutex_lock(&inMutex);
+		while (pthread_mutex_trylock(&mtpMutex)) {
+			pthread_mutex_unlock(&inMutex);
+			usleep(32000);
+			pthread_mutex_lock(&inMutex);
+		}
+	} else {
+		// main mtp thread
+		pthread_mutex_lock(&mtpMutex);
+		while (pthread_mutex_trylock(&inMutex)) {
+			pthread_mutex_unlock(&mtpMutex);
+			usleep(13000);
+			pthread_mutex_lock(&mtpMutex);
+		}
+	}
+}
+
+void MtpStorage::unlockMutex(int thread_type) {
+	if (!use_mutex)
+		return; // mutex is disabled
+	pthread_mutex_unlock(&inMutex);
+	pthread_mutex_unlock(&mtpMutex);
+}
diff --git a/mtp/MtpStorage.h b/mtp/MtpStorage.h
new file mode 100755
index 0000000..418e3db
--- /dev/null
+++ b/mtp/MtpStorage.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_STORAGE_H
+#define _MTP_STORAGE_H
+
+#include "mtp.h"
+#include "MtpObjectInfo.h"
+#include <string>
+#include <deque>
+#include <map>
+#include <libgen.h>
+#include <pthread.h>
+#include "btree.hpp"
+#include "MtpServer.h"
+
+class MtpDatabase;
+struct inotify_event;
+
+class MtpStorage {
+
+private:
+    MtpStorageID            mStorageID;
+    MtpString               mFilePath;
+    MtpString               mDescription;
+    uint64_t                mMaxCapacity;
+    uint64_t                mMaxFileSize;
+    // amount of free space to leave unallocated
+    uint64_t                mReserveSpace;
+    bool                    mRemovable;
+	MtpServer*				mServer;
+    typedef std::map<int, Tree*> maptree;
+    typedef maptree::iterator iter;
+    maptree mtpmap;
+	std::string mtpstorageparent;
+	android::Mutex           mMutex;
+
+public:
+                            MtpStorage(MtpStorageID id, const char* filePath,
+                                    const char* description, uint64_t reserveSpace,
+                                    bool removable, uint64_t maxFileSize, MtpServer* refserver);
+    virtual                 ~MtpStorage();
+
+    inline MtpStorageID     getStorageID() const { return mStorageID; }
+    int                     getType() const;
+    int                     getFileSystemType() const;
+    int                     getAccessCapability() const;
+    uint64_t                getMaxCapacity();
+    uint64_t                getFreeSpace();
+    const char*             getDescription() const;
+    inline const char*      getPath() const { return (const char *)mFilePath; }
+    inline bool             isRemovable() const { return mRemovable; }
+    inline uint64_t         getMaxFileSize() const { return mMaxFileSize; }
+
+	struct PropEntry {
+		MtpObjectHandle handle;
+		uint16_t property;
+		uint16_t datatype;
+		uint64_t intvalue;
+		std::string strvalue;
+	};
+
+	int readDir(const std::string& path, Tree* tree);
+	int createDB();
+	MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectHandle parent);
+	int getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info);
+	MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, uint64_t size, time_t modified);
+	void endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded);
+	int getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet);
+	int getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat);
+	int deleteFile(MtpObjectHandle handle);
+	int renameObject(MtpObjectHandle handle, std::string newName);
+	int getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, PropEntry& prop);
+	void lockMutex(int thread_type);
+	void unlockMutex(int thread_type);
+
+private:
+	pthread_t inotify();
+	int inotify_t();
+	typedef int (MtpStorage::*ThreadPtr)(void);
+	typedef void* (*PThreadPtr)(void *);
+	std::map<int, Tree*> inotifymap;	// inotify wd -> tree
+	pthread_t inotify_thread;
+	int inotify_fd;
+	int addInotify(Tree* tree);
+	void handleInotifyEvent(struct inotify_event* event);
+
+	bool sendEvents;
+	MtpObjectHandle handleCurrentlySending;
+
+	Node* addNewNode(bool isDir, Tree* tree, const std::string& name);
+	Node* findNode(MtpObjectHandle handle);
+	Node* findNodeByPath(const std::string& path);
+	std::string getNodePath(Node* node);
+
+	void queryNodeProperties(std::vector<PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID);
+
+	bool use_mutex;
+	pthread_mutex_t inMutex; // inotify mutex
+	pthread_mutex_t mtpMutex; // main mtp mutex
+};
+
+#endif // _MTP_STORAGE_H
diff --git a/mtp/MtpStorageInfo.cpp b/mtp/MtpStorageInfo.cpp
new file mode 100755
index 0000000..a2b8ca2
--- /dev/null
+++ b/mtp/MtpStorageInfo.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpStorageInfo.h"
+#include "MtpStringBuffer.h"
+
+MtpStorageInfo::MtpStorageInfo(MtpStorageID id)
+	:	mStorageID(id),
+		mStorageType(0),
+		mFileSystemType(0),
+		mAccessCapability(0),
+		mMaxCapacity(0),
+		mFreeSpaceBytes(0),
+		mFreeSpaceObjects(0),
+		mStorageDescription(NULL),
+		mVolumeIdentifier(NULL)
+{
+}
+
+MtpStorageInfo::~MtpStorageInfo() {
+	if (mStorageDescription)
+		free(mStorageDescription);
+	if (mVolumeIdentifier)
+		free(mVolumeIdentifier);
+}
+
+void MtpStorageInfo::read(MtpDataPacket& packet) {
+	MtpStringBuffer string;
+
+	// read the device info
+	mStorageType = packet.getUInt16();
+	mFileSystemType = packet.getUInt16();
+	mAccessCapability = packet.getUInt16();
+	mMaxCapacity = packet.getUInt64();
+	mFreeSpaceBytes = packet.getUInt64();
+	mFreeSpaceObjects = packet.getUInt32();
+
+	packet.getString(string);
+	mStorageDescription = strdup((const char *)string);
+	packet.getString(string);
+	mVolumeIdentifier = strdup((const char *)string);
+}
+
+void MtpStorageInfo::print() {
+	MTPI("Storage Info %08X:\n\tmStorageType: %d\n\tmFileSystemType: %d\n\tmAccessCapability: %d\n",
+			mStorageID, mStorageType, mFileSystemType, mAccessCapability);
+	MTPI("\tmMaxCapacity: %lld\n\tmFreeSpaceBytes: %lld\n\tmFreeSpaceObjects: %d\n",
+			mMaxCapacity, mFreeSpaceBytes, mFreeSpaceObjects);
+	MTPI("\tmStorageDescription: %s\n\tmVolumeIdentifier: %s\n",
+			mStorageDescription, mVolumeIdentifier);
+}
+
diff --git a/mtp/MtpStorageInfo.h b/mtp/MtpStorageInfo.h
new file mode 100755
index 0000000..8858328
--- /dev/null
+++ b/mtp/MtpStorageInfo.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_STORAGE_INFO_H
+#define _MTP_STORAGE_INFO_H
+
+#include "MtpTypes.h"
+
+
+class MtpDataPacket;
+
+class MtpStorageInfo {
+public:
+    MtpStorageID        mStorageID;
+    uint16_t            mStorageType;
+    uint16_t            mFileSystemType;
+    uint16_t            mAccessCapability;
+    uint64_t            mMaxCapacity;
+    uint64_t            mFreeSpaceBytes;
+    uint32_t            mFreeSpaceObjects;
+    char*               mStorageDescription;
+    char*               mVolumeIdentifier;
+
+public:
+                        MtpStorageInfo(MtpStorageID id);
+    virtual             ~MtpStorageInfo();
+
+    void                read(MtpDataPacket& packet);
+
+    void                print();
+};
+
+
+#endif // _MTP_STORAGE_INFO_H
diff --git a/mtp/MtpStringBuffer.cpp b/mtp/MtpStringBuffer.cpp
new file mode 100755
index 0000000..8aeb3ca
--- /dev/null
+++ b/mtp/MtpStringBuffer.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <string.h>
+#include "MtpDataPacket.h"
+#include "MtpStringBuffer.h"
+
+MtpStringBuffer::MtpStringBuffer()
+	:	mCharCount(0),
+		mByteCount(1)
+{
+	mBuffer[0] = 0;
+}
+
+MtpStringBuffer::MtpStringBuffer(const char* src)
+	:	mCharCount(0),
+		mByteCount(1)
+{
+	set(src);
+}
+
+MtpStringBuffer::MtpStringBuffer(const uint16_t* src)
+	:	mCharCount(0),
+		mByteCount(1)
+{
+	set(src);
+}
+
+MtpStringBuffer::MtpStringBuffer(const MtpStringBuffer& src)
+	:   mCharCount(src.mCharCount),
+		mByteCount(src.mByteCount)
+{
+	memcpy(mBuffer, src.mBuffer, mByteCount);
+}
+
+
+MtpStringBuffer::~MtpStringBuffer() {
+}
+
+void MtpStringBuffer::set(const char* src) {
+	size_t length = strlen(src);
+	if (length >= sizeof(mBuffer))
+		length = sizeof(mBuffer) - 1;
+	memcpy(mBuffer, src, length);
+
+	// count the characters
+	int count = 0;
+	char ch;
+	while ((ch = *src++) != 0) {
+		if ((ch & 0x80) == 0) {
+			// single byte character
+		} else if ((ch & 0xE0) == 0xC0) {
+			// two byte character
+			if (! *src++) {
+				// last character was truncated, so ignore last byte
+				length--;
+				break;
+			}
+		} else if ((ch & 0xF0) == 0xE0) {
+			// 3 byte char
+			if (! *src++) {
+				// last character was truncated, so ignore last byte
+				length--;
+				break;
+			}
+			if (! *src++) {
+				// last character was truncated, so ignore last two bytes
+				length -= 2;
+				break;
+			}
+		}
+		count++;
+	}
+
+	mByteCount = length + 1;
+	mBuffer[length] = 0;
+	mCharCount = count;
+}
+
+void MtpStringBuffer::set(const uint16_t* src) {
+	int count = 0;
+	uint16_t ch;
+	uint8_t* dest = mBuffer;
+
+	while ((ch = *src++) != 0 && count < 255) {
+		if (ch >= 0x0800) {
+			*dest++ = (uint8_t)(0xE0 | (ch >> 12));
+			*dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
+			*dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+		} else if (ch >= 0x80) {
+			*dest++ = (uint8_t)(0xC0 | (ch >> 6));
+			*dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+		} else {
+			*dest++ = ch;
+		}
+		count++;
+	}
+	*dest++ = 0;
+	mCharCount = count;
+	mByteCount = dest - mBuffer;
+}
+
+void MtpStringBuffer::readFromPacket(MtpDataPacket* packet) {
+	int count = packet->getUInt8();
+	uint8_t* dest = mBuffer;
+	for (int i = 0; i < count; i++) {
+		uint16_t ch = packet->getUInt16();
+		if (ch >= 0x0800) {
+			*dest++ = (uint8_t)(0xE0 | (ch >> 12));
+			*dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
+			*dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+		} else if (ch >= 0x80) {
+			*dest++ = (uint8_t)(0xC0 | (ch >> 6));
+			*dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+		} else {
+			*dest++ = ch;
+		}
+	}
+	*dest++ = 0;
+	mCharCount = count;
+	mByteCount = dest - mBuffer;
+}
+
+void MtpStringBuffer::writeToPacket(MtpDataPacket* packet) const {
+	int count = mCharCount;
+	const uint8_t* src = mBuffer;
+	packet->putUInt8(count > 0 ? count + 1 : 0);
+
+	// expand utf8 to 16 bit chars
+	for (int i = 0; i < count; i++) {
+		uint16_t ch;
+		uint16_t ch1 = *src++;
+		if ((ch1 & 0x80) == 0) {
+			// single byte character
+			ch = ch1;
+		} else if ((ch1 & 0xE0) == 0xC0) {
+			// two byte character
+			uint16_t ch2 = *src++;
+			ch = ((ch1 & 0x1F) << 6) | (ch2 & 0x3F);
+		} else {
+			// three byte character
+			uint16_t ch2 = *src++;
+			uint16_t ch3 = *src++;
+			ch = ((ch1 & 0x0F) << 12) | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F);
+		}
+		packet->putUInt16(ch);
+	}
+	// only terminate with zero if string is not empty
+	if (count > 0)
+		packet->putUInt16(0);
+}
+
diff --git a/mtp/MtpStringBuffer.h b/mtp/MtpStringBuffer.h
new file mode 100755
index 0000000..9d61ecf
--- /dev/null
+++ b/mtp/MtpStringBuffer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_STRING_BUFFER_H
+#define _MTP_STRING_BUFFER_H
+
+#include <stdint.h>
+
+class MtpDataPacket;
+
+// Represents a utf8 string, with a maximum of 255 characters
+class MtpStringBuffer {
+
+private:
+    // mBuffer contains string in UTF8 format
+    // maximum 3 bytes/character, with 1 extra for zero termination
+    uint8_t         mBuffer[255 * 3 + 1];
+    int             mCharCount;
+    int             mByteCount;
+
+public:
+                    MtpStringBuffer();
+                    MtpStringBuffer(const char* src);
+                    MtpStringBuffer(const uint16_t* src);
+                    MtpStringBuffer(const MtpStringBuffer& src);
+    virtual         ~MtpStringBuffer();
+
+    void            set(const char* src);
+    void            set(const uint16_t* src);
+
+    void            readFromPacket(MtpDataPacket* packet);
+    void            writeToPacket(MtpDataPacket* packet) const;
+
+    inline int      getCharCount() const { return mCharCount; }
+    inline int      getByteCount() const { return mByteCount; }
+
+	inline operator const char*() const { return (const char *)mBuffer; }
+};
+
+#endif // _MTP_STRING_BUFFER_H
diff --git a/mtp/MtpTypes.h b/mtp/MtpTypes.h
new file mode 100755
index 0000000..64e180c
--- /dev/null
+++ b/mtp/MtpTypes.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_TYPES_H
+#define _MTP_TYPES_H
+
+#include <stdint.h>
+#include <vector>
+#include <utils/Vector.h>
+#include <utils/String8.h>
+
+typedef int32_t int128_t[4];
+typedef uint32_t uint128_t[4];
+
+typedef uint16_t MtpOperationCode;
+typedef uint16_t MtpResponseCode;
+typedef uint16_t MtpEventCode;
+typedef uint32_t MtpSessionID;
+typedef uint32_t MtpStorageID;
+typedef uint32_t MtpTransactionID;
+typedef uint16_t MtpPropertyCode;
+typedef uint16_t MtpDataType;
+typedef uint16_t MtpObjectFormat;
+typedef MtpPropertyCode MtpDeviceProperty;
+typedef MtpPropertyCode MtpObjectProperty;
+
+// object handles are unique across all storage but only within a single session.
+// object handles cannot be reused after an object is deleted.
+// values 0x00000000 and 0xFFFFFFFF are reserved for special purposes.
+typedef uint32_t MtpObjectHandle;
+
+// Special values
+#define MTP_PARENT_ROOT         0xFFFFFFFF       // parent is root of the storage
+#define kInvalidObjectHandle    0xFFFFFFFF
+
+class MtpStorage;
+class MtpDevice;
+class MtpProperty;
+
+typedef android::Vector<MtpStorage*> MtpStorageList;
+typedef android::Vector<MtpDevice*> MtpDeviceList;
+typedef android::Vector<MtpProperty*> MtpPropertyList;
+
+typedef android::Vector<uint8_t> UInt8List;
+typedef android::Vector<uint16_t> UInt16List;
+typedef android::Vector<uint32_t> UInt32List;
+typedef android::Vector<uint64_t> UInt64List;
+typedef android::Vector<int8_t> Int8List;
+typedef android::Vector<int16_t> Int16List;
+typedef android::Vector<int32_t> Int32List;
+typedef android::Vector<int64_t> Int64List;
+
+typedef UInt16List MtpObjectPropertyList;
+typedef UInt16List MtpDevicePropertyList;
+typedef UInt16List MtpObjectFormatList;
+typedef UInt32List MtpObjectHandleList;
+typedef UInt16List MtpObjectPropertyList;
+typedef UInt32List MtpStorageIDList;
+
+typedef android::String8    MtpString;
+
+
+#endif // _MTP_TYPES_H
diff --git a/mtp/MtpUtils.cpp b/mtp/MtpUtils.cpp
new file mode 100755
index 0000000..5be8638
--- /dev/null
+++ b/mtp/MtpUtils.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <stdio.h>
+#include <time.h>
+
+#include <cutils/tztime.h>
+#include "MtpUtils.h"
+#include "MtpDebug.h"
+
+
+/*
+DateTime strings follow a compatible subset of the definition found in ISO 8601, and
+take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this
+representation, YYYY shall be replaced by the year, MM replaced by the month (01-12),
+DD replaced by the day (01-31), T is a constant character 'T' delimiting time from date,
+hh is replaced by the hour (00-23), mm is replaced by the minute (00-59), and ss by the
+second (00-59). The ".s" is optional, and represents tenths of a second.
+*/
+
+bool parseDateTime(const char* dateTime, time_t& outSeconds) {
+	int year, month, day, hour, minute, second;
+	struct tm tm;
+
+	if (sscanf(dateTime, "%04d%02d%02dT%02d%02d%02d",
+			&year, &month, &day, &hour, &minute, &second) != 6)
+		return false;
+	const char* tail = dateTime + 15;
+	// skip optional tenth of second
+	if (tail[0] == '.' && tail[1])
+		tail += 2;
+	//FIXME - support +/-hhmm
+	bool useUTC = (tail[0] == 'Z');
+
+	// hack to compute timezone
+	time_t dummy;
+	localtime_r(&dummy, &tm);
+
+	tm.tm_sec = second;
+	tm.tm_min = minute;
+	tm.tm_hour = hour;
+	tm.tm_mday = day;
+	tm.tm_mon = month - 1;  // mktime uses months in 0 - 11 range
+	tm.tm_year = year - 1900;
+	tm.tm_wday = 0;
+	tm.tm_isdst = -1;
+	//if (useUTC) {
+	outSeconds = mktime(&tm);
+	//}
+	/* mktime_tz is blocking :P
+	else {
+		outSeconds = mktime_tz(&tm, tm.tm_zone);
+	}
+	*/
+
+	return true;
+}
+
+void formatDateTime(time_t seconds, char* buffer, int bufferLength) {
+	struct tm tm;
+
+	localtime_r(&seconds, &tm);
+	snprintf(buffer, bufferLength, "%04d%02d%02dT%02d%02d%02d",
+		tm.tm_year + 1900, 
+		tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
+		tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
diff --git a/mtp/MtpUtils.h b/mtp/MtpUtils.h
new file mode 100755
index 0000000..2bca94b
--- /dev/null
+++ b/mtp/MtpUtils.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_UTILS_H
+#define _MTP_UTILS_H
+
+#include <stdint.h>
+
+bool parseDateTime(const char* dateTime, time_t& outSeconds);
+void formatDateTime(time_t seconds, char* buffer, int bufferLength);
+
+#endif // _MTP_UTILS_H
diff --git a/mtp/btree.cpp b/mtp/btree.cpp
new file mode 100755
index 0000000..3a5648d
--- /dev/null
+++ b/mtp/btree.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <utils/threads.h>
+#include "btree.hpp"
+#include "MtpDebug.h"
+
+// Constructor
+Tree::Tree(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name)
+	: Node(handle, parent, name), alreadyRead(false) {
+}
+
+// Destructor
+Tree::~Tree() {
+	for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+		delete it->second;
+}
+
+int Tree::getCount(void) {
+	int count = entries.size();
+	MTPD("node count: %d\n", count);
+	return count;
+}
+
+void Tree::addEntry(Node* node) {
+	if (node->Mtpid() == 0) {
+		MTPE("Tree::addEntry: not adding node with 0 handle.\n");
+		return;
+	}
+	if (node->Mtpid() == node->getMtpParentId()) {
+		MTPE("Tree::addEntry: not adding node with handle %u == parent.\n", node->Mtpid());
+		return;
+	}
+	entries[node->Mtpid()] = node;
+}
+
+Node* Tree::findEntryByName(std::string name) {
+	for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+	{
+		Node* node = it->second;
+		if (node->getName().compare(name) == 0 && node->Mtpid() > 0)
+			return node;
+	}
+	return NULL;
+}
+
+Node* Tree::findNode(MtpObjectHandle handle) {
+	std::map<MtpObjectHandle, Node*>::iterator it = entries.find(handle);	
+	if (it != entries.end())
+		return it->second;
+	return NULL;
+}
+
+void Tree::getmtpids(MtpObjectHandleList* mtpids) {
+	for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+		mtpids->push_back(it->second->Mtpid());
+}
+
+void Tree::deleteNode(MtpObjectHandle handle) {
+	std::map<MtpObjectHandle, Node*>::iterator it = entries.find(handle);	
+	if (it != entries.end()) {
+		delete it->second;
+		entries.erase(it);
+	}
+}
diff --git a/mtp/btree.hpp b/mtp/btree.hpp
new file mode 100755
index 0000000..b284e4f
--- /dev/null
+++ b/mtp/btree.hpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BTREE_HPP
+#define BTREE_HPP
+
+#include <vector>
+#include <map>
+#include "MtpTypes.h"
+
+// A directory entry
+class Node {
+	MtpObjectHandle handle;
+	MtpObjectHandle parent;
+	std::string name;	// name only without path
+
+public:
+	Node();
+	Node(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name);
+	virtual ~Node() {}
+
+	virtual bool isDir() const { return false; }
+
+	void rename(const std::string& newName);
+	MtpObjectHandle Mtpid() const;
+	MtpObjectHandle getMtpParentId() const;
+	const std::string& getName() const;
+
+	void addProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType);
+	void updateProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType);
+	void addProperties(const std::string& path, int storageID);
+	uint64_t getIntProperty(MtpPropertyCode property);
+	struct mtpProperty {
+		MtpPropertyCode property;
+		MtpDataType dataType;
+		uint64_t valueInt;
+		std::string valueStr;
+		mtpProperty() : property(0), dataType(0), valueInt(0) {}
+	};
+	std::vector<mtpProperty>& getMtpProps();
+	std::vector<mtpProperty> mtpProp;
+	const mtpProperty& getProperty(MtpPropertyCode property);
+};
+
+// A directory
+class Tree : public Node {
+	std::map<MtpObjectHandle, Node*> entries;
+	bool alreadyRead;
+public:
+	Tree(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name);
+	~Tree();
+
+	virtual bool isDir() const { return true; }
+
+	void addEntry(Node* node);
+	Node* findNode(MtpObjectHandle handle);
+	void getmtpids(MtpObjectHandleList* mtpids);
+	void deleteNode(MtpObjectHandle handle);
+	std::string getPath(Node* node);
+	int getMtpParentId() { return Node::getMtpParentId(); }
+	int getMtpParentId(Node* node);
+	Node* findEntryByName(std::string name);
+	int getCount();
+	bool wasAlreadyRead() const { return alreadyRead; }
+	void setAlreadyRead(bool b) { alreadyRead = b; }
+};
+
+#endif
diff --git a/mtp/mtp.h b/mtp/mtp.h
new file mode 100755
index 0000000..c329331
--- /dev/null
+++ b/mtp/mtp.h
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_H
+#define _MTP_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define MTP_STANDARD_VERSION            100
+
+// Container Types
+#define MTP_CONTAINER_TYPE_UNDEFINED    0
+#define MTP_CONTAINER_TYPE_COMMAND      1
+#define MTP_CONTAINER_TYPE_DATA         2
+#define MTP_CONTAINER_TYPE_RESPONSE     3
+#define MTP_CONTAINER_TYPE_EVENT        4
+
+// Container Offsets
+#define MTP_CONTAINER_LENGTH_OFFSET             0
+#define MTP_CONTAINER_TYPE_OFFSET               4
+#define MTP_CONTAINER_CODE_OFFSET               6
+#define MTP_CONTAINER_TRANSACTION_ID_OFFSET     8
+#define MTP_CONTAINER_PARAMETER_OFFSET          12
+#define MTP_CONTAINER_HEADER_SIZE               12
+
+// MTP Data Types
+#define MTP_TYPE_UNDEFINED      0x0000          // Undefined
+#define MTP_TYPE_INT8           0x0001          // Signed 8-bit integer
+#define MTP_TYPE_UINT8          0x0002          // Unsigned 8-bit integer
+#define MTP_TYPE_INT16          0x0003          // Signed 16-bit integer
+#define MTP_TYPE_UINT16         0x0004          // Unsigned 16-bit integer
+#define MTP_TYPE_INT32          0x0005          // Signed 32-bit integer
+#define MTP_TYPE_UINT32         0x0006          // Unsigned 32-bit integer
+#define MTP_TYPE_INT64          0x0007          // Signed 64-bit integer
+#define MTP_TYPE_UINT64         0x0008          // Unsigned 64-bit integer
+#define MTP_TYPE_INT128         0x0009          // Signed 128-bit integer
+#define MTP_TYPE_UINT128        0x000A          // Unsigned 128-bit integer
+#define MTP_TYPE_AINT8          0x4001          // Array of signed 8-bit integers
+#define MTP_TYPE_AUINT8         0x4002          // Array of unsigned 8-bit integers
+#define MTP_TYPE_AINT16         0x4003          // Array of signed 16-bit integers
+#define MTP_TYPE_AUINT16        0x4004          // Array of unsigned 16-bit integers
+#define MTP_TYPE_AINT32         0x4005          // Array of signed 32-bit integers
+#define MTP_TYPE_AUINT32        0x4006          // Array of unsigned 32-bit integers
+#define MTP_TYPE_AINT64         0x4007          // Array of signed 64-bit integers
+#define MTP_TYPE_AUINT64        0x4008          // Array of unsigned 64-bit integers
+#define MTP_TYPE_AINT128        0x4009          // Array of signed 128-bit integers
+#define MTP_TYPE_AUINT128       0x400A          // Array of unsigned 128-bit integers
+#define MTP_TYPE_STR            0xFFFF          // Variable-length Unicode string
+
+// MTP Format Codes
+#define MTP_FORMAT_UNDEFINED                            0x3000   // Undefined object
+#define MTP_FORMAT_ASSOCIATION                          0x3001   // Association (for example, a folder)
+#define MTP_FORMAT_SCRIPT                               0x3002   // Device model-specific script
+#define MTP_FORMAT_EXECUTABLE                           0x3003   // Device model-specific binary executable
+#define MTP_FORMAT_TEXT                                 0x3004   // Text file
+#define MTP_FORMAT_HTML                                 0x3005   // Hypertext Markup Language file (text)
+#define MTP_FORMAT_DPOF                                 0x3006   // Digital Print Order Format file (text)
+#define MTP_FORMAT_AIFF                                 0x3007   // Audio clip
+#define MTP_FORMAT_WAV                                  0x3008   // Audio clip
+#define MTP_FORMAT_MP3                                  0x3009   // Audio clip
+#define MTP_FORMAT_AVI                                  0x300A   // Video clip
+#define MTP_FORMAT_MPEG                                 0x300B   // Video clip
+#define MTP_FORMAT_ASF                                  0x300C   // Microsoft Advanced Streaming Format (video)
+#define MTP_FORMAT_DEFINED                              0x3800   // Unknown image object
+#define MTP_FORMAT_EXIF_JPEG                            0x3801   // Exchangeable File Format, JEIDA standard
+#define MTP_FORMAT_TIFF_EP                              0x3802   // Tag Image File Format for Electronic Photography
+#define MTP_FORMAT_FLASHPIX                             0x3803   // Structured Storage Image Format
+#define MTP_FORMAT_BMP                                  0x3804   // Microsoft Windows Bitmap file
+#define MTP_FORMAT_CIFF                                 0x3805   // Canon Camera Image File Format
+#define MTP_FORMAT_GIF                                  0x3807   // Graphics Interchange Format
+#define MTP_FORMAT_JFIF                                 0x3808   // JPEG File Interchange Format
+#define MTP_FORMAT_CD                                   0x3809   // PhotoCD Image Pac
+#define MTP_FORMAT_PICT                                 0x380A   // Quickdraw Image Format
+#define MTP_FORMAT_PNG                                  0x380B   // Portable Network Graphics
+#define MTP_FORMAT_TIFF                                 0x380D   // Tag Image File Format
+#define MTP_FORMAT_TIFF_IT                              0x380E   // Tag Image File Format for Information Technology (graphic arts)
+#define MTP_FORMAT_JP2                                  0x380F   // JPEG2000 Baseline File Format
+#define MTP_FORMAT_JPX                                  0x3810   // JPEG2000 Extended File Format
+#define MTP_FORMAT_UNDEFINED_FIRMWARE                   0xB802
+#define MTP_FORMAT_WINDOWS_IMAGE_FORMAT                 0xB881
+#define MTP_FORMAT_UNDEFINED_AUDIO                      0xB900
+#define MTP_FORMAT_WMA                                  0xB901
+#define MTP_FORMAT_OGG                                  0xB902
+#define MTP_FORMAT_AAC                                  0xB903
+#define MTP_FORMAT_AUDIBLE                              0xB904
+#define MTP_FORMAT_FLAC                                 0xB906
+#define MTP_FORMAT_UNDEFINED_VIDEO                      0xB980
+#define MTP_FORMAT_WMV                                  0xB981
+#define MTP_FORMAT_MP4_CONTAINER                        0xB982  // ISO 14496-1
+#define MTP_FORMAT_MP2                                  0xB983
+#define MTP_FORMAT_3GP_CONTAINER                        0xB984  // 3GPP file format. Details: http://www.3gpp.org/ftp/Specs/html-info/26244.htm (page title - \u201cTransparent end-to-end packet switched streaming service, 3GPP file format\u201d).
+#define MTP_FORMAT_UNDEFINED_COLLECTION                 0xBA00
+#define MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM            0xBA01
+#define MTP_FORMAT_ABSTRACT_IMAGE_ALBUM                 0xBA02
+#define MTP_FORMAT_ABSTRACT_AUDIO_ALBUM                 0xBA03
+#define MTP_FORMAT_ABSTRACT_VIDEO_ALBUM                 0xBA04
+#define MTP_FORMAT_ABSTRACT_AV_PLAYLIST                 0xBA05
+#define MTP_FORMAT_ABSTRACT_CONTACT_GROUP               0xBA06
+#define MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER              0xBA07
+#define MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION        0xBA08
+#define MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST              0xBA09
+#define MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST              0xBA0A
+#define MTP_FORMAT_ABSTRACT_MEDIACAST                   0xBA0B // For use with mediacasts; references multimedia enclosures of RSS feeds or episodic content
+#define MTP_FORMAT_WPL_PLAYLIST                         0xBA10
+#define MTP_FORMAT_M3U_PLAYLIST                         0xBA11
+#define MTP_FORMAT_MPL_PLAYLIST                         0xBA12
+#define MTP_FORMAT_ASX_PLAYLIST                         0xBA13
+#define MTP_FORMAT_PLS_PLAYLIST                         0xBA14
+#define MTP_FORMAT_UNDEFINED_DOCUMENT                   0xBA80
+#define MTP_FORMAT_ABSTRACT_DOCUMENT                    0xBA81
+#define MTP_FORMAT_XML_DOCUMENT                         0xBA82
+#define MTP_FORMAT_MS_WORD_DOCUMENT                     0xBA83
+#define MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT           0xBA84
+#define MTP_FORMAT_MS_EXCEL_SPREADSHEET                 0xBA85
+#define MTP_FORMAT_MS_POWERPOINT_PRESENTATION           0xBA86
+#define MTP_FORMAT_UNDEFINED_MESSAGE                    0xBB00
+#define MTP_FORMAT_ABSTRACT_MESSSAGE                    0xBB01
+#define MTP_FORMAT_UNDEFINED_CONTACT                    0xBB80
+#define MTP_FORMAT_ABSTRACT_CONTACT                     0xBB81
+#define MTP_FORMAT_VCARD_2                              0xBB82
+
+// MTP Object Property Codes
+#define MTP_PROPERTY_STORAGE_ID                             0xDC01
+#define MTP_PROPERTY_OBJECT_FORMAT                          0xDC02
+#define MTP_PROPERTY_PROTECTION_STATUS                      0xDC03
+#define MTP_PROPERTY_OBJECT_SIZE                            0xDC04
+#define MTP_PROPERTY_ASSOCIATION_TYPE                       0xDC05
+#define MTP_PROPERTY_ASSOCIATION_DESC                       0xDC06
+#define MTP_PROPERTY_OBJECT_FILE_NAME                       0xDC07
+#define MTP_PROPERTY_DATE_CREATED                           0xDC08
+#define MTP_PROPERTY_DATE_MODIFIED                          0xDC09
+#define MTP_PROPERTY_KEYWORDS                               0xDC0A
+#define MTP_PROPERTY_PARENT_OBJECT                          0xDC0B
+#define MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS                0xDC0C
+#define MTP_PROPERTY_HIDDEN                                 0xDC0D
+#define MTP_PROPERTY_SYSTEM_OBJECT                          0xDC0E
+#define MTP_PROPERTY_PERSISTENT_UID                         0xDC41
+#define MTP_PROPERTY_SYNC_ID                                0xDC42
+#define MTP_PROPERTY_PROPERTY_BAG                           0xDC43
+#define MTP_PROPERTY_NAME                                   0xDC44
+#define MTP_PROPERTY_CREATED_BY                             0xDC45
+#define MTP_PROPERTY_ARTIST                                 0xDC46
+#define MTP_PROPERTY_DATE_AUTHORED                          0xDC47
+#define MTP_PROPERTY_DESCRIPTION                            0xDC48
+#define MTP_PROPERTY_URL_REFERENCE                          0xDC49
+#define MTP_PROPERTY_LANGUAGE_LOCALE                        0xDC4A
+#define MTP_PROPERTY_COPYRIGHT_INFORMATION                  0xDC4B
+#define MTP_PROPERTY_SOURCE                                 0xDC4C
+#define MTP_PROPERTY_ORIGIN_LOCATION                        0xDC4D
+#define MTP_PROPERTY_DATE_ADDED                             0xDC4E
+#define MTP_PROPERTY_NON_CONSUMABLE                         0xDC4F
+#define MTP_PROPERTY_CORRUPT_UNPLAYABLE                     0xDC50
+#define MTP_PROPERTY_PRODUCER_SERIAL_NUMBER                 0xDC51
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT           0xDC81
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE             0xDC82
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT           0xDC83
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH            0xDC84
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION         0xDC85
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA             0xDC86
+#define MTP_PROPERTY_WIDTH                                  0xDC87
+#define MTP_PROPERTY_HEIGHT                                 0xDC88
+#define MTP_PROPERTY_DURATION                               0xDC89
+#define MTP_PROPERTY_RATING                                 0xDC8A
+#define MTP_PROPERTY_TRACK                                  0xDC8B
+#define MTP_PROPERTY_GENRE                                  0xDC8C
+#define MTP_PROPERTY_CREDITS                                0xDC8D
+#define MTP_PROPERTY_LYRICS                                 0xDC8E
+#define MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID                0xDC8F
+#define MTP_PROPERTY_PRODUCED_BY                            0xDC90
+#define MTP_PROPERTY_USE_COUNT                              0xDC91
+#define MTP_PROPERTY_SKIP_COUNT                             0xDC92
+#define MTP_PROPERTY_LAST_ACCESSED                          0xDC93
+#define MTP_PROPERTY_PARENTAL_RATING                        0xDC94
+#define MTP_PROPERTY_META_GENRE                             0xDC95
+#define MTP_PROPERTY_COMPOSER                               0xDC96
+#define MTP_PROPERTY_EFFECTIVE_RATING                       0xDC97
+#define MTP_PROPERTY_SUBTITLE                               0xDC98
+#define MTP_PROPERTY_ORIGINAL_RELEASE_DATE                  0xDC99
+#define MTP_PROPERTY_ALBUM_NAME                             0xDC9A
+#define MTP_PROPERTY_ALBUM_ARTIST                           0xDC9B
+#define MTP_PROPERTY_MOOD                                   0xDC9C
+#define MTP_PROPERTY_DRM_STATUS                             0xDC9D
+#define MTP_PROPERTY_SUB_DESCRIPTION                        0xDC9E
+#define MTP_PROPERTY_IS_CROPPED                             0xDCD1
+#define MTP_PROPERTY_IS_COLOUR_CORRECTED                    0xDCD2
+#define MTP_PROPERTY_IMAGE_BIT_DEPTH                        0xDCD3
+#define MTP_PROPERTY_F_NUMBER                               0xDCD4
+#define MTP_PROPERTY_EXPOSURE_TIME                          0xDCD5
+#define MTP_PROPERTY_EXPOSURE_INDEX                         0xDCD6
+#define MTP_PROPERTY_TOTAL_BITRATE                          0xDE91
+#define MTP_PROPERTY_BITRATE_TYPE                           0xDE92
+#define MTP_PROPERTY_SAMPLE_RATE                            0xDE93
+#define MTP_PROPERTY_NUMBER_OF_CHANNELS                     0xDE94
+#define MTP_PROPERTY_AUDIO_BIT_DEPTH                        0xDE95
+#define MTP_PROPERTY_SCAN_TYPE                              0xDE97
+#define MTP_PROPERTY_AUDIO_WAVE_CODEC                       0xDE99
+#define MTP_PROPERTY_AUDIO_BITRATE                          0xDE9A
+#define MTP_PROPERTY_VIDEO_FOURCC_CODEC                     0xDE9B
+#define MTP_PROPERTY_VIDEO_BITRATE                          0xDE9C
+#define MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS            0xDE9D
+#define MTP_PROPERTY_KEYFRAME_DISTANCE                      0xDE9E
+#define MTP_PROPERTY_BUFFER_SIZE                            0xDE9F
+#define MTP_PROPERTY_ENCODING_QUALITY                       0xDEA0
+#define MTP_PROPERTY_ENCODING_PROFILE                       0xDEA1
+#define MTP_PROPERTY_DISPLAY_NAME                           0xDCE0
+#define MTP_PROPERTY_BODY_TEXT                              0xDCE1
+#define MTP_PROPERTY_SUBJECT                                0xDCE2
+#define MTP_PROPERTY_PRIORITY                               0xDCE3
+#define MTP_PROPERTY_GIVEN_NAME                             0xDD00
+#define MTP_PROPERTY_MIDDLE_NAMES                           0xDD01
+#define MTP_PROPERTY_FAMILY_NAME                            0xDD02
+#define MTP_PROPERTY_PREFIX                                 0xDD03
+#define MTP_PROPERTY_SUFFIX                                 0xDD04
+#define MTP_PROPERTY_PHONETIC_GIVEN_NAME                    0xDD05
+#define MTP_PROPERTY_PHONETIC_FAMILY_NAME                   0xDD06
+#define MTP_PROPERTY_EMAIL_PRIMARY                          0xDD07
+#define MTP_PROPERTY_EMAIL_PERSONAL_1                       0xDD08
+#define MTP_PROPERTY_EMAIL_PERSONAL_2                       0xDD09
+#define MTP_PROPERTY_EMAIL_BUSINESS_1                       0xDD0A
+#define MTP_PROPERTY_EMAIL_BUSINESS_2                       0xDD0B
+#define MTP_PROPERTY_EMAIL_OTHERS                           0xDD0C
+#define MTP_PROPERTY_PHONE_NUMBER_PRIMARY                   0xDD0D
+#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL                  0xDD0E
+#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2                0xDD0F
+#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS                  0xDD10
+#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2                0xDD11
+#define MTP_PROPERTY_PHONE_NUMBER_MOBILE                    0xDD12
+#define MTP_PROPERTY_PHONE_NUMBER_MOBILE_2                  0xDD13
+#define MTP_PROPERTY_FAX_NUMBER_PRIMARY                     0xDD14
+#define MTP_PROPERTY_FAX_NUMBER_PERSONAL                    0xDD15
+#define MTP_PROPERTY_FAX_NUMBER_BUSINESS                    0xDD16
+#define MTP_PROPERTY_PAGER_NUMBER                           0xDD17
+#define MTP_PROPERTY_PHONE_NUMBER_OTHERS                    0xDD18
+#define MTP_PROPERTY_PRIMARY_WEB_ADDRESS                    0xDD19
+#define MTP_PROPERTY_PERSONAL_WEB_ADDRESS                   0xDD1A
+#define MTP_PROPERTY_BUSINESS_WEB_ADDRESS                   0xDD1B
+#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS              0xDD1C
+#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2            0xDD1D
+#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3            0xDD1E
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL           0xDD1F
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1         0xDD20
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2         0xDD21
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY           0xDD22
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION         0xDD23
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE    0xDD24
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY        0xDD25
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL           0xDD26
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1         0xDD27
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2         0xDD28
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY           0xDD29
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION         0xDD2A
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE    0xDD2B
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY        0xDD2C
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL              0xDD2D
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1            0xDD2E
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2            0xDD2F
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY              0xDD30
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION            0xDD31
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE       0xDD32
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY           0xDD33
+#define MTP_PROPERTY_ORGANIZATION_NAME                      0xDD34
+#define MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME             0xDD35
+#define MTP_PROPERTY_ROLE                                   0xDD36
+#define MTP_PROPERTY_BIRTHDATE                              0xDD37
+#define MTP_PROPERTY_MESSAGE_TO                             0xDD40
+#define MTP_PROPERTY_MESSAGE_CC                             0xDD41
+#define MTP_PROPERTY_MESSAGE_BCC                            0xDD42
+#define MTP_PROPERTY_MESSAGE_READ                           0xDD43
+#define MTP_PROPERTY_MESSAGE_RECEIVED_TIME                  0xDD44
+#define MTP_PROPERTY_MESSAGE_SENDER                         0xDD45
+#define MTP_PROPERTY_ACTIVITY_BEGIN_TIME                    0xDD50
+#define MTP_PROPERTY_ACTIVITY_END_TIME                      0xDD51
+#define MTP_PROPERTY_ACTIVITY_LOCATION                      0xDD52
+#define MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES            0xDD54
+#define MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES            0xDD55
+#define MTP_PROPERTY_ACTIVITY_RESOURCES                     0xDD56
+#define MTP_PROPERTY_ACTIVITY_ACCEPTED                      0xDD57
+#define MTP_PROPERTY_ACTIVITY_TENTATIVE                     0xDD58
+#define MTP_PROPERTY_ACTIVITY_DECLINED                      0xDD59
+#define MTP_PROPERTY_ACTIVITY_REMAINDER_TIME                0xDD5A
+#define MTP_PROPERTY_ACTIVITY_OWNER                         0xDD5B
+#define MTP_PROPERTY_ACTIVITY_STATUS                        0xDD5C
+#define MTP_PROPERTY_OWNER                                  0xDD5D
+#define MTP_PROPERTY_EDITOR                                 0xDD5E
+#define MTP_PROPERTY_WEBMASTER                              0xDD5F
+#define MTP_PROPERTY_URL_SOURCE                             0xDD60
+#define MTP_PROPERTY_URL_DESTINATION                        0xDD61
+#define MTP_PROPERTY_TIME_BOOKMARK                          0xDD62
+#define MTP_PROPERTY_OBJECT_BOOKMARK                        0xDD63
+#define MTP_PROPERTY_BYTE_BOOKMARK                          0xDD64
+#define MTP_PROPERTY_LAST_BUILD_DATE                        0xDD70
+#define MTP_PROPERTY_TIME_TO_LIVE                           0xDD71
+#define MTP_PROPERTY_MEDIA_GUID                             0xDD72
+
+// MTP Device Property Codes
+#define MTP_DEVICE_PROPERTY_UNDEFINED                       0x5000
+#define MTP_DEVICE_PROPERTY_BATTERY_LEVEL                   0x5001
+#define MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE                 0x5002
+#define MTP_DEVICE_PROPERTY_IMAGE_SIZE                      0x5003
+#define MTP_DEVICE_PROPERTY_COMPRESSION_SETTING             0x5004
+#define MTP_DEVICE_PROPERTY_WHITE_BALANCE                   0x5005
+#define MTP_DEVICE_PROPERTY_RGB_GAIN                        0x5006
+#define MTP_DEVICE_PROPERTY_F_NUMBER                        0x5007
+#define MTP_DEVICE_PROPERTY_FOCAL_LENGTH                    0x5008
+#define MTP_DEVICE_PROPERTY_FOCUS_DISTANCE                  0x5009
+#define MTP_DEVICE_PROPERTY_FOCUS_MODE                      0x500A
+#define MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE          0x500B
+#define MTP_DEVICE_PROPERTY_FLASH_MODE                      0x500C
+#define MTP_DEVICE_PROPERTY_EXPOSURE_TIME                   0x500D
+#define MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE           0x500E
+#define MTP_DEVICE_PROPERTY_EXPOSURE_INDEX                  0x500F
+#define MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION      0x5010
+#define MTP_DEVICE_PROPERTY_DATETIME                        0x5011
+#define MTP_DEVICE_PROPERTY_CAPTURE_DELAY                   0x5012
+#define MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE              0x5013
+#define MTP_DEVICE_PROPERTY_CONTRAST                        0x5014
+#define MTP_DEVICE_PROPERTY_SHARPNESS                       0x5015
+#define MTP_DEVICE_PROPERTY_DIGITAL_ZOOM                    0x5016
+#define MTP_DEVICE_PROPERTY_EFFECT_MODE                     0x5017
+#define MTP_DEVICE_PROPERTY_BURST_NUMBER                    0x5018
+#define MTP_DEVICE_PROPERTY_BURST_INTERVAL                  0x5019
+#define MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER                0x501A
+#define MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL              0x501B
+#define MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE             0x501C
+#define MTP_DEVICE_PROPERTY_UPLOAD_URL                      0x501D
+#define MTP_DEVICE_PROPERTY_ARTIST                          0x501E
+#define MTP_DEVICE_PROPERTY_COPYRIGHT_INFO                  0x501F
+#define MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER         0xD401
+#define MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME            0xD402
+#define MTP_DEVICE_PROPERTY_VOLUME                          0xD403
+#define MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED       0xD404
+#define MTP_DEVICE_PROPERTY_DEVICE_ICON                     0xD405
+#define MTP_DEVICE_PROPERTY_PLAYBACK_RATE                   0xD410
+#define MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT                 0xD411
+#define MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX        0xD412
+#define MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO  0xD406
+#define MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE           0xD407
+
+// MTP Operation Codes
+#define MTP_OPERATION_GET_DEVICE_INFO                       0x1001
+#define MTP_OPERATION_OPEN_SESSION                          0x1002
+#define MTP_OPERATION_CLOSE_SESSION                         0x1003
+#define MTP_OPERATION_GET_STORAGE_IDS                       0x1004
+#define MTP_OPERATION_GET_STORAGE_INFO                      0x1005
+#define MTP_OPERATION_GET_NUM_OBJECTS                       0x1006
+#define MTP_OPERATION_GET_OBJECT_HANDLES                    0x1007
+#define MTP_OPERATION_GET_OBJECT_INFO                       0x1008
+#define MTP_OPERATION_GET_OBJECT                            0x1009
+#define MTP_OPERATION_GET_THUMB                             0x100A
+#define MTP_OPERATION_DELETE_OBJECT                         0x100B
+#define MTP_OPERATION_SEND_OBJECT_INFO                      0x100C
+#define MTP_OPERATION_SEND_OBJECT                           0x100D
+#define MTP_OPERATION_INITIATE_CAPTURE                      0x100E
+#define MTP_OPERATION_FORMAT_STORE                          0x100F
+#define MTP_OPERATION_RESET_DEVICE                          0x1010
+#define MTP_OPERATION_SELF_TEST                             0x1011
+#define MTP_OPERATION_SET_OBJECT_PROTECTION                 0x1012
+#define MTP_OPERATION_POWER_DOWN                            0x1013
+#define MTP_OPERATION_GET_DEVICE_PROP_DESC                  0x1014
+#define MTP_OPERATION_GET_DEVICE_PROP_VALUE                 0x1015
+#define MTP_OPERATION_SET_DEVICE_PROP_VALUE                 0x1016
+#define MTP_OPERATION_RESET_DEVICE_PROP_VALUE               0x1017
+#define MTP_OPERATION_TERMINATE_OPEN_CAPTURE                0x1018
+#define MTP_OPERATION_MOVE_OBJECT                           0x1019
+#define MTP_OPERATION_COPY_OBJECT                           0x101A
+#define MTP_OPERATION_GET_PARTIAL_OBJECT                    0x101B
+#define MTP_OPERATION_INITIATE_OPEN_CAPTURE                 0x101C
+#define MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED            0x9801
+#define MTP_OPERATION_GET_OBJECT_PROP_DESC                  0x9802
+#define MTP_OPERATION_GET_OBJECT_PROP_VALUE                 0x9803
+#define MTP_OPERATION_SET_OBJECT_PROP_VALUE                 0x9804
+#define MTP_OPERATION_GET_OBJECT_PROP_LIST                  0x9805
+#define MTP_OPERATION_SET_OBJECT_PROP_LIST                  0x9806
+#define MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC          0x9807
+#define MTP_OPERATION_SEND_OBJECT_PROP_LIST                 0x9808
+#define MTP_OPERATION_GET_OBJECT_REFERENCES                 0x9810
+#define MTP_OPERATION_SET_OBJECT_REFERENCES                 0x9811
+#define MTP_OPERATION_SKIP                                  0x9820
+
+// Android extensions for direct file IO
+
+// Same as GetPartialObject, but with 64 bit offset
+#define MTP_OPERATION_GET_PARTIAL_OBJECT_64                 0x95C1
+// Same as GetPartialObject64, but copying host to device
+#define MTP_OPERATION_SEND_PARTIAL_OBJECT                   0x95C2
+// Truncates file to 64 bit length
+#define MTP_OPERATION_TRUNCATE_OBJECT                       0x95C3
+// Must be called before using SendPartialObject and TruncateObject
+#define MTP_OPERATION_BEGIN_EDIT_OBJECT                     0x95C4
+// Called to commit changes made by SendPartialObject and TruncateObject
+#define MTP_OPERATION_END_EDIT_OBJECT                       0x95C5
+
+// MTP Response Codes
+#define MTP_RESPONSE_UNDEFINED                                  0x2000
+#define MTP_RESPONSE_OK                                         0x2001
+#define MTP_RESPONSE_GENERAL_ERROR                              0x2002
+#define MTP_RESPONSE_SESSION_NOT_OPEN                           0x2003
+#define MTP_RESPONSE_INVALID_TRANSACTION_ID                     0x2004
+#define MTP_RESPONSE_OPERATION_NOT_SUPPORTED                    0x2005
+#define MTP_RESPONSE_PARAMETER_NOT_SUPPORTED                    0x2006
+#define MTP_RESPONSE_INCOMPLETE_TRANSFER                        0x2007
+#define MTP_RESPONSE_INVALID_STORAGE_ID                         0x2008
+#define MTP_RESPONSE_INVALID_OBJECT_HANDLE                      0x2009
+#define MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED                  0x200A
+#define MTP_RESPONSE_INVALID_OBJECT_FORMAT_CODE                 0x200B
+#define MTP_RESPONSE_STORAGE_FULL                               0x200C
+#define MTP_RESPONSE_OBJECT_WRITE_PROTECTED                     0x200D
+#define MTP_RESPONSE_STORE_READ_ONLY                            0x200E
+#define MTP_RESPONSE_ACCESS_DENIED                              0x200F
+#define MTP_RESPONSE_NO_THUMBNAIL_PRESENT                       0x2010
+#define MTP_RESPONSE_SELF_TEST_FAILED                           0x2011
+#define MTP_RESPONSE_PARTIAL_DELETION                           0x2012
+#define MTP_RESPONSE_STORE_NOT_AVAILABLE                        0x2013
+#define MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED        0x2014
+#define MTP_RESPONSE_NO_VALID_OBJECT_INFO                       0x2015
+#define MTP_RESPONSE_INVALID_CODE_FORMAT                        0x2016
+#define MTP_RESPONSE_UNKNOWN_VENDOR_CODE                        0x2017
+#define MTP_RESPONSE_CAPTURE_ALREADY_TERMINATED                 0x2018
+#define MTP_RESPONSE_DEVICE_BUSY                                0x2019
+#define MTP_RESPONSE_INVALID_PARENT_OBJECT                      0x201A
+#define MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT                 0x201B
+#define MTP_RESPONSE_INVALID_DEVICE_PROP_VALUE                  0x201C
+#define MTP_RESPONSE_INVALID_PARAMETER                          0x201D
+#define MTP_RESPONSE_SESSION_ALREADY_OPEN                       0x201E
+#define MTP_RESPONSE_TRANSACTION_CANCELLED                      0x201F
+#define MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED   0x2020
+#define MTP_RESPONSE_INVALID_OBJECT_PROP_CODE                   0xA801
+#define MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT                 0xA802
+#define MTP_RESPONSE_INVALID_OBJECT_PROP_VALUE                  0xA803
+#define MTP_RESPONSE_INVALID_OBJECT_REFERENCE                   0xA804
+#define MTP_RESPONSE_GROUP_NOT_SUPPORTED                        0xA805
+#define MTP_RESPONSE_INVALID_DATASET                            0xA806
+#define MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED         0xA807
+#define MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED         0xA808
+#define MTP_RESPONSE_OBJECT_TOO_LARGE                           0xA809
+#define MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED                  0xA80A
+
+// MTP Event Codes
+#define MTP_EVENT_UNDEFINED                         0x4000
+#define MTP_EVENT_CANCEL_TRANSACTION                0x4001
+#define MTP_EVENT_OBJECT_ADDED                      0x4002
+#define MTP_EVENT_OBJECT_REMOVED                    0x4003
+#define MTP_EVENT_STORE_ADDED                       0x4004
+#define MTP_EVENT_STORE_REMOVED                     0x4005
+#define MTP_EVENT_DEVICE_PROP_CHANGED               0x4006
+#define MTP_EVENT_OBJECT_INFO_CHANGED               0x4007
+#define MTP_EVENT_DEVICE_INFO_CHANGED               0x4008
+#define MTP_EVENT_REQUEST_OBJECT_TRANSFER           0x4009
+#define MTP_EVENT_STORE_FULL                        0x400A
+#define MTP_EVENT_DEVICE_RESET                      0x400B
+#define MTP_EVENT_STORAGE_INFO_CHANGED              0x400C
+#define MTP_EVENT_CAPTURE_COMPLETE                  0x400D
+#define MTP_EVENT_UNREPORTED_STATUS                 0x400E
+#define MTP_EVENT_OBJECT_PROP_CHANGED               0xC801
+#define MTP_EVENT_OBJECT_PROP_DESC_CHANGED          0xC802
+#define MTP_EVENT_OBJECT_REFERENCES_CHANGED         0xC803
+
+// Storage Type
+#define MTP_STORAGE_FIXED_ROM                       0x0001
+#define MTP_STORAGE_REMOVABLE_ROM                   0x0002
+#define MTP_STORAGE_FIXED_RAM                       0x0003
+#define MTP_STORAGE_REMOVABLE_RAM                   0x0004
+
+// Storage File System
+#define MTP_STORAGE_FILESYSTEM_FLAT                 0x0001
+#define MTP_STORAGE_FILESYSTEM_HIERARCHICAL         0x0002
+#define MTP_STORAGE_FILESYSTEM_DCF                  0x0003
+
+// Storage Access Capability
+#define MTP_STORAGE_READ_WRITE                      0x0000
+#define MTP_STORAGE_READ_ONLY_WITHOUT_DELETE        0x0001
+#define MTP_STORAGE_READ_ONLY_WITH_DELETE           0x0002
+
+// Association Type
+#define MTP_ASSOCIATION_TYPE_UNDEFINED              0x0000
+#define MTP_ASSOCIATION_TYPE_GENERIC_FOLDER         0x0001
+
+// Supported Playback Formats
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED 0x3000
+/** Format code for associations (folders and directories) */
+#define SUPPORTED_PLAYBACK_FORMAT_ASSOCIATION 0x3001
+/** Format code for script files */
+#define SUPPORTED_PLAYBACK_FORMAT_SCRIPT 0x3002
+/** Format code for executable files */
+#define SUPPORTED_PLAYBACK_FORMAT_EXECUTABLE 0x3003
+/** Format code for text files */
+#define SUPPORTED_PLAYBACK_FORMAT_TEXT 0x3004
+/** Format code for HTML files */
+#define SUPPORTED_PLAYBACK_FORMAT_HTML 0x3005
+/** Format code for DPOF files */
+#define SUPPORTED_PLAYBACK_FORMAT_DPOF 0x3006
+/** Format code for AIFF audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_AIFF 0x3007
+/** Format code for WAV audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_WAV 0x3008
+/** Format code for MP3 audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_MP3 0x3009
+/** Format code for AVI video files */
+#define SUPPORTED_PLAYBACK_FORMAT_AVI 0x300A
+/** Format code for MPEG video files */
+#define SUPPORTED_PLAYBACK_FORMAT_MPEG 0x300B
+/** Format code for ASF files */
+#define SUPPORTED_PLAYBACK_FORMAT_ASF 0x300C
+/** Format code for JPEG image files */
+#define SUPPORTED_PLAYBACK_FORMAT_EXIF_JPEG 0x3801
+/** Format code for TIFF EP image files */
+#define SUPPORTED_PLAYBACK_FORMAT_TIFF_EP 0x3802
+/** Format code for BMP image files */
+#define SUPPORTED_PLAYBACK_FORMAT_BMP 0x3804
+/** Format code for GIF image files */
+#define SUPPORTED_PLAYBACK_FORMAT_GIF 0x3807
+/** Format code for JFIF image files */
+#define SUPPORTED_PLAYBACK_FORMAT_JFIF 0x3808
+/** Format code for PICT image files */
+#define SUPPORTED_PLAYBACK_FORMAT_PICT 0x380A
+/** Format code for PNG image files */
+#define SUPPORTED_PLAYBACK_FORMAT_PNG 0x380B
+/** Format code for TIFF image files */
+#define SUPPORTED_PLAYBACK_FORMAT_TIFF 0x380D
+/** Format code for JP2 files */
+#define SUPPORTED_PLAYBACK_FORMAT_JP2 0x380F
+/** Format code for JPX files */
+#define SUPPORTED_PLAYBACK_FORMAT_JPX 0x3810
+/** Format code for firmware files */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_FIRMWARE 0xB802
+/** Format code for Windows image files */
+#define SUPPORTED_PLAYBACK_FORMAT_WINDOWS_IMAGE_FORMAT 0xB881
+/** Format code for undefined audio files files */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_AUDIO 0xB900
+/** Format code for WMA audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_WMA 0xB901
+/** Format code for OGG audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_OGG 0xB902
+/** Format code for AAC audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_AAC 0xB903
+/** Format code for Audible audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_AUDIBLE 0xB904
+/** Format code for FLAC audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_FLAC 0xB906
+/** Format code for undefined video files */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_VIDEO 0xB980
+/** Format code for WMV video files */
+#define SUPPORTED_PLAYBACK_FORMAT_WMV 0xB981
+/** Format code for MP4 files */
+#define SUPPORTED_PLAYBACK_FORMAT_MP4_CONTAINER 0xB982
+/** Format code for MP2 files */
+#define SUPPORTED_PLAYBACK_FORMAT_MP2 0xB983
+/** Format code for 3GP files */
+#define SUPPORTED_PLAYBACK_FORMAT_3GP_CONTAINER 0xB984
+/** Format code for undefined collections */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_COLLECTION 0xBA00
+/** Format code for multimedia albums */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM 0xBA01
+/** Format code for image albums */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_IMAGE_ALBUM 0xBA02
+/** Format code for audio albums */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_AUDIO_ALBUM 0xBA03
+/** Format code for video albums */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_VIDEO_ALBUM 0xBA04
+/** Format code for abstract AV playlists */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_AV_PLAYLIST 0xBA05
+/** Format code for abstract audio playlists */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_AUDIO_PLAYLIST 0xBA09
+/** Format code for abstract video playlists */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_VIDEO_PLAYLIST 0xBA0A
+/** Format code for abstract mediacasts */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_MEDIACAST 0xBA0B
+/** Format code for WPL playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_WPL_PLAYLIST 0xBA10
+/** Format code for M3u playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_M3U_PLAYLIST 0xBA11
+/** Format code for MPL playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_MPL_PLAYLIST 0xBA12
+/** Format code for ASX playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_ASX_PLAYLIST 0xBA13
+/** Format code for PLS playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_PLS_PLAYLIST 0xBA14
+/** Format code for undefined document files */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_DOCUMENT 0xBA80
+/** Format code for abstract documents */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_DOCUMENT 0xBA81
+/** Format code for XML documents */
+#define SUPPORTED_PLAYBACK_FORMAT_XML_DOCUMENT 0xBA82
+/** Format code for MS Word documents */
+#define SUPPORTED_PLAYBACK_FORMAT_MS_WORD_DOCUMENT 0xBA83
+/** Format code for MS Excel spreadsheets */
+#define SUPPORTED_PLAYBACK_FORMAT_MS_EXCEL_SPREADSHEET 0xBA85
+/** Format code for MS PowerPoint presentatiosn */
+#define SUPPORTED_PLAYBACK_FORMAT_MS_POWERPOINT_PRESENTATION 0xBA86
+
+#endif // _MTP_H
diff --git a/mtp/mtp_MtpDatabase.cpp b/mtp/mtp_MtpDatabase.cpp
new file mode 100755
index 0000000..05bb5d9
--- /dev/null
+++ b/mtp/mtp_MtpDatabase.cpp
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string>
+#include <map>
+#include <libgen.h>
+#include <cutils/properties.h>
+
+#include "MtpDatabase.h"
+#include "MtpStorage.h"
+#include "MtpDataPacket.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpDebug.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+#include "mtp.h"
+#include "mtp_MtpDatabase.hpp"
+//#include "btree.hpp"
+
+MyMtpDatabase::MyMtpDatabase()
+{
+	storagenum = 0;
+	count = -1;
+}
+
+MyMtpDatabase::~MyMtpDatabase() {
+	std::map<int, MtpStorage*>::iterator i;
+	for (i = storagemap.begin(); i != storagemap.end(); i++) {
+		delete i->second;
+	}
+}
+
+int MyMtpDatabase::DEVICE_PROPERTIES[3] = {
+	MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
+	MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
+	MTP_DEVICE_PROPERTY_IMAGE_SIZE
+};
+
+int MyMtpDatabase::FILE_PROPERTIES[10] = {
+	// NOTE must match beginning of AUDIO_PROPERTIES, VIDEO_PROPERTIES
+	// and IMAGE_PROPERTIES below
+	MTP_PROPERTY_STORAGE_ID,
+	MTP_PROPERTY_OBJECT_FORMAT,
+	MTP_PROPERTY_PROTECTION_STATUS,
+	MTP_PROPERTY_OBJECT_SIZE,
+	MTP_PROPERTY_OBJECT_FILE_NAME,
+	MTP_PROPERTY_DATE_MODIFIED,
+	MTP_PROPERTY_PARENT_OBJECT,
+	MTP_PROPERTY_PERSISTENT_UID,
+	MTP_PROPERTY_NAME,
+	// TODO: why is DISPLAY_NAME not here?
+	MTP_PROPERTY_DATE_ADDED
+};
+
+int MyMtpDatabase::AUDIO_PROPERTIES[19] = {
+	// NOTE must match FILE_PROPERTIES above
+	MTP_PROPERTY_STORAGE_ID,
+	MTP_PROPERTY_OBJECT_FORMAT,
+	MTP_PROPERTY_PROTECTION_STATUS,
+	MTP_PROPERTY_OBJECT_SIZE,
+	MTP_PROPERTY_OBJECT_FILE_NAME,
+	MTP_PROPERTY_DATE_MODIFIED,
+	MTP_PROPERTY_PARENT_OBJECT,
+	MTP_PROPERTY_PERSISTENT_UID,
+	MTP_PROPERTY_NAME,
+	MTP_PROPERTY_DISPLAY_NAME,
+	MTP_PROPERTY_DATE_ADDED,
+
+	// audio specific properties
+	MTP_PROPERTY_ARTIST,
+	MTP_PROPERTY_ALBUM_NAME,
+	MTP_PROPERTY_ALBUM_ARTIST,
+	MTP_PROPERTY_TRACK,
+	MTP_PROPERTY_ORIGINAL_RELEASE_DATE,
+	MTP_PROPERTY_DURATION,
+	MTP_PROPERTY_GENRE,
+	MTP_PROPERTY_COMPOSER
+};
+
+int MyMtpDatabase::VIDEO_PROPERTIES[15] = {
+	// NOTE must match FILE_PROPERTIES above
+	MTP_PROPERTY_STORAGE_ID,
+	MTP_PROPERTY_OBJECT_FORMAT,
+	MTP_PROPERTY_PROTECTION_STATUS,
+	MTP_PROPERTY_OBJECT_SIZE,
+	MTP_PROPERTY_OBJECT_FILE_NAME,
+	MTP_PROPERTY_DATE_MODIFIED,
+	MTP_PROPERTY_PARENT_OBJECT,
+	MTP_PROPERTY_PERSISTENT_UID,
+	MTP_PROPERTY_NAME,
+	MTP_PROPERTY_DISPLAY_NAME,
+	MTP_PROPERTY_DATE_ADDED,
+
+	// video specific properties
+	MTP_PROPERTY_ARTIST,
+	MTP_PROPERTY_ALBUM_NAME,
+	MTP_PROPERTY_DURATION,
+	MTP_PROPERTY_DESCRIPTION
+};
+
+int MyMtpDatabase::IMAGE_PROPERTIES[12] = {
+	// NOTE must match FILE_PROPERTIES above
+	MTP_PROPERTY_STORAGE_ID,
+	MTP_PROPERTY_OBJECT_FORMAT,
+	MTP_PROPERTY_PROTECTION_STATUS,
+	MTP_PROPERTY_OBJECT_SIZE,
+	MTP_PROPERTY_OBJECT_FILE_NAME,
+	MTP_PROPERTY_DATE_MODIFIED,
+	MTP_PROPERTY_PARENT_OBJECT,
+	MTP_PROPERTY_PERSISTENT_UID,
+	MTP_PROPERTY_NAME,
+	MTP_PROPERTY_DISPLAY_NAME,
+	MTP_PROPERTY_DATE_ADDED,
+
+	// image specific properties
+	MTP_PROPERTY_DESCRIPTION
+};
+
+int MyMtpDatabase::ALL_PROPERTIES[25] = {
+	// NOTE must match FILE_PROPERTIES above
+	MTP_PROPERTY_STORAGE_ID,
+	MTP_PROPERTY_OBJECT_FORMAT,
+	MTP_PROPERTY_PROTECTION_STATUS,
+	MTP_PROPERTY_OBJECT_SIZE,
+	MTP_PROPERTY_OBJECT_FILE_NAME,
+	MTP_PROPERTY_DATE_MODIFIED,
+	MTP_PROPERTY_PARENT_OBJECT,
+	MTP_PROPERTY_PERSISTENT_UID,
+	MTP_PROPERTY_NAME,
+	MTP_PROPERTY_DISPLAY_NAME,
+	MTP_PROPERTY_DATE_ADDED,
+
+	// image specific properties
+	MTP_PROPERTY_DESCRIPTION,
+
+	// audio specific properties
+	MTP_PROPERTY_ARTIST,
+	MTP_PROPERTY_ALBUM_NAME,
+	MTP_PROPERTY_ALBUM_ARTIST,
+	MTP_PROPERTY_TRACK,
+	MTP_PROPERTY_ORIGINAL_RELEASE_DATE,
+	MTP_PROPERTY_DURATION,
+	MTP_PROPERTY_GENRE,
+	MTP_PROPERTY_COMPOSER,
+
+	// video specific properties
+	MTP_PROPERTY_ARTIST,
+	MTP_PROPERTY_ALBUM_NAME,
+	MTP_PROPERTY_DURATION,
+	MTP_PROPERTY_DESCRIPTION,
+
+	// image specific properties
+	MTP_PROPERTY_DESCRIPTION
+};
+
+int MyMtpDatabase::SUPPORTED_PLAYBACK_FORMATS[26] = {
+	SUPPORTED_PLAYBACK_FORMAT_UNDEFINED,
+	SUPPORTED_PLAYBACK_FORMAT_ASSOCIATION,
+	SUPPORTED_PLAYBACK_FORMAT_TEXT,
+	SUPPORTED_PLAYBACK_FORMAT_HTML,
+	SUPPORTED_PLAYBACK_FORMAT_WAV,
+	SUPPORTED_PLAYBACK_FORMAT_MP3,
+	SUPPORTED_PLAYBACK_FORMAT_MPEG,
+	SUPPORTED_PLAYBACK_FORMAT_EXIF_JPEG,
+	SUPPORTED_PLAYBACK_FORMAT_TIFF_EP,
+	SUPPORTED_PLAYBACK_FORMAT_BMP,
+	SUPPORTED_PLAYBACK_FORMAT_GIF,
+	SUPPORTED_PLAYBACK_FORMAT_JFIF,
+	SUPPORTED_PLAYBACK_FORMAT_PNG,
+	SUPPORTED_PLAYBACK_FORMAT_TIFF,
+	SUPPORTED_PLAYBACK_FORMAT_WMA,
+	SUPPORTED_PLAYBACK_FORMAT_OGG,
+	SUPPORTED_PLAYBACK_FORMAT_AAC,
+	SUPPORTED_PLAYBACK_FORMAT_MP4_CONTAINER,
+	SUPPORTED_PLAYBACK_FORMAT_MP2,
+	SUPPORTED_PLAYBACK_FORMAT_3GP_CONTAINER,
+	SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_AV_PLAYLIST,
+	SUPPORTED_PLAYBACK_FORMAT_WPL_PLAYLIST,
+	SUPPORTED_PLAYBACK_FORMAT_M3U_PLAYLIST,
+	SUPPORTED_PLAYBACK_FORMAT_PLS_PLAYLIST,
+	SUPPORTED_PLAYBACK_FORMAT_XML_DOCUMENT,
+	SUPPORTED_PLAYBACK_FORMAT_FLAC
+};
+
+MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path,
+											MtpObjectFormat format,
+											MtpObjectHandle parent,
+											MtpStorageID storage,
+											uint64_t size,
+											time_t modified) {
+	if (storagemap.find(storage) == storagemap.end())
+		return kInvalidObjectHandle;
+	return storagemap[storage]->beginSendObject(path, format, parent, size, modified);
+}
+
+void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
+								MtpObjectFormat format, bool succeeded) {
+	MTPD("endSendObject() %s\n", path);
+	if (!succeeded) {
+		MTPE("endSendObject() failed, unlinking %s\n", path);
+		unlink(path);
+	}
+	std::map<int, MtpStorage*>::iterator storit;
+	for (storit = storagemap.begin(); storit != storagemap.end(); storit++)
+		storit->second->endSendObject(path, handle, format, succeeded);
+}
+
+void MyMtpDatabase::createDB(MtpStorage* storage, MtpStorageID storageID) {
+	storagemap[storageID] = storage;
+	storage->createDB();
+}
+
+MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
+									MtpObjectFormat format,
+									MtpObjectHandle parent) {
+	MTPD("storageID: %d\n", storageID);
+	MtpObjectHandleList* list = storagemap[storageID]->getObjectList(storageID, parent);
+	MTPD("list: %d\n", list->size());
+	return list;
+}
+
+int MyMtpDatabase::getNumObjects(MtpStorageID storageID,
+									MtpObjectFormat format,
+									MtpObjectHandle parent) {
+	MtpObjectHandleList* list = storagemap[storageID]->getObjectList(storageID, parent);
+	int size = list->size();
+	delete list;
+	return size;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
+	// This function tells the host PC which file formats the device supports
+	MtpObjectFormatList* list = new MtpObjectFormatList();
+	int length = sizeof(SUPPORTED_PLAYBACK_FORMATS) / sizeof(SUPPORTED_PLAYBACK_FORMATS[0]);
+	MTPD("MyMtpDatabase::getSupportedPlaybackFormats length: %i\n", length);
+	for (int i = 0; i < length; i++) {
+		MTPD("supported playback format: %x\n", SUPPORTED_PLAYBACK_FORMATS[i]);
+		list->push(SUPPORTED_PLAYBACK_FORMATS[i]);
+	}
+	return list;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
+	// Android OS implementation of this function returns NULL
+	// so we are not implementing this function either.
+	MTPD("MyMtpDatabase::getSupportedCaptureFormats returning NULL (This is what Android does as well).\n");
+	return NULL;
+}
+
+MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+	int* properties;
+	MtpObjectPropertyList* list = new MtpObjectPropertyList();
+	int length = 0;
+	switch (format) {
+		case MTP_FORMAT_MP3:
+		case MTP_FORMAT_WAV:
+		case MTP_FORMAT_WMA:
+		case MTP_FORMAT_OGG:
+		case MTP_FORMAT_AAC:
+			properties = AUDIO_PROPERTIES;
+			length = sizeof(AUDIO_PROPERTIES) / sizeof(AUDIO_PROPERTIES[0]);
+			break;
+		case MTP_FORMAT_MPEG:
+		case MTP_FORMAT_3GP_CONTAINER:
+		case MTP_FORMAT_WMV:
+			properties = VIDEO_PROPERTIES;
+			length = sizeof(VIDEO_PROPERTIES) / sizeof(VIDEO_PROPERTIES[0]);
+			break;
+		case MTP_FORMAT_EXIF_JPEG:
+		case MTP_FORMAT_GIF:
+		case MTP_FORMAT_PNG:
+		case MTP_FORMAT_BMP:
+			properties = IMAGE_PROPERTIES;
+			length = sizeof(IMAGE_PROPERTIES) / sizeof(IMAGE_PROPERTIES[0]);
+			break;
+		case 0:
+			properties = ALL_PROPERTIES;
+			length = sizeof(ALL_PROPERTIES) / sizeof(ALL_PROPERTIES[0]);
+			break;
+		default:
+			properties = FILE_PROPERTIES;
+			length = sizeof(FILE_PROPERTIES) / sizeof(FILE_PROPERTIES[0]);
+	}
+	MTPD("MyMtpDatabase::getSupportedObjectProperties length is: %i, format: %x", length, format);
+	for (int i = 0; i < length; i++) {
+		MTPD("supported object property: %x\n", properties[i]);
+		list->push(properties[i]);
+	}
+	return list;
+}
+
+MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
+	MtpDevicePropertyList* list = new MtpDevicePropertyList();
+	int length = sizeof(DEVICE_PROPERTIES) / sizeof(DEVICE_PROPERTIES[0]);
+	MTPD("MyMtpDatabase::getSupportedDeviceProperties length was: %i\n", length);
+	for (int i = 0; i < length; i++)
+		list->push(DEVICE_PROPERTIES[i]);
+	return list;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
+											MtpObjectProperty property,
+											MtpDataPacket& packet) {
+	MTPD("MyMtpDatabase::getObjectPropertyValue mtpid: %u, property: %x\n", handle, property);
+	int type;
+	MtpResponseCode result = MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	MtpStorage::PropEntry prop;
+	if (!getObjectPropertyInfo(property, type)) {
+		MTPE("MyMtpDatabase::getObjectPropertyValue returning MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED\n");
+		return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+	}
+	std::map<int, MtpStorage*>::iterator storit;
+	for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+		if (storit->second->getObjectPropertyValue(handle, property, prop) == 0) {
+			result = MTP_RESPONSE_OK;
+			break;
+		}
+	}
+
+	if (result != MTP_RESPONSE_OK) {
+		MTPE("MyMtpDatabase::getObjectPropertyValue unable to locate handle: %u\n", handle);
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	}
+
+	uint64_t longValue = prop.intvalue;
+	// special case date properties, which are strings to MTP
+	// but stored internally as a uint64
+	if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) {
+		char date[20];
+		formatDateTime(longValue, date, sizeof(date));
+		packet.putString(date);
+		goto out;
+	}
+	// release date is stored internally as just the year
+	if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) {
+		char date[20];
+		snprintf(date, sizeof(date), "%04lld0101T000000", longValue);
+		packet.putString(date);
+		goto out;
+	}
+
+	switch (type) {
+		case MTP_TYPE_INT8:
+			packet.putInt8(longValue);
+			break;
+		case MTP_TYPE_UINT8:
+			packet.putUInt8(longValue);
+			break;
+		case MTP_TYPE_INT16:
+			packet.putInt16(longValue);
+			break;
+		case MTP_TYPE_UINT16:
+			packet.putUInt16(longValue);
+			break;
+		case MTP_TYPE_INT32:
+			packet.putInt32(longValue);
+			break;
+		case MTP_TYPE_UINT32:
+			packet.putUInt32(longValue);
+			break;
+		case MTP_TYPE_INT64:
+			packet.putInt64(longValue);
+			break;
+		case MTP_TYPE_UINT64:
+			packet.putUInt64(longValue);
+			break;
+		case MTP_TYPE_INT128:
+			packet.putInt128(longValue);
+			break;
+		case MTP_TYPE_UINT128:
+			packet.putUInt128(longValue);
+			break;
+		case MTP_TYPE_STR:
+			{
+				/*std::string stringValue = (string)stringValuesArray[0];
+				if (stringValue) {
+					const char* str = stringValue.c_str();
+					if (str == NULL) {
+						return MTP_RESPONSE_GENERAL_ERROR;
+					}
+					packet.putString(str);
+				} else {
+					packet.putEmptyString();
+				}*/
+				packet.putString(prop.strvalue.c_str());
+				MTPD("MTP_TYPE_STR: %x = %s\n", prop.property, prop.strvalue.c_str());
+				//MTPE("STRING unsupported type in getObjectPropertyValue\n");
+				//result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+				break;
+			}
+		default:
+			MTPE("unsupported type in getObjectPropertyValue\n");
+			result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+	}
+out:
+	return result;
+}
+
+MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
+											MtpObjectProperty property,
+											MtpDataPacket& packet) {
+	int type;
+	MTPD("MyMtpDatabase::setObjectPropertyValue start\n");
+	if (!getObjectPropertyInfo(property, type)) {
+		MTPE("MyMtpDatabase::setObjectPropertyValue returning MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED\n");
+		return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+	}
+	MTPD("MyMtpDatabase::setObjectPropertyValue continuing\n");
+	long longValue = 0;
+	std::string stringValue;
+
+	switch (type) {
+		case MTP_TYPE_INT8:
+			MTPD("int8\n");
+			longValue = packet.getInt8();
+			break;
+		case MTP_TYPE_UINT8:
+			MTPD("uint8\n");
+			longValue = packet.getUInt8();
+			break;
+		case MTP_TYPE_INT16:
+			MTPD("int16\n");
+			longValue = packet.getInt16();
+			break;
+		case MTP_TYPE_UINT16:
+			MTPD("uint16\n");
+			longValue = packet.getUInt16();
+			break;
+		case MTP_TYPE_INT32:
+			MTPD("int32\n");
+			longValue = packet.getInt32();
+			break;
+		case MTP_TYPE_UINT32:
+			MTPD("uint32\n");
+			longValue = packet.getUInt32();
+			break;
+		case MTP_TYPE_INT64:
+			MTPD("int64\n");
+			longValue = packet.getInt64();
+			break;
+		case MTP_TYPE_UINT64:
+			MTPD("uint64\n");
+			longValue = packet.getUInt64();
+			break;
+		case MTP_TYPE_STR:
+			{
+				MTPD("string\n");
+				MtpStringBuffer buffer;
+				packet.getString(buffer);
+				stringValue = (const char *)buffer;
+				break;
+			 }
+		default:
+			MTPE("MyMtpDatabase::setObjectPropertyValue unsupported type %i in getObjectPropertyValue\n", type);
+			return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+	}
+
+	int result = MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+
+	switch (property) {
+		case MTP_PROPERTY_OBJECT_FILE_NAME:
+			{
+				MTPD("MyMtpDatabase::setObjectPropertyValue renaming file, handle: %d, new name: '%s'\n", handle, stringValue.c_str());
+				std::map<int, MtpStorage*>::iterator storit;
+				for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+					if (storit->second->renameObject(handle, stringValue) == 0) {
+						MTPD("MTP_RESPONSE_OK\n");
+						result = MTP_RESPONSE_OK;
+						break;
+					}
+				}
+			}
+			break;
+
+		default:
+			MTPE("MyMtpDatabase::setObjectPropertyValue property %x not supported.\n", property);
+			result = MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+	}
+	MTPD("MyMtpDatabase::setObjectPropertyValue returning %d\n", result);
+	return result;
+}
+
+MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
+											MtpDataPacket& packet) {
+	int type, result = 0;
+	char prop_value[PROPERTY_VALUE_MAX];
+	MTPD("property %s\n",
+			MtpDebug::getDevicePropCodeName(property));
+	if (!getDevicePropertyInfo(property, type)) {
+		MTPE("MyMtpDatabase::getDevicePropertyValue MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED\n");
+		return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+	}
+	MTPD("property %s\n",
+			MtpDebug::getDevicePropCodeName(property));
+	MTPD("property %x\n", property);
+	MTPD("MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME %x\n", MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME); 
+	switch (property) {
+		case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+		case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+			result =  MTP_RESPONSE_OK;
+			break;
+		default:
+		{
+			MTPE("MyMtpDatabase::getDevicePropertyValue property %x not supported\n", property);
+			result = MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+			break;
+		}
+	}
+
+	if (result != MTP_RESPONSE_OK) {
+		MTPD("MTP_REPONSE_OK NOT OK\n");
+		return result;
+	}
+
+	long longValue = 0;
+	property_get("ro.build.product", prop_value, "unknown manufacturer");
+	switch (type) {
+		case MTP_TYPE_INT8: {
+			MTPD("MTP_TYPE_INT8\n");
+			packet.putInt8(longValue);
+			break;
+		}
+		case MTP_TYPE_UINT8:
+		{
+			MTPD("MTP_TYPE_UINT8\n");
+			packet.putUInt8(longValue);
+			break;
+		}
+		case MTP_TYPE_INT16:
+		{
+			MTPD("MTP_TYPE_INT16\n");
+			packet.putInt16(longValue);
+			break;
+		}
+		case MTP_TYPE_UINT16:
+		{
+			MTPD("MTP_TYPE_UINT16\n");
+			packet.putUInt16(longValue);
+			break;
+		}
+		case MTP_TYPE_INT32:
+		{
+			MTPD("MTP_TYPE_INT32\n");
+			packet.putInt32(longValue);
+			break;
+		}
+		case MTP_TYPE_UINT32:
+		{
+			MTPD("MTP_TYPE_UINT32\n");
+			packet.putUInt32(longValue);
+			break;
+		}
+		case MTP_TYPE_INT64:
+		{
+			MTPD("MTP_TYPE_INT64\n");
+			packet.putInt64(longValue);
+			break;
+		}
+		case MTP_TYPE_UINT64:
+		{
+			MTPD("MTP_TYPE_UINT64\n");
+			packet.putUInt64(longValue);
+			break;
+		}
+		case MTP_TYPE_INT128:
+		{
+			MTPD("MTP_TYPE_INT128\n");
+			packet.putInt128(longValue);
+			break;
+		}
+		case MTP_TYPE_UINT128:
+		{
+			MTPD("MTP_TYPE_UINT128\n");
+			packet.putInt128(longValue);
+			break;
+		}
+		case MTP_TYPE_STR:
+		{
+			MTPD("MTP_TYPE_STR\n");
+			char* str = prop_value;
+			packet.putString(str);
+			break;
+		 }
+		default:
+			MTPE("MyMtpDatabase::getDevicePropertyValue unsupported type %i in getDevicePropertyValue\n", type);
+			return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
+	}
+
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property, MtpDataPacket& packet) {
+   	int type;
+	MTPE("MyMtpDatabase::setDevicePropertyValue not implemented, returning 0\n");
+	return 0;
+}
+
+MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
+	MTPE("MyMtpDatabase::resetDeviceProperty not implemented, returning -1\n");
+   	return -1;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) {
+	MTPD("getObjectPropertyList()\n");
+	MTPD("property: %x\n", property);
+	std::map<int, MtpStorage*>::iterator storit;
+	for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+		MTPD("MyMtpDatabase::getObjectPropertyList calling getObjectPropertyList\n");
+		if (storit->second->getObjectPropertyList(handle, format, property, groupCode, depth, packet) == 0) {
+			MTPD("MTP_RESPONSE_OK\n");
+   			return MTP_RESPONSE_OK;
+		}
+	}
+	MTPE("MyMtpDatabase::getObjectPropertyList MTP_RESPOSNE_INVALID_OBJECT_HANDLE %i\n", handle);
+	return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
+	std::map<int, MtpStorage*>::iterator storit;
+	for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+		if (storit->second->getObjectInfo(handle, info) == 0) {
+			MTPD("MTP_RESPONSE_OK\n");
+			return MTP_RESPONSE_OK;
+		}
+	}
+	MTPE("MyMtpDatabase::getObjectInfo MTP_RESPONSE_INVALID_OBJECT_HANDLE %i\n", handle);
+	return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
+	MtpString path;
+	int64_t length;
+	MtpObjectFormat format;
+	void* result = NULL;
+	outThumbSize = 0;
+	MTPE("MyMtpDatabase::getThumbnail not implemented, returning 0\n");
+	return 0;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
+	std::map<int, MtpStorage*>::iterator storit;
+	for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+		MTPD("MyMtpDatabase::getObjectFilePath calling getObjectFilePath\n");
+		if (storit->second->getObjectFilePath(handle, outFilePath, outFileLength, outFormat) == 0) {
+			MTPD("MTP_RESPONSE_OK\n");
+			return MTP_RESPONSE_OK;
+		}
+	}
+	MTPE("MyMtpDatabase::getObjectFilePath MTP_RESPOSNE_INVALID_OBJECT_HANDLE %i\n", handle);
+	return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
+	MTPD("deleteFile\n");
+	std::map<int, MtpStorage*>::iterator storit;
+	for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+		if (storit->second->deleteFile(handle) == 0) {
+			MTPD("MTP_RESPONSE_OK\n");
+			return MTP_RESPONSE_OK;
+		}
+	}
+	MTPE("MyMtpDatabase::deleteFile MTP_RESPONSE_INVALID_OBJECT_HANDLE %i\n", handle);
+	return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+struct PropertyTableEntry {
+	MtpObjectProperty   property;
+	int				 type;
+};
+
+static const PropertyTableEntry   kObjectPropertyTable[] = {
+	{   MTP_PROPERTY_STORAGE_ID,		MTP_TYPE_UINT32	 },
+	{   MTP_PROPERTY_OBJECT_FORMAT,	 MTP_TYPE_UINT16	 },
+	{   MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16	 },
+	{   MTP_PROPERTY_OBJECT_SIZE,	   MTP_TYPE_UINT64	 },
+	{   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR		},
+	{   MTP_PROPERTY_DATE_MODIFIED,	 MTP_TYPE_STR		},
+	{   MTP_PROPERTY_PARENT_OBJECT,	 MTP_TYPE_UINT32	 },
+	{   MTP_PROPERTY_PERSISTENT_UID,	MTP_TYPE_UINT128	},
+	{   MTP_PROPERTY_NAME,			  MTP_TYPE_STR		},
+	{   MTP_PROPERTY_DISPLAY_NAME,	  MTP_TYPE_STR		},
+	{   MTP_PROPERTY_DATE_ADDED,		MTP_TYPE_STR		},
+	{   MTP_PROPERTY_ARTIST,			MTP_TYPE_STR		},
+	{   MTP_PROPERTY_ALBUM_NAME,		MTP_TYPE_STR		},
+	{   MTP_PROPERTY_ALBUM_ARTIST,	  MTP_TYPE_STR		},
+	{   MTP_PROPERTY_TRACK,			 MTP_TYPE_UINT16	 },
+	{   MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR	},
+	{   MTP_PROPERTY_GENRE,			 MTP_TYPE_STR		},
+	{   MTP_PROPERTY_COMPOSER,		  MTP_TYPE_STR		},
+	{   MTP_PROPERTY_DURATION,		  MTP_TYPE_UINT32	 },
+	{   MTP_PROPERTY_DESCRIPTION,	   MTP_TYPE_STR		},
+};
+
+static const PropertyTableEntry   kDevicePropertyTable[] = {
+	{   MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,	MTP_TYPE_STR },
+	{   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,	   MTP_TYPE_STR },
+	{   MTP_DEVICE_PROPERTY_IMAGE_SIZE,				 MTP_TYPE_STR },
+};
+
+bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
+	int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
+	const PropertyTableEntry* entry = kObjectPropertyTable;
+	MTPD("MyMtpDatabase::getObjectPropertyInfo size is: %i\n", count);
+	for (int i = 0; i < count; i++, entry++) {
+		if (entry->property == property) {
+			type = entry->type;
+			return true;
+		}
+	}
+	return false;
+}
+
+bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
+	int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
+	const PropertyTableEntry* entry = kDevicePropertyTable;
+	MTPD("MyMtpDatabase::getDevicePropertyInfo count is: %i\n", count);
+	for (int i = 0; i < count; i++, entry++) {
+		if (entry->property == property) {
+			type = entry->type;
+			MTPD("type: %x\n", type);
+			return true;
+		}
+	}
+	return false;
+}
+
+MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
+	// call function and place files with associated handles into int array
+	MTPD("MyMtpDatabase::getObjectReferences returning null, this seems to be what Android always does.\n");
+	MTPD("handle: %d\n", handle);
+	// Windows + Android seems to always return a NULL in this function, c == null path
+	// The way that this is handled in Android then is to do this:
+	return NULL;
+}
+
+MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle,
+													MtpObjectHandleList* references) {
+	int count = references->size();
+	MTPE("MyMtpDatabase::setObjectReferences not implemented, returning 0\n");
+	return 0;
+}
+
+MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
+											MtpObjectFormat format) {
+	MTPD("MyMtpDatabase::getObjectPropertyDesc start\n");
+	MtpProperty* result = NULL;
+	switch (property) {
+		case MTP_PROPERTY_OBJECT_FORMAT:
+			// use format as default value
+			result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
+			break;
+		case MTP_PROPERTY_PROTECTION_STATUS:
+		case MTP_PROPERTY_TRACK:
+			result = new MtpProperty(property, MTP_TYPE_UINT16);
+			break;
+		case MTP_PROPERTY_STORAGE_ID:
+		case MTP_PROPERTY_PARENT_OBJECT:
+		case MTP_PROPERTY_DURATION:
+			result = new MtpProperty(property, MTP_TYPE_UINT32);
+			break;
+		case MTP_PROPERTY_OBJECT_SIZE:
+			result = new MtpProperty(property, MTP_TYPE_UINT64);
+			break;
+		case MTP_PROPERTY_PERSISTENT_UID:
+			result = new MtpProperty(property, MTP_TYPE_UINT128);
+			break;
+		case MTP_PROPERTY_NAME:
+		case MTP_PROPERTY_DISPLAY_NAME:
+		case MTP_PROPERTY_ARTIST:
+		case MTP_PROPERTY_ALBUM_NAME:
+		case MTP_PROPERTY_ALBUM_ARTIST:
+		case MTP_PROPERTY_GENRE:
+		case MTP_PROPERTY_COMPOSER:
+		case MTP_PROPERTY_DESCRIPTION:
+			result = new MtpProperty(property, MTP_TYPE_STR);
+			break;
+		case MTP_PROPERTY_DATE_MODIFIED:
+		case MTP_PROPERTY_DATE_ADDED:
+		case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
+			result = new MtpProperty(property, MTP_TYPE_STR);
+			result->setFormDateTime();
+			break;
+		case MTP_PROPERTY_OBJECT_FILE_NAME:
+			// We allow renaming files and folders
+			result = new MtpProperty(property, MTP_TYPE_STR, true);
+			break;
+	}
+	return result;
+}
+
+MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
+	MtpProperty* result = NULL;
+	int ret;
+	bool writable = false;
+	switch (property) {
+		case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+		case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+			writable = true;
+			// fall through
+		case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
+			result = new MtpProperty(property, MTP_TYPE_STR, writable);
+
+			// get current value
+			// TODO: add actual values
+			result->setCurrentValue('\0');
+			result->setDefaultValue('\0');
+			break;
+		}
+
+	return result;
+}
+
+void MyMtpDatabase::sessionStarted() {
+	MTPD("MyMtpDatabase::sessionStarted not implemented or does nothing, returning\n");
+	return;
+}
+
+void MyMtpDatabase::sessionEnded() {
+	MTPD("MyMtpDatabase::sessionEnded not implemented or does nothing, returning\n");
+	return;
+}
+
+// ----------------------------------------------------------------------------
+
+void MyMtpDatabase::lockMutex(void) {
+	std::map<int, MtpStorage*>::iterator storit;
+	for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+		storit->second->lockMutex(0);
+	}
+}
+
+void MyMtpDatabase::unlockMutex(void) {
+	std::map<int, MtpStorage*>::iterator storit;
+	for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+		storit->second->unlockMutex(0);
+	}
+}
diff --git a/mtp/mtp_MtpDatabase.hpp b/mtp/mtp_MtpDatabase.hpp
new file mode 100755
index 0000000..cc8097b
--- /dev/null
+++ b/mtp/mtp_MtpDatabase.hpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef MTP_MTPDATABASE_HPP
+#define MTP_MTPDATABASE_HPP
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <map>
+#include <string>
+#include <deque>
+
+#include "MtpDatabase.h"
+#include "MtpDataPacket.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+#include "mtp.h"
+
+class MyMtpDatabase : public MtpDatabase {
+private:
+	int* getSupportedObjectProperties(int format);
+
+    static int FILE_PROPERTIES[10];
+	static int DEVICE_PROPERTIES[3];
+    static int AUDIO_PROPERTIES[19];
+    static int VIDEO_PROPERTIES[15];
+    static int IMAGE_PROPERTIES[12];
+    static int ALL_PROPERTIES[25];
+    static int SUPPORTED_PLAYBACK_FORMATS[26];
+	int storagenum;
+	int count;
+	std::string lastfile;
+	std::map<int, MtpStorage*> storagemap;
+	void countDirs(std::string path);
+	int readParentDirs(std::string path, int storageID);
+
+public:
+                                    MyMtpDatabase();
+    virtual                         ~MyMtpDatabase();
+
+	void					createDB(MtpStorage* storage, MtpStorageID storageID);
+    virtual MtpObjectHandle         beginSendObject(const char* path,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent,
+                                            MtpStorageID storage,
+                                            uint64_t size,
+                                            time_t modified);
+
+    virtual void                    endSendObject(const char* path,
+                                            MtpObjectHandle handle,
+                                            MtpObjectFormat format,
+                                            bool succeeded);
+
+    virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
+                                    MtpObjectFormat format,
+                                    MtpObjectHandle parent);
+
+    virtual int                     getNumObjects(MtpStorageID storageID,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent);
+
+    // callee should delete[] the results from these
+    // results can be NULL
+    virtual MtpObjectFormatList*    getSupportedPlaybackFormats();
+    virtual MtpObjectFormatList*    getSupportedCaptureFormats();
+    virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format);
+    virtual MtpDevicePropertyList*  getSupportedDeviceProperties();
+
+    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property);
+
+    virtual MtpResponseCode         getObjectPropertyList(MtpObjectHandle handle,
+                                            uint32_t format, uint32_t property,
+                                            int groupCode, int depth,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
+                                            MtpObjectInfo& info);
+
+    virtual void*                   getThumbnail(MtpObjectHandle handle, size_t& outThumbSize);
+
+    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
+                                            MtpString& outFilePath,
+                                            int64_t& outFileLength,
+                                            MtpObjectFormat& outFormat);
+    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle);
+
+    bool                            getObjectPropertyInfo(MtpObjectProperty property, int& type);
+    bool                            getDevicePropertyInfo(MtpDeviceProperty property, int& type);
+
+    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle);
+
+    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
+                                            MtpObjectHandleList* references);
+
+    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format);
+
+    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property);
+
+    virtual void                    sessionStarted();
+
+    virtual void                    sessionEnded();
+    virtual void                    lockMutex();
+    virtual void                    unlockMutex();
+};
+#endif
diff --git a/mtp/mtp_MtpServer.cpp b/mtp/mtp_MtpServer.cpp
new file mode 100755
index 0000000..9df564f
--- /dev/null
+++ b/mtp/mtp_MtpServer.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <vector>
+#include <utils/threads.h>
+
+#include "mtp_MtpServer.hpp"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpDebug.h"
+
+#include <string>
+
+void twmtp_MtpServer::start()
+{
+	if (setup() == 0) {
+		add_storage();
+		server->run();
+	}
+}
+
+void twmtp_MtpServer::set_storages(storages* mtpstorages) {
+	stores = mtpstorages;
+}
+
+int twmtp_MtpServer::setup()
+{
+	usePtp =  false;
+	MyMtpDatabase* mtpdb = new MyMtpDatabase();
+#ifdef USB_MTP_DEVICE
+#define STRINGIFY(x) #x
+#define EXPAND(x) STRINGIFY(x)
+	MTPI("Using '%s' for MTP device.\n", EXPAND(USB_MTP_DEVICE));
+	int fd = open(EXPAND(USB_MTP_DEVICE), O_RDWR);
+#else
+	int fd = open("/dev/mtp_usb", O_RDWR);
+#endif
+	if (fd >= 0) {
+		MTPD("fd: %d\n", fd);
+		server = new MtpServer(fd, mtpdb, usePtp, 0, 0664, 0775);
+		refserver = server;
+		MTPI("created new mtpserver object\n");
+	} else {
+		MTPE("could not open MTP driver, errno: %d\n", errno);
+		return -1;
+	}
+	return 0;
+}
+
+void twmtp_MtpServer::run()
+{
+	MTPD("running in twmtp\n");
+	server->run();
+}
+
+void twmtp_MtpServer::cleanup()
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	if (server) {
+		delete server;
+	} else {
+		MTPD("server is null in cleanup");
+	}
+}
+
+void twmtp_MtpServer::send_object_added(int handle)
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	if (server)
+		server->sendObjectAdded(handle);
+	else
+		MTPD("server is null in send_object_added");
+}
+
+void twmtp_MtpServer::send_object_removed(int handle)
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	if (server)
+		server->sendObjectRemoved(handle);
+	else
+		MTPD("server is null in send_object_removed");
+}
+
+void twmtp_MtpServer::add_storage()
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	MTPI("adding internal storage\n");
+	for (unsigned int i = 0; i < stores->size(); ++i) {
+			std::string pathStr = stores->at(i)->mount;
+
+			if (!pathStr.empty()) {
+				std::string descriptionStr = stores->at(i)->display;
+				int storageID = stores->at(i)->mtpid;
+				long reserveSpace = 1;
+				bool removable = false;
+				long maxFileSize = 1000000000L;
+				if (descriptionStr != "") {
+					MtpStorage* storage = new MtpStorage(storageID, &pathStr[0], &descriptionStr[0], reserveSpace, removable, maxFileSize, refserver);
+					server->addStorage(storage);
+				}
+		}
+	}
+}
+
+void twmtp_MtpServer::remove_storage(int storageId)
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	if (server) {
+		MtpStorage* storage = server->getStorage(storageId);
+		if (storage) {
+			server->removeStorage(storage);
+			delete storage;
+		}
+	} else
+		MTPD("server is null in remove_storage");
+}
diff --git a/mtp/mtp_MtpServer.hpp b/mtp/mtp_MtpServer.hpp
new file mode 100755
index 0000000..ce6b13c
--- /dev/null
+++ b/mtp/mtp_MtpServer.hpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef MTP_MTPSERVER_HPP
+#define MTP_MTPSERVER_HPP
+#include <utils/Log.h>
+
+#include <string>
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utils/threads.h>
+
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "mtp_MtpDatabase.hpp"
+
+typedef struct Storage {
+	std::string display;
+	std::string mount;
+	int mtpid;
+} storage;
+
+typedef std::vector<storage*> storages;
+
+class twmtp_MtpServer {
+	public:
+		void start();
+		int setup();
+		void run();
+		void cleanup();
+		void send_object_added(int handle);
+		void send_object_removed(int handle);
+		void add_storage();
+		void remove_storage(int storageId);
+		void set_storages(storages* mtpstorages);
+		storages *stores;
+	private:
+		bool usePtp;
+		MtpServer* server;
+		MtpServer* refserver;
+
+};
+#endif
diff --git a/mtp/node.cpp b/mtp/node.cpp
new file mode 100755
index 0000000..1bca1d9
--- /dev/null
+++ b/mtp/node.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <vector>
+#include <sstream>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgen.h>
+
+#include "btree.hpp"
+#include "mtp.h"
+#include "MtpDebug.h"
+
+
+Node::Node()
+	: handle(-1), parent(0), name("")
+{
+}
+
+Node::Node(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name)
+	: handle(handle), parent(parent), name(name)
+{
+}
+
+void Node::rename(const std::string& newName) {
+	name = newName;
+	updateProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, name.c_str(), MTP_TYPE_STR);
+	updateProperty(MTP_PROPERTY_NAME, 0, name.c_str(), MTP_TYPE_STR);
+	updateProperty(MTP_PROPERTY_DISPLAY_NAME, 0, name.c_str(), MTP_TYPE_STR);
+}
+
+MtpObjectHandle Node::Mtpid() const { return handle; }
+MtpObjectHandle Node::getMtpParentId() const { return parent; }
+const std::string& Node::getName() const { return name; }
+
+uint64_t Node::getIntProperty(MtpPropertyCode property) {
+	for (unsigned index = 0; index < mtpProp.size(); ++index) {
+		if (mtpProp[index].property == property)
+			return mtpProp[index].valueInt;
+	}
+	MTPE("Node::getIntProperty failed to find property %x, returning -1\n", (unsigned)property);
+	return -1;
+}
+
+const Node::mtpProperty& Node::getProperty(MtpPropertyCode property) {
+	static const mtpProperty dummyProp;
+	for (size_t i = 0; i < mtpProp.size(); ++i) {
+		if (mtpProp[i].property == property)
+			return mtpProp[i];
+	}
+	MTPE("Node::getProperty failed to find property %x, returning dummy property\n", (unsigned)property);
+	return dummyProp;
+}
+
+void Node::addProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType) {
+//	MTPD("adding property: %lld, valueInt: %lld, valueStr: %s, dataType: %d\n", property, valueInt, valueStr.c_str(), dataType);
+	struct mtpProperty prop;
+	prop.property = property;
+	prop.valueInt = valueInt;
+	prop.valueStr = valueStr;
+	prop.dataType = dataType;
+	mtpProp.push_back(prop);
+}
+
+void Node::updateProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType) {
+	for (unsigned i = 0; i < mtpProp.size(); i++) {
+		if (mtpProp[i].property == property) {
+			mtpProp[i].valueInt = valueInt;
+			mtpProp[i].valueStr = valueStr;
+			mtpProp[i].dataType = dataType;
+			return;
+		}
+	}
+	addProperty(property, valueInt, valueStr, dataType);
+}
+
+std::vector<Node::mtpProperty>& Node::getMtpProps() {
+	return mtpProp;
+}
+
+void Node::addProperties(const std::string& path, int storageID) {
+	MTPD("addProperties: handle: %u, filename: '%s'\n", handle, getName().c_str());
+	struct stat st;
+	int mFormat = 0;
+	uint64_t puid;
+	off_t file_size = 0;
+
+	std::string mtpidStr = static_cast<std::ostringstream*>( &(std::ostringstream() << handle) )->str();
+	std::string storageIDStr = static_cast<std::ostringstream*>( &(std::ostringstream() << storageID) )->str();
+	std::string puidStr = storageIDStr + mtpidStr;
+	if ( ! (std::istringstream(puidStr) >> puid) ) puid = 0;
+	mFormat = MTP_FORMAT_UNDEFINED;   // file
+	if (lstat(path.c_str(), &st) == 0) {
+		file_size = st.st_size;
+		if (S_ISDIR(st.st_mode))
+			mFormat = MTP_FORMAT_ASSOCIATION; // folder
+	}
+
+	// TODO: don't store properties with constant values at all, add them at query time instead
+	addProperty(MTP_PROPERTY_STORAGE_ID, storageID, "", MTP_TYPE_UINT32);
+	addProperty(MTP_PROPERTY_OBJECT_FORMAT, mFormat, "", MTP_TYPE_UINT16);
+	addProperty(MTP_PROPERTY_PROTECTION_STATUS, 0, "", MTP_TYPE_UINT16);
+	addProperty(MTP_PROPERTY_OBJECT_SIZE, file_size, "", MTP_TYPE_UINT64);
+	addProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, getName().c_str(), MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_DATE_MODIFIED, st.st_mtime, "", MTP_TYPE_UINT64);
+	addProperty(MTP_PROPERTY_PARENT_OBJECT, parent, "", MTP_TYPE_UINT32);
+	addProperty(MTP_PROPERTY_PERSISTENT_UID, puid, "", MTP_TYPE_UINT128);
+		// TODO: we can't really support persistent UIDs without a persistent DB.
+		// probably a combination of volume UUID + st_ino would come close.
+		// doesn't help for fs with no native inodes numbers like fat though...
+	addProperty(MTP_PROPERTY_NAME, 0, getName().c_str(), MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_DISPLAY_NAME, 0, getName().c_str(), MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_DATE_ADDED, st.st_mtime, "", MTP_TYPE_UINT64);
+	addProperty(MTP_PROPERTY_DESCRIPTION, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ARTIST, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ALBUM_NAME, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ALBUM_ARTIST, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_TRACK, 0, "", MTP_TYPE_UINT16);
+	addProperty(MTP_PROPERTY_ORIGINAL_RELEASE_DATE, 2014, "", MTP_TYPE_UINT64);	// TODO: extract year from st.st_mtime?
+	addProperty(MTP_PROPERTY_DURATION, 0, "", MTP_TYPE_UINT32);
+	addProperty(MTP_PROPERTY_GENRE, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_COMPOSER, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ARTIST, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ALBUM_NAME, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_DURATION, 0, "", MTP_TYPE_UINT32);
+	addProperty(MTP_PROPERTY_DESCRIPTION, 0, "", MTP_TYPE_STR);
+}
diff --git a/mtp/twrpMtp.cpp b/mtp/twrpMtp.cpp
new file mode 100755
index 0000000..9a7df5b
--- /dev/null
+++ b/mtp/twrpMtp.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <string>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include "MtpTypes.h"
+#include "MtpPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpDatabase.h"
+#include "MtpRequestPacket.h"
+#include "MtpResponsePacket.h"
+#include "mtp_MtpDatabase.hpp"
+#include "mtp_MtpServer.hpp"
+#include "twrpMtp.hpp"
+#include "MtpDebug.h"
+
+#ifdef TWRPMTP
+static void usage(std::string prg) {
+	printf("Usage: %s <OPTIONS>\n", prg.c_str());
+	printf("Options:\n");
+	printf("\t-h, --help\t\tShow Usage\n");
+	printf("\t-s1, --storage1 /path/to/dir\t\tDestination to first storage directory\n");
+	printf("\t-s2, --storage2 /path/to/dir\t\tDestination to first storage directory\n");
+	printf("\t-sN, --storageN /path/to/dir\t\tDestination to first storage directory\n");
+}
+
+int main(int argc, char* argv[]) {
+	printf("argc: %d\n", argc);
+	if (argc < 2) {
+		usage(argv[0]);
+		return 1;
+	}
+
+	std::vector <std::string> storages;
+
+	for (int i = 1; i < argc; ++i) {
+		std::string arg = argv[i];
+		if ((arg == "-h") || (arg == "--help")) {
+			usage(argv[0]);
+		}
+		else {
+			storages.push_back(arg);
+		}
+	}
+	printf("starting\n");
+	twmtp_MtpServer* mtp = new twmtp_MtpServer();
+	mtp->set_storages(storages);
+	mtp->start();
+	return 0;
+}
+#endif //def TWRPMTP
+
+twrpMtp::twrpMtp(int debug_enabled = 0) {
+	if (debug_enabled)
+		MtpDebug::enableDebug();
+	mtpstorages = new storages;
+}
+
+int twrpMtp::start(void) {
+	MTPI("Starting MTP\n");
+	twmtp_MtpServer *mtp = new twmtp_MtpServer();
+	mtp->set_storages(mtpstorages);
+	mtp->start();
+	return 0;
+}
+
+pthread_t twrpMtp::threadserver(void) {
+	pthread_t thread;
+	ThreadPtr mtpptr = &twrpMtp::start;
+	PThreadPtr p = *(PThreadPtr*)&mtpptr;
+	pthread_create(&thread, NULL, p, this);
+	return thread;
+}
+
+pid_t twrpMtp::forkserver(void) {
+	pid_t pid;
+	if ((pid = fork()) == -1) {
+		MTPE("MTP fork failed.\n");
+		return 0;
+	}
+	if (pid == 0) {
+		// Child process
+		start();
+		MTPD("MTP child process exited.\n");
+		_exit(0);
+	} else {
+		return pid;
+	}
+	return 0;
+}
+
+void twrpMtp::addStorage(std::string display, std::string path, int mtpid) {
+	s = new storage;
+	s->display = display;
+	s->mount = path;
+	s->mtpid = mtpid;
+	MTPD("twrpMtp mtpid: %d\n", s->mtpid);
+	mtpstorages->push_back(s);
+}
diff --git a/mtp/twrpMtp.hpp b/mtp/twrpMtp.hpp
new file mode 100755
index 0000000..f0d8f4b
--- /dev/null
+++ b/mtp/twrpMtp.hpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TWRPMTP_HPP
+#define TWRPMTP_HPP
+
+#include <fcntl.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include <string>
+#include <vector>
+#include <pthread.h>
+#include "MtpTypes.h"
+#include "MtpPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpDatabase.h"
+#include "MtpRequestPacket.h"
+#include "MtpResponsePacket.h"
+#include "mtp_MtpDatabase.hpp"
+
+class twrpMtp {
+	public:
+		twrpMtp(int debug_enabled /* = 0 */);
+		pthread_t threadserver(void);
+		pid_t forkserver(void);
+		void addStorage(std::string display, std::string path, int mtpid);
+	private:
+		int start(void);
+		typedef int (twrpMtp::*ThreadPtr)(void);
+		typedef void* (*PThreadPtr)(void *);
+		storages *mtpstorages;
+		storage *s;
+};
+#endif
diff --git a/openaes/Android.mk b/openaes/Android.mk
new file mode 100644
index 0000000..336ebdd
--- /dev/null
+++ b/openaes/Android.mk
@@ -0,0 +1,39 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+	# Build shared binary
+	LOCAL_SRC_FILES:= src/oaes.c \
+	LOCAL_C_INCLUDES := \
+		$(commands_recovery_local_path)/openaes/src/isaac \
+		$(commands_recovery_local_path)/openaes/inc
+	LOCAL_CFLAGS:= -g -c -W
+	LOCAL_MODULE:=openaes
+	LOCAL_MODULE_TAGS:= eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+	LOCAL_SHARED_LIBRARIES = libopenaes libc
+	include $(BUILD_EXECUTABLE)
+
+	# Build shared library
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := libopenaes
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_C_INCLUDES := \
+		$(commands_recovery_local_path)/openaes/src/isaac \
+		$(commands_recovery_local_path)/openaes/inc
+	LOCAL_SRC_FILES = src/oaes_lib.c src/isaac/rand.c
+	LOCAL_SHARED_LIBRARIES = libc
+	include $(BUILD_SHARED_LIBRARY)
+
+	# Build static library
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := libopenaes_static
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_C_INCLUDES := \
+		$(commands_recovery_local_path)/openaes/src/isaac \
+		$(commands_recovery_local_path)/openaes/inc
+	LOCAL_SRC_FILES = src/oaes_lib.c src/isaac/rand.c
+	LOCAL_STATIC_LIBRARIES = libc
+	include $(BUILD_STATIC_LIBRARY)
+endif
diff --git a/openaes/CHANGELOG b/openaes/CHANGELOG
new file mode 100644
index 0000000..8ea94cf
--- /dev/null
+++ b/openaes/CHANGELOG
@@ -0,0 +1,43 @@
+---------------------------------------------------------------------------
+OpenAES-0.7.0
+Nabil S. Al Ramli
+www.nalramli.com
+---------------------------------------------------------------------------
+
+OpenAES-0.7.0
+-------------
+* implement oaes command line utility
+* defect: oaes_decrypt() does not have a way to tell if pad pattern is accidental
+
+OpenAES-0.6.0
+-------------
+* add stepping pause to vt_aes
+
+OpenAES-0.5.0
+-------------
+* defect: algorithm errors with ExpandKey for 192 bit and 256 bit keys
+
+OpenAES-0.4.0
+-------------
+* add vt_aes test program
+* defect: algorithm errors with ShiftRows and MixColumns
+* OAES_DEBUG config to step through encryption and decryption
+* defect: test_performance crash when printing results
+* allow user to specify iv by passing it to oaes_set_option()
+* oaes_key_import_data() and oaes_key_export_data() to operate on key data directly
+* defect: Access violation in oaes_key_export()
+
+OpenAES-0.3.0
+-------------
+* Add CMake support
+* platform independence fixes
+
+OpenAES-0.2.0
+-------------
+* Add performance tests
+* Implement CBC mode in AES algorithm
+* Performance improvements in oaes_shift_rows() and oaes_inv_shift_rows()
+
+OpenAES-0.1.0
+-------------
+* Implement AES algorithm
diff --git a/openaes/CMakeLists.txt b/openaes/CMakeLists.txt
new file mode 100644
index 0000000..251d5a3
--- /dev/null
+++ b/openaes/CMakeLists.txt
@@ -0,0 +1,74 @@
+# ---------------------------------------------------------------------------
+# OpenAES License
+# ---------------------------------------------------------------------------
+# Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 
+#   - Redistributions of source code must retain the above copyright notice,
+#     this list of conditions and the following disclaimer.
+#   - 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.
+# 
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
+# ---------------------------------------------------------------------------
+
+cmake_minimum_required (VERSION 2.8.0)
+
+project ( "oaes" )
+
+include_directories (
+		${CMAKE_CURRENT_SOURCE_DIR}/inc
+		${CMAKE_CURRENT_SOURCE_DIR}/src/isaac
+	)
+
+set (SRC
+		${CMAKE_CURRENT_SOURCE_DIR}/src/oaes_lib.c
+		${CMAKE_CURRENT_SOURCE_DIR}/src/isaac/rand.c
+	)
+
+set (HDR
+		${CMAKE_CURRENT_SOURCE_DIR}/inc/oaes_config.h
+		${CMAKE_CURRENT_SOURCE_DIR}/inc/oaes_lib.h
+		${CMAKE_CURRENT_SOURCE_DIR}/src/isaac/rand.h
+		${CMAKE_CURRENT_SOURCE_DIR}/src/isaac/standard.h
+	)
+
+set (SRC_test_encrypt
+		${CMAKE_CURRENT_SOURCE_DIR}/test/test_encrypt.c
+	)
+
+set (SRC_test_keys
+		${CMAKE_CURRENT_SOURCE_DIR}/test/test_keys.c
+	)
+
+set (SRC_test_performance
+		${CMAKE_CURRENT_SOURCE_DIR}/test/test_performance.c
+	)
+
+set (SRC_vt_aes
+		${CMAKE_CURRENT_SOURCE_DIR}/test/vt_aes.c
+	)
+
+set (SRC_oaes
+		${CMAKE_CURRENT_SOURCE_DIR}/src/oaes.c
+	)
+
+add_executable (test_encrypt ${SRC_test_encrypt} ${SRC} ${HDR})
+add_executable (test_keys ${SRC_test_keys} ${SRC} ${HDR})
+add_executable (test_performance ${SRC_test_performance} ${SRC} ${HDR})
+add_executable (vt_aes ${SRC_vt_aes} ${SRC} ${HDR})
+add_executable (oaes ${SRC_oaes} ${SRC} ${HDR})
diff --git a/openaes/LICENSE b/openaes/LICENSE
new file mode 100644
index 0000000..d824e13
--- /dev/null
+++ b/openaes/LICENSE
@@ -0,0 +1,27 @@
+---------------------------------------------------------------------------
+OpenAES Licence
+---------------------------------------------------------------------------
+Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+  - 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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
+---------------------------------------------------------------------------
diff --git a/openaes/README b/openaes/README
new file mode 100644
index 0000000..6fb957b
--- /dev/null
+++ b/openaes/README
@@ -0,0 +1,78 @@
+---------------------------------------------------------------------------
+OpenAES-0.7.0
+Nabil S. Al Ramli
+www.nalramli.com
+---------------------------------------------------------------------------
+
+License Terms
+-------------
+
+---------------------------------------------------------------------------
+OpenAES Licence
+---------------------------------------------------------------------------
+Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+  - 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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
+---------------------------------------------------------------------------
+
+Introduction
+------------
+
+OpenAES is an open source implementation of the Advanced Encryption
+Standard.  It is distributed as a portable, lightweight C library that can
+be easily integrated into applications.
+
+Compiling
+---------
+
+OpenAES has been tested with the GCC as well as VC compilers.  It is
+necessary to compile the source files located in ./src, and to add ./inc to
+the include paths.
+
+If you are building with OAES_HAVE_ISAAC defined (true by default), then
+you also need to link in the source files under ./src/isaac and also add
+./src/isaac to the include paths.
+
+You may edit ./inc/oaes_config.h to modify build options.
+
+CMake 2.8.0 can be used to build the test programs on different platforms.
+
+In a Linux command line terminal type:
+
+cmake .
+make
+
+In Windows, in a Visual Studio command line window type:
+
+cmake . -G "NMake Makefiles"
+nmake
+
+Usage
+-----
+
+Usage is described in the header file ./inc/oaes_lib.h.
+
+Samples
+-------
+
+Samples applications are provided in the /test folder.
diff --git a/openaes/VERSION b/openaes/VERSION
new file mode 100644
index 0000000..831dbdd
--- /dev/null
+++ b/openaes/VERSION
@@ -0,0 +1 @@
+OpenAES-0.7.0
\ No newline at end of file
diff --git a/openaes/inc/oaes_config.h b/openaes/inc/oaes_config.h
new file mode 100644
index 0000000..d887f03
--- /dev/null
+++ b/openaes/inc/oaes_config.h
@@ -0,0 +1,50 @@
+/* 
+ * ---------------------------------------------------------------------------
+ * OpenAES License
+ * ---------------------------------------------------------------------------
+ * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *   - Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   - 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
+ * ---------------------------------------------------------------------------
+ */
+
+#ifndef _OAES_CONFIG_H
+#define _OAES_CONFIG_H
+
+#ifdef __cplusplus 
+extern "C" {
+#endif
+
+#ifndef OAES_HAVE_ISAAC
+#define OAES_HAVE_ISAAC 1
+#endif // OAES_HAVE_ISAAC
+
+#ifndef OAES_DEBUG
+#define OAES_DEBUG 0
+#endif // OAES_DEBUG
+
+#ifdef __cplusplus 
+}
+#endif
+
+#endif // _OAES_CONFIG_H
diff --git a/openaes/inc/oaes_lib.h b/openaes/inc/oaes_lib.h
new file mode 100644
index 0000000..fd89a70
--- /dev/null
+++ b/openaes/inc/oaes_lib.h
@@ -0,0 +1,172 @@
+/* 
+ * ---------------------------------------------------------------------------
+ * OpenAES License
+ * ---------------------------------------------------------------------------
+ * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *   - Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   - 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
+ * ---------------------------------------------------------------------------
+ */
+
+#ifndef _OAES_LIB_H
+#define _OAES_LIB_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus 
+extern "C" {
+#endif
+
+#define OAES_VERSION "0.7.0"
+#define OAES_BLOCK_SIZE 16
+
+typedef void OAES_CTX;
+
+typedef enum
+{
+	OAES_RET_FIRST = 0,
+	OAES_RET_SUCCESS = 0,
+	OAES_RET_UNKNOWN,
+	OAES_RET_ARG1,
+	OAES_RET_ARG2,
+	OAES_RET_ARG3,
+	OAES_RET_ARG4,
+	OAES_RET_ARG5,
+	OAES_RET_NOKEY,
+	OAES_RET_MEM,
+	OAES_RET_BUF,
+	OAES_RET_HEADER,
+	OAES_RET_COUNT
+} OAES_RET;
+
+/*
+ * oaes_set_option() takes one of these values for its [option] parameter
+ * some options accept either an optional or a required [value] parameter
+ */
+// no option
+#define OAES_OPTION_NONE 0
+// enable ECB mode, disable CBC mode
+#define OAES_OPTION_ECB 1
+// enable CBC mode, disable ECB mode
+// value is optional, may pass uint8_t iv[OAES_BLOCK_SIZE] to specify
+// the value of the initialization vector, iv
+#define OAES_OPTION_CBC 2
+
+#ifdef OAES_DEBUG
+typedef int ( * oaes_step_cb ) (
+		const uint8_t state[OAES_BLOCK_SIZE],
+		const char * step_name,
+		int step_count,
+		void * user_data );
+// enable state stepping mode
+// value is required, must pass oaes_step_cb to receive the state at each step
+#define OAES_OPTION_STEP_ON 4
+// disable state stepping mode
+#define OAES_OPTION_STEP_OFF 8
+#endif // OAES_DEBUG
+
+typedef uint16_t OAES_OPTION;
+
+/*
+ * // usage:
+ * 
+ * OAES_CTX * ctx = oaes_alloc();
+ * .
+ * .
+ * .
+ * {
+ *   oaes_gen_key_xxx( ctx );
+ *   {
+ *     oaes_key_export( ctx, _buf, &_buf_len );
+ *     // or
+ *     oaes_key_export_data( ctx, _buf, &_buf_len );\
+ *   }
+ * }
+ * // or
+ * {
+ *   oaes_key_import( ctx, _buf, _buf_len );
+ *   // or
+ *   oaes_key_import_data( ctx, _buf, _buf_len );
+ * }
+ * .
+ * .
+ * .
+ * oaes_encrypt( ctx, m, m_len, c, &c_len );
+ * .
+ * .
+ * .
+ * oaes_decrypt( ctx, c, c_len, m, &m_len );
+ * .
+ * .
+ * .
+ * oaes_free( &ctx );
+ */
+
+OAES_CTX * oaes_alloc();
+
+OAES_RET oaes_free( OAES_CTX ** ctx );
+
+OAES_RET oaes_set_option( OAES_CTX * ctx,
+		OAES_OPTION option, const void * value );
+
+OAES_RET oaes_key_gen_128( OAES_CTX * ctx );
+
+OAES_RET oaes_key_gen_192( OAES_CTX * ctx );
+
+OAES_RET oaes_key_gen_256( OAES_CTX * ctx );
+
+// export key with header information
+// set data == NULL to get the required data_len
+OAES_RET oaes_key_export( OAES_CTX * ctx,
+		uint8_t * data, size_t * data_len );
+
+// directly export the data from key
+// set data == NULL to get the required data_len
+OAES_RET oaes_key_export_data( OAES_CTX * ctx,
+		uint8_t * data, size_t * data_len );
+
+// import key with header information
+OAES_RET oaes_key_import( OAES_CTX * ctx,
+		const uint8_t * data, size_t data_len );
+
+// directly import data into key
+OAES_RET oaes_key_import_data( OAES_CTX * ctx,
+		const uint8_t * data, size_t data_len );
+
+// set c == NULL to get the required c_len
+OAES_RET oaes_encrypt( OAES_CTX * ctx,
+		const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len );
+
+// set m == NULL to get the required m_len
+OAES_RET oaes_decrypt( OAES_CTX * ctx,
+		const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len );
+
+// set buf == NULL to get the required buf_len
+OAES_RET oaes_sprintf(
+		char * buf, size_t * buf_len, const uint8_t * data, size_t data_len );
+
+#ifdef __cplusplus 
+}
+#endif
+
+#endif // _OAES_LIB_H
diff --git a/openaes/src/isaac/rand.c b/openaes/src/isaac/rand.c
new file mode 100644
index 0000000..63babc7
--- /dev/null
+++ b/openaes/src/isaac/rand.c
@@ -0,0 +1,137 @@
+/*
+------------------------------------------------------------------------------
+rand.c: By Bob Jenkins.  My random number generator, ISAAC.  Public Domain.
+MODIFIED:
+  960327: Creation (addition of randinit, really)
+  970719: use context, not global variables, for internal state
+  980324: added main (ifdef'ed out), also rearranged randinit()
+  010626: Note that this is public domain
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+#include "standard.h"
+#endif
+#ifndef RAND
+#include "rand.h"
+#endif
+
+
+#define ind(mm,x)  (*(ub4 *)((ub1 *)(mm) + ((x) & ((RANDSIZ-1)<<2))))
+#define rngstep(mix,a,b,mm,m,m2,r,x) \
+{ \
+  x = *m;  \
+  a = (a^(mix)) + *(m2++); \
+  *(m++) = y = ind(mm,x) + a + b; \
+  *(r++) = b = ind(mm,y>>RANDSIZL) + x; \
+}
+
+void     isaac(ctx)
+randctx *ctx;
+{
+   register ub4 a,b,x,y,*m,*mm,*m2,*r,*mend;
+   mm=ctx->randmem; r=ctx->randrsl;
+   a = ctx->randa; b = ctx->randb + (++ctx->randc);
+   for (m = mm, mend = m2 = m+(RANDSIZ/2); m<mend; )
+   {
+      rngstep( a<<13, a, b, mm, m, m2, r, x);
+      rngstep( a>>6 , a, b, mm, m, m2, r, x);
+      rngstep( a<<2 , a, b, mm, m, m2, r, x);
+      rngstep( a>>16, a, b, mm, m, m2, r, x);
+   }
+   for (m2 = mm; m2<mend; )
+   {
+      rngstep( a<<13, a, b, mm, m, m2, r, x);
+      rngstep( a>>6 , a, b, mm, m, m2, r, x);
+      rngstep( a<<2 , a, b, mm, m, m2, r, x);
+      rngstep( a>>16, a, b, mm, m, m2, r, x);
+   }
+   ctx->randb = b; ctx->randa = a;
+}
+
+
+#define mix(a,b,c,d,e,f,g,h) \
+{ \
+   a^=b<<11; d+=a; b+=c; \
+   b^=c>>2;  e+=b; c+=d; \
+   c^=d<<8;  f+=c; d+=e; \
+   d^=e>>16; g+=d; e+=f; \
+   e^=f<<10; h+=e; f+=g; \
+   f^=g>>4;  a+=f; g+=h; \
+   g^=h<<8;  b+=g; h+=a; \
+   h^=a>>9;  c+=h; a+=b; \
+}
+
+/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
+void randinit(ctx, flag)
+randctx *ctx;
+word     flag;
+{
+   word i;
+   ub4 a,b,c,d,e,f,g,h;
+   ub4 *m,*r;
+   ctx->randa = ctx->randb = ctx->randc = 0;
+   m=ctx->randmem;
+   r=ctx->randrsl;
+   a=b=c=d=e=f=g=h=0x9e3779b9;  /* the golden ratio */
+
+   for (i=0; i<4; ++i)          /* scramble it */
+   {
+     mix(a,b,c,d,e,f,g,h);
+   }
+
+   if (flag) 
+   {
+     /* initialize using the contents of r[] as the seed */
+     for (i=0; i<RANDSIZ; i+=8)
+     {
+       a+=r[i  ]; b+=r[i+1]; c+=r[i+2]; d+=r[i+3];
+       e+=r[i+4]; f+=r[i+5]; g+=r[i+6]; h+=r[i+7];
+       mix(a,b,c,d,e,f,g,h);
+       m[i  ]=a; m[i+1]=b; m[i+2]=c; m[i+3]=d;
+       m[i+4]=e; m[i+5]=f; m[i+6]=g; m[i+7]=h;
+     }
+     /* do a second pass to make all of the seed affect all of m */
+     for (i=0; i<RANDSIZ; i+=8)
+     {
+       a+=m[i  ]; b+=m[i+1]; c+=m[i+2]; d+=m[i+3];
+       e+=m[i+4]; f+=m[i+5]; g+=m[i+6]; h+=m[i+7];
+       mix(a,b,c,d,e,f,g,h);
+       m[i  ]=a; m[i+1]=b; m[i+2]=c; m[i+3]=d;
+       m[i+4]=e; m[i+5]=f; m[i+6]=g; m[i+7]=h;
+     }
+   }
+   else
+   {
+     /* fill in m[] with messy stuff */
+     for (i=0; i<RANDSIZ; i+=8)
+     {
+       mix(a,b,c,d,e,f,g,h);
+       m[i  ]=a; m[i+1]=b; m[i+2]=c; m[i+3]=d;
+       m[i+4]=e; m[i+5]=f; m[i+6]=g; m[i+7]=h;
+     }
+   }
+
+   isaac(ctx);            /* fill in the first set of results */
+   ctx->randcnt=RANDSIZ;  /* prepare to use the first set of results */
+}
+
+
+#ifdef NEVER
+int main()
+{
+  ub4 i,j;
+  randctx ctx;
+  ctx.randa=ctx.randb=ctx.randc=(ub4)0;
+  for (i=0; i<256; ++i) ctx.randrsl[i]=(ub4)0;
+  randinit(&ctx, TRUE);
+  for (i=0; i<2; ++i)
+  {
+    isaac(&ctx);
+    for (j=0; j<256; ++j)
+    {
+      printf("%.8lx",ctx.randrsl[j]);
+      if ((j&7)==7) printf("\n");
+    }
+  }
+}
+#endif
diff --git a/openaes/src/isaac/rand.h b/openaes/src/isaac/rand.h
new file mode 100644
index 0000000..73f6e3e
--- /dev/null
+++ b/openaes/src/isaac/rand.h
@@ -0,0 +1,56 @@
+/*
+------------------------------------------------------------------------------
+rand.h: definitions for a random number generator
+By Bob Jenkins, 1996, Public Domain
+MODIFIED:
+  960327: Creation (addition of randinit, really)
+  970719: use context, not global variables, for internal state
+  980324: renamed seed to flag
+  980605: recommend RANDSIZL=4 for noncryptography.
+  010626: note this is public domain
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+#include "standard.h"
+#endif
+
+#ifndef RAND
+#define RAND
+#define RANDSIZL   (8)
+#define RANDSIZ    (1<<RANDSIZL)
+
+/* context of random number generator */
+struct randctx
+{
+  ub4 randcnt;
+  ub4 randrsl[RANDSIZ];
+  ub4 randmem[RANDSIZ];
+  ub4 randa;
+  ub4 randb;
+  ub4 randc;
+};
+typedef  struct randctx  randctx;
+
+/*
+------------------------------------------------------------------------------
+ If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed.
+------------------------------------------------------------------------------
+*/
+void randinit(/*_ randctx *r, word flag _*/);
+
+void isaac(/*_ randctx *r _*/);
+
+
+/*
+------------------------------------------------------------------------------
+ Call rand(/o_ randctx *r _o/) to retrieve a single 32-bit random value
+------------------------------------------------------------------------------
+*/
+#define rand(r) \
+   (!(r)->randcnt-- ? \
+     (isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \
+     (r)->randrsl[(r)->randcnt])
+
+#endif  /* RAND */
+
+
diff --git a/openaes/src/isaac/standard.h b/openaes/src/isaac/standard.h
new file mode 100644
index 0000000..202a5d6
--- /dev/null
+++ b/openaes/src/isaac/standard.h
@@ -0,0 +1,57 @@
+/*
+------------------------------------------------------------------------------
+Standard definitions and types, Bob Jenkins
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+# define STANDARD
+# ifndef STDIO
+#  include <stdio.h>
+#  define STDIO
+# endif
+# ifndef STDDEF
+#  include <stddef.h>
+#  define STDDEF
+# endif
+typedef  unsigned long long  ub8;
+#define UB8MAXVAL 0xffffffffffffffffLL
+#define UB8BITS 64
+typedef    signed long long  sb8;
+#define SB8MAXVAL 0x7fffffffffffffffLL
+typedef  unsigned long  int  ub4;   /* unsigned 4-byte quantities */
+#define UB4MAXVAL 0xffffffff
+typedef    signed long  int  sb4;
+#define UB4BITS 32
+#define SB4MAXVAL 0x7fffffff
+typedef  unsigned short int  ub2;
+#define UB2MAXVAL 0xffff
+#define UB2BITS 16
+typedef    signed short int  sb2;
+#define SB2MAXVAL 0x7fff
+typedef  unsigned       char ub1;
+#define UB1MAXVAL 0xff
+#define UB1BITS 8
+typedef    signed       char sb1;   /* signed 1-byte quantities */
+#define SB1MAXVAL 0x7f
+typedef                 int  word;  /* fastest type available */
+
+#define bis(target,mask)  ((target) |=  (mask))
+#define bic(target,mask)  ((target) &= ~(mask))
+#define bit(target,mask)  ((target) &   (mask))
+#ifndef min
+# define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif /* min */
+#ifndef max
+# define max(a,b) (((a)<(b)) ? (b) : (a))
+#endif /* max */
+#ifndef align
+# define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1)))
+#endif /* align */
+#ifndef abs
+# define abs(a)   (((a)>0) ? (a) : -(a))
+#endif
+#define TRUE  1
+#define FALSE 0
+#define SUCCESS 0  /* 1 on VAX */
+
+#endif /* STANDARD */
diff --git a/openaes/src/oaes.c b/openaes/src/oaes.c
new file mode 100644
index 0000000..9a5d268
--- /dev/null
+++ b/openaes/src/oaes.c
@@ -0,0 +1,317 @@
+/* 
+ * ---------------------------------------------------------------------------
+ * OpenAES License
+ * ---------------------------------------------------------------------------
+ * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *   - Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   - 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define OAES_DEBUG 1
+#include "../inc/oaes_lib.h"
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+#include <io.h>
+#else
+__inline static int setmode(int a, int b)
+{
+	return 0;
+}
+#endif
+
+#ifndef __max
+	#define __max(a,b)  (((a) > (b)) ? (a) : (b))
+#endif // __max
+
+#ifndef __min
+	#define __min(a,b)  (((a) < (b)) ? (a) : (b))
+#endif // __min
+
+#define OAES_BUF_LEN_ENC 4096 - 2 * OAES_BLOCK_SIZE
+#define OAES_BUF_LEN_DEC 4096
+
+static void usage( const char * exe_name )
+{
+	if( NULL == exe_name )
+		return;
+	
+	fprintf( stderr,
+			"Usage:\n"
+			"  %s <command> --key <key_data> [options]\n"
+			"\n"
+			"    command:\n"
+			"      enc: encrypt\n"
+			"      dec:  decrypt\n"
+			"\n"
+			"    options:\n"
+			"      --ecb: use ecb mode instead of cbc\n"
+			"      --in <path_in>\n"
+			"      --out <path_out>\n"
+			"\n",
+			exe_name
+	);
+}
+
+int main(int argc, char** argv)
+{
+	size_t _i = 0, _j = 0;
+	OAES_CTX * ctx = NULL;
+	uint8_t _buf_in[OAES_BUF_LEN_DEC];
+	uint8_t *_buf_out = NULL, _key_data[32] = "";
+	size_t _buf_in_len = 0, _buf_out_len = 0, _read_len = 0;
+	size_t _key_data_len = 0;
+	short _is_ecb = 0;
+	char *_file_in = NULL, *_file_out = NULL;
+	int _op = 0;
+	FILE *_f_in = stdin, *_f_out = stdout;
+	
+	fprintf( stderr, "\n"
+		"*******************************************************************************\n"
+		"* OpenAES %-10s                                                          *\n"
+		"* Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com                     *\n"
+		"*******************************************************************************\n\n",
+		OAES_VERSION );
+
+	// pad the key
+	for( _j = 0; _j < 32; _j++ )
+		_key_data[_j] = _j + 1;
+
+	if( argc < 2 )
+	{
+		usage( argv[0] );
+		return EXIT_FAILURE;
+	}
+
+	if( 0 == strcmp( argv[1], "enc" ) )
+	{
+		_op = 0;
+		_read_len = OAES_BUF_LEN_ENC;
+	}
+	else if( 0 == strcmp( argv[1], "dec" ) )
+	{
+		_op = 1;
+		_read_len = OAES_BUF_LEN_DEC;
+	}
+	else
+	{
+		fprintf(stderr, "Error: Unknown command '%s'.", argv[1]);
+		usage( argv[0] );
+		return EXIT_FAILURE;
+	}
+
+	for( _i = 2; _i < argc; _i++ )
+	{
+		int _found = 0;
+
+		if( 0 == strcmp( argv[_i], "--ecb" ) )
+		{
+			_found = 1;
+			_is_ecb = 1;
+		}
+		
+		if( 0 == strcmp( argv[_i], "--key" ) )
+		{
+			_found = 1;
+			_i++; // key_data
+			if( _i >= argc )
+			{
+				fprintf(stderr, "Error: No value specified for '%s'.\n",
+						"--key");
+				usage( argv[0] );
+				return EXIT_FAILURE;
+			}
+			_key_data_len = strlen(argv[_i]);
+			if( 16 >= _key_data_len )
+				_key_data_len = 16;
+			else if( 24 >= _key_data_len )
+				_key_data_len = 24;
+			else
+				_key_data_len = 32;
+			memcpy(_key_data, argv[_i], __min(32, strlen(argv[_i])));
+		}
+		
+		if( 0 == strcmp( argv[_i], "--in" ) )
+		{
+			_found = 1;
+			_i++; // path_in
+			if( _i >= argc )
+			{
+				fprintf(stderr, "Error: No value specified for '%s'.\n",
+						"--in");
+				usage( argv[0] );
+				return EXIT_FAILURE;
+			}
+			_file_in = argv[_i];
+		}
+		
+		if( 0 == strcmp( argv[_i], "--out" ) )
+		{
+			_found = 1;
+			_i++; // path_out
+			if( _i >= argc )
+			{
+				fprintf(stderr, "Error: No value specified for '%s'.\n",
+						"--out");
+				usage( argv[0] );
+				return EXIT_FAILURE;
+			}
+			_file_out = argv[_i];
+		}
+		
+		if( 0 == _found )
+		{
+			fprintf(stderr, "Error: Invalid option '%s'.\n", argv[_i]);
+			usage( argv[0] );
+			return EXIT_FAILURE;
+		}			
+	}
+
+	if( 0 == _key_data_len )
+	{
+		fprintf(stderr, "Error: --key must be specified.\n");
+		return EXIT_FAILURE;
+	}
+
+	if( _file_in )
+	{
+		_f_in = fopen(_file_in, "rb");
+		if( NULL == _f_in )
+		{
+			fprintf(stderr,
+				"Error: Failed to open '-%s' for reading.\n", _file_in);
+			return EXIT_FAILURE;
+		}
+	}
+	else
+	{
+		if( setmode(fileno(stdin), 0x8000) < 0 )
+			fprintf(stderr,"Error: Failed in setmode().\n");
+		_f_in = stdin;
+	}
+
+	if( _file_out )
+	{
+		_f_out = fopen(_file_out, "wb");
+		if( NULL == _f_out )
+		{
+			fprintf(stderr,
+				"Error: Failed to open '-%s' for writing.\n", _file_out);
+			if( _file_in )
+				fclose(_f_in);
+			return EXIT_FAILURE;
+		}
+	}
+	else
+	{
+		if( setmode(fileno(stdout), 0x8000) < 0 )
+			fprintf(stderr, "Error: Failed in setmode().\n");
+		_f_out = stdout;
+	}
+
+	ctx = oaes_alloc();
+	if( NULL == ctx )
+	{
+		fprintf(stderr, "Error: Failed to initialize OAES.\n");
+		if( _file_in )
+			fclose(_f_in);
+		if( _file_out )
+			fclose(_f_out);
+		return EXIT_FAILURE;
+	}
+	if( _is_ecb )
+		if( OAES_RET_SUCCESS != oaes_set_option( ctx, OAES_OPTION_ECB, NULL ) )
+			fprintf(stderr, "Error: Failed to set OAES options.\n");
+
+	oaes_key_import_data( ctx, _key_data, _key_data_len );
+
+	while( _buf_in_len =
+		fread(_buf_in, sizeof(uint8_t), _read_len, _f_in) )
+	{
+		switch(_op)
+		{
+		case 0:
+			if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+					_buf_in, _buf_in_len, NULL, &_buf_out_len ) )
+				fprintf( stderr,
+					"Error: Failed to retrieve required buffer size for "
+					"encryption.\n" );
+			_buf_out = (uint8_t *) calloc( _buf_out_len, sizeof( char ) );
+			if( NULL == _buf_out )
+			{
+				fprintf(stderr,  "Error: Failed to allocate memory.\n" );
+				if( _file_in )
+					fclose(_f_in);
+				if( _file_out )
+					fclose(_f_out);
+				return EXIT_FAILURE;
+			}
+			if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+					_buf_in, _buf_in_len, _buf_out, &_buf_out_len ) )
+				fprintf(stderr, "Error: Encryption failed.\n");
+			fwrite(_buf_out, sizeof(uint8_t), _buf_out_len, _f_out);
+			free(_buf_out);
+			break;
+		case 1:
+			if( OAES_RET_SUCCESS != oaes_decrypt( ctx,
+					_buf_in, _buf_in_len, NULL, &_buf_out_len ) )
+				fprintf( stderr,
+					"Error: Failed to retrieve required buffer size for "
+					"encryption.\n" );
+			_buf_out = (uint8_t *) calloc( _buf_out_len, sizeof( char ) );
+			if( NULL == _buf_out )
+			{
+				fprintf(stderr,  "Error: Failed to allocate memory.\n" );
+				free( _buf_out );
+				if( _file_in )
+					fclose(_f_in);
+				if( _file_out )
+					fclose(_f_out);
+				return EXIT_FAILURE;
+			}
+			if( OAES_RET_SUCCESS != oaes_decrypt( ctx,
+					_buf_in, _buf_in_len, _buf_out, &_buf_out_len ) )
+				fprintf(stderr, "Error: Decryption failed.\n");
+			fwrite(_buf_out, sizeof(uint8_t), _buf_out_len, _f_out);
+			free(_buf_out);
+			break;
+		default:
+			break;
+		}
+	}
+
+
+	if( OAES_RET_SUCCESS !=  oaes_free( &ctx ) )
+		fprintf(stderr, "Error: Failed to uninitialize OAES.\n");
+	
+	if( _file_in )
+		fclose(_f_in);
+	if( _file_out )
+		fclose(_f_out);
+
+	return (EXIT_SUCCESS);
+}
diff --git a/openaes/src/oaes_lib.c b/openaes/src/oaes_lib.c
new file mode 100644
index 0000000..7cfdc1c
--- /dev/null
+++ b/openaes/src/oaes_lib.c
@@ -0,0 +1,1429 @@
+/* 
+ * ---------------------------------------------------------------------------
+ * OpenAES License
+ * ---------------------------------------------------------------------------
+ * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *   - Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   - 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
+ * ---------------------------------------------------------------------------
+ */
+static const char _NR[] = {
+	0x4e,0x61,0x62,0x69,0x6c,0x20,0x53,0x2e,0x20,
+	0x41,0x6c,0x20,0x52,0x61,0x6d,0x6c,0x69,0x00 };
+
+#include <stddef.h>
+#include <time.h> 
+#include <sys/timeb.h>
+#include <malloc.h>
+#include <string.h>
+
+#ifdef WIN32
+#include <process.h>
+#endif
+
+#include "oaes_config.h"
+#include "oaes_lib.h"
+
+#ifdef OAES_HAVE_ISAAC
+#include "rand.h"
+#endif // OAES_HAVE_ISAAC
+
+#define OAES_RKEY_LEN 4
+#define OAES_COL_LEN 4
+#define OAES_ROUND_BASE 7
+
+// the block is padded
+#define OAES_FLAG_PAD 0x01
+
+#ifndef min
+# define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif /* min */
+
+typedef struct _oaes_key
+{
+	size_t data_len;
+	uint8_t *data;
+	size_t exp_data_len;
+	uint8_t *exp_data;
+	size_t num_keys;
+	size_t key_base;
+} oaes_key;
+
+typedef struct _oaes_ctx
+{
+#ifdef OAES_HAVE_ISAAC
+  randctx * rctx;
+#endif // OAES_HAVE_ISAAC
+
+#ifdef OAES_DEBUG
+	oaes_step_cb step_cb;
+#endif // OAES_DEBUG
+
+	oaes_key * key;
+	OAES_OPTION options;
+	uint8_t iv[OAES_BLOCK_SIZE];
+} oaes_ctx;
+
+// "OAES<8-bit header version><8-bit type><16-bit options><8-bit flags><56-bit reserved>"
+static uint8_t oaes_header[OAES_BLOCK_SIZE] = {
+	// 		0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    a,    b,    c,    d,    e,    f,
+	/*0*/	0x4f, 0x41, 0x45, 0x53, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+static uint8_t oaes_gf_8[] = {
+	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
+
+static uint8_t oaes_sub_byte_value[16][16] = {
+	// 		0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    a,    b,    c,    d,    e,    f,
+	/*0*/	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+	/*1*/	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+	/*2*/	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+	/*3*/	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+	/*4*/	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+	/*5*/	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+	/*6*/	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+	/*7*/	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+	/*8*/	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+	/*9*/	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+	/*a*/	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+	/*b*/	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+	/*c*/	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+	/*d*/	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+	/*e*/	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+	/*f*/	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
+};
+
+static uint8_t oaes_inv_sub_byte_value[16][16] = {
+	// 		0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    a,    b,    c,    d,    e,    f,
+	/*0*/	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+	/*1*/	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+	/*2*/	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+	/*3*/	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+	/*4*/	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+	/*5*/	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+	/*6*/	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+	/*7*/	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+	/*8*/	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+	/*9*/	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+	/*a*/	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+	/*b*/	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+	/*c*/	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+	/*d*/	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+	/*e*/	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+	/*f*/	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
+};
+
+static uint8_t oaes_gf_mul_2[16][16] = {
+	// 		0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    a,    b,    c,    d,    e,    f,
+	/*0*/	0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+	/*1*/	0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
+	/*2*/	0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
+	/*3*/	0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
+	/*4*/	0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
+	/*5*/	0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
+	/*6*/	0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+	/*7*/	0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
+	/*8*/	0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
+	/*9*/	0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
+	/*a*/	0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
+	/*b*/	0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
+	/*c*/	0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
+	/*d*/	0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
+	/*e*/	0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
+	/*f*/	0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5,
+};
+
+static uint8_t oaes_gf_mul_3[16][16] = {
+	// 		0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    a,    b,    c,    d,    e,    f,
+	/*0*/	0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
+	/*1*/	0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
+	/*2*/	0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
+	/*3*/	0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
+	/*4*/	0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
+	/*5*/	0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
+	/*6*/	0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
+	/*7*/	0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
+	/*8*/	0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
+	/*9*/	0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
+	/*a*/	0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
+	/*b*/	0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
+	/*c*/	0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
+	/*d*/	0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
+	/*e*/	0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
+	/*f*/	0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a,
+};
+
+static uint8_t oaes_gf_mul_9[16][16] = {
+	// 		0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    a,    b,    c,    d,    e,    f,
+	/*0*/	0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
+	/*1*/	0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
+	/*2*/	0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
+	/*3*/	0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
+	/*4*/	0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
+	/*5*/	0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
+	/*6*/	0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
+	/*7*/	0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
+	/*8*/	0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
+	/*9*/	0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
+	/*a*/	0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
+	/*b*/	0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
+	/*c*/	0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
+	/*d*/	0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
+	/*e*/	0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
+	/*f*/	0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46,
+};
+
+static uint8_t oaes_gf_mul_b[16][16] = {
+	// 		0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    a,    b,    c,    d,    e,    f,
+	/*0*/	0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
+	/*1*/	0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
+	/*2*/	0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
+	/*3*/	0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
+	/*4*/	0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
+	/*5*/	0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
+	/*6*/	0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
+	/*7*/	0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
+	/*8*/	0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
+	/*9*/	0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
+	/*a*/	0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
+	/*b*/	0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
+	/*c*/	0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
+	/*d*/	0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
+	/*e*/	0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
+	/*f*/	0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3,
+};
+
+static uint8_t oaes_gf_mul_d[16][16] = {
+	// 		0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    a,    b,    c,    d,    e,    f,
+	/*0*/	0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
+	/*1*/	0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
+	/*2*/	0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
+	/*3*/	0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
+	/*4*/	0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
+	/*5*/	0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
+	/*6*/	0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
+	/*7*/	0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
+	/*8*/	0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
+	/*9*/	0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
+	/*a*/	0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
+	/*b*/	0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
+	/*c*/	0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
+	/*d*/	0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
+	/*e*/	0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
+	/*f*/	0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97,
+};
+
+static uint8_t oaes_gf_mul_e[16][16] = {
+	// 		0,    1,    2,    3,    4,    5,    6,    7,    8,    9,    a,    b,    c,    d,    e,    f,
+	/*0*/	0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
+	/*1*/	0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
+	/*2*/	0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
+	/*3*/	0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
+	/*4*/	0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
+	/*5*/	0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
+	/*6*/	0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
+	/*7*/	0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
+	/*8*/	0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
+	/*9*/	0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
+	/*a*/	0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
+	/*b*/	0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
+	/*c*/	0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
+	/*d*/	0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
+	/*e*/	0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
+	/*f*/	0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d,
+};
+
+static OAES_RET oaes_sub_byte( uint8_t * byte )
+{
+	size_t _x, _y;
+	
+	if( NULL == byte )
+		return OAES_RET_ARG1;
+
+	_x = _y = *byte;
+	_x &= 0x0f;
+	_y &= 0xf0;
+	_y >>= 4;
+	*byte = oaes_sub_byte_value[_y][_x];
+	
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_inv_sub_byte( uint8_t * byte )
+{
+	size_t _x, _y;
+	
+	if( NULL == byte )
+		return OAES_RET_ARG1;
+
+	_x = _y = *byte;
+	_x &= 0x0f;
+	_y &= 0xf0;
+	_y >>= 4;
+	*byte = oaes_inv_sub_byte_value[_y][_x];
+	
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_word_rot_right( uint8_t word[OAES_COL_LEN] )
+{
+	uint8_t _temp[OAES_COL_LEN];
+	
+	if( NULL == word )
+		return OAES_RET_ARG1;
+
+	memcpy( _temp + 1, word, OAES_COL_LEN - 1 );
+	_temp[0] = word[OAES_COL_LEN - 1];
+	memcpy( word, _temp, OAES_COL_LEN );
+	
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_word_rot_left( uint8_t word[OAES_COL_LEN] )
+{
+	uint8_t _temp[OAES_COL_LEN];
+	
+	if( NULL == word )
+		return OAES_RET_ARG1;
+
+	memcpy( _temp, word + 1, OAES_COL_LEN - 1 );
+	_temp[OAES_COL_LEN - 1] = word[0];
+	memcpy( word, _temp, OAES_COL_LEN );
+	
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_shift_rows( uint8_t block[OAES_BLOCK_SIZE] )
+{
+	uint8_t _temp[OAES_BLOCK_SIZE];
+
+	if( NULL == block )
+		return OAES_RET_ARG1;
+
+	_temp[0x00] = block[0x00];
+	_temp[0x01] = block[0x05];
+	_temp[0x02] = block[0x0a];
+	_temp[0x03] = block[0x0f];
+	_temp[0x04] = block[0x04];
+	_temp[0x05] = block[0x09];
+	_temp[0x06] = block[0x0e];
+	_temp[0x07] = block[0x03];
+	_temp[0x08] = block[0x08];
+	_temp[0x09] = block[0x0d];
+	_temp[0x0a] = block[0x02];
+	_temp[0x0b] = block[0x07];
+	_temp[0x0c] = block[0x0c];
+	_temp[0x0d] = block[0x01];
+	_temp[0x0e] = block[0x06];
+	_temp[0x0f] = block[0x0b];
+	memcpy( block, _temp, OAES_BLOCK_SIZE );
+	
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_inv_shift_rows( uint8_t block[OAES_BLOCK_SIZE] )
+{
+	uint8_t _temp[OAES_BLOCK_SIZE];
+
+	if( NULL == block )
+		return OAES_RET_ARG1;
+
+	_temp[0x00] = block[0x00];
+	_temp[0x01] = block[0x0d];
+	_temp[0x02] = block[0x0a];
+	_temp[0x03] = block[0x07];
+	_temp[0x04] = block[0x04];
+	_temp[0x05] = block[0x01];
+	_temp[0x06] = block[0x0e];
+	_temp[0x07] = block[0x0b];
+	_temp[0x08] = block[0x08];
+	_temp[0x09] = block[0x05];
+	_temp[0x0a] = block[0x02];
+	_temp[0x0b] = block[0x0f];
+	_temp[0x0c] = block[0x0c];
+	_temp[0x0d] = block[0x09];
+	_temp[0x0e] = block[0x06];
+	_temp[0x0f] = block[0x03];
+	memcpy( block, _temp, OAES_BLOCK_SIZE );
+	
+	return OAES_RET_SUCCESS;
+}
+
+static uint8_t oaes_gf_mul(uint8_t left, uint8_t right)
+{
+	size_t _x, _y;
+	
+	_x = _y = left;
+	_x &= 0x0f;
+	_y &= 0xf0;
+	_y >>= 4;
+	
+	switch( right )
+	{
+		case 0x02:
+			return oaes_gf_mul_2[_y][_x];
+			break;
+		case 0x03:
+			return oaes_gf_mul_3[_y][_x];
+			break;
+		case 0x09:
+			return oaes_gf_mul_9[_y][_x];
+			break;
+		case 0x0b:
+			return oaes_gf_mul_b[_y][_x];
+			break;
+		case 0x0d:
+			return oaes_gf_mul_d[_y][_x];
+			break;
+		case 0x0e:
+			return oaes_gf_mul_e[_y][_x];
+			break;
+		default:
+			return left;
+			break;
+	}
+}
+
+static OAES_RET oaes_mix_cols( uint8_t word[OAES_COL_LEN] )
+{
+	uint8_t _temp[OAES_COL_LEN];
+
+	if( NULL == word )
+		return OAES_RET_ARG1;
+	
+	_temp[0] = oaes_gf_mul(word[0], 0x02) ^ oaes_gf_mul( word[1], 0x03 ) ^
+			word[2] ^ word[3];
+	_temp[1] = word[0] ^ oaes_gf_mul( word[1], 0x02 ) ^
+			oaes_gf_mul( word[2], 0x03 ) ^ word[3];
+	_temp[2] = word[0] ^ word[1] ^
+			oaes_gf_mul( word[2], 0x02 ) ^ oaes_gf_mul( word[3], 0x03 );
+	_temp[3] = oaes_gf_mul( word[0], 0x03 ) ^ word[1] ^
+			word[2] ^ oaes_gf_mul( word[3], 0x02 );
+	memcpy( word, _temp, OAES_COL_LEN );
+	
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_inv_mix_cols( uint8_t word[OAES_COL_LEN] )
+{
+	uint8_t _temp[OAES_COL_LEN];
+
+	if( NULL == word )
+		return OAES_RET_ARG1;
+	
+	_temp[0] = oaes_gf_mul( word[0], 0x0e ) ^ oaes_gf_mul( word[1], 0x0b ) ^
+			oaes_gf_mul( word[2], 0x0d ) ^ oaes_gf_mul( word[3], 0x09 );
+	_temp[1] = oaes_gf_mul( word[0], 0x09 ) ^ oaes_gf_mul( word[1], 0x0e ) ^
+			oaes_gf_mul( word[2], 0x0b ) ^ oaes_gf_mul( word[3], 0x0d );
+	_temp[2] = oaes_gf_mul( word[0], 0x0d ) ^ oaes_gf_mul( word[1], 0x09 ) ^
+			oaes_gf_mul( word[2], 0x0e ) ^ oaes_gf_mul( word[3], 0x0b );
+	_temp[3] = oaes_gf_mul( word[0], 0x0b ) ^ oaes_gf_mul( word[1], 0x0d ) ^
+			oaes_gf_mul( word[2], 0x09 ) ^ oaes_gf_mul( word[3], 0x0e );
+	memcpy( word, _temp, OAES_COL_LEN );
+	
+	return OAES_RET_SUCCESS;
+}
+
+OAES_RET oaes_sprintf(
+		char * buf, size_t * buf_len, const uint8_t * data, size_t data_len )
+{
+	size_t _i, _buf_len_in;
+	char _temp[4];
+	
+	if( NULL == buf_len )
+		return OAES_RET_ARG2;
+
+	_buf_len_in = *buf_len;
+	*buf_len = data_len * 3 + data_len / OAES_BLOCK_SIZE + 1;
+	
+	if( NULL == buf )
+		return OAES_RET_SUCCESS;
+
+	if( *buf_len > _buf_len_in )
+		return OAES_RET_BUF;
+
+	if( NULL == data )
+		return OAES_RET_ARG3;
+
+	strcpy( buf, "" );
+	
+	for( _i = 0; _i < data_len; _i++ )
+	{
+		sprintf( _temp, "%02x ", data[_i] );
+		strcat( buf, _temp );
+		if( _i && 0 == ( _i + 1 ) % OAES_BLOCK_SIZE )
+			strcat( buf, "\n" );
+	}
+	
+	return OAES_RET_SUCCESS;
+}
+
+#ifdef OAES_HAVE_ISAAC
+static void oaes_get_seed( char buf[RANDSIZ + 1] )
+{
+	struct timeb timer;
+	struct tm *gmTimer;
+	char * _test = NULL;
+	
+	ftime (&timer);
+	gmTimer = gmtime( &timer.time );
+	_test = (char *) calloc( sizeof( char ), timer.millitm );
+	sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d",
+		gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday,
+		gmTimer->tm_hour, gmTimer->tm_min, gmTimer->tm_sec, timer.millitm,
+		_test + timer.millitm, getpid() );
+	
+	if( _test )
+		free( _test );
+}
+#else
+static uint32_t oaes_get_seed()
+{
+	struct timeb timer;
+	struct tm *gmTimer;
+	char * _test = NULL;
+	uint32_t _ret = 0;
+	
+	ftime (&timer);
+	gmTimer = gmtime( &timer.time );
+	_test = (char *) calloc( sizeof( char ), timer.millitm );
+	_ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday +
+			gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.millitm +
+			(uint32_t) ( _test + timer.millitm ) + getpid();
+
+	if( _test )
+		free( _test );
+	
+	return _ret;
+}
+#endif // OAES_HAVE_ISAAC
+
+static OAES_RET oaes_key_destroy( oaes_key ** key )
+{
+	if( NULL == *key )
+		return OAES_RET_SUCCESS;
+	
+	if( (*key)->data )
+	{
+		free( (*key)->data );
+		(*key)->data = NULL;
+	}
+	
+	if( (*key)->exp_data )
+	{
+		free( (*key)->exp_data );
+		(*key)->exp_data = NULL;
+	}
+	
+	(*key)->data_len = 0;
+	(*key)->exp_data_len = 0;
+	(*key)->num_keys = 0;
+	(*key)->key_base = 0;
+	free( *key );
+	*key = NULL;
+	
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_key_expand( OAES_CTX * ctx )
+{
+	size_t _i, _j;
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == _ctx->key )
+		return OAES_RET_NOKEY;
+	
+	_ctx->key->key_base = _ctx->key->data_len / OAES_RKEY_LEN;
+	_ctx->key->num_keys =  _ctx->key->key_base + OAES_ROUND_BASE;
+					
+	_ctx->key->exp_data_len = _ctx->key->num_keys * OAES_RKEY_LEN * OAES_COL_LEN;
+	_ctx->key->exp_data = (uint8_t *)
+			calloc( _ctx->key->exp_data_len, sizeof( uint8_t ));
+	
+	if( NULL == _ctx->key->exp_data )
+		return OAES_RET_MEM;
+	
+	// the first _ctx->key->data_len are a direct copy
+	memcpy( _ctx->key->exp_data, _ctx->key->data, _ctx->key->data_len );
+
+	// apply ExpandKey algorithm for remainder
+	for( _i = _ctx->key->key_base; _i < _ctx->key->num_keys * OAES_RKEY_LEN; _i++ )
+	{
+		uint8_t _temp[OAES_COL_LEN];
+		
+		memcpy( _temp,
+				_ctx->key->exp_data + ( _i - 1 ) * OAES_RKEY_LEN, OAES_COL_LEN );
+		
+		// transform key column
+		if( 0 == _i % _ctx->key->key_base )
+		{
+			oaes_word_rot_left( _temp );
+
+			for( _j = 0; _j < OAES_COL_LEN; _j++ )
+				oaes_sub_byte( _temp + _j );
+
+			_temp[0] = _temp[0] ^ oaes_gf_8[ _i / _ctx->key->key_base - 1 ];
+		}
+		else if( _ctx->key->key_base > 6 && 4 == _i % _ctx->key->key_base )
+		{
+			for( _j = 0; _j < OAES_COL_LEN; _j++ )
+				oaes_sub_byte( _temp + _j );
+		}
+		
+		for( _j = 0; _j < OAES_COL_LEN; _j++ )
+		{
+			_ctx->key->exp_data[ _i * OAES_RKEY_LEN + _j ] =
+					_ctx->key->exp_data[ ( _i - _ctx->key->key_base ) *
+					OAES_RKEY_LEN + _j ] ^ _temp[_j];
+		}
+	}
+	
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_key_gen( OAES_CTX * ctx, size_t key_size )
+{
+	size_t _i;
+	oaes_key * _key = NULL;
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	OAES_RET _rc = OAES_RET_SUCCESS;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	_key = (oaes_key *) calloc( sizeof( oaes_key ), 1 );
+	
+	if( NULL == _key )
+		return OAES_RET_MEM;
+	
+	if( _ctx->key )
+		oaes_key_destroy( &(_ctx->key) );
+	
+	_key->data_len = key_size;
+	_key->data = (uint8_t *) calloc( key_size, sizeof( uint8_t ));
+	
+	if( NULL == _key->data )
+		return OAES_RET_MEM;
+	
+	for( _i = 0; _i < key_size; _i++ )
+#ifdef OAES_HAVE_ISAAC
+		_key->data[_i] = (uint8_t) rand( _ctx->rctx );
+#else
+		_key->data[_i] = (uint8_t) rand();
+#endif // OAES_HAVE_ISAAC
+	
+	_ctx->key = _key;
+	_rc = _rc || oaes_key_expand( ctx );
+	
+	if( _rc != OAES_RET_SUCCESS )
+	{
+		oaes_key_destroy( &(_ctx->key) );
+		return _rc;
+	}
+	
+	return OAES_RET_SUCCESS;
+}
+
+OAES_RET oaes_key_gen_128( OAES_CTX * ctx )
+{
+	return oaes_key_gen( ctx, 16 );
+}
+
+OAES_RET oaes_key_gen_192( OAES_CTX * ctx )
+{
+	return oaes_key_gen( ctx, 24 );
+}
+
+OAES_RET oaes_key_gen_256( OAES_CTX * ctx )
+{
+	return oaes_key_gen( ctx, 32 );
+}
+
+OAES_RET oaes_key_export( OAES_CTX * ctx,
+		uint8_t * data, size_t * data_len )
+{
+	size_t _data_len_in;
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == _ctx->key )
+		return OAES_RET_NOKEY;
+	
+	if( NULL == data_len )
+		return OAES_RET_ARG3;
+
+	_data_len_in = *data_len;
+	// data + header
+	*data_len = _ctx->key->data_len + OAES_BLOCK_SIZE;
+
+	if( NULL == data )
+		return OAES_RET_SUCCESS;
+	
+	if( _data_len_in < *data_len )
+		return OAES_RET_BUF;
+	
+	// header
+	memcpy( data, oaes_header, OAES_BLOCK_SIZE );
+	data[5] = 0x01;
+	data[7] = _ctx->key->data_len;
+	memcpy( data + OAES_BLOCK_SIZE, _ctx->key->data, _ctx->key->data_len );
+	
+	return OAES_RET_SUCCESS;
+}
+
+OAES_RET oaes_key_export_data( OAES_CTX * ctx,
+		uint8_t * data, size_t * data_len )
+{
+	size_t _data_len_in;
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == _ctx->key )
+		return OAES_RET_NOKEY;
+	
+	if( NULL == data_len )
+		return OAES_RET_ARG3;
+
+	_data_len_in = *data_len;
+	*data_len = _ctx->key->data_len;
+
+	if( NULL == data )
+		return OAES_RET_SUCCESS;
+	
+	if( _data_len_in < *data_len )
+		return OAES_RET_BUF;
+	
+	memcpy( data, _ctx->key->data, *data_len );
+	
+	return OAES_RET_SUCCESS;
+}
+
+OAES_RET oaes_key_import( OAES_CTX * ctx,
+		const uint8_t * data, size_t data_len )
+{
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	OAES_RET _rc = OAES_RET_SUCCESS;
+	int _key_length;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == data )
+		return OAES_RET_ARG2;
+	
+	switch( data_len )
+	{
+		case 16 + OAES_BLOCK_SIZE:
+		case 24 + OAES_BLOCK_SIZE:
+		case 32 + OAES_BLOCK_SIZE:
+			break;
+		default:
+			return OAES_RET_ARG3;
+	}
+	
+	// header
+	if( 0 != memcmp( data, oaes_header, 4 ) )
+		return OAES_RET_HEADER;
+
+	// header version
+	switch( data[4] )
+	{
+		case 0x01:
+			break;
+		default:
+			return OAES_RET_HEADER;
+	}
+	
+	// header type
+	switch( data[5] )
+	{
+		case 0x01:
+			break;
+		default:
+			return OAES_RET_HEADER;
+	}
+	
+	// options
+	_key_length = data[7];
+	switch( _key_length )
+	{
+		case 16:
+		case 24:
+		case 32:
+			break;
+		default:
+			return OAES_RET_HEADER;
+	}
+	
+	if( data_len != _key_length + OAES_BLOCK_SIZE )
+			return OAES_RET_ARG3;
+	
+	if( _ctx->key )
+		oaes_key_destroy( &(_ctx->key) );
+	
+	_ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 );
+	
+	if( NULL == _ctx->key )
+		return OAES_RET_MEM;
+	
+	_ctx->key->data_len = _key_length;
+	_ctx->key->data = (uint8_t *)
+			calloc( _key_length, sizeof( uint8_t ));
+	
+	if( NULL == _ctx->key->data )
+	{
+		oaes_key_destroy( &(_ctx->key) );
+		return OAES_RET_MEM;
+	}
+
+	memcpy( _ctx->key->data, data + OAES_BLOCK_SIZE, _key_length );
+	_rc = _rc || oaes_key_expand( ctx );
+	
+	if( _rc != OAES_RET_SUCCESS )
+	{
+		oaes_key_destroy( &(_ctx->key) );
+		return _rc;
+	}
+	
+	return OAES_RET_SUCCESS;
+}
+
+OAES_RET oaes_key_import_data( OAES_CTX * ctx,
+		const uint8_t * data, size_t data_len )
+{
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	OAES_RET _rc = OAES_RET_SUCCESS;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == data )
+		return OAES_RET_ARG2;
+	
+	switch( data_len )
+	{
+		case 16:
+		case 24:
+		case 32:
+			break;
+		default:
+			return OAES_RET_ARG3;
+	}
+	
+	if( _ctx->key )
+		oaes_key_destroy( &(_ctx->key) );
+	
+	_ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 );
+	
+	if( NULL == _ctx->key )
+		return OAES_RET_MEM;
+	
+	_ctx->key->data_len = data_len;
+	_ctx->key->data = (uint8_t *)
+			calloc( data_len, sizeof( uint8_t ));
+	
+	if( NULL == _ctx->key->data )
+	{
+		oaes_key_destroy( &(_ctx->key) );
+		return OAES_RET_MEM;
+	}
+
+	memcpy( _ctx->key->data, data, data_len );
+	_rc = _rc || oaes_key_expand( ctx );
+	
+	if( _rc != OAES_RET_SUCCESS )
+	{
+		oaes_key_destroy( &(_ctx->key) );
+		return _rc;
+	}
+	
+	return OAES_RET_SUCCESS;
+}
+
+OAES_CTX * oaes_alloc()
+{
+	oaes_ctx * _ctx = (oaes_ctx *) calloc( sizeof( oaes_ctx ), 1 );
+	
+	if( NULL == _ctx )
+		return NULL;
+
+#ifdef OAES_HAVE_ISAAC
+	{
+	  ub4 _i = 0;
+		char _seed[RANDSIZ + 1];
+		
+		_ctx->rctx = (randctx *) calloc( sizeof( randctx ), 1 );
+
+		if( NULL == _ctx->rctx )
+		{
+			free( _ctx );
+			return NULL;
+		}
+
+		oaes_get_seed( _seed );
+		memset( _ctx->rctx->randrsl, 0, RANDSIZ );
+		memcpy( _ctx->rctx->randrsl, _seed, RANDSIZ );
+		randinit( _ctx->rctx, TRUE);
+	}
+#else
+		srand( oaes_get_seed() );
+#endif // OAES_HAVE_ISAAC
+
+	_ctx->key = NULL;
+	oaes_set_option( _ctx, OAES_OPTION_CBC, NULL );
+
+#ifdef OAES_DEBUG
+	_ctx->step_cb = NULL;
+	oaes_set_option( _ctx, OAES_OPTION_STEP_OFF, NULL );
+#endif // OAES_DEBUG
+
+	return (OAES_CTX *) _ctx;
+}
+
+OAES_RET oaes_free( OAES_CTX ** ctx )
+{
+	oaes_ctx ** _ctx = (oaes_ctx **) ctx;
+
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == *_ctx )
+		return OAES_RET_SUCCESS;
+	
+	if( (*_ctx)->key )
+		oaes_key_destroy( &((*_ctx)->key) );
+
+#ifdef OAES_HAVE_ISAAC
+	if( (*_ctx)->rctx )
+	{
+		free( (*_ctx)->rctx );
+		(*_ctx)->rctx = NULL;
+	}
+#endif // OAES_HAVE_ISAAC
+	
+	free( *_ctx );
+	*_ctx = NULL;
+
+	return OAES_RET_SUCCESS;
+}
+
+OAES_RET oaes_set_option( OAES_CTX * ctx,
+		OAES_OPTION option, const void * value )
+{
+	size_t _i;
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+
+	switch( option )
+	{
+		case OAES_OPTION_ECB:
+			_ctx->options &= ~OAES_OPTION_CBC;
+			memset( _ctx->iv, 0, OAES_BLOCK_SIZE );
+			break;
+
+		case OAES_OPTION_CBC:
+			_ctx->options &= ~OAES_OPTION_ECB;
+			if( value )
+				memcpy( _ctx->iv, value, OAES_BLOCK_SIZE );
+			else
+			{
+				for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ )
+#ifdef OAES_HAVE_ISAAC
+					_ctx->iv[_i] = (uint8_t) rand( _ctx->rctx );
+#else
+					_ctx->iv[_i] = (uint8_t) rand();
+#endif // OAES_HAVE_ISAAC
+			}
+			break;
+
+#ifdef OAES_DEBUG
+
+		case OAES_OPTION_STEP_ON:
+			if( value )
+			{
+				_ctx->options &= ~OAES_OPTION_STEP_OFF;
+				_ctx->step_cb = value;
+			}
+			else
+			{
+				_ctx->options &= ~OAES_OPTION_STEP_ON;
+				_ctx->options |= OAES_OPTION_STEP_OFF;
+				_ctx->step_cb = NULL;
+				return OAES_RET_ARG3;
+			}
+			break;
+
+		case OAES_OPTION_STEP_OFF:
+			_ctx->options &= ~OAES_OPTION_STEP_ON;
+			_ctx->step_cb = NULL;
+			break;
+
+#endif // OAES_DEBUG
+
+		default:
+			return OAES_RET_ARG2;
+	}
+
+	_ctx->options |= option;
+
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_encrypt_block(
+		OAES_CTX * ctx, uint8_t * c, size_t c_len )
+{
+	size_t _i, _j;
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == c )
+		return OAES_RET_ARG2;
+	
+	if( c_len != OAES_BLOCK_SIZE )
+		return OAES_RET_ARG3;
+	
+	if( NULL == _ctx->key )
+		return OAES_RET_NOKEY;
+	
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+		_ctx->step_cb( c, "input", 1, NULL );
+#endif // OAES_DEBUG
+
+	// AddRoundKey(State, K0)
+	for( _i = 0; _i < c_len; _i++ )
+		c[_i] = c[_i] ^ _ctx->key->exp_data[_i];
+	
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+	{
+		_ctx->step_cb( _ctx->key->exp_data, "k_sch", 1, NULL );
+		_ctx->step_cb( c, "k_add", 1, NULL );
+	}
+#endif // OAES_DEBUG
+
+	// for round = 1 step 1 to Nr–1
+	for( _i = 1; _i < _ctx->key->num_keys - 1; _i++ )
+	{
+		// SubBytes(state)
+		for( _j = 0; _j < c_len; _j++ )
+			oaes_sub_byte( c + _j );
+
+#ifdef OAES_DEBUG
+		if( _ctx->step_cb )
+			_ctx->step_cb( c, "s_box", _i, NULL );
+#endif // OAES_DEBUG
+
+		// ShiftRows(state)
+		oaes_shift_rows( c );
+		
+#ifdef OAES_DEBUG
+		if( _ctx->step_cb )
+			_ctx->step_cb( c, "s_row", _i, NULL );
+#endif // OAES_DEBUG
+
+		// MixColumns(state)
+		oaes_mix_cols( c );
+		oaes_mix_cols( c + 4 );
+		oaes_mix_cols( c + 8 );
+		oaes_mix_cols( c + 12 );
+		
+#ifdef OAES_DEBUG
+		if( _ctx->step_cb )
+			_ctx->step_cb( c, "m_col", _i, NULL );
+#endif // OAES_DEBUG
+
+		// AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])
+		for( _j = 0; _j < c_len; _j++ )
+			c[_j] = c[_j] ^
+					_ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j];
+
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+	{
+		_ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN,
+				"k_sch", _i, NULL );
+		_ctx->step_cb( c, "k_add", _i, NULL );
+	}
+#endif // OAES_DEBUG
+
+	}
+	
+	// SubBytes(state)
+	for( _i = 0; _i < c_len; _i++ )
+		oaes_sub_byte( c + _i );
+	
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+		_ctx->step_cb( c, "s_box", _ctx->key->num_keys - 1, NULL );
+#endif // OAES_DEBUG
+
+	// ShiftRows(state)
+	oaes_shift_rows( c );
+
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+		_ctx->step_cb( c, "s_row", _ctx->key->num_keys - 1, NULL );
+#endif // OAES_DEBUG
+
+	// AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
+	for( _i = 0; _i < c_len; _i++ )
+		c[_i] = c[_i] ^ _ctx->key->exp_data[
+				( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ];
+
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+	{
+		_ctx->step_cb( _ctx->key->exp_data +
+				( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN,
+				"k_sch", _ctx->key->num_keys - 1, NULL );
+		_ctx->step_cb( c, "output", _ctx->key->num_keys - 1, NULL );
+	}
+#endif // OAES_DEBUG
+
+	return OAES_RET_SUCCESS;
+}
+
+static OAES_RET oaes_decrypt_block(
+		OAES_CTX * ctx, uint8_t * c, size_t c_len )
+{
+	size_t _i, _j;
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == c )
+		return OAES_RET_ARG2;
+	
+	if( c_len != OAES_BLOCK_SIZE )
+		return OAES_RET_ARG3;
+	
+	if( NULL == _ctx->key )
+		return OAES_RET_NOKEY;
+	
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+		_ctx->step_cb( c, "iinput", _ctx->key->num_keys - 1, NULL );
+#endif // OAES_DEBUG
+
+	// AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])
+	for( _i = 0; _i < c_len; _i++ )
+		c[_i] = c[_i] ^ _ctx->key->exp_data[
+				( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ];
+
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+	{
+		_ctx->step_cb( _ctx->key->exp_data +
+				( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN,
+				"ik_sch", _ctx->key->num_keys - 1, NULL );
+		_ctx->step_cb( c, "ik_add", _ctx->key->num_keys - 1, NULL );
+	}
+#endif // OAES_DEBUG
+
+	for( _i = _ctx->key->num_keys - 2; _i > 0; _i-- )
+	{
+		// InvShiftRows(state)
+		oaes_inv_shift_rows( c );
+
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+		_ctx->step_cb( c, "is_row", _i, NULL );
+#endif // OAES_DEBUG
+
+		// InvSubBytes(state)
+		for( _j = 0; _j < c_len; _j++ )
+			oaes_inv_sub_byte( c + _j );
+	
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+		_ctx->step_cb( c, "is_box", _i, NULL );
+#endif // OAES_DEBUG
+
+		// AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])
+		for( _j = 0; _j < c_len; _j++ )
+			c[_j] = c[_j] ^
+					_ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j];
+		
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+	{
+		_ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN,
+				"ik_sch", _i, NULL );
+		_ctx->step_cb( c, "ik_add", _i, NULL );
+	}
+#endif // OAES_DEBUG
+
+		// InvMixColums(state)
+		oaes_inv_mix_cols( c );
+		oaes_inv_mix_cols( c + 4 );
+		oaes_inv_mix_cols( c + 8 );
+		oaes_inv_mix_cols( c + 12 );
+
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+		_ctx->step_cb( c, "im_col", _i, NULL );
+#endif // OAES_DEBUG
+
+	}
+
+	// InvShiftRows(state)
+	oaes_inv_shift_rows( c );
+
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+		_ctx->step_cb( c, "is_row", 1, NULL );
+#endif // OAES_DEBUG
+
+	// InvSubBytes(state)
+	for( _i = 0; _i < c_len; _i++ )
+		oaes_inv_sub_byte( c + _i );
+
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+		_ctx->step_cb( c, "is_box", 1, NULL );
+#endif // OAES_DEBUG
+
+	// AddRoundKey(state, w[0, Nb-1])
+	for( _i = 0; _i < c_len; _i++ )
+		c[_i] = c[_i] ^ _ctx->key->exp_data[_i];
+	
+#ifdef OAES_DEBUG
+	if( _ctx->step_cb )
+	{
+		_ctx->step_cb( _ctx->key->exp_data, "ik_sch", 1, NULL );
+		_ctx->step_cb( c, "ioutput", 1, NULL );
+	}
+#endif // OAES_DEBUG
+
+	return OAES_RET_SUCCESS;
+}
+
+OAES_RET oaes_encrypt( OAES_CTX * ctx,
+		const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len )
+{
+	size_t _i, _j, _c_len_in, _c_data_len;
+	size_t _pad_len = m_len % OAES_BLOCK_SIZE == 0 ?
+			0 : OAES_BLOCK_SIZE - m_len % OAES_BLOCK_SIZE;
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	OAES_RET _rc = OAES_RET_SUCCESS;
+	uint8_t _flags = _pad_len ? OAES_FLAG_PAD : 0;
+	
+	if( NULL == _ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == m )
+		return OAES_RET_ARG2;
+	
+	if( NULL == c_len )
+		return OAES_RET_ARG5;
+	
+	_c_len_in = *c_len;
+	// data + pad
+	_c_data_len = m_len + _pad_len;
+	// header + iv + data + pad
+	*c_len = 2 * OAES_BLOCK_SIZE + m_len + _pad_len;
+
+	if( NULL == c )
+		return OAES_RET_SUCCESS;
+	
+	if( _c_len_in < *c_len )
+		return OAES_RET_BUF;
+	
+	if( NULL == _ctx->key )
+		return OAES_RET_NOKEY;
+	
+	// header
+	memcpy(c, oaes_header, OAES_BLOCK_SIZE );
+	memcpy(c + 6, &_ctx->options, sizeof(_ctx->options));
+	memcpy(c + 8, &_flags, sizeof(_flags));
+	// iv
+	memcpy(c + OAES_BLOCK_SIZE, _ctx->iv, OAES_BLOCK_SIZE );
+	// data
+	memcpy(c + 2 * OAES_BLOCK_SIZE, m, m_len );
+	
+	for( _i = 0; _i < _c_data_len; _i += OAES_BLOCK_SIZE )
+	{
+		uint8_t _block[OAES_BLOCK_SIZE];
+		size_t _block_size = min( m_len - _i, OAES_BLOCK_SIZE );
+
+		memcpy( _block, c + 2 * OAES_BLOCK_SIZE + _i, _block_size );
+		
+		// insert pad
+		for( _j = 0; _j < OAES_BLOCK_SIZE - _block_size; _j++ )
+			_block[ _block_size + _j ] = _j + 1;
+	
+		// CBC
+		if( _ctx->options & OAES_OPTION_CBC )
+		{
+			for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ )
+				_block[_j] = _block[_j] ^ _ctx->iv[_j];
+		}
+
+		_rc = _rc ||
+				oaes_encrypt_block( ctx, _block, OAES_BLOCK_SIZE );
+		memcpy( c + 2 * OAES_BLOCK_SIZE + _i, _block, OAES_BLOCK_SIZE );
+		
+		if( _ctx->options & OAES_OPTION_CBC )
+			memcpy( _ctx->iv, _block, OAES_BLOCK_SIZE );
+	}
+	
+	return _rc;
+}
+
+OAES_RET oaes_decrypt( OAES_CTX * ctx,
+		const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len )
+{
+	size_t _i, _j, _m_len_in;
+	oaes_ctx * _ctx = (oaes_ctx *) ctx;
+	OAES_RET _rc = OAES_RET_SUCCESS;
+	uint8_t _iv[OAES_BLOCK_SIZE];
+	uint8_t _flags;
+	OAES_OPTION _options;
+	
+	if( NULL == ctx )
+		return OAES_RET_ARG1;
+	
+	if( NULL == c )
+		return OAES_RET_ARG2;
+	
+	if( c_len % OAES_BLOCK_SIZE )
+		return OAES_RET_ARG3;
+	
+	if( NULL == m_len )
+		return OAES_RET_ARG5;
+	
+	_m_len_in = *m_len;
+	*m_len = c_len - 2 * OAES_BLOCK_SIZE;
+	
+	if( NULL == m )
+		return OAES_RET_SUCCESS;
+	
+	if( _m_len_in < *m_len )
+		return OAES_RET_BUF;
+	
+	if( NULL == _ctx->key )
+		return OAES_RET_NOKEY;
+	
+	// header
+	if( 0 != memcmp( c, oaes_header, 4 ) )
+		return OAES_RET_HEADER;
+
+	// header version
+	switch( c[4] )
+	{
+		case 0x01:
+			break;
+		default:
+			return OAES_RET_HEADER;
+	}
+	
+	// header type
+	switch( c[5] )
+	{
+		case 0x02:
+			break;
+		default:
+			return OAES_RET_HEADER;
+	}
+	
+	// options
+	memcpy(&_options, c + 6, sizeof(_options));
+	// validate that all options are valid
+	if( _options & ~(
+			  OAES_OPTION_ECB
+			| OAES_OPTION_CBC
+#ifdef OAES_DEBUG
+			| OAES_OPTION_STEP_ON
+			| OAES_OPTION_STEP_OFF
+#endif // OAES_DEBUG
+			) )
+		return OAES_RET_HEADER;
+	if( ( _options & OAES_OPTION_ECB ) &&
+			( _options & OAES_OPTION_CBC ) )
+		return OAES_RET_HEADER;
+	if( _options == OAES_OPTION_NONE )
+		return OAES_RET_HEADER;
+	
+	// flags
+	memcpy(&_flags, c + 8, sizeof(_flags));
+	// validate that all flags are valid
+	if( _flags & ~(
+			  OAES_FLAG_PAD
+			) )
+		return OAES_RET_HEADER;
+
+	// iv
+	memcpy( _iv, c + OAES_BLOCK_SIZE, OAES_BLOCK_SIZE);
+	// data + pad
+	memcpy( m, c + 2 * OAES_BLOCK_SIZE, *m_len );
+	
+	for( _i = 0; _i < *m_len; _i += OAES_BLOCK_SIZE )
+	{
+		if( ( _options & OAES_OPTION_CBC ) && _i > 0 )
+			memcpy( _iv, c + OAES_BLOCK_SIZE + _i, OAES_BLOCK_SIZE );
+		
+		_rc = _rc ||
+				oaes_decrypt_block( ctx, m + _i, min( *m_len - _i, OAES_BLOCK_SIZE ) );
+		
+		// CBC
+		if( _options & OAES_OPTION_CBC )
+		{
+			for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ )
+				m[ _i + _j ] = m[ _i + _j ] ^ _iv[_j];
+		}
+	}
+	
+	// remove pad
+	if( _flags & OAES_FLAG_PAD )
+	{
+		int _is_pad = 1;
+		size_t _temp = (size_t) m[*m_len - 1];
+
+		if( _temp  <= 0x00 || _temp > 0x0f )
+			return OAES_RET_HEADER;
+		for( _i = 0; _i < _temp; _i++ )
+			if( m[*m_len - 1 - _i] != _temp - _i )
+				_is_pad = 0;
+		if( _is_pad )
+		{
+			memset( m + *m_len - _temp, 0, _temp );
+			*m_len -= _temp;
+		}
+		else
+			return OAES_RET_HEADER;
+	}
+	
+	return OAES_RET_SUCCESS;
+}
diff --git a/openaes/test/test_encrypt.c b/openaes/test/test_encrypt.c
new file mode 100644
index 0000000..b229f2d
--- /dev/null
+++ b/openaes/test/test_encrypt.c
@@ -0,0 +1,229 @@
+/* 
+ * ---------------------------------------------------------------------------
+ * OpenAES License
+ * ---------------------------------------------------------------------------
+ * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *   - Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   - 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "oaes_lib.h"
+
+void usage(const char * exe_name)
+{
+	if( NULL == exe_name )
+		return;
+	
+	printf(
+			"Usage:\n"
+			"\t%s [-ecb] [-key < 128 | 192 | 256 >] <text>\n",
+			exe_name
+	);
+}
+
+int main(int argc, char** argv)
+{
+	size_t _i;
+	OAES_CTX * ctx = NULL;
+	uint8_t *_encbuf, *_decbuf;
+	size_t _encbuf_len, _decbuf_len, _buf_len;
+	char *_buf;
+	short _is_ecb = 0;
+	char * _text = NULL;
+	int _key_len = 128;
+	
+	if( argc < 2 )
+	{
+		usage( argv[0] );
+		return EXIT_FAILURE;
+	}
+
+	for( _i = 1; _i < argc; _i++ )
+	{
+		int _found = 0;
+		
+		if( 0 == strcmp( argv[_i], "-ecb" ) )
+		{
+			_found = 1;
+			_is_ecb = 1;
+		}
+		
+		if( 0 == strcmp( argv[_i], "-key" ) )
+		{
+			_found = 1;
+			_i++; // len
+			if( _i >= argc )
+			{
+				printf("Error: No value specified for '-%s'.\n",
+						"key");
+				usage( argv[0] );
+				return EXIT_FAILURE;
+			}
+			_key_len = atoi( argv[_i] );
+			switch( _key_len )
+			{
+				case 128:
+				case 192:
+				case 256:
+					break;
+				default:
+					printf("Error: Invalid value [%d] specified for '-%s'.\n",
+							_key_len, "key");
+					usage( argv[0] );
+					return EXIT_FAILURE;
+			}
+		}
+		
+		if( 0 == _found )
+		{
+			if( _text )
+			{
+				printf("Error: Invalid option '%s'.\n", argv[_i]);
+				usage( argv[0] );
+				return EXIT_FAILURE;
+			}
+			else
+			{
+				_text = (char *) calloc(strlen( argv[_i] ) + 1, sizeof(char));
+				if( NULL == _text )
+				{
+					printf("Error: Failed to allocate memory.\n", argv[_i]);
+					return EXIT_FAILURE;
+				}
+				strcpy( _text, argv[_i] );
+			}
+		}			
+	}
+
+	if( NULL == _text )
+	{
+		usage( argv[0] );
+		return EXIT_FAILURE;
+	}
+
+	oaes_sprintf( NULL, &_buf_len,
+			(const uint8_t *)_text, strlen( _text ) );
+	_buf = (char *) calloc(_buf_len, sizeof(char));
+	printf( "\n***** plaintext  *****\n" );
+	if( _buf )
+	{
+		oaes_sprintf( _buf, &_buf_len,
+				(const uint8_t *)_text, strlen( _text ) );
+		printf( "%s", _buf );
+	}
+	printf( "\n**********************\n" );
+	free( _buf );
+	
+	ctx = oaes_alloc();
+	if( NULL == ctx )
+	{
+		printf("Error: Failed to initialize OAES.\n");
+		free( _text );
+		return EXIT_FAILURE;
+	}
+	if( _is_ecb )
+		if( OAES_RET_SUCCESS != oaes_set_option( ctx, OAES_OPTION_ECB, NULL ) )
+			printf("Error: Failed to set OAES options.\n");
+	switch( _key_len )
+	{
+		case 128:
+			if( OAES_RET_SUCCESS != oaes_key_gen_128(ctx) )
+				printf("Error: Failed to generate OAES %d bit key.\n", _key_len);
+			break;
+		case 192:
+			if( OAES_RET_SUCCESS != oaes_key_gen_192(ctx) )
+				printf("Error: Failed to generate OAES %d bit key.\n", _key_len);
+			break;
+		case 256:
+			if( OAES_RET_SUCCESS != oaes_key_gen_256(ctx) )
+				printf("Error: Failed to generate OAES %d bit key.\n", _key_len);
+			break;
+		default:
+			break;
+	}
+
+	if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+			(const uint8_t *)_text, strlen( _text ), NULL, &_encbuf_len ) )
+		printf("Error: Failed to retrieve required buffer size for encryption.\n");
+	_encbuf = (uint8_t *) calloc( _encbuf_len, sizeof(uint8_t) );
+	if( NULL == _encbuf )
+	{
+		printf( "Error: Failed to allocate memory.\n" );
+		free( _text );
+		return EXIT_FAILURE;
+	}
+	if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+			(const uint8_t *)_text, strlen( _text ), _encbuf, &_encbuf_len ) )
+		printf("Error: Encryption failed.\n");
+
+	if( OAES_RET_SUCCESS != oaes_decrypt( ctx,
+			_encbuf, _encbuf_len, NULL, &_decbuf_len ) )
+		printf("Error: Failed to retrieve required buffer size for encryption.\n");
+	_decbuf = (uint8_t *) calloc( _decbuf_len, sizeof(uint8_t) );
+	if( NULL == _decbuf )
+	{
+		printf( "Error: Failed to allocate memory.\n" );
+		free( _text );
+		free( _encbuf );
+		return EXIT_FAILURE;
+	}
+	if( OAES_RET_SUCCESS != oaes_decrypt( ctx,
+			_encbuf, _encbuf_len, _decbuf, &_decbuf_len ) )
+		printf("Error: Decryption failed.\n");
+
+	if( OAES_RET_SUCCESS !=  oaes_free( &ctx ) )
+		printf("Error: Failed to uninitialize OAES.\n");
+	
+	oaes_sprintf( NULL, &_buf_len, _encbuf, _encbuf_len );
+	_buf = (char *) calloc(_buf_len, sizeof(char));
+	printf( "\n***** cyphertext *****\n" );
+	if( _buf )
+	{
+		oaes_sprintf( _buf, &_buf_len, _encbuf, _encbuf_len );
+		printf( "%s", _buf );
+	}
+	printf( "\n**********************\n" );
+	free( _buf );
+	
+	oaes_sprintf( NULL, &_buf_len, _decbuf, _decbuf_len );
+	_buf = (char *) calloc(_buf_len, sizeof( char));
+	printf( "\n***** plaintext  *****\n" );
+	if( _buf )
+	{
+		oaes_sprintf( _buf, &_buf_len, _decbuf, _decbuf_len );
+		printf( "%s", _buf );
+	}
+	printf( "\n**********************\n\n" );
+	free( _buf );
+	
+	free( _encbuf );
+	free( _decbuf );
+	free( _text );
+
+	return (EXIT_SUCCESS);
+}
diff --git a/openaes/test/test_keys.c b/openaes/test/test_keys.c
new file mode 100644
index 0000000..6a8d625
--- /dev/null
+++ b/openaes/test/test_keys.c
@@ -0,0 +1,248 @@
+/* 
+ * ---------------------------------------------------------------------------
+ * OpenAES License
+ * ---------------------------------------------------------------------------
+ * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *   - Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   - 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 <stdio.h>
+#include <stdlib.h>
+
+#include "oaes_lib.h"
+
+/*
+ * 
+ */
+int main(int argc, char** argv) {
+
+	OAES_CTX * ctx = NULL;
+	uint8_t * _buf;
+	size_t _data_len;
+	FILE * f = NULL;
+	OAES_RET _rc;
+	
+	if( NULL == ( ctx = oaes_alloc() ) )
+	{
+		printf( "Error: Initialization failed.\n" );
+		return 1;
+	}
+	
+	/* ************** Generate 128-bit key and export it **************
+	 * ****************************************************************/
+	if( OAES_RET_SUCCESS != ( _rc = oaes_key_gen_128(ctx) ) )
+	{
+		printf( "Error: Failed to generate 128-bit key [%d].\n", _rc );
+		oaes_free(&ctx);
+		return 1;
+	}
+	
+	if( OAES_RET_SUCCESS != ( _rc = oaes_key_export(ctx, NULL, &_data_len) ) )
+	{
+		printf( "Error: Failed to retrieve key length [%d].\n", _rc );
+		oaes_free(&ctx);
+		return 1;
+	}
+	
+	_buf = (uint8_t *) calloc(_data_len, sizeof(uint8_t));
+	if( _buf )
+	{
+		if( OAES_RET_SUCCESS != ( _rc = oaes_key_export(ctx, _buf, &_data_len) ) )
+		{
+			printf("Error: Failed to export key [%d].\n", _rc);
+			free(_buf);
+			oaes_free(&ctx);
+			return 1;
+		}
+		
+		f = fopen( "key_128", "wb" );
+		if( f )
+		{
+			fwrite(_buf, _data_len, sizeof(uint8_t), f);
+			fclose(f);
+		}
+		free(_buf);
+	}
+	
+	/* ************** Generate 192-bit key and export it **************
+	 * ****************************************************************/
+	if( OAES_RET_SUCCESS != ( _rc = oaes_key_gen_192(ctx) ) )
+	{
+		printf( "Error: Failed to generate 192-bit key [%d].\n", _rc );
+		oaes_free(&ctx);
+		return 1;
+	}
+	
+	if( OAES_RET_SUCCESS != ( _rc = oaes_key_export(ctx, NULL, &_data_len) ) )
+	{
+		printf( "Error: Failed to retrieve key length [%d].\n", _rc );
+		oaes_free(&ctx);
+		return 1;
+	}
+	
+	_buf = (uint8_t *) calloc(_data_len, sizeof(uint8_t));
+	if( _buf )
+	{
+		if( OAES_RET_SUCCESS != ( _rc = oaes_key_export(ctx, _buf, &_data_len) ) )
+		{
+			printf("Error: Failed to export key [%d].\n", _rc);
+			free(_buf);
+			oaes_free(&ctx);
+			return 1;
+		}
+		
+		f = fopen("key_192", "wb");
+		if( f )
+		{
+			fwrite(_buf, _data_len, sizeof(uint8_t), f);
+			fclose(f);
+		}
+		free(_buf);
+	}
+	
+	/* ************** Generate 256-bit key and export it **************
+	 * ****************************************************************/
+	if( OAES_RET_SUCCESS != ( _rc = oaes_key_gen_256(ctx) ) )
+	{
+		printf("Error: Failed to generate 256-bit key [%d].\n", _rc);
+		oaes_free(&ctx);
+		return 1;
+	}
+	
+	if( OAES_RET_SUCCESS != ( _rc = oaes_key_export(ctx, NULL, &_data_len) ) )
+	{
+		printf("Error: Failed to retrieve key length [%d].\n", _rc);
+		oaes_free(&ctx);
+		return 1;
+	}
+	
+	_buf = (uint8_t *) calloc(_data_len, sizeof(uint8_t));
+	if( _buf )
+	{
+		if( OAES_RET_SUCCESS != ( _rc = oaes_key_export(ctx, _buf, &_data_len) ) )
+		{
+			printf("Error: Failed to export key [%d].\n", _rc);
+			free(_buf);
+			oaes_free(&ctx);
+			return 1;
+		}
+		
+		f = fopen("key_256", "wb");
+		if( f )
+		{
+			fwrite(_buf, _data_len, sizeof(uint8_t), f);
+			fclose(f);
+		}
+		free(_buf);
+	}
+	
+	/* ********************** Import 128-bit key **********************
+	 * ****************************************************************/
+	f = fopen("key_128", "rb");
+	if( f )
+	{
+		fseek(f, 0L, SEEK_END);
+		_data_len = ftell(f);
+		fseek(f, 0L, SEEK_SET);
+		_buf = (uint8_t *) calloc(_data_len, sizeof(uint8_t));
+		if( _buf )
+		{
+			fread(_buf, _data_len, sizeof(uint8_t), f);
+			
+			if( OAES_RET_SUCCESS !=
+					( _rc = oaes_key_import(ctx, _buf, _data_len) ) )
+			{
+				printf( "Error: Failed to import key [%d].\n", _rc );
+				free(_buf);
+				fclose(f);
+				oaes_free(&ctx);
+				return 1;
+			}
+			
+			free(_buf);
+		}
+		fclose(f);
+	}
+	
+	/* ********************** Import 192-bit key **********************
+	 * ****************************************************************/
+	f = fopen("key_192", "rb");
+	if( f )
+	{
+		fseek(f, 0L, SEEK_END);
+		_data_len = ftell(f);
+		fseek(f, 0L, SEEK_SET);
+		_buf = (uint8_t *) calloc(_data_len, sizeof(uint8_t));
+		if( _buf )
+		{
+			fread(_buf, _data_len, sizeof(uint8_t), f);
+			
+			if( OAES_RET_SUCCESS !=
+					( _rc = oaes_key_import(ctx, _buf, _data_len) ) )
+			{
+				printf("Error: Failed to import key [%d].\n", _rc);
+				free(_buf);
+				fclose(f);
+				oaes_free(&ctx);
+				return 1;
+			}
+			
+			free(_buf);
+		}
+		fclose(f);
+	}
+	
+	/* ********************** Import 256-bit key **********************
+	 * ****************************************************************/
+	f = fopen("key_256", "rb");
+	if( f )
+	{
+		fseek(f, 0L, SEEK_END);
+		_data_len = ftell(f);
+		fseek(f, 0L, SEEK_SET);
+		_buf = (uint8_t *) calloc(_data_len, sizeof(uint8_t));
+		if( _buf )
+		{
+			fread(_buf, _data_len, sizeof(uint8_t), f);
+			
+			if( OAES_RET_SUCCESS !=
+					( _rc = oaes_key_import(ctx, _buf, _data_len) ) )
+			{
+				printf("Error: Failed to import key [%d].\n", _rc);
+				free(_buf);
+				fclose(f);
+				oaes_free(&ctx);
+				return 1;
+			}
+			
+			free(_buf);
+		}
+		fclose(f);
+	}
+	
+	oaes_free(&ctx);
+
+	return (EXIT_SUCCESS);
+}
diff --git a/openaes/test/test_performance.c b/openaes/test/test_performance.c
new file mode 100644
index 0000000..a005c58
--- /dev/null
+++ b/openaes/test/test_performance.c
@@ -0,0 +1,200 @@
+/* 
+ * ---------------------------------------------------------------------------
+ * OpenAES License
+ * ---------------------------------------------------------------------------
+ * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *   - Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   - 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "oaes_lib.h"
+
+void usage(const char * exe_name)
+{
+	if( NULL == exe_name )
+		return;
+	
+	printf(
+			"Usage:\n"
+			"\t%s [-ecb] [-key < 128 | 192 | 256 >] [-data <data_len>]\n",
+			exe_name
+	);
+}
+
+/*
+ * 
+ */
+int main(int argc, char** argv) {
+
+	size_t _i, _j;
+	time_t _time_start, _time_end;
+	OAES_CTX * ctx = NULL;
+	uint8_t *_encbuf, *_decbuf;
+	size_t _encbuf_len, _decbuf_len;
+	uint8_t _buf[1024 * 1024];
+	short _is_ecb = 0;
+	int _key_len = 128;
+	size_t _data_len = 64;
+	
+	for( _i = 1; _i < argc; _i++ )
+	{
+		int _found = 0;
+		
+		if( 0 == strcmp( argv[_i], "-ecb" ) )
+		{
+			_found = 1;
+			_is_ecb = 1;
+		}
+		
+		if( 0 == strcmp( argv[_i], "-key" ) )
+		{
+			_found = 1;
+			_i++; // key_len
+			if( _i >= argc )
+			{
+				printf("Error: No value specified for '-%s'.\n",
+						"key");
+				usage( argv[0] );
+				return 1;
+			}
+			_key_len = atoi( argv[_i] );
+			switch( _key_len )
+			{
+				case 128:
+				case 192:
+				case 256:
+					break;
+				default:
+					printf("Error: Invalid value [%d] specified for '-%s'.\n",
+							_key_len, "key");
+					usage( argv[0] );
+					return 1;
+			}
+		}
+		
+		if( 0 == strcmp( argv[_i], "-data" ) )
+		{
+			_found = 1;
+			_i++; // data_len
+			if( _i >= argc )
+			{
+				printf("Error: No value specified for '-%s'.\n",
+						"data");
+				usage( argv[0] );
+				return 1;
+			}
+			_data_len = atoi( argv[_i] );
+		}
+		
+		if( 0 == _found )
+		{
+			printf("Error: Invalid option '%s'.\n", argv[_i]);
+			usage( argv[0] );
+			return 1;
+		}			
+	}
+
+	// generate random test data
+	time( &_time_start );
+	srand( _time_start );
+	for( _i = 0; _i < 1024 * 1024; _i++ )
+		_buf[_i] = rand();
+	
+	ctx = oaes_alloc();
+	if( NULL == ctx )
+	{
+		printf("Error: Failed to initialize OAES.\n");
+		return EXIT_FAILURE;
+	}
+	if( _is_ecb )
+		if( OAES_RET_SUCCESS != oaes_set_option( ctx, OAES_OPTION_ECB, NULL ) )
+			printf("Error: Failed to set OAES options.\n");
+	switch( _key_len )
+	{
+		case 128:
+			if( OAES_RET_SUCCESS != oaes_key_gen_128(ctx) )
+				printf("Error: Failed to generate OAES %d bit key.\n", _key_len);
+			break;
+		case 192:
+			if( OAES_RET_SUCCESS != oaes_key_gen_192(ctx) )
+				printf("Error: Failed to generate OAES %d bit key.\n", _key_len);
+			break;
+		case 256:
+			if( OAES_RET_SUCCESS != oaes_key_gen_256(ctx) )
+				printf("Error: Failed to generate OAES %d bit key.\n", _key_len);
+			break;
+		default:
+			break;
+	}
+
+	if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+			(const uint8_t *)_buf, 1024 * 1024, NULL, &_encbuf_len ) )
+		printf("Error: Failed to retrieve required buffer size for encryption.\n");
+	_encbuf = (uint8_t *) calloc( _encbuf_len, sizeof( char ) );
+	if( NULL == _encbuf )
+	{
+		printf( "Error: Failed to allocate memory.\n" );
+		return EXIT_FAILURE;
+	}
+
+	if( OAES_RET_SUCCESS != oaes_decrypt( ctx,
+			_encbuf, _encbuf_len, NULL, &_decbuf_len ) )
+		printf("Error: Failed to retrieve required buffer size for encryption.\n");
+	_decbuf = (uint8_t *) calloc( _decbuf_len, sizeof( char ) );
+	if( NULL == _decbuf )
+	{
+		free( _encbuf );
+		printf( "Error: Failed to allocate memory.\n" );
+		return EXIT_FAILURE;
+	}
+
+	time( &_time_start );
+	
+	for( _i = 0; _i < _data_len; _i++ )
+	{
+		if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+				(const uint8_t *)_buf, 1024 * 1024, _encbuf, &_encbuf_len ) )
+			printf("Error: Encryption failed.\n");
+		if( OAES_RET_SUCCESS !=  oaes_decrypt( ctx,
+				_encbuf, _encbuf_len, _decbuf, &_decbuf_len ) )
+			printf("Error: Decryption failed.\n");
+	}
+	
+	time( &_time_end );
+	printf( "Test encrypt and decrypt:\n\ttime: %lld seconds\n\tdata: %ld MB"
+			"\n\tkey: %d bits\n\tmode: %s\n",
+			_time_end - _time_start, _data_len,
+			_key_len, _is_ecb? "EBC" : "CBC" );
+	free( _encbuf );
+	free( _decbuf );
+	if( OAES_RET_SUCCESS !=  oaes_free( &ctx ) )
+		printf("Error: Failed to uninitialize OAES.\n");
+
+	return (EXIT_SUCCESS);
+}
diff --git a/openaes/test/vt_aes.c b/openaes/test/vt_aes.c
new file mode 100644
index 0000000..1ba755e
--- /dev/null
+++ b/openaes/test/vt_aes.c
@@ -0,0 +1,405 @@
+/* 
+ * ---------------------------------------------------------------------------
+ * OpenAES License
+ * ---------------------------------------------------------------------------
+ * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *   - Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   - 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.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define OAES_DEBUG 1
+#include "oaes_lib.h"
+
+static int _is_step = 1;
+
+static int step_cb(
+		const uint8_t state[OAES_BLOCK_SIZE],
+		const char * step_name,
+		int step_count,
+		void * user_data )
+{
+	size_t _buf_len;
+	char * _buf;
+
+
+	if( NULL == state )
+		return 1;
+
+	oaes_sprintf( NULL, &_buf_len, state, OAES_BLOCK_SIZE );
+	_buf = (char *) calloc( _buf_len, sizeof( char ) );
+
+	if( _buf )
+	{
+		oaes_sprintf( _buf, &_buf_len, state, OAES_BLOCK_SIZE );
+		printf( "round[%2d].%-7s --> %s", step_count, step_name, _buf );
+		free( _buf );
+	}
+	
+	if( 1 == _is_step && '\n' != getchar( ) )
+		_is_step = 0;
+
+	return 0;
+}
+
+static int to_binary(uint8_t * buf, size_t * buf_len, const char * data)
+{
+	size_t _i, _buf_len_in;
+	
+	if( NULL == buf_len )
+		return 1;
+
+	if( NULL == data )
+		return 1;
+
+	_buf_len_in = *buf_len;
+	*buf_len = strlen( data ) / 2;
+	
+	if( NULL == buf )
+		return 0;
+
+	if( *buf_len > _buf_len_in )
+		return 1;
+
+	memset( buf, 0, strlen( data ) / 2 );
+	
+	// lookup ascii table
+	for( _i = 0; _i < strlen( data ); _i++ )
+	{
+		// 0-9
+		if( data[_i] >= 0x30 && data[_i] <= 0x39 )
+			buf[_i / 2] += ( data[_i] - 0x30 ) << ( 4 * ( ( _i + 1 ) % 2 ) ) ;
+		// a-f
+		else if( data[_i] >= 0x41 && data[_i] <= 0x46 )
+			buf[_i / 2] += ( data[_i] - 0x37 ) << ( 4 * ( ( _i + 1 ) % 2 ) );
+		// A-F
+		else if( data[_i] >= 0x61 && data[_i] <= 0x66 )
+			buf[_i / 2] += ( data[_i] - 0x57 ) << ( 4 * ( ( _i + 1 ) % 2 ) );
+		// invalid character
+		else
+			return 1;
+	}
+	
+	return 0;
+}
+
+static void usage(const char * exe_name)
+{
+	if( NULL == exe_name )
+		return;
+	
+	printf(
+			"Usage:\n"
+			"  %s [-step] [-ecb] [[-key < 128 | 192 | 256 | key_data >] [-bin] <text>\n",
+			exe_name
+	);
+}
+
+int main(int argc, char** argv)
+{
+	size_t _i;
+	OAES_CTX * ctx = NULL;
+	uint8_t *_encbuf, *_decbuf, *_key_data = NULL, *_bin_data = NULL;
+	size_t _encbuf_len, _decbuf_len, _buf_len;
+	size_t _key_data_len = 0, _bin_data_len = 0;
+	char *_buf;
+	short _is_ecb = 0, _is_bin = 0;
+	char * _text = NULL, * _key_text = NULL;
+	int _key_len = 128;
+	
+	if( argc < 2 )
+	{
+		usage( argv[0] );
+		return EXIT_FAILURE;
+	}
+
+	for( _i = 1; _i < argc; _i++ )
+	{
+		int _found = 0;
+
+		if( 0 == strcmp( argv[_i], "-nostep" ) )
+		{
+			_found = 1;
+			_is_step = 0;
+		}
+
+		if( 0 == strcmp( argv[_i], "-ecb" ) )
+		{
+			_found = 1;
+			_is_ecb = 1;
+		}
+		
+		if( 0 == strcmp( argv[_i], "-bin" ) )
+		{
+			_found = 1;
+			_is_bin = 1;
+		}
+		
+		if( 0 == strcmp( argv[_i], "-key" ) )
+		{
+			_found = 1;
+			_i++; // len
+			if( _i >= argc )
+			{
+				printf("Error: No value specified for '-%s'.\n",
+						"key");
+				usage( argv[0] );
+				return EXIT_FAILURE;
+			}
+			_key_len = atoi( argv[_i] );
+			switch( _key_len )
+			{
+				case 128:
+				case 192:
+				case 256:
+					break;
+				default:
+					_key_text = argv[_i];
+					if( to_binary( NULL, &_key_data_len, _key_text ) )
+					{
+						printf( "Error: Invalid value [%s] specified for '-%s'.\n",
+								argv[_i], "key" );
+						return EXIT_FAILURE;
+					}
+					switch( _key_data_len )
+					{
+						case 16:
+						case 24:
+						case 32:
+							break;
+						default:
+							printf("Error: key_data [%s] specified for '-%s' has an invalid "
+									"size.\n", argv[_i], "key");
+							usage( argv[0] );
+							return EXIT_FAILURE;
+					}
+			}
+		}
+		
+		if( 0 == _found )
+		{
+			if( _text )
+			{
+				printf("Error: Invalid option '%s'.\n", argv[_i]);
+				usage( argv[0] );
+				return EXIT_FAILURE;
+			}
+			else
+			{
+				_text = argv[_i];
+				if( _is_bin && to_binary( NULL, &_bin_data_len, _text ) )
+				{
+					printf( "Error: Invalid value [%s] specified for '-%s'.\n",
+							argv[_i], "bin" );
+					return EXIT_FAILURE;
+				}
+			}
+		}			
+	}
+
+	if( NULL == _text )
+	{
+		usage( argv[0] );
+		return EXIT_FAILURE;
+	}
+
+	if( _is_step )
+		printf( "\nEnabling step mode, press Return to step.\n\n" );
+
+	if( _is_bin )
+	{
+		_bin_data = (uint8_t *) calloc(_bin_data_len, sizeof(uint8_t));
+		if( NULL == _bin_data )
+		{
+			printf( "Error: Failed to allocate memory.\n" );
+			return EXIT_FAILURE;
+		}
+		if( to_binary( _bin_data, &_bin_data_len, _text ) )
+		{
+			printf( "Error: Could not load data [%s].\n", _text);
+			free( _bin_data );
+			return EXIT_FAILURE;
+		}
+	}
+	else
+	{
+		oaes_sprintf( NULL, &_buf_len, (const uint8_t *)_text, strlen(_text));
+		_buf = (char *) calloc(_buf_len, sizeof(char));
+		printf( "\n***** plaintext  *****\n" );
+		if( _buf )
+		{
+			oaes_sprintf( _buf, &_buf_len,
+					(const uint8_t *)_text, strlen( _text ) );
+			printf( "%s", _buf );
+		}
+		printf( "\n**********************\n" );
+		free( _buf );
+	}
+	
+	ctx = oaes_alloc();
+	if( NULL == ctx )
+	{
+		printf("Error: Failed to initialize OAES.\n");
+		if( _bin_data )
+			free( _bin_data );
+		return EXIT_FAILURE;
+	}
+	if( OAES_RET_SUCCESS != oaes_set_option( ctx, OAES_OPTION_STEP_ON, step_cb ) )
+		printf("Error: Failed to set OAES options.\n");
+	if( _is_ecb )
+		if( OAES_RET_SUCCESS != oaes_set_option( ctx, OAES_OPTION_ECB, NULL ) )
+			printf("Error: Failed to set OAES options.\n");
+
+	if( _key_text )
+	{
+		_key_data = (uint8_t *) calloc(_key_data_len, sizeof(uint8_t));
+		if( NULL == _key_data )
+		{
+			printf( "Error: Failed to allocate memory.\n" );
+			if( _bin_data )
+				free( _bin_data );
+			return EXIT_FAILURE;
+		}
+		if( to_binary( _key_data, &_key_data_len, _key_text ) )
+		{
+			printf( "Error: Could not load key [%s].\n", _key_text);
+			free( _key_data );
+			return EXIT_FAILURE;
+		}
+		oaes_key_import_data( ctx, _key_data, _key_data_len );
+	}
+	else
+		switch( _key_len )
+		{
+			case 128:
+				if( OAES_RET_SUCCESS != oaes_key_gen_128(ctx) )
+					printf("Error: Failed to generate OAES %d bit key.\n", _key_len);
+				break;
+			case 192:
+				if( OAES_RET_SUCCESS != oaes_key_gen_192(ctx) )
+					printf("Error: Failed to generate OAES %d bit key.\n", _key_len);
+				break;
+			case 256:
+				if( OAES_RET_SUCCESS != oaes_key_gen_256(ctx) )
+					printf("Error: Failed to generate OAES %d bit key.\n", _key_len);
+				break;
+			default:
+				break;
+		}
+
+	if( _bin_data )
+	{
+		if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+				_bin_data, _bin_data_len, NULL, &_encbuf_len ) )
+			printf("Error: Failed to retrieve required buffer size for encryption.\n");
+		_encbuf = (uint8_t *) calloc(_encbuf_len, sizeof(uint8_t));
+		if( NULL == _encbuf )
+		{
+			printf( "Error: Failed to allocate memory.\n" );
+			if( _key_data )
+				free( _key_data );
+			free( _bin_data );
+			return EXIT_FAILURE;
+		}
+		printf( "\n" );
+		if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+				_bin_data, _bin_data_len, _encbuf, &_encbuf_len ) )
+			printf("Error: Encryption failed.\n");
+		printf( "\n**********************\n\n" );
+	}
+	else
+	{
+		if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+				(const uint8_t *)_text, strlen( _text ), NULL, &_encbuf_len ) )
+			printf("Error: Failed to retrieve required buffer size for encryption.\n");
+		_encbuf = (uint8_t *) calloc(_encbuf_len, sizeof(uint8_t));
+		if( NULL == _encbuf )
+		{
+			printf( "Error: Failed to allocate memory.\n" );
+			if( _key_data )
+				free( _key_data );
+			return EXIT_FAILURE;
+		}
+		printf( "\n" );
+		if( OAES_RET_SUCCESS != oaes_encrypt( ctx,
+				(const uint8_t *)_text, strlen( _text ), _encbuf, &_encbuf_len ) )
+			printf("Error: Encryption failed.\n");
+		printf( "\n**********************\n\n" );
+	}
+
+	if( OAES_RET_SUCCESS != oaes_decrypt( ctx,
+			_encbuf, _encbuf_len, NULL, &_decbuf_len ) )
+		printf("Error: Failed to retrieve required buffer size for encryption.\n");
+	_decbuf = (uint8_t *) calloc(_decbuf_len, sizeof(uint8_t));
+	if( NULL == _decbuf )
+	{
+		printf( "Error: Failed to allocate memory.\n" );
+		if( _key_data )
+			free( _key_data );
+		if( _bin_data )
+			free( _bin_data );
+		free( _encbuf );
+		return EXIT_FAILURE;
+	}
+	if( OAES_RET_SUCCESS != oaes_decrypt( ctx,
+			_encbuf, _encbuf_len, _decbuf, &_decbuf_len ) )
+		printf("Error: Decryption failed.\n");
+
+	if( OAES_RET_SUCCESS !=  oaes_free( &ctx ) )
+		printf("Error: Failed to uninitialize OAES.\n");
+	
+	oaes_sprintf( NULL, &_buf_len, _encbuf, _encbuf_len );
+	_buf = (char *) calloc(_buf_len, sizeof(char));
+	printf( "\n***** cyphertext *****\n" );
+	if( _buf )
+	{
+		oaes_sprintf( _buf, &_buf_len, _encbuf, _encbuf_len );
+		printf( "%s", _buf );
+	}
+	printf( "\n**********************\n" );
+	free( _buf );
+	
+	oaes_sprintf( NULL, &_buf_len, _decbuf, _decbuf_len );
+	_buf = (char *) calloc(_buf_len, sizeof(char));
+	printf( "\n***** plaintext  *****\n" );
+	if( _buf )
+	{
+		oaes_sprintf( _buf, &_buf_len, _decbuf, _decbuf_len );
+		printf( "%s", _buf );
+	}
+	printf( "\n**********************\n\n" );
+	free( _buf );
+	
+	free( _encbuf );
+	free( _decbuf );
+	if( _key_data )
+		free( _key_data );
+	if( _bin_data )
+		free( _bin_data );
+
+	return (EXIT_SUCCESS);
+}
diff --git a/openrecoveryscript.cpp b/openrecoveryscript.cpp
new file mode 100644
index 0000000..0b71a42
--- /dev/null
+++ b/openrecoveryscript.cpp
@@ -0,0 +1,565 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+#include <vector>
+#include <dirent.h>
+#include <time.h>
+#include <errno.h>
+#include <iostream>
+#include <fstream>
+
+#include "twrp-functions.hpp"
+#include "partitions.hpp"
+#include "twcommon.h"
+#include "openrecoveryscript.hpp"
+#include "variables.h"
+#include "adb_install.h"
+#include "data.hpp"
+extern "C" {
+	#include "twinstall.h"
+	#include "gui/gui.h"
+	int TWinstall_zip(const char* path, int* wipe_cache);
+}
+
+#define SCRIPT_COMMAND_SIZE 512
+
+int OpenRecoveryScript::check_for_script_file(void) {
+	if (!PartitionManager.Mount_By_Path(SCRIPT_FILE_CACHE, false)) {
+		LOGERR("Unable to mount /cache for OpenRecoveryScript support.\n");
+		return 0;
+	}
+	if (TWFunc::Path_Exists(SCRIPT_FILE_CACHE)) {
+		LOGINFO("Script file found: '%s'\n", SCRIPT_FILE_CACHE);
+		// Copy script file to /tmp
+		TWFunc::copy_file(SCRIPT_FILE_CACHE, SCRIPT_FILE_TMP, 0755);
+		// Delete the file from /cache
+		unlink(SCRIPT_FILE_CACHE);
+		return 1;
+	}
+	return 0;
+}
+
+int OpenRecoveryScript::copy_script_file(string filename) {
+	if (TWFunc::Path_Exists(filename)) {
+		LOGINFO("Script file found: '%s'\n", filename.c_str());
+		if (filename == SCRIPT_FILE_TMP)
+			return 1; // file is already in the right place
+		// Copy script file to /tmp
+		TWFunc::copy_file(filename, SCRIPT_FILE_TMP, 0755);
+		// Delete the old file
+		unlink(filename.c_str());
+		return 1;
+	}
+	return 0;
+}
+
+int OpenRecoveryScript::run_script_file(void) {
+	FILE *fp = fopen(SCRIPT_FILE_TMP, "r");
+	int ret_val = 0, cindex, line_len, i, remove_nl, install_cmd = 0, sideload = 0;
+	char script_line[SCRIPT_COMMAND_SIZE], command[SCRIPT_COMMAND_SIZE],
+		 value[SCRIPT_COMMAND_SIZE], mount[SCRIPT_COMMAND_SIZE],
+		 value1[SCRIPT_COMMAND_SIZE], value2[SCRIPT_COMMAND_SIZE];
+	char *val_start, *tok;
+
+	if (fp != NULL) {
+		DataManager::SetValue(TW_SIMULATE_ACTIONS, 0);
+		DataManager::SetValue("ui_progress", 0); // Reset the progress bar
+		while (fgets(script_line, SCRIPT_COMMAND_SIZE, fp) != NULL && ret_val == 0) {
+			cindex = 0;
+			line_len = strlen(script_line);
+			if (line_len < 2)
+				continue; // there's a blank line or line is too short to contain a command
+			//gui_print("script line: '%s'\n", script_line);
+			for (i=0; i<line_len; i++) {
+				if ((int)script_line[i] == 32) {
+					cindex = i;
+					i = line_len;
+				}
+			}
+			memset(command, 0, sizeof(command));
+			memset(value, 0, sizeof(value));
+			if ((int)script_line[line_len - 1] == 10)
+					remove_nl = 2;
+				else
+					remove_nl = 1;
+			if (cindex != 0) {
+				strncpy(command, script_line, cindex);
+				LOGINFO("command is: '%s'\n", command);
+				val_start = script_line;
+				val_start += cindex + 1;
+				if ((int) *val_start == 32)
+					val_start++; //get rid of space
+				if ((int) *val_start == 51)
+					val_start++; //get rid of = at the beginning
+				if ((int) *val_start == 32)
+					val_start++; //get rid of space
+				strncpy(value, val_start, line_len - cindex - remove_nl);
+				LOGINFO("value is: '%s'\n", value);
+			} else {
+				strncpy(command, script_line, line_len - remove_nl + 1);
+				gui_print("command is: '%s' and there is no value\n", command);
+			}
+			if (strcmp(command, "install") == 0) {
+				// Install Zip
+				DataManager::SetValue("tw_action_text2", "Installing Zip");
+				PartitionManager.Mount_All_Storage();
+				ret_val = Install_Command(value);
+				install_cmd = -1;
+			} else if (strcmp(command, "wipe") == 0) {
+				// Wipe
+				if (strcmp(value, "cache") == 0 || strcmp(value, "/cache") == 0) {
+					gui_print("-- Wiping Cache Partition...\n");
+					PartitionManager.Wipe_By_Path("/cache");
+					gui_print("-- Cache Partition Wipe Complete!\n");
+				} else if (strcmp(value, "dalvik") == 0 || strcmp(value, "dalvick") == 0 || strcmp(value, "dalvikcache") == 0 || strcmp(value, "dalvickcache") == 0) {
+					gui_print("-- Wiping Dalvik Cache...\n");
+					PartitionManager.Wipe_Dalvik_Cache();
+					gui_print("-- Dalvik Cache Wipe Complete!\n");
+				} else if (strcmp(value, "data") == 0 || strcmp(value, "/data") == 0 || strcmp(value, "factory") == 0 || strcmp(value, "factoryreset") == 0) {
+					gui_print("-- Wiping Data Partition...\n");
+					PartitionManager.Factory_Reset();
+					gui_print("-- Data Partition Wipe Complete!\n");
+				} else {
+					LOGERR("Error with wipe command value: '%s'\n", value);
+					ret_val = 1;
+				}
+			} else if (strcmp(command, "backup") == 0) {
+				// Backup
+				DataManager::SetValue("tw_action_text2", "Backing Up");
+				tok = strtok(value, " ");
+				strcpy(value1, tok);
+				tok = strtok(NULL, " ");
+				if (tok != NULL) {
+					memset(value2, 0, sizeof(value2));
+					strcpy(value2, tok);
+					line_len = strlen(tok);
+					if ((int)value2[line_len - 1] == 10 || (int)value2[line_len - 1] == 13) {
+						if ((int)value2[line_len - 1] == 10 || (int)value2[line_len - 1] == 13)
+							remove_nl = 2;
+						else
+							remove_nl = 1;
+					} else
+						remove_nl = 0;
+					strncpy(value2, tok, line_len - remove_nl);
+					DataManager::SetValue(TW_BACKUP_NAME, value2);
+					gui_print("Backup folder set to '%s'\n", value2);
+					if (PartitionManager.Check_Backup_Name(true) != 0) {
+						ret_val = 1;
+						continue;
+					}
+				} else {
+					char empt[50];
+					strcpy(empt, "(Current Date)");
+					DataManager::SetValue(TW_BACKUP_NAME, empt);
+				}
+				ret_val = Backup_Command(value1);
+			} else if (strcmp(command, "restore") == 0) {
+				// Restore
+				DataManager::SetValue("tw_action_text2", "Restoring");
+				PartitionManager.Mount_All_Storage();
+				DataManager::SetValue(TW_SKIP_MD5_CHECK_VAR, 0);
+				char folder_path[512], partitions[512];
+
+				string val = value, restore_folder, restore_partitions;
+				size_t pos = val.find_last_of(" ");
+				if (pos == string::npos) {
+					restore_folder = value;
+					partitions[0] = '\0';
+				} else {
+					restore_folder = val.substr(0, pos);
+					restore_partitions = val.substr(pos + 1, val.size() - pos - 1);
+					strcpy(partitions, restore_partitions.c_str());
+				}
+				strcpy(folder_path, restore_folder.c_str());
+				LOGINFO("Restore folder is: '%s' and partitions: '%s'\n", folder_path, partitions);
+				gui_print("Restoring '%s'\n", folder_path);
+
+				if (folder_path[0] != '/') {
+					char backup_folder[512];
+					string folder_var;
+					std::vector<PartitionList> Storage_List;
+
+					PartitionManager.Get_Partition_List("storage", &Storage_List);
+					int listSize = Storage_List.size();
+					for (int i = 0; i < listSize; i++) {
+						if (PartitionManager.Is_Mounted_By_Path(Storage_List.at(i).Mount_Point)) {
+							DataManager::SetValue("tw_storage_path", Storage_List.at(i).Mount_Point);
+							DataManager::GetValue(TW_BACKUPS_FOLDER_VAR, folder_var);
+							sprintf(backup_folder, "%s/%s", folder_var.c_str(), folder_path);
+							if (TWFunc::Path_Exists(backup_folder)) {
+								strcpy(folder_path, backup_folder);
+								break;
+							}
+						}
+					}
+				} else {
+					if (folder_path[strlen(folder_path) - 1] == '/')
+						strcat(folder_path, ".");
+					else
+						strcat(folder_path, "/.");
+				}
+				if (!TWFunc::Path_Exists(folder_path)) {
+					gui_print("Unable to locate backup '%s'\n", folder_path);
+					ret_val = 1;
+					continue;
+				}
+				DataManager::SetValue("tw_restore", folder_path);
+
+				PartitionManager.Set_Restore_Files(folder_path);
+				string Partition_List;
+				int is_encrypted = 0;
+				DataManager::GetValue("tw_restore_encrypted", is_encrypted);
+				DataManager::GetValue("tw_restore_list", Partition_List);
+				if (strlen(partitions) != 0) {
+					string Restore_List;
+
+					memset(value2, 0, sizeof(value2));
+					strcpy(value2, partitions);
+					gui_print("Setting restore options: '%s':\n", value2);
+					line_len = strlen(value2);
+					for (i=0; i<line_len; i++) {
+						if ((value2[i] == 'S' || value2[i] == 's') && Partition_List.find("/system;") != string::npos) {
+							Restore_List += "/system;";
+							gui_print("System\n");
+						} else if ((value2[i] == 'D' || value2[i] == 'd') && Partition_List.find("/data;") != string::npos) {
+							Restore_List += "/data;";
+							gui_print("Data\n");
+						} else if ((value2[i] == 'C' || value2[i] == 'c') && Partition_List.find("/cache;") != string::npos) {
+							Restore_List += "/cache;";
+							gui_print("Cache\n");
+						} else if ((value2[i] == 'R' || value2[i] == 'r') && Partition_List.find("/recovery;") != string::npos) {
+							gui_print("Recovery -- Not allowed to restore recovery\n");
+						} else if (value2[i] == '1' && DataManager::GetIntValue(TW_RESTORE_SP1_VAR) > 0) {
+							gui_print("%s\n", "Special1 -- No Longer Supported...");
+						} else if (value2[i] == '2' && DataManager::GetIntValue(TW_RESTORE_SP2_VAR) > 0) {
+							gui_print("%s\n", "Special2 -- No Longer Supported...");
+						} else if (value2[i] == '3' && DataManager::GetIntValue(TW_RESTORE_SP3_VAR) > 0) {
+							gui_print("%s\n", "Special3 -- No Longer Supported...");
+						} else if ((value2[i] == 'B' || value2[i] == 'b') && Partition_List.find("/boot;") != string::npos) {
+							Restore_List += "/boot;";
+							gui_print("Boot\n");
+						} else if ((value2[i] == 'A' || value2[i] == 'a')  && Partition_List.find("/and-sec;") != string::npos) {
+							Restore_List += "/and-sec;";
+							gui_print("Android Secure\n");
+						} else if ((value2[i] == 'E' || value2[i] == 'e')  && Partition_List.find("/sd-ext;") != string::npos) {
+							Restore_List += "/sd-ext;";
+							gui_print("SD-Ext\n");
+						} else if (value2[i] == 'M' || value2[i] == 'm') {
+							DataManager::SetValue(TW_SKIP_MD5_CHECK_VAR, 1);
+							gui_print("MD5 check skip is on\n");
+						}
+					}
+
+					DataManager::SetValue("tw_restore_selected", Restore_List);
+				} else {
+					DataManager::SetValue("tw_restore_selected", Partition_List);
+				}
+				if (is_encrypted) {
+					LOGERR("Unable to use OpenRecoveryScript to restore an encrypted backup.\n");
+					ret_val = 1;
+				} else if (!PartitionManager.Run_Restore(folder_path))
+					ret_val = 1;
+				else
+					gui_print("Restore complete!\n");
+			} else if (strcmp(command, "mount") == 0) {
+				// Mount
+				DataManager::SetValue("tw_action_text2", "Mounting");
+				if (value[0] != '/') {
+					strcpy(mount, "/");
+					strcat(mount, value);
+				} else
+					strcpy(mount, value);
+				if (PartitionManager.Mount_By_Path(mount, true))
+					gui_print("Mounted '%s'\n", mount);
+			} else if (strcmp(command, "unmount") == 0 || strcmp(command, "umount") == 0) {
+				// Unmount
+				DataManager::SetValue("tw_action_text2", "Unmounting");
+				if (value[0] != '/') {
+					strcpy(mount, "/");
+					strcat(mount, value);
+				} else
+					strcpy(mount, value);
+				if (PartitionManager.UnMount_By_Path(mount, true))
+					gui_print("Unmounted '%s'\n", mount);
+			} else if (strcmp(command, "set") == 0) {
+				// Set value
+				tok = strtok(value, " ");
+				strcpy(value1, tok);
+				tok = strtok(NULL, " ");
+				strcpy(value2, tok);
+				gui_print("Setting '%s' to '%s'\n", value1, value2);
+				DataManager::SetValue(value1, value2);
+			} else if (strcmp(command, "mkdir") == 0) {
+				// Make directory (recursive)
+				DataManager::SetValue("tw_action_text2", "Making Directory");
+				gui_print("Making directory (recursive): '%s'\n", value);
+				if (TWFunc::Recursive_Mkdir(value)) {
+					LOGERR("Unable to create folder: '%s'\n", value);
+					ret_val = 1;
+				}
+			} else if (strcmp(command, "reboot") == 0) {
+				if (strlen(value) && strcmp(value, "recovery") == 0)
+					TWFunc::tw_reboot(rb_recovery);
+				else if (strlen(value) && strcmp(value, "poweroff") == 0)
+					TWFunc::tw_reboot(rb_poweroff);
+				else if (strlen(value) && strcmp(value, "bootloader") == 0)
+					TWFunc::tw_reboot(rb_bootloader);
+				else if (strlen(value) && strcmp(value, "download") == 0)
+					TWFunc::tw_reboot(rb_download);
+				else
+					TWFunc::tw_reboot(rb_system);
+			} else if (strcmp(command, "cmd") == 0) {
+				DataManager::SetValue("tw_action_text2", "Running Command");
+				if (cindex != 0) {
+					TWFunc::Exec_Cmd(value);
+				} else {
+					LOGERR("No value given for cmd\n");
+				}
+			} else if (strcmp(command, "print") == 0) {
+				gui_print("%s\n", value);
+			} else if (strcmp(command, "sideload") == 0) {
+				// ADB Sideload
+				DataManager::SetValue("tw_action_text2", "ADB Sideload");
+				install_cmd = -1;
+
+				int wipe_cache = 0;
+				string result, Sideload_File;
+
+				if (!PartitionManager.Mount_Current_Storage(true)) {
+					ret_val = 1; // failure
+				} else {
+					Sideload_File = DataManager::GetCurrentStoragePath() + "/sideload.zip";
+					if (TWFunc::Path_Exists(Sideload_File)) {
+						unlink(Sideload_File.c_str());
+					}
+					gui_print("Starting ADB sideload feature...\n");
+					DataManager::SetValue("tw_has_cancel", 1);
+					DataManager::SetValue("tw_cancel_action", "adbsideloadcancel");
+					ret_val = apply_from_adb(Sideload_File.c_str());
+					DataManager::SetValue("tw_has_cancel", 0);
+					if (ret_val != 0)
+						ret_val = 1; // failure
+					else if (TWinstall_zip(Sideload_File.c_str(), &wipe_cache) == 0) {
+						if (wipe_cache)
+							PartitionManager.Wipe_By_Path("/cache");
+					} else {
+						ret_val = 1; // failure
+					}
+					sideload = 1; // Causes device to go to the home screen afterwards
+					gui_print("Sideload finished.\n");
+				}
+			} else if (strcmp(command, "fixperms") == 0 || strcmp(command, "fixpermissions") == 0) {
+				ret_val = PartitionManager.Fix_Permissions();
+				if (ret_val != 0)
+					ret_val = 1; // failure
+			} else if (strcmp(command, "decrypt") == 0) {
+				if (*value) {
+					ret_val = PartitionManager.Decrypt_Device(value);
+					if (ret_val != 0)
+						ret_val = 1; // failure
+				} else {
+					LOGERR("No password provided.\n");
+					ret_val = 1; // failure
+				}
+			} else {
+				LOGERR("Unrecognized script command: '%s'\n", command);
+				ret_val = 1;
+			}
+		}
+		fclose(fp);
+		gui_print("Done processing script file\n");
+	} else {
+		LOGERR("Error opening script file '%s'\n", SCRIPT_FILE_TMP);
+		return 1;
+	}
+	if (install_cmd && DataManager::GetIntValue(TW_HAS_INJECTTWRP) == 1 && DataManager::GetIntValue(TW_INJECT_AFTER_ZIP) == 1) {
+		gui_print("Injecting TWRP into boot image...\n");
+		TWPartition* Boot = PartitionManager.Find_Partition_By_Path("/boot");
+		if (Boot == NULL || Boot->Current_File_System != "emmc")
+			TWFunc::Exec_Cmd("injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash");
+		else {
+			string injectcmd = "injecttwrp --dump /tmp/backup_recovery_ramdisk.img /tmp/injected_boot.img --flash bd=" + Boot->Actual_Block_Device;
+			TWFunc::Exec_Cmd(injectcmd.c_str());
+		}
+		gui_print("TWRP injection complete.\n");
+	}
+	if (sideload)
+		ret_val = 1; // Forces booting to the home page after sideload
+	return ret_val;
+}
+
+int OpenRecoveryScript::Insert_ORS_Command(string Command) {
+	ofstream ORSfile(SCRIPT_FILE_TMP);
+	if (ORSfile.is_open()) {
+		//if (Command.substr(Command.size() - 1, 1) != "\n")
+		//	Command += "\n";
+		LOGINFO("Inserting '%s'\n", Command.c_str());
+		ORSfile << Command.c_str();
+		ORSfile.close();
+		return 1;
+	}
+	LOGERR("Unable to append '%s' to '%s'\n", Command.c_str(), SCRIPT_FILE_TMP);
+	return 0;
+}
+
+int OpenRecoveryScript::Install_Command(string Zip) {
+	// Install zip
+	string ret_string;
+	int ret_val = 0, wipe_cache = 0;
+	std::vector<PartitionList> Storage_List;
+	string Full_Path;
+
+	PartitionManager.Mount_All_Storage();
+	PartitionManager.Get_Partition_List("storage", &Storage_List);
+	int listSize = Storage_List.size();
+	for (int i = 0; i < listSize; i++) {
+		if (PartitionManager.Is_Mounted_By_Path(Storage_List.at(i).Mount_Point)) {
+			Full_Path = Storage_List.at(i).Mount_Point + "/" + Zip;
+			if (TWFunc::Path_Exists(Full_Path)) {
+				Zip = Full_Path;
+				break;
+			}
+			Full_Path = Zip;
+			LOGINFO("Trying to find zip '%s' on '%s'...\n", Full_Path.c_str(), Storage_List.at(i).Mount_Point.c_str());
+			ret_string = Locate_Zip_File(Full_Path, Storage_List.at(i).Mount_Point);
+			if (!ret_string.empty()) {
+				Zip = ret_string;
+				break;
+			}
+		}
+	}
+
+	if (!TWFunc::Path_Exists(Zip)) {
+		// zip file doesn't exist
+		gui_print("Unable to locate zip file '%s'.\n", Zip.c_str());
+		ret_val = 1;
+	} else {
+		gui_print("Installing zip file '%s'\n", Zip.c_str());
+		ret_val = TWinstall_zip(Zip.c_str(), &wipe_cache);
+	}
+	if (ret_val != 0) {
+		LOGERR("Error installing zip file '%s'\n", Zip.c_str());
+		ret_val = 1;
+	} else if (wipe_cache)
+		PartitionManager.Wipe_By_Path("/cache");
+
+	return ret_val;
+}
+
+string OpenRecoveryScript::Locate_Zip_File(string Zip, string Storage_Root) {
+	string Path = TWFunc::Get_Path(Zip);
+	string File = TWFunc::Get_Filename(Zip);
+	string pathCpy = Path;
+	string wholePath;
+	size_t pos = Path.find("/", 1);
+
+	while (pos != string::npos)
+	{
+		pathCpy = Path.substr(pos, Path.size() - pos);
+		wholePath = pathCpy + File;
+		LOGINFO("Looking for zip at '%s'\n", wholePath.c_str());
+		if (TWFunc::Path_Exists(wholePath))
+			return wholePath;
+		wholePath = Storage_Root + wholePath;
+		LOGINFO("Looking for zip at '%s'\n", wholePath.c_str());
+		if (TWFunc::Path_Exists(wholePath))
+			return wholePath;
+
+		pos = Path.find("/", pos + 1);
+	}
+	return "";
+}
+
+int OpenRecoveryScript::Backup_Command(string Options) {
+	char value1[SCRIPT_COMMAND_SIZE];
+	int line_len, i;
+	string Backup_List;
+
+	strcpy(value1, Options.c_str());
+
+	DataManager::SetValue(TW_USE_COMPRESSION_VAR, 0);
+	DataManager::SetValue(TW_SKIP_MD5_GENERATE_VAR, 0);
+
+	gui_print("Setting backup options:\n");
+	line_len = Options.size();
+	for (i=0; i<line_len; i++) {
+		if (Options.substr(i, 1) == "S" || Options.substr(i, 1) == "s") {
+			Backup_List += "/system;";
+			gui_print("System\n");
+		} else if (Options.substr(i, 1) == "D" || Options.substr(i, 1) == "d") {
+			Backup_List += "/data;";
+			gui_print("Data\n");
+		} else if (Options.substr(i, 1) == "C" || Options.substr(i, 1) == "c") {
+			Backup_List += "/cache;";
+			gui_print("Cache\n");
+		} else if (Options.substr(i, 1) == "R" || Options.substr(i, 1) == "r") {
+			Backup_List += "/recovery;";
+			gui_print("Recovery\n");
+		} else if (Options.substr(i, 1) == "1") {
+			gui_print("%s\n", "Special1 -- No Longer Supported...");
+		} else if (Options.substr(i, 1) == "2") {
+			gui_print("%s\n", "Special2 -- No Longer Supported...");
+		} else if (Options.substr(i, 1) == "3") {
+			gui_print("%s\n", "Special3 -- No Longer Supported...");
+		} else if (Options.substr(i, 1) == "B" || Options.substr(i, 1) == "b") {
+			Backup_List += "/boot;";
+			gui_print("Boot\n");
+		} else if (Options.substr(i, 1) == "A" || Options.substr(i, 1) == "a") {
+			Backup_List += "/and-sec;";
+			gui_print("Android Secure\n");
+		} else if (Options.substr(i, 1) == "E" || Options.substr(i, 1) == "e") {
+			Backup_List += "/sd-ext;";
+			gui_print("SD-Ext\n");
+		} else if (Options.substr(i, 1) == "O" || Options.substr(i, 1) == "o") {
+			DataManager::SetValue(TW_USE_COMPRESSION_VAR, 1);
+			gui_print("Compression is on\n");
+		} else if (Options.substr(i, 1) == "M" || Options.substr(i, 1) == "m") {
+			DataManager::SetValue(TW_SKIP_MD5_GENERATE_VAR, 1);
+			gui_print("MD5 Generation is off\n");
+		}
+	}
+	DataManager::SetValue("tw_backup_list", Backup_List);
+	if (!PartitionManager.Run_Backup()) {
+		LOGERR("Backup failed!\n");
+		return 1;
+	}
+	gui_print("Backup complete!\n");
+	return 0;
+}
+
+void OpenRecoveryScript::Run_OpenRecoveryScript(void) {
+	DataManager::SetValue("tw_back", "main");
+	DataManager::SetValue("tw_action", "openrecoveryscript");
+	DataManager::SetValue("tw_has_action2", "0");
+	DataManager::SetValue("tw_action2", "");
+	DataManager::SetValue("tw_action2_param", "");
+	DataManager::SetValue("tw_action_text1", "Running OpenRecoveryScript");
+	DataManager::SetValue("tw_action_text2", "");
+	DataManager::SetValue("tw_complete_text1", "OpenRecoveryScript Complete");
+	DataManager::SetValue("tw_has_cancel", 0);
+	DataManager::SetValue("tw_show_reboot", 0);
+	if (gui_startPage("action_page") != 0) {
+		LOGERR("Failed to load OpenRecoveryScript GUI page.\n");
+	}
+}
diff --git a/openrecoveryscript.hpp b/openrecoveryscript.hpp
new file mode 100644
index 0000000..c3eabf6
--- /dev/null
+++ b/openrecoveryscript.hpp
@@ -0,0 +1,40 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _OPENRECOVERYSCRIPT_HPP
+#define _OPENRECOVERYSCRIPT_HPP
+
+#include <string>
+
+using namespace std;
+
+// Partition class
+class OpenRecoveryScript
+{
+public:
+	static int check_for_script_file();                                            // Checks to see if the ORS file is present in /cache
+	static int copy_script_file(string filename);                                  // Copies a script file to the temp folder
+	static int run_script_file();                                                  // Executes the commands in the ORS file
+	static int Insert_ORS_Command(string Command);                                 // Inserts the Command into the SCRIPT_FILE_TMP file
+	static int Install_Command(string Zip);                                        // Installs a zip
+	static string Locate_Zip_File(string Path, string File);                       // Attempts to locate the zip file in storage
+	static int Backup_Command(string Options);                                     // Runs a backup
+	static void Run_OpenRecoveryScript();                                          // Starts the GUI Page for running OpenRecoveryScript
+};
+
+#endif // _OPENRECOVERYSCRIPT_HPP
diff --git a/orscmd/Android.mk b/orscmd/Android.mk
new file mode 100644
index 0000000..8ddf93f
--- /dev/null
+++ b/orscmd/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	orscmd.cpp
+LOCAL_CFLAGS:= -g -c -W
+LOCAL_MODULE:=orscmd
+LOCAL_MODULE_STEM := twrp
+LOCAL_MODULE_TAGS:= eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+include $(BUILD_EXECUTABLE)
diff --git a/orscmd/orscmd.cpp b/orscmd/orscmd.cpp
new file mode 100644
index 0000000..0240ff9
--- /dev/null
+++ b/orscmd/orscmd.cpp
@@ -0,0 +1,91 @@
+/*
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "orscmd.h"
+#include "../variables.h"
+
+void print_version(void) {
+	printf("TWRP openrecoveryscript command line tool, TWRP version %s\n\n", TW_VERSION_STR);
+}
+
+void print_usage(void) {
+	print_version();
+	printf("Allows command line usage of TWRP via openrecoveryscript commands.\n");
+	printf("Some common commands include:\n");
+	printf("  install /path/to/update.zip\n");
+	printf("  backup BSDC backupname\n");
+	printf("  restore backupname BSDC\n");
+	printf("  factoryreset\n");
+	printf("  wipe cache\n");
+	printf("  sideload\n");
+	printf("  set variable value\n");
+	printf("  get variable\n");
+	printf("  decrypt password\n");
+	printf("\nSee more documentation at http://teamw.in/openrecoveryscript\n");
+}
+
+int main(int argc, char **argv) {
+	int read_fd, write_fd, index;
+	char command[1024], result[512];
+
+	if (argc < 2 || strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "?") == 0 || strcmp(argv[1], "-h") == 0) {
+		print_usage();
+		return 0;
+	}
+	if (strcmp(argv[1], "version") == 0 || strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) {
+		print_version();
+		return 0;
+	}
+
+	sprintf(command, "%s", argv[1]);
+	for (index = 2; index < argc; index++) {
+		sprintf(command, "%s %s", command, argv[index]);
+	}
+
+	write_fd = open(ORS_INPUT_FILE, O_WRONLY);
+	if (write_fd < 0) {
+		printf("TWRP does not appear to be running. Waiting for TWRP to start . . .\n");
+		printf("Press CTRL + C to quit.\n");
+		while (write_fd < 0)
+			write_fd = open(ORS_INPUT_FILE, O_WRONLY);
+	}
+	if (write(write_fd, command, sizeof(command)) != sizeof(command)) {
+		printf("Error sending command.\n");
+		close(write_fd);
+		return -1;
+	}
+	read_fd = open(ORS_OUTPUT_FILE, O_RDONLY);
+	if (read_fd < 0) {
+		printf("Unable to open %s for read.\n", ORS_OUTPUT_FILE);
+		return -1;
+	}
+	memset(&result, 0, sizeof(result));
+	while (read(read_fd, &result, sizeof(result)) > 0) {
+		result[510] = '\n';
+		result[511] = '\0';
+		printf("%s", result);
+		memset(&result, 0, sizeof(result));
+	}
+	close(write_fd);
+	close(read_fd);
+	return 0;
+}
diff --git a/orscmd/orscmd.h b/orscmd/orscmd.h
new file mode 100644
index 0000000..f186add
--- /dev/null
+++ b/orscmd/orscmd.h
@@ -0,0 +1,22 @@
+/*
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __ORSCMD_H
+#define __ORSCMD_H
+
+#define ORS_INPUT_FILE "/sbin/orsin"
+#define ORS_OUTPUT_FILE "/sbin/orsout"
+
+#endif //__ORSCMD_H
diff --git a/partition.cpp b/partition.cpp
new file mode 100644
index 0000000..2195d9d
--- /dev/null
+++ b/partition.cpp
@@ -0,0 +1,2096 @@
+/*
+	Copyright 2013 TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <iostream>
+#include <sstream>
+
+#ifdef TW_INCLUDE_CRYPTO
+	#include "cutils/properties.h"
+#endif
+
+#include "libblkid/blkid.h"
+#include "variables.h"
+#include "twcommon.h"
+#include "partitions.hpp"
+#include "data.hpp"
+#include "twrp-functions.hpp"
+#include "twrpDigest.hpp"
+#include "twrpTar.hpp"
+#include "twrpDU.hpp"
+#include "fixPermissions.hpp"
+#include "infomanager.hpp"
+extern "C" {
+	#include "mtdutils/mtdutils.h"
+	#include "mtdutils/mounts.h"
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+	#include "crypto/libcrypt_samsung/include/libcrypt_samsung.h"
+#endif
+#ifdef USE_EXT4
+	#include "make_ext4fs.h"
+#endif
+
+#ifdef TW_INCLUDE_CRYPTO
+	#ifdef TW_INCLUDE_JB_CRYPTO
+		#include "crypto/jb/cryptfs.h"
+	#else
+		#include "crypto/ics/cryptfs.h"
+	#endif
+#endif
+}
+#ifdef HAVE_SELINUX
+#include "selinux/selinux.h"
+#include <selinux/label.h>
+#endif
+#ifdef HAVE_CAPABILITIES
+#include <sys/capability.h>
+#include <sys/xattr.h>
+#include <linux/xattr.h>
+#endif
+
+using namespace std;
+
+extern struct selabel_handle *selinux_handle;
+extern bool datamedia;
+
+struct flag_list {
+	const char *name;
+	unsigned flag;
+};
+
+static struct flag_list mount_flags[] = {
+	{ "noatime",    MS_NOATIME },
+	{ "noexec",     MS_NOEXEC },
+	{ "nosuid",     MS_NOSUID },
+	{ "nodev",      MS_NODEV },
+	{ "nodiratime", MS_NODIRATIME },
+	{ "ro",         MS_RDONLY },
+	{ "rw",         0 },
+	{ "remount",    MS_REMOUNT },
+	{ "bind",       MS_BIND },
+	{ "rec",        MS_REC },
+#ifdef MS_UNBINDABLE
+	{ "unbindable", MS_UNBINDABLE },
+#endif
+#ifdef MS_PRIVATE
+	{ "private",    MS_PRIVATE },
+#endif
+#ifdef MS_SLAVE
+	{ "slave",      MS_SLAVE },
+#endif
+#ifdef MS_SHARED
+	{ "shared",     MS_SHARED },
+#endif
+	{ "sync",       MS_SYNCHRONOUS },
+	{ "defaults",   0 },
+	{ 0,            0 },
+};
+
+TWPartition::TWPartition() {
+	Can_Be_Mounted = false;
+	Can_Be_Wiped = false;
+	Can_Be_Backed_Up = false;
+	Use_Rm_Rf = false;
+	Wipe_During_Factory_Reset = false;
+	Wipe_Available_in_GUI = false;
+	Is_SubPartition = false;
+	Has_SubPartition = false;
+	SubPartition_Of = "";
+	Symlink_Path = "";
+	Symlink_Mount_Point = "";
+	Mount_Point = "";
+	Backup_Path = "";
+	Actual_Block_Device = "";
+	Primary_Block_Device = "";
+	Alternate_Block_Device = "";
+	Removable = false;
+	Is_Present = false;
+	Length = 0;
+	Size = 0;
+	Used = 0;
+	Free = 0;
+	Backup_Size = 0;
+	Can_Be_Encrypted = false;
+	Is_Encrypted = false;
+	Is_Decrypted = false;
+	Decrypted_Block_Device = "";
+	Display_Name = "";
+	Backup_Display_Name = "";
+	Storage_Name = "";
+	Backup_Name = "";
+	Backup_FileName = "";
+	MTD_Name = "";
+	Backup_Method = NONE;
+	Can_Encrypt_Backup = false;
+	Use_Userdata_Encryption = false;
+	Has_Data_Media = false;
+	Has_Android_Secure = false;
+	Is_Storage = false;
+	Is_Settings_Storage = false;
+	Storage_Path = "";
+	Current_File_System = "";
+	Fstab_File_System = "";
+	Mount_Flags = 0;
+	Mount_Options = "";
+	Format_Block_Size = 0;
+	Ignore_Blkid = false;
+	Retain_Layout_Version = false;
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+	EcryptFS_Password = "";
+#endif
+}
+
+TWPartition::~TWPartition(void) {
+	// Do nothing
+}
+
+bool TWPartition::Process_Fstab_Line(string Line, bool Display_Error) {
+	char full_line[MAX_FSTAB_LINE_LENGTH], item[MAX_FSTAB_LINE_LENGTH];
+	int line_len = Line.size(), index = 0, item_index = 0;
+	char* ptr;
+	string Flags;
+	strncpy(full_line, Line.c_str(), line_len);
+	bool skip = false;
+
+	for (index = 0; index < line_len; index++) {
+		if (full_line[index] == 34)
+			skip = !skip;
+		if (!skip && full_line[index] <= 32)
+			full_line[index] = '\0';
+	}
+	Mount_Point = full_line;
+	LOGINFO("Processing '%s'\n", Mount_Point.c_str());
+	Backup_Path = Mount_Point;
+	Storage_Path = Mount_Point;
+	Display_Name = full_line + 1;
+	Backup_Display_Name = Display_Name;
+	Storage_Name = Display_Name;
+	index = Mount_Point.size();
+	while (index < line_len) {
+		while (index < line_len && full_line[index] == '\0')
+			index++;
+		if (index >= line_len)
+			continue;
+		ptr = full_line + index;
+		if (item_index == 0) {
+			// File System
+			Fstab_File_System = ptr;
+			Current_File_System = ptr;
+			item_index++;
+		} else if (item_index == 1) {
+			// Primary Block Device
+			if (Fstab_File_System == "mtd" || Fstab_File_System == "yaffs2") {
+				MTD_Name = ptr;
+				Find_MTD_Block_Device(MTD_Name);
+			} else if (Fstab_File_System == "bml") {
+				if (Mount_Point == "/boot")
+					MTD_Name = "boot";
+				else if (Mount_Point == "/recovery")
+					MTD_Name = "recovery";
+				Primary_Block_Device = ptr;
+				if (*ptr != '/')
+					LOGERR("Until we get better BML support, you will have to find and provide the full block device path to the BML devices e.g. /dev/block/bml9 instead of the partition name\n");
+			} else if (*ptr != '/') {
+				if (Display_Error)
+					LOGERR("Invalid block device on '%s', '%s', %i\n", Line.c_str(), ptr, index);
+				else
+					LOGINFO("Invalid block device on '%s', '%s', %i\n", Line.c_str(), ptr, index);
+				return 0;
+			} else {
+				Primary_Block_Device = ptr;
+				Find_Real_Block_Device(Primary_Block_Device, Display_Error);
+			}
+			item_index++;
+		} else if (item_index > 1) {
+			if (*ptr == '/') {
+				// Alternate Block Device
+				Alternate_Block_Device = ptr;
+				Find_Real_Block_Device(Alternate_Block_Device, Display_Error);
+			} else if (strlen(ptr) > 7 && strncmp(ptr, "length=", 7) == 0) {
+				// Partition length
+				ptr += 7;
+				Length = atoi(ptr);
+			} else if (strlen(ptr) > 6 && strncmp(ptr, "flags=", 6) == 0) {
+				// Custom flags, save for later so that new values aren't overwritten by defaults
+				ptr += 6;
+				Flags = ptr;
+				Process_Flags(Flags, Display_Error);
+			} else if (strlen(ptr) == 4 && (strncmp(ptr, "NULL", 4) == 0 || strncmp(ptr, "null", 4) == 0 || strncmp(ptr, "null", 4) == 0)) {
+				// Do nothing
+			} else {
+				// Unhandled data
+				LOGINFO("Unhandled fstab information: '%s', %i, line: '%s'\n", ptr, index, Line.c_str());
+			}
+		}
+		while (index < line_len && full_line[index] != '\0')
+			index++;
+	}
+
+	if (!Is_File_System(Fstab_File_System) && !Is_Image(Fstab_File_System)) {
+		if (Display_Error)
+			LOGERR("Unknown File System: '%s'\n", Fstab_File_System.c_str());
+		else
+			LOGINFO("Unknown File System: '%s'\n", Fstab_File_System.c_str());
+		return 0;
+	} else if (Is_File_System(Fstab_File_System)) {
+		Find_Actual_Block_Device();
+		Setup_File_System(Display_Error);
+		if (Mount_Point == "/system") {
+			Display_Name = "System";
+			Backup_Display_Name = Display_Name;
+			Storage_Name = Display_Name;
+			Wipe_Available_in_GUI = true;
+			Can_Be_Backed_Up = true;
+		} else if (Mount_Point == "/data") {
+			Display_Name = "Data";
+			Backup_Display_Name = Display_Name;
+			Storage_Name = Display_Name;
+			Wipe_Available_in_GUI = true;
+			Wipe_During_Factory_Reset = true;
+			Can_Be_Backed_Up = true;
+			Can_Encrypt_Backup = true;
+			Use_Userdata_Encryption = true;
+			if (datamedia)
+				Setup_Data_Media();
+#ifdef TW_INCLUDE_CRYPTO
+			Can_Be_Encrypted = true;
+			char crypto_blkdev[255];
+			property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, "error");
+			if (strcmp(crypto_blkdev, "error") != 0) {
+				DataManager::SetValue(TW_DATA_BLK_DEVICE, Primary_Block_Device);
+				DataManager::SetValue(TW_IS_DECRYPTED, 1);
+				Is_Encrypted = true;
+				Is_Decrypted = true;
+				Decrypted_Block_Device = crypto_blkdev;
+				LOGINFO("Data already decrypted, new block device: '%s'\n", crypto_blkdev);
+			} else if (!Mount(false)) {
+				if (Is_Present) {
+#ifdef TW_INCLUDE_JB_CRYPTO
+					// No extra flags needed
+#else
+					property_set("ro.crypto.fs_type", CRYPTO_FS_TYPE);
+					property_set("ro.crypto.fs_real_blkdev", CRYPTO_REAL_BLKDEV);
+					property_set("ro.crypto.fs_mnt_point", CRYPTO_MNT_POINT);
+					property_set("ro.crypto.fs_options", CRYPTO_FS_OPTIONS);
+					property_set("ro.crypto.fs_flags", CRYPTO_FS_FLAGS);
+					property_set("ro.crypto.keyfile.userdata", CRYPTO_KEY_LOC);
+#ifdef CRYPTO_SD_FS_TYPE
+					property_set("ro.crypto.sd_fs_type", CRYPTO_SD_FS_TYPE);
+					property_set("ro.crypto.sd_fs_real_blkdev", CRYPTO_SD_REAL_BLKDEV);
+					property_set("ro.crypto.sd_fs_mnt_point", EXPAND(TW_INTERNAL_STORAGE_PATH));
+#endif
+					property_set("rw.km_fips_status", "ready");
+#endif
+					if (cryptfs_check_footer() == 0) {
+						Is_Encrypted = true;
+						Is_Decrypted = false;
+						Can_Be_Mounted = false;
+						Current_File_System = "emmc";
+						Setup_Image(Display_Error);
+						DataManager::SetValue(TW_IS_ENCRYPTED, 1);
+						DataManager::SetValue(TW_CRYPTO_PASSWORD, "");
+						DataManager::SetValue("tw_crypto_display", "");
+					} else {
+						LOGERR("Could not mount /data and unable to find crypto footer.\n");
+					}
+				} else {
+					LOGERR("Primary block device '%s' for mount point '%s' is not present!\n", Primary_Block_Device.c_str(), Mount_Point.c_str());
+				}
+			} else {
+				// Filesystem is not encrypted and the mount
+				// succeeded, so get it back to the original
+				// unmounted state
+				UnMount(false);
+			}
+			if (datamedia && (!Is_Encrypted || (Is_Encrypted && Is_Decrypted)))
+				Recreate_Media_Folder();
+#else
+			if (datamedia)
+				Recreate_Media_Folder();
+#endif
+		} else if (Mount_Point == "/cache") {
+			Display_Name = "Cache";
+			Backup_Display_Name = Display_Name;
+			Storage_Name = Display_Name;
+			Wipe_Available_in_GUI = true;
+			Wipe_During_Factory_Reset = true;
+			Can_Be_Backed_Up = true;
+			if (Mount(false) && !TWFunc::Path_Exists("/cache/recovery/.")) {
+				LOGINFO("Recreating /cache/recovery folder.\n");
+				if (mkdir("/cache/recovery", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0)
+					return -1;
+			}
+		} else if (Mount_Point == "/datadata") {
+			Wipe_During_Factory_Reset = true;
+			Display_Name = "DataData";
+			Backup_Display_Name = Display_Name;
+			Storage_Name = Display_Name;
+			Is_SubPartition = true;
+			SubPartition_Of = "/data";
+			DataManager::SetValue(TW_HAS_DATADATA, 1);
+			Can_Be_Backed_Up = true;
+			Can_Encrypt_Backup = true;
+			Use_Userdata_Encryption = false; // This whole partition should be encrypted
+		} else if (Mount_Point == "/sd-ext") {
+			Wipe_During_Factory_Reset = true;
+			Display_Name = "SD-Ext";
+			Backup_Display_Name = Display_Name;
+			Storage_Name = Display_Name;
+			Wipe_Available_in_GUI = true;
+			Removable = true;
+			Can_Be_Backed_Up = true;
+			Can_Encrypt_Backup = true;
+			Use_Userdata_Encryption = true;
+		} else if (Mount_Point == "/boot") {
+			Display_Name = "Boot";
+			Backup_Display_Name = Display_Name;
+			DataManager::SetValue("tw_boot_is_mountable", 1);
+			Can_Be_Backed_Up = true;
+		}
+#ifdef TW_EXTERNAL_STORAGE_PATH
+		if (Mount_Point == EXPAND(TW_EXTERNAL_STORAGE_PATH)) {
+			Is_Storage = true;
+			Storage_Path = EXPAND(TW_EXTERNAL_STORAGE_PATH);
+			Removable = true;
+			Wipe_Available_in_GUI = true;
+#else
+		if (Mount_Point == "/sdcard" || Mount_Point == "/external_sd" || Mount_Point == "/external_sdcard") {
+			Is_Storage = true;
+			Removable = true;
+			Wipe_Available_in_GUI = true;
+#endif
+		}
+#ifdef TW_INTERNAL_STORAGE_PATH
+		if (Mount_Point == EXPAND(TW_INTERNAL_STORAGE_PATH)) {
+			Is_Storage = true;
+			Is_Settings_Storage = true;
+			Storage_Path = EXPAND(TW_INTERNAL_STORAGE_PATH);
+			Wipe_Available_in_GUI = true;
+		}
+#else
+		if (Mount_Point == "/emmc" || Mount_Point == "/internal_sd" || Mount_Point == "/internal_sdcard") {
+			Is_Storage = true;
+			Is_Settings_Storage = true;
+			Wipe_Available_in_GUI = true;
+		}
+#endif
+	} else if (Is_Image(Fstab_File_System)) {
+		Find_Actual_Block_Device();
+		Setup_Image(Display_Error);
+		if (Mount_Point == "/boot") {
+			Display_Name = "Boot";
+			Backup_Display_Name = Display_Name;
+			Can_Be_Backed_Up = true;
+		} else if (Mount_Point == "/recovery") {
+			Display_Name = "Recovery";
+			Backup_Display_Name = Display_Name;
+		}
+	}
+
+	// Process any custom flags
+	if (Flags.size() > 0)
+		Process_Flags(Flags, Display_Error);
+	return true;
+}
+
+bool TWPartition::Process_FS_Flags(string& Options, int Flags) {
+	int i;
+	char *p;
+	char *savep;
+	char fs_options[250];
+
+	strlcpy(fs_options, Options.c_str(), sizeof(fs_options));
+	Options = "";
+
+	p = strtok_r(fs_options, ",", &savep);
+	while (p) {
+		/* Look for the flag "p" in the flag list "fl"
+		* If not found, the loop exits with fl[i].name being null.
+		*/
+		for (i = 0; mount_flags[i].name; i++) {
+			if (strncmp(p, mount_flags[i].name, strlen(mount_flags[i].name)) == 0) {
+				Flags |= mount_flags[i].flag;
+				break;
+			}
+		}
+
+		if (!mount_flags[i].name) {
+			if (Options.size() > 0)
+				Options += ",";
+			Options += p;
+		}
+		p = strtok_r(NULL, ",", &savep);
+	}
+
+	return true;
+}
+
+bool TWPartition::Process_Flags(string Flags, bool Display_Error) {
+	char flags[MAX_FSTAB_LINE_LENGTH];
+	int flags_len, index = 0, ptr_len;
+	char* ptr;
+	bool skip = false, has_display_name = false, has_storage_name = false, has_backup_name = false;
+
+	strcpy(flags, Flags.c_str());
+	flags_len = Flags.size();
+	for (index = 0; index < flags_len; index++) {
+		if (flags[index] == 34)
+			skip = !skip;
+		if (!skip && flags[index] == ';')
+			flags[index] = '\0';
+	}
+
+	index = 0;
+	while (index < flags_len) {
+		while (index < flags_len && flags[index] == '\0')
+			index++;
+		if (index >= flags_len)
+			continue;
+		ptr = flags + index;
+		ptr_len = strlen(ptr);
+		if (strcmp(ptr, "removable") == 0) {
+			Removable = true;
+		} else if (strncmp(ptr, "storage", 7) == 0) {
+			if (ptr_len == 7) {
+				Is_Storage = true;
+			} else if (ptr_len == 9) {
+				ptr += 9;
+				if (*ptr == '1' || *ptr == 'y' || *ptr == 'Y') {
+					LOGINFO("storage set to true\n");
+					Is_Storage = true;
+				} else {
+					LOGINFO("storage set to false\n");
+					Is_Storage = false;
+				}
+			}
+		} else if (strcmp(ptr, "settingsstorage") == 0) {
+			Is_Storage = true;
+		} else if (strcmp(ptr, "andsec") == 0) {
+			Has_Android_Secure = true;
+		} else if (strcmp(ptr, "canbewiped") == 0) {
+			Can_Be_Wiped = true;
+		} else if (strcmp(ptr, "usermrf") == 0) {
+			Use_Rm_Rf = true;
+		} else if (ptr_len > 7 && strncmp(ptr, "backup=", 7) == 0) {
+			ptr += 7;
+			if (*ptr == '1' || *ptr == 'y' || *ptr == 'Y')
+				Can_Be_Backed_Up = true;
+			else
+				Can_Be_Backed_Up = false;
+		} else if (strcmp(ptr, "wipeingui") == 0) {
+			Can_Be_Wiped = true;
+			Wipe_Available_in_GUI = true;
+		} else if (strcmp(ptr, "wipeduringfactoryreset") == 0) {
+			Can_Be_Wiped = true;
+			Wipe_Available_in_GUI = true;
+			Wipe_During_Factory_Reset = true;
+		} else if (ptr_len > 15 && strncmp(ptr, "subpartitionof=", 15) == 0) {
+			ptr += 15;
+			Is_SubPartition = true;
+			SubPartition_Of = ptr;
+		} else if (strcmp(ptr, "ignoreblkid") == 0) {
+			Ignore_Blkid = true;
+		} else if (strcmp(ptr, "retainlayoutversion") == 0) {
+			Retain_Layout_Version = true;
+		} else if (ptr_len > 8 && strncmp(ptr, "symlink=", 8) == 0) {
+			ptr += 8;
+			Symlink_Path = ptr;
+		} else if (ptr_len > 8 && strncmp(ptr, "display=", 8) == 0) {
+			has_display_name = true;
+			ptr += 8;
+			if (*ptr == '\"') ptr++;
+			Display_Name = ptr;
+			if (Display_Name.substr(Display_Name.size() - 1, 1) == "\"") {
+				Display_Name.resize(Display_Name.size() - 1);
+			}
+		} else if (ptr_len > 11 && strncmp(ptr, "storagename=", 11) == 0) {
+			has_storage_name = true;
+			ptr += 11;
+			if (*ptr == '\"') ptr++;
+			Storage_Name = ptr;
+			if (Storage_Name.substr(Storage_Name.size() - 1, 1) == "\"") {
+				Storage_Name.resize(Storage_Name.size() - 1);
+			}
+		} else if (ptr_len > 11 && strncmp(ptr, "backupname=", 10) == 0) {
+			has_backup_name = true;
+			ptr += 10;
+			if (*ptr == '\"') ptr++;
+			Backup_Display_Name = ptr;
+			if (Backup_Display_Name.substr(Backup_Display_Name.size() - 1, 1) == "\"") {
+				Backup_Display_Name.resize(Backup_Display_Name.size() - 1);
+			}
+		} else if (ptr_len > 10 && strncmp(ptr, "blocksize=", 10) == 0) {
+			ptr += 10;
+			Format_Block_Size = atoi(ptr);
+		} else if (ptr_len > 7 && strncmp(ptr, "length=", 7) == 0) {
+			ptr += 7;
+			Length = atoi(ptr);
+		} else if (ptr_len > 17 && strncmp(ptr, "canencryptbackup=", 17) == 0) {
+			ptr += 17;
+			if (*ptr == '1' || *ptr == 'y' || *ptr == 'Y')
+				Can_Encrypt_Backup = true;
+			else
+				Can_Encrypt_Backup = false;
+		} else if (ptr_len > 21 && strncmp(ptr, "userdataencryptbackup=", 21) == 0) {
+			ptr += 21;
+			if (*ptr == '1' || *ptr == 'y' || *ptr == 'Y') {
+				Can_Encrypt_Backup = true;
+				Use_Userdata_Encryption = true;
+			} else {
+				Use_Userdata_Encryption = false;
+			}
+		} else if (ptr_len > 8 && strncmp(ptr, "fsflags=", 8) == 0) {
+			ptr += 8;
+			if (*ptr == '\"') ptr++;
+
+			Mount_Options = ptr;
+			if (Mount_Options.substr(Mount_Options.size() - 1, 1) == "\"") {
+				Mount_Options.resize(Mount_Options.size() - 1);
+			}
+			Process_FS_Flags(Mount_Options, Mount_Flags);
+		} else {
+			if (Display_Error)
+				LOGERR("Unhandled flag: '%s'\n", ptr);
+			else
+				LOGINFO("Unhandled flag: '%s'\n", ptr);
+		}
+		while (index < flags_len && flags[index] != '\0')
+			index++;
+	}
+	if (has_display_name && !has_storage_name)
+		Storage_Name = Display_Name;
+	if (!has_display_name && has_storage_name)
+		Display_Name = Storage_Name;
+	if (has_display_name && !has_backup_name && Backup_Display_Name != "Android Secure")
+		Backup_Display_Name = Display_Name;
+	if (!has_display_name && has_backup_name)
+		Display_Name = Backup_Display_Name;
+	return true;
+}
+
+bool TWPartition::Is_File_System(string File_System) {
+	if (File_System == "ext2" ||
+		File_System == "ext3" ||
+		File_System == "ext4" ||
+		File_System == "vfat" ||
+		File_System == "ntfs" ||
+		File_System == "yaffs2" ||
+		File_System == "exfat" ||
+		File_System == "f2fs" ||
+		File_System == "auto")
+		return true;
+	else
+		return false;
+}
+
+bool TWPartition::Is_Image(string File_System) {
+	if (File_System == "emmc" || File_System == "mtd" || File_System == "bml")
+		return true;
+	else
+		return false;
+}
+
+bool TWPartition::Make_Dir(string Path, bool Display_Error) {
+	if (!TWFunc::Path_Exists(Path)) {
+		if (mkdir(Path.c_str(), 0777) == -1) {
+			if (Display_Error)
+				LOGERR("Can not create '%s' folder.\n", Path.c_str());
+			else
+				LOGINFO("Can not create '%s' folder.\n", Path.c_str());
+			return false;
+		} else {
+			LOGINFO("Created '%s' folder.\n", Path.c_str());
+			return true;
+		}
+	}
+	return true;
+}
+
+void TWPartition::Setup_File_System(bool Display_Error) {
+	struct statfs st;
+
+	Can_Be_Mounted = true;
+	Can_Be_Wiped = true;
+
+	// Make the mount point folder if it doesn't exist
+	Make_Dir(Mount_Point, Display_Error);
+	Display_Name = Mount_Point.substr(1, Mount_Point.size() - 1);
+	Backup_Name = Display_Name;
+	Backup_Method = FILES;
+}
+
+void TWPartition::Setup_Image(bool Display_Error) {
+	Display_Name = Mount_Point.substr(1, Mount_Point.size() - 1);
+	Backup_Name = Display_Name;
+	if (Current_File_System == "emmc")
+		Backup_Method = DD;
+	else if (Current_File_System == "mtd" || Current_File_System == "bml")
+		Backup_Method = FLASH_UTILS;
+	else
+		LOGINFO("Unhandled file system '%s' on image '%s'\n", Current_File_System.c_str(), Display_Name.c_str());
+	if (Find_Partition_Size()) {
+		Used = Size;
+		Backup_Size = Size;
+	} else {
+		if (Display_Error)
+			LOGERR("Unable to find partition size for '%s'\n", Mount_Point.c_str());
+		else
+			LOGINFO("Unable to find partition size for '%s'\n", Mount_Point.c_str());
+	}
+}
+
+void TWPartition::Setup_AndSec(void) {
+	Backup_Display_Name = "Android Secure";
+	Backup_Name = "and-sec";
+	Can_Be_Backed_Up = true;
+	Has_Android_Secure = true;
+	Symlink_Path = Mount_Point + "/.android_secure";
+	Symlink_Mount_Point = "/and-sec";
+	Backup_Path = Symlink_Mount_Point;
+	Make_Dir("/and-sec", true);
+	Recreate_AndSec_Folder();
+	Mount_Storage_Retry();
+}
+
+void TWPartition::Setup_Data_Media() {
+	LOGINFO("Setting up '%s' as data/media emulated storage.\n", Mount_Point.c_str());
+	Storage_Name = "Internal Storage";
+	Has_Data_Media = true;
+	Is_Storage = true;
+	Is_Settings_Storage = true;
+	Storage_Path = "/data/media";
+	Symlink_Path = Storage_Path;
+	if (strcmp(EXPAND(TW_EXTERNAL_STORAGE_PATH), "/sdcard") == 0) {
+		Make_Dir("/emmc", false);
+		Symlink_Mount_Point = "/emmc";
+	} else {
+		Make_Dir("/sdcard", false);
+		Symlink_Mount_Point = "/sdcard";
+	}
+	if (Mount(false) && TWFunc::Path_Exists("/data/media/0")) {
+		Storage_Path = "/data/media/0";
+		Symlink_Path = Storage_Path;
+		DataManager::SetValue(TW_INTERNAL_PATH, "/data/media/0");
+		UnMount(true);
+	}
+	DataManager::SetValue("tw_has_internal", 1);
+	DataManager::SetValue("tw_has_data_media", 1);
+	du.add_absolute_dir("/data/media");
+}
+
+void TWPartition::Find_Real_Block_Device(string& Block, bool Display_Error) {
+	char device[512], realDevice[512];
+
+	strcpy(device, Block.c_str());
+	memset(realDevice, 0, sizeof(realDevice));
+	while (readlink(device, realDevice, sizeof(realDevice)) > 0)
+	{
+		strcpy(device, realDevice);
+		memset(realDevice, 0, sizeof(realDevice));
+	}
+
+	if (device[0] != '/') {
+		if (Display_Error)
+			LOGERR("Invalid symlink path '%s' found on block device '%s'\n", device, Block.c_str());
+		else
+			LOGINFO("Invalid symlink path '%s' found on block device '%s'\n", device, Block.c_str());
+		return;
+	} else {
+		Block = device;
+		return;
+	}
+}
+
+void TWPartition::Mount_Storage_Retry(void) {
+	// On some devices, storage doesn't want to mount right away, retry and sleep
+	if (!Mount(true)) {
+		int retry_count = 5;
+		while (retry_count > 0 && !Mount(false)) {
+			usleep(500000);
+			retry_count--;
+		}
+		Mount(true);
+	}
+}
+
+bool TWPartition::Find_MTD_Block_Device(string MTD_Name) {
+	FILE *fp = NULL;
+	char line[255];
+
+	fp = fopen("/proc/mtd", "rt");
+	if (fp == NULL) {
+		LOGERR("Device does not support /proc/mtd\n");
+		return false;
+	}
+
+	while (fgets(line, sizeof(line), fp) != NULL)
+	{
+		char device[32], label[32];
+		unsigned long size = 0;
+		char* fstype = NULL;
+		int deviceId;
+
+		sscanf(line, "%s %lx %*s %*c%s", device, &size, label);
+
+		// Skip header and blank lines
+		if ((strcmp(device, "dev:") == 0) || (strlen(line) < 8))
+			continue;
+
+		// Strip off the trailing " from the label
+		label[strlen(label)-1] = '\0';
+
+		if (strcmp(label, MTD_Name.c_str()) == 0) {
+			// We found our device
+			// Strip off the trailing : from the device
+			device[strlen(device)-1] = '\0';
+			if (sscanf(device,"mtd%d", &deviceId) == 1) {
+				sprintf(device, "/dev/block/mtdblock%d", deviceId);
+				Primary_Block_Device = device;
+				fclose(fp);
+				return true;
+			}
+		}
+	}
+	fclose(fp);
+
+	return false;
+}
+
+bool TWPartition::Get_Size_Via_statfs(bool Display_Error) {
+	struct statfs st;
+	string Local_Path = Mount_Point + "/.";
+
+	if (!Mount(Display_Error))
+		return false;
+
+	if (statfs(Local_Path.c_str(), &st) != 0) {
+		if (!Removable) {
+			if (Display_Error)
+				LOGERR("Unable to statfs '%s'\n", Local_Path.c_str());
+			else
+				LOGINFO("Unable to statfs '%s'\n", Local_Path.c_str());
+		}
+		return false;
+	}
+	Size = (st.f_blocks * st.f_bsize);
+	Used = ((st.f_blocks - st.f_bfree) * st.f_bsize);
+	Free = (st.f_bfree * st.f_bsize);
+	Backup_Size = Used;
+	return true;
+}
+
+bool TWPartition::Get_Size_Via_df(bool Display_Error) {
+	FILE* fp;
+	char command[255], line[512];
+	int include_block = 1;
+	unsigned int min_len;
+
+	if (!Mount(Display_Error))
+		return false;
+
+	min_len = Actual_Block_Device.size() + 2;
+	sprintf(command, "df %s > /tmp/dfoutput.txt", Mount_Point.c_str());
+	TWFunc::Exec_Cmd(command);
+	fp = fopen("/tmp/dfoutput.txt", "rt");
+	if (fp == NULL) {
+		LOGINFO("Unable to open /tmp/dfoutput.txt.\n");
+		return false;
+	}
+
+	while (fgets(line, sizeof(line), fp) != NULL)
+	{
+		unsigned long blocks, used, available;
+		char device[64];
+		char tmpString[64];
+
+		if (strncmp(line, "Filesystem", 10) == 0)
+			continue;
+		if (strlen(line) < min_len) {
+			include_block = 0;
+			continue;
+		}
+		if (include_block) {
+			sscanf(line, "%s %lu %lu %lu", device, &blocks, &used, &available);
+		} else {
+			// The device block string is so long that the df information is on the next line
+			int space_count = 0;
+			sprintf(tmpString, "/dev/block/%s", Actual_Block_Device.c_str());
+			while (tmpString[space_count] == 32)
+				space_count++;
+			sscanf(line + space_count, "%lu %lu %lu", &blocks, &used, &available);
+		}
+
+		// Adjust block size to byte size
+		Size = blocks * 1024ULL;
+		Used = used * 1024ULL;
+		Free = available * 1024ULL;
+		Backup_Size = Used;
+	}
+	fclose(fp);
+	return true;
+}
+
+bool TWPartition::Find_Partition_Size(void) {
+	FILE* fp;
+	char line[512];
+	string tmpdevice;
+
+	fp = fopen("/proc/dumchar_info", "rt");
+	if (fp != NULL) {
+		while (fgets(line, sizeof(line), fp) != NULL)
+		{
+			char label[32], device[32];
+			unsigned long size = 0;
+
+			sscanf(line, "%s %lx %*x %*u %s", label, &size, device);
+
+			// Skip header, annotation	and blank lines
+			if ((strncmp(device, "/dev/", 5) != 0) || (strlen(line) < 8))
+				continue;
+
+			tmpdevice = "/dev/";
+			tmpdevice += label;
+			if (tmpdevice == Primary_Block_Device || tmpdevice == Alternate_Block_Device) {
+				Size = size;
+				fclose(fp);
+				return true;
+			}
+		}
+	}
+
+	// In this case, we'll first get the partitions we care about (with labels)
+	fp = fopen("/proc/partitions", "rt");
+	if (fp == NULL)
+		return false;
+
+	while (fgets(line, sizeof(line), fp) != NULL)
+	{
+		unsigned long major, minor, blocks;
+		char device[512];
+		char tmpString[64];
+
+		if (strlen(line) < 7 || line[0] == 'm')	 continue;
+		sscanf(line + 1, "%lu %lu %lu %s", &major, &minor, &blocks, device);
+
+		tmpdevice = "/dev/block/";
+		tmpdevice += device;
+		if (tmpdevice == Primary_Block_Device || tmpdevice == Alternate_Block_Device) {
+			// Adjust block size to byte size
+			Size = blocks * 1024ULL;
+			fclose(fp);
+			return true;
+		}
+	}
+	fclose(fp);
+	return false;
+}
+
+bool TWPartition::Is_Mounted(void) {
+	if (!Can_Be_Mounted)
+		return false;
+
+	struct stat st1, st2;
+	string test_path;
+
+	// Check to see if the mount point directory exists
+	test_path = Mount_Point + "/.";
+	if (stat(test_path.c_str(), &st1) != 0)  return false;
+
+	// Check to see if the directory above the mount point exists
+	test_path = Mount_Point + "/../.";
+	if (stat(test_path.c_str(), &st2) != 0)  return false;
+
+	// Compare the device IDs -- if they match then we're (probably) using tmpfs instead of an actual device
+	int ret = (st1.st_dev != st2.st_dev) ? true : false;
+
+	return ret;
+}
+
+bool TWPartition::Mount(bool Display_Error) {
+	int exfat_mounted = 0;
+
+	if (Is_Mounted()) {
+		return true;
+	} else if (!Can_Be_Mounted) {
+		return false;
+	}
+
+	Find_Actual_Block_Device();
+
+	// Check the current file system before mounting
+	Check_FS_Type();
+	if (Current_File_System == "exfat" && TWFunc::Path_Exists("/sbin/exfat-fuse")) {
+		string cmd = "/sbin/exfat-fuse -o big_writes,max_read=131072,max_write=131072 " + Actual_Block_Device + " " + Mount_Point;
+		LOGINFO("cmd: %s\n", cmd.c_str());
+		string result;
+		if (TWFunc::Exec_Cmd(cmd, result) != 0) {
+			LOGINFO("exfat-fuse failed to mount with result '%s', trying vfat\n", result.c_str());
+			Current_File_System = "vfat";
+		} else {
+#ifdef TW_NO_EXFAT_FUSE
+			UnMount(false);
+			// We'll let the kernel handle it but using exfat-fuse to detect if the file system is actually exfat
+			// Some kernels let us mount vfat as exfat which doesn't work out too well
+#else
+			exfat_mounted = 1;
+#endif
+		}
+	}
+	if (Fstab_File_System == "yaffs2") {
+		// mount an MTD partition as a YAFFS2 filesystem.
+		const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
+		if (mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), Fstab_File_System.c_str(), flags, NULL) < 0) {
+			if (mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), Fstab_File_System.c_str(), flags | MS_RDONLY, NULL) < 0) {
+				if (Display_Error)
+					LOGERR("Failed to mount '%s' (MTD)\n", Mount_Point.c_str());
+				else
+					LOGINFO("Failed to mount '%s' (MTD)\n", Mount_Point.c_str());
+				return false;
+			} else {
+				LOGINFO("Mounted '%s' (MTD) as RO\n", Mount_Point.c_str());
+				return true;
+			}
+		} else {
+			struct stat st;
+			string test_path = Mount_Point;
+			if (stat(test_path.c_str(), &st) < 0) {
+				if (Display_Error)
+					LOGERR("Failed to mount '%s' (MTD)\n", Mount_Point.c_str());
+				else
+					LOGINFO("Failed to mount '%s' (MTD)\n", Mount_Point.c_str());
+				return false;
+			}
+			mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
+			if (new_mode != st.st_mode) {
+				LOGINFO("Fixing execute permissions for %s\n", Mount_Point.c_str());
+				if (chmod(Mount_Point.c_str(), new_mode) < 0) {
+					if (Display_Error)
+						LOGERR("Couldn't fix permissions for %s: %s\n", Mount_Point.c_str(), strerror(errno));
+					else
+						LOGINFO("Couldn't fix permissions for %s: %s\n", Mount_Point.c_str(), strerror(errno));
+					return false;
+				}
+			}
+			return true;
+		}
+	} else if (!exfat_mounted && mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), Current_File_System.c_str(), Mount_Flags, Mount_Options.c_str()) != 0 && mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), Current_File_System.c_str(), Mount_Flags, NULL) != 0) {
+#ifdef TW_NO_EXFAT_FUSE
+		if (Current_File_System == "exfat") {
+			LOGINFO("Mounting exfat failed, trying vfat...\n");
+			if (mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), "vfat", 0, NULL) != 0) {
+				if (Display_Error)
+					LOGERR("Unable to mount '%s'\n", Mount_Point.c_str());
+				else
+					LOGINFO("Unable to mount '%s'\n", Mount_Point.c_str());
+				LOGINFO("Actual block device: '%s', current file system: '%s', flags: 0x%8x, options: '%s'\n", Actual_Block_Device.c_str(), Current_File_System.c_str(), Mount_Flags, Mount_Options.c_str());
+				return false;
+			}
+		} else {
+#endif
+			if (!Removable && Display_Error)
+				LOGERR("Unable to mount '%s'\n", Mount_Point.c_str());
+			else
+				LOGINFO("Unable to mount '%s'\n", Mount_Point.c_str());
+			LOGINFO("Actual block device: '%s', current file system: '%s'\n", Actual_Block_Device.c_str(), Current_File_System.c_str());
+			return false;
+#ifdef TW_NO_EXFAT_FUSE
+		}
+#endif
+	}
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+	string MetaEcfsFile = EXPAND(TW_EXTERNAL_STORAGE_PATH);
+	MetaEcfsFile += "/.MetaEcfsFile";
+	if (EcryptFS_Password.size() > 0 && PartitionManager.Mount_By_Path("/data", false) && TWFunc::Path_Exists(MetaEcfsFile)) {
+		if (mount_ecryptfs_drive(EcryptFS_Password.c_str(), Mount_Point.c_str(), Mount_Point.c_str(), 0) != 0) {
+			if (Display_Error)
+				LOGERR("Unable to mount ecryptfs for '%s'\n", Mount_Point.c_str());
+			else
+				LOGINFO("Unable to mount ecryptfs for '%s'\n", Mount_Point.c_str());
+		} else {
+			LOGINFO("Successfully mounted ecryptfs for '%s'\n", Mount_Point.c_str());
+			Is_Decrypted = true;
+		}
+	} else if (Mount_Point == EXPAND(TW_EXTERNAL_STORAGE_PATH)) {
+		if (Is_Decrypted)
+			LOGINFO("Mounting external storage, '%s' is not encrypted\n", Mount_Point.c_str());
+		Is_Decrypted = false;
+	}
+#endif
+	if (Removable)
+		Update_Size(Display_Error);
+
+	if (!Symlink_Mount_Point.empty()) {
+		string Command = "mount '" + Symlink_Path + "' '" + Symlink_Mount_Point + "'";
+		TWFunc::Exec_Cmd(Command);
+	}
+	return true;
+}
+
+bool TWPartition::UnMount(bool Display_Error) {
+	if (Is_Mounted()) {
+		int never_unmount_system;
+
+		DataManager::GetValue(TW_DONT_UNMOUNT_SYSTEM, never_unmount_system);
+		if (never_unmount_system == 1 && Mount_Point == "/system")
+			return true; // Never unmount system if you're not supposed to unmount it
+
+		if (Is_Storage)
+			TWFunc::Toggle_MTP(false);
+
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+		if (EcryptFS_Password.size() > 0) {
+			if (unmount_ecryptfs_drive(Mount_Point.c_str()) != 0) {
+				if (Display_Error)
+					LOGERR("Unable to unmount ecryptfs for '%s'\n", Mount_Point.c_str());
+				else
+					LOGINFO("Unable to unmount ecryptfs for '%s'\n", Mount_Point.c_str());
+			} else {
+				LOGINFO("Successfully unmounted ecryptfs for '%s'\n", Mount_Point.c_str());
+			}
+		}
+#endif
+
+		if (!Symlink_Mount_Point.empty())
+			umount(Symlink_Mount_Point.c_str());
+
+		umount(Mount_Point.c_str());
+		if (Is_Mounted()) {
+			if (Display_Error)
+				LOGERR("Unable to unmount '%s'\n", Mount_Point.c_str());
+			else
+				LOGINFO("Unable to unmount '%s'\n", Mount_Point.c_str());
+			return false;
+		} else {
+			return true;
+		}
+	} else {
+		return true;
+	}
+}
+
+bool TWPartition::Wipe(string New_File_System) {
+	bool wiped = false, update_crypt = false, recreate_media = true, mtp_toggle = true;
+	int check;
+	string Layout_Filename = Mount_Point + "/.layout_version";
+
+	if (!Can_Be_Wiped) {
+		LOGERR("Partition '%s' cannot be wiped.\n", Mount_Point.c_str());
+		return false;
+	}
+
+	if (Mount_Point == "/cache")
+		Log_Offset = 0;
+
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+	if (Mount_Point == "/data" && Mount(false)) {
+		if (TWFunc::Path_Exists("/data/system/edk_p_sd"))
+			TWFunc::copy_file("/data/system/edk_p_sd", "/tmp/edk_p_sd", 0600);
+	}
+#endif
+
+	if (Retain_Layout_Version && Mount(false) && TWFunc::Path_Exists(Layout_Filename))
+		TWFunc::copy_file(Layout_Filename, "/.layout_version", 0600);
+	else
+		unlink("/.layout_version");
+
+	if (Has_Data_Media && Current_File_System == New_File_System) {
+		wiped = Wipe_Data_Without_Wiping_Media();
+		recreate_media = false;
+		mtp_toggle = false;
+	} else {
+		DataManager::GetValue(TW_RM_RF_VAR, check);
+
+		if (check || Use_Rm_Rf)
+			wiped = Wipe_RMRF();
+		else if (New_File_System == "ext4")
+			wiped = Wipe_EXT4();
+		else if (New_File_System == "ext2" || New_File_System == "ext3")
+			wiped = Wipe_EXT23(New_File_System);
+		else if (New_File_System == "vfat")
+			wiped = Wipe_FAT();
+		else if (New_File_System == "exfat")
+			wiped = Wipe_EXFAT();
+		else if (New_File_System == "yaffs2")
+			wiped = Wipe_MTD();
+		else if (New_File_System == "f2fs")
+			wiped = Wipe_F2FS();
+		else {
+			if (Is_Storage) {
+				TWFunc::Toggle_MTP(true);
+			}
+			LOGERR("Unable to wipe '%s' -- unknown file system '%s'\n", Mount_Point.c_str(), New_File_System.c_str());
+			unlink("/.layout_version");
+			return false;
+		}
+		update_crypt = wiped;
+	}
+
+	if (wiped) {
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+		if (Mount_Point == "/data" && Mount(false)) {
+			if (TWFunc::Path_Exists("/tmp/edk_p_sd")) {
+				Make_Dir("/data/system", true);
+				TWFunc::copy_file("/tmp/edk_p_sd", "/data/system/edk_p_sd", 0600);
+			}
+		}
+#endif
+
+		if (Mount_Point == "/cache")
+			DataManager::Output_Version();
+
+		if (TWFunc::Path_Exists("/.layout_version") && Mount(false))
+			TWFunc::copy_file("/.layout_version", Layout_Filename, 0600);
+
+		if (update_crypt) {
+			Setup_File_System(false);
+			if (Is_Encrypted && !Is_Decrypted) {
+				// just wiped an encrypted partition back to its unencrypted state
+				Is_Encrypted = false;
+				Is_Decrypted = false;
+				Decrypted_Block_Device = "";
+				if (Mount_Point == "/data") {
+					DataManager::SetValue(TW_IS_ENCRYPTED, 0);
+					DataManager::SetValue(TW_IS_DECRYPTED, 0);
+				}
+			}
+		}
+
+		if (Mount_Point == "/data" && Has_Data_Media && recreate_media) {
+			Recreate_Media_Folder();
+		}
+	}
+	if (Is_Storage && mtp_toggle) {
+		TWFunc::Toggle_MTP(true);
+	}
+	return wiped;
+}
+
+bool TWPartition::Wipe() {
+	if (Is_File_System(Current_File_System))
+		return Wipe(Current_File_System);
+	else
+		return Wipe(Fstab_File_System);
+}
+
+bool TWPartition::Wipe_AndSec(void) {
+	if (!Has_Android_Secure)
+		return false;
+
+	if (!Mount(true))
+		return false;
+
+	gui_print("Wiping %s\n", Backup_Display_Name.c_str());
+	TWFunc::removeDir(Mount_Point + "/.android_secure/", true);
+	return true;
+}
+
+bool TWPartition::Can_Repair() {
+	if (Current_File_System == "vfat" && TWFunc::Path_Exists("/sbin/dosfsck"))
+		return true;
+	else if ((Current_File_System == "ext2" || Current_File_System == "ext3" || Current_File_System == "ext4") && TWFunc::Path_Exists("/sbin/e2fsck"))
+		return true;
+	else if (Current_File_System == "exfat" && TWFunc::Path_Exists("/sbin/fsck.exfat"))
+		return true;
+	else if (Current_File_System == "f2fs" && TWFunc::Path_Exists("/sbin/fsck.f2fs"))
+		return true;
+	return false;
+}
+
+bool TWPartition::Repair() {
+	string command;
+
+	if (Current_File_System == "vfat") {
+		if (!TWFunc::Path_Exists("/sbin/dosfsck")) {
+			gui_print("dosfsck does not exist! Cannot repair!\n");
+			return false;
+		}
+		if (!UnMount(true))
+			return false;
+		gui_print("Repairing %s using dosfsck...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "/sbin/dosfsck -y " + Actual_Block_Device;
+		LOGINFO("Repair command: %s\n", command.c_str());
+		if (TWFunc::Exec_Cmd(command) == 0) {
+			gui_print("Done.\n");
+			return true;
+		} else {
+			LOGERR("Unable to repair '%s'.\n", Mount_Point.c_str());
+			return false;
+		}
+	}
+	if (Current_File_System == "ext2" || Current_File_System == "ext3" || Current_File_System == "ext4") {
+		if (!TWFunc::Path_Exists("/sbin/e2fsck")) {
+			gui_print("e2fsck does not exist! Cannot repair!\n");
+			return false;
+		}
+		if (!UnMount(true))
+			return false;
+		gui_print("Repairing %s using e2fsck...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "/sbin/e2fsck -p " + Actual_Block_Device;
+		LOGINFO("Repair command: %s\n", command.c_str());
+		if (TWFunc::Exec_Cmd(command) == 0) {
+			gui_print("Done.\n");
+			return true;
+		} else {
+			LOGERR("Unable to repair '%s'.\n", Mount_Point.c_str());
+			return false;
+		}
+	}
+	if (Current_File_System == "exfat") {
+		if (!TWFunc::Path_Exists("/sbin/fsck.exfat")) {
+			gui_print("fsck.exfat does not exist! Cannot repair!\n");
+			return false;
+		}
+		if (!UnMount(true))
+			return false;
+		gui_print("Repairing %s using fsck.exfat...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "/sbin/fsck.exfat " + Actual_Block_Device;
+		LOGINFO("Repair command: %s\n", command.c_str());
+		if (TWFunc::Exec_Cmd(command) == 0) {
+			gui_print("Done.\n");
+			return true;
+		} else {
+			LOGERR("Unable to repair '%s'.\n", Mount_Point.c_str());
+			return false;
+		}
+	}
+	if (Current_File_System == "f2fs") {
+		if (!TWFunc::Path_Exists("/sbin/fsck.f2fs")) {
+			gui_print("fsck.f2fs does not exist! Cannot repair!\n");
+			return false;
+		}
+		if (!UnMount(true))
+			return false;
+		gui_print("Repairing %s using fsck.f2fs...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "/sbin/fsck.f2fs " + Actual_Block_Device;
+		LOGINFO("Repair command: %s\n", command.c_str());
+		if (TWFunc::Exec_Cmd(command) == 0) {
+			gui_print("Done.\n");
+			return true;
+		} else {
+			LOGERR("Unable to repair '%s'.\n", Mount_Point.c_str());
+			return false;
+		}
+	}
+	return false;
+}
+
+bool TWPartition::Backup(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size) {
+	if (Backup_Method == FILES)
+		return Backup_Tar(backup_folder, overall_size, other_backups_size);
+	else if (Backup_Method == DD)
+		return Backup_DD(backup_folder);
+	else if (Backup_Method == FLASH_UTILS)
+		return Backup_Dump_Image(backup_folder);
+	LOGERR("Unknown backup method for '%s'\n", Mount_Point.c_str());
+	return false;
+}
+
+bool TWPartition::Check_MD5(string restore_folder) {
+	string Full_Filename, md5file;
+	char split_filename[512];
+	int index = 0;
+	twrpDigest md5sum;
+
+	sync();
+
+	memset(split_filename, 0, sizeof(split_filename));
+	Full_Filename = restore_folder + "/" + Backup_FileName;
+	if (!TWFunc::Path_Exists(Full_Filename)) {
+		// This is a split archive, we presume
+		sprintf(split_filename, "%s%03i", Full_Filename.c_str(), index);
+		LOGINFO("split_filename: %s\n", split_filename);
+		md5file = split_filename;
+		md5file += ".md5";
+		if (!TWFunc::Path_Exists(md5file)) {
+			LOGERR("No md5 file found for '%s'.\n", split_filename);
+			LOGERR("Please unselect Enable MD5 verification to restore.\n");
+			return false;
+		}
+		md5sum.setfn(split_filename);
+		while (index < 1000) {
+			if (TWFunc::Path_Exists(split_filename) && md5sum.verify_md5digest() != 0) {
+				LOGERR("MD5 failed to match on '%s'.\n", split_filename);
+				return false;
+			}
+			index++;
+			sprintf(split_filename, "%s%03i", Full_Filename.c_str(), index);
+			md5sum.setfn(split_filename);
+		}
+		return true;
+	} else {
+		// Single file archive
+		md5file = Full_Filename + ".md5";
+		if (!TWFunc::Path_Exists(md5file)) {
+			LOGERR("No md5 file found for '%s'.\n", Full_Filename.c_str());
+			LOGERR("Please unselect Enable MD5 verification to restore.\n");
+			return false;
+		}
+		md5sum.setfn(Full_Filename);
+		if (md5sum.verify_md5digest() != 0) {
+			LOGERR("MD5 failed to match on '%s'.\n", Full_Filename.c_str());
+			return false;
+		} else
+			return true;
+	}
+	return false;
+}
+
+bool TWPartition::Restore(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size) {
+	string Restore_File_System;
+
+	TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Display_Name, "Restoring");
+	LOGINFO("Restore filename is: %s\n", Backup_FileName.c_str());
+
+	Restore_File_System = Get_Restore_File_System(restore_folder);
+
+	if (Is_File_System(Restore_File_System))
+		return Restore_Tar(restore_folder, Restore_File_System, total_restore_size, already_restored_size);
+	else if (Is_Image(Restore_File_System)) {
+		*already_restored_size += TWFunc::Get_File_Size(Backup_Name);
+		if (Restore_File_System == "emmc")
+			return Restore_DD(restore_folder, total_restore_size, already_restored_size);
+		else if (Restore_File_System == "mtd" || Restore_File_System == "bml")
+			return Restore_Flash_Image(restore_folder, total_restore_size, already_restored_size);
+	}
+
+	LOGERR("Unknown restore method for '%s'\n", Mount_Point.c_str());
+	return false;
+}
+
+string TWPartition::Get_Restore_File_System(string restore_folder) {
+	size_t first_period, second_period;
+	string Restore_File_System;
+
+	// Parse backup filename to extract the file system before wiping
+	first_period = Backup_FileName.find(".");
+	if (first_period == string::npos) {
+		LOGERR("Unable to find file system (first period).\n");
+		return string();
+	}
+	Restore_File_System = Backup_FileName.substr(first_period + 1, Backup_FileName.size() - first_period - 1);
+	second_period = Restore_File_System.find(".");
+	if (second_period == string::npos) {
+		LOGERR("Unable to find file system (second period).\n");
+		return string();
+	}
+	Restore_File_System.resize(second_period);
+	LOGINFO("Restore file system is: '%s'.\n", Restore_File_System.c_str());
+	return Restore_File_System;
+}
+
+string TWPartition::Backup_Method_By_Name() {
+	if (Backup_Method == NONE)
+		return "none";
+	else if (Backup_Method == FILES)
+		return "files";
+	else if (Backup_Method == DD)
+		return "dd";
+	else if (Backup_Method == FLASH_UTILS)
+		return "flash_utils";
+	else
+		return "undefined";
+	return "ERROR!";
+}
+
+bool TWPartition::Decrypt(string Password) {
+	LOGINFO("STUB TWPartition::Decrypt, password: '%s'\n", Password.c_str());
+	// Is this needed?
+	return 1;
+}
+
+bool TWPartition::Wipe_Encryption() {
+	bool Save_Data_Media = Has_Data_Media;
+
+	if (!UnMount(true))
+		return false;
+
+	Has_Data_Media = false;
+	Decrypted_Block_Device = "";
+	Is_Decrypted = false;
+	Is_Encrypted = false;
+	Find_Actual_Block_Device();
+	bool mtp_was_enabled = TWFunc::Toggle_MTP(false);
+	if (Wipe(Fstab_File_System)) {
+		Has_Data_Media = Save_Data_Media;
+		if (Has_Data_Media && !Symlink_Mount_Point.empty()) {
+			Recreate_Media_Folder();
+		}
+#ifndef TW_OEM_BUILD
+		gui_print("You may need to reboot recovery to be able to use /data again.\n");
+#endif
+		TWFunc::Toggle_MTP(mtp_was_enabled);
+		return true;
+	} else {
+		Has_Data_Media = Save_Data_Media;
+		LOGERR("Unable to format to remove encryption.\n");
+		return false;
+	}
+	return false;
+}
+
+void TWPartition::Check_FS_Type() {
+	const char* type;
+	blkid_probe pr;
+
+	if (Fstab_File_System == "yaffs2" || Fstab_File_System == "mtd" || Fstab_File_System == "bml" || Ignore_Blkid)
+		return; // Running blkid on some mtd devices causes a massive crash or needs to be skipped
+
+	Find_Actual_Block_Device();
+	if (!Is_Present)
+		return;
+
+	pr = blkid_new_probe_from_filename(Actual_Block_Device.c_str());
+	if (blkid_do_fullprobe(pr)) {
+		blkid_free_probe(pr);
+		LOGINFO("Can't probe device %s\n", Actual_Block_Device.c_str());
+		return;
+	}
+
+	if (blkid_probe_lookup_value(pr, "TYPE", &type, NULL) < 0) {
+		blkid_free_probe(pr);
+		LOGINFO("can't find filesystem on device %s\n", Actual_Block_Device.c_str());
+		return;
+	}
+
+	Current_File_System = type;
+	blkid_free_probe(pr);
+}
+
+bool TWPartition::Wipe_EXT23(string File_System) {
+	if (!UnMount(true))
+		return false;
+
+	if (TWFunc::Path_Exists("/sbin/mke2fs")) {
+		string command;
+
+		gui_print("Formatting %s using mke2fs...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "mke2fs -t " + File_System + " -m 0 " + Actual_Block_Device;
+		LOGINFO("mke2fs command: %s\n", command.c_str());
+		if (TWFunc::Exec_Cmd(command) == 0) {
+			Current_File_System = File_System;
+			Recreate_AndSec_Folder();
+			gui_print("Done.\n");
+			return true;
+		} else {
+			LOGERR("Unable to wipe '%s'.\n", Mount_Point.c_str());
+			return false;
+		}
+	} else
+		return Wipe_RMRF();
+
+	return false;
+}
+
+bool TWPartition::Wipe_EXT4() {
+	Find_Actual_Block_Device();
+	if (!Is_Present) {
+		LOGERR("Block device not present, cannot wipe %s.\n", Display_Name.c_str());
+		return false;
+	}
+	if (!UnMount(true))
+		return false;
+
+#if defined(HAVE_SELINUX) && defined(USE_EXT4)
+	int ret;
+	char *secontext = NULL;
+
+	gui_print("Formatting %s using make_ext4fs function.\n", Display_Name.c_str());
+
+	if (!selinux_handle || selabel_lookup(selinux_handle, &secontext, Mount_Point.c_str(), S_IFDIR) < 0) {
+		LOGINFO("Cannot lookup security context for '%s'\n", Mount_Point.c_str());
+		ret = make_ext4fs(Actual_Block_Device.c_str(), Length, Mount_Point.c_str(), NULL);
+	} else {
+		ret = make_ext4fs(Actual_Block_Device.c_str(), Length, Mount_Point.c_str(), selinux_handle);
+	}
+	if (ret != 0) {
+		LOGERR("Unable to wipe '%s' using function call.\n", Mount_Point.c_str());
+		return false;
+	} else {
+		string sedir = Mount_Point + "/lost+found";
+		PartitionManager.Mount_By_Path(sedir.c_str(), true);
+		rmdir(sedir.c_str());
+		mkdir(sedir.c_str(), S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP);
+		return true;
+	}
+#else
+	if (TWFunc::Path_Exists("/sbin/make_ext4fs")) {
+		string Command;
+
+		gui_print("Formatting %s using make_ext4fs...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		Command = "make_ext4fs";
+		if (!Is_Decrypted && Length != 0) {
+			// Only use length if we're not decrypted
+			char len[32];
+			sprintf(len, "%i", Length);
+			Command += " -l ";
+			Command += len;
+		}
+		if (TWFunc::Path_Exists("/file_contexts")) {
+			Command += " -S /file_contexts";
+		}
+		Command += " -a " + Mount_Point + " " + Actual_Block_Device;
+		LOGINFO("make_ext4fs command: %s\n", Command.c_str());
+		if (TWFunc::Exec_Cmd(Command) == 0) {
+			Current_File_System = "ext4";
+			Recreate_AndSec_Folder();
+			gui_print("Done.\n");
+			return true;
+		} else {
+			LOGERR("Unable to wipe '%s'.\n", Mount_Point.c_str());
+			return false;
+		}
+	} else
+		return Wipe_EXT23("ext4");
+#endif
+	return false;
+}
+
+bool TWPartition::Wipe_FAT() {
+	string command;
+
+	if (TWFunc::Path_Exists("/sbin/mkdosfs")) {
+		if (!UnMount(true))
+			return false;
+
+		gui_print("Formatting %s using mkdosfs...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "mkdosfs " + Actual_Block_Device;
+		if (TWFunc::Exec_Cmd(command) == 0) {
+			Current_File_System = "vfat";
+			Recreate_AndSec_Folder();
+			gui_print("Done.\n");
+			return true;
+		} else {
+			LOGERR("Unable to wipe '%s'.\n", Mount_Point.c_str());
+			return false;
+		}
+		return true;
+	}
+	else
+		return Wipe_RMRF();
+
+	return false;
+}
+
+bool TWPartition::Wipe_EXFAT() {
+	string command;
+
+	if (TWFunc::Path_Exists("/sbin/mkexfatfs")) {
+		if (!UnMount(true))
+			return false;
+
+		gui_print("Formatting %s using mkexfatfs...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "mkexfatfs " + Actual_Block_Device;
+		if (TWFunc::Exec_Cmd(command) == 0) {
+			Recreate_AndSec_Folder();
+			gui_print("Done.\n");
+			return true;
+		} else {
+			LOGERR("Unable to wipe '%s'.\n", Mount_Point.c_str());
+			return false;
+		}
+		return true;
+	}
+	return false;
+}
+
+bool TWPartition::Wipe_MTD() {
+	if (!UnMount(true))
+		return false;
+
+	gui_print("MTD Formatting \"%s\"\n", MTD_Name.c_str());
+
+	mtd_scan_partitions();
+	const MtdPartition* mtd = mtd_find_partition_by_name(MTD_Name.c_str());
+	if (mtd == NULL) {
+		LOGERR("No mtd partition named '%s'", MTD_Name.c_str());
+		return false;
+	}
+
+	MtdWriteContext* ctx = mtd_write_partition(mtd);
+	if (ctx == NULL) {
+		LOGERR("Can't write '%s', failed to format.", MTD_Name.c_str());
+		return false;
+	}
+	if (mtd_erase_blocks(ctx, -1) == -1) {
+		mtd_write_close(ctx);
+		LOGERR("Failed to format '%s'", MTD_Name.c_str());
+		return false;
+	}
+	if (mtd_write_close(ctx) != 0) {
+		LOGERR("Failed to close '%s'", MTD_Name.c_str());
+		return false;
+	}
+	Current_File_System = "yaffs2";
+	Recreate_AndSec_Folder();
+	gui_print("Done.\n");
+	return true;
+}
+
+bool TWPartition::Wipe_RMRF() {
+	if (Is_Storage)
+		TWFunc::Toggle_MTP(false);
+	if (!Mount(true))
+		return false;
+
+	gui_print("Removing all files under '%s'\n", Mount_Point.c_str());
+	TWFunc::removeDir(Mount_Point, true);
+	Recreate_AndSec_Folder();
+	return true;
+}
+
+bool TWPartition::Wipe_F2FS() {
+	string command;
+
+	if (TWFunc::Path_Exists("/sbin/mkfs.f2fs")) {
+		if (!UnMount(true))
+			return false;
+
+		gui_print("Formatting %s using mkfs.f2fs...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "mkfs.f2fs " + Actual_Block_Device;
+		if (TWFunc::Exec_Cmd(command) == 0) {
+			Recreate_AndSec_Folder();
+			gui_print("Done.\n");
+			return true;
+		} else {
+			LOGERR("Unable to wipe '%s'.\n", Mount_Point.c_str());
+			return false;
+		}
+		return true;
+	} else {
+		gui_print("mkfs.f2fs binary not found, using rm -rf to wipe.\n");
+		return Wipe_RMRF();
+	}
+	return false;
+}
+
+bool TWPartition::Wipe_Data_Without_Wiping_Media() {
+#ifdef TW_OEM_BUILD
+	// In an OEM Build we want to do a full format
+	return Wipe_Encryption();
+#else
+	string dir;
+	#ifdef HAVE_SELINUX
+	fixPermissions perms;
+	#endif
+
+	// This handles wiping data on devices with "sdcard" in /data/media
+	if (!Mount(true))
+		return false;
+
+	gui_print("Wiping data without wiping /data/media ...\n");
+
+	DIR* d;
+	d = opendir("/data");
+	if (d != NULL) {
+		struct dirent* de;
+		while ((de = readdir(d)) != NULL) {
+			if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)	 continue;
+			// The media folder is the "internal sdcard"
+			// The .layout_version file is responsible for determining whether 4.2 decides up upgrade
+			// the media folder for multi-user.
+			//TODO: convert this to use twrpDU.cpp
+			if (strcmp(de->d_name, "media") == 0 || strcmp(de->d_name, ".layout_version") == 0)   continue;
+
+			dir = "/data/";
+			dir.append(de->d_name);
+			if (de->d_type == DT_DIR) {
+				TWFunc::removeDir(dir, false);
+			} else if (de->d_type == DT_REG || de->d_type == DT_LNK || de->d_type == DT_FIFO || de->d_type == DT_SOCK) {
+				if (!unlink(dir.c_str()))
+					LOGINFO("Unable to unlink '%s'\n", dir.c_str());
+			}
+		}
+		closedir(d);
+
+		gui_print("Done.\n");
+		return true;
+	}
+	gui_print("Dirent failed to open /data, error!\n");
+	return false;
+#endif // ifdef TW_OEM_BUILD
+}
+
+bool TWPartition::Backup_Tar(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size) {
+	char back_name[255], split_index[5];
+	string Full_FileName, Split_FileName, Tar_Args, Command;
+	int use_compression, use_encryption = 0, index, backup_count;
+	struct stat st;
+	unsigned long long total_bsize = 0, file_size;
+	twrpTar tar;
+	vector <string> files;
+
+	if (!Mount(true))
+		return false;
+
+	TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Backup_Display_Name, "Backing Up");
+	gui_print("Backing up %s...\n", Backup_Display_Name.c_str());
+
+	DataManager::GetValue(TW_USE_COMPRESSION_VAR, use_compression);
+	tar.use_compression = use_compression;
+
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	DataManager::GetValue("tw_encrypt_backup", use_encryption);
+	if (use_encryption && Can_Encrypt_Backup) {
+		tar.use_encryption = use_encryption;
+		if (Use_Userdata_Encryption)
+			tar.userdata_encryption = use_encryption;
+		string Password;
+		DataManager::GetValue("tw_backup_password", Password);
+		tar.setpassword(Password);
+	} else {
+		use_encryption = false;
+	}
+#endif
+
+	sprintf(back_name, "%s.%s.win", Backup_Name.c_str(), Current_File_System.c_str());
+	Backup_FileName = back_name;
+	Full_FileName = backup_folder + "/" + Backup_FileName;
+	tar.has_data_media = Has_Data_Media;
+	Full_FileName = backup_folder + "/" + Backup_FileName;
+	tar.setdir(Backup_Path);
+	tar.setfn(Full_FileName);
+	tar.setsize(Backup_Size);
+	tar.partition_name = Backup_Name;
+	tar.backup_folder = backup_folder;
+	if (tar.createTarFork(overall_size, other_backups_size) != 0)
+		return false;
+	return true;
+}
+
+bool TWPartition::Backup_DD(string backup_folder) {
+	char back_name[255], backup_size[32];
+	string Full_FileName, Command, DD_BS;
+	int use_compression;
+
+	sprintf(backup_size, "%llu", Backup_Size);
+	DD_BS = backup_size;
+
+	TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Display_Name, "Backing Up");
+	gui_print("Backing up %s...\n", Display_Name.c_str());
+
+	sprintf(back_name, "%s.%s.win", Backup_Name.c_str(), Current_File_System.c_str());
+	Backup_FileName = back_name;
+
+	Full_FileName = backup_folder + "/" + Backup_FileName;
+
+	Command = "dd if=" + Actual_Block_Device + " of='" + Full_FileName + "'" + " bs=" + DD_BS + "c count=1";
+	LOGINFO("Backup command: '%s'\n", Command.c_str());
+	TWFunc::Exec_Cmd(Command);
+	if (TWFunc::Get_File_Size(Full_FileName) == 0) {
+		LOGERR("Backup file size for '%s' is 0 bytes.\n", Full_FileName.c_str());
+		return false;
+	}
+	return true;
+}
+
+bool TWPartition::Backup_Dump_Image(string backup_folder) {
+	char back_name[255];
+	string Full_FileName, Command;
+	int use_compression;
+
+	TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Display_Name, "Backing Up");
+	gui_print("Backing up %s...\n", Display_Name.c_str());
+
+	sprintf(back_name, "%s.%s.win", Backup_Name.c_str(), Current_File_System.c_str());
+	Backup_FileName = back_name;
+
+	Full_FileName = backup_folder + "/" + Backup_FileName;
+
+	Command = "dump_image " + MTD_Name + " '" + Full_FileName + "'";
+	LOGINFO("Backup command: '%s'\n", Command.c_str());
+	TWFunc::Exec_Cmd(Command);
+	if (TWFunc::Get_File_Size(Full_FileName) == 0) {
+		// Actual size may not match backup size due to bad blocks on MTD devices so just check for 0 bytes
+		LOGERR("Backup file size for '%s' is 0 bytes.\n", Full_FileName.c_str());
+		return false;
+	}
+	return true;
+}
+
+unsigned long long TWPartition::Get_Restore_Size(string restore_folder) {
+	InfoManager restore_info(restore_folder + "/" + Backup_Name + ".info");
+	if (restore_info.LoadValues() == 0) {
+		if (restore_info.GetValue("backup_size", Restore_Size) == 0) {
+			LOGINFO("Read info file, restore size is %llu\n", Restore_Size);
+			return Restore_Size;
+		}
+	}
+	string Full_FileName, Restore_File_System = Get_Restore_File_System(restore_folder);
+
+	Full_FileName = restore_folder + "/" + Backup_FileName;
+
+	if (Is_Image(Restore_File_System)) {
+		Restore_Size = TWFunc::Get_File_Size(Full_FileName);
+		return Restore_Size;
+	}
+
+	twrpTar tar;
+	tar.setdir(Backup_Path);
+	tar.setfn(Full_FileName);
+	tar.backup_name = Backup_Name;
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	string Password;
+	DataManager::GetValue("tw_restore_password", Password);
+	if (!Password.empty())
+		tar.setpassword(Password);
+#endif
+	tar.partition_name = Backup_Name;
+	tar.backup_folder = restore_folder;
+	Restore_Size = tar.get_size();
+	return Restore_Size;
+}
+
+bool TWPartition::Restore_Tar(string restore_folder, string Restore_File_System, const unsigned long long *total_restore_size, unsigned long long *already_restored_size) {
+	string Full_FileName, Command;
+	int index = 0;
+	char split_index[5];
+	bool ret = false;
+
+	if (Has_Android_Secure) {
+		if (!Wipe_AndSec())
+			return false;
+	} else {
+		gui_print("Wiping %s...\n", Display_Name.c_str());
+		if (Has_Data_Media && Mount_Point == "/data" && Restore_File_System != Current_File_System) {
+			gui_print("WARNING: This /data backup was made with %s file system!\n", Restore_File_System.c_str());
+			gui_print("The backup may not boot unless you change back to %s.\n", Restore_File_System.c_str());
+			if (!Wipe_Data_Without_Wiping_Media())
+				return false;
+		} else {
+			if (!Wipe(Restore_File_System))
+				return false;
+		}
+	}
+	TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Backup_Display_Name, "Restoring");
+	gui_print("Restoring %s...\n", Backup_Display_Name.c_str());
+
+	if (!Mount(true))
+		return false;
+
+	Full_FileName = restore_folder + "/" + Backup_FileName;
+	twrpTar tar;
+	tar.setdir(Backup_Path);
+	tar.setfn(Full_FileName);
+	tar.backup_name = Backup_Name;
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	string Password;
+	DataManager::GetValue("tw_restore_password", Password);
+	if (!Password.empty())
+		tar.setpassword(Password);
+#endif
+	if (tar.extractTarFork(total_restore_size, already_restored_size) != 0)
+		ret = false;
+	else
+		ret = true;
+#ifdef HAVE_CAPABILITIES
+	// Restore capabilities to the run-as binary
+	if (Mount_Point == "/system" && Mount(true) && TWFunc::Path_Exists("/system/bin/run-as")) {
+		struct vfs_cap_data cap_data;
+		uint64_t capabilities = (1 << CAP_SETUID) | (1 << CAP_SETGID);
+
+		memset(&cap_data, 0, sizeof(cap_data));
+		cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
+		cap_data.data[0].permitted = (uint32_t) (capabilities & 0xffffffff);
+		cap_data.data[0].inheritable = 0;
+		cap_data.data[1].permitted = (uint32_t) (capabilities >> 32);
+		cap_data.data[1].inheritable = 0;
+		if (setxattr("/system/bin/run-as", XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) {
+			LOGINFO("Failed to reset capabilities of /system/bin/run-as binary.\n");
+		} else {
+			LOGINFO("Reset capabilities of /system/bin/run-as binary successful.\n");
+		}
+	}
+#endif
+	return ret;
+}
+
+bool TWPartition::Restore_DD(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size) {
+	string Full_FileName, Command;
+	double display_percent, progress_percent;
+	char size_progress[1024];
+
+	TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Display_Name, "Restoring");
+	Full_FileName = restore_folder + "/" + Backup_FileName;
+
+	if (!Find_Partition_Size()) {
+		LOGERR("Unable to find partition size for '%s'\n", Mount_Point.c_str());
+		return false;
+	}
+	unsigned long long backup_size = TWFunc::Get_File_Size(Full_FileName);
+	if (backup_size > Size) {
+		LOGERR("Size (%iMB) of backup '%s' is larger than target device '%s' (%iMB)\n",
+			(int)(backup_size / 1048576LLU), Full_FileName.c_str(),
+			Actual_Block_Device.c_str(), (int)(Size / 1048576LLU));
+		return false;
+	}
+
+	gui_print("Restoring %s...\n", Display_Name.c_str());
+	Command = "dd bs=4096 if='" + Full_FileName + "' of=" + Actual_Block_Device;
+	LOGINFO("Restore command: '%s'\n", Command.c_str());
+	TWFunc::Exec_Cmd(Command);
+	display_percent = (double)(Restore_Size + *already_restored_size) / (double)(*total_restore_size) * 100;
+	sprintf(size_progress, "%lluMB of %lluMB, %i%%", (Restore_Size + *already_restored_size) / 1048576, *total_restore_size / 1048576, (int)(display_percent));
+	DataManager::SetValue("tw_size_progress", size_progress);
+	progress_percent = (display_percent / 100);
+	DataManager::SetProgress((float)(progress_percent));
+	*already_restored_size += Restore_Size;
+	return true;
+}
+
+bool TWPartition::Restore_Flash_Image(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size) {
+	string Full_FileName, Command;
+	double display_percent, progress_percent;
+	char size_progress[1024];
+
+	gui_print("Restoring %s...\n", Display_Name.c_str());
+	Full_FileName = restore_folder + "/" + Backup_FileName;
+	// Sometimes flash image doesn't like to flash due to the first 2KB matching, so we erase first to ensure that it flashes
+	Command = "erase_image " + MTD_Name;
+	LOGINFO("Erase command: '%s'\n", Command.c_str());
+	TWFunc::Exec_Cmd(Command);
+	Command = "flash_image " + MTD_Name + " '" + Full_FileName + "'";
+	LOGINFO("Restore command: '%s'\n", Command.c_str());
+	TWFunc::Exec_Cmd(Command);
+	display_percent = (double)(Restore_Size + *already_restored_size) / (double)(*total_restore_size) * 100;
+	sprintf(size_progress, "%lluMB of %lluMB, %i%%", (Restore_Size + *already_restored_size) / 1048576, *total_restore_size / 1048576, (int)(display_percent));
+	DataManager::SetValue("tw_size_progress", size_progress);
+	progress_percent = (display_percent / 100);
+	DataManager::SetProgress((float)(progress_percent));
+	*already_restored_size += Restore_Size;
+	return true;
+}
+
+bool TWPartition::Update_Size(bool Display_Error) {
+	bool ret = false, Was_Already_Mounted = false;
+
+	if (!Can_Be_Mounted && !Is_Encrypted)
+		return false;
+
+	Was_Already_Mounted = Is_Mounted();
+	if (Removable || Is_Encrypted) {
+		if (!Mount(false))
+			return true;
+	} else if (!Mount(Display_Error))
+		return false;
+
+	ret = Get_Size_Via_statfs(Display_Error);
+	if (!ret || Size == 0) {
+		if (!Get_Size_Via_df(Display_Error)) {
+			if (!Was_Already_Mounted)
+				UnMount(false);
+			return false;
+		}
+	}
+
+	if (Has_Data_Media) {
+		if (Mount(Display_Error)) {
+			unsigned long long data_media_used, actual_data;
+			Used = du.Get_Folder_Size("/data");
+			Backup_Size = Used;
+			int bak = (int)(Used / 1048576LLU);
+			int fre = (int)(Free / 1048576LLU);
+			LOGINFO("Data backup size is %iMB, free: %iMB.\n", bak, fre);
+		} else {
+			if (!Was_Already_Mounted)
+				UnMount(false);
+			return false;
+		}
+	} else if (Has_Android_Secure) {
+		if (Mount(Display_Error))
+			Backup_Size = du.Get_Folder_Size(Backup_Path);
+		else {
+			if (!Was_Already_Mounted)
+				UnMount(false);
+			return false;
+		}
+	}
+	if (!Was_Already_Mounted)
+		UnMount(false);
+	return true;
+}
+
+void TWPartition::Find_Actual_Block_Device(void) {
+	if (Is_Decrypted && !Decrypted_Block_Device.empty()) {
+		Actual_Block_Device = Decrypted_Block_Device;
+		if (TWFunc::Path_Exists(Decrypted_Block_Device))
+			Is_Present = true;
+	} else if (TWFunc::Path_Exists(Primary_Block_Device)) {
+		Is_Present = true;
+		Actual_Block_Device = Primary_Block_Device;
+		return;
+	}
+	if (Is_Decrypted) {
+	} else if (!Alternate_Block_Device.empty() && TWFunc::Path_Exists(Alternate_Block_Device)) {
+		Actual_Block_Device = Alternate_Block_Device;
+		Is_Present = true;
+	} else {
+		Is_Present = false;
+	}
+}
+
+void TWPartition::Recreate_Media_Folder(void) {
+	string Command;
+
+	#ifdef HAVE_SELINUX
+	fixPermissions perms;
+	#endif
+
+	if (!Mount(true)) {
+		LOGERR("Unable to recreate /data/media folder.\n");
+	} else if (!TWFunc::Path_Exists("/data/media")) {
+		PartitionManager.Mount_By_Path(Symlink_Mount_Point, true);
+		LOGINFO("Recreating /data/media folder.\n");
+		mkdir("/data/media", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+#ifdef HAVE_SELINUX
+		perms.fixDataInternalContexts();
+#endif
+		// Toggle mount to ensure that "internal sdcard" gets mounted
+		PartitionManager.UnMount_By_Path(Symlink_Mount_Point, true);
+		PartitionManager.Mount_By_Path(Symlink_Mount_Point, true);
+	}
+}
+
+void TWPartition::Recreate_AndSec_Folder(void) {
+	if (!Has_Android_Secure)
+		return;
+	LOGINFO("Creating %s: %s\n", Backup_Display_Name.c_str(), Symlink_Path.c_str());
+	if (!Mount(true)) {
+		LOGERR("Unable to recreate %s folder.\n", Backup_Name.c_str());
+	} else if (!TWFunc::Path_Exists(Symlink_Path)) {
+		LOGINFO("Recreating %s folder.\n", Backup_Name.c_str());
+		PartitionManager.Mount_By_Path(Symlink_Mount_Point, true);
+		mkdir(Symlink_Path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+		PartitionManager.UnMount_By_Path(Symlink_Mount_Point, true);
+	}
+}
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
new file mode 100644
index 0000000..96266a3
--- /dev/null
+++ b/partitionmanager.cpp
@@ -0,0 +1,2007 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+#include <vector>
+#include <dirent.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <iostream>
+#include <iomanip>
+#include <sys/wait.h>
+#include "variables.h"
+#include "twcommon.h"
+#include "partitions.hpp"
+#include "data.hpp"
+#include "twrp-functions.hpp"
+#include "fixPermissions.hpp"
+#include "twrpDigest.hpp"
+#include "twrpDU.hpp"
+
+#ifdef TW_HAS_MTP
+#include "mtp/mtp_MtpServer.hpp"
+#include "mtp/twrpMtp.hpp"
+#endif
+
+extern "C" {
+	#include "cutils/properties.h"
+}
+
+#ifdef TW_INCLUDE_CRYPTO
+	#ifdef TW_INCLUDE_JB_CRYPTO
+		#include "crypto/jb/cryptfs.h"
+	#else
+		#include "crypto/ics/cryptfs.h"
+	#endif
+#endif
+
+extern bool datamedia;
+
+TWPartitionManager::TWPartitionManager(void) {
+	mtp_was_enabled = false;
+}
+
+int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error) {
+	FILE *fstabFile;
+	char fstab_line[MAX_FSTAB_LINE_LENGTH];
+	TWPartition* settings_partition = NULL;
+	TWPartition* andsec_partition = NULL;
+
+	fstabFile = fopen(Fstab_Filename.c_str(), "rt");
+	if (fstabFile == NULL) {
+		LOGERR("Critical Error: Unable to open fstab at '%s'.\n", Fstab_Filename.c_str());
+		return false;
+	}
+
+	while (fgets(fstab_line, sizeof(fstab_line), fstabFile) != NULL) {
+		if (fstab_line[0] != '/')
+			continue;
+
+		if (fstab_line[strlen(fstab_line) - 1] != '\n')
+			fstab_line[strlen(fstab_line)] = '\n';
+		TWPartition* partition = new TWPartition();
+		string line = fstab_line;
+		memset(fstab_line, 0, sizeof(fstab_line));
+
+		if (partition->Process_Fstab_Line(line, Display_Error)) {
+			if (!settings_partition && partition->Is_Settings_Storage && partition->Is_Present) {
+				settings_partition = partition;
+			} else {
+				partition->Is_Settings_Storage = false;
+			}
+			if (!andsec_partition && partition->Has_Android_Secure && partition->Is_Present) {
+				andsec_partition = partition;
+			} else {
+				partition->Has_Android_Secure = false;
+			}
+			Partitions.push_back(partition);
+		} else {
+			delete partition;
+		}
+	}
+	fclose(fstabFile);
+	if (!datamedia && !settings_partition && Find_Partition_By_Path("/sdcard") == NULL && Find_Partition_By_Path("/internal_sd") == NULL && Find_Partition_By_Path("/internal_sdcard") == NULL && Find_Partition_By_Path("/emmc") == NULL) {
+		// Attempt to automatically identify /data/media emulated storage devices
+		TWPartition* Dat = Find_Partition_By_Path("/data");
+		if (Dat) {
+			LOGINFO("Using automatic handling for /data/media emulated storage device.\n");
+			datamedia = true;
+			Dat->Setup_Data_Media();
+			settings_partition = Dat;
+		}
+	}
+	if (!settings_partition) {
+		std::vector<TWPartition*>::iterator iter;
+		for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+			if ((*iter)->Is_Storage) {
+				settings_partition = (*iter);
+				break;
+			}
+		}
+		if (!settings_partition)
+			LOGERR("Unable to locate storage partition for storing settings file.\n");
+	}
+	if (!Write_Fstab()) {
+		if (Display_Error)
+			LOGERR("Error creating fstab\n");
+		else
+			LOGINFO("Error creating fstab\n");
+	}
+
+	if (andsec_partition) {
+		Setup_Android_Secure_Location(andsec_partition);
+	} else if (settings_partition) {
+		Setup_Android_Secure_Location(settings_partition);
+	}
+	if (settings_partition) {
+		Setup_Settings_Storage_Partition(settings_partition);
+	}
+	Update_System_Details();
+	UnMount_Main_Partitions();
+	return true;
+}
+
+int TWPartitionManager::Write_Fstab(void) {
+	FILE *fp;
+	std::vector<TWPartition*>::iterator iter;
+	string Line;
+
+	fp = fopen("/etc/fstab", "w");
+	if (fp == NULL) {
+		LOGINFO("Can not open /etc/fstab.\n");
+		return false;
+	}
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Can_Be_Mounted) {
+			Line = (*iter)->Actual_Block_Device + " " + (*iter)->Mount_Point + " " + (*iter)->Current_File_System + " rw\n";
+			fputs(Line.c_str(), fp);
+		}
+		// Handle subpartition tracking
+		if ((*iter)->Is_SubPartition) {
+			TWPartition* ParentPartition = Find_Partition_By_Path((*iter)->SubPartition_Of);
+			if (ParentPartition)
+				ParentPartition->Has_SubPartition = true;
+			else
+				LOGERR("Unable to locate parent partition '%s' of '%s'\n", (*iter)->SubPartition_Of.c_str(), (*iter)->Mount_Point.c_str());
+		}
+	}
+	fclose(fp);
+	return true;
+}
+
+void TWPartitionManager::Setup_Settings_Storage_Partition(TWPartition* Part) {
+	DataManager::SetValue("tw_settings_path", Part->Storage_Path);
+	DataManager::SetValue("tw_storage_path", Part->Storage_Path);
+	LOGINFO("Settings storage is '%s'\n", Part->Storage_Path.c_str());
+}
+
+void TWPartitionManager::Setup_Android_Secure_Location(TWPartition* Part) {
+	if (Part->Has_Android_Secure)
+		Part->Setup_AndSec();
+	else if (!datamedia)
+		Part->Setup_AndSec();
+}
+
+void TWPartitionManager::Output_Partition_Logging(void) {
+	std::vector<TWPartition*>::iterator iter;
+
+	printf("\n\nPartition Logs:\n");
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++)
+		Output_Partition((*iter));
+}
+
+void TWPartitionManager::Output_Partition(TWPartition* Part) {
+	unsigned long long mb = 1048576;
+
+	printf("%s | %s | Size: %iMB", Part->Mount_Point.c_str(), Part->Actual_Block_Device.c_str(), (int)(Part->Size / mb));
+	if (Part->Can_Be_Mounted) {
+		printf(" Used: %iMB Free: %iMB Backup Size: %iMB", (int)(Part->Used / mb), (int)(Part->Free / mb), (int)(Part->Backup_Size / mb));
+	}
+	printf("\n   Flags: ");
+	if (Part->Can_Be_Mounted)
+		printf("Can_Be_Mounted ");
+	if (Part->Can_Be_Wiped)
+		printf("Can_Be_Wiped ");
+	if (Part->Use_Rm_Rf)
+		printf("Use_Rm_Rf ");
+	if (Part->Can_Be_Backed_Up)
+		printf("Can_Be_Backed_Up ");
+	if (Part->Wipe_During_Factory_Reset)
+		printf("Wipe_During_Factory_Reset ");
+	if (Part->Wipe_Available_in_GUI)
+		printf("Wipe_Available_in_GUI ");
+	if (Part->Is_SubPartition)
+		printf("Is_SubPartition ");
+	if (Part->Has_SubPartition)
+		printf("Has_SubPartition ");
+	if (Part->Removable)
+		printf("Removable ");
+	if (Part->Is_Present)
+		printf("IsPresent ");
+	if (Part->Can_Be_Encrypted)
+		printf("Can_Be_Encrypted ");
+	if (Part->Is_Encrypted)
+		printf("Is_Encrypted ");
+	if (Part->Is_Decrypted)
+		printf("Is_Decrypted ");
+	if (Part->Has_Data_Media)
+		printf("Has_Data_Media ");
+	if (Part->Can_Encrypt_Backup)
+		printf("Can_Encrypt_Backup ");
+	if (Part->Use_Userdata_Encryption)
+		printf("Use_Userdata_Encryption ");
+	if (Part->Has_Android_Secure)
+		printf("Has_Android_Secure ");
+	if (Part->Is_Storage)
+		printf("Is_Storage ");
+	if (Part->Is_Settings_Storage)
+		printf("Is_Settings_Storage ");
+	if (Part->Ignore_Blkid)
+		printf("Ignore_Blkid ");
+	if (Part->Retain_Layout_Version)
+		printf("Retain_Layout_Version ");
+	printf("\n");
+	if (!Part->SubPartition_Of.empty())
+		printf("   SubPartition_Of: %s\n", Part->SubPartition_Of.c_str());
+	if (!Part->Symlink_Path.empty())
+		printf("   Symlink_Path: %s\n", Part->Symlink_Path.c_str());
+	if (!Part->Symlink_Mount_Point.empty())
+		printf("   Symlink_Mount_Point: %s\n", Part->Symlink_Mount_Point.c_str());
+	if (!Part->Primary_Block_Device.empty())
+		printf("   Primary_Block_Device: %s\n", Part->Primary_Block_Device.c_str());
+	if (!Part->Alternate_Block_Device.empty())
+		printf("   Alternate_Block_Device: %s\n", Part->Alternate_Block_Device.c_str());
+	if (!Part->Decrypted_Block_Device.empty())
+		printf("   Decrypted_Block_Device: %s\n", Part->Decrypted_Block_Device.c_str());
+	if (Part->Length != 0)
+		printf("   Length: %i\n", Part->Length);
+	if (!Part->Display_Name.empty())
+		printf("   Display_Name: %s\n", Part->Display_Name.c_str());
+	if (!Part->Storage_Name.empty())
+		printf("   Storage_Name: %s\n", Part->Storage_Name.c_str());
+	if (!Part->Backup_Path.empty())
+		printf("   Backup_Path: %s\n", Part->Backup_Path.c_str());
+	if (!Part->Backup_Name.empty())
+		printf("   Backup_Name: %s\n", Part->Backup_Name.c_str());
+	if (!Part->Backup_Display_Name.empty())
+		printf("   Backup_Display_Name: %s\n", Part->Backup_Display_Name.c_str());
+	if (!Part->Backup_FileName.empty())
+		printf("   Backup_FileName: %s\n", Part->Backup_FileName.c_str());
+	if (!Part->Storage_Path.empty())
+		printf("   Storage_Path: %s\n", Part->Storage_Path.c_str());
+	if (!Part->Current_File_System.empty())
+		printf("   Current_File_System: %s\n", Part->Current_File_System.c_str());
+	if (!Part->Fstab_File_System.empty())
+		printf("   Fstab_File_System: %s\n", Part->Fstab_File_System.c_str());
+	if (Part->Format_Block_Size != 0)
+		printf("   Format_Block_Size: %i\n", Part->Format_Block_Size);
+	if (!Part->MTD_Name.empty())
+		printf("   MTD_Name: %s\n", Part->MTD_Name.c_str());
+	string back_meth = Part->Backup_Method_By_Name();
+	printf("   Backup_Method: %s\n", back_meth.c_str());
+	if (Part->Mount_Flags || !Part->Mount_Options.empty())
+		printf("   Mount_Flags=0x%8x, Mount_Options=%s\n", Part->Mount_Flags, Part->Mount_Options.c_str());
+	printf("\n");
+}
+
+int TWPartitionManager::Mount_By_Path(string Path, bool Display_Error) {
+	std::vector<TWPartition*>::iterator iter;
+	int ret = false;
+	bool found = false;
+	string Local_Path = TWFunc::Get_Root_Path(Path);
+
+	if (Local_Path == "/tmp" || Local_Path == "/")
+		return true;
+
+	// Iterate through all partitions
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Mount_Point == Local_Path || (!(*iter)->Symlink_Mount_Point.empty() && (*iter)->Symlink_Mount_Point == Local_Path)) {
+			ret = (*iter)->Mount(Display_Error);
+			found = true;
+		} else if ((*iter)->Is_SubPartition && (*iter)->SubPartition_Of == Local_Path) {
+			(*iter)->Mount(Display_Error);
+		}
+	}
+	if (found) {
+		return ret;
+	} else if (Display_Error) {
+		LOGERR("Mount: Unable to find partition for path '%s'\n", Local_Path.c_str());
+	} else {
+		LOGINFO("Mount: Unable to find partition for path '%s'\n", Local_Path.c_str());
+	}
+	return false;
+}
+
+int TWPartitionManager::UnMount_By_Path(string Path, bool Display_Error) {
+	std::vector<TWPartition*>::iterator iter;
+	int ret = false;
+	bool found = false;
+	string Local_Path = TWFunc::Get_Root_Path(Path);
+
+	// Iterate through all partitions
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Mount_Point == Local_Path || (!(*iter)->Symlink_Mount_Point.empty() && (*iter)->Symlink_Mount_Point == Local_Path)) {
+			ret = (*iter)->UnMount(Display_Error);
+			found = true;
+		} else if ((*iter)->Is_SubPartition && (*iter)->SubPartition_Of == Local_Path) {
+			(*iter)->UnMount(Display_Error);
+		}
+	}
+	if (found) {
+		return ret;
+	} else if (Display_Error) {
+		LOGERR("UnMount: Unable to find partition for path '%s'\n", Local_Path.c_str());
+	} else {
+		LOGINFO("UnMount: Unable to find partition for path '%s'\n", Local_Path.c_str());
+	}
+	return false;
+}
+
+int TWPartitionManager::Is_Mounted_By_Path(string Path) {
+	TWPartition* Part = Find_Partition_By_Path(Path);
+
+	if (Part)
+		return Part->Is_Mounted();
+	else
+		LOGINFO("Is_Mounted: Unable to find partition for path '%s'\n", Path.c_str());
+	return false;
+}
+
+int TWPartitionManager::Mount_Current_Storage(bool Display_Error) {
+	string current_storage_path = DataManager::GetCurrentStoragePath();
+
+	if (Mount_By_Path(current_storage_path, Display_Error)) {
+		TWPartition* FreeStorage = Find_Partition_By_Path(current_storage_path);
+		if (FreeStorage)
+			DataManager::SetValue(TW_STORAGE_FREE_SIZE, (int)(FreeStorage->Free / 1048576LLU));
+		return true;
+	}
+	return false;
+}
+
+int TWPartitionManager::Mount_Settings_Storage(bool Display_Error) {
+	return Mount_By_Path(DataManager::GetSettingsStoragePath(), Display_Error);
+}
+
+TWPartition* TWPartitionManager::Find_Partition_By_Path(string Path) {
+	std::vector<TWPartition*>::iterator iter;
+	string Local_Path = TWFunc::Get_Root_Path(Path);
+
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Mount_Point == Local_Path || (!(*iter)->Symlink_Mount_Point.empty() && (*iter)->Symlink_Mount_Point == Local_Path))
+			return (*iter);
+	}
+	return NULL;
+}
+
+int TWPartitionManager::Check_Backup_Name(bool Display_Error) {
+	// Check the backup name to ensure that it is the correct size and contains only valid characters
+	// and that a backup with that name doesn't already exist
+	char backup_name[MAX_BACKUP_NAME_LEN];
+	char backup_loc[255], tw_image_dir[255];
+	int copy_size;
+	int index, cur_char;
+	string Backup_Name, Backup_Loc;
+
+	DataManager::GetValue(TW_BACKUP_NAME, Backup_Name);
+	copy_size = Backup_Name.size();
+	// Check size
+	if (copy_size > MAX_BACKUP_NAME_LEN) {
+		if (Display_Error)
+			LOGERR("Backup name is too long.\n");
+		return -2;
+	}
+
+	// Check each character
+	strncpy(backup_name, Backup_Name.c_str(), copy_size);
+	if (copy_size == 1 && strncmp(backup_name, "0", 1) == 0)
+		return 0; // A "0" (zero) means to use the current timestamp for the backup name
+	for (index=0; index<copy_size; index++) {
+		cur_char = (int)backup_name[index];
+		if (cur_char == 32 || (cur_char >= 48 && cur_char <= 57) || (cur_char >= 65 && cur_char <= 91) || cur_char == 93 || cur_char == 95 || (cur_char >= 97 && cur_char <= 123) || cur_char == 125 || cur_char == 45 || cur_char == 46) {
+			// These are valid characters
+			// Numbers
+			// Upper case letters
+			// Lower case letters
+			// Space
+			// and -_.{}[]
+		} else {
+			if (Display_Error)
+				LOGERR("Backup name '%s' contains invalid character: '%c'\n", backup_name, (char)cur_char);
+			return -3;
+		}
+	}
+
+	// Check to make sure that a backup with this name doesn't already exist
+	DataManager::GetValue(TW_BACKUPS_FOLDER_VAR, Backup_Loc);
+	strcpy(backup_loc, Backup_Loc.c_str());
+	sprintf(tw_image_dir,"%s/%s", backup_loc, Backup_Name.c_str());
+	if (TWFunc::Path_Exists(tw_image_dir)) {
+		if (Display_Error)
+			LOGERR("A backup with this name already exists.\n");
+		return -4;
+	}
+	// No problems found, return 0
+	return 0;
+}
+
+bool TWPartitionManager::Make_MD5(bool generate_md5, string Backup_Folder, string Backup_Filename)
+{
+	string command;
+	string Full_File = Backup_Folder + Backup_Filename;
+	string result;
+	twrpDigest md5sum;
+
+	if (!generate_md5)
+		return true;
+
+	TWFunc::GUI_Operation_Text(TW_GENERATE_MD5_TEXT, "Generating MD5");
+	gui_print(" * Generating md5...\n");
+
+	if (TWFunc::Path_Exists(Full_File)) {
+		md5sum.setfn(Backup_Folder + Backup_Filename);
+		if (md5sum.computeMD5() == 0)
+			if (md5sum.write_md5digest() == 0)
+				gui_print(" * MD5 Created.\n");
+			else
+				return -1;
+		else
+			gui_print(" * MD5 Error!\n");
+	} else {
+		char filename[512];
+		int index = 0;
+		string strfn;
+		sprintf(filename, "%s%03i", Full_File.c_str(), index);
+		strfn = filename;
+		while (index < 1000) {
+			md5sum.setfn(filename);
+			if (TWFunc::Path_Exists(filename)) {
+				if (md5sum.computeMD5() == 0) {
+					if (md5sum.write_md5digest() != 0)
+					{
+						gui_print(" * MD5 Error.\n");
+						return false;
+					}
+				} else {
+					gui_print(" * Error computing MD5.\n");
+					return false;
+				}
+			}
+			index++;
+			sprintf(filename, "%s%03i", Full_File.c_str(), index);
+			strfn = filename;
+		}
+		if (index == 0) {
+			LOGERR("Backup file: '%s' not found!\n", filename);
+			return false;
+		}
+		gui_print(" * MD5 Created.\n");
+	}
+	return true;
+}
+
+bool TWPartitionManager::Backup_Partition(TWPartition* Part, string Backup_Folder, bool generate_md5, unsigned long long* img_bytes_remaining, unsigned long long* file_bytes_remaining, unsigned long *img_time, unsigned long *file_time, unsigned long long *img_bytes, unsigned long long *file_bytes) {
+	time_t start, stop;
+	int img_bps;
+	unsigned long long file_bps;
+	unsigned long total_time, remain_time, section_time;
+	int use_compression, backup_time;
+	float pos;
+	unsigned long long total_size, current_size;
+
+	if (Part == NULL)
+		return true;
+
+	DataManager::GetValue(TW_BACKUP_AVG_IMG_RATE, img_bps);
+
+	DataManager::GetValue(TW_USE_COMPRESSION_VAR, use_compression);
+	if (use_compression)
+		DataManager::GetValue(TW_BACKUP_AVG_FILE_COMP_RATE, file_bps);
+	else
+		DataManager::GetValue(TW_BACKUP_AVG_FILE_RATE, file_bps);
+
+	// We know the speed for both, how far into the whole backup are we, based on time
+	total_time = (*img_bytes / (unsigned long)img_bps) + (*file_bytes / (unsigned long)file_bps);
+	remain_time = (*img_bytes_remaining / (unsigned long)img_bps) + (*file_bytes_remaining / (unsigned long)file_bps);
+
+	//pos = (total_time - remain_time) / (float) total_time;
+	total_size = *file_bytes + *img_bytes;
+	current_size = *file_bytes + *img_bytes - *file_bytes_remaining - *img_bytes_remaining;
+	pos = ((float)(current_size) / (float)(total_size));
+	DataManager::SetProgress(pos);
+
+	LOGINFO("Estimated total time: %lu\nEstimated remaining time: %lu\n", total_time, remain_time);
+
+	// And get the time
+	if (Part->Backup_Method == 1)
+		section_time = Part->Backup_Size / file_bps;
+	else
+		section_time = Part->Backup_Size / img_bps;
+
+	// Set the position
+	pos = section_time / (float) total_time;
+	//DataManager::ShowProgress(pos, section_time);
+
+	TWFunc::SetPerformanceMode(true);
+	time(&start);
+
+	if (Part->Backup(Backup_Folder, &total_size, &current_size)) {
+		bool md5Success = false;
+		current_size += Part->Backup_Size;
+		pos = (float)((float)(current_size) / (float)(total_size));
+		DataManager::SetProgress(pos);
+		if (Part->Has_SubPartition) {
+			std::vector<TWPartition*>::iterator subpart;
+
+			for (subpart = Partitions.begin(); subpart != Partitions.end(); subpart++) {
+				if ((*subpart)->Can_Be_Backed_Up && (*subpart)->Is_SubPartition && (*subpart)->SubPartition_Of == Part->Mount_Point) {
+					if (!(*subpart)->Backup(Backup_Folder, &total_size, &current_size)) {
+						TWFunc::SetPerformanceMode(false);
+						return false;
+					}
+					sync();
+					sync();
+					if (!Make_MD5(generate_md5, Backup_Folder, (*subpart)->Backup_FileName)) {
+						TWFunc::SetPerformanceMode(false);
+						return false;
+					}
+					if (Part->Backup_Method == 1) {
+						*file_bytes_remaining -= (*subpart)->Backup_Size;
+					} else {
+						*img_bytes_remaining -= (*subpart)->Backup_Size;
+					}
+					current_size += Part->Backup_Size;
+					pos = (float)(current_size / total_size);
+					DataManager::SetProgress(pos);
+				}
+			}
+		}
+		time(&stop);
+		backup_time = (int) difftime(stop, start);
+		LOGINFO("Partition Backup time: %d\n", backup_time);
+		if (Part->Backup_Method == 1) {
+			*file_bytes_remaining -= Part->Backup_Size;
+			*file_time += backup_time;
+		} else {
+			*img_bytes_remaining -= Part->Backup_Size;
+			*img_time += backup_time;
+		}
+
+		md5Success = Make_MD5(generate_md5, Backup_Folder, Part->Backup_FileName);
+		TWFunc::SetPerformanceMode(false);
+		return md5Success;
+	} else {
+		TWFunc::SetPerformanceMode(false);
+		return false;
+	}
+}
+
+int TWPartitionManager::Run_Backup(void) {
+	int check, do_md5, partition_count = 0;
+	string Backup_Folder, Backup_Name, Full_Backup_Path, Backup_List, backup_path;
+	unsigned long long total_bytes = 0, file_bytes = 0, img_bytes = 0, free_space = 0, img_bytes_remaining, file_bytes_remaining, subpart_size;
+	unsigned long img_time = 0, file_time = 0;
+	TWPartition* backup_part = NULL;
+	TWPartition* storage = NULL;
+	std::vector<TWPartition*>::iterator subpart;
+	struct tm *t;
+	time_t start, stop, seconds, total_start, total_stop;
+	size_t start_pos = 0, end_pos = 0;
+	seconds = time(0);
+	t = localtime(&seconds);
+
+	time(&total_start);
+
+	Update_System_Details();
+
+	if (!Mount_Current_Storage(true))
+		return false;
+
+	DataManager::GetValue(TW_SKIP_MD5_GENERATE_VAR, do_md5);
+	if (do_md5 == 0)
+		do_md5 = true;
+	else
+		do_md5 = false;
+
+	DataManager::GetValue(TW_BACKUPS_FOLDER_VAR, Backup_Folder);
+	DataManager::GetValue(TW_BACKUP_NAME, Backup_Name);
+	if (Backup_Name == "(Current Date)") {
+		Backup_Name = TWFunc::Get_Current_Date();
+	} else if (Backup_Name == "(Auto Generate)" || Backup_Name == "0" || Backup_Name.empty()) {
+		TWFunc::Auto_Generate_Backup_Name();
+		DataManager::GetValue(TW_BACKUP_NAME, Backup_Name);
+	}
+	LOGINFO("Backup Name is: '%s'\n", Backup_Name.c_str());
+	Full_Backup_Path = Backup_Folder + "/" + Backup_Name + "/";
+	LOGINFO("Full_Backup_Path is: '%s'\n", Full_Backup_Path.c_str());
+
+	LOGINFO("Calculating backup details...\n");
+	DataManager::GetValue("tw_backup_list", Backup_List);
+	if (!Backup_List.empty()) {
+		end_pos = Backup_List.find(";", start_pos);
+		while (end_pos != string::npos && start_pos < Backup_List.size()) {
+			backup_path = Backup_List.substr(start_pos, end_pos - start_pos);
+			backup_part = Find_Partition_By_Path(backup_path);
+			if (backup_part != NULL) {
+				partition_count++;
+				if (backup_part->Backup_Method == 1)
+					file_bytes += backup_part->Backup_Size;
+				else
+					img_bytes += backup_part->Backup_Size;
+				if (backup_part->Has_SubPartition) {
+					std::vector<TWPartition*>::iterator subpart;
+
+					for (subpart = Partitions.begin(); subpart != Partitions.end(); subpart++) {
+						if ((*subpart)->Can_Be_Backed_Up && (*subpart)->Is_Present && (*subpart)->Is_SubPartition && (*subpart)->SubPartition_Of == backup_part->Mount_Point) {
+							partition_count++;
+							if ((*subpart)->Backup_Method == 1)
+								file_bytes += (*subpart)->Backup_Size;
+							else
+								img_bytes += (*subpart)->Backup_Size;
+						}
+					}
+				}
+			} else {
+				LOGERR("Unable to locate '%s' partition for backup calculations.\n", backup_path.c_str());
+			}
+			start_pos = end_pos + 1;
+			end_pos = Backup_List.find(";", start_pos);
+		}
+	}
+
+	if (partition_count == 0) {
+		gui_print("No partitions selected for backup.\n");
+		return false;
+	}
+	total_bytes = file_bytes + img_bytes;
+	gui_print(" * Total number of partitions to back up: %d\n", partition_count);
+	gui_print(" * Total size of all data: %lluMB\n", total_bytes / 1024 / 1024);
+	storage = Find_Partition_By_Path(DataManager::GetCurrentStoragePath());
+	if (storage != NULL) {
+		free_space = storage->Free;
+		gui_print(" * Available space: %lluMB\n", free_space / 1024 / 1024);
+	} else {
+		LOGERR("Unable to locate storage device.\n");
+		return false;
+	}
+	if (free_space - (32 * 1024 * 1024) < total_bytes) {
+		// We require an extra 32MB just in case
+		LOGERR("Not enough free space on storage.\n");
+		return false;
+	}
+	img_bytes_remaining = img_bytes;
+	file_bytes_remaining = file_bytes;
+
+	gui_print("\n[BACKUP STARTED]\n");
+	gui_print(" * Backup Folder: %s\n", Full_Backup_Path.c_str());
+	if (!TWFunc::Recursive_Mkdir(Full_Backup_Path)) {
+		LOGERR("Failed to make backup folder.\n");
+		return false;
+	}
+
+	DataManager::SetProgress(0.0);
+
+	start_pos = 0;
+	end_pos = Backup_List.find(";", start_pos);
+	while (end_pos != string::npos && start_pos < Backup_List.size()) {
+		backup_path = Backup_List.substr(start_pos, end_pos - start_pos);
+		backup_part = Find_Partition_By_Path(backup_path);
+		if (backup_part != NULL) {
+			if (!Backup_Partition(backup_part, Full_Backup_Path, do_md5, &img_bytes_remaining, &file_bytes_remaining, &img_time, &file_time, &img_bytes, &file_bytes))
+				return false;
+		} else {
+			LOGERR("Unable to locate '%s' partition for backup process.\n", backup_path.c_str());
+		}
+		start_pos = end_pos + 1;
+		end_pos = Backup_List.find(";", start_pos);
+	}
+
+	// Average BPS
+	if (img_time == 0)
+		img_time = 1;
+	if (file_time == 0)
+		file_time = 1;
+	int img_bps = (int)img_bytes / (int)img_time;
+	unsigned long long file_bps = file_bytes / (int)file_time;
+
+	gui_print("Average backup rate for file systems: %llu MB/sec\n", (file_bps / (1024 * 1024)));
+	gui_print("Average backup rate for imaged drives: %lu MB/sec\n", (img_bps / (1024 * 1024)));
+
+	time(&total_stop);
+	int total_time = (int) difftime(total_stop, total_start);
+	uint64_t actual_backup_size = du.Get_Folder_Size(Full_Backup_Path);
+	actual_backup_size /= (1024LLU * 1024LLU);
+
+	int prev_img_bps, use_compression;
+	unsigned long long prev_file_bps;
+	DataManager::GetValue(TW_BACKUP_AVG_IMG_RATE, prev_img_bps);
+	img_bps += (prev_img_bps * 4);
+	img_bps /= 5;
+
+	DataManager::GetValue(TW_USE_COMPRESSION_VAR, use_compression);
+	if (use_compression)
+		DataManager::GetValue(TW_BACKUP_AVG_FILE_COMP_RATE, prev_file_bps);
+	else
+		DataManager::GetValue(TW_BACKUP_AVG_FILE_RATE, prev_file_bps);
+	file_bps += (prev_file_bps * 4);
+	file_bps /= 5;
+
+	DataManager::SetValue(TW_BACKUP_AVG_IMG_RATE, img_bps);
+	if (use_compression)
+		DataManager::SetValue(TW_BACKUP_AVG_FILE_COMP_RATE, file_bps);
+	else
+		DataManager::SetValue(TW_BACKUP_AVG_FILE_RATE, file_bps);
+
+	gui_print("[%llu MB TOTAL BACKED UP]\n", actual_backup_size);
+	Update_System_Details();
+	UnMount_Main_Partitions();
+	gui_print_color("highlight", "[BACKUP COMPLETED IN %d SECONDS]\n\n", total_time); // the end
+	string backup_log = Full_Backup_Path + "recovery.log";
+	TWFunc::copy_file("/tmp/recovery.log", backup_log, 0644);
+	return true;
+}
+
+bool TWPartitionManager::Restore_Partition(TWPartition* Part, string Restore_Name, int partition_count, const unsigned long long *total_restore_size, unsigned long long *already_restored_size) {
+	time_t Start, Stop;
+	TWFunc::SetPerformanceMode(true);
+	time(&Start);
+	//DataManager::ShowProgress(1.0 / (float)partition_count, 150);
+	if (!Part->Restore(Restore_Name, total_restore_size, already_restored_size)) {
+		TWFunc::SetPerformanceMode(false);
+		return false;
+	}
+	if (Part->Has_SubPartition) {
+		std::vector<TWPartition*>::iterator subpart;
+
+		for (subpart = Partitions.begin(); subpart != Partitions.end(); subpart++) {
+			if ((*subpart)->Is_SubPartition && (*subpart)->SubPartition_Of == Part->Mount_Point) {
+				if (!(*subpart)->Restore(Restore_Name, total_restore_size, already_restored_size)) {
+					TWFunc::SetPerformanceMode(false);
+					return false;
+				}
+			}
+		}
+	}
+	time(&Stop);
+	TWFunc::SetPerformanceMode(false);
+	gui_print("[%s done (%d seconds)]\n\n", Part->Backup_Display_Name.c_str(), (int)difftime(Stop, Start));
+	return true;
+}
+
+int TWPartitionManager::Run_Restore(string Restore_Name) {
+	int check_md5, check, partition_count = 0;
+	TWPartition* restore_part = NULL;
+	time_t rStart, rStop;
+	time(&rStart);
+	string Restore_List, restore_path;
+	size_t start_pos = 0, end_pos;
+	unsigned long long total_restore_size = 0, already_restored_size = 0;
+
+	gui_print("\n[RESTORE STARTED]\n\n");
+	gui_print("Restore folder: '%s'\n", Restore_Name.c_str());
+
+	if (!Mount_Current_Storage(true))
+		return false;
+
+	DataManager::GetValue(TW_SKIP_MD5_CHECK_VAR, check_md5);
+	if (check_md5 > 0) {
+		// Check MD5 files first before restoring to ensure that all of them match before starting a restore
+		TWFunc::GUI_Operation_Text(TW_VERIFY_MD5_TEXT, "Verifying MD5");
+		gui_print("Verifying MD5...\n");
+	} else {
+		gui_print("Skipping MD5 check based on user setting.\n");
+	}
+	gui_print("Calculating restore details...\n");
+	DataManager::GetValue("tw_restore_selected", Restore_List);
+	if (!Restore_List.empty()) {
+		end_pos = Restore_List.find(";", start_pos);
+		while (end_pos != string::npos && start_pos < Restore_List.size()) {
+			restore_path = Restore_List.substr(start_pos, end_pos - start_pos);
+			restore_part = Find_Partition_By_Path(restore_path);
+			if (restore_part != NULL) {
+				partition_count++;
+				if (check_md5 > 0 && !restore_part->Check_MD5(Restore_Name))
+					return false;
+				total_restore_size += restore_part->Get_Restore_Size(Restore_Name);
+				if (restore_part->Has_SubPartition) {
+					std::vector<TWPartition*>::iterator subpart;
+
+					for (subpart = Partitions.begin(); subpart != Partitions.end(); subpart++) {
+						if ((*subpart)->Is_SubPartition && (*subpart)->SubPartition_Of == restore_part->Mount_Point) {
+							if (check_md5 > 0 && !(*subpart)->Check_MD5(Restore_Name))
+								return false;
+							total_restore_size += (*subpart)->Get_Restore_Size(Restore_Name);
+						}
+					}
+				}
+			} else {
+				LOGERR("Unable to locate '%s' partition for restoring (restore list).\n", restore_path.c_str());
+			}
+			start_pos = end_pos + 1;
+			end_pos = Restore_List.find(";", start_pos);
+		}
+	}
+
+	if (partition_count == 0) {
+		LOGERR("No partitions selected for restore.\n");
+		return false;
+	}
+
+	gui_print("Restoring %i partitions...\n", partition_count);
+	gui_print("Total restore size is %lluMB\n", total_restore_size / 1048576);
+	DataManager::SetProgress(0.0);
+
+	start_pos = 0;
+	if (!Restore_List.empty()) {
+		end_pos = Restore_List.find(";", start_pos);
+		while (end_pos != string::npos && start_pos < Restore_List.size()) {
+			restore_path = Restore_List.substr(start_pos, end_pos - start_pos);
+			restore_part = Find_Partition_By_Path(restore_path);
+			if (restore_part != NULL) {
+				partition_count++;
+				if (!Restore_Partition(restore_part, Restore_Name, partition_count, &total_restore_size, &already_restored_size))
+					return false;
+			} else {
+				LOGERR("Unable to locate '%s' partition for restoring.\n", restore_path.c_str());
+			}
+			start_pos = end_pos + 1;
+			end_pos = Restore_List.find(";", start_pos);
+		}
+	}
+	TWFunc::GUI_Operation_Text(TW_UPDATE_SYSTEM_DETAILS_TEXT, "Updating System Details");
+	Update_System_Details();
+	UnMount_Main_Partitions();
+	time(&rStop);
+	gui_print_color("highlight", "[RESTORE COMPLETED IN %d SECONDS]\n\n",(int)difftime(rStop,rStart));
+	DataManager::SetValue("tw_file_progress", "");
+	return true;
+}
+
+void TWPartitionManager::Set_Restore_Files(string Restore_Name) {
+	// Start with the default values
+	string Restore_List;
+	bool get_date = true, check_encryption = true;
+
+	DataManager::SetValue("tw_restore_encrypted", 0);
+
+	DIR* d;
+	d = opendir(Restore_Name.c_str());
+	if (d == NULL)
+	{
+		LOGERR("Error opening %s\n", Restore_Name.c_str());
+		return;
+	}
+
+	struct dirent* de;
+	while ((de = readdir(d)) != NULL)
+	{
+		// Strip off three components
+		char str[256];
+		char* label;
+		char* fstype = NULL;
+		char* extn = NULL;
+		char* ptr;
+
+		strcpy(str, de->d_name);
+		if (strlen(str) <= 2)
+			continue;
+
+		if (get_date) {
+			char file_path[255];
+			struct stat st;
+
+			strcpy(file_path, Restore_Name.c_str());
+			strcat(file_path, "/");
+			strcat(file_path, str);
+			stat(file_path, &st);
+			string backup_date = ctime((const time_t*)(&st.st_mtime));
+			DataManager::SetValue(TW_RESTORE_FILE_DATE, backup_date);
+			get_date = false;
+		}
+
+		label = str;
+		ptr = label;
+		while (*ptr && *ptr != '.')	 ptr++;
+		if (*ptr == '.')
+		{
+			*ptr = 0x00;
+			ptr++;
+			fstype = ptr;
+		}
+		while (*ptr && *ptr != '.')	 ptr++;
+		if (*ptr == '.')
+		{
+			*ptr = 0x00;
+			ptr++;
+			extn = ptr;
+		}
+
+		if (fstype == NULL || extn == NULL || strcmp(fstype, "log") == 0) continue;
+		int extnlength = strlen(extn);
+		if (extnlength != 3 && extnlength != 6) continue;
+		if (extnlength >= 3 && strncmp(extn, "win", 3) != 0) continue;
+		//if (extnlength == 6 && strncmp(extn, "win000", 6) != 0) continue;
+
+		if (check_encryption) {
+			string filename = Restore_Name + "/";
+			filename += de->d_name;
+			if (TWFunc::Get_File_Type(filename) == 2) {
+				LOGINFO("'%s' is encrypted\n", filename.c_str());
+				DataManager::SetValue("tw_restore_encrypted", 1);
+			}
+		}
+		if (extnlength == 6 && strncmp(extn, "win000", 6) != 0) continue;
+
+		TWPartition* Part = Find_Partition_By_Path(label);
+		if (Part == NULL)
+		{
+			LOGERR(" Unable to locate partition by backup name: '%s'\n", label);
+			continue;
+		}
+
+		Part->Backup_FileName = de->d_name;
+		if (strlen(extn) > 3) {
+			Part->Backup_FileName.resize(Part->Backup_FileName.size() - strlen(extn) + 3);
+		}
+
+		Restore_List += Part->Backup_Path + ";";
+	}
+	closedir(d);
+
+	// Set the final value
+	DataManager::SetValue("tw_restore_list", Restore_List);
+	DataManager::SetValue("tw_restore_selected", Restore_List);
+	return;
+}
+
+int TWPartitionManager::Wipe_By_Path(string Path) {
+	std::vector<TWPartition*>::iterator iter;
+	int ret = false;
+	bool found = false;
+	string Local_Path = TWFunc::Get_Root_Path(Path);
+
+	// Iterate through all partitions
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Mount_Point == Local_Path || (!(*iter)->Symlink_Mount_Point.empty() && (*iter)->Symlink_Mount_Point == Local_Path)) {
+			if (Path == "/and-sec")
+				ret = (*iter)->Wipe_AndSec();
+			else
+				ret = (*iter)->Wipe();
+			found = true;
+		} else if ((*iter)->Is_SubPartition && (*iter)->SubPartition_Of == Local_Path) {
+			(*iter)->Wipe();
+		}
+	}
+	if (found) {
+		return ret;
+	} else
+		LOGERR("Wipe: Unable to find partition for path '%s'\n", Local_Path.c_str());
+	return false;
+}
+
+int TWPartitionManager::Wipe_By_Path(string Path, string New_File_System) {
+	std::vector<TWPartition*>::iterator iter;
+	int ret = false;
+	bool found = false;
+	string Local_Path = TWFunc::Get_Root_Path(Path);
+
+	// Iterate through all partitions
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Mount_Point == Local_Path || (!(*iter)->Symlink_Mount_Point.empty() && (*iter)->Symlink_Mount_Point == Local_Path)) {
+			if (Path == "/and-sec")
+				ret = (*iter)->Wipe_AndSec();
+			else
+				ret = (*iter)->Wipe(New_File_System);
+			found = true;
+		} else if ((*iter)->Is_SubPartition && (*iter)->SubPartition_Of == Local_Path) {
+			(*iter)->Wipe(New_File_System);
+		}
+	}
+	if (found) {
+		return ret;
+	} else
+		LOGERR("Wipe: Unable to find partition for path '%s'\n", Local_Path.c_str());
+	return false;
+}
+
+int TWPartitionManager::Factory_Reset(void) {
+	std::vector<TWPartition*>::iterator iter;
+	int ret = true;
+
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Wipe_During_Factory_Reset && (*iter)->Is_Present) {
+			if (!(*iter)->Wipe())
+				ret = false;
+		} else if ((*iter)->Has_Android_Secure) {
+			if (!(*iter)->Wipe_AndSec())
+				ret = false;
+		}
+	}
+	return ret;
+}
+
+int TWPartitionManager::Wipe_Dalvik_Cache(void) {
+	struct stat st;
+	vector <string> dir;
+
+	if (!Mount_By_Path("/data", true))
+		return false;
+
+	if (!Mount_By_Path("/cache", true))
+		return false;
+
+	dir.push_back("/data/dalvik-cache");
+	dir.push_back("/cache/dalvik-cache");
+	dir.push_back("/cache/dc");
+	gui_print("\nWiping Dalvik Cache Directories...\n");
+	for (unsigned i = 0; i < dir.size(); ++i) {
+		if (stat(dir.at(i).c_str(), &st) == 0) {
+			TWFunc::removeDir(dir.at(i), false);
+			gui_print("Cleaned: %s...\n", dir.at(i).c_str());
+		}
+	}
+	TWPartition* sdext = Find_Partition_By_Path("/sd-ext");
+	if (sdext && sdext->Is_Present && sdext->Mount(false))
+	{
+		if (stat("/sd-ext/dalvik-cache", &st) == 0)
+		{
+			TWFunc::removeDir("/sd-ext/dalvik-cache", false);
+			gui_print("Cleaned: /sd-ext/dalvik-cache...\n");
+		}
+	}
+	gui_print("-- Dalvik Cache Directories Wipe Complete!\n\n");
+	return true;
+}
+
+int TWPartitionManager::Wipe_Rotate_Data(void) {
+	if (!Mount_By_Path("/data", true))
+		return false;
+
+	unlink("/data/misc/akmd*");
+	unlink("/data/misc/rild*");
+	gui_print("Rotation data wiped.\n");
+	return true;
+}
+
+int TWPartitionManager::Wipe_Battery_Stats(void) {
+	struct stat st;
+
+	if (!Mount_By_Path("/data", true))
+		return false;
+
+	if (0 != stat("/data/system/batterystats.bin", &st)) {
+		gui_print("No Battery Stats Found. No Need To Wipe.\n");
+	} else {
+		remove("/data/system/batterystats.bin");
+		gui_print("Cleared battery stats.\n");
+	}
+	return true;
+}
+
+int TWPartitionManager::Wipe_Android_Secure(void) {
+	std::vector<TWPartition*>::iterator iter;
+	int ret = false;
+	bool found = false;
+
+	// Iterate through all partitions
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Has_Android_Secure) {
+			ret = (*iter)->Wipe_AndSec();
+			found = true;
+		}
+	}
+	if (found) {
+		return ret;
+	} else {
+		LOGERR("No android secure partitions found.\n");
+	}
+	return false;
+}
+
+int TWPartitionManager::Format_Data(void) {
+	TWPartition* dat = Find_Partition_By_Path("/data");
+
+	if (dat != NULL) {
+		if (!dat->UnMount(true))
+			return false;
+
+		return dat->Wipe_Encryption();
+	} else {
+		LOGERR("Unable to locate /data.\n");
+		return false;
+	}
+	return false;
+}
+
+int TWPartitionManager::Wipe_Media_From_Data(void) {
+	TWPartition* dat = Find_Partition_By_Path("/data");
+
+	if (dat != NULL) {
+		if (!dat->Has_Data_Media) {
+			LOGERR("This device does not have /data/media\n");
+			return false;
+		}
+		if (!dat->Mount(true))
+			return false;
+
+		gui_print("Wiping internal storage -- /data/media...\n");
+		mtp_was_enabled = TWFunc::Toggle_MTP(false);
+		TWFunc::removeDir("/data/media", false);
+		if (mkdir("/data/media", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0) {
+			if (mtp_was_enabled) {
+				if (!Enable_MTP())
+					Disable_MTP();
+			}
+			return false;
+		}
+		if (dat->Has_Data_Media) {
+			dat->Recreate_Media_Folder();
+			// Unmount and remount - slightly hackish way to ensure that the "/sdcard" folder is still mounted properly after wiping
+			dat->UnMount(false);
+			dat->Mount(false);
+		}
+		if (mtp_was_enabled) {
+			if (!Enable_MTP())
+				Disable_MTP();
+		}
+		return true;
+	} else {
+		LOGERR("Unable to locate /data.\n");
+		return false;
+	}
+	return false;
+}
+
+int TWPartitionManager::Repair_By_Path(string Path, bool Display_Error) {
+	std::vector<TWPartition*>::iterator iter;
+	int ret = false;
+	bool found = false;
+	string Local_Path = TWFunc::Get_Root_Path(Path);
+
+	if (Local_Path == "/tmp" || Local_Path == "/")
+		return true;
+
+	// Iterate through all partitions
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Mount_Point == Local_Path || (!(*iter)->Symlink_Mount_Point.empty() && (*iter)->Symlink_Mount_Point == Local_Path)) {
+			ret = (*iter)->Repair();
+			found = true;
+		} else if ((*iter)->Is_SubPartition && (*iter)->SubPartition_Of == Local_Path) {
+			(*iter)->Repair();
+		}
+	}
+	if (found) {
+		return ret;
+	} else if (Display_Error) {
+		LOGERR("Repair: Unable to find partition for path '%s'\n", Local_Path.c_str());
+	} else {
+		LOGINFO("Repair: Unable to find partition for path '%s'\n", Local_Path.c_str());
+	}
+	return false;
+}
+
+void TWPartitionManager::Update_System_Details(void) {
+	std::vector<TWPartition*>::iterator iter;
+	int data_size = 0;
+
+	gui_print("Updating partition details...\n");
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Can_Be_Mounted) {
+			(*iter)->Update_Size(true);
+			if ((*iter)->Mount_Point == "/system") {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_SYSTEM_SIZE, backup_display_size);
+			} else if ((*iter)->Mount_Point == "/data" || (*iter)->Mount_Point == "/datadata") {
+				data_size += (int)((*iter)->Backup_Size / 1048576LLU);
+			} else if ((*iter)->Mount_Point == "/cache") {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_CACHE_SIZE, backup_display_size);
+			} else if ((*iter)->Mount_Point == "/sd-ext") {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_SDEXT_SIZE, backup_display_size);
+				if ((*iter)->Backup_Size == 0) {
+					DataManager::SetValue(TW_HAS_SDEXT_PARTITION, 0);
+					DataManager::SetValue(TW_BACKUP_SDEXT_VAR, 0);
+				} else
+					DataManager::SetValue(TW_HAS_SDEXT_PARTITION, 1);
+			} else if ((*iter)->Has_Android_Secure) {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_ANDSEC_SIZE, backup_display_size);
+				if ((*iter)->Backup_Size == 0) {
+					DataManager::SetValue(TW_HAS_ANDROID_SECURE, 0);
+					DataManager::SetValue(TW_BACKUP_ANDSEC_VAR, 0);
+				} else
+					DataManager::SetValue(TW_HAS_ANDROID_SECURE, 1);
+			} else if ((*iter)->Mount_Point == "/boot") {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_BOOT_SIZE, backup_display_size);
+				if ((*iter)->Backup_Size == 0) {
+					DataManager::SetValue("tw_has_boot_partition", 0);
+					DataManager::SetValue(TW_BACKUP_BOOT_VAR, 0);
+				} else
+					DataManager::SetValue("tw_has_boot_partition", 1);
+			}
+#ifdef SP1_NAME
+			if ((*iter)->Backup_Name == EXPAND(SP1_NAME)) {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_SP1_SIZE, backup_display_size);
+			}
+#endif
+#ifdef SP2_NAME
+			if ((*iter)->Backup_Name == EXPAND(SP2_NAME)) {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_SP2_SIZE, backup_display_size);
+			}
+#endif
+#ifdef SP3_NAME
+			if ((*iter)->Backup_Name == EXPAND(SP3_NAME)) {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_SP3_SIZE, backup_display_size);
+			}
+#endif
+		} else {
+			// Handle unmountable partitions in case we reset defaults
+			if ((*iter)->Mount_Point == "/boot") {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_BOOT_SIZE, backup_display_size);
+				if ((*iter)->Backup_Size == 0) {
+					DataManager::SetValue(TW_HAS_BOOT_PARTITION, 0);
+					DataManager::SetValue(TW_BACKUP_BOOT_VAR, 0);
+				} else
+					DataManager::SetValue(TW_HAS_BOOT_PARTITION, 1);
+			} else if ((*iter)->Mount_Point == "/recovery") {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_RECOVERY_SIZE, backup_display_size);
+				if ((*iter)->Backup_Size == 0) {
+					DataManager::SetValue(TW_HAS_RECOVERY_PARTITION, 0);
+					DataManager::SetValue(TW_BACKUP_RECOVERY_VAR, 0);
+				} else
+					DataManager::SetValue(TW_HAS_RECOVERY_PARTITION, 1);
+			} else if ((*iter)->Mount_Point == "/data") {
+				data_size += (int)((*iter)->Backup_Size / 1048576LLU);
+			}
+#ifdef SP1_NAME
+			if ((*iter)->Backup_Name == EXPAND(SP1_NAME)) {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_SP1_SIZE, backup_display_size);
+			}
+#endif
+#ifdef SP2_NAME
+			if ((*iter)->Backup_Name == EXPAND(SP2_NAME)) {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_SP2_SIZE, backup_display_size);
+			}
+#endif
+#ifdef SP3_NAME
+			if ((*iter)->Backup_Name == EXPAND(SP3_NAME)) {
+				int backup_display_size = (int)((*iter)->Backup_Size / 1048576LLU);
+				DataManager::SetValue(TW_BACKUP_SP3_SIZE, backup_display_size);
+			}
+#endif
+		}
+	}
+	gui_print("...done\n");
+	DataManager::SetValue(TW_BACKUP_DATA_SIZE, data_size);
+	string current_storage_path = DataManager::GetCurrentStoragePath();
+	TWPartition* FreeStorage = Find_Partition_By_Path(current_storage_path);
+	if (FreeStorage != NULL) {
+		// Attempt to mount storage
+		if (!FreeStorage->Mount(false)) {
+			// We couldn't mount storage... check to see if we have dual storage
+			int has_dual_storage;
+			DataManager::GetValue(TW_HAS_DUAL_STORAGE, has_dual_storage);
+			if (has_dual_storage == 1) {
+				// We have dual storage, see if we're using the internal storage that should always be present
+				if (current_storage_path == DataManager::GetSettingsStoragePath()) {
+					if (!FreeStorage->Is_Encrypted) {
+						// Not able to use internal, so error!
+						LOGERR("Unable to mount internal storage.\n");
+					}
+					DataManager::SetValue(TW_STORAGE_FREE_SIZE, 0);
+				} else {
+					// We were using external, flip to internal
+					DataManager::SetValue(TW_USE_EXTERNAL_STORAGE, 0);
+					current_storage_path = DataManager::GetCurrentStoragePath();
+					FreeStorage = Find_Partition_By_Path(current_storage_path);
+					if (FreeStorage != NULL) {
+						DataManager::SetValue(TW_STORAGE_FREE_SIZE, (int)(FreeStorage->Free / 1048576LLU));
+					} else {
+						LOGERR("Unable to locate internal storage partition.\n");
+						DataManager::SetValue(TW_STORAGE_FREE_SIZE, 0);
+					}
+				}
+			} else {
+				// No dual storage and unable to mount storage, error!
+				LOGERR("Unable to mount storage.\n");
+				DataManager::SetValue(TW_STORAGE_FREE_SIZE, 0);
+			}
+		} else {
+			DataManager::SetValue(TW_STORAGE_FREE_SIZE, (int)(FreeStorage->Free / 1048576LLU));
+		}
+	} else {
+		LOGINFO("Unable to find storage partition '%s'.\n", current_storage_path.c_str());
+	}
+	if (!Write_Fstab())
+		LOGERR("Error creating fstab\n");
+	return;
+}
+
+int TWPartitionManager::Decrypt_Device(string Password) {
+#ifdef TW_INCLUDE_CRYPTO
+	int ret_val, password_len;
+	char crypto_blkdev[255], cPassword[255];
+	size_t result;
+
+	property_set("ro.crypto.state", "encrypted");
+#ifdef TW_INCLUDE_JB_CRYPTO
+	// No extra flags needed
+#else
+	property_set("ro.crypto.fs_type", CRYPTO_FS_TYPE);
+	property_set("ro.crypto.fs_real_blkdev", CRYPTO_REAL_BLKDEV);
+	property_set("ro.crypto.fs_mnt_point", CRYPTO_MNT_POINT);
+	property_set("ro.crypto.fs_options", CRYPTO_FS_OPTIONS);
+	property_set("ro.crypto.fs_flags", CRYPTO_FS_FLAGS);
+	property_set("ro.crypto.keyfile.userdata", CRYPTO_KEY_LOC);
+
+#ifdef CRYPTO_SD_FS_TYPE
+	property_set("ro.crypto.sd_fs_type", CRYPTO_SD_FS_TYPE);
+	property_set("ro.crypto.sd_fs_real_blkdev", CRYPTO_SD_REAL_BLKDEV);
+	property_set("ro.crypto.sd_fs_mnt_point", EXPAND(TW_INTERNAL_STORAGE_PATH));
+#endif
+
+	property_set("rw.km_fips_status", "ready");
+
+#endif
+
+	// some samsung devices store "footer" on efs partition
+	TWPartition *efs = Find_Partition_By_Path("/efs");
+	if(efs && !efs->Is_Mounted())
+		efs->Mount(false);
+	else
+		efs = 0;
+#ifdef TW_EXTERNAL_STORAGE_PATH
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+	TWPartition* sdcard = Find_Partition_By_Path(EXPAND(TW_EXTERNAL_STORAGE_PATH));
+	if (sdcard && sdcard->Mount(false)) {
+		property_set("ro.crypto.external_encrypted", "1");
+		property_set("ro.crypto.external_blkdev", sdcard->Actual_Block_Device.c_str());
+	} else {
+		property_set("ro.crypto.external_encrypted", "0");
+	}
+#endif
+#endif
+
+	strcpy(cPassword, Password.c_str());
+	int pwret = cryptfs_check_passwd(cPassword);
+
+	if (pwret != 0) {
+		LOGERR("Failed to decrypt data.\n");
+		return -1;
+	}
+
+	if(efs)
+		efs->UnMount(false);
+
+	property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, "error");
+	if (strcmp(crypto_blkdev, "error") == 0) {
+		LOGERR("Error retrieving decrypted data block device.\n");
+	} else {
+		TWPartition* dat = Find_Partition_By_Path("/data");
+		if (dat != NULL) {
+			DataManager::SetValue(TW_DATA_BLK_DEVICE, dat->Primary_Block_Device);
+			DataManager::SetValue(TW_IS_DECRYPTED, 1);
+			dat->Is_Decrypted = true;
+			dat->Decrypted_Block_Device = crypto_blkdev;
+			dat->Setup_File_System(false);
+			dat->Current_File_System = dat->Fstab_File_System; // Needed if we're ignoring blkid because encrypted devices start out as emmc
+			gui_print("Data successfully decrypted, new block device: '%s'\n", crypto_blkdev);
+
+#ifdef CRYPTO_SD_FS_TYPE
+			char crypto_blkdev_sd[255];
+			property_get("ro.crypto.sd_fs_crypto_blkdev", crypto_blkdev_sd, "error");
+			if (strcmp(crypto_blkdev_sd, "error") == 0) {
+				LOGERR("Error retrieving decrypted data block device.\n");
+			} else if(TWPartition* emmc = Find_Partition_By_Path(EXPAND(TW_INTERNAL_STORAGE_PATH))){
+				emmc->Is_Decrypted = true;
+				emmc->Decrypted_Block_Device = crypto_blkdev_sd;
+				emmc->Setup_File_System(false);
+				gui_print("Internal SD successfully decrypted, new block device: '%s'\n", crypto_blkdev_sd);
+			}
+#endif //ifdef CRYPTO_SD_FS_TYPE
+#ifdef TW_EXTERNAL_STORAGE_PATH
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+			char is_external_decrypted[255];
+			property_get("ro.crypto.external_use_ecryptfs", is_external_decrypted, "0");
+			if (strcmp(is_external_decrypted, "1") == 0) {
+				sdcard->Is_Decrypted = true;
+				sdcard->EcryptFS_Password = Password;
+				sdcard->Decrypted_Block_Device = sdcard->Actual_Block_Device;
+				string MetaEcfsFile = EXPAND(TW_EXTERNAL_STORAGE_PATH);
+				MetaEcfsFile += "/.MetaEcfsFile";
+				if (!TWFunc::Path_Exists(MetaEcfsFile)) {
+					// External storage isn't actually encrypted so unmount and remount without ecryptfs
+					sdcard->UnMount(false);
+					sdcard->Mount(false);
+				}
+			} else {
+				LOGINFO("External storage '%s' is not encrypted.\n", sdcard->Mount_Point.c_str());
+				sdcard->Is_Decrypted = false;
+				sdcard->Decrypted_Block_Device = "";
+			}
+#endif
+#endif //ifdef TW_EXTERNAL_STORAGE_PATH
+
+			// Sleep for a bit so that the device will be ready
+			sleep(1);
+			if (dat->Has_Data_Media && dat->Mount(false) && TWFunc::Path_Exists("/data/media/0")) {
+				dat->Storage_Path = "/data/media/0";
+				dat->Symlink_Path = dat->Storage_Path;
+				DataManager::SetValue("tw_storage_path", "/data/media/0");
+				dat->UnMount(false);
+				Output_Partition(dat);
+			}
+			Update_System_Details();
+			UnMount_Main_Partitions();
+		} else
+			LOGERR("Unable to locate data partition.\n");
+	}
+	return 0;
+#else
+	LOGERR("No crypto support was compiled into this build.\n");
+	return -1;
+#endif
+	return 1;
+}
+
+int TWPartitionManager::Fix_Permissions(void) {
+	int result = 0;
+	if (!Mount_By_Path("/data", true))
+		return false;
+
+	if (!Mount_By_Path("/system", true))
+		return false;
+
+	Mount_By_Path("/sd-ext", false);
+
+	fixPermissions perms;
+	result = perms.fixPerms(true, false);
+	UnMount_Main_Partitions();
+	gui_print("Done.\n\n");
+	return result;
+}
+
+TWPartition* TWPartitionManager::Find_Next_Storage(string Path, string Exclude) {
+	std::vector<TWPartition*>::iterator iter = Partitions.begin();
+
+	if (!Path.empty()) {
+		string Search_Path = TWFunc::Get_Root_Path(Path);
+		for (; iter != Partitions.end(); iter++) {
+			if ((*iter)->Mount_Point == Search_Path) {
+				iter++;
+				break;
+			}
+		}
+	}
+
+	for (; iter != Partitions.end(); iter++) {
+		if ((*iter)->Is_Storage && (*iter)->Is_Present && (*iter)->Mount_Point != Exclude) {
+			return (*iter);
+		}
+	}
+
+	return NULL;
+}
+
+int TWPartitionManager::Open_Lun_File(string Partition_Path, string Lun_File) {
+	TWPartition* Part = Find_Partition_By_Path(Partition_Path);
+
+	if (Part == NULL) {
+		LOGERR("Unable to locate '%s' for USB storage mode.", Partition_Path.c_str());
+		return false;
+	}
+	LOGINFO("USB mount '%s', '%s' > '%s'\n", Partition_Path.c_str(), Part->Actual_Block_Device.c_str(), Lun_File.c_str());
+	if (!Part->UnMount(true) || !Part->Is_Present)
+		return false;
+
+	if (TWFunc::write_file(Lun_File, Part->Actual_Block_Device)) {
+		LOGERR("Unable to write to ums lunfile '%s': (%s)\n", Lun_File.c_str(), strerror(errno));
+		return false;
+	}
+	return true;
+}
+
+int TWPartitionManager::usb_storage_enable(void) {
+	int has_dual, has_data_media;
+	char lun_file[255];
+	bool has_multiple_lun = false;
+
+	property_set("sys.storage.ums_enabled", "1");
+	sleep(1);
+	DataManager::GetValue(TW_HAS_DATA_MEDIA, has_data_media);
+	string Lun_File_str = CUSTOM_LUN_FILE;
+	size_t found = Lun_File_str.find("%");
+	if (found != string::npos) {
+		sprintf(lun_file, CUSTOM_LUN_FILE, 1);
+		if (TWFunc::Path_Exists(lun_file))
+			has_multiple_lun = true;
+	}
+	mtp_was_enabled = TWFunc::Toggle_MTP(false);
+	if (!has_multiple_lun) {
+		LOGINFO("Device doesn't have multiple lun files, mount current storage\n");
+		sprintf(lun_file, CUSTOM_LUN_FILE, 0);
+		if (TWFunc::Get_Root_Path(DataManager::GetCurrentStoragePath()) == "/data") {
+			TWPartition* Mount = Find_Next_Storage("", "/data");
+			if (Mount) {
+				if (!Open_Lun_File(Mount->Mount_Point, lun_file)) {
+					goto error_handle;
+				}
+			} else {
+				LOGERR("Unable to find storage partition to mount to USB\n");
+				goto error_handle;
+			}
+		} else if (!Open_Lun_File(DataManager::GetCurrentStoragePath(), lun_file)) {
+			goto error_handle;
+		}
+	} else {
+		LOGINFO("Device has multiple lun files\n");
+		TWPartition* Mount1;
+		TWPartition* Mount2;
+		sprintf(lun_file, CUSTOM_LUN_FILE, 0);
+		Mount1 = Find_Next_Storage("", "/data");
+		if (Mount1) {
+			if (!Open_Lun_File(Mount1->Mount_Point, lun_file)) {
+				goto error_handle;
+			}
+			sprintf(lun_file, CUSTOM_LUN_FILE, 1);
+			Mount2 = Find_Next_Storage(Mount1->Mount_Point, "/data");
+			if (Mount2) {
+				Open_Lun_File(Mount2->Mount_Point, lun_file);
+			}
+		} else {
+			LOGERR("Unable to find storage partition to mount to USB\n");
+			goto error_handle;
+		}
+	}
+	return true;
+error_handle:
+	if (mtp_was_enabled)
+		if (!Enable_MTP())
+			Disable_MTP();
+	property_set("sys.storage.ums_enabled", "0");
+	return false;
+}
+
+int TWPartitionManager::usb_storage_disable(void) {
+	int index, ret;
+	char lun_file[255], ch[2] = {0, 0};
+	string str = ch;
+
+	for (index=0; index<2; index++) {
+		sprintf(lun_file, CUSTOM_LUN_FILE, index);
+		ret = TWFunc::write_file(lun_file, str);
+		if (ret < 0) {
+			break;
+		}
+	}
+	Mount_All_Storage();
+	Update_System_Details();
+	UnMount_Main_Partitions();
+	property_set("sys.storage.ums_enabled", "0");
+	if (mtp_was_enabled)
+		if (!Enable_MTP())
+			Disable_MTP();
+	if (ret < 0 && index == 0) {
+		LOGERR("Unable to write to ums lunfile '%s'.", lun_file);
+		return false;
+	} else {
+		return true;
+	}
+	return true;
+}
+
+void TWPartitionManager::Mount_All_Storage(void) {
+	std::vector<TWPartition*>::iterator iter;
+
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Is_Storage)
+			(*iter)->Mount(false);
+	}
+}
+
+void TWPartitionManager::UnMount_Main_Partitions(void) {
+	// Unmounts system and data if data is not data/media
+	// Also unmounts boot if boot is mountable
+	LOGINFO("Unmounting main partitions...\n");
+
+	TWPartition* Boot_Partition = Find_Partition_By_Path("/boot");
+
+	UnMount_By_Path("/system", true);
+	if (!datamedia)
+		UnMount_By_Path("/data", true);
+
+	if (Boot_Partition != NULL && Boot_Partition->Can_Be_Mounted)
+		Boot_Partition->UnMount(true);
+}
+
+int TWPartitionManager::Partition_SDCard(void) {
+	char mkdir_path[255], temp[255], line[512];
+	string Command, Device, fat_str, ext_str, swap_str, start_loc, end_loc, ext_format, sd_path, tmpdevice;
+	int ext, swap, total_size = 0, fat_size;
+	FILE* fp;
+
+	gui_print("Partitioning SD Card...\n");
+#ifdef TW_EXTERNAL_STORAGE_PATH
+	TWPartition* SDCard = Find_Partition_By_Path(EXPAND(TW_EXTERNAL_STORAGE_PATH));
+#else
+	TWPartition* SDCard = Find_Partition_By_Path("/sdcard");
+#endif
+	if (SDCard == NULL || !SDCard->Removable || SDCard->Has_Data_Media) {
+		LOGERR("Unable to locate device to partition.\n");
+		return false;
+	}
+	if (!SDCard->UnMount(true))
+		return false;
+	TWPartition* SDext = Find_Partition_By_Path("/sd-ext");
+	if (SDext != NULL) {
+		if (!SDext->UnMount(true))
+			return false;
+	}
+
+	TWFunc::Exec_Cmd("umount \"$SWAPPATH\"");
+	Device = SDCard->Actual_Block_Device;
+	// Just use the root block device
+	Device.resize(strlen("/dev/block/mmcblkX"));
+
+	// Find the size of the block device:
+	fp = fopen("/proc/partitions", "rt");
+	if (fp == NULL) {
+		LOGERR("Unable to open /proc/partitions\n");
+		return false;
+	}
+
+	while (fgets(line, sizeof(line), fp) != NULL)
+	{
+		unsigned long major, minor, blocks;
+		char device[512];
+		char tmpString[64];
+
+		if (strlen(line) < 7 || line[0] == 'm')	 continue;
+		sscanf(line + 1, "%lu %lu %lu %s", &major, &minor, &blocks, device);
+
+		tmpdevice = "/dev/block/";
+		tmpdevice += device;
+		if (tmpdevice == Device) {
+			// Adjust block size to byte size
+			total_size = (int)(blocks * 1024ULL / 1000000LLU);
+			break;
+		}
+	}
+	fclose(fp);
+
+	DataManager::GetValue("tw_sdext_size", ext);
+	DataManager::GetValue("tw_swap_size", swap);
+	DataManager::GetValue("tw_sdpart_file_system", ext_format);
+	fat_size = total_size - ext - swap;
+	LOGINFO("sd card block device is '%s', sdcard size is: %iMB, fat size: %iMB, ext size: %iMB, ext system: '%s', swap size: %iMB\n", Device.c_str(), total_size, fat_size, ext, ext_format.c_str(), swap);
+	memset(temp, 0, sizeof(temp));
+	sprintf(temp, "%i", fat_size);
+	fat_str = temp;
+	memset(temp, 0, sizeof(temp));
+	sprintf(temp, "%i", fat_size + ext);
+	ext_str = temp;
+	memset(temp, 0, sizeof(temp));
+	sprintf(temp, "%i", fat_size + ext + swap);
+	swap_str = temp;
+	if (ext + swap > total_size) {
+		LOGERR("EXT + Swap size is larger than sdcard size.\n");
+		return false;
+	}
+	gui_print("Removing partition table...\n");
+	Command = "parted -s " + Device + " mklabel msdos";
+	LOGINFO("Command is: '%s'\n", Command.c_str());
+	if (TWFunc::Exec_Cmd(Command) != 0) {
+		LOGERR("Unable to remove partition table.\n");
+		Update_System_Details();
+		return false;
+	}
+	gui_print("Creating FAT32 partition...\n");
+	Command = "parted " + Device + " mkpartfs primary fat32 0 " + fat_str + "MB";
+	LOGINFO("Command is: '%s'\n", Command.c_str());
+	if (TWFunc::Exec_Cmd(Command) != 0) {
+		LOGERR("Unable to create FAT32 partition.\n");
+		return false;
+	}
+	if (ext > 0) {
+		gui_print("Creating EXT partition...\n");
+		Command = "parted " + Device + " mkpartfs primary ext2 " + fat_str + "MB " + ext_str + "MB";
+		LOGINFO("Command is: '%s'\n", Command.c_str());
+		if (TWFunc::Exec_Cmd(Command) != 0) {
+			LOGERR("Unable to create EXT partition.\n");
+			Update_System_Details();
+			return false;
+		}
+	}
+	if (swap > 0) {
+		gui_print("Creating swap partition...\n");
+		Command = "parted " + Device + " mkpartfs primary linux-swap " + ext_str + "MB " + swap_str + "MB";
+		LOGINFO("Command is: '%s'\n", Command.c_str());
+		if (TWFunc::Exec_Cmd(Command) != 0) {
+			LOGERR("Unable to create swap partition.\n");
+			Update_System_Details();
+			return false;
+		}
+	}
+	// recreate TWRP folder and rewrite settings - these will be gone after sdcard is partitioned
+#ifdef TW_EXTERNAL_STORAGE_PATH
+	Mount_By_Path(EXPAND(TW_EXTERNAL_STORAGE_PATH), 1);
+	DataManager::GetValue(TW_EXTERNAL_PATH, sd_path);
+	memset(mkdir_path, 0, sizeof(mkdir_path));
+	sprintf(mkdir_path, "%s/TWRP", sd_path.c_str());
+#else
+	Mount_By_Path("/sdcard", 1);
+	strcpy(mkdir_path, "/sdcard/TWRP");
+#endif
+	mkdir(mkdir_path, 0777);
+	DataManager::Flush();
+#ifdef TW_EXTERNAL_STORAGE_PATH
+	DataManager::SetValue(TW_ZIP_EXTERNAL_VAR, EXPAND(TW_EXTERNAL_STORAGE_PATH));
+	if (DataManager::GetIntValue(TW_USE_EXTERNAL_STORAGE) == 1)
+		DataManager::SetValue(TW_ZIP_LOCATION_VAR, EXPAND(TW_EXTERNAL_STORAGE_PATH));
+#else
+	DataManager::SetValue(TW_ZIP_EXTERNAL_VAR, "/sdcard");
+	if (DataManager::GetIntValue(TW_USE_EXTERNAL_STORAGE) == 1)
+		DataManager::SetValue(TW_ZIP_LOCATION_VAR, "/sdcard");
+#endif
+	if (ext > 0) {
+		if (SDext == NULL) {
+			LOGERR("Unable to locate sd-ext partition.\n");
+			return false;
+		}
+		Command = "mke2fs -t " + ext_format + " -m 0 " + SDext->Actual_Block_Device;
+		gui_print("Formatting sd-ext as %s...\n", ext_format.c_str());
+		LOGINFO("Formatting sd-ext after partitioning, command: '%s'\n", Command.c_str());
+		TWFunc::Exec_Cmd(Command);
+	}
+
+	Update_System_Details();
+	gui_print("Partitioning complete.\n");
+	return true;
+}
+
+void TWPartitionManager::Get_Partition_List(string ListType, std::vector<PartitionList> *Partition_List) {
+	std::vector<TWPartition*>::iterator iter;
+	if (ListType == "mount") {
+		for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+			if ((*iter)->Can_Be_Mounted && !(*iter)->Is_SubPartition) {
+				struct PartitionList part;
+				part.Display_Name = (*iter)->Display_Name;
+				part.Mount_Point = (*iter)->Mount_Point;
+				part.selected = (*iter)->Is_Mounted();
+				Partition_List->push_back(part);
+			}
+		}
+	} else if (ListType == "storage") {
+		char free_space[255];
+		string Current_Storage = DataManager::GetCurrentStoragePath();
+		for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+			if ((*iter)->Is_Storage) {
+				struct PartitionList part;
+				sprintf(free_space, "%llu", (*iter)->Free / 1024 / 1024);
+				part.Display_Name = (*iter)->Storage_Name + " (";
+				part.Display_Name += free_space;
+				part.Display_Name += "MB)";
+				part.Mount_Point = (*iter)->Storage_Path;
+				if ((*iter)->Storage_Path == Current_Storage)
+					part.selected = 1;
+				else
+					part.selected = 0;
+				Partition_List->push_back(part);
+			}
+		}
+	} else if (ListType == "backup") {
+		char backup_size[255];
+		unsigned long long Backup_Size;
+		for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+			if ((*iter)->Can_Be_Backed_Up && !(*iter)->Is_SubPartition && (*iter)->Is_Present) {
+				struct PartitionList part;
+				Backup_Size = (*iter)->Backup_Size;
+				if ((*iter)->Has_SubPartition) {
+					std::vector<TWPartition*>::iterator subpart;
+
+					for (subpart = Partitions.begin(); subpart != Partitions.end(); subpart++) {
+						if ((*subpart)->Is_SubPartition && (*subpart)->Can_Be_Backed_Up && (*subpart)->Is_Present && (*subpart)->SubPartition_Of == (*iter)->Mount_Point)
+							Backup_Size += (*subpart)->Backup_Size;
+					}
+				}
+				sprintf(backup_size, "%llu", Backup_Size / 1024 / 1024);
+				part.Display_Name = (*iter)->Backup_Display_Name + " (";
+				part.Display_Name += backup_size;
+				part.Display_Name += "MB)";
+				part.Mount_Point = (*iter)->Backup_Path;
+				part.selected = 0;
+				Partition_List->push_back(part);
+			}
+		}
+	} else if (ListType == "restore") {
+		string Restore_List, restore_path;
+		TWPartition* restore_part = NULL;
+
+		DataManager::GetValue("tw_restore_list", Restore_List);
+		if (!Restore_List.empty()) {
+			size_t start_pos = 0, end_pos = Restore_List.find(";", start_pos);
+			while (end_pos != string::npos && start_pos < Restore_List.size()) {
+				restore_path = Restore_List.substr(start_pos, end_pos - start_pos);
+				if ((restore_part = Find_Partition_By_Path(restore_path)) != NULL) {
+					if ((restore_part->Backup_Name == "recovery" && !restore_part->Can_Be_Backed_Up) || restore_part->Is_SubPartition) {
+						// Don't allow restore of recovery (causes problems on some devices)
+						// Don't add subpartitions to the list of items
+					} else {
+						struct PartitionList part;
+						part.Display_Name = restore_part->Backup_Display_Name;
+						part.Mount_Point = restore_part->Backup_Path;
+						part.selected = 1;
+						Partition_List->push_back(part);
+					}
+				} else {
+					LOGERR("Unable to locate '%s' partition for restore.\n", restore_path.c_str());
+				}
+				start_pos = end_pos + 1;
+				end_pos = Restore_List.find(";", start_pos);
+			}
+		}
+	} else if (ListType == "wipe") {
+		struct PartitionList dalvik;
+		dalvik.Display_Name = "Dalvik Cache";
+		dalvik.Mount_Point = "DALVIK";
+		dalvik.selected = 0;
+		Partition_List->push_back(dalvik);
+		for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+			if ((*iter)->Wipe_Available_in_GUI && !(*iter)->Is_SubPartition) {
+				struct PartitionList part;
+				part.Display_Name = (*iter)->Display_Name;
+				part.Mount_Point = (*iter)->Mount_Point;
+				part.selected = 0;
+				Partition_List->push_back(part);
+			}
+			if ((*iter)->Has_Android_Secure) {
+				struct PartitionList part;
+				part.Display_Name = (*iter)->Backup_Display_Name;
+				part.Mount_Point = (*iter)->Backup_Path;
+				part.selected = 0;
+				Partition_List->push_back(part);
+			}
+			if ((*iter)->Has_Data_Media) {
+				struct PartitionList datamedia;
+				datamedia.Display_Name = (*iter)->Storage_Name;
+				datamedia.Mount_Point = "INTERNAL";
+				datamedia.selected = 0;
+				Partition_List->push_back(datamedia);
+			}
+		}
+	} else {
+		LOGERR("Unknown list type '%s' requested for TWPartitionManager::Get_Partition_List\n", ListType.c_str());
+	}
+}
+
+int TWPartitionManager::Fstab_Processed(void) {
+	return Partitions.size();
+}
+
+void TWPartitionManager::Output_Storage_Fstab(void) {
+	std::vector<TWPartition*>::iterator iter;
+	char storage_partition[255];
+	string Temp;
+	FILE *fp = fopen("/cache/recovery/storage.fstab", "w");
+
+	if (fp == NULL) {
+		LOGERR("Unable to open '/cache/recovery/storage.fstab'.\n");
+		return;
+	}
+
+	// Iterate through all partitions
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Is_Storage) {
+			Temp = (*iter)->Storage_Path + ";" + (*iter)->Storage_Name + ";\n";
+			strcpy(storage_partition, Temp.c_str());
+			fwrite(storage_partition, sizeof(storage_partition[0]), strlen(storage_partition) / sizeof(storage_partition[0]), fp);
+		}
+	}
+	fclose(fp);
+}
+
+TWPartition *TWPartitionManager::Get_Default_Storage_Partition()
+{
+	TWPartition *res = NULL;
+	for (std::vector<TWPartition*>::iterator iter = Partitions.begin(); iter != Partitions.end(); ++iter) {
+		if(!(*iter)->Is_Storage)
+			continue;
+
+		if((*iter)->Is_Settings_Storage)
+			return *iter;
+
+		if(!res)
+			res = *iter;
+	}
+	return res;
+}
+
+bool TWPartitionManager::Enable_MTP(void) {
+#ifdef TW_HAS_MTP
+	if (mtppid) {
+		LOGERR("MTP already enabled\n");
+		return true;
+	}
+	//Launch MTP Responder
+	LOGINFO("Starting MTP\n");
+	char vendor[PROPERTY_VALUE_MAX];
+	char product[PROPERTY_VALUE_MAX];
+	int count = 0;
+	property_set("sys.usb.config", "none");
+	property_get("usb.vendor", vendor, "18D1");
+	property_get("usb.product.mtpadb", product, "4EE2");
+	string vendorstr = vendor;
+	string productstr = product;
+	TWFunc::write_file("/sys/class/android_usb/android0/idVendor", vendorstr);
+	TWFunc::write_file("/sys/class/android_usb/android0/idProduct", productstr);
+	property_set("sys.usb.config", "mtp,adb");
+	std::vector<TWPartition*>::iterator iter;
+	/* To enable MTP debug, use the twrp command line feature to
+	 * twrp set tw_mtp_debug 1
+	 */
+	twrpMtp *mtp = new twrpMtp(DataManager::GetIntValue("tw_mtp_debug"));
+	unsigned int storageid = 1 << 16;	// upper 16 bits are for physical storage device, we pretend to have only one
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Is_Storage && (*iter)->Is_Present && (*iter)->Mount(false)) {
+			++storageid;
+			printf("twrp addStorage %s, mtpstorageid: %u\n", (*iter)->Storage_Path.c_str(), storageid);
+			mtp->addStorage((*iter)->Storage_Name, (*iter)->Storage_Path, storageid);
+			count++;
+		}
+	}
+	if (count) {
+		mtppid = mtp->forkserver();
+		if (mtppid) {
+			DataManager::SetValue("tw_mtp_enabled", 1);
+			return true;
+		} else {
+			LOGERR("Failed to enable MTP\n");
+			return false;
+		}
+	}
+	LOGERR("No valid storage partitions found for MTP.\n");
+#else
+	LOGERR("MTP support not included\n");
+#endif
+	DataManager::SetValue("tw_mtp_enabled", 0);
+	return false;
+}
+
+bool TWPartitionManager::Disable_MTP(void) {
+#ifdef TW_HAS_MTP
+	char vendor[PROPERTY_VALUE_MAX];
+	char product[PROPERTY_VALUE_MAX];
+	property_set("sys.usb.config", "none");
+	property_get("usb.vendor", vendor, "18D1");
+	property_get("usb.product.adb", product, "D002");
+	string vendorstr = vendor;
+	string productstr = product;
+	TWFunc::write_file("/sys/class/android_usb/android0/idVendor", vendorstr);
+	TWFunc::write_file("/sys/class/android_usb/android0/idProduct", productstr);
+	if (mtppid) {
+		LOGINFO("Disabling MTP\n");
+		int status;
+		kill(mtppid, SIGKILL);
+		mtppid = 0;
+		// We don't care about the exit value, but this prevents a zombie process
+		waitpid(mtppid, &status, 0);
+	}
+	property_set("sys.usb.config", "adb");
+	DataManager::SetValue("tw_mtp_enabled", 0);
+	return true;
+#else
+	LOGERR("MTP support not included\n");
+	DataManager::SetValue("tw_mtp_enabled", 0);
+	return false;
+#endif
+}
diff --git a/partitions.hpp b/partitions.hpp
new file mode 100644
index 0000000..3f5aadd
--- /dev/null
+++ b/partitions.hpp
@@ -0,0 +1,236 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __TWRP_Partition_Manager
+#define __TWRP_Partition_Manager
+
+#include <vector>
+#include <string>
+#include "twrpDU.hpp"
+
+#define MAX_FSTAB_LINE_LENGTH 2048
+
+using namespace std;
+
+struct PartitionList {
+	std::string Display_Name;
+	std::string Mount_Point;
+	unsigned int selected;
+};
+
+// Partition class
+class TWPartition
+{
+public:
+	enum Backup_Method_enum {
+		NONE = 0,
+		FILES = 1,
+		DD = 2,
+		FLASH_UTILS = 3,
+	};
+
+public:
+	TWPartition();
+	virtual ~TWPartition();
+
+public:
+	bool Is_Mounted();                                                        // Checks mount to see if the partition is currently mounted
+	bool Mount(bool Display_Error);                                           // Mounts the partition if it is not mounted
+	bool UnMount(bool Display_Error);                                         // Unmounts the partition if it is mounted
+	bool Wipe(string New_File_System);                                        // Wipes the partition
+	bool Wipe();                                                              // Wipes the partition
+	bool Wipe_AndSec();                                                       // Wipes android secure
+	bool Can_Repair();                                                        // Checks to see if we have everything needed to be able to repair the current file system
+	bool Repair();                                                            // Repairs the current file system
+	bool Backup(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size); // Backs up the partition to the folder specified
+	bool Check_MD5(string restore_folder);                                    // Checks MD5 of a backup
+	bool Restore(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size); // Restores the partition using the backup folder provided
+	unsigned long long Get_Restore_Size(string restore_folder);               // Returns the overall restore size of the backup
+	string Backup_Method_By_Name();                                           // Returns a string of the backup method for human readable output
+	bool Decrypt(string Password);                                            // Decrypts the partition, return 0 for failure and -1 for success
+	bool Wipe_Encryption();                                                   // Ignores wipe commands for /data/media devices and formats the original block device
+	void Check_FS_Type();                                                     // Checks the fs type using blkid, does not do anything on MTD / yaffs2 because this crashes on some devices
+	bool Update_Size(bool Display_Error);                                     // Updates size information
+	void Recreate_Media_Folder();                                             // Recreates the /data/media folder
+
+public:
+	string Current_File_System;                                               // Current file system
+	string Actual_Block_Device;                                               // Actual block device (one of primary, alternate, or decrypted)
+	string MTD_Name;                                                          // Name of the partition for MTD devices
+	bool Is_Present;                                                          // Indicates if the partition is currently present as a block device
+
+protected:
+	bool Has_Data_Media;                                                      // Indicates presence of /data/media, may affect wiping and backup methods
+	void Setup_Data_Media();                                                  // Sets up a partition as a /data/media emulated storage partition
+
+private:
+	bool Process_Fstab_Line(string Line, bool Display_Error);                 // Processes a fstab line
+	void Find_Actual_Block_Device();                                          // Determines the correct block device and stores it in Actual_Block_Device
+
+	bool Process_Flags(string Flags, bool Display_Error);                     // Process custom fstab flags
+	bool Process_FS_Flags(string& Options, int Flags);                        // Process standard fstab fs flags
+	bool Is_File_System(string File_System);                                  // Checks to see if the file system given is considered a file system
+	bool Is_Image(string File_System);                                        // Checks to see if the file system given is considered an image
+	void Setup_File_System(bool Display_Error);                               // Sets defaults for a file system partition
+	void Setup_Image(bool Display_Error);                                     // Sets defaults for an image partition
+	void Setup_AndSec(void);                                                  // Sets up .android_secure settings
+	void Find_Real_Block_Device(string& Block_Device, bool Display_Error);    // Checks the block device given and follows symlinks until it gets to the real block device
+	bool Find_Partition_Size();                                               // Finds the partition size from /proc/partitions
+	unsigned long long Get_Size_Via_du(string Path, bool Display_Error);      // Uses du to get sizes
+	bool Wipe_EXT23(string File_System);                                      // Formats as ext3 or ext2
+	bool Wipe_EXT4();                                                         // Formats using ext4, uses make_ext4fs when present
+	bool Wipe_FAT();                                                          // Formats as FAT if mkdosfs exits otherwise rm -rf wipe
+	bool Wipe_EXFAT();                                                        // Formats as EXFAT
+	bool Wipe_MTD();                                                          // Formats as yaffs2 for MTD memory types
+	bool Wipe_RMRF();                                                         // Uses rm -rf to wipe
+	bool Wipe_F2FS();                                                         // Uses mkfs.f2fs to wipe
+	bool Wipe_Data_Without_Wiping_Media();                                    // Uses rm -rf to wipe but does not wipe /data/media
+	bool Backup_Tar(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size); // Backs up using tar for file systems
+	bool Backup_DD(string backup_folder);                                     // Backs up using dd for emmc memory types
+	bool Backup_Dump_Image(string backup_folder);                             // Backs up using dump_image for MTD memory types
+	string Get_Restore_File_System(string restore_folder);                    // Returns the file system that was in place at the time of the backup
+	bool Restore_Tar(string restore_folder, string Restore_File_System, const unsigned long long *total_restore_size, unsigned long long *already_restored_size); // Restore using tar for file systems
+	bool Restore_DD(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size); // Restore using dd for emmc memory types
+	bool Restore_Flash_Image(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size); // Restore using flash_image for MTD memory types
+	bool Get_Size_Via_statfs(bool Display_Error);                             // Get Partition size, used, and free space using statfs
+	bool Get_Size_Via_df(bool Display_Error);                                 // Get Partition size, used, and free space using df command
+	bool Make_Dir(string Path, bool Display_Error);                           // Creates a directory if it doesn't already exist
+	bool Find_MTD_Block_Device(string MTD_Name);                              // Finds the mtd block device based on the name from the fstab
+	void Recreate_AndSec_Folder(void);                                        // Recreates the .android_secure folder
+	void Mount_Storage_Retry(void);                                           // Tries multiple times with a half second delay to mount a device in case storage is slow to mount
+
+private:
+	bool Can_Be_Mounted;                                                      // Indicates that the partition can be mounted
+	bool Can_Be_Wiped;                                                        // Indicates that the partition can be wiped
+	bool Can_Be_Backed_Up;                                                    // Indicates that the partition will show up in the backup list
+	bool Use_Rm_Rf;                                                           // Indicates that the partition will always be formatted w/ "rm -rf *"
+	bool Wipe_During_Factory_Reset;                                           // Indicates that this partition is wiped during a factory reset
+	bool Wipe_Available_in_GUI;                                               // Inidcates that the wipe can be user initiated in the GUI system
+	bool Is_SubPartition;                                                     // Indicates that this partition is a sub-partition of another partition (e.g. datadata is a sub-partition of data)
+	bool Has_SubPartition;                                                    // Indicates that this partition has a sub-partition
+	string SubPartition_Of;                                                   // Indicates which partition is the parent partition of this partition (e.g. /data is the parent partition of /datadata)
+	string Symlink_Path;                                                      // Symlink path (e.g. /data/media)
+	string Symlink_Mount_Point;                                               // /sdcard could be the symlink mount point for /data/media
+	string Mount_Point;                                                       // Mount point for this partition (e.g. /system or /data)
+	string Backup_Path;                                                       // Path for backup
+	string Primary_Block_Device;                                              // Block device (e.g. /dev/block/mmcblk1p1)
+	string Alternate_Block_Device;                                            // Alternate block device (e.g. /dev/block/mmcblk1)
+	string Decrypted_Block_Device;                                            // Decrypted block device available after decryption
+	bool Removable;                                                           // Indicates if this partition is removable -- affects how often we check overall size, if present, etc.
+	int Length;                                                               // Used by make_ext4fs to leave free space at the end of the partition block for things like a crypto footer
+	unsigned long long Size;                                                  // Overall size of the partition
+	unsigned long long Used;                                                  // Overall used space
+	unsigned long long Free;                                                  // Overall free space
+	unsigned long long Backup_Size;                                           // Backup size -- may be different than used space especially when /data/media is present
+	unsigned long long Restore_Size;                                          // Restore size of the current restore operation
+	bool Can_Be_Encrypted;                                                    // This partition might be encrypted, affects error handling, can only be true if crypto support is compiled in
+	bool Is_Encrypted;                                                        // This partition is thought to be encrypted -- it wouldn't mount for some reason, only avialble with crypto support
+	bool Is_Decrypted;                                                        // This partition has successfully been decrypted
+	string Display_Name;                                                      // Display name for the GUI
+	string Backup_Name;                                                       // Backup name -- used for backup filenames
+	string Backup_Display_Name;                                               // Name displayed in the partition list for backup selection
+	string Storage_Name;                                                      // Name displayed in the partition list for storage selection
+	string Backup_FileName;                                                   // Actual backup filename
+	Backup_Method_enum Backup_Method;                                         // Method used for backup
+	bool Can_Encrypt_Backup;                                                  // Indicates if this item can be encrypted during backup
+	bool Use_Userdata_Encryption;                                             // Indicates if we will use userdata encryption splitting on an encrypted backup
+	bool Has_Android_Secure;                                                  // Indicates the presence of .android_secure on this partition
+	bool Is_Storage;                                                          // Indicates if this partition is used for storage for backup, restore, and installing zips
+	bool Is_Settings_Storage;                                                 // Indicates that this storage partition is the location of the .twrps settings file and the location that is used for custom themes
+	string Storage_Path;                                                      // Indicates the path to the storage -- root indicates mount point, media/ indicates e.g. /data/media
+	string Fstab_File_System;                                                 // File system from the recovery.fstab
+	int Mount_Flags;                                                          // File system flags from recovery.fstab
+	string Mount_Options;                                                     // File system options from recovery.fstab
+	int Format_Block_Size;                                                    // Block size for formatting
+	bool Ignore_Blkid;                                                        // Ignore blkid results due to superblocks lying to us on certain devices / partitions
+	bool Retain_Layout_Version;                                               // Retains the .layout_version file during a wipe (needed on devices like Sony Xperia T where /data and /data/media are separate partitions)
+#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
+	string EcryptFS_Password;                                                 // Have to store the encryption password to remount
+#endif
+
+friend class TWPartitionManager;
+friend class DataManager;
+friend class GUIPartitionList;
+friend class GUIAction;
+};
+
+class TWPartitionManager
+{
+public:
+	TWPartitionManager();													  // Constructor for TWRPartionManager
+	~TWPartitionManager() {}
+
+public:
+	int Process_Fstab(string Fstab_Filename, bool Display_Error);             // Parses the fstab and populates the partitions
+	int Write_Fstab();                                                        // Creates /etc/fstab file that's used by the command line for mount commands
+	void Output_Partition_Logging();                                          // Outputs partition information to the log
+	int Mount_By_Path(string Path, bool Display_Error);                       // Mounts partition based on path (e.g. /system)
+	int UnMount_By_Path(string Path, bool Display_Error);                     // Unmounts partition based on path
+	int Is_Mounted_By_Path(string Path);                                      // Checks if partition is mounted based on path
+	int Mount_Current_Storage(bool Display_Error);                            // Mounts the current storage location
+	int Mount_Settings_Storage(bool Display_Error);                           // Mounts the settings file storage location (usually internal)
+	TWPartition* Find_Partition_By_Path(string Path);                         // Returns a pointer to a partition based on path
+	int Check_Backup_Name(bool Display_Error);                                // Checks the current backup name to ensure that it is valid
+	int Run_Backup();                                                         // Initiates a backup in the current storage
+	int Run_Restore(string Restore_Name);                                     // Restores a backup
+	void Set_Restore_Files(string Restore_Name);                              // Used to gather a list of available backup partitions for the user to select for a restore
+	int Wipe_By_Path(string Path);                                            // Wipes a partition based on path
+	int Wipe_By_Path(string Path, string New_File_System);                    // Wipes a partition based on path
+	int Factory_Reset();                                                      // Performs a factory reset
+	int Wipe_Dalvik_Cache();                                                  // Wipes dalvik cache
+	int Wipe_Rotate_Data();                                                   // Wipes rotation data --
+	int Wipe_Battery_Stats();                                                 // Wipe battery stats -- /data/system/batterystats.bin
+	int Wipe_Android_Secure();                                                // Wipes android secure
+	int Format_Data();                                                        // Really formats data on /data/media devices -- also removes encryption
+	int Wipe_Media_From_Data();                                               // Removes and recreates the media folder on /data/media devices
+	int Repair_By_Path(string Path, bool Display_Error);                      // Repairs a partition based on path
+	void Update_System_Details();                                             // Updates fstab, file systems, sizes, etc.
+	int Decrypt_Device(string Password);                                      // Attempt to decrypt any encrypted partitions
+	int usb_storage_enable(void);                                             // Enable USB storage mode
+	int usb_storage_disable(void);                                            // Disable USB storage mode
+	void Mount_All_Storage(void);                                             // Mounts all storage locations
+	void UnMount_Main_Partitions(void);                                       // Unmounts system and data if not data/media and boot if boot is mountable
+	int Partition_SDCard(void);                                               // Repartitions the sdcard
+	TWPartition *Get_Default_Storage_Partition();                             // Returns a pointer to a default storage partition
+
+	int Fix_Permissions();
+	void Get_Partition_List(string ListType, std::vector<PartitionList> *Partition_List);
+	int Fstab_Processed();                                                    // Indicates if the fstab has been processed or not
+	void Output_Storage_Fstab();                                              // Creates a /cache/recovery/storage.fstab file with a list of all potential storage locations for app use
+	bool Enable_MTP();                                                        // Enables MTP
+	bool Disable_MTP();                                                       // Disables MTP
+
+private:
+	void Setup_Settings_Storage_Partition(TWPartition* Part);                 // Sets up settings storage
+	void Setup_Android_Secure_Location(TWPartition* Part);                    // Sets up .android_secure if needed
+	bool Make_MD5(bool generate_md5, string Backup_Folder, string Backup_Filename); // Generates an MD5 after a backup is made
+	bool Backup_Partition(TWPartition* Part, string Backup_Folder, bool generate_md5, unsigned long long* img_bytes_remaining, unsigned long long* file_bytes_remaining, unsigned long *img_time, unsigned long *file_time, unsigned long long *img_bytes, unsigned long long *file_bytes);
+	bool Restore_Partition(TWPartition* Part, string Restore_Name, int partition_count, const unsigned long long *total_restore_size, unsigned long long *already_restored_size);
+	void Output_Partition(TWPartition* Part);
+	TWPartition* Find_Next_Storage(string Path, string Exclude);
+	int Open_Lun_File(string Partition_Path, string Lun_File);
+	pid_t mtppid;
+	bool mtp_was_enabled;
+
+private:
+	std::vector<TWPartition*> Partitions;                                     // Vector list of all partitions
+};
+
+extern TWPartitionManager PartitionManager;
+
+#endif // __TWRP_Partition_Manager
diff --git a/pigz/Android.mk b/pigz/Android.mk
new file mode 100644
index 0000000..9b9313d
--- /dev/null
+++ b/pigz/Android.mk
@@ -0,0 +1,31 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := pigz
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS :=
+LOCAL_SRC_FILES = pigz.c yarn.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+					external/zlib
+LOCAL_SHARED_LIBRARIES += libz libc
+
+include $(BUILD_EXECUTABLE)
+
+PIGZ_TOOLS := unpigz
+SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(PIGZ_TOOLS))
+$(SYMLINKS): PIGZ_BINARY := $(LOCAL_MODULE)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE)
+	@echo "Symlink: $@ -> $(PIGZ_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(PIGZ_BINARY) $@
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := unpigz_symlink
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(SYMLINKS)
+include $(BUILD_PHONY_PACKAGE)
+SYMLINKS :=
diff --git a/pigz/Makefile b/pigz/Makefile
new file mode 100644
index 0000000..822902c
--- /dev/null
+++ b/pigz/Makefile
@@ -0,0 +1,58 @@
+CC=cc
+CFLAGS=-O3 -Wall -Wextra
+
+pigz: pigz.o yarn.o
+	$(CC) -o pigz pigz.o yarn.o -lpthread -lz
+	ln -f pigz unpigz
+
+pigz.o: pigz.c yarn.h
+
+yarn.o: yarn.c yarn.h
+
+dev: pigz pigzt pigzn
+
+pigzt: pigzt.o yarnt.o
+	$(CC) -o pigzt pigzt.o yarnt.o -lpthread -lz
+
+pigzt.o: pigz.c yarn.h
+	$(CC) -Wall -O3 -DDEBUG -g -c -o pigzt.o pigz.c
+
+yarnt.o: yarn.c yarn.h
+	$(CC) -Wall -O3 -DDEBUG -g -c -o yarnt.o yarn.c
+
+pigzn: pigzn.o
+	$(CC) -o pigzn pigzn.o -lz
+
+pigzn.o: pigz.c
+	$(CC) -Wall -O3 -DDEBUG -DNOTHREAD -g -c -o pigzn.o pigz.c
+
+test: pigz
+	./pigz -kf pigz.c ; ./pigz -t pigz.c.gz
+	./pigz -kfb 32 pigz.c ; ./pigz -t pigz.c.gz
+	./pigz -kfp 1 pigz.c ; ./pigz -t pigz.c.gz
+	./pigz -kfz pigz.c ; ./pigz -t pigz.c.zz
+	./pigz -kfK pigz.c ; ./pigz -t pigz.c.zip
+	printf "" | ./pigz -cdf | wc -c | test `cat` -eq 0
+	printf "x" | ./pigz -cdf | wc -c | test `cat` -eq 1
+	printf "xy" | ./pigz -cdf | wc -c | test `cat` -eq 2
+	printf "xyz" | ./pigz -cdf | wc -c | test `cat` -eq 3
+	(printf "w" | gzip ; printf "x") | ./pigz -cdf | wc -c | test `cat` -eq 2
+	(printf "w" | gzip ; printf "xy") | ./pigz -cdf | wc -c | test `cat` -eq 3
+	(printf "w" | gzip ; printf "xyz") | ./pigz -cdf | wc -c | test `cat` -eq 4
+	-@if test "`whereis compress | grep /`" != ""; then \
+	  echo 'compress -f < pigz.c | ./unpigz | cmp - pigz.c' ;\
+	  compress -f < pigz.c | ./unpigz | cmp - pigz.c ;\
+	fi
+	@rm -f pigz.c.gz pigz.c.zz pigz.c.zip
+
+tests: dev test
+	./pigzn -kf pigz.c ; ./pigz -t pigz.c.gz
+	@rm -f pigz.c.gz
+
+docs: pigz.pdf
+
+pigz.pdf: pigz.1
+	groff -mandoc -f H -T ps pigz.1 | ps2pdf - pigz.pdf
+
+clean:
+	@rm -f *.o pigz unpigz pigzn pigzt pigz.c.gz pigz.c.zz pigz.c.zip
diff --git a/pigz/README b/pigz/README
new file mode 100644
index 0000000..bf29102
--- /dev/null
+++ b/pigz/README
@@ -0,0 +1,48 @@
+pigz 2.2.5 (xx Apr 2012) by Mark Adler
+
+pigz, which stands for Parallel Implementation of GZip, is a fully functional
+replacement for gzip that exploits multiple processors and multiple cores to
+the hilt when compressing data.
+
+pigz was written by Mark Adler, and uses the zlib and pthread libraries.
+
+This version of pigz is written to be portable across Unix-style operating
+systems that provide the zlib and pthread libraries.
+
+Type "make" in this directory to build the "pigz" executable.  You can then
+install the executable wherever you like in your path (e.g. /usr/local/bin/).
+Type "pigz" to see the command help and all of the command options.
+
+The latest version of pigz can be found at http://zlib.net/pigz/ .  You need
+zlib version 1.2.3 or later to compile pigz.  You can find the latest version
+of zlib at http://zlib.net/ .  You can look in pigz.c for the change history.
+
+Questions, comments, bug reports, fixes, etc. can be emailed to Mark at his
+address in the license below.
+
+The license from pigz.c is copied here:
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the author be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Mark Adler
+  madler@alumni.caltech.edu
+
+  Mark accepts donations for providing this software.  Donations are not
+  required or expected.  Any amount that you feel is appropriate would be
+  appreciated.  You can use this link:
+
+  https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=536055
diff --git a/pigz/pigz.1 b/pigz/pigz.1
new file mode 100644
index 0000000..8d75ca2
--- /dev/null
+++ b/pigz/pigz.1
@@ -0,0 +1,193 @@
+.TH PIGZ 1 local
+.SH NAME
+pigz, unpigz \- compress or expand files
+.SH SYNOPSIS
+.ll +8
+.B pigz
+.RB [ " \-cdfhikKlLnNqrRtTz0..9 " ]
+[
+.B -b
+.I blocksize
+]
+[
+.B -p
+.I threads
+]
+[
+.B -S
+.I suffix
+]
+[
+.I "name \&..."
+]
+.ll -8
+.br
+.B unpigz
+.RB [ " \-cfhikKlLnNqrRtTz " ]
+[
+.B -b
+.I blocksize
+]
+[
+.B -p
+.I threads
+]
+[
+.B -S
+.I suffix
+]
+[
+.I "name \&..."
+]
+.SH DESCRIPTION
+.I Pigz
+compresses using threads to make use of multiple processors and cores.
+The input is broken up into 128 KB chunks with each compressed in parallel.
+The individual check value for each chunk is also calculated in parallel.
+The compressed data is written in order to the output, and a combined check
+value is calculated from the individual check values.
+.PP
+The compressed data format generated is in the gzip, zlib, or single-entry
+zip format using the deflate compression method.  The compression produces
+partial raw deflate streams which are concatenated by a single write thread
+and wrapped with the appropriate header and trailer, where the trailer
+contains the combined check value.
+.PP
+Each partial raw deflate stream is terminated by an empty stored block
+(using the Z_SYNC_FLUSH option of zlib), in order to end that partial bit
+stream at a byte boundary.  That allows the partial streams to be
+concatenated simply as sequences of bytes.  This adds a very small four to
+five byte overhead to the output for each input chunk.
+.PP
+The default input block size is 128K, but can be changed with the
+.B -b
+option.  The number of compress threads is set by default to the number
+of online processors,
+which can be changed using the
+.B -p
+option.  Specifying
+.B -p 1
+avoids the use of threads entirely.
+.PP
+The input blocks, while compressed independently, have the last 32K of the
+previous block loaded as a preset dictionary to preserve the compression
+effectiveness of deflating in a single thread.  This can be turned off using
+the
+.B -i
+or
+.B --independent
+option, so that the blocks can be decompressed
+independently for partial error recovery or for random access.
+.PP
+Decompression can't be parallelized, at least not without specially prepared
+deflate streams for that purpose.  As a result,
+.I pigz
+uses a single thread
+(the main thread) for decompression, but will create three other threads for
+reading, writing, and check calculation, which can speed up decompression
+under some circumstances.  Parallel decompression can be turned off by
+specifying one process
+(
+.B -dp 1
+or
+.B -tp 1
+).
+.PP
+Compressed files can be restored to their original form using
+.I pigz -d
+or
+.I unpigz.
+
+.SH OPTIONS
+.TP
+.B -# --fast --best
+Regulate the speed of compression using the specified digit
+.IR # ,
+where
+.B \-1
+or
+.B \-\-fast
+indicates the fastest compression method (less compression)
+and
+.B \-9
+or
+.B \-\-best
+indicates the slowest compression method (best compression).
+Level 0 is no compression.
+.TP
+.B -b --blocksize mmm
+Set compression block size to mmmK (default 128KiB).
+.TP
+.B -c --stdout --to-stdout
+Write all processed output to stdout (won't delete).
+.TP
+.B -d --decompress --uncompress
+Decompress the compressed input.
+.TP
+.B -f --force
+Force overwrite, compress .gz, links, and to terminal.
+.TP
+.B -h --help
+Display a help screen and quit.
+.TP
+.B -i --independent
+Compress blocks independently for damage recovery.
+.TP
+.B -k --keep
+Do not delete original file after processing.
+.TP
+.B -K --zip
+Compress to PKWare zip (.zip) single entry format.
+.TP
+.B -l --list
+List the contents of the compressed input.
+.TP
+.B -L --license
+Display the
+.I pigz
+license and quit.
+.TP
+.B -n --no-name
+Do not store or restore file name in/from header.
+.TP
+.B -N --name
+Store/restore file name and mod time in/from header.
+.TP
+.B -p --processes n
+Allow up to n processes (default is the number of online processors)
+.TP
+.B -q --quiet --silent
+Print no messages, even on error.
+.TP
+.B -r --recursive
+Process the contents of all subdirectories.
+.TP
+.B -R --rsyncable
+Input-determined block locations for rsync.
+.TP
+.B -S --suffix .sss
+Use suffix .sss instead of .gz (for compression).
+.TP
+.B -t --test
+Test the integrity of the compressed input.
+.TP
+.B -T --no-time
+Do not store or restore mod time in/from header.
+.TP
+.B -v --verbose
+Provide more verbose output.
+.TP
+.B -V --version
+Show the version of pigz.
+.TP
+.B -z --zlib
+Compress to zlib (.zz) instead of gzip format.
+.TP
+.B --
+All arguments after "--" are treated as file names (for names that start with "-")
+.SH "COPYRIGHT NOTICE"
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the author be held liable for any damages
+arising from the use of this software.
+.PP
+Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Mark Adler <madler@alumni.caltech.edu>
diff --git a/pigz/pigz.c b/pigz/pigz.c
new file mode 100644
index 0000000..5416bc9
--- /dev/null
+++ b/pigz/pigz.c
@@ -0,0 +1,3682 @@
+/* pigz.c -- parallel implementation of gzip
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Mark Adler
+ * Version 2.2.5  28 Jul 2012  Mark Adler
+ */
+
+/*
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the author be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Mark Adler
+  madler@alumni.caltech.edu
+
+  Mark accepts donations for providing this software.  Donations are not
+  required or expected.  Any amount that you feel is appropriate would be
+  appreciated.  You can use this link:
+
+  https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=536055
+
+ */
+
+/* Version history:
+   1.0    17 Jan 2007  First version, pipe only
+   1.1    28 Jan 2007  Avoid void * arithmetic (some compilers don't get that)
+                       Add note about requiring zlib 1.2.3
+                       Allow compression level 0 (no compression)
+                       Completely rewrite parallelism -- add a write thread
+                       Use deflateSetDictionary() to make use of history
+                       Tune argument defaults to best performance on four cores
+   1.2.1   1 Feb 2007  Add long command line options, add all gzip options
+                       Add debugging options
+   1.2.2  19 Feb 2007  Add list (--list) function
+                       Process file names on command line, write .gz output
+                       Write name and time in gzip header, set output file time
+                       Implement all command line options except --recursive
+                       Add --keep option to prevent deleting input files
+                       Add thread tracing information with -vv used
+                       Copy crc32_combine() from zlib (shared libraries issue)
+   1.3    25 Feb 2007  Implement --recursive
+                       Expand help to show all options
+                       Show help if no arguments or output piping are provided
+                       Process options in GZIP environment variable
+                       Add progress indicator to write thread if --verbose
+   1.4     4 Mar 2007  Add --independent to facilitate damaged file recovery
+                       Reallocate jobs for new --blocksize or --processes
+                       Do not delete original if writing to stdout
+                       Allow --processes 1, which does no threading
+                       Add NOTHREAD define to compile without threads
+                       Incorporate license text from zlib in source code
+   1.5    25 Mar 2007  Reinitialize jobs for new compression level
+                       Copy attributes and owner from input file to output file
+                       Add decompression and testing
+                       Add -lt (or -ltv) to show all entries and proper lengths
+                       Add decompression, testing, listing of LZW (.Z) files
+                       Only generate and show trace log if DEBUG defined
+                       Take "-" argument to mean read file from stdin
+   1.6    30 Mar 2007  Add zlib stream compression (--zlib), and decompression
+   1.7    29 Apr 2007  Decompress first entry of a zip file (if deflated)
+                       Avoid empty deflate blocks at end of deflate stream
+                       Show zlib check value (Adler-32) when listing
+                       Don't complain when decompressing empty file
+                       Warn about trailing junk for gzip and zlib streams
+                       Make listings consistent, ignore gzip extra flags
+                       Add zip stream compression (--zip)
+   1.8    13 May 2007  Document --zip option in help output
+   2.0    19 Oct 2008  Complete rewrite of thread usage and synchronization
+                       Use polling threads and a pool of memory buffers
+                       Remove direct pthread library use, hide in yarn.c
+   2.0.1  20 Oct 2008  Check version of zlib at compile time, need >= 1.2.3
+   2.1    24 Oct 2008  Decompress with read, write, inflate, and check threads
+                       Remove spurious use of ctime_r(), ctime() more portable
+                       Change application of job->calc lock to be a semaphore
+                       Detect size of off_t at run time to select %lu vs. %llu
+                       #define large file support macro even if not __linux__
+                       Remove _LARGEFILE64_SOURCE, _FILE_OFFSET_BITS is enough
+                       Detect file-too-large error and report, blame build
+                       Replace check combination routines with those from zlib
+   2.1.1  28 Oct 2008  Fix a leak for files with an integer number of blocks
+                       Update for yarn 1.1 (yarn_prefix and yarn_abort)
+   2.1.2  30 Oct 2008  Work around use of beta zlib in production systems
+   2.1.3   8 Nov 2008  Don't use zlib combination routines, put back in pigz
+   2.1.4   9 Nov 2008  Fix bug when decompressing very short files
+   2.1.5  20 Jul 2009  Added 2008, 2009 to --license statement
+                       Allow numeric parameter immediately after -p or -b
+                       Enforce parameter after -p, -b, -s, before other options
+                       Enforce numeric parameters to have only numeric digits
+                       Try to determine the number of processors for -p default
+                       Fix --suffix short option to be -S to match gzip [Bloch]
+                       Decompress if executable named "unpigz" [Amundsen]
+                       Add a little bit of testing to Makefile
+   2.1.6  17 Jan 2010  Added pigz.spec to distribution for RPM systems [Brown]
+                       Avoid some compiler warnings
+                       Process symbolic links if piping to stdout [Hoffstätte]
+                       Decompress if executable named "gunzip" [Hoffstätte]
+                       Allow ".tgz" suffix [Chernookiy]
+                       Fix adler32 comparison on .zz files
+   2.1.7  17 Dec 2011  Avoid unused parameter warning in reenter()
+                       Don't assume 2's complement ints in compress_thread()
+                       Replicate gzip -cdf cat-like behavior
+                       Replicate gzip -- option to suppress option decoding
+                       Test output from make test instead of showing it
+                       Updated pigz.spec to install unpigz, pigz.1 [Obermaier]
+                       Add PIGZ environment variable [Mueller]
+                       Replicate gzip suffix search when decoding or listing
+                       Fix bug in load() to set in_left to zero on end of file
+                       Do not check suffix when input file won't be modified
+                       Decompress to stdout if name is "*cat" [Hayasaka]
+                       Write data descriptor signature to be like Info-ZIP
+                       Update and sort options list in help
+                       Use CC variable for compiler in Makefile
+                       Exit with code 2 if a warning has been issued
+                       Fix thread synchronization problem when tracing
+                       Change macro name MAX to MAX2 to avoid library conflicts
+                       Determine number of processors on HP-UX [Lloyd]
+   2.2    31 Dec 2011  Check for expansion bound busting (e.g. modified zlib)
+                       Make the "threads" list head global variable volatile
+                       Fix construction and printing of 32-bit check values
+                       Add --rsyncable functionality
+   2.2.1   1 Jan 2012  Fix bug in --rsyncable buffer management
+   2.2.2   1 Jan 2012  Fix another bug in --rsyncable buffer management
+   2.2.3  15 Jan 2012  Remove volatile in yarn.c
+                       Reduce the number of input buffers
+                       Change initial rsyncable hash to comparison value
+                       Improve the efficiency of arriving at a byte boundary
+                       Add thread portability #defines from yarn.c
+                       Have rsyncable compression be independent of threading
+                       Fix bug where constructed dictionaries not being used
+   2.2.4  11 Mar 2012  Avoid some return value warnings
+                       Improve the portability of printing the off_t type
+                       Check for existence of compress binary before using
+                       Update zlib version checking to 1.2.6 for new functions
+                       Fix bug in zip (-K) output
+                       Fix license in pigz.spec
+                       Remove thread portability #defines in pigz.c
+   2.2.5  28 Jul 2012  Avoid race condition in free_pool()
+                       Change suffix to .tar when decompressing or listing .tgz
+                       Print name of executable in error messages
+                       Show help properly when the name is unpigz or gunzip
+                       Fix permissions security problem before output is closed
+ */
+
+#define VERSION "pigz 2.2.5\n"
+
+/* To-do:
+    - make source portable for Windows, VMS, etc. (see gzip source code)
+    - make build portable (currently good for Unixish)
+ */
+
+/*
+   pigz compresses using threads to make use of multiple processors and cores.
+   The input is broken up into 128 KB chunks with each compressed in parallel.
+   The individual check value for each chunk is also calculated in parallel.
+   The compressed data is written in order to the output, and a combined check
+   value is calculated from the individual check values.
+
+   The compressed data format generated is in the gzip, zlib, or single-entry
+   zip format using the deflate compression method.  The compression produces
+   partial raw deflate streams which are concatenated by a single write thread
+   and wrapped with the appropriate header and trailer, where the trailer
+   contains the combined check value.
+
+   Each partial raw deflate stream is terminated by an empty stored block
+   (using the Z_SYNC_FLUSH option of zlib), in order to end that partial bit
+   stream at a byte boundary, unless that partial stream happens to already end
+   at a byte boundary (the latter requires zlib 1.2.6 or later).  Ending on a
+   byte boundary allows the partial streams to be concatenated simply as
+   sequences of bytes.  This adds a very small four to five byte overhead
+   (average 3.75 bytes) to the output for each input chunk.
+
+   The default input block size is 128K, but can be changed with the -b option.
+   The number of compress threads is set by default to 8, which can be changed
+   using the -p option.  Specifying -p 1 avoids the use of threads entirely.
+   pigz will try to determine the number of processors in the machine, in which
+   case if that number is two or greater, pigz will use that as the default for
+   -p instead of 8.
+
+   The input blocks, while compressed independently, have the last 32K of the
+   previous block loaded as a preset dictionary to preserve the compression
+   effectiveness of deflating in a single thread.  This can be turned off using
+   the --independent or -i option, so that the blocks can be decompressed
+   independently for partial error recovery or for random access.
+
+   Decompression can't be parallelized, at least not without specially prepared
+   deflate streams for that purpose.  As a result, pigz uses a single thread
+   (the main thread) for decompression, but will create three other threads for
+   reading, writing, and check calculation, which can speed up decompression
+   under some circumstances.  Parallel decompression can be turned off by
+   specifying one process (-dp 1 or -tp 1).
+
+   pigz requires zlib 1.2.1 or later to allow setting the dictionary when doing
+   raw deflate.  Since zlib 1.2.3 corrects security vulnerabilities in zlib
+   version 1.2.1 and 1.2.2, conditionals check for zlib 1.2.3 or later during
+   the compilation of pigz.c.  zlib 1.2.4 includes some improvements to
+   Z_FULL_FLUSH and deflateSetDictionary() that permit identical output for
+   pigz with and without threads, which is not possible with zlib 1.2.3.  This
+   may be important for uses of pigz -R where small changes in the contents
+   should result in small changes in the archive for rsync.  Note that due to
+   the details of how the lower levels of compression result in greater speed,
+   compression level 3 and below does not permit identical pigz output with
+   and without threads.
+
+   pigz uses the POSIX pthread library for thread control and communication,
+   through the yarn.h interface to yarn.c.  yarn.c can be replaced with
+   equivalent implementations using other thread libraries.  pigz can be
+   compiled with NOTHREAD #defined to not use threads at all (in which case
+   pigz will not be able to live up to the "parallel" in its name).
+ */
+
+/*
+   Details of parallel compression implementation:
+
+   When doing parallel compression, pigz uses the main thread to read the input
+   in 'size' sized chunks (see -b), and puts those in a compression job list,
+   each with a sequence number to keep track of the ordering.  If it is not the
+   first chunk, then that job also points to the previous input buffer, from
+   which the last 32K will be used as a dictionary (unless -i is specified).
+   This sets a lower limit of 32K on 'size'.
+
+   pigz launches up to 'procs' compression threads (see -p).  Each compression
+   thread continues to look for jobs in the compression list and perform those
+   jobs until instructed to return.  When a job is pulled, the dictionary, if
+   provided, will be loaded into the deflate engine and then that input buffer
+   is dropped for reuse.  Then the input data is compressed into an output
+   buffer that grows in size if necessary to hold the compressed data. The job
+   is then put into the write job list, sorted by the sequence number. The
+   compress thread however continues to calculate the check value on the input
+   data, either a CRC-32 or Adler-32, possibly in parallel with the write
+   thread writing the output data.  Once that's done, the compress thread drops
+   the input buffer and also releases the lock on the check value so that the
+   write thread can combine it with the previous check values.  The compress
+   thread has then completed that job, and goes to look for another.
+
+   All of the compress threads are left running and waiting even after the last
+   chunk is processed, so that they can support the next input to be compressed
+   (more than one input file on the command line).  Once pigz is done, it will
+   call all the compress threads home (that'll do pig, that'll do).
+
+   Before starting to read the input, the main thread launches the write thread
+   so that it is ready pick up jobs immediately.  The compress thread puts the
+   write jobs in the list in sequence sorted order, so that the first job in
+   the list is always has the lowest sequence number.  The write thread waits
+   for the next write job in sequence, and then gets that job.  The job still
+   holds its input buffer, from which the write thread gets the input buffer
+   length for use in check value combination.  Then the write thread drops that
+   input buffer to allow its reuse.  Holding on to the input buffer until the
+   write thread starts also has the benefit that the read and compress threads
+   can't get way ahead of the write thread and build up a large backlog of
+   unwritten compressed data.  The write thread will write the compressed data,
+   drop the output buffer, and then wait for the check value to be unlocked
+   by the compress thread.  Then the write thread combines the check value for
+   this chunk with the total check value for eventual use in the trailer.  If
+   this is not the last chunk, the write thread then goes back to look for the
+   next output chunk in sequence.  After the last chunk, the write thread
+   returns and joins the main thread.  Unlike the compress threads, a new write
+   thread is launched for each input stream.  The write thread writes the
+   appropriate header and trailer around the compressed data.
+
+   The input and output buffers are reused through their collection in pools.
+   Each buffer has a use count, which when decremented to zero returns the
+   buffer to the respective pool.  Each input buffer has up to three parallel
+   uses: as the input for compression, as the data for the check value
+   calculation, and as a dictionary for compression.  Each output buffer has
+   only one use, which is as the output of compression followed serially as
+   data to be written.  The input pool is limited in the number of buffers, so
+   that reading does not get way ahead of compression and eat up memory with
+   more input than can be used.  The limit is approximately two times the
+   number of compression threads.  In the case that reading is fast as compared
+   to compression, that number allows a second set of buffers to be read while
+   the first set of compressions are being performed.  The number of output
+   buffers is not directly limited, but is indirectly limited by the release of
+   input buffers to about the same number.
+ */
+
+/* use large file functions if available */
+#define _FILE_OFFSET_BITS 64
+
+/* included headers and what is expected from each */
+#include <stdio.h>      /* fflush(), fprintf(), fputs(), getchar(), putc(), */
+                        /* puts(), printf(), vasprintf(), stderr, EOF, NULL,
+                           SEEK_END, size_t, off_t */
+#include <stdlib.h>     /* exit(), malloc(), free(), realloc(), atol(), */
+                        /* atoi(), getenv() */
+#include <stdarg.h>     /* va_start(), va_end(), va_list */
+#include <string.h>     /* memset(), memchr(), memcpy(), strcmp(), strcpy() */
+                        /* strncpy(), strlen(), strcat(), strrchr() */
+#include <errno.h>      /* errno, EEXIST */
+#include <assert.h>     /* assert() */
+#include <time.h>       /* ctime(), time(), time_t, mktime() */
+#include <signal.h>     /* signal(), SIGINT */
+#include <sys/types.h>  /* ssize_t */
+#include <sys/stat.h>   /* chmod(), stat(), fstat(), lstat(), struct stat, */
+                        /* S_IFDIR, S_IFLNK, S_IFMT, S_IFREG */
+#include <sys/time.h>   /* utimes(), gettimeofday(), struct timeval */
+#include <unistd.h>     /* unlink(), _exit(), read(), write(), close(), */
+                        /* lseek(), isatty(), chown() */
+#include <fcntl.h>      /* open(), O_CREAT, O_EXCL, O_RDONLY, O_TRUNC, */
+                        /* O_WRONLY */
+#include <dirent.h>     /* opendir(), readdir(), closedir(), DIR, */
+                        /* struct dirent */
+#include <limits.h>     /* PATH_MAX, UINT_MAX */
+#if __STDC_VERSION__-0 >= 199901L || __GNUC__-0 >= 3
+#  include <inttypes.h> /* intmax_t */
+#endif
+
+#ifdef __hpux
+#  include <sys/param.h>
+#  include <sys/pstat.h>
+#endif
+
+#include "zlib.h"       /* deflateInit2(), deflateReset(), deflate(), */
+                        /* deflateEnd(), deflateSetDictionary(), crc32(),
+                           inflateBackInit(), inflateBack(), inflateBackEnd(),
+                           Z_DEFAULT_COMPRESSION, Z_DEFAULT_STRATEGY,
+                           Z_DEFLATED, Z_NO_FLUSH, Z_NULL, Z_OK,
+                           Z_SYNC_FLUSH, z_stream */
+#if !defined(ZLIB_VERNUM) || ZLIB_VERNUM < 0x1230
+#  error Need zlib version 1.2.3 or later
+#endif
+
+#ifndef NOTHREAD
+#  include "yarn.h"     /* thread, launch(), join(), join_all(), */
+                        /* lock, new_lock(), possess(), twist(), wait_for(),
+                           release(), peek_lock(), free_lock(), yarn_name */
+#endif
+
+/* for local functions and globals */
+#define local static
+
+/* prevent end-of-line conversions on MSDOSish operating systems */
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+#  include <io.h>       /* setmode(), O_BINARY */
+#  define SET_BINARY_MODE(fd) setmode(fd, O_BINARY)
+#else
+#  define SET_BINARY_MODE(fd)
+#endif
+
+/* release an allocated pointer, if allocated, and mark as unallocated */
+#define RELEASE(ptr) \
+    do { \
+        if ((ptr) != NULL) { \
+            free(ptr); \
+            ptr = NULL; \
+        } \
+    } while (0)
+
+/* sliding dictionary size for deflate */
+#define DICT 32768U
+
+/* largest power of 2 that fits in an unsigned int -- used to limit requests
+   to zlib functions that use unsigned int lengths */
+#define MAXP2 (UINT_MAX - (UINT_MAX >> 1))
+
+/* rsyncable constants -- RSYNCBITS is the number of bits in the mask for
+   comparison.  For random input data, there will be a hit on average every
+   1<<RSYNCBITS bytes.  So for an RSYNCBITS of 12, there will be an average of
+   one hit every 4096 bytes, resulting in a mean block size of 4096.  RSYNCMASK
+   is the resulting bit mask.  RSYNCHIT is what the hash value is compared to
+   after applying the mask.
+
+   The choice of 12 for RSYNCBITS is consistent with the original rsyncable
+   patch for gzip which also uses a 12-bit mask.  This results in a relatively
+   small hit to compression, on the order of 1.5% to 3%.  A mask of 13 bits can
+   be used instead if a hit of less than 1% to the compression is desired, at
+   the expense of more blocks transmitted for rsync updates.  (Your mileage may
+   vary.)
+
+   This implementation of rsyncable uses a different hash algorithm than what
+   the gzip rsyncable patch uses in order to provide better performance in
+   several regards.  The algorithm is simply to shift the hash value left one
+   bit and exclusive-or that with the next byte.  This is masked to the number
+   of hash bits (RSYNCMASK) and compared to all ones except for a zero in the
+   top bit (RSYNCHIT). This rolling hash has a very small window of 19 bytes
+   (RSYNCBITS+7).  The small window provides the benefit of much more rapid
+   resynchronization after a change, than does the 4096-byte window of the gzip
+   rsyncable patch.
+
+   The comparison value is chosen to avoid matching any repeated bytes or short
+   sequences.  The gzip rsyncable patch on the other hand uses a sum and zero
+   for comparison, which results in certain bad behaviors, such as always
+   matching everywhere in a long sequence of zeros.  Such sequences occur
+   frequently in tar files.
+
+   This hash efficiently discards history older than 19 bytes simply by
+   shifting that data past the top of the mask -- no history needs to be
+   retained to undo its impact on the hash value, as is needed for a sum.
+
+   The choice of the comparison value (RSYNCHIT) has the virtue of avoiding
+   extremely short blocks.  The shortest block is five bytes (RSYNCBITS-7) from
+   hit to hit, and is unlikely.  Whereas with the gzip rsyncable algorithm,
+   blocks of one byte are not only possible, but in fact are the most likely
+   block size.
+
+   Thanks and acknowledgement to Kevin Day for his experimentation and insights
+   on rsyncable hash characteristics that led to some of the choices here.
+ */
+#define RSYNCBITS 12
+#define RSYNCMASK ((1U << RSYNCBITS) - 1)
+#define RSYNCHIT (RSYNCMASK >> 1)
+
+/* initial pool counts and sizes -- INBUFS is the limit on the number of input
+   spaces as a function of the number of processors (used to throttle the
+   creation of compression jobs), OUTPOOL is the initial size of the output
+   data buffer, chosen to make resizing of the buffer very unlikely */
+#define INBUFS(p) (((p)<<1)+3)
+#define OUTPOOL(s) ((s)+((s)>>4))
+
+/* globals (modified by main thread only when it's the only thread) */
+local char *prog;           /* name by which pigz was invoked */
+local int ind;              /* input file descriptor */
+local int outd;             /* output file descriptor */
+local char in[PATH_MAX+1];  /* input file name (accommodate recursion) */
+local char *out = NULL;     /* output file name (allocated if not NULL) */
+local int verbosity;        /* 0 = quiet, 1 = normal, 2 = verbose, 3 = trace */
+local int headis;           /* 1 to store name, 2 to store date, 3 both */
+local int pipeout;          /* write output to stdout even if file */
+local int keep;             /* true to prevent deletion of input file */
+local int force;            /* true to overwrite, compress links, cat */
+local int form;             /* gzip = 0, zlib = 1, zip = 2 or 3 */
+local unsigned char magic1; /* first byte of possible header when decoding */
+local int recurse;          /* true to dive down into directory structure */
+local char *sufx;           /* suffix to use (".gz" or user supplied) */
+local char *name;           /* name for gzip header */
+local time_t mtime;         /* time stamp from input file for gzip header */
+local int list;             /* true to list files instead of compress */
+local int first = 1;        /* true if we need to print listing header */
+local int decode;           /* 0 to compress, 1 to decompress, 2 to test */
+local int level;            /* compression level */
+local int rsync;            /* true for rsync blocking */
+local int procs;            /* maximum number of compression threads (>= 1) */
+local int setdict;          /* true to initialize dictionary in each thread */
+local size_t size;          /* uncompressed input size per thread (>= 32K) */
+local int warned = 0;       /* true if a warning has been given */
+
+/* saved gzip/zip header data for decompression, testing, and listing */
+local time_t stamp;                 /* time stamp from gzip header */
+local char *hname = NULL;           /* name from header (allocated) */
+local unsigned long zip_crc;        /* local header crc */
+local unsigned long zip_clen;       /* local header compressed length */
+local unsigned long zip_ulen;       /* local header uncompressed length */
+
+/* display a complaint with the program name on stderr */
+local int complain(char *fmt, ...)
+{
+    va_list ap;
+
+    if (verbosity > 0) {
+        fprintf(stderr, "%s: ", prog);
+        va_start(ap, fmt);
+        vfprintf(stderr, fmt, ap);
+        va_end(ap);
+        putc('\n', stderr);
+        fflush(stderr);
+        warned = 1;
+    }
+    return 0;
+}
+
+/* exit with error, delete output file if in the middle of writing it */
+local int bail(char *why, char *what)
+{
+    if (outd != -1 && out != NULL)
+        unlink(out);
+    complain("abort: %s%s", why, what);
+    exit(1);
+    return 0;
+}
+
+#ifdef DEBUG
+
+/* starting time of day for tracing */
+local struct timeval start;
+
+/* trace log */
+local struct log {
+    struct timeval when;    /* time of entry */
+    char *msg;              /* message */
+    struct log *next;       /* next entry */
+} *log_head, **log_tail = NULL;
+#ifndef NOTHREAD
+  local lock *log_lock = NULL;
+#endif
+
+/* maximum log entry length */
+#define MAXMSG 256
+
+/* set up log (call from main thread before other threads launched) */
+local void log_init(void)
+{
+    if (log_tail == NULL) {
+#ifndef NOTHREAD
+        log_lock = new_lock(0);
+#endif
+        log_head = NULL;
+        log_tail = &log_head;
+    }
+}
+
+/* add entry to trace log */
+local void log_add(char *fmt, ...)
+{
+    struct timeval now;
+    struct log *me;
+    va_list ap;
+    char msg[MAXMSG];
+
+    gettimeofday(&now, NULL);
+    me = malloc(sizeof(struct log));
+    if (me == NULL)
+        bail("not enough memory", "");
+    me->when = now;
+    va_start(ap, fmt);
+    vsnprintf(msg, MAXMSG, fmt, ap);
+    va_end(ap);
+    me->msg = malloc(strlen(msg) + 1);
+    if (me->msg == NULL) {
+        free(me);
+        bail("not enough memory", "");
+    }
+    strcpy(me->msg, msg);
+    me->next = NULL;
+#ifndef NOTHREAD
+    assert(log_lock != NULL);
+    possess(log_lock);
+#endif
+    *log_tail = me;
+    log_tail = &(me->next);
+#ifndef NOTHREAD
+    twist(log_lock, BY, +1);
+#endif
+}
+
+/* pull entry from trace log and print it, return false if empty */
+local int log_show(void)
+{
+    struct log *me;
+    struct timeval diff;
+
+    if (log_tail == NULL)
+        return 0;
+#ifndef NOTHREAD
+    possess(log_lock);
+#endif
+    me = log_head;
+    if (me == NULL) {
+#ifndef NOTHREAD
+        release(log_lock);
+#endif
+        return 0;
+    }
+    log_head = me->next;
+    if (me->next == NULL)
+        log_tail = &log_head;
+#ifndef NOTHREAD
+    twist(log_lock, BY, -1);
+#endif
+    diff.tv_usec = me->when.tv_usec - start.tv_usec;
+    diff.tv_sec = me->when.tv_sec - start.tv_sec;
+    if (diff.tv_usec < 0) {
+        diff.tv_usec += 1000000L;
+        diff.tv_sec--;
+    }
+    fprintf(stderr, "trace %ld.%06ld %s\n",
+            (long)diff.tv_sec, (long)diff.tv_usec, me->msg);
+    fflush(stderr);
+    free(me->msg);
+    free(me);
+    return 1;
+}
+
+/* release log resources (need to do log_init() to use again) */
+local void log_free(void)
+{
+    struct log *me;
+
+    if (log_tail != NULL) {
+#ifndef NOTHREAD
+        possess(log_lock);
+#endif
+        while ((me = log_head) != NULL) {
+            log_head = me->next;
+            free(me->msg);
+            free(me);
+        }
+#ifndef NOTHREAD
+        twist(log_lock, TO, 0);
+        free_lock(log_lock);
+        log_lock = NULL;
+#endif
+        log_tail = NULL;
+    }
+}
+
+/* show entries until no more, free log */
+local void log_dump(void)
+{
+    if (log_tail == NULL)
+        return;
+    while (log_show())
+        ;
+    log_free();
+}
+
+/* debugging macro */
+#define Trace(x) \
+    do { \
+        if (verbosity > 2) { \
+            log_add x; \
+        } \
+    } while (0)
+
+#else /* !DEBUG */
+
+#define log_dump()
+#define Trace(x)
+
+#endif
+
+/* read up to len bytes into buf, repeating read() calls as needed */
+local size_t readn(int desc, unsigned char *buf, size_t len)
+{
+    ssize_t ret;
+    size_t got;
+
+    got = 0;
+    while (len) {
+        ret = read(desc, buf, len);
+        if (ret < 0)
+            bail("read error on ", in);
+        if (ret == 0)
+            break;
+        buf += ret;
+        len -= ret;
+        got += ret;
+    }
+    return got;
+}
+
+/* write len bytes, repeating write() calls as needed */
+local void writen(int desc, unsigned char *buf, size_t len)
+{
+    ssize_t ret;
+
+    while (len) {
+        ret = write(desc, buf, len);
+        if (ret < 1) {
+            complain("write error code %d", errno);
+            bail("write error on ", out);
+        }
+        buf += ret;
+        len -= ret;
+    }
+}
+
+/* convert Unix time to MS-DOS date and time, assuming current timezone
+   (you got a better idea?) */
+local unsigned long time2dos(time_t t)
+{
+    struct tm *tm;
+    unsigned long dos;
+
+    if (t == 0)
+        t = time(NULL);
+    tm = localtime(&t);
+    if (tm->tm_year < 80 || tm->tm_year > 207)
+        return 0;
+    dos = (tm->tm_year - 80) << 25;
+    dos += (tm->tm_mon + 1) << 21;
+    dos += tm->tm_mday << 16;
+    dos += tm->tm_hour << 11;
+    dos += tm->tm_min << 5;
+    dos += (tm->tm_sec + 1) >> 1;   /* round to double-seconds */
+    return dos;
+}
+
+/* put a 4-byte integer into a byte array in LSB order or MSB order */
+#define PUT2L(a,b) (*(a)=(b)&0xff,(a)[1]=(b)>>8)
+#define PUT4L(a,b) (PUT2L(a,(b)&0xffff),PUT2L((a)+2,(b)>>16))
+#define PUT4M(a,b) (*(a)=(b)>>24,(a)[1]=(b)>>16,(a)[2]=(b)>>8,(a)[3]=(b))
+
+/* write a gzip, zlib, or zip header using the information in the globals */
+local unsigned long put_header(void)
+{
+    unsigned long len;
+    unsigned char head[30];
+
+    if (form > 1) {                 /* zip */
+        /* write local header */
+        PUT4L(head, 0x04034b50UL);  /* local header signature */
+        PUT2L(head + 4, 20);        /* version needed to extract (2.0) */
+        PUT2L(head + 6, 8);         /* flags: data descriptor follows data */
+        PUT2L(head + 8, 8);         /* deflate */
+        PUT4L(head + 10, time2dos(mtime));
+        PUT4L(head + 14, 0);        /* crc (not here) */
+        PUT4L(head + 18, 0);        /* compressed length (not here) */
+        PUT4L(head + 22, 0);        /* uncompressed length (not here) */
+        PUT2L(head + 26, name == NULL ? 1 : strlen(name));  /* name length */
+        PUT2L(head + 28, 9);        /* length of extra field (see below) */
+        writen(outd, head, 30);     /* write local header */
+        len = 30;
+
+        /* write file name (use "-" for stdin) */
+        if (name == NULL)
+            writen(outd, (unsigned char *)"-", 1);
+        else
+            writen(outd, (unsigned char *)name, strlen(name));
+        len += name == NULL ? 1 : strlen(name);
+
+        /* write extended timestamp extra field block (9 bytes) */
+        PUT2L(head, 0x5455);        /* extended timestamp signature */
+        PUT2L(head + 2, 5);         /* number of data bytes in this block */
+        head[4] = 1;                /* flag presence of mod time */
+        PUT4L(head + 5, mtime);     /* mod time */
+        writen(outd, head, 9);      /* write extra field block */
+        len += 9;
+    }
+    else if (form) {                /* zlib */
+        head[0] = 0x78;             /* deflate, 32K window */
+        head[1] = (level == 9 ? 3 : (level == 1 ? 0 :
+            (level >= 6 || level == Z_DEFAULT_COMPRESSION ? 1 :  2))) << 6;
+        head[1] += 31 - (((head[0] << 8) + head[1]) % 31);
+        writen(outd, head, 2);
+        len = 2;
+    }
+    else {                          /* gzip */
+        head[0] = 31;
+        head[1] = 139;
+        head[2] = 8;                /* deflate */
+        head[3] = name != NULL ? 8 : 0;
+        PUT4L(head + 4, mtime);
+        head[8] = level == 9 ? 2 : (level == 1 ? 4 : 0);
+        head[9] = 3;                /* unix */
+        writen(outd, head, 10);
+        len = 10;
+        if (name != NULL)
+            writen(outd, (unsigned char *)name, strlen(name) + 1);
+        if (name != NULL)
+            len += strlen(name) + 1;
+    }
+    return len;
+}
+
+/* write a gzip, zlib, or zip trailer */
+local void put_trailer(unsigned long ulen, unsigned long clen,
+                       unsigned long check, unsigned long head)
+{
+    unsigned char tail[46];
+
+    if (form > 1) {                 /* zip */
+        unsigned long cent;
+
+        /* write data descriptor (as promised in local header) */
+        PUT4L(tail, 0x08074b50UL);
+        PUT4L(tail + 4, check);
+        PUT4L(tail + 8, clen);
+        PUT4L(tail + 12, ulen);
+        writen(outd, tail, 16);
+
+        /* write central file header */
+        PUT4L(tail, 0x02014b50UL);  /* central header signature */
+        tail[4] = 63;               /* obeyed version 6.3 of the zip spec */
+        tail[5] = 255;              /* ignore external attributes */
+        PUT2L(tail + 6, 20);        /* version needed to extract (2.0) */
+        PUT2L(tail + 8, 8);         /* data descriptor is present */
+        PUT2L(tail + 10, 8);        /* deflate */
+        PUT4L(tail + 12, time2dos(mtime));
+        PUT4L(tail + 16, check);    /* crc */
+        PUT4L(tail + 20, clen);     /* compressed length */
+        PUT4L(tail + 24, ulen);     /* uncompressed length */
+        PUT2L(tail + 28, name == NULL ? 1 : strlen(name));  /* name length */
+        PUT2L(tail + 30, 9);        /* length of extra field (see below) */
+        PUT2L(tail + 32, 0);        /* no file comment */
+        PUT2L(tail + 34, 0);        /* disk number 0 */
+        PUT2L(tail + 36, 0);        /* internal file attributes */
+        PUT4L(tail + 38, 0);        /* external file attributes (ignored) */
+        PUT4L(tail + 42, 0);        /* offset of local header */
+        writen(outd, tail, 46);     /* write central file header */
+        cent = 46;
+
+        /* write file name (use "-" for stdin) */
+        if (name == NULL)
+            writen(outd, (unsigned char *)"-", 1);
+        else
+            writen(outd, (unsigned char *)name, strlen(name));
+        cent += name == NULL ? 1 : strlen(name);
+
+        /* write extended timestamp extra field block (9 bytes) */
+        PUT2L(tail, 0x5455);        /* extended timestamp signature */
+        PUT2L(tail + 2, 5);         /* number of data bytes in this block */
+        tail[4] = 1;                /* flag presence of mod time */
+        PUT4L(tail + 5, mtime);     /* mod time */
+        writen(outd, tail, 9);      /* write extra field block */
+        cent += 9;
+
+        /* write end of central directory record */
+        PUT4L(tail, 0x06054b50UL);  /* end of central directory signature */
+        PUT2L(tail + 4, 0);         /* number of this disk */
+        PUT2L(tail + 6, 0);         /* disk with start of central directory */
+        PUT2L(tail + 8, 1);         /* number of entries on this disk */
+        PUT2L(tail + 10, 1);        /* total number of entries */
+        PUT4L(tail + 12, cent);     /* size of central directory */
+        PUT4L(tail + 16, head + clen + 16); /* offset of central directory */
+        PUT2L(tail + 20, 0);        /* no zip file comment */
+        writen(outd, tail, 22);     /* write end of central directory record */
+    }
+    else if (form) {                /* zlib */
+        PUT4M(tail, check);
+        writen(outd, tail, 4);
+    }
+    else {                          /* gzip */
+        PUT4L(tail, check);
+        PUT4L(tail + 4, ulen);
+        writen(outd, tail, 8);
+    }
+}
+
+/* compute check value depending on format */
+#define CHECK(a,b,c) (form == 1 ? adler32(a,b,c) : crc32(a,b,c))
+
+#ifndef NOTHREAD
+/* -- threaded portions of pigz -- */
+
+/* -- check value combination routines for parallel calculation -- */
+
+#define COMB(a,b,c) (form == 1 ? adler32_comb(a,b,c) : crc32_comb(a,b,c))
+/* combine two crc-32's or two adler-32's (copied from zlib 1.2.3 so that pigz
+   can be compatible with older versions of zlib) */
+
+/* we copy the combination routines from zlib here, in order to avoid
+   linkage issues with the zlib 1.2.3 builds on Sun, Ubuntu, and others */
+
+local unsigned long gf2_matrix_times(unsigned long *mat, unsigned long vec)
+{
+    unsigned long sum;
+
+    sum = 0;
+    while (vec) {
+        if (vec & 1)
+            sum ^= *mat;
+        vec >>= 1;
+        mat++;
+    }
+    return sum;
+}
+
+local void gf2_matrix_square(unsigned long *square, unsigned long *mat)
+{
+    int n;
+
+    for (n = 0; n < 32; n++)
+        square[n] = gf2_matrix_times(mat, mat[n]);
+}
+
+local unsigned long crc32_comb(unsigned long crc1, unsigned long crc2,
+                               size_t len2)
+{
+    int n;
+    unsigned long row;
+    unsigned long even[32];     /* even-power-of-two zeros operator */
+    unsigned long odd[32];      /* odd-power-of-two zeros operator */
+
+    /* degenerate case */
+    if (len2 == 0)
+        return crc1;
+
+    /* put operator for one zero bit in odd */
+    odd[0] = 0xedb88320UL;          /* CRC-32 polynomial */
+    row = 1;
+    for (n = 1; n < 32; n++) {
+        odd[n] = row;
+        row <<= 1;
+    }
+
+    /* put operator for two zero bits in even */
+    gf2_matrix_square(even, odd);
+
+    /* put operator for four zero bits in odd */
+    gf2_matrix_square(odd, even);
+
+    /* apply len2 zeros to crc1 (first square will put the operator for one
+       zero byte, eight zero bits, in even) */
+    do {
+        /* apply zeros operator for this bit of len2 */
+        gf2_matrix_square(even, odd);
+        if (len2 & 1)
+            crc1 = gf2_matrix_times(even, crc1);
+        len2 >>= 1;
+
+        /* if no more bits set, then done */
+        if (len2 == 0)
+            break;
+
+        /* another iteration of the loop with odd and even swapped */
+        gf2_matrix_square(odd, even);
+        if (len2 & 1)
+            crc1 = gf2_matrix_times(odd, crc1);
+        len2 >>= 1;
+
+        /* if no more bits set, then done */
+    } while (len2 != 0);
+
+    /* return combined crc */
+    crc1 ^= crc2;
+    return crc1;
+}
+
+#define BASE 65521U     /* largest prime smaller than 65536 */
+#define LOW16 0xffff    /* mask lower 16 bits */
+
+local unsigned long adler32_comb(unsigned long adler1, unsigned long adler2,
+                                 size_t len2)
+{
+    unsigned long sum1;
+    unsigned long sum2;
+    unsigned rem;
+
+    /* the derivation of this formula is left as an exercise for the reader */
+    rem = (unsigned)(len2 % BASE);
+    sum1 = adler1 & LOW16;
+    sum2 = (rem * sum1) % BASE;
+    sum1 += (adler2 & LOW16) + BASE - 1;
+    sum2 += ((adler1 >> 16) & LOW16) + ((adler2 >> 16) & LOW16) + BASE - rem;
+    if (sum1 >= BASE) sum1 -= BASE;
+    if (sum1 >= BASE) sum1 -= BASE;
+    if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1);
+    if (sum2 >= BASE) sum2 -= BASE;
+    return sum1 | (sum2 << 16);
+}
+
+/* -- pool of spaces for buffer management -- */
+
+/* These routines manage a pool of spaces.  Each pool specifies a fixed size
+   buffer to be contained in each space.  Each space has a use count, which
+   when decremented to zero returns the space to the pool.  If a space is
+   requested from the pool and the pool is empty, a space is immediately
+   created unless a specified limit on the number of spaces has been reached.
+   Only if the limit is reached will it wait for a space to be returned to the
+   pool.  Each space knows what pool it belongs to, so that it can be returned.
+ */
+
+/* a space (one buffer for each space) */
+struct space {
+    lock *use;              /* use count -- return to pool when zero */
+    unsigned char *buf;     /* buffer of size size */
+    size_t size;            /* current size of this buffer */
+    size_t len;             /* for application usage (initially zero) */
+    struct pool *pool;      /* pool to return to */
+    struct space *next;     /* for pool linked list */
+};
+
+/* pool of spaces (one pool for each type needed) */
+struct pool {
+    lock *have;             /* unused spaces available, lock for list */
+    struct space *head;     /* linked list of available buffers */
+    size_t size;            /* size of new buffers in this pool */
+    int limit;              /* number of new spaces allowed, or -1 */
+    int made;               /* number of buffers made */
+};
+
+/* initialize a pool (pool structure itself provided, not allocated) -- the
+   limit is the maximum number of spaces in the pool, or -1 to indicate no
+   limit, i.e., to never wait for a buffer to return to the pool */
+local void new_pool(struct pool *pool, size_t size, int limit)
+{
+    pool->have = new_lock(0);
+    pool->head = NULL;
+    pool->size = size;
+    pool->limit = limit;
+    pool->made = 0;
+}
+
+/* get a space from a pool -- the use count is initially set to one, so there
+   is no need to call use_space() for the first use */
+local struct space *get_space(struct pool *pool)
+{
+    struct space *space;
+
+    /* if can't create any more, wait for a space to show up */
+    possess(pool->have);
+    if (pool->limit == 0)
+        wait_for(pool->have, NOT_TO_BE, 0);
+
+    /* if a space is available, pull it from the list and return it */
+    if (pool->head != NULL) {
+        space = pool->head;
+        possess(space->use);
+        pool->head = space->next;
+        twist(pool->have, BY, -1);      /* one less in pool */
+        twist(space->use, TO, 1);       /* initially one user */
+        space->len = 0;
+        return space;
+    }
+
+    /* nothing available, don't want to wait, make a new space */
+    assert(pool->limit != 0);
+    if (pool->limit > 0)
+        pool->limit--;
+    pool->made++;
+    release(pool->have);
+    space = malloc(sizeof(struct space));
+    if (space == NULL)
+        bail("not enough memory", "");
+    space->use = new_lock(1);           /* initially one user */
+    space->buf = malloc(pool->size);
+    if (space->buf == NULL)
+        bail("not enough memory", "");
+    space->size = pool->size;
+    space->len = 0;
+    space->pool = pool;                 /* remember the pool this belongs to */
+    return space;
+}
+
+/* compute next size up by multiplying by about 2**(1/3) and round to the next
+   power of 2 if we're close (so three applications results in doubling) -- if
+   small, go up to at least 16, if overflow, go to max size_t value */
+local size_t grow(size_t size)
+{
+    size_t was, top;
+    int shift;
+
+    was = size;
+    size += size >> 2;
+    top = size;
+    for (shift = 0; top > 7; shift++)
+        top >>= 1;
+    if (top == 7)
+        size = (size_t)1 << (shift + 3);
+    if (size < 16)
+        size = 16;
+    if (size <= was)
+        size = (size_t)0 - 1;
+    return size;
+}
+
+/* increase the size of the buffer in space */
+local void grow_space(struct space *space)
+{
+    size_t more;
+
+    /* compute next size up */
+    more = grow(space->size);
+    if (more == space->size)
+        bail("not enough memory", "");
+
+    /* reallocate the buffer */
+    space->buf = realloc(space->buf, more);
+    if (space->buf == NULL)
+        bail("not enough memory", "");
+    space->size = more;
+}
+
+/* increment the use count to require one more drop before returning this space
+   to the pool */
+local void use_space(struct space *space)
+{
+    possess(space->use);
+    twist(space->use, BY, +1);
+}
+
+/* drop a space, returning it to the pool if the use count is zero */
+local void drop_space(struct space *space)
+{
+    int use;
+    struct pool *pool;
+
+    possess(space->use);
+    use = peek_lock(space->use);
+    assert(use != 0);
+    if (use == 1) {
+        pool = space->pool;
+        possess(pool->have);
+        space->next = pool->head;
+        pool->head = space;
+        twist(pool->have, BY, +1);
+    }
+    twist(space->use, BY, -1);
+}
+
+/* free the memory and lock resources of a pool -- return number of spaces for
+   debugging and resource usage measurement */
+local int free_pool(struct pool *pool)
+{
+    int count;
+    struct space *space;
+
+    possess(pool->have);
+    count = 0;
+    while ((space = pool->head) != NULL) {
+        pool->head = space->next;
+        free(space->buf);
+        free_lock(space->use);
+        free(space);
+        count++;
+    }
+    assert(count == pool->made);
+    release(pool->have);
+    free_lock(pool->have);
+    return count;
+}
+
+/* input and output buffer pools */
+local struct pool in_pool;
+local struct pool out_pool;
+local struct pool dict_pool;
+local struct pool lens_pool;
+
+/* -- parallel compression -- */
+
+/* compress or write job (passed from compress list to write list) -- if seq is
+   equal to -1, compress_thread is instructed to return; if more is false then
+   this is the last chunk, which after writing tells write_thread to return */
+struct job {
+    long seq;                   /* sequence number */
+    int more;                   /* true if this is not the last chunk */
+    struct space *in;           /* input data to compress */
+    struct space *out;          /* dictionary or resulting compressed data */
+    struct space *lens;         /* coded list of flush block lengths */
+    unsigned long check;        /* check value for input data */
+    lock *calc;                 /* released when check calculation complete */
+    struct job *next;           /* next job in the list (either list) */
+};
+
+/* list of compress jobs (with tail for appending to list) */
+local lock *compress_have = NULL;   /* number of compress jobs waiting */
+local struct job *compress_head, **compress_tail;
+
+/* list of write jobs */
+local lock *write_first;            /* lowest sequence number in list */
+local struct job *write_head;
+
+/* number of compression threads running */
+local int cthreads = 0;
+
+/* write thread if running */
+local thread *writeth = NULL;
+
+/* setup job lists (call from main thread) */
+local void setup_jobs(void)
+{
+    /* set up only if not already set up*/
+    if (compress_have != NULL)
+        return;
+
+    /* allocate locks and initialize lists */
+    compress_have = new_lock(0);
+    compress_head = NULL;
+    compress_tail = &compress_head;
+    write_first = new_lock(-1);
+    write_head = NULL;
+
+    /* initialize buffer pools (initial size for out_pool not critical, since
+       buffers will be grown in size if needed -- initial size chosen to make
+       this unlikely -- same for lens_pool) */
+    new_pool(&in_pool, size, INBUFS(procs));
+    new_pool(&out_pool, OUTPOOL(size), -1);
+    new_pool(&dict_pool, DICT, -1);
+    new_pool(&lens_pool, size >> (RSYNCBITS - 1), -1);
+}
+
+/* command the compress threads to all return, then join them all (call from
+   main thread), free all the thread-related resources */
+local void finish_jobs(void)
+{
+    struct job job;
+    int caught;
+
+    /* only do this once */
+    if (compress_have == NULL)
+        return;
+
+    /* command all of the extant compress threads to return */
+    possess(compress_have);
+    job.seq = -1;
+    job.next = NULL;
+    compress_head = &job;
+    compress_tail = &(job.next);
+    twist(compress_have, BY, +1);       /* will wake them all up */
+
+    /* join all of the compress threads, verify they all came back */
+    caught = join_all();
+    Trace(("-- joined %d compress threads", caught));
+    assert(caught == cthreads);
+    cthreads = 0;
+
+    /* free the resources */
+    caught = free_pool(&lens_pool);
+    Trace(("-- freed %d block lengths buffers", caught));
+    caught = free_pool(&dict_pool);
+    Trace(("-- freed %d dictionary buffers", caught));
+    caught = free_pool(&out_pool);
+    Trace(("-- freed %d output buffers", caught));
+    caught = free_pool(&in_pool);
+    Trace(("-- freed %d input buffers", caught));
+    free_lock(write_first);
+    free_lock(compress_have);
+    compress_have = NULL;
+}
+
+/* compress all strm->avail_in bytes at strm->next_in to out->buf, updating
+   out->len, grow the size of the buffer (out->size) if necessary -- respect
+   the size limitations of the zlib stream data types (size_t may be larger
+   than unsigned) */
+local void deflate_engine(z_stream *strm, struct space *out, int flush)
+{
+    size_t room;
+
+    do {
+        room = out->size - out->len;
+        if (room == 0) {
+            grow_space(out);
+            room = out->size - out->len;
+        }
+        strm->next_out = out->buf + out->len;
+        strm->avail_out = room < UINT_MAX ? (unsigned)room : UINT_MAX;
+        (void)deflate(strm, flush);
+        out->len = strm->next_out - out->buf;
+    } while (strm->avail_out == 0);
+    assert(strm->avail_in == 0);
+}
+
+/* get the next compression job from the head of the list, compress and compute
+   the check value on the input, and put a job in the write list with the
+   results -- keep looking for more jobs, returning when a job is found with a
+   sequence number of -1 (leave that job in the list for other incarnations to
+   find) */
+local void compress_thread(void *dummy)
+{
+    struct job *job;                /* job pulled and working on */
+    struct job *here, **prior;      /* pointers for inserting in write list */
+    unsigned long check;            /* check value of input */
+    unsigned char *next;            /* pointer for blocks, check value data */
+    size_t left;                    /* input left to process */
+    size_t len;                     /* remaining bytes to compress/check */
+#if ZLIB_VERNUM >= 0x1260
+    int bits;                       /* deflate pending bits */
+#endif
+    z_stream strm;                  /* deflate stream */
+
+    (void)dummy;
+
+    /* initialize the deflate stream for this thread */
+    strm.zfree = Z_NULL;
+    strm.zalloc = Z_NULL;
+    strm.opaque = Z_NULL;
+    if (deflateInit2(&strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) !=
+            Z_OK)
+        bail("not enough memory", "");
+
+    /* keep looking for work */
+    for (;;) {
+        /* get a job (like I tell my son) */
+        possess(compress_have);
+        wait_for(compress_have, NOT_TO_BE, 0);
+        job = compress_head;
+        assert(job != NULL);
+        if (job->seq == -1)
+            break;
+        compress_head = job->next;
+        if (job->next == NULL)
+            compress_tail = &compress_head;
+        twist(compress_have, BY, -1);
+
+        /* got a job -- initialize and set the compression level (note that if
+           deflateParams() is called immediately after deflateReset(), there is
+           no need to initialize the input/output for the stream) */
+        Trace(("-- compressing #%ld", job->seq));
+        (void)deflateReset(&strm);
+        (void)deflateParams(&strm, level, Z_DEFAULT_STRATEGY);
+
+        /* set dictionary if provided, release that input or dictionary buffer
+           (not NULL if dict is true and if this is not the first work unit) */
+        if (job->out != NULL) {
+            len = job->out->len;
+            left = len < DICT ? len : DICT;
+            deflateSetDictionary(&strm, job->out->buf + (len - left), left);
+            drop_space(job->out);
+        }
+
+        /* set up input and output */
+        job->out = get_space(&out_pool);
+        strm.next_in = job->in->buf;
+        strm.next_out = job->out->buf;
+
+        /* compress each block, either flushing or finishing */
+        next = job->lens == NULL ? NULL : job->lens->buf;
+        left = job->in->len;
+        job->out->len = 0;
+        do {
+            /* decode next block length from blocks list */
+            len = next == NULL ? 128 : *next++;
+            if (len < 128)                          /* 64..32831 */
+                len = (len << 8) + (*next++) + 64;
+            else if (len == 128)                    /* end of list */
+                len = left;
+            else if (len < 192)                     /* 1..63 */
+                len &= 0x3f;
+            else {                                  /* 32832..4227135 */
+                len = ((len & 0x3f) << 16) + (*next++ << 8) + 32832U;
+                len += *next++;
+            }
+            left -= len;
+
+            /* run MAXP2-sized amounts of input through deflate -- this loop is
+               needed for those cases where the unsigned type is smaller than
+               the size_t type, or when len is close to the limit of the size_t
+               type */
+            while (len > MAXP2) {
+                strm.avail_in = MAXP2;
+                deflate_engine(&strm, job->out, Z_NO_FLUSH);
+                len -= MAXP2;
+            }
+
+            /* run the last piece through deflate -- end on a byte boundary,
+               using a sync marker if necessary, or finish the deflate stream
+               if this is the last block */
+            strm.avail_in = (unsigned)len;
+            if (left || job->more) {
+#if ZLIB_VERNUM >= 0x1260
+                deflate_engine(&strm, job->out, Z_BLOCK);
+
+                /* add just enough empty blocks to get to a byte boundary */
+                (void)deflatePending(&strm, Z_NULL, &bits);
+                if (bits & 1)
+                    deflate_engine(&strm, job->out, Z_SYNC_FLUSH);
+                else if (bits & 7) {
+                    do {
+                        bits = deflatePrime(&strm, 10, 2);  /* static empty */
+                        assert(bits == Z_OK);
+                        (void)deflatePending(&strm, Z_NULL, &bits);
+                    } while (bits & 7);
+                    deflate_engine(&strm, job->out, Z_BLOCK);
+                }
+#else
+                deflate_engine(&strm, job->out, Z_SYNC_FLUSH);
+#endif
+            }
+            else
+                deflate_engine(&strm, job->out, Z_FINISH);
+        } while (left);
+        if (job->lens != NULL) {
+            drop_space(job->lens);
+            job->lens = NULL;
+        }
+        Trace(("-- compressed #%ld%s", job->seq, job->more ? "" : " (last)"));
+
+        /* reserve input buffer until check value has been calculated */
+        use_space(job->in);
+
+        /* insert write job in list in sorted order, alert write thread */
+        possess(write_first);
+        prior = &write_head;
+        while ((here = *prior) != NULL) {
+            if (here->seq > job->seq)
+                break;
+            prior = &(here->next);
+        }
+        job->next = here;
+        *prior = job;
+        twist(write_first, TO, write_head->seq);
+
+        /* calculate the check value in parallel with writing, alert the write
+           thread that the calculation is complete, and drop this usage of the
+           input buffer */
+        len = job->in->len;
+        next = job->in->buf;
+        check = CHECK(0L, Z_NULL, 0);
+        while (len > MAXP2) {
+            check = CHECK(check, next, MAXP2);
+            len -= MAXP2;
+            next += MAXP2;
+        }
+        check = CHECK(check, next, (unsigned)len);
+        drop_space(job->in);
+        job->check = check;
+        Trace(("-- checked #%ld%s", job->seq, job->more ? "" : " (last)"));
+        possess(job->calc);
+        twist(job->calc, TO, 1);
+
+        /* done with that one -- go find another job */
+    }
+
+    /* found job with seq == -1 -- free deflate memory and return to join */
+    release(compress_have);
+    (void)deflateEnd(&strm);
+}
+
+/* collect the write jobs off of the list in sequence order and write out the
+   compressed data until the last chunk is written -- also write the header and
+   trailer and combine the individual check values of the input buffers */
+local void write_thread(void *dummy)
+{
+    long seq;                       /* next sequence number looking for */
+    struct job *job;                /* job pulled and working on */
+    size_t len;                     /* input length */
+    int more;                       /* true if more chunks to write */
+    unsigned long head;             /* header length */
+    unsigned long ulen;             /* total uncompressed size (overflow ok) */
+    unsigned long clen;             /* total compressed size (overflow ok) */
+    unsigned long check;            /* check value of uncompressed data */
+
+    (void)dummy;
+
+    /* build and write header */
+    Trace(("-- write thread running"));
+    head = put_header();
+
+    /* process output of compress threads until end of input */
+    ulen = clen = 0;
+    check = CHECK(0L, Z_NULL, 0);
+    seq = 0;
+    do {
+        /* get next write job in order */
+        possess(write_first);
+        wait_for(write_first, TO_BE, seq);
+        job = write_head;
+        write_head = job->next;
+        twist(write_first, TO, write_head == NULL ? -1 : write_head->seq);
+
+        /* update lengths, save uncompressed length for COMB */
+        more = job->more;
+        len = job->in->len;
+        drop_space(job->in);
+        ulen += (unsigned long)len;
+        clen += (unsigned long)(job->out->len);
+
+        /* write the compressed data and drop the output buffer */
+        Trace(("-- writing #%ld", seq));
+        writen(outd, job->out->buf, job->out->len);
+        drop_space(job->out);
+        Trace(("-- wrote #%ld%s", seq, more ? "" : " (last)"));
+
+        /* wait for check calculation to complete, then combine, once
+           the compress thread is done with the input, release it */
+        possess(job->calc);
+        wait_for(job->calc, TO_BE, 1);
+        release(job->calc);
+        check = COMB(check, job->check, len);
+
+        /* free the job */
+        free_lock(job->calc);
+        free(job);
+
+        /* get the next buffer in sequence */
+        seq++;
+    } while (more);
+
+    /* write trailer */
+    put_trailer(ulen, clen, check, head);
+
+    /* verify no more jobs, prepare for next use */
+    possess(compress_have);
+    assert(compress_head == NULL && peek_lock(compress_have) == 0);
+    release(compress_have);
+    possess(write_first);
+    assert(write_head == NULL);
+    twist(write_first, TO, -1);
+}
+
+/* encode a hash hit to the block lengths list -- hit == 0 ends the list */
+local void append_len(struct job *job, size_t len)
+{
+    struct space *lens;
+
+    assert(len < 4227136UL);
+    if (job->lens == NULL)
+        job->lens = get_space(&lens_pool);
+    lens = job->lens;
+    if (lens->size < lens->len + 3)
+        grow_space(lens);
+    if (len < 64)
+        lens->buf[lens->len++] = len + 128;
+    else if (len < 32832U) {
+        len -= 64;
+        lens->buf[lens->len++] = len >> 8;
+        lens->buf[lens->len++] = len;
+    }
+    else {
+        len -= 32832U;
+        lens->buf[lens->len++] = (len >> 16) + 192;
+        lens->buf[lens->len++] = len >> 8;
+        lens->buf[lens->len++] = len;
+    }
+}
+
+/* compress ind to outd, using multiple threads for the compression and check
+   value calculations and one other thread for writing the output -- compress
+   threads will be launched and left running (waiting actually) to support
+   subsequent calls of parallel_compress() */
+local void parallel_compress(void)
+{
+    long seq;                       /* sequence number */
+    struct space *curr;             /* input data to compress */
+    struct space *next;             /* input data that follows curr */
+    struct space *hold;             /* input data that follows next */
+    struct space *dict;             /* dictionary for next compression */
+    struct job *job;                /* job for compress, then write */
+    int more;                       /* true if more input to read */
+    unsigned hash;                  /* hash for rsyncable */
+    unsigned char *scan;            /* next byte to compute hash on */
+    unsigned char *end;             /* after end of data to compute hash on */
+    unsigned char *last;            /* position after last hit */
+    size_t left;                    /* last hit in curr to end of curr */
+    size_t len;                     /* for various length computations */
+
+    /* if first time or after an option change, setup the job lists */
+    setup_jobs();
+
+    /* start write thread */
+    writeth = launch(write_thread, NULL);
+
+    /* read from input and start compress threads (write thread will pick up
+     the output of the compress threads) */
+    seq = 0;
+    next = get_space(&in_pool);
+    next->len = readn(ind, next->buf, next->size);
+    hold = NULL;
+    dict = NULL;
+    scan = next->buf;
+    hash = RSYNCHIT;
+    left = 0;
+    do {
+        /* create a new job */
+        job = malloc(sizeof(struct job));
+        if (job == NULL)
+            bail("not enough memory", "");
+        job->calc = new_lock(0);
+
+        /* update input spaces */
+        curr = next;
+        next = hold;
+        hold = NULL;
+
+        /* get more input if we don't already have some */
+        if (next == NULL) {
+            next = get_space(&in_pool);
+            next->len = readn(ind, next->buf, next->size);
+        }
+
+        /* if rsyncable, generate block lengths and prepare curr for job to
+           likely have less than size bytes (up to the last hash hit) */
+        job->lens = NULL;
+        if (rsync && curr->len) {
+            /* compute the hash function starting where we last left off to
+               cover either size bytes or to EOF, whichever is less, through
+               the data in curr (and in the next loop, through next) -- save
+               the block lengths resulting from the hash hits in the job->lens
+               list */
+            if (left == 0) {
+                /* scan is in curr */
+                last = curr->buf;
+                end = curr->buf + curr->len;
+                while (scan < end) {
+                    hash = ((hash << 1) ^ *scan++) & RSYNCMASK;
+                    if (hash == RSYNCHIT) {
+                        len = scan - last;
+                        append_len(job, len);
+                        last = scan;
+                    }
+                }
+
+                /* continue scan in next */
+                left = scan - last;
+                scan = next->buf;
+            }
+
+            /* scan in next for enough bytes to fill curr, or what is available
+               in next, whichever is less (if next isn't full, then we're at
+               the end of the file) -- the bytes in curr since the last hit,
+               stored in left, counts towards the size of the first block */
+            last = next->buf;
+            len = curr->size - curr->len;
+            if (len > next->len)
+                len = next->len;
+            end = next->buf + len;
+            while (scan < end) {
+                hash = ((hash << 1) ^ *scan++) & RSYNCMASK;
+                if (hash == RSYNCHIT) {
+                    len = (scan - last) + left;
+                    left = 0;
+                    append_len(job, len);
+                    last = scan;
+                }
+            }
+            append_len(job, 0);
+
+            /* create input in curr for job up to last hit or entire buffer if
+               no hits at all -- save remainder in next and possibly hold */
+            len = (job->lens->len == 1 ? scan : last) - next->buf;
+            if (len) {
+                /* got hits in next, or no hits in either -- copy to curr */
+                memcpy(curr->buf + curr->len, next->buf, len);
+                curr->len += len;
+                memmove(next->buf, next->buf + len, next->len - len);
+                next->len -= len;
+                scan -= len;
+                left = 0;
+            }
+            else if (job->lens->len != 1 && left && next->len) {
+                /* had hits in curr, but none in next, and last hit in curr
+                   wasn't right at the end, so we have input there to save --
+                   use curr up to the last hit, save the rest, moving next to
+                   hold */
+                hold = next;
+                next = get_space(&in_pool);
+                memcpy(next->buf, curr->buf + (curr->len - left), left);
+                next->len = left;
+                curr->len -= left;
+            }
+            else {
+                /* else, last match happened to be right at the end of curr,
+                   or we're at the end of the input compressing the rest */
+                left = 0;
+            }
+        }
+
+        /* compress curr->buf to curr->len -- compress thread will drop curr */
+        job->in = curr;
+
+        /* set job->more if there is more to compress after curr */
+        more = next->len != 0;
+        job->more = more;
+
+        /* provide dictionary for this job, prepare dictionary for next job */
+        job->out = dict;
+        if (more && setdict) {
+            if (curr->len >= DICT || job->out == NULL) {
+                dict = curr;
+                use_space(dict);
+            }
+            else {
+                dict = get_space(&dict_pool);
+                len = DICT - curr->len;
+                memcpy(dict->buf, job->out->buf + (job->out->len - len), len);
+                memcpy(dict->buf + len, curr->buf, curr->len);
+                dict->len = DICT;
+            }
+        }
+
+        /* preparation of job is complete */
+        job->seq = seq;
+        Trace(("-- read #%ld%s", seq, more ? "" : " (last)"));
+        if (++seq < 1)
+            bail("input too long: ", in);
+
+        /* start another compress thread if needed */
+        if (cthreads < seq && cthreads < procs) {
+            (void)launch(compress_thread, NULL);
+            cthreads++;
+        }
+
+        /* put job at end of compress list, let all the compressors know */
+        possess(compress_have);
+        job->next = NULL;
+        *compress_tail = job;
+        compress_tail = &(job->next);
+        twist(compress_have, BY, +1);
+    } while (more);
+    drop_space(next);
+
+    /* wait for the write thread to complete (we leave the compress threads out
+       there and waiting in case there is another stream to compress) */
+    join(writeth);
+    writeth = NULL;
+    Trace(("-- write thread joined"));
+}
+
+#endif
+
+/* repeated code in single_compress to compress available input and write it */
+#define DEFLATE_WRITE(flush) \
+    do { \
+        do { \
+            strm->avail_out = out_size; \
+            strm->next_out = out; \
+            (void)deflate(strm, flush); \
+            writen(outd, out, out_size - strm->avail_out); \
+            clen += out_size - strm->avail_out; \
+        } while (strm->avail_out == 0); \
+        assert(strm->avail_in == 0); \
+    } while (0)
+
+/* do a simple compression in a single thread from ind to outd -- if reset is
+   true, instead free the memory that was allocated and retained for input,
+   output, and deflate */
+local void single_compress(int reset)
+{
+    size_t got;                     /* amount read */
+    size_t more;                    /* amount of next read (0 if eof) */
+    size_t start;                   /* start of next read */
+    size_t block;                   /* bytes in current block for -i */
+    unsigned hash;                  /* hash for rsyncable */
+#if ZLIB_VERNUM >= 0x1260
+    int bits;                       /* deflate pending bits */
+#endif
+    unsigned char *scan;            /* pointer for hash computation */
+    size_t left;                    /* bytes left to compress after hash hit */
+    unsigned long head;             /* header length */
+    unsigned long ulen;             /* total uncompressed size (overflow ok) */
+    unsigned long clen;             /* total compressed size (overflow ok) */
+    unsigned long check;            /* check value of uncompressed data */
+    static unsigned out_size;       /* size of output buffer */
+    static unsigned char *in, *next, *out;  /* reused i/o buffers */
+    static z_stream *strm = NULL;   /* reused deflate structure */
+
+    /* if requested, just release the allocations and return */
+    if (reset) {
+        if (strm != NULL) {
+            (void)deflateEnd(strm);
+            free(strm);
+            free(out);
+            free(next);
+            free(in);
+            strm = NULL;
+        }
+        return;
+    }
+
+    /* initialize the deflate structure if this is the first time */
+    if (strm == NULL) {
+        out_size = size > MAXP2 ? MAXP2 : (unsigned)size;
+        if ((in = malloc(size)) == NULL ||
+            (next = malloc(size)) == NULL ||
+            (out = malloc(out_size)) == NULL ||
+            (strm = malloc(sizeof(z_stream))) == NULL)
+            bail("not enough memory", "");
+        strm->zfree = Z_NULL;
+        strm->zalloc = Z_NULL;
+        strm->opaque = Z_NULL;
+        if (deflateInit2(strm, level, Z_DEFLATED, -15, 8,
+                         Z_DEFAULT_STRATEGY) != Z_OK)
+            bail("not enough memory", "");
+    }
+
+    /* write header */
+    head = put_header();
+
+    /* set compression level in case it changed */
+    (void)deflateReset(strm);
+    (void)deflateParams(strm, level, Z_DEFAULT_STRATEGY);
+
+    /* do raw deflate and calculate check value */
+    got = 0;
+    more = readn(ind, next, size);
+    ulen = (unsigned)more;
+    start = 0;
+    clen = 0;
+    block = 0;
+    check = CHECK(0L, Z_NULL, 0);
+    hash = RSYNCHIT;
+    do {
+        /* get data to compress, see if there is any more input */
+        if (got == 0) {
+            scan = in;  in = next;  next = scan;
+            strm->next_in = in + start;
+            got = more;
+            more = readn(ind, next, size);
+            ulen += (unsigned long)more;
+            start = 0;
+        }
+
+        /* if rsyncable, compute hash until a hit or the end of the block */
+        left = 0;
+        if (rsync && got) {
+            scan = strm->next_in;
+            left = got;
+            do {
+                if (left == 0) {
+                    /* went to the end -- if no more or no hit in size bytes,
+                       then proceed to do a flush or finish with got bytes */
+                    if (more == 0 || got == size)
+                        break;
+
+                    /* fill in[] with what's left there and as much as possible
+                       from next[] -- set up to continue hash hit search */
+                    memmove(in, strm->next_in, got);
+                    strm->next_in = in;
+                    scan = in + got;
+                    left = more > size - got ? size - got : more;
+                    memcpy(scan, next + start, left);
+                    got += left;
+                    more -= left;
+                    start += left;
+
+                    /* if that emptied the next buffer, try to refill it */
+                    if (more == 0) {
+                        more = readn(ind, next, size);
+                        ulen += (unsigned long)more;
+                        start = 0;
+                    }
+                }
+                left--;
+                hash = ((hash << 1) ^ *scan++) & RSYNCMASK;
+            } while (hash != RSYNCHIT);
+            got -= left;
+        }
+
+        /* clear history for --independent option */
+        if (!setdict) {
+            block += got;
+            if (block > size) {
+                (void)deflateReset(strm);
+                block = got;
+            }
+        }
+
+        /* compress MAXP2-size chunks in case unsigned type is small */
+        while (got > MAXP2) {
+            strm->avail_in = MAXP2;
+            check = CHECK(check, strm->next_in, strm->avail_in);
+            DEFLATE_WRITE(Z_NO_FLUSH);
+            got -= MAXP2;
+        }
+
+        /* compress the remainder, emit a block -- finish if end of input */
+        strm->avail_in = (unsigned)got;
+        got = left;
+        check = CHECK(check, strm->next_in, strm->avail_in);
+        if (more || got) {
+#if ZLIB_VERNUM >= 0x1260
+            DEFLATE_WRITE(Z_BLOCK);
+            (void)deflatePending(strm, Z_NULL, &bits);
+            if (bits & 1)
+                DEFLATE_WRITE(Z_SYNC_FLUSH);
+            else if (bits & 7) {
+                do {
+                    bits = deflatePrime(strm, 10, 2);
+                    assert(bits == Z_OK);
+                    (void)deflatePending(strm, Z_NULL, &bits);
+                } while (bits & 7);
+                DEFLATE_WRITE(Z_NO_FLUSH);
+            }
+#else
+            DEFLATE_WRITE(Z_SYNC_FLUSH);
+#endif
+        }
+        else
+            DEFLATE_WRITE(Z_FINISH);
+
+        /* do until no more input */
+    } while (more || got);
+
+    /* write trailer */
+    put_trailer(ulen, clen, check, head);
+}
+
+/* --- decompression --- */
+
+/* globals for decompression and listing buffered reading */
+#define BUF 32768U                  /* input buffer size */
+local unsigned char in_buf[BUF];    /* input buffer */
+local unsigned char *in_next;       /* next unused byte in buffer */
+local size_t in_left;               /* number of unused bytes in buffer */
+local int in_eof;                   /* true if reached end of file on input */
+local int in_short;                 /* true if last read didn't fill buffer */
+local off_t in_tot;                 /* total bytes read from input */
+local off_t out_tot;                /* total bytes written to output */
+local unsigned long out_check;      /* check value of output */
+
+#ifndef NOTHREAD
+/* parallel reading */
+
+local unsigned char in_buf2[BUF];   /* second buffer for parallel reads */
+local size_t in_len;                /* data waiting in next buffer */
+local int in_which;                 /* -1: start, 0: in_buf2, 1: in_buf */
+local lock *load_state;             /* value = 0 to wait, 1 to read a buffer */
+local thread *load_thread;          /* load_read() thread for joining */
+
+/* parallel read thread */
+local void load_read(void *dummy)
+{
+    size_t len;
+
+    (void)dummy;
+
+    Trace(("-- launched decompress read thread"));
+    do {
+        possess(load_state);
+        wait_for(load_state, TO_BE, 1);
+        in_len = len = readn(ind, in_which ? in_buf : in_buf2, BUF);
+        Trace(("-- decompress read thread read %lu bytes", len));
+        twist(load_state, TO, 0);
+    } while (len == BUF);
+    Trace(("-- exited decompress read thread"));
+}
+
+#endif
+
+/* load() is called when the input has been consumed in order to provide more
+   input data: load the input buffer with BUF or less bytes (less if at end of
+   file) from the file ind, set in_next to point to the in_left bytes read,
+   update in_tot, and return in_left -- in_eof is set to true when in_left has
+   gone to zero and there is no more data left to read from ind */
+local size_t load(void)
+{
+    /* if already detected end of file, do nothing */
+    if (in_short) {
+        in_eof = 1;
+        in_left = 0;
+        return 0;
+    }
+
+#ifndef NOTHREAD
+    /* if first time in or procs == 1, read a buffer to have something to
+       return, otherwise wait for the previous read job to complete */
+    if (procs > 1) {
+        /* if first time, fire up the read thread, ask for a read */
+        if (in_which == -1) {
+            in_which = 1;
+            load_state = new_lock(1);
+            load_thread = launch(load_read, NULL);
+        }
+
+        /* wait for the previously requested read to complete */
+        possess(load_state);
+        wait_for(load_state, TO_BE, 0);
+        release(load_state);
+
+        /* set up input buffer with the data just read */
+        in_next = in_which ? in_buf : in_buf2;
+        in_left = in_len;
+
+        /* if not at end of file, alert read thread to load next buffer,
+           alternate between in_buf and in_buf2 */
+        if (in_len == BUF) {
+            in_which = 1 - in_which;
+            possess(load_state);
+            twist(load_state, TO, 1);
+        }
+
+        /* at end of file -- join read thread (already exited), clean up */
+        else {
+            join(load_thread);
+            free_lock(load_state);
+            in_which = -1;
+        }
+    }
+    else
+#endif
+    {
+        /* don't use threads -- simply read a buffer into in_buf */
+        in_left = readn(ind, in_next = in_buf, BUF);
+    }
+
+    /* note end of file */
+    if (in_left < BUF) {
+        in_short = 1;
+
+        /* if we got bupkis, now is the time to mark eof */
+        if (in_left == 0)
+            in_eof = 1;
+    }
+
+    /* update the total and return the available bytes */
+    in_tot += in_left;
+    return in_left;
+}
+
+/* initialize for reading new input */
+local void in_init(void)
+{
+    in_left = 0;
+    in_eof = 0;
+    in_short = 0;
+    in_tot = 0;
+#ifndef NOTHREAD
+    in_which = -1;
+#endif
+}
+
+/* buffered reading macros for decompression and listing */
+#define GET() (in_eof || (in_left == 0 && load() == 0) ? EOF : \
+               (in_left--, *in_next++))
+#define GET2() (tmp2 = GET(), tmp2 + ((unsigned)(GET()) << 8))
+#define GET4() (tmp4 = GET2(), tmp4 + ((unsigned long)(GET2()) << 16))
+#define SKIP(dist) \
+    do { \
+        size_t togo = (dist); \
+        while (togo > in_left) { \
+            togo -= in_left; \
+            if (load() == 0) \
+                return -1; \
+        } \
+        in_left -= togo; \
+        in_next += togo; \
+    } while (0)
+
+/* pull LSB order or MSB order integers from an unsigned char buffer */
+#define PULL2L(p) ((p)[0] + ((unsigned)((p)[1]) << 8))
+#define PULL4L(p) (PULL2L(p) + ((unsigned long)(PULL2L((p) + 2)) << 16))
+#define PULL2M(p) (((unsigned)((p)[0]) << 8) + (p)[1])
+#define PULL4M(p) (((unsigned long)(PULL2M(p)) << 16) + PULL2M((p) + 2))
+
+/* convert MS-DOS date and time to a Unix time, assuming current timezone
+   (you got a better idea?) */
+local time_t dos2time(unsigned long dos)
+{
+    struct tm tm;
+
+    if (dos == 0)
+        return time(NULL);
+    tm.tm_year = ((int)(dos >> 25) & 0x7f) + 80;
+    tm.tm_mon  = ((int)(dos >> 21) & 0xf) - 1;
+    tm.tm_mday = (int)(dos >> 16) & 0x1f;
+    tm.tm_hour = (int)(dos >> 11) & 0x1f;
+    tm.tm_min  = (int)(dos >> 5) & 0x3f;
+    tm.tm_sec  = (int)(dos << 1) & 0x3e;
+    tm.tm_isdst = -1;           /* figure out if DST or not */
+    return mktime(&tm);
+}
+
+/* convert an unsigned 32-bit integer to signed, even if long > 32 bits */
+local long tolong(unsigned long val)
+{
+    return (long)(val & 0x7fffffffUL) - (long)(val & 0x80000000UL);
+}
+
+#define LOW32 0xffffffffUL
+
+/* process zip extra field to extract zip64 lengths and Unix mod time */
+local int read_extra(unsigned len, int save)
+{
+    unsigned id, size, tmp2;
+    unsigned long tmp4;
+
+    /* process extra blocks */
+    while (len >= 4) {
+        id = GET2();
+        size = GET2();
+        if (in_eof)
+            return -1;
+        len -= 4;
+        if (size > len)
+            break;
+        len -= size;
+        if (id == 0x0001) {
+            /* Zip64 Extended Information Extra Field */
+            if (zip_ulen == LOW32 && size >= 8) {
+                zip_ulen = GET4();
+                SKIP(4);
+                size -= 8;
+            }
+            if (zip_clen == LOW32 && size >= 8) {
+                zip_clen = GET4();
+                SKIP(4);
+                size -= 8;
+            }
+        }
+        if (save) {
+            if ((id == 0x000d || id == 0x5855) && size >= 8) {
+                /* PKWare Unix or Info-ZIP Type 1 Unix block */
+                SKIP(4);
+                stamp = tolong(GET4());
+                size -= 8;
+            }
+            if (id == 0x5455 && size >= 5) {
+                /* Extended Timestamp block */
+                size--;
+                if (GET() & 1) {
+                    stamp = tolong(GET4());
+                    size -= 4;
+                }
+            }
+        }
+        SKIP(size);
+    }
+    SKIP(len);
+    return 0;
+}
+
+/* read a gzip, zip, zlib, or lzw header from ind and extract useful
+   information, return the method -- or on error return negative: -1 is
+   immediate EOF, -2 is not a recognized compressed format, -3 is premature EOF
+   within the header, -4 is unexpected header flag values; a method of 256 is
+   lzw -- set form to indicate gzip, zlib, or zip */
+local int get_header(int save)
+{
+    unsigned magic;             /* magic header */
+    int method;                 /* compression method */
+    int flags;                  /* header flags */
+    unsigned fname, extra;      /* name and extra field lengths */
+    unsigned tmp2;              /* for macro */
+    unsigned long tmp4;         /* for macro */
+
+    /* clear return information */
+    if (save) {
+        stamp = 0;
+        RELEASE(hname);
+    }
+
+    /* see if it's a gzip, zlib, or lzw file */
+    form = 0;
+    magic1 = GET();
+    if (in_eof)
+        return -1;
+    magic = magic1 << 8;
+    magic += GET();
+    if (in_eof)
+        return -2;
+    if (magic % 31 == 0) {          /* it's zlib */
+        form = 1;
+        return (int)((magic >> 8) & 0xf);
+    }
+    if (magic == 0x1f9d)            /* it's lzw */
+        return 256;
+    if (magic == 0x504b) {          /* it's zip */
+        if (GET() != 3 || GET() != 4)
+            return -3;
+        SKIP(2);
+        flags = GET2();
+        if (in_eof)
+            return -3;
+        if (flags & 0xfff0)
+            return -4;
+        method = GET2();
+        if (flags & 1)              /* encrypted */
+            method = 255;           /* mark as unknown method */
+        if (in_eof)
+            return -3;
+        if (save)
+            stamp = dos2time(GET4());
+        else
+            SKIP(4);
+        zip_crc = GET4();
+        zip_clen = GET4();
+        zip_ulen = GET4();
+        fname = GET2();
+        extra = GET2();
+        if (save) {
+            char *next = hname = malloc(fname + 1);
+            if (hname == NULL)
+                bail("not enough memory", "");
+            while (fname > in_left) {
+                memcpy(next, in_next, in_left);
+                fname -= in_left;
+                next += in_left;
+                if (load() == 0)
+                    return -3;
+            }
+            memcpy(next, in_next, fname);
+            in_left -= fname;
+            in_next += fname;
+            next += fname;
+            *next = 0;
+        }
+        else
+            SKIP(fname);
+        read_extra(extra, save);
+        form = 2 + ((flags & 8) >> 3);
+        return in_eof ? -3 : method;
+    }
+    if (magic != 0x1f8b) {          /* not gzip */
+        in_left++;      /* unget second magic byte */
+        in_next--;
+        return -2;
+    }
+
+    /* it's gzip -- get method and flags */
+    method = GET();
+    flags = GET();
+    if (in_eof)
+        return -1;
+    if (flags & 0xe0)
+        return -4;
+
+    /* get time stamp */
+    if (save)
+        stamp = tolong(GET4());
+    else
+        SKIP(4);
+
+    /* skip extra field and OS */
+    SKIP(2);
+
+    /* skip extra field, if present */
+    if (flags & 4) {
+        extra = GET2();
+        if (in_eof)
+            return -3;
+        SKIP(extra);
+    }
+
+    /* read file name, if present, into allocated memory */
+    if ((flags & 8) && save) {
+        unsigned char *end;
+        size_t copy, have, size = 128;
+        hname = malloc(size);
+        if (hname == NULL)
+            bail("not enough memory", "");
+        have = 0;
+        do {
+            if (in_left == 0 && load() == 0)
+                return -3;
+            end = memchr(in_next, 0, in_left);
+            copy = end == NULL ? in_left : (size_t)(end - in_next) + 1;
+            if (have + copy > size) {
+                while (have + copy > (size <<= 1))
+                    ;
+                hname = realloc(hname, size);
+                if (hname == NULL)
+                    bail("not enough memory", "");
+            }
+            memcpy(hname + have, in_next, copy);
+            have += copy;
+            in_left -= copy;
+            in_next += copy;
+        } while (end == NULL);
+    }
+    else if (flags & 8)
+        while (GET() != 0)
+            if (in_eof)
+                return -3;
+
+    /* skip comment */
+    if (flags & 16)
+        while (GET() != 0)
+            if (in_eof)
+                return -3;
+
+    /* skip header crc */
+    if (flags & 2)
+        SKIP(2);
+
+    /* return compression method */
+    return method;
+}
+
+/* --- list contents of compressed input (gzip, zlib, or lzw) */
+
+/* find standard compressed file suffix, return length of suffix */
+local size_t compressed_suffix(char *nm)
+{
+    size_t len;
+
+    len = strlen(nm);
+    if (len > 4) {
+        nm += len - 4;
+        len = 4;
+        if (strcmp(nm, ".zip") == 0 || strcmp(nm, ".ZIP") == 0 ||
+            strcmp(nm, ".tgz") == 0)
+            return 4;
+    }
+    if (len > 3) {
+        nm += len - 3;
+        len = 3;
+        if (strcmp(nm, ".gz") == 0 || strcmp(nm, "-gz") == 0 ||
+            strcmp(nm, ".zz") == 0 || strcmp(nm, "-zz") == 0)
+            return 3;
+    }
+    if (len > 2) {
+        nm += len - 2;
+        if (strcmp(nm, ".z") == 0 || strcmp(nm, "-z") == 0 ||
+            strcmp(nm, "_z") == 0 || strcmp(nm, ".Z") == 0)
+            return 2;
+    }
+    return 0;
+}
+
+/* listing file name lengths for -l and -lv */
+#define NAMEMAX1 48     /* name display limit at verbosity 1 */
+#define NAMEMAX2 16     /* name display limit at verbosity 2 */
+
+/* print gzip or lzw file information */
+local void show_info(int method, unsigned long check, off_t len, int cont)
+{
+    size_t max;             /* maximum name length for current verbosity */
+    size_t n;               /* name length without suffix */
+    time_t now;             /* for getting current year */
+    char mod[26];           /* modification time in text */
+    char name[NAMEMAX1+1];  /* header or file name, possibly truncated */
+
+    /* create abbreviated name from header file name or actual file name */
+    max = verbosity > 1 ? NAMEMAX2 : NAMEMAX1;
+    memset(name, 0, max + 1);
+    if (cont)
+        strncpy(name, "<...>", max + 1);
+    else if (hname == NULL) {
+        n = strlen(in) - compressed_suffix(in);
+        strncpy(name, in, n > max + 1 ? max + 1 : n);
+        if (strcmp(in + n, ".tgz") == 0 && n < max + 1)
+            strncpy(name + n, ".tar", max + 1 - n);
+    }
+    else
+        strncpy(name, hname, max + 1);
+    if (name[max])
+        strcpy(name + max - 3, "...");
+
+    /* convert time stamp to text */
+    if (stamp) {
+        strcpy(mod, ctime(&stamp));
+        now = time(NULL);
+        if (strcmp(mod + 20, ctime(&now) + 20) != 0)
+            strcpy(mod + 11, mod + 19);
+    }
+    else
+        strcpy(mod + 4, "------ -----");
+    mod[16] = 0;
+
+    /* if first time, print header */
+    if (first) {
+        if (verbosity > 1)
+            fputs("method    check    timestamp    ", stdout);
+        if (verbosity > 0)
+            puts("compressed   original reduced  name");
+        first = 0;
+    }
+
+    /* print information */
+    if (verbosity > 1) {
+        if (form == 3 && !decode)
+            printf("zip%3d  --------  %s  ", method, mod + 4);
+        else if (form > 1)
+            printf("zip%3d  %08lx  %s  ", method, check, mod + 4);
+        else if (form)
+            printf("zlib%2d  %08lx  %s  ", method, check, mod + 4);
+        else if (method == 256)
+            printf("lzw     --------  %s  ", mod + 4);
+        else
+            printf("gzip%2d  %08lx  %s  ", method, check, mod + 4);
+    }
+    if (verbosity > 0) {
+        if ((form == 3 && !decode) ||
+            (method == 8 && in_tot > (len + (len >> 10) + 12)) ||
+            (method == 256 && in_tot > len + (len >> 1) + 3))
+#if __STDC_VERSION__-0 >= 199901L || __GNUC__-0 >= 3
+            printf("%10jd %10jd?  unk    %s\n",
+                   (intmax_t)in_tot, (intmax_t)len, name);
+        else
+            printf("%10jd %10jd %6.1f%%  %s\n",
+                   (intmax_t)in_tot, (intmax_t)len,
+                   len == 0 ? 0 : 100 * (len - in_tot)/(double)len,
+                   name);
+#else
+            printf(sizeof(off_t) == sizeof(long) ?
+                   "%10ld %10ld?  unk    %s\n" : "%10lld %10lld?  unk    %s\n",
+                   in_tot, len, name);
+        else
+            printf(sizeof(off_t) == sizeof(long) ?
+                   "%10ld %10ld %6.1f%%  %s\n" : "%10lld %10lld %6.1f%%  %s\n",
+                   in_tot, len,
+                   len == 0 ? 0 : 100 * (len - in_tot)/(double)len,
+                   name);
+#endif
+    }
+}
+
+/* list content information about the gzip file at ind (only works if the gzip
+   file contains a single gzip stream with no junk at the end, and only works
+   well if the uncompressed length is less than 4 GB) */
+local void list_info(void)
+{
+    int method;             /* get_header() return value */
+    size_t n;               /* available trailer bytes */
+    off_t at;               /* used to calculate compressed length */
+    unsigned char tail[8];  /* trailer containing check and length */
+    unsigned long check, len;   /* check value and length from trailer */
+
+    /* initialize input buffer */
+    in_init();
+
+    /* read header information and position input after header */
+    method = get_header(1);
+    if (method < 0) {
+        RELEASE(hname);
+        if (method != -1 && verbosity > 1)
+            complain("%s not a compressed file -- skipping", in);
+        return;
+    }
+
+    /* list zip file */
+    if (form > 1) {
+        in_tot = zip_clen;
+        show_info(method, zip_crc, zip_ulen, 0);
+        return;
+    }
+
+    /* list zlib file */
+    if (form) {
+        at = lseek(ind, 0, SEEK_END);
+        if (at == -1) {
+            check = 0;
+            do {
+                len = in_left < 4 ? in_left : 4;
+                in_next += in_left - len;
+                while (len--)
+                    check = (check << 8) + *in_next++;
+            } while (load() != 0);
+            check &= LOW32;
+        }
+        else {
+            in_tot = at;
+            lseek(ind, -4, SEEK_END);
+            readn(ind, tail, 4);
+            check = PULL4M(tail);
+        }
+        in_tot -= 6;
+        show_info(method, check, 0, 0);
+        return;
+    }
+
+    /* list lzw file */
+    if (method == 256) {
+        at = lseek(ind, 0, SEEK_END);
+        if (at == -1)
+            while (load() != 0)
+                ;
+        else
+            in_tot = at;
+        in_tot -= 3;
+        show_info(method, 0, 0, 0);
+        return;
+    }
+
+    /* skip to end to get trailer (8 bytes), compute compressed length */
+    if (in_short) {                     /* whole thing already read */
+        if (in_left < 8) {
+            complain("%s not a valid gzip file -- skipping", in);
+            return;
+        }
+        in_tot = in_left - 8;           /* compressed size */
+        memcpy(tail, in_next + (in_left - 8), 8);
+    }
+    else if ((at = lseek(ind, -8, SEEK_END)) != -1) {
+        in_tot = at - in_tot + in_left; /* compressed size */
+        readn(ind, tail, 8);            /* get trailer */
+    }
+    else {                              /* can't seek */
+        at = in_tot - in_left;          /* save header size */
+        do {
+            n = in_left < 8 ? in_left : 8;
+            memcpy(tail, in_next + (in_left - n), n);
+            load();
+        } while (in_left == BUF);       /* read until end */
+        if (in_left < 8) {
+            if (n + in_left < 8) {
+                complain("%s not a valid gzip file -- skipping", in);
+                return;
+            }
+            if (in_left) {
+                if (n + in_left > 8)
+                    memcpy(tail, tail + n - (8 - in_left), 8 - in_left);
+                memcpy(tail + 8 - in_left, in_next, in_left);
+            }
+        }
+        else
+            memcpy(tail, in_next + (in_left - 8), 8);
+        in_tot -= at + 8;
+    }
+    if (in_tot < 2) {
+        complain("%s not a valid gzip file -- skipping", in);
+        return;
+    }
+
+    /* convert trailer to check and uncompressed length (modulo 2^32) */
+    check = PULL4L(tail);
+    len = PULL4L(tail + 4);
+
+    /* list information about contents */
+    show_info(method, check, len, 0);
+    RELEASE(hname);
+}
+
+/* --- copy input to output (when acting like cat) --- */
+
+local void cat(void)
+{
+    /* write first magic byte (if we're here, there's at least one byte) */
+    writen(outd, &magic1, 1);
+    out_tot = 1;
+
+    /* copy the remainder of the input to the output (if there were any more
+       bytes of input, then in_left is non-zero and in_next is pointing to the
+       second magic byte) */
+    while (in_left) {
+        writen(outd, in_next, in_left);
+        out_tot += in_left;
+        in_left = 0;
+        load();
+    }
+}
+
+/* --- decompress deflate input --- */
+
+/* call-back input function for inflateBack() */
+local unsigned inb(void *desc, unsigned char **buf)
+{
+    (void)desc;
+    load();
+    *buf = in_next;
+    return in_left;
+}
+
+/* output buffers and window for infchk() and unlzw() */
+#define OUTSIZE 32768U      /* must be at least 32K for inflateBack() window */
+local unsigned char out_buf[OUTSIZE];
+
+#ifndef NOTHREAD
+/* output data for parallel write and check */
+local unsigned char out_copy[OUTSIZE];
+local size_t out_len;
+
+/* outb threads states */
+local lock *outb_write_more = NULL;
+local lock *outb_check_more;
+
+/* output write thread */
+local void outb_write(void *dummy)
+{
+    size_t len;
+
+    (void)dummy;
+
+    Trace(("-- launched decompress write thread"));
+    do {
+        possess(outb_write_more);
+        wait_for(outb_write_more, TO_BE, 1);
+        len = out_len;
+        if (len && decode == 1)
+            writen(outd, out_copy, len);
+        Trace(("-- decompress wrote %lu bytes", len));
+        twist(outb_write_more, TO, 0);
+    } while (len);
+    Trace(("-- exited decompress write thread"));
+}
+
+/* output check thread */
+local void outb_check(void *dummy)
+{
+    size_t len;
+
+    (void)dummy;
+
+    Trace(("-- launched decompress check thread"));
+    do {
+        possess(outb_check_more);
+        wait_for(outb_check_more, TO_BE, 1);
+        len = out_len;
+        out_check = CHECK(out_check, out_copy, len);
+        Trace(("-- decompress checked %lu bytes", len));
+        twist(outb_check_more, TO, 0);
+    } while (len);
+    Trace(("-- exited decompress check thread"));
+}
+#endif
+
+/* call-back output function for inflateBack() -- wait for the last write and
+   check calculation to complete, copy the write buffer, and then alert the
+   write and check threads and return for more decompression while that's
+   going on (or just write and check if no threads or if proc == 1) */
+local int outb(void *desc, unsigned char *buf, unsigned len)
+{
+#ifndef NOTHREAD
+    static thread *wr, *ch;
+
+    (void)desc;
+
+    if (procs > 1) {
+        /* if first time, initialize state and launch threads */
+        if (outb_write_more == NULL) {
+            outb_write_more = new_lock(0);
+            outb_check_more = new_lock(0);
+            wr = launch(outb_write, NULL);
+            ch = launch(outb_check, NULL);
+        }
+
+        /* wait for previous write and check threads to complete */
+        possess(outb_check_more);
+        wait_for(outb_check_more, TO_BE, 0);
+        possess(outb_write_more);
+        wait_for(outb_write_more, TO_BE, 0);
+
+        /* copy the output and alert the worker bees */
+        out_len = len;
+        out_tot += len;
+        memcpy(out_copy, buf, len);
+        twist(outb_write_more, TO, 1);
+        twist(outb_check_more, TO, 1);
+
+        /* if requested with len == 0, clean up -- terminate and join write and
+           check threads, free lock */
+        if (len == 0) {
+            join(ch);
+            join(wr);
+            free_lock(outb_check_more);
+            free_lock(outb_write_more);
+            outb_write_more = NULL;
+        }
+
+        /* return for more decompression while last buffer is being written
+           and having its check value calculated -- we wait for those to finish
+           the next time this function is called */
+        return 0;
+    }
+#endif
+
+    /* if just one process or no threads, then do it without threads */
+    if (len) {
+        if (decode == 1)
+            writen(outd, buf, len);
+        out_check = CHECK(out_check, buf, len);
+        out_tot += len;
+    }
+    return 0;
+}
+
+/* inflate for decompression or testing -- decompress from ind to outd unless
+   decode != 1, in which case just test ind, and then also list if list != 0;
+   look for and decode multiple, concatenated gzip and/or zlib streams;
+   read and check the gzip, zlib, or zip trailer */
+local void infchk(void)
+{
+    int ret, cont;
+    unsigned long check, len;
+    z_stream strm;
+    unsigned tmp2;
+    unsigned long tmp4;
+    off_t clen;
+
+    cont = 0;
+    do {
+        /* header already read -- set up for decompression */
+        in_tot = in_left;               /* track compressed data length */
+        out_tot = 0;
+        out_check = CHECK(0L, Z_NULL, 0);
+        strm.zalloc = Z_NULL;
+        strm.zfree = Z_NULL;
+        strm.opaque = Z_NULL;
+        ret = inflateBackInit(&strm, 15, out_buf);
+        if (ret != Z_OK)
+            bail("not enough memory", "");
+
+        /* decompress, compute lengths and check value */
+        strm.avail_in = in_left;
+        strm.next_in = in_next;
+        ret = inflateBack(&strm, inb, NULL, outb, NULL);
+        if (ret != Z_STREAM_END)
+            bail("corrupted input -- invalid deflate data: ", in);
+        in_left = strm.avail_in;
+        in_next = strm.next_in;
+        inflateBackEnd(&strm);
+        outb(NULL, NULL, 0);        /* finish off final write and check */
+
+        /* compute compressed data length */
+        clen = in_tot - in_left;
+
+        /* read and check trailer */
+        if (form > 1) {             /* zip local trailer (if any) */
+            if (form == 3) {        /* data descriptor follows */
+                /* read original version of data descriptor */
+                zip_crc = GET4();
+                zip_clen = GET4();
+                zip_ulen = GET4();
+                if (in_eof)
+                    bail("corrupted zip entry -- missing trailer: ", in);
+
+                /* if crc doesn't match, try info-zip variant with sig */
+                if (zip_crc != out_check) {
+                    if (zip_crc != 0x08074b50UL || zip_clen != out_check)
+                        bail("corrupted zip entry -- crc32 mismatch: ", in);
+                    zip_crc = zip_clen;
+                    zip_clen = zip_ulen;
+                    zip_ulen = GET4();
+                }
+
+                /* handle incredibly rare cases where crc equals signature */
+                else if (zip_crc == 0x08074b50UL && zip_clen == zip_crc &&
+                         ((clen & LOW32) != zip_crc || zip_ulen == zip_crc)) {
+                    zip_crc = zip_clen;
+                    zip_clen = zip_ulen;
+                    zip_ulen = GET4();
+                }
+
+                /* if second length doesn't match, try 64-bit lengths */
+                if (zip_ulen != (out_tot & LOW32)) {
+                    zip_ulen = GET4();
+                    (void)GET4();
+                }
+                if (in_eof)
+                    bail("corrupted zip entry -- missing trailer: ", in);
+            }
+            if (zip_clen != (clen & LOW32) || zip_ulen != (out_tot & LOW32))
+                bail("corrupted zip entry -- length mismatch: ", in);
+            check = zip_crc;
+        }
+        else if (form == 1) {       /* zlib (big-endian) trailer */
+            check = (unsigned long)(GET()) << 24;
+            check += (unsigned long)(GET()) << 16;
+            check += (unsigned)(GET()) << 8;
+            check += GET();
+            if (in_eof)
+                bail("corrupted zlib stream -- missing trailer: ", in);
+            if (check != out_check)
+                bail("corrupted zlib stream -- adler32 mismatch: ", in);
+        }
+        else {                      /* gzip trailer */
+            check = GET4();
+            len = GET4();
+            if (in_eof)
+                bail("corrupted gzip stream -- missing trailer: ", in);
+            if (check != out_check)
+                bail("corrupted gzip stream -- crc32 mismatch: ", in);
+            if (len != (out_tot & LOW32))
+                bail("corrupted gzip stream -- length mismatch: ", in);
+        }
+
+        /* show file information if requested */
+        if (list) {
+            in_tot = clen;
+            show_info(8, check, out_tot, cont);
+            cont = 1;
+        }
+
+        /* if a gzip or zlib entry follows a gzip or zlib entry, decompress it
+           (don't replace saved header information from first entry) */
+    } while (form < 2 && (ret = get_header(0)) == 8 && form < 2);
+
+    /* gzip -cdf copies junk after gzip stream directly to output */
+    if (form < 2 && ret == -2 && force && pipeout && decode != 2 && !list)
+        cat();
+    else if (ret != -1 && form < 2)
+        complain("%s OK, has trailing junk which was ignored", in);
+}
+
+/* --- decompress Unix compress (LZW) input --- */
+
+/* memory for unlzw() --
+   the first 256 entries of prefix[] and suffix[] are never used, could
+   have offset the index, but it's faster to waste the memory */
+unsigned short prefix[65536];           /* index to LZW prefix string */
+unsigned char suffix[65536];            /* one-character LZW suffix */
+unsigned char match[65280 + 2];         /* buffer for reversed match */
+
+/* throw out what's left in the current bits byte buffer (this is a vestigial
+   aspect of the compressed data format derived from an implementation that
+   made use of a special VAX machine instruction!) */
+#define FLUSHCODE() \
+    do { \
+        left = 0; \
+        rem = 0; \
+        if (chunk > in_left) { \
+            chunk -= in_left; \
+            if (load() == 0) \
+                break; \
+            if (chunk > in_left) { \
+                chunk = in_left = 0; \
+                break; \
+            } \
+        } \
+        in_left -= chunk; \
+        in_next += chunk; \
+        chunk = 0; \
+    } while (0)
+
+/* Decompress a compress (LZW) file from ind to outd.  The compress magic
+   header (two bytes) has already been read and verified. */
+local void unlzw(void)
+{
+    int got;                    /* byte just read by GET() */
+    unsigned chunk;             /* bytes left in current chunk */
+    int left;                   /* bits left in rem */
+    unsigned rem;               /* unused bits from input */
+    int bits;                   /* current bits per code */
+    unsigned code;              /* code, table traversal index */
+    unsigned mask;              /* mask for current bits codes */
+    int max;                    /* maximum bits per code for this stream */
+    int flags;                  /* compress flags, then block compress flag */
+    unsigned end;               /* last valid entry in prefix/suffix tables */
+    unsigned temp;              /* current code */
+    unsigned prev;              /* previous code */
+    unsigned final;             /* last character written for previous code */
+    unsigned stack;             /* next position for reversed string */
+    unsigned outcnt;            /* bytes in output buffer */
+    unsigned char *p;
+
+    /* process remainder of compress header -- a flags byte */
+    out_tot = 0;
+    flags = GET();
+    if (in_eof)
+        bail("missing lzw data: ", in);
+    if (flags & 0x60)
+        bail("unknown lzw flags set: ", in);
+    max = flags & 0x1f;
+    if (max < 9 || max > 16)
+        bail("lzw bits out of range: ", in);
+    if (max == 9)                           /* 9 doesn't really mean 9 */
+        max = 10;
+    flags &= 0x80;                          /* true if block compress */
+
+    /* clear table */
+    bits = 9;
+    mask = 0x1ff;
+    end = flags ? 256 : 255;
+
+    /* set up: get first 9-bit code, which is the first decompressed byte, but
+       don't create a table entry until the next code */
+    got = GET();
+    if (in_eof)                             /* no compressed data is ok */
+        return;
+    final = prev = (unsigned)got;           /* low 8 bits of code */
+    got = GET();
+    if (in_eof || (got & 1) != 0)           /* missing a bit or code >= 256 */
+        bail("invalid lzw code: ", in);
+    rem = (unsigned)got >> 1;               /* remaining 7 bits */
+    left = 7;
+    chunk = bits - 2;                       /* 7 bytes left in this chunk */
+    out_buf[0] = (unsigned char)final;      /* write first decompressed byte */
+    outcnt = 1;
+
+    /* decode codes */
+    stack = 0;
+    for (;;) {
+        /* if the table will be full after this, increment the code size */
+        if (end >= mask && bits < max) {
+            FLUSHCODE();
+            bits++;
+            mask <<= 1;
+            mask++;
+        }
+
+        /* get a code of length bits */
+        if (chunk == 0)                     /* decrement chunk modulo bits */
+            chunk = bits;
+        code = rem;                         /* low bits of code */
+        got = GET();
+        if (in_eof) {                       /* EOF is end of compressed data */
+            /* write remaining buffered output */
+            out_tot += outcnt;
+            if (outcnt && decode == 1)
+                writen(outd, out_buf, outcnt);
+            return;
+        }
+        code += (unsigned)got << left;      /* middle (or high) bits of code */
+        left += 8;
+        chunk--;
+        if (bits > left) {                  /* need more bits */
+            got = GET();
+            if (in_eof)                     /* can't end in middle of code */
+                bail("invalid lzw code: ", in);
+            code += (unsigned)got << left;  /* high bits of code */
+            left += 8;
+            chunk--;
+        }
+        code &= mask;                       /* mask to current code length */
+        left -= bits;                       /* number of unused bits */
+        rem = (unsigned)got >> (8 - left);  /* unused bits from last byte */
+
+        /* process clear code (256) */
+        if (code == 256 && flags) {
+            FLUSHCODE();
+            bits = 9;                       /* initialize bits and mask */
+            mask = 0x1ff;
+            end = 255;                      /* empty table */
+            continue;                       /* get next code */
+        }
+
+        /* special code to reuse last match */
+        temp = code;                        /* save the current code */
+        if (code > end) {
+            /* Be picky on the allowed code here, and make sure that the code
+               we drop through (prev) will be a valid index so that random
+               input does not cause an exception.  The code != end + 1 check is
+               empirically derived, and not checked in the original uncompress
+               code.  If this ever causes a problem, that check could be safely
+               removed.  Leaving this check in greatly improves pigz's ability
+               to detect random or corrupted input after a compress header.
+               In any case, the prev > end check must be retained. */
+            if (code != end + 1 || prev > end)
+                bail("invalid lzw code: ", in);
+            match[stack++] = (unsigned char)final;
+            code = prev;
+        }
+
+        /* walk through linked list to generate output in reverse order */
+        p = match + stack;
+        while (code >= 256) {
+            *p++ = suffix[code];
+            code = prefix[code];
+        }
+        stack = p - match;
+        match[stack++] = (unsigned char)code;
+        final = code;
+
+        /* link new table entry */
+        if (end < mask) {
+            end++;
+            prefix[end] = (unsigned short)prev;
+            suffix[end] = (unsigned char)final;
+        }
+
+        /* set previous code for next iteration */
+        prev = temp;
+
+        /* write output in forward order */
+        while (stack > OUTSIZE - outcnt) {
+            while (outcnt < OUTSIZE)
+                out_buf[outcnt++] = match[--stack];
+            out_tot += outcnt;
+            if (decode == 1)
+                writen(outd, out_buf, outcnt);
+            outcnt = 0;
+        }
+        p = match + stack;
+        do {
+            out_buf[outcnt++] = *--p;
+        } while (p > match);
+        stack = 0;
+
+        /* loop for next code with final and prev as the last match, rem and
+           left provide the first 0..7 bits of the next code, end is the last
+           valid table entry */
+    }
+}
+
+/* --- file processing --- */
+
+/* extract file name from path */
+local char *justname(char *path)
+{
+    char *p;
+
+    p = path + strlen(path);
+    while (--p >= path)
+        if (*p == '/')
+            break;
+    return p + 1;
+}
+
+/* Copy file attributes, from -> to, as best we can.  This is best effort, so
+   no errors are reported.  The mode bits, including suid, sgid, and the sticky
+   bit are copied (if allowed), the owner's user id and group id are copied
+   (again if allowed), and the access and modify times are copied. */
+local void copymeta(char *from, char *to)
+{
+    struct stat st;
+    struct timeval times[2];
+
+    /* get all of from's Unix meta data, return if not a regular file */
+    if (stat(from, &st) != 0 || (st.st_mode & S_IFMT) != S_IFREG)
+        return;
+
+    /* set to's mode bits, ignore errors */
+    (void)chmod(to, st.st_mode & 07777);
+
+    /* copy owner's user and group, ignore errors */
+    (void)chown(to, st.st_uid, st.st_gid);
+
+    /* copy access and modify times, ignore errors */
+    times[0].tv_sec = st.st_atime;
+    times[0].tv_usec = 0;
+    times[1].tv_sec = st.st_mtime;
+    times[1].tv_usec = 0;
+    (void)utimes(to, times);
+}
+
+/* set the access and modify times of fd to t */
+local void touch(char *path, time_t t)
+{
+    struct timeval times[2];
+
+    times[0].tv_sec = t;
+    times[0].tv_usec = 0;
+    times[1].tv_sec = t;
+    times[1].tv_usec = 0;
+    (void)utimes(path, times);
+}
+
+/* process provided input file, or stdin if path is NULL -- process() can
+   call itself for recursive directory processing */
+local void process(char *path)
+{
+    int method = -1;                /* get_header() return value */
+    size_t len;                     /* length of base name (minus suffix) */
+    struct stat st;                 /* to get file type and mod time */
+    /* all compressed suffixes for decoding search, in length order */
+    static char *sufs[] = {".z", "-z", "_z", ".Z", ".gz", "-gz", ".zz", "-zz",
+                           ".zip", ".ZIP", ".tgz", NULL};
+
+    /* open input file with name in, descriptor ind -- set name and mtime */
+    if (path == NULL) {
+        strcpy(in, "<stdin>");
+        ind = 0;
+        name = NULL;
+        mtime = headis & 2 ?
+                (fstat(ind, &st) ? time(NULL) : st.st_mtime) : 0;
+        len = 0;
+    }
+    else {
+        /* set input file name (already set if recursed here) */
+        if (path != in) {
+            strncpy(in, path, sizeof(in));
+            if (in[sizeof(in) - 1])
+                bail("name too long: ", path);
+        }
+        len = strlen(in);
+
+        /* try to stat input file -- if not there and decoding, look for that
+           name with compressed suffixes */
+        if (lstat(in, &st)) {
+            if (errno == ENOENT && (list || decode)) {
+                char **try = sufs;
+                do {
+                    if (*try == NULL || len + strlen(*try) >= sizeof(in))
+                        break;
+                    strcpy(in + len, *try++);
+                    errno = 0;
+                } while (lstat(in, &st) && errno == ENOENT);
+            }
+#ifdef EOVERFLOW
+            if (errno == EOVERFLOW || errno == EFBIG)
+                bail(in,
+                    " too large -- not compiled with large file support");
+#endif
+            if (errno) {
+                in[len] = 0;
+                complain("%s does not exist -- skipping", in);
+                return;
+            }
+            len = strlen(in);
+        }
+
+        /* only process regular files, but allow symbolic links if -f,
+           recurse into directory if -r */
+        if ((st.st_mode & S_IFMT) != S_IFREG &&
+            (st.st_mode & S_IFMT) != S_IFLNK &&
+            (st.st_mode & S_IFMT) != S_IFDIR) {
+            complain("%s is a special file or device -- skipping", in);
+            return;
+        }
+        if ((st.st_mode & S_IFMT) == S_IFLNK && !force && !pipeout) {
+            complain("%s is a symbolic link -- skipping", in);
+            return;
+        }
+        if ((st.st_mode & S_IFMT) == S_IFDIR && !recurse) {
+            complain("%s is a directory -- skipping", in);
+            return;
+        }
+
+        /* recurse into directory (assumes Unix) */
+        if ((st.st_mode & S_IFMT) == S_IFDIR) {
+            char *roll, *item, *cut, *base, *bigger;
+            size_t len, hold;
+            DIR *here;
+            struct dirent *next;
+
+            /* accumulate list of entries (need to do this, since readdir()
+               behavior not defined if directory modified between calls) */
+            here = opendir(in);
+            if (here == NULL)
+                return;
+            hold = 512;
+            roll = malloc(hold);
+            if (roll == NULL)
+                bail("not enough memory", "");
+            *roll = 0;
+            item = roll;
+            while ((next = readdir(here)) != NULL) {
+                if (next->d_name[0] == 0 ||
+                    (next->d_name[0] == '.' && (next->d_name[1] == 0 ||
+                     (next->d_name[1] == '.' && next->d_name[2] == 0))))
+                    continue;
+                len = strlen(next->d_name) + 1;
+                if (item + len + 1 > roll + hold) {
+                    do {                    /* make roll bigger */
+                        hold <<= 1;
+                    } while (item + len + 1 > roll + hold);
+                    bigger = realloc(roll, hold);
+                    if (bigger == NULL) {
+                        free(roll);
+                        bail("not enough memory", "");
+                    }
+                    item = bigger + (item - roll);
+                    roll = bigger;
+                }
+                strcpy(item, next->d_name);
+                item += len;
+                *item = 0;
+            }
+            closedir(here);
+
+            /* run process() for each entry in the directory */
+            cut = base = in + strlen(in);
+            if (base > in && base[-1] != (unsigned char)'/') {
+                if ((size_t)(base - in) >= sizeof(in))
+                    bail("path too long", in);
+                *base++ = '/';
+            }
+            item = roll;
+            while (*item) {
+                strncpy(base, item, sizeof(in) - (base - in));
+                if (in[sizeof(in) - 1]) {
+                    strcpy(in + (sizeof(in) - 4), "...");
+                    bail("path too long: ", in);
+                }
+                process(in);
+                item += strlen(item) + 1;
+            }
+            *cut = 0;
+
+            /* release list of entries */
+            free(roll);
+            return;
+        }
+
+        /* don't compress .gz (or provided suffix) files, unless -f */
+        if (!(force || list || decode) && len >= strlen(sufx) &&
+                strcmp(in + len - strlen(sufx), sufx) == 0) {
+            complain("%s ends with %s -- skipping", in, sufx);
+            return;
+        }
+
+        /* only decompress over input file with compressed suffix */
+        if (decode && !pipeout) {
+            int suf = compressed_suffix(in);
+            if (suf == 0) {
+                complain("%s does not have compressed suffix -- skipping", in);
+                return;
+            }
+            len -= suf;
+        }
+
+        /* open input file */
+        ind = open(in, O_RDONLY, 0);
+        if (ind < 0)
+            bail("read error on ", in);
+
+        /* prepare gzip header information for compression */
+        name = headis & 1 ? justname(in) : NULL;
+        mtime = headis & 2 ? st.st_mtime : 0;
+    }
+    SET_BINARY_MODE(ind);
+
+    /* if decoding or testing, try to read gzip header */
+    hname = NULL;
+    if (decode) {
+        in_init();
+        method = get_header(1);
+        if (method != 8 && method != 256 &&
+                /* gzip -cdf acts like cat on uncompressed input */
+                !(method == -2 && force && pipeout && decode != 2 && !list)) {
+            RELEASE(hname);
+            if (ind != 0)
+                close(ind);
+            if (method != -1)
+                complain(method < 0 ? "%s is not compressed -- skipping" :
+                         "%s has unknown compression method -- skipping", in);
+            return;
+        }
+
+        /* if requested, test input file (possibly a special list) */
+        if (decode == 2) {
+            if (method == 8)
+                infchk();
+            else {
+                unlzw();
+                if (list) {
+                    in_tot -= 3;
+                    show_info(method, 0, out_tot, 0);
+                }
+            }
+            RELEASE(hname);
+            if (ind != 0)
+                close(ind);
+            return;
+        }
+    }
+
+    /* if requested, just list information about input file */
+    if (list) {
+        list_info();
+        RELEASE(hname);
+        if (ind != 0)
+            close(ind);
+        return;
+    }
+
+    /* create output file out, descriptor outd */
+    if (path == NULL || pipeout) {
+        /* write to stdout */
+        out = malloc(strlen("<stdout>") + 1);
+        if (out == NULL)
+            bail("not enough memory", "");
+        strcpy(out, "<stdout>");
+        outd = 1;
+        if (!decode && !force && isatty(outd))
+            bail("trying to write compressed data to a terminal",
+                 " (use -f to force)");
+    }
+    else {
+        char *to, *repl;
+
+        /* use header name for output when decompressing with -N */
+        to = in;
+        if (decode && (headis & 1) != 0 && hname != NULL) {
+            to = hname;
+            len = strlen(hname);
+        }
+
+        /* replace .tgx with .tar when decoding */
+        repl = decode && strcmp(to + len, ".tgz") ? "" : ".tar";
+
+        /* create output file and open to write */
+        out = malloc(len + (decode ? strlen(repl) : strlen(sufx)) + 1);
+        if (out == NULL)
+            bail("not enough memory", "");
+        memcpy(out, to, len);
+        strcpy(out + len, decode ? repl : sufx);
+        outd = open(out, O_CREAT | O_TRUNC | O_WRONLY |
+                         (force ? 0 : O_EXCL), 0600);
+
+        /* if exists and not -f, give user a chance to overwrite */
+        if (outd < 0 && errno == EEXIST && isatty(0) && verbosity) {
+            int ch, reply;
+
+            fprintf(stderr, "%s exists -- overwrite (y/n)? ", out);
+            fflush(stderr);
+            reply = -1;
+            do {
+                ch = getchar();
+                if (reply < 0 && ch != ' ' && ch != '\t')
+                    reply = ch == 'y' || ch == 'Y' ? 1 : 0;
+            } while (ch != EOF && ch != '\n' && ch != '\r');
+            if (reply == 1)
+                outd = open(out, O_CREAT | O_TRUNC | O_WRONLY,
+                            0600);
+        }
+
+        /* if exists and no overwrite, report and go on to next */
+        if (outd < 0 && errno == EEXIST) {
+            complain("%s exists -- skipping", out);
+            RELEASE(out);
+            RELEASE(hname);
+            if (ind != 0)
+                close(ind);
+            return;
+        }
+
+        /* if some other error, give up */
+        if (outd < 0)
+            bail("write error on ", out);
+    }
+    SET_BINARY_MODE(outd);
+    RELEASE(hname);
+
+    /* process ind to outd */
+    if (verbosity > 1)
+        fprintf(stderr, "%s to %s ", in, out);
+    if (decode) {
+        if (method == 8)
+            infchk();
+        else if (method == 256)
+            unlzw();
+        else
+            cat();
+    }
+#ifndef NOTHREAD
+    else if (procs > 1)
+        parallel_compress();
+#endif
+    else
+        single_compress(0);
+    if (verbosity > 1) {
+        putc('\n', stderr);
+        fflush(stderr);
+    }
+
+    /* finish up, copy attributes, set times, delete original */
+    if (ind != 0)
+        close(ind);
+    if (outd != 1) {
+        if (close(outd))
+            bail("write error on ", out);
+        outd = -1;              /* now prevent deletion on interrupt */
+        if (ind != 0) {
+            copymeta(in, out);
+            if (!keep)
+                unlink(in);
+        }
+        if (decode && (headis & 2) != 0 && stamp)
+            touch(out, stamp);
+    }
+    RELEASE(out);
+}
+
+local char *helptext[] = {
+"Usage: pigz [options] [files ...]",
+"  will compress files in place, adding the suffix '.gz'.  If no files are",
+#ifdef NOTHREAD
+"  specified, stdin will be compressed to stdout.  pigz does what gzip does.",
+#else
+"  specified, stdin will be compressed to stdout.  pigz does what gzip does,",
+"  but spreads the work over multiple processors and cores when compressing.",
+#endif
+"",
+"Options:",
+"  -0 to -9, --fast, --best   Compression levels, --fast is -1, --best is -9",
+"  -b, --blocksize mmm  Set compression block size to mmmK (default 128K)",
+"  -c, --stdout         Write all processed output to stdout (won't delete)",
+"  -d, --decompress     Decompress the compressed input",
+"  -f, --force          Force overwrite, compress .gz, links, and to terminal",
+"  -h, --help           Display a help screen and quit",
+"  -i, --independent    Compress blocks independently for damage recovery",
+"  -k, --keep           Do not delete original file after processing",
+"  -K, --zip            Compress to PKWare zip (.zip) single entry format",
+"  -l, --list           List the contents of the compressed input",
+"  -L, --license        Display the pigz license and quit",
+"  -n, --no-name        Do not store or restore file name in/from header",
+"  -N, --name           Store/restore file name and mod time in/from header",
+#ifndef NOTHREAD
+"  -p, --processes n    Allow up to n compression threads (default is the",
+"                       number of online processors, or 8 if unknown)",
+#endif
+"  -q, --quiet          Print no messages, even on error",
+"  -r, --recursive      Process the contents of all subdirectories",
+"  -R, --rsyncable      Input-determined block locations for rsync",
+"  -S, --suffix .sss    Use suffix .sss instead of .gz (for compression)",
+"  -t, --test           Test the integrity of the compressed input",
+"  -T, --no-time        Do not store or restore mod time in/from header",
+#ifdef DEBUG
+"  -v, --verbose        Provide more verbose output (-vv to debug)",
+#else
+"  -v, --verbose        Provide more verbose output",
+#endif
+"  -V  --version        Show the version of pigz",
+"  -z, --zlib           Compress to zlib (.zz) instead of gzip format",
+"  --                   All arguments after \"--\" are treated as files"
+};
+
+/* display the help text above */
+local void help(void)
+{
+    int n;
+
+    if (verbosity == 0)
+        return;
+    for (n = 0; n < (int)(sizeof(helptext) / sizeof(char *)); n++)
+        fprintf(stderr, "%s\n", helptext[n]);
+    fflush(stderr);
+    exit(0);
+}
+
+#ifndef NOTHREAD
+
+/* try to determine the number of processors */
+local int nprocs(int n)
+{
+#  ifdef _SC_NPROCESSORS_ONLN
+    n = (int)sysconf(_SC_NPROCESSORS_ONLN);
+#  else
+#    ifdef _SC_NPROC_ONLN
+    n = (int)sysconf(_SC_NPROC_ONLN);
+#    else
+#      ifdef __hpux
+    struct pst_dynamic psd;
+
+    if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) != -1)
+        n = psd.psd_proc_cnt;
+#      endif
+#    endif
+#  endif
+    return n;
+}
+
+#endif
+
+/* set option defaults */
+local void defaults(void)
+{
+    level = Z_DEFAULT_COMPRESSION;
+#ifdef NOTHREAD
+    procs = 1;
+#else
+    procs = nprocs(8);
+#endif
+    size = 131072UL;
+    rsync = 0;                      /* don't do rsync blocking */
+    setdict = 1;                    /* initialize dictionary each thread */
+    verbosity = 1;                  /* normal message level */
+    headis = 3;                     /* store/restore name and timestamp */
+    pipeout = 0;                    /* don't force output to stdout */
+    sufx = ".gz";                   /* compressed file suffix */
+    decode = 0;                     /* compress */
+    list = 0;                       /* compress */
+    keep = 0;                       /* delete input file once compressed */
+    force = 0;                      /* don't overwrite, don't compress links */
+    recurse = 0;                    /* don't go into directories */
+    form = 0;                       /* use gzip format */
+}
+
+/* long options conversion to short options */
+local char *longopts[][2] = {
+    {"LZW", "Z"}, {"ascii", "a"}, {"best", "9"}, {"bits", "Z"},
+    {"blocksize", "b"}, {"decompress", "d"}, {"fast", "1"}, {"force", "f"},
+    {"help", "h"}, {"independent", "i"}, {"keep", "k"}, {"license", "L"},
+    {"list", "l"}, {"name", "N"}, {"no-name", "n"}, {"no-time", "T"},
+    {"processes", "p"}, {"quiet", "q"}, {"recursive", "r"}, {"rsyncable", "R"},
+    {"silent", "q"}, {"stdout", "c"}, {"suffix", "S"}, {"test", "t"},
+    {"to-stdout", "c"}, {"uncompress", "d"}, {"verbose", "v"},
+    {"version", "V"}, {"zip", "K"}, {"zlib", "z"}};
+#define NLOPTS (sizeof(longopts) / (sizeof(char *) << 1))
+
+/* either new buffer size, new compression level, or new number of processes --
+   get rid of old buffers and threads to force the creation of new ones with
+   the new settings */
+local void new_opts(void)
+{
+    single_compress(1);
+#ifndef NOTHREAD
+    finish_jobs();
+#endif
+}
+
+/* verify that arg is only digits, and if so, return the decimal value */
+local size_t num(char *arg)
+{
+    char *str = arg;
+    size_t val = 0;
+
+    if (*str == 0)
+        bail("internal error: empty parameter", "");
+    do {
+        if (*str < '0' || *str > '9')
+            bail("invalid numeric parameter: ", arg);
+        val = val * 10 + (*str - '0');
+        /* %% need to detect overflow here */
+    } while (*++str);
+    return val;
+}
+
+/* process an option, return true if a file name and not an option */
+local int option(char *arg)
+{
+    static int get = 0;     /* if not zero, look for option parameter */
+    char bad[3] = "-X";     /* for error messages (X is replaced) */
+
+    /* if no argument or dash option, check status of get */
+    if (get && (arg == NULL || *arg == '-')) {
+        bad[1] = "bpS"[get - 1];
+        bail("missing parameter after ", bad);
+    }
+    if (arg == NULL)
+        return 0;
+
+    /* process long option or short options */
+    if (*arg == '-') {
+        /* a single dash will be interpreted as stdin */
+        if (*++arg == 0)
+            return 1;
+
+        /* process long option (fall through with equivalent short option) */
+        if (*arg == '-') {
+            int j;
+
+            arg++;
+            for (j = NLOPTS - 1; j >= 0; j--)
+                if (strcmp(arg, longopts[j][0]) == 0) {
+                    arg = longopts[j][1];
+                    break;
+                }
+            if (j < 0)
+                bail("invalid option: ", arg - 2);
+        }
+
+        /* process short options (more than one allowed after dash) */
+        do {
+            /* if looking for a parameter, don't process more single character
+               options until we have the parameter */
+            if (get) {
+                if (get == 3)
+                    bail("invalid usage: -s must be followed by space", "");
+                break;      /* allow -pnnn and -bnnn, fall to parameter code */
+            }
+
+            /* process next single character option */
+            bad[1] = *arg;
+            switch (*arg) {
+            case '0': case '1': case '2': case '3': case '4':
+            case '5': case '6': case '7': case '8': case '9':
+                level = *arg - '0';
+                new_opts();
+                break;
+            case 'K':  form = 2;  sufx = ".zip";  break;
+            case 'L':
+                fputs(VERSION, stderr);
+                fputs("Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012"
+                      " Mark Adler\n",
+                      stderr);
+                fputs("Subject to the terms of the zlib license.\n",
+                      stderr);
+                fputs("No warranty is provided or implied.\n", stderr);
+                exit(0);
+            case 'N':  headis = 3;  break;
+            case 'T':  headis &= ~2;  break;
+            case 'R':  rsync = 1;  break;
+            case 'S':  get = 3;  break;
+            case 'V':  fputs(VERSION, stderr);  exit(0);
+            case 'Z':
+                bail("invalid option: LZW output not supported: ", bad);
+            case 'a':
+                bail("invalid option: ascii conversion not supported: ", bad);
+            case 'b':  get = 1;  break;
+            case 'c':  pipeout = 1;  break;
+            case 'd':  decode = 1;  headis = 0;  break;
+            case 'f':  force = 1;  break;
+            case 'h':  help();  break;
+            case 'i':  setdict = 0;  break;
+            case 'k':  keep = 1;  break;
+            case 'l':  list = 1;  break;
+            case 'n':  headis &= ~1;  break;
+            case 'p':  get = 2;  break;
+            case 'q':  verbosity = 0;  break;
+            case 'r':  recurse = 1;  break;
+            case 't':  decode = 2;  break;
+            case 'v':  verbosity++;  break;
+            case 'z':  form = 1;  sufx = ".zz";  break;
+            default:
+                bail("invalid option: ", bad);
+            }
+        } while (*++arg);
+        if (*arg == 0)
+            return 0;
+    }
+
+    /* process option parameter for -b, -p, or -S */
+    if (get) {
+        size_t n;
+
+        if (get == 1) {
+            n = num(arg);
+            size = n << 10;                     /* chunk size */
+            if (size < DICT)
+                bail("block size too small (must be >= 32K)", "");
+            if (n != size >> 10 ||
+                OUTPOOL(size) < size ||
+                (ssize_t)OUTPOOL(size) < 0 ||
+                size > (1UL << 22))
+                bail("block size too large: ", arg);
+            new_opts();
+        }
+        else if (get == 2) {
+            n = num(arg);
+            procs = (int)n;                     /* # processes */
+            if (procs < 1)
+                bail("invalid number of processes: ", arg);
+            if ((size_t)procs != n || INBUFS(procs) < 1)
+                bail("too many processes: ", arg);
+#ifdef NOTHREAD
+            if (procs > 1)
+                bail("compiled without threads", "");
+#endif
+            new_opts();
+        }
+        else if (get == 3)
+            sufx = arg;                         /* gz suffix */
+        get = 0;
+        return 0;
+    }
+
+    /* neither an option nor parameter */
+    return 1;
+}
+
+/* catch termination signal */
+local void cut_short(int sig)
+{
+    (void)sig;
+    Trace(("termination by user"));
+    if (outd != -1 && out != NULL)
+        unlink(out);
+    log_dump();
+    _exit(1);
+}
+
+/* Process arguments, compress in the gzip format.  Note that procs must be at
+   least two in order to provide a dictionary in one work unit for the other
+   work unit, and that size must be at least 32K to store a full dictionary. */
+int main(int argc, char **argv)
+{
+    int n;                          /* general index */
+    int noop;                       /* true to suppress option decoding */
+    unsigned long done;             /* number of named files processed */
+    char *opts, *p;                 /* environment default options, marker */
+
+    /* save pointer to program name for error messages */
+    p = strrchr(argv[0], '/');
+    p = p == NULL ? argv[0] : p + 1;
+    prog = *p ? p : "pigz";
+
+    /* prepare for interrupts and logging */
+    signal(SIGINT, cut_short);
+#ifndef NOTHREAD
+    yarn_prefix = prog;             /* prefix for yarn error messages */
+    yarn_abort = cut_short;         /* call on thread error */
+#endif
+#ifdef DEBUG
+    gettimeofday(&start, NULL);     /* starting time for log entries */
+    log_init();                     /* initialize logging */
+#endif
+
+    /* set all options to defaults */
+    defaults();
+
+    /* process user environment variable defaults in GZIP */
+    opts = getenv("GZIP");
+    if (opts != NULL) {
+        while (*opts) {
+            while (*opts == ' ' || *opts == '\t')
+                opts++;
+            p = opts;
+            while (*p && *p != ' ' && *p != '\t')
+                p++;
+            n = *p;
+            *p = 0;
+            if (option(opts))
+                bail("cannot provide files in GZIP environment variable", "");
+            opts = p + (n ? 1 : 0);
+        }
+        option(NULL);
+    }
+
+    /* process user environment variable defaults in PIGZ as well */
+    opts = getenv("PIGZ");
+    if (opts != NULL) {
+        while (*opts) {
+            while (*opts == ' ' || *opts == '\t')
+                opts++;
+            p = opts;
+            while (*p && *p != ' ' && *p != '\t')
+                p++;
+            n = *p;
+            *p = 0;
+            if (option(opts))
+                bail("cannot provide files in PIGZ environment variable", "");
+            opts = p + (n ? 1 : 0);
+        }
+        option(NULL);
+    }
+
+    /* decompress if named "unpigz" or "gunzip", to stdout if "*cat" */
+    if (strcmp(prog, "unpigz") == 0 || strcmp(prog, "gunzip") == 0)
+        decode = 1, headis = 0;
+    if ((n = strlen(prog)) > 2 && strcmp(prog + n - 3, "cat") == 0)
+        decode = 1, headis = 0, pipeout = 1;
+
+    /* if no arguments and compressed data to or from a terminal, show help */
+    if (argc < 2 && isatty(decode ? 0 : 1))
+        help();
+
+    /* process command-line arguments, no options after "--" */
+    done = noop = 0;
+    for (n = 1; n < argc; n++)
+        if (noop == 0 && strcmp(argv[n], "--") == 0) {
+            noop = 1;
+            option(NULL);
+        }
+        else if (noop || option(argv[n])) { /* true if file name, process it */
+            if (done == 1 && pipeout && !decode && !list && form > 1)
+                complain("warning: output will be concatenated zip files -- "
+                         "will not be able to extract");
+            process(strcmp(argv[n], "-") ? argv[n] : NULL);
+            done++;
+        }
+    option(NULL);
+
+    /* list stdin or compress stdin to stdout if no file names provided */
+    if (done == 0)
+        process(NULL);
+
+    /* done -- release resources, show log */
+    new_opts();
+    log_dump();
+    return warned ? 2 : 0;
+}
diff --git a/pigz/pigz.pdf b/pigz/pigz.pdf
new file mode 100644
index 0000000..3d382f4
--- /dev/null
+++ b/pigz/pigz.pdf
Binary files differ
diff --git a/pigz/pigz.spec b/pigz/pigz.spec
new file mode 100644
index 0000000..96daadb
--- /dev/null
+++ b/pigz/pigz.spec
@@ -0,0 +1,33 @@
+Summary: pigz is a parallel implementation of gzip which utilizes multiple cores
+Name: pigz
+Version: 2.2.5
+Release: 1
+Source0: %{name}-%{version}.tar.gz
+License: zlib
+Group: Applications/Tools
+Packager: Duncan Brown <duncan@duncanbrown.org>
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+URL: http://www.zlib.net/pigz
+
+%description
+pigz, which stands for parallel implementation of gzip, is a fully functional replacement for gzip that exploits multiple processors and multiple cores to the hilt when compressing data. pigz was written by Mark Adler, and uses the zlib and pthread libraries.
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+%prep
+mkdir -p $RPM_BUILD_ROOT
+
+%setup -q
+%build
+make
+mkdir -p ${RPM_BUILD_ROOT}/usr/bin
+mkdir -p ${RPM_BUILD_ROOT}/usr/man/man1
+mv pigz unpigz ${RPM_BUILD_ROOT}/usr/bin
+mv pigz.1 ${RPM_BUILD_ROOT}/usr/man/man1
+
+%files
+%defattr(-,root,root)
+/usr/bin/pigz
+/usr/bin/unpigz
+%doc
+/usr/man/man1/pigz.1
diff --git a/pigz/yarn.c b/pigz/yarn.c
new file mode 100644
index 0000000..5e557f3
--- /dev/null
+++ b/pigz/yarn.c
@@ -0,0 +1,375 @@
+/* yarn.c -- generic thread operations implemented using pthread functions
+ * Copyright (C) 2008, 2012 Mark Adler
+ * Version 1.3  13 Jan 2012  Mark Adler
+ * For conditions of distribution and use, see copyright notice in yarn.h
+ */
+
+/* Basic thread operations implemented using the POSIX pthread library.  All
+   pthread references are isolated within this module to allow alternate
+   implementations with other thread libraries.  See yarn.h for the description
+   of these operations. */
+
+/* Version history:
+   1.0    19 Oct 2008  First version
+   1.1    26 Oct 2008  No need to set the stack size -- remove
+                       Add yarn_abort() function for clean-up on error exit
+   1.2    19 Dec 2011  (changes reversed in 1.3)
+   1.3    13 Jan 2012  Add large file #define for consistency with pigz.c
+                       Update thread portability #defines per IEEE 1003.1-2008
+                       Fix documentation in yarn.h for yarn_prefix
+ */
+
+/* for thread portability */
+#define _XOPEN_SOURCE 700
+#define _POSIX_C_SOURCE 200809L
+#define _THREAD_SAFE
+
+/* use large file functions if available */
+#define _FILE_OFFSET_BITS 64
+
+/* external libraries and entities referenced */
+#include <stdio.h>      /* fprintf(), stderr */
+#include <stdlib.h>     /* exit(), malloc(), free(), NULL */
+#include <pthread.h>    /* pthread_t, pthread_create(), pthread_join(), */
+    /* pthread_attr_t, pthread_attr_init(), pthread_attr_destroy(),
+       PTHREAD_CREATE_JOINABLE, pthread_attr_setdetachstate(),
+       pthread_self(), pthread_equal(),
+       pthread_mutex_t, PTHREAD_MUTEX_INITIALIZER, pthread_mutex_init(),
+       pthread_mutex_lock(), pthread_mutex_unlock(), pthread_mutex_destroy(),
+       pthread_cond_t, PTHREAD_COND_INITIALIZER, pthread_cond_init(),
+       pthread_cond_broadcast(), pthread_cond_wait(), pthread_cond_destroy() */
+#include <errno.h>      /* ENOMEM, EAGAIN, EINVAL */
+
+/* interface definition */
+#include "yarn.h"
+
+/* constants */
+#define local static            /* for non-exported functions and globals */
+
+/* error handling external globals, resettable by application */
+char *yarn_prefix = "yarn";
+void (*yarn_abort)(int) = NULL;
+
+
+/* immediately exit -- use for errors that shouldn't ever happen */
+local void fail(int err)
+{
+    fprintf(stderr, "%s: %s (%d) -- aborting\n", yarn_prefix,
+            err == ENOMEM ? "out of memory" : "internal pthread error", err);
+    if (yarn_abort != NULL)
+        yarn_abort(err);
+    exit(err == ENOMEM || err == EAGAIN ? err : EINVAL);
+}
+
+/* memory handling routines provided by user -- if none are provided, malloc()
+   and free() are used, which are therefore assumed to be thread-safe */
+typedef void *(*malloc_t)(size_t);
+typedef void (*free_t)(void *);
+local malloc_t my_malloc_f = malloc;
+local free_t my_free = free;
+
+/* use user-supplied allocation routines instead of malloc() and free() */
+void yarn_mem(malloc_t lease, free_t vacate)
+{
+    my_malloc_f = lease;
+    my_free = vacate;
+}
+
+/* memory allocation that cannot fail (from the point of view of the caller) */
+local void *my_malloc(size_t size)
+{
+    void *block;
+
+    if ((block = my_malloc_f(size)) == NULL)
+        fail(ENOMEM);
+    return block;
+}
+
+/* -- lock functions -- */
+
+struct lock_s {
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    long value;
+};
+
+lock *new_lock(long initial)
+{
+    int ret;
+    lock *bolt;
+
+    bolt = my_malloc(sizeof(struct lock_s));
+    if ((ret = pthread_mutex_init(&(bolt->mutex), NULL)) ||
+        (ret = pthread_cond_init(&(bolt->cond), NULL)))
+        fail(ret);
+    bolt->value = initial;
+    return bolt;
+}
+
+void possess(lock *bolt)
+{
+    int ret;
+
+    if ((ret = pthread_mutex_lock(&(bolt->mutex))) != 0)
+        fail(ret);
+}
+
+void release(lock *bolt)
+{
+    int ret;
+
+    if ((ret = pthread_mutex_unlock(&(bolt->mutex))) != 0)
+        fail(ret);
+}
+
+void twist(lock *bolt, enum twist_op op, long val)
+{
+    int ret;
+
+    if (op == TO)
+        bolt->value = val;
+    else if (op == BY)
+        bolt->value += val;
+    if ((ret = pthread_cond_broadcast(&(bolt->cond))) ||
+        (ret = pthread_mutex_unlock(&(bolt->mutex))))
+        fail(ret);
+}
+
+#define until(a) while(!(a))
+
+void wait_for(lock *bolt, enum wait_op op, long val)
+{
+    int ret;
+
+    switch (op) {
+    case TO_BE:
+        until (bolt->value == val)
+            if ((ret = pthread_cond_wait(&(bolt->cond), &(bolt->mutex))) != 0)
+                fail(ret);
+        break;
+    case NOT_TO_BE:
+        until (bolt->value != val)
+            if ((ret = pthread_cond_wait(&(bolt->cond), &(bolt->mutex))) != 0)
+                fail(ret);
+        break;
+    case TO_BE_MORE_THAN:
+        until (bolt->value > val)
+            if ((ret = pthread_cond_wait(&(bolt->cond), &(bolt->mutex))) != 0)
+                fail(ret);
+        break;
+    case TO_BE_LESS_THAN:
+        until (bolt->value < val)
+            if ((ret = pthread_cond_wait(&(bolt->cond), &(bolt->mutex))) != 0)
+                fail(ret);
+    }
+}
+
+long peek_lock(lock *bolt)
+{
+    return bolt->value;
+}
+
+void free_lock(lock *bolt)
+{
+    int ret;
+    if ((ret = pthread_cond_destroy(&(bolt->cond))) ||
+        (ret = pthread_mutex_destroy(&(bolt->mutex))))
+        fail(ret);
+    my_free(bolt);
+}
+
+/* -- thread functions (uses lock functions above) -- */
+
+struct thread_s {
+    pthread_t id;
+    int done;                   /* true if this thread has exited */
+    thread *next;               /* for list of all launched threads */
+};
+
+/* list of threads launched but not joined, count of threads exited but not
+   joined (incremented by ignition() just before exiting) */
+local lock threads_lock = {
+    PTHREAD_MUTEX_INITIALIZER,
+    PTHREAD_COND_INITIALIZER,
+    0                           /* number of threads exited but not joined */
+};
+local thread *threads = NULL;       /* list of extant threads */
+
+/* structure in which to pass the probe and its payload to ignition() */
+struct capsule {
+    void (*probe)(void *);
+    void *payload;
+};
+
+/* mark the calling thread as done and alert join_all() */
+local void reenter(void *dummy)
+{
+    thread *match, **prior;
+    pthread_t me;
+
+    (void)dummy;
+
+    /* find this thread in the threads list by matching the thread id */
+    me = pthread_self();
+    possess(&(threads_lock));
+    prior = &(threads);
+    while ((match = *prior) != NULL) {
+        if (pthread_equal(match->id, me))
+            break;
+        prior = &(match->next);
+    }
+    if (match == NULL)
+        fail(EINVAL);
+
+    /* mark this thread as done and move it to the head of the list */
+    match->done = 1;
+    if (threads != match) {
+        *prior = match->next;
+        match->next = threads;
+        threads = match;
+    }
+
+    /* update the count of threads to be joined and alert join_all() */
+    twist(&(threads_lock), BY, +1);
+}
+
+/* all threads go through this routine so that just before the thread exits,
+   it marks itself as done in the threads list and alerts join_all() so that
+   the thread resources can be released -- use cleanup stack so that the
+   marking occurs even if the thread is cancelled */
+local void *ignition(void *arg)
+{
+    struct capsule *capsule = arg;
+
+    /* run reenter() before leaving */
+    pthread_cleanup_push(reenter, NULL);
+
+    /* execute the requested function with argument */
+    capsule->probe(capsule->payload);
+    my_free(capsule);
+
+    /* mark this thread as done and let join_all() know */
+    pthread_cleanup_pop(1);
+
+    /* exit thread */
+    return NULL;
+}
+
+/* not all POSIX implementations create threads as joinable by default, so that
+   is made explicit here */
+thread *launch(void (*probe)(void *), void *payload)
+{
+    int ret;
+    thread *th;
+    struct capsule *capsule;
+    pthread_attr_t attr;
+
+    /* construct the requested call and argument for the ignition() routine
+       (allocated instead of automatic so that we're sure this will still be
+       there when ignition() actually starts up -- ignition() will free this
+       allocation) */
+    capsule = my_malloc(sizeof(struct capsule));
+    capsule->probe = probe;
+    capsule->payload = payload;
+
+    /* assure this thread is in the list before join_all() or ignition() looks
+       for it */
+    possess(&(threads_lock));
+
+    /* create the thread and call ignition() from that thread */
+    th = my_malloc(sizeof(struct thread_s));
+    if ((ret = pthread_attr_init(&attr)) ||
+        (ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)) ||
+        (ret = pthread_create(&(th->id), &attr, ignition, capsule)) ||
+        (ret = pthread_attr_destroy(&attr)))
+        fail(ret);
+
+    /* put the thread in the threads list for join_all() */
+    th->done = 0;
+    th->next = threads;
+    threads = th;
+    release(&(threads_lock));
+    return th;
+}
+
+void join(thread *ally)
+{
+    int ret;
+    thread *match, **prior;
+
+    /* wait for thread to exit and return its resources */
+    if ((ret = pthread_join(ally->id, NULL)) != 0)
+        fail(ret);
+
+    /* find the thread in the threads list */
+    possess(&(threads_lock));
+    prior = &(threads);
+    while ((match = *prior) != NULL) {
+        if (match == ally)
+            break;
+        prior = &(match->next);
+    }
+    if (match == NULL)
+        fail(EINVAL);
+
+    /* remove thread from list and update exited count, free thread */
+    if (match->done)
+        threads_lock.value--;
+    *prior = match->next;
+    release(&(threads_lock));
+    my_free(ally);
+}
+
+/* This implementation of join_all() only attempts to join threads that have
+   announced that they have exited (see ignition()).  When there are many
+   threads, this is faster than waiting for some random thread to exit while a
+   bunch of other threads have already exited. */
+int join_all(void)
+{
+    int ret, count;
+    thread *match, **prior;
+
+    /* grab the threads list and initialize the joined count */
+    count = 0;
+    possess(&(threads_lock));
+
+    /* do until threads list is empty */
+    while (threads != NULL) {
+        /* wait until at least one thread has reentered */
+        wait_for(&(threads_lock), NOT_TO_BE, 0);
+
+        /* find the first thread marked done (should be at or near the top) */
+        prior = &(threads);
+        while ((match = *prior) != NULL) {
+            if (match->done)
+                break;
+            prior = &(match->next);
+        }
+        if (match == NULL)
+            fail(EINVAL);
+
+        /* join the thread (will be almost immediate), remove from the threads
+           list, update the reenter count, and free the thread */
+        if ((ret = pthread_join(match->id, NULL)) != 0)
+            fail(ret);
+        threads_lock.value--;
+        *prior = match->next;
+        my_free(match);
+        count++;
+    }
+
+    /* let go of the threads list and return the number of threads joined */
+    release(&(threads_lock));
+    return count;
+}
+
+/* cancel and join the thread -- the thread will cancel when it gets to a file
+   operation, a sleep or pause, or a condition wait */
+void destruct(thread *off_course)
+{
+    int ret;
+
+    if ((ret = pthread_cancel(off_course->id)) != 0)
+        fail(ret);
+    join(off_course);
+}
diff --git a/pigz/yarn.h b/pigz/yarn.h
new file mode 100644
index 0000000..7e3f914
--- /dev/null
+++ b/pigz/yarn.h
@@ -0,0 +1,134 @@
+/* yarn.h -- generic interface for thread operations
+ * Copyright (C) 2008, 2011 Mark Adler
+ * Version 1.3  13 Jan 2012  Mark Adler
+ */
+
+/*
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the author be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Mark Adler
+  madler@alumni.caltech.edu
+ */
+
+/* Basic thread operations
+
+   This interface isolates the local operating system implementation of threads
+   from the application in order to facilitate platform independent use of
+   threads.  All of the implementation details are deliberately hidden.
+
+   Assuming adequate system resources and proper use, none of these functions
+   can fail.  As a result, any errors encountered will cause an exit() to be
+   executed.
+
+   These functions allow the simple launching and joining of threads, and the
+   locking of objects and synchronization of changes of objects.  The latter is
+   implemented with a single lock type that contains an integer value.  The
+   value can be ignored for simple exclusive access to an object, or the value
+   can be used to signal and wait for changes to an object.
+
+   -- Arguments --
+
+   thread *thread;          identifier for launched thread, used by join
+   void probe(void *);      pointer to function "probe", run when thread starts
+   void *payload;           single argument passed to the probe function
+   lock *lock;              a lock with a value -- used for exclusive access to
+                            an object and to synchronize threads waiting for
+                            changes to an object
+   long val;                value to set lock, increment lock, or wait for
+   int n;                   number of threads joined
+
+   -- Thread functions --
+
+   thread = launch(probe, payload) - launch a thread -- exit via probe() return
+   join(thread) - join a thread and by joining end it, waiting for the thread
+        to exit if it hasn't already -- will free the resources allocated by
+        launch() (don't try to join the same thread more than once)
+   n = join_all() - join all threads launched by launch() that are not joined
+        yet and free the resources allocated by the launches, usually to clean
+        up when the thread processing is done -- join_all() returns an int with
+        the count of the number of threads joined (join_all() should only be
+        called from the main thread, and should only be called after any calls
+        of join() have completed)
+   destruct(thread) - terminate the thread in mid-execution and join it
+        (depending on the implementation, the termination may not be immediate,
+        but may wait for the thread to execute certain thread or file i/o
+        operations)
+
+   -- Lock functions --
+
+   lock = new_lock(val) - create a new lock with initial value val (lock is
+        created in the released state)
+   possess(lock) - acquire exclusive possession of a lock, waiting if necessary
+   twist(lock, [TO | BY], val) - set lock to or increment lock by val, signal
+        all threads waiting on this lock and then release the lock -- must
+        possess the lock before calling (twist releases, so don't do a
+        release() after a twist() on the same lock)
+   wait_for(lock, [TO_BE | NOT_TO_BE | TO_BE_MORE_THAN | TO_BE_LESS_THAN], val)
+        - wait on lock value to be, not to be, be greater than, or be less than
+        val -- must possess the lock before calling, will possess the lock on
+        return but the lock is released while waiting to permit other threads
+        to use twist() to change the value and signal the change (so make sure
+        that the object is in a usable state when waiting)
+   release(lock) - release a possessed lock (do not try to release a lock that
+        the current thread does not possess)
+   val = peek_lock(lock) - return the value of the lock (assumes that lock is
+        already possessed, no possess or release is done by peek_lock())
+   free_lock(lock) - free the resources allocated by new_lock() (application
+        must assure that the lock is released before calling free_lock())
+
+   -- Memory allocation ---
+
+   yarn_mem(better_malloc, better_free) - set the memory allocation and free
+        routines for use by the yarn routines where the supplied routines have
+        the same interface and operation as malloc() and free(), and may be
+        provided in order to supply thread-safe memory allocation routines or
+        for any other reason -- by default malloc() and free() will be used
+
+   -- Error control --
+
+   yarn_prefix - a char pointer to a string that will be the prefix for any
+        error messages that these routines generate before exiting -- if not
+        changed by the application, "yarn" will be used
+   yarn_abort - an external function that will be executed when there is an
+        internal yarn error, due to out of memory or misuse -- this function
+        may exit to abort the application, or if it returns, the yarn error
+        handler will exit (set to NULL by default for no action)
+ */
+
+extern char *yarn_prefix;
+extern void (*yarn_abort)(int);
+
+void yarn_mem(void *(*)(size_t), void (*)(void *));
+
+typedef struct thread_s thread;
+thread *launch(void (*)(void *), void *);
+void join(thread *);
+int join_all(void);
+void destruct(thread *);
+
+typedef struct lock_s lock;
+lock *new_lock(long);
+void possess(lock *);
+void release(lock *);
+enum twist_op { TO, BY };
+void twist(lock *, enum twist_op, long);
+enum wait_op {
+    TO_BE, /* or */ NOT_TO_BE, /* that is the question */
+    TO_BE_MORE_THAN, TO_BE_LESS_THAN };
+void wait_for(lock *, enum wait_op, long);
+long peek_lock(lock *);
+void free_lock(lock *);
diff --git a/prebuilt/99SuperSUDaemon b/prebuilt/99SuperSUDaemon
new file mode 100755
index 0000000..de738c1
--- /dev/null
+++ b/prebuilt/99SuperSUDaemon
@@ -0,0 +1,2 @@
+#!/system/bin/sh
+/system/xbin/daemonsu --auto-daemon &
diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk
new file mode 100644
index 0000000..12d6024
--- /dev/null
+++ b/prebuilt/Android.mk
@@ -0,0 +1,291 @@
+LOCAL_PATH := $(call my-dir)
+
+RELINK := $(LOCAL_PATH)/relink.sh
+
+#dummy file to trigger required modules
+include $(CLEAR_VARS)
+LOCAL_MODULE := teamwin
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+
+# Manage list
+RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/dump_image
+RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/flash_image
+RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/erase_image
+ifneq ($(TW_USE_TOOLBOX), true)
+	RELINK_SOURCE_FILES += $(TARGET_OUT_OPTIONAL_EXECUTABLES)/busybox
+else
+	RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/mksh
+endif
+RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/pigz
+RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/dosfsck
+RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/dosfslabel
+RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/mkdosfs
+RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/e2fsck
+RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/mke2fs
+RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/tune2fs
+RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/linker
+#RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/twrpmtp
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libc.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcutils.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcorkscrew.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libusbhost.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgccdemangle.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libutils.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libdl.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_blkid.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_com_err.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_e2p.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2fs.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_profile.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_uuid.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libpng.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/liblog.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libm.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libstdc++.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libz.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libminuitwrp.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libminadbd.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libminzip.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmtdutils.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libtar.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libblkid.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmmcutils.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbmlutils.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libflashutils.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libstlport.so
+#RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmincrypt.so
+RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/toolbox
+ifneq ($(TW_OEM_BUILD),true)
+    RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/twrp
+else
+    TW_EXCLUDE_MTP := true
+endif
+ifneq ($(TW_EXCLUDE_MTP), true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libtwrpmtp.so
+endif
+ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext4_utils.so
+endif
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libaosprecovery.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libjpeg.so
+ifeq ($(TWHAVE_SELINUX), true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libselinux.so
+    ifneq ($(TARGET_USERIMAGES_USE_EXT4), true)
+        RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext4_utils.so
+    endif
+endif
+ifeq ($(BUILD_ID), GINGERBREAD)
+    TW_NO_EXFAT := true
+endif
+ifneq ($(TW_NO_EXFAT), true)
+    RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/mkexfatfs
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libexfat.so
+else
+    TW_NO_EXFAT_FUSE := true
+endif
+ifneq ($(TW_NO_EXFAT_FUSE), true)
+    RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/exfat-fuse
+endif
+ifeq ($(TW_INCLUDE_BLOBPACK), true)
+    RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/blobpack
+endif
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+    RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/injecttwrp
+endif
+ifeq ($(TW_INCLUDE_DUMLOCK), true)
+    RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/htcdumlock
+endif
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcryptfsics.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so
+endif
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcryptfsjb.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so
+endif
+ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/make_ext4fs
+endif
+ifneq ($(wildcard system/core/libsparse/Android.mk),)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libsparse.so
+endif
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+    RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/openaes
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libopenaes.so
+endif
+ifeq ($(TARGET_USERIMAGES_USE_F2FS), true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/mkfs.f2fs
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/fsck.f2fs
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/fibmap.f2fs
+endif
+ifneq ($(wildcard system/core/reboot/Android.mk),)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/reboot
+endif
+ifneq ($(TW_DISABLE_TTF), true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libft2.so
+endif
+
+TWRP_AUTOGEN := $(intermediates)/teamwin
+
+GEN := $(intermediates)/teamwin
+$(GEN): $(RELINK)
+$(GEN): $(RELINK_SOURCE_FILES) $(call intermediates-dir-for,EXECUTABLES,recovery)/recovery
+	$(RELINK) $(TARGET_RECOVERY_ROOT_OUT)/sbin $(RELINK_SOURCE_FILES)
+
+LOCAL_GENERATED_SOURCES := $(GEN)
+LOCAL_SRC_FILES := teamwin $(GEN)
+include $(BUILD_PREBUILT)
+
+#fix_permissions
+include $(CLEAR_VARS)
+LOCAL_MODULE := fix_permissions.sh
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+#mke2fs.conf
+include $(CLEAR_VARS)
+LOCAL_MODULE := mke2fs.conf
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+ifeq ($(BOARD_HAS_NO_REAL_SDCARD),)
+	#parted
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := parted
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+endif
+
+# copy license file for OpenAES
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := ../openaes/LICENSE
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/license/openaes
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+endif
+
+ifeq ($(TW_INCLUDE_DUMLOCK), true)
+	#htcdumlock for /system for dumlock
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := htcdumlocksys
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/res/htcd
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+	#flash_image for /system for dumlock
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := flash_imagesys
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/res/htcd
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+	#dump_image for /system for dumlock
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := dump_imagesys
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/res/htcd
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+	#libbmlutils for /system for dumlock
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := libbmlutils.so
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/res/htcd
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+	#libflashutils for /system for dumlock
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := libflashutils.so
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/res/htcd
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+	#libmmcutils for /system for dumlock
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := libmmcutils.so
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/res/htcd
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+	#libmtdutils for /system for dumlock
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := libmtdutils.so
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/res/htcd
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+	#HTCDumlock.apk
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := HTCDumlock.apk
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/res/htcd
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+endif
+
+ifneq ($(TW_EXCLUDE_SUPERSU), true)
+	#su binary
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := su
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/supersu
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+       #install-recovery.sh
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := install-recovery.sh
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/supersu
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+       #99SuperSUDaemon
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := 99SuperSUDaemon
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/supersu
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+
+	#SuperSU special installer APK
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := Superuser.apk
+	LOCAL_MODULE_TAGS := eng
+	LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+	LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/supersu
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+endif
diff --git a/prebuilt/HTCDumlock.apk b/prebuilt/HTCDumlock.apk
new file mode 100644
index 0000000..8298197
--- /dev/null
+++ b/prebuilt/HTCDumlock.apk
Binary files differ
diff --git a/prebuilt/Superuser.apk b/prebuilt/Superuser.apk
new file mode 100644
index 0000000..99eb7d2
--- /dev/null
+++ b/prebuilt/Superuser.apk
Binary files differ
diff --git a/prebuilt/chattr b/prebuilt/chattr
new file mode 100755
index 0000000..12ccf86
--- /dev/null
+++ b/prebuilt/chattr
Binary files differ
diff --git a/prebuilt/dump_imagesys b/prebuilt/dump_imagesys
new file mode 100644
index 0000000..e1bdb62
--- /dev/null
+++ b/prebuilt/dump_imagesys
Binary files differ
diff --git a/prebuilt/fix_permissions.sh b/prebuilt/fix_permissions.sh
new file mode 100755
index 0000000..3723a12
--- /dev/null
+++ b/prebuilt/fix_permissions.sh
@@ -0,0 +1,484 @@
+#!/sbin/sh
+#
+# Warning: if you want to run this script in cm-recovery change the above to #!/sbin/sh
+#
+# fix_permissions - fixes permissions on Android data directories after upgrade
+# shade@chemlab.org
+#
+# original concept: http://blog.elsdoerfer.name/2009/05/25/android-fix-package-uid-mismatches/
+# implementation by: Cyanogen
+# improved by: ankn, smeat, thenefield, farmatito, rikupw, Kastro
+#
+# v1.1-v1.31r3 - many improvements and concepts from XDA developers.
+# v1.34 through v2.00 -  A lot of frustration [by Kastro]
+# v2.01	- Completely rewrote the script for SPEED, thanks for the input farmatito
+#         /data/data depth recursion is tweaked;
+#         fixed single mode;
+#         functions created for modularity;
+#         logging can be disabled via CLI for more speed;
+#         runtime computation added to end (Runtime: mins secs);
+#         progress (current # of total) added to screen;
+#         fixed CLI argument parsing, now you can have more than one option!;
+#         debug cli option;
+#         verbosity can be disabled via CLI option for less noise;;
+#         [by Kastro, (XDA: k4str0), twitter;mattcarver]
+# v2.02 - ignore com.htc.resources.apk if it exists and minor code cleanups,
+#         fix help text, implement simulated run (-s) [farmatito]
+# v2.03 - fixed chown group ownership output [Kastro]
+# v2.04 - replaced /system/sd with $SD_EXT_DIRECTORY [Firerat]
+VERSION="2.04"
+
+# Defaults
+DEBUG=0 # Debug off by default
+LOGGING=1 # Logging on by default
+VERBOSE=1 # Verbose on by default
+
+# Messages
+UID_MSG="Changing user ownership for:"
+GID_MSG="Changing group ownership for:"
+PERM_MSG="Changing permissions for:"
+
+# Programs needed
+ECHO="busybox echo"
+GREP="busybox grep"
+EGREP="busybox egrep"
+CAT="busybox cat"
+CHOWN="busybox chown"
+CHMOD="busybox chmod"
+MOUNT="busybox mount"
+UMOUNT="busybox umount"
+CUT="busybox cut"
+FIND="busybox find"
+LS="busybox ls"
+TR="busybox tr"
+TEE="busybox tee"
+TEST="busybox test"
+SED="busybox sed"
+RM="busybox rm"
+WC="busybox wc"
+EXPR="busybox expr"
+DATE="busybox date"
+
+# Initialise vars
+CODEPATH=""
+UID=""
+GID=""
+PACKAGE=""
+REMOVE=0
+NOSYSTEM=0
+ONLY_ONE=""
+SIMULATE=0
+SYSREMOUNT=0
+SYSMOUNT=0
+DATAMOUNT=0
+SYSSDMOUNT=0
+FP_STARTTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
+FP_STARTEPOCH=$( $DATE +%s )
+if $TEST "$SD_EXT_DIRECTORY" = ""; then
+    #check for mount point, /system/sd included in tests for backward compatibility
+    for MP in /sd-ext /system/sd;do
+        if $TEST -d $MP; then
+            SD_EXT_DIRECTORY=$MP
+            break
+        fi
+    done
+fi
+fp_usage()
+{
+   $ECHO "Usage $0 [OPTIONS] [APK_PATH]"
+   $ECHO "      -d         turn on debug"
+   $ECHO "      -f         fix only package APK_PATH"
+   $ECHO "      -l         disable logging for this run (faster)"
+   $ECHO "      -r         remove stale data directories"
+   $ECHO "                 of uninstalled packages while fixing permissions"
+   $ECHO "      -s         simulate only"
+   $ECHO "      -u         check only non-system directories"
+   $ECHO "      -v         disable verbosity for this run (less output)"
+   $ECHO "      -V         print version"
+   $ECHO "      -h         this help"
+}
+
+fp_parseargs()
+{
+   # Parse options
+   while $TEST $# -ne 0; do
+      case "$1" in
+         -d)
+            DEBUG=1
+         ;;
+         -f)
+            if $TEST $# -lt 2; then
+               $ECHO "$0: missing argument for option $1"
+               exit 1
+            else
+               if $TEST $( $ECHO $2 | $CUT -c1 ) != "-"; then
+                  ONLY_ONE=$2
+                  shift;
+               else
+                  $ECHO "$0: missing argument for option $1"
+                  exit 1
+               fi
+            fi
+         ;;
+         -r)
+            REMOVE=1
+         ;;
+         -s)
+            SIMULATE=1
+         ;;
+         -l)
+            if $TEST $LOGGING -eq 0; then
+               LOGGING=1
+            else
+               LOGGING=0
+            fi
+         ;;
+         -v)
+            if $TEST $VERBOSE -eq 0; then
+               VERBOSE=1
+            else
+               VERBOSE=0
+            fi
+         ;;
+         -u)
+            NOSYSTEM=1
+         ;;
+         -V)
+            $ECHO "$0 $VERSION"
+            exit 0
+         ;;
+         -h)
+            fp_usage
+            exit 0
+         ;;
+         -*)
+            $ECHO "$0: unknown option $1"
+            $ECHO
+            fp_usage
+            exit 1
+         ;;
+      esac
+      shift;
+   done
+}
+
+fp_print()
+{
+   MSG=$@
+   if $TEST $LOGGING -eq 1; then
+      $ECHO $MSG | $TEE -a $LOG_FILE
+   else
+      $ECHO $MSG
+   fi
+}
+
+fp_start()
+{
+   if $TEST $SIMULATE -eq 0 ; then
+      if $TEST $( $GREP -c " /system " "/proc/mounts" ) -ne 0; then
+         DEVICE=$( $GREP " /system " "/proc/mounts" | $CUT -d ' ' -f1 )
+         if $TEST $DEBUG -eq 1; then
+            fp_print "/system mounted on $DEVICE"
+         fi
+         if $TEST $( $GREP " /system " "/proc/mounts" | $GREP -c " ro " ) -ne 0; then
+            $MOUNT -o remount,rw $DEVICE /system
+            SYSREMOUNT=1
+         fi
+      else
+         $MOUNT /system > /dev/null 2>&1
+         SYSMOUNT=1
+      fi
+
+      if $TEST $( $GREP -c " /data " "/proc/mounts" ) -eq 0; then
+         $MOUNT /data > /dev/null 2>&1
+         DATAMOUNT=1
+      fi
+
+      if $TEST -e /dev/block/mmcblk0p2 && $TEST $( $GREP -c " $SD_EXT_DIRECTORY " "/proc/mounts" ) -eq 0; then
+         $MOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
+         SYSSDMOUNT=1
+      fi
+   fi
+   if $TEST $( $MOUNT | $GREP -c /sdcard ) -eq 0; then
+      LOG_FILE="/data/fix_permissions.log"
+   else
+      LOG_FILE="/sdcard/fix_permissions.log"
+   fi
+   if $TEST ! -e "$LOG_FILE"; then
+      > $LOG_FILE
+   fi
+
+   fp_print "$0 $VERSION started at $FP_STARTTIME"
+}
+
+fp_chown_uid()
+{
+   FP_OLDUID=$1
+   FP_UID=$2
+   FP_FILE=$3
+
+   #if user ownership doesn't equal then change them
+   if $TEST "$FP_OLDUID" != "$FP_UID"; then
+      if $TEST $VERBOSE -ne 0; then
+         fp_print "$UID_MSG $FP_FILE from '$FP_OLDUID' to '$FP_UID'"
+      fi
+      if $TEST $SIMULATE -eq 0; then
+         $CHOWN $FP_UID "$FP_FILE"
+      fi
+   fi
+}
+
+fp_chown_gid()
+{
+   FP_OLDGID=$1
+   FP_GID=$2
+   FP_FILE=$3
+
+   #if group ownership doesn't equal then change them
+   if $TEST "$FP_OLDGID" != "$FP_GID"; then
+      if $TEST $VERBOSE -ne 0; then
+         fp_print "$GID_MSG $FP_FILE from '$FP_OLDGID' to '$FP_GID'"
+      fi
+      if $TEST $SIMULATE -eq 0; then
+         $CHOWN :$FP_GID "$FP_FILE"
+      fi
+   fi
+}
+
+fp_chmod()
+{
+   FP_OLDPER=$1
+   FP_OLDPER=$( $ECHO $FP_OLDPER | cut -c2-10 )
+   FP_PERSTR=$2
+   FP_PERNUM=$3
+   FP_FILE=$4
+
+   #if the permissions are not equal
+   if $TEST "$FP_OLDPER" != "$FP_PERSTR"; then
+      if $TEST $VERBOSE -ne 0; then
+         fp_print "$PERM_MSG $FP_FILE from '$FP_OLDPER' to '$FP_PERSTR' ($FP_PERNUM)"
+      fi
+      #change the permissions
+      if $TEST $SIMULATE -eq 0; then
+         $CHMOD $FP_PERNUM "$FP_FILE"
+      fi
+   fi
+}
+
+fp_all()
+{
+   FP_NUMS=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $WC -l )
+   I=0
+   $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | while read all_line; do
+      I=$( $EXPR $I + 1 )
+      fp_package "$all_line" $I $FP_NUMS
+   done
+}
+
+fp_single()
+{
+   FP_SFOUND=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE | wc -l )
+   if $TEST $FP_SFOUND -gt 1; then
+      fp_print "Cannot perform single operation on $FP_SFOUND matched package(s)."
+      elif $TEST $FP_SFOUND = "" -o $FP_SFOUND -eq 0; then
+      fp_print "Could not find the package you specified in the packages.xml file."
+   else
+      FP_SPKG=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE )
+      fp_package "${FP_SPKG}" 1 1
+   fi
+}
+
+fp_package()
+{
+   pkgline=$1
+   curnum=$2
+   endnum=$3
+   CODEPATH=$( $ECHO $pkgline | $SED 's%.* codePath="\(.*\)".*%\1%' |  $CUT -d '"' -f1 )
+   PACKAGE=$( $ECHO $pkgline | $SED 's%.* name="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
+   UID=$( $ECHO $pkgline | $SED 's%.*serId="\(.*\)".*%\1%' |  $CUT -d '"' -f1 )
+   GID=$UID
+   APPDIR=$( $ECHO $CODEPATH | $SED 's%^\(.*\)/.*%\1%' )
+   APK=$( $ECHO $CODEPATH | $SED 's%^.*/\(.*\..*\)$%\1%' )
+
+   #debug
+   if $TEST $DEBUG -eq 1; then
+      fp_print "CODEPATH: $CODEPATH APPDIR: $APPDIR APK:$APK UID/GID:$UID:$GID"
+   fi
+
+   #check for existence of apk
+   if $TEST -e $CODEPATH;  then
+      fp_print "Processing ($curnum of $endnum): $PACKAGE..."
+
+      #lets get existing permissions of CODEPATH
+      OLD_UGD=$( $LS -ln "$CODEPATH" )
+      OLD_PER=$( $ECHO $OLD_UGD | $CUT -d ' ' -f1 )
+      OLD_UID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f3 )
+      OLD_GID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f4 )
+
+      #apk source dirs
+      if $TEST "$APPDIR" = "/system/app"; then
+         #skip system apps if set
+         if $TEST "$NOSYSTEM" = "1"; then
+            fp_print "***SKIPPING SYSTEM APP ($PACKAGE)!"
+            return
+         fi
+         fp_chown_uid $OLD_UID 0 "$CODEPATH"
+         fp_chown_gid $OLD_GID 0 "$CODEPATH"
+         fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
+         elif $TEST "$APPDIR" = "/data/app" || $TEST "$APPDIR" = "/sd-ext/app"; then
+         fp_chown_uid $OLD_UID 1000 "$CODEPATH"
+         fp_chown_gid $OLD_GID 1000 "$CODEPATH"
+         fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
+         elif $TEST "$APPDIR" = "/data/app-private" || $TEST "$APPDIR" = "/sd-ext/app-private"; then
+         fp_chown_uid $OLD_UID 1000 "$CODEPATH"
+         fp_chown_gid $OLD_GID $GID "$CODEPATH"
+         fp_chmod $OLD_PER "rw-r-----" 640 "$CODEPATH"
+      fi
+   else
+      fp_print "$CODEPATH does not exist ($curnum of $endnum). Reinstall..."
+      if $TEST $REMOVE -eq 1; then
+         if $TEST -d /data/data/$PACKAGE ; then
+            fp_print "Removing stale dir /data/data/$PACKAGE"
+            if $TEST $SIMULATE -eq 0 ; then
+               $RM -R /data/data/$PACKAGE
+            fi
+         fi
+      fi
+   fi
+
+   #the data/data for the package
+   if $TEST -d "/data/data/$PACKAGE"; then
+      #find all directories in /data/data/$PACKAGE
+      $FIND /data/data/$PACKAGE -type d -exec $LS -ldn {} \; | while read dataline; do
+         #get existing permissions of that directory
+         OLD_PER=$( $ECHO $dataline | $CUT -d ' ' -f1 )
+         OLD_UID=$( $ECHO $dataline | $CUT -d ' ' -f3 )
+         OLD_GID=$( $ECHO $dataline | $CUT -d ' ' -f4 )
+         FILEDIR=$( $ECHO $dataline | $CUT -d ' ' -f9 )
+         FOURDIR=$( $ECHO $FILEDIR | $CUT -d '/' -f5 )
+
+         #set defaults for iteration
+         ISLIB=0
+         REVPERM=755
+         REVPSTR="rwxr-xr-x"
+         REVUID=$UID
+         REVGID=$GID
+
+         if $TEST "$FOURDIR" = ""; then
+            #package directory, perms:755 owner:$UID:$GID
+            fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
+            elif $TEST "$FOURDIR" = "lib"; then
+            #lib directory, perms:755 owner:1000:1000
+            #lib files, perms:755 owner:1000:1000
+            ISLIB=1
+            REVPERM=755
+            REVPSTR="rwxr-xr-x"
+            REVUID=1000
+            REVGID=1000
+            fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
+            elif $TEST "$FOURDIR" = "shared_prefs"; then
+            #shared_prefs directories, perms:771 owner:$UID:$GID
+            #shared_prefs files, perms:660 owner:$UID:$GID
+            REVPERM=660
+            REVPSTR="rw-rw----"
+            fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
+            elif $TEST "$FOURDIR" = "databases"; then
+            #databases directories, perms:771 owner:$UID:$GID
+            #databases files, perms:660 owner:$UID:$GID
+            REVPERM=660
+            REVPSTR="rw-rw----"
+            fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
+            elif $TEST "$FOURDIR" = "cache"; then
+            #cache directories, perms:771 owner:$UID:$GID
+            #cache files, perms:600 owner:$UID:GID
+            REVPERM=600
+            REVPSTR="rw-------"
+            fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
+         else
+            #other directories, perms:771 owner:$UID:$GID
+            REVPERM=771
+            REVPSTR="rwxrwx--x"
+            fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
+         fi
+
+         #change ownership of directories matched
+         if $TEST "$ISLIB" = "1"; then
+            fp_chown_uid $OLD_UID 1000 "$FILEDIR"
+            fp_chown_gid $OLD_GID 1000 "$FILEDIR"
+         else
+            fp_chown_uid $OLD_UID $UID "$FILEDIR"
+            fp_chown_gid $OLD_GID $GID "$FILEDIR"
+         fi
+
+         #if any files exist in directory with improper permissions reset them
+         $FIND $FILEDIR -type f -maxdepth 1 ! -perm $REVPERM -exec $LS -ln {} \; | while read subline; do
+            OLD_PER=$( $ECHO $subline | $CUT -d ' ' -f1 )
+            SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
+            fp_chmod $OLD_PER $REVPSTR $REVPERM "$SUBFILE"
+         done
+
+         #if any files exist in directory with improper user reset them
+         $FIND $FILEDIR -type f -maxdepth 1 ! -user $REVUID -exec $LS -ln {} \; | while read subline; do
+            OLD_UID=$( $ECHO $subline | $CUT -d ' ' -f3 )
+            SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
+            fp_chown_uid $OLD_UID $REVUID "$SUBFILE"
+         done
+
+         #if any files exist in directory with improper group reset them
+         $FIND $FILEDIR -type f -maxdepth 1 ! -group $REVGID -exec $LS -ln {} \; | while read subline; do
+            OLD_GID=$( $ECHO $subline | $CUT -d ' ' -f4 )
+            SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
+            fp_chown_gid $OLD_GID $REVGID "$SUBFILE"
+         done
+      done
+   fi
+}
+
+date_diff()
+{
+   if $TEST $# -ne 2; then
+      FP_DDM="E"
+      FP_DDS="E"
+      return
+   fi
+   FP_DDD=$( $EXPR $2 - $1 )
+   FP_DDM=$( $EXPR $FP_DDD / 60 )
+   FP_DDS=$( $EXPR $FP_DDD % 60 )
+}
+
+fp_end()
+{
+   if $TEST $SYSREMOUNT -eq 1; then
+      $MOUNT -o remount,ro $DEVICE /system > /dev/null 2>&1
+   fi
+
+   if $TEST $SYSSDMOUNT -eq 1; then
+      $UMOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
+   fi
+
+   if $TEST $SYSMOUNT -eq 1; then
+      $UMOUNT /system > /dev/null 2>&1
+   fi
+
+   if $TEST $DATAMOUNT -eq 1; then
+      $UMOUNT /data > /dev/null 2>&1
+   fi
+
+   FP_ENDTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
+   FP_ENDEPOCH=$( $DATE +%s )
+
+   date_diff $FP_STARTEPOCH $FP_ENDEPOCH
+
+   fp_print "$0 $VERSION ended at $FP_ENDTIME (Runtime:${FP_DDM}m${FP_DDS}s)"
+}
+
+#MAIN SCRIPT
+
+fp_parseargs $@
+fp_start
+if $TEST "$ONLY_ONE" != "" -a "$ONLY_ONE" != "0" ; then
+   fp_single "$ONLY_ONE"
+else
+   fp_all
+fi
+fp_end
diff --git a/prebuilt/flash_imagesys b/prebuilt/flash_imagesys
new file mode 100644
index 0000000..a08d91d
--- /dev/null
+++ b/prebuilt/flash_imagesys
Binary files differ
diff --git a/prebuilt/htcdumlock b/prebuilt/htcdumlock
new file mode 100644
index 0000000..18068e5
--- /dev/null
+++ b/prebuilt/htcdumlock
Binary files differ
diff --git a/prebuilt/htcdumlocksys b/prebuilt/htcdumlocksys
new file mode 100644
index 0000000..c63725e
--- /dev/null
+++ b/prebuilt/htcdumlocksys
Binary files differ
diff --git a/prebuilt/install-recovery.sh b/prebuilt/install-recovery.sh
new file mode 100755
index 0000000..7c65d0e
--- /dev/null
+++ b/prebuilt/install-recovery.sh
@@ -0,0 +1,17 @@
+#!/system/bin/sh
+
+# If you're implementing this in a custom kernel/firmware,
+# I suggest you use a different script name, and add a service
+# to launch it from init.rc
+
+# Launches SuperSU in daemon mode only on Android 4.3+.
+# Nothing will happen on 4.2.x or older, unless SELinux+Enforcing.
+# If you want to force loading the daemon, use "--daemon" instead
+
+/system/xbin/daemonsu --auto-daemon &
+
+# Some apps like to run stuff from this script as well, that will
+# obviously break root - in your code, just search this file
+# for "install-recovery-2.sh", and if present, write there instead.
+
+/system/etc/install-recovery-2.sh
diff --git a/prebuilt/libbmlutils.so b/prebuilt/libbmlutils.so
new file mode 100644
index 0000000..48a3e3b
--- /dev/null
+++ b/prebuilt/libbmlutils.so
Binary files differ
diff --git a/prebuilt/libflashutils.so b/prebuilt/libflashutils.so
new file mode 100644
index 0000000..91fcde0
--- /dev/null
+++ b/prebuilt/libflashutils.so
Binary files differ
diff --git a/prebuilt/libmmcutils.so b/prebuilt/libmmcutils.so
new file mode 100644
index 0000000..5124876
--- /dev/null
+++ b/prebuilt/libmmcutils.so
Binary files differ
diff --git a/prebuilt/libmtdutils.so b/prebuilt/libmtdutils.so
new file mode 100644
index 0000000..1cbc61e
--- /dev/null
+++ b/prebuilt/libmtdutils.so
Binary files differ
diff --git a/prebuilt/mke2fs.conf b/prebuilt/mke2fs.conf
new file mode 100644
index 0000000..e231d7c
--- /dev/null
+++ b/prebuilt/mke2fs.conf
@@ -0,0 +1,17 @@
+[defaults]
+	base_features = sparse_super,filetype,resize_inode,dir_index,ext_attr
+	blocksize = 4096
+	inode_size = 256
+	inode_ratio = 16384
+
+[fs_types]
+	ext2 = {
+
+	}
+	ext3 = {
+		features = has_journal
+	}
+	ext4 = {
+		features = has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize
+		inode_size = 256
+	}
diff --git a/prebuilt/parted b/prebuilt/parted
new file mode 100755
index 0000000..bb3d432
--- /dev/null
+++ b/prebuilt/parted
Binary files differ
diff --git a/prebuilt/relink-binaries.sh b/prebuilt/relink-binaries.sh
new file mode 100755
index 0000000..3752853
--- /dev/null
+++ b/prebuilt/relink-binaries.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+process_file()
+{
+   src=$1
+   dst=$1-1 #/$(basename $2)
+   cp -f -p $src $dst
+
+   sed "s|/system/bin/linker\x0|/sbin/linker\x0\x0\x0\x0\x0\x0\x0|g" $dst | sed "s|/system/bin/sh\x0|/sbin/sh\x0\x0\x0\x0\x0\x0\x0|g" > $dst-mod
+   #sed "s|/sbin/linker\x0|/system/bin/linker\x0\x0\x0\x0\x0\x0\x0|g" $dst | sed "s|/sbin/sh\x0|/system/bin/sh\x0\x0\x0\x0\x0\x0\x0|g" > $dst-mod
+   rm $dst
+}
+
+
+dest=$1
+for ARG in $*
+do
+   process_file $dest $ARG
+done
\ No newline at end of file
diff --git a/prebuilt/relink.sh b/prebuilt/relink.sh
new file mode 100755
index 0000000..4e9445a
--- /dev/null
+++ b/prebuilt/relink.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+process_file()
+{
+    dst=$1/$(basename $2)
+    src=$2
+    if [ $dst == $src ]; then
+      cp -f -p $src $src.tmp
+      src=$2.tmp
+    else
+      cp -f -p $src $dst
+    fi
+
+    sed "s|/system/bin/linker\x0|/sbin/linker\x0\x0\x0\x0\x0\x0\x0|g" $src | sed "s|/system/bin/sh\x0|/sbin/sh\x0\x0\x0\x0\x0\x0\x0|g" > $dst
+
+    if [ $1 == $(dirname $2) ]; then
+      rm -f $src
+    fi
+}
+
+
+dest=$1
+shift 1
+for ARG in $*
+do
+    process_file $dest $ARG
+done
diff --git a/prebuilt/su b/prebuilt/su
new file mode 100755
index 0000000..9415427
--- /dev/null
+++ b/prebuilt/su
Binary files differ
diff --git a/prebuilt/teamwin b/prebuilt/teamwin
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/prebuilt/teamwin
@@ -0,0 +1 @@
+
diff --git a/prebuilt/twrp_fonts.py b/prebuilt/twrp_fonts.py
new file mode 100755
index 0000000..4392409
--- /dev/null
+++ b/prebuilt/twrp_fonts.py
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+# -*- coding: utf8 -*-
+import codecs,os,gzip,ctypes,ctypes.util,sys
+from struct import *
+from PIL import Image, ImageDraw, ImageFont
+
+# ====== Python script to convert TrueTypeFonts to TWRP's .dat format ======
+# This script was originally made by https://github.com/suky for his chinese version of TWRP
+# and then translated to English by feilplane at #twrp of irc.freenode.net.
+# However, it was not compatible with vanilla TWRP, so https://github.com/Tasssadar rewrote
+# most of it and it now has very little in common with the original script.
+
+class Reference():
+    def __init__(self, val):
+        self.__value = val
+
+    def get(self):
+        return self.__value
+
+    def set(self, val):
+        self.__value = val
+
+quiet = Reference(False)
+
+def log(text):
+    if not quiet.get():
+        sys.stdout.write(text)
+
+def write_data(f, width, height, offsets, data):
+    f.write(pack("<I", width))
+    f.write(pack("<I", height))
+    for off in offsets:
+        f.write(pack("<I", off))
+    f.write(data)
+
+if __name__ == "__main__":
+    fontsize = Reference(20)
+    out_fname = Reference("font.dat")
+    voffset = Reference(None)
+    padding = Reference(0)
+    font_fname = Reference(None)
+    preview = Reference(None)
+
+    arg_parser = [
+        ["-s", "--size=", fontsize, int],
+        ["-o", "--output=", out_fname, str],
+        ["-p", "--preview=", preview, str],
+        [None, "--padding=", padding, int],
+        ["-q", "--quiet", quiet, None],
+        [None, "--voffset=", voffset, int]
+    ]
+
+    argv = sys.argv
+    argc = len(argv)
+    i = 1
+    while i < argc:
+        arg = argv[i]
+        arg_next = argv[i+1] if i+1 < argc else None
+
+        if arg == "--help" or arg == "-h":
+            print ("This script converts TrueTypeFonts to .dat file for TWRP recovery.\n\n"
+                "Usage: %s [SWITCHES] [TRUETYPE FILE]\n\n"
+                "  -h, --help                   - print help\n"
+                "  -o, --output=[FILE]          - output file or '-' for stdout (default: font.dat)\n"
+                "  -p, --preview=[FILE]         - generate font preview to png file\n"
+                "  --padding=[PIXELS]           - horizontal padding around each character (default: 0)\n"
+                "  -q, --quiet                  - Do not print any output\n"
+                "  -s, --size=[SIZE IN PIXELS]  - specify font size in points (default: 20)\n"
+                "  --voffset=[PIXELS]           - vertical offset (default: font size*0.25)\n\n"
+                "Example:\n"
+                "  %s -s 40 -o ComicSans_40.dat -p preview.png ComicSans.ttf\n") % (
+                    sys.argv[0], sys.argv[0]
+                )
+            exit(0)
+
+        found = False
+        for p in arg_parser:
+            if p[0] and arg == p[0] and (arg_next or not p[3]):
+                if p[3]:
+                    p[2].set(p[3](arg_next))
+                else:
+                    p[2].set(True)
+                i += 1
+                found = True
+                break
+            elif p[1] and arg.startswith(p[1]):
+                if p[3]:
+                    p[2].set(p[3](arg[len(p[1]):]))
+                else:
+                    p[2].set(True)
+                found = True
+                break
+
+        if not found:
+            font_fname.set(arg)
+
+        i += 1
+
+    if not voffset.get():
+        voffset.set(int(fontsize.get()*0.25))
+
+    if out_fname.get() == "-":
+        quiet.set(True)
+
+    log("Loading font %s...\n" % font_fname.get())
+    font = ImageFont.truetype(font_fname.get(), fontsize.get(), 0, "utf-32be")
+    cwidth = 0
+    cheight = font.getsize('A')[1]
+    offsets = []
+    renders = []
+    data = bytes()
+
+    # temp Image and ImageDraw to get access to textsize
+    res = Image.new('L', (1, 1), 0)
+    res_draw = ImageDraw.Draw(res)
+
+    # Measure each character and render it to separate Image
+    log("Rendering characters...\n")
+    for i in range(32, 128):
+        w, h = res_draw.textsize(chr(i), font)
+        w += padding.get()*2
+        offsets.append(cwidth)
+        cwidth += w
+        if h > cheight:
+            cheight = h
+        ichr = Image.new('L', (w, cheight*2))
+        ichr_draw = ImageDraw.Draw(ichr)
+        ichr_draw.text((padding.get(), 0), chr(i), 255, font)
+        renders.append(ichr)
+
+    # Twice the height to account for under-the-baseline characters
+    cheight *= 2
+
+    # Create the result bitmap
+    log("Creating result bitmap...\n")
+    res = Image.new('L', (cwidth, cheight), 0)
+    res_draw = ImageDraw.Draw(res)
+
+    # Paste all characters into result bitmap
+    for i in range(len(renders)):
+        res.paste(renders[i], (offsets[i], 0))
+        # uncomment to draw lines separating each character (for debug)
+        #res_draw.rectangle([offsets[i], 0, offsets[i], cheight], outline="blue")
+
+    # crop the blank areas on top and bottom
+    (_, start_y, _, end_y) = res.getbbox()
+    res = res.crop((0, start_y, cwidth, end_y))
+    cheight = (end_y - start_y) + voffset.get()
+    new_res = Image.new('L', (cwidth, cheight))
+    new_res.paste(res, (0, voffset.get()))
+    res = new_res
+
+    # save the preview
+    if preview.get():
+        log("Saving preview to %s...\n" % preview.get())
+        res.save(preview.get())
+
+    # Pack the data.
+    # The "data" is a B/W bitmap with all 96 characters next to each other
+    # on one line. It is as wide as all the characters combined and as
+    # high as the tallest character, plus padding.
+    # Each byte contains info about eight pixels, starting from
+    # highest to lowest bit:
+    # bits:   | 7  6  5  4  3  2  1  0 | 15 14 13 12 11 10 9  8  | ...
+    # pixels: | 0  1  2  3  4  5  6  7 | 8  9  10 11 12 13 14 15 | ...
+    log("Packing data...\n")
+    bit = 0
+    bit_itr = 0
+    for c in res.tostring():
+        # FIXME: How to handle antialiasing?
+        # if c != '\x00':
+        # In Python3, c is int, in Python2, c is string. Because of reasons.
+        try:
+            fill = (ord(c) >= 127)
+        except TypeError:
+            fill = (c >= 127)
+        if fill:
+            bit |= (1 << (7-bit_itr))
+        bit_itr += 1
+        if bit_itr >= 8:
+            data += pack("<B", bit)
+            bit_itr = 0
+            bit = 0
+
+    # Write them to the file.
+    # Format:
+    # 000: width
+    # 004: height
+    # 008: offsets of each characters (96*uint32)
+    # 392: data as described above
+    log("Writing to %s...\n" % out_fname.get())
+    if out_fname.get() == "-":
+        write_data(sys.stdout, cwidth, cheight, offsets, data)
+    else:
+        with open(out_fname.get(), 'wb') as f:
+            write_data(f, cwidth, cheight, offsets, data)
+
+    exit(0)
diff --git a/recovery.cpp b/recovery.cpp
index e1a2a96..c6e1270 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -33,7 +33,11 @@
 #include "bootloader.h"
 #include "common.h"
 #include "cutils/properties.h"
+#ifdef ANDROID_RB_RESTART
 #include "cutils/android_reboot.h"
+#else
+#include <sys/reboot.h>
+#endif
 #include "install.h"
 #include "minui/minui.h"
 #include "minzip/DirUtil.h"
@@ -48,8 +52,20 @@
 #include "fuse_sdcard_provider.h"
 }
 
+extern "C" {
+#include "data.h"
+#include "gui/gui.h"
+}
+#include "partitions.hpp"
+#include "variables.h"
+#include "openrecoveryscript.hpp"
+#include "twrp-functions.hpp"
+
+TWPartitionManager PartitionManager;
+
 struct selabel_handle *sehandle;
 
+
 static const struct option OPTIONS[] = {
   { "send_intent", required_argument, NULL, 's' },
   { "update_package", required_argument, NULL, 'u' },
@@ -260,7 +276,7 @@
 }
 
 // How much of the temp log we have copied to the copy in cache.
-static long tmplog_offset = 0;
+//static long tmplog_offset = 0;
 
 static void
 copy_log_file(const char* source, const char* destination, int append) {
@@ -893,6 +909,10 @@
 
 int
 main(int argc, char **argv) {
+    // Recovery needs to install world-readable files, so clear umask
+    // set by init
+    umask(0);
+
     time_t start = time(NULL);
 
     redirect_stdio(TEMPORARY_LOG_FILE);
@@ -904,15 +924,41 @@
     // anything in the command file or bootloader control block; the
     // only way recovery should be run with this argument is when it
     // starts a copy of itself from the apply_from_adb() function.
-    if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
-        adb_main();
+    if (argc == 3 && strcmp(argv[1], "--adbd") == 0) {
+        adb_main(argv[2]);
         return 0;
     }
 
-    printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
+<<<<<<< HEAD
+    printf("Starting TWRP %s on %s", TW_VERSION_STR, ctime(&start));
+
+    Device* device = make_device();
+    ui = device->GetUI();
+
+	//ui->Init();
+    //ui->SetBackground(RecoveryUI::NONE);
+    //load_volume_table();
+
+	// Load default values to set DataManager constants and handle ifdefs
+	DataManager_LoadDefaults();
+	printf("Starting the UI...");
+	gui_init();
+	printf("=> Linking mtab\n");
+	symlink("/proc/mounts", "/etc/mtab");
+	printf("=> Processing recovery.fstab\n");
+	if (!PartitionManager.Process_Fstab("/etc/recovery.fstab", 1)) {
+		LOGE("Failing out of recovery due to problem with recovery.fstab.\n");
+		//return -1;
+	}
+	PartitionManager.Output_Partition_Logging();
+	// Load up all the resources
+	gui_loadResources();
+
+	PartitionManager.Mount_By_Path("/cache", true);
 
     load_volume_table();
     ensure_path_mounted(LAST_LOG_FILE);
+
     rotate_last_logs(KEEP_LOG_COUNT);
     get_args(&argc, &argv);
 
@@ -920,6 +966,7 @@
     const char *update_package = NULL;
     int wipe_data = 0, wipe_cache = 0, show_text = 0;
     bool just_exit = false;
+	bool perform_backup = false;
     bool shutdown_after = false;
 
     int arg;
@@ -980,7 +1027,7 @@
         ui->Print("Warning: No file_contexts\n");
     }
 
-    device->StartRecovery();
+    //device->StartRecovery();
 
     printf("Command:");
     for (arg = 0; arg < argc; arg++) {
@@ -1008,15 +1055,52 @@
     property_get("ro.build.display.id", recovery_version, "");
     printf("\n");
 
-    int status = INSTALL_SUCCESS;
+	// Check for and run startup script if script exists
+	TWFunc::check_and_run_script("/sbin/runatboot.sh", "boot");
+	TWFunc::check_and_run_script("/sbin/postrecoveryboot.sh", "boot");
 
+#ifdef TW_INCLUDE_INJECTTWRP
+	// Back up TWRP Ramdisk if needed:
+	TWPartition* Boot = PartitionManager.Find_Partition_By_Path("/boot");
+	LOGI("Backing up TWRP ramdisk...\n");
+	if (Boot == NULL || Boot->Current_File_System != "emmc")
+		TWFunc::Exec_Cmd("injecttwrp --backup /tmp/backup_recovery_ramdisk.img");
+	else {
+		string injectcmd = "injecttwrp --backup /tmp/backup_recovery_ramdisk.img bd=" + Boot->Actual_Block_Device;
+		TWFunc::Exec_Cmd(injectcmd);
+	}
+	LOGI("Backup of TWRP ramdisk done.\n");
+#endif
+
+    int status = INSTALL_SUCCESS;
+	string ORSCommand;
+
+	if (perform_backup) {
+		char empt[50];
+		gui_console_only();
+		strcpy(empt, "(Current Date)");
+		DataManager_SetStrValue(TW_BACKUP_NAME, empt);
+		if (!OpenRecoveryScript::Insert_ORS_Command("backup BSDCAE\n"))
+			status = INSTALL_ERROR;
+	}
+	if (status == INSTALL_SUCCESS) { // Prevent other actions if backup failed
     if (update_package != NULL) {
+		ORSCommand = "install ";
+		ORSCommand += update_package;
+		ORSCommand += "\n";
+
+		if (OpenRecoveryScript::Insert_ORS_Command(ORSCommand))
+			status = INSTALL_SUCCESS;
+		else
+			status = INSTALL_ERROR;
+		/*
         status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true);
         if (status == INSTALL_SUCCESS && wipe_cache) {
             if (erase_volume("/cache")) {
                 LOGE("Cache wipe (requested by package) failed.");
             }
         }
+
         if (status != INSTALL_SUCCESS) {
             ui->Print("Installation aborted.\n");
 
@@ -1030,18 +1114,66 @@
             }
         }
     } else if (wipe_data) {
+		if (!OpenRecoveryScript::Insert_ORS_Command("wipe data\n"))
+			status = INSTALL_ERROR;
+		/*
         if (device->WipeData()) status = INSTALL_ERROR;
         if (erase_volume("/data")) status = INSTALL_ERROR;
         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
         if (erase_persistent_partition() == -1 ) status = INSTALL_ERROR;
+		*/
         if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n");
     } else if (wipe_cache) {
-        if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
+        if (!OpenRecoveryScript::Insert_ORS_Command("wipe cache\n"))
+			status = INSTALL_ERROR;
         if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n");
     } else if (!just_exit) {
         status = INSTALL_NONE;  // No command specified
         ui->SetBackground(RecoveryUI::NO_COMMAND);
     }
+	}
+
+	finish_recovery(NULL);
+	// Offer to decrypt if the device is encrypted
+	if (DataManager_GetIntValue(TW_IS_ENCRYPTED) != 0) {
+		LOGI("Is encrypted, do decrypt page first\n");
+		if (gui_startPage("decrypt") != 0) {
+			LOGE("Failed to start decrypt GUI page.\n");
+		}
+	}
+
+	// Read the settings file
+	DataManager_ReadSettingsFile();
+	// Run any outstanding OpenRecoveryScript
+	if (DataManager_GetIntValue(TW_IS_ENCRYPTED) == 0 && (TWFunc::Path_Exists(SCRIPT_FILE_TMP) || TWFunc::Path_Exists(SCRIPT_FILE_CACHE))) {
+		OpenRecoveryScript::Run_OpenRecoveryScript();
+	}
+	// Launch the main GUI
+	gui_start();
+
+	// Check for su to see if the device is rooted or not
+	if (PartitionManager.Mount_By_Path("/system", false)) {
+		// Disable flashing of stock recovery
+		if (TWFunc::Path_Exists("/system/recovery-from-boot.p") && TWFunc::Path_Exists("/system/etc/install-recovery.sh")) {
+			rename("/system/recovery-from-boot.p", "/system/recovery-from-boot.bak");
+			ui_print("Renamed stock recovery file in /system to prevent\nthe stock ROM from replacing TWRP.\n");
+		}
+		if (TWFunc::Path_Exists("/supersu/su") && !TWFunc::Path_Exists("/system/bin/su") && !TWFunc::Path_Exists("/system/xbin/su") && !TWFunc::Path_Exists("/system/bin/.ext/.su")) {
+			// Device doesn't have su installed
+			DataManager_SetIntValue("tw_busy", 1);
+			if (gui_startPage("installsu") != 0) {
+				LOGE("Failed to start decrypt GUI page.\n");
+			}
+		} else if (TWFunc::Check_su_Perms() > 0) {
+			// su perms are set incorrectly
+			DataManager_SetIntValue("tw_busy", 1);
+			if (gui_startPage("fixsu") != 0) {
+				LOGE("Failed to start decrypt GUI page.\n");
+			}
+		}
+		sync();
+		PartitionManager.UnMount_By_Path("/system", false);
+	}
 
     if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
         copy_logs();
@@ -1055,6 +1187,27 @@
 
     // Save logs and clean up before rebooting or shutting down.
     finish_recovery(send_intent);
+    ui->Print("Rebooting...\n");
+	char backup_arg_char[50];
+	strcpy(backup_arg_char, DataManager_GetStrValue("tw_reboot_arg"));
+	string backup_arg = backup_arg_char;
+	if (backup_arg == "recovery")
+		TWFunc::tw_reboot(rb_recovery);
+	else if (backup_arg == "poweroff")
+		TWFunc::tw_reboot(rb_poweroff);
+	else if (backup_arg == "bootloader")
+		TWFunc::tw_reboot(rb_bootloader);
+	else if (backup_arg == "download")
+		TWFunc::tw_reboot(rb_download);
+	else
+		TWFunc::tw_reboot(rb_system);
+
+#ifdef ANDROID_RB_RESTART
+    android_reboot(ANDROID_RB_RESTART, 0, 0);
+#else
+	reboot(RB_AUTOBOOT);
+#endif
+    property_set(ANDROID_RB_PROPERTY, "reboot,");
 
     switch (after) {
         case Device::SHUTDOWN:
diff --git a/recovery_ui.h b/recovery_ui.h
new file mode 100644
index 0000000..9dae934
--- /dev/null
+++ b/recovery_ui.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _RECOVERY_UI_H
+#define _RECOVERY_UI_H
+
+// Called when recovery starts up.  Returns 0.
+extern int device_recovery_start();
+
+// Called in the input thread when a new key (key_code) is pressed.
+// *key_pressed is an array of KEY_MAX+1 bytes indicating which other
+// keys are already pressed.  Return true if the text display should
+// be toggled.
+extern int device_toggle_display(volatile char* key_pressed, int key_code);
+
+// Called in the input thread when a new key (key_code) is pressed.
+// *key_pressed is an array of KEY_MAX+1 bytes indicating which other
+// keys are already pressed.  Return true if the device should reboot
+// immediately.
+extern int device_reboot_now(volatile char* key_pressed, int key_code);
+
+// Called from the main thread when recovery is waiting for input and
+// a key is pressed.  key is the code of the key pressed; visible is
+// true if the recovery menu is being shown.  Implementations can call
+// ui_key_pressed() to discover if other keys are being held down.
+// Return one of the defined constants below in order to:
+//
+//   - move the menu highlight (HIGHLIGHT_*)
+//   - invoke the highlighted item (SELECT_ITEM)
+//   - do nothing (NO_ACTION)
+//   - invoke a specific action (a menu position: any non-negative number)
+extern int device_handle_key(int key, int visible);
+
+// Perform a recovery action selected from the menu.  'which' will be
+// the item number of the selected menu item, or a non-negative number
+// returned from device_handle_key().  The menu will be hidden when
+// this is called; implementations can call ui_print() to print
+// information to the screen.
+extern int device_perform_action(int which);
+
+// Called when we do a wipe data/factory reset operation (either via a
+// reboot from the main system with the --wipe_data flag, or when the
+// user boots into recovery manually and selects the option from the
+// menu.)  Can perform whatever device-specific wiping actions are
+// needed.  Return 0 on success.  The userdata and cache partitions
+// are erased after this returns (whether it returns success or not).
+int device_wipe_data();
+
+#define NO_ACTION           -1
+
+#define HIGHLIGHT_UP        -2
+#define HIGHLIGHT_DOWN      -3
+#define SELECT_ITEM         -4
+#define UP_A_LEVEL          -5
+#define HOME_MENU           -6
+#define MENU_MENU           -7
+
+// Again, just to keep custom recovery builds happy
+#define ITEM_APPLY_CACHE     4
+
+// Header text to display above the main menu.
+extern char* MENU_HEADERS[];
+
+// Text of menu items.
+extern char* MENU_ITEMS[];
+
+// NOTE: Main Menu index definitions moved to recovery.c
+
+int sdcard_directory(const char* path);
+int get_menu_selection(char** headers, char** items, int menu_only, int initial_selection);
+void prompt_and_wait();
+void finish_recovery(const char *send_intent);
+#endif
diff --git a/res/dummyfileforgit b/res/dummyfileforgit
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/res/dummyfileforgit
diff --git a/res/images/dummyfile2 b/res/images/dummyfile2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/res/images/dummyfile2
diff --git a/roots.cpp b/roots.cpp
index 0d47577..d69245f 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -24,9 +24,11 @@
 #include <ctype.h>
 #include <fcntl.h>
 
+extern "C" {
 #include <fs_mgr.h>
 #include "mtdutils/mtdutils.h"
 #include "mtdutils/mounts.h"
+}
 #include "roots.h"
 #include "common.h"
 #include "make_ext4fs.h"
@@ -75,6 +77,10 @@
 }
 
 int ensure_path_mounted(const char* path) {
+	if (PartitionManager.Mount_By_Path(path, true))
+		return 0;
+	else
+		return -1;
     Volume* v = volume_for_path(path);
     if (v == NULL) {
         LOGE("unknown volume for path [%s]\n", path);
@@ -127,6 +133,10 @@
 }
 
 int ensure_path_unmounted(const char* path) {
+	if (PartitionManager.UnMount_By_Path(path, true))
+		return 0;
+	else
+		return -1;
     Volume* v = volume_for_path(path);
     if (v == NULL) {
         LOGE("unknown volume for path [%s]\n", path);
@@ -169,6 +179,10 @@
 }
 
 int format_volume(const char* volume) {
+	if (PartitionManager.Wipe_By_Path(volume))
+		return 0;
+	else
+		return -1;
     Volume* v = volume_for_path(volume);
     if (v == NULL) {
         LOGE("unknown volume \"%s\"\n", volume);
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 03ef049..6fff30a 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -33,6 +33,12 @@
 #include "minui/minui.h"
 #include "screen_ui.h"
 #include "ui.h"
+extern "C" {
+#include "minuitwrp/minui.h"
+int twgr_text(int x, int y, const char *s);
+#include "gui/gui.h"
+}
+#include "data.hpp"
 
 static int char_width;
 static int char_height;
@@ -269,7 +275,7 @@
 // Updates only the progress bar, if possible, otherwise redraws the screen.
 // Should only be called with updateMutex locked.
 void ScreenRecoveryUI::update_progress_locked()
-{
+{return;
     if (show_text || !pagesIdentical) {
         draw_screen_locked();    // Must redraw the whole screen
         pagesIdentical = true;
@@ -432,6 +438,9 @@
 
 void ScreenRecoveryUI::ShowProgress(float portion, float seconds)
 {
+	DataManager::SetValue("ui_progress_portion", (float)(portion * 100.0));
+    DataManager::SetValue("ui_progress_frames", seconds * 30);
+
     pthread_mutex_lock(&updateMutex);
     progressBarType = DETERMINATE;
     progressScopeStart += progressScopeSize;
@@ -445,6 +454,8 @@
 
 void ScreenRecoveryUI::SetProgress(float fraction)
 {
+	DataManager::SetValue("ui_progress", (float) (fraction * 100.0)); return;
+
     pthread_mutex_lock(&updateMutex);
     if (fraction < 0.0) fraction = 0.0;
     if (fraction > 1.0) fraction = 1.0;
@@ -475,6 +486,9 @@
     vsnprintf(buf, 256, fmt, ap);
     va_end(ap);
 
+	gui_print("%s", buf);
+	return;
+
     fputs(buf, stdout);
 
     // This can get called before ui_init(), so be careful.
diff --git a/tarWrite.c b/tarWrite.c
new file mode 100644
index 0000000..9b6e721
--- /dev/null
+++ b/tarWrite.c
@@ -0,0 +1,92 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <fcntl.h>
+#include "libtar/libtar.h"
+#include "twcommon.h"
+
+int flush = 0, eot_count = -1;
+unsigned char *write_buffer;
+unsigned buffer_size = 4096;
+unsigned buffer_loc = 0;
+int buffer_status = 0;
+
+void reinit_libtar_buffer(void) {
+	flush = 0;
+	eot_count = -1;
+	buffer_loc = 0;
+	buffer_status = 1;
+}
+
+void init_libtar_buffer(unsigned new_buff_size) {
+	if (new_buff_size != 0)
+		buffer_size = new_buff_size;
+
+	reinit_libtar_buffer();
+	write_buffer = (unsigned char*) malloc(sizeof(char *) * buffer_size);
+}
+
+void free_libtar_buffer(void) {
+	if (buffer_status > 0)
+		free(write_buffer);
+	buffer_status = 0;
+}
+
+ssize_t write_libtar_buffer(int fd, const void *buffer, size_t size) {
+	void* ptr;
+
+	if (flush == 0) {
+		ptr = write_buffer + buffer_loc;
+		memcpy(ptr, buffer, size);
+		buffer_loc += size;
+		if (eot_count >= 0 && eot_count < 2)
+			eot_count++;
+			/* At the end of the tar file, libtar will add 2 blank blocks.
+			   Once we have received both EOT blocks, we will immediately
+			   write anything in the buffer to the file.
+			*/
+
+		if (buffer_loc >= buffer_size || eot_count >= 2) {
+			flush = 1;
+		}
+	}
+	if (flush == 1) {
+		flush = 0;
+		if (buffer_loc == 0) {
+			// nothing to write
+			return 0;
+		}
+		if (write(fd, write_buffer, buffer_loc) != buffer_loc) {
+			LOGERR("Error writing tar file!\n");
+			buffer_loc = 0;
+			return -1;
+		} else {
+			buffer_loc = 0;
+			return size;
+		}
+	} else {
+		return size;
+	}
+	// Shouldn't ever get here
+	return -1;
+}
+
+void flush_libtar_buffer(int fd) {
+	eot_count = 0;
+	buffer_status = 2;
+}
diff --git a/tarWrite.h b/tarWrite.h
new file mode 100644
index 0000000..498ca0a
--- /dev/null
+++ b/tarWrite.h
@@ -0,0 +1,28 @@
+/*
+        Copyright 2013 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TARWRITE_HEADER
+#define _TARWRITE_HEADER
+
+void reinit_libtar_buffer();
+void init_libtar_buffer(unsigned new_buff_size);
+void free_libtar_buffer();
+writefunc_t write_libtar_buffer(int fd, const void *buffer, size_t size);
+void flush_libtar_buffer(int fd);
+
+#endif  // _TARWRITE_HEADER
\ No newline at end of file
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
new file mode 100644
index 0000000..8a96c81
--- /dev/null
+++ b/toolbox/Android.mk
@@ -0,0 +1,149 @@
+LOCAL_PATH:= system/core/toolbox/
+include $(CLEAR_VARS)
+
+TOOLS := \
+	start \
+	stop \
+	getprop \
+	setprop
+
+# If busybox does not have SELinux support, provide these tools with toolbox.
+# Note that RECOVERY_BUSYBOX_TOOLS will be empty if TW_USE_TOOLBOX == true.
+ifeq ($(TWHAVE_SELINUX), true)
+	TOOLS_FOR_SELINUX := \
+		ls \
+		getenforce \
+		setenforce \
+		chcon \
+		restorecon \
+		runcon \
+		getsebool \
+		setsebool \
+		load_policy
+	TOOLS += $(filter-out $(RECOVERY_BUSYBOX_TOOLS), $(TOOLS_FOR_SELINUX))
+endif
+
+ifeq ($(TW_USE_TOOLBOX), true)
+	TOOLS += \
+		mount \
+		cat \
+		ps \
+		kill \
+		ln \
+		insmod \
+		rmmod \
+		lsmod \
+		ifconfig \
+		setconsole \
+		rm \
+		mkdir \
+		rmdir \
+		getevent \
+		sendevent \
+		date \
+		wipe \
+		sync \
+		umount \
+		notify \
+		cmp \
+		dmesg \
+		route \
+		hd \
+		dd \
+		df \
+		watchprops \
+		log \
+		sleep \
+		renice \
+		printenv \
+		smd \
+		chmod \
+		chown \
+		newfs_msdos \
+		netstat \
+		ioctl \
+		mv \
+		schedtop \
+		top \
+		iftop \
+		id \
+		uptime \
+		vmstat \
+		nandread \
+		ionice \
+		touch \
+		lsof \
+		du \
+		md5 \
+		clear \
+		swapon \
+		swapoff \
+		mkswap \
+		readlink
+	ifneq ($(TWHAVE_SELINUX), true)
+		TOOLS += ls
+	endif
+endif
+
+LOCAL_SRC_FILES := \
+	toolbox.c \
+	$(patsubst %,%.c,$(TOOLS))
+
+ifneq ($(wildcard system/core/toolbox/dynarray.c),)
+    LOCAL_SRC_FILES += dynarray.c
+endif
+
+# reboot.c was removed in 4.4 kitkat
+#TOOLS += reboot
+
+#ifeq ($(BOARD_USES_BOOTMENU),true)
+#	LOCAL_SRC_FILES += ../../../external/bootmenu/libreboot/reboot.c
+#else
+#	LOCAL_SRC_FILES += reboot.c
+#endif
+
+LOCAL_C_INCLUDES := bionic/libc/bionic
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	liblog \
+	libc
+
+ifeq ($(TWHAVE_SELINUX), true)
+	LOCAL_SHARED_LIBRARIES += libselinux
+endif
+
+LOCAL_MODULE := toolbox_recovery
+LOCAL_MODULE_STEM := toolbox
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_MODULE_TAGS := optional
+
+# Including this will define $(intermediates).
+#
+include $(BUILD_EXECUTABLE)
+
+$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h
+
+TOOLS_H := $(intermediates)/tools.h
+$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS)
+$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done
+$(TOOLS_H): $(LOCAL_PATH)/Android.mk
+$(TOOLS_H):
+	$(transform-generated-source)
+
+# Make #!/system/bin/toolbox launchers for each tool.
+#
+SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(TOOLS))
+$(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE_STEM)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+	@echo "Symlink: $@ -> $(TOOLBOX_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(TOOLBOX_BINARY) $@
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := toolbox_symlinks
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(SYMLINKS)
+include $(BUILD_PHONY_PACKAGE)
+SYMLINKS :=
diff --git a/twcommon.h b/twcommon.h
new file mode 100644
index 0000000..d54446f
--- /dev/null
+++ b/twcommon.h
@@ -0,0 +1,25 @@
+#ifndef TWCOMMON_HPP
+#define TWCOMMON_HPP
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef BUILD_TWRPTAR_MAIN
+#include "gui/gui.h"
+#define LOGERR(...) gui_print_color("error", "E:" __VA_ARGS__)
+#define LOGINFO(...) fprintf(stdout, "I:" __VA_ARGS__)
+#else
+#define LOGERR(...) printf("E:" __VA_ARGS__)
+#define LOGINFO(...) printf("I:" __VA_ARGS__)
+#define gui_print(...) printf( __VA_ARGS__ )
+#endif
+
+#define STRINGIFY(x) #x
+#define EXPAND(x) STRINGIFY(x)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // TWCOMMON_HPP
diff --git a/twinstall.cpp b/twinstall.cpp
new file mode 100644
index 0000000..4247cf7
--- /dev/null
+++ b/twinstall.cpp
@@ -0,0 +1,286 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include "twcommon.h"
+#include "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+#include "minui/minui.h"
+#ifdef HAVE_SELINUX
+#include "minzip/SysUtil.h"
+#include "minzip/Zip.h"
+#else
+#include "minzipold/SysUtil.h"
+#include "minzipold/Zip.h"
+#endif
+#include "mtdutils/mounts.h"
+#include "mtdutils/mtdutils.h"
+#include "verifier.h"
+#include "variables.h"
+#include "data.hpp"
+#include "partitions.hpp"
+#include "twrpDigest.hpp"
+#include "twrp-functions.hpp"
+extern "C" {
+	#include "gui/gui.h"
+	#include "legacy_property_service.h"
+}
+
+static const char* properties_path = "/dev/__properties__";
+static const char* properties_path_renamed = "/dev/__properties_kk__";
+static bool legacy_props_env_initd = false;
+static bool legacy_props_path_modified = false;
+
+static int switch_to_legacy_properties()
+{
+	if (!legacy_props_env_initd) {
+		if (legacy_properties_init() != 0)
+			return -1;
+
+		char tmp[32];
+		int propfd, propsz;
+		legacy_get_property_workspace(&propfd, &propsz);
+		sprintf(tmp, "%d,%d", dup(propfd), propsz);
+		setenv("ANDROID_PROPERTY_WORKSPACE", tmp, 1);
+		legacy_props_env_initd = true;
+	}
+
+	if (TWFunc::Path_Exists(properties_path)) {
+		// hide real properties so that the updater uses the envvar to find the legacy format properties
+		if (rename(properties_path, properties_path_renamed) != 0) {
+			LOGERR("Renaming %s failed: %s\n", properties_path, strerror(errno));
+			return -1;
+		} else {
+			legacy_props_path_modified = true;
+		}
+	}
+
+	return 0;
+}
+
+static int switch_to_new_properties()
+{
+	if (TWFunc::Path_Exists(properties_path_renamed)) {
+		if (rename(properties_path_renamed, properties_path) != 0) {
+			LOGERR("Renaming %s failed: %s\n", properties_path_renamed, strerror(errno));
+			return -1;
+		} else {
+			legacy_props_path_modified = false;
+		}
+	}
+
+	return 0;
+}
+
+static int Run_Update_Binary(const char *path, ZipArchive *Zip, int* wipe_cache) {
+	const ZipEntry* binary_location = mzFindZipEntry(Zip, ASSUMED_UPDATE_BINARY_NAME);
+	string Temp_Binary = "/tmp/updater";
+	int binary_fd, ret_val, pipe_fd[2], status, zip_verify;
+	char buffer[1024];
+	const char** args = (const char**)malloc(sizeof(char*) * 5);
+	FILE* child_data;
+
+	if (binary_location == NULL) {
+		mzCloseZipArchive(Zip);
+		return INSTALL_CORRUPT;
+	}
+
+	// Delete any existing updater
+	if (TWFunc::Path_Exists(Temp_Binary) && unlink(Temp_Binary.c_str()) != 0) {
+		LOGINFO("Unable to unlink '%s'\n", Temp_Binary.c_str());
+	}
+
+	binary_fd = creat(Temp_Binary.c_str(), 0755);
+	if (binary_fd < 0) {
+		mzCloseZipArchive(Zip);
+		LOGERR("Could not create file for updater extract in '%s'\n", Temp_Binary.c_str());
+		return INSTALL_ERROR;
+	}
+
+	ret_val = mzExtractZipEntryToFile(Zip, binary_location, binary_fd);
+	close(binary_fd);
+
+	if (!ret_val) {
+		mzCloseZipArchive(Zip);
+		LOGERR("Could not extract '%s'\n", ASSUMED_UPDATE_BINARY_NAME);
+		return INSTALL_ERROR;
+	}
+
+	// If exists, extract file_contexts from the zip file
+	const ZipEntry* selinx_contexts = mzFindZipEntry(Zip, "file_contexts");
+	if (selinx_contexts == NULL) {
+		mzCloseZipArchive(Zip);
+		LOGINFO("Zip does not contain SELinux file_contexts file in its root.\n");
+	} else {
+		string output_filename = "/file_contexts";
+		LOGINFO("Zip contains SELinux file_contexts file in its root. Extracting to %s\n", output_filename.c_str());
+		// Delete any file_contexts
+		if (TWFunc::Path_Exists(output_filename) && unlink(output_filename.c_str()) != 0) {
+			LOGINFO("Unable to unlink '%s'\n", output_filename.c_str());
+		}
+
+		int file_contexts_fd = creat(output_filename.c_str(), 0644);
+		if (file_contexts_fd < 0) {
+			mzCloseZipArchive(Zip);
+			LOGERR("Could not extract file_contexts to '%s'\n", output_filename.c_str());
+			return INSTALL_ERROR;
+		}
+
+		ret_val = mzExtractZipEntryToFile(Zip, selinx_contexts, file_contexts_fd);
+		close(file_contexts_fd);
+
+		if (!ret_val) {
+			mzCloseZipArchive(Zip);
+			LOGERR("Could not extract '%s'\n", ASSUMED_UPDATE_BINARY_NAME);
+			return INSTALL_ERROR;
+		}
+	}
+	mzCloseZipArchive(Zip);
+
+#ifndef TW_NO_LEGACY_PROPS
+	/* Set legacy properties */
+	if (switch_to_legacy_properties() != 0) {
+		LOGERR("Legacy property environment did not initialize successfully. Properties may not be detected.\n");
+	} else {
+		LOGINFO("Legacy property environment initialized.\n");
+	}
+#endif
+
+	pipe(pipe_fd);
+
+	args[0] = Temp_Binary.c_str();
+	args[1] = EXPAND(RECOVERY_API_VERSION);
+	char* temp = (char*)malloc(10);
+	sprintf(temp, "%d", pipe_fd[1]);
+	args[2] = temp;
+	args[3] = (char*)path;
+	args[4] = NULL;
+
+	pid_t pid = fork();
+	if (pid == 0) {
+		close(pipe_fd[0]);
+		execve(Temp_Binary.c_str(), (char* const*)args, environ);
+		printf("E:Can't execute '%s'\n", Temp_Binary.c_str());
+		_exit(-1);
+	}
+	close(pipe_fd[1]);
+
+	*wipe_cache = 0;
+
+	DataManager::GetValue(TW_SIGNED_ZIP_VERIFY_VAR, zip_verify);
+	child_data = fdopen(pipe_fd[0], "r");
+	while (fgets(buffer, sizeof(buffer), child_data) != NULL) {
+		char* command = strtok(buffer, " \n");
+		if (command == NULL) {
+			continue;
+		} else if (strcmp(command, "progress") == 0) {
+			char* fraction_char = strtok(NULL, " \n");
+			char* seconds_char = strtok(NULL, " \n");
+
+			float fraction_float = strtof(fraction_char, NULL);
+			int seconds_float = strtol(seconds_char, NULL, 10);
+
+			if (zip_verify)
+				DataManager::ShowProgress(fraction_float * (1 - VERIFICATION_PROGRESS_FRACTION), seconds_float);
+			else
+				DataManager::ShowProgress(fraction_float, seconds_float);
+		} else if (strcmp(command, "set_progress") == 0) {
+			char* fraction_char = strtok(NULL, " \n");
+			float fraction_float = strtof(fraction_char, NULL);
+			DataManager::SetProgress(fraction_float);
+		} else if (strcmp(command, "ui_print") == 0) {
+			char* display_value = strtok(NULL, "\n");
+			if (display_value) {
+				gui_print("%s", display_value);
+			} else {
+				gui_print("\n");
+			}
+		} else if (strcmp(command, "wipe_cache") == 0) {
+			*wipe_cache = 1;
+		} else if (strcmp(command, "clear_display") == 0) {
+			// Do nothing, not supported by TWRP
+		} else {
+			LOGERR("unknown command [%s]\n", command);
+		}
+	}
+	fclose(child_data);
+
+	waitpid(pid, &status, 0);
+
+#ifndef TW_NO_LEGACY_PROPS
+	/* Unset legacy properties */
+	if (legacy_props_path_modified) {
+		if (switch_to_new_properties() != 0) {
+			LOGERR("Legacy property environment did not disable successfully. Legacy properties may still be in use.\n");
+		} else {
+			LOGINFO("Legacy property environment disabled.\n");
+		}
+	}
+#endif
+
+	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+		LOGERR("Error executing updater binary in zip '%s'\n", path);
+		return INSTALL_ERROR;
+	}
+
+	return INSTALL_SUCCESS;
+}
+
+extern "C" int TWinstall_zip(const char* path, int* wipe_cache) {
+	int ret_val, zip_verify = 1, md5_return, key_count;
+	twrpDigest md5sum;
+	string strpath = path;
+	ZipArchive Zip;
+
+	gui_print("Installing '%s'...\nChecking for MD5 file...\n", path);
+	md5sum.setfn(strpath);
+	md5_return = md5sum.verify_md5digest();
+	if (md5_return == -2) { // md5 did not match
+		LOGERR("Aborting zip install\n");
+		return INSTALL_CORRUPT;
+	}
+
+#ifndef TW_OEM_BUILD
+	DataManager::GetValue(TW_SIGNED_ZIP_VERIFY_VAR, zip_verify);
+#endif
+	DataManager::SetProgress(0);
+	if (zip_verify) {
+		gui_print("Verifying zip signature...\n");
+		ret_val = verify_file(path);
+		if (ret_val != VERIFY_SUCCESS) {
+			LOGERR("Zip signature verification failed: %i\n", ret_val);
+			return -1;
+		}
+	}
+	ret_val = mzOpenZipArchive(path, &Zip);
+	if (ret_val != 0) {
+		LOGERR("Zip file is corrupt!\n", path);
+		return INSTALL_CORRUPT;
+	}
+	return Run_Update_Binary(path, &Zip, wipe_cache);
+}
diff --git a/twinstall.h b/twinstall.h
new file mode 100644
index 0000000..ea467a2
--- /dev/null
+++ b/twinstall.h
@@ -0,0 +1,14 @@
+#ifndef RECOVERY_TWINSTALL_H_
+#define RECOVERY_TWINSTALL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int TWinstall_zip(const char* path, int* wipe_cache);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // RECOVERY_TWINSTALL_H_
diff --git a/twrp-functions.cpp b/twrp-functions.cpp
new file mode 100644
index 0000000..b962457
--- /dev/null
+++ b/twrp-functions.cpp
@@ -0,0 +1,1298 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <unistd.h>
+#include <vector>
+#include <dirent.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include "twrp-functions.hpp"
+#include "twcommon.h"
+#ifndef BUILD_TWRPTAR_MAIN
+#include "data.hpp"
+#include "partitions.hpp"
+#include "variables.h"
+#include "bootloader.h"
+#include "cutils/properties.h"
+#ifdef ANDROID_RB_POWEROFF
+	#include "cutils/android_reboot.h"
+#endif
+#endif // ndef BUILD_TWRPTAR_MAIN
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	#include "openaes/inc/oaes_lib.h"
+#endif
+
+extern "C" {
+	#include "libcrecovery/common.h"
+}
+
+/* Execute a command */
+int TWFunc::Exec_Cmd(const string& cmd, string &result) {
+	FILE* exec;
+	char buffer[130];
+	int ret = 0;
+	exec = __popen(cmd.c_str(), "r");
+	if (!exec) return -1;
+	while(!feof(exec)) {
+		memset(&buffer, 0, sizeof(buffer));
+		if (fgets(buffer, 128, exec) != NULL) {
+			buffer[128] = '\n';
+			buffer[129] = 0;
+			result += buffer;
+		}
+	}
+	ret = __pclose(exec);
+	return ret;
+}
+
+int TWFunc::Exec_Cmd(const string& cmd) {
+	pid_t pid;
+	int status;
+	switch(pid = fork())
+	{
+		case -1:
+			LOGERR("Exec_Cmd(): vfork failed: %d!\n", errno);
+			return -1;
+		case 0: // child
+			execl("/sbin/sh", "sh", "-c", cmd.c_str(), NULL);
+			_exit(127);
+			break;
+		default:
+		{
+			if (TWFunc::Wait_For_Child(pid, &status, cmd) != 0)
+				return -1;
+			else
+				return 0;
+		}
+	}
+}
+
+// Returns "file.name" from a full /path/to/file.name
+string TWFunc::Get_Filename(string Path) {
+	size_t pos = Path.find_last_of("/");
+	if (pos != string::npos) {
+		string Filename;
+		Filename = Path.substr(pos + 1, Path.size() - pos - 1);
+		return Filename;
+	} else
+		return Path;
+}
+
+// Returns "/path/to/" from a full /path/to/file.name
+string TWFunc::Get_Path(string Path) {
+	size_t pos = Path.find_last_of("/");
+	if (pos != string::npos) {
+		string Pathonly;
+		Pathonly = Path.substr(0, pos + 1);
+		return Pathonly;
+	} else
+		return Path;
+}
+
+int TWFunc::Wait_For_Child(pid_t pid, int *status, string Child_Name) {
+	pid_t rc_pid;
+
+	rc_pid = waitpid(pid, status, 0);
+	if (rc_pid > 0) {
+		if (WEXITSTATUS(*status) == 0)
+			LOGINFO("%s process ended with RC=%d\n", Child_Name.c_str(), WEXITSTATUS(*status)); // Success
+		else if (WIFSIGNALED(*status)) {
+			LOGINFO("%s process ended with signal: %d\n", Child_Name.c_str(), WTERMSIG(*status)); // Seg fault or some other non-graceful termination
+			return -1;
+		} else if (WEXITSTATUS(*status) != 0) {
+			LOGINFO("%s process ended with ERROR=%d\n", Child_Name.c_str(), WEXITSTATUS(*status)); // Graceful exit, but there was an error
+			return -1;
+		}
+	} else { // no PID returned
+		if (errno == ECHILD)
+			LOGINFO("%s no child process exist\n", Child_Name.c_str());
+		else {
+			LOGINFO("%s Unexpected error\n", Child_Name.c_str());
+			return -1;
+		}
+	}
+	return 0;
+}
+
+bool TWFunc::Path_Exists(string Path) {
+	// Check to see if the Path exists
+	struct stat st;
+	if (stat(Path.c_str(), &st) != 0)
+		return false;
+	else
+		return true;
+}
+
+int TWFunc::Get_File_Type(string fn) {
+	string::size_type i = 0;
+	int firstbyte = 0, secondbyte = 0;
+	char header[3];
+
+	ifstream f;
+	f.open(fn.c_str(), ios::in | ios::binary);
+	f.get(header, 3);
+	f.close();
+	firstbyte = header[i] & 0xff;
+	secondbyte = header[++i] & 0xff;
+
+	if (firstbyte == 0x1f && secondbyte == 0x8b)
+		return 1; // Compressed
+	else if (firstbyte == 0x4f && secondbyte == 0x41)
+		return 2; // Encrypted
+	else
+		return 0; // Unknown
+
+	return 0;
+}
+
+int TWFunc::Try_Decrypting_File(string fn, string password) {
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	OAES_CTX * ctx = NULL;
+	uint8_t _key_data[32] = "";
+	FILE *f;
+	uint8_t buffer[4096];
+	uint8_t *buffer_out = NULL;
+	uint8_t *ptr = NULL;
+	size_t read_len = 0, out_len = 0;
+	int firstbyte = 0, secondbyte = 0, key_len;
+	size_t _j = 0;
+	size_t _key_data_len = 0;
+
+	// mostly kanged from OpenAES oaes.c
+	for( _j = 0; _j < 32; _j++ )
+		_key_data[_j] = _j + 1;
+	_key_data_len = password.size();
+	if( 16 >= _key_data_len )
+		_key_data_len = 16;
+	else if( 24 >= _key_data_len )
+		_key_data_len = 24;
+	else
+	_key_data_len = 32;
+	memcpy(_key_data, password.c_str(), password.size());
+
+	ctx = oaes_alloc();
+	if (ctx == NULL) {
+		LOGERR("Failed to allocate OAES\n");
+		return -1;
+	}
+
+	oaes_key_import_data(ctx, _key_data, _key_data_len);
+
+	f = fopen(fn.c_str(), "rb");
+	if (f == NULL) {
+		LOGERR("Failed to open '%s' to try decrypt\n", fn.c_str());
+		return -1;
+	}
+	read_len = fread(buffer, sizeof(uint8_t), 4096, f);
+	if (read_len <= 0) {
+		LOGERR("Read size during try decrypt failed\n");
+		fclose(f);
+		return -1;
+	}
+	if (oaes_decrypt(ctx, buffer, read_len, NULL, &out_len) != OAES_RET_SUCCESS) {
+		LOGERR("Error: Failed to retrieve required buffer size for trying decryption.\n");
+		fclose(f);
+		return -1;
+	}
+	buffer_out = (uint8_t *) calloc(out_len, sizeof(char));
+	if (buffer_out == NULL) {
+		LOGERR("Failed to allocate output buffer for try decrypt.\n");
+		fclose(f);
+		return -1;
+	}
+	if (oaes_decrypt(ctx, buffer, read_len, buffer_out, &out_len) != OAES_RET_SUCCESS) {
+		LOGERR("Failed to decrypt file '%s'\n", fn.c_str());
+		fclose(f);
+		free(buffer_out);
+		return 0;
+	}
+	fclose(f);
+	if (out_len < 2) {
+		LOGINFO("Successfully decrypted '%s' but read length %i too small.\n", fn.c_str(), out_len);
+		free(buffer_out);
+		return 1; // Decrypted successfully
+	}
+	ptr = buffer_out;
+	firstbyte = *ptr & 0xff;
+	ptr++;
+	secondbyte = *ptr & 0xff;
+	if (firstbyte == 0x1f && secondbyte == 0x8b) {
+		LOGINFO("Successfully decrypted '%s' and file is compressed.\n", fn.c_str());
+		free(buffer_out);
+		return 3; // Compressed
+	}
+	if (out_len >= 262) {
+		ptr = buffer_out + 257;
+		if (strncmp((char*)ptr, "ustar", 5) == 0) {
+			LOGINFO("Successfully decrypted '%s' and file is tar format.\n", fn.c_str());
+			free(buffer_out);
+			return 2; // Tar
+		}
+	}
+	free(buffer_out);
+	LOGINFO("No errors decrypting '%s' but no known file format.\n", fn.c_str());
+	return 1; // Decrypted successfully
+#else
+	LOGERR("Encrypted backup support not included.\n");
+	return -1;
+#endif
+}
+
+unsigned long TWFunc::Get_File_Size(string Path) {
+	struct stat st;
+
+	if (stat(Path.c_str(), &st) != 0)
+		return 0;
+	return st.st_size;
+}
+
+std::string TWFunc::Remove_Trailing_Slashes(const std::string& path, bool leaveLast)
+{
+	std::string res;
+	size_t last_idx = 0, idx = 0;
+
+	while(last_idx != std::string::npos)
+	{
+		if(last_idx != 0)
+			res += '/';
+
+		idx = path.find_first_of('/', last_idx);
+		if(idx == std::string::npos) {
+			res += path.substr(last_idx, idx);
+			break;
+		}
+
+		res += path.substr(last_idx, idx-last_idx);
+		last_idx = path.find_first_not_of('/', idx);
+	}
+
+	if(leaveLast)
+		res += '/';
+	return res;
+}
+
+vector<string> TWFunc::split_string(const string &in, char del, bool skip_empty) {
+	vector<string> res;
+
+	if (in.empty() || del == '\0')
+		return res;
+
+	string field;
+	istringstream f(in);
+	if (del == '\n') {
+		while(getline(f, field)) {
+			if (field.empty() && skip_empty)
+				continue;
+		res.push_back(field);
+		}
+	} else {
+		while(getline(f, field, del)) {
+			if (field.empty() && skip_empty)
+				continue;
+			res.push_back(field);
+		}
+	}
+	return res;
+}
+
+#ifndef BUILD_TWRPTAR_MAIN
+
+// Returns "/path" from a full /path/to/file.name
+string TWFunc::Get_Root_Path(string Path) {
+	string Local_Path = Path;
+
+	// Make sure that we have a leading slash
+	if (Local_Path.substr(0, 1) != "/")
+		Local_Path = "/" + Local_Path;
+
+	// Trim the path to get the root path only
+	size_t position = Local_Path.find("/", 2);
+	if (position != string::npos) {
+		Local_Path.resize(position);
+	}
+	return Local_Path;
+}
+
+void TWFunc::install_htc_dumlock(void) {
+	int need_libs = 0;
+
+	if (!PartitionManager.Mount_By_Path("/system", true))
+		return;
+
+	if (!PartitionManager.Mount_By_Path("/data", true))
+		return;
+
+	gui_print("Installing HTC Dumlock to system...\n");
+	copy_file("/res/htcd/htcdumlocksys", "/system/bin/htcdumlock", 0755);
+	if (!Path_Exists("/system/bin/flash_image")) {
+		gui_print("Installing flash_image...\n");
+		copy_file("/res/htcd/flash_imagesys", "/system/bin/flash_image", 0755);
+		need_libs = 1;
+	} else
+		gui_print("flash_image is already installed, skipping...\n");
+	if (!Path_Exists("/system/bin/dump_image")) {
+		gui_print("Installing dump_image...\n");
+		copy_file("/res/htcd/dump_imagesys", "/system/bin/dump_image", 0755);
+		need_libs = 1;
+	} else
+		gui_print("dump_image is already installed, skipping...\n");
+	if (need_libs) {
+		gui_print("Installing libs needed for flash_image and dump_image...\n");
+		copy_file("/res/htcd/libbmlutils.so", "/system/lib/libbmlutils.so", 0755);
+		copy_file("/res/htcd/libflashutils.so", "/system/lib/libflashutils.so", 0755);
+		copy_file("/res/htcd/libmmcutils.so", "/system/lib/libmmcutils.so", 0755);
+		copy_file("/res/htcd/libmtdutils.so", "/system/lib/libmtdutils.so", 0755);
+	}
+	gui_print("Installing HTC Dumlock app...\n");
+	mkdir("/data/app", 0777);
+	unlink("/data/app/com.teamwin.htcdumlock*");
+	copy_file("/res/htcd/HTCDumlock.apk", "/data/app/com.teamwin.htcdumlock.apk", 0777);
+	sync();
+	gui_print("HTC Dumlock is installed.\n");
+}
+
+void TWFunc::htc_dumlock_restore_original_boot(void) {
+	if (!PartitionManager.Mount_By_Path("/sdcard", true))
+		return;
+
+	gui_print("Restoring original boot...\n");
+	Exec_Cmd("htcdumlock restore");
+	gui_print("Original boot restored.\n");
+}
+
+void TWFunc::htc_dumlock_reflash_recovery_to_boot(void) {
+	if (!PartitionManager.Mount_By_Path("/sdcard", true))
+		return;
+	gui_print("Reflashing recovery to boot...\n");
+	Exec_Cmd("htcdumlock recovery noreboot");
+	gui_print("Recovery is flashed to boot.\n");
+}
+
+int TWFunc::Recursive_Mkdir(string Path) {
+	string pathCpy = Path;
+	string wholePath;
+	size_t pos = pathCpy.find("/", 2);
+
+	while (pos != string::npos)
+	{
+		wholePath = pathCpy.substr(0, pos);
+		if (mkdir(wholePath.c_str(), 0777) && errno != EEXIST) {
+			LOGERR("Unable to create folder: %s  (errno=%d)\n", wholePath.c_str(), errno);
+			return false;
+		}
+
+		pos = pathCpy.find("/", pos + 1);
+	}
+	if (mkdir(wholePath.c_str(), 0777) && errno != EEXIST)
+		return false;
+	return true;
+}
+
+void TWFunc::GUI_Operation_Text(string Read_Value, string Default_Text) {
+	string Display_Text;
+
+	DataManager::GetValue(Read_Value, Display_Text);
+	if (Display_Text.empty())
+		Display_Text = Default_Text;
+
+	DataManager::SetValue("tw_operation", Display_Text);
+	DataManager::SetValue("tw_partition", "");
+}
+
+void TWFunc::GUI_Operation_Text(string Read_Value, string Partition_Name, string Default_Text) {
+	string Display_Text;
+
+	DataManager::GetValue(Read_Value, Display_Text);
+	if (Display_Text.empty())
+		Display_Text = Default_Text;
+
+	DataManager::SetValue("tw_operation", Display_Text);
+	DataManager::SetValue("tw_partition", Partition_Name);
+}
+
+void TWFunc::Copy_Log(string Source, string Destination) {
+	PartitionManager.Mount_By_Path(Destination, false);
+	FILE *destination_log = fopen(Destination.c_str(), "a");
+	if (destination_log == NULL) {
+		LOGERR("TWFunc::Copy_Log -- Can't open destination log file: '%s'\n", Destination.c_str());
+	} else {
+		FILE *source_log = fopen(Source.c_str(), "r");
+		if (source_log != NULL) {
+			fseek(source_log, Log_Offset, SEEK_SET);
+			char buffer[4096];
+			while (fgets(buffer, sizeof(buffer), source_log))
+				fputs(buffer, destination_log); // Buffered write of log file
+			Log_Offset = ftell(source_log);
+			fflush(source_log);
+			fclose(source_log);
+		}
+		fflush(destination_log);
+		fclose(destination_log);
+	}
+}
+
+void TWFunc::Update_Log_File(void) {
+	// Copy logs to cache so the system can find out what happened.
+	if (PartitionManager.Mount_By_Path("/cache", false)) {
+		if (!TWFunc::Path_Exists("/cache/recovery/.")) {
+			LOGINFO("Recreating /cache/recovery folder.\n");
+			if (mkdir("/cache/recovery", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0)
+				LOGINFO("Unable to create /cache/recovery folder.\n");
+		}
+		Copy_Log(TMP_LOG_FILE, "/cache/recovery/log");
+		copy_file("/cache/recovery/log", "/cache/recovery/last_log", 600);
+		chown("/cache/recovery/log", 1000, 1000);
+		chmod("/cache/recovery/log", 0600);
+		chmod("/cache/recovery/last_log", 0640);
+	} else {
+		LOGINFO("Failed to mount /cache for TWFunc::Update_Log_File\n");
+	}
+
+	// Reset bootloader message
+	TWPartition* Part = PartitionManager.Find_Partition_By_Path("/misc");
+	if (Part != NULL) {
+		struct bootloader_message boot;
+		memset(&boot, 0, sizeof(boot));
+		if (Part->Current_File_System == "mtd") {
+			if (set_bootloader_message_mtd_name(&boot, Part->MTD_Name.c_str()) != 0)
+				LOGERR("Unable to set MTD bootloader message.\n");
+		} else if (Part->Current_File_System == "emmc") {
+			if (set_bootloader_message_block_name(&boot, Part->Actual_Block_Device.c_str()) != 0)
+				LOGERR("Unable to set emmc bootloader message.\n");
+		} else {
+			LOGERR("Unknown file system for /misc: '%s'\n", Part->Current_File_System.c_str());
+		}
+	}
+
+	if (PartitionManager.Mount_By_Path("/cache", true)) {
+		if (unlink("/cache/recovery/command") && errno != ENOENT) {
+			LOGINFO("Can't unlink %s\n", "/cache/recovery/command");
+		}
+	}
+
+	sync();
+}
+
+void TWFunc::Update_Intent_File(string Intent) {
+	if (PartitionManager.Mount_By_Path("/cache", false) && !Intent.empty()) {
+		TWFunc::write_file("/cache/recovery/intent", Intent);
+	}
+}
+
+// reboot: Reboot the system. Return -1 on error, no return on success
+int TWFunc::tw_reboot(RebootCommand command)
+{
+	// Always force a sync before we reboot
+	sync();
+
+	switch (command) {
+		case rb_current:
+		case rb_system:
+			Update_Log_File();
+			Update_Intent_File("s");
+			sync();
+			check_and_run_script("/sbin/rebootsystem.sh", "reboot system");
+			return reboot(RB_AUTOBOOT);
+		case rb_recovery:
+			check_and_run_script("/sbin/rebootrecovery.sh", "reboot recovery");
+			return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, (void*) "recovery");
+		case rb_bootloader:
+			check_and_run_script("/sbin/rebootbootloader.sh", "reboot bootloader");
+			return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, (void*) "bootloader");
+		case rb_poweroff:
+			check_and_run_script("/sbin/poweroff.sh", "power off");
+#ifdef ANDROID_RB_POWEROFF
+			android_reboot(ANDROID_RB_POWEROFF, 0, 0);
+#endif
+			return reboot(RB_POWER_OFF);
+		case rb_download:
+			check_and_run_script("/sbin/rebootdownload.sh", "reboot download");
+			return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, (void*) "download");
+		default:
+			return -1;
+	}
+	return -1;
+}
+
+void TWFunc::check_and_run_script(const char* script_file, const char* display_name)
+{
+	// Check for and run startup script if script exists
+	struct stat st;
+	if (stat(script_file, &st) == 0) {
+		gui_print("Running %s script...\n", display_name);
+		chmod(script_file, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+		TWFunc::Exec_Cmd(script_file);
+		gui_print("\nFinished running %s script.\n", display_name);
+	}
+}
+
+int TWFunc::removeDir(const string path, bool skipParent) {
+	DIR *d = opendir(path.c_str());
+	int r = 0;
+	string new_path;
+
+	if (d == NULL) {
+		LOGERR("Error opening '%s'\n", path.c_str());
+		return -1;
+	}
+
+	if (d) {
+		struct dirent *p;
+		while (!r && (p = readdir(d))) {
+			if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
+				continue;
+			new_path = path + "/";
+			new_path.append(p->d_name);
+			if (p->d_type == DT_DIR) {
+				r = removeDir(new_path, true);
+				if (!r) {
+					if (p->d_type == DT_DIR)
+						r = rmdir(new_path.c_str());
+					else
+						LOGINFO("Unable to removeDir '%s': %s\n", new_path.c_str(), strerror(errno));
+				}
+			} else if (p->d_type == DT_REG || p->d_type == DT_LNK || p->d_type == DT_FIFO || p->d_type == DT_SOCK) {
+				r = unlink(new_path.c_str());
+				if (r != 0) {
+					LOGINFO("Unable to unlink '%s: %s'\n", new_path.c_str(), strerror(errno));
+				}
+			}
+		}
+		closedir(d);
+
+		if (!r) {
+			if (skipParent)
+				return 0;
+			else
+				r = rmdir(path.c_str());
+		}
+	}
+	return r;
+}
+
+int TWFunc::copy_file(string src, string dst, int mode) {
+	LOGINFO("Copying file %s to %s\n", src.c_str(), dst.c_str());
+	ifstream srcfile(src.c_str(), ios::binary);
+	ofstream dstfile(dst.c_str(), ios::binary);
+	dstfile << srcfile.rdbuf();
+	srcfile.close();
+	dstfile.close();
+	if (chmod(dst.c_str(), mode) != 0)
+		return -1;
+	return 0;
+}
+
+unsigned int TWFunc::Get_D_Type_From_Stat(string Path) {
+	struct stat st;
+
+	stat(Path.c_str(), &st);
+	if (st.st_mode & S_IFDIR)
+		return DT_DIR;
+	else if (st.st_mode & S_IFBLK)
+		return DT_BLK;
+	else if (st.st_mode & S_IFCHR)
+		return DT_CHR;
+	else if (st.st_mode & S_IFIFO)
+		return DT_FIFO;
+	else if (st.st_mode & S_IFLNK)
+		return DT_LNK;
+	else if (st.st_mode & S_IFREG)
+		return DT_REG;
+	else if (st.st_mode & S_IFSOCK)
+		return DT_SOCK;
+	return DT_UNKNOWN;
+}
+
+int TWFunc::read_file(string fn, string& results) {
+	ifstream file;
+	file.open(fn.c_str(), ios::in);
+
+	if (file.is_open()) {
+		file >> results;
+		file.close();
+		return 0;
+	}
+
+	LOGINFO("Cannot find file %s\n", fn.c_str());
+	return -1;
+}
+
+int TWFunc::read_file(string fn, vector<string>& results) {
+	ifstream file;
+	string line;
+	file.open(fn.c_str(), ios::in);
+	if (file.is_open()) {
+		while (getline(file, line))
+			results.push_back(line);
+		file.close();
+		return 0;
+	}
+	LOGINFO("Cannot find file %s\n", fn.c_str());
+	return -1;
+}
+
+int TWFunc::read_file(string fn, uint64_t& results) {
+	ifstream file;
+	file.open(fn.c_str(), ios::in);
+
+	if (file.is_open()) {
+		file >> results;
+		file.close();
+		return 0;
+	}
+
+	LOGINFO("Cannot find file %s\n", fn.c_str());
+	return -1;
+}
+
+int TWFunc::write_file(string fn, string& line) {
+	FILE *file;
+	file = fopen(fn.c_str(), "w");
+	if (file != NULL) {
+		fwrite(line.c_str(), line.size(), 1, file);
+		fclose(file);
+		return 0;
+	}
+	LOGINFO("Cannot find file %s\n", fn.c_str());
+	return -1;
+}
+
+timespec TWFunc::timespec_diff(timespec& start, timespec& end)
+{
+	timespec temp;
+	if ((end.tv_nsec-start.tv_nsec)<0) {
+		temp.tv_sec = end.tv_sec-start.tv_sec-1;
+		temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
+	} else {
+		temp.tv_sec = end.tv_sec-start.tv_sec;
+		temp.tv_nsec = end.tv_nsec-start.tv_nsec;
+	}
+	return temp;
+}
+
+int32_t TWFunc::timespec_diff_ms(timespec& start, timespec& end)
+{
+	return ((end.tv_sec * 1000) + end.tv_nsec/1000000) -
+			((start.tv_sec * 1000) + start.tv_nsec/1000000);
+}
+
+int TWFunc::drop_caches(void) {
+	string file = "/proc/sys/vm/drop_caches";
+	string value = "3";
+	if (write_file(file, value) != 0)
+		return -1;
+	return 0;
+}
+
+int TWFunc::Check_su_Perms(void) {
+	struct stat st;
+	int ret = 0;
+
+	if (!PartitionManager.Mount_By_Path("/system", false))
+		return 0;
+
+	// Check to ensure that perms are 6755 for all 3 file locations
+	if (stat("/system/bin/su", &st) == 0) {
+		if ((st.st_mode & (S_ISUID | S_ISGID | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != (S_ISUID | S_ISGID | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) || st.st_uid != 0 || st.st_gid != 0) {
+			ret = 1;
+		}
+	}
+	if (stat("/system/xbin/su", &st) == 0) {
+		if ((st.st_mode & (S_ISUID | S_ISGID | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != (S_ISUID | S_ISGID | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) || st.st_uid != 0 || st.st_gid != 0) {
+			ret += 2;
+		}
+	}
+	if (stat("/system/bin/.ext/.su", &st) == 0) {
+		if ((st.st_mode & (S_ISUID | S_ISGID | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != (S_ISUID | S_ISGID | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) || st.st_uid != 0 || st.st_gid != 0) {
+			ret += 4;
+		}
+	}
+	return ret;
+}
+
+bool TWFunc::Fix_su_Perms(void) {
+	if (!PartitionManager.Mount_By_Path("/system", true))
+		return false;
+
+	string propvalue = System_Property_Get("ro.build.version.sdk");
+	string su_perms = "6755";
+	if (!propvalue.empty()) {
+		int sdk_version = atoi(propvalue.c_str());
+		if (sdk_version >= 18)
+			su_perms = "0755";
+	}
+
+	string file = "/system/bin/su";
+	if (TWFunc::Path_Exists(file)) {
+		if (chown(file.c_str(), 0, 0) != 0) {
+			LOGERR("Failed to chown '%s'\n", file.c_str());
+			return false;
+		}
+		if (tw_chmod(file, su_perms) != 0) {
+			LOGERR("Failed to chmod '%s'\n", file.c_str());
+			return false;
+		}
+	}
+	file = "/system/xbin/su";
+	if (TWFunc::Path_Exists(file)) {
+		if (chown(file.c_str(), 0, 0) != 0) {
+			LOGERR("Failed to chown '%s'\n", file.c_str());
+			return false;
+		}
+		if (tw_chmod(file, su_perms) != 0) {
+			LOGERR("Failed to chmod '%s'\n", file.c_str());
+			return false;
+		}
+	}
+	file = "/system/xbin/daemonsu";
+	if (TWFunc::Path_Exists(file)) {
+		if (chown(file.c_str(), 0, 0) != 0) {
+			LOGERR("Failed to chown '%s'\n", file.c_str());
+			return false;
+		}
+		if (tw_chmod(file, "0755") != 0) {
+			LOGERR("Failed to chmod '%s'\n", file.c_str());
+			return false;
+		}
+	}
+	file = "/system/bin/.ext/.su";
+	if (TWFunc::Path_Exists(file)) {
+		if (chown(file.c_str(), 0, 0) != 0) {
+			LOGERR("Failed to chown '%s'\n", file.c_str());
+			return false;
+		}
+		if (tw_chmod(file, su_perms) != 0) {
+			LOGERR("Failed to chmod '%s'\n", file.c_str());
+			return false;
+		}
+	}
+	file = "/system/etc/install-recovery.sh";
+	if (TWFunc::Path_Exists(file)) {
+		if (chown(file.c_str(), 0, 0) != 0) {
+			LOGERR("Failed to chown '%s'\n", file.c_str());
+			return false;
+		}
+		if (tw_chmod(file, "0755") != 0) {
+			LOGERR("Failed to chmod '%s'\n", file.c_str());
+			return false;
+		}
+	}
+	file = "/system/etc/init.d/99SuperSUDaemon";
+	if (TWFunc::Path_Exists(file)) {
+		if (chown(file.c_str(), 0, 0) != 0) {
+			LOGERR("Failed to chown '%s'\n", file.c_str());
+			return false;
+		}
+		if (tw_chmod(file, "0755") != 0) {
+			LOGERR("Failed to chmod '%s'\n", file.c_str());
+			return false;
+		}
+	}
+	file = "/system/app/Superuser.apk";
+	if (TWFunc::Path_Exists(file)) {
+		if (chown(file.c_str(), 0, 0) != 0) {
+			LOGERR("Failed to chown '%s'\n", file.c_str());
+			return false;
+		}
+		if (tw_chmod(file, "0644") != 0) {
+			LOGERR("Failed to chmod '%s'\n", file.c_str());
+			return false;
+		}
+	}
+	sync();
+	if (!PartitionManager.UnMount_By_Path("/system", true))
+		return false;
+	return true;
+}
+
+int TWFunc::tw_chmod(const string& fn, const string& mode) {
+	long mask = 0;
+	std::string::size_type n = mode.length();
+	int cls = 0;
+
+	if(n == 3)
+		++cls;
+	else if(n != 4)
+	{
+		LOGERR("TWFunc::tw_chmod used with %u long mode string (should be 3 or 4)!\n", mode.length());
+		return -1;
+	}
+
+	for (n = 0; n < mode.length(); ++n, ++cls) {
+		if (cls == 0) {
+			if (mode[n] == '0')
+				continue;
+			else if (mode[n] == '1')
+				mask |= S_ISVTX;
+			else if (mode[n] == '2')
+				mask |= S_ISGID;
+			else if (mode[n] == '4')
+				mask |= S_ISUID;
+			else if (mode[n] == '5') {
+				mask |= S_ISVTX;
+				mask |= S_ISUID;
+			}
+			else if (mode[n] == '6') {
+				mask |= S_ISGID;
+				mask |= S_ISUID;
+			}
+			else if (mode[n] == '7') {
+				mask |= S_ISVTX;
+				mask |= S_ISGID;
+				mask |= S_ISUID;
+			}
+		}
+		else if (cls == 1) {
+			if (mode[n] == '7') {
+				mask |= S_IRWXU;
+			}
+			else if (mode[n] == '6') {
+				mask |= S_IRUSR;
+				mask |= S_IWUSR;
+			}
+			else if (mode[n] == '5') {
+				mask |= S_IRUSR;
+				mask |= S_IXUSR;
+			}
+			else if (mode[n] == '4')
+				mask |= S_IRUSR;
+			else if (mode[n] == '3') {
+				mask |= S_IWUSR;
+				mask |= S_IRUSR;
+			}
+			else if (mode[n] == '2')
+				mask |= S_IWUSR;
+			else if (mode[n] == '1')
+				mask |= S_IXUSR;
+		}
+		else if (cls == 2) {
+			if (mode[n] == '7') {
+				mask |= S_IRWXG;
+			}
+			else if (mode[n] == '6') {
+				mask |= S_IRGRP;
+				mask |= S_IWGRP;
+			}
+			else if (mode[n] == '5') {
+				mask |= S_IRGRP;
+				mask |= S_IXGRP;
+			}
+			else if (mode[n] == '4')
+				mask |= S_IRGRP;
+			else if (mode[n] == '3') {
+				mask |= S_IWGRP;
+				mask |= S_IXGRP;
+			}
+			else if (mode[n] == '2')
+				mask |= S_IWGRP;
+			else if (mode[n] == '1')
+				mask |= S_IXGRP;
+		}
+		else if (cls == 3) {
+			if (mode[n] == '7') {
+				mask |= S_IRWXO;
+			}
+			else if (mode[n] == '6') {
+				mask |= S_IROTH;
+				mask |= S_IWOTH;
+			}
+			else if (mode[n] == '5') {
+				mask |= S_IROTH;
+				mask |= S_IXOTH;
+			}
+			else if (mode[n] == '4')
+				mask |= S_IROTH;
+			else if (mode[n] == '3') {
+				mask |= S_IWOTH;
+				mask |= S_IXOTH;
+			}
+			else if (mode[n] == '2')
+				mask |= S_IWOTH;
+			else if (mode[n] == '1')
+				mask |= S_IXOTH;
+		}
+	}
+
+	if (chmod(fn.c_str(), mask) != 0) {
+		LOGERR("Unable to chmod '%s' %l\n", fn.c_str(), mask);
+		return -1;
+	}
+
+	return 0;
+}
+
+bool TWFunc::Install_SuperSU(void) {
+	if (!PartitionManager.Mount_By_Path("/system", true))
+		return false;
+
+	TWFunc::Exec_Cmd("/sbin/chattr -i /system/xbin/su");
+	if (copy_file("/supersu/su", "/system/xbin/su", 0755) != 0) {
+		LOGERR("Failed to copy su binary to /system/bin\n");
+		return false;
+	}
+	if (!Path_Exists("/system/bin/.ext")) {
+		mkdir("/system/bin/.ext", 0777);
+	}
+	TWFunc::Exec_Cmd("/sbin/chattr -i /system/bin/.ext/su");
+	if (copy_file("/supersu/su", "/system/bin/.ext/su", 0755) != 0) {
+		LOGERR("Failed to copy su binary to /system/bin/.ext/su\n");
+		return false;
+	}
+	TWFunc::Exec_Cmd("/sbin/chattr -i /system/xbin/daemonsu");
+	if (copy_file("/supersu/su", "/system/xbin/daemonsu", 0755) != 0) {
+		LOGERR("Failed to copy su binary to /system/xbin/daemonsu\n");
+		return false;
+	}
+	if (Path_Exists("/system/etc/init.d")) {
+		TWFunc::Exec_Cmd("/sbin/chattr -i /system/etc/init.d/99SuperSUDaemon");
+		if (copy_file("/supersu/99SuperSUDaemon", "/system/etc/init.d/99SuperSUDaemon", 0755) != 0) {
+			LOGERR("Failed to copy 99SuperSUDaemon to /system/etc/init.d/99SuperSUDaemon\n");
+			return false;
+		}
+	} else {
+		TWFunc::Exec_Cmd("/sbin/chattr -i /system/etc/install-recovery.sh");
+		if (copy_file("/supersu/install-recovery.sh", "/system/etc/install-recovery.sh", 0755) != 0) {
+			LOGERR("Failed to copy install-recovery.sh to /system/etc/install-recovery.sh\n");
+			return false;
+		}
+	}
+	if (copy_file("/supersu/Superuser.apk", "/system/app/Superuser.apk", 0644) != 0) {
+		LOGERR("Failed to copy Superuser app to /system/app\n");
+		return false;
+	}
+	if (!Fix_su_Perms())
+		return false;
+	return true;
+}
+
+bool TWFunc::Try_Decrypting_Backup(string Restore_Path, string Password) {
+	DIR* d;
+
+	string Filename;
+	Restore_Path += "/";
+	d = opendir(Restore_Path.c_str());
+	if (d == NULL) {
+		LOGERR("Error opening '%s'\n", Restore_Path.c_str());
+		return false;
+	}
+
+	struct dirent* de;
+	while ((de = readdir(d)) != NULL) {
+		Filename = Restore_Path;
+		Filename += de->d_name;
+		if (TWFunc::Get_File_Type(Filename) == 2) {
+			if (TWFunc::Try_Decrypting_File(Filename, Password) < 2) {
+				DataManager::SetValue("tw_restore_password", ""); // Clear the bad password
+				DataManager::SetValue("tw_restore_display", "");  // Also clear the display mask
+				closedir(d);
+				return false;
+			}
+		}
+	}
+	closedir(d);
+	return true;
+}
+
+string TWFunc::Get_Current_Date() {
+	string Current_Date;
+	time_t seconds = time(0);
+	struct tm *t = localtime(&seconds);
+	char timestamp[255];
+	sprintf(timestamp,"%04d-%02d-%02d--%02d-%02d-%02d",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
+	Current_Date = timestamp;
+	return Current_Date;
+}
+
+string TWFunc::System_Property_Get(string Prop_Name) {
+	bool mount_state = PartitionManager.Is_Mounted_By_Path("/system");
+	std::vector<string> buildprop;
+	string propvalue;
+	if (!PartitionManager.Mount_By_Path("/system", true))
+		return propvalue;
+	if (TWFunc::read_file("/system/build.prop", buildprop) != 0) {
+		LOGINFO("Unable to open /system/build.prop for getting '%s'.\n", Prop_Name.c_str());
+		DataManager::SetValue(TW_BACKUP_NAME, Get_Current_Date());
+		if (!mount_state)
+			PartitionManager.UnMount_By_Path("/system", false);
+		return propvalue;
+	}
+	int line_count = buildprop.size();
+	int index;
+	size_t start_pos = 0, end_pos;
+	string propname;
+	for (index = 0; index < line_count; index++) {
+		end_pos = buildprop.at(index).find("=", start_pos);
+		propname = buildprop.at(index).substr(start_pos, end_pos);
+		if (propname == Prop_Name) {
+			propvalue = buildprop.at(index).substr(end_pos + 1, buildprop.at(index).size());
+			if (!mount_state)
+				PartitionManager.UnMount_By_Path("/system", false);
+			return propvalue;
+		}
+	}
+	if (!mount_state)
+		PartitionManager.UnMount_By_Path("/system", false);
+	return propvalue;
+}
+
+void TWFunc::Auto_Generate_Backup_Name() {
+	string propvalue = System_Property_Get("ro.build.display.id");
+	if (propvalue.empty()) {
+		DataManager::SetValue(TW_BACKUP_NAME, Get_Current_Date());
+		return;
+	}
+	string Backup_Name = Get_Current_Date();
+	Backup_Name += " " + propvalue;
+	if (Backup_Name.size() > MAX_BACKUP_NAME_LEN)
+		Backup_Name.resize(MAX_BACKUP_NAME_LEN);
+	// Trailing spaces cause problems on some file systems, so remove them
+	string space_check, space = " ";
+	space_check = Backup_Name.substr(Backup_Name.size() - 1, 1);
+	while (space_check == space) {
+		Backup_Name.resize(Backup_Name.size() - 1);
+		space_check = Backup_Name.substr(Backup_Name.size() - 1, 1);
+	}
+	DataManager::SetValue(TW_BACKUP_NAME, Backup_Name);
+	if (PartitionManager.Check_Backup_Name(false) != 0) {
+		LOGINFO("Auto generated backup name '%s' contains invalid characters, using date instead.\n", Backup_Name.c_str());
+		DataManager::SetValue(TW_BACKUP_NAME, Get_Current_Date());
+	}
+}
+
+void TWFunc::Fixup_Time_On_Boot()
+{
+#ifdef QCOM_RTC_FIX
+
+	LOGINFO("TWFunc::Fixup_Time: Pre-fix date and time: %s\n", TWFunc::Get_Current_Date().c_str());
+
+	struct timeval tv;
+	uint64_t offset = 0;
+	std::string sepoch = "/sys/class/rtc/rtc0/since_epoch";
+
+	if (TWFunc::read_file(sepoch, offset) == 0) {
+
+		LOGINFO("TWFunc::Fixup_Time: Setting time offset from file %s\n", sepoch.c_str());
+
+		tv.tv_sec = offset;
+		tv.tv_usec = 0;
+		settimeofday(&tv, NULL);
+
+		gettimeofday(&tv, NULL);
+
+		if (tv.tv_sec > 1405209403) { // Anything older then 12 Jul 2014 23:56:43 GMT will do nicely thank you ;)
+
+			LOGINFO("TWFunc::Fixup_Time: Date and time corrected: %s\n", TWFunc::Get_Current_Date().c_str());
+			return;
+
+		}
+
+	} else {
+
+		LOGINFO("TWFunc::Fixup_Time: opening %s failed\n", sepoch.c_str());
+
+	}
+
+	LOGINFO("TWFunc::Fixup_Time: will attempt to use the ats files now.\n", sepoch.c_str());
+
+	// Devices with Qualcomm Snapdragon 800 do some shenanigans with RTC.
+	// They never set it, it just ticks forward from 1970-01-01 00:00,
+	// and then they have files /data/system/time/ats_* with 64bit offset
+	// in miliseconds which, when added to the RTC, gives the correct time.
+	// So, the time is: (offset_from_ats + value_from_RTC)
+	// There are multiple ats files, they are for different systems? Bases?
+	// Like, ats_1 is for modem and ats_2 is for TOD (time of day?).
+	// Look at file time_genoff.h in CodeAurora, qcom-opensource/time-services
+
+	static const char *paths[] = { "/data/system/time/", "/data/time/"  };
+
+	FILE *f;
+	DIR *d;
+	offset = 0;
+	struct dirent *dt;
+	std::string ats_path;
+
+	if(!PartitionManager.Mount_By_Path("/data", false))
+		return;
+
+	// Prefer ats_2, it seems to be the one we want according to logcat on hammerhead
+	// - it is the one for ATS_TOD (time of day?).
+	// However, I never saw a device where the offset differs between ats files.
+	for(size_t i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i)
+	{
+		DIR *d = opendir(paths[i]);
+		if(!d)
+			continue;
+
+		while((dt = readdir(d)))
+		{
+			if(dt->d_type != DT_REG || strncmp(dt->d_name, "ats_", 4) != 0)
+				continue;
+
+			if(ats_path.empty() || strcmp(dt->d_name, "ats_2") == 0)
+				ats_path = std::string(paths[i]).append(dt->d_name);
+		}
+
+		closedir(d);
+	}
+
+	if(ats_path.empty())
+	{
+		LOGINFO("TWFunc::Fixup_Time: no ats files found, leaving untouched!\n");
+		return;
+	}
+
+	f = fopen(ats_path.c_str(), "r");
+	if(!f)
+	{
+		LOGINFO("TWFunc::Fixup_Time: failed to open file %s\n", ats_path.c_str());
+		return;
+	}
+
+	if(fread(&offset, sizeof(offset), 1, f) != 1)
+	{
+		LOGINFO("TWFunc::Fixup_Time: failed load uint64 from file %s\n", ats_path.c_str());
+		fclose(f);
+		return;
+	}
+	fclose(f);
+
+	LOGINFO("TWFunc::Fixup_Time: Setting time offset from file %s, offset %llu\n", ats_path.c_str(), offset);
+
+	gettimeofday(&tv, NULL);
+
+	tv.tv_sec += offset/1000;
+	tv.tv_usec += (offset%1000)*1000;
+
+	while(tv.tv_usec >= 1000000)
+	{
+		++tv.tv_sec;
+		tv.tv_usec -= 1000000;
+	}
+
+	settimeofday(&tv, NULL);
+
+	LOGINFO("TWFunc::Fixup_Time: Date and time corrected: %s\n", TWFunc::Get_Current_Date().c_str());
+
+#endif
+}
+
+std::vector<std::string> TWFunc::Split_String(const std::string& str, const std::string& delimiter, bool removeEmpty)
+{
+	std::vector<std::string> res;
+	size_t idx = 0, idx_last = 0;
+
+	while(idx < str.size())
+	{
+		idx = str.find_first_of(delimiter, idx_last);
+		if(idx == std::string::npos)
+			idx = str.size();
+
+		if(idx-idx_last != 0 || !removeEmpty)
+			res.push_back(str.substr(idx_last, idx-idx_last));
+
+		idx_last = idx + delimiter.size();
+	}
+
+	return res;
+}
+
+bool TWFunc::Create_Dir_Recursive(const std::string& path, mode_t mode, uid_t uid, gid_t gid)
+{
+	std::vector<std::string> parts = Split_String(path, "/");
+	std::string cur_path;
+	struct stat info;
+	for(size_t i = 0; i < parts.size(); ++i)
+	{
+		cur_path += "/" + parts[i];
+		if(stat(cur_path.c_str(), &info) < 0 || !S_ISDIR(info.st_mode))
+		{
+			if(mkdir(cur_path.c_str(), mode) < 0)
+				return false;
+			chown(cur_path.c_str(), uid, gid);
+		}
+	}
+	return true;
+}
+
+int TWFunc::Set_Brightness(std::string brightness_value)
+{
+
+	std::string brightness_file = DataManager::GetStrValue("tw_brightness_file");;
+
+	if (brightness_file.compare("/nobrightness") != 0) {
+		std::string secondary_brightness_file = DataManager::GetStrValue("tw_secondary_brightness_file");
+		LOGINFO("TWFunc::Set_Brightness: Setting brightness control to %s\n", brightness_value.c_str());
+		int result = TWFunc::write_file(brightness_file, brightness_value);
+		if (secondary_brightness_file != "") {
+			LOGINFO("TWFunc::Set_Brightness: Setting SECONDARY brightness control to %s\n", brightness_value.c_str());
+			TWFunc::write_file(secondary_brightness_file, brightness_value);
+		}
+		return result;
+	}
+	return -1;
+}
+
+bool TWFunc::Toggle_MTP(bool enable) {
+#ifdef TW_HAS_MTP
+	static int was_enabled = false;
+
+	if (enable && was_enabled) {
+		if (!PartitionManager.Enable_MTP())
+			PartitionManager.Disable_MTP();
+	} else {
+		was_enabled = DataManager::GetIntValue("tw_mtp_enabled");
+		PartitionManager.Disable_MTP();
+		usleep(500);
+	}
+	return was_enabled;
+#else
+	return false;
+#endif
+}
+
+void TWFunc::SetPerformanceMode(bool mode) {
+	if (mode) {
+		property_set("recovery.perf.mode", "1");
+	} else {
+		property_set("recovery.perf.mode", "0");
+	}
+	// Some time for events to catch up to init handlers
+	usleep(500000);
+}
+
+std::string TWFunc::to_string(unsigned long value) {
+	std::ostringstream os;
+	os << value;
+	return os.str();
+}
+
+#endif // ndef BUILD_TWRPTAR_MAIN
diff --git a/twrp-functions.hpp b/twrp-functions.hpp
new file mode 100644
index 0000000..1cc531c
--- /dev/null
+++ b/twrp-functions.hpp
@@ -0,0 +1,103 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TWRPFUNCTIONS_HPP
+#define _TWRPFUNCTIONS_HPP
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+typedef enum
+{
+	rb_current = 0,
+	rb_system,
+	rb_recovery,
+	rb_poweroff,
+	rb_bootloader,     // May also be fastboot
+	rb_download,
+} RebootCommand;
+
+// Partition class
+class TWFunc
+{
+public:
+	static string Get_Root_Path(string Path);                                   // Trims any trailing folders or filenames from the path, also adds a leading / if not present
+	static string Get_Path(string Path);                                        // Trims everything after the last / in the string
+	static string Get_Filename(string Path);                                    // Trims the path off of a filename
+
+	static int Exec_Cmd(const string& cmd, string &result);                     //execute a command and return the result as a string by reference
+	static int Exec_Cmd(const string& cmd);                                     //execute a command
+	static int Wait_For_Child(pid_t pid, int *status, string Child_Name);       // Waits for pid to exit and checks exit status
+	static bool Path_Exists(string Path);                                       // Returns true if the path exists
+	static int Get_File_Type(string fn); // Determines file type, 0 for unknown, 1 for gzip, 2 for OAES encrypted
+	static int Try_Decrypting_File(string fn, string password); // -1 for some error, 0 for failed to decrypt, 1 for decrypted, 3 for decrypted and found gzip format
+	static unsigned long Get_File_Size(string Path);                            // Returns the size of a file
+	static std::string Remove_Trailing_Slashes(const std::string& path, bool leaveLast = false); // Normalizes the path, e.g /data//media/ -> /data/media
+	static vector<string> split_string(const string &in, char del, bool skip_empty);
+
+#ifndef BUILD_TWRPTAR_MAIN
+	static void install_htc_dumlock(void);                                      // Installs HTC Dumlock
+	static void htc_dumlock_restore_original_boot(void);                        // Restores the backup of boot from HTC Dumlock
+	static void htc_dumlock_reflash_recovery_to_boot(void);                     // Reflashes the current recovery to boot
+	static int Recursive_Mkdir(string Path);                                    // Recursively makes the entire path
+	static void GUI_Operation_Text(string Read_Value, string Default_Text);     // Updates text for display in the GUI, e.g. Backing up %partition name%
+	static void GUI_Operation_Text(string Read_Value, string Partition_Name, string Default_Text); // Same as above but includes partition name
+	static void Update_Log_File(void);                                          // Writes the log to last_log
+	static void Update_Intent_File(string Intent);                              // Updates intent file
+	static int tw_reboot(RebootCommand command);                                // Prepares the device for rebooting
+	static void check_and_run_script(const char* script_file, const char* display_name); // checks for the existence of a script, chmods it to 755, then runs it
+	static int removeDir(const string path, bool removeParent); //recursively remove a directory
+	static int copy_file(string src, string dst, int mode); //copy file from src to dst with mode permissions
+	static unsigned int Get_D_Type_From_Stat(string Path);                      // Returns a dirent dt_type value using stat instead of dirent
+	static timespec timespec_diff(timespec& start, timespec& end);	            // Return a diff for 2 times
+	static int32_t timespec_diff_ms(timespec& start, timespec& end);            // Returns diff in ms
+	static int read_file(string fn, vector<string>& results); //read from file
+	static int read_file(string fn, string& results); //read from file
+	static int read_file(string fn, uint64_t& results); //read from file
+	static int write_file(string fn, string& line); //write from file
+	static int drop_caches(void); //drop linux cache memory
+	static int Check_su_Perms(void); // check perms and owner of su binary in various locations
+	static bool Fix_su_Perms(void); // sets proper permissions for su binaries and superuser apk
+	static int tw_chmod(const string& fn, const string& mode); // chmod function that converts a 3 or 4 char string into st_mode automatically
+	static bool Install_SuperSU(void); // Installs su binary and apk and sets proper permissions
+	static bool Try_Decrypting_Backup(string Restore_Path, string Password); // true for success, false for failed to decrypt
+	static string System_Property_Get(string Prop_Name);                // Returns value of Prop_Name from reading /system/build.prop
+	static string Get_Current_Date(void);                               // Returns the current date in ccyy-m-dd--hh-nn-ss format
+	static void Auto_Generate_Backup_Name();                            // Populates TW_BACKUP_NAME with a backup name based on current date and ro.build.display.id from /system/build.prop
+	static void Fixup_Time_On_Boot(); // Fixes time on devices which need it
+	static std::vector<std::string> Split_String(const std::string& str, const std::string& delimiter, bool removeEmpty = true); // Splits string by delimiter
+	static bool Create_Dir_Recursive(const std::string& path, mode_t mode = 0755, uid_t uid = -1, gid_t gid = -1);  // Create directory and it's parents, if they don't exist. mode, uid and gid are set to all _newly_ created folders. If whole path exists, do nothing.
+	static int Set_Brightness(std::string brightness_value); // Well, you can read, it does what it says, passing return int from TWFunc::Write_File ;)
+	static bool Toggle_MTP(bool enable);                                        // Disables MTP if enable is false and re-enables MTP if enable is true and it was enabled the last time it was toggled off
+	static std::string to_string(unsigned long value); //convert ul to string
+
+	static void SetPerformanceMode(bool mode); // support recovery.perf.mode
+
+private:
+	static void Copy_Log(string Source, string Destination);
+
+};
+
+extern int Log_Offset;
+#else
+};
+#endif // ndef BUILD_TWRPTAR_MAIN
+
+#endif // _TWRPFUNCTIONS_HPP
diff --git a/twrp.cpp b/twrp.cpp
new file mode 100644
index 0000000..2849af7
--- /dev/null
+++ b/twrp.cpp
@@ -0,0 +1,374 @@
+/*
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "cutils/properties.h"
+extern "C" {
+#include "minadbd/adb.h"
+#include "bootloader.h"
+}
+
+#ifdef ANDROID_RB_RESTART
+#include "cutils/android_reboot.h"
+#else
+#include <sys/reboot.h>
+#endif
+
+extern "C" {
+#include "gui/gui.h"
+}
+#include "twcommon.h"
+#include "twrp-functions.hpp"
+#include "data.hpp"
+#include "partitions.hpp"
+#include "openrecoveryscript.hpp"
+#include "variables.h"
+#include "twrpDU.hpp"
+
+#ifdef HAVE_SELINUX
+#include "selinux/label.h"
+struct selabel_handle *selinux_handle;
+#endif
+
+TWPartitionManager PartitionManager;
+int Log_Offset;
+bool datamedia;
+twrpDU du;
+
+static void Print_Prop(const char *key, const char *name, void *cookie) {
+	printf("%s=%s\n", key, name);
+}
+
+int main(int argc, char **argv) {
+	// Recovery needs to install world-readable files, so clear umask
+	// set by init
+	umask(0);
+
+	Log_Offset = 0;
+
+	// Set up temporary log file (/tmp/recovery.log)
+	freopen(TMP_LOG_FILE, "a", stdout);
+	setbuf(stdout, NULL);
+	freopen(TMP_LOG_FILE, "a", stderr);
+	setbuf(stderr, NULL);
+
+	// Handle ADB sideload
+	if (argc == 3 && strcmp(argv[1], "--adbd") == 0) {
+		adb_main(argv[2]);
+		return 0;
+	}
+
+#ifdef RECOVERY_SDCARD_ON_DATA
+	datamedia = true;
+#endif
+
+	char crash_prop_val[PROPERTY_VALUE_MAX];
+	int crash_counter;
+	property_get("twrp.crash_counter", crash_prop_val, "-1");
+	crash_counter = atoi(crash_prop_val) + 1;
+	snprintf(crash_prop_val, sizeof(crash_prop_val), "%d", crash_counter);
+	property_set("twrp.crash_counter", crash_prop_val);
+	property_set("ro.twrp.boot", "1");
+	property_set("ro.twrp.version", TW_VERSION_STR);
+
+	time_t StartupTime = time(NULL);
+	printf("Starting TWRP %s on %s (pid %d)", TW_VERSION_STR, ctime(&StartupTime), getpid());
+
+	// Load default values to set DataManager constants and handle ifdefs
+	DataManager::SetDefaultValues();
+	printf("Starting the UI...");
+	gui_init();
+	printf("=> Linking mtab\n");
+	symlink("/proc/mounts", "/etc/mtab");
+	if (TWFunc::Path_Exists("/etc/twrp.fstab")) {
+		if (TWFunc::Path_Exists("/etc/recovery.fstab")) {
+			printf("Renaming regular /etc/recovery.fstab -> /etc/recovery.fstab.bak\n");
+			rename("/etc/recovery.fstab", "/etc/recovery.fstab.bak");
+		}
+		printf("Moving /etc/twrp.fstab -> /etc/recovery.fstab\n");
+		rename("/etc/twrp.fstab", "/etc/recovery.fstab");
+	}
+	printf("=> Processing recovery.fstab\n");
+	if (!PartitionManager.Process_Fstab("/etc/recovery.fstab", 1)) {
+		LOGERR("Failing out of recovery due to problem with recovery.fstab.\n");
+		return -1;
+	}
+	PartitionManager.Output_Partition_Logging();
+	// Load up all the resources
+	gui_loadResources();
+
+#ifdef HAVE_SELINUX
+	if (TWFunc::Path_Exists("/prebuilt_file_contexts")) {
+		if (TWFunc::Path_Exists("/file_contexts")) {
+			printf("Renaming regular /file_contexts -> /file_contexts.bak\n");
+			rename("/file_contexts", "/file_contexts.bak");
+		}
+		printf("Moving /prebuilt_file_contexts -> /file_contexts\n");
+		rename("/prebuilt_file_contexts", "/file_contexts");
+	}
+	struct selinux_opt selinux_options[] = {
+		{ SELABEL_OPT_PATH, "/file_contexts" }
+	};
+	selinux_handle = selabel_open(SELABEL_CTX_FILE, selinux_options, 1);
+	if (!selinux_handle)
+		printf("No file contexts for SELinux\n");
+	else
+		printf("SELinux contexts loaded from /file_contexts\n");
+	{ // Check to ensure SELinux can be supported by the kernel
+		char *contexts = NULL;
+
+		if (PartitionManager.Mount_By_Path("/cache", true) && TWFunc::Path_Exists("/cache/recovery")) {
+			lgetfilecon("/cache/recovery", &contexts);
+			if (!contexts) {
+				lsetfilecon("/cache/recovery", "test");
+				lgetfilecon("/cache/recovery", &contexts);
+			}
+		} else {
+			LOGINFO("Could not check /cache/recovery SELinux contexts, using /sbin/teamwin instead which may be inaccurate.\n");
+			lgetfilecon("/sbin/teamwin", &contexts);
+		}
+		if (!contexts) {
+			gui_print_color("warning", "Kernel does not have support for reading SELinux contexts.\n");
+		} else {
+			free(contexts);
+			gui_print("Full SELinux support is present.\n");
+		}
+	}
+#else
+	gui_print_color("warning", "No SELinux support (no libselinux).\n");
+#endif
+
+	PartitionManager.Mount_By_Path("/cache", true);
+
+	string Zip_File, Reboot_Value;
+	bool Cache_Wipe = false, Factory_Reset = false, Perform_Backup = false, Shutdown = false;
+
+	{
+		TWPartition* misc = PartitionManager.Find_Partition_By_Path("/misc");
+		if (misc != NULL) {
+			if (misc->Current_File_System == "emmc") {
+				set_device_type('e');
+				set_device_name(misc->Actual_Block_Device.c_str());
+			} else if (misc->Current_File_System == "mtd") {
+				set_device_type('m');
+				set_device_name(misc->MTD_Name.c_str());
+			} else {
+				LOGERR("Unknown file system for /misc\n");
+			}
+		}
+		get_args(&argc, &argv);
+
+		int index, index2, len;
+		char* argptr;
+		char* ptr;
+		printf("Startup Commands: ");
+		for (index = 1; index < argc; index++) {
+			argptr = argv[index];
+			printf(" '%s'", argv[index]);
+			len = strlen(argv[index]);
+			if (*argptr == '-') {argptr++; len--;}
+			if (*argptr == '-') {argptr++; len--;}
+			if (*argptr == 'u') {
+				ptr = argptr;
+				index2 = 0;
+				while (*ptr != '=' && *ptr != '\n')
+					ptr++;
+				// skip the = before grabbing Zip_File
+				while (*ptr == '=')
+					ptr++;
+				if (*ptr) {
+					Zip_File = ptr;
+				} else
+					LOGERR("argument error specifying zip file\n");
+			} else if (*argptr == 'w') {
+				if (len == 9)
+					Factory_Reset = true;
+				else if (len == 10)
+					Cache_Wipe = true;
+			} else if (*argptr == 'n') {
+				Perform_Backup = true;
+			} else if (*argptr == 'p') {
+				Shutdown = true;
+			} else if (*argptr == 's') {
+				ptr = argptr;
+				index2 = 0;
+				while (*ptr != '=' && *ptr != '\n')
+					ptr++;
+				if (*ptr) {
+					Reboot_Value = *ptr;
+				}
+			}
+		}
+		printf("\n");
+	}
+
+	if(crash_counter == 0) {
+		property_list(Print_Prop, NULL);
+		printf("\n");
+	} else {
+		printf("twrp.crash_counter=%d\n", crash_counter);
+	}
+
+	// Check for and run startup script if script exists
+	TWFunc::check_and_run_script("/sbin/runatboot.sh", "boot");
+	TWFunc::check_and_run_script("/sbin/postrecoveryboot.sh", "boot");
+
+#ifdef TW_INCLUDE_INJECTTWRP
+	// Back up TWRP Ramdisk if needed:
+	TWPartition* Boot = PartitionManager.Find_Partition_By_Path("/boot");
+	LOGINFO("Backing up TWRP ramdisk...\n");
+	if (Boot == NULL || Boot->Current_File_System != "emmc")
+		TWFunc::Exec_Cmd("injecttwrp --backup /tmp/backup_recovery_ramdisk.img");
+	else {
+		string injectcmd = "injecttwrp --backup /tmp/backup_recovery_ramdisk.img bd=" + Boot->Actual_Block_Device;
+		TWFunc::Exec_Cmd(injectcmd);
+	}
+	LOGINFO("Backup of TWRP ramdisk done.\n");
+#endif
+
+	bool Keep_Going = true;
+	if (Perform_Backup) {
+		DataManager::SetValue(TW_BACKUP_NAME, "(Auto Generate)");
+		if (!OpenRecoveryScript::Insert_ORS_Command("backup BSDCAE\n"))
+			Keep_Going = false;
+	}
+	if (Keep_Going && !Zip_File.empty()) {
+		string ORSCommand = "install " + Zip_File;
+
+		if (!OpenRecoveryScript::Insert_ORS_Command(ORSCommand))
+			Keep_Going = false;
+	}
+	if (Keep_Going) {
+		if (Factory_Reset) {
+			if (!OpenRecoveryScript::Insert_ORS_Command("wipe data\n"))
+				Keep_Going = false;
+		} else if (Cache_Wipe) {
+			if (!OpenRecoveryScript::Insert_ORS_Command("wipe cache\n"))
+				Keep_Going = false;
+		}
+	}
+
+	TWFunc::Update_Log_File();
+	// Offer to decrypt if the device is encrypted
+	if (DataManager::GetIntValue(TW_IS_ENCRYPTED) != 0) {
+		LOGINFO("Is encrypted, do decrypt page first\n");
+		if (gui_startPage("decrypt") != 0) {
+			LOGERR("Failed to start decrypt GUI page.\n");
+		}
+	}
+
+	// Read the settings file
+#ifdef TW_HAS_MTP
+	// We unmount partitions sometimes during early boot which may override
+	// the default of MTP being enabled by auto toggling MTP off. This
+	// will force it back to enabled then get overridden by the settings
+	// file, assuming that an entry for tw_mtp_enabled is set.
+	DataManager::SetValue("tw_mtp_enabled", 1);
+#endif
+	DataManager::ReadSettingsFile();
+
+	// Fixup the RTC clock on devices which require it
+	if(crash_counter == 0)
+		TWFunc::Fixup_Time_On_Boot();
+
+	// Run any outstanding OpenRecoveryScript
+	if (DataManager::GetIntValue(TW_IS_ENCRYPTED) == 0 && (TWFunc::Path_Exists(SCRIPT_FILE_TMP) || TWFunc::Path_Exists(SCRIPT_FILE_CACHE))) {
+		OpenRecoveryScript::Run_OpenRecoveryScript();
+	}
+
+#ifdef TW_HAS_MTP
+	// Enable MTP?
+	char mtp_crash_check[PROPERTY_VALUE_MAX];
+	property_get("mtp.crash_check", mtp_crash_check, "0");
+	if (strcmp(mtp_crash_check, "0") == 0) {
+		property_set("mtp.crash_check", "1");
+		if (DataManager::GetIntValue(TW_IS_ENCRYPTED) != 0) {
+			if (DataManager::GetIntValue(TW_IS_DECRYPTED) != 0 && DataManager::GetIntValue("tw_mtp_enabled") == 1) {
+				LOGINFO("Enabling MTP during startup\n");
+				if (!PartitionManager.Enable_MTP())
+					PartitionManager.Disable_MTP();
+				else
+					gui_print("MTP Enabled\n");
+			}
+		} else if (DataManager::GetIntValue("tw_mtp_enabled") == 1) {
+			LOGINFO("Enabling MTP during startup\n");
+			if (!PartitionManager.Enable_MTP())
+				PartitionManager.Disable_MTP();
+			else
+				gui_print("MTP Enabled\n");
+		}
+		property_set("mtp.crash_check", "0");
+	} else {
+		gui_print_color("warning", "MTP Crashed, not starting MTP on boot.\n");
+		DataManager::SetValue("tw_mtp_enabled", 0);
+	}
+#endif
+
+	// Launch the main GUI
+	gui_start();
+
+	// Check for su to see if the device is rooted or not
+	if (PartitionManager.Mount_By_Path("/system", false)) {
+		// Disable flashing of stock recovery
+		if (TWFunc::Path_Exists("/system/recovery-from-boot.p") && TWFunc::Path_Exists("/system/etc/install-recovery.sh")) {
+			rename("/system/recovery-from-boot.p", "/system/recovery-from-boot.bak");
+			gui_print("Renamed stock recovery file in /system to prevent\nthe stock ROM from replacing TWRP.\n");
+		}
+		if (TWFunc::Path_Exists("/supersu/su") && !TWFunc::Path_Exists("/system/bin/su") && !TWFunc::Path_Exists("/system/xbin/su") && !TWFunc::Path_Exists("/system/bin/.ext/.su")) {
+			// Device doesn't have su installed
+			DataManager::SetValue("tw_busy", 1);
+			if (gui_startPage("installsu") != 0) {
+				LOGERR("Failed to start SuperSU install page.\n");
+			}
+		} else if (TWFunc::Check_su_Perms() > 0) {
+			// su perms are set incorrectly
+			LOGINFO("Root permissions appear to be lost... fixing. (This will always happen on 4.3+ ROMs with SELinux.\n");
+			TWFunc::Fix_su_Perms();
+		}
+		sync();
+		PartitionManager.UnMount_By_Path("/system", false);
+	}
+
+	// Reboot
+	TWFunc::Update_Intent_File(Reboot_Value);
+	TWFunc::Update_Log_File();
+	gui_print("Rebooting...\n");
+	string Reboot_Arg;
+	DataManager::GetValue("tw_reboot_arg", Reboot_Arg);
+	if (Reboot_Arg == "recovery")
+		TWFunc::tw_reboot(rb_recovery);
+	else if (Reboot_Arg == "poweroff")
+		TWFunc::tw_reboot(rb_poweroff);
+	else if (Reboot_Arg == "bootloader")
+		TWFunc::tw_reboot(rb_bootloader);
+	else if (Reboot_Arg == "download")
+		TWFunc::tw_reboot(rb_download);
+	else
+		TWFunc::tw_reboot(rb_system);
+
+#ifdef ANDROID_RB_RESTART
+	android_reboot(ANDROID_RB_RESTART, 0, 0);
+#else
+	reboot(RB_AUTOBOOT);
+#endif
+	return 0;
+}
diff --git a/twrpDU.cpp b/twrpDU.cpp
new file mode 100644
index 0000000..271dea8
--- /dev/null
+++ b/twrpDU.cpp
@@ -0,0 +1,114 @@
+/*
+		Copyright 2013 TeamWin
+		This file is part of TWRP/TeamWin Recovery Project.
+
+		TWRP is free software: you can redistribute it and/or modify
+		it under the terms of the GNU General Public License as published by
+		the Free Software Foundation, either version 3 of the License, or
+		(at your option) any later version.
+
+		TWRP is distributed in the hope that it will be useful,
+		but WITHOUT ANY WARRANTY; without even the implied warranty of
+		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+		GNU General Public License for more details.
+
+		You should have received a copy of the GNU General Public License
+		along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+extern "C" {
+	#include "libtar/libtar.h"
+}
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <algorithm>
+#include "twrpDU.hpp"
+#include "twrp-functions.hpp"
+
+using namespace std;
+
+extern bool datamedia;
+
+twrpDU::twrpDU() {
+	add_relative_dir(".");
+	add_relative_dir("..");
+	add_relative_dir("lost+found");
+	add_absolute_dir("/data/data/com.google.android.music/files");
+}
+
+void twrpDU::add_relative_dir(const string& dir) {
+	relativedir.push_back(dir);
+}
+
+void twrpDU::clear_relative_dir(string dir) {
+	vector<string>::iterator iter = relativedir.begin();
+	while (iter != relativedir.end()) {
+		if (*iter == dir)
+			iter = relativedir.erase(iter);
+		else
+			iter++;
+	}
+}
+
+void twrpDU::add_absolute_dir(const string& dir) {
+	absolutedir.push_back(TWFunc::Remove_Trailing_Slashes(dir));
+}
+
+vector<string> twrpDU::get_absolute_dirs(void) {
+	return absolutedir;
+}
+
+uint64_t twrpDU::Get_Folder_Size(const string& Path) {
+	DIR* d;
+	struct dirent* de;
+	struct stat st;
+	uint64_t dusize = 0;
+	string FullPath;
+
+	d = opendir(Path.c_str());
+	if (d == NULL) {
+		LOGERR("error opening '%s'\n", Path.c_str());
+		LOGERR("error: %s\n", strerror(errno));
+		return 0;
+	}
+
+	while ((de = readdir(d)) != NULL) {
+		FullPath = Path + "/";
+		FullPath += de->d_name;
+		if (lstat(FullPath.c_str(), &st)) {
+			LOGERR("Unable to stat '%s'\n", FullPath.c_str());
+			continue;
+		}
+		if ((st.st_mode & S_IFDIR) && !check_skip_dirs(FullPath) && de->d_type != DT_SOCK) {
+			dusize += Get_Folder_Size(FullPath);
+		} else if (st.st_mode & S_IFREG) {
+			dusize += (uint64_t)(st.st_size);
+		}
+	}
+	closedir(d);
+	return dusize;
+}
+
+bool twrpDU::check_relative_skip_dirs(const string& dir) {
+	return std::find(relativedir.begin(), relativedir.end(), dir) != relativedir.end();
+}
+
+bool twrpDU::check_absolute_skip_dirs(const string& path) {
+	return std::find(absolutedir.begin(), absolutedir.end(), path) != absolutedir.end();
+}
+
+bool twrpDU::check_skip_dirs(const string& path) {
+	string normalized = TWFunc::Remove_Trailing_Slashes(path);
+	size_t slashIdx = normalized.find_last_of('/');
+	if(slashIdx != std::string::npos && slashIdx+1 < normalized.size()) {
+		if(check_relative_skip_dirs(normalized.substr(slashIdx+1)))
+			return true;
+	}
+	return check_absolute_skip_dirs(normalized);
+}
diff --git a/twrpDU.hpp b/twrpDU.hpp
new file mode 100644
index 0000000..e947de9
--- /dev/null
+++ b/twrpDU.hpp
@@ -0,0 +1,54 @@
+/*
+        Copyright 2013 TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef TWRPDU_HPP
+#define TWRPDU_HPP
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstream>
+#include <string>
+#include <vector>
+#include "twcommon.h"
+
+using namespace std;
+
+class twrpDU {
+
+public:
+	twrpDU();
+	uint64_t Get_Folder_Size(const string& Path); // Gets the folder's size using stat
+	void add_absolute_dir(const string& Path);
+	void add_relative_dir(const string& Path);
+	bool check_relative_skip_dirs(const string& dir);
+	bool check_absolute_skip_dirs(const string& path);
+	bool check_skip_dirs(const string& path);
+	vector<string> get_absolute_dirs(void);
+	void clear_relative_dir(string dir);
+private:
+	vector<string> absolutedir;
+	vector<string> relativedir;
+};
+
+extern twrpDU du;
+#endif
diff --git a/twrpDigest.cpp b/twrpDigest.cpp
new file mode 100644
index 0000000..0693c55
--- /dev/null
+++ b/twrpDigest.cpp
@@ -0,0 +1,146 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+extern "C"
+{
+	#include "digest/md5.h"
+	#include "gui/gui.h"
+	#include "libcrecovery/common.h"
+}
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <libgen.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstream>
+#include <sys/mman.h>
+#include "twcommon.h"
+#include "data.hpp"
+#include "variables.h"
+#include "twrp-functions.hpp"
+#include "twrpDigest.hpp"
+
+using namespace std;
+
+void twrpDigest::setfn(string fn) {
+	md5fn = fn;
+}
+
+int twrpDigest::computeMD5(void) {
+	string line;
+	struct MD5Context md5c;
+	FILE *file;
+	int len;
+	unsigned char buf[1024];
+	MD5Init(&md5c);
+	file = fopen(md5fn.c_str(), "rb");
+	if (file == NULL)
+		return -1;
+	while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
+		MD5Update(&md5c, buf, len);
+	}
+	fclose(file);
+	MD5Final(md5sum, &md5c);
+	return 0;
+}
+
+int twrpDigest::write_md5digest(void) {
+	int i;
+	string md5string, md5file;
+	char hex[3];
+	md5file = md5fn + ".md5";
+
+	for (i = 0; i < 16; ++i) {
+		snprintf(hex, 3, "%02x", md5sum[i]);
+		md5string += hex;
+	}
+	md5string += "  ";
+	md5string += basename((char*) md5fn.c_str());
+	md5string +=  + "\n";
+	TWFunc::write_file(md5file, md5string);
+	LOGINFO("MD5 for %s: %s\n", md5fn.c_str(), md5string.c_str());
+	return 0;
+}
+
+int twrpDigest::read_md5digest(void) {
+	int i = 0;
+	bool foundMd5File = false;
+	string md5file = "";
+	vector<string> md5ext;
+	md5ext.push_back(".md5");
+	md5ext.push_back(".md5sum");
+
+	while (i < md5ext.size()) {
+		md5file = md5fn + md5ext[i];
+		if (TWFunc::Path_Exists(md5file)) {
+			foundMd5File = true;
+			break;
+		}
+		i++;
+	}
+
+	if (!foundMd5File) {
+		gui_print("Skipping MD5 check: no MD5 file found\n");
+		return -1;
+	} else if (TWFunc::read_file(md5file, line) != 0) {
+		gui_print("Skipping MD5 check: MD5 file unreadable\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+/* verify_md5digest return codes:
+	-2: md5 did not match
+	-1: no md5 file found
+	 0: md5 matches
+	 1: md5 file unreadable
+*/
+
+int twrpDigest::verify_md5digest(void) {
+	string buf;
+	char hex[3];
+	int i, ret;
+	string md5string;
+
+	ret = read_md5digest();
+	if (ret != 0)
+		return ret;
+	stringstream ss(line);
+	vector<string> tokens;
+	while (ss >> buf)
+		tokens.push_back(buf);
+	computeMD5();
+	for (i = 0; i < 16; ++i) {
+		snprintf(hex, 3, "%02x", md5sum[i]);
+		md5string += hex;
+	}
+	if (tokens.at(0) != md5string) {
+		LOGERR("MD5 does not match\n");
+		return -2;
+	}
+
+	gui_print("MD5 matched\n");
+	return 0;
+}
diff --git a/twrpDigest.hpp b/twrpDigest.hpp
new file mode 100644
index 0000000..5edd4d3
--- /dev/null
+++ b/twrpDigest.hpp
@@ -0,0 +1,38 @@
+/*
+        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+extern "C" {
+	#include "digest/md5.h"
+}
+
+using namespace std;
+
+class twrpDigest
+{
+public:
+	void setfn(string fn);
+	int computeMD5(void);
+	int verify_md5digest(void);
+	int write_md5digest(void);
+
+private:
+	int read_md5digest(void);
+	string md5fn;
+	string line;
+	unsigned char md5sum[MD5LENGTH];
+};
diff --git a/twrpTar.cpp b/twrpTar.cpp
new file mode 100644
index 0000000..28ac91a
--- /dev/null
+++ b/twrpTar.cpp
@@ -0,0 +1,1392 @@
+
+/*
+	Copyright 2013 TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+extern "C" {
+	#include "libtar/libtar.h"
+	#include "twrpTar.h"
+	#include "tarWrite.h"
+}
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <dirent.h>
+#include <libgen.h>
+#include <sys/mman.h>
+#include "twrpTar.hpp"
+#include "twcommon.h"
+#include "variables.h"
+#include "twrp-functions.hpp"
+#ifndef BUILD_TWRPTAR_MAIN
+#include "data.hpp"
+#include "infomanager.hpp"
+#endif //ndef BUILD_TWRPTAR_MAIN
+
+using namespace std;
+
+twrpTar::twrpTar(void) {
+	use_encryption = 0;
+	userdata_encryption = 0;
+	use_compression = 0;
+	split_archives = 0;
+	has_data_media = 0;
+	pigz_pid = 0;
+	oaes_pid = 0;
+	Total_Backup_Size = 0;
+	include_root_dir = true;
+}
+
+twrpTar::~twrpTar(void) {
+	// Do nothing
+}
+
+void twrpTar::setfn(string fn) {
+	tarfn = fn;
+}
+
+void twrpTar::setdir(string dir) {
+	tardir = dir;
+}
+
+void twrpTar::setsize(unsigned long long backup_size) {
+	Total_Backup_Size = backup_size;
+}
+
+void twrpTar::setpassword(string pass) {
+	password = pass;
+}
+
+int twrpTar::createTarFork(const unsigned long long *overall_size, const unsigned long long *other_backups_size) {
+	int status = 0;
+	pid_t pid, rc_pid;
+	int progress_pipe[2], ret;
+
+	file_count = 0;
+
+	if (pipe(progress_pipe) < 0) {
+		LOGERR("Error creating progress tracking pipe\n");
+		return -1;
+	}
+	if ((pid = fork()) == -1) {
+		LOGINFO("create tar failed to fork.\n");
+		close(progress_pipe[0]);
+		close(progress_pipe[1]);
+		return -1;
+	}
+	if (pid == 0) {
+		// Child process
+
+		// Child closes input side of progress pipe
+		close(progress_pipe[0]);
+		progress_pipe_fd = progress_pipe[1];
+
+		if (use_encryption || userdata_encryption) {
+			LOGINFO("Using encryption\n");
+			DIR* d;
+			struct dirent* de;
+			unsigned long long regular_size = 0, encrypt_size = 0, target_size = 0, core_count = 1, total_size;
+			unsigned enc_thread_id = 1, regular_thread_id = 0, i, start_thread_id = 1;
+			int item_len, ret, thread_error = 0;
+			std::vector<TarListStruct> RegularList;
+			std::vector<TarListStruct> EncryptList;
+			string FileName;
+			struct TarListStruct TarItem;
+			twrpTar reg, enc[9];
+			struct stat st;
+			pthread_t enc_thread[9];
+			pthread_attr_t tattr;
+			void *thread_return;
+
+			core_count = sysconf(_SC_NPROCESSORS_CONF);
+			if (core_count > 8)
+				core_count = 8;
+			LOGINFO("   Core Count      : %llu\n", core_count);
+			Archive_Current_Size = 0;
+
+			d = opendir(tardir.c_str());
+			if (d == NULL) {
+				LOGERR("error opening '%s'\n", tardir.c_str());
+				close(progress_pipe[1]);
+				_exit(-1);
+			}
+			// Figure out the size of all data to be encrypted and create a list of unencrypted files
+			while ((de = readdir(d)) != NULL) {
+				FileName = tardir + "/" + de->d_name;
+
+				if (de->d_type == DT_BLK || de->d_type == DT_CHR || du.check_skip_dirs(FileName))
+					continue;
+				if (de->d_type == DT_DIR) {
+					item_len = strlen(de->d_name);
+					if (userdata_encryption && ((item_len >= 3 && strncmp(de->d_name, "app", 3) == 0) || (item_len >= 6 && strncmp(de->d_name, "dalvik", 6) == 0))) {
+						ret = Generate_TarList(FileName, &RegularList, &target_size, &regular_thread_id);
+						if (ret < 0) {
+							LOGERR("Error in Generate_TarList with regular list!\n");
+							closedir(d);
+							close(progress_pipe_fd);
+							close(progress_pipe[1]);
+							_exit(-1);
+						}
+						file_count = (unsigned long long)(ret);
+						regular_size += du.Get_Folder_Size(FileName);
+					} else {
+						encrypt_size += du.Get_Folder_Size(FileName);
+					}
+				} else if (de->d_type == DT_REG) {
+					stat(FileName.c_str(), &st);
+					encrypt_size += (unsigned long long)(st.st_size);
+				}
+			}
+			closedir(d);
+
+			target_size = encrypt_size / core_count;
+			target_size++;
+			LOGINFO("   Unencrypted size: %llu\n", regular_size);
+			LOGINFO("   Encrypted size  : %llu\n", encrypt_size);
+			LOGINFO("   Target size     : %llu\n", target_size);
+			if (!userdata_encryption) {
+				enc_thread_id = 0;
+				start_thread_id = 0;
+				core_count--;
+			}
+			Archive_Current_Size = 0;
+
+			d = opendir(tardir.c_str());
+			if (d == NULL) {
+				LOGERR("error opening '%s'\n", tardir.c_str());
+				close(progress_pipe[1]);
+				_exit(-1);
+			}
+			// Divide up the encrypted file list for threading
+			while ((de = readdir(d)) != NULL) {
+				FileName = tardir + "/" + de->d_name;
+
+				if (de->d_type == DT_BLK || de->d_type == DT_CHR || du.check_skip_dirs(FileName))
+					continue;
+				if (de->d_type == DT_DIR) {
+					item_len = strlen(de->d_name);
+					if (userdata_encryption && ((item_len >= 3 && strncmp(de->d_name, "app", 3) == 0) || (item_len >= 6 && strncmp(de->d_name, "dalvik", 6) == 0))) {
+						// Do nothing, we added these to RegularList earlier
+					} else {
+						FileName = tardir + "/" + de->d_name;
+						ret = Generate_TarList(FileName, &EncryptList, &target_size, &enc_thread_id);
+						if (ret < 0) {
+							LOGERR("Error in Generate_TarList with encrypted list!\n");
+							closedir(d);
+							close(progress_pipe[1]);
+							_exit(-1);
+						}
+						file_count += (unsigned long long)(ret);
+					}
+				} else if (de->d_type == DT_REG || de->d_type == DT_LNK) {
+					stat(FileName.c_str(), &st);
+					if (de->d_type == DT_REG)
+						Archive_Current_Size += (unsigned long long)(st.st_size);
+					TarItem.fn = FileName;
+					TarItem.thread_id = enc_thread_id;
+					EncryptList.push_back(TarItem);
+					file_count++;
+				}
+			}
+			closedir(d);
+			if (enc_thread_id != core_count) {
+				LOGERR("Error dividing up threads for encryption, %i threads for %i cores!\n", enc_thread_id, core_count);
+				if (enc_thread_id > core_count) {
+					close(progress_pipe[1]);
+					_exit(-1);
+				} else {
+					LOGERR("Continuining anyway.");
+				}
+			}
+
+			// Send file count to parent
+			write(progress_pipe_fd, &file_count, sizeof(file_count));
+			// Send backup size to parent
+			total_size = regular_size + encrypt_size;
+			write(progress_pipe_fd, &total_size, sizeof(total_size));
+
+			if (userdata_encryption) {
+				// Create a backup of unencrypted data
+				reg.setfn(tarfn);
+				reg.ItemList = &RegularList;
+				reg.thread_id = 0;
+				reg.use_encryption = 0;
+				reg.use_compression = use_compression;
+				reg.split_archives = 1;
+				reg.progress_pipe_fd = progress_pipe_fd;
+				LOGINFO("Creating unencrypted backup...\n");
+				if (createList((void*)&reg) != 0) {
+					LOGERR("Error creating unencrypted backup.\n");
+					close(progress_pipe[1]);
+					_exit(-1);
+				}
+			}
+
+			if (pthread_attr_init(&tattr)) {
+				LOGERR("Unable to pthread_attr_init\n");
+				close(progress_pipe[1]);
+				_exit(-1);
+			}
+			if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) {
+				LOGERR("Error setting pthread_attr_setdetachstate\n");
+				close(progress_pipe[1]);
+				_exit(-1);
+			}
+			if (pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM)) {
+				LOGERR("Error setting pthread_attr_setscope\n");
+				close(progress_pipe[1]);
+				_exit(-1);
+			}
+			/*if (pthread_attr_setstacksize(&tattr, 524288)) {
+				LOGERR("Error setting pthread_attr_setstacksize\n");
+				_exit(-1);
+			}*/
+
+			// Create threads for the divided up encryption lists
+			for (i = start_thread_id; i <= core_count; i++) {
+				enc[i].setdir(tardir);
+				enc[i].setfn(tarfn);
+				enc[i].ItemList = &EncryptList;
+				enc[i].thread_id = i;
+				enc[i].use_encryption = use_encryption;
+				enc[i].setpassword(password);
+				enc[i].use_compression = use_compression;
+				enc[i].split_archives = 1;
+				enc[i].progress_pipe_fd = progress_pipe_fd;
+				LOGINFO("Start encryption thread %i\n", i);
+				ret = pthread_create(&enc_thread[i], &tattr, createList, (void*)&enc[i]);
+				if (ret) {
+					LOGINFO("Unable to create %i thread for encryption! %i\nContinuing in same thread (backup will be slower).\n", i, ret);
+					if (createList((void*)&enc[i]) != 0) {
+						LOGERR("Error creating encrypted backup %i.\n", i);
+						close(progress_pipe[1]);
+						_exit(-1);
+					} else {
+						enc[i].thread_id = i + 1;
+					}
+				}
+				usleep(100000); // Need a short delay before starting the next thread or the threads will never finish for some reason.
+			}
+			if (pthread_attr_destroy(&tattr)) {
+				LOGERR("Failed to pthread_attr_destroy\n");
+			}
+			for (i = start_thread_id; i <= core_count; i++) {
+				if (enc[i].thread_id == i) {
+					if (pthread_join(enc_thread[i], &thread_return)) {
+						LOGERR("Error joining thread %i\n", i);
+						close(progress_pipe[1]);
+						_exit(-1);
+					} else {
+						LOGINFO("Joined thread %i.\n", i);
+						ret = (int)thread_return;
+						if (ret != 0) {
+							thread_error = 1;
+							LOGERR("Thread %i returned an error %i.\n", i, ret);
+							close(progress_pipe[1]);
+							_exit(-1);
+						}
+					}
+				} else {
+					LOGINFO("Skipping joining thread %i because of pthread failure.\n", i);
+				}
+			}
+			if (thread_error) {
+				LOGERR("Error returned by one or more threads.\n");
+				close(progress_pipe[1]);
+				_exit(-1);
+			}
+			LOGINFO("Finished encrypted backup.\n");
+			close(progress_pipe[1]);
+			_exit(0);
+		} else {
+			// Not encrypted
+			std::vector<TarListStruct> FileList;
+			unsigned thread_id = 0;
+			unsigned long long target_size = 0;
+			twrpTar reg;
+			int ret;
+
+			// Generate list of files to back up
+			ret = Generate_TarList(tardir, &FileList, &target_size, &thread_id);
+			if (ret < 0) {
+				LOGERR("Error in Generate_TarList!\n");
+				close(progress_pipe[1]);
+				_exit(-1);
+			}
+			file_count = (unsigned long long)(ret);
+			// Create a backup
+			reg.setfn(tarfn);
+			reg.ItemList = &FileList;
+			reg.thread_id = 0;
+			reg.use_encryption = 0;
+			reg.use_compression = use_compression;
+			reg.setsize(Total_Backup_Size);
+			reg.progress_pipe_fd = progress_pipe_fd;
+			if (Total_Backup_Size > MAX_ARCHIVE_SIZE) {
+				gui_print("Breaking backup file into multiple archives...\n");
+				reg.split_archives = 1;
+			} else {
+				reg.split_archives = 0;
+			}
+			LOGINFO("Creating backup...\n");
+			write(progress_pipe_fd, &file_count, sizeof(file_count));
+			write(progress_pipe_fd, &Total_Backup_Size, sizeof(Total_Backup_Size));
+			if (createList((void*)&reg) != 0) {
+				LOGERR("Error creating backup.\n");
+				close(progress_pipe[1]);
+				_exit(-1);
+			}
+			close(progress_pipe[1]);
+			_exit(0);
+		}
+	} else {
+		// Parent side
+		unsigned long long fs, size_backup, files_backup, total_backup_size;
+		int first_data = 0;
+		double display_percent, progress_percent;
+		char file_progress[1024];
+		char size_progress[1024];
+		files_backup = 0;
+		size_backup = 0;
+
+		// Parent closes output side
+		close(progress_pipe[1]);
+
+		// Read progress data from children
+		while (read(progress_pipe[0], &fs, sizeof(fs)) > 0) {
+			if (first_data == 0) {
+				// First incoming data is the file count
+				file_count = fs;
+				if (file_count == 0) file_count = 1; // prevent division by 0 below
+				first_data = 1;
+			} else if (first_data == 1) {
+				// Second incoming data is total size
+				total_backup_size = fs;
+				first_data = 2;
+			} else {
+				files_backup++;
+				size_backup += fs;
+				display_percent = (double)(files_backup) / (double)(file_count) * 100;
+				sprintf(file_progress, "%llu of %llu files, %i%%", files_backup, file_count, (int)(display_percent));
+#ifndef BUILD_TWRPTAR_MAIN
+				DataManager::SetValue("tw_file_progress", file_progress);
+				display_percent = (double)(size_backup + *other_backups_size) / (double)(*overall_size) * 100;
+				sprintf(size_progress, "%lluMB of %lluMB, %i%%", (size_backup + *other_backups_size) / 1048576, *overall_size / 1048576, (int)(display_percent));
+				DataManager::SetValue("tw_size_progress", size_progress);
+				progress_percent = (display_percent / 100);
+				DataManager::SetProgress((float)(progress_percent));
+#endif //ndef BUILD_TWRPTAR_MAIN
+			}
+		}
+		close(progress_pipe[0]);
+#ifndef BUILD_TWRPTAR_MAIN
+		DataManager::SetValue("tw_file_progress", "");
+		DataManager::SetValue("tw_size_progress", "");
+
+		InfoManager backup_info(backup_folder + partition_name + ".info");
+		backup_info.SetValue("backup_size", size_backup);
+		if (use_compression && use_encryption)
+			backup_info.SetValue("backup_type", 3);
+		else if (use_encryption)
+			backup_info.SetValue("backup_type", 2);
+		else if (use_compression)
+			backup_info.SetValue("backup_type", 1);
+		else
+			backup_info.SetValue("backup_type", 0);
+		backup_info.SetValue("file_count", files_backup);
+		backup_info.SaveValues();
+#endif //ndef BUILD_TWRPTAR_MAIN
+		if (TWFunc::Wait_For_Child(pid, &status, "createTarFork()") != 0)
+			return -1;
+	}
+	return 0;
+}
+
+int twrpTar::extractTarFork(const unsigned long long *overall_size, unsigned long long *other_backups_size) {
+	int status = 0;
+	pid_t pid, rc_pid;
+	int progress_pipe[2], ret;
+
+	if (pipe(progress_pipe) < 0) {
+		LOGERR("Error creating progress tracking pipe\n");
+		return -1;
+	}
+
+	pid = fork();
+	if (pid >= 0) // fork was successful
+	{
+		if (pid == 0) // child process
+		{
+			close(progress_pipe[0]);
+			progress_pipe_fd = progress_pipe[1];
+			if (TWFunc::Path_Exists(tarfn)) {
+				LOGINFO("Single archive\n");
+				if (extract() != 0)
+					_exit(-1);
+				else
+					_exit(0);
+			} else {
+				LOGINFO("Multiple archives\n");
+				string temp;
+				char actual_filename[255];
+				twrpTar tars[9];
+				pthread_t tar_thread[9];
+				pthread_attr_t tattr;
+				int thread_count = 0, i, start_thread_id = 1, ret, thread_error = 0;
+				void *thread_return;
+
+				basefn = tarfn;
+				temp = basefn + "%i%02i";
+				tarfn += "000";
+				if (!TWFunc::Path_Exists(tarfn)) {
+					LOGERR("Unable to locate '%s' or '%s'\n", basefn.c_str(), tarfn.c_str());
+					close(progress_pipe_fd);
+					_exit(-1);
+				}
+				if (TWFunc::Get_File_Type(tarfn) != 2) {
+					LOGINFO("First tar file '%s' not encrypted\n", tarfn.c_str());
+					tars[0].basefn = basefn;
+					tars[0].thread_id = 0;
+					tars[0].progress_pipe_fd = progress_pipe_fd;
+					if (extractMulti((void*)&tars[0]) != 0) {
+						LOGERR("Error extracting split archive.\n");
+						close(progress_pipe_fd);
+						_exit(-1);
+					}
+				} else {
+					start_thread_id = 0;
+				}
+				// Start threading encrypted restores
+				if (pthread_attr_init(&tattr)) {
+					LOGERR("Unable to pthread_attr_init\n");
+					close(progress_pipe_fd);
+					_exit(-1);
+				}
+				if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) {
+					LOGERR("Error setting pthread_attr_setdetachstate\n");
+					close(progress_pipe_fd);
+					_exit(-1);
+				}
+				if (pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM)) {
+					LOGERR("Error setting pthread_attr_setscope\n");
+					close(progress_pipe_fd);
+					_exit(-1);
+				}
+				/*if (pthread_attr_setstacksize(&tattr, 524288)) {
+					LOGERR("Error setting pthread_attr_setstacksize\n");
+					close(progress_pipe_fd);
+					_exit(-1);
+				}*/
+				for (i = start_thread_id; i < 9; i++) {
+					sprintf(actual_filename, temp.c_str(), i, 0);
+					if (TWFunc::Path_Exists(actual_filename)) {
+						thread_count++;
+						tars[i].basefn = basefn;
+						tars[i].setpassword(password);
+						tars[i].thread_id = i;
+						tars[i].progress_pipe_fd = progress_pipe_fd;
+						LOGINFO("Creating extract thread ID %i\n", i);
+						ret = pthread_create(&tar_thread[i], &tattr, extractMulti, (void*)&tars[i]);
+						if (ret) {
+							LOGINFO("Unable to create %i thread for extraction! %i\nContinuing in same thread (restore will be slower).\n", i, ret);
+							if (extractMulti((void*)&tars[i]) != 0) {
+								LOGERR("Error extracting backup in thread %i.\n", i);
+								close(progress_pipe_fd);
+								_exit(-1);
+							} else {
+								tars[i].thread_id = i + 1;
+							}
+						}
+						usleep(100000); // Need a short delay before starting the next thread or the threads will never finish for some reason.
+					} else {
+						break;
+					}
+				}
+				for (i = start_thread_id; i < thread_count + start_thread_id; i++) {
+					if (tars[i].thread_id == i) {
+						if (pthread_join(tar_thread[i], &thread_return)) {
+							LOGERR("Error joining thread %i\n", i);
+							close(progress_pipe_fd);
+							_exit(-1);
+						} else {
+							LOGINFO("Joined thread %i.\n", i);
+							ret = (int)thread_return;
+							if (ret != 0) {
+								thread_error = 1;
+								LOGERR("Thread %i returned an error %i.\n", i, ret);
+								close(progress_pipe_fd);
+								_exit(-1);
+							}
+						}
+					} else {
+						LOGINFO("Skipping joining thread %i because of pthread failure.\n", i);
+					}
+				}
+				if (thread_error) {
+					LOGERR("Error returned by one or more threads.\n");
+					close(progress_pipe_fd);
+					_exit(-1);
+				}
+				LOGINFO("Finished encrypted restore.\n");
+				close(progress_pipe_fd);
+				_exit(0);
+			}
+		}
+		else // parent process
+		{
+			unsigned long long fs, size_backup;
+			double display_percent, progress_percent;
+			char size_progress[1024];
+			size_backup = 0;
+
+			// Parent closes output side
+			close(progress_pipe[1]);
+
+			// Read progress data from children
+			while (read(progress_pipe[0], &fs, sizeof(fs)) > 0) {
+				size_backup += fs;
+				display_percent = (double)(size_backup + *other_backups_size) / (double)(*overall_size) * 100;
+				sprintf(size_progress, "%lluMB of %lluMB, %i%%", (size_backup + *other_backups_size) / 1048576, *overall_size / 1048576, (int)(display_percent));
+				progress_percent = (display_percent / 100);
+#ifndef BUILD_TWRPTAR_MAIN
+				DataManager::SetValue("tw_size_progress", size_progress);
+				DataManager::SetProgress((float)(progress_percent));
+#endif //ndef BUILD_TWRPTAR_MAIN
+			}
+			close(progress_pipe[0]);
+#ifndef BUILD_TWRPTAR_MAIN
+			DataManager::SetValue("tw_file_progress", "");
+#endif //ndef BUILD_TWRPTAR_MAIN
+			*other_backups_size += size_backup;
+
+			if (TWFunc::Wait_For_Child(pid, &status, "extractTarFork()") != 0)
+				return -1;
+		}
+	}
+	else // fork has failed
+	{
+		close(progress_pipe[0]);
+		close(progress_pipe[1]);
+		LOGINFO("extract tar failed to fork.\n");
+		return -1;
+	}
+	return 0;
+}
+
+int twrpTar::Generate_TarList(string Path, std::vector<TarListStruct> *TarList, unsigned long long *Target_Size, unsigned *thread_id) {
+	DIR* d;
+	struct dirent* de;
+	struct stat st;
+	string FileName;
+	struct TarListStruct TarItem;
+	string::size_type i;
+	int ret, file_count;
+	file_count = 0;
+
+	d = opendir(Path.c_str());
+	if (d == NULL) {
+		LOGERR("Error opening '%s' -- error: %s\n", Path.c_str(), strerror(errno));
+		closedir(d);
+		return -1;
+	}
+	while ((de = readdir(d)) != NULL) {
+		FileName = Path + "/" + de->d_name;
+
+		if (de->d_type == DT_BLK || de->d_type == DT_CHR || du.check_skip_dirs(FileName))
+			continue;
+		TarItem.fn = FileName;
+		TarItem.thread_id = *thread_id;
+		if (de->d_type == DT_DIR) {
+			TarList->push_back(TarItem);
+			ret = Generate_TarList(FileName, TarList, Target_Size, thread_id);
+			if (ret < 0)
+				return -1;
+			file_count += ret;
+		} else if (de->d_type == DT_REG || de->d_type == DT_LNK) {
+			stat(FileName.c_str(), &st);
+			TarList->push_back(TarItem);
+			if (de->d_type == DT_REG) {
+				file_count++;
+				Archive_Current_Size += st.st_size;
+			}
+			if (Archive_Current_Size != 0 && *Target_Size != 0 && Archive_Current_Size > *Target_Size) {
+				*thread_id = *thread_id + 1;
+				Archive_Current_Size = 0;
+			}
+		}
+	}
+	closedir(d);
+	return file_count;
+}
+
+int twrpTar::extractTar() {
+	char* charRootDir = (char*) tardir.c_str();
+	if (openTar() == -1)
+		return -1;
+	if (tar_extract_all(t, charRootDir, &progress_pipe_fd) != 0) {
+		LOGERR("Unable to extract tar archive '%s'\n", tarfn.c_str());
+		return -1;
+	}
+	if (tar_close(t) != 0) {
+		LOGERR("Unable to close tar file\n");
+		return -1;
+	}
+	return 0;
+}
+
+int twrpTar::extract() {
+	Archive_Current_Type = TWFunc::Get_File_Type(tarfn);
+
+	if (Archive_Current_Type == 1) {
+		//if you return the extractTGZ function directly, stack crashes happen
+		LOGINFO("Extracting gzipped tar\n");
+		int ret = extractTar();
+		return ret;
+	} else if (Archive_Current_Type == 2) {
+		int ret = TWFunc::Try_Decrypting_File(tarfn, password);
+		if (ret < 1) {
+			LOGERR("Failed to decrypt tar file '%s'\n", tarfn.c_str());
+			return -1;
+		}
+		if (ret == 1) {
+			LOGERR("Decrypted file is not in tar format.\n");
+			return -1;
+		}
+		if (ret == 3) {
+			LOGINFO("Extracting encrypted and compressed tar.\n");
+			Archive_Current_Type = 3;
+		} else
+			LOGINFO("Extracting encrypted tar.\n");
+		return extractTar();
+	} else {
+		LOGINFO("Extracting uncompressed tar\n");
+		return extractTar();
+	}
+}
+
+int twrpTar::tarList(std::vector<TarListStruct> *TarList, unsigned thread_id) {
+	struct stat st;
+	char buf[PATH_MAX];
+	int list_size = TarList->size(), i = 0, archive_count = 0;
+	string temp;
+	char actual_filename[PATH_MAX];
+	char *ptr;
+	unsigned long long fs;
+
+	if (split_archives) {
+		basefn = tarfn;
+		temp = basefn + "%i%02i";
+		sprintf(actual_filename, temp.c_str(), thread_id, archive_count);
+		tarfn = actual_filename;
+		include_root_dir = true;
+	} else {
+		include_root_dir = false;
+	}
+	LOGINFO("Creating tar file '%s'\n", tarfn.c_str());
+	if (createTar() != 0) {
+		LOGERR("Error creating tar '%s' for thread %i\n", tarfn.c_str(), thread_id);
+		return -2;
+	}
+	Archive_Current_Size = 0;
+
+	while (i < list_size) {
+		if (TarList->at(i).thread_id == thread_id) {
+			strcpy(buf, TarList->at(i).fn.c_str());
+			lstat(buf, &st);
+			if (S_ISREG(st.st_mode)) { // item is a regular file
+				fs = (unsigned long long)(st.st_size);
+				if (split_archives && Archive_Current_Size + fs > MAX_ARCHIVE_SIZE) {
+					if (closeTar() != 0) {
+						LOGERR("Error closing '%s' on thread %i\n", tarfn.c_str(), thread_id);
+						return -3;
+					}
+					archive_count++;
+					gui_print("Splitting thread ID %i into archive %i\n", thread_id, archive_count + 1);
+					if (archive_count > 99) {
+						LOGERR("Too many archives for thread %i\n", thread_id);
+						return -4;
+					}
+					sprintf(actual_filename, temp.c_str(), thread_id, archive_count);
+					tarfn = actual_filename;
+					if (createTar() != 0) {
+						LOGERR("Error creating tar '%s' for thread %i\n", tarfn.c_str(), thread_id);
+						return -2;
+					}
+					Archive_Current_Size = 0;
+				}
+				Archive_Current_Size += fs;
+				write(progress_pipe_fd, &fs, sizeof(fs));
+			}
+			LOGINFO("addFile '%s' including root: %i\n", buf, include_root_dir);
+			if (addFile(buf, include_root_dir) != 0) {
+				LOGERR("Error adding file '%s' to '%s'\n", buf, tarfn.c_str());
+				return -1;
+			}
+		}
+		i++;
+	}
+	if (closeTar() != 0) {
+		LOGERR("Error closing '%s' on thread %i\n", tarfn.c_str(), thread_id);
+		return -3;
+	}
+	LOGINFO("Thread id %i tarList done, %i archives.\n", thread_id, archive_count, i, list_size);
+	return 0;
+}
+
+void* twrpTar::createList(void *cookie) {
+
+	twrpTar* threadTar = (twrpTar*) cookie;
+	if (threadTar->tarList(threadTar->ItemList, threadTar->thread_id) != 0) {
+		LOGINFO("ERROR tarList for thread ID %i\n", threadTar->thread_id);
+		return (void*)-2;
+	}
+	LOGINFO("Thread ID %i finished successfully.\n", threadTar->thread_id);
+	return (void*)0;
+}
+
+void* twrpTar::extractMulti(void *cookie) {
+
+	twrpTar* threadTar = (twrpTar*) cookie;
+	int archive_count = 0;
+	string temp = threadTar->basefn + "%i%02i";
+	char actual_filename[255];
+	sprintf(actual_filename, temp.c_str(), threadTar->thread_id, archive_count);
+	while (TWFunc::Path_Exists(actual_filename)) {
+		threadTar->tarfn = actual_filename;
+		if (threadTar->extract() != 0) {
+			LOGINFO("Error extracting '%s' in thread ID %i\n", actual_filename, threadTar->thread_id);
+			return (void*)-2;
+		}
+		archive_count++;
+		if (archive_count > 99)
+			break;
+		sprintf(actual_filename, temp.c_str(), threadTar->thread_id, archive_count);
+	}
+	LOGINFO("Thread ID %i finished successfully.\n", threadTar->thread_id);
+	return (void*)0;
+}
+
+int twrpTar::addFilesToExistingTar(vector <string> files, string fn) {
+	char* charTarFile = (char*) fn.c_str();
+
+	if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1)
+		return -1;
+	removeEOT(charTarFile);
+	if (tar_open(&t, charTarFile, NULL, O_WRONLY | O_APPEND | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1)
+		return -1;
+	for (unsigned int i = 0; i < files.size(); ++i) {
+		char* file = (char*) files.at(i).c_str();
+		if (tar_append_file(t, file, file) == -1)
+			return -1;
+	}
+	if (tar_append_eof(t) == -1)
+		return -1;
+	if (tar_close(t) == -1)
+		return -1;
+	return 0;
+}
+
+int twrpTar::createTar() {
+	char* charTarFile = (char*) tarfn.c_str();
+	char* charRootDir = (char*) tardir.c_str();
+	static tartype_t type = { open, close, read, write_tar };
+
+	if (use_encryption && use_compression) {
+		// Compressed and encrypted
+		Archive_Current_Type = 3;
+		LOGINFO("Using encryption and compression...\n");
+		int i, pipes[4];
+
+		if (pipe(pipes) < 0) {
+			LOGERR("Error creating first pipe\n");
+			return -1;
+		}
+		if (pipe(pipes + 2) < 0) {
+			LOGERR("Error creating second pipe\n");
+			return -1;
+		}
+		int output_fd = open(tarfn.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+		if (output_fd < 0) {
+			LOGERR("Failed to open '%s'\n", tarfn.c_str());
+			for (i = 0; i < 4; i++)
+				close(pipes[i]); // close all
+			return -1;
+		}
+		pigz_pid = fork();
+
+		if (pigz_pid < 0) {
+			LOGERR("pigz fork() failed\n");
+			close(output_fd);
+			for (i = 0; i < 4; i++)
+				close(pipes[i]); // close all
+			return -1;
+		} else if (pigz_pid == 0) {
+			// pigz Child
+			close(pipes[1]);
+			close(pipes[2]);
+			close(0);
+			dup2(pipes[0], 0);
+			close(1);
+			dup2(pipes[3], 1);
+			if (execlp("pigz", "pigz", "-", NULL) < 0) {
+				LOGERR("execlp pigz ERROR!\n");
+				close(output_fd);
+				close(pipes[0]);
+				close(pipes[3]);
+				_exit(-1);
+			}
+		} else {
+			// Parent
+			oaes_pid = fork();
+
+			if (oaes_pid < 0) {
+				LOGERR("openaes fork() failed\n");
+				close(output_fd);
+				for (i = 0; i < 4; i++)
+					close(pipes[i]); // close all
+				return -1;
+			} else if (oaes_pid == 0) {
+				// openaes Child
+				close(pipes[0]);
+				close(pipes[1]);
+				close(pipes[3]);
+				close(0);
+				dup2(pipes[2], 0);
+				close(1);
+				dup2(output_fd, 1);
+				if (execlp("openaes", "openaes", "enc", "--key", password.c_str(), NULL) < 0) {
+					LOGERR("execlp openaes ERROR!\n");
+					close(pipes[2]);
+					close(output_fd);
+					_exit(-1);
+				}
+			} else {
+				// Parent
+				close(pipes[0]);
+				close(pipes[2]);
+				close(pipes[3]);
+				fd = pipes[1];
+				if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+					close(fd);
+					LOGERR("tar_fdopen failed\n");
+					return -1;
+				}
+				return 0;
+			}
+		}
+	} else if (use_compression) {
+		// Compressed
+		Archive_Current_Type = 1;
+		LOGINFO("Using compression...\n");
+		int pigzfd[2];
+		int output_fd = open(tarfn.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+		if (output_fd < 0) {
+			LOGERR("Failed to open '%s'\n", tarfn.c_str());
+			close(pigzfd[0]);
+			return -1;
+		}
+
+		if (pipe(pigzfd) < 0) {
+			LOGERR("Error creating pipe\n");
+			close(output_fd);
+			return -1;
+		}
+		pigz_pid = fork();
+
+		if (pigz_pid < 0) {
+			LOGERR("fork() failed\n");
+			close(output_fd);
+			close(pigzfd[0]);
+			close(pigzfd[1]);
+			return -1;
+		} else if (pigz_pid == 0) {
+			// Child
+			close(pigzfd[1]);   // close unused output pipe
+			dup2(pigzfd[0], 0); // remap stdin
+			dup2(output_fd, 1); // remap stdout to output file
+			if (execlp("pigz", "pigz", "-", NULL) < 0) {
+				LOGERR("execlp pigz ERROR!\n");
+				close(output_fd);
+				close(pigzfd[0]);
+				_exit(-1);
+			}
+		} else {
+			// Parent
+			close(pigzfd[0]); // close parent input
+			fd = pigzfd[1];   // copy parent output
+			if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+				close(fd);
+				LOGERR("tar_fdopen failed\n");
+				return -1;
+			}
+		}
+	} else if (use_encryption) {
+		// Encrypted
+		Archive_Current_Type = 2;
+		LOGINFO("Using encryption...\n");
+		int oaesfd[2];
+		int output_fd = open(tarfn.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+		if (output_fd < 0) {
+			LOGERR("Failed to open '%s'\n", tarfn.c_str());
+			return -1;
+		}
+		if (pipe(oaesfd) < 0) {
+			LOGERR("Error creating pipe\n");
+			close(output_fd);
+			return -1;
+		}
+		oaes_pid = fork();
+
+		if (oaes_pid < 0) {
+			LOGERR("fork() failed\n");
+			close(output_fd);
+			close(oaesfd[0]);
+			close(oaesfd[1]);
+			return -1;
+		} else if (oaes_pid == 0) {
+			// Child
+			close(oaesfd[1]);   // close unused
+			dup2(oaesfd[0], 0); // remap stdin
+			dup2(output_fd, 1); // remap stdout to output file
+			if (execlp("openaes", "openaes", "enc", "--key", password.c_str(), NULL) < 0) {
+				LOGERR("execlp openaes ERROR!\n");
+				close(output_fd);
+				close(oaesfd[0]);
+				_exit(-1);
+			}
+		} else {
+			// Parent
+			close(oaesfd[0]); // close parent input
+			fd = oaesfd[1];   // copy parent output
+			if(tar_fdopen(&t, fd, charRootDir, NULL, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+				close(fd);
+				LOGERR("tar_fdopen failed\n");
+				return -1;
+			}
+			return 0;
+		}
+	} else {
+		// Not compressed or encrypted
+		init_libtar_buffer(0);
+		if (tar_open(&t, charTarFile, &type, O_WRONLY | O_CREAT | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1) {
+			LOGERR("tar_open error opening '%s'\n", tarfn.c_str());
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int twrpTar::openTar() {
+	char* charRootDir = (char*) tardir.c_str();
+	char* charTarFile = (char*) tarfn.c_str();
+	string Password;
+
+	if (Archive_Current_Type == 3) {
+		LOGINFO("Opening encrypted and compressed backup...\n");
+		int i, pipes[4];
+		int input_fd = open(tarfn.c_str(), O_RDONLY | O_LARGEFILE);
+		if (input_fd < 0) {
+			LOGERR("Failed to open '%s'\n", tarfn.c_str());
+			return -1;
+		}
+
+		if (pipe(pipes) < 0) {
+			LOGERR("Error creating first pipe\n");
+			close(input_fd);
+			return -1;
+		}
+		if (pipe(pipes + 2) < 0) {
+			LOGERR("Error creating second pipe\n");
+			close(pipes[0]);
+			close(pipes[1]);
+			close(input_fd);
+			return -1;
+		}
+		oaes_pid = fork();
+
+		if (oaes_pid < 0) {
+			LOGERR("pigz fork() failed\n");
+			close(input_fd);
+			for (i = 0; i < 4; i++)
+				close(pipes[i]); // close all
+			return -1;
+		} else if (oaes_pid == 0) {
+			// openaes Child
+			close(pipes[0]); // Close pipes that are not used by this child
+			close(pipes[2]);
+			close(pipes[3]);
+			close(0);
+			dup2(input_fd, 0);
+			close(1);
+			dup2(pipes[1], 1);
+			if (execlp("openaes", "openaes", "dec", "--key", password.c_str(), NULL) < 0) {
+				LOGERR("execlp openaes ERROR!\n");
+				close(input_fd);
+				close(pipes[1]);
+				_exit(-1);
+			}
+		} else {
+			// Parent
+			pigz_pid = fork();
+
+			if (pigz_pid < 0) {
+				LOGERR("openaes fork() failed\n");
+				close(input_fd);
+				for (i = 0; i < 4; i++)
+					close(pipes[i]); // close all
+				return -1;
+			} else if (pigz_pid == 0) {
+				// pigz Child
+				close(pipes[1]); // Close pipes not used by this child
+				close(pipes[2]);
+				close(0);
+				dup2(pipes[0], 0);
+				close(1);
+				dup2(pipes[3], 1);
+				if (execlp("pigz", "pigz", "-d", "-c", NULL) < 0) {
+					LOGERR("execlp pigz ERROR!\n");
+					close(input_fd);
+					close(pipes[0]);
+					close(pipes[3]);
+					_exit(-1);
+				}
+			} else {
+				// Parent
+				close(pipes[0]); // Close pipes not used by parent
+				close(pipes[1]);
+				close(pipes[3]);
+				fd = pipes[2];
+				if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+					close(fd);
+					LOGERR("tar_fdopen failed\n");
+					return -1;
+				}
+			}
+		}
+	} else if (Archive_Current_Type == 2) {
+		LOGINFO("Opening encrypted backup...\n");
+		int oaesfd[2];
+		int input_fd = open(tarfn.c_str(), O_RDONLY | O_LARGEFILE);
+		if (input_fd < 0) {
+			LOGERR("Failed to open '%s'\n", tarfn.c_str());
+			return -1;
+		}
+
+		if (pipe(oaesfd) < 0) {
+			LOGERR("Error creating pipe\n");
+			close(input_fd);
+			return -1;
+		}
+
+		oaes_pid = fork();
+		if (oaes_pid < 0) {
+			LOGERR("fork() failed\n");
+			close(input_fd);
+			close(oaesfd[0]);
+			close(oaesfd[1]);
+			return -1;
+		} else if (oaes_pid == 0) {
+			// Child
+			close(oaesfd[0]); // Close unused pipe
+			close(0);   // close stdin
+			dup2(oaesfd[1], 1); // remap stdout
+			dup2(input_fd, 0); // remap input fd to stdin
+			if (execlp("openaes", "openaes", "dec", "--key", password.c_str(), NULL) < 0) {
+				LOGERR("execlp openaes ERROR!\n");
+				close(input_fd);
+				close(oaesfd[1]);
+				_exit(-1);
+			}
+		} else {
+			// Parent
+			close(oaesfd[1]); // close parent output
+			fd = oaesfd[0];   // copy parent input
+			if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+				close(fd);
+				LOGERR("tar_fdopen failed\n");
+				return -1;
+			}
+		}
+	} else if (Archive_Current_Type == 1) {
+		LOGINFO("Opening as a gzip...\n");
+		int pigzfd[2];
+		int input_fd = open(tarfn.c_str(), O_RDONLY | O_LARGEFILE);
+		if (input_fd < 0) {
+			LOGERR("Failed to open '%s'\n", tarfn.c_str());
+			return -1;
+		}
+		if (pipe(pigzfd) < 0) {
+			LOGERR("Error creating pipe\n");
+			close(input_fd);
+			return -1;
+		}
+
+		pigz_pid = fork();
+		if (pigz_pid < 0) {
+			LOGERR("fork() failed\n");
+			close(input_fd);
+			close(pigzfd[0]);
+			close(pigzfd[1]);
+			return -1;
+		} else if (pigz_pid == 0) {
+			// Child
+			close(pigzfd[0]);
+			dup2(input_fd, 0); // remap input fd to stdin
+			dup2(pigzfd[1], 1); // remap stdout
+			if (execlp("pigz", "pigz", "-d", "-c", NULL) < 0) {
+				close(pigzfd[1]);
+				close(input_fd);
+				LOGERR("execlp openaes ERROR!\n");
+				_exit(-1);
+			}
+		} else {
+			// Parent
+			close(pigzfd[1]); // close parent output
+			fd = pigzfd[0];   // copy parent input
+			if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+				close(fd);
+				LOGERR("tar_fdopen failed\n");
+				return -1;
+			}
+		}
+	} else if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) {
+		LOGERR("Unable to open tar archive '%s'\n", charTarFile);
+		return -1;
+	}
+	return 0;
+}
+
+string twrpTar::Strip_Root_Dir(string Path) {
+	string temp;
+	size_t slash;
+
+	if (Path.substr(0, 1) == "/")
+		temp = Path.substr(1, Path.size() - 1);
+	else
+		temp = Path;
+	slash = temp.find("/");
+	if (slash == string::npos)
+		return temp;
+	else {
+		string stripped;
+
+		stripped = temp.substr(slash, temp.size() - slash);
+		return stripped;
+	}
+	return temp;
+}
+
+int twrpTar::addFile(string fn, bool include_root) {
+	char* charTarFile = (char*) fn.c_str();
+	if (include_root) {
+		if (tar_append_file(t, charTarFile, NULL) == -1)
+			return -1;
+	} else {
+		string temp = Strip_Root_Dir(fn);
+		char* charTarPath = (char*) temp.c_str();
+		if (tar_append_file(t, charTarFile, charTarPath) == -1)
+			return -1;
+	}
+	return 0;
+}
+
+int twrpTar::closeTar() {
+	flush_libtar_buffer(t->fd);
+	if (tar_append_eof(t) != 0) {
+		LOGERR("tar_append_eof(): %s\n", strerror(errno));
+		tar_close(t);
+		return -1;
+	}
+	if (tar_close(t) != 0) {
+		LOGERR("Unable to close tar archive: '%s'\n", tarfn.c_str());
+		return -1;
+	}
+	if (Archive_Current_Type > 0) {
+		close(fd);
+		int status;
+		if (pigz_pid > 0 && TWFunc::Wait_For_Child(pigz_pid, &status, "pigz") != 0)
+			return -1;
+		if (oaes_pid > 0 && TWFunc::Wait_For_Child(oaes_pid, &status, "openaes") != 0)
+			return -1;
+	}
+	free_libtar_buffer();
+	if (use_compression && !use_encryption) {
+		string gzname = tarfn + ".gz";
+		if (TWFunc::Path_Exists(gzname)) {
+			rename(gzname.c_str(), tarfn.c_str());
+		}
+	}
+	if (TWFunc::Get_File_Size(tarfn) == 0) {
+		LOGERR("Backup file size for '%s' is 0 bytes.\n", tarfn.c_str());
+		return -1;
+	}
+	return 0;
+}
+
+int twrpTar::removeEOT(string tarFile) {
+	char* charTarFile = (char*) tarFile.c_str();
+	off_t tarFileEnd;
+	while (th_read(t) == 0) {
+		if (TH_ISREG(t))
+			tar_skip_regfile(t);
+		tarFileEnd = lseek(t->fd, 0, SEEK_CUR);
+	}
+	if (tar_close(t) == -1)
+		return -1;
+	if (truncate(charTarFile, tarFileEnd) == -1)
+		return -1;
+	return 0;
+}
+
+int twrpTar::entryExists(string entry) {
+	char* searchstr = (char*)entry.c_str();
+	int ret;
+
+	Archive_Current_Type = TWFunc::Get_File_Type(tarfn);
+
+	if (openTar() == -1)
+		ret = 0;
+	else
+		ret = tar_find(t, searchstr);
+
+	if (closeTar() != 0)
+		LOGINFO("Unable to close tar after searching for entry.\n");
+
+	return ret;
+}
+
+unsigned long long twrpTar::get_size() {
+	if (TWFunc::Path_Exists(tarfn)) {
+		LOGINFO("Single archive\n");
+		int type = 0;
+		return uncompressedSize(tarfn, &type);
+	} else {
+		LOGINFO("Multiple archives\n");
+		string temp;
+		char actual_filename[255];
+		int archive_count = 0, i, type = 0, temp_type = 0;
+		unsigned long long total_restore_size = 0;
+
+		basefn = tarfn;
+		temp = basefn + "%i%02i";
+		tarfn += "000";
+		thread_id = 0;
+		sprintf(actual_filename, temp.c_str(), thread_id, archive_count);
+		if (!TWFunc::Path_Exists(actual_filename)) {
+			LOGERR("Unable to locate '%s' or '%s'\n", basefn.c_str(), tarfn.c_str());
+			return 0;
+		}
+		for (i = 0; i < 9; i++) {
+			archive_count = 0;
+			sprintf(actual_filename, temp.c_str(), i, archive_count);
+			while (TWFunc::Path_Exists(actual_filename)) {
+				total_restore_size += uncompressedSize(actual_filename, &temp_type);
+				if (temp_type > type)
+					type = temp_type;
+				archive_count++;
+				if (archive_count > 99)
+					break;
+				sprintf(actual_filename, temp.c_str(), i, archive_count);
+			}
+		}
+#ifndef BUILD_TWRPTAR_MAIN
+		InfoManager backup_info(backup_folder + "/" + partition_name + ".info");
+		backup_info.SetValue("backup_size", total_restore_size);
+		backup_info.SetValue("backup_type", type);
+		backup_info.SaveValues();
+#endif //ndef BUILD_TWRPTAR_MAIN
+		return total_restore_size;
+	}
+	return 0;
+}
+
+unsigned long long twrpTar::uncompressedSize(string filename, int *archive_type) {
+	int type = 0;
+	unsigned long long total_size = 0;
+	string Tar, Command, result;
+	vector<string> split;
+
+	Tar = TWFunc::Get_Filename(filename);
+	type = TWFunc::Get_File_Type(filename);
+	if (type == 0) {
+		total_size = TWFunc::Get_File_Size(filename);
+		*archive_type = 0;
+	} else if (type == 1) {
+		// Compressed
+		Command = "pigz -l '" + filename + "'";
+		/* if we set Command = "pigz -l " + tarfn + " | sed '1d' | cut -f5 -d' '";
+		we get the uncompressed size at once. */
+		TWFunc::Exec_Cmd(Command, result);
+		if (!result.empty()) {
+			/* Expected output:
+			compressed original  reduced name
+			95855838   179403776 -1.3%   data.yaffs2.win
+			^
+			split[5]
+			*/
+			split = TWFunc::split_string(result, ' ', true);
+			if (split.size() > 4)
+			total_size = atoi(split[5].c_str());
+		}
+		*archive_type = 1;
+	} else if (type == 2) {
+		// File is encrypted and may be compressed
+		int ret = TWFunc::Try_Decrypting_File(filename, password);
+		*archive_type = 2;
+		if (ret < 1) {
+			LOGERR("Failed to decrypt tar file '%s'\n", filename.c_str());
+			total_size = TWFunc::Get_File_Size(filename);
+		} else if (ret == 1) {
+			LOGERR("Decrypted file is not in tar format.\n");
+			total_size = TWFunc::Get_File_Size(filename);
+		} else if (ret == 3) {
+			*archive_type = 3;
+			Command = "openaes dec --key \"" + password + "\" --in '" + filename + "' | pigz -l";
+			/* if we set Command = "pigz -l " + tarfn + " | sed '1d' | cut -f5 -d' '";
+			we get the uncompressed size at once. */
+			TWFunc::Exec_Cmd(Command, result);
+			if (!result.empty()) {
+				LOGINFO("result was: '%s'\n", result.c_str());
+				/* Expected output:
+				compressed original  reduced name
+				95855838   179403776 -1.3%   data.yaffs2.win
+				^
+				split[5]
+				*/
+				split = TWFunc::split_string(result, ' ', true);
+				if (split.size() > 4)
+				total_size = atoi(split[5].c_str());
+			}
+		} else {
+			total_size = TWFunc::Get_File_Size(filename);
+		}
+	}
+
+	return total_size;
+}
+
+extern "C" ssize_t write_tar(int fd, const void *buffer, size_t size) {
+	return (ssize_t) write_libtar_buffer(fd, buffer, size);
+}
diff --git a/twrpTar.h b/twrpTar.h
new file mode 100644
index 0000000..a73b917
--- /dev/null
+++ b/twrpTar.h
@@ -0,0 +1,25 @@
+/*
+	Copyright 2012 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TWRPTAR_HEADER
+#define _TWRPTAR_HEADER
+
+ssize_t write_tar(int fd, const void *buffer, size_t size);
+
+#endif  // _TWRPTAR_HEADER
+
diff --git a/twrpTar.hpp b/twrpTar.hpp
new file mode 100644
index 0000000..7994752
--- /dev/null
+++ b/twrpTar.hpp
@@ -0,0 +1,101 @@
+/*
+        Copyright 2012 bigbiff/Dees_Troy TeamWin
+        This file is part of TWRP/TeamWin Recovery Project.
+
+        TWRP is free software: you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation, either version 3 of the License, or
+        (at your option) any later version.
+
+        TWRP is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+extern "C" {
+	#include "libtar/libtar.h"
+}
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstream>
+#include <string>
+#include <vector>
+#include "twrpDU.hpp"
+
+using namespace std;
+
+struct TarListStruct {
+	std::string fn;
+	unsigned thread_id;
+};
+
+struct thread_data_struct {
+	std::vector<TarListStruct> *TarList;
+	unsigned thread_id;
+};
+
+class twrpTar {
+public:
+	twrpTar();
+	virtual ~twrpTar();
+	int createTarFork(const unsigned long long *overall_size, const unsigned long long *other_backups_size);
+	int extractTarFork(const unsigned long long *overall_size, unsigned long long *other_backups_size);
+	void setfn(string fn);
+	void setdir(string dir);
+	void setsize(unsigned long long backup_size);
+	void setpassword(string pass);
+	unsigned long long get_size();
+
+public:
+	int use_encryption;
+	int userdata_encryption;
+	int use_compression;
+	int split_archives;
+	int has_data_media;
+	string backup_name;
+	int progress_pipe_fd;
+	string partition_name;
+	string backup_folder;
+
+private:
+	int extract();
+	int addFilesToExistingTar(vector <string> files, string tarFile);
+	int createTar();
+	int addFile(string fn, bool include_root);
+	int entryExists(string entry);
+	int closeTar();
+	int removeEOT(string tarFile);
+	int extractTar();
+	string Strip_Root_Dir(string Path);
+	int openTar();
+	int Generate_TarList(string Path, std::vector<TarListStruct> *TarList, unsigned long long *Target_Size, unsigned *thread_id);
+	static void* createList(void *cookie);
+	static void* extractMulti(void *cookie);
+	int tarList(std::vector<TarListStruct> *TarList, unsigned thread_id);
+	unsigned long long uncompressedSize(string filename, int *archive_type);
+
+	int Archive_Current_Type;
+	unsigned long long Archive_Current_Size;
+	unsigned long long Total_Backup_Size;
+	bool include_root_dir;
+	TAR *t;
+	int fd;
+	pid_t pigz_pid;
+	pid_t oaes_pid;
+	unsigned long long file_count;
+
+	string tardir;
+	string tarfn;
+	string basefn;
+	string password;
+
+	std::vector<TarListStruct> *ItemList;
+	int thread_id;
+};
diff --git a/twrpTarMain/Android.mk b/twrpTarMain/Android.mk
new file mode 100644
index 0000000..5cd6fe1
--- /dev/null
+++ b/twrpTarMain/Android.mk
@@ -0,0 +1,71 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Build static binary
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	twrpTarMain.cpp \
+	../twrp-functions.cpp \
+	../twrpTar.cpp \
+	../tarWrite.c \
+	../twrpDU.cpp
+LOCAL_CFLAGS:= -g -c -W -DBUILD_TWRPTAR_MAIN
+
+LOCAL_C_INCLUDES += bionic external/stlport/stlport
+LOCAL_STATIC_LIBRARIES := libc libtar_static libstlport_static libstdc++
+
+ifeq ($(TWHAVE_SELINUX), true)
+    LOCAL_C_INCLUDES += external/libselinux/include
+    LOCAL_STATIC_LIBRARIES += libselinux
+    LOCAL_CFLAGS += -DHAVE_SELINUX -g
+endif
+ifneq ($(RECOVERY_SDCARD_ON_DATA),)
+	LOCAL_CFLAGS += -DRECOVERY_SDCARD_ON_DATA
+endif
+ifeq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+    LOCAL_CFLAGS += -DTW_EXCLUDE_ENCRYPTED_BACKUPS
+else
+	LOCAL_STATIC_LIBRARIES += libopenaes_static
+endif
+
+LOCAL_MODULE:= twrpTar_static
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_TAGS:= eng
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+include $(BUILD_EXECUTABLE)
+
+
+# Build shared binary
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	twrpTarMain.cpp \
+	../twrp-functions.cpp \
+	../twrpTar.cpp \
+	../tarWrite.c \
+	../twrpDU.cpp
+LOCAL_CFLAGS:= -g -c -W -DBUILD_TWRPTAR_MAIN
+
+LOCAL_C_INCLUDES += bionic external/stlport/stlport
+LOCAL_SHARED_LIBRARIES := libc libtar libstlport libstdc++
+
+ifeq ($(TWHAVE_SELINUX), true)
+    LOCAL_C_INCLUDES += external/libselinux/include
+    LOCAL_SHARED_LIBRARIES += libselinux
+    LOCAL_CFLAGS += -DHAVE_SELINUX -g
+endif
+ifneq ($(RECOVERY_SDCARD_ON_DATA),)
+	LOCAL_CFLAGS += -DRECOVERY_SDCARD_ON_DATA
+endif
+ifeq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+    LOCAL_CFLAGS += -DTW_EXCLUDE_ENCRYPTED_BACKUPS
+else
+	LOCAL_SHARED_LIBRARIES += libopenaes
+endif
+
+LOCAL_MODULE:= twrpTar
+LOCAL_MODULE_TAGS:= eng
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+include $(BUILD_EXECUTABLE)
diff --git a/twrpTarMain/twrpTarMain.cpp b/twrpTarMain/twrpTarMain.cpp
new file mode 100644
index 0000000..c1705c7
--- /dev/null
+++ b/twrpTarMain/twrpTarMain.cpp
@@ -0,0 +1,161 @@
+
+/*
+	Copyright 2014 TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	TWRP is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "../twrp-functions.hpp"
+#include "../twrpTar.hpp"
+#include "../twrpDU.hpp"
+#include <string.h>
+
+twrpDU du;
+
+void usage() {
+	printf("twrpTar <action> [options]\n\n");
+	printf("actions: -c create\n");
+	printf("         -x extract\n\n");
+	printf(" -d    target directory\n");
+	printf(" -t    output file\n");
+	printf(" -m    skip media subfolder (has data media)\n");
+	printf(" -z    compress backup (/sbin/pigz must be present)\n");
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	printf(" -e    encrypt/decrypt backup followed by password (/sbin/openaes must be present)\n");
+	printf(" -u    encrypt using userdata encryption (must be used with -e\n");
+#endif
+	printf("\n\n");
+	printf("Example: twrpTar -c -d /cache -t /sdcard/test.tar\n");
+	printf("         twrpTar -x -d /cache -t /sdcard/test.tar\n");
+}
+
+int main(int argc, char **argv) {
+	twrpTar tar;
+	int use_encryption = 0, userdata_encryption = 0, has_data_media = 0, use_compression = 0, include_root = 0;
+	int i, action = 0;
+	unsigned j;
+	string Directory, Tar_Filename;
+	unsigned long long temp1 = 0, temp2 = 0;
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	string Password;
+#endif
+
+	if (argc < 2) {
+		usage();
+		return 0;
+	}
+
+	if (strcmp(argv[1], "-c") == 0)
+		action = 1; // create tar
+	else if (strcmp(argv[1], "-x") == 0)
+		action = 2; // extract tar
+	else {
+		printf("Invalid action '%s' specified.\n", argv[1]);
+		usage();
+		return -1;
+	}
+
+	for (i = 2; i < argc; i++) {
+		if (strcmp(argv[i], "-d") == 0) {
+			i++;
+			if (argc <= i) {
+				printf("No argument specified for %s\n", argv[i - 1]);
+				usage();
+				return -1;
+			} else {
+				Directory = argv[i];
+			}
+		} else if (strcmp(argv[i], "-t") == 0) {
+			i++;
+			if (argc <= i) {
+				printf("No argument specified for %s\n", argv[i - 1]);
+				usage();
+				return -1;
+			} else {
+				Tar_Filename = argv[i];
+			}
+		} else if (strcmp(argv[i], "-e") == 0) {
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+			i++;
+			if (argc <= i) {
+				printf("No argument specified for %s\n", argv[i - 1]);
+				usage();
+				return -1;
+			} else {
+				use_encryption = 1;
+				Password = argv[i];
+			}
+#else
+			printf("Encrypted tar file support not present\n");
+			usage();
+			return -1;
+#endif
+		} else if (strcmp(argv[i], "-m") == 0) {
+			if (action == 2)
+				printf("NOTE: %s option not needed when extracting.\n", argv[i]);
+			has_data_media = 1;
+		} else if (strcmp(argv[i], "-z") == 0) {
+			if (action == 2)
+				printf("NOTE: %s option not needed when extracting.\n", argv[i]);
+			use_compression = 1;
+		} else if (strcmp(argv[i], "-u") == 0) {
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+			if (action == 2)
+				printf("NOTE: %s option not needed when extracting.\n", argv[i]);
+			userdata_encryption = 1;
+#else
+			printf("Encrypted tar file support not present\n");
+			usage();
+			return -1;
+#endif
+		}
+	}
+
+	tar.has_data_media = has_data_media;
+	tar.setdir(Directory);
+	tar.setfn(Tar_Filename);
+	tar.setsize(du.Get_Folder_Size(Directory));
+	tar.use_compression = use_compression;
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	if (userdata_encryption && !use_encryption) {
+		printf("userdata encryption set without encryption option\n");
+		usage();
+		return -1;
+	}
+	if (use_encryption) {
+		tar.use_encryption = use_encryption;
+		tar.userdata_encryption = userdata_encryption;
+		tar.setpassword(Password);
+	} else {
+		use_encryption = false;
+	}
+#endif
+	if (action == 1) {
+		if (tar.createTarFork(&temp1, &temp2) != 0) {
+			sync();
+			return -1;
+		}
+		sync();
+		printf("\n\ntar created successfully.\n");
+	} else if (action == 2) {
+		if (tar.extractTarFork(&temp1, &temp2) != 0) {
+			sync();
+			return -1;
+		}
+		sync();
+		printf("\n\ntar extracted successfully.\n");
+	}
+	return 0;
+}
diff --git a/ui.cpp b/ui.cpp
index c8f08cd..8e5b0f0 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -28,7 +28,9 @@
 #include <time.h>
 #include <unistd.h>
 
+#ifdef ANDROID_RB_RESTART
 #include <cutils/android_reboot.h>
+#endif
 
 #include "common.h"
 #include "roots.h"
@@ -154,9 +156,11 @@
             break;
 
           case RecoveryUI::REBOOT:
+#ifdef ANDROID_RB_RESTART
             if (reboot_enabled) {
                 android_reboot(ANDROID_RB_RESTART, 0, 0);
             }
+#endif
             break;
 
           case RecoveryUI::ENQUEUE:
diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk
index 878d275..b8755fb 100644
--- a/uncrypt/Android.mk
+++ b/uncrypt/Android.mk
@@ -16,6 +16,7 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_C_INCLUDES += $(commands_recovery_local_path)
 LOCAL_SRC_FILES := uncrypt.c
 
 LOCAL_MODULE := uncrypt
diff --git a/updater/Android.mk b/updater/Android.mk
index a3a900a..5c28969 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -24,14 +24,27 @@
 LOCAL_CFLAGS += -Wno-unused-parameter
 LOCAL_C_INCLUDES += system/extras/ext4_utils
 LOCAL_STATIC_LIBRARIES += \
+    libext4_utils \
+    libz
+ifneq ($(wildcard system/core/libmincrypt/rsa_e_3.c),)
+LOCAL_STATIC_LIBRARIES = \
     libext4_utils_static \
     libsparse_static \
     libz
 endif
+ifneq ($(wildcard system/core/include/mincrypt/sha256.h),)
+LOCAL_STATIC_LIBRARIES = \
+    libext4_utils_static \
+    libsparse_static \
+    libz
+endif
+endif
 
 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
 LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
-LOCAL_STATIC_LIBRARIES += libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libflashutils libmmcutils libbmlutils
+LOCAL_STATIC_LIBRARIES += libmincrypttwrp libbz
+LOCAL_STATIC_LIBRARIES += libminelf
 LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc
 LOCAL_STATIC_LIBRARIES += libselinux
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
diff --git a/updater/install.c b/updater/install.c
index ff7de47..0030647 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -45,6 +45,8 @@
 #include "mtdutils/mounts.h"
 #include "mtdutils/mtdutils.h"
 #include "updater.h"
+#include "applypatch/applypatch.h"
+#include "flashutils/flashutils.h"
 #include "install.h"
 
 #ifdef USE_EXT4
@@ -1061,66 +1063,14 @@
         goto done;
     }
 
-    mtd_scan_partitions();
-    const MtdPartition* mtd = mtd_find_partition_by_name(partition);
-    if (mtd == NULL) {
-        printf("%s: no mtd partition named \"%s\"\n", name, partition);
+    char* filename = contents->data;
+    if (0 == restore_raw_partition(NULL, partition, filename))
+        result = strdup(partition);
+    else {
         result = strdup("");
         goto done;
     }
 
-    MtdWriteContext* ctx = mtd_write_partition(mtd);
-    if (ctx == NULL) {
-        printf("%s: can't write mtd partition \"%s\"\n",
-                name, partition);
-        result = strdup("");
-        goto done;
-    }
-
-    bool success;
-
-    if (contents->type == VAL_STRING) {
-        // we're given a filename as the contents
-        char* filename = contents->data;
-        FILE* f = fopen(filename, "rb");
-        if (f == NULL) {
-            printf("%s: can't open %s: %s\n",
-                    name, filename, strerror(errno));
-            result = strdup("");
-            goto done;
-        }
-
-        success = true;
-        char* buffer = malloc(BUFSIZ);
-        int read;
-        while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
-            int wrote = mtd_write_data(ctx, buffer, read);
-            success = success && (wrote == read);
-        }
-        free(buffer);
-        fclose(f);
-    } else {
-        // we're given a blob as the contents
-        ssize_t wrote = mtd_write_data(ctx, contents->data, contents->size);
-        success = (wrote == contents->size);
-    }
-    if (!success) {
-        printf("mtd_write_data to %s failed: %s\n",
-                partition, strerror(errno));
-    }
-
-    if (mtd_erase_blocks(ctx, -1) == -1) {
-        printf("%s: error erasing blocks of %s\n", name, partition);
-    }
-    if (mtd_write_close(ctx) != 0) {
-        printf("%s: error closing write of %s\n", name, partition);
-    }
-
-    printf("%s %s partition\n",
-           success ? "wrote" : "failed to write", partition);
-
-    result = success ? partition : strdup("");
-
 done:
     if (result != partition) FreeValue(partition_value);
     FreeValue(contents);
diff --git a/updater/updater.c b/updater/updater.c
index 465e123..78c0dc1 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -17,6 +17,7 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <fcntl.h>
 
 #include "edify/expr.h"
 #include "updater.h"
@@ -33,6 +34,8 @@
 // Where in the package we expect to find the edify script to execute.
 // (Note it's "updateR-script", not the older "update-script".)
 #define SCRIPT_NAME "META-INF/com/google/android/updater-script"
+#define SELINUX_CONTEXTS_ZIP "file_contexts"
+#define SELINUX_CONTEXTS_TMP "/tmp/file_contexts"
 
 struct selabel_handle *sehandle;
 
@@ -95,6 +98,23 @@
     }
     script[script_entry->uncompLen] = '\0';
 
+    const ZipEntry* file_contexts_entry = mzFindZipEntry(&za, SELINUX_CONTEXTS_ZIP);
+    if (file_contexts_entry != NULL) {
+        int file_contexts_fd = creat(SELINUX_CONTEXTS_TMP, 0644);
+		if (file_contexts_fd < 0) {
+			fprintf(stderr, "Could not extract %s to '%s'\n", SELINUX_CONTEXTS_ZIP, SELINUX_CONTEXTS_TMP);
+			return 3;
+		}
+
+		int ret_val = mzExtractZipEntryToFile(&za, file_contexts_entry, file_contexts_fd);
+		close(file_contexts_fd);
+
+		if (!ret_val) {
+			fprintf(stderr, "Could not extract '%s'\n", SELINUX_CONTEXTS_ZIP);
+			return 3;
+		}
+    }
+
     // Configure edify's functions.
 
     RegisterBuiltins();
@@ -113,11 +133,19 @@
         return 6;
     }
 
-    struct selinux_opt seopts[] = {
-      { SELABEL_OPT_PATH, "/file_contexts" }
-    };
+    if (access(SELINUX_CONTEXTS_TMP, R_OK) == 0) {
+        struct selinux_opt seopts[] = {
+          { SELABEL_OPT_PATH, SELINUX_CONTEXTS_TMP }
+        };
 
-    sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+        sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+    } else {
+        struct selinux_opt seopts[] = {
+          { SELABEL_OPT_PATH, "/file_contexts" }
+        };
+
+        sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+    }
 
     if (!sehandle) {
         fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");
diff --git a/variables.h b/variables.h
new file mode 100644
index 0000000..7c3e758
--- /dev/null
+++ b/variables.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VARIABLES_HEADER_
+#define _VARIABLES_HEADER_
+
+#define TW_VERSION_STR              "2.8.2.0"
+
+#define TW_USE_COMPRESSION_VAR      "tw_use_compression"
+#define TW_FILENAME                 "tw_filename"
+#define TW_ZIP_INDEX                "tw_zip_index"
+#define TW_ZIP_QUEUE_COUNT       "tw_zip_queue_count"
+
+#define MAX_BACKUP_NAME_LEN 64
+#define TW_BACKUP_TEXT              "tw_backup_text"
+#define TW_BACKUP_NAME		        "tw_backup_name"
+#define TW_BACKUP_SYSTEM_VAR        "tw_backup_system"
+#define TW_BACKUP_DATA_VAR          "tw_backup_data"
+#define TW_BACKUP_BOOT_VAR          "tw_backup_boot"
+#define TW_BACKUP_RECOVERY_VAR      "tw_backup_recovery"
+#define TW_BACKUP_CACHE_VAR         "tw_backup_cache"
+#define TW_BACKUP_ANDSEC_VAR        "tw_backup_andsec"
+#define TW_BACKUP_SDEXT_VAR         "tw_backup_sdext"
+#define TW_BACKUP_SP1_VAR           "tw_backup_sp1"
+#define TW_BACKUP_SP2_VAR           "tw_backup_sp2"
+#define TW_BACKUP_SP3_VAR           "tw_backup_sp3"
+#define TW_BACKUP_AVG_IMG_RATE      "tw_backup_avg_img_rate"
+#define TW_BACKUP_AVG_FILE_RATE     "tw_backup_avg_file_rate"
+#define TW_BACKUP_AVG_FILE_COMP_RATE    "tw_backup_avg_file_comp_rate"
+#define TW_BACKUP_SYSTEM_SIZE       "tw_backup_system_size"
+#define TW_BACKUP_DATA_SIZE         "tw_backup_data_size"
+#define TW_BACKUP_BOOT_SIZE         "tw_backup_boot_size"
+#define TW_BACKUP_RECOVERY_SIZE     "tw_backup_recovery_size"
+#define TW_BACKUP_CACHE_SIZE        "tw_backup_cache_size"
+#define TW_BACKUP_ANDSEC_SIZE       "tw_backup_andsec_size"
+#define TW_BACKUP_SDEXT_SIZE        "tw_backup_sdext_size"
+#define TW_BACKUP_SP1_SIZE          "tw_backup_sp1_size"
+#define TW_BACKUP_SP2_SIZE          "tw_backup_sp2_size"
+#define TW_BACKUP_SP3_SIZE          "tw_backup_sp3_size"
+#define TW_STORAGE_FREE_SIZE        "tw_storage_free_size"
+#define TW_GENERATE_MD5_TEXT        "tw_generate_md5_text"
+
+#define TW_RESTORE_TEXT             "tw_restore_text"
+#define TW_RESTORE_SYSTEM_VAR       "tw_restore_system"
+#define TW_RESTORE_DATA_VAR         "tw_restore_data"
+#define TW_RESTORE_BOOT_VAR         "tw_restore_boot"
+#define TW_RESTORE_RECOVERY_VAR     "tw_restore_recovery"
+#define TW_RESTORE_CACHE_VAR        "tw_restore_cache"
+#define TW_RESTORE_ANDSEC_VAR       "tw_restore_andsec"
+#define TW_RESTORE_SDEXT_VAR        "tw_restore_sdext"
+#define TW_RESTORE_SP1_VAR          "tw_restore_sp1"
+#define TW_RESTORE_SP2_VAR          "tw_restore_sp2"
+#define TW_RESTORE_SP3_VAR          "tw_restore_sp3"
+#define TW_RESTORE_AVG_IMG_RATE     "tw_restore_avg_img_rate"
+#define TW_RESTORE_AVG_FILE_RATE    "tw_restore_avg_file_rate"
+#define TW_RESTORE_AVG_FILE_COMP_RATE    "tw_restore_avg_file_comp_rate"
+#define TW_RESTORE_FILE_DATE        "tw_restore_file_date"
+#define TW_VERIFY_MD5_TEXT          "tw_verify_md5_text"
+#define TW_UPDATE_SYSTEM_DETAILS_TEXT "tw_update_system_details_text"
+
+#define TW_SHOW_SPAM_VAR            "tw_show_spam"
+#define TW_COLOR_THEME_VAR          "tw_color_theme"
+#define TW_VERSION_VAR              "tw_version"
+#define TW_SORT_FILES_BY_DATE_VAR   "tw_sort_files_by_date"
+#define TW_GUI_SORT_ORDER           "tw_gui_sort_order"
+#define TW_ZIP_LOCATION_VAR         "tw_zip_location"
+#define TW_ZIP_INTERNAL_VAR         "tw_zip_internal"
+#define TW_ZIP_EXTERNAL_VAR         "tw_zip_external"
+#define TW_FORCE_MD5_CHECK_VAR      "tw_force_md5_check"
+#define TW_SKIP_MD5_CHECK_VAR       "tw_skip_md5_check"
+#define TW_SKIP_MD5_GENERATE_VAR    "tw_skip_md5_generate"
+#define TW_SIGNED_ZIP_VERIFY_VAR    "tw_signed_zip_verify"
+#define TW_REBOOT_AFTER_FLASH_VAR   "tw_reboot_after_flash_option"
+#define TW_TIME_ZONE_VAR            "tw_time_zone"
+#define TW_RM_RF_VAR                "tw_rm_rf"
+
+#define TW_BACKUPS_FOLDER_VAR       "tw_backups_folder"
+
+#define TW_SP1_PARTITION_NAME_VAR   "tw_sp1_name"
+#define TW_SP2_PARTITION_NAME_VAR   "tw_sp2_name"
+#define TW_SP3_PARTITION_NAME_VAR   "tw_sp3_name"
+
+#define TW_SDEXT_SIZE               "tw_sdext_size"
+#define TW_SWAP_SIZE                "tw_swap_size"
+#define TW_SDPART_FILE_SYSTEM       "tw_sdpart_file_system"
+#define TW_TIME_ZONE_GUISEL         "tw_time_zone_guisel"
+#define TW_TIME_ZONE_GUIOFFSET      "tw_time_zone_guioffset"
+#define TW_TIME_ZONE_GUIDST         "tw_time_zone_guidst"
+
+#define TW_ACTION_BUSY              "tw_busy"
+
+#define TW_ALLOW_PARTITION_SDCARD   "tw_allow_partition_sdcard"
+
+#define TW_SCREEN_OFF               "tw_screen_off"
+
+#define TW_REBOOT_SYSTEM            "tw_reboot_system"
+#define TW_REBOOT_RECOVERY          "tw_reboot_recovery"
+#define TW_REBOOT_POWEROFF          "tw_reboot_poweroff"
+#define TW_REBOOT_BOOTLOADER        "tw_reboot_bootloader"
+
+#define TW_HAS_DUAL_STORAGE         "tw_has_dual_storage"
+#define TW_USE_EXTERNAL_STORAGE     "tw_use_external_storage"
+#define TW_HAS_INTERNAL             "tw_has_internal"
+#define TW_INTERNAL_PATH            "tw_internal_path"         // /data/media or /internal
+#define TW_INTERNAL_MOUNT           "tw_internal_mount"        // /data or /internal
+#define TW_INTERNAL_LABEL           "tw_internal_label"        // data or internal
+#define TW_HAS_EXTERNAL             "tw_has_external"
+#define TW_EXTERNAL_PATH            "tw_external_path"         // /sdcard or /external/sdcard2
+#define TW_EXTERNAL_MOUNT           "tw_external_mount"        // /sdcard or /external
+#define TW_EXTERNAL_LABEL           "tw_external_label"        // sdcard or external
+
+#define TW_HAS_DATA_MEDIA           "tw_has_data_media"
+
+#define TW_HAS_BOOT_PARTITION       "tw_has_boot_partition"
+#define TW_HAS_RECOVERY_PARTITION   "tw_has_recovery_partition"
+#define TW_HAS_ANDROID_SECURE       "tw_has_android_secure"
+#define TW_HAS_SDEXT_PARTITION      "tw_has_sdext_partition"
+#define TW_HAS_USB_STORAGE          "tw_has_usb_storage"
+#define TW_NO_BATTERY_PERCENT       "tw_no_battery_percent"
+#define TW_POWER_BUTTON             "tw_power_button"
+#define TW_SIMULATE_ACTIONS         "tw_simulate_actions"
+#define TW_SIMULATE_FAIL            "tw_simulate_fail"
+#define TW_DONT_UNMOUNT_SYSTEM      "tw_dont_unmount_system"
+// #define TW_ALWAYS_RMRF              "tw_always_rmrf"
+
+#define TW_SHOW_DUMLOCK             "tw_show_dumlock"
+#define TW_HAS_INJECTTWRP           "tw_has_injecttwrp"
+#define TW_INJECT_AFTER_ZIP         "tw_inject_after_zip"
+#define TW_HAS_DATADATA             "tw_has_datadata"
+#define TW_FLASH_ZIP_IN_PLACE       "tw_flash_zip_in_place"
+#define TW_MIN_SYSTEM_SIZE          "50" // minimum system size to allow a reboot
+#define TW_MIN_SYSTEM_VAR           "tw_min_system"
+#define TW_DOWNLOAD_MODE            "tw_download_mode"
+#define TW_IS_ENCRYPTED             "tw_is_encrypted"
+#define TW_IS_DECRYPTED             "tw_is_decrypted"
+#define TW_HAS_CRYPTO               "tw_has_crypto"
+#define TW_CRYPTO_PASSWORD          "tw_crypto_password"
+#define TW_DATA_BLK_DEVICE          "tw_data_blk_device"  // Original block device - not decrypted
+#define TW_SDEXT_DISABLE_EXT4       "tw_sdext_disable_ext4"
+#define TW_MILITARY_TIME            "tw_military_time"
+
+// Also used:
+//   tw_boot_is_mountable
+//   tw_system_is_mountable
+//   tw_data_is_mountable
+//   tw_cache_is_mountable
+//   tw_sdcext_is_mountable
+//   tw_sdcint_is_mountable
+//   tw_sd-ext_is_mountable
+//   tw_sp1_is_mountable
+//   tw_sp2_is_mountable
+//   tw_sp3_is_mountable
+
+// Max archive size for tar backups before we split (1.5GB)
+#define MAX_ARCHIVE_SIZE 1610612736LLU
+//#define MAX_ARCHIVE_SIZE 52428800LLU // 50MB split for testing
+
+#ifndef CUSTOM_LUN_FILE
+#define CUSTOM_LUN_FILE "/sys/devices/platform/usb_mass_storage/lun%d/file"
+#endif
+
+#ifndef TW_BRIGHTNESS_PATH
+#define TW_BRIGHTNESS_PATH /nobrightness
+#endif
+
+// For OpenRecoveryScript
+#define SCRIPT_FILE_CACHE "/cache/recovery/openrecoveryscript"
+#define SCRIPT_FILE_TMP "/tmp/openrecoveryscript"
+#define TMP_LOG_FILE "/tmp/recovery.log"
+
+#endif  // _VARIABLES_HEADER_
diff --git a/verifier.cpp b/verifier.cpp
index eeff95a..b96ba3a 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -30,7 +30,9 @@
 #include <stdio.h>
 #include <errno.h>
 
-extern RecoveryUI* ui;
+//extern RecoveryUI* ui;
+
+#define PUBLIC_KEYS_FILE "/res/keys"
 
 /*
  * Simple version of PKCS#7 SignedData extraction. This extracts the
@@ -110,10 +112,16 @@
 //
 // Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
 // or no key matches the signature).
+int verify_file(unsigned char* addr, size_t length) {
+    //ui->SetProgress(0.0);
 
-int verify_file(unsigned char* addr, size_t length,
-                const Certificate* pKeys, unsigned int numKeys) {
-    ui->SetProgress(0.0);
+    int numKeys;
+    Certificate* pKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
+    if (pKeys == NULL) {
+        LOGE("Failed to load keys\n");
+        return INSTALL_CORRUPT;
+    }
+    LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
 
     // An archive with a whole-file signature will end in six bytes:
     //
@@ -216,7 +224,7 @@
 
         double f = so_far / (double)signed_len;
         if (f > frac + 0.02 || size == so_far) {
-            ui->SetProgress(f);
+            //ui->SetProgress(f);
             frac = f;
         }
     }
@@ -287,6 +295,7 @@
         } else {
             LOGI("Unknown key type %d\n", pKeys[i].key_type);
         }
+		LOGI("i: %i, eocd_size: %i, RSANUMBYTES: %i\n", i, eocd_size, RSANUMBYTES);
     }
     free(sig_der);
     LOGE("failed to verify whole-file signature\n");
@@ -447,6 +456,10 @@
                 LOGE("unexpected character between keys\n");
                 goto exit;
             }
+<<<<<<< HEAD
+            LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len);
+=======
+>>>>>>> cddb68b5eafbeba696d5276bda1f1a9f70bbde42
         }
     }
 
diff --git a/verifier.h b/verifier.h
index 15f8d98..43fd5ad 100644
--- a/verifier.h
+++ b/verifier.h
@@ -20,6 +20,14 @@
 #include "mincrypt/p256.h"
 #include "mincrypt/rsa.h"
 
+#define ASSUMED_UPDATE_BINARY_NAME  "META-INF/com/google/android/update-binary"
+
+enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT };
+
+static const float VERIFICATION_PROGRESS_FRACTION = 0.25;
+
+typedef struct Certificate {
+
 typedef struct {
     p256_int x;
     p256_int y;
@@ -42,8 +50,7 @@
  * is signed and the signature matches one of the given keys.  Return
  * one of the constants below.
  */
-int verify_file(unsigned char* addr, size_t length,
-                const Certificate *pKeys, unsigned int numKeys);
+int verify_file(unsigned char* addr, size_t length);
 
 Certificate* load_keys(const char* filename, int* numKeys);
 
diff --git a/verifier_test.cpp b/verifier_test.cpp
index 10a5dda..3ba270d 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -21,7 +21,9 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
+/*
 #include "common.h"
+*/
 #include "verifier.h"
 #include "ui.h"
 #include "mincrypt/sha.h"
@@ -237,7 +239,8 @@
         return 4;
     }
 
-    int result = verify_file(map.addr, map.length, certs, num_keys);
+    int result = verify_file(map.addr, map.length);
+
     if (result == VERIFY_SUCCESS) {
         printf("VERIFIED\n");
         return 0;