Merge up to AOSP marshmallow-release

In order to maintain compatibility with older trees, we now have
minadbd.old and minui.old. I had to use a TARGET_GLOBAL_CFLAG to
handle ifdef issues in minui/minui.d because healthd includes
minui/minui.h and there was no other alternative to make minui.h
compatible with older trees without having to modify healthd rules
which is outside of TWRP.

Note that the new minui does not currently have support for qcom
overlay graphics. Support for this graphics mode will likely be
added in a later patch set. If you are building in a 6.0 tree and
have a device that needs qcom overlay graphics, be warned, as off
mode charging may not work properly. A dead battery in this case
could potentially brick your device if it is unable to charge as
healthd handles charging duties.

Update rules for building toolbox and add rules for making toybox

Use permissive.sh in init.rc which will follow symlinks so we do
not have to worry about what binary is supplying the setenforce
functionality (toolbox, toybox, or busybox).

Fix a few warnings in the main recovery binary source code.

Fix a few includes that were missing that prevented compiling in
6.0

Change-Id: Ia67aa2107d260883da5e365475a19bea538e8b97
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 0484065..ed32528 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,88 +14,432 @@
 
 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
+TWRES_PATH := "/twres/"
+TWHTCD_PATH := $(TWRES_PATH)htcd/
 
-LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
-LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
-
-LOCAL_MODULE := libfusesideload
-
-LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
+TARGET_RECOVERY_GUI := true
 
 LOCAL_SRC_FILES := \
-    adb_install.cpp \
-    asn1_decoder.cpp \
-    bootloader.cpp \
-    device.cpp \
-    fuse_sdcard_provider.c \
-    install.cpp \
-    recovery.cpp \
-    roots.cpp \
-    screen_ui.cpp \
-    ui.cpp \
-    verifier.cpp \
+    twrp.cpp \
+    fixPermissions.cpp \
+    twrpTar.cpp \
+    twrpDU.cpp \
+    twrpDigest.cpp \
+    digest/md5.c \
+    find_file.cpp \
+    infomanager.cpp
+
+LOCAL_SRC_FILES += \
+    data.cpp \
+    partition.cpp \
+    partitionmanager.cpp \
+    twinstall.cpp \
+    twrp-functions.cpp \
+    openrecoveryscript.cpp \
+    tarWrite.c
+
+ifneq ($(TARGET_RECOVERY_REBOOT_SRC),)
+  LOCAL_SRC_FILES += $(TARGET_RECOVERY_REBOOT_SRC)
+endif
 
 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 \
+#    libminui \
+#    libpixelflinger_static \
+#    libpng \
+#    libfs_mgr \
+#    libcutils \
+#    liblog \
+#    libselinux \
+#    libstdc++ \
+#    libm \
+#    libc
+
 LOCAL_C_INCLUDES += \
     system/vold \
     system/extras/ext4_utils \
     system/core/adb \
 
-LOCAL_STATIC_LIBRARIES := \
-    libext4_utils_static \
-    libsparse_static \
-    libminzip \
-    libz \
-    libmtdutils \
-    libmincrypt \
-    libminadbd \
-    libfusesideload \
-    libminui \
-    libpng \
-    libfs_mgr \
-    libbase \
-    libcutils \
-    liblog \
-    libselinux \
-    libstdc++ \
-    libm \
-    libc
+LOCAL_C_INCLUDES += bionic external/stlport/stlport external/openssl/include $(LOCAL_PATH)/libmincrypt/includes
 
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_STATIC_LIBRARIES += libguitwrp
+LOCAL_SHARED_LIBRARIES += libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libaosprecovery
+LOCAL_SHARED_LIBRARIES += libcrecovery
+
+ifneq ($(wildcard external/stlport/Android.mk),)
+    LOCAL_SHARED_LIBRARIES += libstlport
+else
+    LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+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
-    LOCAL_STATIC_LIBRARIES += libext4_utils_static libz
+    LOCAL_SHARED_LIBRARIES += libext4_utils
+    ifneq ($(wildcard external/lz4/Android.mk),)
+        #LOCAL_STATIC_LIBRARIES += liblz4-static
+    endif
+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
+        ifneq ($(wildcard external/lz4/Android.mk),)
+            LOCAL_STATIC_LIBRARIES += liblz4-static
+        endif
+    endif
 endif
 
 LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
 
-ifeq ($(TARGET_RECOVERY_UI_LIB),)
-  LOCAL_SRC_FILES += default_device.cpp
+#ifeq ($(TARGET_RECOVERY_UI_LIB),)
+#  LOCAL_SRC_FILES += default_device.cpp
+#else
+#  LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
+#endif
+
+LOCAL_C_INCLUDES += system/extras/ext4_utils
+
+#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_JB_CRYPTO), true)
+    TW_INCLUDE_CRYPTO := true
+endif
+ifeq ($(TW_INCLUDE_L_CRYPTO), true)
+    TW_INCLUDE_CRYPTO := true
+endif
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO
+    LOCAL_SHARED_LIBRARIES += libcryptfslollipop
+    LOCAL_C_INCLUDES += external/boringssl/src/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_EXCLUDE_ENCRYPTED_BACKUPS), true)
+    LOCAL_SHARED_LIBRARIES += libopenaes
 else
-  LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
+    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
+ifneq ($(TARGET_RECOVERY_INITRC),)
+    TW_EXCLUDE_DEFAULT_USB_INIT := true
+endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0)
+    LOCAL_CFLAGS += -DTW_USE_NEW_MINADBD
+endif
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+    dump_image \
+    erase_image \
+    flash_image \
+    fix_permissions.sh \
+    mke2fs.conf \
+    pigz \
+    teamwin \
+    toolbox_symlinks \
+    twrp \
+    unpigz_symlink \
+    dosfsck \
+    dosfslabel \
+    fsck_msdos_symlink \
+    mkdosfs
+
+ifneq ($(TARGET_ARCH), arm64)
+    ifneq ($(TARGET_ARCH), x86_64)
+        LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker
+    else
+        LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
+    endif
+else
+    LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
+endif
+ifneq ($(TW_USE_TOOLBOX), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += busybox_symlinks
+else
+    ifneq ($(wildcard external/toybox/Android.mk),)
+        LOCAL_ADDITIONAL_DEPENDENCIES += toybox_symlinks
+    endif
+endif
+ifneq ($(TW_NO_EXFAT), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += mkexfatfs
+endif
+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_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
+ifneq ($(TW_EXCLUDE_DEFAULT_USB_INIT), true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += init.recovery.usb.rc
+endif
+# Allow devices to specify device-specific recovery dependencies
+ifneq ($(TARGET_RECOVERY_DEVICE_MODULES),)
+    LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_RECOVERY_DEVICE_MODULES)
+endif
+LOCAL_CFLAGS += -DTWRES=\"$(TWRES_PATH)\"
+LOCAL_CFLAGS += -DTWHTCD_PATH=\"$(TWHTCD_PATH)\"
+ifeq ($(TW_INCLUDE_NTFS_3G),true)
+    LOCAL_ADDITIONAL_DEPENDENCIES += \
+        ntfs-3g \
+        ntfsfix \
+        mkntfs
 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)
+ifneq (,$(filter $(PLATFORM_SDK_VERSION),16 17 18))
+ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_BUSYBOX_SYMLINKS)
+endif
+include $(BUILD_PHONY_PACKAGE)
+RECOVERY_BUSYBOX_SYMLINKS :=
+endif # !TW_USE_TOOLBOX
+
 # All the APIs for testing
 include $(CLEAR_VARS)
 LOCAL_MODULE := libverifier
@@ -105,17 +449,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_TAGS := optional
+LOCAL_MODULE := libfusesideload
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes
+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 \
@@ -123,14 +483,89 @@
     libc
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
 
-include $(LOCAL_PATH)/minui/Android.mk \
-    $(LOCAL_PATH)/minzip/Android.mk \
-    $(LOCAL_PATH)/minadbd/Android.mk \
-    $(LOCAL_PATH)/mtdutils/Android.mk \
-    $(LOCAL_PATH)/tests/Android.mk \
+LOCAL_MODULE := libaosprecovery
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes
+LOCAL_SRC_FILES := adb_install.cpp asn1_decoder.cpp bootloader.cpp legacy_property_service.c verifier.cpp set_metadata.c tw_atomic.cpp
+LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils libfusesideload libmincrypttwrp libselinux
+
+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)/tests/Android.mk \
     $(LOCAL_PATH)/tools/Android.mk \
     $(LOCAL_PATH)/edify/Android.mk \
-    $(LOCAL_PATH)/uncrypt/Android.mk \
     $(LOCAL_PATH)/updater/Android.mk \
     $(LOCAL_PATH)/applypatch/Android.mk
+
+ifeq ($(wildcard system/core/uncrypt/Android.mk),)
+    include $(commands_recovery_local_path)/uncrypt/Android.mk
+endif
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0)
+    include $(commands_recovery_local_path)/minadbd/Android.mk \
+        $(commands_recovery_local_path)/minui/Android.mk
+else
+    TARGET_GLOBAL_CFLAGS += -DTW_USE_OLD_MINUI_H
+    include $(commands_recovery_local_path)/minadbd.old/Android.mk \
+        $(commands_recovery_local_path)/minui.old/Android.mk
+endif
+
+#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)/libtar/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 \
+    $(commands_recovery_local_path)/minzip/Android.mk \
+    $(commands_recovery_local_path)/dosfstools/Android.mk \
+    $(commands_recovery_local_path)/etc/Android.mk \
+    $(commands_recovery_local_path)/toybox/Android.mk \
+    $(commands_recovery_local_path)/libpixelflinger/Android.mk
+
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+    include $(commands_recovery_local_path)/crypto/lollipop/Android.mk
+    include $(commands_recovery_local_path)/crypto/scrypt/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
+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
index bab7e87..96f3789 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,4 @@
-The Recovery Image
-==================
+**Team Win Recovery Project (TWRP)**
 
-Quick turn-around testing
--------------------------
+You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide").
 
-    mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
-
-    # To boot into the new recovery image
-    # without flashing the recovery partition:
-    adb reboot bootloader
-    fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
diff --git a/adb_install.cpp b/adb_install.cpp
index e3b94ea..5c54468 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -24,29 +24,39 @@
 #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"
 #include "minadbd/fuse_adb_provider.h"
 #include "fuse_sideload.h"
 
 static RecoveryUI* ui = NULL;
 
-static void
+void
 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 (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) {
+/*
         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));
     }
 }
 
@@ -56,11 +66,16 @@
     set_usb_driver(false);
 }
 
+bool is_ro_debuggable() {
+    char value[PROPERTY_VALUE_MAX+1];
+    return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1');
+}
 
-static void
+void
 maybe_restart_adbd() {
+    char value[PROPERTY_VALUE_MAX+1];
     if (is_ro_debuggable()) {
-        ui->Print("Restarting adbd...\n");
+        printf("Restarting adbd...\n");
         set_usb_driver(true);
         property_set("ctl.start", "adbd");
     }
@@ -71,23 +86,23 @@
 #define ADB_INSTALL_TIMEOUT 300
 
 int
-apply_from_adb(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) {
-    modified_flash = true;
-
-    ui = ui_;
+apply_from_adb(const char* install_file, pid_t* child_pid) {
 
     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);
     }
 
+    *child_pid = child;
+    // caller can now kill the child thread from another thread
+
     // 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.)
@@ -97,7 +112,7 @@
     struct stat st;
     for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
         if (waitpid(child, &status, WNOHANG) != 0) {
-            result = INSTALL_ERROR;
+            result = -1;
             waited = true;
             break;
         }
@@ -107,16 +122,20 @@
                 sleep(1);
                 continue;
             } else {
-                ui->Print("\nTimed out waiting for package.\n\n");
-                result = INSTALL_ERROR;
+                printf("\nTimed out waiting for package: %s\n\n", strerror(errno));
+                result = -1;
                 kill(child, SIGKILL);
                 break;
             }
         }
-        result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false);
-        break;
+        // Install is handled elsewhere in TWRP
+        //install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false);
+	return 0;
     }
 
+    // if we got here, something failed
+    *child_pid = 0;
+
     if (!waited) {
         // Calling stat() on this magic filename signals the minadbd
         // subprocess to shut down.
@@ -131,9 +150,10 @@
 
     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");
+            result = -2;
         } else if (!WIFSIGNALED(status)) {
-            ui->Print("\n(adbd status %d)\n", WEXITSTATUS(status));
+            printf("status %d\n", WEXITSTATUS(status));
         }
     }
 
diff --git a/adb_install.h b/adb_install.h
index efad436..24e9e21 100644
--- a/adb_install.h
+++ b/adb_install.h
@@ -17,8 +17,10 @@
 #ifndef _ADB_INSTALL_H
 #define _ADB_INSTALL_H
 
-class RecoveryUI;
+//class RecoveryUI;
 
-int apply_from_adb(RecoveryUI* h, bool* wipe_cache, const char* install_file);
+void set_usb_driver(bool enabled);
+void maybe_restart_adbd();
+int apply_from_adb(const char* install_file, pid_t* child_pid);
 
 #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 2358d42..bc45e3c 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;
@@ -838,7 +869,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
@@ -880,7 +912,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.
@@ -925,7 +958,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..7216775
--- /dev/null
+++ b/bmlutils/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+LOCAL_STATIC_LIBRARY := libcrecovery
+
+$(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_STATIC_LIBRARY)
+
+#Added for building TWRP dynamic:
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+LOCAL_SHARED_LIBRARIES := libcrecovery
+
+$(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..4c7c49d
--- /dev/null
+++ b/bmlutils/bmlutils.c
@@ -0,0 +1,198 @@
+#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>
+
+#include "../libcrecovery/common.h"
+
+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..b5b20e9 100644
--- a/bootloader.cpp
+++ b/bootloader.cpp
@@ -14,17 +14,41 @@
  * 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>
 #include <stdio.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <stdlib.h>
+
+// fake Volume struct that allows us to use the AOSP code easily
+struct Volume
+{
+    char fs_type[8];
+    char blk_device[256];
+};
+
+static Volume misc;
+
+void set_misc_device(const char* type, const char* name) {
+    strlcpy(misc.fs_type, type, sizeof(misc.fs_type));
+    if (strlen(name) >= sizeof(misc.blk_device)) {
+        LOGE("New device name of '%s' is too large for bootloader.cpp\n", name);
+    } else {
+        strcpy(misc.blk_device, name);
+    }
+}
 
 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);
@@ -32,11 +56,19 @@
 static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v);
 
 int get_bootloader_message(struct bootloader_message *out) {
+#if 0
     Volume* v = volume_for_path("/misc");
     if (v == NULL) {
       LOGE("Cannot load volume /misc!\n");
       return -1;
     }
+#else
+    Volume* v = &misc;
+    if (v->fs_type[0] == 0) {
+        LOGI("Not using /misc, not defined in fstab.\n");
+        return -1;
+    }
+#endif
     if (strcmp(v->fs_type, "mtd") == 0) {
         return get_bootloader_message_mtd(out, v);
     } else if (strcmp(v->fs_type, "emmc") == 0) {
@@ -47,11 +79,19 @@
 }
 
 int set_bootloader_message(const struct bootloader_message *in) {
+#if 0
     Volume* v = volume_for_path("/misc");
     if (v == NULL) {
       LOGE("Cannot load volume /misc!\n");
       return -1;
     }
+#else
+    Volume* v = &misc;
+    if (v->fs_type[0] == 0) {
+        LOGI("Not using /misc, not defined in fstab.\n");
+        return -1;
+    }
+#endif
     if (strcmp(v->fs_type, "mtd") == 0) {
         return set_bootloader_message_mtd(in, v);
     } else if (strcmp(v->fs_type, "emmc") == 0) {
@@ -168,6 +208,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 +228,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 +247,82 @@
     }
     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", (int)sizeof(boot.command), boot.command);
+    }
+
+    if (boot.status[0] != 0 && boot.status[0] != 255) {
+        LOGI("Boot status: %.*s\n", (int)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 *token;
+            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;
+                token = strtok(buf, "\r\n");
+                if (token != NULL) {
+                    (*argv)[*argc] = strdup(token);  // Strip newline.
+                } else {
+                    --*argc;
+                }
+            }
+
+            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..b9d70ed 100644
--- a/bootloader.h
+++ b/bootloader.h
@@ -64,6 +64,9 @@
 int get_bootloader_message(struct bootloader_message *out);
 int set_bootloader_message(const struct bootloader_message *in);
 
+void set_misc_device(const char* type, const char* name);
+void get_args(int *argc, char ***argv);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/common.h b/common.h
index b818ceb..3afb633 100644
--- a/common.h
+++ b/common.h
@@ -25,7 +25,7 @@
 extern "C" {
 #endif
 
-#define LOGE(...) ui_print("E:" __VA_ARGS__)
+#define LOGE(...) fprintf(stdout, "E:" __VA_ARGS__)
 #define LOGW(...) fprintf(stdout, "W:" __VA_ARGS__)
 #define LOGI(...) fprintf(stdout, "I:" __VA_ARGS__)
 
@@ -41,7 +41,7 @@
 #define EXPAND(x) STRINGIFY(x)
 
 extern bool modified_flash;
-typedef struct fstab_rec Volume;
+//typedef struct fstab_rec Volume;
 
 // fopen a file, mounting volumes and making parent dirs as necessary.
 FILE* fopen_path(const char *path, const char *mode);
diff --git a/crypto/lollipop/Android.mk b/crypto/lollipop/Android.mk
new file mode 100644
index 0000000..16dfd28
--- /dev/null
+++ b/crypto/lollipop/Android.mk
@@ -0,0 +1,52 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcryptfslollipop
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_CFLAGS :=
+LOCAL_SRC_FILES = cryptfs.c
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+    LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+    LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+    LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+    LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
+    LOCAL_C_INCLUDES +=  external/boringssl/src/include
+endif
+
+LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpdec
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := main.c cryptfs.c
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libc
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+    LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+    LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+    LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+    LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
+    LOCAL_C_INCLUDES +=  external/boringssl/src/include
+endif
+
+LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/lollipop/cryptfs.c b/crypto/lollipop/cryptfs.c
new file mode 100644
index 0000000..87e4c98
--- /dev/null
+++ b/crypto/lollipop/cryptfs.c
@@ -0,0 +1,1871 @@
+/*
+ * 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 <linux/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.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 <errno.h>
+#include <linux/kdev_t.h>
+#include <time.h>
+#include "cryptfs.h"
+#include "cutils/properties.h"
+#include "crypto_scrypt.h"
+
+#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+#include <hardware/keymaster.h>
+#else
+#include <stdbool.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <hardware/keymaster0.h>
+#include <hardware/keymaster1.h>
+#endif
+
+#ifndef min /* already defined by windows.h */
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define UNUSED __attribute__((unused))
+
+#define UNUSED __attribute__((unused))
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+#include "cryptfs_hw.h"
+#endif
+
+#define DM_CRYPT_BUF_SIZE 4096
+
+#define HASH_COUNT 2000
+#define KEY_LEN_BYTES 16
+#define IV_LEN_BYTES 16
+
+#define KEY_IN_FOOTER  "footer"
+
+// "default_password" encoded into hex (d=0x64 etc)
+#define DEFAULT_PASSWORD "64656661756c745f70617373776f7264"
+
+#define EXT4_FS 1
+#define F2FS_FS 2
+
+#define TABLE_LOAD_RETRIES 10
+
+#define RSA_KEY_SIZE 2048
+#define RSA_KEY_SIZE_BYTES (RSA_KEY_SIZE / 8)
+#define RSA_EXPONENT 0x10001
+#define KEYMASTER_CRYPTFS_RATE_LIMIT 1  // Maximum one try per second
+
+#define RETRY_MOUNT_ATTEMPTS 10
+#define RETRY_MOUNT_DELAY_SECONDS 1
+
+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;
+static char key_fname[PROPERTY_VALUE_MAX] = "";
+static char real_blkdev[PROPERTY_VALUE_MAX] = "";
+static char file_system[PROPERTY_VALUE_MAX] = "";
+
+void set_partition_data(const char* block_device, const char* key_location, const char* fs)
+{
+  strcpy(key_fname, key_location);
+  strcpy(real_blkdev, block_device);
+  strcpy(file_system, fs);
+}
+
+#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+static int keymaster_init(keymaster_device_t **keymaster_dev)
+{
+    int rc;
+
+    const hw_module_t* mod;
+    rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+    if (rc) {
+        printf("could not find any keystore module\n");
+        goto out;
+    }
+
+    rc = keymaster_open(mod, keymaster_dev);
+    if (rc) {
+        printf("could not open keymaster device in %s (%s)\n",
+            KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+        goto out;
+    }
+
+    return 0;
+
+out:
+    *keymaster_dev = NULL;
+    return rc;
+}
+
+/* Should we use keymaster? */
+static int keymaster_check_compatibility()
+{
+    keymaster_device_t *keymaster_dev = 0;
+    int rc = 0;
+
+    if (keymaster_init(&keymaster_dev)) {
+        printf("Failed to init keymaster\n");
+        rc = -1;
+        goto out;
+    }
+
+    printf("keymaster version is %d\n", keymaster_dev->common.module->module_api_version);
+
+#if (KEYMASTER_HEADER_VERSION >= 3)
+    if (keymaster_dev->common.module->module_api_version
+            < KEYMASTER_MODULE_API_VERSION_0_3) {
+        rc = 0;
+        goto out;
+    }
+
+    if (keymaster_dev->flags & KEYMASTER_BLOBS_ARE_STANDALONE) {
+        rc = 1;
+    }
+
+#endif
+out:
+    keymaster_close(keymaster_dev);
+    return rc;
+}
+
+/* Create a new keymaster key and store it in this footer */
+static int keymaster_create_key(struct crypt_mnt_ftr *ftr)
+{
+    uint8_t* key = 0;
+    keymaster_device_t *keymaster_dev = 0;
+
+    if (keymaster_init(&keymaster_dev)) {
+        printf("Failed to init keymaster\n");
+        return -1;
+    }
+
+    int rc = 0;
+
+    keymaster_rsa_keygen_params_t params;
+    memset(&params, '\0', sizeof(params));
+    params.public_exponent = RSA_EXPONENT;
+    params.modulus_size = RSA_KEY_SIZE;
+
+    size_t key_size;
+    if (keymaster_dev->generate_keypair(keymaster_dev, TYPE_RSA, &params,
+                                        &key, &key_size)) {
+        printf("Failed to generate keypair\n");
+        rc = -1;
+        goto out;
+    }
+
+    if (key_size > KEYMASTER_BLOB_SIZE) {
+        printf("Keymaster key too large for crypto footer\n");
+        rc = -1;
+        goto out;
+    }
+
+    memcpy(ftr->keymaster_blob, key, key_size);
+    ftr->keymaster_blob_size = key_size;
+
+out:
+    keymaster_close(keymaster_dev);
+    free(key);
+    return rc;
+}
+
+/* This signs the given object using the keymaster key. */
+static int keymaster_sign_object(struct crypt_mnt_ftr *ftr,
+                                 const unsigned char *object,
+                                 const size_t object_size,
+                                 unsigned char **signature,
+                                 size_t *signature_size)
+{
+    int rc = 0;
+    keymaster_device_t *keymaster_dev = 0;
+    if (keymaster_init(&keymaster_dev)) {
+        printf("Failed to init keymaster\n");
+        return -1;
+    }
+
+    /* We currently set the digest type to DIGEST_NONE because it's the
+     * only supported value for keymaster. A similar issue exists with
+     * PADDING_NONE. Long term both of these should likely change.
+     */
+    keymaster_rsa_sign_params_t params;
+    params.digest_type = DIGEST_NONE;
+    params.padding_type = PADDING_NONE;
+
+    unsigned char to_sign[RSA_KEY_SIZE_BYTES];
+    size_t to_sign_size = sizeof(to_sign);
+    memset(to_sign, 0, RSA_KEY_SIZE_BYTES);
+
+    // To sign a message with RSA, the message must satisfy two
+    // constraints:
+    //
+    // 1. The message, when interpreted as a big-endian numeric value, must
+    //    be strictly less than the public modulus of the RSA key.  Note
+    //    that because the most significant bit of the public modulus is
+    //    guaranteed to be 1 (else it's an (n-1)-bit key, not an n-bit
+    //    key), an n-bit message with most significant bit 0 always
+    //    satisfies this requirement.
+    //
+    // 2. The message must have the same length in bits as the public
+    //    modulus of the RSA key.  This requirement isn't mathematically
+    //    necessary, but is necessary to ensure consistency in
+    //    implementations.
+    switch (ftr->kdf_type) {
+        case KDF_SCRYPT_KEYMASTER_UNPADDED:
+            // This is broken: It produces a message which is shorter than
+            // the public modulus, failing criterion 2.
+            memcpy(to_sign, object, object_size);
+            to_sign_size = object_size;
+            printf("Signing unpadded object\n");
+            break;
+        case KDF_SCRYPT_KEYMASTER_BADLY_PADDED:
+            // This is broken: Since the value of object is uniformly
+            // distributed, it produces a message that is larger than the
+            // public modulus with probability 0.25.
+            memcpy(to_sign, object, min(RSA_KEY_SIZE_BYTES, object_size));
+            printf("Signing end-padded object\n");
+            break;
+        case KDF_SCRYPT_KEYMASTER:
+            // This ensures the most significant byte of the signed message
+            // is zero.  We could have zero-padded to the left instead, but
+            // this approach is slightly more robust against changes in
+            // object size.  However, it's still broken (but not unusably
+            // so) because we really should be using a proper RSA padding
+            // function, such as OAEP.
+            //
+            // TODO(paullawrence): When keymaster 0.4 is available, change
+            // this to use the padding options it provides.
+            memcpy(to_sign + 1, object, min(RSA_KEY_SIZE_BYTES - 1, object_size));
+            printf("Signing safely-padded object\n");
+            break;
+        default:
+            printf("Unknown KDF type %d\n", ftr->kdf_type);
+            return -1;
+    }
+
+    rc = keymaster_dev->sign_data(keymaster_dev,
+                                  &params,
+                                  ftr->keymaster_blob,
+                                  ftr->keymaster_blob_size,
+                                  to_sign,
+                                  to_sign_size,
+                                  signature,
+                                  signature_size);
+
+    keymaster_close(keymaster_dev);
+    return rc;
+}
+#else //#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+static int keymaster_init(keymaster0_device_t **keymaster0_dev,
+                          keymaster1_device_t **keymaster1_dev)
+{
+    int rc;
+
+    const hw_module_t* mod;
+    rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+    if (rc) {
+        printf("could not find any keystore module\n");
+        goto err;
+    }
+
+    printf("keymaster module name is %s\n", mod->name);
+    printf("keymaster version is %d\n", mod->module_api_version);
+
+    *keymaster0_dev = NULL;
+    *keymaster1_dev = NULL;
+    if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
+        printf("Found keymaster1 module, using keymaster1 API.\n");
+        rc = keymaster1_open(mod, keymaster1_dev);
+    } else {
+        printf("Found keymaster0 module, using keymaster0 API.\n");
+        rc = keymaster0_open(mod, keymaster0_dev);
+    }
+
+    if (rc) {
+        printf("could not open keymaster device in %s (%s)\n",
+              KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+        goto err;
+    }
+
+    return 0;
+
+err:
+    *keymaster0_dev = NULL;
+    *keymaster1_dev = NULL;
+    return rc;
+}
+
+/* Should we use keymaster? */
+static int keymaster_check_compatibility()
+{
+    keymaster0_device_t *keymaster0_dev = 0;
+    keymaster1_device_t *keymaster1_dev = 0;
+    int rc = 0;
+
+    if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) {
+        printf("Failed to init keymaster\n");
+        rc = -1;
+        goto out;
+    }
+
+    if (keymaster1_dev) {
+        rc = 1;
+        goto out;
+    }
+
+    // TODO(swillden): Check to see if there's any reason to require v0.3.  I think v0.1 and v0.2
+    // should work.
+    if (keymaster0_dev->common.module->module_api_version
+            < KEYMASTER_MODULE_API_VERSION_0_3) {
+        rc = 0;
+        goto out;
+    }
+
+    if (!(keymaster0_dev->flags & KEYMASTER_SOFTWARE_ONLY) &&
+        (keymaster0_dev->flags & KEYMASTER_BLOBS_ARE_STANDALONE)) {
+        rc = 1;
+    }
+
+out:
+    if (keymaster1_dev) {
+        keymaster1_close(keymaster1_dev);
+    }
+    if (keymaster0_dev) {
+        keymaster0_close(keymaster0_dev);
+    }
+    return rc;
+}
+
+/* Create a new keymaster key and store it in this footer */
+static int keymaster_create_key(struct crypt_mnt_ftr *ftr)
+{
+    uint8_t* key = 0;
+    keymaster0_device_t *keymaster0_dev = 0;
+    keymaster1_device_t *keymaster1_dev = 0;
+
+    if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) {
+        printf("Failed to init keymaster\n");
+        return -1;
+    }
+
+    int rc = 0;
+    size_t key_size = 0;
+    if (keymaster1_dev) {
+        keymaster_key_param_t params[] = {
+            /* Algorithm & size specifications.  Stick with RSA for now.  Switch to AES later. */
+            keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
+            keymaster_param_int(KM_TAG_KEY_SIZE, RSA_KEY_SIZE),
+            keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, RSA_EXPONENT),
+
+	    /* The only allowed purpose for this key is signing. */
+	    keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+
+            /* Padding & digest specifications. */
+            keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+            keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+
+            /* Require that the key be usable in standalone mode.  File system isn't available. */
+            keymaster_param_enum(KM_TAG_BLOB_USAGE_REQUIREMENTS, KM_BLOB_STANDALONE),
+
+            /* No auth requirements, because cryptfs is not yet integrated with gatekeeper. */
+            keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+
+            /* Rate-limit key usage attempts, to rate-limit brute force */
+            keymaster_param_int(KM_TAG_MIN_SECONDS_BETWEEN_OPS, KEYMASTER_CRYPTFS_RATE_LIMIT),
+        };
+        keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) };
+        keymaster_key_blob_t key_blob;
+        keymaster_error_t error = keymaster1_dev->generate_key(keymaster1_dev, &param_set,
+                                                               &key_blob,
+                                                               NULL /* characteristics */);
+        if (error != KM_ERROR_OK) {
+            printf("Failed to generate keymaster1 key, error %d\n", error);
+            rc = -1;
+            goto out;
+        }
+
+        key = (uint8_t*)key_blob.key_material;
+        key_size = key_blob.key_material_size;
+    }
+    else if (keymaster0_dev) {
+        keymaster_rsa_keygen_params_t params;
+        memset(&params, '\0', sizeof(params));
+        params.public_exponent = RSA_EXPONENT;
+        params.modulus_size = RSA_KEY_SIZE;
+
+        if (keymaster0_dev->generate_keypair(keymaster0_dev, TYPE_RSA, &params,
+                                             &key, &key_size)) {
+            printf("Failed to generate keypair\n");
+            rc = -1;
+            goto out;
+        }
+    } else {
+        printf("Cryptfs bug: keymaster_init succeeded but didn't initialize a device\n");
+        rc = -1;
+        goto out;
+    }
+
+    if (key_size > KEYMASTER_BLOB_SIZE) {
+        printf("Keymaster key too large for crypto footer\n");
+        rc = -1;
+        goto out;
+    }
+
+    memcpy(ftr->keymaster_blob, key, key_size);
+    ftr->keymaster_blob_size = key_size;
+
+out:
+    if (keymaster0_dev)
+        keymaster0_close(keymaster0_dev);
+    if (keymaster1_dev)
+        keymaster1_close(keymaster1_dev);
+    free(key);
+    return rc;
+}
+
+/* This signs the given object using the keymaster key. */
+static int keymaster_sign_object(struct crypt_mnt_ftr *ftr,
+                                 const unsigned char *object,
+                                 const size_t object_size,
+                                 unsigned char **signature,
+                                 size_t *signature_size)
+{
+    int rc = 0;
+    keymaster0_device_t *keymaster0_dev = 0;
+    keymaster1_device_t *keymaster1_dev = 0;
+    if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) {
+        printf("Failed to init keymaster\n");
+        rc = -1;
+        goto out;
+    }
+
+    unsigned char to_sign[RSA_KEY_SIZE_BYTES];
+    size_t to_sign_size = sizeof(to_sign);
+    memset(to_sign, 0, RSA_KEY_SIZE_BYTES);
+
+    // To sign a message with RSA, the message must satisfy two
+    // constraints:
+    //
+    // 1. The message, when interpreted as a big-endian numeric value, must
+    //    be strictly less than the public modulus of the RSA key.  Note
+    //    that because the most significant bit of the public modulus is
+    //    guaranteed to be 1 (else it's an (n-1)-bit key, not an n-bit
+    //    key), an n-bit message with most significant bit 0 always
+    //    satisfies this requirement.
+    //
+    // 2. The message must have the same length in bits as the public
+    //    modulus of the RSA key.  This requirement isn't mathematically
+    //    necessary, but is necessary to ensure consistency in
+    //    implementations.
+    switch (ftr->kdf_type) {
+        case KDF_SCRYPT_KEYMASTER:
+            // This ensures the most significant byte of the signed message
+            // is zero.  We could have zero-padded to the left instead, but
+            // this approach is slightly more robust against changes in
+            // object size.  However, it's still broken (but not unusably
+            // so) because we really should be using a proper deterministic
+            // RSA padding function, such as PKCS1.
+            memcpy(to_sign + 1, object, min(RSA_KEY_SIZE_BYTES - 1, object_size));
+            printf("Signing safely-padded object\n");
+            break;
+        default:
+            printf("Unknown KDF type %d\n", ftr->kdf_type);
+            rc = -1;
+            goto out;
+    }
+
+    if (keymaster0_dev) {
+        keymaster_rsa_sign_params_t params;
+        params.digest_type = DIGEST_NONE;
+        params.padding_type = PADDING_NONE;
+
+        rc = keymaster0_dev->sign_data(keymaster0_dev,
+                                      &params,
+                                      ftr->keymaster_blob,
+                                      ftr->keymaster_blob_size,
+                                      to_sign,
+                                      to_sign_size,
+                                      signature,
+                                      signature_size);
+        goto out;
+    } else if (keymaster1_dev) {
+        keymaster_key_blob_t key = { ftr->keymaster_blob, ftr->keymaster_blob_size };
+        keymaster_key_param_t params[] = {
+            keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+            keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+        };
+        keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) };
+        keymaster_operation_handle_t op_handle;
+        keymaster_error_t error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key,
+                                                        &param_set, NULL /* out_params */,
+                                                        &op_handle);
+        if (error == KM_ERROR_KEY_RATE_LIMIT_EXCEEDED) {
+            // Key usage has been rate-limited.  Wait a bit and try again.
+            sleep(KEYMASTER_CRYPTFS_RATE_LIMIT);
+            error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key,
+                                          &param_set, NULL /* out_params */,
+                                          &op_handle);
+        }
+        if (error != KM_ERROR_OK) {
+            printf("Error starting keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+
+        keymaster_blob_t input = { to_sign, to_sign_size };
+        size_t input_consumed;
+        error = keymaster1_dev->update(keymaster1_dev, op_handle, NULL /* in_params */,
+                                       &input, &input_consumed, NULL /* out_params */,
+                                       NULL /* output */);
+        if (error != KM_ERROR_OK) {
+            printf("Error sending data to keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+        if (input_consumed != to_sign_size) {
+            // This should never happen.  If it does, it's a bug in the keymaster implementation.
+            printf("Keymaster update() did not consume all data.\n");
+            keymaster1_dev->abort(keymaster1_dev, op_handle);
+            rc = -1;
+            goto out;
+        }
+
+        keymaster_blob_t tmp_sig;
+        error = keymaster1_dev->finish(keymaster1_dev, op_handle, NULL /* in_params */,
+                                       NULL /* verify signature */, NULL /* out_params */,
+                                       &tmp_sig);
+        if (error != KM_ERROR_OK) {
+            printf("Error finishing keymaster signature transaction: %d\n", error);
+            rc = -1;
+            goto out;
+        }
+
+        *signature = (uint8_t*)tmp_sig.data;
+        *signature_size = tmp_sig.data_length;
+    } else {
+        printf("Cryptfs bug: keymaster_init succeded but didn't initialize a device.\n");
+        rc = -1;
+        goto out;
+    }
+
+    out:
+        if (keymaster1_dev)
+            keymaster1_close(keymaster1_dev);
+        if (keymaster0_dev)
+            keymaster0_close(keymaster0_dev);
+
+        return rc;
+}
+#endif //#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+
+/* Store password when userdata is successfully decrypted and mounted.
+ * Cleared by cryptfs_clear_password
+ *
+ * To avoid a double prompt at boot, we need to store the CryptKeeper
+ * password and pass it to KeyGuard, which uses it to unlock KeyStore.
+ * Since the entire framework is torn down and rebuilt after encryption,
+ * we have to use a daemon or similar to store the password. Since vold
+ * is secured against IPC except from system processes, it seems a reasonable
+ * place to store this.
+ *
+ * password should be cleared once it has been used.
+ *
+ * password is aged out after password_max_age_seconds seconds.
+ */
+static char* password = 0;
+static int password_expiry_time = 0;
+static const int password_max_age_seconds = 60;
+
+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\n", 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_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;
+  unsigned int nr_sec;
+  int rc = -1;
+
+  if (!cached_data) {
+    printf("get_crypt_ftr_info crypto key location: '%s'\n", key_fname);
+    if (!strcmp(key_fname, 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_fname, 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;
+}
+
+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;
+}
+
+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 hexdigit (char c)
+{
+    if (c >= '0' && c <= '9') return c - '0';
+    c = tolower(c);
+    if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+    return -1;
+}
+
+static unsigned char* convert_hex_ascii_to_key(const char* master_key_ascii,
+                                               unsigned int* out_keysize)
+{
+    unsigned int i;
+    *out_keysize = 0;
+
+    size_t size = strlen (master_key_ascii);
+    if (size % 2) {
+        printf("Trying to convert ascii string of odd length\n");
+        return NULL;
+    }
+
+    unsigned char* master_key = (unsigned char*) malloc(size / 2);
+    if (master_key == 0) {
+        printf("Cannot allocate\n");
+        return NULL;
+    }
+
+    for (i = 0; i < size; i += 2) {
+        int high_nibble = hexdigit (master_key_ascii[i]);
+        int low_nibble = hexdigit (master_key_ascii[i + 1]);
+
+        if(high_nibble < 0 || low_nibble < 0) {
+            printf("Invalid hex string\n");
+            free (master_key);
+            return NULL;
+        }
+
+        master_key[*out_keysize] = high_nibble * 16 + low_nibble;
+        (*out_keysize)++;
+    }
+
+    return master_key;
+}
+
+/* Convert a binary key of specified length into an ascii hex string equivalent,
+ * without the leading 0x and with null termination
+ */
+static 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;
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name) && is_hw_fde_enabled()) {
+   printf("load_crypto_mapping_table using req-crypt\n");
+   strlcpy(tgt->target_type, "req-crypt",DM_MAX_TYPE_NAME);
+ } else {
+   printf("load_crypto_mapping_table using crypt\n");
+   strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME);
+ }
+#else
+  strcpy(tgt->target_type, "crypt");
+#endif
+
+  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);
+
+  printf("%s: target_type = %s\n", __func__, tgt->target_type);
+  printf("%s: real_blk_name = %s, extra_params = %s\n", __func__, 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 flag;
+    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) {
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+        if (is_hw_fde_enabled()) {
+            flag = (!strcmp(v->name, "crypt") || !strcmp(v->name, "req-crypt"));
+        } else {
+            flag = (!strcmp(v->name, "crypt"));
+        }
+        printf("get_dm_crypt_version flag: %i, name: '%s'\n", flag, v->name);
+        if (flag) {
+#else
+        if (! strcmp(v->name, "crypt")) {
+#endif
+            /* 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=0;
+  int i;
+  int retval = -1;
+  int version[3];
+  char *extra_params;
+  int load_count;
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+  char encrypted_state[PROPERTY_VALUE_MAX] = {0};
+  char progress[PROPERTY_VALUE_MAX] = {0};
+#endif
+
+  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);
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+  if (is_hw_fde_enabled() && is_hw_disk_encryption((char*) crypt_ftr->crypto_type_name)) {
+      /* Set fde_enabled if either FDE completed or in-progress */
+      property_get("ro.crypto.state", encrypted_state, ""); /* FDE completed */
+      property_get("vold.encrypt_progress", progress, ""); /* FDE in progress */
+      if (!strcmp(encrypted_state, "encrypted") || strcmp(progress, "")) {
+          extra_params = "fde_enabled";
+          printf("create_crypto_blk_dev extra_params set to fde_enabled\n");
+      } else {
+          extra_params = "fde_disabled";
+          printf("create_crypto_blk_dev extra_params set to fde_disabled\n");
+      }
+  } else {
+      extra_params = "";
+      printf("create_crypto_blk_dev extra_params set to empty string\n");
+      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");
+          }
+      }
+  }
+#else
+  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");
+      }
+  }
+#endif
+
+  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;
+}
+
+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 int pbkdf2(const char *passwd, const unsigned char *salt,
+                  unsigned char *ikey, void *params UNUSED)
+{
+    printf("Using pbkdf2 for cryptfs KDF\n");
+
+    /* Turn the password into a key and IV that can decrypt the master key */
+    unsigned int keysize;
+    char* master_key = (char*)convert_hex_ascii_to_key(passwd, &keysize);
+    if (!master_key) return -1;
+    PKCS5_PBKDF2_HMAC_SHA1(master_key, keysize, salt, SALT_LEN,
+                           HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey);
+
+    memset(master_key, 0, keysize);
+    free (master_key);
+    return 0;
+}
+
+static int scrypt(const char *passwd, const unsigned char *salt,
+                  unsigned char *ikey, void *params)
+{
+    printf("Using scrypt for cryptfs KDF\n");
+
+    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 */
+    unsigned int keysize;
+    unsigned char* master_key = convert_hex_ascii_to_key(passwd, &keysize);
+    if (!master_key) return -1;
+    crypto_scrypt(master_key, keysize, salt, SALT_LEN, N, r, p, ikey,
+            KEY_LEN_BYTES + IV_LEN_BYTES);
+
+    memset(master_key, 0, keysize);
+    free (master_key);
+    return 0;
+}
+
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+                            unsigned char *ikey, void *params)
+{
+    printf("Using scrypt with keymaster for cryptfs KDF\n");
+
+    int rc;
+    unsigned int key_size;
+    size_t signature_size;
+    unsigned char* signature;
+    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;
+
+    unsigned char* master_key = convert_hex_ascii_to_key(passwd, &key_size);
+    if (!master_key) {
+        printf("Failed to convert passwd from hex\n");
+        return -1;
+    }
+
+    rc = crypto_scrypt(master_key, key_size, salt, SALT_LEN,
+                       N, r, p, ikey, KEY_LEN_BYTES + IV_LEN_BYTES);
+    memset(master_key, 0, key_size);
+    free(master_key);
+
+    if (rc) {
+        printf("scrypt failed\n");
+        return -1;
+    }
+
+    if (keymaster_sign_object(ftr, ikey, KEY_LEN_BYTES + IV_LEN_BYTES,
+                              &signature, &signature_size)) {
+        printf("Signing failed\n");
+        return -1;
+    }
+
+    rc = crypto_scrypt(signature, signature_size, salt, SALT_LEN,
+                       N, r, p, ikey, KEY_LEN_BYTES + IV_LEN_BYTES);
+    free(signature);
+
+    if (rc) {
+        printf("scrypt failed\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int encrypt_master_key(const char *passwd, const unsigned char *salt,
+                              const 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;
+    int rc = 0;
+
+    /* Turn the password into an intermediate key and IV that can decrypt the master key */
+    get_device_scrypt_params(crypt_ftr);
+
+    switch (crypt_ftr->kdf_type) {
+    case KDF_SCRYPT_KEYMASTER_UNPADDED:
+    case KDF_SCRYPT_KEYMASTER_BADLY_PADDED:
+    case KDF_SCRYPT_KEYMASTER:
+        if (keymaster_create_key(crypt_ftr)) {
+            printf("keymaster_create_key failed\n");
+            return -1;
+        }
+
+        if (scrypt_keymaster(passwd, salt, ikey, crypt_ftr)) {
+            printf("scrypt failed\n");
+            return -1;
+        }
+        break;
+
+    case KDF_SCRYPT:
+        if (scrypt(passwd, salt, ikey, crypt_ftr)) {
+            printf("scrypt failed\n");
+            return -1;
+        }
+        break;
+
+    default:
+        printf("Invalid kdf_type\n");
+        return -1;
+    }
+
+    /* 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;
+    }
+#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+    if (! EVP_EncryptFinal(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) {
+#else
+    if (! EVP_EncryptFinal_ex(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) {
+#endif
+        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;
+    }
+
+    /* Store the scrypt of the intermediate key, so we can validate if it's a
+       password error or mount error when things go wrong.
+       Note there's no need to check for errors, since if this is incorrect, we
+       simply won't wipe userdata, which is the correct default behavior
+    */
+    int N = 1 << crypt_ftr->N_factor;
+    int r = 1 << crypt_ftr->r_factor;
+    int p = 1 << crypt_ftr->p_factor;
+
+    rc = crypto_scrypt(ikey, KEY_LEN_BYTES,
+                       crypt_ftr->salt, sizeof(crypt_ftr->salt), N, r, p,
+                       crypt_ftr->scrypted_intermediate_key,
+                       sizeof(crypt_ftr->scrypted_intermediate_key));
+
+    if (rc) {
+      printf("encrypt_master_key: crypto_scrypt failed\n");
+    }
+
+    return 0;
+}
+
+static int decrypt_master_key_aux(char *passwd, unsigned char *salt,
+                                  unsigned char *encrypted_master_key,
+                                  unsigned char *decrypted_master_key,
+                                  kdf_func kdf, void *kdf_params,
+                                  unsigned char** intermediate_key,
+                                  size_t* intermediate_key_size)
+{
+  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 an intermediate key and IV that can decrypt the
+     master key */
+  if (kdf(passwd, salt, ikey, kdf_params)) {
+    printf("kdf failed\n");
+    return -1;
+  }
+
+  /* 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;
+  }
+#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+  if (! EVP_DecryptFinal(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) {
+#else
+  if (! EVP_DecryptFinal_ex(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) {
+#endif
+    return -1;
+  }
+
+  if (decrypted_len + final_len != KEY_LEN_BYTES) {
+    return -1;
+  }
+
+  /* Copy intermediate key if needed by params */
+  if (intermediate_key && intermediate_key_size) {
+    *intermediate_key = (unsigned char*) malloc(KEY_LEN_BYTES);
+    if (intermediate_key) {
+      memcpy(*intermediate_key, ikey, KEY_LEN_BYTES);
+      *intermediate_key_size = KEY_LEN_BYTES;
+    }
+  }
+
+  return 0;
+}
+
+static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
+{
+    if (ftr->kdf_type == KDF_SCRYPT_KEYMASTER_UNPADDED ||
+        ftr->kdf_type == KDF_SCRYPT_KEYMASTER_BADLY_PADDED ||
+        ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
+        *kdf = scrypt_keymaster;
+        *kdf_params = ftr;
+    } else if (ftr->kdf_type == KDF_SCRYPT) {
+        *kdf = scrypt;
+        *kdf_params = ftr;
+    } else {
+        *kdf = pbkdf2;
+        *kdf_params = NULL;
+    }
+}
+
+static int decrypt_master_key(char *passwd, unsigned char *decrypted_master_key,
+                              struct crypt_mnt_ftr *crypt_ftr,
+                              unsigned char** intermediate_key,
+                              size_t* intermediate_key_size)
+{
+    kdf_func kdf;
+    void *kdf_params;
+    int ret;
+
+    get_kdf_func(crypt_ftr, &kdf, &kdf_params);
+    ret = decrypt_master_key_aux(passwd, crypt_ftr->salt, crypt_ftr->master_key,
+                                 decrypted_master_key, kdf, kdf_params,
+                                 intermediate_key, intermediate_key_size);
+    if (ret != 0) {
+        printf("failure decrypting master key\n");
+    }
+
+    return ret;
+}
+
+static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+                                   char *passwd, char *mount_point, char *label)
+{
+  /* Allocate enough space for a 256 bit key, but we may use less */
+  unsigned char decrypted_master_key[32];
+  char crypto_blkdev[MAXPATHLEN];
+  char tmp_mount_point[64];
+  unsigned int orig_failed_decrypt_count;
+  int rc = 0;
+  kdf_func kdf;
+  void *kdf_params;
+  int use_keymaster = 0;
+  int upgrade = 0;
+  unsigned char* intermediate_key = 0;
+  size_t intermediate_key_size = 0;
+
+  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) ) {
+    if (decrypt_master_key(passwd, decrypted_master_key, crypt_ftr,
+                           &intermediate_key, &intermediate_key_size)) {
+      printf("Failed to decrypt master key\n");
+      rc = -1;
+      goto errout;
+    }
+  }
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+  if (is_hw_fde_enabled()) {
+    if(is_hw_disk_encryption((char*) crypt_ftr->crypto_type_name)) {
+      if (!set_hw_device_encryption_key(passwd, (char*) crypt_ftr->crypto_type_name)) {
+        rc = -1;
+        printf("Failed to set_hw_device_encryption_key\n");
+        goto errout;
+      }
+    }
+  }
+#endif
+
+  // Create crypto block device - all (non fatal) code paths
+  // need it
+  if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
+                            real_blkdev, crypto_blkdev, label)) {
+     printf("Error creating decrypted block device\n");
+     rc = -1;
+     goto errout;
+  }
+
+  /* Work out if the problem is the password or the data */
+  unsigned char scrypted_intermediate_key[sizeof(crypt_ftr->
+                                                 scrypted_intermediate_key)];
+  int N = 1 << crypt_ftr->N_factor;
+  int r = 1 << crypt_ftr->r_factor;
+  int p = 1 << crypt_ftr->p_factor;
+
+  rc = crypto_scrypt(intermediate_key, intermediate_key_size,
+                     crypt_ftr->salt, sizeof(crypt_ftr->salt),
+                     N, r, p, scrypted_intermediate_key,
+                     sizeof(scrypted_intermediate_key));
+
+  // Does the key match the crypto footer?
+  if (rc == 0 && memcmp(scrypted_intermediate_key,
+                        crypt_ftr->scrypted_intermediate_key,
+                        sizeof(scrypted_intermediate_key)) == 0) {
+    printf("Password matches\n");
+    rc = 0;
+  } else {
+    /* Try mounting the file system anyway, just in case the problem's with
+     * the footer, not the key. */
+    sprintf(tmp_mount_point, "%s/tmp_mnt", mount_point);
+    mkdir(tmp_mount_point, 0755);
+    if (mount(crypto_blkdev, tmp_mount_point, file_system, NULL, NULL) != 0) {
+      printf("Error temp mounting decrypted block device '%s'\n", crypto_blkdev);
+      delete_crypto_blk_dev(label);
+
+      rc = ++crypt_ftr->failed_decrypt_count;
+      //put_crypt_ftr_and_key(crypt_ftr); // Do not penalize for attempting to decrypt in recovery
+    } else {
+      /* Success! */
+      printf("Password did not match but decrypted drive mounted - continue\n");
+      umount(tmp_mount_point);
+      rc = 0;
+    }
+  }
+
+  if (rc == 0) {
+    /*crypt_ftr->failed_decrypt_count = 0;
+    if (orig_failed_decrypt_count != 0) {
+      put_crypt_ftr_and_key(crypt_ftr);
+    }*/
+
+    /* 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;
+    printf("%s(): Master key saved\n", __FUNCTION__);*/
+    rc = 0;
+
+    // Upgrade if we're not using the latest KDF.
+    /*use_keymaster = keymaster_check_compatibility();
+    if (crypt_ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
+        // Don't allow downgrade
+    } else if (use_keymaster == 1 && crypt_ftr->kdf_type != KDF_SCRYPT_KEYMASTER) {
+        crypt_ftr->kdf_type = KDF_SCRYPT_KEYMASTER;
+        upgrade = 1;
+    } else if (use_keymaster == 0 && crypt_ftr->kdf_type != KDF_SCRYPT) {
+        crypt_ftr->kdf_type = KDF_SCRYPT;
+        upgrade = 1;
+    }
+
+    if (upgrade) {
+        rc = encrypt_master_key(passwd, crypt_ftr->salt, saved_master_key,
+                                crypt_ftr->master_key, crypt_ftr);
+        if (!rc) {
+            rc = put_crypt_ftr_and_key(crypt_ftr);
+        }
+        printf("Key Derivation Function upgrade: rc=%d\n", rc);
+
+        // Do not fail even if upgrade failed - machine is bootable
+        // Note that if this code is ever hit, there is a *serious* problem
+        // since KDFs should never fail. You *must* fix the kdf before
+        // proceeding!
+        if (rc) {
+          printf("Upgrade failed with error %d,"
+                " but continuing with previous state\n",
+                rc);
+          rc = 0;
+        }
+    }*/
+  }
+
+ errout:
+  if (intermediate_key) {
+    memset(intermediate_key, 0, intermediate_key_size);
+    free(intermediate_key);
+  }
+  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);
+}
+
+int check_unmounted_and_get_ftr(struct crypt_mnt_ftr* crypt_ftr)
+{
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    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\n");
+        //return -1;
+    }
+
+    if (get_crypt_ftr_and_key(crypt_ftr)) {
+        printf("Error getting crypt footer and key\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * TODO - transition patterns to new format in calling code
+ *        and remove this vile hack, and the use of hex in
+ *        the password passing code.
+ *
+ * Patterns are passed in zero based (i.e. the top left dot
+ * is represented by zero, the top middle one etc), but we want
+ * to store them '1' based.
+ * This is to allow us to migrate the calling code to use this
+ * convention. It also solves a nasty problem whereby scrypt ignores
+ * trailing zeros, so patterns ending at the top left could be
+ * truncated, and similarly, you could add the top left to any
+ * pattern and still match.
+ * adjust_passwd is a hack function that returns the alternate representation
+ * if the password appears to be a pattern (hex numbers all less than 09)
+ * If it succeeds we need to try both, and in particular try the alternate
+ * first. If the original matches, then we need to update the footer
+ * with the alternate.
+ * All code that accepts passwords must adjust them first. Since
+ * cryptfs_check_passwd is always the first function called after a migration
+ * (and indeed on any boot) we only need to do the double try in this
+ * function.
+ */
+char* adjust_passwd(const char* passwd)
+{
+    size_t index, length;
+
+    if (!passwd) {
+        return 0;
+    }
+
+    // Check even length. Hex encoded passwords are always
+    // an even length, since each character encodes to two characters.
+    length = strlen(passwd);
+    if (length % 2) {
+        printf("Password not correctly hex encoded.\n");
+        return 0;
+    }
+
+    // Check password is old-style pattern - a collection of hex
+    // encoded bytes less than 9 (00 through 08)
+    for (index = 0; index < length; index +=2) {
+        if (passwd[index] != '0'
+            || passwd[index + 1] < '0' || passwd[index + 1] > '8') {
+            return 0;
+        }
+    }
+
+    // Allocate room for adjusted passwd and null terminate
+    char* adjusted = malloc(length + 1);
+    adjusted[length] = 0;
+
+    // Add 0x31 ('1') to each character
+    for (index = 0; index < length; index += 2) {
+        // output is 31 through 39 so set first byte to three, second to src + 1
+        adjusted[index] = '3';
+        adjusted[index + 1] = passwd[index + 1] + 1;
+    }
+
+    return adjusted;
+}
+
+/*
+ * Passwords in L get passed from Android to cryptfs in hex, so a '1'
+ * gets converted to '31' where 31 is 0x31 which is the ascii character
+ * code in hex of the character '1'. This function will convert the
+ * regular character codes to their hexadecimal representation to make
+ * decrypt work properly with Android 5.0 lollipop decryption.
+ */
+char* hexadj_passwd(const char* passwd, int has_hw_crypto)
+{
+    size_t index, length;
+    const char* ptr = passwd;
+
+    if (!passwd) {
+        return 0;
+    }
+
+    length = strlen(passwd);
+
+    // Allocate room for hex passwd and null terminate
+    char* hex = malloc((length * 2) + 1);
+    hex[length * 2] = 0;
+
+    // Convert to hex
+    for (index = 0; index < length; index++) {
+        sprintf(hex + (index * 2), "%02X", *ptr);
+        ptr++;
+    }
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+    if (has_hw_crypto) {
+        printf("hexadj_passwd converting to lower case for hardware disk crypto.\n");
+        length *= 2;
+        for (index = 0; index < length; index++) {
+            hex[index] = tolower(hex[index]);
+        }
+    }
+#endif
+    return hex;
+}
+
+int cryptfs_check_footer()
+{
+    int rc = -1;
+    struct crypt_mnt_ftr crypt_ftr;
+
+    rc = get_crypt_ftr_and_key(&crypt_ftr);
+
+    return rc;
+}
+
+int cryptfs_check_passwd(char *passwd)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    int rc;
+    int has_hw_crypto = 0;
+
+    rc = check_unmounted_and_get_ftr(&crypt_ftr);
+    if (rc)
+        return rc;
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+    printf("CONFIG_HW_DISK_ENCRYPTION present\n");
+    if (is_hw_fde_enabled() && is_hw_disk_encryption((char*) crypt_ftr.crypto_type_name))
+        has_hw_crypto = 1;
+#endif
+
+    //if (passwd) printf("passwd: '%s'\n", passwd);
+    char* adjusted_passwd;
+    if (!has_hw_crypto)
+        adjusted_passwd = adjust_passwd(passwd);
+    //if (adjusted_passwd) printf("adjusted_passwd: '%s'\n", adjusted_passwd);
+    char* hex_passwd = hexadj_passwd(passwd, has_hw_crypto);
+    //if (hex_passwd) printf("hex_passwd: '%s'\n", hex_passwd);
+    printf("has_hw_crypto is %i\n", has_hw_crypto);
+    if (!has_hw_crypto && adjusted_passwd) {
+        int failed_decrypt_count = crypt_ftr.failed_decrypt_count;
+        //printf("trying adjusted password '%s'\n", adjusted_passwd);
+        rc = test_mount_encrypted_fs(&crypt_ftr, hex_passwd,
+                                     DATA_MNT_POINT, "userdata");
+
+        // Maybe the original one still works?
+        if (rc) {
+            // Don't double count this failure
+            //printf("trying passwd '%s'\n", passwd);
+            crypt_ftr.failed_decrypt_count = failed_decrypt_count;
+            rc = test_mount_encrypted_fs(&crypt_ftr, passwd,
+                                         DATA_MNT_POINT, "userdata");
+            if (!rc) {
+                // cryptfs_changepw also adjusts so pass original
+                // Note that adjust_passwd only recognises patterns
+                // so we can safely use CRYPT_TYPE_PATTERN
+                printf("TWRP NOT Updating pattern to new format\n");
+                //cryptfs_changepw(CRYPT_TYPE_PATTERN, passwd);
+            } else if (hex_passwd) {
+                //printf("trying hex_passwd '%s'\n", hex_passwd);
+                rc = test_mount_encrypted_fs(&crypt_ftr, hex_passwd,
+                                         DATA_MNT_POINT, "userdata");
+            }
+        }
+        free(adjusted_passwd);
+    } else {
+        if (hex_passwd) {
+            //printf("2trying hex_passwd '%s'\n", hex_passwd);
+            rc = test_mount_encrypted_fs(&crypt_ftr, hex_passwd,
+                                         DATA_MNT_POINT, "userdata");
+        } else {
+            rc = 1;
+        }
+        if (rc && passwd) {
+            //printf("2trying passwd '%s'\n", passwd);
+            rc = test_mount_encrypted_fs(&crypt_ftr, passwd,
+                                     DATA_MNT_POINT, "userdata");
+        }
+    }
+
+    if (hex_passwd)
+        free(hex_passwd);
+
+    /*if (rc == 0 && crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
+        printf("cryptfs_check_passwd update expiry time?\n");
+        cryptfs_clear_password();
+        password = strdup(passwd);
+        struct timespec now;
+        clock_gettime(CLOCK_BOOTTIME, &now);
+        password_expiry_time = now.tv_sec + password_max_age_seconds;
+    }*/
+
+    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\n");
+        return -2;
+    }
+
+    if (!master_key_saved) {
+        printf("encrypted fs not yet mounted, aborting\n");
+        return -1;
+    }
+
+    if (!saved_mount_point) {
+        printf("encrypted fs failed to save mount point, aborting\n");
+        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 {
+        char* adjusted_passwd = adjust_passwd(passwd);
+        if (adjusted_passwd) {
+            passwd = adjusted_passwd;
+        }
+
+        decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
+        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;
+        }
+
+        free(adjusted_passwd);
+    }
+
+    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 int 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;
+
+    switch (keymaster_check_compatibility()) {
+    case 1:
+        ftr->kdf_type = KDF_SCRYPT_KEYMASTER;
+        break;
+
+    case 0:
+        ftr->kdf_type = KDF_SCRYPT;
+        break;
+
+    default:
+        printf("keymaster_check_compatibility failed\n");
+        return -1;
+    }
+
+    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;
+    }
+
+    return 0;
+}
+
+/* Returns type of the password, default, pattern, pin or password.
+ */
+int cryptfs_get_password_type(void)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+
+    if (get_crypt_ftr_and_key(&crypt_ftr)) {
+        printf("Error getting crypt footer and key\n");
+        return -1;
+    }
+
+    if (crypt_ftr.flags & CRYPT_INCONSISTENT_STATE) {
+        return -1;
+    }
+
+    return crypt_ftr.crypt_type;
+}
diff --git a/crypto/lollipop/cryptfs.h b/crypto/lollipop/cryptfs.h
new file mode 100644
index 0000000..bc8b463
--- /dev/null
+++ b/crypto/lollipop/cryptfs.h
@@ -0,0 +1,219 @@
+/*
+ * 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>
+#include "openssl/sha.h"
+
+/* The current cryptfs version */
+#define CURRENT_MAJOR_VERSION 1
+#define CURRENT_MINOR_VERSION 3
+
+#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
+#define SCRYPT_LEN 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 /* Encryption partially completed,
+                                            encrypted_upto valid*/
+#define CRYPT_INCONSISTENT_STATE 0x4 /* Set when starting encryption, clear when
+                                        exit cleanly, either through success or
+                                        correctly marked partial encryption */
+#define CRYPT_DATA_CORRUPT 0x8 /* Set when encryption is fine, but the
+                                  underlying volume is corrupt */
+
+/* Allowed values for type in the structure below */
+#define CRYPT_TYPE_PASSWORD 0 /* master_key is encrypted with a password
+                               * Must be zero to be compatible with pre-L
+                               * devices where type is always password.*/
+#define CRYPT_TYPE_DEFAULT  1 /* master_key is encrypted with default
+                               * password */
+#define CRYPT_TYPE_PATTERN  2 /* master_key is encrypted with a pattern */
+#define CRYPT_TYPE_PIN      3 /* master_key is encrypted with a pin */
+#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */
+
+#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
+/* TODO(paullawrence): Remove KDF_SCRYPT_KEYMASTER_UNPADDED and KDF_SCRYPT_KEYMASTER_BADLY_PADDED
+ * when it is safe to do so. */
+#define KDF_SCRYPT_KEYMASTER_UNPADDED 3
+#define KDF_SCRYPT_KEYMASTER_BADLY_PADDED 4
+#define KDF_SCRYPT_KEYMASTER 5
+
+/* Maximum allowed keymaster blob size. */
+#define KEYMASTER_BLOB_SIZE 2048
+
+/* __le32 and __le16 defined in system/extras/ext4_utils/ext4_utils.h */
+#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 crypt_type;    /* how master_key is encrypted. Must be a
+                         * CRYPT_TYPE_XXX value */
+  __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) */
+  __le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
+                            we have to stop (e.g. power low) this is the last
+                            encrypted 512 byte sector.*/
+  __le8  hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
+                                                    set, hash of first block, used
+                                                    to validate before continuing*/
+
+  /* key_master key, used to sign the derived key which is then used to generate
+   * the intermediate key
+   * This key should be used for no other purposes! We use this key to sign unpadded 
+   * data, which is acceptable but only if the key is not reused elsewhere. */
+  __le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
+  __le32 keymaster_blob_size;
+
+  /* Store scrypt of salted intermediate key. When decryption fails, we can
+     check if this matches, and if it does, we know that the problem is with the
+     drive, and there is no point in asking the user for more passwords.
+
+     Note that if any part of this structure is corrupt, this will not match and
+     we will continue to believe the user entered the wrong password. In that
+     case the only solution is for the user to enter a password enough times to
+     force a wipe.
+
+     Note also that there is no need to worry about migration. If this data is
+     wrong, we simply won't recognise a right password, and will continue to
+     prompt. On the first password change, this value will be populated and
+     then we will be OK.
+   */
+  unsigned char scrypted_intermediate_key[SCRYPT_LEN];
+};
+
+/* 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
+
+#define DATA_MNT_POINT "/data"
+
+/* Return values for cryptfs_crypto_complete */
+#define CRYPTO_COMPLETE_NOT_ENCRYPTED  1
+#define CRYPTO_COMPLETE_ENCRYPTED      0
+#define CRYPTO_COMPLETE_BAD_METADATA  -1
+#define CRYPTO_COMPLETE_PARTIAL       -2
+#define CRYPTO_COMPLETE_INCONSISTENT  -3
+#define CRYPTO_COMPLETE_CORRUPT       -4
+
+/* Return values for cryptfs_enable_inplace*() */
+#define ENABLE_INPLACE_OK 0
+#define ENABLE_INPLACE_ERR_OTHER -1
+#define ENABLE_INPLACE_ERR_DEV -2  /* crypto_blkdev issue */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  typedef int (*kdf_func)(const char *passwd, const unsigned char *salt,
+                          unsigned char *ikey, void *params);
+
+  void set_partition_data(const char* block_device, const char* key_location, const char* fs);
+  int cryptfs_check_footer();
+  int cryptfs_check_passwd(char *pw);
+  int cryptfs_verify_passwd(char *newpw);
+  int cryptfs_get_password_type(void);
+  int delete_crypto_blk_dev(char *name);
+#ifdef __cplusplus
+}
+#endif
diff --git a/crypto/lollipop/main.c b/crypto/lollipop/main.c
new file mode 100644
index 0000000..232afb9
--- /dev/null
+++ b/crypto/lollipop/main.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.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 <errno.h>
+#include <linux/kdev_t.h>
+#include <time.h>
+#include "cryptfs.h"
+#include "cutils/properties.h"
+#include "crypto_scrypt.h"
+
+int main() {
+	set_partition_data("/dev/block/platform/sdhci-tegra.3/by-name/UDA", "/dev/block/platform/sdhci-tegra.3/by-name/MD1", "f2fs");
+	//int ret = cryptfs_check_passwd("30303030");
+	int ret = cryptfs_check_passwd("0000");
+	return 0;
+}
diff --git a/crypto/scrypt/Android.mk b/crypto/scrypt/Android.mk
new file mode 100644
index 0000000..4514f94
--- /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 $(LOCAL_PATH)/tests/Android.mk
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..bbe1063
--- /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 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)/Scrypt-config.mk
+
diff --git a/crypto/scrypt/Scrypt.mk b/crypto/scrypt/Scrypt.mk
new file mode 100644
index 0000000..baa41ec
--- /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 external/boringssl/src/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) $(commands_recovery_local_path)/crypto/scrypt/lib/util
+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) $(commands_recovery_local_path)/crypto/scrypt/lib/util
+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..c20b41d
--- /dev/null
+++ b/crypto/scrypt/tests/Android.mk
@@ -0,0 +1,26 @@
+# Build the scrypt unit tests
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+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_TAGS := optional
+LOCAL_MODULE := scrypttwrp_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..ffb568d
--- /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 <UniquePtr.h>
+#include <utils/Log.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..7d92383
--- /dev/null
+++ b/data.cpp
@@ -0,0 +1,1105 @@
+/*
+	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 <ctype.h>
+
+#include <string>
+#include <utility>
+#include <map>
+#include <fstream>
+#include <sstream>
+#include <pthread.h>
+
+#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"
+#include "set_metadata.h"
+#include <cutils/properties.h>
+
+#define DEVID_MAX 64
+#define HWID_MAX 32
+
+#ifndef TW_MAX_BRIGHTNESS
+#define TW_MAX_BRIGHTNESS 255
+#endif
+
+extern "C"
+{
+	#include "twcommon.h"
+	#include "gui/pages.h"
+	#include "minuitwrp/minui.h"
+	void gui_notifyVarChange(const char *name, const char* value);
+}
+
+#define FILE_VERSION 0x00010010
+
+using namespace std;
+
+map<string, DataManager::TStrIntPair>   DataManager::mValues;
+map<string, string>                     DataManager::mConstValues;
+string                                  DataManager::mBackingFile;
+int                                     DataManager::mInitialized = 0;
+
+extern bool datamedia;
+
+#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+pthread_mutex_t DataManager::m_valuesLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+#else
+pthread_mutex_t DataManager::m_valuesLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+#endif
+
+// Device ID functions
+void DataManager::sanitize_device_id(char* device_id) {
+	const char* whitelist ="-._";
+	char str[DEVID_MAX];
+	char* c = str;
+
+	snprintf(str, DEVID_MAX, "%s", device_id);
+	memset(device_id, 0, strlen(device_id));
+	while (*c) {
+		if (isalnum(*c) || 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;
+	size_t i;
+	char line[2048];
+	char hardware_id[HWID_MAX] = { 0 };
+	char device_id[DEVID_MAX] = { 0 };
+	char* token;
+
+#ifdef TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
+	// Use (product_model)_(hardware_id) 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 (i = 0; i < strlen(model_id); i++) {
+			if (model_id[i] == ' ')
+				model_id[i] = '_';
+		}
+		snprintf(device_id, DEVID_MAX, "%s", model_id);
+
+		if (strlen(device_id) < DEVID_MAX) {
+			fp = fopen("proc_cpuinfo.txt", "rt");
+			if (fp != NULL) {
+				while (fgets(line, sizeof(line), fp) != NULL) {
+					if (memcmp(line, CPUINFO_HARDWARE,
+							CPUINFO_HARDWARE_LEN) == 0) {
+						// skip past "Hardware", spaces, and colon
+						token = line + CPUINFO_HARDWARE_LEN;
+						while (*token && (!isgraph(*token) || *token == ':'))
+							token++;
+
+						if (*token && *token != '\n'
+								&& strcmp("UNKNOWN\n", token)) {
+							snprintf(hardware_id, HWID_MAX, "%s", token);
+							if (hardware_id[strlen(hardware_id)-1] == '\n')
+								hardware_id[strlen(hardware_id)-1] = 0;
+							LOGINFO("=> hardware id from cpuinfo: '%s'\n",
+									hardware_id);
+						}
+						break;
+					}
+				}
+				fclose(fp);
+			}
+		}
+
+		if (hardware_id[0] != 0)
+			snprintf(device_id, DEVID_MAX, "%s_%s", model_id, hardware_id);
+
+		sanitize_device_id(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
+	// Check the cmdline to see if the serial number was supplied
+	fp = fopen("/proc/cmdline", "rt");
+	if (fp != NULL) {
+		fgets(line, sizeof(line), fp);
+		fclose(fp); // cmdline is only one line long
+
+		token = strtok(line, " ");
+		while (token) {
+			if (memcmp(token, CMDLINE_SERIALNO, CMDLINE_SERIALNO_LEN) == 0) {
+				token += CMDLINE_SERIALNO_LEN;
+				snprintf(device_id, DEVID_MAX, "%s", token);
+				sanitize_device_id(device_id); // also removes newlines
+				mConstValues.insert(make_pair("device_id", device_id));
+				return;
+			}
+			token = strtok(NULL, " ");
+		}
+	}
+#endif
+	// Check cpuinfo for serial number; if found, use as device_id
+	// If serial number is not found, fallback to hardware_id for the device_id
+	fp = fopen("/proc/cpuinfo", "rt");
+	if (fp != NULL) {
+		while (fgets(line, sizeof(line), fp) != NULL) {
+			if (memcmp(line, CPUINFO_SERIALNO, CPUINFO_SERIALNO_LEN) == 0) {
+				// skip past "Serial", spaces, and colon
+				token = line + CPUINFO_SERIALNO_LEN;
+				while (*token && (!isgraph(*token) || *token == ':'))
+					token++;
+
+				if (*token && *token != '\n') {
+					snprintf(device_id, DEVID_MAX, "%s", token);
+					sanitize_device_id(device_id); // also removes newlines
+					LOGINFO("=> serial from cpuinfo: '%s'\n", device_id);
+					mConstValues.insert(make_pair("device_id", device_id));
+					fclose(fp);
+					return;
+				}
+			} else if (memcmp(line, CPUINFO_HARDWARE,
+					CPUINFO_HARDWARE_LEN) == 0) {
+				// skip past "Hardware", spaces, and colon
+				token = line + CPUINFO_HARDWARE_LEN;
+				while (*token && (!isgraph(*token) || *token == ':'))
+					token++;
+
+				if (*token && *token != '\n') {
+					snprintf(hardware_id, HWID_MAX, "%s", token);
+					if (hardware_id[strlen(hardware_id)-1] == '\n')
+						hardware_id[strlen(hardware_id)-1] = 0;
+					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);
+		snprintf(device_id, DEVID_MAX, "%s", hardware_id);
+		sanitize_device_id(device_id);
+		mConstValues.insert(make_pair("device_id", device_id));
+		return;
+	}
+
+	strcpy(device_id, "serialno");
+	LOGERR("=> device id not found, using '%s'\n", device_id);
+	mConstValues.insert(make_pair("device_id", device_id));
+	return;
+}
+
+int DataManager::ResetDefaults()
+{
+	pthread_mutex_lock(&m_valuesLock);
+	mValues.clear();
+	pthread_mutex_unlock(&m_valuesLock);
+
+	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;
+
+		pthread_mutex_lock(&m_valuesLock);
+
+		pos = mValues.find(Name);
+		if (pos != mValues.end())
+		{
+			pos->second.first = Value;
+			pos->second.second = 1;
+		}
+		else
+			mValues.insert(TNameValuePair(Name, TStrIntPair(Value, 1)));
+
+		pthread_mutex_unlock(&m_valuesLock);
+
+#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);
+
+	pthread_mutex_lock(&m_valuesLock);
+
+	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);
+		}
+	}
+
+	pthread_mutex_unlock(&m_valuesLock);
+
+	fclose(out);
+	tw_set_default_metadata(mBackingFile.c_str());
+#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;
+
+	// Handle property
+	if (localStr.length() > 9 && localStr.substr(0, 9) == "property.") {
+		char property_value[PROPERTY_VALUE_MAX];
+		property_get(localStr.substr(9).c_str(), property_value, "");
+		value = property_value;
+		return 0;
+	}
+
+	map<string, string>::iterator constPos;
+	constPos = mConstValues.find(localStr);
+	if (constPos != mConstValues.end())
+	{
+		value = constPos->second;
+		return 0;
+	}
+
+	pthread_mutex_lock(&m_valuesLock);
+	map<string, TStrIntPair>::iterator pos;
+	pos = mValues.find(localStr);
+	if (pos == mValues.end()){
+		pthread_mutex_unlock(&m_valuesLock);
+		return -1;
+	}
+
+	value = pos->second.first;
+	pthread_mutex_unlock(&m_valuesLock);
+	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 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();
+
+	// Handle property
+	if (varName.length() > 9 && varName.substr(0, 9) == "property.") {
+		int ret = property_set(varName.substr(9).c_str(), value.c_str());
+		if (ret)
+			LOGERR("Error setting property '%s' to '%s'\n", varName.substr(9).c_str(), value.c_str());
+		return ret;
+	}
+
+	// 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;
+
+	pthread_mutex_lock(&m_valuesLock);
+
+	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();
+
+	pthread_mutex_unlock(&m_valuesLock);
+
+#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");
+	pthread_mutex_lock(&m_valuesLock);
+	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());
+	pthread_mutex_unlock(&m_valuesLock);
+}
+
+void DataManager::update_tz_environment_variables(void)
+{
+	setenv("TZ", GetStrValue(TW_TIME_ZONE_VAR).c_str(), 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();
+
+	pthread_mutex_lock(&m_valuesLock);
+
+	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,M3.2.0,M11.1.0", 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,M3.2.0,M11.1.0", 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
+	mValues.insert(make_pair("tw_mount_system_ro", make_pair("2", 1)));
+	mValues.insert(make_pair("tw_never_show_system_ro_page", make_pair("0", 1)));
+
+	pthread_mutex_unlock(&m_valuesLock);
+}
+
+// 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")
+	{
+	   int tw_no_cpu_temp;
+	   GetValue("tw_no_cpu_temp", tw_no_cpu_temp);
+	   if (tw_no_cpu_temp == 1) return -1;
+
+	   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)
+{
+#ifndef TW_OEM_BUILD
+	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());
+#endif
+}
+
+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", GetSettingsStoragePath().c_str());
+	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::GetSettingsStoragePath(void)
+{
+	return GetStrValue("tw_settings_path");
+}
+
+void DataManager::Vibrate(const string varName)
+{
+	int vib_value = 0;
+	GetValue(varName, vib_value);
+	if (vib_value) {
+		vibrate(vib_value);
+	}
+}
diff --git a/data.hpp b/data.hpp
new file mode 100644
index 0000000..2ec2174
--- /dev/null
+++ b/data.hpp
@@ -0,0 +1,87 @@
+/*
+	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>
+#include <pthread.h>
+
+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);
+
+	// 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 GetSettingsStoragePath(void);
+
+protected:
+	typedef pair<string, int> TStrIntPair;
+	typedef pair<string, TStrIntPair> TNameValuePair;
+	static map<string, TStrIntPair> mValues;
+	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);
+
+	static pthread_mutex_t m_valuesLock;
+};
+
+#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..cf30688
--- /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..67cc5df
--- /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_RECOVERY_ROOT_OUT)/sbin/,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)
+LOCAL_C_INCLUDES += 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/Android.mk b/etc/Android.mk
new file mode 100644
index 0000000..89ea0cc
--- /dev/null
+++ b/etc/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2015 TeamWin Recovery 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.
+
+ifneq ($(TW_EXCLUDE_DEFAULT_USB_INIT), true)
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := init.recovery.usb.rc
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+
+# Cannot send to TARGET_RECOVERY_ROOT_OUT since build system wipes init*.rc
+# during ramdisk creation and only allows init.recovery.*.rc files to be copied
+# from TARGET_ROOT_OUT thereafter
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+endif
diff --git a/etc/init.rc b/etc/init.rc
index 6c07c60..3cf5d4c 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -1,18 +1,23 @@
+import /init.recovery.usb.rc
 import /init.recovery.${ro.hardware}.rc
 
 on early-init
     start ueventd
     start healthd
 
+service set_permissive /sbin/permissive.sh
+    oneshot
+    seclabel u:r:recovery:s0
+
 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
@@ -26,19 +31,12 @@
     write /proc/sys/vm/max_map_count 1000000
 
 on fs
+    mount pstore pstore /sys/fs/pstore
+
     mkdir /dev/usb-ffs 0770 shell shell
     mkdir /dev/usb-ffs/adb 0770 shell shell
     mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
 
-    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/f_ffs/aliases adb
-    write /sys/class/android_usb/android0/functions adb
-    write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer}
-    write /sys/class/android_usb/android0/iProduct ${ro.product.model}
-    write /sys/class/android_usb/android0/iSerial ${ro.serialno}
-
 on boot
     ifup lo
     hostname localhost
@@ -83,7 +81,6 @@
     seclabel u:r:healthd:s0
 
 service recovery /sbin/recovery
-    seclabel u:r:recovery:s0
 
 service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
     disabled
@@ -92,8 +89,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/etc/init.recovery.usb.rc b/etc/init.recovery.usb.rc
new file mode 100644
index 0000000..b4a852a
--- /dev/null
+++ b/etc/init.recovery.usb.rc
@@ -0,0 +1,38 @@
+on fs
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/idVendor 18D1
+    write /sys/class/android_usb/android0/idProduct 4EE2
+    write /sys/class/android_usb/android0/f_ffs/aliases adb
+    write /sys/class/android_usb/android0/functions mtp,adb
+    write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer}
+    write /sys/class/android_usb/android0/iProduct ${ro.product.model}
+    write /sys/class/android_usb/android0/iSerial ${ro.serialno}
+
+on property:sys.storage.ums_enabled=1
+    write /sys/class/android_usb/android0/enable 0
+    write /sys/class/android_usb/android0/functions mass_storage,adb
+    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 ${sys.usb.config}
+    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
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..78db534
--- /dev/null
+++ b/find_file.cpp
@@ -0,0 +1,91 @@
+/*
+		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 <stdlib.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..0648eb6
--- /dev/null
+++ b/fixPermissions.cpp
@@ -0,0 +1,548 @@
+/*
+	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;
+
+static const mode_t kMode_0600 = 0600; // S_IRUSR | S_IWUSR
+static const mode_t kMode_0640 = 0640; // S_IRUSR | S_IWUSR | S_IRGRP
+static const mode_t kMode_0644 = 0644; // S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
+static const mode_t kMode_0660 = 0660; // S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
+static const mode_t kMode_0755 = 0755; // S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
+static const mode_t kMode_0771 = 0771; // S_IRWXU | S_IRWXG | S_IXOTH
+
+fixPermissions::fixPermissions() : head(NULL) {
+}
+
+fixPermissions::~fixPermissions() {
+	deletePackages();
+}
+
+#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;
+	}
+	// TODO: what about /data/media/1 etc.?
+	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) {
+	string packageFile = "/data/system/packages.xml";
+	debug = enable_debug;
+	remove_data = remove_data_for_missing_apps;
+	bool 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(packageFile)) != 0) {
+		return -1;
+	}
+
+	gui_print("Fixing app permissions...\n");
+	if (fixApps() != 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;
+		}
+	}
+	gui_print("Done fixing permissions.\n");
+	return 0;
+}
+
+int fixPermissions::fixContexts()
+{
+#ifdef HAVE_SELINUX
+	gui_print("Fixing /data/data/ contexts.\n");
+	fixDataDataContexts();
+	fixDataInternalContexts();
+	gui_print("Done fixing contexts.\n");
+	return 0;
+#endif
+	gui_print("Not fixing SELinux contexts; support not compiled in.\n");
+	return -1;
+}
+
+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, mode_t mode) {
+	LOGINFO("Fixing %s, mode: %o\n", fn.c_str(), mode);
+
+	if (chmod(fn.c_str(), mode) != 0) {
+		LOGERR("Unable to chmod '%s' %o\n", fn.c_str(), mode);
+		return -1;
+	}
+
+	return 0;
+}
+
+int fixPermissions::fixApps() {
+	package* temp = head;
+	while (temp != NULL) {
+		struct stat st;
+		if (stat(temp->codePath.c_str(), &st) == 0) {
+			int new_uid = 0;
+			int new_gid = 0;
+			mode_t perms = 0;
+			bool fix = false;
+			if (temp->appDir.compare("/system/app") == 0 || temp->appDir.compare("/system/priv-app") == 0) {
+				fix = true;
+				new_uid = 0;
+				new_gid = 0;
+				perms = kMode_0644;
+			} else if (temp->appDir.compare("/data/app") == 0 || temp->appDir.compare("/sd-ext/app") == 0) {
+				fix = true;
+				new_uid = 1000;
+				new_gid = 1000;
+				perms = kMode_0644;
+			} else if (temp->appDir.compare("/data/app-private") == 0 || temp->appDir.compare("/sd-ext/app-private") == 0) {
+				fix = true;
+				new_uid = 1000;
+				new_gid = temp->gid;
+				perms = kMode_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 (S_ISDIR(st.st_mode)) {
+					// Android 5.0 introduced codePath pointing to a directory instead of the apk itself
+					// TODO: check what this should do
+					if (fixDir(temp->codePath, new_uid, new_gid, kMode_0755, new_uid, new_gid, perms) != 0)
+						return -1;
+				} else {
+					if (pchown(temp->codePath, new_uid, new_gid) != 0)
+						return -1;
+					if (pchmod(temp->codePath, perms) != 0)
+						return -1;
+				}
+			}
+		} else if (remove_data) {
+			//Remove data directory since app isn't installed
+			string datapath = "/data/data/" + temp->dDir;
+			if (TWFunc::Path_Exists(datapath) && 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(), datapath.c_str(), temp->appDir.c_str());
+				if (TWFunc::removeDir(datapath, false) != 0) {
+					LOGINFO("Unable to removeDir '%s'\n", datapath.c_str());
+					return -1;
+				}
+			}
+		}
+		temp = temp->next;
+	}
+	return 0;
+}
+
+int fixPermissions::fixAllFiles(string directory, int uid, int gid, mode_t 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::fixDir(const string& dir, int diruid, int dirgid, mode_t dirmode, int fileuid, int filegid, mode_t filemode)
+{
+	if (pchmod(dir.c_str(), dirmode) != 0)
+		return -1;
+	if (pchown(dir.c_str(), diruid, dirgid) != 0)
+		return -1;
+	if (fixAllFiles(dir, fileuid, filegid, filemode) != 0)
+		return -1;
+	return 0;
+}
+
+int fixPermissions::fixDataData(string dataDir) {
+	package* temp = head;
+	while (temp != NULL) {
+		string dir = dataDir + temp->dDir;
+		if (TWFunc::Path_Exists(dir)) {
+			vector <string> dataDataDirs = listAllDirectories(dir);
+			for (unsigned n = 0; n < dataDataDirs.size(); ++n) {
+				string directory = dir + "/";
+				directory.append(dataDataDirs.at(n));
+				if (debug)
+					LOGINFO("Looking at data directory: '%s'\n", directory.c_str());
+				if (dataDataDirs.at(n) == ".") {
+					if (fixDir(directory, temp->uid, temp->gid, kMode_0755, temp->uid, temp->gid, kMode_0755) != 0)
+						return -1;
+				}
+				else if (dataDataDirs.at(n) == "..") {
+					if (debug)
+						LOGINFO("Skipping ..\n");
+					continue;
+				}
+				// TODO: when any of these fails, do we really want to stop everything? 
+				else if (dataDataDirs.at(n) == "lib") {
+					if (fixDir(directory, 1000, 1000, kMode_0755, 1000, 1000, kMode_0755) != 0)
+						return -1;
+				}
+				else if (dataDataDirs.at(n) == "shared_prefs") {
+					if (fixDir(directory, temp->uid, temp->gid,kMode_0771, temp->uid, temp->gid, kMode_0660) != 0)
+						return -1;
+				}
+				else if (dataDataDirs.at(n) == "databases") {
+					if (fixDir(directory, temp->uid, temp->gid,kMode_0771, temp->uid, temp->gid, kMode_0660) != 0)
+						return -1;
+				}
+				else if (dataDataDirs.at(n) == "cache") {
+					if (fixDir(directory, temp->uid, temp->gid,kMode_0771, temp->uid, temp->gid, kMode_0600) != 0)
+						return -1;
+				}
+				else {
+					if (fixDir(directory, temp->uid, temp->gid,kMode_0771, temp->uid, temp->gid, kMode_0755) != 0)
+						return -1;
+				}
+			}
+		}
+		temp = temp->next;
+	}
+	return 0;
+}
+
+// TODO: merge to listAllDirEntries(path, type)
+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;
+}
+
+void fixPermissions::deletePackages() {
+	while (head) {
+		package* temp = head;
+		head = temp->next;
+		delete temp;
+	}
+}
+
+int fixPermissions::getPackages(const string& packageFile) {
+	deletePackages();
+	head = NULL;
+
+	// TODO: simply skip all packages in /system/framework? or why are these excluded?
+	vector <string> skip;
+	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);
+	int len = (int) xmlFile.tellg();
+	xmlFile.seekg(0, ios::beg);
+	vector<char> xmlBuf(len + 1);
+	xmlFile.read(&xmlBuf[0], len);
+	xmlBuf[len] = '\0';
+	xml_document<> pkgDoc;
+	LOGINFO("Parsing packages.xml, size=%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;
+	}
+
+	// Get packages
+	for (xml_node<>* node = pkgNode->first_node(); node; node = node->next_sibling()) {
+		if (node->type() != node_element)
+			continue;
+		string elementName = node->name();
+		// we want <package> and <updated-package>
+		if (!(elementName == "package" || elementName == "updated-package"))
+			continue;
+
+		xml_attribute<>* attName = node->first_attribute("name");
+		if (!attName)
+			continue;
+		string name = attName->value();
+
+		xml_attribute<>* attCodePath = node->first_attribute("codePath");
+		if (!attCodePath)
+		{
+			LOGINFO("No codePath on %s, skipping.\n", name.c_str());
+			continue;
+		}
+		string codePath = attCodePath->value();
+
+		bool doskip = std::find(skip.begin(), skip.end(), codePath) != skip.end();
+		if (doskip) {
+			if (debug)
+				LOGINFO("Skipping package %s\n", codePath.c_str());
+			continue;
+		}
+
+		if (debug)
+			LOGINFO("Loading pkg: %s\n", name.c_str());
+
+		package* temp = new package;
+		temp->pkgName = name;
+		temp->codePath = codePath;
+		temp->appDir = codePath;
+		temp->dDir = name;
+		xml_attribute<>* attUserId = node->first_attribute("userId");
+		if (!attUserId)
+			attUserId = node->first_attribute("sharedUserId");
+		if (!attUserId) {
+			LOGINFO("Problem with userID on %s\n", name.c_str());
+		} else {
+			temp->uid = atoi(attUserId->value());
+			temp->gid = atoi(attUserId->value());
+		}
+		temp->next = head;
+		head = temp;
+	}
+
+	if (head == NULL) {
+		LOGERR("No package found to fix.\n");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/fixPermissions.hpp b/fixPermissions.hpp
new file mode 100644
index 0000000..f61a9a1
--- /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:
+		fixPermissions();
+		~fixPermissions();
+		int fixPerms(bool enable_debug, bool remove_data_for_missing_apps);
+		int fixContexts();
+		int fixDataInternalContexts(void);
+
+	private:
+		int pchown(string fn, int puid, int pgid);
+		int pchmod(string fn, mode_t mode);
+		vector <string> listAllDirectories(string path);
+		vector <string> listAllFiles(string path);
+		void deletePackages();
+		int getPackages(const string& packageFile);
+		int fixApps();
+		int fixAllFiles(string directory, int uid, int gid, mode_t file_perms);
+		int fixDir(const string& dir, int diruid, int dirgid, mode_t dirmode, int fileuid, int filegid, mode_t filemode);
+		int fixDataData(string dataDir);
+		int restorecon(string entry, struct stat *sb);
+		int fixDataDataContexts(void);
+		int fixContextsRecursively(string path, int level);
+
+		struct package {
+			string pkgName;
+			string codePath;
+			string appDir;
+			string dDir;
+			int gid;
+			int uid;
+			package *next;
+		};
+		bool debug;
+		bool remove_data;
+		package* head;
+};
diff --git a/flashutils/Android.mk b/flashutils/Android.mk
new file mode 100644
index 0000000..ab552b1
--- /dev/null
+++ b/flashutils/Android.mk
@@ -0,0 +1,125 @@
+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 libmtdutils libmmcutils 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_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 := libmtdutils libflashutils libmmcutils libbmlutils libcutils libc
+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 := libmtdutils libflashutils libmmcutils libbmlutils libcutils libc
+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 := libmtdutils libflashutils libmmcutils libbmlutils libcutils libc
+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..fe1181f
--- /dev/null
+++ b/flashutils/flashutils.c
@@ -0,0 +1,157 @@
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <string.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.h b/fuse.h
new file mode 100644
index 0000000..8135d47
--- /dev/null
+++ b/fuse.h
@@ -0,0 +1,574 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _LINUX_FUSE_H
+#define _LINUX_FUSE_H
+#include <stdint.h>
+#define FUSE_KERNEL_VERSION 7
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_KERNEL_MINOR_VERSION 22
+#define FUSE_ROOT_ID 1
+struct fuse_attr {
+ uint64_t ino;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t size;
+ uint64_t blocks;
+ uint64_t atime;
+ uint64_t mtime;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t ctime;
+ uint32_t atimensec;
+ uint32_t mtimensec;
+ uint32_t ctimensec;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t mode;
+ uint32_t nlink;
+ uint32_t uid;
+ uint32_t gid;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t rdev;
+ uint32_t blksize;
+ uint32_t padding;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_kstatfs {
+ uint64_t blocks;
+ uint64_t bfree;
+ uint64_t bavail;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t files;
+ uint64_t ffree;
+ uint32_t bsize;
+ uint32_t namelen;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t frsize;
+ uint32_t padding;
+ uint32_t spare[6];
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_file_lock {
+ uint64_t start;
+ uint64_t end;
+ uint32_t type;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t pid;
+};
+#define FATTR_MODE (1 << 0)
+#define FATTR_UID (1 << 1)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FATTR_GID (1 << 2)
+#define FATTR_SIZE (1 << 3)
+#define FATTR_ATIME (1 << 4)
+#define FATTR_MTIME (1 << 5)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FATTR_FH (1 << 6)
+#define FATTR_ATIME_NOW (1 << 7)
+#define FATTR_MTIME_NOW (1 << 8)
+#define FATTR_LOCKOWNER (1 << 9)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FOPEN_DIRECT_IO (1 << 0)
+#define FOPEN_KEEP_CACHE (1 << 1)
+#define FOPEN_NONSEEKABLE (1 << 2)
+#define FUSE_ASYNC_READ (1 << 0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#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)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_BIG_WRITES (1 << 5)
+#define FUSE_DONT_MASK (1 << 6)
+#define FUSE_SPLICE_WRITE (1 << 7)
+#define FUSE_SPLICE_MOVE (1 << 8)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_SPLICE_READ (1 << 9)
+#define FUSE_FLOCK_LOCKS (1 << 10)
+#define FUSE_HAS_IOCTL_DIR (1 << 11)
+#define FUSE_AUTO_INVAL_DATA (1 << 12)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_DO_READDIRPLUS (1 << 13)
+#define FUSE_READDIRPLUS_AUTO (1 << 14)
+#define FUSE_ASYNC_DIO (1 << 15)
+#define CUSE_UNRESTRICTED_IOCTL (1 << 0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_RELEASE_FLUSH (1 << 0)
+#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1)
+#define FUSE_GETATTR_FH (1 << 0)
+#define FUSE_LK_FLOCK (1 << 0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_WRITE_CACHE (1 << 0)
+#define FUSE_WRITE_LOCKOWNER (1 << 1)
+#define FUSE_READ_LOCKOWNER (1 << 1)
+#define FUSE_IOCTL_COMPAT (1 << 0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
+#define FUSE_IOCTL_RETRY (1 << 2)
+#define FUSE_IOCTL_32BIT (1 << 3)
+#define FUSE_IOCTL_DIR (1 << 4)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_IOCTL_MAX_IOV 256
+#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
+enum fuse_opcode {
+ FUSE_LOOKUP = 1,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_FORGET = 2,
+ FUSE_GETATTR = 3,
+ FUSE_SETATTR = 4,
+ FUSE_READLINK = 5,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_SYMLINK = 6,
+ FUSE_MKNOD = 8,
+ FUSE_MKDIR = 9,
+ FUSE_UNLINK = 10,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_RMDIR = 11,
+ FUSE_RENAME = 12,
+ FUSE_LINK = 13,
+ FUSE_OPEN = 14,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_READ = 15,
+ FUSE_WRITE = 16,
+ FUSE_STATFS = 17,
+ FUSE_RELEASE = 18,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_FSYNC = 20,
+ FUSE_SETXATTR = 21,
+ FUSE_GETXATTR = 22,
+ FUSE_LISTXATTR = 23,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_REMOVEXATTR = 24,
+ FUSE_FLUSH = 25,
+ FUSE_INIT = 26,
+ FUSE_OPENDIR = 27,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_READDIR = 28,
+ FUSE_RELEASEDIR = 29,
+ FUSE_FSYNCDIR = 30,
+ FUSE_GETLK = 31,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_SETLK = 32,
+ FUSE_SETLKW = 33,
+ FUSE_ACCESS = 34,
+ FUSE_CREATE = 35,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_INTERRUPT = 36,
+ FUSE_BMAP = 37,
+ FUSE_DESTROY = 38,
+ FUSE_IOCTL = 39,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_POLL = 40,
+ FUSE_NOTIFY_REPLY = 41,
+ FUSE_BATCH_FORGET = 42,
+ FUSE_FALLOCATE = 43,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_READDIRPLUS = 44,
+ CUSE_INIT = 4096,
+};
+enum fuse_notify_code {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_NOTIFY_POLL = 1,
+ FUSE_NOTIFY_INVAL_INODE = 2,
+ FUSE_NOTIFY_INVAL_ENTRY = 3,
+ FUSE_NOTIFY_STORE = 4,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ FUSE_NOTIFY_RETRIEVE = 5,
+ FUSE_NOTIFY_DELETE = 6,
+ FUSE_NOTIFY_CODE_MAX,
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_MIN_READ_BUFFER 8192
+#define FUSE_COMPAT_ENTRY_OUT_SIZE 120
+struct fuse_entry_out {
+ uint64_t nodeid;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t generation;
+ uint64_t entry_valid;
+ uint64_t attr_valid;
+ uint32_t entry_valid_nsec;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t attr_valid_nsec;
+ struct fuse_attr attr;
+};
+struct fuse_forget_in {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t nlookup;
+};
+struct fuse_forget_one {
+ uint64_t nodeid;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t nlookup;
+};
+struct fuse_batch_forget_in {
+ uint32_t count;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t dummy;
+};
+struct fuse_getattr_in {
+ uint32_t getattr_flags;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t dummy;
+ uint64_t fh;
+};
+#define FUSE_COMPAT_ATTR_OUT_SIZE 96
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_attr_out {
+ uint64_t attr_valid;
+ uint32_t attr_valid_nsec;
+ uint32_t dummy;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct fuse_attr attr;
+};
+#define FUSE_COMPAT_MKNOD_IN_SIZE 8
+struct fuse_mknod_in {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t mode;
+ uint32_t rdev;
+ uint32_t umask;
+ uint32_t padding;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct fuse_mkdir_in {
+ uint32_t mode;
+ uint32_t umask;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct fuse_rename_in {
+ uint64_t newdir;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_link_in {
+ uint64_t oldnodeid;
+};
+struct fuse_setattr_in {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t valid;
+ uint32_t padding;
+ uint64_t fh;
+ uint64_t size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t lock_owner;
+ uint64_t atime;
+ uint64_t mtime;
+ uint64_t unused2;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t atimensec;
+ uint32_t mtimensec;
+ uint32_t unused3;
+ uint32_t mode;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t unused4;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t unused5;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct fuse_open_in {
+ uint32_t flags;
+ uint32_t unused;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct fuse_create_in {
+ uint32_t flags;
+ uint32_t mode;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t umask;
+ uint32_t padding;
+};
+struct fuse_open_out {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t fh;
+ uint32_t open_flags;
+ uint32_t padding;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_release_in {
+ uint64_t fh;
+ uint32_t flags;
+ uint32_t release_flags;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t lock_owner;
+};
+struct fuse_flush_in {
+ uint64_t fh;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t unused;
+ uint32_t padding;
+ uint64_t lock_owner;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_read_in {
+ uint64_t fh;
+ uint64_t offset;
+ uint32_t size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t read_flags;
+ uint64_t lock_owner;
+ uint32_t flags;
+ uint32_t padding;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+#define FUSE_COMPAT_WRITE_IN_SIZE 24
+struct fuse_write_in {
+ uint64_t fh;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t offset;
+ uint32_t size;
+ uint32_t write_flags;
+ uint64_t lock_owner;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t flags;
+ uint32_t padding;
+};
+struct fuse_write_out {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t size;
+ uint32_t padding;
+};
+#define FUSE_COMPAT_STATFS_SIZE 48
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_statfs_out {
+ struct fuse_kstatfs st;
+};
+struct fuse_fsync_in {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t fh;
+ uint32_t fsync_flags;
+ uint32_t padding;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_setxattr_in {
+ uint32_t size;
+ uint32_t flags;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_getxattr_in {
+ uint32_t size;
+ uint32_t padding;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_getxattr_out {
+ uint32_t size;
+ uint32_t padding;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_lk_in {
+ uint64_t fh;
+ uint64_t owner;
+ struct fuse_file_lock lk;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t lk_flags;
+ uint32_t padding;
+};
+struct fuse_lk_out {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct fuse_file_lock lk;
+};
+struct fuse_access_in {
+ uint32_t mask;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t padding;
+};
+struct fuse_init_in {
+ uint32_t major;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t minor;
+ uint32_t max_readahead;
+ uint32_t flags;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_init_out {
+ uint32_t major;
+ uint32_t minor;
+ uint32_t max_readahead;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t flags;
+ uint16_t max_background;
+ uint16_t congestion_threshold;
+ uint32_t max_write;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+#define CUSE_INIT_INFO_MAX 4096
+struct cuse_init_in {
+ uint32_t major;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t minor;
+ uint32_t unused;
+ uint32_t flags;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct cuse_init_out {
+ uint32_t major;
+ uint32_t minor;
+ uint32_t unused;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t flags;
+ uint32_t max_read;
+ uint32_t max_write;
+ uint32_t dev_major;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t dev_minor;
+ uint32_t spare[10];
+};
+struct fuse_interrupt_in {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t unique;
+};
+struct fuse_bmap_in {
+ uint64_t block;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t blocksize;
+ uint32_t padding;
+};
+struct fuse_bmap_out {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t block;
+};
+struct fuse_ioctl_in {
+ uint64_t fh;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t flags;
+ uint32_t cmd;
+ uint64_t arg;
+ uint32_t in_size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t out_size;
+};
+struct fuse_ioctl_iovec {
+ uint64_t base;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t len;
+};
+struct fuse_ioctl_out {
+ int32_t result;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t flags;
+ uint32_t in_iovs;
+ uint32_t out_iovs;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_poll_in {
+ uint64_t fh;
+ uint64_t kh;
+ uint32_t flags;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t events;
+};
+struct fuse_poll_out {
+ uint32_t revents;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t padding;
+};
+struct fuse_notify_poll_wakeup_out {
+ uint64_t kh;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct fuse_fallocate_in {
+ uint64_t fh;
+ uint64_t offset;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t length;
+ uint32_t mode;
+ uint32_t padding;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_in_header {
+ uint32_t len;
+ uint32_t opcode;
+ uint64_t unique;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t nodeid;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t pid;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t padding;
+};
+struct fuse_out_header {
+ uint32_t len;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ int32_t error;
+ uint64_t unique;
+};
+struct fuse_dirent {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t ino;
+ uint64_t off;
+ uint32_t namelen;
+ uint32_t type;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ char name[];
+};
+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
+#define FUSE_DIRENT_ALIGN(x)   (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define FUSE_DIRENT_SIZE(d)   FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
+struct fuse_direntplus {
+ struct fuse_entry_out entry_out;
+ struct fuse_dirent dirent;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+#define FUSE_NAME_OFFSET_DIRENTPLUS   offsetof(struct fuse_direntplus, dirent.name)
+#define FUSE_DIRENTPLUS_SIZE(d)   FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen)
+struct fuse_notify_inval_inode_out {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t ino;
+ int64_t off;
+ int64_t len;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_notify_inval_entry_out {
+ uint64_t parent;
+ uint32_t namelen;
+ uint32_t padding;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct fuse_notify_delete_out {
+ uint64_t parent;
+ uint64_t child;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t namelen;
+ uint32_t padding;
+};
+struct fuse_notify_store_out {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t nodeid;
+ uint64_t offset;
+ uint32_t size;
+ uint32_t padding;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct fuse_notify_retrieve_out {
+ uint64_t notify_unique;
+ uint64_t nodeid;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint64_t offset;
+ uint32_t size;
+ uint32_t padding;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fuse_notify_retrieve_in {
+ uint64_t dummy1;
+ uint64_t offset;
+ uint32_t size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ uint32_t dummy2;
+ uint64_t dummy3;
+ uint64_t dummy4;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
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/fuse_sideload.c b/fuse_sideload.c
index 48e6cc5..3259c5f 100644
--- a/fuse_sideload.c
+++ b/fuse_sideload.c
@@ -46,7 +46,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <linux/fuse.h>
+#include "fuse.h"
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/gui/Android.mk b/gui/Android.mk
new file mode 100644
index 0000000..1fef03d
--- /dev/null
+++ b/gui/Android.mk
@@ -0,0 +1,181 @@
+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 \
+    scrolllist.cpp \
+    patternpassword.cpp
+
+ifneq ($(TWRP_CUSTOM_KEYBOARD),)
+    LOCAL_SRC_FILES += $(TWRP_CUSTOM_KEYBOARD)
+else
+    LOCAL_SRC_FILES += hardwarekeyboard.cpp
+endif
+
+LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libminzip libaosprecovery
+LOCAL_MODULE := libguitwrp
+
+#TWRP_EVENT_LOGGING := true
+ifeq ($(TWRP_EVENT_LOGGING), true)
+    LOCAL_CFLAGS += -D_EVENT_LOGGING
+endif
+ifneq ($(TW_USE_KEY_CODE_TOUCH_SYNC),)
+    LOCAL_CFLAGS += -DTW_USE_KEY_CODE_TOUCH_SYNC=$(TW_USE_KEY_CODE_TOUCH_SYNC)
+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
+ifneq ($(TW_X_OFFSET),)
+    LOCAL_CFLAGS += -DTW_X_OFFSET=$(TW_X_OFFSET)
+endif
+ifneq ($(TW_Y_OFFSET),)
+    LOCAL_CFLAGS += -DTW_Y_OFFSET=$(TW_Y_OFFSET)
+endif
+ifeq ($(TW_ROUND_SCREEN), true)
+    LOCAL_CFLAGS += -DTW_ROUND_SCREEN
+endif
+
+LOCAL_C_INCLUDES += bionic external/stlport/stlport system/core/libpixelflinger/include
+LOCAL_CFLAGS += -DTWRES=\"$(TWRES_PATH)\"
+
+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)$(TWRES_PATH)
+TWRP_RES := $(commands_recovery_local_path)/gui/devices/common/res/*
+# enable this to use new themes:
+#TWRP_NEW_THEME := true
+
+ifeq ($(TW_CUSTOM_THEME),)
+    ifeq ($(TW_THEME),)
+        # This converts the old DEVICE_RESOLUTION flag to the new TW_THEME flag
+        PORTRAIT_MDPI := 320x480 480x800 480x854 540x960
+        PORTRAIT_HDPI := 720x1280 800x1280 1080x1920 1200x1920 1440x2560 1600x2560
+        WATCH_MDPI := 240x240 280x280 320x320
+        LANDSCAPE_MDPI := 800x480 1024x600 1024x768
+        LANDSCAPE_HDPI := 1280x800 1920x1200 2560x1600
+        ifneq ($(filter $(DEVICE_RESOLUTION), $(PORTRAIT_MDPI)),)
+            TW_THEME := portrait_mdpi
+        else ifneq ($(filter $(DEVICE_RESOLUTION), $(PORTRAIT_HDPI)),)
+            TW_THEME := portrait_hdpi
+        else ifneq ($(filter $(DEVICE_RESOLUTION), $(WATCH_MDPI)),)
+            TW_THEME := watch_mdpi
+        else ifneq ($(filter $(DEVICE_RESOLUTION), $(LANDSCAPE_MDPI)),)
+            TW_THEME := landscape_mdpi
+        else ifneq ($(filter $(DEVICE_RESOLUTION), $(LANDSCAPE_HDPI)),)
+            TW_THEME := landscape_hdpi
+        endif
+    endif
+ifeq ($(TWRP_NEW_THEME),true)
+    TWRP_THEME_LOC := $(commands_recovery_local_path)/gui/theme/$(TW_THEME)
+    TWRP_RES := $(commands_recovery_local_path)/gui/theme/common/fonts
+    TWRP_RES += $(commands_recovery_local_path)/gui/theme/common/$(word 1,$(subst _, ,$(TW_THEME))).xml
+# for future copying of used include xmls and fonts:
+# UI_XML := $(TWRP_THEME_LOC)/ui.xml
+# TWRP_INCLUDE_XMLS := $(shell xmllint --xpath '/recovery/include/xmlfile/@name' $(UI_XML)|sed -n 's/[^\"]*\"\([^\"]*\)\"[^\"]*/\1\n/gp'|sort|uniq)
+# TWRP_FONTS_TTF := $(shell xmllint --xpath '/recovery/resources/font/@filename' $(UI_XML)|sed -n 's/[^\"]*\"\([^\"]*\)\"[^\"]*/\1\n/gp'|sort|uniq)
+# TWRP_FONTS_DAT := $(shell xmllint --xpath '/recovery/resources/font/@fallback' $(UI_XML)|sed -n 's/[^\"]*\"\([^\"]*\)\"[^\"]*/\1.dat\n/gp'|sort|uniq)
+ifeq ($(wildcard $(TWRP_THEME_LOC)/ui.xml),)
+    $(warning ****************************************************************************)
+    $(warning * TW_THEME is not valid: '$(TW_THEME)')
+    $(warning * Please choose an appropriate TW_THEME or create a new one for your device.)
+    $(warning * Available themes:)
+    $(warning * $(notdir $(wildcard $(commands_recovery_local_path)/gui/theme/*_*)))
+    $(warning ****************************************************************************)
+    $(error stopping)
+endif
+else
+    TWRP_RES += $(commands_recovery_local_path)/gui/devices/$(word 1,$(subst _, ,$(TW_THEME)))/res/*
+    ifeq ($(TW_THEME), portrait_mdpi)
+        TWRP_THEME_LOC := $(commands_recovery_local_path)/gui/devices/480x800/res
+    else ifeq ($(TW_THEME), portrait_hdpi)
+        TWRP_THEME_LOC := $(commands_recovery_local_path)/gui/devices/1080x1920/res
+    else ifeq ($(TW_THEME), watch_mdpi)
+        TWRP_THEME_LOC := $(commands_recovery_local_path)/gui/devices/320x320/res
+    else ifeq ($(TW_THEME), landscape_mdpi)
+        TWRP_THEME_LOC := $(commands_recovery_local_path)/gui/devices/800x480/res
+    else ifeq ($(TW_THEME), landscape_hdpi)
+        TWRP_THEME_LOC := $(commands_recovery_local_path)/gui/devices/1920x1200/res
+    else
+        $(warning ****************************************************************************)
+        $(warning * TW_THEME ($(TW_THEME)) is not valid.)
+        $(warning * Please choose an appropriate TW_THEME or create a new one for your device.)
+        $(warning * Valid options are portrait_mdpi portrait_hdpi watch_mdpi)
+        $(warning *                   landscape_mdpi landscape_hdpi)
+        $(warning ****************************************************************************)
+        $(error stopping)
+    endif
+endif
+else
+    TWRP_THEME_LOC := $(TW_CUSTOM_THEME)
+endif
+
+ifeq ($(TW_DISABLE_TTF), true)
+    TWRP_REMOVE_FONT := rm -f $(TARGET_RECOVERY_ROOT_OUT)$(TWRES_PATH)fonts/*.ttf
+else
+    TWRP_REMOVE_FONT := rm -f $(TARGET_RECOVERY_ROOT_OUT)$(TWRES_PATH)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)$(TWRES_PATH)
+	cp -fr $(TWRP_RES) $(TARGET_RECOVERY_ROOT_OUT)$(TWRES_PATH)
+	cp -fr $(TWRP_THEME_LOC)/* $(TARGET_RECOVERY_ROOT_OUT)$(TWRES_PATH)
+	$(TWRP_REMOVE_FONT)
+	mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/sbin/
+ifneq ($(TW_USE_TOOLBOX), true)
+	ln -sf $(TWRP_SH_TARGET) $(TARGET_RECOVERY_ROOT_OUT)/sbin/sh
+endif
+	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..0f8c7ee
--- /dev/null
+++ b/gui/action.cpp
@@ -0,0 +1,1826 @@
+/*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"
+#include "../fuse_sideload.h"
+#include "blanktimer.hpp"
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+#include "../variables.h"
+#include "../twinstall.h"
+#include "cutils/properties.h"
+#include "../adb_install.h"
+#include "../set_metadata.h"
+};
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../tw_atomic.hpp"
+
+void curtainClose(void);
+
+GUIAction::mapFunc GUIAction::mf;
+std::set<string> GUIAction::setActionsRunningInCallerThread;
+static string zip_queue[10];
+static int zip_queue_index;
+static pthread_t terminal_command;
+pid_t sideload_child_pid;
+
+static void *ActionThread_work_wrapper(void *data);
+
+class ActionThread
+{
+public:
+	ActionThread();
+	~ActionThread();
+
+	void threadActions(GUIAction *act);
+	void run(void *data);
+private:
+	friend void *ActionThread_work_wrapper(void*);
+	struct ThreadData
+	{
+		ActionThread *this_;
+		GUIAction *act;
+		ThreadData(ActionThread *this_, GUIAction *act) : this_(this_), act(act) {}
+	};
+
+	pthread_t m_thread;
+	bool m_thread_running;
+	pthread_mutex_t m_act_lock;
+};
+
+static ActionThread action_thread;	// for all kinds of longer running actions
+static ActionThread cancel_thread;	// for longer running "cancel" actions
+
+static void *ActionThread_work_wrapper(void *data)
+{
+	static_cast<ActionThread::ThreadData*>(data)->this_->run(data);
+	return NULL;
+}
+
+ActionThread::ActionThread()
+{
+	m_thread_running = false;
+	pthread_mutex_init(&m_act_lock, NULL);
+}
+
+ActionThread::~ActionThread()
+{
+	pthread_mutex_lock(&m_act_lock);
+	if(m_thread_running) {
+		pthread_mutex_unlock(&m_act_lock);
+		pthread_join(m_thread, NULL);
+	} else {
+		pthread_mutex_unlock(&m_act_lock);
+	}
+	pthread_mutex_destroy(&m_act_lock);
+}
+
+void ActionThread::threadActions(GUIAction *act)
+{
+	pthread_mutex_lock(&m_act_lock);
+	if (m_thread_running) {
+		pthread_mutex_unlock(&m_act_lock);
+		LOGERR("Another threaded action is already running -- not running %u actions starting with '%s'\n",
+				act->mActions.size(), act->mActions[0].mFunction.c_str());
+	} else {
+		m_thread_running = true;
+		pthread_mutex_unlock(&m_act_lock);
+		ThreadData *d = new ThreadData(this, act);
+		pthread_create(&m_thread, NULL, &ActionThread_work_wrapper, d);
+	}
+}
+
+void ActionThread::run(void *data)
+{
+	ThreadData *d = (ThreadData*)data;
+	GUIAction* act = d->act;
+
+	std::vector<GUIAction::Action>::iterator it;
+	for (it = act->mActions.begin(); it != act->mActions.end(); ++it)
+		act->doAction(*it);
+
+	pthread_mutex_lock(&m_act_lock);
+	m_thread_running = false;
+	pthread_mutex_unlock(&m_act_lock);
+	delete d;
+}
+
+GUIAction::GUIAction(xml_node<>* node)
+	: GUIObject(node)
+{
+	xml_node<>* child;
+	xml_node<>* actions;
+	xml_attribute<>* attr;
+
+	if (!node)  return;
+
+	if (mf.empty()) {
+#define ADD_ACTION(n) mf[#n] = &GUIAction::n
+#define ADD_ACTION_EX(name, func) mf[name] = &GUIAction::func
+		// These actions will be run in the caller's thread
+		ADD_ACTION(reboot);
+		ADD_ACTION(home);
+		ADD_ACTION(key);
+		ADD_ACTION(page);
+		ADD_ACTION(reload);
+		ADD_ACTION(readBackup);
+		ADD_ACTION(set);
+		ADD_ACTION(clear);
+		ADD_ACTION(mount);
+		ADD_ACTION(unmount);
+		ADD_ACTION_EX("umount", unmount);
+		ADD_ACTION(restoredefaultsettings);
+		ADD_ACTION(copylog);
+		ADD_ACTION(compute);
+		ADD_ACTION_EX("addsubtract", compute);
+		ADD_ACTION(setguitimezone);
+		ADD_ACTION(overlay);
+		ADD_ACTION(queuezip);
+		ADD_ACTION(cancelzip);
+		ADD_ACTION(queueclear);
+		ADD_ACTION(sleep);
+		ADD_ACTION(appenddatetobackupname);
+		ADD_ACTION(generatebackupname);
+		ADD_ACTION(checkpartitionlist);
+		ADD_ACTION(getpartitiondetails);
+		ADD_ACTION(screenshot);
+		ADD_ACTION(setbrightness);
+		ADD_ACTION(fileexists);
+		ADD_ACTION(killterminal);
+		ADD_ACTION(checkbackupname);
+		ADD_ACTION(adbsideloadcancel);
+		ADD_ACTION(fixsu);
+		ADD_ACTION(startmtp);
+		ADD_ACTION(stopmtp);
+		ADD_ACTION(cancelbackup);
+		ADD_ACTION(checkpartitionlifetimewrites);
+		ADD_ACTION(mountsystemtoggle);
+
+		// remember actions that run in the caller thread
+		for (mapFunc::const_iterator it = mf.begin(); it != mf.end(); ++it)
+			setActionsRunningInCallerThread.insert(it->first);
+
+		// These actions will run in a separate thread
+		ADD_ACTION(flash);
+		ADD_ACTION(wipe);
+		ADD_ACTION(refreshsizes);
+		ADD_ACTION(nandroid);
+		ADD_ACTION(fixpermissions);
+		ADD_ACTION(dd);
+		ADD_ACTION(partitionsd);
+		ADD_ACTION(installhtcdumlock);
+		ADD_ACTION(htcdumlockrestoreboot);
+		ADD_ACTION(htcdumlockreflashrecovery);
+		ADD_ACTION(cmd);
+		ADD_ACTION(terminalcommand);
+		ADD_ACTION(reinjecttwrp);
+		ADD_ACTION(decrypt);
+		ADD_ACTION(adbsideload);
+		ADD_ACTION(openrecoveryscript);
+		ADD_ACTION(installsu);
+		ADD_ACTION(decrypt_backup);
+		ADD_ACTION(repair);
+		ADD_ACTION(resize);
+		ADD_ACTION(changefilesystem);
+		ADD_ACTION(flashimage);
+	}
+
+	// First, get the action
+	actions = FindNode(node, "actions");
+	if (actions)	child = FindNode(actions, "action");
+	else			child = FindNode(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 = FindNode(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++)
+	{
+		if (PartitionManager.stop_backup.get_value()) {
+			DataManager::SetValue("tw_cancel_backup", 1);
+			gui_print("Backup Canceled.\n");
+			DataManager::SetValue("ui_progress", 0);
+			PartitionManager.stop_backup.set_value(0);
+			return;
+		}
+		usleep(500000);
+		DataManager::SetValue("ui_progress", i * 20);
+	}
+}
+
+int GUIAction::flash_zip(std::string filename, int* wipe_cache)
+{
+	int ret_val = 0;
+
+	DataManager::SetValue("ui_progress", 0);
+
+	if (filename.empty())
+	{
+		LOGERR("No file specified.\n");
+		return -1;
+	}
+
+	if (!PartitionManager.Mount_By_Path(filename, true))
+		return -1;
+
+	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;
+}
+
+GUIAction::ThreadType GUIAction::getThreadType(const GUIAction::Action& action)
+{
+	string func = gui_parse_text(action.mFunction);
+	bool needsThread = setActionsRunningInCallerThread.find(func) == setActionsRunningInCallerThread.end();
+	if (needsThread) {
+		if (func == "cancelbackup")
+			return THREAD_CANCEL;
+		else
+			return THREAD_ACTION;
+	}
+	return THREAD_NONE;
+}
+
+int GUIAction::doActions()
+{
+	if (mActions.size() < 1)
+		return -1;
+
+	// Determine in which thread to run the actions.
+	// Do it for all actions at once before starting, so that we can cancel the whole batch if the thread is already busy.
+	ThreadType threadType = THREAD_NONE;
+	std::vector<Action>::iterator it;
+	for (it = mActions.begin(); it != mActions.end(); ++it) {
+		ThreadType tt = getThreadType(*it);
+		if (tt == THREAD_NONE)
+			continue;
+		if (threadType == THREAD_NONE)
+			threadType = tt;
+		else if (threadType != tt) {
+			LOGERR("Can't mix normal and cancel actions in the same list.\n"
+				"Running the whole batch in the cancel thread.\n");
+			threadType = THREAD_CANCEL;
+			break;
+		}
+	}
+
+	// Now run the actions in the desired thread.
+	switch (threadType) {
+		case THREAD_ACTION:
+			action_thread.threadActions(this);
+			break;
+
+		case THREAD_CANCEL:
+			cancel_thread.threadActions(this);
+			break;
+
+		default: {
+			// no iterators here because theme reloading might kill our object
+			const size_t cnt = mActions.size();
+			for (size_t i = 0; i < cnt; ++i)
+				doAction(mActions[i]);
+		}
+	}
+
+	return 0;
+}
+
+int GUIAction::doAction(Action action)
+{
+	DataManager::GetValue(TW_SIMULATE_ACTIONS, simulate);
+
+	std::string function = gui_parse_text(action.mFunction);
+	std::string arg = gui_parse_text(action.mArg);
+
+	// find function and execute it
+	mapFunc::const_iterator funcitr = mf.find(function);
+	if (funcitr != mf.end())
+		return (this->*funcitr->second)(arg);
+
+	LOGERR("Unknown action '%s'\n", function.c_str());
+	return -1;
+}
+
+void GUIAction::operation_start(const string operation_name)
+{
+	LOGINFO("operation_start: '%s'\n", operation_name.c_str());
+	time(&Start);
+	DataManager::SetValue(TW_ACTION_BUSY, 1);
+	DataManager::SetValue("ui_progress", 0);
+	DataManager::SetValue("tw_operation", operation_name);
+	DataManager::SetValue("tw_operation_state", 0);
+	DataManager::SetValue("tw_operation_status", 0);
+}
+
+void GUIAction::operation_end(const int operation_status)
+{
+	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);
+	blankTimer.resetTimerAndUnblank();
+	time(&Stop);
+	if ((int) difftime(Stop, Start) > 10)
+		DataManager::Vibrate("tw_action_vibrate");
+	LOGINFO("operation_end - status=%d\n", operation_status);
+}
+
+int GUIAction::reboot(std::string arg)
+{
+	//curtainClose(); this sometimes causes a crash
+
+	sync();
+	DataManager::SetValue("tw_gui_done", 1);
+	DataManager::SetValue("tw_reboot_arg", arg);
+
+	return 0;
+}
+
+int GUIAction::home(std::string arg)
+{
+	PageManager::SelectPackage("TWRP");
+	gui_changePage("main");
+	return 0;
+}
+
+int GUIAction::key(std::string arg)
+{
+	const int key = getKeyByName(arg);
+	PageManager::NotifyKey(key, true);
+	PageManager::NotifyKey(key, false);
+	return 0;
+}
+
+int GUIAction::page(std::string arg)
+{
+	std::string page_name = gui_parse_text(arg);
+	return gui_changePage(page_name);
+}
+
+int GUIAction::reload(std::string arg)
+{
+	int check = 0, ret_val = 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 || 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", TWRES "ui.xml"))
+		{
+			LOGERR("Failed to load base packages.\n");
+			ret_val = 1;
+		}
+	}
+	return 0;
+}
+
+int GUIAction::readBackup(std::string arg)
+{
+	string Restore_Name;
+	DataManager::GetValue("tw_restore", Restore_Name);
+	PartitionManager.Set_Restore_Files(Restore_Name);
+	return 0;
+}
+
+int GUIAction::set(std::string arg)
+{
+	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;
+}
+
+int GUIAction::clear(std::string arg)
+{
+	DataManager::SetValue(arg, "0");
+	return 0;
+}
+
+int GUIAction::mount(std::string arg)
+{
+	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);
+		PartitionManager.Add_MTP_Storage(arg);
+	} else
+		gui_print("Simulating actions...\n");
+	return 0;
+}
+
+int GUIAction::unmount(std::string arg)
+{
+	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;
+}
+
+int GUIAction::restoredefaultsettings(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::copylog(std::string arg)
+{
+	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);
+		tw_set_default_metadata(dst.c_str());
+		sync();
+		gui_print("Copied recovery log to %s.\n", DataManager::GetCurrentStoragePath().c_str());
+	} else
+		simulate_progress_bar();
+	operation_end(0);
+	return 0;
+}
+
+
+int GUIAction::compute(std::string arg)
+{
+	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;
+}
+
+int GUIAction::setguitimezone(std::string arg)
+{
+	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;
+}
+
+int GUIAction::overlay(std::string arg)
+{
+	return gui_changeOverlay(arg);
+}
+
+int GUIAction::queuezip(std::string arg)
+{
+	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;
+}
+
+int GUIAction::cancelzip(std::string arg)
+{
+	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;
+}
+
+int GUIAction::queueclear(std::string arg)
+{
+	zip_queue_index = 0;
+	DataManager::SetValue(TW_ZIP_QUEUE_COUNT, zip_queue_index);
+	return 0;
+}
+
+int GUIAction::sleep(std::string arg)
+{
+	operation_start("Sleep");
+	usleep(atoi(arg.c_str()));
+	operation_end(0);
+	return 0;
+}
+
+int GUIAction::appenddatetobackupname(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::generatebackupname(std::string arg)
+{
+	operation_start("GenerateBackupName");
+	TWFunc::Auto_Generate_Backup_Name();
+	operation_end(0);
+	return 0;
+}
+
+int GUIAction::checkpartitionlist(std::string arg)
+{
+	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;
+}
+
+int GUIAction::getpartitiondetails(std::string arg)
+{
+	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 (Part->Can_Resize())
+					DataManager::SetValue("tw_partition_can_resize", 1);
+				else
+					DataManager::SetValue("tw_partition_can_resize", 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;
+}
+
+int GUIAction::screenshot(std::string arg)
+{
+	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;
+}
+
+int GUIAction::setbrightness(std::string arg)
+{
+	return TWFunc::Set_Brightness(arg);
+}
+
+int GUIAction::fileexists(std::string arg)
+{
+	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);
+	else
+		operation_end(1);
+	return 0;
+}
+
+void GUIAction::reinject_after_flash()
+{
+	if (DataManager::GetIntValue(TW_HAS_INJECTTWRP) == 1 && DataManager::GetIntValue(TW_INJECT_AFTER_ZIP) == 1) {
+		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");
+		}
+	}
+}
+
+int GUIAction::flash(std::string arg)
+{
+	int i, ret_val = 0, wipe_cache = 0;
+	// We're going to jump to this page first, like a loading page
+	gui_changePage(arg);
+	for (i=0; i<zip_queue_index; i++) {
+		string zip_path = zip_queue[i];
+		size_t slashpos = zip_path.find_last_of('/');
+		string zip_filename = (slashpos == string::npos) ? zip_path : zip_path.substr(slashpos + 1);
+		operation_start("Flashing");
+		DataManager::SetValue("tw_filename", zip_path);
+		DataManager::SetValue("tw_file", zip_filename);
+		DataManager::SetValue(TW_ZIP_INDEX, (i + 1));
+
+		TWFunc::SetPerformanceMode(true);
+		ret_val = flash_zip(zip_path, &wipe_cache);
+		TWFunc::SetPerformanceMode(false);
+		if (ret_val != 0) {
+			gui_print("Error flashing zip '%s'\n", zip_path.c_str());
+			ret_val = 1;
+			break;
+		}
+	}
+	zip_queue_index = 0;
+
+	if (wipe_cache) {
+		gui_print("One or more zip requested a cache wipe\nWiping cache now.\n");
+		PartitionManager.Wipe_By_Path("/cache");
+	}
+
+	reinject_after_flash();
+	PartitionManager.Update_System_Details();
+	operation_end(ret_val);
+	DataManager::SetValue(TW_ZIP_QUEUE_COUNT, zip_queue_index);
+	return 0;
+}
+
+int GUIAction::wipe(std::string arg)
+{
+	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);
+#ifndef 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);
+	return 0;
+}
+
+int GUIAction::refreshsizes(std::string arg)
+{
+	operation_start("Refreshing Sizes");
+	if (simulate) {
+		simulate_progress_bar();
+	} else
+		PartitionManager.Update_System_Details();
+	operation_end(0);
+	return 0;
+}
+
+int GUIAction::nandroid(std::string arg)
+{
+	if (simulate) {
+		PartitionManager.stop_backup.set_value(0);
+		DataManager::SetValue("tw_partition", "Simulation");
+		simulate_progress_bar();
+		operation_end(0);
+	} else {
+		operation_start("Nandroid");
+		int ret = 0;
+
+		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);
+				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);
+			return -1;
+		}
+		DataManager::SetValue("tw_encrypt_backup", 0);
+		if (!PartitionManager.stop_backup.get_value()) {
+			if (ret == false)
+				ret = 1; // 1 for failure
+			else
+				ret = 0; // 0 for success
+			DataManager::SetValue("tw_cancel_backup", 0);
+		}
+		else {
+			DataManager::SetValue("tw_cancel_backup", 1);
+			gui_print("Backup Canceled.\n");
+			ret = 0;
+		}
+		operation_end(ret);
+		return ret;
+	}
+	return 0;
+}
+
+int GUIAction::cancelbackup(std::string arg) {
+	if (simulate) {
+		PartitionManager.stop_backup.set_value(1);
+	}
+	else {
+		int op_status = PartitionManager.Cancel_Backup();
+		if (op_status != 0)
+			op_status = 1; // failure
+	}
+
+	return 0;
+}
+
+int GUIAction::fixpermissions(std::string arg)
+{
+	int op_status = 0;
+
+	operation_start("Fix Permissions");
+	LOGINFO("fix permissions started!\n");
+	if (simulate) {
+		simulate_progress_bar();
+	} else {
+		op_status = PartitionManager.Fix_Permissions();
+		if (op_status != 0)
+			op_status = 1; // failure
+	}
+	operation_end(op_status);
+	return 0;
+}
+
+int GUIAction::dd(std::string arg)
+{
+	operation_start("imaging");
+
+	if (simulate) {
+		simulate_progress_bar();
+	} else {
+		string cmd = "dd " + arg;
+		TWFunc::Exec_Cmd(cmd);
+	}
+	operation_end(0);
+	return 0;
+}
+
+int GUIAction::partitionsd(std::string arg)
+{
+	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);
+	return 0;
+
+}
+
+int GUIAction::installhtcdumlock(std::string arg)
+{
+	operation_start("Install HTC Dumlock");
+	if (simulate) {
+		simulate_progress_bar();
+	} else
+		TWFunc::install_htc_dumlock();
+
+	operation_end(0);
+	return 0;
+}
+
+int GUIAction::htcdumlockrestoreboot(std::string arg)
+{
+	operation_start("HTC Dumlock Restore Boot");
+	if (simulate) {
+		simulate_progress_bar();
+	} else
+		TWFunc::htc_dumlock_restore_original_boot();
+
+	operation_end(0);
+	return 0;
+}
+
+int GUIAction::htcdumlockreflashrecovery(std::string arg)
+{
+	operation_start("HTC Dumlock Reflash Recovery");
+	if (simulate) {
+		simulate_progress_bar();
+	} else
+		TWFunc::htc_dumlock_reflash_recovery_to_boot();
+
+	operation_end(0);
+	return 0;
+}
+
+int GUIAction::cmd(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::terminalcommand(std::string arg)
+{
+	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);
+	} else if (arg == "exit") {
+		LOGINFO("Exiting terminal\n");
+		operation_end(op_status);
+		page("main");
+	} else {
+		command = "cd \"" + cmdpath + "\" && " + arg + " 2>&1";;
+		LOGINFO("Actual command is: '%s'\n", command.c_str());
+		DataManager::SetValue("tw_terminal_state", 1);
+		DataManager::SetValue("tw_background_thread_running", 1);
+		FILE* fp;
+		char line[512];
+
+		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
+					if(fgets(line, sizeof(line), fp) != NULL)
+						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 0;
+}
+
+int GUIAction::killterminal(std::string arg)
+{
+	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;
+}
+
+int GUIAction::reinjecttwrp(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::checkbackupname(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::decrypt(std::string arg)
+{
+	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 {
+
+			DataManager::SetValue(TW_IS_ENCRYPTED, 0);
+
+			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) {
+				if (tw_get_default_metadata(DataManager::GetSettingsStoragePath().c_str()) != 0) {
+					LOGINFO("Failed to get default contexts and file mode for storage files.\n");
+				} else {
+					LOGINFO("Got default contexts and file mode for storage files.\n");
+				}
+			}
+		}
+	}
+
+	operation_end(op_status);
+	return 0;
+}
+
+int GUIAction::adbsideload(std::string arg)
+{
+	operation_start("Sideload");
+	if (simulate) {
+		simulate_progress_bar();
+		operation_end(0);
+	} else {
+		gui_print("Starting ADB sideload feature...\n");
+		bool mtp_was_enabled = TWFunc::Toggle_MTP(false);
+
+		// wait for the adb connection
+		int ret = apply_from_adb("/", &sideload_child_pid);
+		DataManager::SetValue("tw_has_cancel", 0); // Remove cancel button from gui now that the zip install is going to start
+
+		if (ret != 0) {
+			if (ret == -2)
+				gui_print("You need adb 1.0.32 or newer to sideload to this device.\n");
+			ret = 1; // failure
+		} else {
+			int wipe_cache = 0;
+			int wipe_dalvik = 0;
+			DataManager::GetValue("tw_wipe_dalvik", wipe_dalvik);
+
+			if (TWinstall_zip(FUSE_SIDELOAD_HOST_PATHNAME, &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 (sideload_child_pid) {
+			LOGINFO("Signaling child sideload process to exit.\n");
+			struct stat st;
+			// Calling stat() on this magic filename signals the minadbd
+			// subprocess to shut down.
+			stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+			int status;
+			LOGINFO("Waiting for child sideload process to exit.\n");
+			waitpid(sideload_child_pid, &status, 0);
+		}
+		property_set("ctl.start", "adbd");
+		TWFunc::Toggle_MTP(mtp_was_enabled);
+		reinject_after_flash();
+		operation_end(ret);
+	}
+	return 0;
+}
+
+int GUIAction::adbsideloadcancel(std::string arg)
+{
+	struct stat st;
+	DataManager::SetValue("tw_has_cancel", 0); // Remove cancel button from gui
+	gui_print("Cancelling ADB sideload...\n");
+	LOGINFO("Signaling child sideload process to exit.\n");
+	// Calling stat() on this magic filename signals the minadbd
+	// subprocess to shut down.
+	stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+	if (!sideload_child_pid) {
+		LOGERR("Unable to get child ID\n");
+		return 0;
+	}
+	::sleep(1);
+	LOGINFO("Killing child sideload process.\n");
+	kill(sideload_child_pid, SIGTERM);
+	int status;
+	LOGINFO("Waiting for child sideload process to exit.\n");
+	waitpid(sideload_child_pid, &status, 0);
+	sideload_child_pid = 0;
+	DataManager::SetValue("tw_page_done", "1"); // For OpenRecoveryScript support
+	return 0;
+}
+
+int GUIAction::openrecoveryscript(std::string arg)
+{
+	int op_status = 1;
+
+	operation_start("OpenRecoveryScript");
+	if (simulate) {
+		simulate_progress_bar();
+		operation_end(0);
+	} 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;
+				op_status = 0;
+			}
+		}
+		// 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;
+				op_status = 0;
+			}
+		}
+		if (reboot) {
+			// Disable stock recovery reflashing
+			TWFunc::Disable_Stock_Recovery_Replace();
+			usleep(2000000); // Sleep for 2 seconds before rebooting
+			TWFunc::tw_reboot(rb_system);
+			usleep(5000000); // Sleep for 5 seconds to allow reboot to occur
+		} else {
+			DataManager::SetValue("tw_page_done", 1);
+		}
+		operation_end(op_status);
+	}
+	return 0;
+}
+
+int GUIAction::installsu(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::fixsu(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::decrypt_backup(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::repair(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::resize(std::string arg)
+{
+	int op_status = 0;
+
+	operation_start("Resize Partition");
+	if (simulate) {
+		simulate_progress_bar();
+	} else {
+		string part_path;
+		DataManager::GetValue("tw_partition_mount_point", part_path);
+		if (PartitionManager.Resize_By_Path(part_path, true)) {
+			op_status = 0; // success
+		} else {
+			LOGERR("Error resizing file system.\n");
+			op_status = 1; // fail
+		}
+	}
+
+	operation_end(op_status);
+	return 0;
+}
+
+int GUIAction::changefilesystem(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::startmtp(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::stopmtp(std::string arg)
+{
+	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);
+	return 0;
+}
+
+int GUIAction::flashimage(std::string arg)
+{
+	int op_status = 0;
+
+	operation_start("Flash Image");
+	string path, filename, full_filename;
+	DataManager::GetValue("tw_zip_location", path);
+	DataManager::GetValue("tw_file", filename);
+	full_filename = path + "/" + filename;
+	if (PartitionManager.Flash_Image(full_filename))
+		op_status = 0; // success
+	else
+		op_status = 1; // fail
+
+	operation_end(op_status);
+	return 0;
+}
+
+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());
+}
+
+int GUIAction::checkpartitionlifetimewrites(std::string arg)
+{
+	int op_status = 0;
+	TWPartition* sys = PartitionManager.Find_Partition_By_Path(arg);
+
+	operation_start("Check Partition Lifetime Writes");
+	if (sys) {
+		if (sys->Check_Lifetime_Writes() != 0)
+			DataManager::SetValue("tw_lifetime_writes", 1);
+		else
+			DataManager::SetValue("tw_lifetime_writes", 0);
+		op_status = 0; // success
+	} else {
+		DataManager::SetValue("tw_lifetime_writes", 1);
+		op_status = 1; // fail
+	}
+
+	operation_end(op_status);
+	return 0;
+}
+
+int GUIAction::mountsystemtoggle(std::string arg)
+{
+	int op_status = 0;
+	bool remount_system = PartitionManager.Is_Mounted_By_Path("/system");
+
+	operation_start("Toggle System Mount");
+	if (!PartitionManager.UnMount_By_Path("/system", true)) {
+		op_status = 1; // fail
+	} else {
+		TWPartition* Part = PartitionManager.Find_Partition_By_Path("/system");
+		if (Part) {
+			if (arg == "0") {
+				DataManager::SetValue("tw_mount_system_ro", 0);
+				Part->Change_Mount_Read_Only(false);
+			} else {
+				DataManager::SetValue("tw_mount_system_ro", 1);
+				Part->Change_Mount_Read_Only(true);
+			}
+			if (remount_system) {
+				Part->Mount(true);
+			}
+			op_status = 0; // success
+		} else {
+			op_status = 1; // fail
+		}
+	}
+
+	operation_end(op_status);
+	return 0;
+}
diff --git a/gui/animation.cpp b/gui/animation.cpp
new file mode 100644
index 0000000..888b4ab
--- /dev/null
+++ b/gui/animation.cpp
@@ -0,0 +1,129 @@
+// 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;
+
+	mAnimation = NULL;
+	mFrame = 1;
+	mFPS = 1;
+	mLoop = -1;
+	mRender = 1;
+	mUpdateCount = 0;
+
+	if (!node)  return;
+
+	mAnimation = LoadAttrAnimation(FindNode(node, "resource"), "name");
+
+	// Load the placement
+	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, NULL, NULL, &mPlacement);
+
+	child = FindNode(node, "speed");
+	if (child)
+	{
+		mFPS = LoadAttrInt(child, "fps", mFPS);
+		mRender = LoadAttrInt(child, "render", mRender);
+	}
+	if (mFPS > 30)  mFPS = 30;
+
+	child = FindNode(node, "loop");
+	if (child)
+	{
+		xml_attribute<>* attr = child->first_attribute("frame");
+		if (attr)
+			mLoop = atoi(attr->value()) - 1;
+		mFrame = LoadAttrInt(child, "start", mFrame);
+	}
+
+	// Fetch the render sizes
+	if (mAnimation && mAnimation->GetResource())
+	{
+		mRenderW = mAnimation->GetWidth();
+		mRenderH = mAnimation->GetHeight();
+
+		// 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..95b6c47
--- /dev/null
+++ b/gui/blanktimer.cpp
@@ -0,0 +1,123 @@
+/*
+        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 <string>
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include "pages.hpp"
+#include "blanktimer.hpp"
+#include "../data.hpp"
+extern "C" {
+#include "../minuitwrp/minui.h"
+#include "../twcommon.h"
+}
+#include "../twrp-functions.hpp"
+#include "../variables.h"
+
+blanktimer::blanktimer(void) {
+	pthread_mutex_init(&mutex, NULL);
+	setTime(0); // no timeout
+	state = kOn;
+	orig_brightness = getBrightness();
+}
+
+bool blanktimer::isScreenOff() {
+	return state >= kOff;
+}
+
+void blanktimer::setTime(int newtime) {
+	pthread_mutex_lock(&mutex);
+	sleepTimer = newtime;
+	pthread_mutex_unlock(&mutex);
+}
+
+void blanktimer::setTimer(void) {
+	clock_gettime(CLOCK_MONOTONIC, &btimer);
+}
+
+void blanktimer::checkForTimeout() {
+#ifndef TW_NO_SCREEN_TIMEOUT
+	pthread_mutex_lock(&mutex);
+	timespec curTime, diff;
+	clock_gettime(CLOCK_MONOTONIC, &curTime);
+	diff = TWFunc::timespec_diff(btimer, curTime);
+	if (sleepTimer > 2 && diff.tv_sec > (sleepTimer - 2) && state == kOn) {
+		orig_brightness = getBrightness();
+		state = kDim;
+		TWFunc::Set_Brightness("5");
+	}
+	if (sleepTimer && diff.tv_sec > sleepTimer && state < kOff) {
+		state = kOff;
+		TWFunc::Set_Brightness("0");
+		TWFunc::check_and_run_script("/sbin/postscreenblank.sh", "blank");
+		PageManager::ChangeOverlay("lock");
+	}
+#ifndef TW_NO_SCREEN_BLANK
+	if (state == kOff && gr_fb_blank(1) >= 0) {
+		state = kBlanked;
+	}
+#endif
+	pthread_mutex_unlock(&mutex);
+#endif
+}
+
+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) {
+#ifndef TW_NO_SCREEN_TIMEOUT
+	pthread_mutex_lock(&mutex);
+	setTimer();
+	switch (state) {
+		case kBlanked:
+#ifndef TW_NO_SCREEN_BLANK
+			if (gr_fb_blank(0) < 0) {
+				LOGINFO("blanktimer::resetTimerAndUnblank failed to gr_fb_blank(0)\n");
+				break;
+			}
+#endif
+			// TODO: this is asymmetric with postscreenblank.sh - shouldn't it be under the next case label?
+			TWFunc::check_and_run_script("/sbin/postscreenunblank.sh", "unblank");
+			// No break here, we want to keep going
+		case kOff:
+			gui_forceRender();
+			// No break here, we want to keep going
+		case kDim:
+			if (orig_brightness != "/nobrightness")
+				TWFunc::Set_Brightness(orig_brightness);
+			state = kOn;
+		case kOn:
+			break;
+	}
+	pthread_mutex_unlock(&mutex);
+#endif
+}
diff --git a/gui/blanktimer.hpp b/gui/blanktimer.hpp
new file mode 100644
index 0000000..5e61790
--- /dev/null
+++ b/gui/blanktimer.hpp
@@ -0,0 +1,56 @@
+/*
+        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 <sys/time.h>
+
+using namespace std;
+
+class blanktimer
+{
+public:
+	blanktimer();
+
+	// set timeout in seconds
+	void setTime(int newtime);
+
+	// call this in regular intervals
+	void checkForTimeout();
+
+	// call this when an input event is received or when an operation is finished
+	void resetTimerAndUnblank();
+
+	bool isScreenOff();
+
+private:
+	void setTimer(void);
+	string getBrightness(void);
+
+	pthread_mutex_t mutex;
+	enum State { kOn = 0, kDim = 1, kOff = 2, kBlanked = 3 };
+	State state;
+	timespec btimer;
+	long sleepTimer;
+	string orig_brightness;
+};
+
+extern blanktimer blankTimer;
+
+#endif // __BLANKTIMER_HEADER_HPP
diff --git a/gui/button.cpp b/gui/button.cpp
new file mode 100644
index 0000000..18b5560
--- /dev/null
+++ b/gui/button.cpp
@@ -0,0 +1,261 @@
+/*
+	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);
+
+	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
+	mFillColor = LoadAttrColor(FindNode(node, "fill"), "color", &hasFill);
+	if (!hasFill && mButtonImg == NULL) {
+		LOGERR("No image resource or fill specified for button.\n");
+	}
+
+	// The icon is a special case
+	mButtonIcon = LoadAttrImage(FindNode(node, "icon"), "resource");
+
+	mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlightColor);
+
+	int x, y, w, h;
+	TextPlacement = TOP_LEFT;
+	if (mButtonImg) {
+		mButtonImg->GetRenderPos(x, y, w, h);
+	} else if (hasFill) {
+		LoadPlacement(FindNode(node, "placement"), &x, &y, &w, &h, &TextPlacement);
+	}
+	SetRenderPos(x, y, w, h);
+}
+
+GUIButton::~GUIButton()
+{
+	delete mButtonImg;
+	delete mButtonLabel;
+	delete mAction;
+}
+
+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 = mButtonIcon->GetWidth();
+	mIconH = mButtonIcon->GetHeight();
+
+	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..46a7708
--- /dev/null
+++ b/gui/checkbox.cpp
@@ -0,0 +1,165 @@
+// 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 = FindNode(node, "image");
+	if (child)
+	{
+		mChecked = LoadAttrImage(child, "checked");
+		mUnchecked = LoadAttrImage(child, "unchecked");
+	}
+
+	// Get the variable data
+	child = FindNode(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 = mChecked->GetWidth();
+	mCheckH = mChecked->GetHeight();
+	if (mCheckW == 0)
+	{
+		mCheckW = mUnchecked->GetWidth();
+		mCheckH = mUnchecked->GetHeight();
+	}
+
+	int x, y, w, h;
+	mLabel->GetRenderPos(x, y, w, h);
+	SetRenderPos(x, y, 0, 0);
+}
+
+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..1544d77
--- /dev/null
+++ b/gui/console.cpp
@@ -0,0 +1,328 @@
+// 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) : GUIScrollList(node)
+{
+	xml_node<>* child;
+
+	mLastCount = 0;
+	scrollToEnd = true;
+	mSlideoutX = mSlideoutY = mSlideoutW = mSlideoutH = 0;
+	mSlideout = 0;
+	mSlideoutState = visible;
+
+	allowSelection = false;	// console doesn't support list item selections
+
+	if (!node)
+	{
+		mRenderX = 0; mRenderY = 0; mRenderW = gr_fb_width(); mRenderH = gr_fb_height();
+	}
+	else
+	{
+		child = FindNode(node, "color");
+		if (child)
+		{
+			mFontColor = LoadAttrColor(child, "foreground", mFontColor);
+			mBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor);
+			//mScrollColor = LoadAttrColor(child, "scroll", mScrollColor);
+		}
+
+		child = FindNode(node, "slideout");
+		if (child)
+		{
+			mSlideout = 1;
+			mSlideoutState = hidden;
+			LoadPlacement(child, &mSlideoutX, &mSlideoutY, &mSlideoutW, &mSlideoutH, &mPlacement);
+
+			mSlideoutImage = LoadAttrImage(child, "resource");
+
+			if (mSlideoutImage && mSlideoutImage->GetResource())
+			{
+				mSlideoutW = mSlideoutImage->GetWidth();
+				mSlideoutH = mSlideoutImage->GetHeight();
+				if (mPlacement == CENTER || mPlacement == CENTER_X_ONLY) {
+					mSlideoutX = mSlideoutX - (mSlideoutW / 2);
+					if (mPlacement == CENTER) {
+						mSlideoutY = mSlideoutY - (mSlideoutH / 2);
+					}
+				}
+			}
+		}
+	}
+}
+
+int GUIConsole::RenderSlideout(void)
+{
+	if (!mSlideoutImage || !mSlideoutImage->GetResource())
+		return -1;
+
+	gr_blit(mSlideoutImage->GetResource(), 0, 0, mSlideoutW, mSlideoutH, mSlideoutX, mSlideoutY);
+	return 0;
+}
+
+bool GUIConsole::AddLines()
+{
+	if (mLastCount == gConsole.size())
+		return false; // nothing to add
+
+	size_t prevCount = mLastCount;
+	mLastCount = gConsole.size();
+
+	// 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 (size_t i = prevCount; i < mLastCount; i++) {
+		string curr_line = gConsole[i];
+		string curr_color = gConsoleColor[i];
+		for(;;) {
+			size_t line_char_width = gr_maxExW(curr_line.c_str(), mFont->GetResource(), mRenderW);
+			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;
+			}
+		}
+	}
+	return true;
+}
+
+int GUIConsole::RenderConsole(void)
+{
+	AddLines();
+	GUIScrollList::Render();
+
+	// if last line is fully visible, keep tracking the last line when new lines are added
+	int bottom_offset = GetDisplayRemainder() - actualItemHeight;
+	bool isAtBottom = firstDisplayedItem == GetItemCount() - GetDisplayItemCount() - (bottom_offset != 0) && y_offset == bottom_offset;
+	if (isAtBottom)
+		scrollToEnd = true;
+#if 0
+	// debug - show if we are tracking the last line
+	if (scrollToEnd) {
+		gr_color(0,255,0,255);
+		gr_fill(mRenderX+mRenderW-5, mRenderY+mRenderH-5, 5, 5);
+	} else {
+		gr_color(255,0,0,255);
+		gr_fill(mRenderX+mRenderW-5, mRenderY+mRenderH-5, 5, 5);
+	}
+#endif
+	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 (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 console, we reset the position
+		SetVisibleListLocation(rConsole.size() - 1);
+		mUpdate = 1;
+		scrollToEnd = true;
+	}
+
+	if (AddLines()) {
+		// someone added new text
+		// at least the scrollbar must be updated, even if the new lines are currently not visible
+		mUpdate = 1;
+	}
+
+	if (scrollToEnd) {
+		// keep the last line in view
+		SetVisibleListLocation(rConsole.size() - 1);
+	}
+
+	GUIScrollList::Update();
+
+	if (mUpdate) {
+		mUpdate = 0;
+		if (Render() == 0)
+			return 2;
+	}
+	return 0;
+}
+
+// IsInRegion - Checks if the request is handled by this object
+//  Return 1 if this object handles the request, 0 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 GUIScrollList::IsInRegion(x, y);
+}
+
+// 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 && x >= mSlideoutX && x < mSlideoutX + mSlideoutW && y >= mSlideoutY && y < mSlideoutY + mSlideoutH) {
+		if (state == TOUCH_START) {
+			if (mSlideoutState == hidden)
+				mSlideoutState = request_show;
+			else if (mSlideoutState == visible)
+				mSlideoutState = request_hide;
+		}
+		return 1;
+	}
+	scrollToEnd = false;
+	return GUIScrollList::NotifyTouch(state, x, y);
+}
+
+size_t GUIConsole::GetItemCount()
+{
+	return rConsole.size();
+}
+
+void GUIConsole::RenderItem(size_t itemindex, int yPos, bool selected)
+{
+	// Set the color for the font
+	if (rConsoleColor[itemindex] == "normal") {
+		gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
+	} else {
+		COLOR FontColor;
+		std::string color = rConsoleColor[itemindex];
+		ConvertStrToColor(color, &FontColor);
+		FontColor.alpha = 255;
+		gr_color(FontColor.red, FontColor.green, FontColor.blue, FontColor.alpha);
+	}
+
+	// render text
+	const char* text = rConsole[itemindex].c_str();
+	gr_textEx(mRenderX, yPos, text, mFont->GetResource());
+}
+
+void GUIConsole::NotifySelect(size_t item_selected)
+{
+	// do nothing - console ignores selections
+}
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/twrplogo.png b/gui/devices/1080x1920/res/images/twrplogo.png
new file mode 100644
index 0000000..16b1b2a
--- /dev/null
+++ b/gui/devices/1080x1920/res/images/twrplogo.png
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..a611d05
--- /dev/null
+++ b/gui/devices/1080x1920/res/ui.xml
@@ -0,0 +1,443 @@
+<?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="fixed" type="font" filename="DroidSansMono.ttf" size="30" />
+		<resource name="twrplogo" type="image" filename="twrplogo" retainaspect="1" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" retainaspect="1" />
+		<resource name="folder_icon" type="image" filename="folder" retainaspect="1" />
+		<resource name="slideout" type="image" filename="slideout" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" retainaspect="1" />
+		<resource name="radio_false" type="image" filename="radio_empty" retainaspect="1" />
+		<resource name="radio_true" type="image" filename="radio_selected" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="back_icon" type="image" filename="back-icon" retainaspect="1" />
+		<resource name="slider" type="image" filename="slider" retainaspect="1" />
+		<resource name="slider-used" type="image" filename="slider-used" retainaspect="1" />
+		<resource name="slider-touch" type="image" filename="slider-touch" retainaspect="1" />
+		<resource name="unlock-icon" type="image" filename="unlock" retainaspect="1" />
+		<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" retainaspect="1" />
+	</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="flash_list_height" value="500" />
+		<variable name="tz_selected_y" value="240" />
+		<variable name="tz_set_y" value="1500" />
+		<variable name="tz_current_y" value="1425" />
+		<variable name="system_ro_y" value="1770" />
+		<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_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="1070" />
+		<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="#50505080" />
+		<variable name="fastscroll_rectcolor" value="#33B5E580" />
+		<variable name="fastscroll_w" value="90" />
+		<variable name="fastscroll_linew" value="3" />
+		<variable name="fastscroll_rectw" value="21" />
+		<variable name="fastscroll_recth" value="60" />
+		<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_y" value="700" />
+		<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" />
+		<variable name="pattern_x" value="216" />
+		<variable name="pattern_y" value="508" />
+		<variable name="pattern_width" value="648" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="23" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="18" />
+	</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="fill" color="%button_fill_color%">
+				<placement x="0" y="0" w="1080" h="176" />
+			</object>
+
+			<object type="fill" color="%highlight%">
+				<placement x="0" y="176" w="1080" h="4" />
+			</object>
+
+			<object type="image">
+				<image resource="twrplogo" />
+				<placement x="85" y="85" placement="4" />
+			</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="&gt;" var2="0" />
+					<condition var1="tw_battery" op="&lt;" 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%" placement="1" />
+				<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="%center_x%" y="1841" placement="5" />
+				<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="&amp;" 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="&amp;" 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="&amp;" 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="&lt;" key08="&gt;" 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/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/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/twrplogo.png b/gui/devices/1920x1200/res/images/twrplogo.png
new file mode 100644
index 0000000..6847db6
--- /dev/null
+++ b/gui/devices/1920x1200/res/images/twrplogo.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..cac0279
--- /dev/null
+++ b/gui/devices/1920x1200/res/ui.xml
@@ -0,0 +1,456 @@
+<?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="fixed" type="font" filename="DroidSansMono.ttf" size="22" />
+		<resource name="twrplogo" type="image" filename="twrplogo" retainaspect="1" />
+		<resource name="main_button" type="image" filename="button" />
+		<resource name="file_icon" type="image" filename="file" retainaspect="1" />
+		<resource name="folder_icon" type="image" filename="folder" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" retainaspect="1" />
+		<resource name="radio_false" type="image" filename="radio_empty" retainaspect="1" />
+		<resource name="radio_true" type="image" filename="radio_selected" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="back_icon" type="image" filename="back-icon" retainaspect="1" />
+		<resource name="console_button" type="image" filename="console-toggle" retainaspect="1" />
+		<resource name="slider" type="image" filename="slider" retainaspect="1" />
+		<resource name="slider-used" type="image" filename="slider-used" retainaspect="1" />
+		<resource name="slider-touch" type="image" filename="slider-touch" retainaspect="1" />
+		<resource name="unlock-icon" type="image" filename="unlock" retainaspect="1" />
+		<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" retainaspect="1" />
+	</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="nandcheck_row8" value="725" />
+		<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_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="#50505080" />
+		<variable name="fastscroll_rectcolor" value="#33B5E580" />
+		<variable name="fastscroll_w" value="60" />
+		<variable name="fastscroll_linew" value="3" />
+		<variable name="fastscroll_rectw" value="15" />
+		<variable name="fastscroll_recth" value="40" />
+		<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_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="flash_list_height" value="300" />
+		<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" />
+		<variable name="pattern_x" value="480" />
+		<variable name="pattern_y" value="240" />
+		<variable name="pattern_width" value="720" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="23" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="18" />
+	</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="fill" color="%button_fill_color%">
+				<placement x="0" y="0" w="1920" h="87" />
+			</object>
+
+			<object type="fill" color="%highlight%">
+				<placement x="0" y="87" w="1920" h="3" />
+			</object>
+
+			<object type="image">
+				<image resource="twrplogo" />
+				<placement x="55" y="45" placement="4" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="100" y="5" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="100" y="40" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op="&gt;" var2="0" />
+					<condition var1="tw_battery" op="&lt;" 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="-" long04="_" />
+				</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:!" key10="154:?" key11="265:layout1" />
+					<row4 key01="600:" key02="720: " key03=":" key04="_" />
+				</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:&amp;" key06="156:*" key07="156:-" key08="156:+" key09="156:(" key10="156:)" key11="303:action" />
+					<row3 key01="269:layout4" key02="154:&lt;" key03="154:&gt;" 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/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/cursor.png b/gui/devices/320x320/res/images/cursor.png
new file mode 100644
index 0000000..32c8ae1
--- /dev/null
+++ b/gui/devices/320x320/res/images/cursor.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/twrplogo.png b/gui/devices/320x320/res/images/twrplogo.png
new file mode 100644
index 0000000..a95be1a
--- /dev/null
+++ b/gui/devices/320x320/res/images/twrplogo.png
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..1dec405
--- /dev/null
+++ b/gui/devices/320x320/res/ui.xml
@@ -0,0 +1,434 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<details>
+		<resolution width="320" height="320" />
+		<roundscreen offset_x="40" offset_y="40" />
+		<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="fixed" type="font" filename="DroidSansMono.ttf" size="12" />
+		<resource name="twrplogo" type="image" filename="twrplogo" retainaspect="1" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" retainaspect="1" />
+		<resource name="folder_icon" type="image" filename="folder" retainaspect="1" />
+		<resource name="slideout" type="image" filename="slideout" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" retainaspect="1" />
+		<resource name="radio_false" type="image" filename="radio_empty" retainaspect="1" />
+		<resource name="radio_true" type="image" filename="radio_selected" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="back_icon" type="image" filename="back-icon" retainaspect="1" />
+		<resource name="slider" type="image" filename="slider" retainaspect="1" />
+		<resource name="slider-used" type="image" filename="slider-used" retainaspect="1" />
+		<resource name="slider-touch" type="image" filename="slider-touch" retainaspect="1" />
+		<resource name="unlock-icon" type="image" filename="unlock" retainaspect="1" />
+		<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" retainaspect="1" />
+	</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="flash_list_height" value="160" />
+		<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_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="317" />
+		<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="#50505080" />
+		<variable name="fastscroll_rectcolor" value="#33B5E580" />
+		<variable name="fastscroll_w" value="32" />
+		<variable name="fastscroll_linew" value="1" />
+		<variable name="fastscroll_rectw" value="7" />
+		<variable name="fastscroll_recth" value="24" />
+		<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_y" value="120" />
+		<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="system_ro_y" value="216" />
+		<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="193" />
+		<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" />
+		<variable name="pattern_x" value="70" />
+		<variable name="pattern_y" value="22" />
+		<variable name="pattern_width" value="180" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="7" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="4" />
+	</variables>
+
+	<templates>
+		<template name="twrpheader">
+			<object type="fill" color="%button_fill_color%">
+				<placement x="0" y="0" w="320" h="26" />
+			</object>
+
+			<object type="fill" color="%highlight%">
+				<placement x="0" y="26" w="320" h="1" />
+			</object>
+
+			<object type="image">
+				<image resource="twrplogo" />
+				<placement x="13" y="13" placement="4" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="25" 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="25" y="21" />
+				<text>SIMULATION</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="160" y="3" placement="5" />
+				<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="&gt;" var2="0" />
+					<condition var1="tw_battery" op="&lt;" 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%" placement="1" />
+				<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="key">back</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="%center_x%" y="288" placement="5" />
+				<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="&amp;" 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="&lt;" key08="&gt;" 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/twrplogo.png b/gui/devices/480x800/res/images/twrplogo.png
new file mode 100644
index 0000000..4b39da8
--- /dev/null
+++ b/gui/devices/480x800/res/images/twrplogo.png
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..341332f
--- /dev/null
+++ b/gui/devices/480x800/res/ui.xml
@@ -0,0 +1,430 @@
+<?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="fixed" type="font" filename="DroidSansMono.ttf" size="16" />
+		<resource name="twrplogo" type="image" filename="twrplogo" retainaspect="1" />
+		<resource name="main_button" type="image" filename="menu-button" />
+		<resource name="file_icon" type="image" filename="file" retainaspect="1" />
+		<resource name="folder_icon" type="image" filename="folder" retainaspect="1" />
+		<resource name="slideout" type="image" filename="slideout" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" retainaspect="1" />
+		<resource name="radio_false" type="image" filename="radio_empty" retainaspect="1" />
+		<resource name="radio_true" type="image" filename="radio_selected" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="back_icon" type="image" filename="back-icon" retainaspect="1" />
+		<resource name="slider" type="image" filename="slider" retainaspect="1" />
+		<resource name="slider-used" type="image" filename="slider-used" retainaspect="1" />
+		<resource name="slider-touch" type="image" filename="slider-touch" retainaspect="1" />
+		<resource name="unlock-icon" type="image" filename="unlock" retainaspect="1" />
+		<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" retainaspect="1" />
+	</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="flash_list_height" value="210" />
+		<variable name="tz_selected_y" value="110" />
+		<variable name="tz_set_y" value="580" />
+		<variable name="tz_current_y" value="730" />
+		<variable name="system_ro_y" value="550" />
+		<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_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="470" />
+		<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="#50505080" />
+		<variable name="fastscroll_rectcolor" value="#33B5E580" />
+		<variable name="fastscroll_w" value="40" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="10" />
+		<variable name="fastscroll_recth" value="30" />
+		<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_y" value="250" />
+		<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" />
+		<variable name="pattern_x" value="90" />
+		<variable name="pattern_y" value="250" />
+		<variable name="pattern_width" value="300" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="12" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="9" />
+	</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="fill" color="%button_fill_color%">
+				<placement x="0" y="0" w="480" h="98" />
+			</object>
+
+			<object type="fill" color="%highlight%">
+				<placement x="0" y="98" w="480" h="2" />
+			</object>
+
+			<object type="image">
+				<image resource="twrplogo" />
+				<placement x="50" y="50" placement="4" />
+			</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="&gt;" var2="0" />
+					<condition var1="tw_battery" op="&lt;" 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%" placement="1" />
+				<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="%center_x%" y="760" placement="5" />
+				<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="&amp;" 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="&lt;" key08="&gt;" 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/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/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/twrplogo.png b/gui/devices/800x480/res/images/twrplogo.png
new file mode 100644
index 0000000..309012f
--- /dev/null
+++ b/gui/devices/800x480/res/images/twrplogo.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..12e0ee7
--- /dev/null
+++ b/gui/devices/800x480/res/ui.xml
@@ -0,0 +1,465 @@
+<?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="fixed" type="font" filename="DroidSansMono.ttf" size="12" />
+		<resource name="twrplogo" type="image" filename="twrplogo" retainaspect="1" />
+		<resource name="main_button" type="image" filename="button" />
+		<resource name="file_icon" type="image" filename="file" retainaspect="1" />
+		<resource name="folder_icon" type="image" filename="folder" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="checkbox_true" type="image" filename="checkbox_checked" retainaspect="1" />
+		<resource name="radio_false" type="image" filename="radio_empty" retainaspect="1" />
+		<resource name="radio_true" type="image" filename="radio_selected" retainaspect="1" />
+		<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" retainaspect="1" />
+		<resource name="back_icon" type="image" filename="back-icon" retainaspect="1" />
+		<resource name="console_button" type="image" filename="console-toggle" retainaspect="1" />
+		<resource name="slider" type="image" filename="slider" retainaspect="1" />
+		<resource name="slider-used" type="image" filename="slider-used" retainaspect="1" />
+		<resource name="slider-touch" type="image" filename="slider-touch" retainaspect="1" />
+		<resource name="unlock-icon" type="image" filename="unlock" retainaspect="1" />
+		<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" retainaspect="1" />
+	</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="nandcheck_row8" value="340" />
+		<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="#50505080" />
+		<variable name="fastscroll_rectcolor" value="#33B5E580" />
+		<variable name="fastscroll_w" value="25" />
+		<variable name="fastscroll_linew" value="2" />
+		<variable name="fastscroll_rectw" value="12" />
+		<variable name="fastscroll_recth" value="20" />
+		<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_y" value="150" />
+		<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="flash_list_height" value="140" />
+		<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" />
+		<variable name="pattern_x" value="200" />
+		<variable name="pattern_y" value="110" />
+		<variable name="pattern_width" value="300" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="10" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="7" />
+	</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="fill" color="%button_fill_color%">
+				<placement x="0" y="0" w="800" h="58" />
+			</object>
+
+			<object type="fill" color="%highlight%">
+				<placement x="0" y="58" w="800" h="2" />
+			</object>
+
+			<object type="image">
+				<image resource="twrplogo" />
+				<placement x="35" y="29" placement="4" />
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="70" y="2" />
+				<text>Team Win Recovery Project  v%tw_version%</text>
+			</object>
+
+			<object type="text" color="%text_color%">
+				<font resource="font" />
+				<placement x="70" y="37" />
+				<conditions>
+					<condition var1="tw_no_battery_percent" var2="0" />
+					<condition var1="tw_battery" op="&gt;" var2="0" />
+					<condition var1="tw_battery" op="&lt;" 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="&amp;" key05="*" key06="-" key07="+" key08="(" key09=")" key10="109:action" />
+					<row3 key01="79:layout4" key02="72:&lt;" key03="72:&gt;" 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/DroidSansMono.ttf b/gui/devices/common/res/fonts/DroidSansMono.ttf
new file mode 100644
index 0000000..4085cee
--- /dev/null
+++ b/gui/devices/common/res/fonts/DroidSansMono.ttf
Binary files differ
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..c75be3c
--- /dev/null
+++ b/gui/devices/landscape/res/landscape.xml
@@ -0,0 +1,3714 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<styles>
+		<style name="buttontext">
+			<highlight color="%highlight_color%" />
+			<font resource="font" color="%button_text_color%" />
+		</style>
+
+		<style name="button">
+			<style name="buttontext" />
+			<image resource="main_button" />
+		</style>
+
+		<style name="mediumbutton">
+			<style name="buttontext" />
+			<image resource="medium_button" />
+		</style>
+
+		<style name="mediumwidebutton">
+			<style name="buttontext" />
+			<image resource="mediumwide_button" />
+		</style>
+
+		<style name="fillbutton">
+			<style name="buttontext" />
+			<fill color="%button_fill_color%" />
+		</style>
+
+		<style name="rebootsystem">
+			<condition var1="tw_reboot_system" var2="1" />
+			<style name="button" />
+			<text>Reboot System</text>
+			<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>
+		</style>
+
+		<style name="scrolllist">
+			<highlight color="%fileselector_highlight_color%" />
+			<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%" />
+			<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+			<background color="%fileselector_background%" />
+			<font resource="font" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+		</style>
+
+		<style name="fileselector">
+			<style name="scrolllist" />
+			<icon folder="folder_icon" file="file_icon" />
+			<sort name="tw_gui_sort_order" />
+		</style>
+
+		<style name="partitionlist">
+			<style name="scrolllist" />
+			<icon selected="checkbox_true" unselected="checkbox_false" />
+		</style>
+
+		<style name="text">
+			<font resource="font" color="%text_color%" />
+		</style>
+
+		<style name="checkbox">
+			<font resource="font" color="%text_color%" />
+			<image checked="checkbox_true" unchecked="checkbox_false" />
+		</style>
+
+		<style name="slider">
+			<text>Swipe to Confirm</text>
+			<font resource="font" color="%text_color%" />
+			<placement x="%center_x%" y="%slider_y%" placement="5" />
+			<resource base="slider" used="slider-used" touch="slider-touch" />
+		</style>
+
+		<style name="console">
+			<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+			<font resource="fixed" />
+			<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+		</style>
+
+		<style name="input">
+			<background color="%input_background_color%" />
+			<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+			<font resource="font" color="%text_color%" />
+		</style>
+
+		<style name="slidervalue">
+			<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%" />
+		</style>
+	</styles>
+
+	<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">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>Install</text>
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Backup</text>
+				<action function="page">backup</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col3_x%" y="%row1_y%" />
+				<text>Restore</text>
+				<action function="page">restore</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col4_x%" y="%row1_y%" />
+				<text>Wipe</text>
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Mount</text>
+				<action function="page">mount</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Settings</text>
+				<action function="page">settings</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col3_x%" y="%row2_y%" />
+				<text>Advanced</text>
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col4_x%" y="%row2_y%" />
+				<text>Reboot</text>
+				<action function="page">reboot</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="install">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Select Zip to Install</text>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%button_full_center_x%" y="%zipstorage_text_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<placement x="%fileselector_folder_x%" y="%fileselector_install_y%" w="%fileselector_folder_width%" h="%fileselector_install_height%" />
+				<text>Folders:</text>
+				<filter folders="1" files="0" />
+				<path name="tw_zip_location" default="/sdcard" />
+				<data name="select" />
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_file_x%" y="%fileselector_install_y%" w="%fileselector_file_width%" h="%fileselector_install_height%" />
+				<text>%tw_zip_location%</text>
+				<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="button">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Images...</text>
+				<actions>
+					<action function="page">install_image</action>
+				</actions>
+			</object>
+
+			<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">
+				<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">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Folder:</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_zip_location%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>File to flash:</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>%tw_file%</text>
+			</object>
+
+			<object type="text">
+				<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%" />
+				<text>Zip file signature verification?</text>
+				<data variable="tw_signed_zip_verify" />
+			</object>
+
+			<object type="text">
+				<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" style="mediumwidebutton">
+				<condition var1="tw_zip_queue_count" op="!=" var2="10"></condition>
+				<placement x="%col2_x%" y="%row5_medium_y%" />
+				<text>Add More Zips</text>
+				<action function="page">install</action>
+			</object>
+
+			<object type="button" style="mediumwidebutton">
+				<placement x="%col3_x%" y="%row5_medium_y%" />
+				<text>Clear Queue</text>
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Confirm Flash</text>
+				<action function="flash">flash_zip</action>
+			</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">
+				<placement x="%col1_x%" y="%row16_text_y%" />
+				<text>Flashing file %tw_zip_index% of %tw_zip_queue_count%</text>
+			</object>
+
+			<object type="text">
+				<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">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row17_text_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font"  color="%text_success_color%" />
+				<placement x="%center_x%" y="%row17_text_y%" placement="5" />
+				<text>Successful</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%slider_y%" />
+				<text>Wipe Cache/Dalvik</text>
+				<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 &amp; Dalvik?</action>
+					<action function="set">tw_action_text1=Wiping Cache &amp; Dalvik...</action>
+					<action function="set">tw_complete_text1=Cache &amp; 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" style="rebootsystem">
+				<placement x="%col4_x%" y="%slider_y%" />
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%slider_y%" />
+				<text>Home</text>
+				<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="install_image">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Select Image to Flash</text>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%button_full_center_x%" y="%zipstorage_text_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=install_image</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_folder_x%" y="%fileselector_install_y%" w="%fileselector_folder_width%" h="%fileselector_install_height%" />
+				<text>Folders:</text>
+				<filter folders="1" files="0" />
+				<path name="tw_zip_location" default="/sdcard" />
+				<data name="select" />
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_file_x%" y="%fileselector_install_y%" w="%fileselector_file_width%" h="%fileselector_install_height%" />
+				<text>%tw_zip_location%</text>
+				<filter extn=".img" 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="button">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Zips...</text>
+				<actions>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_filename" op="modified" />
+				<action function="page">flashimage_confirm</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">install</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="flashimage_confirm">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<placement x="%col2_x%" y="%row1_text_y%" w="%listbox_width%" h="%flash_list_height%" />
+				<text>Select Partition to Flash Image:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<data name="tw_flash_partition" />
+				<listtype name="flashimg" />
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row8_text_y%" placement="5" />
+				<text>Folder:</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row9_text_y%" placement="5" />
+				<text>%tw_zip_location%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row10_text_y%" placement="5" />
+				<text>File to flash:</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row11_text_y%" placement="5" />
+				<text>%tw_file%</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Confirm Flash</text>
+				<actions>
+					<action function="set">tw_back=flashimage_confirm</action>
+					<action function="set">tw_action=flashimage</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_action_text1=Flashing Image...</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1=Image Flashed</action>
+					<action function="page">action_page</action>
+				</actions>
+				<action function="flashimage"></action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=install_image</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>
+
+			<object type="template" name="footer" />
+		</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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_text1%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_text2%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_text3%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_text4%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row12_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<text>%tw_slider_text%</text>
+				<action function="page">action_page</action>
+			</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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text">
+				<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">
+				<condition var1="tw_has_cancel" var2="1" />
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text">
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_complete_text1%</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" color="%text_success_color%" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Successful</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="button">
+				<condition var1="tw_show_reboot" var2="0" />
+				<placement x="%col_center_x%" y="%slider_y%" />
+				<text>Back</text>
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="button" style="rebootsystem">
+				<condition var1="tw_show_reboot" var2="1" />
+				<placement x="%col_center_x%" y="%slider_y%" />
+			</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="&gt;=" var2="%tw_min_system%" />
+				<action function="reboot">%tw_action_param%</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_backup_system_size" op="&lt;" var2="%tw_min_system%" />
+				<action function="page">confirm_action</action>
+			</object>
+		</page>
+
+		<page name="reboot">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Reboot Menu</text>
+			</object>
+
+			<object type="button" style="rebootsystem">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>System</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_reboot_poweroff" var2="1" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Power Off</text>
+				<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">
+				<condition var1="tw_reboot_recovery" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Recovery</text>
+				<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">
+				<condition var1="tw_reboot_bootloader" var2="1" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Bootloader</text>
+				<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">
+				<condition var1="tw_download_mode" var2="1" />
+				<placement x="%col3_x%" y="%row2_y%" />
+				<text>Download</text>
+				<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">
+				<placement x="%col2_x%" y="%fileselector_install_y%" w="%fileselector_folderonly_width%" h="%fileselector_install_height%" />
+				<text>Select Storage:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<data name="tw_storage_path" />
+				<listtype name="storage" />
+			</object>
+
+			<object type="button">
+				<placement x="%filemanager_select_x%" y="%row2_y%" />
+				<text>OK</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Menu</text>
+			</object>
+
+			<object type="partitionlist">
+				<placement x="%backup_list_x%" y="%backup_list_y%" w="%backup_list_width%" h="%mount_list_height%" />
+				<text>Select Partitions to Mount:</text>
+				<listtype name="mount" />
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_is_decrypted" var2="0" />
+				</conditions>
+				<placement x="%col3_x%" y="row1_y" />
+				<text>Decrypt Data</text>
+				<action function="page">decrypt</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_has_usb_storage" var2="1" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<text>Mount USB Storage</text>
+				<action function="page">usb_mount</action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="0" />
+				</conditions>
+				<placement x="%col3_x%" y="row1_y" />
+				<text>Enable MTP</text>
+				<action function="startmtp"></action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="1" />
+				</conditions>
+				<placement x="%col3_x%" y="row1_y" />
+				<text>Disable MTP</text>
+				<action function="stopmtp"></action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_is_decrypted" var2="0" />
+				</conditions>
+				<placement x="%col3_x%" y="row1_y" />
+				<text>Decrypt Data</text>
+				<action function="page">decrypt</action>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col3_x%" y="%backup_storage_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<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">
+				<placement x="%col3_x%" y="%row10_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_mount_system_ro" op="=" var2="0" />
+				<text>Only mount system read-only</text>
+				<image resource="checkbox_false" />
+				<action function="mountsystemtoggle">1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col3_x%" y="%row10_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_mount_system_ro" op="!=" var2="0" />
+				<text>Only mount system read-only</text>
+				<image resource="checkbox_true" />
+				<actions>
+					<action function="set">tw_lifetime_writes=2</action>
+					<action function="page">system_readonly_check</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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>USB Storage Mounted -- Be sure to safely remove your device from your computer before unmounting!</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row1_y%" />
+				<text>Unmount</text>
+				<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="system_readonly_check">
+			<object type="action">
+				<action function="checkpartitionlifetimewrites">/system</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_lifetime_writes" var2="1" />
+				<actions>
+					<action function="mountsystemtoggle">0</action>
+					<action function="page">mount</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_lifetime_writes" var2="0" />
+				<actions>
+					<action function="set">tw_back=mount</action>
+					<action function="page">system_readonly</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="wipe">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Factory Reset: Wipes Data, Cache, and Dalvik</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_has_data_media" var2="1" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>(not including internal storage)</text>
+			</object>
+
+			<object type="text">
+				<conditions>
+					<condition var1="tw_has_android_secure" var2="1" />
+					<condition var1="fileexists" var2="/and-sec" />
+				</conditions>
+				<placement x="%center_x%" y="%row3_text_y%" placement="1" />
+				<text>Android Secure  </text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_has_sdext_partition" var2="1" />
+				<placement x="%center_x%" y="%row3_text_y%" />
+				<text>  SD-EXT</text>
+			</object>
+
+			<object type="text">
+				<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">
+				<placement x="%col2_x%" y="%wipe_button_y%" />
+				<text>Advanced Wipe</text>
+				<action function="page">advancedwipe</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_has_data_media" var2="1" />
+				<placement x="%col3_x%" y="%wipe_button_y%" />
+				<text>Format Data</text>
+				<actions>
+					<action function="page">formatdata</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_has_data_media" var2="0" />
+				</conditions>
+				<placement x="%col3_x%" y="%wipe_button_y%" />
+				<text>Wipe Encryption</text>
+				<actions>
+					<action function="set">tw_back=wipe</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=DATAMEDIA</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Wipe Encryption from Data?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting Data...</action>
+					<action function="set">tw_complete_text1=Data Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Format Data</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row12_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Factory Reset</text>
+				<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="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">
+				<placement x="%backup_list_x%" y="%backup_list_y%" w="%backup_list_width%" h="%backup_list_height%" />
+				<text>Select Partitions to Wipe:</text>
+				<data name="tw_wipe_list" />
+				<listtype name="wipe" />
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col3_x%" y="%backup_storage_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<text>Repair or Change File System</text>
+				<actions>
+					<action function="checkpartitionlist"></action>
+					<action function="page">checkpartitionlist</action>
+				</actions>
+			</object>
+
+			<object type="text">
+				<condition var1="partitionlisterror" var2="1" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%col3_x%" y="%backup_storage_y%" />
+				<text>Invalid partition selection</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Wipe</text>
+				<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="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">
+				<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">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>videos, media, and removes encryption on internal storage.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>This cannot be undone. Press back to cancel.</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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</action>
+			</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">
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<text>Partition Options for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col_right_x%" y="%row1_text_y%" placement="1" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_is_present" op="!=" var2="0" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Present: Yes</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_is_present" op="=" var2="0" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Present: No</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_removable" op="!=" var2="0" />
+				<placement x="%col3_x%" y="%row3_text_y%" />
+				<text>Removable: Yes</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_removable" op="=" var2="0" />
+				<placement x="%col3_x%" y="%row3_text_y%" />
+				<text>Removable: No</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Size: %tw_partition_size%MB</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col2_x%" y="%row4_text_y%" />
+				<text>Used: %tw_partition_used%MB</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col3_x%" y="%row4_text_y%" />
+				<text>Free: %tw_partition_free%MB</text>
+			</object>
+
+			<object type="text">
+				<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_resize" op="=" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Resize</text>
+				<actions>
+					<action function="set">tw_back=partitionoptions</action>
+					<action function="set">tw_action=resize</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_has_action2=1</action>
+					<action function="set">tw_action2=getpartitiondetails</action>
+					<action function="set">tw_text1=Resize %tw_partition_name%?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Resizing...</action>
+					<action function="set">tw_complete_text1=Resize Complete</action>
+					<action function="set">tw_slider_text=Swipe to Resize</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_can_repair" op="=" var2="1" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Repair</text>
+				<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">
+				<placement x="%col3_x%" y="%row2_y%" />
+				<text>Change File System</text>
+				<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">
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<text>Change file system for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col_right_x%" y="%row1_text_y%" placement="1" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>EXT2</text>
+				<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" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>EXT3</text>
+				<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" />
+				<placement x="%col3_x%" y="%row1_y%" />
+				<text>EXT4</text>
+				<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" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<text>F2FS</text>
+				<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" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>FAT</text>
+				<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" />
+				<placement x="%col3_x%" y="%row2_y%" />
+				<text>exFAT</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Back Up Device</text>
+			</object>
+
+			<object type="partitionlist">
+				<placement x="%backup_list_x%" y="%backup_list_y%" w="%backup_list_width%" h="%backup_list_height%" />
+				<text>Select Partitions to Back Up:</text>
+				<data name="tw_backup_list" />
+				<listtype name="backup" />
+			</object>
+
+			<object type="text">
+				<placement x="%col_right_x%" y="%row2_text_y%" placement="1" />
+				<text>Backup Name: %tw_backup_name%</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col3_x%" y="%row1_y%" />
+				<text>Refresh Sizes</text>
+				<actions>
+					<action function="refreshsizes"></action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col4_x%" y="%row1_y%" />
+				<text>Set Backup Name</text>
+				<actions>
+					<action function="set">tw_fileexists=0</action>
+					<action function="page">backupname1</action>
+				</actions>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col3_x%" y="%backup_storage_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<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" style="fillbutton">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="0" />
+				</conditions>
+				<placement x="%col3_x%" y="%backup_encrypt_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<text>No Encryption</text>
+				<actions>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="1" />
+				</conditions>
+				<placement x="%col3_x%" y="%backup_encrypt_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
+				<text>Using Encryption</text>
+				<actions>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col3_x%" y="%nandcheck_row6%" />
+				<text>Enable Compression (Requires more time)</text>
+				<data variable="tw_use_compression" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col3_x%" y="%nandcheck_row7%" />
+				<text>Skip MD5 generation on backups</text>
+				<data variable="tw_skip_md5_generate" />
+			</object>
+
+                        <object type="checkbox">
+                                <placement x="%col3_x%" y="%nandcheck_row8%" />
+                                <font resource="font" color="%text_color%" />
+                                <text>Disable Free Space Check.</text>
+                                <data variable="tw_disable_free_space" />
+                                <image checked="checkbox_true" unchecked="checkbox_false" />
+                        </object>
+
+			<object type="slider">
+				<text>Swipe to Back Up</text>
+				<actions>
+					<action function="set">tw_operation_state=0</action>
+					<action function="page">backup_run</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="backupname1">
+			<object type="action">
+				<condition var1="tw_backup_name" op="=" var2="(Auto Generate)" />
+				<action function="generatebackupname"></action>
+			</object>
+
+			<object type="action">
+				<action function="page">backupname2</action>
+			</object>
+		</page>
+
+		<page name="backupname2">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<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" />
+				<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">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<font resource="font" color="%text_fail_color%"/>
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%cancel_button_y%" />
+				<text>Append Date</text>
+				<action function="appenddatetobackupname"></action>
+			</object>
+
+			<object type="button">
+				<placement x="%col3_x%" y="%cancel_button_y%" />
+				<text>Cancel / Clear</text>
+				<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">
+				<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" />
+				<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">
+				<condition var1="tw_password_not_match" var2="1" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Passwords Do Not Match</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<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">
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<text>%tw_file_progress%</text>
+			</object>
+
+			<object type="text">
+				<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="button" style="mediumbutton">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="cancelbackup"></action>
+				</actions>
+			</object>
+
+			<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" />
+				<condition var1="tw_cancel_backup" var2="0" />
+				<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="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<condition var1="tw_cancel_backup" var2="1" />
+				<actions>
+					<action function="set">tw_back=backup</action>
+					<action function="set">tw_complete_text1=Backup Cancelled</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" style="fillbutton">
+				<placement x="%col2_x%" y="%zipstorage_text_y%" w="%fileselector_folderonly_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<placement x="%col2_x%" y="%fileselector_install_y%" w="%fileselector_folderonly_width%" h="%fileselector_install_height%" />
+				<text>Select Package to Restore:</text>
+				<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">
+				<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" />
+				<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">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">restore</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col3_x%" y="%cancel_button_y%" />
+				<text>Delete</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% &amp;&amp; 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">
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Package to Restore: %tw_restore_name%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Package Date: %tw_restore_file_date%</text>
+			</object>
+
+			<object type="partitionlist">
+				<placement x="%backup_list_x%" y="%restore_list_y%" w="%backup_list_width%" h="%restore_list_height%" />
+				<text>Select Partitions to Restore:</text>
+				<data name="tw_restore_list" selectedlist="tw_restore_selected" />
+				<listtype name="restore" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col3_x%" y="%nandcheck_row6%" />
+				<text>Enable MD5 checking of backup files</text>
+				<data variable="tw_skip_md5_check" />
+			</object>
+
+			<object type="button">
+				<placement x="%col4_x%" y="%row1_y%" />
+				<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">
+				<placement x="%col3_x%" y="%row1_y%" />
+				<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% &amp;&amp; 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">
+				<text>Swipe to Restore</text>
+				<action function="page">restore_run</action>
+			</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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<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" />
+				<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% &amp;&amp; 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">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<font resource="font" color="%text_fail_color%" />
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text">
+				<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">
+				<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%" />
+				<text>Zip file signature verification?</text>
+				<data variable="tw_signed_zip_verify" />>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Use rm -rf instead of formatting?</text>
+				<data variable="tw_rm_rf" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Skip MD5 generation on backups</text>
+				<data variable="tw_skip_md5_generate" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>Enable MD5 checking of backup files</text>
+				<data variable="tw_skip_md5_check" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row6_text_y%" />
+				<text>Use 24-hour clock</text>
+				<data variable="tw_military_time" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row7_text_y%" />
+				<text>Simulate most actions for theme testing</text>
+				<data variable="tw_simulate_actions" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<placement x="%col1_x%" y="%row8_text_y%" />
+				<text>Simulate failure for actions</text>
+				<data variable="tw_simulate_fail" />
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Time Zone</text>
+				<action function="page">timezone</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Restore Defaults</text>
+				<action function="restoredefaultsettings"></action>
+			</object>
+
+			<object type="button">
+				<placement x="%col3_x%" y="%row2_y%" />
+				<text>Vibration Duration</text>
+				<action function="page">Vibrate</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col4_x%" y="%row2_y%" />
+				<text>Screen</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Select Time Zone</text>
+			</object>
+
+			<object type="listbox" style="scrolllist">
+				<placement x="%listbox_x%" y="%listbox_y%" w="%listbox_width%" h="%listbox_tz_height%" />
+				<text>Select Time Zone:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<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,M3.2.0,M11.1.0</listitem>
+				<listitem name="(UTC -7) Mountain Time">MST7;MDT,M3.2.0,M11.1.0</listitem>
+				<listitem name="(UTC -6) Central Time">CST6;CDT,M3.2.0,M11.1.0</listitem>
+				<listitem name="(UTC -5) Eastern Time">EST5;EDT,M3.2.0,M11.1.0</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,M3.5.0,M10.5.0</listitem>
+				<listitem name="(UTC +1) Berlin, Brussels, Paris">CET-1;CEST,M3.5.0,M10.5.0</listitem>
+				<listitem name="(UTC +2) Athens, Istanbul, South Africa">WET-2;WET,M3.5.0,M10.5.0</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%" />
+				<text>Do you use daylight savings time (DST)?</text>
+				<data variable="tw_time_zone_guidst" />
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row13_text_y%" placement="5" />
+				<text>Offset (usually 0): %tw_time_zone_guioffset%</text>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col1_medium_x%" y="%row_offsetmedium_y%" />
+				<text>0</text>
+				<action function="set">tw_time_zone_guioffset=0</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col2_medium_x%" y="%row_offsetmedium_y%" />
+				<text>15</text>
+				<action function="set">tw_time_zone_guioffset=15</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col3_medium_x%" y="%row_offsetmedium_y%" />
+				<text>30</text>
+				<action function="set">tw_time_zone_guioffset=30</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col4_medium_x%" y="%row_offsetmedium_y%" />
+				<text>45</text>
+				<action function="set">tw_time_zone_guioffset=45</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col4_x%" y="%row2_y%" />
+				<text>Set Time Zone</text>
+				<action function="setguitimezone"></action>
+			</object>
+
+			<object type="text">
+				<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">
+				<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%" />
+				<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%" />
+				<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">
+				<conditions>
+					<condition var1="tw_screen_timeout_secs" op="!=" var2="0" />
+					<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				</conditions>
+				<placement x="slidervalue_x" y="%row5_text_y%" w="%slidervalue_w%" />
+				<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%" />
+				<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">
+				<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%" />
+				<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%" />
+				<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%" />
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Advanced</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>Copy Log to SD</text>
+				<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">
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Fix Permissions</text>
+				<action function="page">fixperms</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col3_x%" y="%row1_y%" />
+				<text>Terminal Command</text>
+				<action function="page">terminalfolder</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col4_x%" y="%row1_y%" />
+				<text>ADB Sideload</text>
+				<action function="page">sideload</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_allow_partition_sdcard" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Partition SD Card</text>
+				<action function="page">partsdcard</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>File Manager</text>
+				<action function="page">filemanagerlist</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col3_x%" y="%row2_y%" />
+				<text>Reload Theme</text>
+				<action function="reload"></action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col4_x%" y="%row2_y%" />
+				<text>HTC Dumlock</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Partition SD Card</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_sdext_x%" y="%row1_sdext_y%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_sdext_size-128</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_sdext_x%" y="%row1_sdext_y%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_sdext_size+128</action>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row_extsize_y%" placement="5" />
+				<text>EXT Size: %tw_sdext_size%</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_sdext_x%" y="%row2_sdext_y%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_swap_size-32</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_sdext_x%" y="%row2_sdext_y%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_swap_size+32</action>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row_swapsize_y%" placement="5" />
+				<text>Swap Size: %tw_swap_size%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row8_text_y%" placement="5" />
+				<text>File system: %tw_sdpart_file_system%</text>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col2_medium_x%" y="%row4_medium_y%" />
+				<text>EXT3</text>
+				<action function="set">tw_sdpart_file_system=ext3</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<condition var1="tw_sdext_disable_ext4" var2="0" />
+				<placement x="%col3_medium_x%" y="%row4_medium_y%" />
+				<text>EXT4</text>
+				<action function="set">tw_sdpart_file_system=ext4</action>
+			</object>
+
+			<object type="text">
+				<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">
+				<placement x="%center_x%" y="%row13_text_y%" placement="5" />
+				<text>This action cannot be undone!</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Confirm Partition</text>
+				<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="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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>HTC Dumlock</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>Restore Original Boot</text>
+				<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">
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Reflash Recovery->Boot</text>
+				<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">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Install HTC Dumlock</text>
+				<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="%center_x%" y="%lock_y%" placement="4" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Unlock</text>
+				<action function="overlay"></action>
+			</object>
+		</page>
+
+
+		<page name="filemanagerlist">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>File Manager: Select a File or Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_folder_x%" y="%fileselector_install_y%" w="%fileselector_folder_width%" h="%fileselector_install_height%" />
+				<text>Folders:</text>
+				<filter folders="1" files="0" />
+				<path name="tw_file_location1" default="/sdcard" />
+				<data name="select" />
+				<selection name="tw_selection1" />
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_file_x%" y="%fileselector_install_y%" w="%fileselector_file_width%" h="%fileselector_install_height%" />
+				<text>%tw_file_location1%</text>
+				<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">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Select Folder</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_type% Selected:</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Copy File</text>
+				<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">
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Copy Folder</text>
+				<actions>
+					<action function="set">tw_filemanager_command=cd &quot;%tw_file_location1%&quot; &amp;&amp; cd .. &amp;&amp; cp -R</action>
+					<action function="set">tw_fm_text1=Copying</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Move</text>
+				<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">
+				<placement x="%col3_x%" y="%row2_y%" />
+				<text>chmod 755</text>
+				<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">
+				<placement x="%col4_x%" y="%row2_y%" />
+				<text>chmod</text>
+				<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">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>Delete</text>
+				<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">
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<text>Rename File</text>
+				<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">
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col4_x%" y="%row1_y%" />
+				<text>Rename Folder</text>
+				<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; &amp;&amp; cd .. &amp;&amp; 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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>Browse to Destination Folder &amp; Press Select</text>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%col2_x%" y="%fileselector_install_y%" w="%fileselector_folderonly_width%" h="%fileselector_install_height%" />
+				<text>%tw_file_location2%</text>
+				<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">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Select Folder</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<font resource="font" color="%text_color%" />
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_text1%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>%tw_fm_text2%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>%tw_fm_text3%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row10_text_y%" placement="5"/>
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<action function="page">filemanageracction</action>
+			</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">
+				<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="action">
+				<condition var1="tw_crypto_pwtype" var2="2" />
+				<action function="page">decrypt_pattern</action>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<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="decrypt_pattern">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Please Enter Your Pattern</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Pattern Failed, Please Try Again</text>
+			</object>
+
+			<object type="patternpassword">
+				<placement x="%pattern_x%" y="%pattern_y%" w="%pattern_width%" h="%pattern_width%" />
+				<dot color="%pattern_dot_color%" activecolor="%pattern_dot_color_active%" radius="%pattern_dot_radius%" />
+				<line color="%pattern_line_color%" width="%pattern_line_width%" />
+				<data name="tw_crypto_password"/>
+				<action function="page">trydecrypt</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col4_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="trydecrypt">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>Browse to Starting Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%col2_x%" y="%fileselector_install_y%" w="%fileselector_folderonly_width%" h="%fileselector_install_height%" />
+				<text>%tw_terminal_location%</text>
+				<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">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Select Folder</text>
+				<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%" />
+			</object>
+
+			<object type="text">
+				<placement x="%input_x%" y="%terminal_text_y%" placement="0" />
+				<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" />
+				<text>%tw_terminal_command%</text>
+				<font resource="fixed" color="%text_color%" />
+				<data name="tw_terminal_command" />
+				<restrict minlen="1" />
+				<action function="terminalcommand">%tw_terminal_command%</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<condition var1="tw_terminal_state" var2="1" />
+				<placement x="%filemanager_select_x%" y="%terminal_button_y%" />
+				<text>KILL</text>
+				<action function="killterminal"></action>
+			</object>
+
+			<object type="button">
+				<placement x="%home_button_x%" y="%terminal_button_y%" />
+				<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="%terminal_button_y%" />
+				<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">
+				<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%" />
+				<text>Wipe Dalvik Cache.</text>
+				<data variable="tw_wipe_dalvik" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Wipe Cache.</text>
+				<data variable="tw_wipe_cache" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Start Sideload</text>
+				<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="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="fixperms">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>Fix Permissions</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col2_x%" y="%row2_text_y%" />
+				<text>Note: Fixing permissions is rarely needed.</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Also fix SELinux contexts</text>
+				<data variable="tw_fixperms_restorecon" />
+			</object>
+
+			<object type="text">
+				<placement x="%col2_x%" y="%row4_text_y%" />
+				<text>Fixing SELinux contexts may cause your device to not boot properly.</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Fix Permissions</text>
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=fixpermissions</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">action_page</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="installsu">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>Install SuperSU?</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>Your device does not appear to be rooted.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>Install SuperSU now? This will root your device.</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row2_medium_y%" />
+				<text>Do Not Install</text>
+				<action function="set">tw_page_done=1</action>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Install</text>
+				<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>
+		</page>
+
+		<page name="system_readonly">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Keep System Read Only?</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>TWRP can leave your system partition unmodified to make it easier for you to take official updates.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>TWRP will be unable to prevent the stock ROM from replacing TWRP and will not offer to root your device.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Installing zips or performing adb operations may still modify the system partition.</text>
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_is_encrypted" var2="0" />
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>Never show this screen during boot again</text>
+				<data variable="tw_never_show_system_ro_page" />
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row7_text_y%" />
+				<text>Keep Read Only</text>
+				<actions>
+					<action function="mountsystemtoggle">1</action>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">%tw_back%</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Allow Modifications</text>
+				<actions>
+					<action function="mountsystemtoggle">0</action>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">%tw_back%</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</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..969044c
--- /dev/null
+++ b/gui/devices/portrait/res/portrait.xml
@@ -0,0 +1,3740 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<styles>
+		<style name="buttontext">
+			<highlight color="%highlight_color%" />
+			<font resource="font" color="%button_text_color%" />
+		</style>
+
+		<style name="button">
+			<style name="buttontext" />
+			<image resource="main_button" />
+		</style>
+
+		<style name="mediumbutton">
+			<style name="buttontext" />
+			<image resource="medium_button" />
+		</style>
+
+		<style name="fillbutton">
+			<style name="buttontext" />
+			<fill color="%button_fill_color%" />
+		</style>
+
+		<style name="rebootsystem">
+			<condition var1="tw_reboot_system" var2="1" />
+			<style name="button" />
+			<text>Reboot System</text>
+			<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>
+		</style>
+
+		<style name="scrolllist">
+			<highlight color="%fileselector_highlight_color%" />
+			<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%" />
+			<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+			<background color="%fileselector_background%" />
+			<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+		</style>
+
+		<style name="fileselector">
+			<style name="scrolllist" />
+			<icon folder="folder_icon" file="file_icon" />
+			<sort name="tw_gui_sort_order" />
+		</style>
+
+		<style name="partitionlist">
+			<style name="scrolllist" />
+			<icon selected="checkbox_true" unselected="checkbox_false" />
+		</style>
+
+		<style name="text">
+			<font resource="font" color="%text_color%" />
+		</style>
+
+		<style name="checkbox">
+			<font resource="font" color="%text_color%" />
+			<image checked="checkbox_true" unchecked="checkbox_false" />
+		</style>
+
+		<style name="slider">
+			<text>Swipe to Confirm</text>
+			<font resource="font" color="%text_color%" />
+			<placement x="%center_x%" y="%slider_y%" placement="5" />
+			<resource base="slider" used="slider-used" touch="slider-touch" />
+		</style>
+
+		<style name="console">
+			<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+			<font resource="fixed" />
+			<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+		</style>
+
+		<style name="input">
+			<background color="%input_background_color%" />
+			<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+			<font resource="font" color="%text_color%" />
+		</style>
+
+		<style name="slidervalue">
+			<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%" />
+		</style>
+	</styles>
+
+	<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">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>Install</text>
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Wipe</text>
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Backup</text>
+				<action function="page">backup</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Restore</text>
+				<action function="page">restore</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Mount</text>
+				<action function="page">mount</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row3_y%" />
+				<text>Settings</text>
+				<action function="page">settings</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>Advanced</text>
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Reboot</text>
+				<action function="page">reboot</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="install">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Select Zip to Install</text>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%row1_text_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<placement x="%fileselector_x%" y="%row3_text_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_zip_location%</text>
+				<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="button" style="mediumbutton">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Images...</text>
+				<actions>
+					<action function="page">install_image</action>
+				</actions>
+			</object>
+
+			<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>This operation may install incompatible</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>software and render your device unusable.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>Folder:</text>
+			</object>
+
+			<object type="text">
+				<font resource="mediumfont" color="%text_color%"/>
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_zip_location%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>File to flash:</text>
+			</object>
+
+			<object type="text">
+				<font resource="mediumfont" color="%text_color%"/>
+				<placement x="%center_x%" y="%row6_text_y%" placement="5" />
+				<text>%tw_file%</text>
+			</object>
+
+			<object type="text">
+				<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%" />
+				<text>Zip file signature verification.</text>
+				<data variable="tw_signed_zip_verify" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_has_injecttwrp" var2="1" />
+				<placement x="%col1_x%" y="%row10_text_y%" />
+				<text>Inject TWRP after install.</text>
+				<data variable="tw_inject_after_zip" />
+			</object>
+
+			<object type="text">
+				<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">
+				<text>Swipe to Confirm Flash</text>
+				<action function="flash">flash_zip</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_zip_queue_count" op="!=" var2="10"></condition>
+				<placement x="%col1_x%" y="%row_queue_y%" />
+				<text>Add More Zips</text>
+				<action function="page">install</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row_queue_y%" />
+				<text>Clear Zip Queue</text>
+				<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%" />
+			</object>
+
+			<object type="text">
+				<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">
+				<font resource="mediumfont" color="%text_color%"/>
+				<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">
+				<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%" />
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>Wipe cache/dalvik</text>
+				<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 &amp; Dalvik?</action>
+					<action function="set">tw_action_text1=Wiping Cache &amp; Dalvik...</action>
+					<action function="set">tw_complete_text1=Cache &amp; 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">
+				<style name="rebootsystem" />
+				<placement x="%col2_x%" y="%row4_y%" />
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row3_y%" />
+				<text>Home</text>
+				<actions>
+					<action function="set">tw_clear_destination=main2</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%zip_status_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" color="%text_success_color%"/>
+				<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="install_image">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Select Image to Install</text>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%row1_text_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=install_image</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_x%" y="%row3_text_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_zip_location%</text>
+				<filter extn=".img" 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="button" style="mediumbutton">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Zips...</text>
+				<actions>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_filename" op="modified" />
+				<action function="page">flashimage_confirm</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">install</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="flashimage_confirm">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%flash_list_height%" />
+				<text>Select Partition to Flash Image:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<data name="tw_flash_partition" />
+				<listtype name="flashimg" />
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row8_text_y%" placement="5" />
+				<text>Folder:</text>
+			</object>
+
+			<object type="text">
+				<font resource="mediumfont" color="%text_color%"/>
+				<placement x="%center_x%" y="%row9_text_y%" placement="5" />
+				<text>%tw_zip_location%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row10_text_y%" placement="5" />
+				<text>File to flash:</text>
+			</object>
+
+			<object type="text">
+				<font resource="mediumfont" color="%text_color%"/>
+				<placement x="%center_x%" y="%row11_text_y%" placement="5" />
+				<text>%tw_file%</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Confirm Flash</text>
+				<actions>
+					<action function="set">tw_back=flashimage_confirm</action>
+					<action function="set">tw_action=flashimage</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_action_text1=Flashing Image...</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1=Image Flashed</action>
+					<action function="page">action_page</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=install_image</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>
+
+			<object type="template" name="footer" />
+		</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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_text1%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_text2%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_text3%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_text4%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row15_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<text>%tw_slider_text%</text>
+				<action function="page">action_page</action>
+			</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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text">
+				<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" style="mediumbutton">
+				<condition var1="tw_has_cancel" var2="1" />
+				<placement x="%col_center_medium_x%" y="%row4_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text">
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_complete_text1%</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font"  color="%text_success_color%"/>
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Successful</text>
+			</object>
+
+			<object type="template" name="action_page_console" />
+
+			<object type="button">
+				<condition var1="tw_show_reboot" var2="0" />
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<text>Back</text>
+				<actions>
+					<action function="set">tw_clear_destination=%tw_back%</action>
+					<action function="page">clear_vars</action>
+				</actions>
+			</object>
+
+			<object type="button" style="rebootsystem">
+				<condition var1="tw_show_reboot" var2="1" />
+				<placement x="%col_center_x%" y="%row4_y%" />
+			</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="&gt;=" var2="%tw_min_system%" />
+				<action function="reboot">%tw_action_param%</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_backup_system_size" op="&lt;" var2="%tw_min_system%" />
+				<action function="page">confirm_action</action>
+			</object>
+		</page>
+
+		<page name="wipe">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Factory Reset</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Wipes Data, Cache, and Dalvik</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_has_data_media" var2="1" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>(not including internal storage)</text>
+			</object>
+
+			<object type="text">
+				<conditions>
+					<condition var1="tw_has_android_secure" var2="1" />
+					<condition var1="fileexists" var2="/and-sec" />
+				</conditions>
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Android Secure</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_has_sdext_partition" var2="1" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>SD-EXT</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row6_text_y%" placement="5" />
+				<text>Most of the time this is</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row7_text_y%" placement="5" />
+				<text>the only wipe that you need.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row16_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%wipe_button_y%" />
+				<text>Advanced Wipe</text>
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_has_data_media" var2="1" />
+				<placement x="%col2_x%" y="%wipe_button_y%" />
+				<text>Format Data</text>
+				<actions>
+					<action function="page">formatdata</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_has_data_media" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="%wipe_button_y%" />
+				<text>Wipe Encryption</text>
+				<actions>
+					<action function="set">tw_back=wipe</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=DATAMEDIA</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Wipe Encryption from Data?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting Data...</action>
+					<action function="set">tw_complete_text1=Data Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Format Data</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Factory Reset</text>
+				<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="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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Wipe Menu</text>
+			</object>
+
+			<object type="partitionlist">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%wipe_list_height%" />
+				<text>Select Partitions to Wipe:</text>
+				<data name="tw_wipe_list" />
+				<listtype name="wipe" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Wipe</text>
+				<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" style="fillbutton">
+				<placement x="%col1_x%" y="%wipe_button_row1%" w="%button_fill_full_width%" h="%button_fill_half_height%" />
+				<text>Repair or Change File System</text>
+				<actions>
+					<action function="checkpartitionlist"></action>
+					<action function="page">checkpartitionlist</action>
+				</actions>
+			</object>
+
+			<object type="text">
+				<font resource="font" color="%text_fail_color%"/>
+				<condition var1="partitionlisterror" var2="1" />
+				<placement x="%center_x%" y="%wipe_button_row1%" placement="5" />
+				<text>Invalid partition selection</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Format Data will wipe all of your apps,</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>backups, pictures, videos, media, and</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>removes encryption on internal storage.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>This cannot be undone. Press back to cancel.</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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</action>
+			</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Partition Options for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_is_present" op="!=" var2="0" />
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Present: Yes</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_is_present" op="=" var2="0" />
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Present: No</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_removable" op="!=" var2="0" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Removable: Yes</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_removable" op="=" var2="0" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Removable: No</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Size: %tw_partition_size%MB</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col2_x%" y="%row4_text_y%" />
+				<text>Used: %tw_partition_used%MB</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>Free: %tw_partition_free%MB</text>
+			</object>
+
+			<object type="text">
+				<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_resize" op="=" var2="1" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Resize</text>
+				<actions>
+					<action function="set">tw_back=partitionoptions</action>
+					<action function="set">tw_action=resize</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_has_action2=1</action>
+					<action function="set">tw_action2=getpartitiondetails</action>
+					<action function="set">tw_text1=Resize %tw_partition_name%?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Resizing...</action>
+					<action function="set">tw_complete_text1=Resize Complete</action>
+					<action function="set">tw_slider_text=Swipe to Resize</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_can_repair" op="=" var2="1" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>Repair</text>
+				<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">
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Change File System</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Change file system for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>Some ROMs or kernels may not support some</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>EXT2</text>
+				<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" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>EXT3</text>
+				<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" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>EXT4</text>
+				<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" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<text>F2FS</text>
+				<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" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>FAT</text>
+				<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" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>exFAT</text>
+				<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" style="fillbutton">
+				<placement x="%col1_x%" y="%row1_header_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<placement x="%listbox_x%" y="%row2_text_y%" w="%listbox_width%" h="%backup_list_height%" />
+				<text>Select Partitions to Back Up:</text>
+				<data name="tw_backup_list" />
+				<listtype name="backup" />
+			</object>
+
+			<object type="button" style="fillbutton">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="0" />
+				</conditions>
+				<placement x="%col1_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<text>No Encryption</text>
+				<actions>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="1" />
+				</conditions>
+				<placement x="%col1_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<text>Using Encryption</text>
+				<actions>
+					<action function="set">tw_password_not_match=0</action>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col2_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<text>Refresh Sizes</text>
+				<actions>
+					<action function="refreshsizes"></action>
+					<action function="page">backup</action>
+				</actions>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%backup_button_row2%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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%" />
+				<text>Enable compression.</text>
+				<data variable="tw_use_compression" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row16_text_y%" />
+				<text>Skip MD5 generation during backup.</text>
+				<data variable="tw_skip_md5_generate" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row17_text_y%" />
+				<font resource="font" color="%text_color%" />
+				<text>Disable Free Space Check.</text>
+				<data variable="tw_disable_free_space" />
+				<image checked="checkbox_true" unchecked="checkbox_false" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Back Up</text>
+				<action function="page">backup_run</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="backupname1">
+			<object type="action">
+				<condition var1="tw_backup_name" op="=" var2="(Auto Generate)" />
+				<action function="generatebackupname"></action>
+			</object>
+
+			<object type="action">
+				<action function="page">backupname2</action>
+			</object>
+		</page>
+
+		<page name="backupname2">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<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" />
+				<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">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<font resource="font" color="%text_fail_color%"/>
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Append Date</text>
+				<action function="appenddatetobackupname"></action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Encrypt your backup?</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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">
+				<condition var1="tw_password_not_match" var2="1" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Passwords Do Not Match</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Encrypt your backup?</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_file_progress%</text>
+			</object>
+
+			<object type="text">
+				<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="button" style="mediumbutton">
+				<placement x="%col_center_medium_x%" y="%row4_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="cancelbackup"></action>
+				</actions>
+			</object>
+
+			<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" />
+				<condition var1="tw_cancel_backup" var2="0" />
+				<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="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<condition var1="tw_cancel_backup" var2="1" />
+				<actions>
+					<action function="set">tw_back=backup</action>
+					<action function="set">tw_complete_text1=Backup Cancelled</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" style="fillbutton">
+				<placement x="%col1_x%" y="%row1_header_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<placement x="%fileselector_x%" y="%row2_text_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>Select Package to Restore:</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Backup Encrypted</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">restore</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Delete</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% &amp;&amp; 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">
+				<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">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%backup_list_height%" />
+				<text>Restoring: %tw_restore_name%</text>
+				<data name="tw_restore_list" selectedlist="tw_restore_selected" />
+				<listtype name="restore" />
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<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" style="fillbutton">
+				<placement x="%col2_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<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% &amp;&amp; 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%" />
+				<text>Enable MD5 verification of backup files.</text>
+				<data variable="tw_skip_md5_check" />
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row16_text_y%" placement="5" />
+				<text>Package Date: %tw_restore_file_date%</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Restore</text>
+				<action function="page">restore_run</action>
+			</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<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" />
+				<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% &amp;&amp; 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">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<font resource="font" color="%text_fail_color%"/>
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text">
+				<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">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%storage_list_height%" />
+				<text>Select Storage:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<data name="tw_storage_path" />
+				<listtype name="storage" />
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<text>OK</text>
+				<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">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%mount_list_height%" />
+				<text>Select Partitions to Mount:</text>
+				<listtype name="mount" />
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%mount_storage_row%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<condition var1="tw_has_usb_storage" var2="1" />
+				<placement x="%col1_x%" y="row4_y" />
+				<text>Mount USB Storage</text>
+				<action function="page">usb_mount</action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<text>Enable MTP</text>
+				<action function="startmtp"></action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="1" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<text>Disable MTP</text>
+				<action function="stopmtp"></action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_is_decrypted" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<text>Decrypt Data</text>
+				<action function="page">decrypt</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%system_ro_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_mount_system_ro" op="=" var2="0" />
+				<text>Only mount system read-only</text>
+				<image resource="checkbox_false" />
+				<action function="mountsystemtoggle">1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%system_ro_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_mount_system_ro" op="!=" var2="0" />
+				<text>Only mount system read-only</text>
+				<image resource="checkbox_true" />
+				<actions>
+					<action function="set">tw_lifetime_writes=2</action>
+					<action function="page">system_readonly_check</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>USB Storage Mounted</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<text>Be sure to safely remove your device</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<text>from your computer before unmounting!</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Unmount</text>
+				<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="system_readonly_check">
+			<object type="action">
+				<action function="checkpartitionlifetimewrites">/system</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_lifetime_writes" var2="1" />
+				<actions>
+					<action function="mountsystemtoggle">0</action>
+					<action function="page">mount</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_lifetime_writes" var2="0" />
+				<actions>
+					<action function="set">tw_back=mount</action>
+					<action function="page">system_readonly</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="reboot">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Reboot Menu</text>
+			</object>
+
+			<object type="button" style="rebootsystem">
+				<condition var1="tw_reboot_system" var2="1" />
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>System</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_reboot_poweroff" var2="1" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Power Off</text>
+				<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">
+				<condition var1="tw_reboot_recovery" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Recovery</text>
+				<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">
+				<condition var1="tw_reboot_bootloader" var2="1" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Bootloader</text>
+				<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">
+				<condition var1="tw_download_mode" var2="1" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Download</text>
+				<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">
+				<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%" />
+				<text>Zip file signature verification.</text>
+				<data variable="tw_signed_zip_verify" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<text>Use rm -rf instead of formatting.</text>
+				<data variable="tw_rm_rf" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Skip MD5 generation during backup.</text>
+				<data variable="tw_skip_md5_generate" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Enable MD5 verification of backup files.</text>
+				<data variable="tw_skip_md5_check" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>Use 24-hour clock.</text>
+				<data variable="tw_military_time" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row6_text_y%" />
+				<text>Simulate actions for theme testing.</text>
+				<data variable="tw_simulate_actions" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<placement x="%col1_x%" y="%row7_text_y%" />
+				<text>Simulate failure for actions.</text>
+				<data variable="tw_simulate_fail" />
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Time Zone</text>
+				<action function="page">timezone</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row3_y%" />
+				<text>Screen</text>
+				<action function="page">screen</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>Restore Defaults</text>
+				<action function="restoredefaultsettings"></action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Vibration Duration</text>
+				<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" style="scrolllist">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%listbox_tz_height%" />
+				<text>Select Time Zone:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<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,M3.2.0,M11.1.0</listitem>
+				<listitem name="(UTC -7) Mountain Time">MST7;MDT,M3.2.0,M11.1.0</listitem>
+				<listitem name="(UTC -6) Central Time">CST6;CDT,M3.2.0,M11.1.0</listitem>
+				<listitem name="(UTC -5) Eastern Time">EST5;EDT,M3.2.0,M11.1.0</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,M3.5.0,M10.5.0</listitem>
+				<listitem name="(UTC +1) Berlin, Brussels, Paris">CET-1;CEST,M3.5.0,M10.5.0</listitem>
+				<listitem name="(UTC +2) Athens, Istanbul, South Africa">WET-2;WET,M3.5.0,M10.5.0</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%" />
+				<text>Do you use daylight savings time (DST)?</text>
+				<data variable="tw_time_zone_guidst" />
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row_offset_text_y%" placement="5" />
+				<text>Offset (usually 0): %tw_time_zone_guioffset%</text>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col1_medium_x%" y="%row_offset_medium_y%" />
+				<text>None</text>
+				<action function="set">tw_time_zone_guioffset=0</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col2_medium_x%" y="%row_offset_medium_y%" />
+				<text>15</text>
+				<action function="set">tw_time_zone_guioffset=15</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col3_medium_x%" y="%row_offset_medium_y%" />
+				<text>30</text>
+				<action function="set">tw_time_zone_guioffset=30</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col4_medium_x%" y="%row_offset_medium_y%" />
+				<text>45</text>
+				<action function="set">tw_time_zone_guioffset=45</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%tz_set_y%" />
+				<text>Set Time Zone</text>
+				<action function="setguitimezone"></action>
+			</object>
+
+			<object type="text">
+				<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">
+				<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">
+				<conditions>
+					<condition var1="tw_screen_timeout_secs" op="!=" var2="0" />
+					<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				</conditions>
+				<placement x="col1_x" y="%row4_text_y%" w="%slidervalue_w%" />
+				<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%" />
+				<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">
+				<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%" />
+				<text>Button Vibration:</text>
+				<data variable="tw_button_vibrate" min="0" max="300" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row8_text_y%" w="%slidervalue_w%" />
+				<text>Keyboard Vibration:</text>
+				<data variable="tw_keyboard_vibrate" min="0" max="300" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row12_text_y%" w="%slidervalue_w%" />
+				<text>Action Vibration:</text>
+				<data variable="tw_action_vibrate" min="0" max="500" />
+			</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Advanced</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>Copy Log to SD</text>
+				<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">
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Fix Permissions</text>
+				<action function="page">fixperms</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_allow_partition_sdcard" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Partition SD Card</text>
+				<action function="page">partsdcard</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>File Manager</text>
+				<action function="page">filemanagerlist</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Terminal Command</text>
+				<action function="page">terminalfolder</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row3_y%" />
+				<text>Reload Theme</text>
+				<action function="reload"></action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>ADB Sideload</text>
+				<action function="page">sideload</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>HTC Dumlock</text>
+				<action function="page">htcdumlock</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_has_injecttwrp" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Re-Inject TWRP</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Partition SD Card</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_sdext_size-128</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sd_plus_x%" y="%row1_y%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_sdext_size+128</action>
+			</object>
+
+			<object type="text">
+				<placement x="%sdext_text_x%" y="%sdext_text_y%" />
+				<text>EXT Size: %tw_sdext_size%</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%sdswap_button_y%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_swap_size-32</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sd_plus_x%" y="%sdswap_button_y%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_swap_size+32</action>
+			</object>
+
+			<object type="text">
+				<placement x="%sdswap_text_x%" y="%sdswap_text_y%" />
+				<text>Swap Size: %tw_swap_size%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%sdfilesystem_text_y%" />
+				<text>File system: %tw_sdpart_file_system%</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%sdfilesystem_button_y%" />
+				<text>EXT3</text>
+				<action function="set">tw_sdpart_file_system=ext3</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_sdext_disable_ext4" var2="0" />
+				<placement x="%col2_x%" y="%sdfilesystem_button_y%" />
+				<text>EXT4</text>
+				<action function="set">tw_sdpart_file_system=ext4</action>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row12_text_y%" />
+				<text>You will lose all files on your SD card!</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row13_text_y%" />
+				<text>This action cannot be undone!</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Partition</text>
+				<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="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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>HTC Dumlock</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>Restore Original Boot</text>
+				<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">
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Reflash Recovery</text>
+				<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">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Install HTC Dumlock</text>
+				<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="%center_x%" y="%lock_y%" placement="4" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Unlock</text>
+				<action function="overlay"></action>
+			</object>
+		</page>
+
+		<page name="filemanagerlist">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>File Manager: Select a File or Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_file_location1%</text>
+				<filter 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" style="mediumbutton">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Select</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_type% Selected:</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Copy File</text>
+				<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">
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Copy Folder</text>
+				<actions>
+					<action function="set">tw_filemanager_command=cd &quot;%tw_file_location1%&quot; &amp;&amp; cd .. &amp;&amp; cp -R</action>
+					<action function="set">tw_fm_text1=Copying</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Move</text>
+				<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">
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>chmod 755</text>
+				<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">
+				<placement x="%col2_x%" y="%row3_y%" />
+				<text>chmod</text>
+				<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">
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>Delete</text>
+				<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">
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Rename File</text>
+				<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">
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Rename Folder</text>
+				<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; &amp;&amp; cd .. &amp;&amp; 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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Browse to Destination Folder &amp; Press Select</text>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_file_location2%</text>
+				<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" style="mediumbutton">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Select</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_text1%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>%tw_fm_text2%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>%tw_fm_text3%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row10_text_y%" placement="5"/>
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<action function="page">filemanageracction</action>
+			</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">
+				<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="action">
+				<condition var1="tw_crypto_pwtype" var2="2" />
+				<action function="page">decrypt_pattern</action>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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="decrypt_pattern">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Please Enter Your Pattern</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Pattern Failed, Please Try Again</text>
+			</object>
+
+			<object type="patternpassword">
+				<placement x="%pattern_x%" y="%pattern_y%" w="%pattern_width%" h="%pattern_width%" />
+				<dot color="%pattern_dot_color%" activecolor="%pattern_dot_color_active%" radius="%pattern_dot_radius%" />
+				<line color="%pattern_line_color%" width="%pattern_line_width%" />
+				<data name="tw_crypto_password"/>
+				<action function="page">trydecrypt</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="trydecrypt">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Browse to Starting Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_terminal_location%</text>
+				<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" style="mediumbutton">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Select</text>
+				<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%" />
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%terminal_text_y%" placement="0" />
+				<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" />
+				<text>%tw_terminal_command%</text>
+				<font resource="fixed" color="%text_color%" />
+				<data name="tw_terminal_command" />
+				<restrict minlen="1" />
+				<action function="terminalcommand">%tw_terminal_command%</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<condition var1="tw_terminal_state" var2="1" />
+				<placement x="%filemanager_select_x%" y="%terminal_button_y%" />
+				<text>KILL</text>
+				<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">
+				<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%" />
+				<text>Wipe Dalvik Cache.</text>
+				<data variable="tw_wipe_dalvik" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Wipe Cache.</text>
+				<data variable="tw_wipe_cache" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Start Sideload</text>
+				<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="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="fixperms">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Fix Permissions</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<text>Note: Fixing permissions is rarely needed.</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Also fix SELinux contexts</text>
+				<data variable="tw_fixperms_restorecon" />
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Fixing SELinux contexts may cause</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>your device to not boot properly.</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Fix Permissions</text>
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=fixpermissions</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">action_page</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="installsu">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Install SuperSU?</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>Your device does not appear to be rooted.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>Install SuperSU now?</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>This will root your device.</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row3_y%" />
+				<text>Do Not Install</text>
+				<action function="set">tw_page_done=1</action>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Install</text>
+				<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>
+		</page>
+
+		<page name="system_readonly">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Keep System Read Only?</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>TWRP can leave your system partition unmodified</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>to make it easier for you to take official updates.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>TWRP will be unable to prevent the stock ROM from</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>replacing TWRP and will not offer to root your device.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row6_text_y%" placement="5" />
+				<text>Installing zips or performing adb operations may still</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row7_text_y%" placement="5" />
+				<text>modify the system partition.</text>
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_is_encrypted" var2="0" />
+				<placement x="%col1_x%" y="%row8_text_y%" />
+				<text>Never show this screen during boot again</text>
+				<data variable="tw_never_show_system_ro_page" />
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row10_text_y%" />
+				<text>Keep Read Only</text>
+				<actions>
+					<action function="mountsystemtoggle">1</action>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">%tw_back%</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Allow Modifications</text>
+				<actions>
+					<action function="mountsystemtoggle">0</action>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">%tw_back%</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</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..28198fb
--- /dev/null
+++ b/gui/devices/watch/res/watch.xml
@@ -0,0 +1,3727 @@
+<?xml version="1.0"?>
+
+<recovery>
+	<styles>
+		<style name="buttontext">
+			<highlight color="%highlight_color%" />
+			<font resource="font" color="%button_text_color%" />
+		</style>
+
+		<style name="button">
+			<style name="buttontext" />
+			<image resource="main_button" />
+		</style>
+
+		<style name="mediumbutton">
+			<style name="buttontext" />
+			<image resource="medium_button" />
+		</style>
+
+		<style name="fillbutton">
+			<style name="buttontext" />
+			<fill color="%button_fill_color%" />
+		</style>
+
+		<style name="rebootsystem">
+			<condition var1="tw_reboot_system" var2="1" />
+			<style name="button" />
+			<text>Reboot System</text>
+			<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>
+		</style>
+
+		<style name="scrolllist">
+			<highlight color="%fileselector_highlight_color%" />
+			<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%" />
+			<separator color="%fileselector_separatorcolor%" height="%fileselector_separatorheight%" />
+			<background color="%fileselector_background%" />
+			<font resource="filelist" spacing="%fileselector_spacing%" color="%text_color%" highlightcolor="%fileselector_highlight_font_color%" />
+		</style>
+
+		<style name="fileselector">
+			<style name="scrolllist" />
+			<icon folder="folder_icon" file="file_icon" />
+			<sort name="tw_gui_sort_order" />
+		</style>
+
+		<style name="partitionlist">
+			<style name="scrolllist" />
+			<icon selected="checkbox_true" unselected="checkbox_false" />
+		</style>
+
+		<style name="text">
+			<font resource="font" color="%text_color%" />
+		</style>
+
+		<style name="checkbox">
+			<font resource="font" color="%text_color%" />
+			<image checked="checkbox_true" unchecked="checkbox_false" />
+		</style>
+
+		<style name="slider">
+			<text>Swipe to Confirm</text>
+			<font resource="font" color="%text_color%" />
+			<placement x="%center_x%" y="%slider_y%" placement="5" />
+			<resource base="slider" used="slider-used" touch="slider-touch" />
+		</style>
+
+		<style name="console">
+			<color foreground="%console_foreground%" background="%console_background%" scroll="%console_scroll%" />
+			<font resource="fixed" />
+			<fastscroll linecolor="%fastscroll_linecolor%" rectcolor="%fastscroll_rectcolor%" w="%fastscroll_w%" linew="%fastscroll_linew%" rectw="%fastscroll_rectw%" recth="%fastscroll_recth%" />
+		</style>
+
+		<style name="input">
+			<background color="%input_background_color%" />
+			<cursor color="%input_cursor_color%" hasfocus="1" width="%input_cursor_width%" />
+			<font resource="font" color="%text_color%" />
+		</style>
+
+		<style name="slidervalue">
+			<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%" />
+		</style>
+	</styles>
+
+	<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">
+				<placement x="%col1_x%" y="%row1_home_y%" />
+				<text>Install</text>
+				<action function="page">install_select</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row1_home_y%" />
+				<text>Wipe</text>
+				<action function="page">wipe</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_home_y%" />
+				<text>Backup</text>
+				<action function="page">backup</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_home_y%" />
+				<text>Restore</text>
+				<action function="page">restore</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row3_home_y%" />
+				<text>Mount</text>
+				<action function="page">mount</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row3_home_y%" />
+				<text>Settings</text>
+				<action function="page">settings</action>
+			</object>
+
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row4_home_y%" />
+				<text>Advanced</text>
+				<action function="page">advanced</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row4_home_y%" />
+				<text>Reboot</text>
+				<action function="page">reboot</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="install_select">
+			<object type="template" name="twrpheader" />
+
+			<object type="template" name="header" />
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row1_home_y%" />
+				<text>Install Zips</text>
+				<actions>
+					<action function="queueclear"></action>
+					<action function="page">install</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row1_home_y%" />
+				<text>Install Images</text>
+				<action function="page">install_image</action>
+			</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="install">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Select Zip to Install</text>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%row1_text_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<placement x="%fileselector_x%" y="%fileselector_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_zip_location%</text>
+				<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">install_select</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>This operation may install incompatible</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>software and render your device unusable.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Folder and File:</text>
+			</object>
+
+			<object type="text">
+				<font resource="mediumfont" color="%text_color%" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_zip_location%</text>
+			</object>
+
+			<object type="text">
+				<font resource="mediumfont" color="%text_color%" />
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_file%</text>
+			</object>
+
+			<object type="text">
+				<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%" />
+				<text>Zip file signature verification.</text>
+				<data variable="tw_signed_zip_verify" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_has_injecttwrp" var2="1" />
+				<placement x="%col1_x%" y="%row7_text_y%" />
+				<text>Inject TWRP after install.</text>
+				<data variable="tw_inject_after_zip" />
+			</object>
+
+			<object type="text">
+				<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">
+				<text>Swipe to Confirm Flash</text>
+				<action function="flash">flash_zip</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_zip_queue_count" op="!=" var2="10"></condition>
+				<placement x="%col1_x%" y="%row_queue_y%" />
+				<text>Add More Zips</text>
+				<action function="page">install</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row_queue_y%" />
+				<text>Clear Zip Queue</text>
+				<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%" />
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row10_text_y%" placement="5" />
+				<text>Flashing file %tw_zip_index% of %tw_zip_queue_count%</text>
+			</object>
+
+			<object type="text">
+				<font resource="mediumfont" color="%text_color%"/>
+				<placement x="%center_x%" y="%row11_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">
+				<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%" />
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>Wipe cache/dalvik</text>
+				<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 &amp; Dalvik?</action>
+					<action function="set">tw_action_text1=Wiping Cache &amp; Dalvik...</action>
+					<action function="set">tw_complete_text1=Cache &amp; 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" style="rebootsystem">
+				<placement x="%col2_x%" y="%row4_y%" />
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%zip_status_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" color="%text_success_color%" />
+				<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="install_image">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Select Image to Install</text>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%row1_text_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<text>Storage: %tw_storage_display_name% (%tw_storage_free_size% MB)</text>
+				<actions>
+					<action function="set">tw_back=install_image</action>
+					<action function="page">selectstorage</action>
+				</actions>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_x%" y="%row3_text_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_zip_location%</text>
+				<filter extn=".img" 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" />
+				<action function="page">flashimage_confirm</action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<action function="page">install_select</action>
+			</object>
+
+			<object type="action">
+				<touch key="home" />
+				<action function="page">main</action>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="flashimage_confirm">
+			<object type="template" name="header" />
+
+			<object type="partitionlist">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%flash_list_height%" />
+				<text>Select Partition to Flash Image:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<data name="tw_flash_partition" />
+				<listtype name="flashimg" />
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row8_text_y%" placement="5" />
+				<text>Folder:</text>
+			</object>
+
+			<object type="text">
+				<font resource="mediumfont" color="%text_color%" />
+				<placement x="%center_x%" y="%row9_text_y%" placement="5" />
+				<text>%tw_zip_location%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row10_text_y%" placement="5" />
+				<text>File to flash:</text>
+			</object>
+
+			<object type="text" >
+				<font resource="mediumfont" color="%text_color%" />
+				<placement x="%center_x%" y="%row11_text_y%" placement="5" />
+				<text>%tw_file%</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Confirm Flash</text>
+				<actions>
+					<action function="set">tw_back=flashimage_confirm</action>
+					<action function="set">tw_action=flashimage</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_action_text1=Flashing Image...</action>
+					<action function="set">tw_action_text2=</action>
+					<action function="set">tw_complete_text1=Image Flashed</action>
+					<action function="page">action_page</action>
+				</actions>
+				<action function="flashimage"></action>
+			</object>
+
+			<object type="action">
+				<touch key="back" />
+				<actions>
+					<action function="set">tw_clear_destination=install_image</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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_text1%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_text2%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>%tw_text3%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>%tw_text4%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row15_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<text>%tw_slider_text%</text>
+				<action function="page">action_page</action>
+			</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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text">
+				<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" style="mediumbutton">
+				<condition var1="tw_has_cancel" var2="1" />
+				<placement x="%col_center_medium_x%" y="%row4_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_action_text1%</text>
+			</object>
+
+			<object type="text">
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_complete_text1%</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" op="!=" var2="0" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Failed</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_operation_status" var2="0" />
+				<font resource="font" color="%text_success_color%" />
+				<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="&gt;=" var2="%tw_min_system%" />
+				<action function="reboot">%tw_action_param%</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_backup_system_size" op="&lt;" var2="%tw_min_system%" />
+				<action function="page">confirm_action</action>
+			</object>
+		</page>
+
+		<page name="wipe">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Factory Reset</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Wipes Data, Cache, and Dalvik</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_has_data_media" var2="1" />
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>(not including internal storage)</text>
+			</object>
+
+			<object type="text">
+				<conditions>
+					<condition var1="tw_has_android_secure" var2="1" />
+					<condition var1="fileexists" var2="/and-sec" />
+				</conditions>
+				<placement x="%col2_x%" y="%row4_text_y%" placement="1" />
+				<text>Android Secure  </text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_has_sdext_partition" var2="1" />
+				<placement x="%col2_x%" y="%row4_text_y%" />
+				<text>  SD-EXT</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>Most of the time this is</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row6_text_y%" placement="5" />
+				<text>the only wipe that you need.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row16_text_y%" placement="5" />
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%wipe_button_y%" />
+				<text>Advanced Wipe</text>
+				<actions>
+					<action function="set">partitionlisterror=0</action>
+					<action function="page">advancedwipe</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_has_data_media" var2="1" />
+				<placement x="%col2_x%" y="%wipe_button_y%" />
+				<text>Format Data</text>
+				<actions>
+					<action function="page">formatdata</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_has_data_media" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="%wipe_button_y%" />
+				<text>Wipe Encryption</text>
+				<actions>
+					<action function="set">tw_back=wipe</action>
+					<action function="set">tw_action=wipe</action>
+					<action function="set">tw_action_param=DATAMEDIA</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1=Wipe Encryption from Data?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Formatting Data...</action>
+					<action function="set">tw_complete_text1=Data Format Complete</action>
+					<action function="set">tw_slider_text=Swipe to Format Data</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Factory Reset</text>
+				<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="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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Wipe Menu</text>
+			</object>
+
+			<object type="partitionlist">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%wipe_list_height%" />
+				<text>Select Partitions to Wipe:</text>
+				<data name="tw_wipe_list" />
+				<listtype name="wipe" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Wipe</text>
+				<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" style="fillbutton">
+				<placement x="%col1_x%" y="%wipe_button_row1%" w="%button_fill_full_width%" h="%button_fill_half_height%" />
+				<text>Repair or Change File System</text>
+				<actions>
+					<action function="checkpartitionlist"></action>
+					<action function="page">checkpartitionlist</action>
+				</actions>
+			</object>
+
+			<object type="text">
+				<condition var1="partitionlisterror" var2="1" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%invalid_partition_y%" placement="5" />
+				<text>Invalid partition selection</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Format Data will wipe all of your apps,</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>backups, pictures, videos, media, and</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>removes encryption on internal storage.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>This cannot be undone. Press back to cancel.</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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</action>
+			</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Partition Options for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_is_present" op="!=" var2="0" />
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Present: Yes</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_is_present" op="=" var2="0" />
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Present: No</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_removable" op="!=" var2="0" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Removable: Yes</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_partition_removable" op="=" var2="0" />
+				<placement x="%col2_x%" y="%row3_text_y%" />
+				<text>Removable: No</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Size: %tw_partition_size%MB</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col2_x%" y="%row4_text_y%" />
+				<text>Used: %tw_partition_used%MB</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>Free: %tw_partition_free%MB</text>
+			</object>
+
+			<object type="text">
+				<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_resize" op="=" var2="1" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Resize</text>
+				<actions>
+					<action function="set">tw_back=partitionoptions</action>
+					<action function="set">tw_action=resize</action>
+					<action function="set">tw_action_param=%tw_partition_mount_point%</action>
+					<action function="set">tw_has_action2=1</action>
+					<action function="set">tw_action2=getpartitiondetails</action>
+					<action function="set">tw_text1=Resize %tw_partition_name%?</action>
+					<action function="set">tw_text2=</action>
+					<action function="set">tw_action_text1=Resizing...</action>
+					<action function="set">tw_complete_text1=Resize Complete</action>
+					<action function="set">tw_slider_text=Swipe to Resize</action>
+					<action function="page">confirm_action</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_partition_can_repair" op="=" var2="1" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>Repair</text>
+				<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">
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Change File Sys</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Change file system for: %tw_partition_name%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Mount Point: %tw_partition_mount_point%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Current file system: %tw_partition_file_system%</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>EXT2</text>
+				<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" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>EXT3</text>
+				<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" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>EXT4</text>
+				<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" />
+				<placement x="%col2_x%" y="%row3_y%" />
+				<text>F2FS</text>
+				<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" />
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>FAT</text>
+				<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" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>exFAT</text>
+				<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" style="fillbutton">
+				<placement x="%col1_x%" y="%row1_header_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<placement x="%listbox_x%" y="%backup_list_y%" w="%listbox_width%" h="%backup_list_height%" />
+				<text>Select Partitions to Back Up:</text>
+				<data name="tw_backup_list" />
+				<listtype name="backup" />
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col2_x%" y="%backup_button_row1%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<text>More...</text>
+				<action function="page">backupoptions</action>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%backup_button_row2%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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%" />
+				<text>Compression</text>
+				<data variable="tw_use_compression" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Back Up</text>
+				<action function="page">backup_run</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>
+		</page>
+
+		<page name="backupoptions">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>More Backup Options</text>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="0" />
+				</conditions>
+				<placement x="%col1_x%" y="%row6_text_y%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<text>No Encryption</text>
+				<actions>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<conditions>
+					<condition var1="tw_include_encrypted_backup" var2="1" />
+					<condition var1="tw_encrypt_backup" var2="1" />
+				</conditions>
+				<placement x="%col1_x%" y="%row6_text_y%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<text>Using Encryption</text>
+				<actions>
+					<action function="set">tw_password_not_match=0</action>
+					<action function="page">backupencryption</action>
+				</actions>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col2_x%" y="%row6_text_y%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<text>Refresh Sizes</text>
+				<actions>
+					<action function="refreshsizes"></action>
+					<action function="page">backupoptions</action>
+				</actions>
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%backup_button_row2%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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%" />
+				<text>Enable compression.</text>
+				<data variable="tw_use_compression" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Skip MD5 generation during backup.</text>
+				<data variable="tw_skip_md5_generate" />
+			</object>
+
+                        <object type="checkbox">
+                                <placement x="%col1_x%" y="%row4_text_y%" />
+                                <font resource="font" color="%text_color%" />
+                                <text>Disable Free Space Check.</text>
+                                <data variable="tw_disable_free_space" />
+                                <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"></action>
+			</object>
+
+			<object type="action">
+				<action function="page">backupname2</action>
+			</object>
+		</page>
+
+		<page name="backupname2">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<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" />
+				<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">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<font resource="font" color="%text_fail_color%" />
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Append Date</text>
+				<action function="appenddatetobackupname"></action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Encrypt your backup?</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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">
+				<condition var1="tw_password_not_match" var2="1" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Passwords Do Not Match</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Encrypt your backup?</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>%tw_file_progress%</text>
+			</object>
+
+			<object type="text">
+				<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="button" style="mediumbutton">
+				<placement x="%col_center_medium_x%" y="%row4_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="cancelbackup"></action>
+				</actions>
+			</object>
+
+			<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" />
+				<condition var1="tw_cancel_backup" var2="0" />
+				<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="action">
+				<condition var1="tw_operation_state" var2="1" />
+				<condition var1="tw_cancel_backup" var2="1" />
+				<actions>
+					<action function="set">tw_back=backup</action>
+					<action function="set">tw_complete_text1=Backup Cancelled</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" style="fillbutton">
+				<placement x="%col1_x%" y="%row1_header_y%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<placement x="%fileselector_x%" y="%row2_text_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>Select Package to Restore:</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Backup Encrypted</text>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">restore</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Delete</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% &amp;&amp; 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">
+				<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">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%restore_list_height%" />
+				<text>Restoring: %tw_restore_name%</text>
+				<data name="tw_restore_list" selectedlist="tw_restore_selected" />
+				<listtype name="restore" />
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%backup_button_row2%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<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" style="fillbutton">
+				<placement x="%col2_x%" y="%backup_button_row2%" w="%button_fill_main_width%" h="%button_fill_quarter_height%" />
+				<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% &amp;&amp; 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%" />
+				<text>Enable MD5 verification of backup.</text>
+				<data variable="tw_skip_md5_check" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Restore</text>
+				<action function="page">restore_run</action>
+			</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<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" />
+				<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% &amp;&amp; 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">
+				<condition var1="tw_fileexists" var2="1" />
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<font resource="font" color="%text_fail_color%" />
+				<text>A backup with that name already exists!</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>%tw_operation% %tw_partition%</text>
+			</object>
+
+			<object type="text">
+				<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">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%storage_list_height%" />
+				<text>Select Storage:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<data name="tw_storage_path" />
+				<listtype name="storage" />
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<text>OK</text>
+				<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">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%mount_list_height%" />
+				<text>Select Partitions to Mount:</text>
+				<listtype name="mount" />
+			</object>
+
+			<object type="button" style="fillbutton">
+				<placement x="%col1_x%" y="%mount_storage_row%" w="%button_fill_full_width%" h="%button_fill_quarter_height%" />
+				<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">
+				<condition var1="tw_has_usb_storage" var2="1" />
+				<placement x="%col1_x%" y="row4_y" />
+				<text>USB Storage</text>
+				<action function="page">usb_mount</action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<text>Enable MTP</text>
+				<action function="startmtp"></action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_has_mtp" var2="1" />
+					<condition var1="tw_mtp_enabled" var2="1" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<text>Disable MTP</text>
+				<action function="stopmtp"></action>
+			</object>
+
+			<object type="button">
+				<conditions>
+					<condition var1="tw_is_encrypted" var2="1" />
+					<condition var1="tw_is_decrypted" var2="0" />
+				</conditions>
+				<placement x="%col2_x%" y="row4_y" />
+				<text>Decrypt Data</text>
+				<action function="page">decrypt</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%system_ro_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_mount_system_ro" op="=" var2="0" />
+				<text>Only mount system read-only</text>
+				<image resource="checkbox_false" />
+				<action function="mountsystemtoggle">1</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%system_ro_y%" />
+				<font resource="font" color="%text_color%" />
+				<condition var1="tw_mount_system_ro" op="!=" var2="0" />
+				<text>Only mount system read-only</text>
+				<image resource="checkbox_true" />
+				<actions>
+					<action function="set">tw_lifetime_writes=2</action>
+					<action function="page">system_readonly_check</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">
+				<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">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<text>from your computer before unmounting!</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Unmount</text>
+				<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="system_readonly_check">
+			<object type="action">
+				<action function="checkpartitionlifetimewrites">/system</action>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_lifetime_writes" var2="1" />
+				<actions>
+					<action function="mountsystemtoggle">0</action>
+					<action function="page">mount</action>
+				</actions>
+			</object>
+
+			<object type="action">
+				<condition var1="tw_lifetime_writes" var2="0" />
+				<actions>
+					<action function="set">tw_back=mount</action>
+					<action function="page">system_readonly</action>
+				</actions>
+			</object>
+		</page>
+
+		<page name="reboot">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Reboot Menu</text>
+			</object>
+
+			<object type="button" style="rebootsystem">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>System</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_reboot_poweroff" var2="1" />
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Power Off</text>
+				<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">
+				<condition var1="tw_reboot_recovery" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Recovery</text>
+				<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">
+				<condition var1="tw_reboot_bootloader" var2="1" />
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Bootloader</text>
+				<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">
+				<condition var1="tw_download_mode" var2="1" />
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Download</text>
+				<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%" />
+				<text>Zip file signature verification.</text>
+				<data variable="tw_signed_zip_verify" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row1_text_y%" />
+				<text>Use rm -rf instead of formatting.</text>
+				<data variable="tw_rm_rf" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<text>Skip MD5 generation during backup.</text>
+				<data variable="tw_skip_md5_generate" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Enable MD5 verification of backup files.</text>
+				<data variable="tw_skip_md5_check" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Use 24-hour clock.</text>
+				<data variable="tw_military_time" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>Simulate actions for theme testing.</text>
+				<data variable="tw_simulate_actions" />
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_simulate_actions" var2="1" />
+				<placement x="%col1_x%" y="%row6_text_y%" />
+				<text>Simulate failure for actions.</text>
+				<data variable="tw_simulate_fail" />
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Time Zone</text>
+				<action function="page">timezone</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row3_y%" />
+				<text>Screen</text>
+				<action function="page">screen</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>Restore Defaults</text>
+				<action function="restoredefaultsettings"></action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Vibration</text>
+				<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" style="scrolllist">
+				<placement x="%listbox_x%" y="%row1_header_y%" w="%listbox_width%" h="%listbox_tz_height%" />
+				<text>Select Time Zone:</text>
+				<icon selected="radio_true" unselected="radio_false" />
+				<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,M3.2.0,M11.1.0</listitem>
+				<listitem name="(UTC -7) Mountain Time">MST7;MDT,M3.2.0,M11.1.0</listitem>
+				<listitem name="(UTC -6) Central Time">CST6;CDT,M3.2.0,M11.1.0</listitem>
+				<listitem name="(UTC -5) Eastern Time">EST5;EDT,M3.2.0,M11.1.0</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,M3.5.0,M10.5.0</listitem>
+				<listitem name="(UTC +1) Berlin, Brussels, Paris">CET-1;CEST,M3.5.0,M10.5.0</listitem>
+				<listitem name="(UTC +2) Athens, Istanbul, South Africa">WET-2;WET,M3.5.0,M10.5.0</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%" />
+				<text>Do you use daylight savings time (DST)?</text>
+				<data variable="tw_time_zone_guidst" />
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row_offset_text_y%" placement="5" />
+				<text>Offset (usually 0): %tw_time_zone_guioffset%</text>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col1_medium_x%" y="%row_offset_medium_y%" />
+				<text>None</text>
+				<action function="set">tw_time_zone_guioffset=0</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col2_medium_x%" y="%row_offset_medium_y%" />
+				<text>15</text>
+				<action function="set">tw_time_zone_guioffset=15</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col3_medium_x%" y="%row_offset_medium_y%" />
+				<text>30</text>
+				<action function="set">tw_time_zone_guioffset=30</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<placement x="%col4_medium_x%" y="%row_offset_medium_y%" />
+				<text>45</text>
+				<action function="set">tw_time_zone_guioffset=45</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%tz_set_y%" />
+				<text>Set Time Zone</text>
+				<action function="setguitimezone"></action>
+			</object>
+
+			<object type="text">
+				<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">
+				<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">
+				<conditions>
+					<condition var1="tw_screen_timeout_secs" op="!=" var2="0" />
+					<condition var1="tw_no_screen_timeout" op="!=" var2="1" />
+				</conditions>
+				<placement x="col1_x" y="%row4_text_y%" w="%slidervalue_w%" />
+				<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%" />
+				<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">
+				<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%" />
+				<text>Button Vibration:</text>
+				<data variable="tw_button_vibrate" min="0" max="300" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row5_text_y%" w="%slidervalue_w%" />
+				<text>Keyboard Vibration:</text>
+				<data variable="tw_keyboard_vibrate" min="0" max="300" />
+			</object>
+
+			<object type="slidervalue">
+				<placement x="col1_x" y="%row9_text_y%" w="%slidervalue_w%" />
+				<text>Action Vibration:</text>
+				<data variable="tw_action_vibrate" min="0" max="500" />
+			</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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Advanced</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>Copy Log to SD</text>
+				<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">
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Fix Permissions</text>
+				<action function="page">fixperms</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_allow_partition_sdcard" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Partition SD Card</text>
+				<action function="page">partsdcard</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>File Manager</text>
+				<action function="page">filemanagerlist</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>Terminal Command</text>
+				<action function="page">terminalfolder</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row3_y%" />
+				<text>Reload Theme</text>
+				<action function="reload"></action>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>ADB Sideload</text>
+				<action function="page">sideload</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_show_dumlock" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>HTC Dumlock</text>
+				<action function="page">htcdumlock</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_has_injecttwrp" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Re-Inject TWRP</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Partition SD Card</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_sdext_size-128</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sd_plus_x%" y="%row1_y%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_sdext_size+128</action>
+			</object>
+
+			<object type="text">
+				<placement x="%sdext_text_x%" y="%sdext_text_y%" />
+				<text>EXT Size: %tw_sdext_size%</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%sdswap_button_y%" />
+				<text></text>
+				<image resource="minus_button" />
+				<action function="addsubtract">tw_swap_size-32</action>
+			</object>
+
+			<object type="button">
+				<placement x="%sd_plus_x%" y="%sdswap_button_y%" />
+				<text></text>
+				<image resource="plus_button" />
+				<action function="addsubtract">tw_swap_size+32</action>
+			</object>
+
+			<object type="text">
+				<placement x="%sdswap_text_x%" y="%sdswap_text_y%" />
+				<text>Swap Size: %tw_swap_size%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%sdfilesystem_text_y%" />
+				<text>File system: %tw_sdpart_file_system%</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%sdfilesystem_button_y%" />
+				<font resource="font" color="%button_text_color%" />
+				<text>EXT3</text>
+				<action function="set">tw_sdpart_file_system=ext3</action>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_sdext_disable_ext4" var2="0" />
+				<placement x="%col2_x%" y="%sdfilesystem_button_y%" />
+				<text>EXT4</text>
+				<image resource="main_button" />
+				<action function="set">tw_sdpart_file_system=ext4</action>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row10_text_y%" />
+				<text>You will lose all files on your SD card!</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row11_text_y%" />
+				<text>This action cannot be undone!</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Partition</text>
+				<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="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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>HTC Dumlock</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col1_x%" y="%row1_y%" />
+				<text>Restore Original Boot</text>
+				<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">
+				<placement x="%col2_x%" y="%row1_y%" />
+				<text>Reflash Recovery</text>
+				<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">
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Install HTC Dumlock</text>
+				<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="%center_x%" y="%lock_y%" placement="4" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Unlock</text>
+				<action function="overlay"></action>
+			</object>
+		</page>
+
+		<page name="filemanagerlist">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>File Manager: Select a File or Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_file_location1%</text>
+				<filter 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" style="mediumbutton">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Select</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_type% Selected:</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="button">
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Copy File</text>
+				<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">
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col1_x%" y="%row2_y%" />
+				<text>Copy Folder</text>
+				<actions>
+					<action function="set">tw_filemanager_command=cd &quot;%tw_file_location1%&quot; &amp;&amp; cd .. &amp;&amp; cp -R</action>
+					<action function="set">tw_fm_text1=Copying</action>
+					<action function="page">choosedestinationfolder</action>
+				</actions>
+			</object>
+
+			<object type="button">
+				<placement x="%col2_x%" y="%row2_y%" />
+				<text>Move</text>
+				<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">
+				<placement x="%col1_x%" y="%row3_y%" />
+				<text>chmod 755</text>
+				<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">
+				<placement x="%col2_x%" y="%row3_y%" />
+				<text>chmod</text>
+				<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">
+				<placement x="%col1_x%" y="%row4_y%" />
+				<text>Delete</text>
+				<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">
+				<condition var1="tw_fm_isfolder" var2="0" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Rename File</text>
+				<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">
+				<condition var1="tw_fm_isfolder" var2="1" />
+				<placement x="%col2_x%" y="%row4_y%" />
+				<text>Rename Folder</text>
+				<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; &amp;&amp; cd .. &amp;&amp; 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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Browse to Destination &amp; Press Select</text>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_file_location2%</text>
+				<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" style="mediumbutton">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Select</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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" />
+				<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">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5"/>
+				<text>%tw_fm_text1%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>%tw_filename1%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>%tw_fm_text2%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>%tw_fm_text3%</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row10_text_y%" placement="5"/>
+				<text>Press back button to cancel.</text>
+			</object>
+
+			<object type="slider">
+				<action function="page">filemanageracction</action>
+			</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">
+				<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="action">
+				<condition var1="tw_crypto_pwtype" var2="2" />
+				<action function="page">decrypt_pattern</action>
+			</object>
+
+			<object type="text">
+				<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" />
+				<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">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Password Failed, Please Try Again</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row2_y%" />
+				<text>Cancel</text>
+				<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="decrypt_pattern">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Please Enter Your Pattern</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Pattern Failed, Please Try Again</text>
+			</object>
+
+			<object type="patternpassword">
+				<placement x="%pattern_x%" y="%pattern_y%" w="%pattern_width%" h="%pattern_width%" />
+				<dot color="%pattern_dot_color%" activecolor="%pattern_dot_color_active%" radius="%pattern_dot_radius%" />
+				<line color="%pattern_line_color%" width="%pattern_line_width%" />
+				<data name="tw_crypto_password"/>
+				<action function="page">trydecrypt</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
+		<page name="trydecrypt">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<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">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Browse to Starting Folder</text>
+			</object>
+
+			<object type="fileselector">
+				<placement x="%fileselector_x%" y="%row1_y%" w="%fileselector_width%" h="%fileselector_install_height%" />
+				<text>%tw_terminal_location%</text>
+				<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" style="mediumbutton">
+				<placement x="%filemanager_select_x%" y="%filemanager_select_y%" />
+				<text>Select</text>
+				<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%" />
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%terminal_text_y%" placement="0" />
+				<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" />
+				<text>%tw_terminal_command%</text>
+				<font resource="fixed" color="%text_color%" />
+				<data name="tw_terminal_command" />
+				<restrict minlen="1" />
+				<action function="terminalcommand">%tw_terminal_command%</action>
+			</object>
+
+			<object type="button" style="mediumbutton">
+				<condition var1="tw_terminal_state" var2="1" />
+				<placement x="%filemanager_select_x%" y="%terminal_button_y%" />
+				<text>KILL</text>
+				<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">
+				<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%" />
+				<text>Wipe Dalvik Cache.</text>
+				<data variable="tw_wipe_dalvik" />
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Wipe Cache.</text>
+				<data variable="tw_wipe_cache" />
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Start Sideload</text>
+				<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="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="fixperms">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Fix Permissions</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row2_text_y%" />
+				<text>Note: Fixing permissions is rarely needed.</text>
+			</object>
+
+			<object type="checkbox">
+				<placement x="%col1_x%" y="%row3_text_y%" />
+				<text>Also fix SELinux contexts</text>
+				<data variable="tw_fixperms_restorecon" />
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row4_text_y%" />
+				<text>Fixing SELinux contexts may cause</text>
+			</object>
+
+			<object type="text">
+				<placement x="%col1_x%" y="%row5_text_y%" />
+				<text>your device to not boot properly.</text>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Fix Permissions</text>
+				<actions>
+					<action function="set">tw_back=advanced</action>
+					<action function="set">tw_action=fixpermissions</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">action_page</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="installsu">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5"/>
+				<text>Install SuperSU?</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5"/>
+				<text>Your device does not appear to be rooted.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5"/>
+				<text>Install SuperSU now?</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5"/>
+				<text>This will root your device.</text>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row3_y%" />
+				<text>Do Not Install</text>
+				<action function="set">tw_page_done=1</action>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Install</text>
+				<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>
+		</page>
+
+		<page name="system_readonly">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Keep System Read Only?</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>TWRP can leave your system partition unmodified</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>to make it easier for you to take official updates.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row3_text_y%" placement="5" />
+				<text>TWRP will be unable to prevent the stock ROM from</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row4_text_y%" placement="5" />
+				<text>replacing TWRP and will not offer to root your device.</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row5_text_y%" placement="5" />
+				<text>Installing zips or performing adb operations may still</text>
+			</object>
+
+			<object type="text">
+				<placement x="%center_x%" y="%row6_text_y%" placement="5" />
+				<text>modify the system partition.</text>
+			</object>
+
+			<object type="checkbox">
+				<condition var1="tw_is_encrypted" var2="0" />
+				<placement x="%col1_x%" y="%row7_text_y%" />
+				<text>Never show this screen during boot again</text>
+				<data variable="tw_never_show_system_ro_page" />
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row9_text_y%" />
+				<text>Keep Read Only</text>
+				<actions>
+					<action function="mountsystemtoggle">1</action>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">%tw_back%</action>
+				</actions>
+			</object>
+
+			<object type="slider">
+				<text>Swipe to Allow Modifications</text>
+				<actions>
+					<action function="mountsystemtoggle">0</action>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">%tw_back%</action>
+				</actions>
+			</object>
+		</page>
+	</pages>
+</recovery>
diff --git a/gui/fileselector.cpp b/gui/fileselector.cpp
new file mode 100644
index 0000000..a97ff34
--- /dev/null
+++ b/gui/fileselector.cpp
@@ -0,0 +1,382 @@
+/*
+	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 <string.h>
+#include <sys/stat.h>
+#include <dirent.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)"
+
+int GUIFileSelector::mSortOrder = 0;
+
+GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIScrollList(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mFolderIcon = mFileIcon = NULL;
+	mShowFolders = mShowFiles = mShowNavFolders = 1;
+	mUpdate = 0;
+	mPathVar = "cwd";
+	updateFileList = false;
+
+	// Load filter for filtering files (e.g. *.zip for only zips)
+	child = FindNode(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 = FindNode(node, "path");
+	if (child) {
+		attr = child->first_attribute("name");
+		if (attr)
+			mPathVar = attr->value();
+		attr = child->first_attribute("default");
+		if (attr) {
+			mPathDefault = attr->value();
+			DataManager::SetValue(mPathVar, attr->value());
+		}
+	}
+
+	// Handle the result variable
+	child = FindNode(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 = FindNode(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 = FindNode(node, "selection");
+	if (child && (attr = child->first_attribute("name")))
+		mSelection = attr->value();
+	else
+		mSelection = "0";
+
+	// Get folder and file icons if present
+	child = FindNode(node, "icon");
+	if (child) {
+		mFolderIcon = LoadAttrImage(child, "folder");
+		mFileIcon = LoadAttrImage(child, "file");
+	}
+	int iconWidth = std::max(mFolderIcon->GetWidth(), mFileIcon->GetWidth());
+	int iconHeight = std::max(mFolderIcon->GetHeight(), mFileIcon->GetHeight());
+	SetMaxIconSize(iconWidth, iconHeight);
+
+	// Fetch the file/folder list
+	std::string value;
+	DataManager::GetValue(mPathVar, value);
+	GetFileList(value);
+}
+
+GUIFileSelector::~GUIFileSelector()
+{
+}
+
+int GUIFileSelector::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	GUIScrollList::Update();
+
+	// Update the file list if needed
+	if (updateFileList) {
+		string value;
+		DataManager::GetValue(mPathVar, value);
+		if (GetFileList(value) == 0) {
+			updateFileList = false;
+			mUpdate = 1;
+		} else
+			return 0;
+	}
+
+	if (mUpdate) {
+		mUpdate = 0;
+		if (Render() == 0)
+			return 2;
+	}
+	return 0;
+}
+
+int GUIFileSelector::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIScrollList::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 (varName == mPathVar || varName == mSortVariable) {
+		if (varName == mSortVariable) {
+			DataManager::GetValue(mSortVariable, mSortOrder);
+		} else {
+			// Reset the list to the top
+			SetVisibleListLocation(0);
+			if (value.empty())
+				DataManager::SetValue(mPathVar, mPathDefault);
+		}
+		updateFileList = true;
+		mUpdate = 1;
+		return 0;
+	}
+	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);
+	}
+	return 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)
+{
+	GUIScrollList::SetPageFocus(inFocus);
+	if (inFocus) {
+		std::string value;
+		DataManager::GetValue(mPathVar, value);
+		if (value.empty())
+			DataManager::SetValue(mPathVar, mPathDefault);
+		updateFileList = true;
+		mUpdate = 1;
+	}
+}
+
+size_t GUIFileSelector::GetItemCount()
+{
+	size_t folderSize = mShowFolders ? mFolderList.size() : 0;
+	size_t fileSize = mShowFiles ? mFileList.size() : 0;
+	return folderSize + fileSize;
+}
+
+void GUIFileSelector::RenderItem(size_t itemindex, int yPos, bool selected)
+{
+	size_t folderSize = mShowFolders ? mFolderList.size() : 0;
+	size_t fileSize = mShowFiles ? mFileList.size() : 0;
+
+	ImageResource* icon;
+	std::string text;
+
+	if (itemindex < folderSize) {
+		text = mFolderList.at(itemindex).fileName;
+		icon = mFolderIcon;
+	} else {
+		text = mFileList.at(itemindex - folderSize).fileName;
+		icon = mFileIcon;
+	}
+
+	RenderStdItem(yPos, selected, icon, text.c_str());
+}
+
+void GUIFileSelector::NotifySelect(size_t item_selected)
+{
+	size_t folderSize = mShowFolders ? mFolderList.size() : 0;
+	size_t fileSize = mShowFiles ? mFileList.size() : 0;
+
+	if (item_selected < folderSize + fileSize) {
+		// We've selected an item!
+		std::string str;
+		if (item_selected < folderSize) {
+			std::string cwd;
+
+			str = mFolderList.at(item_selected).fileName;
+			if (mSelection != "0")
+				DataManager::SetValue(mSelection, str);
+			DataManager::GetValue(mPathVar, cwd);
+
+			// Ignore requests to do nothing
+			if (str == ".")	 return;
+			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) {
+				// nav folders and files are disabled, this is probably the restore list and we need to save chosen location to mVariable instead of mPathVar
+				DataManager::SetValue(mVariable, cwd);
+			} else {
+				// We are changing paths, so we need to set mPathVar
+				DataManager::SetValue(mPathVar, cwd);
+			}
+		} else if (!mVariable.empty()) {
+			str = mFileList.at(item_selected - folderSize).fileName;
+			if (mSelection != "0")
+				DataManager::SetValue(mSelection, str);
+
+			std::string cwd;
+			DataManager::GetValue(mPathVar, cwd);
+			if (cwd != "/")
+				cwd += "/";
+			DataManager::SetValue(mVariable, cwd + str);
+		}
+	}
+	mUpdate = 1;
+}
diff --git a/gui/fill.cpp b/gui/fill.cpp
new file mode 100644
index 0000000..b315cd0
--- /dev/null
+++ b/gui/fill.cpp
@@ -0,0 +1,52 @@
+// 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)
+{
+	bool has_color = false;
+	mColor = LoadAttrColor(node, "color", &has_color);
+	if (!has_color) {
+		LOGERR("No color specified for fill\n");
+		return;
+	}
+
+	// Load the placement
+	LoadPlacement(FindNode(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..ebd7053
--- /dev/null
+++ b/gui/gui.cpp
@@ -0,0 +1,1051 @@
+/*
+        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"
+#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"
+#include "blanktimer.hpp"
+#include "../tw_atomic.hpp"
+
+// Enable to print render time of each frame to the log file
+//#define PRINT_RENDER_TIME 1
+
+#ifdef _EVENT_LOGGING
+#define LOGEVENT(...) LOGERR(__VA_ARGS__)
+#else
+#define LOGEVENT(...) do {} while (0)
+#endif
+
+const static int CURTAIN_FADE = 32;
+
+using namespace rapidxml;
+
+// Global values
+static gr_surface gCurtain = NULL;
+static int gGuiInitialized = 0;
+static TWAtomicInt gGuiConsoleRunning;
+static TWAtomicInt gGuiConsoleTerminate;
+static TWAtomicInt gForceRender;
+const int gNoAnimation = 1;
+blanktimer blankTimer;
+int ors_read_fd = -1;
+static float scale_theme_w = 1;
+static float scale_theme_h = 1;
+
+// 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), TW_X_OFFSET, TW_Y_OFFSET);
+	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
+}
+
+class InputHandler
+{
+public:
+	void init()
+	{
+		// these might be read from DataManager in the future
+		touch_hold_ms = 500;
+		touch_repeat_ms = 100;
+		key_hold_ms = 500;
+		key_repeat_ms = 100;
+		touch_status = TS_NONE;
+		key_status = KS_NONE;
+		state = AS_NO_ACTION;
+		x = y = 0;
+
+#ifndef TW_NO_SCREEN_TIMEOUT
+		{
+			string seconds;
+			DataManager::GetValue("tw_screen_timeout_secs", seconds);
+			blankTimer.setTime(atoi(seconds.c_str()));
+			blankTimer.resetTimerAndUnblank();
+		}
+#else
+		LOGINFO("Skipping screen timeout: TW_NO_SCREEN_TIMEOUT is set\n");
+#endif
+	}
+
+	// process input events. returns true if any event was received.
+	bool processInput(int timeout_ms);
+
+	void handleDrag();
+
+private:
+	// timeouts for touch/key hold and repeat
+	int touch_hold_ms;
+	int touch_repeat_ms;
+	int key_hold_ms;
+	int key_repeat_ms;
+
+	enum touch_status_enum {
+		TS_NONE = 0,
+		TS_TOUCH_AND_HOLD = 1,
+		TS_TOUCH_REPEAT = 2,
+	};
+
+	enum key_status_enum {
+		KS_NONE = 0,
+		KS_KEY_PRESSED = 1,
+		KS_KEY_REPEAT = 2,
+	};
+
+	enum action_state_enum {
+		AS_IN_ACTION_AREA = 0, // we've touched a spot with an action
+		AS_NO_ACTION = 1,    // we've touched in an empty area (no action) and ignore remaining events until touch release
+	};
+	touch_status_enum touch_status;
+	key_status_enum key_status;
+	action_state_enum state;
+	int x, y; // x and y coordinates of last touch
+	struct timeval touchStart; // used to track time for long press / key repeat
+
+	void processHoldAndRepeat();
+	void process_EV_REL(input_event& ev);
+	void process_EV_ABS(input_event& ev);
+	void process_EV_KEY(input_event& ev);
+
+	void doTouchStart();
+};
+
+InputHandler input_handler;
+
+
+bool InputHandler::processInput(int timeout_ms)
+{
+	input_event ev;
+	int ret = ev_get(&ev, timeout_ms);
+
+	if (ret < 0)
+	{
+		// This path means that we did not get any new touch data, but
+		// we do not get new touch data if you press and hold on either
+		// the screen or on a keyboard key or mouse button
+		if (touch_status || key_status)
+			processHoldAndRepeat();
+		return (ret != -2);  // -2 means no more events in the queue
+	}
+
+	switch (ev.type)
+	{
+	case EV_ABS:
+		process_EV_ABS(ev);
+		break;
+
+	case EV_REL:
+		process_EV_REL(ev);
+		break;
+
+	case EV_KEY:
+		process_EV_KEY(ev);
+		break;
+	}
+
+	blankTimer.resetTimerAndUnblank();
+	return true;  // we got an event, so there might be more in the queue
+}
+
+void InputHandler::processHoldAndRepeat()
+{
+	HardwareKeyboard *kb = PageManager::GetHardwareKeyboard();
+
+	// touch and key repeat section
+	struct timeval curTime;
+	gettimeofday(&curTime, NULL);
+	long seconds = curTime.tv_sec - touchStart.tv_sec;
+	long useconds = curTime.tv_usec - touchStart.tv_usec;
+	long mtime = ((seconds) * 1000 + useconds / 1000.0) + 0.5;
+
+	if (touch_status == TS_TOUCH_AND_HOLD && mtime > touch_hold_ms)
+	{
+		touch_status = TS_TOUCH_REPEAT;
+		gettimeofday(&touchStart, NULL);
+		LOGEVENT("TOUCH_HOLD: %d,%d\n", x, y);
+		PageManager::NotifyTouch(TOUCH_HOLD, x, y);
+	}
+	else if (touch_status == TS_TOUCH_REPEAT && mtime > touch_repeat_ms)
+	{
+		LOGEVENT("TOUCH_REPEAT: %d,%d\n", x, y);
+		gettimeofday(&touchStart, NULL);
+		PageManager::NotifyTouch(TOUCH_REPEAT, x, y);
+	}
+	else if (key_status == KS_KEY_PRESSED && mtime > key_hold_ms)
+	{
+		LOGEVENT("KEY_HOLD: %d,%d\n", x, y);
+		gettimeofday(&touchStart, NULL);
+		key_status = KS_KEY_REPEAT;
+		kb->KeyRepeat();
+	}
+	else if (key_status == KS_KEY_REPEAT && mtime > key_repeat_ms)
+	{
+		LOGEVENT("KEY_REPEAT: %d,%d\n", x, y);
+		gettimeofday(&touchStart, NULL);
+		kb->KeyRepeat();
+	}
+}
+
+void InputHandler::doTouchStart()
+{
+	LOGEVENT("TOUCH_START: %d,%d\n", x, y);
+	if (PageManager::NotifyTouch(TOUCH_START, x, y) > 0)
+		state = AS_NO_ACTION;
+	else
+		state = AS_IN_ACTION_AREA;
+	touch_status = TS_TOUCH_AND_HOLD;
+	gettimeofday(&touchStart, NULL);
+}
+
+void InputHandler::process_EV_ABS(input_event& ev)
+{
+	x = ev.value >> 16;
+	y = ev.value & 0xFFFF;
+
+	if (ev.code == 0)
+	{
+#ifndef TW_USE_KEY_CODE_TOUCH_SYNC
+		if (state == AS_IN_ACTION_AREA)
+		{
+			LOGEVENT("TOUCH_RELEASE: %d,%d\n", x, y);
+			PageManager::NotifyTouch(TOUCH_RELEASE, x, y);
+		}
+		touch_status = TS_NONE;
+#endif
+	}
+	else
+	{
+		if (!touch_status)
+		{
+#ifndef TW_USE_KEY_CODE_TOUCH_SYNC
+			doTouchStart();
+#endif
+		}
+		else
+		{
+			if (state == AS_IN_ACTION_AREA)
+			{
+				LOGEVENT("TOUCH_DRAG: %d,%d\n", x, y);
+			}
+		}
+	}
+}
+
+void InputHandler::process_EV_KEY(input_event& ev)
+{
+	HardwareKeyboard *kb = PageManager::GetHardwareKeyboard();
+
+	// Handle key-press here
+	LOGEVENT("TOUCH_KEY: %d\n", ev.code);
+	// Left mouse button is treated as a touch
+	if(ev.code == BTN_LEFT)
+	{
+		MouseCursor *cursor = PageManager::GetMouseCursor();
+		if(ev.value == 1)
+		{
+			cursor->GetPos(x, y);
+			doTouchStart();
+		}
+		else if(touch_status)
+		{
+			// Left mouse button was previously pressed and now is
+			// being released so send a TOUCH_RELEASE
+			if (state == AS_IN_ACTION_AREA)
+			{
+				cursor->GetPos(x, y);
+
+				LOGEVENT("Mouse TOUCH_RELEASE: %d,%d\n", x, y);
+				PageManager::NotifyTouch(TOUCH_RELEASE, x, y);
+			}
+			touch_status = TS_NONE;
+		}
+	}
+	// 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
+#ifdef TW_USE_KEY_CODE_TOUCH_SYNC
+		if (ev.code == TW_USE_KEY_CODE_TOUCH_SYNC) {
+			LOGEVENT("key code %i key press == touch start %i %i\n", TW_USE_KEY_CODE_TOUCH_SYNC, x, y);
+			doTouchStart();
+			return;
+		}
+#endif
+		if (kb->KeyDown(ev.code)) {
+			// Key repeat is enabled for this key
+			key_status = KS_KEY_PRESSED;
+			touch_status = TS_NONE;
+			gettimeofday(&touchStart, NULL);
+		} else {
+			key_status = KS_NONE;
+			touch_status = TS_NONE;
+		}
+	} else {
+		// This is a key release
+		kb->KeyUp(ev.code);
+		key_status = KS_NONE;
+		touch_status = TS_NONE;
+#ifdef TW_USE_KEY_CODE_TOUCH_SYNC
+		if (ev.code == TW_USE_KEY_CODE_TOUCH_SYNC) {
+			LOGEVENT("key code %i key release == touch release %i %i\n", TW_USE_KEY_CODE_TOUCH_SYNC, x, y);
+			PageManager::NotifyTouch(TOUCH_RELEASE, x, y);
+		}
+#endif
+	}
+}
+
+void InputHandler::process_EV_REL(input_event& ev)
+{
+	// Mouse movement
+	MouseCursor *cursor = PageManager::GetMouseCursor();
+	LOGEVENT("EV_REL %d %d\n", ev.code, ev.value);
+	if(ev.code == REL_X)
+		cursor->Move(ev.value, 0);
+	else if(ev.code == REL_Y)
+		cursor->Move(0, ev.value);
+
+	if(touch_status) {
+		cursor->GetPos(x, y);
+		LOGEVENT("Mouse TOUCH_DRAG: %d, %d\n", x, y);
+		key_status = KS_NONE;
+	}
+}
+
+void InputHandler::handleDrag()
+{
+	// This allows us to only send one NotifyTouch event per render
+	// cycle to reduce overhead and perceived input latency.
+	static int prevx = 0, prevy = 0; // these track where the last drag notice was so that we don't send duplicate drag notices
+	if (touch_status && (x != prevx || y != prevy)) {
+		prevx = x;
+		prevy = y;
+		if (PageManager::NotifyTouch(TOUCH_DRAG, x, y) > 0)
+			state = AS_NO_ACTION;
+		else
+			state = AS_IN_ACTION_AREA;
+	}
+}
+
+static void setup_ors_command()
+{
+	ors_read_fd = -1;
+
+	unlink(ORS_INPUT_FILE);
+	if (mkfifo(ORS_INPUT_FILE, 06660) != 0) {
+		LOGINFO("Unable to mkfifo %s\n", ORS_INPUT_FILE);
+		return;
+	}
+	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;
+	}
+
+	ors_read_fd = open(ORS_INPUT_FILE, O_RDONLY | O_NONBLOCK);
+	if (ors_read_fd < 0) {
+		LOGINFO("Unable to open %s\n", ORS_INPUT_FILE);
+		unlink(ORS_INPUT_FILE);
+		unlink(ORS_OUTPUT_FILE);
+	}
+}
+
+static void ors_command_read()
+{
+	FILE* orsout;
+	char command[1024], result[512];
+	int set_page_done = 0, read_ret = 0;
+
+	if ((read_ret = read(ors_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(ors_read_fd);
+			ors_read_fd = -1;
+			LOGINFO("Unable to fopen %s\n", ORS_OUTPUT_FILE);
+			unlink(ORS_INPUT_FILE);
+			unlink(ORS_OUTPUT_FILE);
+			return;
+		}
+		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 (strlen(command) > 9 && strncmp(command, "decrypt", 7) == 0) {
+					char* pass = command + 8;
+					gui_print("Attempting to decrypt data partition via command line.\n");
+					if (PartitionManager.Decrypt_Device(pass) == 0) {
+						set_page_done = 1;
+					}
+				} else if (OpenRecoveryScript::Insert_ORS_Command(command)) {
+					OpenRecoveryScript::run_script_file();
+				}
+				gui_set_FILE(NULL);
+				gGuiConsoleTerminate.set_value(1);
+			}
+		}
+		fclose(orsout);
+		LOGINFO("Done reading ORS command from command line\n");
+		if (set_page_done) {
+			DataManager::SetValue("tw_page_done", 1);
+		} else {
+			// The select function will return ready to read and the
+			// read function will return errno 19 no such device unless
+			// we set everything up all over again.
+			close(ors_read_fd);
+			setup_ors_command();
+		}
+	} else {
+		LOGINFO("ORS command line read returned an error: %i, %i, %s\n", read_ret, errno, strerror(errno));
+	}
+	return;
+}
+
+// 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(int input_timeout_ms)
+{
+	static timespec lastCall;
+	static int initialized = 0;
+
+	if (!initialized)
+	{
+		clock_gettime(CLOCK_MONOTONIC, &lastCall);
+		initialized = 1;
+		return;
+	}
+
+	do
+	{
+		bool got_event = input_handler.processInput(input_timeout_ms); // get inputs but don't send drag notices
+		timespec curTime;
+		clock_gettime(CLOCK_MONOTONIC, &curTime);
+
+		timespec diff = TWFunc::timespec_diff(lastCall, curTime);
+
+		// This is really 2 or 30 times per second
+		// As long as we get events, increase the timeout so we can catch up with input
+		long timeout = got_event ? 500000000 : 33333333;
+
+		if (diff.tv_sec || diff.tv_nsec > timeout)
+		{
+			// int32_t input_time = TWFunc::timespec_diff_ms(lastCall, curTime);
+			// LOGINFO("loopTimer(): %u ms, count: %u\n", input_time, count);
+
+			lastCall = curTime;
+			input_handler.handleDrag(); // send only drag notices if needed
+			return;
+		}
+
+		// We need to sleep some period time microseconds
+		//unsigned int sleepTime = 33333 -(diff.tv_nsec / 1000);
+		//usleep(sleepTime); // removed so we can scan for input
+		input_timeout_ms = 0;
+	} while (1);
+}
+
+static int runPages(const char *page_name, const int stop_on_page_done)
+{
+	DataManager::SetValue("tw_page_done", 0);
+	DataManager::SetValue("tw_gui_done", 0);
+
+	if (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);
+
+#ifndef TW_OEM_BUILD
+	struct timeval timeout;
+	fd_set fdset;
+	int has_data = 0;
+#endif
+
+	int input_timeout_ms = 0;
+	int idle_frames = 0;
+
+	for (;;)
+	{
+		loopTimer(input_timeout_ms);
+#ifndef TW_OEM_BUILD
+		if (ors_read_fd > 0) {
+			FD_ZERO(&fdset);
+			FD_SET(ors_read_fd, &fdset);
+			timeout.tv_sec = 0;
+			timeout.tv_usec = 1;
+			has_data = select(ors_read_fd+1, &fdset, NULL, NULL, &timeout);
+			if (has_data > 0) {
+				ors_command_read();
+			}
+		}
+#endif
+
+		if (gGuiConsoleRunning.get_value()) {
+			continue;
+		}
+
+		if (!gForceRender.get_value())
+		{
+			int ret = PageManager::Update();
+			if (ret == 0)
+				++idle_frames;
+			else
+				idle_frames = 0;
+			// due to possible animation objects, we need to delay activating the input timeout
+			input_timeout_ms = idle_frames > 15 ? 1000 : 0;
+
+#ifndef PRINT_RENDER_TIME
+			if (ret > 1)
+				PageManager::Render();
+
+			if (ret > 0)
+				flip();
+#else
+			if (ret > 1)
+			{
+				timespec start, end;
+				int32_t render_t, flip_t;
+				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 > 0)
+				flip();
+#endif
+		}
+		else
+		{
+			gForceRender.set_value(0);
+			PageManager::Render();
+			flip();
+			input_timeout_ms = 0;
+		}
+
+		blankTimer.checkForTimeout();
+		if (stop_on_page_done && DataManager::GetIntValue("tw_page_done") != 0)
+		{
+			gui_changePage("main");
+			break;
+		}
+		if (DataManager::GetIntValue("tw_gui_done") != 0)
+			break;
+	}
+	if (ors_read_fd > 0)
+		close(ors_read_fd);
+	ors_read_fd = -1;
+	gGuiRunning = 0;
+	return 0;
+}
+
+int gui_forceRender(void)
+{
+	gForceRender.set_value(1);
+	return 0;
+}
+
+int gui_changePage(std::string newPage)
+{
+	LOGINFO("Set page: '%s'\n", newPage.c_str());
+	PageManager::ChangePage(newPage);
+	gForceRender.set_value(1);
+	return 0;
+}
+
+int gui_changeOverlay(std::string overlay)
+{
+	LOGINFO("Set overlay: '%s'\n", overlay.c_str());
+	PageManager::ChangeOverlay(overlay);
+	gForceRender.set_value(1);
+	return 0;
+}
+
+int gui_changePackage(std::string newPackage)
+{
+	PageManager::SelectPackage(newPackage);
+	gForceRender.set_value(1);
+	return 0;
+}
+
+std::string gui_parse_text(std::string str)
+{
+	// This function parses text for DataManager values encompassed by %value% in the XML
+	// and string resources (%@resource_name%)
+	size_t pos = 0;
+
+	while (1)
+	{
+		size_t next = str.find('%', pos);
+		if (next == std::string::npos)
+			return str;
+
+		size_t 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 (var.size() > 0 && var[0] == '@') {
+				// this is a string resource ("%@string_name%")
+				value = PageManager::GetResources()->FindString(var.substr(1));
+				str.insert(next, value);
+			}
+			else if (DataManager::GetValue(var, value) == 0)
+				str.insert(next, value);
+		}
+
+		pos = next + 1;
+	}
+}
+
+extern "C" int gui_init(void)
+{
+	gr_init();
+	std::string curtain_path = TWRES "images/curtain.jpg";
+	gr_surface source_Surface = NULL;
+
+	if (res_create_surface(curtain_path.c_str(), &source_Surface))
+	{
+		printf("Unable to locate '%s'\nDid you set a TW_THEME in your config files?\n", curtain_path.c_str());
+		return -1;
+	}
+	if (gr_get_width(source_Surface) != gr_fb_width() || gr_get_height(source_Surface) != gr_fb_height()) {
+		// We need to scale the curtain to fit the screen
+		float scale_w = (float)gr_fb_width() / (float)gr_get_width(source_Surface);
+		float scale_h = (float)gr_fb_height() / (float)gr_get_height(source_Surface);
+		if (res_scale_surface(source_Surface, &gCurtain, scale_w, scale_h)) {
+			LOGINFO("Failed to scale curtain\n");
+			gCurtain = source_Surface;
+		} else {
+			LOGINFO("Scaling the curtain width %fx and height %fx\n", scale_w, scale_h);
+		}
+	} else {
+		gCurtain = source_Surface;
+	}
+
+	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", TWRES "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", TWRES "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_loadCustomResources(void)
+{
+#ifndef TW_OEM_BUILD
+	if (!PartitionManager.Mount_Settings_Storage(false)) {
+		LOGERR("Unable to mount settings storage during GUI startup.\n");
+		return -1;
+	}
+
+	std::string theme_path = DataManager::GetSettingsStoragePath();
+	theme_path += "/TWRP/theme/ui.zip";
+	// Check for a custom theme
+	if (TWFunc::Path_Exists(theme_path)) {
+		// There is a custom theme, try to load it
+		if (PageManager::ReloadPackage("TWRP", theme_path)) {
+			// Custom theme failed to load, try to load stock theme
+			if (PageManager::ReloadPackage("TWRP", TWRES "ui.xml")) {
+				LOGERR("Failed to load base packages.\n");
+				goto error;
+			}
+		}
+	}
+	// Set the default package
+	PageManager::SelectPackage("TWRP");
+#endif
+	return 0;
+
+error:
+	LOGERR("An internal error has occurred: unable to load theme.\n");
+	gGuiInitialized = 0;
+	return -1;
+}
+
+extern "C" int gui_start(void)
+{
+	return gui_startPage(NULL, 1, 0);
+}
+
+extern "C" int gui_startPage(const char *page_name, const int allow_commands, int stop_on_page_done)
+{
+	if (!gGuiInitialized)
+		return -1;
+
+	gGuiConsoleTerminate.set_value(1);
+
+	while (gGuiConsoleRunning.get_value())
+		usleep(10000);
+
+	// Set the default package
+	PageManager::SelectPackage("TWRP");
+
+	input_handler.init();
+#ifndef TW_OEM_BUILD
+	if (allow_commands)
+	{
+		if (ors_read_fd < 0)
+			setup_ors_command();
+	} else {
+		if (ors_read_fd >= 0) {
+			close(ors_read_fd);
+			ors_read_fd = -1;
+		}
+	}
+#endif
+	return runPages(page_name, stop_on_page_done);
+}
+
+static void * console_thread(void *cookie)
+{
+	PageManager::SwitchToConsole();
+
+	while (!gGuiConsoleTerminate.get_value())
+	{
+		loopTimer(0);
+
+		if (!gForceRender.get_value())
+		{
+			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
+		{
+			gForceRender.set_value(0);
+			PageManager::Render();
+			flip();
+		}
+	}
+	gGuiConsoleRunning.set_value(0);
+	gForceRender.set_value(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.set_value(0);
+
+	if (gGuiConsoleRunning.get_value())
+		return 0;
+
+	gGuiConsoleRunning.set_value(1);
+
+	// Start by spinning off an input handler.
+	pthread_t t;
+	pthread_create(&t, NULL, console_thread, NULL);
+
+	return 0;
+}
+
+extern "C" void set_scale_values(float w, float h)
+{
+	scale_theme_w = w;
+	scale_theme_h = h;
+}
+
+extern "C" int scale_theme_x(int initial_x)
+{
+	if (scale_theme_w != 1) {
+		int scaled = (float)initial_x * scale_theme_w;
+		if (scaled == 0 && initial_x > 0)
+			return 1;
+		return scaled;
+	}
+	return initial_x;
+}
+
+extern "C" int scale_theme_y(int initial_y)
+{
+	if (scale_theme_h != 1) {
+		int scaled = (float)initial_y * scale_theme_h;
+		if (scaled == 0 && initial_y > 0)
+			return 1;
+		return scaled;
+	}
+	return initial_y;
+}
+
+extern "C" int scale_theme_min(int initial_value)
+{
+	if (scale_theme_w != 1 || scale_theme_h != 1) {
+		if (scale_theme_w < scale_theme_h)
+			return scale_theme_x(initial_value);
+		else
+			return scale_theme_y(initial_value);
+	}
+	return initial_value;
+}
+
+extern "C" float get_scale_w()
+{
+	return scale_theme_w;
+}
+
+extern "C" float get_scale_h()
+{
+	return scale_theme_h;
+}
diff --git a/gui/gui.h b/gui/gui.h
new file mode 100644
index 0000000..f6f0483
--- /dev/null
+++ b/gui/gui.h
@@ -0,0 +1,42 @@
+/*
+        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_loadCustomResources();
+int gui_start();
+int gui_startPage(const char* page_name, const int allow_comands, int stop_on_page_done);
+void gui_print(const char *fmt, ...);
+void gui_print_color(const char *color, const char *fmt, ...);
+void gui_set_FILE(FILE* f);
+
+void set_scale_values(float w, float h);
+int scale_theme_x(int initial_x);
+int scale_theme_y(int initial_y);
+int scale_theme_min(int initial_value);
+float get_scale_w();
+float get_scale_h();
+
+#endif  // _GUI_HEADER
+
diff --git a/gui/hardwarekeyboard.cpp b/gui/hardwarekeyboard.cpp
new file mode 100644
index 0000000..1f34c5e
--- /dev/null
+++ b/gui/hardwarekeyboard.cpp
@@ -0,0 +1,416 @@
+// hardwarekeyboard.cpp - HardwareKeyboard object
+
+#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 <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../common.h"
+}
+
+#include "objects.hpp"
+#include <linux/input.h>
+
+HardwareKeyboard::HardwareKeyboard()
+ : mLastKeyChar(0)
+{
+}
+
+HardwareKeyboard::~HardwareKeyboard()
+{
+}
+
+// Map keys to other keys.
+static int TranslateKeyCode(int key_code)
+{
+	switch (key_code) {
+		case KEY_HOMEPAGE: // Home key on Asus Transformer hardware keyboard
+			return KEY_HOME;
+		case KEY_SLEEP: // Lock key on Asus Transformer hardware keyboard
+			return KEY_POWER;
+	}
+	return key_code;
+}
+
+int HardwareKeyboard::KeyDown(int key_code)
+{
+#ifdef _EVENT_LOGGING
+	LOGE("HardwareKeyboard::KeyDown %i\n", key_code);
+#endif
+	key_code = TranslateKeyCode(key_code);
+
+	// determine if any Shift key is held down
+	bool shiftkey = false;
+	std::set<int>::iterator it = mPressedKeys.find(KEY_LEFTSHIFT);
+	if (it == mPressedKeys.end())
+		it = mPressedKeys.find(KEY_RIGHTSHIFT);
+	if (it != mPressedKeys.end())
+		shiftkey = true;
+
+	mPressedKeys.insert(key_code);
+
+	int keyboard = -1;
+
+	switch (key_code) {
+		case KEY_A:
+			if (shiftkey)
+				keyboard = 'A';
+			else
+				keyboard = 'a';
+			break;
+		case KEY_B:
+			if (shiftkey)
+				keyboard = 'B';
+			else
+				keyboard = 'b';
+			break;
+		case KEY_C:
+			if (shiftkey)
+				keyboard = 'C';
+			else
+				keyboard = 'c';
+			break;
+		case KEY_D:
+			if (shiftkey)
+				keyboard = 'D';
+			else
+				keyboard = 'd';
+			break;
+		case KEY_E:
+			if (shiftkey)
+				keyboard = 'E';
+			else
+				keyboard = 'e';
+			break;
+		case KEY_F:
+			if (shiftkey)
+				keyboard = 'F';
+			else
+				keyboard = 'f';
+			break;
+		case KEY_G:
+			if (shiftkey)
+				keyboard = 'G';
+			else
+				keyboard = 'g';
+			break;
+		case KEY_H:
+			if (shiftkey)
+				keyboard = 'H';
+			else
+				keyboard = 'h';
+			break;
+		case KEY_I:
+			if (shiftkey)
+				keyboard = 'I';
+			else
+				keyboard = 'i';
+			break;
+		case KEY_J:
+			if (shiftkey)
+				keyboard = 'J';
+			else
+				keyboard = 'j';
+			break;
+		case KEY_K:
+			if (shiftkey)
+				keyboard = 'K';
+			else
+				keyboard = 'k';
+			break;
+		case KEY_L:
+			if (shiftkey)
+				keyboard = 'L';
+			else
+				keyboard = 'l';
+			break;
+		case KEY_M:
+			if (shiftkey)
+				keyboard = 'M';
+			else
+				keyboard = 'm';
+			break;
+		case KEY_N:
+			if (shiftkey)
+				keyboard = 'N';
+			else
+				keyboard = 'n';
+			break;
+		case KEY_O:
+			if (shiftkey)
+				keyboard = 'O';
+			else
+				keyboard = 'o';
+			break;
+		case KEY_P:
+			if (shiftkey)
+				keyboard = 'P';
+			else
+				keyboard = 'p';
+			break;
+		case KEY_Q:
+			if (shiftkey)
+				keyboard = 'Q';
+			else
+				keyboard = 'q';
+			break;
+		case KEY_R:
+			if (shiftkey)
+				keyboard = 'R';
+			else
+				keyboard = 'r';
+			break;
+		case KEY_S:
+			if (shiftkey)
+				keyboard = 'S';
+			else
+				keyboard = 's';
+			break;
+		case KEY_T:
+			if (shiftkey)
+				keyboard = 'T';
+			else
+				keyboard = 't';
+			break;
+		case KEY_U:
+			if (shiftkey)
+				keyboard = 'U';
+			else
+				keyboard = 'u';
+			break;
+		case KEY_V:
+			if (shiftkey)
+				keyboard = 'V';
+			else
+				keyboard = 'v';
+			break;
+		case KEY_W:
+			if (shiftkey)
+				keyboard = 'W';
+			else
+				keyboard = 'w';
+			break;
+		case KEY_X:
+			if (shiftkey)
+				keyboard = 'X';
+			else
+				keyboard = 'x';
+			break;
+		case KEY_Y:
+			if (shiftkey)
+				keyboard = 'Y';
+			else
+				keyboard = 'y';
+			break;
+		case KEY_Z:
+			if (shiftkey)
+				keyboard = 'Z';
+			else
+				keyboard = 'z';
+			break;
+		case KEY_0:
+			if (shiftkey)
+				keyboard = ')';
+			else
+				keyboard = '0';
+			break;
+		case KEY_1:
+			if (shiftkey)
+				keyboard = '!';
+			else
+				keyboard = '1';
+			break;
+		case KEY_2:
+			if (shiftkey)
+				keyboard = '@';
+			else
+				keyboard = '2';
+			break;
+		case KEY_3:
+			if (shiftkey)
+				keyboard = '#';
+			else
+				keyboard = '3';
+			break;
+		case KEY_4:
+			if (shiftkey)
+				keyboard = '$';
+			else
+				keyboard = '4';
+			break;
+		case KEY_5:
+			if (shiftkey)
+				keyboard = '%';
+			else
+				keyboard = '5';
+			break;
+		case KEY_6:
+			if (shiftkey)
+				keyboard = '^';
+			else
+				keyboard = '6';
+			break;
+		case KEY_7:
+			if (shiftkey)
+				keyboard = '&';
+			else
+				keyboard = '7';
+			break;
+		case KEY_8:
+			if (shiftkey)
+				keyboard = '*';
+			else
+				keyboard = '8';
+			break;
+		case KEY_9:
+			if (shiftkey)
+				keyboard = '(';
+			else
+				keyboard = '9';
+			break;
+		case KEY_SPACE:
+			keyboard = ' ';
+			break;
+		case KEY_BACKSPACE:
+			keyboard = KEYBOARD_BACKSPACE;
+			break;
+		case KEY_ENTER:
+			keyboard = KEYBOARD_ACTION;
+			break;
+		case KEY_SLASH:
+			if (shiftkey)
+				keyboard = '?';
+			else
+				keyboard = '/';
+			break;
+		case KEY_DOT:
+			if (shiftkey)
+				keyboard = '>';
+			else
+				keyboard = '.';
+			break;
+		case KEY_COMMA:
+			if (shiftkey)
+				keyboard = '<';
+			else
+				keyboard = ',';
+			break;
+		case KEY_MINUS:
+			if (shiftkey)
+				keyboard = '_';
+			else
+				keyboard = '-';
+			break;
+		case KEY_GRAVE:
+			if (shiftkey)
+				keyboard = '~';
+			else
+				keyboard = '`';
+			break;
+		case KEY_EQUAL:
+			if (shiftkey)
+				keyboard = '+';
+			else
+				keyboard = '=';
+			break;
+		case KEY_LEFTBRACE:
+			if (shiftkey)
+				keyboard = '{';
+			else
+				keyboard = '[';
+			break;
+		case KEY_RIGHTBRACE:
+			if (shiftkey)
+				keyboard = '}';
+			else
+				keyboard = ']';
+			break;
+		case KEY_BACKSLASH:
+			if (shiftkey)
+				keyboard = '|';
+			else
+				keyboard = '\\';
+			break;
+		case KEY_SEMICOLON:
+			if (shiftkey)
+				keyboard = ':';
+			else
+				keyboard = ';';
+			break;
+		case KEY_APOSTROPHE:
+			if (shiftkey)
+				keyboard = '\"';
+			else
+				keyboard = '\'';
+			break;
+		case KEY_UP: // Up arrow
+			keyboard = KEYBOARD_ARROW_UP;
+			break;
+		case KEY_DOWN: // Down arrow
+			keyboard = KEYBOARD_ARROW_DOWN;
+			break;
+		case KEY_LEFT: // Left arrow
+			keyboard = KEYBOARD_ARROW_LEFT;
+			break;
+		case KEY_RIGHT: // Right arrow
+			keyboard = KEYBOARD_ARROW_RIGHT;
+			break;
+
+#ifdef _EVENT_LOGGING
+		default:
+			LOGE("Unmapped keycode: %i\n", key_code);
+			break;
+#endif
+	}
+	if (keyboard != -1) {
+		mLastKeyChar = keyboard;
+		// NotifyKeyboard means: "report character to input widget". KEYBOARD_* codes are special, others are ASCII chars.
+		if (!PageManager::NotifyKeyboard(keyboard))
+			return 1;  // Return 1 to enable key repeat
+	} else {
+		mLastKeyChar = 0;
+		PageManager::NotifyKey(key_code, true);
+	}
+	return 0;
+}
+
+int HardwareKeyboard::KeyUp(int key_code)
+{
+#ifdef _EVENT_LOGGING
+	LOGE("HardwareKeyboard::KeyUp %i\n", key_code);
+#endif
+	key_code = TranslateKeyCode(key_code);
+	std::set<int>::iterator itr = mPressedKeys.find(key_code);
+	if (itr != mPressedKeys.end()) {
+		mPressedKeys.erase(itr);
+		PageManager::NotifyKey(key_code, false);
+	}
+	return 0;
+}
+
+int HardwareKeyboard::KeyRepeat()
+{
+#ifdef _EVENT_LOGGING
+	LOGE("HardwareKeyboard::KeyRepeat: %i\n", mLastKeyChar);
+#endif
+	if (mLastKeyChar)
+		PageManager::NotifyKeyboard(mLastKeyChar);
+	return 0;
+}
+
+void HardwareKeyboard::ConsumeKeyRelease(int key)
+{
+	// causes following KeyUp event to suppress notifications
+	mPressedKeys.erase(key);
+}
diff --git a/gui/image.cpp b/gui/image.cpp
new file mode 100644
index 0000000..8b43aaa
--- /dev/null
+++ b/gui/image.cpp
@@ -0,0 +1,92 @@
+// 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)
+{
+	mImage = NULL;
+	mHighlightImage = NULL;
+	isHighlighted = false;
+
+	if (!node)
+		return;
+
+	mImage = LoadAttrImage(FindNode(node, "image"), "resource");
+	mHighlightImage = LoadAttrImage(FindNode(node, "image"), "highlightresource");
+
+	// Load the placement
+	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, NULL, NULL, &mPlacement);
+
+	if (mImage && mImage->GetResource())
+	{
+		mRenderW = mImage->GetWidth();
+		mRenderH = mImage->GetHeight();
+
+		// 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 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..ca27ea8
--- /dev/null
+++ b/gui/input.cpp
@@ -0,0 +1,713 @@
+/*
+        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 = FindNode(node, "background");
+	if (child)
+	{
+		mBackground = LoadAttrImage(child, "resource");
+		mBackgroundColor = LoadAttrColor(child, "color", mBackgroundColor);
+	}
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundW = mBackground->GetWidth();
+		mBackgroundH = mBackground->GetHeight();
+	}
+
+	// Load the cursor color
+	child = FindNode(node, "cursor");
+	if (child)
+	{
+		mCursor = LoadAttrImage(child, "resource");
+		mCursorColor = LoadAttrColor(child, "color", mCursorColor);
+		attr = child->first_attribute("hasfocus");
+		if (attr)
+		{
+			std::string focus = attr->value();
+			SetInputFocus(atoi(focus.c_str()));
+		}
+		CursorWidth = LoadAttrIntScaleX(child, "width", CursorWidth);
+	}
+	DrawCursor = HasInputFocus;
+
+	// Load the font
+	child = FindNode(node, "font");
+	if (child)
+	{
+		mFont = LoadAttrFont(child, "resource");
+		mFontHeight = mFont->GetHeight();
+	}
+
+	child = FindNode(node, "text");
+	if (child)  mText = child->value();
+	mLastValue = gui_parse_text(mText);
+
+	child = FindNode(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());
+		mMask = LoadAttrString(child, "mask");
+		HasMask = !mMask.empty();
+		attr = child->first_attribute("maskvariable");
+		if (attr)
+			mMaskVariable = attr->value();
+		else
+			mMaskVariable = mVariable;
+	}
+
+	// Load input restrictions
+	child = FindNode(node, "restrict");
+	if (child)
+	{
+		MinLen = LoadAttrInt(child, "minlen", MinLen);
+		MaxLen = LoadAttrInt(child, "maxlen", MaxLen);
+		AllowedList = LoadAttrString(child, "allow");
+		HasAllowed = !AllowedList.empty();
+		DisabledList = LoadAttrString(child, "disable");
+		HasDisabled = !DisabledList.empty();
+	}
+
+	// Load the placement
+	LoadPlacement(FindNode(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()
+{
+	delete mInputText;
+	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 {
+				variableValue.insert(mCursorLocation + skipChars, 1, key);
+				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..f35a5d3
--- /dev/null
+++ b/gui/keyboard.cpp
@@ -0,0 +1,571 @@
+/*
+        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 <stdlib.h>
+#include <string.h>
+#include "../data.hpp"
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+#include "gui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUIKeyboard::GUIKeyboard(xml_node<>* node)
+	: GUIObject(node)
+{
+	int layoutindex, rowindex, keyindex, Xindex, Yindex, keyHeight = 0, keyWidth = 0;
+	currentKey = NULL;
+	highlightRenderCount = 0;
+	hasHighlight = hasCapsHighlight = false;
+	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++) {
+		layouts[layoutindex].keyboardImg = NULL;
+		memset(layouts[layoutindex].keys, 0, sizeof(Layout::keys));
+		memset(layouts[layoutindex].row_end_y, 0, sizeof(Layout::row_end_y));
+	}
+
+	mRendered = false;
+	currentLayout = 1;
+	CapsLockOn = false;
+
+	if (!node)  return;
+
+	mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlight);
+	mCapsHighlightColor = LoadAttrColor(FindNode(node, "capshighlight"), "color", &hasCapsHighlight);
+
+	child = FindNode(node, "keymargin");
+	mKeyMarginX = LoadAttrIntScaleX(child, "x", 0);
+	mKeyMarginY = LoadAttrIntScaleY(child, "y", 0);
+
+	child = FindNode(node, "background");
+	mBackgroundColor = LoadAttrColor(child, "color", COLOR(32,32,32,255));
+
+	child = FindNode(node, "key-alphanumeric");
+	mFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel"));
+	mFontColor = LoadAttrColor(child, "textcolor", COLOR(255,255,255,255));
+	mKeyColorAlphanumeric = LoadAttrColor(child, "color", COLOR(0,0,0,0));
+
+	child = FindNode(node, "key-other");
+	mSmallFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel-small"));
+	mFontColorSmall = LoadAttrColor(child, "textcolor", COLOR(192,192,192,255));
+	mKeyColorOther = LoadAttrColor(child, "color", COLOR(0,0,0,0));
+
+	child = FindNode(node, "longpress");
+	mLongpressFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel-longpress"));
+	mLongpressFontColor = LoadAttrColor(child, "textcolor", COLOR(128,128,128,255));
+	LoadPlacement(child, &longpressOffsetX, &longpressOffsetY);
+
+	LoadKeyLabels(node, 0); // load global key labels
+
+	// compatibility ugliness: resources should be specified in the layouts themselves instead
+	// Load the images for the different layouts
+	child = FindNode(node, "layout");
+	if (child)
+	{
+		layoutindex = 1;
+		strcpy(resource, "resource1");
+		attr = child->first_attribute(resource);
+		while (attr && layoutindex < (MAX_KEYBOARD_LAYOUTS + 1)) {
+			layouts[layoutindex - 1].keyboardImg = LoadAttrImage(child, resource);
+
+			layoutindex++;
+			resource[8] = (char)(layoutindex + 48);
+			attr = child->first_attribute(resource);
+		}
+	}
+
+	// Check the first image to get height and width
+	if (layouts[0].keyboardImg && layouts[0].keyboardImg->GetResource())
+	{
+		mRenderW = layouts[0].keyboardImg->GetWidth();
+		mRenderH = layouts[0].keyboardImg->GetHeight();
+	}
+
+	// Load all of the layout maps
+	layoutindex = 1;
+	strcpy(layout, "layout1");
+	keylayout = FindNode(node, layout);
+	while (keylayout)
+	{
+		if (layoutindex > MAX_KEYBOARD_LAYOUTS) {
+			LOGERR("Too many layouts defined in keyboard.\n");
+			return;
+		}
+
+		LoadKeyLabels(keylayout, layoutindex); // load per-layout key labels
+
+		Layout& lay = layouts[layoutindex - 1];
+
+		child = keylayout->first_node("keysize");
+		keyHeight = LoadAttrIntScaleY(child, "height", 0);
+		keyWidth = LoadAttrIntScaleX(child, "width", 0);
+		// compatibility ugliness: capslock="0" means that this is the caps layout. Also it has nothing to do with keysize.
+		lay.is_caps = (LoadAttrInt(child, "capslock", 1) == 0);
+		// compatibility ugliness: revert_layout has nothing to do with keysize.
+		lay.revert_layout = LoadAttrInt(child, "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;
+			lay.row_end_y[rowindex - 1] = Yindex;
+
+			keyindex = 1;
+			Xindex = 0;
+			strcpy(key, "key01");
+			attr = keyrow->first_attribute(key);
+
+			while (attr) {
+				if (keyindex > MAX_KEYBOARD_KEYS) {
+					LOGERR("Too many keys defined in a keyboard row.\n");
+					return;
+				}
+
+				const char* keyinfo = attr->value();
+
+				if (strlen(keyinfo) == 0) {
+					LOGERR("No key info on layout%i, row%i, key%dd.\n", layoutindex, rowindex, keyindex);
+					return;
+				}
+
+				if (ParseKey(keyinfo, lay.keys[rowindex - 1][keyindex - 1], Xindex, keyWidth, false))
+					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) {
+					const char* keyinfo = attr->value();
+
+					if (strlen(keyinfo) == 0) {
+						LOGERR("No long press info on layout%i, row%i, long%dd.\n", layoutindex, rowindex, keyindex);
+						return;
+					}
+
+					if (ParseKey(keyinfo, lay.keys[rowindex - 1][keyindex - 1], Xindex, keyWidth, true))
+						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 = FindNode(node, layout);
+	}
+
+	int x, y;
+	// Load the placement
+	LoadPlacement(FindNode(node, "placement"), &x, &y, &mRenderW, &mRenderH);
+	SetRenderPos(x, y, mRenderW, mRenderH);
+	return;
+}
+
+GUIKeyboard::~GUIKeyboard()
+{
+}
+
+int GUIKeyboard::ParseKey(const char* keyinfo, Key& key, int& Xindex, int keyWidth, bool longpress)
+{
+	key.layout = 0;
+	int keychar = 0;
+	if (strlen(keyinfo) == 1) {
+		// This is a single key, simple definition
+		keychar = keyinfo[0];
+	} else {
+		// This key has extra data: {keywidth}:{what_the_key_does}
+		keyWidth = scale_theme_x(atoi(keyinfo));
+
+		const char* ptr = keyinfo;
+		while (*ptr > 32 && *ptr != ':')
+			ptr++;
+		if (*ptr != ':')
+			return -1;  // no colon is an error
+		ptr++;
+
+		if (*ptr == 0) {  // This is an empty area
+			keychar = 0;
+		} else if (strlen(ptr) == 1) {  // This is the character that this key uses
+			keychar = *ptr;
+		} else if (*ptr == 'c') {  // This is an ASCII character code: "c:{number}"
+			keychar = atoi(ptr + 2);
+		} else if (*ptr == 'l') {  // This is a different layout: "layout{number}"
+			keychar = KEYBOARD_LAYOUT;
+			key.layout = atoi(ptr + 6);
+		} else if (*ptr == 'a') {  // This is an action: "action"
+			keychar = KEYBOARD_ACTION;
+		} else
+			return -1;
+	}
+
+	if (longpress) {
+		key.longpresskey = keychar;
+	} else {
+		key.key = keychar;
+		Xindex += keyWidth;
+		key.end_x = Xindex - 1;
+	}
+
+	return 0;
+}
+
+void GUIKeyboard::LoadKeyLabels(xml_node<>* parent, int layout)
+{
+	for (xml_node<>* child = parent->first_node(); child; child = child->next_sibling()) {
+		std::string name = child->name();
+		if (name == "keylabel") {
+			std::string keydef = LoadAttrString(child, "key", "");
+			Key tempkey;
+			int dummyX;
+			if (ParseKey(keydef.c_str(), tempkey, dummyX, 0, false) == 0) {
+				KeyLabel keylabel;
+				keylabel.key = tempkey.key;
+				keylabel.layout_from = layout;
+				keylabel.layout_to = tempkey.layout;
+				keylabel.text = LoadAttrString(child, "text", "");
+				keylabel.image = LoadAttrImage(child, "resource");
+				mKeyLabels.push_back(keylabel);
+			} else {
+				LOGERR("Ignoring invalid keylabel in layout %d: '%s'.\n", layout, keydef.c_str());
+			}
+		}
+	}
+}
+
+void GUIKeyboard::DrawKey(Key& key, int keyX, int keyY, int keyW, int keyH)
+{
+	unsigned char keychar = key.key;
+	if (!keychar)
+		return;
+
+	// key background
+	COLOR& c = (keychar >= 32 && keychar < 127) ? mKeyColorAlphanumeric : mKeyColorOther;
+	gr_color(c.red, c.green, c.blue, c.alpha);
+	keyX += mKeyMarginX;
+	keyY += mKeyMarginY;
+	keyW -= mKeyMarginX * 2;
+	keyH -= mKeyMarginY * 2;
+	gr_fill(keyX, keyY, keyW, keyH);
+
+	// key label
+	FontResource* labelFont = mFont;
+	string labelText;
+	ImageResource* labelImage = NULL;
+	if (keychar > 32 && keychar < 127) {
+		labelText = (char) keychar;
+		gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
+	}
+	else {
+		// search for a special key label
+		for (std::vector<KeyLabel>::iterator it = mKeyLabels.begin(); it != mKeyLabels.end(); ++it) {
+			if (it->layout_from > 0 && it->layout_from != currentLayout)
+				continue; // this label is for another layout
+			if (it->key == key.key && it->layout_to == key.layout)
+			{
+				// found a label
+				labelText = it->text;
+				labelImage = it->image;
+				break;
+			}
+		}
+		labelFont = mSmallFont;
+		gr_color(mFontColorSmall.red, mFontColorSmall.green, mFontColorSmall.blue, mFontColorSmall.alpha);
+	}
+
+	if (labelImage)
+	{
+		int w = labelImage->GetWidth();
+		int h = labelImage->GetHeight();
+		int x = keyX + (keyW - w) / 2;
+		int y = keyY + (keyH - h) / 2;
+		gr_blit(labelImage->GetResource(), 0, 0, w, h, x, y);
+	}
+	else if (!labelText.empty())
+	{
+		void* fontResource = labelFont->GetResource();
+		int textW = gr_measureEx(labelText.c_str(), fontResource);
+		int textH = labelFont->GetHeight();
+		int textX = keyX + (keyW - textW) / 2;
+		int textY = keyY + (keyH - textH) / 2;
+		gr_textEx(textX, textY, labelText.c_str(), fontResource);
+	}
+
+	// longpress key label (only if font is defined)
+	keychar = key.longpresskey;
+	if (keychar > 32 && keychar < 127 && mLongpressFont->GetResource()) {
+		void* fontResource = mLongpressFont->GetResource();
+		gr_color(mLongpressFontColor.red, mLongpressFontColor.green, mLongpressFontColor.blue, mLongpressFontColor.alpha);
+		string text(1, keychar);
+		int textH = mLongpressFont->GetHeight();
+		int textW = gr_measureEx(text.c_str(), fontResource);
+		int textX = keyX + keyW - longpressOffsetX - textW;
+		int textY = keyY + longpressOffsetY;
+		gr_textEx(textX, textY, text.c_str(), fontResource);
+	}
+}
+
+int GUIKeyboard::Render(void)
+{
+	if (!isConditionTrue())
+	{
+		mRendered = false;
+		return 0;
+	}
+
+	Layout& lay = layouts[currentLayout - 1];
+
+	bool drawKeys = false;
+	if (lay.keyboardImg && lay.keyboardImg->GetResource())
+		// keyboard is image based
+		gr_blit(lay.keyboardImg->GetResource(), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
+	else {
+		// keyboard is software drawn
+		// fill background
+		gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha);
+		gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
+		drawKeys = true;
+	}
+
+	// draw keys
+	int y1 = 0;
+	for (int row = 0; row < MAX_KEYBOARD_ROWS; ++row) {
+		int rowY = mRenderY + y1;
+		int rowH = lay.row_end_y[row] - y1;
+		y1 = lay.row_end_y[row];
+		int x1 = 0;
+		for (int col = 0; col < MAX_KEYBOARD_KEYS; ++col) {
+			Key& key = lay.keys[row][col];
+			int keyY = rowY;
+			int keyH = rowH;
+			int keyX = mRenderX + x1;
+			int keyW = key.end_x - x1;
+			x1 = key.end_x;
+
+			// Draw key for software drawn keyboard
+			if (drawKeys)
+				DrawKey(key, keyX, keyY, keyW, keyH);
+
+			// Draw highlight for capslock
+			if (hasCapsHighlight && lay.is_caps && CapsLockOn && (int)key.key == KEYBOARD_LAYOUT && key.layout == lay.revert_layout) {
+				gr_color(mCapsHighlightColor.red, mCapsHighlightColor.green, mCapsHighlightColor.blue, mCapsHighlightColor.alpha);
+				gr_fill(keyX, keyY, keyW, keyH);
+			}
+
+			// Highlight current key
+			if (hasHighlight && &key == currentKey && highlightRenderCount != 0) {
+				gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
+				gr_fill(keyX, keyY, keyW, keyH);
+			}
+		}
+	}
+
+	if (!hasHighlight || highlightRenderCount == 0)
+		mRendered = true;
+	else if (highlightRenderCount > 0)
+		highlightRenderCount--;
+	return 0;
+}
+
+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)
+{
+	RenderObject::SetRenderPos(x, y, w, h);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	return 0;
+}
+
+GUIKeyboard::Key* GUIKeyboard::HitTestKey(int x, int y)
+{
+	if (!IsInRegion(x, y))
+		return NULL;
+
+	int rely = y - mRenderY;
+	int relx = x - mRenderX;
+
+	Layout& lay = layouts[currentLayout - 1];
+
+	// Find the correct row
+	int row;
+	for (row = 0; row < MAX_KEYBOARD_ROWS; ++row) {
+		if (lay.row_end_y[row] > rely)
+			break;
+	}
+	if (row == MAX_KEYBOARD_ROWS)
+		return NULL;
+
+	// Find the correct key (column)
+	int col;
+	int x1 = 0;
+	for (col = 0; col < MAX_KEYBOARD_KEYS; ++col) {
+		Key& key = lay.keys[row][col];
+		if (x1 <= relx && relx < key.end_x && key.key != 0) {
+			// This is the key that was pressed!
+			return &key;
+		}
+		x1 = key.end_x;
+	}
+	return NULL;
+}
+
+int GUIKeyboard::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	static int was_held = 0, startX = 0;
+
+	if (!isConditionTrue())	 return -1;
+
+	switch (state)
+	{
+	case TOUCH_START:
+		was_held = 0;
+		startX = x;
+		currentKey = HitTestKey(x, y);
+		if (currentKey)
+			highlightRenderCount = -1;
+		else
+			highlightRenderCount = 0;
+		mRendered = false;
+		break;
+
+	case TOUCH_DRAG:
+		break;
+
+	case TOUCH_RELEASE:
+		if (x < startX - (mRenderW * 0.5)) {
+			if (highlightRenderCount != 0) {
+				highlightRenderCount = 0;
+				mRendered = false;
+			}
+			PageManager::NotifyKeyboard(KEYBOARD_SWIPE_LEFT);
+			return 0;
+		} else if (x > startX + (mRenderW * 0.5)) {
+			if (highlightRenderCount != 0) {
+				highlightRenderCount = 0;
+				mRendered = false;
+			}
+			PageManager::NotifyKeyboard(KEYBOARD_SWIPE_RIGHT);
+			return 0;
+		}
+		// fall through
+	case TOUCH_HOLD:
+	case TOUCH_REPEAT:
+		if (!currentKey) {
+			if (highlightRenderCount != 0) {
+				highlightRenderCount = 0;
+				mRendered = false;
+			}
+			return 0;
+		}
+
+		if (highlightRenderCount != 0) {
+			if (state == TOUCH_RELEASE)
+				highlightRenderCount = 2;
+			else
+				highlightRenderCount = -1;
+			mRendered = false;
+		}
+
+		if (HitTestKey(x, y) != currentKey) {
+			// We dragged off of the starting key
+			currentKey = NULL;
+			if (highlightRenderCount != 0) {
+				highlightRenderCount = 0;
+				mRendered = false;
+			}
+			return 0;
+		} else {
+			Key& key = *currentKey;
+			Layout& lay = layouts[currentLayout - 1];
+			if (state == TOUCH_RELEASE && was_held == 0) {
+				DataManager::Vibrate("tw_keyboard_vibrate");
+				if ((int)key.key == KEYBOARD_LAYOUT) {
+					// Switch layouts
+					if (lay.is_caps && key.layout == lay.revert_layout && !CapsLockOn) {
+						CapsLockOn = true; // Set the caps lock
+					} else {
+						CapsLockOn = false; // Unset the caps lock and change layouts
+						currentLayout = key.layout;
+					}
+					mRendered = false;
+				} else if ((int)key.key == KEYBOARD_ACTION) {
+					// Action
+					highlightRenderCount = 0;
+					// Send action notification
+					PageManager::NotifyKeyboard(key.key);
+				} else if ((int)key.key < KEYBOARD_SPECIAL_KEYS && (int)key.key > 0) {
+					// Regular key
+					PageManager::NotifyKeyboard(key.key);
+					if (!CapsLockOn && lay.is_caps) {
+						// caps lock was not set, change layouts
+						currentLayout = lay.revert_layout;
+						mRendered = false;
+					}
+				}
+			} else if (state == TOUCH_HOLD) {
+				was_held = 1;
+				if ((int)key.key == KEYBOARD_BACKSPACE) {
+					// Repeat backspace
+					PageManager::NotifyKeyboard(key.key);
+				} else if ((int)key.longpresskey < KEYBOARD_SPECIAL_KEYS && (int)key.longpresskey > 0) {
+					// Long Press Key
+					DataManager::Vibrate("tw_keyboard_vibrate");
+					PageManager::NotifyKeyboard(key.longpresskey);
+				}
+			} else if (state == TOUCH_REPEAT) {
+				was_held = 1;
+				if ((int)key.key == KEYBOARD_BACKSPACE) {
+					// Repeat backspace
+					PageManager::NotifyKeyboard(key.key);
+				}
+			}
+		}
+		break;
+	}
+
+	return 0;
+}
diff --git a/gui/listbox.cpp b/gui/listbox.cpp
new file mode 100644
index 0000000..4c9a68a
--- /dev/null
+++ b/gui/listbox.cpp
@@ -0,0 +1,176 @@
+/*
+	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 <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+
+GUIListBox::GUIListBox(xml_node<>* node) : GUIScrollList(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+	mIconSelected = mIconUnselected = NULL;
+	mUpdate = 0;
+
+	// Get the icons, if any
+	child = FindNode(node, "icon");
+	if (child) {
+		mIconSelected = LoadAttrImage(child, "selected");
+		mIconUnselected = LoadAttrImage(child, "unselected");
+	}
+	int iconWidth = std::max(mIconSelected->GetWidth(), mIconUnselected->GetWidth());
+	int iconHeight = std::max(mIconSelected->GetHeight(), mIconUnselected->GetHeight());
+	SetMaxIconSize(iconWidth, iconHeight);
+
+	// Handle the result variable
+	child = FindNode(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());
+		// Get the currently selected value for the list
+		DataManager::GetValue(mVariable, currentValue);
+	}
+	else
+		allowSelection = false;		// allows using listbox as a read-only list
+
+	// Get the data for the list
+	child = FindNode(node, "listitem");
+	if (!child) return;
+	while (child) {
+		ListData data;
+
+		attr = child->first_attribute("name");
+		if (!attr)
+			continue;
+		data.displayName = gui_parse_text(attr->value());
+		data.variableValue = gui_parse_text(child->value());
+		if (child->value() == currentValue) {
+			data.selected = 1;
+		} else {
+			data.selected = 0;
+		}
+		data.action = NULL;
+		xml_node<>* action = child->first_node("action");
+		if (action) {
+			data.action = new GUIAction(action);
+			allowSelection = true;
+		}
+
+		mList.push_back(data);
+
+		child = child->next_sibling("listitem");
+	}
+}
+
+GUIListBox::~GUIListBox()
+{
+}
+
+int GUIListBox::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	GUIScrollList::Update();
+
+	if (mUpdate) {
+		mUpdate = 0;
+		if (Render() == 0)
+			return 2;
+	}
+	return 0;
+}
+
+int GUIListBox::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIScrollList::NotifyVarChange(varName, value);
+
+	if(!isConditionTrue())
+		return 0;
+
+	// Check to see if the variable that we are using to store the list selected value has been updated
+	if (varName == mVariable) {
+		int i, listSize = mList.size();
+
+		currentValue = value;
+
+		for (i = 0; i < listSize; i++) {
+			if (mList.at(i).variableValue == currentValue) {
+				mList.at(i).selected = 1;
+				SetVisibleListLocation(i);
+			} else
+				mList.at(i).selected = 0;
+		}
+		mUpdate = 1;
+		return 0;
+	}
+	return 0;
+}
+
+void GUIListBox::SetPageFocus(int inFocus)
+{
+	GUIScrollList::SetPageFocus(inFocus);
+	if (inFocus) {
+		DataManager::GetValue(mVariable, currentValue);
+		NotifyVarChange(mVariable, currentValue);
+	}
+}
+
+size_t GUIListBox::GetItemCount()
+{
+	return mList.size();
+}
+
+void GUIListBox::RenderItem(size_t itemindex, int yPos, bool selected)
+{
+	// note: the "selected" parameter above is for the currently touched item
+	// don't confuse it with the more persistent "selected" flag per list item used below
+	ImageResource* icon = mList.at(itemindex).selected ? mIconSelected : mIconUnselected;
+	const std::string& text = mList.at(itemindex).displayName;
+
+	RenderStdItem(yPos, selected, icon, text.c_str());
+}
+
+void GUIListBox::NotifySelect(size_t item_selected)
+{
+	for (size_t i = 0; i < mList.size(); i++) {
+		mList.at(i).selected = 0;
+	}
+	if (item_selected < mList.size()) {
+		ListData& data = mList.at(item_selected);
+		data.selected = 1;
+		string str = data.variableValue;	// [check] should this set currentValue instead?
+		DataManager::SetValue(mVariable, str);
+		if (data.action)
+			data.action->doActions();
+	}
+	mUpdate = 1;
+}
diff --git a/gui/mousecursor.cpp b/gui/mousecursor.cpp
new file mode 100644
index 0000000..84e6322
--- /dev/null
+++ b/gui/mousecursor.cpp
@@ -0,0 +1,147 @@
+#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 = FindNode(node, "placement");
+	if(child)
+		LoadPlacement(child, &mRenderX, &mRenderY, &mRenderW, &mRenderH);
+
+	child = FindNode(node, "background");
+	if(child)
+	{
+		m_color = LoadAttrColor(child, "color", m_color);
+		m_image = LoadAttrImage(child, "resource");
+		if(m_image)
+		{
+			mRenderW = m_image->GetWidth();
+			mRenderH = m_image->GetHeight();
+		}
+	}
+
+	child = FindNode(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..7cce5db
--- /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 = FindNode(node, "conditions");
+	if (condition)  condition = FindNode(condition, "condition");
+	else			condition = FindNode(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..f8569d6
--- /dev/null
+++ b/gui/objects.hpp
@@ -0,0 +1,1157 @@
+/*
+	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>
+
+using namespace rapidxml;
+
+#include "../data.hpp"
+#include "resources.hpp"
+#include "pages.hpp"
+#include "../partitions.hpp"
+
+#ifndef TW_X_OFFSET
+#define TW_X_OFFSET 0
+#endif
+#ifndef TW_Y_OFFSET
+#define TW_Y_OFFSET 0
+#endif
+
+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 1 if this object handles the request, 0 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;
+	FontResource* mFont;
+	int mIsStatic;
+	int mVarChanged;
+	int mFontHeight;
+	unsigned maxWidth;
+	unsigned charSkip;
+};
+
+// 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:
+	ImageResource* mImage;
+	ImageResource* 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
+{
+	friend class ActionThread;
+
+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);
+
+	int doActions();
+
+protected:
+	class Action
+	{
+	public:
+		std::string mFunction;
+		std::string mArg;
+	};
+
+	std::vector<Action> mActions;
+	std::map<int, bool> mKeys;
+
+protected:
+	enum ThreadType { THREAD_NONE, THREAD_ACTION, THREAD_CANCEL };
+
+	int getKeyByName(std::string key);
+	int doAction(Action action);
+	ThreadType getThreadType(const Action& action);
+	void simulate_progress_bar(void);
+	int flash_zip(std::string filename, int* wipe_cache);
+	void reinject_after_flash();
+	void operation_start(const string operation_name);
+	void operation_end(const int operation_status);
+	time_t Start;
+
+	// map action name to function pointer
+	typedef int (GUIAction::*execFunction)(std::string);
+	typedef std::map<std::string, execFunction> mapFunc;
+	static mapFunc mf;
+	static std::set<std::string> setActionsRunningInCallerThread;
+
+	// GUI actions
+	int reboot(std::string arg);
+	int home(std::string arg);
+	int key(std::string arg);
+	int page(std::string arg);
+	int reload(std::string arg);
+	int readBackup(std::string arg);
+	int set(std::string arg);
+	int clear(std::string arg);
+	int mount(std::string arg);
+	int unmount(std::string arg);
+	int restoredefaultsettings(std::string arg);
+	int copylog(std::string arg);
+	int compute(std::string arg);
+	int setguitimezone(std::string arg);
+	int overlay(std::string arg);
+	int queuezip(std::string arg);
+	int cancelzip(std::string arg);
+	int queueclear(std::string arg);
+	int sleep(std::string arg);
+	int appenddatetobackupname(std::string arg);
+	int generatebackupname(std::string arg);
+	int checkpartitionlist(std::string arg);
+	int getpartitiondetails(std::string arg);
+	int screenshot(std::string arg);
+	int setbrightness(std::string arg);
+
+	// (originally) threaded actions
+	int fileexists(std::string arg);
+	int flash(std::string arg);
+	int wipe(std::string arg);
+	int refreshsizes(std::string arg);
+	int nandroid(std::string arg);
+	int fixpermissions(std::string arg);
+	int dd(std::string arg);
+	int partitionsd(std::string arg);
+	int installhtcdumlock(std::string arg);
+	int htcdumlockrestoreboot(std::string arg);
+	int htcdumlockreflashrecovery(std::string arg);
+	int cmd(std::string arg);
+	int terminalcommand(std::string arg);
+	int killterminal(std::string arg);
+	int reinjecttwrp(std::string arg);
+	int checkbackupname(std::string arg);
+	int decrypt(std::string arg);
+	int adbsideload(std::string arg);
+	int adbsideloadcancel(std::string arg);
+	int openrecoveryscript(std::string arg);
+	int installsu(std::string arg);
+	int fixsu(std::string arg);
+	int decrypt_backup(std::string arg);
+	int repair(std::string arg);
+	int resize(std::string arg);
+	int changefilesystem(std::string arg);
+	int startmtp(std::string arg);
+	int stopmtp(std::string arg);
+	int flashimage(std::string arg);
+	int cancelbackup(std::string arg);
+	int checkpartitionlifetimewrites(std::string arg);
+	int mountsystemtoggle(std::string arg);
+
+	int simulate;
+};
+
+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;
+	ImageResource* 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:
+	ImageResource* mChecked;
+	ImageResource* mUnchecked;
+	GUIText* mLabel;
+	int mTextX, mTextY;
+	int mCheckX, mCheckY, mCheckW, mCheckH;
+	int mLastState;
+	bool mRendered;
+	std::string mVarName;
+};
+
+class GUIScrollList : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIScrollList(xml_node<>* node);
+	virtual ~GUIScrollList();
+
+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:
+	// derived classes need to implement these
+	// get number of items
+	virtual size_t GetItemCount() { return 0; }
+	// render a single item in rect (mRenderX, yPos, mRenderW, actualItemHeight)
+	virtual void RenderItem(size_t itemindex, int yPos, bool selected);
+	// an item was selected
+	virtual void NotifySelect(size_t item_selected) {}
+
+	// render a standard-layout list item with optional icon and text
+	void RenderStdItem(int yPos, bool selected, ImageResource* icon, const char* text, int iconAndTextH = 0);
+
+	enum { NO_ITEM = (size_t)-1 };
+	// returns item index at coordinates or NO_ITEM if there is no item there
+	size_t HitTestItem(int x, int y);
+
+	// Called by the derived class to set the max icon size so we can calculate the proper actualItemHeight and center smaller icons if the icon size varies
+	void SetMaxIconSize(int w, int h);
+
+	// This will make sure that the item indicated by list_index is visible on the screen
+	void SetVisibleListLocation(size_t list_index);
+
+	// Handle scrolling changes for drags and kinetic scrolling
+	void HandleScrolling();
+
+	// Returns many full rows the list is capable of displaying
+	int GetDisplayItemCount();
+
+	// Returns the size in pixels of a partial item or row size
+	int GetDisplayRemainder();
+
+protected:
+	// Background
+	COLOR mBackgroundColor;
+	ImageResource* mBackground; // background image, if any, automatically centered
+
+	// Header
+	COLOR mHeaderBackgroundColor;
+	COLOR mHeaderFontColor;
+	std::string mHeaderText; // Original header text without parsing any variables
+	std::string mLastHeaderValue; // Header text after parsing variables
+	bool mHeaderIsStatic; // indicates if the header is static (no need to check for changes in NotifyVarChange)
+	int mHeaderH; // actual header height including font, icon, padding, and separator heights
+	ImageResource* mHeaderIcon;
+	int mHeaderIconHeight, mHeaderIconWidth; // width and height of the header icon if present
+	int mHeaderSeparatorH; // Height of the separator between header and list items
+	COLOR mHeaderSeparatorColor; // color of the header separator
+
+	// Per-item layout
+	FontResource* mFont;
+	COLOR mFontColor;
+	bool hasHighlightColor; // indicates if a highlight color was set
+	COLOR mHighlightColor; // background row highlight color
+	COLOR mFontHighlightColor;
+	int mFontHeight;
+	int actualItemHeight; // Actual height of each item in pixels including max icon size, font size, and padding
+	int maxIconWidth, maxIconHeight; // max icon width and height for the list, set by derived class in SetMaxIconSize
+	int mItemSpacing; // stores the spacing or padding on the y axis, part of the actualItemHeight
+	int mSeparatorH; // Height of the separator between items
+	COLOR mSeparatorColor; // color of the separator that is between items
+
+	// Scrollbar
+	int mFastScrollW; // width of the fastscroll area
+	int mFastScrollLineW; // width of the line for fastscroll rendering
+	int mFastScrollRectW; // width of the rectangle for fastscroll
+	int mFastScrollRectH; // minimum height of the rectangle for fastscroll
+	COLOR mFastScrollLineColor;
+	COLOR mFastScrollRectColor;
+
+	// Scrolling and dynamic state
+	int mFastScrollRectCurrentY; // current top of fastscroll rect relative to list top
+	int mFastScrollRectCurrentH; // current height of fastscroll rect
+	int mFastScrollRectTouchY; // offset from top of fastscroll rect where the user initially touched
+	bool hasScroll; // indicates that we have enough items in the list to scroll
+	int firstDisplayedItem; // this item goes at the top of the display list - may only be partially visible
+	int scrollingSpeed; // on a touch release, this is set based on the difference in the y-axis between the last 2 touches and indicates how fast the kinetic scrolling will go
+	int y_offset; // this is how many pixels offset in the y axis for per pixel scrolling, is always <= 0 and should never be < -actualItemHeight
+	bool allowSelection; // true if touched item can be selected, false for pure read-only lists and the console
+	size_t selectedItem; // selected item index after the initial touch, set to -1 if we are scrolling
+	int touchDebounce; // debounce for touches, minimum of 6 pixels but may be larger calculated based actualItemHeight / 3
+	int lastY, last2Y; // last 2 touch locations, used for tracking kinetic scroll speed
+	int fastScroll; // indicates that the inital touch was inside the fastscroll region - makes for easier fast scrolling as the touches don't have to stay within the fast scroll region and you drag your finger
+	int mUpdate; // indicates that a change took place and we need to re-render
+};
+
+class GUIFileSelector : public GUIScrollList
+{
+public:
+	GUIFileSelector(xml_node<>* node);
+	virtual ~GUIFileSelector();
+
+public:
+	// 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
+	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);
+
+	virtual size_t GetItemCount();
+	virtual void RenderItem(size_t itemindex, int yPos, bool selected);
+	virtual void NotifySelect(size_t item_selected);
+
+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 GetFileList(const std::string folder);
+	static bool fileSort(FileData d1, FileData d2);
+
+protected:
+	std::vector<FileData> mFolderList;
+	std::vector<FileData> mFileList;
+	std::string mPathVar; // current path displayed, saved in the data manager
+	std::string mPathDefault; // default value for the path if none is set in mPathVar
+	std::string mExtn; // used for filtering the file list, for example, *.zip
+	std::string mVariable; // set when the user selects an item, pull path like /path/to/foo
+	std::string mSortVariable; // data manager variable used to change the sorting of files
+	std::string mSelection; // set when the user selects an item without the full path like selecting /path/to/foo would just be set to foo
+	int mShowFolders, mShowFiles; // indicates if the list should show folders and/or files
+	int mShowNavFolders; // indicates if the list should include the "up a level" item and allow you to traverse folders (nav folders are disabled for the restore list, for instance)
+	static int mSortOrder; // must be static because it is used by the static function fileSort
+	ImageResource* mFolderIcon;
+	ImageResource* mFileIcon;
+	bool updateFileList;
+};
+
+class GUIListBox : public GUIScrollList
+{
+public:
+	GUIListBox(xml_node<>* node);
+	virtual ~GUIListBox();
+
+public:
+	// 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
+	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);
+
+	virtual size_t GetItemCount();
+	virtual void RenderItem(size_t itemindex, int yPos, bool selected);
+	virtual void NotifySelect(size_t item_selected);
+
+protected:
+	struct ListData {
+		std::string displayName;
+		std::string variableValue;
+		unsigned int selected;
+		GUIAction* action;
+	};
+
+protected:
+	std::vector<ListData> mList;
+	std::string mVariable;
+	std::string currentValue;
+	ImageResource* mIconSelected;
+	ImageResource* mIconUnselected;
+};
+
+class GUIPartitionList : public GUIScrollList
+{
+public:
+	GUIPartitionList(xml_node<>* node);
+	virtual ~GUIPartitionList();
+
+public:
+	// 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();
+
+	// NotifyVarChange - 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);
+
+	virtual size_t GetItemCount();
+	virtual void RenderItem(size_t itemindex, int yPos, bool selected);
+	virtual void NotifySelect(size_t item_selected);
+
+protected:
+	void MatchList();
+	void SetPosition();
+
+protected:
+	std::vector<PartitionList> mList;
+	std::string ListType;
+	std::string mVariable;
+	std::string selectedList;
+	std::string currentValue;
+	std::string mLastValue;
+	ImageResource* mIconSelected;
+	ImageResource* mIconUnselected;
+	bool updateList;
+};
+
+class GUIConsole : public GUIScrollList
+{
+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);
+
+	// IsInRegion - Checks if the request is handled by this object
+	//  Return 1 if this object handles the request, 0 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);
+
+	// ScrollList interface
+	virtual size_t GetItemCount();
+	virtual void RenderItem(size_t itemindex, int yPos, bool selected);
+	virtual void NotifySelect(size_t item_selected);
+protected:
+	enum SlideoutState
+	{
+		hidden = 0,
+		visible,
+		request_hide,
+		request_show
+	};
+
+	ImageResource* mSlideoutImage;
+	size_t mLastCount; // lines from gConsole that are already split and copied into rConsole
+	bool scrollToEnd; // true if we want to keep tracking the last line
+	int mSlideoutX, mSlideoutY, mSlideoutW, mSlideoutH;
+	int mSlideout;
+	SlideoutState mSlideoutState;
+	std::vector<std::string> rConsole;
+	std::vector<std::string> rConsoleColor;
+
+protected:
+	bool AddLines();
+	int RenderSlideout(void);
+	int RenderConsole(void);
+};
+
+// 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:
+	ImageResource* mEmptyBar;
+	ImageResource* 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;
+	GUIText* sSliderLabel;
+	ImageResource* sSlider;
+	ImageResource* sSliderUsed;
+	ImageResource* sTouch;
+	int sTouchW, sTouchH;
+	int sCurTouchX;
+	int sUpdate;
+};
+
+#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:
+	struct Key
+	{
+		unsigned char key; // ASCII code or one of the special KEYBOARD_* codes above
+		unsigned char longpresskey;
+		int end_x;
+		int layout;
+	};
+	int ParseKey(const char* keyinfo, Key& key, int& Xindex, int keyWidth, bool longpress);
+	void LoadKeyLabels(xml_node<>* parent, int layout);
+	void DrawKey(Key& key, int keyX, int keyY, int keyW, int keyH);
+
+	enum {
+		MAX_KEYBOARD_LAYOUTS = 5,
+		MAX_KEYBOARD_ROWS = 9,
+		MAX_KEYBOARD_KEYS = 20
+	};
+	struct Layout
+	{
+		ImageResource* keyboardImg;
+		struct Key keys[MAX_KEYBOARD_ROWS][MAX_KEYBOARD_KEYS];
+		int row_end_y[MAX_KEYBOARD_ROWS];
+		bool is_caps;
+		int revert_layout;
+	};
+	Layout layouts[MAX_KEYBOARD_LAYOUTS];
+
+	struct KeyLabel
+	{
+		unsigned char key; // same as in struct Key
+		int layout_from; // 1-based; 0 for labels that apply to all layouts
+		int layout_to; // same as Key.layout
+		string text; // key label text
+		ImageResource* image; // image (overrides text if defined)
+	};
+	std::vector<KeyLabel> mKeyLabels;
+
+	// Find key at screen coordinates
+	Key* HitTestKey(int x, int y);
+
+	bool mRendered;
+	std::string mVariable;
+	int currentLayout;
+	bool CapsLockOn;
+	int highlightRenderCount;
+	Key* currentKey;
+	bool hasHighlight, hasCapsHighlight;
+	COLOR mHighlightColor;
+	COLOR mCapsHighlightColor;
+	COLOR mFontColor; // for centered key labels
+	COLOR mFontColorSmall; // for centered key labels
+	FontResource* mFont; // for main key labels
+	FontResource* mSmallFont; // for key labels like "?123"
+	FontResource* mLongpressFont; // for the small longpress label in the upper right corner
+	int longpressOffsetX, longpressOffsetY; // distance of the longpress label from the key corner
+	COLOR mLongpressFontColor;
+	COLOR mBackgroundColor; // keyboard background color
+	COLOR mKeyColorAlphanumeric; // key background color
+	COLOR mKeyColorOther; // key background color
+	int mKeyMarginX, mKeyMarginY; // space around key boxes - applied to left/right and top/bottom
+};
+
+// GUIInput - Used for keyboard input
+class GUIInput : public GUIObject, public RenderObject, public ActionObject, public InputObject
+{
+public:
+	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;
+	ImageResource* mBackground;
+	ImageResource* mCursor;
+	FontResource* 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();
+	virtual ~HardwareKeyboard();
+
+public:
+	// called by the input handler for key events
+	int KeyDown(int key_code);
+	int KeyUp(int key_code);
+
+	// called by the input handler when holding a key down
+	int KeyRepeat();
+
+	// called by multi-key actions to suppress key-release notifications
+	void ConsumeKeyRelease(int key);
+
+private:
+	int mLastKeyChar;
+	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;
+	FontResource *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;
+	ImageResource *mBackgroundImage;
+	ImageResource *mHandleImage;
+	ImageResource *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;
+	ImageResource *m_image;
+	bool m_present;
+};
+
+class GUIPatternPassword : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIPatternPassword(xml_node<>* node);
+	virtual ~GUIPatternPassword();
+
+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:
+	void CalculateDotPositions();
+	void ResetActiveDots();
+	void ConnectDot(int dot_idx);
+	void ConnectIntermediateDots(int dot_idx);
+	int InDot(int x, int y);
+	bool DotUsed(int dot_idx);
+	static bool IsInRect(int x, int y, int rx, int ry, int rw, int rh);
+	void PatternDrawn();
+
+	struct Dot {
+		int x;
+		int y;
+		bool active;
+	};
+
+	Dot mDots[9];
+	int mConnectedDots[9];
+	size_t mConnectedDotsLen;
+	int mCurLineX;
+	int mCurLineY;
+	bool mTrackingTouch;
+	bool mNeedRender;
+
+	COLOR mDotColor;
+	COLOR mActiveDotColor;
+	COLOR mLineColor;
+	ImageResource *mDotImage;
+	ImageResource *mActiveDotImage;
+	gr_surface mDotCircle;
+	gr_surface mActiveDotCircle;
+	int mDotRadius;
+	int mLineWidth;
+
+	std::string mPassVar;
+	GUIAction *mAction;
+	int mUpdate;
+};
+
+
+// Helper APIs
+xml_node<>* FindNode(xml_node<>* parent, const char* nodename, int depth = 0);
+std::string LoadAttrString(xml_node<>* element, const char* attrname, const char* defaultvalue = "");
+int LoadAttrInt(xml_node<>* element, const char* attrname, int defaultvalue = 0);
+int LoadAttrIntScaleX(xml_node<>* element, const char* attrname, int defaultvalue = 0);
+int LoadAttrIntScaleY(xml_node<>* element, const char* attrname, int defaultvalue = 0);
+COLOR LoadAttrColor(xml_node<>* element, const char* attrname, bool* found_color, COLOR defaultvalue = COLOR(0,0,0,0));
+COLOR LoadAttrColor(xml_node<>* element, const char* attrname, COLOR defaultvalue = COLOR(0,0,0,0));
+FontResource* LoadAttrFont(xml_node<>* element, const char* attrname);
+ImageResource* LoadAttrImage(xml_node<>* element, const char* attrname);
+AnimationResource* LoadAttrAnimation(xml_node<>* element, const char* attrname);
+
+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..4c98a72
--- /dev/null
+++ b/gui/pages.cpp
@@ -0,0 +1,1470 @@
+/*
+	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 "../twrp-functions.hpp"
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+#include "../minzip/SysUtil.h"
+#include "../minzip/Zip.h"
+#include "gui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "blanktimer.hpp"
+
+extern int gGuiRunning;
+
+std::map<std::string, PageSet*> PageManager::mPageSets;
+PageSet* PageManager::mCurrentSet;
+PageSet* PageManager::mBaseSet = NULL;
+MouseCursor *PageManager::mMouseCursor = NULL;
+HardwareKeyboard *PageManager::mHardwareKeyboard = NULL;
+
+int tw_x_offset = 0;
+int tw_y_offset = 0;
+
+// 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
+xml_node<>* FindNode(xml_node<>* parent, const char* nodename, int depth /* = 0 */)
+{
+	if (!parent)
+		return NULL;
+
+	xml_node<>* child = parent->first_node(nodename);
+	if (child)
+		return child;
+
+	if (depth == 10) {
+		LOGERR("Too many style loops detected.\n");
+		return NULL;
+	}
+
+	xml_node<>* style = parent->first_node("style");
+	if (style) {
+		while (style) {
+			if (!style->first_attribute("name")) {
+				LOGERR("No name given for style.\n");
+				continue;
+			} else {
+				std::string name = style->first_attribute("name")->value();
+				xml_node<>* node = PageManager::FindStyle(name);
+
+				if (node) {
+					// We found the style that was named
+					xml_node<>* stylenode = FindNode(node, nodename, depth + 1);
+					if (stylenode)
+						return stylenode;
+				}
+			}
+			style = style->next_sibling("style");
+		}
+	} else {
+		// Search for stylename in the parent node <object type="foo" stylename="foo2">
+		xml_attribute<>* attr = parent->first_attribute("style");
+		// If no style is found anywhere else and the node wasn't found in the object itself
+		// as a special case we will search for a style that uses the same style name as the
+		// object type, so <object type="button"> would search for a style named "button"
+		if (!attr)
+			attr = parent->first_attribute("type");
+		if (attr) {
+			xml_node<>* node = PageManager::FindStyle(attr->value());
+			if (node) {
+				xml_node<>* stylenode = FindNode(node, nodename, depth + 1);
+				if (stylenode)
+					return stylenode;
+			}
+		}
+	}
+	return NULL;
+}
+
+std::string LoadAttrString(xml_node<>* element, const char* attrname, const char* defaultvalue)
+{
+	if (!element)
+		return defaultvalue;
+
+	xml_attribute<>* attr = element->first_attribute(attrname);
+	return attr ? attr->value() : defaultvalue;
+}
+
+int LoadAttrInt(xml_node<>* element, const char* attrname, int defaultvalue)
+{
+	string value = LoadAttrString(element, attrname);
+	// resolve variables
+	DataManager::GetValue(value, value);
+	return value.empty() ? defaultvalue : atoi(value.c_str());
+}
+
+int LoadAttrIntScaleX(xml_node<>* element, const char* attrname, int defaultvalue)
+{
+	return scale_theme_x(LoadAttrInt(element, attrname, defaultvalue));
+}
+
+int LoadAttrIntScaleY(xml_node<>* element, const char* attrname, int defaultvalue)
+{
+	return scale_theme_y(LoadAttrInt(element, attrname, defaultvalue));
+}
+
+COLOR LoadAttrColor(xml_node<>* element, const char* attrname, bool* found_color, COLOR defaultvalue)
+{
+	string value = LoadAttrString(element, attrname);
+	*found_color = !value.empty();
+	// resolve variables
+	DataManager::GetValue(value, value);
+	COLOR ret = defaultvalue;
+	if (ConvertStrToColor(value, &ret) == 0)
+		return ret;
+	else
+		return defaultvalue;
+}
+
+COLOR LoadAttrColor(xml_node<>* element, const char* attrname, COLOR defaultvalue)
+{
+	bool found_color = false;
+	return LoadAttrColor(element, attrname, &found_color, defaultvalue);
+}
+
+FontResource* LoadAttrFont(xml_node<>* element, const char* attrname)
+{
+	std::string name = LoadAttrString(element, attrname, "");
+	if (name.empty())
+		return NULL;
+	else
+		return PageManager::GetResources()->FindFont(name);
+}
+
+ImageResource* LoadAttrImage(xml_node<>* element, const char* attrname)
+{
+	std::string name = LoadAttrString(element, attrname, "");
+	if (name.empty())
+		return NULL;
+	else
+		return PageManager::GetResources()->FindImage(name);
+}
+
+AnimationResource* LoadAttrAnimation(xml_node<>* element, const char* attrname)
+{
+	std::string name = LoadAttrString(element, attrname, "");
+	if (name.empty())
+		return NULL;
+	else
+		return PageManager::GetResources()->FindAnimation(name);
+}
+
+bool LoadPlacement(xml_node<>* node, int* x, int* y, int* w /* = NULL */, int* h /* = NULL */, RenderObject::Placement* placement /* = NULL */)
+{
+	if (!node)
+		return false;
+
+	if (node->first_attribute("x"))
+		*x = LoadAttrIntScaleX(node, "x") + tw_x_offset;
+
+	if (node->first_attribute("y"))
+		*y = LoadAttrIntScaleY(node, "y") + tw_y_offset;
+
+	if (w && node->first_attribute("w"))
+		*w = LoadAttrIntScaleX(node, "w");
+
+	if (h && node->first_attribute("h"))
+		*h = LoadAttrIntScaleY(node, "h");
+
+	if (placement && node->first_attribute("placement"))
+		*placement = (RenderObject::Placement) LoadAttrInt(node, "placement");
+
+	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)
+{
+	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, 0);
+}
+
+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, int depth)
+{
+	if (depth == 10)
+	{
+		LOGERR("Page processing depth has exceeded 10. Failing out. This is likely a recursive template.\n");
+		return false;
+	}
+
+	for (xml_node<>* child = page->first_node(); child; child = child->next_sibling())
+	{
+		std::string type = child->name();
+
+		if (type == "background") {
+			mBackground = LoadAttrColor(child, "color", COLOR(0,0,0,0));
+			continue;
+		}
+
+		if (type == "object") {
+			// legacy format : <object type="...">
+			xml_attribute<>* attr = child->first_attribute("type");
+			type = attr ? attr->value() : "*unspecified*";
+		}
+
+		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 == "patternpassword")
+		{
+			GUIPatternPassword* element = new GUIPatternPassword(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");
+					}
+					// [check] why is there no if (node_found) here too?
+				}
+			}
+		}
+		else
+		{
+			LOGERR("Unknown object type: %s.\n", type.c_str());
+		}
+	}
+	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 = new ResourceManager;
+	mCurrentPage = NULL;
+
+	mXmlFile = xmlFile;
+	if (xmlFile)
+		mDoc.parse<0>(mXmlFile);
+	else
+		mCurrentPage = new Page(NULL, NULL);
+}
+
+PageSet::~PageSet()
+{
+	mOverlays.clear();
+	for (std::vector<Page*>::iterator itr = mPages.begin(); itr != mPages.end(); ++itr)
+		delete *itr;
+
+	delete mResources;
+	free(mXmlFile);
+
+	mDoc.clear();
+
+	for (std::vector<xml_document<>*>::iterator itr = mIncludedDocs.begin(); itr != mIncludedDocs.end(); ++itr) {
+		(*itr)->clear();
+		delete *itr;
+	}
+}
+
+int PageSet::Load(ZipArchive* package)
+{
+	xml_node<>* parent;
+	xml_node<>* child;
+	xml_node<>* xmltemplate;
+	xml_node<>* xmlstyle;
+
+	parent = mDoc.first_node("recovery");
+	if (!parent)
+		parent = mDoc.first_node("install");
+
+	set_scale_values(1, 1); // Reset any previous scaling values
+
+	// Now, let's parse the XML
+	LOGINFO("Checking resolution...\n");
+	child = parent->first_node("details");
+	if (child) {
+		xml_node<>* resolution = child->first_node("resolution");
+		if (resolution) {
+			xml_attribute<>* width_attr = resolution->first_attribute("width");
+			xml_attribute<>* height_attr = resolution->first_attribute("height");
+			xml_attribute<>* noscale_attr = resolution->first_attribute("noscaling");
+			if (width_attr && height_attr && !noscale_attr) {
+				int width = atoi(width_attr->value());
+				int height = atoi(height_attr->value());
+				int offx = 0, offy = 0;
+#ifdef TW_ROUND_SCREEN
+				xml_node<>* roundscreen = child->first_node("roundscreen");
+				if (roundscreen) {
+					LOGINFO("TW_ROUND_SCREEN := true, using round screen XML settings.\n");
+					xml_attribute<>* offx_attr = roundscreen->first_attribute("offset_x");
+					xml_attribute<>* offy_attr = roundscreen->first_attribute("offset_y");
+					if (offx_attr) {
+						offx = atoi(offx_attr->value());
+					}
+					if (offy_attr) {
+						offy = atoi(offy_attr->value());
+					}
+				}
+#endif
+				if (width != 0 && height != 0) {
+					float scale_w = ((float)gr_fb_width() - ((float)offx * 2.0)) / (float)width;
+					float scale_h = ((float)gr_fb_height() - ((float)offy * 2.0)) / (float)height;
+#ifdef TW_ROUND_SCREEN
+					float scale_off_w = (float)gr_fb_width() / (float)width;
+					float scale_off_h = (float)gr_fb_height() / (float)height;
+					tw_x_offset = offx * scale_off_w;
+					tw_y_offset = offy * scale_off_h;
+#endif
+					if (scale_w != 1 || scale_h != 1) {
+						LOGINFO("Scaling theme width %fx and height %fx, offsets x: %i y: %i\n", scale_w, scale_h, tw_x_offset, tw_y_offset);
+						set_scale_values(scale_w, scale_h);
+					}
+				}
+			} else {
+				LOGINFO("XML does not contain width and height, no scaling will be applied\n");
+			}
+		} else {
+			LOGINFO("XML contains no resolution tag, no scaling will be applied.\n");
+		}
+	} else {
+		LOGINFO("XML contains no details tag, no scaling will be applied.\n");
+	}
+	LOGINFO("Loading resources...\n");
+	child = parent->first_node("resources");
+	if (child)
+		mResources->LoadResources(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);
+
+	// Load styles if present
+	xmlstyle = parent->first_node("styles");
+	if (xmlstyle)
+		styles.push_back(xmlstyle);
+
+	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;
+	xml_node<>* xmlstyle;
+	long len;
+	char* xmlFile = NULL;
+	string filename;
+	xml_document<> *doc = NULL;
+
+	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;
+
+		if (!package) {
+			// We can try to load the XML directly...
+			filename = TWRES;
+			filename += attr->value();
+			LOGINFO("PageSet::CheckInclude loading filename: '%s'\n", filename.c_str());
+			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();
+			LOGINFO("PageSet::CheckInclude loading filename: '%s'\n", filename.c_str());
+			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;
+			}
+		}
+
+		xmlFile[len] = '\0';
+		doc = new xml_document<>();
+		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);
+
+		// Load styles if present
+		xmlstyle = parent->first_node("styles");
+		if (xmlstyle)
+			styles.push_back(xmlstyle);
+
+		child = parent->first_node("pages");
+		if (child && LoadPages(child))
+		{
+			templates.pop_back();
+			doc->clear();
+			delete doc;
+			return -1;
+		}
+
+		mIncludedDocs.push_back(doc);
+
+		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 (page) {
+		if (mOverlays.size() >= 10) {
+			LOGERR("Too many overlays requested, max is 10.\n");
+			return -1;
+		}
+
+		std::vector<Page*>::iterator iter;
+		for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) {
+			if ((*iter)->GetName() == page->GetName()) {
+				mOverlays.erase(iter);
+				// SetOverlay() is (and should stay) the only function which
+				// adds to mOverlays. Then, each page can appear at most once.
+				break;
+			}
+		}
+
+		page->SetPageFocus(1);
+		page->NotifyVarChange("", "");
+
+		if (!mOverlays.empty())
+			mOverlays.back()->SetPageFocus(0);
+
+		mOverlays.push_back(page);
+	} else {
+		if (!mOverlays.empty()) {
+			mOverlays.back()->SetPageFocus(0);
+			mOverlays.pop_back();
+			if (!mOverlays.empty())
+				mOverlays.back()->SetPageFocus(1);
+			else if (mCurrentPage)
+				mCurrentPage->SetPageFocus(1); // Just in case somehow the regular page lost focus, we'll set it again
+		}
+	}
+	return 0;
+}
+
+const ResourceManager* PageSet::GetResources()
+{
+	return mResources;
+}
+
+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)
+		{
+			if (strcmp(name->value(), "tw_x_offset") == 0) {
+				tw_x_offset = atoi(value->value());
+				child = child->next_sibling("variable");
+				continue;
+			}
+			if (strcmp(name->value(), "tw_y_offset") == 0) {
+				tw_y_offset = atoi(value->value());
+				child = child->next_sibling("variable");
+				continue;
+			}
+			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;
+
+	std::vector<Page*>::iterator iter;
+
+	for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) {
+		ret = ((*iter) ? (*iter)->Render() : -1);
+		if (ret < 0)
+			return ret;
+	}
+	return ret;
+}
+
+int PageSet::Update(void)
+{
+	int ret;
+
+	ret = (mCurrentPage ? mCurrentPage->Update() : -1);
+	if (ret < 0 || ret > 1)
+		return ret;
+
+	std::vector<Page*>::iterator iter;
+
+	for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) {
+		ret = ((*iter) ? (*iter)->Update() : -1);
+		if (ret < 0)
+			return ret;
+	}
+	return ret;
+}
+
+int PageSet::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if (!mOverlays.empty())
+		return mOverlays.back()->NotifyTouch(state, x, y);
+
+	return (mCurrentPage ? mCurrentPage->NotifyTouch(state, x, y) : -1);
+}
+
+int PageSet::NotifyKey(int key, bool down)
+{
+	if (!mOverlays.empty())
+		return mOverlays.back()->NotifyKey(key, down);
+
+	return (mCurrentPage ? mCurrentPage->NotifyKey(key, down) : -1);
+}
+
+int PageSet::NotifyKeyboard(int key)
+{
+	if (!mOverlays.empty())
+		return mOverlays.back()->NotifyKeyboard(key);
+
+	return (mCurrentPage ? mCurrentPage->NotifyKeyboard(key) : -1);
+}
+
+int PageSet::SetKeyBoardFocus(int inFocus)
+{
+	if (!mOverlays.empty())
+		return mOverlays.back()->SetKeyBoardFocus(inFocus);
+
+	return (mCurrentPage ? mCurrentPage->SetKeyBoardFocus(inFocus) : -1);
+}
+
+int PageSet::NotifyVarChange(std::string varName, std::string value)
+{
+	std::vector<Page*>::iterator iter;
+
+	for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++)
+		(*iter)->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;
+	MemMapping map;
+
+	// Open the XML file
+	LOGINFO("Loading package: %s (%s)\n", name.c_str(), package.c_str());
+	if (package.size() > 4 && package.substr(package.size() - 4) != ".zip")
+	{
+		LOGINFO("Load XML directly\n");
+		tw_x_offset = TW_X_OFFSET;
+		tw_y_offset = TW_Y_OFFSET;
+		// 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
+	{
+		LOGINFO("Loading zip theme\n");
+		tw_x_offset = 0;
+		tw_y_offset = 0;
+		if (!TWFunc::Path_Exists(package))
+			return -1;
+		if (sysMapFile(package.c_str(), &map) != 0) {
+			LOGERR("Failed to map '%s'\n", package.c_str());
+			return -1;
+		}
+		if (mzOpenZipArchive(map.addr, map.length, &zip)) {
+			LOGERR("Unable to open zip archive '%s'\n", package.c_str());
+			sysReleaseMap(&map);
+			return -1;
+		}
+		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);
+		sysReleaseMap(&map);
+	}
+	return ret;
+
+error:
+	LOGERR("An internal error has occurred.\n");
+	if (pZip) {
+		mzCloseZipArchive(pZip);
+		sysReleaseMap(&map);
+	}
+	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 '%s'.\n", package.c_str());
+		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);
+	}
+}
+
+const ResourceManager* PageManager::GetResources()
+{
+	return (mCurrentSet ? mCurrentSet->GetResources() : 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;
+}
+
+xml_node<>* PageManager::FindStyle(std::string name)
+{
+	for (std::vector<xml_node<>*>::iterator itr = mCurrentSet->styles.begin(); itr != mCurrentSet->styles.end(); itr++) {
+		xml_node<>* node = (*itr)->first_node("style");
+
+		while (node) {
+			if (!node->first_attribute("name"))
+				continue;
+
+			if (name == node->first_attribute("name")->value())
+				return node;
+			node = node->next_sibling("style");
+		}
+	}
+	return NULL;
+}
+
+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)
+{
+	if(blankTimer.isScreenOff())
+		return 0;
+
+	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..81112f9
--- /dev/null
+++ b/gui/pages.hpp
@@ -0,0 +1,170 @@
+// pages.hpp - Base classes for page manager of GUI
+
+#ifndef _PAGES_HEADER_HPP
+#define _PAGES_HEADER_HPP
+
+#include "../minzip/Zip.h"
+#include <vector>
+#include <map>
+#include "rapidxml.hpp"
+using namespace rapidxml;
+
+enum TOUCH_STATE {
+	TOUCH_START = 0,
+	TOUCH_DRAG = 1,
+	TOUCH_RELEASE = 2,
+	TOUCH_HOLD = 3,
+	TOUCH_REPEAT = 4
+};
+
+struct COLOR {
+	unsigned char red;
+	unsigned char green;
+	unsigned char blue;
+	unsigned char alpha;
+	COLOR() : red(0), green(0), blue(0), alpha(0) {}
+	COLOR(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255)
+		: red(r), green(g), blue(b), alpha(a) {}
+};
+
+// 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);
+	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, int depth);
+};
+
+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);
+	const ResourceManager* GetResources();
+
+	// 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);
+
+	std::vector<xml_node<>*> styles;
+
+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;
+	std::vector<Page*> mOverlays; // Special case for popup dialogs and the lock screen
+	std::vector<xml_document<>*> mIncludedDocs;
+};
+
+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 const ResourceManager* GetResources();
+
+	// 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();
+
+	static xml_node<>* FindStyle(std::string name);
+
+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..102802d
--- /dev/null
+++ b/gui/partitionlist.cpp
@@ -0,0 +1,285 @@
+/*
+	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 <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+#include "../partitions.hpp"
+
+GUIPartitionList::GUIPartitionList(xml_node<>* node) : GUIScrollList(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	mIconSelected = mIconUnselected = NULL;
+	mUpdate = 0;
+	updateList = false;
+
+	child = FindNode(node, "icon");
+	if (child)
+	{
+		mIconSelected = LoadAttrImage(child, "selected");
+		mIconUnselected = LoadAttrImage(child, "unselected");
+	}
+
+	// Handle the result variable
+	child = FindNode(node, "data");
+	if (child)
+	{
+		attr = child->first_attribute("name");
+		if (attr)
+			mVariable = attr->value();
+		attr = child->first_attribute("selectedlist");
+		if (attr)
+			selectedList = attr->value();
+	}
+
+	int iconWidth = std::max(mIconSelected->GetWidth(), mIconUnselected->GetWidth());
+	int iconHeight = std::max(mIconSelected->GetHeight(), mIconUnselected->GetHeight());
+	SetMaxIconSize(iconWidth, iconHeight);
+
+	child = FindNode(node, "listtype");
+	if (child && (attr = child->first_attribute("name"))) {
+		ListType = attr->value();
+		updateList = true;
+	} else {
+		mList.clear();
+		LOGERR("No partition listtype specified for partitionlist GUI element\n");
+		return;
+	}
+}
+
+GUIPartitionList::~GUIPartitionList()
+{
+}
+
+int GUIPartitionList::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	// 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;
+			}
+		}
+	}
+
+	GUIScrollList::Update();
+
+	if (updateList) {
+		int listSize = 0;
+
+		// Completely update the list if needed -- Used primarily for 
+		// restore as the list for restore will change depending on what 
+		// partitions were backed up
+		mList.clear();
+		PartitionManager.Get_Partition_List(ListType, &mList);
+		SetVisibleListLocation(0);
+		updateList = false;
+		mUpdate = 1;
+		if (ListType == "backup" || ListType == "flashimg")
+			MatchList();
+	}
+
+	if (mUpdate) {
+		mUpdate = 0;
+		if (Render() == 0)
+			return 2;
+	}
+
+	return 0;
+}
+
+int GUIPartitionList::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIScrollList::NotifyVarChange(varName, value);
+
+	if(!isConditionTrue())
+		return 0;
+
+	if (varName == mVariable && !mUpdate)
+	{
+		if (ListType == "storage") {
+			currentValue = value;
+			SetPosition();
+		} else if (ListType == "backup") {
+			MatchList();
+		} else if (ListType == "restore") {
+			updateList = true;
+			SetVisibleListLocation(0);
+		}
+
+		mUpdate = 1;
+		return 0;
+	}
+	return 0;
+}
+
+void GUIPartitionList::SetPageFocus(int inFocus)
+{
+	GUIScrollList::SetPageFocus(inFocus);
+	if (inFocus) {
+		if (ListType == "storage" || ListType == "flashimg") {
+			DataManager::GetValue(mVariable, currentValue);
+			SetPosition();
+		}
+		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;
+		}
+	}
+}
+
+void GUIPartitionList::SetPosition() {
+	int listSize = mList.size();
+
+	SetVisibleListLocation(0);
+	for (int i = 0; i < listSize; i++) {
+		if (mList.at(i).Mount_Point == currentValue) {
+			mList.at(i).selected = 1;
+			SetVisibleListLocation(i);
+		} else {
+			mList.at(i).selected = 0;
+		}
+	}
+}
+
+size_t GUIPartitionList::GetItemCount()
+{
+	return mList.size();
+}
+
+void GUIPartitionList::RenderItem(size_t itemindex, int yPos, bool selected)
+{
+	// note: the "selected" parameter above is for the currently touched item
+	// don't confuse it with the more persistent "selected" flag per list item used below
+	ImageResource* icon = mList.at(itemindex).selected ? mIconSelected : mIconUnselected;
+	const std::string& text = mList.at(itemindex).Display_Name;
+
+	RenderStdItem(yPos, selected, icon, text.c_str());
+}
+
+void GUIPartitionList::NotifySelect(size_t item_selected)
+{
+	if (item_selected < mList.size()) {
+		int listSize = mList.size();
+		if (ListType == "mount") {
+			if (!mList.at(item_selected).selected) {
+				if (PartitionManager.Mount_By_Path(mList.at(item_selected).Mount_Point, true)) {
+					mList.at(item_selected).selected = 1;
+					PartitionManager.Add_MTP_Storage(mList.at(item_selected).Mount_Point);
+					mUpdate = 1;
+				}
+			} else {
+				if (PartitionManager.UnMount_By_Path(mList.at(item_selected).Mount_Point, true)) {
+					mList.at(item_selected).selected = 0;
+					mUpdate = 1;
+				}
+			}
+		} else if (!mVariable.empty()) {
+			if (ListType == "storage") {
+				int i;
+				std::string str = mList.at(item_selected).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;
+				}
+				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(item_selected).Display_Name = Part->Storage_Name + " (";
+						mList.at(item_selected).Display_Name += free_space;
+						mList.at(item_selected).Display_Name += "MB)";
+					}
+					mList.at(item_selected).selected = 1;
+					mUpdate = 1;
+
+					DataManager::SetValue(mVariable, str);
+				}
+			} else {
+				if (ListType == "flashimg") { // only one item can be selected for flashing images
+					for (int i=0; i<listSize; i++)
+						mList.at(i).selected = 0;
+				}
+				if (mList.at(item_selected).selected)
+					mList.at(item_selected).selected = 0;
+				else
+					mList.at(item_selected).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);
+			}
+		}
+	}
+}
diff --git a/gui/patternpassword.cpp b/gui/patternpassword.cpp
new file mode 100644
index 0000000..ed15285
--- /dev/null
+++ b/gui/patternpassword.cpp
@@ -0,0 +1,318 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.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"
+
+GUIPatternPassword::GUIPatternPassword(xml_node<>* node)
+	: GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	ResetActiveDots();
+	mTrackingTouch = false;
+	mNeedRender = true;
+
+	ConvertStrToColor("blue", &mDotColor);
+	ConvertStrToColor("white", &mActiveDotColor);
+	ConvertStrToColor("blue", &mLineColor);
+
+	mDotImage = mActiveDotImage = NULL;
+	mDotCircle = mActiveDotCircle = NULL;
+	mDotRadius = 50;
+	mLineWidth = 35;
+
+	mAction = NULL;
+	mUpdate = 0;
+
+	if (!node)
+		return;
+
+	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH, &mPlacement);
+
+	mAction = new GUIAction(node);
+
+	child = FindNode(node, "dot");
+	if(child)
+	{
+		mDotColor = LoadAttrColor(child, "color", mDotColor);
+		mActiveDotColor = LoadAttrColor(child, "activecolor", mActiveDotColor);
+		mDotRadius = LoadAttrIntScaleX(child, "radius", mDotRadius);
+
+		mDotImage = LoadAttrImage(child, "image");
+		mActiveDotImage = LoadAttrImage(child, "activeimage");
+	}
+
+	child = FindNode(node, "line");
+	if(child)
+	{
+		mLineColor = LoadAttrColor(child, "color", mLineColor);
+		mLineWidth = LoadAttrIntScaleX(child, "width", mLineWidth);
+	}
+
+	child = FindNode(node, "data");
+	if(child)
+		mPassVar = LoadAttrString(child, "name", "");
+
+
+	if(!mDotImage || !mDotImage->GetResource() || !mActiveDotImage || !mActiveDotImage->GetResource())
+	{
+		mDotCircle = gr_render_circle(mDotRadius, mDotColor.red, mDotColor.green, mDotColor.blue, mDotColor.alpha);
+		mActiveDotCircle = gr_render_circle(mDotRadius/2, mActiveDotColor.red, mActiveDotColor.green, mActiveDotColor.blue, mActiveDotColor.alpha);
+	}
+	else
+		mDotRadius = mDotImage->GetWidth()/2;
+
+	SetRenderPos(mRenderX, mRenderY, mRenderW, mRenderH);
+}
+
+GUIPatternPassword::~GUIPatternPassword()
+{
+	delete mDotImage;
+	delete mActiveDotImage;
+	delete mAction;
+
+	if(mDotCircle)
+		gr_free_surface(mDotCircle);
+
+	if(mActiveDotCircle)
+		gr_free_surface(mActiveDotCircle);
+}
+
+void GUIPatternPassword::ResetActiveDots()
+{
+	mConnectedDotsLen = 0;
+	mCurLineX = mCurLineY = -1;
+	for(int i = 0; i < 9; ++i)
+		mDots[i].active = false;
+}
+
+int GUIPatternPassword::SetRenderPos(int x, int y, int w, int h)
+{
+	mRenderX = x;
+	mRenderY = y;
+
+	if (w || h)
+	{
+		mRenderW = w;
+		mRenderH = h;
+
+		mAction->SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+		SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	}
+
+	CalculateDotPositions();
+	return 0;
+}
+
+void GUIPatternPassword::CalculateDotPositions(void)
+{
+	const int step_x = (mRenderW - mDotRadius*2) / 2;
+	const int step_y = (mRenderH - mDotRadius*2) / 2;
+	int x = mRenderX;
+	int y = mRenderY;
+
+	for(int r = 0; r < 3; ++r)
+	{
+		for(int c = 0; c < 3; ++c)
+		{
+			mDots[3*r+c].x = x;
+			mDots[3*r+c].y = y;
+			x += step_x;
+		}
+		x = mRenderX;
+		y += step_y;
+	}
+}
+
+int GUIPatternPassword::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	gr_color(mLineColor.red, mLineColor.green, mLineColor.blue, mLineColor.alpha);
+	for(size_t i = 1; i < mConnectedDotsLen; ++i) {
+		const Dot& dp = mDots[mConnectedDots[i-1]];
+		const Dot& dc = mDots[mConnectedDots[i]];
+		gr_line(dp.x + mDotRadius, dp.y + mDotRadius, dc.x + mDotRadius, dc.y + mDotRadius, mLineWidth);
+	}
+
+	if(mConnectedDotsLen > 0 && mTrackingTouch) {
+		const Dot& dc = mDots[mConnectedDots[mConnectedDotsLen-1]];
+		gr_line(dc.x + mDotRadius, dc.y + mDotRadius, mCurLineX, mCurLineY, mLineWidth);
+	}
+
+	for(int i = 0; i < 9; ++i) {
+		if(mDotCircle) {
+			gr_blit(mDotCircle, 0, 0, gr_get_width(mDotCircle), gr_get_height(mDotCircle), mDots[i].x, mDots[i].y);
+			if(mDots[i].active) {
+				gr_blit(mActiveDotCircle, 0, 0, gr_get_width(mActiveDotCircle), gr_get_height(mActiveDotCircle), mDots[i].x + mDotRadius/2, mDots[i].y + mDotRadius/2);
+			}
+		} else {
+			if(mDots[i].active) {
+				gr_blit(mActiveDotImage->GetResource(), 0, 0, mActiveDotImage->GetWidth(), mActiveDotImage->GetHeight(),
+						mDots[i].x + (mDotRadius - mActiveDotImage->GetWidth()/2), mDots[i].y + (mDotRadius - mActiveDotImage->GetHeight()/2));
+			} else {
+				gr_blit(mDotImage->GetResource(), 0, 0, mDotImage->GetWidth(), mDotImage->GetHeight(), mDots[i].x, mDots[i].y);
+			}
+		}
+	}
+	return 0;
+}
+
+int GUIPatternPassword::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	int res = mNeedRender ? 2 : 1;
+	mNeedRender = false;
+	return res;
+}
+
+bool GUIPatternPassword::IsInRect(int x, int y, int rx, int ry, int rw, int rh)
+{
+	return x >= rx && y >= ry && x <= rx+rw && y <= ry+rh;
+}
+
+int GUIPatternPassword::InDot(int x, int y)
+{
+	for(int i = 0; i < 9; ++i) {
+		if(IsInRect(x, y, mDots[i].x - mDotRadius*1.5, mDots[i].y - mDotRadius*1.5, mDotRadius*6, mDotRadius*6))
+			return i;
+	}
+	return -1;
+}
+
+bool GUIPatternPassword::DotUsed(int dot_idx)
+{
+	for(size_t i = 0; i < mConnectedDotsLen; ++i) {
+		if(mConnectedDots[i] == dot_idx)
+			return true;
+	}
+	return false;
+}
+
+void GUIPatternPassword::ConnectDot(int dot_idx)
+{
+	if(mConnectedDotsLen >= 9)
+	{
+		LOGERR("mConnectedDots in GUIPatternPassword has overflown!\n");
+		return;
+	}
+
+	mConnectedDots[mConnectedDotsLen++] = dot_idx;
+	mDots[dot_idx].active = true;
+}
+
+void GUIPatternPassword::ConnectIntermediateDots(int dot_idx)
+{
+	if(mConnectedDotsLen == 0)
+		return;
+
+	const int last_dot = mConnectedDots[mConnectedDotsLen-1];
+	int mid = -1;
+
+	// The line is vertical and has crossed a point in the middle
+	if(dot_idx%3 == last_dot%3 && abs(dot_idx - last_dot) > 3) {
+		mid = 3 + dot_idx%3;
+	// the line is horizontal and has crossed a point in the middle
+	} else if(dot_idx/3 == last_dot/3 && abs(dot_idx - last_dot) > 1) {
+		mid = (dot_idx/3)*3 + 1;
+	// the line is diagonal and has crossed the middle point
+	} else if((dot_idx == 0 && last_dot == 8) || (dot_idx == 8 && last_dot == 0) ||
+			(dot_idx == 2 && last_dot == 6) || (dot_idx == 6 && last_dot == 2)) {
+		mid = 4;
+	} else {
+		return;
+	}
+
+	if(!DotUsed(mid))
+		ConnectDot(mid);
+}
+
+int GUIPatternPassword::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if(!isConditionTrue())
+		return -1;
+
+	switch (state)
+	{
+		case TOUCH_START:
+		{
+			const int dot_idx = InDot(x, y);
+			if(dot_idx == -1)
+				break;
+
+			mTrackingTouch = true;
+			ResetActiveDots();
+			ConnectDot(dot_idx);
+			DataManager::Vibrate("tw_button_vibrate");
+			mCurLineX = x;
+			mCurLineY = y;
+			mNeedRender = true;
+			break;
+		}
+		case TOUCH_DRAG:
+		{
+			if(!mTrackingTouch)
+				break;
+
+			const int dot_idx = InDot(x, y);
+			if(dot_idx != -1 && !DotUsed(dot_idx))
+			{
+				ConnectIntermediateDots(dot_idx);
+				ConnectDot(dot_idx);
+				DataManager::Vibrate("tw_button_vibrate");
+			}
+
+			mCurLineX = x;
+			mCurLineY = y;
+			mNeedRender = true;
+			break;
+		}
+		case TOUCH_RELEASE:
+		{
+			if(!mTrackingTouch)
+				break;
+
+			mNeedRender = true;
+			mTrackingTouch = false;
+			PatternDrawn();
+			ResetActiveDots();
+			break;
+		}
+		default:
+			break;
+	}
+	return 0;
+}
+
+void GUIPatternPassword::PatternDrawn()
+{
+	if(!mPassVar.empty() && mConnectedDotsLen > 0)
+	{
+		std::string pass;
+		for(size_t i = 0; i < mConnectedDotsLen; ++i)
+			pass += '1' + mConnectedDots[i];
+		DataManager::SetValue(mPassVar, pass);
+	}
+
+	if(mAction)
+		mAction->doActions();
+}
diff --git a/gui/progressbar.cpp b/gui/progressbar.cpp
new file mode 100644
index 0000000..a478a40
--- /dev/null
+++ b/gui/progressbar.cpp
@@ -0,0 +1,226 @@
+// 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 = FindNode(node, "resource");
+	if (child)
+	{
+		mEmptyBar = LoadAttrImage(child, "empty");
+		mFullBar = LoadAttrImage(child, "full");
+	}
+
+	// Load the placement
+	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY);
+
+	// Load the data
+	child = FindNode(node, "data");
+	if (child)
+	{
+		mMinValVar = LoadAttrString(child, "min");
+		mMaxValVar = LoadAttrString(child, "max");
+		mCurValVar = LoadAttrString(child, "name");
+	}
+
+	mRenderW = mEmptyBar->GetWidth();
+	mRenderH = mEmptyBar->GetHeight();
+}
+
+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..41655c3
--- /dev/null
+++ b/gui/resources.cpp
@@ -0,0 +1,377 @@
+// resource.cpp - Source to manage GUI resources
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <iomanip>
+
+#include "../minzip/Zip.h"
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+#include "gui.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;
+}
+
+void Resource::LoadImage(ZipArchive* pZip, std::string file, gr_surface* source)
+{
+	if (ExtractResource(pZip, "images", file, ".png", TMP_RESOURCE_NAME) == 0)
+	{
+		res_create_surface(TMP_RESOURCE_NAME, source);
+		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, source);
+		unlink(TMP_RESOURCE_NAME);
+	}
+	else if (!pZip)
+	{
+		// File name in xml may have included .png so try without adding .png
+		res_create_surface(file.c_str(), source);
+	}
+}
+
+void Resource::CheckAndScaleImage(gr_surface source, gr_surface* destination, int retain_aspect)
+{
+	if (!source) {
+		*destination = NULL;
+		return;
+	}
+	if (get_scale_w() != 0 && get_scale_h() != 0) {
+		float scale_w = get_scale_w(), scale_h = get_scale_h();
+		if (retain_aspect) {
+			if (scale_w < scale_h)
+				scale_h = scale_w;
+			else
+				scale_w = scale_h;
+		}
+		if (res_scale_surface(source, destination, scale_w, scale_h)) {
+			LOGINFO("Error scaling image, using regular size.\n");
+			*destination = source;
+		}
+	} else {
+		*destination = source;
+	}
+}
+
+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 = scale_theme_min(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(TWRES "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;
+	gr_surface temp_surface = NULL;
+
+	mSurface = NULL;
+	if (!node) {
+		LOGERR("ImageResource node is NULL\n");
+		return;
+	}
+
+	if (node->first_attribute("filename"))
+		file = node->first_attribute("filename")->value();
+	else {
+		LOGERR("No filename specified for image resource.\n");
+		return;
+	}
+
+	bool retain_aspect = (node->first_attribute("retainaspect") != NULL);
+	// the value does not matter, if retainaspect is present, we assume that we want to retain it
+	LoadImage(pZip, file, &temp_surface);
+	CheckAndScaleImage(temp_surface, &mSurface, retain_aspect);
+}
+
+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();
+	else {
+		LOGERR("No filename specified for image resource.\n");
+		return;
+	}
+
+	bool retain_aspect = (node->first_attribute("retainaspect") != NULL);
+	// the value does not matter, if retainaspect is present, we assume that we want to retain it
+	for (;;)
+	{
+		std::ostringstream fileName;
+		fileName << file << std::setfill ('0') << std::setw (3) << fileNum;
+
+		gr_surface surface, temp_surface = NULL;
+		LoadImage(pZip, fileName.str(), &temp_surface);
+		CheckAndScaleImage(temp_surface, &surface, retain_aspect);
+		if (surface) {
+			mSurfaces.push_back(surface);
+			fileNum++;
+		} else
+			break; // Done loading animation images
+	}
+}
+
+AnimationResource::~AnimationResource()
+{
+	std::vector<gr_surface>::iterator it;
+
+	for (it = mSurfaces.begin(); it != mSurfaces.end(); ++it)
+		res_free_surface(*it);
+
+	mSurfaces.clear();
+}
+
+FontResource* ResourceManager::FindFont(const std::string& name) const
+{
+	for (std::vector<FontResource*>::const_iterator it = mFonts.begin(); it != mFonts.end(); ++it)
+		if (name == (*it)->GetName())
+			return *it;
+	return NULL;
+}
+
+ImageResource* ResourceManager::FindImage(const std::string& name) const
+{
+	for (std::vector<ImageResource*>::const_iterator it = mImages.begin(); it != mImages.end(); ++it)
+		if (name == (*it)->GetName())
+			return *it;
+	return NULL;
+}
+
+AnimationResource* ResourceManager::FindAnimation(const std::string& name) const
+{
+	for (std::vector<AnimationResource*>::const_iterator it = mAnimations.begin(); it != mAnimations.end(); ++it)
+		if (name == (*it)->GetName())
+			return *it;
+	return NULL;
+}
+
+std::string ResourceManager::FindString(const std::string& name) const
+{
+	std::map<std::string, std::string>::const_iterator it = mStrings.find(name);
+	if (it != mStrings.end())
+		return it->second;
+	return "[" + name + ("]");
+}
+
+ResourceManager::ResourceManager()
+{
+}
+
+void ResourceManager::LoadResources(xml_node<>* resList, ZipArchive* pZip)
+{
+	if (!resList)
+		return;
+
+	for (xml_node<>* child = resList->first_node(); child; child = child->next_sibling())
+	{
+		std::string type = child->name();
+		if (type == "resource") {
+			// legacy format : <resource type="...">
+			xml_attribute<>* attr = child->first_attribute("type");
+			type = attr ? attr->value() : "*unspecified*";
+		}
+
+		bool error = false;
+		if (type == "font")
+		{
+			FontResource* res = new FontResource(child, pZip);
+			if (res->GetResource())
+				mFonts.push_back(res);
+			else {
+				error = true;
+				delete res;
+			}
+		}
+		else if (type == "image")
+		{
+			ImageResource* res = new ImageResource(child, pZip);
+			if (res->GetResource())
+				mImages.push_back(res);
+			else {
+				error = true;
+				delete res;
+			}
+		}
+		else if (type == "animation")
+		{
+			AnimationResource* res = new AnimationResource(child, pZip);
+			if (res->GetResourceCount())
+				mAnimations.push_back(res);
+			else {
+				error = true;
+				delete res;
+			}
+		}
+		else if (type == "string")
+		{
+			if (xml_attribute<>* attr = child->first_attribute("name"))
+				mStrings[attr->value()] = child->value();
+			else
+				error = true;
+		}
+		else
+		{
+			LOGERR("Resource type (%s) not supported.\n", type.c_str());
+			error = true;
+		}
+
+		if (error)
+		{
+			std::string res_name;
+			if (child->first_attribute("name"))
+				res_name = child->first_attribute("name")->value();
+			if (res_name.empty() && child->first_attribute("filename"))
+				res_name = child->first_attribute("filename")->value();
+
+			if (!res_name.empty()) {
+				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());
+		}
+	}
+}
+
+ResourceManager::~ResourceManager()
+{
+	for (std::vector<FontResource*>::iterator it = mFonts.begin(); it != mFonts.end(); ++it)
+		delete *it;
+
+	for (std::vector<ImageResource*>::iterator it = mImages.begin(); it != mImages.end(); ++it)
+		delete *it;
+
+	for (std::vector<AnimationResource*>::iterator it = mAnimations.begin(); it != mAnimations.end(); ++it)
+		delete *it;
+}
diff --git a/gui/resources.hpp b/gui/resources.hpp
new file mode 100644
index 0000000..0eb3267
--- /dev/null
+++ b/gui/resources.hpp
@@ -0,0 +1,110 @@
+// resources.hpp - Base classes for resource management of GUI
+
+#ifndef _RESOURCE_HEADER
+#define _RESOURCE_HEADER
+
+#include <string>
+#include <vector>
+#include <map>
+
+struct ZipArchive;
+
+extern "C" {
+#include "../minuitwrp/minui.h"
+}
+
+// Base Objects
+class Resource
+{
+public:
+	Resource(xml_node<>* node, ZipArchive* pZip);
+	virtual ~Resource() {}
+
+public:
+	std::string GetName() { return mName; }
+
+private:
+	std::string mName;
+
+protected:
+	static int ExtractResource(ZipArchive* pZip, std::string folderName, std::string fileName, std::string fileExtn, std::string destFile);
+	static void LoadImage(ZipArchive* pZip, std::string file, gr_surface* source);
+	static void CheckAndScaleImage(gr_surface source, gr_surface* destination, int retain_aspect);
+};
+
+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:
+	void* GetResource() { return this ? mFont : NULL; }
+	int GetHeight() { return gr_getMaxFontHeight(this ? mFont : NULL); }
+
+protected:
+	void* mFont;
+	Type m_type;
+};
+
+class ImageResource : public Resource
+{
+public:
+	ImageResource(xml_node<>* node, ZipArchive* pZip);
+	virtual ~ImageResource();
+
+public:
+	gr_surface GetResource() { return this ? mSurface : NULL; }
+	int GetWidth() { return gr_get_width(this ? mSurface : NULL); }
+	int GetHeight() { return gr_get_height(this ? mSurface : NULL); }
+
+protected:
+	gr_surface mSurface;
+};
+
+class AnimationResource : public Resource
+{
+public:
+	AnimationResource(xml_node<>* node, ZipArchive* pZip);
+	virtual ~AnimationResource();
+
+public:
+	gr_surface GetResource() { return (!this || mSurfaces.empty()) ? NULL : mSurfaces.at(0); }
+	gr_surface GetResource(int entry) { return (!this || mSurfaces.empty()) ? NULL : mSurfaces.at(entry); }
+	int GetWidth() { return gr_get_width(this ? GetResource() : NULL); }
+	int GetHeight() { return gr_get_height(this ? GetResource() : NULL); }
+	int GetResourceCount() { return mSurfaces.size(); }
+
+protected:
+	std::vector<gr_surface> mSurfaces;
+};
+
+class ResourceManager
+{
+public:
+	ResourceManager();
+	virtual ~ResourceManager();
+	void LoadResources(xml_node<>* resList, ZipArchive* pZip);
+
+public:
+	FontResource* FindFont(const std::string& name) const;
+	ImageResource* FindImage(const std::string& name) const;
+	AnimationResource* FindAnimation(const std::string& name) const;
+	std::string FindString(const std::string& name) const;
+
+private:
+	std::vector<FontResource*> mFonts;
+	std::vector<ImageResource*> mImages;
+	std::vector<AnimationResource*> mAnimations;
+	std::map<std::string, std::string> mStrings;
+};
+
+#endif  // _RESOURCE_HEADER
diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp
new file mode 100644
index 0000000..ec42fe6
--- /dev/null
+++ b/gui/scrolllist.cpp
@@ -0,0 +1,609 @@
+/*
+	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 <string.h>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+
+const float SCROLLING_SPEED_DECREMENT = 0.9; // friction
+const int SCROLLING_FLOOR = 2; // minimum pixels for scrolling to stop
+
+GUIScrollList::GUIScrollList(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	firstDisplayedItem = mItemSpacing = mFontHeight = mSeparatorH = y_offset = scrollingSpeed = 0;
+	maxIconWidth = maxIconHeight =  mHeaderIconHeight = mHeaderIconWidth = 0;
+	mHeaderSeparatorH = mHeaderH = actualItemHeight = 0;
+	mHeaderIsStatic = false;
+	mBackground = mHeaderIcon = NULL;
+	mFont = NULL;
+	mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
+	mFastScrollRectCurrentY = mFastScrollRectCurrentH = mFastScrollRectTouchY = 0;
+	lastY = last2Y = fastScroll = 0;
+	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;
+	allowSelection = true;
+	selectedItem = NO_ITEM;
+
+	// Load header text
+	// note: node can be NULL for the emergency console
+	child = node ? node->first_node("text") : NULL;
+	if (child)  mHeaderText = child->value();
+	// Simple way to check for static state
+	mLastHeaderValue = gui_parse_text(mHeaderText);
+	mHeaderIsStatic = (mLastHeaderValue == mHeaderText);
+
+	mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlightColor);
+
+	child = FindNode(node, "background");
+	if (child)
+	{
+		mBackground = LoadAttrImage(child, "resource");
+		mBackgroundColor = LoadAttrColor(child, "color");
+	}
+
+	// Load the placement
+	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+
+	// Load the font, and possibly override the color
+	child = FindNode(node, "font");
+	if (child)
+	{
+		mFont = LoadAttrFont(child, "resource");
+		mFontColor = LoadAttrColor(child, "color");
+		mFontHighlightColor = LoadAttrColor(child, "highlightcolor", mFontColor);
+		mItemSpacing = LoadAttrIntScaleY(child, "spacing");
+	}
+
+	// Load the separator if it exists
+	child = FindNode(node, "separator");
+	if (child)
+	{
+		mSeparatorColor = LoadAttrColor(child, "color");
+		mSeparatorH = LoadAttrIntScaleY(child, "height");
+	}
+
+	// Fast scroll
+	child = FindNode(node, "fastscroll");
+	if (child)
+	{
+		mFastScrollLineColor = LoadAttrColor(child, "linecolor");
+		mFastScrollRectColor = LoadAttrColor(child, "rectcolor");
+
+		mFastScrollW = LoadAttrIntScaleX(child, "w");
+		mFastScrollLineW = LoadAttrIntScaleX(child, "linew");
+		mFastScrollRectW = LoadAttrIntScaleX(child, "rectw");
+		mFastScrollRectH = LoadAttrIntScaleY(child, "recth");
+	}
+
+	// Retrieve the line height
+	mFontHeight = mFont->GetHeight();
+	actualItemHeight = mFontHeight + mItemSpacing + mSeparatorH;
+
+	// Load the header if it exists
+	child = FindNode(node, "header");
+	if (child)
+	{
+		mHeaderH = mFontHeight;
+		mHeaderIcon = LoadAttrImage(child, "icon");
+		mHeaderBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor);
+		mHeaderFontColor = LoadAttrColor(child, "textcolor", mFontColor);
+		mHeaderSeparatorColor = LoadAttrColor(child, "separatorcolor", mSeparatorColor);
+		mHeaderSeparatorH = LoadAttrIntScaleY(child, "separatorheight", mSeparatorH);
+
+		if (mHeaderIcon && mHeaderIcon->GetResource())
+		{
+			mHeaderIconWidth = mHeaderIcon->GetWidth();
+			mHeaderIconHeight = mHeaderIcon->GetHeight();
+			if (mHeaderIconHeight > mHeaderH)
+				mHeaderH = mHeaderIconHeight;
+			if (mHeaderIconWidth > maxIconWidth)
+				maxIconWidth = mHeaderIconWidth;
+		}
+
+		mHeaderH += mItemSpacing + mHeaderSeparatorH;
+		if (mHeaderH < actualItemHeight)
+			mHeaderH = actualItemHeight;
+	}
+
+	if (actualItemHeight / 3 > 6)
+		touchDebounce = actualItemHeight / 3;
+}
+
+GUIScrollList::~GUIScrollList()
+{
+}
+
+void GUIScrollList::SetMaxIconSize(int w, int h)
+{
+	if (w > maxIconWidth)
+		maxIconWidth = w;
+	if (h > maxIconHeight)
+		maxIconHeight = h;
+	if (maxIconHeight > mFontHeight) {
+		actualItemHeight = maxIconHeight + mItemSpacing + mSeparatorH;
+		if (mHeaderH > 0 && actualItemHeight > mHeaderH)
+			mHeaderH = actualItemHeight;
+	}
+}
+
+void GUIScrollList::SetVisibleListLocation(size_t list_index)
+{
+	// This will make sure that the item indicated by list_index is visible on the screen
+	size_t lines = GetDisplayItemCount();
+
+	if (list_index <= (unsigned)firstDisplayedItem) {
+		// list_index is above the currently displayed items, put the selected item at the very top
+		firstDisplayedItem = list_index;
+		y_offset = 0;
+	} else if (list_index >= firstDisplayedItem + lines) {
+		// list_index is below the currently displayed items, put the selected item at the very bottom
+		firstDisplayedItem = list_index - lines + 1;
+		if (GetDisplayRemainder() != 0) {
+			// There's a partial row displayed, set the scrolling offset so that the selected item really is at the very bottom
+			firstDisplayedItem--;
+			y_offset = GetDisplayRemainder() - actualItemHeight;
+		} else {
+			// There's no partial row so zero out the offset
+			y_offset = 0;
+		}
+		if (firstDisplayedItem < 0)
+			firstDisplayedItem = 0;
+	}
+	scrollingSpeed = 0; // stop kinetic scrolling on setting visible location
+	mUpdate = 1;
+}
+
+int GUIScrollList::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	// First step, fill background
+	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha);
+	gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
+
+	// don't paint outside of the box
+	gr_clip(mRenderX, mRenderY, mRenderW, mRenderH);
+
+	// Next, render the background resource (if it exists)
+	if (mBackground && mBackground->GetResource())
+	{
+		int BackgroundW = mBackground->GetWidth();
+		int BackgroundH = mBackground->GetHeight();
+		int BackgroundX = mRenderX + ((mRenderW - BackgroundW) / 2);
+		int BackgroundY = mRenderY + ((mRenderH - BackgroundH) / 2);
+		gr_blit(mBackground->GetResource(), 0, 0, BackgroundW, BackgroundH, BackgroundX, BackgroundY);
+	}
+
+	// This tells us how many full lines we can actually render
+	size_t lines = GetDisplayItemCount();
+
+	size_t listSize = GetItemCount();
+	int listW = mRenderW; // this is only used for the separators - the list items are rendered in the full width of the list
+
+	if (listSize <= lines) {
+		hasScroll = false;
+		scrollingSpeed = 0;
+		lines = listSize;
+		y_offset = 0;
+	} else {
+		hasScroll = true;
+		listW -= mFastScrollW; // space for fast scroll
+		lines++;
+		if (lines < listSize)
+			lines++;
+	}
+
+	int yPos = mRenderY + mHeaderH + y_offset;
+
+	// render all visible items
+	for (size_t line = 0; line < lines; line++)
+	{
+		size_t itemindex = line + firstDisplayedItem;
+		if (itemindex >= listSize)
+			break;
+
+		RenderItem(itemindex, yPos, itemindex == selectedItem);
+
+		// Add the separator
+		gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, mSeparatorColor.alpha);
+		gr_fill(mRenderX, yPos + actualItemHeight - mSeparatorH, listW, mSeparatorH);
+
+		// Move the yPos
+		yPos += actualItemHeight;
+	}
+
+	// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
+	yPos = mRenderY;
+	if (mHeaderH > 0) {
+		// First step, fill background
+		gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, mHeaderBackgroundColor.alpha);
+		gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
+
+		int IconOffsetX = 0;
+
+		// render the icon if it exists
+		if (mHeaderIcon && mHeaderIcon->GetResource())
+		{
+			gr_blit(mHeaderIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - maxIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
+			IconOffsetX = maxIconWidth;
+		}
+
+		// render the text
+		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, mHeaderFontColor.alpha);
+		gr_textEx(mRenderX + IconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastHeaderValue.c_str(), mFont->GetResource());
+
+		// Add the separator
+		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, mHeaderSeparatorColor.alpha);
+		gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
+	}
+
+	// reset clipping
+	gr_noclip();
+
+	// render fast scroll
+	if (hasScroll) {
+		int fWidth = mRenderW - listW;
+		int fHeight = mRenderH - mHeaderH;
+		int centerX = listW + mRenderX + fWidth / 2;
+
+		// first determine the total list height and where we are in the list
+		int totalHeight = GetItemCount() * actualItemHeight; // total height of the full list in pixels
+		int topPos = firstDisplayedItem * actualItemHeight - y_offset;
+
+		// now scale it proportionally to the scrollbar height
+		int boxH = fHeight * fHeight / totalHeight; // proportional height of the displayed portion
+		boxH = std::max(boxH, mFastScrollRectH); // but keep a minimum height
+		int boxY = (fHeight - boxH) * topPos / (totalHeight - fHeight); // pixels relative to top of list
+		int boxW = mFastScrollRectW;
+
+		int x = centerX - boxW / 2;
+		int y = mRenderY + mHeaderH + boxY;
+
+		// line above and below box (needs to be split because box can be transparent)
+		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, mFastScrollLineColor.alpha);
+		gr_fill(centerX - mFastScrollLineW / 2, mRenderY + mHeaderH, mFastScrollLineW, boxY);
+		gr_fill(centerX - mFastScrollLineW / 2, y + boxH, mFastScrollLineW, fHeight - boxY - boxH);
+
+		// box
+		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, mFastScrollRectColor.alpha);
+		gr_fill(x, y, boxW, boxH);
+
+		mFastScrollRectCurrentY = boxY;
+		mFastScrollRectCurrentH = boxH;
+	}
+	mUpdate = 0;
+	return 0;
+}
+
+void GUIScrollList::RenderItem(size_t itemindex, int yPos, bool selected)
+{
+	RenderStdItem(yPos, selected, NULL, "implement RenderItem!");
+}
+
+void GUIScrollList::RenderStdItem(int yPos, bool selected, ImageResource* icon, const char* text, int iconAndTextH)
+{
+	if (hasHighlightColor && selected) {
+		// Highlight the item background of the selected item
+		gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
+		gr_fill(mRenderX, yPos, mRenderW, actualItemHeight);
+	}
+
+	if (selected) {
+		// Use the highlight color for the font
+		gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, mFontHighlightColor.alpha);
+	} else {
+		// Set the color for the font
+		gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
+	}
+
+	if (!iconAndTextH)
+		iconAndTextH = actualItemHeight;
+
+	// render icon
+	if (icon && icon->GetResource()) {
+		int iconH = icon->GetHeight();
+		int iconW = icon->GetWidth();
+		int iconY = yPos + (iconAndTextH - iconH) / 2;
+		int iconX = mRenderX + (maxIconWidth - iconW) / 2;
+		gr_blit(icon->GetResource(), 0, 0, iconW, iconH, iconX, iconY);
+	}
+
+	// render label text
+	int textX = mRenderX + maxIconWidth + 5;
+	int textY = yPos + (iconAndTextH - mFontHeight) / 2;
+	gr_textEx(textX, textY, text, mFont->GetResource());
+}
+
+int GUIScrollList::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mHeaderIsStatic) {
+		std::string newValue = gui_parse_text(mHeaderText);
+		if (mLastHeaderValue != newValue) {
+			mLastHeaderValue = newValue;
+			mUpdate = 1;
+		}
+	}
+
+	// Handle kinetic scrolling
+	// maximum number of items to scroll per update
+	float maxItemsScrolledPerFrame = std::max(2.5, float(GetDisplayItemCount() / 4) + 0.5);
+
+	int maxScrollDistance = actualItemHeight * maxItemsScrolledPerFrame;
+	int oldScrollingSpeed = scrollingSpeed;
+	if (scrollingSpeed == 0) {
+		// Do nothing
+		return 0;
+	} else if (scrollingSpeed > 0) {
+		if (scrollingSpeed < maxScrollDistance)
+			y_offset += scrollingSpeed;
+		else
+			y_offset += maxScrollDistance;
+		scrollingSpeed *= SCROLLING_SPEED_DECREMENT;
+		if (scrollingSpeed == oldScrollingSpeed)
+			--scrollingSpeed;
+	} else if (scrollingSpeed < 0) {
+		if (abs(scrollingSpeed) < maxScrollDistance)
+			y_offset += scrollingSpeed;
+		else
+			y_offset -= maxScrollDistance;
+		scrollingSpeed *= SCROLLING_SPEED_DECREMENT;
+		if (scrollingSpeed == oldScrollingSpeed)
+			++scrollingSpeed;
+	}
+	if (abs(scrollingSpeed) < SCROLLING_FLOOR)
+		scrollingSpeed = 0;
+	HandleScrolling();
+	mUpdate = 1;
+
+	return 0;
+}
+
+size_t GUIScrollList::HitTestItem(int x, int y)
+{
+	// We only care about y position
+	if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH)
+		return NO_ITEM;
+
+	int startSelection = (y - mRenderY - mHeaderH);
+
+	// Locate the correct item
+	size_t actualSelection = firstDisplayedItem;
+	int selectY = y_offset;
+	while (selectY + actualItemHeight < startSelection) {
+		selectY += actualItemHeight;
+		actualSelection++;
+	}
+
+	if (actualSelection < GetItemCount())
+		return actualSelection;
+
+	return NO_ITEM;
+}
+
+int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if(!isConditionTrue())
+		return -1;
+
+	switch (state)
+	{
+	case TOUCH_START:
+		if (hasScroll && x >= mRenderX + mRenderW - mFastScrollW) {
+			fastScroll = 1; // Initial touch is in the fast scroll region
+			int fastScrollBoxTop = mFastScrollRectCurrentY + mRenderY + mHeaderH;
+			int fastScrollBoxBottom = fastScrollBoxTop + mFastScrollRectCurrentH;
+			if (y >= fastScrollBoxTop && y < fastScrollBoxBottom)
+				// user grabbed the fastscroll bar
+				// try to keep the initially touched part of the scrollbar under the finger
+				mFastScrollRectTouchY = y - fastScrollBoxTop;
+			else
+				// user tapped outside the fastscroll bar
+				// center fastscroll rect on the initial touch position
+				mFastScrollRectTouchY = mFastScrollRectCurrentH / 2;
+		}
+
+		if (scrollingSpeed != 0) {
+			selectedItem = NO_ITEM; // this allows the user to tap the list to stop the scrolling without selecting the item they tap
+			scrollingSpeed = 0; // stop scrolling on a new touch
+		} else if (!fastScroll && allowSelection) {
+			// find out which item the user touched
+			selectedItem = HitTestItem(x, y);
+		}
+		if (selectedItem != NO_ITEM)
+			mUpdate = 1;
+		lastY = last2Y = y;
+		break;
+
+	case TOUCH_DRAG:
+		if (fastScroll)
+		{
+			int relY = y - mRenderY - mHeaderH; // touch position relative to window
+			int windowH = mRenderH - mHeaderH;
+			int totalHeight = GetItemCount() * actualItemHeight; // total height of the full list in pixels
+
+			// calculate new top position of the fastscroll bar relative to window
+			int newY = relY - mFastScrollRectTouchY;
+			// keep it fully inside the list
+			newY = std::min(std::max(newY, 0), windowH - mFastScrollRectCurrentH);
+
+			// now compute the new scroll position for the list
+			int newTopPos = newY * (totalHeight - windowH) / (windowH - mFastScrollRectCurrentH); // new top pixel of list
+			newTopPos = std::min(newTopPos, totalHeight - windowH); // account for rounding errors
+			firstDisplayedItem = newTopPos / actualItemHeight;
+			y_offset = - newTopPos % actualItemHeight;
+
+			selectedItem = NO_ITEM;
+			mUpdate = 1;
+			scrollingSpeed = 0; // prevent kinetic scrolling when using fast scroll
+			break;
+		}
+
+		// Provide some debounce on initial touches
+		if (selectedItem != NO_ITEM && abs(y - lastY) < touchDebounce) {
+			mUpdate = 1;
+			break;
+		}
+
+		selectedItem = NO_ITEM; // nothing is selected because we dragged too far
+		// Handle scrolling
+		if (hasScroll) {
+			y_offset += y - lastY; // adjust the scrolling offset based on the difference between the starting touch and the current touch
+			last2Y = lastY; // keep track of previous y locations so that we can tell how fast to scroll for kinetic scrolling
+			lastY = y; // update last touch to the current touch so we can tell how far and what direction we scroll for the next touch event
+
+			HandleScrolling();
+		} else
+			y_offset = 0;
+		mUpdate = 1;
+		break;
+
+	case TOUCH_RELEASE:
+		if (fastScroll)
+			mUpdate = 1; // get rid of touch effects on the fastscroll bar
+		fastScroll = 0;
+		if (selectedItem != NO_ITEM) {
+			// We've selected an item!
+			NotifySelect(selectedItem);
+			mUpdate = 1;
+
+			DataManager::Vibrate("tw_button_vibrate");
+			selectedItem = NO_ITEM;
+		} else {
+			// Start kinetic scrolling
+			scrollingSpeed = lastY - last2Y;
+			if (abs(scrollingSpeed) < touchDebounce)
+				scrollingSpeed = 0;
+		}
+	case TOUCH_REPEAT:
+	case TOUCH_HOLD:
+		break;
+	}
+	return 0;
+}
+
+void GUIScrollList::HandleScrolling()
+{
+	// handle dragging downward, scrolling upward
+	// the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
+	while(firstDisplayedItem && y_offset > 0) {
+		firstDisplayedItem--;
+		y_offset -= actualItemHeight;
+	}
+	if (firstDisplayedItem == 0 && y_offset > 0) {
+		y_offset = 0; // user kept dragging downward past the top of the list, so always reset the offset to 0 since we can't scroll any further in this direction
+		scrollingSpeed = 0; // stop kinetic scrolling
+	}
+
+	// handle dragging upward, scrolling downward
+	int totalSize = GetItemCount();
+	int lines = GetDisplayItemCount(); // number of full lines our list can display at once
+	int bottom_offset = GetDisplayRemainder() - actualItemHeight; // extra display area that can display a partial line for per pixel scrolling
+
+	// the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
+	while (firstDisplayedItem + lines + (bottom_offset ? 1 : 0) < totalSize && abs(y_offset) > actualItemHeight) {
+		firstDisplayedItem++;
+		y_offset += actualItemHeight;
+	}
+	// Check if we dragged too far, set the list at the bottom and adjust offset as needed
+	if (bottom_offset != 0 && firstDisplayedItem + lines + 1 >= totalSize && y_offset <= bottom_offset) {
+		firstDisplayedItem = totalSize - lines - 1;
+		y_offset = bottom_offset;
+		scrollingSpeed = 0; // stop kinetic scrolling
+	} else if (firstDisplayedItem + lines >= totalSize && y_offset < 0) {
+		firstDisplayedItem = totalSize - lines;
+		y_offset = 0;
+		scrollingSpeed = 0; // stop kinetic scrolling
+	}
+}
+
+int GUIScrollList::GetDisplayItemCount()
+{
+	return (mRenderH - mHeaderH) / (actualItemHeight);
+}
+
+int GUIScrollList::GetDisplayRemainder()
+{
+	return (mRenderH - mHeaderH) % actualItemHeight;
+}
+
+int GUIScrollList::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 (mLastHeaderValue != newValue) {
+			mLastHeaderValue = newValue;
+			firstDisplayedItem = 0;
+			y_offset = 0;
+			scrollingSpeed = 0; // stop kinetic scrolling on variable changes
+			mUpdate = 1;
+		}
+	}
+	return 0;
+}
+
+int GUIScrollList::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 GUIScrollList::SetPageFocus(int inFocus)
+{
+	if (inFocus) {
+		NotifyVarChange("", ""); // This forces a check for the header text
+		scrollingSpeed = 0; // stop kinetic scrolling on page changes
+		mUpdate = 1;
+	}
+}
diff --git a/gui/slider.cpp b/gui/slider.cpp
new file mode 100644
index 0000000..a388447
--- /dev/null
+++ b/gui/slider.cpp
@@ -0,0 +1,201 @@
+// 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;
+	sSliderLabel = NULL;
+	sSlider = NULL;
+	sSliderUsed = NULL;
+	sTouch = NULL;
+	sTouchW = 20;
+
+	if (!node)
+	{
+		LOGERR("GUISlider created without XML node\n");
+		return;
+	}
+
+	// Load the resources
+	child = FindNode(node, "resource");
+	if (child)
+	{
+		sSlider = LoadAttrImage(child, "base");
+		sSliderUsed = LoadAttrImage(child, "used");
+		sTouch = LoadAttrImage(child, "touch");
+	}
+
+	// Load the text label
+	sSliderLabel = new GUIText(node);
+	if (sSliderLabel->Render() < 0)
+	{
+		delete sSliderLabel;
+		sSliderLabel = NULL;
+	}
+
+	// Load the placement
+	Placement TextPlacement = CENTER;
+	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH, &TextPlacement);
+
+	mRenderW = sSlider->GetWidth();
+	mRenderH = sSlider->GetHeight();
+	if (TextPlacement == CENTER || TextPlacement == CENTER_X_ONLY) {
+		mRenderX = mRenderX - (mRenderW / 2);
+		if (TextPlacement == CENTER) {
+			mRenderY = mRenderY - (mRenderH / 2);
+		}
+	}
+	if (sSliderLabel) {
+		int sTextX = mRenderX + (mRenderW / 2);
+		int w, h;
+		sSliderLabel->GetCurrentBounds(w, h);
+		int sTextY = mRenderY + ((mRenderH - h) / 2);
+		sSliderLabel->SetRenderPos(sTextX, sTextY);
+	}
+	if (sTouch && sTouch->GetResource())
+	{
+		sTouchW = sTouch->GetWidth();  // Width of the "touch image" that follows the touch (arrow)
+		sTouchH = sTouch->GetHeight(); // 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;
+	delete sSliderLabel;
+}
+
+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)));
+
+	if (sSliderLabel) {
+		int ret = sSliderLabel->Render();
+		if (ret < 0)		return ret;
+	}
+
+	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..3974c37
--- /dev/null
+++ b/gui/slidervalue.cpp
@@ -0,0 +1,422 @@
+// 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 = FindNode(node, "font");
+	if (child)
+	{
+		mFont = LoadAttrFont(child, "resource");
+		mTextColor = LoadAttrColor(child, "color", mTextColor);
+	}
+
+	// Load the placement
+	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW);
+
+	child = FindNode(node, "colors");
+	if (child)
+	{
+		mLineColor = LoadAttrColor(child, "line");
+		mSliderColor = LoadAttrColor(child, "slider");
+	}
+
+	child = FindNode(node, "resource");
+	if (child)
+	{
+		mBackgroundImage = LoadAttrImage(child, "background");
+		mHandleImage = LoadAttrImage(child, "handle");
+		mHandleHoverImage = LoadAttrImage(child, "handlehover");
+	}
+
+	child = FindNode(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 = FindNode(node, "dimensions");
+	if (child)
+	{
+		mLineH = LoadAttrIntScaleY(child, "lineh", mLineH);
+		mLinePadding = LoadAttrIntScaleX(child, "linepadding", mLinePadding);
+		mSliderW = LoadAttrIntScaleX(child, "sliderw", mSliderW);
+		mSliderH = LoadAttrIntScaleY(child, "sliderh", mSliderH);
+	}
+
+	mFontHeight = mFont->GetHeight();
+
+	if(mShowCurr)
+	{
+		int maxLen = std::max(strlen(mMinStr.c_str()), strlen(mMaxStr.c_str()));
+		mValueStr = new char[maxLen+1];
+	}
+
+	loadValue(true);
+
+	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 = mBackgroundImage->GetWidth();
+		mLineH = mBackgroundImage->GetHeight();
+	}
+	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..effb935
--- /dev/null
+++ b/gui/text.cpp
@@ -0,0 +1,170 @@
+// 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)
+{
+	mFont = NULL;
+	mIsStatic = 1;
+	mVarChanged = 0;
+	mFontHeight = 0;
+	maxWidth = 0;
+	charSkip = 0;
+	isHighlighted = false;
+
+	if (!node)
+		return;
+
+	// Load colors
+	mColor = LoadAttrColor(node, "color", COLOR(0,0,0,255));
+	mHighlightColor = LoadAttrColor(node, "highlightcolor", mColor);
+
+	// Load the font, and possibly override the color
+	mFont = LoadAttrFont(FindNode(node, "font"), "resource");
+	mColor = LoadAttrColor(FindNode(node, "font"), "color", mColor);
+	mHighlightColor = LoadAttrColor(FindNode(node, "font"), "highlightcolor", mColor);
+
+	// Load the placement
+	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH, &mPlacement);
+
+	xml_node<>* child = FindNode(node, "text");
+	if (child)  mText = child->value();
+
+	// Simple way to check for static state
+	mLastValue = gui_parse_text(mText);
+	if (mLastValue != mText)   mIsStatic = 0;
+
+	mFontHeight = mFont->GetHeight();
+}
+
+int GUIText::Render(void)
+{
+	if (!isConditionTrue())
+		return 0;
+
+	void* fontResource = NULL;
+	if (mFont)
+		fontResource = mFont->GetResource();
+
+	mLastValue = gui_parse_text(mText);
+	string 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 (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 = gui_parse_text(mText);
+	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 = gui_parse_text(mText);
+	w = gr_measureEx(mLastValue.c_str(), fontResource);
+	return 0;
+}
+
+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..9119612
--- /dev/null
+++ b/htcdumlock/htcdumlock.c
@@ -0,0 +1,364 @@
+/*
+ * 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 DEVID_MAX 64
+
+#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[DEVID_MAX] = { 0 };
+int verbose = 0, java = 0;
+
+void sanitize_device_id(void) {
+	const char* whitelist ="-._";
+	char str[DEVID_MAX];
+	char* c = str;
+
+	snprintf(str, DEVID_MAX, "%s", device_id);
+	memset(device_id, 0, strlen(device_id));
+	while (*c) {
+		if (isalnum(*c) || strchr(whitelist, *c))
+			strncat(device_id, c, 1);
+		c++;
+	}
+	return;
+}
+
+/* Recent HTC devices that still take advantage of dumlock
+   can safely rely on cmdline device_id retrieval */
+void get_device_id(void)
+{
+	FILE *fp;
+	char line[2048];
+	char* token;
+
+	// Check the cmdline to see if the serial number was supplied
+	fp = fopen("/proc/cmdline", "rt");
+	if (fp != NULL) {
+		fgets(line, sizeof(line), fp);
+		fclose(fp); // cmdline is only one line long
+
+		token = strtok(line, " ");
+		while (token) {
+			if (memcmp(token, CMDLINE_SERIALNO, CMDLINE_SERIALNO_LEN) == 0) {
+				token += CMDLINE_SERIALNO_LEN;
+				snprintf(device_id, DEVID_MAX, "%s", token);
+				sanitize_device_id(); // also removes newlines
+				return;
+			}
+			token = strtok(NULL, " ");
+		}
+	}
+
+	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, "serialno") == 0) {
+		printf("Error, dummy device ID detected!\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..864c431
--- /dev/null
+++ b/infomanager.cpp
@@ -0,0 +1,208 @@
+/*
+	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"
+#include "set_metadata.h"
+
+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);
+	tw_set_default_metadata(File.c_str());
+	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.cpp b/install.cpp
index c7d382f..b46bbdf 100644
--- a/install.cpp
+++ b/install.cpp
@@ -232,7 +232,7 @@
     ui->Print("Verifying update package...\n");
 
     int err;
-    err = verify_file(map.addr, map.length, loadedKeys, numKeys);
+    err = verify_file(map.addr, map.length);
     free(loadedKeys);
     LOGI("verify_file returned %d\n", err);
     if (err != VERIFY_SUCCESS) {
diff --git a/install.h b/install.h
index 680499d..092fd8f 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, bool* 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..490a245
--- /dev/null
+++ b/legacy_property_service.c
@@ -0,0 +1,216 @@
+/*
+ * 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 "../../bionic/libc/private/bionic_futex.h"
+
+#include <cutils/properties.h>
+
+#include "legacy_properties.h"
+
+#include <sys/mman.h>
+// Not available in 5.0
+//#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 long) pi) - ((unsigned long) 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..ab20bf5
--- /dev/null
+++ b/libblkid/Android.mk
@@ -0,0 +1,194 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libutil-linux
+LOCAL_MODULE_TAGS := optional
+#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DHAVE_LOFF_T -DHAVE_ERR_H -DHAVE_MEMPCPY -DHAVE_FSYNC
+LOCAL_SRC_FILES = 	lib/at.c \
+			lib/blkdev.c \
+			lib/canonicalize.c \
+			lib/colors.c \
+			lib/crc32.c \
+			lib/crc64.c \
+			lib/env.c \
+			lib/exec_shell.c \
+			lib/fileutils.c \
+			lib/ismounted.c \
+			lib/langinfo.c \
+			lib/linux_version.c \
+			lib/loopdev.c \
+			lib/mangle.c \
+			lib/match.c \
+			lib/mbsalign.c \
+			lib/md5.c \
+			lib/pager.c \
+			lib/path.c \
+			lib/procutils.c \
+			lib/randutils.c \
+			lib/setproctitle.c \
+			lib/strutils.c \
+			lib/sysfs.c \
+
+LOCAL_C_INCLUDES += \
+			$(LOCAL_PATH)/include \
+		    $(LOCAL_PATH)/src
+
+LOCAL_SHARED_LIBRARIES += libc
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libuuid
+LOCAL_MODULE_TAGS := optional
+#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DHAVE_LOFF_T -DHAVE_ERR_H -DHAVE_MEMPCPY -DHAVE_FSYNC
+LOCAL_SRC_FILES =	libuuid/src/clear.c \
+			libuuid/src/copy.c \
+			libuuid/src/isnull.c \
+			libuuid/src/parse.c \
+			libuuid/src/unpack.c \
+			libuuid/src/uuid_time.c \
+			libuuid/src/compare.c \
+			libuuid/src/gen_uuid.c \
+			libuuid/src/pack.c \
+			libuuid/src/test_uuid.c \
+			libuuid/src/unparse.c
+
+LOCAL_C_INCLUDES += 	$(LOCAL_PATH)/libuuid/src \
+			$(LOCAL_PATH)/include \
+			$(LOCAL_PATH)/src
+
+LOCAL_SHARED_LIBRARIES += libc libutil-linux
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libfdisk
+LOCAL_MODULE_TAGS := optional
+#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DHAVE_LOFF_T -DHAVE_ERR_H -DHAVE_MEMPCPY -DHAVE_FSYNC
+LOCAL_SRC_FILES = 	libfdisk/src/alignment.c \
+			libfdisk/src/context.c  \
+			libfdisk/src/init.c   \
+			libfdisk/src/partition.c \
+			libfdisk/src/sgi.c \
+			libfdisk/src/test.c \
+			libfdisk/src/ask.c \
+			libfdisk/src/dos.c \
+			libfdisk/src/iter.c \
+			libfdisk/src/parttype.c \
+			libfdisk/src/sun.c \
+			libfdisk/src/utils.c \
+			libfdisk/src/bsd.c \
+			libfdisk/src/gpt.c \
+			libfdisk/src/label.c \
+			libfdisk/src/script.c \
+			libfdisk/src/table.c
+
+LOCAL_C_INCLUDES += 	$(LOCAL_PATH)/libfdisk/src \
+			$(LOCAL_PATH)/include \
+			$(LOCAL_PATH)/libuuid/src \
+			$(LOCAL_PATH)/src
+
+LOCAL_SHARED_LIBRARIES += libc libutil-linux libuuid
+include $(BUILD_SHARED_LIBRARY)
+
+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 -DHAVE_ERR_H -DHAVE_MEMPCPY -DHAVE_FSYNC
+LOCAL_SRC_FILES = 	src/cache.c \
+			src/config.c \
+			src/dev.c \
+			src/devname.c \
+			src/devno.c \
+			src/encode.c \
+			src/evaluate.c \
+			src/getsize.c \
+			src/init.c \
+			src/llseek.c \
+			src/probe.c \
+			src/read.c \
+			src/resolve.c \
+			src/save.c \
+			src/tag.c \
+			src/verify.c \
+			src/version.c \
+			src/partitions/aix.c \
+			src/partitions/bsd.c \
+			src/partitions/dos.c \
+			src/partitions/gpt.c \
+			src/partitions/mac.c \
+			src/partitions/minix.c \
+			src/partitions/partitions.c \
+			src/partitions/sgi.c \
+			src/partitions/solaris_x86.c \
+			src/partitions/sun.c \
+			src/partitions/ultrix.c \
+			src/partitions/unixware.c \
+			src/superblocks/adaptec_raid.c  \
+			src/superblocks/bcache.c  \
+			src/superblocks/befs.c  \
+			src/superblocks/bfs.c \
+			src/superblocks/btrfs.c \
+			src/superblocks/cramfs.c \
+			src/superblocks/ddf_raid.c \
+			src/superblocks/drbd.c \
+			src/superblocks/drbdproxy_datalog.c \
+			src/superblocks/exfat.c \
+			src/superblocks/ext.c \
+			src/superblocks/f2fs.c \
+			src/superblocks/gfs.c \
+			src/superblocks/hfs.c \
+			src/superblocks/highpoint_raid.c \
+			src/superblocks/hpfs.c \
+			src/superblocks/iso9660.c \
+			src/superblocks/isw_raid.c \
+			src/superblocks/jfs.c \
+			src/superblocks/jmicron_raid.c \
+			src/superblocks/linux_raid.c \
+			src/superblocks/lsi_raid.c \
+			src/superblocks/luks.c \
+			src/superblocks/lvm.c \
+			src/superblocks/minix.c \
+			src/superblocks/netware.c \
+			src/superblocks/nilfs.c \
+			src/superblocks/ntfs.c \
+			src/superblocks/nvidia_raid.c \
+			src/superblocks/ocfs.c \
+			src/superblocks/promise_raid.c \
+			src/superblocks/refs.c \
+			src/superblocks/reiserfs.c \
+			src/superblocks/romfs.c \
+			src/superblocks/silicon_raid.c \
+			src/superblocks/squashfs.c \
+			src/superblocks/superblocks.c \
+			src/superblocks/swap.c \
+			src/superblocks/sysv.c \
+			src/superblocks/ubifs.c \
+			src/superblocks/udf.c \
+			src/superblocks/ufs.c \
+			src/superblocks/vfat.c \
+			src/superblocks/via_raid.c \
+			src/superblocks/vmfs.c \
+			src/superblocks/vxfs.c \
+			src/superblocks/xfs.c \
+			src/superblocks/zfs.c \
+			src/topology/dm.c \
+			src/topology/evms.c \
+			src/topology/ioctl.c \
+			src/topology/lvm.c \
+			src/topology/md.c \
+			src/topology/sysfs.c \
+			src/topology/topology.c \
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include \
+			$(LOCAL_PATH)/src
+
+LOCAL_SHARED_LIBRARIES += libc libutil-linux
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libblkid/COPYING b/libblkid/COPYING
new file mode 100644
index 0000000..be1a5b3
--- /dev/null
+++ b/libblkid/COPYING
@@ -0,0 +1,8 @@
+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.
+
+The complete text of the license is available in the
+../Documentation/licenses/COPYING.LGPLv2.1 file.
diff --git a/libblkid/Makemodule.am b/libblkid/Makemodule.am
new file mode 100644
index 0000000..b4f6f9c
--- /dev/null
+++ b/libblkid/Makemodule.am
@@ -0,0 +1,16 @@
+if BUILD_LIBBLKID
+
+include libblkid/src/Makemodule.am
+include libblkid/samples/Makemodule.am
+
+if ENABLE_GTK_DOC
+# Docs uses separate Makefiles
+SUBDIRS += libblkid/docs
+endif
+
+pkgconfig_DATA += libblkid/blkid.pc
+PATHFILES      += libblkid/blkid.pc
+dist_man_MANS  += libblkid/libblkid.3
+EXTRA_DIST     += libblkid/libblkid.3 libblkid/COPYING
+
+endif # BUILD_LIBBLKID
diff --git a/libblkid/blkid.pc.in b/libblkid/blkid.pc.in
new file mode 100644
index 0000000..40ec8a9
--- /dev/null
+++ b/libblkid/blkid.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@usrlib_execdir@
+includedir=@includedir@
+
+Name: blkid
+Description: Block device id library
+Version: @LIBBLKID_VERSION@
+Requires.private: uuid
+Cflags: -I${includedir}/blkid
+Libs: -L${libdir} -lblkid
diff --git a/libblkid/docs/.gitignore b/libblkid/docs/.gitignore
new file mode 100644
index 0000000..f91f93d
--- /dev/null
+++ b/libblkid/docs/.gitignore
@@ -0,0 +1,18 @@
+*-decl-list.txt
+*-decl.txt
+*-overrides.txt
+*-undeclared.txt
+*-undocumented.txt
+*-unused.txt
+*.args
+*.bak
+*.hierarchy
+*.interfaces
+*.prerequisites
+*.signals
+*.stamp
+*.types
+html/*
+tmpl/*
+version.xml
+xml/*
diff --git a/libblkid/docs/Makefile.am b/libblkid/docs/Makefile.am
new file mode 100644
index 0000000..dc6dd3e
--- /dev/null
+++ b/libblkid/docs/Makefile.am
@@ -0,0 +1,95 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.10 at least.
+AUTOMAKE_OPTIONS = 1.10
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=libblkid
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=../src
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=--deprecated-guards="BLKID_DISABLE_DEPRECATED"
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml
+MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space blkid
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=$(top_builddir)/libblkid/src/blkid.h
+CFILE_GLOB=$(top_srcdir)/libblkid/src/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+EXTRA_HFILES=
+
+# Header files to ignore when scanning. Use base file name, no paths
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
+IGNORE_HFILES=blkidP.h partitions.h superblocks.h \
+	      topology.h aix.h dos.h iso9660.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files = $(builddir)/version.xml $(srcdir)/libblkid-config.xml
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS=
+GTKDOC_LIBS=
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/config/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST += version.xml.in $(srcdir)/libblkid-config.xml
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES += version.xml
+
diff --git a/libblkid/docs/libblkid-config.xml b/libblkid/docs/libblkid-config.xml
new file mode 100644
index 0000000..89fbb7f
--- /dev/null
+++ b/libblkid/docs/libblkid-config.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<refentry id="libblkid-config">
+<refmeta>
+<refentrytitle role="top_of_page" id="libblkid-config.top_of_page">Config file</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBBLKID Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>Config file</refname>
+<refpurpose>config file to control paths and basic library behavior</refpurpose>
+</refnamediv>
+
+<refsect1 id="libblkid-config.description" role="desc">
+<title role="desc.title">Description</title>
+<para>
+The standard location of the
+/etc/blkid.conf config file can be overridden by the environment variable
+BLKID_CONF.  The following options control the libblkid library:
+</para>
+
+<variablelist role="params">
+ <varlistentry>
+  <term>SEND_UEVENT=<parameter>yes|not</parameter></term>
+  <listitem><simpara>
+   Sends uevent when /dev/disk/by-{label,uuid}/
+   symlink does not match with LABEL or UUID on the device. Default is "yes".
+  </simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+  <term>CACHE_FILE=<parameter>path</parameter></term>
+  <listitem><simpara>
+   Overrides the standard location of the cache file. This
+   setting can be overridden by the environment variable BLKID_FILE. Default is
+   /etc/blkid.tab.
+  </simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+  <term>EVALUATE=<parameter>method</parameter></term>
+  <listitem><simpara>
+   Defines LABEL and UUID evaluation method(s). Currently,
+   the libblkid library supports "udev" and "scan" methods. More than one methods
+   may be specified in a comma separated list. Default is "udev,scan". The "udev"
+   method uses udev /dev/disk/by-* symlinks and the "scan" method scans all
+   block devices from the /proc/partitions file.
+  </simpara></listitem>
+ </varlistentry>
+</variablelist>
+
+</refsect1>
+
+</refentry>
diff --git a/libblkid/docs/libblkid-docs.xml b/libblkid/docs/libblkid-docs.xml
new file mode 100644
index 0000000..1f412c1
--- /dev/null
+++ b/libblkid/docs/libblkid-docs.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+  <bookinfo>
+    <title>libblkid Reference Manual</title>
+    <releaseinfo>for libblkid version &version;</releaseinfo>
+    <copyright>
+      <year>2009-2013</year>
+      <holder>Karel Zak &lt;kzak@redhat.com&gt;</holder>
+    </copyright>
+  </bookinfo>
+
+  <part id="gtk">
+    <title>libblkid Overview</title>
+    <partintro>
+    <para>
+The libblkid library is used to identify block devices (disks) as to their
+content (e.g.  filesystem type, partitions) as well as extracting additional
+information such as filesystem labels/volume names, partitions, unique
+identifiers/serial numbers, etc.  A common use is to allow use of LABEL= and
+UUID= tags instead of hard-coding specific block device names into
+configuration files.
+    </para>
+    <para>
+The libblkid librray
+was written by Andreas Dilger for the ext2 filesystem utilties, with input
+from Ted Ts'o.  The library was subsequently heavily modified by Ted Ts'o.
+    </para>
+    <para>
+The low-level probing code, topology and partitions support was written
+by Karel Zak. Currently, the library is mainatned by Karel Zak.
+    </para>
+    <para>
+The library is part of the util-linux package since version 2.15 and is
+available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+    </para>
+  </partintro>
+  <xi:include href="xml/libblkid-config.xml"/>
+ </part>
+
+  <part>
+    <title>High-level</title>
+    <xi:include href="xml/evaluate.xml"/>
+    <xi:include href="xml/cache.xml"/>
+    <xi:include href="xml/search.xml"/>
+  </part>
+  <part>
+    <title>Low-level</title>
+    <xi:include href="xml/init.xml"/>
+    <xi:include href="xml/lowprobe.xml"/>
+    <xi:include href="xml/lowprobe-tags.xml"/>
+    <xi:include href="xml/superblocks.xml"/>
+    <xi:include href="xml/partitions.xml"/>
+    <xi:include href="xml/topology.xml"/>
+  </part>
+  <part>
+    <title>Common utils</title>
+    <xi:include href="xml/encode.xml"/>
+    <xi:include href="xml/misc.xml"/>
+  </part>
+
+  <index id="api-index-full">
+    <title>API Index</title>
+    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+  </index>
+</book>
diff --git a/libblkid/docs/libblkid-sections.txt b/libblkid/docs/libblkid-sections.txt
new file mode 100644
index 0000000..53cf84b
--- /dev/null
+++ b/libblkid/docs/libblkid-sections.txt
@@ -0,0 +1,201 @@
+<SECTION>
+<FILE>evaluate</FILE>
+blkid_evaluate_tag
+blkid_evaluate_spec
+</SECTION>
+
+<SECTION>
+<FILE>init</FILE>
+blkid_init_debug
+</SECTION>
+
+<SECTION>
+<FILE>cache</FILE>
+blkid_cache
+blkid_gc_cache
+blkid_get_cache
+blkid_put_cache
+blkid_probe_all
+blkid_probe_all_removable
+blkid_probe_all_new
+blkid_verify
+</SECTION>
+
+<SECTION>
+<FILE>search</FILE>
+blkid_dev
+blkid_dev_devname
+blkid_dev_has_tag
+blkid_dev_iterate
+blkid_dev_iterate_begin
+blkid_dev_iterate_end
+blkid_dev_next
+blkid_dev_set_search
+blkid_find_dev_with_tag
+blkid_get_dev
+blkid_get_devname
+blkid_get_tag_value
+blkid_tag_iterate
+blkid_tag_iterate_begin
+blkid_tag_iterate_end
+blkid_tag_next
+</SECTION>
+
+<SECTION>
+<FILE>lowprobe</FILE>
+blkid_probe
+blkid_free_probe
+blkid_new_probe
+blkid_new_probe_from_filename
+blkid_probe_get_devno
+blkid_probe_get_fd
+blkid_probe_get_offset
+blkid_probe_get_sectors
+blkid_probe_get_sectorsize
+blkid_probe_get_size
+blkid_probe_get_wholedisk_devno
+blkid_probe_is_wholedisk
+blkid_probe_set_device
+blkid_probe_step_back
+blkid_reset_probe
+</SECTION>
+
+<SECTION>
+<FILE>lowprobe-tags</FILE>
+blkid_do_fullprobe
+blkid_do_wipe
+blkid_do_probe
+blkid_do_safeprobe
+<SUBSECTION>
+blkid_probe_get_value
+blkid_probe_has_value
+blkid_probe_lookup_value
+blkid_probe_numof_values
+</SECTION>
+
+<SECTION>
+<FILE>partitions</FILE>
+blkid_partlist
+blkid_partition
+blkid_parttable
+blkid_probe_enable_partitions
+blkid_probe_set_partitions_flags
+blkid_probe_filter_partitions_type
+blkid_probe_invert_partitions_filter
+blkid_probe_reset_partitions_filter
+<SUBSECTION>
+blkid_known_pttype
+<SUBSECTION>
+blkid_partition_get_name
+blkid_partition_get_flags
+blkid_partition_get_partno
+blkid_partition_get_size
+blkid_partition_get_start
+blkid_partition_get_table
+blkid_partition_get_type
+blkid_partition_get_type_string
+blkid_partition_get_uuid
+blkid_partition_is_extended
+blkid_partition_is_logical
+blkid_partition_is_primary
+<SUBSECTION>
+blkid_partlist_get_partition
+blkid_partlist_get_partition_by_partno
+blkid_partlist_numof_partitions
+blkid_partlist_devno_to_partition
+blkid_partlist_get_table
+<SUBSECTION>
+blkid_parttable_get_id
+blkid_parttable_get_offset
+blkid_parttable_get_parent
+blkid_parttable_get_type
+<SUBSECTION>
+blkid_probe_get_partitions
+</SECTION>
+
+<SECTION>
+<FILE>superblocks</FILE>
+blkid_probe_enable_superblocks
+<SUBSECTION>
+blkid_known_fstype
+blkid_superblocks_get_name
+<SUBSECTION>
+blkid_probe_filter_superblocks_type
+blkid_probe_filter_superblocks_usage
+blkid_probe_invert_superblocks_filter
+blkid_probe_reset_superblocks_filter
+blkid_probe_set_superblocks_flags
+<SUBSECTION>
+blkid_probe_reset_filter
+blkid_probe_filter_types
+blkid_probe_filter_usage
+blkid_probe_invert_filter
+blkid_probe_set_request
+</SECTION>
+
+<SECTION>
+<FILE>topology</FILE>
+blkid_topology
+blkid_probe_enable_topology
+<SUBSECTION>
+blkid_probe_get_topology
+blkid_topology_get_alignment_offset
+blkid_topology_get_logical_sector_size
+blkid_topology_get_minimum_io_size
+blkid_topology_get_optimal_io_size
+blkid_topology_get_physical_sector_size
+</SECTION>
+
+<SECTION>
+<FILE>encode</FILE>
+blkid_encode_string
+blkid_safe_string
+</SECTION>
+
+<SECTION>
+<FILE>misc</FILE>
+blkid_loff_t
+blkid_devno_to_devname
+blkid_devno_to_wholedisk
+blkid_get_dev_size
+blkid_get_library_version
+blkid_parse_tag_string
+blkid_parse_version_string
+blkid_send_uevent
+BLKID_VERSION
+BLKID_DATE
+BLKID_FLTR_NOTIN
+BLKID_FLTR_ONLYIN
+BLKID_DEV_CREATE
+BLKID_DEV_FIND
+BLKID_DEV_NORMAL
+BLKID_DEV_VERIFY
+BLKID_PARTS_ENTRY_DETAILS
+BLKID_PARTS_FORCE_GPT
+BLKID_PARTS_MAGIC
+BLKID_PROBREQ_LABEL
+BLKID_PROBREQ_LABELRAW
+BLKID_PROBREQ_SECTYPE
+BLKID_PROBREQ_TYPE
+BLKID_PROBREQ_USAGE
+BLKID_PROBREQ_UUID
+BLKID_PROBREQ_UUIDRAW
+BLKID_PROBREQ_VERSION
+BLKID_SUBLKS_BADCSUM
+BLKID_SUBLKS_DEFAULT
+BLKID_SUBLKS_LABEL
+BLKID_SUBLKS_LABELRAW
+BLKID_SUBLKS_MAGIC
+BLKID_SUBLKS_SECTYPE
+BLKID_SUBLKS_TYPE
+BLKID_SUBLKS_USAGE
+BLKID_SUBLKS_UUID
+BLKID_SUBLKS_UUIDRAW
+BLKID_SUBLKS_VERSION
+BLKID_USAGE_CRYPTO
+BLKID_USAGE_FILESYSTEM
+BLKID_USAGE_OTHER
+BLKID_USAGE_RAID
+</SECTION>
+
+
diff --git a/libblkid/docs/version.xml.in b/libblkid/docs/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/libblkid/docs/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/libblkid/include/Makemodule.am b/libblkid/include/Makemodule.am
new file mode 100644
index 0000000..c4a52e4
--- /dev/null
+++ b/libblkid/include/Makemodule.am
@@ -0,0 +1,57 @@
+
+dist_noinst_HEADERS += \
+	include/all-io.h \
+	include/at.h \
+	include/bitops.h \
+	include/blkdev.h \
+	include/monotonic.h \
+	include/c.h \
+	include/canonicalize.h \
+	include/carefulputc.h \
+	include/closestream.h \
+	include/colors.h \
+	include/cpuset.h \
+	include/crc32.h \
+	include/crc64.h \
+	include/debug.h \
+	include/env.h \
+	include/exec_shell.h \
+	include/exitcodes.h \
+	include/fileutils.h \
+	include/ismounted.h \
+	include/linux_reboot.h \
+	include/linux_version.h \
+	include/list.h \
+	include/loopdev.h \
+	include/mangle.h \
+	include/match.h \
+	include/mbsalign.h \
+	include/md5.h \
+	include/minix.h \
+	include/namespace.h \
+	include/nls.h \
+	include/optutils.h \
+	include/pager.h \
+	include/pamfail.h \
+	include/path.h \
+	include/pathnames.h \
+	include/procutils.h \
+	include/randutils.h \
+	include/readutmp.h \
+	include/rpmatch.h \
+	include/setproctitle.h \
+	include/strutils.h \
+	include/swapprober.h \
+	include/swapheader.h \
+	include/sysfs.h \
+	include/timer.h \
+	include/timeutils.h \
+	include/ttyutils.h \
+	include/widechar.h \
+	include/xalloc.h \
+	include/pt-sgi.h \
+	include/pt-bsd.h \
+	include/pt-mbr.h \
+	include/pt-mbr-partnames.h \
+	include/pt-sun.h \
+	include/statfs_magic.h
diff --git a/libblkid/include/all-io.h b/libblkid/include/all-io.h
new file mode 100644
index 0000000..9a4aeba
--- /dev/null
+++ b/libblkid/include/all-io.h
@@ -0,0 +1,84 @@
+/*
+ * 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(250000);
+	}
+	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(250000);
+	}
+	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)) {
+				usleep(250000);
+				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/include/at.h b/libblkid/include/at.h
new file mode 100644
index 0000000..63a80f0
--- /dev/null
+++ b/libblkid/include/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/include/bitops.h b/libblkid/include/bitops.h
new file mode 100644
index 0000000..498ec63
--- /dev/null
+++ b/libblkid/include/bitops.h
@@ -0,0 +1,124 @@
+/*
+ * 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
+
+#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/include/blkdev.h b/libblkid/include/blkdev.h
new file mode 100644
index 0000000..c994795
--- /dev/null
+++ b/libblkid/include/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 ioctls, 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/cgit/linux/kernel/git/torvalds/linux.git/tree/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/include/blkid.h b/libblkid/include/blkid.h
new file mode 100644
index 0000000..4d43130
--- /dev/null
+++ b/libblkid/include/blkid.h
@@ -0,0 +1,414 @@
+/*
+ * 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>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBBLKID_VERSION   "2.25.0"
+#define LIBBLKID_DATE      "22-Jul-2014"
+
+/**
+ * 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_init_debug(int mask);
+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);
+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);
+
+/* 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);
+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);
+
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+					 const char *type,
+					 const char *value);
+
+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));
+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));
+
+extern dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr)
+			__ul_attribute__((nonnull));
+
+extern int blkid_probe_is_wholedisk(blkid_probe pr)
+			__ul_attribute__((nonnull));
+
+extern blkid_loff_t blkid_probe_get_size(blkid_probe pr);
+extern blkid_loff_t blkid_probe_get_offset(blkid_probe pr);
+extern unsigned int blkid_probe_get_sectorsize(blkid_probe pr);
+extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr);
+
+extern int blkid_probe_get_fd(blkid_probe pr);
+
+/*
+ * superblocks probing
+ */
+extern int blkid_known_fstype(const char *fstype);
+
+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_BADCSUM	(1 << 10) /* allow a bad checksum */
+
+#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);
+
+extern unsigned long blkid_topology_get_alignment_offset(blkid_topology tp)
+			__ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp)
+			__ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp)
+			__ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp)
+			__ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp)
+			__ul_attribute__((nonnull));
+
+/*
+ * partitions probing
+ */
+extern int blkid_known_pttype(const char *pttype);
+
+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);
+
+extern int blkid_partlist_numof_partitions(blkid_partlist ls);
+extern blkid_parttable blkid_partlist_get_table(blkid_partlist ls);
+extern blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n);
+extern blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n);
+extern blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno);
+extern blkid_parttable blkid_partition_get_table(blkid_partition par);
+
+extern const char *blkid_partition_get_name(blkid_partition par);
+extern const char *blkid_partition_get_uuid(blkid_partition par);
+extern int blkid_partition_get_partno(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_start(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_size(blkid_partition par);
+
+extern int blkid_partition_get_type(blkid_partition par)
+			__ul_attribute__((nonnull));
+
+extern const char *blkid_partition_get_type_string(blkid_partition par);
+
+extern unsigned long long blkid_partition_get_flags(blkid_partition par)
+			__ul_attribute__((nonnull));
+
+extern int blkid_partition_is_logical(blkid_partition par)
+			__ul_attribute__((nonnull));
+extern int blkid_partition_is_extended(blkid_partition par)
+			__ul_attribute__((nonnull));
+extern int blkid_partition_is_primary(blkid_partition par)
+			__ul_attribute__((nonnull));
+
+extern const char *blkid_parttable_get_type(blkid_parttable tab);
+extern const char *blkid_parttable_get_id(blkid_parttable tab);
+
+extern blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab);
+extern blkid_partition blkid_parttable_get_parent(blkid_parttable tab);
+
+/*
+ * 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);
+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));
+
+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/include/canonicalize.h b/libblkid/include/canonicalize.h
new file mode 100644
index 0000000..7a18aca
--- /dev/null
+++ b/libblkid/include/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/include/carefulputc.h b/libblkid/include/carefulputc.h
new file mode 100644
index 0000000..3a0ec5b
--- /dev/null
+++ b/libblkid/include/carefulputc.h
@@ -0,0 +1,67 @@
+#ifndef UTIL_LINUX_CAREFUULPUTC_H
+#define UTIL_LINUX_CAREFUULPUTC_H
+
+/*
+ * A putc() for use in write and wall (that sometimes are sgid tty).
+ * It avoids control characters in our locale, and also ASCII control
+ * characters.   Note that the locale of the recipient is unknown.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+static inline int fputc_careful(int c, FILE *fp, const char fail)
+{
+	int ret;
+
+	if (isprint(c) || c == '\a' || c == '\t' || c == '\r' || c == '\n')
+		ret = putc(c, fp);
+	else if (!isascii(c))
+		ret = fprintf(fp, "\\%3o", (unsigned char)c);
+	else {
+		ret = putc(fail, fp);
+		if (ret != EOF)
+			ret = putc(c ^ 0x40, fp);
+	}
+	return (ret < 0) ? EOF : 0;
+}
+
+static inline void 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 ||		/* \ */
+		    (unsigned char) *p == 0x60 ||		/* ` */
+		    (unsigned char) *p == 0x24 ||		/* $ */
+		    !isprint((unsigned char) *p) ||
+		    iscntrl((unsigned char) *p)) {
+
+			fprintf(out, "\\x%02x", (unsigned char) *p);
+		} else
+			fputc(*p, out);
+	}
+	fputc('"', out);
+}
+
+static inline void 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);
+	}
+}
+
+
+#endif  /*  _CAREFUULPUTC_H  */
diff --git a/libblkid/include/closestream.h b/libblkid/include/closestream.h
new file mode 100644
index 0000000..7842456
--- /dev/null
+++ b/libblkid/include/closestream.h
@@ -0,0 +1,71 @@
+#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 == EPIPE))
+			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);
+}
+
+#ifndef HAVE_FSYNC
+static inline int
+fsync(int fd __attribute__((__unused__)))
+{
+	return 0;
+}
+#endif
+
+static inline int
+close_fd(int fd)
+{
+	const int fsync_fail = (fsync(fd) != 0);
+	const int close_fail = (close(fd) != 0);
+
+	if (fsync_fail || close_fail)
+		return EOF;
+	return 0;
+}
+
+#endif /* UTIL_LINUX_CLOSESTREAM_H */
diff --git a/libblkid/include/colors.h b/libblkid/include/colors.h
new file mode 100644
index 0000000..97efc48
--- /dev/null
+++ b/libblkid/include/colors.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@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_BROWN		"\033[33m"	/* well, brown */
+#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"
+
+/* --color[=WHEN] */
+enum colortmode {
+	UL_COLORMODE_AUTO = 0,
+	UL_COLORMODE_NEVER,
+	UL_COLORMODE_ALWAYS,
+	UL_COLORMODE_UNDEF,
+
+	__UL_NCOLORMODES	/* last */
+};
+
+extern int colormode_from_string(const char *str);
+extern int colormode_or_err(const char *str, const char *errmsg);
+
+/* Initialize the global variable UL_COLOR_TERM_OK */
+extern int colors_init(int mode, const char *util_name);
+
+/* Returns 1 or 0 */
+extern int colors_wanted(void);
+
+/* temporary enable/disable colors */
+extern void colors_off(void);
+extern void colors_on(void);
+
+
+/* Set the color */
+extern void color_fenable(const char *seq, FILE *f);
+
+extern void color_scheme_fenable(const char *name, const char *dflt, FILE *f);
+extern const char *color_scheme_get_sequence(const char *name, const char *dflt);
+
+static inline void color_enable(const char *seq)
+{
+	color_fenable(seq, stdout);
+}
+
+static inline void color_scheme_enable(const char *name, const char *dflt)
+{
+	color_scheme_fenable(name, dflt, stdout);
+}
+
+/* Reset colors to default */
+extern void color_fdisable(FILE *f);
+
+static inline void color_disable(void)
+{
+	color_fdisable(stdout);
+}
+
+/* converts "red" to UL_COLOR_RED, etc. */
+extern const char *color_sequence_from_colorname(const char *str);
+
+
+#endif /* UTIL_LINUX_COLORS_H */
diff --git a/libblkid/include/config.h b/libblkid/include/config.h
new file mode 100644
index 0000000..0a71fcf
--- /dev/null
+++ b/libblkid/include/config.h
@@ -0,0 +1,687 @@
+/* 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 */
+
+/* Enable agetty --reload feature */
+#define AGETTY_RELOAD 1
+
+/* Should chfn and chsh require the user to enter the password? */
+#define CHFN_CHSH_PASSWORD 1
+
+/* Path to hwclock adjtime file */
+#define CONFIG_ADJTIME_PATH "/etc/adjtime"
+
+/* 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 you have the Mac OS X function CFLocaleCopyCurrent in the
+   CoreFoundation framework. */
+/* #undef HAVE_CFLOCALECOPYCURRENT */
+
+/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
+   the CoreFoundation framework. */
+/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 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 `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 `_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 to 1 if you have the `getsgnam' function. */
+#define HAVE_GETSGNAM 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Define if you have the iconv() function and it works. */
+/* #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 `inotify_init1' function. */
+#define HAVE_INOTIFY_INIT1 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 if libsystemd is available */
+/* #undef HAVE_LIBSYSTEMD */
+
+/* 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 if libuser is available */
+/* #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/gsmmux.h> header file. */
+/* #undef HAVE_LINUX_GSMMUX_H */
+
+/* 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/securebits.h> header file. */
+#define HAVE_LINUX_SECUREBITS_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 1
+
+/* 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 `open_memstream' function. */
+#define HAVE_OPEN_MEMSTREAM 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 `qsort_r' function. */
+#define HAVE_QSORT_R 1
+
+/* Define if curses library has the resizeterm(). */
+/* #undef HAVE_RESIZETERM */
+
+/* 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. */
+#define HAVE_SECURE_GETENV 1
+
+/* Define to 1 if you have the `security_get_initial_context' function. */
+/* #undef HAVE_SECURITY_GET_INITIAL_CONTEXT */
+
+/* Define to 1 if you have the <security/openpam.h> header file. */
+/* #undef HAVE_SECURITY_OPENPAM_H */
+
+/* Define to 1 if you have the <security/pam_appl.h> header file. */
+/* #undef HAVE_SECURITY_PAM_APPL_H */
+
+/* Define to 1 if you have the <security/pam_misc.h> header file. */
+/* #undef HAVE_SECURITY_PAM_MISC_H */
+
+/* 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 the system has the type `sighandler_t'. */
+#define HAVE_SIGHANDLER_T 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 */
+
+/* Add SMACK support */
+/* #undef HAVE_SMACK */
+
+/* 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 `sysinfo' function. */
+#define HAVE_SYSINFO 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/ttydefaults.h> header file. */
+#define HAVE_SYS_TTYDEFAULTS_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 if curses library has the use_default_colors(). */
+/* #undef HAVE_USE_DEFAULT_COLORS */
+
+/* Define to 1 if you have the `usleep' function. */
+#define HAVE_USLEEP 1
+
+/* Define to 1 if you have the `utimensat' function. */
+#define HAVE_UTIMENSAT 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. */
+/* #undef HAVE___SECURE_GETENV */
+
+/* libblkid date string */
+#define LIBBLKID_DATE "22-Jul-2014"
+
+/* libblkid version string */
+#define LIBBLKID_VERSION "2.25.0"
+
+/* libfdisk version string */
+#define LIBFDISK_VERSION "2.25.0"
+
+/* libmount version string */
+#define LIBMOUNT_VERSION "2.25.0"
+
+/* libsmartcols version string */
+#define LIBSMARTCOLS_VERSION "2.25.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/"
+
+/* 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.25.590-bf6c"
+
+/* 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.25.590-bf6c"
+
+/* 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 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.25.590-bf6c"
+
+/* 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
+
+/* Enable large inode numbers on Mac OS X 10.5.  */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#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/include/cpuset.h b/libblkid/include/cpuset.h
new file mode 100644
index 0000000..f8948a9
--- /dev/null
+++ b/libblkid/include/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/include/crc32.h b/libblkid/include/crc32.h
new file mode 100644
index 0000000..2616910
--- /dev/null
+++ b/libblkid/include/crc32.h
@@ -0,0 +1,10 @@
+#ifndef UL_NG_CRC32_H
+#define UL_NG_CRC32_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len);
+
+#endif
+
diff --git a/libblkid/include/crc64.h b/libblkid/include/crc64.h
new file mode 100644
index 0000000..40475d5
--- /dev/null
+++ b/libblkid/include/crc64.h
@@ -0,0 +1,9 @@
+#ifndef UTIL_LINUX_CRC64_H
+#define UTIL_LINUX_CRC64_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern uint64_t crc64(uint64_t seed, const unsigned char *data, size_t len);
+
+#endif
diff --git a/libblkid/include/debug.h b/libblkid/include/debug.h
new file mode 100644
index 0000000..0229ab3
--- /dev/null
+++ b/libblkid/include/debug.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_DEBUG_H
+#define UTIL_LINUX_DEBUG_H
+
+
+/*
+ * util-linux debug macros
+ *
+ * The debug stuff is based on <name>_debug_mask that controls what outputs is
+ * expected. The mask is usually initialized by <NAME>_DEBUG= env.variable
+ *
+ * After successful initialization the flag <PREFIX>_DEBUG_INIT is always set
+ * to the mask (this flag is required). The <PREFIX> is usually library API
+ * prefix (e.g. MNT_) or program name (e.g. CFDISK_)
+ *
+ * In the code is possible to use
+ *
+ *	DBG(FOO, ul_debug("this is output for foo"));
+ *
+ * where for the FOO has to be defined <PREFIX>_DEBUG_FOO.
+ *
+ * It's possible to initialize the mask by comma delimited strings with
+ * subsystem names (e.g. "LIBMOUNT_DEBUG=options,tab"). In this case is
+ * necessary to define mask names array. This functionality is optional.
+ *
+ * It's stringly recommended to use UL_* macros to define/declare/use
+ * the debug stuff.
+ *
+ * See disk-utils/cfdisk.c: cfdisk_init_debug()  for programs debug
+ *  or libmount/src/init.c: mnt_init_debug()     for library debug
+ *
+ */
+
+#include <stdarg.h>
+#include <string.h>
+
+struct ul_debug_maskname {
+	const char *name;
+	int mask;
+	const char *help;
+};
+#define UL_DEBUG_EMPTY_MASKNAMES {{ NULL, 0, NULL }}
+#define UL_DEBUG_DEFINE_MASKNAMES(m) static const struct ul_debug_maskname m ## _masknames[]
+#define UL_DEBUG_MASKNAMES(m)	m ## _masknames
+
+#define UL_DEBUG_DEFINE_MASK(m) int m ## _debug_mask
+#define UL_DEBUG_DECLARE_MASK(m) extern UL_DEBUG_DEFINE_MASK(m)
+
+/* p - flag prefix, m - flag postfix */
+#define UL_DEBUG_DEFINE_FLAG(p, m) p ## m
+
+/* l - library name, p - flag prefix, m - flag postfix, x - function */
+#define __UL_DBG(l, p, m, x) \
+	do { \
+		if ((p ## m) & l ## _debug_mask) { \
+			fprintf(stderr, "%d: %s: %8s: ", getpid(), # l, # m); \
+			x; \
+		} \
+	} while (0)
+
+#define __UL_DBG_CALL(l, p, m, x) \
+	do { \
+		if ((p ## m) & l ## _debug_mask) { \
+			x; \
+		} \
+	} while (0)
+
+#define __UL_DBG_FLUSH(l, p) \
+	do { \
+		if (l ## _debug_mask && \
+		    l ## _debug_mask != p ## INIT) { \
+			fflush(stderr); \
+		} \
+	} while (0)
+
+
+#define __UL_INIT_DEBUG(lib, pref, mask, env) \
+	do { \
+		if (lib ## _debug_mask & pref ## INIT) \
+		; \
+		else if (!mask) { \
+			char *str = getenv(# env); \
+			if (str) \
+				lib ## _debug_mask = ul_debug_parse_envmask(lib ## _masknames, str); \
+		} else \
+			lib ## _debug_mask = mask; \
+		lib ## _debug_mask |= pref ## INIT; \
+	} while (0)
+
+
+static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+ul_debug(const char *mesg, ...)
+{
+	va_list ap;
+	va_start(ap, mesg);
+	vfprintf(stderr, mesg, ap);
+	va_end(ap);
+	fputc('\n', stderr);
+}
+
+static inline void __attribute__ ((__format__ (__printf__, 2, 3)))
+ul_debugobj(void *handler, const char *mesg, ...)
+{
+	va_list ap;
+
+	if (handler)
+		fprintf(stderr, "[%p]: ", handler);
+	va_start(ap, mesg);
+	vfprintf(stderr, mesg, ap);
+	va_end(ap);
+	fputc('\n', stderr);
+}
+
+static inline int ul_debug_parse_envmask(
+			const struct ul_debug_maskname flagnames[],
+			const char *mask)
+{
+	int res;
+	char *ptr;
+
+	/* let's check for a numeric mask first */
+	res = strtoul(mask, &ptr, 0);
+
+	/* perhaps it's a comma-separated string? */
+	if (ptr && *ptr && flagnames && flagnames[0].name) {
+		char *msbuf, *ms, *name;
+		res = 0;
+
+		ms = msbuf = strdup(mask);
+		if (!ms)
+			return res;
+
+		while ((name = strtok_r(ms, ",", &ptr))) {
+			const struct ul_debug_maskname *d;
+			ms = ptr;
+
+			for (d = flagnames; d && d->name; d++) {
+				if (strcmp(name, d->name) == 0) {
+					res |= d->mask;
+					break;
+				}
+			}
+			/* nothing else we can do by OR-ing the mask */
+			if (res == 0xffff)
+				break;
+		}
+		free(msbuf);
+	} else if (ptr && strcmp(ptr, "all") == 0)
+		res = 0xffff;
+
+	return res;
+}
+
+static inline void ul_debug_print_masks(
+			const char *env,
+			const struct ul_debug_maskname flagnames[])
+{
+	const struct ul_debug_maskname *d;
+
+	if (!flagnames)
+		return;
+
+	fprintf(stderr, "Available \"%s=<name>[,...]|<mask>\" debug masks:\n",
+			env);
+	for (d = flagnames; d && d->name; d++) {
+		if (!d->help)
+			continue;
+		fprintf(stderr, "   %-8s [0x%04x] : %s\n",
+				d->name, d->mask, d->help);
+	}
+}
+
+#endif /* UTIL_LINUX_DEBUG_H */
diff --git a/libblkid/include/env.h b/libblkid/include/env.h
new file mode 100644
index 0000000..a53d310
--- /dev/null
+++ b/libblkid/include/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/include/exec_shell.h b/libblkid/include/exec_shell.h
new file mode 100644
index 0000000..a2aa757
--- /dev/null
+++ b/libblkid/include/exec_shell.h
@@ -0,0 +1 @@
+extern void __attribute__((__noreturn__)) exec_shell(void);
diff --git a/libblkid/include/exitcodes.h b/libblkid/include/exitcodes.h
new file mode 100644
index 0000000..24ee123
--- /dev/null
+++ b/libblkid/include/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/include/fileutils.h b/libblkid/include/fileutils.h
new file mode 100644
index 0000000..b4e0a4a
--- /dev/null
+++ b/libblkid/include/fileutils.h
@@ -0,0 +1,33 @@
+#ifndef UTIL_LINUX_FILEUTILS
+#define UTIL_LINUX_FILEUTILS
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "c.h"
+
+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);
+
+extern int mkdir_p(const char *path, mode_t mode);
+extern char *stripoff_last_component(char *path);
+
+#endif /* UTIL_LINUX_FILEUTILS */
diff --git a/libblkid/include/ismounted.h b/libblkid/include/ismounted.h
new file mode 100644
index 0000000..57918cb
--- /dev/null
+++ b/libblkid/include/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/include/linux_reboot.h b/libblkid/include/linux_reboot.h
new file mode 100644
index 0000000..9cebc67
--- /dev/null
+++ b/libblkid/include/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/include/linux_version.h b/libblkid/include/linux_version.h
new file mode 100644
index 0000000..a6a1e99
--- /dev/null
+++ b/libblkid/include/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/include/list.h b/libblkid/include/list.h
new file mode 100644
index 0000000..3c08aa5
--- /dev/null
+++ b/libblkid/include/list.h
@@ -0,0 +1,346 @@
+/*
+ * 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) __extension__ ({              \
+	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,
+					  void *data),
+			       void *data,
+			       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, data) <= 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,
+						    void *data),
+					 void *data,
+					 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, data) <= 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, data);
+
+		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,
+				   void *data),
+			void *data)
+{
+	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, data, 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, data, part[lev], list);
+
+	merge_and_restore_back_links(cmp, data, head, part[max_lev], list);
+}
+
+#undef _INLINE_
+
+#endif /* UTIL_LINUX_LIST_H */
diff --git a/libblkid/include/loopdev.h b/libblkid/include/loopdev.h
new file mode 100644
index 0000000..573a569
--- /dev/null
+++ b/libblkid/include/loopdev.h
@@ -0,0 +1,193 @@
+#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 ioctl 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	info_failed:1;	/* LOOP_GET_STATUS ioctl failed */
+	unsigned int    control_ok:1;	/* /dev/loop-control success */
+
+	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 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 int loopcxt_add_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);
+extern int loopcxt_set_capacity(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/include/mangle.h b/libblkid/include/mangle.h
new file mode 100644
index 0000000..ec492b5
--- /dev/null
+++ b/libblkid/include/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/include/match.h b/libblkid/include/match.h
new file mode 100644
index 0000000..94440c2
--- /dev/null
+++ b/libblkid/include/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/include/mbsalign.h b/libblkid/include/mbsalign.h
new file mode 100644
index 0000000..5eaf606
--- /dev/null
+++ b/libblkid/include/mbsalign.h
@@ -0,0 +1,56 @@
+/* Align/Truncate a string in a given screen width
+   Copyright (C) 2009-2010 Free Software Foundation, Inc.
+   Copyright (C) 2010-2013 Karel Zak <kzak@redhat.com>
+
+   This program 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 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 UTIL_LINUX_MBSALIGN_H
+# define UTIL_LINUX_MBSALIGN_H
+# 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);
+
+extern size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz);
+extern size_t mbs_safe_width(const char *s);
+
+extern char *mbs_safe_encode(const char *s, size_t *width);
+extern char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf);
+extern size_t mbs_safe_encode_size(size_t bytes);
+
+#endif /* UTIL_LINUX_MBSALIGN_H */
diff --git a/libblkid/include/md5.h b/libblkid/include/md5.h
new file mode 100644
index 0000000..d997e37
--- /dev/null
+++ b/libblkid/include/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/include/minix.h b/libblkid/include/minix.h
new file mode 100644
index 0000000..f28991c
--- /dev/null
+++ b/libblkid/include/minix.h
@@ -0,0 +1,85 @@
+#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          /* minix V1 fs, 14 char names */
+#define MINIX_SUPER_MAGIC2   0x138F          /* minix V1 fs, 30 char names */
+
+#define MINIX2_SUPER_MAGIC   0x2468	     /* minix V2 fs, 14 char names */
+#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/include/monotonic.h b/libblkid/include/monotonic.h
new file mode 100644
index 0000000..f3b03d3
--- /dev/null
+++ b/libblkid/include/monotonic.h
@@ -0,0 +1,11 @@
+#ifndef UTIL_LINUX_BOOTTIME_H
+#define UTIL_LINUX_BOOTTIME_H
+
+/*
+ * Uses clock_gettime() that requires $CLOCKGETTIME_LIBS
+ */
+extern int get_boot_time(struct timeval *boot_time);
+
+extern int gettime_monotonic(struct timeval *tv);
+
+#endif /* UTIL_LINUX_BOOTTIME_H */
diff --git a/libblkid/include/namespace.h b/libblkid/include/namespace.h
new file mode 100644
index 0000000..ea231ca
--- /dev/null
+++ b/libblkid/include/namespace.h
@@ -0,0 +1,44 @@
+/* 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_NEWNS
+#  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
+
+# if !defined(HAVE_UNSHARE) || !defined(HAVE_SETNS)
+#  include <sys/syscall.h>
+# endif
+
+# if !defined(HAVE_UNSHARE) && defined(SYS_unshare)
+static inline int unshare(int flags)
+{
+	return syscall(SYS_unshare, flags);
+}
+# endif
+
+# if !defined(HAVE_SETNS) && defined(SYS_setns)
+static inline int setns(int fd, int nstype)
+{
+	return syscall(SYS_setns, fd, nstype);
+}
+# endif
+
+#endif	/* UTIL_LINUX_NAMESPACE_H */
diff --git a/libblkid/include/nls.h b/libblkid/include/nls.h
new file mode 100644
index 0000000..3eabfe6
--- /dev/null
+++ b/libblkid/include/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/include/optutils.h b/libblkid/include/optutils.h
new file mode 100644
index 0000000..99ad7e4
--- /dev/null
+++ b/libblkid/include/optutils.h
@@ -0,0 +1,103 @@
+#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 maximal number of the options in the group is 15 (size of the array is
+ * 16, last is zero).
+ *
+ * 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) {
+				size_t ct = 0;
+
+				fprintf(stderr, _("%s: these options are "
+						  "mutually exclusive:"),
+						program_invocation_short_name);
+
+				for (op = excl[e];
+				     ct + 1 < ARRAY_SIZE(excl[0]) && *op;
+				     op++, ct++) {
+					const char *n = option_to_longopt(*op, opts);
+					if (n)
+						fprintf(stderr, " --%s", n);
+					else
+						fprintf(stderr, " -%c", *op);
+				}
+				fputc('\n', stderr);
+				exit(OPTUTILS_EXIT_CODE);
+			}
+			break;
+		}
+	}
+}
+
+#endif
+
diff --git a/libblkid/include/pager.h b/libblkid/include/pager.h
new file mode 100644
index 0000000..9ca42eb
--- /dev/null
+++ b/libblkid/include/pager.h
@@ -0,0 +1,6 @@
+#ifndef UTIL_LINUX_PAGER
+#define UTIL_LINUX_PAGER
+
+void setup_pager(void);
+
+#endif
diff --git a/libblkid/include/pamfail.h b/libblkid/include/pamfail.h
new file mode 100644
index 0000000..bb83b94
--- /dev/null
+++ b/libblkid/include/pamfail.h
@@ -0,0 +1,26 @@
+/*
+ * 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>
+#ifdef HAVE_SECURITY_PAM_MISC_H
+# include <security/pam_misc.h>
+#elif defined(HAVE_SECURITY_OPENPAM_H)
+# include <security/openpam.h>
+#endif
+#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/include/path.h b/libblkid/include/path.h
new file mode 100644
index 0000000..45da692
--- /dev/null
+++ b/libblkid/include/path.h
@@ -0,0 +1,33 @@
+#ifndef UTIL_LINUX_PATH_H
+#define UTIL_LINUX_PATH_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+extern char *path_strdup(const char *path, ...)
+			__attribute__ ((__format__ (__printf__, 1, 2)));
+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/include/pathnames.h b/libblkid/include/pathnames.h
new file mode 100644
index 0000000..0d21b98
--- /dev/null
+++ b/libblkid/include/pathnames.h
@@ -0,0 +1,196 @@
+/*
+ * 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"
+
+#define _PATH_NOLOGIN_TXT	"/etc/nologin.txt"
+
+#ifndef _PATH_MAILDIR
+#define	_PATH_MAILDIR		"/var/spool/mail"
+#endif
+#define	_PATH_MOTDFILE		"/etc/motd"
+#define	_PATH_NOLOGIN		"/etc/nologin"
+#define	_PATH_VAR_NOLOGIN	"/var/run/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"
+
+#define _PATH_TERMCOLORS_DIRNAME "terminal-colors.d"
+#define _PATH_TERMCOLORS_DIR	"/etc/" _PATH_TERMCOLORS_DIRNAME
+
+/* 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_OS_RELEASE	"/etc/os-release"
+#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_UIDMAP	"/proc/self/uid_map"
+#define _PATH_PROC_GIDMAP	"/proc/self/gid_map"
+
+#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_MEM		"/dev/mem"
+
+#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 */
+#ifdef CONFIG_ADJTIME_PATH
+# define _PATH_ADJTIME		CONFIG_ADJTIME_PATH
+#else
+# define _PATH_ADJTIME		"/etc/adjtime"
+#endif
+
+#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/include/procutils.h b/libblkid/include/procutils.h
new file mode 100644
index 0000000..14b766c
--- /dev/null
+++ b/libblkid/include/procutils.h
@@ -0,0 +1,32 @@
+#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);
+
+struct proc_processes {
+	DIR *dir;
+
+	const char *fltr_name;
+	uid_t fltr_uid;
+
+	unsigned int has_fltr_name : 1,
+		     has_fltr_uid : 1;
+};
+
+extern struct proc_processes *proc_open_processes(void);
+extern void proc_close_processes(struct proc_processes *ps);
+
+extern void proc_processes_filter_by_name(struct proc_processes *ps, const char *name);
+extern void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid);
+extern int proc_next_pid(struct proc_processes *ps, pid_t *pid);
+
+
+#endif /* UTIL_LINUX_PROCUTILS */
diff --git a/libblkid/include/pt-bsd.h b/libblkid/include/pt-bsd.h
new file mode 100644
index 0000000..9bf47a5
--- /dev/null
+++ b/libblkid/include/pt-bsd.h
@@ -0,0 +1,156 @@
+#ifndef UTIL_LINUX_PT_BSD_H
+#define UTIL_LINUX_PT_BSD_H
+
+#define BSD_MAXPARTITIONS	16
+#define BSD_FS_UNUSED		0
+
+#ifndef BSD_DISKMAGIC
+# define BSD_DISKMAGIC     ((uint32_t) 0x82564557)
+#endif
+
+#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
+
+#if defined (__alpha__) || defined (__powerpc__) || \
+    defined (__ia64__) || defined (__hppa__)
+# define BSD_LABELSECTOR   0
+# define BSD_LABELOFFSET   64
+#else
+# define BSD_LABELSECTOR   1
+# define BSD_LABELOFFSET   0
+#endif
+
+#define	BSD_BBSIZE        8192		/* size of boot area, with label */
+#define	BSD_SBSIZE        8192		/* max size of fs superblock */
+
+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));
+
+
+/* d_type values: */
+#define	BSD_DTYPE_SMD		1		/* SMD, XSMD; VAX hp/up */
+#define	BSD_DTYPE_MSCP		2		/* MSCP */
+#define	BSD_DTYPE_DEC		3		/* other DEC (rk, rl) */
+#define	BSD_DTYPE_SCSI		4		/* SCSI */
+#define	BSD_DTYPE_ESDI		5		/* ESDI interface */
+#define	BSD_DTYPE_ST506		6		/* ST506 etc. */
+#define	BSD_DTYPE_HPIB		7		/* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL		8		/* HP Fiber-link */
+#define	BSD_DTYPE_FLOPPY	10		/* floppy */
+
+/* d_subtype values: */
+#define BSD_DSTYPE_INDOSPART	0x8		/* is inside dos partition */
+#define BSD_DSTYPE_DOSPART(s)	((s) & 3)	/* dos partition number */
+#define BSD_DSTYPE_GEOMETRY	0x10		/* drive params in label */
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define	BSD_FS_UNUSED	0		/* unused */
+#define	BSD_FS_SWAP    	1		/* swap */
+#define	BSD_FS_V6      	2		/* Sixth Edition */
+#define	BSD_FS_V7      	3		/* Seventh Edition */
+#define	BSD_FS_SYSV    	4		/* System V */
+#define	BSD_FS_V71K    	5		/* V7 with 1K blocks (4.1, 2.9) */
+#define	BSD_FS_V8      	6		/* Eighth Edition, 4K blocks */
+#define	BSD_FS_BSDFFS	7		/* 4.2BSD fast file system */
+#define	BSD_FS_BSDLFS	9		/* 4.4BSD log-structured file system */
+#define	BSD_FS_OTHER	10		/* in use, but unknown/unsupported */
+#define	BSD_FS_HPFS	11		/* OS/2 high-performance file system */
+#define	BSD_FS_ISO9660	12		/* ISO-9660 filesystem (cdrom) */
+#define BSD_FS_ISOFS	BSD_FS_ISO9660
+#define	BSD_FS_BOOT	13		/* partition contains bootstrap */
+#define BSD_FS_ADOS	14		/* AmigaDOS fast file system */
+#define BSD_FS_HFS	15		/* Macintosh HFS */
+#define BSD_FS_ADVFS	16		/* Digital Unix AdvFS */
+
+/* this is annoying, but it's also the way it is :-( */
+#ifdef __alpha__
+#define	BSD_FS_EXT2	8		/* ext2 file system */
+#else
+#define	BSD_FS_MSDOS	8		/* MS-DOS file system */
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define	BSD_D_REMOVABLE	0x01		/* removable media */
+#define	BSD_D_ECC      	0x02		/* supports ECC */
+#define	BSD_D_BADSECT	0x04		/* supports bad sector forw. */
+#define	BSD_D_RAMDISK	0x08		/* disk emulator */
+#define	BSD_D_CHAIN    	0x10		/* can do back-back transfers */
+#define	BSD_D_DOSPART	0x20		/* within MSDOS partition */
+
+#endif /* UTIL_LINUX_PT_BSD_H */
diff --git a/libblkid/include/pt-mbr-partnames.h b/libblkid/include/pt-mbr-partnames.h
new file mode 100644
index 0000000..282adba
--- /dev/null
+++ b/libblkid/include/pt-mbr-partnames.h
@@ -0,0 +1,105 @@
+	{0x00, N_("Empty")},
+	{0x01, N_("FAT12")},
+	{0x02, N_("XENIX root")},
+	{0x03, N_("XENIX usr")},
+	{0x04, N_("FAT16 <32M")},
+	{0x05, N_("Extended")},		/* DOS 3.3+ extended partition */
+	{0x06, N_("FAT16")},		/* DOS 16-bit >=32M */
+	{0x07, N_("HPFS/NTFS/exFAT")},	/* OS/2 IFS, eg, HPFS or NTFS or QNX or exFAT */
+	{0x08, N_("AIX")},		/* AIX boot (AIX -- PS/2 port) or SplitDrive */
+	{0x09, N_("AIX bootable")},	/* AIX data or Coherent */
+	{0x0a, N_("OS/2 Boot Manager")},/* OS/2 Boot Manager */
+	{0x0b, N_("W95 FAT32")},
+	{0x0c, N_("W95 FAT32 (LBA)")},/* LBA really is `Extended Int 13h' */
+	{0x0e, N_("W95 FAT16 (LBA)")},
+	{0x0f, N_("W95 Ext'd (LBA)")},
+	{0x10, N_("OPUS")},
+	{0x11, N_("Hidden FAT12")},
+	{0x12, N_("Compaq diagnostics")},
+	{0x14, N_("Hidden FAT16 <32M")},
+	{0x16, N_("Hidden FAT16")},
+	{0x17, N_("Hidden HPFS/NTFS")},
+	{0x18, N_("AST SmartSleep")},
+	{0x1b, N_("Hidden W95 FAT32")},
+	{0x1c, N_("Hidden W95 FAT32 (LBA)")},
+	{0x1e, N_("Hidden W95 FAT16 (LBA)")},
+	{0x24, N_("NEC DOS")},
+	{0x27, N_("Hidden NTFS WinRE")},
+	{0x39, N_("Plan 9")},
+	{0x3c, N_("PartitionMagic recovery")},
+	{0x40, N_("Venix 80286")},
+	{0x41, N_("PPC PReP Boot")},
+	{0x42, N_("SFS")},
+	{0x4d, N_("QNX4.x")},
+	{0x4e, N_("QNX4.x 2nd part")},
+	{0x4f, N_("QNX4.x 3rd part")},
+	{0x50, N_("OnTrack DM")},
+	{0x51, N_("OnTrack DM6 Aux1")},	/* (or Novell) */
+	{0x52, N_("CP/M")},		/* CP/M or Microport SysV/AT */
+	{0x53, N_("OnTrack DM6 Aux3")},
+	{0x54, N_("OnTrackDM6")},
+	{0x55, N_("EZ-Drive")},
+	{0x56, N_("Golden Bow")},
+	{0x5c, N_("Priam Edisk")},
+	{0x61, N_("SpeedStor")},
+	{0x63, N_("GNU HURD or SysV")},	/* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+	{0x64, N_("Novell Netware 286")},
+	{0x65, N_("Novell Netware 386")},
+	{0x70, N_("DiskSecure Multi-Boot")},
+	{0x75, N_("PC/IX")},
+	{0x80, N_("Old Minix")},	/* Minix 1.4a and earlier */
+	{0x81, N_("Minix / old Linux")},/* Minix 1.4b and later */
+	{0x82, N_("Linux swap / Solaris")},
+	{0x83, N_("Linux")},
+	{0x84, N_("OS/2 hidden C: drive")},
+	{0x85, N_("Linux extended")},
+	{0x86, N_("NTFS volume set")},
+	{0x87, N_("NTFS volume set")},
+	{0x88, N_("Linux plaintext")},
+	{0x8e, N_("Linux LVM")},
+	{0x93, N_("Amoeba")},
+	{0x94, N_("Amoeba BBT")},	/* (bad block table) */
+	{0x9f, N_("BSD/OS")},		/* BSDI */
+	{0xa0, N_("IBM Thinkpad hibernation")},
+	{0xa5, N_("FreeBSD")},		/* various BSD flavours */
+	{0xa6, N_("OpenBSD")},
+	{0xa7, N_("NeXTSTEP")},
+	{0xa8, N_("Darwin UFS")},
+	{0xa9, N_("NetBSD")},
+	{0xab, N_("Darwin boot")},
+	{0xaf, N_("HFS / HFS+")},
+	{0xb7, N_("BSDI fs")},
+	{0xb8, N_("BSDI swap")},
+	{0xbb, N_("Boot Wizard hidden")},
+	{0xbe, N_("Solaris boot")},
+	{0xbf, N_("Solaris")},
+	{0xc1, N_("DRDOS/sec (FAT-12)")},
+	{0xc4, N_("DRDOS/sec (FAT-16 < 32M)")},
+	{0xc6, N_("DRDOS/sec (FAT-16)")},
+	{0xc7, N_("Syrinx")},
+	{0xda, N_("Non-FS data")},
+	{0xdb, N_("CP/M / CTOS / ...")},/* CP/M or Concurrent CP/M or
+					   Concurrent DOS or CTOS */
+	{0xde, N_("Dell Utility")},	/* Dell PowerEdge Server utilities */
+	{0xdf, N_("BootIt")},		/* BootIt EMBRM */
+	{0xe1, N_("DOS access")},	/* DOS access or SpeedStor 12-bit FAT
+					   extended partition */
+	{0xe3, N_("DOS R/O")},		/* DOS R/O or SpeedStor */
+	{0xe4, N_("SpeedStor")},	/* SpeedStor 16-bit FAT extended
+					   partition < 1024 cyl. */
+	{0xeb, N_("BeOS fs")},
+	{0xee, N_("GPT")},		/* Intel EFI GUID Partition Table */
+	{0xef, N_("EFI (FAT-12/16/32)")},/* Intel EFI System Partition */
+	{0xf0, N_("Linux/PA-RISC boot")},/* Linux/PA-RISC boot loader */
+	{0xf1, N_("SpeedStor")},
+	{0xf4, N_("SpeedStor")},	/* SpeedStor large partition */
+	{0xf2, N_("DOS secondary")},	/* DOS 3.3+ secondary */
+	{0xfb, N_("VMware VMFS")},
+	{0xfc, N_("VMware VMKCORE")},	/* VMware kernel dump partition */
+	{0xfd, N_("Linux raid autodetect")},/* New (2.2.x) raid partition with
+					       autodetect using persistent
+					       superblock */
+	{0xfe, N_("LANstep")},		/* SpeedStor >1024 cyl. or LANstep */
+	{0xff, N_("BBT")},		/* Xenix Bad Block Table */
+
+	{ 0, 0 }
diff --git a/libblkid/include/pt-mbr.h b/libblkid/include/pt-mbr.h
new file mode 100644
index 0000000..1279e3c
--- /dev/null
+++ b/libblkid/include/pt-mbr.h
@@ -0,0 +1,178 @@
+#ifndef UTIL_LINUX_PT_MBR_H
+#define UTIL_LINUX_PT_MBR_H
+
+struct dos_partition {
+	unsigned char boot_ind;		/* 0x80 - active */
+	unsigned char bh, bs, bc;	/* begin CHS */
+	unsigned char sys_ind;
+	unsigned char eh, es, ec;	/* end CHS */
+	unsigned char start_sect[4];
+	unsigned char nr_sects[4];
+} __attribute__((packed));
+
+#define MBR_PT_OFFSET		0x1be
+
+static inline struct dos_partition *mbr_get_partition(unsigned char *mbr, int i)
+{
+	return (struct dos_partition *)
+		(mbr + MBR_PT_OFFSET + (i * sizeof(struct dos_partition)));
+}
+
+/* assemble badly aligned little endian integer */
+static inline unsigned int __dos_assemble_4le(const unsigned char *p)
+{
+	return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+static inline void __dos_store_4le(unsigned char *p, unsigned int val)
+{
+	p[0] = (val & 0xff);
+	p[1] = ((val >> 8) & 0xff);
+	p[2] = ((val >> 16) & 0xff);
+	p[3] = ((val >> 24) & 0xff);
+}
+
+static inline unsigned int dos_partition_get_start(struct dos_partition *p)
+{
+	return __dos_assemble_4le(&(p->start_sect[0]));
+}
+
+static inline void dos_partition_set_start(struct dos_partition *p, unsigned int n)
+{
+	__dos_store_4le(p->start_sect, n);
+}
+
+static inline unsigned int dos_partition_get_size(struct dos_partition *p)
+{
+	return __dos_assemble_4le(&(p->nr_sects[0]));
+}
+
+static inline void dos_partition_set_size(struct dos_partition *p, unsigned int n)
+{
+	__dos_store_4le(p->nr_sects, n);
+}
+
+static inline int mbr_is_valid_magic(const unsigned char *mbr)
+{
+	return mbr[510] == 0x55 && mbr[511] == 0xaa ? 1 : 0;
+}
+
+static inline void mbr_set_magic(unsigned char *b)
+{
+	b[510] = 0x55;
+	b[511] = 0xaa;
+}
+
+static inline unsigned int mbr_get_id(const unsigned char *mbr)
+{
+	return __dos_assemble_4le(&mbr[440]);
+}
+
+static inline void mbr_set_id(unsigned char *b, unsigned int id)
+{
+	__dos_store_4le(&b[440], id);
+}
+
+enum {
+	MBR_EMPTY_PARTITION		= 0x00,
+	MBR_FAT12_PARTITION		= 0x01,
+	MBR_XENIX_ROOT_PARTITION	= 0x02,
+	MBR_XENIX_USR_PARTITION		= 0x03,
+	MBR_FAT16_LESS32M_PARTITION	= 0x04,
+	MBR_DOS_EXTENDED_PARTITION	= 0x05,
+	MBR_FAT16_PARTITION		= 0x06, /* DOS 16-bit >=32M */
+	MBR_HPFS_NTFS_PARTITION		= 0x07, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+	MBR_AIX_PARTITION		= 0x08, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+	MBR_AIX_BOOTABLE_PARTITION	= 0x09, /* AIX data or Coherent */
+	MBR_OS2_BOOTMNGR_PARTITION	= 0x0a, /* OS/2 Boot Manager */
+	MBR_W95_FAT32_PARTITION		= 0x0b,
+	MBR_W95_FAT32_LBA_PARTITION	= 0x0c, /* LBA really is `Extended Int 13h' */
+	MBR_W95_FAT16_LBA_PARTITION	= 0x0e,
+	MBR_W95_EXTENDED_PARTITION	= 0x0f,
+	MBR_OPUS_PARTITION		= 0x10,
+	MBR_HIDDEN_FAT12_PARTITION	= 0x11,
+	MBR_COMPAQ_DIAGNOSTICS_PARTITION = 0x12,
+	MBR_HIDDEN_FAT16_L32M_PARTITION	= 0x14,
+	MBR_HIDDEN_FAT16_PARTITION	= 0x16,
+	MBR_HIDDEN_HPFS_NTFS_PARTITION	= 0x17,
+	MBR_AST_SMARTSLEEP_PARTITION	= 0x18,
+	MBR_HIDDEN_W95_FAT32_PARTITION	= 0x1b,
+	MBR_HIDDEN_W95_FAT32LBA_PARTITION = 0x1c,
+	MBR_HIDDEN_W95_FAT16LBA_PARTITION = 0x1e,
+	MBR_NEC_DOS_PARTITION		= 0x24,
+	MBR_PLAN9_PARTITION		= 0x39,
+	MBR_PARTITIONMAGIC_PARTITION	= 0x3c,
+	MBR_VENIX80286_PARTITION	= 0x40,
+	MBR_PPC_PREP_BOOT_PARTITION	= 0x41,
+	MBR_SFS_PARTITION		= 0x42,
+	MBR_QNX_4X_PARTITION		= 0x4d,
+	MBR_QNX_4X_2ND_PARTITION	= 0x4e,
+	MBR_QNX_4X_3RD_PARTITION	= 0x4f,
+	MBR_DM_PARTITION		= 0x50,
+	MBR_DM6_AUX1_PARTITION		= 0x51, /* (or Novell) */
+	MBR_CPM_PARTITION		= 0x52, /* CP/M or Microport SysV/AT */
+	MBR_DM6_AUX3_PARTITION		= 0x53,
+	MBR_DM6_PARTITION		= 0x54,
+	MBR_EZ_DRIVE_PARTITION		= 0x55,
+	MBR_GOLDEN_BOW_PARTITION	= 0x56,
+	MBR_PRIAM_EDISK_PARTITION	= 0x5c,
+	MBR_SPEEDSTOR_PARTITION		= 0x61,
+	MBR_GNU_HURD_PARTITION		= 0x63, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+	MBR_UNIXWARE_PARTITION		= MBR_GNU_HURD_PARTITION,
+	MBR_NETWARE_286_PARTITION	= 0x64,
+	MBR_NETWARE_386_PARTITION	= 0x65,
+	MBR_DISKSECURE_MULTIBOOT_PARTITION = 0x70,
+	MBR_PC_IX_PARTITION		= 0x75,
+	MBR_OLD_MINIX_PARTITION		= 0x80, /* Minix 1.4a and earlier */
+	MBR_MINIX_PARTITION		= 0x81, /* Minix 1.4b and later */
+	MBR_LINUX_SWAP_PARTITION	= 0x82,
+	MBR_SOLARIS_X86_PARTITION	= MBR_LINUX_SWAP_PARTITION,
+	MBR_LINUX_DATA_PARTITION	= 0x83,
+	MBR_OS2_HIDDEN_DRIVE_PARTITION	= 0x84,
+	MBR_LINUX_EXTENDED_PARTITION	= 0x85,
+	MBR_NTFS_VOL_SET1_PARTITION	= 0x86,
+	MBR_NTFS_VOL_SET2_PARTITION	= 0x87,
+	MBR_LINUX_PLAINTEXT_PARTITION	= 0x88,
+	MBR_LINUX_LVM_PARTITION		= 0x8e,
+	MBR_AMOEBA_PARTITION		= 0x93,
+	MBR_AMOEBA_BBT_PARTITION	= 0x94, /* (bad block table) */
+	MBR_BSD_OS_PARTITION		= 0x9f, /* BSDI */
+	MBR_THINKPAD_HIBERNATION_PARTITION = 0xa0,
+	MBR_FREEBSD_PARTITION		= 0xa5, /* various BSD flavours */
+	MBR_OPENBSD_PARTITION		= 0xa6,
+	MBR_NEXTSTEP_PARTITION		= 0xa7,
+	MBR_DARWIN_UFS_PARTITION	= 0xa8,
+	MBR_NETBSD_PARTITION		= 0xa9,
+	MBR_DARWIN_BOOT_PARTITION	= 0xab,
+	MBR_HFS_HFS_PARTITION		= 0xaf,
+	MBR_BSDI_FS_PARTITION		= 0xb7,
+	MBR_BSDI_SWAP_PARTITION		= 0xb8,
+	MBR_BOOTWIZARD_HIDDEN_PARTITION	= 0xbb,
+	MBR_SOLARIS_BOOT_PARTITION	= 0xbe,
+	MBR_SOLARIS_PARTITION		= 0xbf,
+	MBR_DRDOS_FAT12_PARTITION	= 0xc1,
+	MBR_DRDOS_FAT16_L32M_PARTITION	= 0xc4,
+	MBR_DRDOS_FAT16_PARTITION	= 0xc6,
+	MBR_SYRINX_PARTITION		= 0xc7,
+	MBR_NONFS_DATA_PARTITION	= 0xda,
+	MBR_CPM_CTOS_PARTITION		= 0xdb, /* CP/M or Concurrent CP/M or Concurrent DOS or CTOS */
+	MBR_DELL_UTILITY_PARTITION	= 0xde, /* Dell PowerEdge Server utilities */
+	MBR_BOOTIT_PARTITION		= 0xdf, /* BootIt EMBRM */
+	MBR_DOS_ACCESS_PARTITION	= 0xe1, /* DOS access or SpeedStor 12-bit FAT extended partition */
+	MBR_DOS_RO_PARTITION		= 0xe3, /* DOS R/O or SpeedStor */
+	MBR_SPEEDSTOR_EXTENDED_PARTITION = 0xe4, /* SpeedStor 16-bit FAT extended partition < 1024 cyl. */
+	MBR_BEOS_FS_PARTITION		= 0xeb,
+	MBR_GPT_PARTITION		= 0xee, /* Intel EFI GUID Partition Table */
+	MBR_EFI_SYSTEM_PARTITION	= 0xef, /* Intel EFI System Partition */
+	MBR_LINUX_PARISC_BOOT_PARTITION	= 0xf0, /* Linux/PA-RISC boot loader */
+	MBR_SPEEDSTOR1_PARTITION	= 0xf1,
+	MBR_SPEEDSTOR2_PARTITION	= 0xf4, /* SpeedStor large partition */
+	MBR_DOS_SECONDARY_PARTITION	= 0xf2, /* DOS 3.3+ secondary */
+	MBR_VMWARE_VMFS_PARTITION	= 0xfb,
+	MBR_VMWARE_VMKCORE_PARTITION	= 0xfc, /* VMware kernel dump partition */
+	MBR_LINUX_RAID_PARTITION	= 0xfd, /* New (2.2.x) raid partition with autodetect using persistent superblock */
+	MBR_LANSTEP_PARTITION		= 0xfe, /* SpeedStor >1024 cyl. or LANstep */
+	MBR_XENIX_BBT_PARTITION		= 0xff, /* Xenix Bad Block Table */
+};
+
+#endif /* UTIL_LINUX_PT_MBR_H */
diff --git a/libblkid/include/pt-sgi.h b/libblkid/include/pt-sgi.h
new file mode 100644
index 0000000..547b37a
--- /dev/null
+++ b/libblkid/include/pt-sgi.h
@@ -0,0 +1,110 @@
+#ifndef UTIL_LINUX_PT_SUN_H
+#define UTIL_LINUX_PT_SUN_H
+
+#include <stdint.h>
+
+#define	SGI_LABEL_MAGIC		0x0be5a941
+
+#define SGI_MAXPARTITIONS	16
+#define SGI_MAXVOLUMES		15
+
+/* partition types */
+enum {
+	SGI_TYPE_VOLHDR		= 0x00,
+	SGI_TYPE_TRKREPL	= 0x01,
+	SGI_TYPE_SECREPL	= 0x02,
+	SGI_TYPE_SWAP		= 0x03,
+	SGI_TYPE_BSD		= 0x04,
+	SGI_TYPE_SYSV		= 0x05,
+	SGI_TYPE_ENTIRE_DISK	= 0x06,
+	SGI_TYPE_EFS		= 0x07,
+	SGI_TYPE_LVOL		= 0x08,
+	SGI_TYPE_RLVOL		= 0x09,
+	SGI_TYPE_XFS		= 0x0a,
+	SGI_TYPE_XFSLOG		= 0x0b,
+	SGI_TYPE_XLV		= 0x0c,
+	SGI_TYPE_XVM		= 0x0d
+};
+
+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;		/* SGI_DEVPARAM_* 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));
+
+enum {
+	SGI_DEVPARAM_SECTOR_SLIP	= 0x01,
+	SGI_DEVPARAM_SECTOR_FWD		= 0x02,
+	SGI_DEVPARAM_TRACK_FWD		= 0x04,
+	SGI_DEVPARAM_TRACK_MULTIVOL	= 0x08,
+	SGI_DEVPARAM_IGNORE_ERRORS	= 0x10,
+	SGI_DEVPARAM_RESEEK		= 0x20,
+	SGI_DEVPARAM_CMDTAGQ_ENABLE	= 0x40
+};
+
+
+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[SGI_MAXVOLUMES];
+
+	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 inline uint32_t sgi_pt_checksum(struct sgi_disklabel *label)
+{
+	int i;
+	uint32_t *ptr = (uint32_t *) label;
+	uint32_t sum = 0;
+
+	i = sizeof(*label) / sizeof(*ptr);
+
+	while (i) {
+		i--;
+		sum -= be32_to_cpu(ptr[i]);
+	}
+
+	return sum;
+}
+
+#endif /* UTIL_LINUX_PT_SUN_H */
diff --git a/libblkid/include/pt-sun.h b/libblkid/include/pt-sun.h
new file mode 100644
index 0000000..b085268
--- /dev/null
+++ b/libblkid/include/pt-sun.h
@@ -0,0 +1,90 @@
+#ifndef UTIL_LINUX_PT_SUN_H
+#define UTIL_LINUX_PT_SUN_H
+
+#include <stdint.h>
+
+#define SUN_LABEL_MAGIC		0xDABE
+
+/* Supported VTOC setting */
+#define SUN_VTOC_SANITY		0x600DDEEE	/* magic number */
+#define SUN_VTOC_VERSION	1
+#define SUN_MAXPARTITIONS	8
+
+struct sun_disklabel {
+	unsigned char label_id[128];   /* Informative text string */
+
+	struct sun_vtoc {
+		uint32_t version;     /* version */
+		char	 volume_id[8];/* volume name */
+		uint16_t nparts;      /* num of partitions */
+
+		struct sun_info {        /* partition information */
+			uint16_t id;     /* SUN_TAG_*  */
+			uint16_t flags;  /* SUN_FLAG_* */
+		} __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 rpm;                  /* disk rotational speed */
+	uint16_t pcyl;                 /* physical cylinder count */
+	uint16_t apc;                  /* extra sects per cylinder */
+	uint16_t obs1;
+	uint16_t obs2;
+	uint16_t intrlv;               /* interleave factor */
+	uint16_t ncyl;                 /* data cylinder count */
+	uint16_t acyl;                 /* alt. cylinder count */
+	uint16_t nhead;                /* 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));
+
+
+#define SUN_TAG_UNASSIGNED	0x00	/* Unassigned partition */
+#define SUN_TAG_BOOT		0x01	/* Boot partition	*/
+#define SUN_TAG_ROOT		0x02	/* Root filesystem	*/
+#define SUN_TAG_SWAP		0x03	/* Swap partition	*/
+#define SUN_TAG_USR		0x04	/* /usr filesystem	*/
+#define SUN_TAG_WHOLEDISK	0x05	/* Full-disk slice	*/
+#define SUN_TAG_STAND		0x06	/* Stand partition	*/
+#define SUN_TAG_VAR		0x07	/* /var filesystem	*/
+#define SUN_TAG_HOME		0x08	/* /home filesystem	*/
+#define SUN_TAG_ALTSCTR		0x09	/* Alt sector partition	*/
+#define SUN_TAG_CACHE		0x0a	/* Cachefs partition	*/
+#define SUN_TAG_RESERVED	0x0b	/* SMI reserved data	*/
+#define SUN_TAG_LINUX_SWAP	0x82	/* Linux SWAP		*/
+#define SUN_TAG_LINUX_NATIVE	0x83	/* Linux filesystem	*/
+#define SUN_TAG_LINUX_LVM	0x8e	/* Linux LVM		*/
+#define SUN_TAG_LINUX_RAID	0xfd	/* LInux RAID		*/
+
+#define SUN_FLAG_UNMNT		0x01	/* Unmountable partition*/
+#define SUN_FLAG_RONLY		0x10	/* Read only		*/
+
+static inline uint16_t sun_pt_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;
+}
+
+#endif /* UTIL_LINUX_PT_SUN_H */
diff --git a/libblkid/include/randutils.h b/libblkid/include/randutils.h
new file mode 100644
index 0000000..17e2a02
--- /dev/null
+++ b/libblkid/include/randutils.h
@@ -0,0 +1,13 @@
+#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);
+extern const char *random_tell_source(void);
+
+#endif
diff --git a/libblkid/include/readutmp.h b/libblkid/include/readutmp.h
new file mode 100644
index 0000000..93251ea
--- /dev/null
+++ b/libblkid/include/readutmp.h
@@ -0,0 +1,28 @@
+/* Declarations for GNU's read utmp module.
+
+   Copyright (C) 1992-2007, 2009-2014 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 jla; revised by djm */
+
+#ifndef READUTMP_H
+#define READUTMP_H
+
+#include <sys/types.h>
+#include <utmp.h>
+
+int read_utmp (char const *file, size_t *n_entries, struct utmp **utmp_buf);
+
+#endif /* READUTMP_H */
diff --git a/libblkid/include/rpmatch.h b/libblkid/include/rpmatch.h
new file mode 100644
index 0000000..d62634b
--- /dev/null
+++ b/libblkid/include/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/include/setproctitle.h b/libblkid/include/setproctitle.h
new file mode 100644
index 0000000..70a9efa
--- /dev/null
+++ b/libblkid/include/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/include/statfs_magic.h b/libblkid/include/statfs_magic.h
new file mode 100644
index 0000000..7397a4e
--- /dev/null
+++ b/libblkid/include/statfs_magic.h
@@ -0,0 +1,99 @@
+#ifndef UTIL_LINUX_STATFS_MAGIC_H
+#define UTIL_LINUX_STATFS_MAGIC_H
+
+#include <sys/statfs.h>
+
+/*
+ * If possible then don't depend on internal libc __SWORD_TYPE type.
+ */
+#ifdef __GNUC__
+#define F_TYPE_EQUAL(a, b) (a == (__typeof__(a)) b)
+#else
+#define F_TYPE_EQUAL(a, b) (a == (__SWORD_TYPE) b)
+#endif
+
+/*
+ *  Unfortunately, Linux kernel hedeader file <linux/magic.h> is incomplete
+ *  mess and kernel returns by statfs f_type many numbers that are nowhere
+ *  specified (in API).
+ *
+ *  This is collection of the magic numbers.
+ */
+#define STATFS_ADFS_MAGIC	0xadf5
+#define STATFS_AFFS_MAGIC	0xadff
+#define STATFS_AFS_MAGIC	0x5346414F
+#define STATFS_AUTOFS_MAGIC	0x0187
+#define STATFS_BDEVFS_MAGIC	0x62646576
+#define STATFS_BEFS_MAGIC	0x42465331
+#define STATFS_BFS_MAGIC	0x1BADFACE
+#define STATFS_BINFMTFS_MAGIC	0x42494e4d
+#define STATFS_BTRFS_MAGIC	0x9123683E
+#define STATFS_CEPH_MAGIC	0x00c36400
+#define STATFS_CGROUP_MAGIC	0x27e0eb
+#define STATFS_CIFS_MAGIC	0xff534d42
+#define STATFS_CODA_MAGIC	0x73757245
+#define STATFS_CONFIGFS_MAGIC	0x62656570
+#define STATFS_CRAMFS_MAGIC	0x28cd3d45
+#define STATFS_DEBUGFS_MAGIC	0x64626720
+#define STATFS_DEVPTS_MAGIC	0x1cd1
+#define STATFS_ECRYPTFS_MAGIC	0xf15f
+#define STATFS_EFIVARFS_MAGIC	0xde5e81e4
+#define STATFS_EFS_MAGIC	0x414A53
+#define STATFS_EXOFS_MAGIC	0x5DF5
+#define STATFS_EXT2_MAGIC	0xEF53
+#define STATFS_EXT3_MAGIC	0xEF53
+#define STATFS_EXT4_MAGIC	0xEF53
+#define STATFS_F2FS_MAGIC	0xF2F52010
+#define STATFS_FUSE_MAGIC	0x65735546
+#define STATFS_FUTEXFS_MAGIC	0xBAD1DEA
+#define STATFS_GFS2_MAGIC	0x01161970
+#define STATFS_HFSPLUS_MAGIC	0x482b
+#define STATFS_HOSTFS_MAGIC	0x00c0ffee
+#define STATFS_HPFS_MAGIC	0xf995e849
+#define STATFS_HPPFS_MAGIC	0xb00000ee
+#define STATFS_HUGETLBFS_MAGIC	0x958458f6
+#define STATFS_ISOFS_MAGIC	0x9660
+#define STATFS_JFFS2_MAGIC	0x72b6
+#define STATFS_JFS_MAGIC	0x3153464a
+#define STATFS_LOGFS_MAGIC	0xc97e8168
+#define STATFS_MINIX2_MAGIC	0x2468
+#define STATFS_MINIX2_MAGIC2	0x2478
+#define STATFS_MINIX3_MAGIC	0x4d5a
+#define STATFS_MINIX_MAGIC	0x137F
+#define STATFS_MINIX_MAGIC2	0x138F
+#define STATFS_MQUEUE_MAGIC	0x19800202
+#define STATFS_MSDOS_MAGIC	0x4d44
+#define STATFS_NCP_MAGIC	0x564c
+#define STATFS_NFS_MAGIC	0x6969
+#define STATFS_NILFS_MAGIC	0x3434
+#define STATFS_NTFS_MAGIC	0x5346544e
+#define STATFS_OCFS2_MAGIC	0x7461636f
+#define STATFS_OMFS_MAGIC	0xC2993D87
+#define STATFS_OPENPROMFS_MAGIC	0x9fa1
+#define STATFS_PIPEFS_MAGIC	0x50495045
+#define STATFS_PROC_MAGIC	0x9fa0
+#define STATFS_PSTOREFS_MAGIC	0x6165676C
+#define STATFS_QNX4_MAGIC	0x002f
+#define STATFS_QNX6_MAGIC	0x68191122
+#define STATFS_RAMFS_MAGIC	0x858458f6
+#define STATFS_REISERFS_MAGIC	0x52654973
+#define STATFS_ROMFS_MAGIC	0x7275
+#define STATFS_SECURITYFS_MAGIC	0x73636673
+#define STATFS_SELINUXFS_MAGIC	0xf97cff8c
+#define STATFS_SMACKFS_MAGIC	0x43415d53
+#define STATFS_SMB_MAGIC	0x517B
+#define STATFS_SOCKFS_MAGIC	0x534F434B
+#define STATFS_SQUASHFS_MAGIC	0x73717368
+#define STATFS_SYSFS_MAGIC	0x62656572
+#define STATFS_TMPFS_MAGIC	0x01021994
+#define STATFS_UBIFS_MAGIC	0x24051905
+#define STATFS_UDF_MAGIC	0x15013346
+#define STATFS_UFS2_MAGIC	0x19540119
+#define STATFS_UFS_MAGIC	0x00011954
+#define STATFS_V9FS_MAGIC	0x01021997
+#define STATFS_VXFS_MAGIC	0xa501FCF5
+#define STATFS_XENFS_MAGIC	0xabba1974
+#define STATFS_XFS_MAGIC	0x58465342
+
+#endif /* UTIL_LINUX_STATFS_MAGIC_H */
+
diff --git a/libblkid/include/strutils.h b/libblkid/include/strutils.h
new file mode 100644
index 0000000..4d8463a
--- /dev/null
+++ b/libblkid/include/strutils.h
@@ -0,0 +1,204 @@
+#ifndef UTIL_LINUX_STRUTILS
+#define UTIL_LINUX_STRUTILS
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+/* default strtoxx_or_err() exit code */
+#ifndef STRTOXX_EXIT_CODE
+# define STRTOXX_EXIT_CODE EXIT_FAILURE
+#endif
+
+
+extern int parse_size(const char *str, uintmax_t *res, int *power);
+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);
+
+extern void strtotimeval_or_err(const char *str, struct timeval *tv,
+		const char *errmesg);
+
+extern int isdigit_string(const char *str);
+
+#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;
+}
+
+static inline char *strdup_to_offset(void *stru, size_t offset, const char *str)
+{
+	char *n = NULL;
+	char **o = (char **) ((char *) stru + offset);
+
+	if (str) {
+		n = strdup(str);
+		if (!n)
+			return NULL;
+	}
+
+	free(*o);
+	*o = n;
+	return n;
+}
+
+#define strdup_to_struct_member(_s, _m, _str) \
+		strdup_to_offset((void *) _s, offsetof(__typeof__(*(_s)), _m), _str)
+
+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);
+
+/*
+ * Match string beginning.
+ */
+static inline const char *startswith(const char *s, const char *prefix)
+{
+	size_t sz = prefix ? strlen(prefix) : 0;
+
+        if (s && sz && strncmp(s, prefix, sz) == 0)
+                return s + sz;
+	return NULL;
+}
+
+/*
+ * Case insensitive match string beginning.
+ */
+static inline const char *startswith_no_case(const char *s, const char *prefix)
+{
+	size_t sz = prefix ? strlen(prefix) : 0;
+
+        if (s && sz && strncasecmp(s, prefix, sz) == 0)
+                return s + sz;
+	return NULL;
+}
+
+/*
+ * Match string ending.
+ */
+static inline const char *endswith(const char *s, const char *postfix)
+{
+	size_t sl = s ? strlen(s) : 0;
+	size_t pl = postfix ? strlen(postfix) : 0;
+
+	if (pl == 0)
+		return (char *)s + sl;
+	if (sl < pl)
+		return NULL;
+	if (memcmp(s + sl - pl, postfix, pl) != 0)
+		return NULL;
+	return (char *)s + sl - pl;
+}
+
+/*
+ * Skip leading white space.
+ */
+static inline const char *skip_space(const char *p)
+{
+	while (isspace(*p))
+		++p;
+	return p;
+}
+
+static inline const char *skip_blank(const char *p)
+{
+	while (isblank(*p))
+		++p;
+	return p;
+}
+
+
+/* Removes whitespace from the right-hand side of a string (trailing
+ * whitespace).
+ *
+ * Returns size of the new string (without \0).
+ */
+static inline size_t rtrim_whitespace(unsigned char *str)
+{
+	size_t i = strlen((char *) str);
+
+	while (i) {
+		i--;
+		if (!isspace(str[i])) {
+			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).
+ */
+static inline size_t 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;
+}
+
+#endif
diff --git a/libblkid/include/swapheader.h b/libblkid/include/swapheader.h
new file mode 100644
index 0000000..3fce0d0
--- /dev/null
+++ b/libblkid/include/swapheader.h
@@ -0,0 +1,23 @@
+#ifndef _SWAPHEADER_H
+#define _SWAPHEADER_H
+
+#define SWAP_VERSION 1
+#define SWAP_UUID_LENGTH 16
+#define SWAP_LABEL_LENGTH 16
+#define SWAP_SIGNATURE "SWAPSPACE2"
+#define SWAP_SIGNATURE_SZ (sizeof(SWAP_SIGNATURE) - 1)
+
+#include <stdint.h>
+
+struct swap_header_v1_2 {
+	char	      bootbits[1024];    /* Space for disklabel etc. */
+	uint32_t      version;
+	uint32_t      last_page;
+	uint32_t      nr_badpages;
+	unsigned char uuid[SWAP_UUID_LENGTH];
+	char	      volume_name[SWAP_LABEL_LENGTH];
+	uint32_t      padding[117];
+	uint32_t      badpages[1];
+};
+
+#endif /* _SWAPHEADER_H */
diff --git a/libblkid/include/swapprober.h b/libblkid/include/swapprober.h
new file mode 100644
index 0000000..5107700
--- /dev/null
+++ b/libblkid/include/swapprober.h
@@ -0,0 +1,9 @@
+#ifndef UTIL_LINUX_SWAP_PROBER_H
+#define UTIL_LINUX_SWAP_PROBER_H
+
+#include <blkid.h>
+
+blkid_probe get_swap_prober(const char *devname);
+
+#endif /* UTIL_LINUX_SWAP_PROBER_H */
+
diff --git a/libblkid/include/sysfs.h b/libblkid/include/sysfs.h
new file mode 100644
index 0000000..1de624a
--- /dev/null
+++ b/libblkid/include/sysfs.h
@@ -0,0 +1,94 @@
+/*
+ * 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 int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str);
+extern int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num);
+
+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 char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz);
+extern int sysfs_next_subsystem(struct sysfs_cxt *cxt, char *devchain, char **subsys);
+extern int sysfs_is_hotpluggable(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_devno_is_lvm_private(dev_t devno);
+extern int sysfs_devno_is_wholedisk(dev_t devno);
+
+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/include/timer.h b/libblkid/include/timer.h
new file mode 100644
index 0000000..79ef649
--- /dev/null
+++ b/libblkid/include/timer.h
@@ -0,0 +1,31 @@
+#ifndef UTIL_LINUX_TIMER_H
+#define UTIL_LINUX_TIMER_H
+
+#include <signal.h>
+#include <sys/time.h>
+
+static inline int setup_timer(
+			struct itimerval *timer,
+			struct itimerval *old_timer,
+			struct sigaction *old_sa,
+			void (*timeout_handler)(int))
+{
+	struct sigaction sa;
+
+	memset(&sa, 0, sizeof sa);
+	sa.sa_handler = timeout_handler;
+	sa.sa_flags = SA_RESETHAND;
+	sigaction(SIGALRM, &sa, old_sa);
+
+	return setitimer(ITIMER_REAL, timer, old_timer);
+}
+
+static inline void cancel_timer(
+			struct itimerval *old_timer,
+			struct sigaction *old_sa)
+{
+	setitimer(ITIMER_REAL, old_timer, NULL);
+	sigaction(SIGALRM, old_sa, NULL);
+}
+
+#endif
diff --git a/libblkid/include/timeutils.h b/libblkid/include/timeutils.h
new file mode 100644
index 0000000..8ed501b
--- /dev/null
+++ b/libblkid/include/timeutils.h
@@ -0,0 +1,56 @@
+/***
+  First set of functions in this file are part of systemd, and were
+  copied to util-linux at August 2013.
+
+  Copyright 2010 Lennart Poettering
+  Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+
+  systemd 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.
+
+  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+#ifndef UTIL_LINUX_TIME_UTIL_H
+#define UTIL_LINUX_TIME_UTIL_H
+
+#include <stdio.h>
+#include <inttypes.h>
+
+typedef uint64_t usec_t;
+typedef uint64_t nsec_t;
+
+#define MSEC_PER_SEC  1000ULL
+#define USEC_PER_SEC  1000000ULL
+#define USEC_PER_MSEC 1000ULL
+#define NSEC_PER_SEC  1000000000ULL
+#define NSEC_PER_MSEC 1000000ULL
+#define NSEC_PER_USEC 1000ULL
+
+#define USEC_PER_MINUTE	(60ULL*USEC_PER_SEC)
+#define NSEC_PER_MINUTE	(60ULL*NSEC_PER_SEC)
+#define USEC_PER_HOUR	(60ULL*USEC_PER_MINUTE)
+#define NSEC_PER_HOUR	(60ULL*NSEC_PER_MINUTE)
+#define USEC_PER_DAY	(24ULL*USEC_PER_HOUR)
+#define NSEC_PER_DAY	(24ULL*NSEC_PER_HOUR)
+#define USEC_PER_WEEK	(7ULL*USEC_PER_DAY)
+#define NSEC_PER_WEEK	(7ULL*NSEC_PER_DAY)
+#define USEC_PER_MONTH	(2629800ULL*USEC_PER_SEC)
+#define NSEC_PER_MONTH	(2629800ULL*NSEC_PER_SEC)
+#define USEC_PER_YEAR	(31557600ULL*USEC_PER_SEC)
+#define NSEC_PER_YEAR	(31557600ULL*NSEC_PER_SEC)
+
+#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1)	/* weekdays can be unicode */
+#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
+#define FORMAT_TIMESPAN_MAX 64
+
+int parse_timestamp(const char *t, usec_t *usec);
+
+#endif /* UTIL_LINUX_TIME_UTIL_H */
diff --git a/libblkid/include/ttyutils.h b/libblkid/include/ttyutils.h
new file mode 100644
index 0000000..e842f9f
--- /dev/null
+++ b/libblkid/include/ttyutils.h
@@ -0,0 +1,168 @@
+/*
+ * 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
+#ifdef HAVE_SYS_TTYDEFAULTS_H
+#include <sys/ttydefaults.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(int fd, 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'
+	 */
+#ifndef IUCLC
+# define IUCLC 0
+#endif
+#ifndef NL0
+# define NL0 0
+#endif
+#ifndef CR0
+# define CR0 0
+#endif
+#ifndef BS0
+# define BS0 0
+#endif
+#ifndef VT0
+# define VT0 0
+#endif
+#ifndef FF0
+# define FF0 0
+#endif
+#ifndef OLCUC
+# define OLCUC 0
+#endif
+#ifndef OFILL
+# define OFILL 0
+#endif
+#ifndef NLDLY
+# define NLDLY 0
+#endif
+#ifndef CRDLY
+# define CRDLY 0
+#endif
+#ifndef BSDLY
+# define BSDLY 0
+#endif
+#ifndef VTDLY
+# define VTDLY 0
+#endif
+#ifndef FFDLY
+# define FFDLY 0
+#endif
+
+	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|ECHOCTL);
+	tp->c_lflag &= ~(ECHONL|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/include/widechar.h b/libblkid/include/widechar.h
new file mode 100644
index 0000000..b023b5f
--- /dev/null
+++ b/libblkid/include/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/include/xalloc.h b/libblkid/include/xalloc.h
new file mode 100644
index 0000000..f012fb2
--- /dev/null
+++ b/libblkid/include/xalloc.h
@@ -0,0 +1,118 @@
+/*
+ * 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 __attribute__((warn_unused_result)) *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 char * __attribute__((warn_unused_result)) xstrndup(const char *str, size_t size)
+{
+        char *ret;
+
+        if (!str)
+                return NULL;
+
+        ret = strndup(str, size);
+
+        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 int xvasprintf(char **strp, const char *fmt, va_list ap)
+{
+	int ret = vasprintf(&(*strp), fmt, ap);
+	if (ret < 0)
+		err(XALLOC_EXIT_CODE, "cannot allocate string");
+	return ret;
+}
+
+
+static inline char * __attribute__((warn_unused_result)) xgethostname(void)
+{
+	char *name;
+	size_t sz = get_hostname_max() + 1;
+
+	name = xmalloc(sizeof(char) * sz);
+
+	if (gethostname(name, sz) != 0) {
+		free(name);
+		return NULL;
+	}
+	name[sz - 1] = '\0';
+	return name;
+}
+
+#endif
diff --git a/libblkid/lib/Makemodule.am b/libblkid/lib/Makemodule.am
new file mode 100644
index 0000000..565294e
--- /dev/null
+++ b/libblkid/lib/Makemodule.am
@@ -0,0 +1,115 @@
+
+noinst_LTLIBRARIES += libcommon.la
+libcommon_la_CFLAGS = $(AM_CFLAGS)
+libcommon_la_SOURCES = \
+	lib/at.c \
+	lib/blkdev.c \
+	lib/canonicalize.c \
+	lib/colors.c \
+	lib/crc32.c \
+	lib/crc64.c \
+	lib/env.c \
+	lib/fileutils.c \
+	lib/ismounted.c \
+	lib/mangle.c \
+	lib/match.c \
+	lib/mbsalign.c \
+	lib/md5.c \
+	lib/pager.c \
+	lib/path.c \
+	lib/procutils.c \
+	lib/randutils.c \
+	lib/setproctitle.c \
+	lib/strutils.c \
+	lib/sysfs.c \
+	lib/timeutils.c \
+	lib/ttyutils.c \
+	lib/exec_shell.c \
+	lib/readutmp.c
+
+if LINUX
+libcommon_la_SOURCES += \
+	lib/linux_version.c \
+	lib/loopdev.c
+endif
+
+if !HAVE_LANGINFO
+libcommon_la_SOURCES += lib/langinfo.c
+endif
+
+if HAVE_CPU_SET_T
+libcommon_la_SOURCES += lib/cpuset.c
+endif
+
+dist_man_MANS += lib/terminal-colors.d.5
+
+check_PROGRAMS += \
+	test_at \
+	test_blkdev \
+	test_canonicalize \
+	test_colors \
+	test_fileutils \
+	test_ismounted \
+	test_mangle \
+	test_procutils \
+	test_randutils \
+	test_strutils \
+	test_ttyutils
+
+if LINUX
+if HAVE_CPU_SET_T
+check_PROGRAMS += test_cpuset
+endif
+check_PROGRAMS += \
+	test_sysfs \
+	test_pager
+endif
+
+test_ttyutils_SOURCES = lib/ttyutils.c
+test_ttyutils_CFLAGS = -DTEST_PROGRAM
+test_ttyutils_LDADD = libcommon.la
+
+test_blkdev_SOURCES = lib/blkdev.c
+test_blkdev_CFLAGS = -DTEST_PROGRAM_BLKDEV
+test_blkdev_LDADD = libcommon.la
+
+test_ismounted_SOURCES = lib/ismounted.c
+test_ismounted_CFLAGS = -DTEST_PROGRAM
+test_ismounted_LDADD = libcommon.la
+
+test_mangle_SOURCES = lib/mangle.c
+test_mangle_CFLAGS = -DTEST_PROGRAM
+
+test_at_SOURCES = lib/at.c
+test_at_CFLAGS = -DTEST_PROGRAM_AT
+
+test_strutils_SOURCES = lib/strutils.c
+test_strutils_CFLAGS = -DTEST_PROGRAM
+
+test_colors_SOURCES = lib/colors.c
+test_colors_CFLAGS = -DTEST_PROGRAM
+
+test_randutils_SOURCES = lib/randutils.c
+test_randutils_CFLAGS = -DTEST_PROGRAM
+
+test_procutils_SOURCES = lib/procutils.c lib/at.c
+test_procutils_CFLAGS = -DTEST_PROGRAM
+
+if LINUX
+test_cpuset_SOURCES = lib/cpuset.c
+test_cpuset_CFLAGS = -DTEST_PROGRAM
+
+test_sysfs_SOURCES = lib/sysfs.c
+test_sysfs_CFLAGS = -DTEST_PROGRAM_SYSFS
+test_sysfs_LDADD = libcommon.la
+
+test_pager_SOURCES = lib/pager.c
+test_pager_CFLAGS = -DTEST_PROGRAM
+endif
+
+test_fileutils_SOURCES = lib/fileutils.c
+test_fileutils_CFLAGS = -DTEST_PROGRAM
+
+test_canonicalize_SOURCES = lib/canonicalize.c
+test_canonicalize_CFLAGS = -DTEST_PROGRAM_CANONICALIZE
+
diff --git a/libblkid/lib/at.c b/libblkid/lib/at.c
new file mode 100644
index 0000000..f8bfe13
--- /dev/null
+++ b/libblkid/lib/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/lib/blkdev.c b/libblkid/lib/blkdev.c
new file mode 100644
index 0000000..a293529
--- /dev/null
+++ b/libblkid/lib/blkdev.c
@@ -0,0 +1,378 @@
+/*
+ * 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
+
+#ifdef __FreeBSD_kernel__
+#include <sys/disk.h>
+#endif
+
+#include "blkdev.h"
+#include "c.h"
+#include "linux_version.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_BLKDEV
+#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|O_CLOEXEC)) < 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/lib/canonicalize.c b/libblkid/lib/canonicalize.c
new file mode 100644
index 0000000..303703b
--- /dev/null
+++ b/libblkid/lib/canonicalize.c
@@ -0,0 +1,145 @@
+/*
+ * canonicalize.c -- canonicalize pathname by removing symlinks
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2009-2013 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "canonicalize.h"
+
+/*
+ * 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;
+
+	if (!ptname || !*ptname)
+		return 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);
+
+		if (access(path, F_OK) == 0)
+			res = strdup(path);
+	}
+	fclose(f);
+	return res;
+}
+
+static int is_dm_devname(char *canonical, char **name)
+{
+	struct stat sb;
+	char *p = strrchr(canonical, '/');
+
+	*name = NULL;
+
+	if (!p
+	    || strncmp(p, "/dm-", 4) != 0
+	    || !isdigit(*(p + 4))
+	    || stat(canonical, &sb) != 0
+	    || !S_ISBLK(sb.st_mode))
+		return 0;
+
+	*name = p + 1;
+	return 1;
+}
+
+char *canonicalize_path(const char *path)
+{
+	char *canonical, *dmname;
+
+	if (!path || !*path)
+		return NULL;
+
+	canonical = realpath(path, NULL);
+	if (!canonical)
+		return strdup(path);
+
+	if (is_dm_devname(canonical, &dmname)) {
+		char *dm = canonicalize_dm_name(dmname);
+		if (dm) {
+			free(canonical);
+			return dm;
+		}
+	}
+
+	return canonical;
+}
+
+char *canonicalize_path_restricted(const char *path)
+{
+	char *canonical, *dmname;
+	int errsv;
+	uid_t euid;
+	gid_t egid;
+
+	if (!path || !*path)
+		return NULL;
+
+	euid = geteuid();
+	egid = getegid();
+
+	/* drop permissions */
+	if (setegid(getgid()) < 0 || seteuid(getuid()) < 0)
+		return NULL;
+
+	errsv = errno = 0;
+
+	canonical = realpath(path, NULL);
+	if (!canonical)
+		errsv = errno;
+	else if (is_dm_devname(canonical, &dmname)) {
+		char *dm = canonicalize_dm_name(dmname);
+		if (dm) {
+			free(canonical);
+			canonical = dm;
+		}
+	}
+
+	/* restore */
+	if (setegid(egid) < 0 || seteuid(euid) < 0) {
+		free(canonical);
+		return NULL;
+	}
+
+	errno = errsv;
+	return canonical;
+}
+
+
+#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/lib/colors.c b/libblkid/lib/colors.c
new file mode 100644
index 0000000..6f79ac4
--- /dev/null
+++ b/libblkid/lib/colors.c
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "c.h"
+#include "colors.h"
+#include "pathnames.h"
+#include "strutils.h"
+
+#include "debug.h"
+
+/*
+ * terminal-colors.d debug stuff
+ */
+UL_DEBUG_DEFINE_MASK(termcolors);
+UL_DEBUG_DEFINE_MASKNAMES(termcolors) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define TERMCOLORS_DEBUG_INIT	(1 << 1)
+#define TERMCOLORS_DEBUG_CONF	(1 << 2)
+#define TERMCOLORS_DEBUG_SCHEME	(1 << 3)
+#define TERMCOLORS_DEBUG_ALL	0xFFFF
+
+#define DBG(m, x)       __UL_DBG(termcolors, TERMCOLORS_DEBUG_, m, x)
+#define ON_DBG(m, x)    __UL_DBG_CALL(termcolors, TERMCOLORS_DEBUG_, m, x)
+
+/*
+ * terminal-colors.d file types
+ */
+enum {
+	UL_COLORFILE_DISABLE,		/* .disable */
+	UL_COLORFILE_ENABLE,		/* .enable */
+	UL_COLORFILE_SCHEME,		/* .scheme */
+
+	__UL_COLORFILE_COUNT
+};
+
+struct ul_color_scheme {
+	char *name;
+	char *seq;
+};
+
+/*
+ * Global colors control struct
+ *
+ * The terminal-colors.d/ evaluation is based on "scores":
+ *
+ *  filename                    score
+ *  ---------------------------------------
+ *  type			1
+ *  @termname.type		10 + 1
+ *  utilname.type		20 + 1
+ *  utilname@termname.type	20 + 10 + 1
+ *
+ * the match with higher score wins. The score is per type.
+ */
+struct ul_color_ctl {
+	const char	*utilname;	/* util name */
+	const char	*termname;	/* terminal name ($TERM) */
+
+	char		*sfile;	/* path to scheme */
+
+	struct ul_color_scheme	*schemes;	/* array with color schemes */
+	size_t			nschemes;	/* number of the items */
+	size_t			schemes_sz;	/* number of the allocated items */
+
+	int		mode;		/* UL_COLORMODE_* */
+	unsigned int	has_colors : 1,	/* based on mode and scores[] */
+			disabled   : 1, /* disable colors */
+			cs_configured : 1, /* color schemes read */
+			configured : 1; /* terminal-colors.d parsed */
+
+	int		scores[__UL_COLORFILE_COUNT];	/* the best match */
+};
+
+/*
+ * Control struct, globally shared.
+ */
+static struct ul_color_ctl ul_colors;
+
+static void colors_free_schemes(struct ul_color_ctl *cc);
+static int colors_read_schemes(struct ul_color_ctl *cc);
+
+/*
+ * qsort/bsearch buddy
+ */
+static int cmp_scheme_name(const void *a0, const void *b0)
+{
+	struct ul_color_scheme	*a = (struct ul_color_scheme *) a0,
+				*b = (struct ul_color_scheme *) b0;
+	return strcmp(a->name, b->name);
+}
+
+/*
+ * Maintains human readable color names
+ */
+const char *color_sequence_from_colorname(const char *str)
+{
+	static const struct ul_color_scheme basic_schemes[] = {
+		{ "black",	UL_COLOR_BLACK           },
+		{ "blue",	UL_COLOR_BLUE            },
+		{ "brown",	UL_COLOR_BROWN           },
+		{ "cyan",	UL_COLOR_CYAN            },
+		{ "darkgray",	UL_COLOR_DARK_GRAY       },
+		{ "gray",	UL_COLOR_GRAY            },
+		{ "green",	UL_COLOR_GREEN           },
+		{ "lightblue",	UL_COLOR_BOLD_BLUE       },
+		{ "lightcyan",	UL_COLOR_BOLD_CYAN       },
+		{ "lightgray,",	UL_COLOR_GRAY            },
+		{ "lightgreen", UL_COLOR_BOLD_GREEN      },
+		{ "lightmagenta", UL_COLOR_BOLD_MAGENTA  },
+		{ "lightred",	UL_COLOR_BOLD_RED        },
+		{ "magenta",	UL_COLOR_MAGENTA         },
+		{ "red",	UL_COLOR_RED             },
+		{ "yellow",	UL_COLOR_BOLD_YELLOW     },
+	};
+	struct ul_color_scheme key = { .name = (char *) str }, *res;
+
+	if (!str)
+		return NULL;
+
+	res = bsearch(&key, basic_schemes, ARRAY_SIZE(basic_schemes),
+				sizeof(struct ul_color_scheme),
+				cmp_scheme_name);
+	return res ? res->seq : NULL;
+}
+
+
+/*
+ * Resets control struct (note that we don't allocate the struct)
+ */
+static void colors_reset(struct ul_color_ctl *cc)
+{
+	if (!cc)
+		return;
+
+	colors_free_schemes(cc);
+
+	free(cc->sfile);
+
+	cc->sfile = NULL;
+	cc->utilname = NULL;
+	cc->termname = NULL;
+	cc->mode = UL_COLORMODE_UNDEF;
+
+	memset(cc->scores, 0, sizeof(cc->scores));
+}
+
+static void colors_debug(struct ul_color_ctl *cc)
+{
+	size_t i;
+
+	if (!cc)
+		return;
+
+	printf("Colors:\n");
+	printf("\tutilname = '%s'\n", cc->utilname);
+	printf("\ttermname = '%s'\n", cc->termname);
+	printf("\tscheme file = '%s'\n", cc->sfile);
+	printf("\tmode = %s\n",
+			cc->mode == UL_COLORMODE_UNDEF ? "undefined" :
+			cc->mode == UL_COLORMODE_AUTO ?  "auto" :
+			cc->mode == UL_COLORMODE_NEVER ? "never" :
+			cc->mode == UL_COLORMODE_ALWAYS ? "always" : "???");
+	printf("\thas_colors = %d\n", cc->has_colors);
+	printf("\tdisabled = %d\n", cc->disabled);
+	printf("\tconfigured = %d\n", cc->configured);
+	printf("\tcs configured = %d\n", cc->cs_configured);
+
+	fputc('\n', stdout);
+
+	for (i = 0; i < ARRAY_SIZE(cc->scores); i++)
+		printf("\tscore %s = %d\n",
+				i == UL_COLORFILE_DISABLE ? "disable" :
+				i == UL_COLORFILE_ENABLE ? "enable" :
+				i == UL_COLORFILE_SCHEME ? "scheme" : "???",
+				cc->scores[i]);
+
+	fputc('\n', stdout);
+
+	for (i = 0; i < cc->nschemes; i++) {
+		printf("\tscheme #%02zu ", i);
+		color_scheme_enable(cc->schemes[i].name, NULL);
+		fputs(cc->schemes[i].name, stdout);
+		color_disable();
+		fputc('\n', stdout);
+	}
+	fputc('\n', stdout);
+}
+
+/*
+ * Parses [[<utilname>][@<termname>].]<type>
+ */
+static int filename_to_tokens(const char *str,
+			      const char **name, size_t *namesz,
+			      const char **term, size_t *termsz,
+			      int  *filetype)
+{
+	const char *type_start, *term_start, *p;
+
+	if (!str || !*str || *str == '.' || strlen(str) > PATH_MAX)
+		return -EINVAL;
+
+	/* parse .type */
+	p = strrchr(str, '.');
+	type_start = p ? p + 1 : str;
+
+	if (strcmp(type_start, "disable") == 0)
+		*filetype = UL_COLORFILE_DISABLE;
+	else if (strcmp(type_start, "enable") == 0)
+		*filetype = UL_COLORFILE_ENABLE;
+	else if (strcmp(type_start, "scheme") == 0)
+		*filetype = UL_COLORFILE_SCHEME;
+	else {
+		DBG(CONF, ul_debug("unknown type '%s'", type_start));
+		return 1;				/* unknown type */
+	}
+
+	if (type_start == str)
+		return 0;				/* "type" only */
+
+	/* parse @termname */
+	p = strchr(str, '@');
+	term_start = p ? p + 1 : NULL;
+	if (term_start) {
+		*term = term_start;
+		*termsz = type_start - term_start - 1;
+		if (term_start - 1 == str)
+			return 0;			/* "@termname.type" */
+	}
+
+	/* parse utilname */
+	p = term_start ? term_start : type_start;
+	*name =  str;
+	*namesz	= p - str - 1;
+
+	return 0;
+}
+
+/*
+ * Scans @dirname and select the best matches for UL_COLORFILE_* types.
+ * The result is stored to cc->scores. The path to the best "scheme"
+ * file is stored to cc->scheme.
+ */
+static int colors_readdir(struct ul_color_ctl *cc, const char *dirname)
+{
+	DIR *dir;
+	int rc = 0;
+	struct dirent *d;
+	char sfile[PATH_MAX] = { '\0' };
+	size_t namesz, termsz;
+
+	if (!dirname || !cc || !cc->utilname || !*cc->utilname)
+		return -EINVAL;
+
+	DBG(CONF, ul_debug("reading dir: '%s'", dirname));
+
+	dir = opendir(dirname);
+	if (!dir)
+		return -errno;
+
+	namesz = strlen(cc->utilname);
+	termsz = cc->termname ? strlen(cc->termname) : 0;
+
+	while ((d = readdir(dir))) {
+		int type, score = 1;
+		const char *tk_name = NULL, *tk_term = NULL;
+		size_t tk_namesz = 0, tk_termsz = 0;
+
+		if (*d->d_name == '.')
+			continue;
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK &&
+		    d->d_type != DT_REG)
+			continue;
+#endif
+		if (filename_to_tokens(d->d_name,
+				       &tk_name, &tk_namesz,
+				       &tk_term, &tk_termsz, &type) != 0)
+			continue;
+
+		/* count teoretical score before we check names to avoid
+		 * unnecessary strcmp() */
+		if (tk_name)
+			score += 20;
+		if (tk_term)
+			score += 10;
+
+		DBG(CONF, ul_debug("item '%s': score=%d "
+			"[cur: %d, name(%zu): %s, term(%zu): %s]",
+			d->d_name, score, cc->scores[type],
+			tk_namesz, tk_name,
+			tk_termsz, tk_term));
+
+
+		if (score < cc->scores[type])
+			continue;
+
+		/* filter out by names */
+		if (tk_namesz && (tk_namesz != namesz ||
+				 strncmp(tk_name, cc->utilname, namesz) != 0))
+			continue;
+
+		if (tk_termsz && (termsz == 0 || tk_termsz != termsz ||
+				  strncmp(tk_term, cc->termname, termsz) != 0))
+			continue;
+
+		DBG(CONF, ul_debug("setting '%s' from %d -to-> %d",
+					type == UL_COLORFILE_SCHEME ? "scheme" :
+					type == UL_COLORFILE_DISABLE ? "disable" :
+					type == UL_COLORFILE_ENABLE ? "enable" : "???",
+					cc->scores[type], score));
+		cc->scores[type] = score;
+		if (type == UL_COLORFILE_SCHEME)
+			strncpy(sfile, d->d_name, sizeof(sfile));
+	}
+
+	if (*sfile) {
+		sfile[sizeof(sfile) - 1] = '\0';
+		if (asprintf(&cc->sfile, "%s/%s", dirname, sfile) <= 0)
+			rc = -ENOMEM;
+	}
+
+	closedir(dir);
+	return rc;
+}
+
+/* atexit() wrapper */
+static void colors_deinit(void)
+{
+	colors_reset(&ul_colors);
+}
+
+/*
+ * Returns path to $XDG_CONFIG_HOME/terminal-colors.d
+ */
+static char *colors_get_homedir(char *buf, size_t bufsz)
+{
+	char *p = getenv("XDG_CONFIG_HOME");
+
+	if (p) {
+		snprintf(buf, bufsz, "%s/" _PATH_TERMCOLORS_DIRNAME, p);
+		return buf;
+	}
+
+	p = getenv("HOME");
+	if (p) {
+		snprintf(buf, bufsz, "%s/.config/" _PATH_TERMCOLORS_DIRNAME, p);
+		return buf;
+	}
+
+	return NULL;
+}
+
+/* canonicalize sequence */
+static int cn_sequence(const char *str, char **seq)
+{
+	char *in, *out;
+
+	if (!str)
+		return -EINVAL;
+
+	*seq = NULL;
+
+	/* convert logical names like "red" to the real sequence */
+	if (*str != '\\' && isalpha(*str)) {
+		const char *s = color_sequence_from_colorname(str);
+		*seq = strdup(s ? s : str);
+
+		return *seq ? 0 : -ENOMEM;
+	}
+
+	/* convert xx;yy sequences to "\033[xx;yy" */
+	if (asprintf(seq, "\033[%sm", str) < 1)
+		return -ENOMEM;
+
+	for (in = *seq, out = *seq; in && *in; in++) {
+		if (*in != '\\') {
+			*out++ = *in;
+			continue;
+		}
+		switch(*(in + 1)) {
+		case 'a':
+			*out++ = '\a';	/* Bell */
+			break;
+		case 'b':
+			*out++ = '\b';	/* Backspace */
+			break;
+		case 'e':
+			*out++ = '\033';	/* Escape */
+			break;
+		case 'f':
+			*out++ = '\f';	/* Form Feed */
+			break;
+		case 'n':
+			*out++ = '\n';	/* Newline */
+			break;
+		case 'r':
+			*out++ = '\r';	/* Carriage Return */
+			break;
+		case 't':
+			*out++ = '\t';	/* Tab */
+			break;
+		case 'v':
+			*out++ = '\v';	/* Vertical Tab */
+			break;
+		case '\\':
+			*out++ = '\\';	/* Backslash */
+			break;
+		case '_':
+			*out++ = ' ';	/* Space */
+			break;
+		case '#':
+			*out++ = '#';	/* Hash mark */
+			break;
+		case '?':
+			*out++ = '?';	/* Qestion mark */
+			break;
+		default:
+			*out++ = *in;
+			*out++ = *(in + 1);
+			break;
+		}
+		in++;
+	}
+	*out = '\0';
+
+	return 0;
+}
+
+
+/*
+ * Adds one color sequence to array with color scheme.
+ * When returning success (0) this function takes ownership of
+ * @seq and @name, which have to be allocated strings.
+ */
+static int colors_add_scheme(struct ul_color_ctl *cc,
+			     char *name,
+			     char *seq0)
+{
+	struct ul_color_scheme *cs = NULL;
+	char *seq = NULL;
+	int rc;
+
+	if (!cc || !name || !*name || !seq0 || !*seq0)
+		return -EINVAL;
+
+	DBG(SCHEME, ul_debug("add '%s'", name));
+
+	rc = cn_sequence(seq0, &seq);
+	if (rc)
+		return rc;
+
+	rc = -ENOMEM;
+
+	/* convert logical name (e.g. "red") to real ESC code */
+	if (isalpha(*seq)) {
+		const char *s = color_sequence_from_colorname(seq);
+		char *p;
+
+		if (!s) {
+			DBG(SCHEME, ul_debug("unknown logical name: %s", seq));
+			rc = -EINVAL;
+			goto err;
+		}
+
+		p = strdup(s);
+		if (!p)
+			goto err;
+		free(seq);
+		seq = p;
+	}
+
+	/* enlarge the array */
+	if (cc->nschemes == cc->schemes_sz) {
+		void *tmp = realloc(cc->schemes, (cc->nschemes + 10)
+					* sizeof(struct ul_color_scheme));
+		if (!tmp)
+			goto err;
+		cc->schemes = tmp;
+		cc->schemes_sz = cc->nschemes + 10;
+	}
+
+	/* add a new item */
+	cs = &cc->schemes[cc->nschemes];
+	cs->seq = seq;
+	cs->name = strdup(name);
+	if (!cs->name)
+		goto err;
+
+	cc->nschemes++;
+	return 0;
+err:
+	if (cs) {
+		free(cs->seq);
+		free(cs->name);
+		cs->seq = cs->name = NULL;
+	} else
+		free(seq);
+	return rc;
+}
+
+/*
+ * Deallocates all regards to color schemes
+ */
+static void colors_free_schemes(struct ul_color_ctl *cc)
+{
+	size_t i;
+
+	DBG(SCHEME, ul_debug("free scheme"));
+
+	for (i = 0; i < cc->nschemes; i++) {
+		free(cc->schemes[i].name);
+		free(cc->schemes[i].seq);
+	}
+
+	free(cc->schemes);
+	cc->schemes = NULL;
+	cc->nschemes = 0;
+	cc->schemes_sz = 0;
+}
+
+/*
+ * The scheme configuration has to be sorted for bsearch
+ */
+static void colors_sort_schemes(struct ul_color_ctl *cc)
+{
+	if (!cc->nschemes)
+		return;
+
+	DBG(SCHEME, ul_debug("sort scheme"));
+
+	qsort(cc->schemes, cc->nschemes,
+	      sizeof(struct ul_color_scheme), cmp_scheme_name);
+}
+
+/*
+ * Returns just one color scheme
+ */
+static struct ul_color_scheme *colors_get_scheme(struct ul_color_ctl *cc,
+						 const char *name)
+{
+	struct ul_color_scheme key = { .name = (char *) name}, *res;
+
+	if (!cc || !name || !*name)
+		return NULL;
+
+	if (!cc->cs_configured) {
+		int rc = colors_read_schemes(cc);
+		if (rc)
+			return NULL;
+	}
+	if (!cc->nschemes)
+		return NULL;
+
+	DBG(SCHEME, ul_debug("search '%s'", name));
+
+	res = bsearch(&key, cc->schemes, cc->nschemes,
+				sizeof(struct ul_color_scheme),
+				cmp_scheme_name);
+
+	return res && res->seq ? res  : NULL;
+}
+
+/*
+ * Parses filenames in terminal-colors.d
+ */
+static int colors_read_configuration(struct ul_color_ctl *cc)
+{
+	int rc = -ENOENT;
+	char *dirname, buf[PATH_MAX];
+
+	cc->termname = getenv("TERM");
+
+	dirname = colors_get_homedir(buf, sizeof(buf));
+	if (dirname)
+		rc = colors_readdir(cc, dirname);		/* ~/.config */
+	if (rc == -EPERM || rc == -EACCES || rc == -ENOENT)
+		rc = colors_readdir(cc, _PATH_TERMCOLORS_DIR);	/* /etc */
+
+	cc->configured = 1;
+	return rc;
+}
+
+/*
+ * Reads terminal-colors.d/ scheme file into array schemes
+ */
+static int colors_read_schemes(struct ul_color_ctl *cc)
+{
+	int rc = 0;
+	FILE *f = NULL;
+	char buf[BUFSIZ],
+	     cn[129], seq[129];
+
+	if (!cc->configured)
+		rc = colors_read_configuration(cc);
+
+	cc->cs_configured = 1;
+
+	if (rc || !cc->sfile)
+		goto done;
+
+	DBG(SCHEME, ul_debug("reading file '%s'", cc->sfile));
+
+	f = fopen(cc->sfile, "r");
+	if (!f) {
+		rc = -errno;
+		goto done;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		char *p = strchr(buf, '\n');
+
+		if (!p) {
+			if (feof(f))
+				p = strchr(buf, '\0');
+			else {
+				rc = -errno;
+				goto done;
+			}
+		}
+		*p = '\0';
+		p = (char *) skip_blank(buf);
+		if (*p == '\0' || *p == '#')
+			continue;
+
+		rc = sscanf(p, "%128[^ ] %128[^\n ]", cn, seq);
+		if (rc == 2 && *cn && *seq) {
+			rc = colors_add_scheme(cc, cn, seq);	/* set rc=0 on success */
+			if (rc)
+				goto done;
+		}
+	}
+	rc = 0;
+
+done:
+	if (f)
+		fclose(f);
+	colors_sort_schemes(cc);
+
+	return rc;
+}
+
+
+static void termcolors_init_debug(void)
+{
+	__UL_INIT_DEBUG(termcolors, TERMCOLORS_DEBUG_, 0, TERMINAL_COLORS_DEBUG);
+}
+
+/**
+ * colors_init:
+ * @mode: UL_COLORMODE_*
+ * @name: util argv[0]
+ *
+ * Initialize private color control struct and initialize the colors
+ * status. The color schemes are parsed on demand by colors_get_scheme().
+ *
+ * Returns: >0 on success.
+ */
+int colors_init(int mode, const char *name)
+{
+	int atty = -1;
+	struct ul_color_ctl *cc = &ul_colors;
+
+	cc->utilname = name;
+	cc->mode = mode;
+
+	termcolors_init_debug();
+
+	if (mode == UL_COLORMODE_UNDEF && (atty = isatty(STDOUT_FILENO))) {
+		int rc = colors_read_configuration(cc);
+		if (rc)
+			cc->mode = UL_COLORMODE_AUTO;
+		else {
+
+			/* evaluate scores */
+			if (cc->scores[UL_COLORFILE_DISABLE] >
+			    cc->scores[UL_COLORFILE_ENABLE])
+				cc->mode = UL_COLORMODE_NEVER;
+			else
+				cc->mode = UL_COLORMODE_AUTO;
+
+			atexit(colors_deinit);
+		}
+	}
+
+	switch (cc->mode) {
+	case UL_COLORMODE_AUTO:
+		cc->has_colors = atty == -1 ? isatty(STDOUT_FILENO) : atty;
+		break;
+	case UL_COLORMODE_ALWAYS:
+		cc->has_colors = 1;
+		break;
+	case UL_COLORMODE_NEVER:
+	default:
+		cc->has_colors = 0;
+	}
+
+	ON_DBG(CONF, colors_debug(cc));
+
+	return cc->has_colors;
+}
+
+/*
+ * Temporary disable colors (this setting is independent on terminal-colors.d/)
+ */
+void colors_off(void)
+{
+	ul_colors.disabled = 1;
+}
+
+/*
+ * Enable colors
+ */
+void colors_on(void)
+{
+	ul_colors.disabled = 0;
+}
+
+/*
+ * Is terminal-colors.d/ configured to use colors?
+ */
+int colors_wanted(void)
+{
+	return ul_colors.has_colors;
+}
+
+/*
+ * Enable @seq color
+ */
+void color_fenable(const char *seq, FILE *f)
+{
+	if (!ul_colors.disabled && ul_colors.has_colors && seq)
+		fputs(seq, f);
+}
+
+/*
+ * Returns escape sequence by logical @name, if undefined then returns @dflt.
+ */
+const char *color_scheme_get_sequence(const char *name, const char *dflt)
+{
+	struct ul_color_scheme *cs;
+
+	if (ul_colors.disabled || !ul_colors.has_colors)
+		return NULL;
+
+	cs = colors_get_scheme(&ul_colors, name);
+	return cs && cs->seq ? cs->seq : dflt;
+}
+
+/*
+ * Enable color by logical @name, if undefined enable @dflt.
+ */
+void color_scheme_fenable(const char *name, const char *dflt, FILE *f)
+{
+	const char *seq = color_scheme_get_sequence(name, dflt);
+
+	if (!seq)
+		return;
+	color_fenable(seq, f);
+}
+
+
+/*
+ * Disable previously enabled color
+ */
+void color_fdisable(FILE *f)
+{
+	if (!ul_colors.disabled && ul_colors.has_colors)
+		fputs(UL_COLOR_RESET, f);
+}
+
+/*
+ * Parses @str to return UL_COLORMODE_*
+ */
+int colormode_from_string(const char *str)
+{
+	size_t i;
+	static const char *modes[] = {
+		[UL_COLORMODE_AUTO]   = "auto",
+		[UL_COLORMODE_NEVER]  = "never",
+		[UL_COLORMODE_ALWAYS] = "always",
+		[UL_COLORMODE_UNDEF] = ""
+	};
+
+	if (!str || !*str)
+		return -EINVAL;
+
+	assert(ARRAY_SIZE(modes) == __UL_NCOLORMODES);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strcasecmp(str, modes[i]) == 0)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+/*
+ * Parses @str and exit(EXIT_FAILURE) on error
+ */
+int colormode_or_err(const char *str, const char *errmsg)
+{
+	const char *p = str && *str == '=' ? str + 1 : str;
+	int colormode;
+
+	colormode = colormode_from_string(p);
+	if (colormode < 0)
+		errx(EXIT_FAILURE, "%s: '%s'", errmsg, p);
+
+	return colormode;
+}
+
+#ifdef TEST_PROGRAM
+# include <getopt.h>
+int main(int argc, char *argv[])
+{
+	static const struct option longopts[] = {
+		{ "mode",	required_argument, 0, 'm' },
+		{ "color",	required_argument, 0, 'c' },
+		{ "color-scheme", required_argument, 0, 'C' },
+		{ "name",	required_argument, 0, 'n' },
+		{ NULL, 0, 0, 0 }
+	};
+	int c, mode = UL_COLORMODE_UNDEF;	/* default */
+	const char *color = "red", *name = NULL, *color_scheme = NULL;
+	const char *seq = NULL;
+
+	while ((c = getopt_long(argc, argv, "C:c:m:n:", longopts, NULL)) != -1) {
+		switch (c) {
+		case 'c':
+			color = optarg;
+			break;
+		case 'C':
+			color_scheme = optarg;
+			break;
+		case 'm':
+			mode = colormode_or_err(optarg, "unsupported color mode");
+			break;
+		case 'n':
+			name = optarg;
+			break;
+		default:
+			fprintf(stderr, "usage: %s [options]\n"
+			" -m, --mode <auto|never|always>  default is undefined\n"
+			" -c, --color <red|blue|...>      color for the test message\n"
+			" -C, --color-scheme <name>       color for the test message\n"
+			" -n, --name <utilname>           util name\n",
+			program_invocation_short_name);
+			return EXIT_FAILURE;
+		}
+	}
+
+	colors_init(mode, name ? name : program_invocation_short_name);
+
+	seq = color_sequence_from_colorname(color);
+
+	if (color_scheme)
+		color_scheme_enable(color_scheme, seq);
+	else
+		color_enable(seq);
+	printf("Hello World!");
+	color_disable();
+	fputc('\n', stdout);
+
+	return EXIT_SUCCESS;
+}
+#endif
+
diff --git a/libblkid/lib/cpuset.c b/libblkid/lib/cpuset.c
new file mode 100644
index 0000000..d715720
--- /dev/null
+++ b/libblkid/lib/cpuset.c
@@ -0,0 +1,403 @@
+/*
+ * 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(mask);
+	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/lib/crc32.c b/libblkid/lib/crc32.c
new file mode 100644
index 0000000..be98f1a
--- /dev/null
+++ b/libblkid/lib/crc32.c
@@ -0,0 +1,118 @@
+/*
+ *  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) {
+		crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		len--;
+	}
+
+	return crc;
+}
+
diff --git a/libblkid/lib/crc64.c b/libblkid/lib/crc64.c
new file mode 100644
index 0000000..0be78e6
--- /dev/null
+++ b/libblkid/lib/crc64.c
@@ -0,0 +1,109 @@
+#include "crc64.h"
+
+static const uint64_t crc64_tab[256] = {
+	0x0000000000000000ULL, 0x42F0E1EBA9EA3693ULL, 0x85E1C3D753D46D26ULL,
+	0xC711223CFA3E5BB5ULL, 0x493366450E42ECDFULL, 0x0BC387AEA7A8DA4CULL,
+	0xCCD2A5925D9681F9ULL, 0x8E224479F47CB76AULL, 0x9266CC8A1C85D9BEULL,
+	0xD0962D61B56FEF2DULL, 0x17870F5D4F51B498ULL, 0x5577EEB6E6BB820BULL,
+	0xDB55AACF12C73561ULL, 0x99A54B24BB2D03F2ULL, 0x5EB4691841135847ULL,
+	0x1C4488F3E8F96ED4ULL, 0x663D78FF90E185EFULL, 0x24CD9914390BB37CULL,
+	0xE3DCBB28C335E8C9ULL, 0xA12C5AC36ADFDE5AULL, 0x2F0E1EBA9EA36930ULL,
+	0x6DFEFF5137495FA3ULL, 0xAAEFDD6DCD770416ULL, 0xE81F3C86649D3285ULL,
+	0xF45BB4758C645C51ULL, 0xB6AB559E258E6AC2ULL, 0x71BA77A2DFB03177ULL,
+	0x334A9649765A07E4ULL, 0xBD68D2308226B08EULL, 0xFF9833DB2BCC861DULL,
+	0x388911E7D1F2DDA8ULL, 0x7A79F00C7818EB3BULL, 0xCC7AF1FF21C30BDEULL,
+	0x8E8A101488293D4DULL, 0x499B3228721766F8ULL, 0x0B6BD3C3DBFD506BULL,
+	0x854997BA2F81E701ULL, 0xC7B97651866BD192ULL, 0x00A8546D7C558A27ULL,
+	0x4258B586D5BFBCB4ULL, 0x5E1C3D753D46D260ULL, 0x1CECDC9E94ACE4F3ULL,
+	0xDBFDFEA26E92BF46ULL, 0x990D1F49C77889D5ULL, 0x172F5B3033043EBFULL,
+	0x55DFBADB9AEE082CULL, 0x92CE98E760D05399ULL, 0xD03E790CC93A650AULL,
+	0xAA478900B1228E31ULL, 0xE8B768EB18C8B8A2ULL, 0x2FA64AD7E2F6E317ULL,
+	0x6D56AB3C4B1CD584ULL, 0xE374EF45BF6062EEULL, 0xA1840EAE168A547DULL,
+	0x66952C92ECB40FC8ULL, 0x2465CD79455E395BULL, 0x3821458AADA7578FULL,
+	0x7AD1A461044D611CULL, 0xBDC0865DFE733AA9ULL, 0xFF3067B657990C3AULL,
+	0x711223CFA3E5BB50ULL, 0x33E2C2240A0F8DC3ULL, 0xF4F3E018F031D676ULL,
+	0xB60301F359DBE0E5ULL, 0xDA050215EA6C212FULL, 0x98F5E3FE438617BCULL,
+	0x5FE4C1C2B9B84C09ULL, 0x1D14202910527A9AULL, 0x93366450E42ECDF0ULL,
+	0xD1C685BB4DC4FB63ULL, 0x16D7A787B7FAA0D6ULL, 0x5427466C1E109645ULL,
+	0x4863CE9FF6E9F891ULL, 0x0A932F745F03CE02ULL, 0xCD820D48A53D95B7ULL,
+	0x8F72ECA30CD7A324ULL, 0x0150A8DAF8AB144EULL, 0x43A04931514122DDULL,
+	0x84B16B0DAB7F7968ULL, 0xC6418AE602954FFBULL, 0xBC387AEA7A8DA4C0ULL,
+	0xFEC89B01D3679253ULL, 0x39D9B93D2959C9E6ULL, 0x7B2958D680B3FF75ULL,
+	0xF50B1CAF74CF481FULL, 0xB7FBFD44DD257E8CULL, 0x70EADF78271B2539ULL,
+	0x321A3E938EF113AAULL, 0x2E5EB66066087D7EULL, 0x6CAE578BCFE24BEDULL,
+	0xABBF75B735DC1058ULL, 0xE94F945C9C3626CBULL, 0x676DD025684A91A1ULL,
+	0x259D31CEC1A0A732ULL, 0xE28C13F23B9EFC87ULL, 0xA07CF2199274CA14ULL,
+	0x167FF3EACBAF2AF1ULL, 0x548F120162451C62ULL, 0x939E303D987B47D7ULL,
+	0xD16ED1D631917144ULL, 0x5F4C95AFC5EDC62EULL, 0x1DBC74446C07F0BDULL,
+	0xDAAD56789639AB08ULL, 0x985DB7933FD39D9BULL, 0x84193F60D72AF34FULL,
+	0xC6E9DE8B7EC0C5DCULL, 0x01F8FCB784FE9E69ULL, 0x43081D5C2D14A8FAULL,
+	0xCD2A5925D9681F90ULL, 0x8FDAB8CE70822903ULL, 0x48CB9AF28ABC72B6ULL,
+	0x0A3B7B1923564425ULL, 0x70428B155B4EAF1EULL, 0x32B26AFEF2A4998DULL,
+	0xF5A348C2089AC238ULL, 0xB753A929A170F4ABULL, 0x3971ED50550C43C1ULL,
+	0x7B810CBBFCE67552ULL, 0xBC902E8706D82EE7ULL, 0xFE60CF6CAF321874ULL,
+	0xE224479F47CB76A0ULL, 0xA0D4A674EE214033ULL, 0x67C58448141F1B86ULL,
+	0x253565A3BDF52D15ULL, 0xAB1721DA49899A7FULL, 0xE9E7C031E063ACECULL,
+	0x2EF6E20D1A5DF759ULL, 0x6C0603E6B3B7C1CAULL, 0xF6FAE5C07D3274CDULL,
+	0xB40A042BD4D8425EULL, 0x731B26172EE619EBULL, 0x31EBC7FC870C2F78ULL,
+	0xBFC9838573709812ULL, 0xFD39626EDA9AAE81ULL, 0x3A28405220A4F534ULL,
+	0x78D8A1B9894EC3A7ULL, 0x649C294A61B7AD73ULL, 0x266CC8A1C85D9BE0ULL,
+	0xE17DEA9D3263C055ULL, 0xA38D0B769B89F6C6ULL, 0x2DAF4F0F6FF541ACULL,
+	0x6F5FAEE4C61F773FULL, 0xA84E8CD83C212C8AULL, 0xEABE6D3395CB1A19ULL,
+	0x90C79D3FEDD3F122ULL, 0xD2377CD44439C7B1ULL, 0x15265EE8BE079C04ULL,
+	0x57D6BF0317EDAA97ULL, 0xD9F4FB7AE3911DFDULL, 0x9B041A914A7B2B6EULL,
+	0x5C1538ADB04570DBULL, 0x1EE5D94619AF4648ULL, 0x02A151B5F156289CULL,
+	0x4051B05E58BC1E0FULL, 0x87409262A28245BAULL, 0xC5B073890B687329ULL,
+	0x4B9237F0FF14C443ULL, 0x0962D61B56FEF2D0ULL, 0xCE73F427ACC0A965ULL,
+	0x8C8315CC052A9FF6ULL, 0x3A80143F5CF17F13ULL, 0x7870F5D4F51B4980ULL,
+	0xBF61D7E80F251235ULL, 0xFD913603A6CF24A6ULL, 0x73B3727A52B393CCULL,
+	0x31439391FB59A55FULL, 0xF652B1AD0167FEEAULL, 0xB4A25046A88DC879ULL,
+	0xA8E6D8B54074A6ADULL, 0xEA16395EE99E903EULL, 0x2D071B6213A0CB8BULL,
+	0x6FF7FA89BA4AFD18ULL, 0xE1D5BEF04E364A72ULL, 0xA3255F1BE7DC7CE1ULL,
+	0x64347D271DE22754ULL, 0x26C49CCCB40811C7ULL, 0x5CBD6CC0CC10FAFCULL,
+	0x1E4D8D2B65FACC6FULL, 0xD95CAF179FC497DAULL, 0x9BAC4EFC362EA149ULL,
+	0x158E0A85C2521623ULL, 0x577EEB6E6BB820B0ULL, 0x906FC95291867B05ULL,
+	0xD29F28B9386C4D96ULL, 0xCEDBA04AD0952342ULL, 0x8C2B41A1797F15D1ULL,
+	0x4B3A639D83414E64ULL, 0x09CA82762AAB78F7ULL, 0x87E8C60FDED7CF9DULL,
+	0xC51827E4773DF90EULL, 0x020905D88D03A2BBULL, 0x40F9E43324E99428ULL,
+	0x2CFFE7D5975E55E2ULL, 0x6E0F063E3EB46371ULL, 0xA91E2402C48A38C4ULL,
+	0xEBEEC5E96D600E57ULL, 0x65CC8190991CB93DULL, 0x273C607B30F68FAEULL,
+	0xE02D4247CAC8D41BULL, 0xA2DDA3AC6322E288ULL, 0xBE992B5F8BDB8C5CULL,
+	0xFC69CAB42231BACFULL, 0x3B78E888D80FE17AULL, 0x7988096371E5D7E9ULL,
+	0xF7AA4D1A85996083ULL, 0xB55AACF12C735610ULL, 0x724B8ECDD64D0DA5ULL,
+	0x30BB6F267FA73B36ULL, 0x4AC29F2A07BFD00DULL, 0x08327EC1AE55E69EULL,
+	0xCF235CFD546BBD2BULL, 0x8DD3BD16FD818BB8ULL, 0x03F1F96F09FD3CD2ULL,
+	0x41011884A0170A41ULL, 0x86103AB85A2951F4ULL, 0xC4E0DB53F3C36767ULL,
+	0xD8A453A01B3A09B3ULL, 0x9A54B24BB2D03F20ULL, 0x5D45907748EE6495ULL,
+	0x1FB5719CE1045206ULL, 0x919735E51578E56CULL, 0xD367D40EBC92D3FFULL,
+	0x1476F63246AC884AULL, 0x568617D9EF46BED9ULL, 0xE085162AB69D5E3CULL,
+	0xA275F7C11F7768AFULL, 0x6564D5FDE549331AULL, 0x279434164CA30589ULL,
+	0xA9B6706FB8DFB2E3ULL, 0xEB46918411358470ULL, 0x2C57B3B8EB0BDFC5ULL,
+	0x6EA7525342E1E956ULL, 0x72E3DAA0AA188782ULL, 0x30133B4B03F2B111ULL,
+	0xF7021977F9CCEAA4ULL, 0xB5F2F89C5026DC37ULL, 0x3BD0BCE5A45A6B5DULL,
+	0x79205D0E0DB05DCEULL, 0xBE317F32F78E067BULL, 0xFCC19ED95E6430E8ULL,
+	0x86B86ED5267CDBD3ULL, 0xC4488F3E8F96ED40ULL, 0x0359AD0275A8B6F5ULL,
+	0x41A94CE9DC428066ULL, 0xCF8B0890283E370CULL, 0x8D7BE97B81D4019FULL,
+	0x4A6ACB477BEA5A2AULL, 0x089A2AACD2006CB9ULL, 0x14DEA25F3AF9026DULL,
+	0x562E43B4931334FEULL, 0x913F6188692D6F4BULL, 0xD3CF8063C0C759D8ULL,
+	0x5DEDC41A34BBEEB2ULL, 0x1F1D25F19D51D821ULL, 0xD80C07CD676F8394ULL,
+	0x9AFCE626CE85B507ULL
+};
+
+/*
+ * This a generic crc64() function, it takes seed as an argument,
+ * and does __not__ xor at the end. Then individual users can do
+ * whatever they need.
+ */
+uint64_t crc64(uint64_t seed, const unsigned char *data, size_t len)
+{
+	uint64_t crc = seed;
+
+	while (len) {
+		int i = ((int) (crc >> 56) ^ *data++) & 0xFF;
+		crc = crc64_tab[i] ^ (crc << 8);
+		len--;
+	}
+
+	return crc;
+}
+
diff --git a/libblkid/lib/env.c b/libblkid/lib/env.c
new file mode 100644
index 0000000..c79e0e0
--- /dev/null
+++ b/libblkid/lib/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/lib/exec_shell.c b/libblkid/lib/exec_shell.c
new file mode 100644
index 0000000..2b723ac
--- /dev/null
+++ b/libblkid/lib/exec_shell.c
@@ -0,0 +1,47 @@
+/*
+ * exec_shell() - launch a shell, else exit!
+ *
+ * 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, 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <libgen.h>
+
+#include "nls.h"
+#include "c.h"
+#include "xalloc.h"
+
+#include "exec_shell.h"
+
+#define DEFAULT_SHELL "/bin/sh"
+
+void 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/lib/fileutils.c b/libblkid/lib/fileutils.c
new file mode 100644
index 0000000..c6eb0d6
--- /dev/null
+++ b/libblkid/lib/fileutils.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <paths.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"
+
+#ifndef _PATH_TMP
+#define _PATH_TMP   "/tmp/"
+#endif
+
+/* 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, rc;
+
+	/* 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)
+		rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv,
+			  program_invocation_short_name);
+	else
+		rc = asprintf(&localtmp, "%s/%s.XXXXXX", _PATH_TMP,
+			  program_invocation_short_name);
+
+	if (rc < 0)
+		return -1;
+
+	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
+
+
+int mkdir_p(const char *path, mode_t mode)
+{
+	char *p, *dir;
+	int rc = 0;
+
+	if (!path || !*path)
+		return -EINVAL;
+
+	dir = p = strdup(path);
+	if (!dir)
+		return -ENOMEM;
+
+	if (*p == '/')
+		p++;
+
+	while (p && *p) {
+		char *e = strchr(p, '/');
+		if (e)
+			*e = '\0';
+		if (*p) {
+			rc = mkdir(dir, mode);
+			if (rc && errno != EEXIST)
+				break;
+			rc = 0;
+		}
+		if (!e)
+			break;
+		*e = '/';
+		p = e + 1;
+	}
+
+	free(dir);
+	return rc;
+}
+
+/* returns basename and keeps dirname in the @path, if @path is "/" (root)
+ * then returns empty string */
+char *stripoff_last_component(char *path)
+{
+	char *p = path ? strrchr(path, '/') : NULL;
+
+	if (!p)
+		return NULL;
+	*p = '\0';
+	return p + 1;
+}
diff --git a/libblkid/lib/ismounted.c b/libblkid/lib/ismounted.c
new file mode 100644
index 0000000..8099bd7
--- /dev/null
+++ b/libblkid/lib/ismounted.c
@@ -0,0 +1,388 @@
+/*
+ * 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|O_CLOEXEC, 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;
+		if (mtpt && mtlen)
+			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|O_CLOEXEC);
+	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/lib/langinfo.c b/libblkid/lib/langinfo.c
new file mode 100644
index 0000000..deeab9b
--- /dev/null
+++ b/libblkid/lib/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/lib/linux_version.c b/libblkid/lib/linux_version.c
new file mode 100644
index 0000000..2bcc2cc
--- /dev/null
+++ b/libblkid/lib/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/lib/loopdev.c b/libblkid/lib/loopdev.c
new file mode 100644
index 0000000..09b9bbf
--- /dev/null
+++ b/libblkid/lib/loopdev.c
@@ -0,0 +1,1572 @@
+/*
+ * 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"
+#include "blkdev.h"
+#include "debug.h"
+
+/*
+ * Debug stuff (based on include/debug.h)
+ */
+UL_DEBUG_DEFINE_MASK(loopdev);
+UL_DEBUG_DEFINE_MASKNAMES(loopdev) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define LOOPDEV_DEBUG_INIT	(1 << 1)
+#define LOOPDEV_DEBUG_CXT	(1 << 2)
+#define LOOPDEV_DEBUG_ITER	(1 << 3)
+#define LOOPDEV_DEBUG_SETUP	(1 << 4)
+#define SFDISKPROG_DEBUG_ALL	0xFFFF
+
+#define DBG(m, x)       __UL_DBG(loopdev, LOOPDEV_DEBUG_, m, x)
+#define ON_DBG(m, x)    __UL_DBG_CALL(loopdev, LOOPDEV_DEBUG_, m, x)
+
+static void loopdev_init_debug(void)
+{
+	if (loopdev_debug_mask)
+		return;
+	__UL_INIT_DEBUG(loopdev, LOOPDEV_DEBUG_, 0, LOOPDEV_DEBUG);
+}
+
+/*
+ * 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>)
+ *
+ * This sets the device name, but does not check if the device exists!
+ *
+ * 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(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "%s name 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;
+
+	loopdev_init_debug();
+	DBG(CXT, ul_debugobj(lc, "initialize context"));
+
+	memcpy(lc, &dummy, sizeof(dummy));
+	lc->flags = flags;
+
+	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(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "init: ignore ioctls"));
+	}
+
+	if (!(lc->flags & LOOPDEV_FL_CONTROL) && !stat(_PATH_DEV_LOOPCTL, &st)) {
+		lc->flags |= LOOPDEV_FL_CONTROL;
+		DBG(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "de-initialize"));
+
+	free(lc->filename);
+	lc->filename = NULL;
+
+	ignore_result( loopcxt_set_device(lc, NULL) );
+	loopcxt_deinit_iterator(lc);
+
+	errno = errsv;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns newly allocated device path.
+ */
+char *loopcxt_strdup_device(struct loopdev_cxt *lc)
+{
+	if (!lc || !*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 : 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(CXT, ul_debugobj(lc, "sysfs: failed devname to devno"));
+			return NULL;
+		}
+		if (sysfs_init(&lc->sysfs, devno, NULL)) {
+			DBG(CXT, ul_debugobj(lc, "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 | O_CLOEXEC);
+		DBG(CXT, ul_debugobj(lc, "open %s [%s]: %m", lc->device,
+				lc->flags & LOOPDEV_FL_RDWR ? "rw" : "ro"));
+	}
+	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;
+
+
+	iter = &lc->iter;
+	DBG(ITER, ul_debugobj(iter, "initialize"));
+
+	/* 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;
+
+	iter = &lc->iter;
+	DBG(ITER, ul_debugobj(iter, "de-initialize"));
+
+	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 */
+
+	if (!is_loopdev(lc->device)) {
+		DBG(ITER, ul_debugobj(&lc->iter, "%s does not exist", lc->device));
+		return -errno;
+	}
+
+	DBG(ITER, ul_debugobj(&lc->iter, "%s exist", lc->device));
+
+	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(ITER, ul_debugobj(&lc->iter, "failed to use %s device", lc->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;
+
+	DBG(ITER, ul_debug("scan dir: %s", dirname));
+
+	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;
+
+			errno = 0;
+			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(ITER, ul_debugobj(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(ITER, ul_debugobj(iter, "checking %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(ITER, ul_debugobj(iter, "scanning /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(ITER, ul_debugobj(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;
+
+
+	iter = &lc->iter;
+	if (iter->done)
+		return 1;
+
+	DBG(ITER, ul_debugobj(iter, "next"));
+
+	/* 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(ITER, ul_debugobj(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(ITER, ul_debugobj(iter, "next: scanning /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) {
+		errno = EINVAL;
+		return NULL;
+	}
+	errno = 0;
+	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(CXT, ul_debugobj(lc, "reading loop_info64 OK"));
+		return &lc->info;
+	}
+
+	lc->info_failed = 1;
+	DBG(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "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;
+		} else
+			rc = -errno;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "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;
+		} else
+			rc = -errno;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "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;
+
+	/* not provided by sysfs */
+	if (lo) {
+		if (type)
+			*type = lo->lo_encrypt_type;
+		rc = 0;
+	} else
+		rc = -errno;
+
+	DBG(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "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;
+
+	if (lo) {
+		if (devno)
+			*devno = lo->lo_device;
+		rc = 0;
+	} else
+		rc = -errno;
+
+	DBG(CXT, ul_debugobj(lc, "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;
+
+	if (lo) {
+		if (ino)
+			*ino = lo->lo_inode;
+		rc = 0;
+	} else
+		rc = -errno;
+
+	DBG(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "set backing file=%s", lc->info.lo_file_name));
+	return 0;
+}
+
+/*
+ * In kernels prior to v3.9, if the offset or sizelimit options
+ * are used, the block device's size won't be synced automatically.
+ * blockdev --getsize64 and filesystems will use the backing
+ * file size until the block device has been re-opened or the
+ * LOOP_SET_CAPACITY ioctl is called to sync the sizes.
+ *
+ * Since mount -oloop uses the LO_FLAGS_AUTOCLEAR option and passes
+ * the open file descriptor to the mount system call, we need to use
+ * the ioctl. Calling losetup directly doesn't have this problem since
+ * it closes the device when it exits and whatever consumes the device
+ * next will re-open it, causing the resync.
+ */
+static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
+{
+	uint64_t size, expected_size;
+	int dev_fd;
+	struct stat st;
+
+	if (!lc->info.lo_offset && !lc->info.lo_sizelimit)
+		return 0;
+
+	if (fstat(file_fd, &st)) {
+		DBG(CXT, ul_debugobj(lc, "failed to fstat backing file"));
+		return -errno;
+	}
+	if (S_ISBLK(st.st_mode)) {
+		if (blkdev_get_size(file_fd,
+				(unsigned long long *) &expected_size)) {
+			DBG(CXT, ul_debugobj(lc, "failed to determine device size"));
+			return -errno;
+		}
+	} else
+		expected_size = st.st_size;
+
+	if (expected_size == 0 || expected_size <= lc->info.lo_offset) {
+		DBG(CXT, ul_debugobj(lc, "failed to determine expected size"));
+		return 0;	/* ignore this error */
+	}
+
+	if (lc->info.lo_offset > 0)
+		expected_size -= lc->info.lo_offset;
+
+	if (lc->info.lo_sizelimit > 0 && lc->info.lo_sizelimit < expected_size)
+		expected_size = lc->info.lo_sizelimit;
+
+	dev_fd = loopcxt_get_fd(lc);
+	if (dev_fd < 0) {
+		DBG(CXT, ul_debugobj(lc, "failed to get loop FD"));
+		return -errno;
+	}
+
+	if (blkdev_get_size(dev_fd, (unsigned long long *) &size)) {
+		DBG(CXT, ul_debugobj(lc, "failed to determine loopdev size"));
+		return -errno;
+	}
+
+	/* It's block device, so, align to 512-byte sectors */
+	if (expected_size % 512) {
+		DBG(CXT, ul_debugobj(lc, "expected size misaligned to 512-byte sectors"));
+		expected_size = (expected_size >> 9) << 9;
+	}
+
+	if (expected_size != size) {
+		DBG(CXT, ul_debugobj(lc, "warning: loopdev and expected "
+				      "size dismatch (%ju/%ju)",
+				      size, expected_size));
+
+		if (loopcxt_set_capacity(lc)) {
+			/* ioctl not available */
+			if (errno == ENOTTY || errno == EINVAL)
+				errno = ERANGE;
+			return -errno;
+		}
+
+		if (blkdev_get_size(dev_fd, (unsigned long long *) &size))
+			return -errno;
+
+		if (expected_size != size) {
+			errno = ERANGE;
+			DBG(CXT, ul_debugobj(lc, "failed to set loopdev size, "
+					"size: %ju, expected: %ju",
+					size, expected_size));
+			return -errno;
+		}
+	}
+
+	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, cnt = 0;
+
+	if (!lc || !*lc->device || !lc->filename)
+		return -EINVAL;
+
+	DBG(SETUP, ul_debugobj(lc, "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 | O_CLOEXEC)) < 0) {
+		if (mode != O_RDONLY && (errno == EROFS || errno == EACCES))
+			file_fd = open(lc->filename, mode = O_RDONLY);
+
+		if (file_fd < 0) {
+			DBG(SETUP, ul_debugobj(lc, "open backing file failed: %m"));
+			return -errno;
+		}
+	}
+	DBG(SETUP, ul_debugobj(lc, "backing file open: OK"));
+
+	if (lc->fd != -1 && lc->mode != mode) {
+		DBG(SETUP, ul_debugobj(lc, "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;
+	}
+
+	do {
+		errno = 0;
+		dev_fd = loopcxt_get_fd(lc);
+		if (dev_fd >= 0 || lc->control_ok == 0)
+			break;
+		if (errno != EACCES && errno != ENOENT)
+			break;
+		/* We have permissions to open /dev/loop-control, but open
+		 * /dev/loopN failed with EACCES, it's probably because udevd
+		 * does not applied chown yet. Let's wait a moment. */
+		usleep(25000);
+	} while (cnt++ < 16);
+
+	if (dev_fd < 0) {
+		rc = -errno;
+		goto err;
+	}
+
+	DBG(SETUP, ul_debugobj(lc, "device open: OK"));
+
+	/*
+	 * Set FD
+	 */
+	if (ioctl(dev_fd, LOOP_SET_FD, file_fd) < 0) {
+		rc = -errno;
+		DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD failed: %m"));
+		goto err;
+	}
+
+	DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD: OK"));
+
+	if (ioctl(dev_fd, LOOP_SET_STATUS64, &lc->info)) {
+		DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64 failed: %m"));
+		goto err;
+	}
+
+	DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64: OK"));
+
+	if ((rc = loopcxt_check_size(lc, file_fd)))
+		goto err;
+
+	close(file_fd);
+
+	memset(&lc->info, 0, sizeof(lc->info));
+	lc->has_info = 0;
+	lc->info_failed = 0;
+
+	DBG(SETUP, ul_debugobj(lc, "success [rc=0]"));
+	return 0;
+err:
+	if (file_fd >= 0)
+		close(file_fd);
+	if (dev_fd >= 0 && rc != -EBUSY)
+		ioctl(dev_fd, LOOP_CLR_FD, 0);
+
+	DBG(SETUP, ul_debugobj(lc, "failed [rc=%d]", rc));
+	return rc;
+}
+
+int loopcxt_set_capacity(struct loopdev_cxt *lc)
+{
+	int fd = loopcxt_get_fd(lc);
+
+	if (fd < 0)
+		return -EINVAL;
+
+	/* Kernels prior to v2.6.30 don't support this ioctl */
+	if (ioctl(fd, LOOP_SET_CAPACITY, 0) < 0) {
+		int rc = -errno;
+		DBG(CXT, ul_debugobj(lc, "LOOP_SET_CAPACITY failed: %m"));
+		return rc;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "capacity set"));
+	return 0;
+}
+
+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(CXT, ul_debugobj(lc, "LOOP_CLR_FD failed: %m"));
+		return -errno;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "device removed"));
+	return 0;
+}
+
+int loopcxt_add_device(struct loopdev_cxt *lc)
+{
+	int rc = -EINVAL;
+	int ctl, nr = -1;
+	const char *p, *dev = loopcxt_get_device(lc);
+
+	if (!dev)
+		goto done;
+
+	if (!(lc->flags & LOOPDEV_FL_CONTROL)) {
+		rc = -ENOSYS;
+		goto done;
+	}
+
+	p = strrchr(dev, '/');
+	if (!p || (sscanf(p, "/loop%d", &nr) != 1 && sscanf(p, "/%d", &nr) != 1)
+	       || nr < 0)
+		goto done;
+
+	ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+	if (ctl >= 0) {
+		DBG(CXT, ul_debugobj(lc, "add_device %d", nr));
+		rc = ioctl(ctl, LOOP_CTL_ADD, nr);
+		close(ctl);
+	}
+	lc->control_ok = rc >= 0 ? 1 : 0;
+done:
+	DBG(CXT, ul_debugobj(lc, "add_device done [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * 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(CXT, ul_debugobj(lc, "find_unused requested"));
+
+	if (lc->flags & LOOPDEV_FL_CONTROL) {
+		int ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+
+		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);
+		}
+		lc->control_ok = ctl >= 0 && rc == 0 ? 1 : 0;
+		if (ctl >= 0)
+			close(ctl);
+		DBG(CXT, ul_debugobj(lc, "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(CXT, ul_debugobj(lc, "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;
+}
+
diff --git a/libblkid/lib/mangle.c b/libblkid/lib/mangle.c
new file mode 100644
index 0000000..5236e97
--- /dev/null
+++ b/libblkid/lib/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/lib/match.c b/libblkid/lib/match.c
new file mode 100644
index 0000000..9be82b0
--- /dev/null
+++ b/libblkid/lib/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/lib/mbsalign.c b/libblkid/lib/mbsalign.c
new file mode 100644
index 0000000..5e52e8f
--- /dev/null
+++ b/libblkid/lib/mbsalign.c
@@ -0,0 +1,466 @@
+/* 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 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 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 <ctype.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.  */
+
+/*
+ * 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().
+ *
+ * Returns: number of cells, @sz returns number of bytes.
+ */
+size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz)
+{
+	mbstate_t st;
+	const char *p = buf, *last = buf;
+	size_t width = 0, bytes = 0;
+
+	memset(&st, 0, sizeof(st));
+
+	if (p && *p && bufsz)
+		last = p + (bufsz - 1);
+
+	while (p && *p && p <= last) {
+		if (iscntrl((unsigned char) *p)) {
+			width += 4, bytes += 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;
+				if (isprint((unsigned char) *p))
+					width += 1, bytes += 1;
+				else
+					width += 4, bytes += 4;
+
+			} else if (!iswprint(wc)) {
+				width += len * 4;	/* hex encode whole sequence */
+				bytes += len * 4;
+			} else {
+				width += wcwidth(wc);	/* number of cells */
+				bytes += len;		/* number of bytes */
+			}
+			p += len;
+		}
+#else
+		else if (!isprint((unsigned char) *p)) {
+			width += 4, bytes += 4;		/* *p encoded to \x?? */
+			p++;
+		} else {
+			width++, bytes++;
+			p++;
+		}
+#endif
+	}
+
+	if (sz)
+		*sz = bytes;
+	return width;
+}
+
+size_t mbs_safe_width(const char *s)
+{
+	if (!s || !*s)
+		return 0;
+	return mbs_safe_nwidth(s, strlen(s), NULL);
+}
+
+/*
+ * Copy @s to @buf and replace control and non-printable chars with
+ * \x?? hex sequence. The @width returns number of cells.
+ *
+ * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s)))
+ * bytes.
+ */
+char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf)
+{
+	mbstate_t st;
+	const char *p = s;
+	char *r;
+	size_t sz = s ? strlen(s) : 0;
+
+	if (!sz || !buf)
+		return NULL;
+
+	memset(&st, 0, sizeof(st));
+
+	r = buf;
+	*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 buf;
+}
+
+size_t mbs_safe_encode_size(size_t bytes)
+{
+	return (bytes * 4) + 1;
+}
+
+/*
+ * Returns allocated string where all control and non-printable chars are
+ * replaced with \x?? hex sequence.
+ */
+char *mbs_safe_encode(const char *s, size_t *width)
+{
+	size_t sz = s ? strlen(s) : 0;
+	char *buf;
+
+	if (!sz)
+		return NULL;
+	buf = malloc(mbs_safe_encode_size(sz));
+	if (!buf)
+		return NULL;
+
+	return mbs_safe_encode_to_buffer(s, width, buf);
+}
+
+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?  */
+  for (/* nothing */; n_spaces && (dest < dest_end); n_spaces--)
+    *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, space_left;
+  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;
+      size_t end_spaces;
+
+      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);
+      space_left = dest_end - dest;
+      dest = 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/lib/md5.c b/libblkid/lib/md5.c
new file mode 100644
index 0000000..488d16e
--- /dev/null
+++ b/libblkid/lib/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/lib/monotonic.c b/libblkid/lib/monotonic.c
new file mode 100644
index 0000000..3d4a443
--- /dev/null
+++ b/libblkid/lib/monotonic.c
@@ -0,0 +1,68 @@
+/*
+ * Please, don't add this file to libcommon because clock_gettime() requires
+ * -lrt on systems with old libc.
+ */
+#include <time.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+
+#include "c.h"
+#include "nls.h"
+#include "monotonic.h"
+
+int get_boot_time(struct timeval *boot_time)
+{
+#ifdef CLOCK_BOOTTIME
+	struct timespec hires_uptime;
+	struct timeval lores_uptime;
+#endif
+	struct timeval now;
+#ifdef HAVE_SYSINFO
+	struct sysinfo info;
+#endif
+
+	if (gettimeofday(&now, NULL) != 0) {
+		warn(_("gettimeofday failed"));
+		return -errno;
+	}
+#ifdef CLOCK_BOOTTIME
+	if (clock_gettime(CLOCK_BOOTTIME, &hires_uptime) == 0) {
+		TIMESPEC_TO_TIMEVAL(&lores_uptime, &hires_uptime);
+		timersub(&now, &lores_uptime, boot_time);
+		return 0;
+	}
+#endif
+#ifdef HAVE_SYSINFO
+	/* fallback */
+	if (sysinfo(&info) != 0)
+		warn(_("sysinfo failed"));
+
+	boot_time->tv_sec = now.tv_sec - info.uptime;
+	boot_time->tv_usec = 0;
+	return 0;
+#else
+	return -ENOSYS;
+#endif
+}
+
+int gettime_monotonic(struct timeval *tv)
+{
+#ifdef CLOCK_MONOTONIC
+	/* Can slew only by ntp and adjtime */
+	int ret;
+	struct timespec ts;
+
+# ifdef CLOCK_MONOTONIC_RAW
+	/* Linux specific, cant slew */
+	if (!(ret = clock_gettime(CLOCK_MONOTONIC_RAW, &ts))) {
+# else
+	if (!(ret = clock_gettime(CLOCK_MONOTONIC, &ts))) {
+# endif
+		tv->tv_sec = ts.tv_sec;
+		tv->tv_usec = ts.tv_nsec / 1000;
+	}
+	return ret;
+#else
+	return gettimeofday(tv, NULL);
+#endif
+}
diff --git a/libblkid/lib/pager.c b/libblkid/lib/pager.c
new file mode 100644
index 0000000..9e09cd5
--- /dev/null
+++ b/libblkid/lib/pager.c
@@ -0,0 +1,210 @@
+/*
+ * 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 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], STDIN_FILENO);
+			close_pair(fdin);
+		} else if (cmd->in > 0) {
+			dup2(cmd->in, STDIN_FILENO);
+			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(STDIN_FILENO, &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(STDOUT_FILENO);
+	close(STDERR_FILENO);
+	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(STDOUT_FILENO))
+		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, STDOUT_FILENO);
+	if (isatty(STDERR_FILENO))
+		dup2(pager_process.in, STDERR_FILENO);
+	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/lib/path.c b/libblkid/lib/path.c
new file mode 100644
index 0000000..fc90c0a
--- /dev/null
+++ b/libblkid/lib/path.c
@@ -0,0 +1,258 @@
+/*
+ * 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;
+}
+
+char *
+path_strdup(const char *path, ...)
+{
+	const char *p;
+	va_list ap;
+
+	va_start(ap, path);
+	p = path_vcreate(path, ap);
+	va_end(ap);
+
+	return p ? strdup(p) : NULL;
+}
+
+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, _("cannot 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, _("cannot 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, _("cannot 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|O_CLOEXEC, 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, _("cannot 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/lib/procutils.c b/libblkid/lib/procutils.c
new file mode 100644
index 0000000..ef96941
--- /dev/null
+++ b/libblkid/lib/procutils.c
@@ -0,0 +1,264 @@
+/*
+ * 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 "at.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 -EINVAL;
+
+	*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;
+		errno = 0;
+		*tid = (pid_t) strtol(d->d_name, &end, 10);
+		if (errno || d->d_name == end || (end && *end))
+			return -1;
+
+	} while (!*tid);
+
+	return 0;
+}
+
+struct proc_processes *proc_open_processes(void)
+{
+	struct proc_processes *ps;
+
+	ps = calloc(1, sizeof(struct proc_processes));
+	if (ps) {
+		ps->dir = opendir("/proc");
+		if (ps->dir)
+			return ps;
+	}
+
+	free(ps);
+	return NULL;
+}
+
+void proc_close_processes(struct proc_processes *ps)
+{
+	if (ps && ps->dir)
+		closedir(ps->dir);
+	free(ps);
+}
+
+void proc_processes_filter_by_name(struct proc_processes *ps, const char *name)
+{
+	ps->fltr_name = name;
+	ps->has_fltr_name = name ? 1 : 0;
+}
+
+void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid)
+{
+	ps->fltr_uid = uid;
+	ps->has_fltr_uid = 1;
+}
+
+int proc_next_pid(struct proc_processes *ps, pid_t *pid)
+{
+	struct dirent *d;
+
+	if (!ps || !pid)
+		return -EINVAL;
+
+	*pid = 0;
+	errno = 0;
+
+	do {
+		char buf[BUFSIZ], *p;
+
+		d = readdir(ps->dir);
+		if (!d)
+			return errno ? -1 : 1;		/* error or end-of-dir */
+
+
+		if (!isdigit((unsigned char) *d->d_name))
+			continue;
+
+		/* filter out by UID */
+		if (ps->has_fltr_uid) {
+			struct stat st;
+
+			if (fstat_at(dirfd(ps->dir), "/proc", d->d_name, &st, 0))
+				continue;
+			if (ps->fltr_uid != st.st_uid)
+				continue;
+		}
+
+		/* filter out by NAME */
+		if (ps->has_fltr_name) {
+			char procname[256];
+			FILE *f;
+
+			snprintf(buf, sizeof(buf), "%s/stat", d->d_name);
+			f = fopen_at(dirfd(ps->dir), "/proc", buf,
+						O_CLOEXEC|O_RDONLY, "r");
+			if (!f)
+				continue;
+
+			p = fgets(buf, sizeof(buf), f);
+			fclose(f);
+			if (!p)
+				continue;
+
+			if (sscanf(buf, "%*d (%255[^)])", procname) != 1)
+				continue;
+
+			/* ok, we got the process name. */
+			if (strcmp(procname, ps->fltr_name) != 0)
+				continue;
+		}
+
+		p = NULL;
+		errno = 0;
+		*pid = (pid_t) strtol(d->d_name, &p, 10);
+		if (errno || d->d_name == p || (p && *p))
+			return errno ? -errno : -1;
+
+		return 0;
+	} while (1);
+
+	return 0;
+}
+
+#ifdef TEST_PROGRAM
+
+static int test_tasks(int argc, char *argv[])
+{
+	pid_t tid, pid;
+	struct proc_tasks *ts;
+
+	if (argc != 2)
+		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;
+}
+
+static int test_processes(int argc, char *argv[])
+{
+	pid_t pid;
+	struct proc_processes *ps;
+
+	ps = proc_open_processes();
+	if (!ps)
+		err(EXIT_FAILURE, "open list of processes failed");
+
+	if (argc >= 3 && strcmp(argv[1], "--name") == 0)
+		proc_processes_filter_by_name(ps, argv[2]);
+
+	if (argc >= 3 && strcmp(argv[1], "--uid") == 0)
+		proc_processes_filter_by_uid(ps, (uid_t) atol(argv[2]));
+
+	while (proc_next_pid(ps, &pid) == 0)
+		printf(" %d", pid);
+
+	printf("\n");
+        proc_close_processes(ps);
+	return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc < 2) {
+		fprintf(stderr, "usage: %1$s --tasks <pid>\n"
+				"       %1$s --processes [---name <name>] [--uid <uid>]\n",
+				program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	if (strcmp(argv[1], "--tasks") == 0)
+		return test_tasks(argc - 1, argv + 1);
+	if (strcmp(argv[1], "--processes") == 0)
+		return test_processes(argc - 1, argv + 1);
+
+	return EXIT_FAILURE;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/randutils.c b/libblkid/lib/randutils.c
new file mode 100644
index 0000000..684ac0a
--- /dev/null
+++ b/libblkid/lib/randutils.c
@@ -0,0 +1,147 @@
+/*
+ * 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 "c.h"
+#include "randutils.h"
+#include "nls.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 | O_CLOEXEC);
+	if (fd == -1)
+		fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+	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;
+}
+
+
+/*
+ * Tell source of randomness.
+ */
+const char *random_tell_source(void)
+{
+	size_t i;
+	static const char *random_sources[] = {
+		"/dev/urandom",
+		"/dev/random"
+	};
+
+	for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
+		if (!access(random_sources[i], R_OK))
+			return random_sources[i];
+	}
+
+	return _("libc pseudo-random functions");
+}
+
+#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/lib/readutmp.c b/libblkid/lib/readutmp.c
new file mode 100644
index 0000000..b11e9a4
--- /dev/null
+++ b/libblkid/lib/readutmp.c
@@ -0,0 +1,78 @@
+/* GNU's read utmp module.
+
+	 Copyright (C) 1992-2001, 2003-2006, 2009-2014 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 jla; revised by djm */
+/* extracted for util-linux by ooprala */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "xalloc.h"
+#include "readutmp.h"
+
+/* Read the utmp entries corresponding to file FILE into freshly-
+	 malloc'd storage, set *UTMP_BUF to that pointer, set *N_ENTRIES to
+	 the number of entries, and return zero.	If there is any error,
+	 return -1, setting errno, and don't modify the parameters.
+	 If OPTIONS & READ_UTMP_CHECK_PIDS is nonzero, omit entries whose
+	 process-IDs do not currently exist.	*/
+int
+read_utmp (char const *file, size_t *n_entries, struct utmp **utmp_buf)
+{
+	size_t n_read = 0;
+	size_t n_alloc = 0;
+	struct utmp *utmp = NULL;
+	struct utmp *u;
+
+	/* Ignore the return value for now.
+		 Solaris' utmpname returns 1 upon success -- which is contrary
+		 to what the GNU libc version does.	In addition, older GNU libc
+		 versions are actually void.	 */
+	utmpname(file);
+
+	setutent();
+
+	errno = 0;
+	while ((u = getutent()) != NULL) {
+		if (n_read == n_alloc) {
+			n_alloc += 32;
+			utmp = xrealloc(utmp, n_alloc * sizeof (struct utmp));
+			if (!utmp)
+				return -1;
+		}
+		utmp[n_read++] = *u;
+	}
+	if (!u && errno) {
+		free(utmp);
+		return -1;
+	}
+
+	endutent();
+
+	*n_entries = n_read;
+	*utmp_buf = utmp;
+
+	return 0;
+}
diff --git a/libblkid/lib/setproctitle.c b/libblkid/lib/setproctitle.c
new file mode 100644
index 0000000..4bcf8c8
--- /dev/null
+++ b/libblkid/lib/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/lib/strutils.c b/libblkid/lib/strutils.c
new file mode 100644
index 0000000..9fe9481
--- /dev/null
+++ b/libblkid/lib/strutils.c
@@ -0,0 +1,763 @@
+/*
+ * 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 <assert.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 -ERANGE;
+		*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,Z,Y}
+ *        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,Z,Y}
+ * for example:
+ *		10KB	= 10000
+ *
+ * The optinal 'power' variable returns number associated with used suffix
+ * {K,M,G,T,P,E,Z,Y}  = {1,2,3,4,5,6,7,8}.
+ *
+ * The function also supports decimal point, for example:
+ *              0.5MB   = 500000
+ *              0.5MiB  = 512000
+ *
+ * Note that the function does not accept numbers with '-' (negative sign)
+ * prefix.
+ */
+int parse_size(const char *str, uintmax_t *res, int *power)
+{
+	char *p;
+	uintmax_t x, frac = 0;
+	int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
+
+	static const char *suf  = "KMGTPEYZ";
+	static const char *suf2 = "kmgtpeyz";
+	const char *sp;
+
+	*res = 0;
+
+	if (!str || !*str) {
+		rc = -EINVAL;
+		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 == '-') {
+		rc = -EINVAL;
+		goto err;
+	}
+	p = NULL;
+
+	errno = 0;
+	x = strtoumax(str, &p, 0);
+
+	if (p == str ||
+	    (errno != 0 && (x == UINTMAX_MAX || x == 0))) {
+		rc = errno ? -errno : -1;
+		goto err;
+	}
+	if (!p || !*p)
+		goto done;			/* without suffix */
+
+	/*
+	 * Check size suffixes
+	 */
+check_suffix:
+	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)) {
+		struct lconv const *l = localeconv();
+		char *dp = l ? l->decimal_point : NULL;
+		size_t dpsz = dp ? strlen(dp) : 0;
+
+		if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
+			char *fstr = p + dpsz;
+
+			for (p = fstr; *p && *p == '0'; p++)
+				frac_zeros++;
+			errno = 0, p = NULL;
+			frac = strtoumax(fstr, &p, 0);
+			if (p == fstr ||
+			    (errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
+				rc = errno ? -errno : -1;
+				goto err;
+			}
+			if (frac && (!p  || !*p)) {
+				rc = -EINVAL;
+				goto err;		/* without suffix, but with frac */
+			}
+			goto check_suffix;
+		}
+		rc = -EINVAL;
+		goto err;			/* unexpected suffix */
+	}
+
+	sp = strchr(suf, *p);
+	if (sp)
+		pwr = (sp - suf) + 1;
+	else {
+		sp = strchr(suf2, *p);
+		if (sp)
+			pwr = (sp - suf2) + 1;
+		else {
+			rc = -EINVAL;
+			goto err;
+		}
+	}
+
+	rc = do_scale_by_power(&x, base, pwr);
+	if (power)
+		*power = pwr;
+	if (frac && pwr) {
+		int zeros_in_pwr = frac_zeros % 3;
+		int frac_pwr = pwr - (frac_zeros / 3) - 1;
+		uintmax_t y = frac * (zeros_in_pwr == 0 ? 100 :
+				      zeros_in_pwr == 1 ?  10 : 1);
+
+		if (frac_pwr < 0) {
+			rc = -EINVAL;
+			goto err;
+		}
+		do_scale_by_power(&y, base, frac_pwr);
+		x += y;
+	}
+done:
+	*res = x;
+err:
+	return rc;
+}
+
+int strtosize(const char *str, uintmax_t *res)
+{
+	return parse_size(str, res, NULL);
+}
+
+int isdigit_string(const char *str)
+{
+	const char *p;
+
+	for (p = str; p && *p && isdigit((unsigned char) *p); p++);
+
+	return p && p > str && !*p;
+}
+
+
+#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);
+}
+
+
+void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
+{
+	double user_input;
+
+	user_input = strtod_or_err(str, errmesg);
+	tv->tv_sec = (time_t) user_input;
+	tv->tv_usec = (long)((user_input - tv->tv_sec) * 1000000);
+}
+
+/*
+ * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
+ * be 11 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/lib/swapprober.c b/libblkid/lib/swapprober.c
new file mode 100644
index 0000000..5a4b112
--- /dev/null
+++ b/libblkid/lib/swapprober.c
@@ -0,0 +1,49 @@
+
+#include "c.h"
+#include "nls.h"
+
+#include "swapheader.h"
+#include "swapprober.h"
+
+blkid_probe get_swap_prober(const char *devname)
+{
+	blkid_probe pr;
+	int rc;
+	const char *version = NULL;
+	char *swap_filter[] = { "swap", NULL };
+
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr) {
+		warn(_("%s: unable to probe device"), devname);
+		return NULL;
+	}
+
+	blkid_probe_enable_superblocks(pr, TRUE);
+	blkid_probe_set_superblocks_flags(pr,
+			BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
+			BLKID_SUBLKS_VERSION);
+
+	blkid_probe_filter_superblocks_type(pr, BLKID_FLTR_ONLYIN, swap_filter);
+
+	rc = blkid_do_safeprobe(pr);
+	if (rc == -1)
+		warn(_("%s: unable to probe device"), devname);
+	else if (rc == -2)
+		warnx(_("%s: ambiguous probing result; use wipefs(8)"), devname);
+	else if (rc == 1)
+		warnx(_("%s: not a valid swap partition"), devname);
+
+	if (rc == 0) {
+		/* Only the SWAPSPACE2 is supported. */
+		if (blkid_probe_lookup_value(pr, "VERSION", &version, NULL) == 0
+		    && version
+		    && strcmp(version, stringify_value(SWAP_VERSION)))
+			warnx(_("%s: unsupported swap version '%s'"),
+						devname, version);
+		else
+			return pr;
+	}
+
+	blkid_free_probe(pr);
+	return NULL;
+}
diff --git a/libblkid/lib/sysfs.c b/libblkid/lib/sysfs.c
new file mode 100644
index 0000000..8070750
--- /dev/null
+++ b/libblkid/lib/sysfs.c
@@ -0,0 +1,1074 @@
+/*
+ * 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 <string.h>
+#include <libgen.h>
+
+#include "c.h"
+#include "at.h"
+#include "pathnames.h"
+#include "sysfs.h"
+#include "fileutils.h"
+#include "all-io.h"
+
+#define STRINGIFY(x) #x
+#define EXPAND(x) STRINGIFY(x)
+
+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" UL_CLOEXECSTR);
+		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|O_CLOEXEC);
+	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 flags)
+{
+	int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, flags);
+
+	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, flags);
+	}
+	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, O_RDONLY|O_CLOEXEC);
+
+	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, O_RDONLY|O_CLOEXEC);
+
+	return fd < 0 ? NULL : fdopen(fd, "r" UL_CLOEXECSTR);
+}
+
+
+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 &&
+	    d->d_type != DT_UNKNOWN)
+		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 faccessat(dirfd(dir), path, R_OK, 0) == 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;
+}
+
+int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str)
+{
+	int fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
+	int rc, errsv;
+
+	if (fd < 0)
+		return -errno;
+	rc = write_all(fd, str, strlen(str));
+
+	errsv = errno;
+	close(fd);
+	errno = errsv;
+	return rc;
+}
+
+int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num)
+{
+	char buf[sizeof(STRINGIFY(ULLONG_MAX))];
+	int fd, rc = 0, len, errsv;
+
+	fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
+	if (fd < 0)
+		return -errno;
+
+	len = snprintf(buf, sizeof(buf), "%ju", num);
+	if (len < 0 || (size_t) len + 1 > sizeof(buf))
+		rc = -errno;
+	else
+		rc = write_all(fd, buf, len);
+
+	errsv = errno;
+	close(fd);
+	errno = errsv;
+	return rc;
+}
+
+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;
+}
+
+#define SUBSYSTEM_LINKNAME	"/subsystem"
+
+/*
+ * For example:
+ *
+ * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
+ *                           1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
+ *
+ * The function check if <chain>/subsystem symlink exists, if yes then returns
+ * basename of the readlink result, and remove the last subdirectory from the
+ * <chain> path.
+ */
+static char *get_subsystem(char *chain, char *buf, size_t bufsz)
+{
+	size_t len;
+	char *p;
+
+	if (!chain || !*chain)
+		return NULL;
+
+	len = strlen(chain);
+	if (len + sizeof(SUBSYSTEM_LINKNAME) > PATH_MAX)
+		return NULL;
+
+	do {
+		ssize_t sz;
+
+		/* append "/subsystem" to the path */
+		memcpy(chain + len, SUBSYSTEM_LINKNAME, sizeof(SUBSYSTEM_LINKNAME));
+
+		/* try if subsystem symlink exists */
+		sz = readlink(chain, buf, bufsz - 1);
+
+		/* remove last subsystem from chain */
+		chain[len] = '\0';
+		p = strrchr(chain, '/');
+		if (p) {
+			*p = '\0';
+			len = p - chain;
+		}
+
+		if (sz > 0) {
+			/* we found symlink to subsystem, return basename */
+			buf[sz] = '\0';
+			return basename(buf);
+		}
+
+	} while (p);
+
+	return NULL;
+}
+
+/*
+ * Returns complete path to the device, the patch contains all all sybsystems
+ * used for the device.
+ */
+char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz)
+{
+	/* read /sys/dev/block/<maj>:<min> symlink */
+	size_t sz = sysfs_readlink(cxt, NULL, buf, bufsz);
+	if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > bufsz)
+		return NULL;
+
+	buf[sz++] = '\0';
+
+	/* create absolute patch from the link */
+	memmove(buf + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, buf, sz);
+	memcpy(buf, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1);
+
+	return buf;
+}
+
+/*
+ * The @subsys returns the next subsystem in the chain. Function modifies
+ * @devchain string.
+ *
+ * Returns: 0 in success, <0 on error, 1 on end of chain
+ */
+int sysfs_next_subsystem(struct sysfs_cxt *cxt __attribute__((unused)),
+			 char *devchain, char **subsys)
+{
+	char subbuf[PATH_MAX];
+	char *sub;
+
+	if (!subsys || !devchain)
+		return -EINVAL;
+
+	while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
+		*subsys = strdup(sub);
+		if (!*subsys)
+			return -ENOMEM;
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int is_hotpluggable_subsystem(const char *name)
+{
+	static const char * const hotplug_subsystems[] = {
+		"usb",
+		"ieee1394",
+		"pcmcia",
+		"mmc",
+		"ccw"
+	};
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(hotplug_subsystems); i++)
+		if (strcmp(name, hotplug_subsystems[i]) == 0)
+			return 1;
+
+	return 0;
+}
+
+int sysfs_is_hotpluggable(struct sysfs_cxt *cxt)
+{
+	char buf[PATH_MAX], *chain, *sub;
+	int rc = 0;
+
+
+	/* check /sys/dev/block/<maj>:<min>/removable attribute */
+	if (sysfs_read_int(cxt, "removable", &rc) == 0 && rc == 1)
+		return 1;
+
+	chain = sysfs_get_devchain(cxt, buf, sizeof(buf));
+
+	while (chain && sysfs_next_subsystem(cxt, chain, &sub) == 0) {
+		rc = is_hotpluggable_subsystem(sub);
+		if (rc) {
+			free(sub);
+			break;
+		}
+		free(sub);
+	}
+
+	return rc;
+}
+
+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;
+}
+
+/*
+ * Returns by @diskdevno whole disk device devno and (optionaly) by
+ * @diskname the whole disk device name.
+ */
+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;
+}
+
+/*
+ * Returns 1 if the device is private LVM device.
+ */
+int sysfs_devno_is_lvm_private(dev_t devno)
+{
+	struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
+	char *uuid = NULL;
+	int rc = 0;
+
+	if (sysfs_init(&cxt, devno, NULL) != 0)
+		return 0;
+
+	uuid = sysfs_strdup(&cxt, "dm/uuid");
+
+	/* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
+	 * is the "LVM" prefix and "-<name>" postfix).
+	 */
+	if (uuid && strncmp(uuid, "LVM-", 4) == 0) {
+		char *p = strrchr(uuid + 4, '-');
+
+		if (p && *(p + 1))
+			rc = 1;
+	}
+
+	sysfs_deinit(&cxt);
+	free(uuid);
+	return rc;
+}
+
+/*
+ * Return 0 or 1, or < 0 in case of error
+ */
+int sysfs_devno_is_wholedisk(dev_t devno)
+{
+	dev_t disk;
+
+	if (sysfs_devno_to_wholedisk(devno, NULL, 0, &disk) != 0)
+		return -1;
+
+	return devno == disk;
+}
+
+
+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, '/');
+	if (!hctl)
+		return -1;
+	hctl++;
+
+	if (sscanf(hctl, "%u:%u:%u:%u", &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" UL_CLOEXECSTR)))
+                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], *sub, *chain;
+	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)));
+	printf("HOTPLUG: %s\n", sysfs_is_hotpluggable(&cxt) ? "yes" : "no");
+
+	chain = sysfs_get_devchain(&cxt, path, sizeof(path));
+	printf("SUBSUSTEMS:\n");
+
+	while (chain && sysfs_next_subsystem(&cxt, chain, &sub) == 0) {
+		printf("\t%s\n", sub);
+		free(sub);
+	}
+
+
+	sysfs_deinit(&cxt);
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/lib/terminal-colors.d.5 b/libblkid/lib/terminal-colors.d.5
new file mode 100644
index 0000000..66ecf2c
--- /dev/null
+++ b/libblkid/lib/terminal-colors.d.5
@@ -0,0 +1,190 @@
+.\" terminal-colors.d.5 --
+.\" Copyright 2014 Ondrej Oprala <ooprala@redhat.com>
+.\" Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+.\" Copyright 2014 Red Hat, Inc.
+.\" May be distributed under the GNU General Public License
+.TH "TERMINAL_COLORS.D" "5" "January 2014" "util-linux" "terminal-colors.d"
+.SH "NAME"
+terminal-colors.d \- Configure output colorization for various utilities
+.SH "SYNOPSIS"
+/etc/terminal-colors\&.d/[[\fIname\fR][@\fIterm\fR]\&.][\fItype\fR]
+.SH "DESCRIPTION"
+Files in this directory determine the default behavior for utilities
+when coloring output.
+
+The
+.I name
+is a utility name.  The name is optional and when none is specified then the
+file is used for all unspecified utilities.
+
+The
+.I term
+is a terminal identifier (the TERM environment variable).
+The terminal identifier is optional and when none is specified then the file
+is used for all unspecified terminals.
+
+The
+.I type
+is a file type.  Supported file types are:
+.TP
+.B disable
+Turns off output colorization for all compatible utilities.
+.TP
+.B enable
+Turns on output colorization; any matching
+.B disable
+files are ignored.
+.TP
+.B scheme
+Specifies colors used for output.  The file format may be specific to the utility,
+the default format is described below.
+.PP
+If there are more files that match for a utility, then the file with the more
+specific filename wins.  For example, the filename "@xterm.scheme" has less
+priority than "dmesg@xterm.scheme".  The lowest priority are those files without a
+utility name and terminal identifier (e.g. "disable").
+
+The user-specific
+.I $XDG_CONFIG_HOME/terminal-colors.d
+or
+.I $HOME/.config/terminal-colors.d
+overrides the global setting.
+
+.SH EXAMPLES
+Disable colors for all compatible utilities:
+.RS
+.br
+.B "touch /etc/terminal-colors.d/disable"
+.br
+.RE
+
+Disable colors for all compatible utils on a vt100 terminal:
+.RS
+.br
+.B "touch /etc/terminal-colors.d/@vt100.disable"
+.br
+.RE
+
+Disable colors for all compatible utils except dmesg(1):
+.RS
+.br
+.B "touch /etc/terminal-colors.d/disable"
+.sp
+.B "touch /etc/terminal-colors.d/dmesg.enable"
+.br
+.RE
+
+.SH DEFAULT SCHEME FILES FORMAT
+The following statement is recognized:
+
+.RS
+.br
+.B "name color-sequence"
+.br
+.RE
+
+The
+.B name
+is a logical name of color sequence (for example "error").  The names are
+specific to the utilities.  For more details always see the COLORS section
+in the man page for the utility.
+
+The
+.B color-sequence
+is a color name, ASCII color sequences or escape sequences.
+
+.SS Color names
+black, blue, brown, cyan, darkgray, gray, green, lightblue, lightcyan
+lightgray, lightgreen, lightmagenta, lightred, magenta, red and yellow
+.SS ANSI color sequences
+The color sequences are composed of sequences of numbers
+separated by semicolons.  The most common codes are:
+.sp
+.RS
+.TS
+l l.
+ 0	to restore default color
+ 1	for brighter colors
+ 4	for underlined text
+ 5	for flashing text
+30	for black foreground
+31	for red foreground
+32	for green foreground
+33	for yellow (or brown) foreground
+34	for blue foreground
+35	for purple foreground
+36	for cyan foreground
+37	for white (or gray) foreground
+40	for black background
+41	for red background
+42	for green background
+43	for yellow (or brown) background
+44	for blue background
+45	for purple background
+46	for cyan background
+47	for white (or gray) background
+.TE
+.RE
+.SS Escape sequences
+To specify control or blank characters in the color sequences,
+C-style \e-escaped notation can be used:
+.sp
+.RS
+.TS
+lb l.
+\ea	Bell (ASCII 7)
+\eb	Backspace (ASCII 8)
+\ee	Escape (ASCII 27)
+\ef	Form feed (ASCII 12)
+\en	Newline (ASCII 10)
+\er	Carriage Return (ASCII 13)
+\et	Tab (ASCII 9)
+\ev	Vertical Tab (ASCII 11)
+\e?	Delete (ASCII 127)
+\e_	Space
+\e\e	Backslash (\e)
+\e^	Caret (^)
+\e#	Hash mark (#)
+.TE
+.RE
+.sp
+Please note that escapes are necessary to enter a space, backslash,
+caret, or any control character anywhere in the string, as well as a
+hash mark as the first character.
+
+For example, to use a red background for alert messages in the output of
+.BR dmesg (1),
+use:
+
+.RS
+.br
+.B "echo 'alert 37;41' >> /etc/terminal-colors.d/dmesg.scheme"
+.br
+.RE
+
+.SS Comments
+Lines where the first non-blank character is a # (hash) are ignored.
+Any other use of the hash character is not interpreted as introducing
+a comment.
+
+.SH FILES
+.B $XDG_CONFIG_HOME/terminal-colors.d
+.br
+.B $HOME/.config/terminal-colors.d
+.br
+.B /etc/terminal-colors.d
+
+.SH ENVIRONMENT
+.IP TERMINAL_COLORS_DEBUG=all
+enables debug output.
+
+.SH COMPATIBILITY
+The terminal-colors.d functionality is currently supported by all util-linux
+utilities which provides colorized output.  For more details always see the
+COLORS section in the man page for the utility.
+
+.SH AVAILABILITY
+terminal-colors.d is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/libblkid/lib/timeutils.c b/libblkid/lib/timeutils.c
new file mode 100644
index 0000000..b811041
--- /dev/null
+++ b/libblkid/lib/timeutils.c
@@ -0,0 +1,342 @@
+/***
+  First set of functions in this file are part of systemd, and were
+  copied to util-linux at August 2013.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd 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.
+
+  systemd 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 util-linux; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "timeutils.h"
+
+#define WHITESPACE " \t\n\r"
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+static int parse_sec(const char *t, usec_t *usec)
+{
+	 static const struct {
+		const char *suffix;
+		usec_t usec;
+	 } table[] = {
+		{ "seconds",	USEC_PER_SEC },
+		{ "second",	USEC_PER_SEC },
+		{ "sec",	USEC_PER_SEC },
+		{ "s",		USEC_PER_SEC },
+		{ "minutes",	USEC_PER_MINUTE },
+		{ "minute",	USEC_PER_MINUTE },
+		{ "min",	USEC_PER_MINUTE },
+		{ "months",	USEC_PER_MONTH },
+		{ "month",	USEC_PER_MONTH },
+		{ "msec",	USEC_PER_MSEC },
+		{ "ms",		USEC_PER_MSEC },
+		{ "m",		USEC_PER_MINUTE },
+		{ "hours",	USEC_PER_HOUR },
+		{ "hour",	USEC_PER_HOUR },
+		{ "hr",		USEC_PER_HOUR },
+		{ "h",		USEC_PER_HOUR },
+		{ "days",	USEC_PER_DAY },
+		{ "day",	USEC_PER_DAY },
+		{ "d",		USEC_PER_DAY },
+		{ "weeks",	USEC_PER_WEEK },
+		{ "week",	USEC_PER_WEEK },
+		{ "w",		USEC_PER_WEEK },
+		{ "years",	USEC_PER_YEAR },
+		{ "year",	USEC_PER_YEAR },
+		{ "y",		USEC_PER_YEAR },
+		{ "usec",	1ULL },
+		{ "us",		1ULL },
+		{ "",		USEC_PER_SEC },	/* default is sec */
+	 };
+
+	const char *p;
+	usec_t r = 0;
+	int something = FALSE;
+
+	assert(t);
+	assert(usec);
+
+	p = t;
+	for (;;) {
+		long long l, z = 0;
+		char *e;
+		unsigned i, n = 0;
+
+		p += strspn(p, WHITESPACE);
+
+		if (*p == 0) {
+			if (!something)
+				return -EINVAL;
+
+			break;
+		}
+
+		errno = 0;
+		l = strtoll(p, &e, 10);
+
+		if (errno > 0)
+			return -errno;
+
+		if (l < 0)
+			return -ERANGE;
+
+		if (*e == '.') {
+			char *b = e + 1;
+
+			errno = 0;
+			z = strtoll(b, &e, 10);
+			if (errno > 0)
+				return -errno;
+
+			if (z < 0)
+				return -ERANGE;
+
+			if (e == b)
+				return -EINVAL;
+
+			n = e - b;
+
+		} else if (e == p)
+			return -EINVAL;
+
+		e += strspn(e, WHITESPACE);
+
+		for (i = 0; i < ARRAY_SIZE(table); i++)
+			if (startswith(e, table[i].suffix)) {
+				usec_t k = (usec_t) z * table[i].usec;
+
+				for (; n > 0; n--)
+					k /= 10;
+
+				r += (usec_t) l *table[i].usec + k;
+				p = e + strlen(table[i].suffix);
+
+				something = TRUE;
+				break;
+			}
+
+		if (i >= ARRAY_SIZE(table))
+			return -EINVAL;
+
+	}
+
+	*usec = r;
+
+	return 0;
+}
+
+int parse_timestamp(const char *t, usec_t *usec)
+{
+	 static const struct {
+		const char *name;
+		const int nr;
+	 } day_nr[] = {
+		{ "Sunday",	0 },
+		{ "Sun",	0 },
+		{ "Monday",	1 },
+		{ "Mon",	1 },
+		{ "Tuesday",	2 },
+		{ "Tue",	2 },
+		{ "Wednesday",	3 },
+		{ "Wed",	3 },
+		{ "Thursday",	4 },
+		{ "Thu",	4 },
+		{ "Friday",	5 },
+		{ "Fri",	5 },
+		{ "Saturday",	6 },
+		{ "Sat",	6 },
+	 };
+
+	const char *k;
+	struct tm tm, copy;
+	time_t x;
+	usec_t plus = 0, minus = 0, ret;
+	int r, weekday = -1;
+	unsigned i;
+
+	/*
+	 * Allowed syntaxes:
+	 *
+	 *   2012-09-22 16:34:22
+	 *   2012-09-22 16:34	  (seconds will be set to 0)
+	 *   2012-09-22		  (time will be set to 00:00:00)
+	 *   16:34:22		  (date will be set to today)
+	 *   16:34		  (date will be set to today, seconds to 0)
+	 *   now
+	 *   yesterday		  (time is set to 00:00:00)
+	 *   today		  (time is set to 00:00:00)
+	 *   tomorrow		  (time is set to 00:00:00)
+	 *   +5min
+	 *   -5days
+	 *
+	 */
+
+	assert(t);
+	assert(usec);
+
+	x = time(NULL);
+	localtime_r(&x, &tm);
+	tm.tm_isdst = -1;
+
+	if (streq(t, "now"))
+		goto finish;
+
+	else if (streq(t, "today")) {
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+
+	} else if (streq(t, "yesterday")) {
+		tm.tm_mday--;
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+
+	} else if (streq(t, "tomorrow")) {
+		tm.tm_mday++;
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+
+	} else if (t[0] == '+') {
+
+		r = parse_sec(t + 1, &plus);
+		if (r < 0)
+			return r;
+
+		goto finish;
+	} else if (t[0] == '-') {
+
+		r = parse_sec(t + 1, &minus);
+		if (r < 0)
+			return r;
+
+		goto finish;
+
+	} else if (endswith(t, " ago")) {
+		char *z;
+
+		z = strndup(t, strlen(t) - 4);
+		if (!z)
+			return -ENOMEM;
+
+		r = parse_sec(z, &minus);
+		free(z);
+		if (r < 0)
+			return r;
+
+		goto finish;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(day_nr); i++) {
+		size_t skip;
+
+		if (!startswith_no_case(t, day_nr[i].name))
+			continue;
+
+		skip = strlen(day_nr[i].name);
+		if (t[skip] != ' ')
+			continue;
+
+		weekday = day_nr[i].nr;
+		t += skip + 1;
+		break;
+	}
+
+	copy = tm;
+	k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+	if (k && *k == 0)
+		goto finish;
+
+	tm = copy;
+	k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+	if (k && *k == 0)
+		goto finish;
+
+	tm = copy;
+	k = strptime(t, "%y-%m-%d %H:%M", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%y-%m-%d", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%Y-%m-%d", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%H:%M:%S", &tm);
+	if (k && *k == 0)
+		goto finish;
+
+	tm = copy;
+	k = strptime(t, "%H:%M", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%Y%m%d%H%M%S", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	return -EINVAL;
+
+ finish:
+	x = mktime(&tm);
+	if (x == (time_t)-1)
+		return -EINVAL;
+
+	if (weekday >= 0 && tm.tm_wday != weekday)
+		return -EINVAL;
+
+	ret = (usec_t) x *USEC_PER_SEC;
+
+	ret += plus;
+	if (ret > minus)
+		ret -= minus;
+	else
+		ret = 0;
+
+	*usec = ret;
+
+	return 0;
+}
diff --git a/libblkid/lib/ttyutils.c b/libblkid/lib/ttyutils.c
new file mode 100644
index 0000000..ea551e2
--- /dev/null
+++ b/libblkid/lib/ttyutils.c
@@ -0,0 +1,95 @@
+/*
+ * 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 (STDIN_FILENO, TIOCGSIZE, &t_win) == 0)
+		return t_win.ts_cols;
+#endif
+#ifdef TIOCGWINSZ
+	if (ioctl (STDIN_FILENO, 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(int fd,
+		      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(fd);
+	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(STDERR_FILENO, &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/libblkid.3 b/libblkid/libblkid.3
new file mode 100644
index 0000000..58ca91c
--- /dev/null
+++ b/libblkid/libblkid.3
@@ -0,0 +1,79 @@
+.\" Copyright 2001 Andreas Dilger (adilger@turbolinux.com)
+.\"
+.\" This man page was created for libblkid.so.1.0 from e2fsprogs-1.24.
+.\"
+.\" This file may be copied under the terms of the GNU Lesser General Public
+.\" License.
+.\"
+.\" Created  Wed Sep 14 12:02:12 2001, Andreas Dilger
+.TH LIBBLKID 3 "May 2009" "util-linux" "Programmer's Manual"
+.SH NAME
+libblkid \- block device identification library
+.SH SYNOPSIS
+.B #include <blkid.h>
+.sp
+.B cc
+.I file.c
+.B \-lblkid
+.SH DESCRIPTION
+The
+.B libblkid
+library is used to identify block devices (disks) as to their content (e.g.
+filesystem type) as well as extracting additional information such as
+filesystem labels/volume names, unique identifiers/serial numbers.
+A common use is to allow use of LABEL= and UUID= tags instead of hard-coding
+specific block device names into configuration files.
+.P
+The low-level part of the library also allows to extract information about
+partitions and block device topology.
+.P
+The high-level part of the library keeps information about block devices in a
+cache file 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.
+.P
+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).
+.P
+The high-level part of the library supports two methods to evaluate LABEL/UUID.
+It reads information directly from a block device or read information from
+/dev/disk/by-* udev symlinks.  The udev is preferred method by default.
+.P
+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.
+.P
+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
+.B required
+in this situation.
+.SH CONFIGURATION FILE
+The standard location of the
+.I /etc/blkid.conf
+config file can be overridden by the environment variable BLKID_CONF.  For more
+details about the config file see
+.BR blkid (8)
+man page.
+.SH AUTHOR
+.B libblkid
+was written by Andreas Dilger for the ext2 filesystem utilties, with input
+from Ted Ts'o.  The library was subsequently heavily modified by Ted Ts'o.
+
+The low-level probing code was rewritten by Karel Zak.
+.SH COPYING
+.B libblkid
+is available under the terms of the GNU Library General Public License (LGPL),
+version 2 (or at your discretion any later version).
+.SH "SEE ALSO"
+.BR blkid (8),
+.BR findfs (8)
+.SH AVAILABILITY
+libblkid is part of the util-linux package since version 2.15 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/libblkid/libfdisk/COPYING b/libblkid/libfdisk/COPYING
new file mode 100644
index 0000000..be1a5b3
--- /dev/null
+++ b/libblkid/libfdisk/COPYING
@@ -0,0 +1,8 @@
+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.
+
+The complete text of the license is available in the
+../Documentation/licenses/COPYING.LGPLv2.1 file.
diff --git a/libblkid/libfdisk/Makemodule.am b/libblkid/libfdisk/Makemodule.am
new file mode 100644
index 0000000..5d83341
--- /dev/null
+++ b/libblkid/libfdisk/Makemodule.am
@@ -0,0 +1,14 @@
+if BUILD_LIBFDISK
+
+include libfdisk/src/Makemodule.am
+
+if ENABLE_GTK_DOC
+# Docs uses separate Makefiles
+SUBDIRS += libfdisk/docs
+endif
+
+pkgconfig_DATA += libfdisk/fdisk.pc
+PATHFILES      += libfdisk/fdisk.pc
+EXTRA_DIST     += libfdisk/COPYING
+
+endif # BUILD_LIBFDISK
diff --git a/libblkid/libfdisk/docs/.gitignore b/libblkid/libfdisk/docs/.gitignore
new file mode 100644
index 0000000..f91f93d
--- /dev/null
+++ b/libblkid/libfdisk/docs/.gitignore
@@ -0,0 +1,18 @@
+*-decl-list.txt
+*-decl.txt
+*-overrides.txt
+*-undeclared.txt
+*-undocumented.txt
+*-unused.txt
+*.args
+*.bak
+*.hierarchy
+*.interfaces
+*.prerequisites
+*.signals
+*.stamp
+*.types
+html/*
+tmpl/*
+version.xml
+xml/*
diff --git a/libblkid/libfdisk/docs/Makefile.am b/libblkid/libfdisk/docs/Makefile.am
new file mode 100644
index 0000000..dc70979
--- /dev/null
+++ b/libblkid/libfdisk/docs/Makefile.am
@@ -0,0 +1,93 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.10 at least.
+AUTOMAKE_OPTIONS = 1.10
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=libfdisk
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=../src
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml
+MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space fdisk
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=$(top_builddir)/libfdisk/src/libfdisk.h
+CFILE_GLOB=$(top_srcdir)/libfdisk/src/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+EXTRA_HFILES=
+
+# Header files to ignore when scanning. Use base file name, no paths
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
+IGNORE_HFILES=fdiskP.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files = $(builddir)/version.xml
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS=
+GTKDOC_LIBS=
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/config/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST += version.xml.in
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES += version.xml
diff --git a/libblkid/libfdisk/docs/libfdisk-docs.xml b/libblkid/libfdisk/docs/libfdisk-docs.xml
new file mode 100644
index 0000000..546d007
--- /dev/null
+++ b/libblkid/libfdisk/docs/libfdisk-docs.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+  <bookinfo>
+    <title>libfdisk Reference Manual</title>
+    <releaseinfo>for libfdisk version &version;</releaseinfo>
+    <copyright>
+      <year>2014</year>
+      <holder>Karel Zak &lt;kzak@redhat.com&gt;</holder>
+    </copyright>
+  </bookinfo>
+
+  <part id="over">
+    <title>libfdisk Overview</title>
+    <partintro>
+    <para>
+The libfdisk library is used for manipulating with partition tables.
+    </para>
+    <para>
+The library is part of the util-linux package since version 2.26 and is
+available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+    </para>
+  </partintro>
+ </part>
+
+  <part>
+    <title>Basic handlers and setting</title>
+    <xi:include href="xml/context.xml"/>
+    <xi:include href="xml/ask.xml"/>
+    <xi:include href="xml/alignment.xml"/>
+    <xi:include href="xml/script.xml"/>
+  </part>
+   <part>
+    <title>Partitining</title>
+    <xi:include href="xml/label.xml"/>
+    <xi:include href="xml/partition.xml"/>
+    <xi:include href="xml/table.xml"/>
+    <xi:include href="xml/parttype.xml"/>
+  </part>
+  <part>
+    <title>Label specific functions</title>
+    <xi:include href="xml/dos.xml"/>
+    <xi:include href="xml/gpt.xml"/>
+    <xi:include href="xml/sun.xml"/>
+    <xi:include href="xml/sgi.xml"/>
+    <xi:include href="xml/bsd.xml"/>
+  </part>
+  <part>
+    <title>Misc</title>
+    <xi:include href="xml/iter.xml"/>
+    <xi:include href="xml/utils.xml"/>
+    <xi:include href="xml/init.xml"/>
+  </part>
+  <index id="api-index-full">
+    <title>API Index</title>
+    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+  </index>
+</book>
diff --git a/libblkid/libfdisk/docs/libfdisk-sections.txt b/libblkid/libfdisk/docs/libfdisk-sections.txt
new file mode 100644
index 0000000..c0aaeae
--- /dev/null
+++ b/libblkid/libfdisk/docs/libfdisk-sections.txt
@@ -0,0 +1,303 @@
+<SECTION>
+<FILE>init</FILE>
+fdisk_init_debug
+</SECTION>
+
+<SECTION>
+<FILE>ask</FILE>
+fdisk_info
+fdisk_warn
+fdisk_warnx
+fdisk_set_ask
+<SUBSECTION>
+fdisk_ask
+fdisk_ask_get_query
+fdisk_ask_get_type
+fdisk_ask_menu_get_default
+fdisk_ask_menu_get_item
+fdisk_ask_menu_get_nitems
+fdisk_ask_menu_get_result
+fdisk_ask_menu_set_result
+fdisk_ask_number
+fdisk_ask_number_get_base
+fdisk_ask_number_get_default
+fdisk_ask_number_get_high
+fdisk_ask_number_get_low
+fdisk_ask_number_get_range
+fdisk_ask_number_get_result
+fdisk_ask_number_get_unit
+fdisk_ask_number_inchars
+fdisk_ask_number_set_relative
+fdisk_ask_number_set_result
+fdisk_ask_partnum
+fdisk_ask_print_get_errno
+fdisk_ask_print_get_mesg
+fdisk_ask_string
+fdisk_ask_string_get_result
+fdisk_ask_string_set_result
+fdisk_ask_yesno
+fdisk_ask_yesno_get_result
+fdisk_ask_yesno_set_result
+fdisk_ref_ask
+fdisk_unref_ask
+</SECTION>
+
+<SECTION>
+<FILE>alignment</FILE>
+fdisk_align_lba
+fdisk_align_lba_in_range
+fdisk_has_user_device_properties
+fdisk_lba_is_phy_aligned
+fdisk_override_geometry
+fdisk_reread_partition_table
+fdisk_reset_alignment
+fdisk_reset_device_properties
+fdisk_save_user_geometry
+fdisk_save_user_sector_size
+</SECTION>
+
+<SECTION>
+<FILE>label</FILE>
+fdisk_create_disklabel
+fdisk_list_disklabel
+fdisk_locate_disklabel
+fdisk_reorder_partitions
+fdisk_set_disklabel_id
+fdisk_set_partition_type
+fdisk_toggle_partition_flag
+fdisk_verify_disklabel
+fdisk_write_disklabel
+<SUBSECTION>
+fdisk_get_disklabel_id
+fdisk_get_label
+fdisk_get_nlabels
+fdisk_next_label
+fdisk_get_npartitions
+<SUBSECTION>
+fdisk_field
+fdisk_field_get_id
+fdisk_field_get_name
+fdisk_field_get_width
+fdisk_field_is_number
+<SUBSECTION>
+fdisk_label
+fdisk_label_get_field
+fdisk_label_get_field_by_name
+fdisk_label_get_fields_ids
+fdisk_label_get_name
+fdisk_label_get_nparttypes
+fdisk_label_get_parttype
+fdisk_label_get_parttype_from_code
+fdisk_label_get_parttype_from_string
+fdisk_label_get_type
+fdisk_label_has_code_parttypes
+fdisk_label_is_changed
+fdisk_label_is_disabled
+fdisk_label_parse_parttype
+fdisk_label_require_geometry
+fdisk_label_set_changed
+fdisk_label_set_disabled
+</SECTION>
+
+<SECTION>
+<FILE>script</FILE>
+fdisk_set_script
+fdisk_get_script
+<SUBSECTION>
+fdisk_apply_script
+fdisk_apply_script_headers
+<SUBSECTION>
+fdisk_script
+fdisk_new_script
+fdisk_new_script_from_file
+fdisk_ref_script
+fdisk_script_get_header
+fdisk_script_get_nlines
+fdisk_script_get_table
+fdisk_script_read_context
+fdisk_script_read_file
+fdisk_script_read_line
+fdisk_script_set_header
+fdisk_script_write_file
+fdisk_unref_script
+</SECTION>
+
+<SECTION>
+<FILE>bsd</FILE>
+fdisk_bsd_edit_disklabel
+fdisk_bsd_link_partition
+fdisk_bsd_write_bootstrap
+</SECTION>
+
+<SECTION>
+<FILE>partition</FILE>
+fdisk_add_partition
+fdisk_delete_all_partitions
+fdisk_delete_partition
+fdisk_get_partition
+fdisk_is_partition_used
+fdisk_set_partition
+<SUBSECTION>
+fdisk_partition
+fdisk_new_partition
+fdisk_partition_cmp_partno
+fdisk_partition_cmp_start
+fdisk_partition_end_follow_default
+fdisk_partition_end_is_default
+fdisk_partition_get_attrs
+fdisk_partition_get_end
+fdisk_partition_get_name
+fdisk_partition_get_parent
+fdisk_partition_get_partno
+fdisk_partition_get_size
+fdisk_partition_get_start
+fdisk_partition_get_type
+fdisk_partition_get_uuid
+fdisk_partition_has_end
+fdisk_partition_has_partno
+fdisk_partition_has_size
+fdisk_partition_has_start
+fdisk_partition_is_bootable
+fdisk_partition_is_container
+fdisk_partition_is_freespace
+fdisk_partition_is_nested
+fdisk_partition_is_used
+fdisk_partition_next_partno
+fdisk_partition_partno_follow_default
+fdisk_partition_set_attrs
+fdisk_partition_set_name
+fdisk_partition_set_partno
+fdisk_partition_set_size
+fdisk_partition_set_start
+fdisk_partition_set_type
+fdisk_partition_set_uuid
+fdisk_partition_size_explicit
+fdisk_partition_start_follow_default
+fdisk_partition_start_is_default
+fdisk_partition_to_string
+fdisk_partition_unset_partno
+fdisk_partition_unset_size
+fdisk_partition_unset_start
+fdisk_ref_partition
+fdisk_reset_partition
+fdisk_unref_partition
+</SECTION>
+
+<SECTION>
+<FILE>dos</FILE>
+fdisk_dos_enable_compatible
+fdisk_dos_is_compatible
+fdisk_dos_move_begin
+</SECTION>
+
+<SECTION>
+<FILE>sgi</FILE>
+fdisk_sgi_create_info
+fdisk_sgi_set_bootfile
+</SECTION>
+
+<SECTION>
+<FILE>gpt</FILE>
+fdisk_gpt_is_hybrid
+</SECTION>
+
+<SECTION>
+<FILE>sun</FILE>
+fdisk_sun_set_alt_cyl
+fdisk_sun_set_ilfact
+fdisk_sun_set_pcylcount
+fdisk_sun_set_rspeed
+fdisk_sun_set_xcyl
+</SECTION>
+
+<SECTION>
+<FILE>parttype</FILE>
+fdisk_parttype
+fdisk_copy_parttype
+fdisk_new_parttype
+fdisk_new_unknown_parttype
+fdisk_parttype_get_code
+fdisk_parttype_get_name
+fdisk_parttype_get_string
+fdisk_parttype_is_unknown
+fdisk_parttype_set_code
+fdisk_parttype_set_name
+fdisk_parttype_set_typestr
+fdisk_ref_parttype
+fdisk_unref_parttype
+</SECTION>
+
+<SECTION>
+<FILE>table</FILE>
+fdisk_get_freespaces
+fdisk_get_partitions
+<SUBSECTION>
+fdisk_table
+fdisk_apply_table
+fdisk_new_table
+fdisk_ref_table
+fdisk_reset_table
+fdisk_table_add_partition
+fdisk_table_get_nents
+fdisk_table_get_partition
+fdisk_table_is_empty
+fdisk_table_next_partition
+fdisk_table_remove_partition
+fdisk_table_sort_partitions
+fdisk_table_wrong_order
+fdisk_unref_table
+</SECTION>
+
+
+<SECTION>
+<FILE>context</FILE>
+fdisk_context
+fdisk_assign_device
+fdisk_deassign_device
+fdisk_enable_details
+fdisk_enable_listonly
+fdisk_get_alignment_offset
+fdisk_get_devfd
+fdisk_get_devname
+fdisk_get_first_lba
+fdisk_get_geom_cylinders
+fdisk_get_geom_heads
+fdisk_get_geom_sectors
+fdisk_get_grain_size
+fdisk_get_last_lba
+fdisk_get_minimal_iosize
+fdisk_get_nsectors
+fdisk_get_optimal_iosize
+fdisk_get_parent
+fdisk_get_physector_size
+fdisk_get_sector_size
+fdisk_get_unit
+fdisk_get_units_per_sector
+fdisk_has_label
+fdisk_has_user_device_properties
+fdisk_is_details
+fdisk_is_labeltype
+fdisk_is_listonly
+fdisk_is_readonly
+fdisk_new_context
+fdisk_new_nested_context
+fdisk_ref_context
+fdisk_set_first_lba
+fdisk_set_last_lba
+fdisk_set_unit
+fdisk_unref_context
+fdisk_use_cylinders
+</SECTION>
+
+<SECTION>
+<FILE>utils</FILE>
+fdisk_partname
+</SECTION>
+
+<SECTION>
+<FILE>iter</FILE>
+fdisk_free_iter
+fdisk_iter_get_direction
+fdisk_new_iter
+fdisk_reset_iter
+</SECTION>
diff --git a/libblkid/libfdisk/docs/version.xml.in b/libblkid/libfdisk/docs/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/libblkid/libfdisk/docs/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/libblkid/libfdisk/fdisk.pc.in b/libblkid/libfdisk/fdisk.pc.in
new file mode 100644
index 0000000..bf81df0
--- /dev/null
+++ b/libblkid/libfdisk/fdisk.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@usrlib_execdir@
+includedir=@includedir@
+
+Name: fdisk
+Description: fdisk library
+Version: @LIBFDISK_VERSION@
+Requires.private: @LIBFDISK_PC_REQUIRES@
+Cflags: -I${includedir}/libfdisk
+Libs: -L${libdir} -lfdisk
diff --git a/libblkid/libfdisk/src/.gitignore b/libblkid/libfdisk/src/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libblkid/libfdisk/src/.gitignore
diff --git a/libblkid/libfdisk/src/Makemodule.am b/libblkid/libfdisk/src/Makemodule.am
new file mode 100644
index 0000000..18ddec7
--- /dev/null
+++ b/libblkid/libfdisk/src/Makemodule.am
@@ -0,0 +1,112 @@
+
+# libfdisk.h is generated, so it's stored in builddir!
+fdiskincdir = $(includedir)/libfdisk
+nodist_fdiskinc_HEADERS = $(top_builddir)/libfdisk/src/libfdisk.h
+
+usrlib_exec_LTLIBRARIES += libfdisk.la
+libfdisk_la_SOURCES = \
+	include/list.h \
+	\
+	libfdisk/src/fdiskP.h \
+	libfdisk/src/init.c \
+	libfdisk/src/test.c \
+	libfdisk/src/ask.c \
+	libfdisk/src/alignment.c \
+	libfdisk/src/label.c \
+	libfdisk/src/utils.c \
+	libfdisk/src/context.c \
+	libfdisk/src/parttype.c \
+	libfdisk/src/partition.c \
+	libfdisk/src/table.c \
+	libfdisk/src/iter.c \
+	libfdisk/src/script.c \
+	\
+	libfdisk/src/sun.c \
+	libfdisk/src/sgi.c \
+	libfdisk/src/dos.c \
+	libfdisk/src/bsd.c \
+	libfdisk/src/gpt.c \
+	$(nodist_fdiskinc_HEADERS)
+
+
+nodist_libfdisk_la_SOURCES = libfdisk/src/fdiskP.h
+
+libfdisk_la_LIBADD = libcommon.la libuuid.la
+
+libfdisk_la_CFLAGS = \
+	$(SOLIB_CFLAGS) \
+	-I$(ul_libuuid_incdir) \
+	-I$(ul_libfdisk_incdir) \
+	-I$(top_srcdir)/libfdisk/src
+
+libfdisk_la_DEPENDENCIES = \
+	libcommon.la \
+	libuuid.la \
+	libfdisk/src/libfdisk.sym \
+	libfdisk/src/libfdisk.h.in
+
+libfdisk_la_LDFLAGS = \
+	$(SOLIB_LDFLAGS) \
+	-Wl,--version-script=$(top_srcdir)/libfdisk/src/libfdisk.sym \
+	-version-info $(LIBFDISK_VERSION_INFO)
+
+if BUILD_LIBBLKID
+libfdisk_la_LIBADD += libblkid.la
+libfdisk_la_DEPENDENCIES += libblkid.la
+libfdisk_la_CFLAGS += -I$(ul_libblkid_incdir)
+endif
+
+EXTRA_DIST += \
+	libfdisk/src/libfdisk.sym \
+	libfdisk/src/libfdisk.h.in
+
+if BUILD_LIBFDISK_TESTS
+check_PROGRAMS += \
+	test_fdisk_ask \
+	test_fdisk_script \
+	test_fdisk_utils
+
+libfdisk_tests_cflags  = -DTEST_PROGRAM $(libfdisk_la_CFLAGS)
+libfdisk_tests_ldflags = libuuid.la -static
+libfdisk_tests_ldadd   = libfdisk.la $(UUID_LIBS)
+
+if BUILD_LIBBLKID
+libfdisk_tests_ldflags += libblkid.la
+endif
+
+test_fdisk_ask_SOURCES = libfdisk/src/ask.c
+test_fdisk_ask_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_ask_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_ask_LDADD = $(libfdisk_tests_ldadd)
+
+test_fdisk_utils_SOURCES = libfdisk/src/utils.c
+test_fdisk_utils_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_utils_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_utils_LDADD = $(libfdisk_tests_ldadd)
+
+test_fdisk_script_SOURCES = libfdisk/src/script.c
+test_fdisk_script_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_script_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_script_LDADD = $(libfdisk_tests_ldadd)
+
+endif # BUILD_LIBFDISK_TESTS
+
+
+# move lib from $(usrlib_execdir) to $(libdir) if needed
+install-exec-hook-libfdisk:
+	if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/libfdisk.so"; then \
+		mkdir -p $(DESTDIR)$(libdir); \
+		mv $(DESTDIR)$(usrlib_execdir)/libfdisk.so.* $(DESTDIR)$(libdir); \
+		so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libfdisk.so); \
+		so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
+		(cd $(DESTDIR)$(usrlib_execdir) && \
+			rm -f libfdisk.so && \
+			$(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libfdisk.so); \
+	fi
+
+uninstall-hook-libfdisk:
+	rm -f $(DESTDIR)$(libdir)/libfdisk.so*
+
+INSTALL_EXEC_HOOKS += install-exec-hook-libfdisk
+UNINSTALL_HOOKS += uninstall-hook-libfdisk
+
diff --git a/libblkid/libfdisk/src/alignment.c b/libblkid/libfdisk/src/alignment.c
new file mode 100644
index 0000000..67f1ddd
--- /dev/null
+++ b/libblkid/libfdisk/src/alignment.c
@@ -0,0 +1,654 @@
+
+#ifdef HAVE_LIBBLKID
+#include <blkid.h>
+#endif
+#include "blkdev.h"
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: alignment
+ * @title: Alignment
+ * @short_description: functions to align partitions and work with disk topology and geometry
+ *
+ * The libfdisk aligns the end of the partitions to make it possible to align
+ * the next partition to the "grain" (see fdisk_get_grain()). The grain is
+ * usually 1MiB (or more for devices where optimal I/O is greater than 1MiB).
+ *
+ * It means that the library does not align strictly to physical sector size
+ * (or minimal or optimal I/O), but it uses greater granularity. It makes
+ * partition tables more portable. If you copy disk layout from 512-sector to
+ * 4K-sector device, all partitions are still aligned to physical sectors.
+ *
+ * This unified concept also makes partition tables more user friendly, all
+ * tables look same, LBA of the first partition is 2048 sectors everywhere, etc.
+ *
+ * It's recommended to not change any alignment or device properties. All is
+ * initialized by default by fdisk_assign_device().
+ *
+ * Note that terminology used by libfdisk is: 
+ *   - device properties: I/O limits (topology), geometry, sector size, ...
+ *   - alignment: first, last LBA, grain, ...
+ *
+ * The alignment setting may be modified by disk label driver.
+ */
+
+/*
+ * Alignment according to logical granularity (usually 1MiB)
+ */
+static int lba_is_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
+	uintmax_t offset;
+
+	if (cxt->grain > granularity)
+		granularity = cxt->grain;
+	offset = (lba * cxt->sector_size) & (granularity - 1);
+
+	return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+}
+
+/*
+ * Alignment according to physical device topology (usually minimal i/o size)
+ */
+static int lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
+	uintmax_t offset = (lba * cxt->sector_size) & (granularity - 1);
+
+	return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+}
+
+/**
+ * fdisk_align_lba:
+ * @cxt: context
+ * @lba: address to align
+ * @direction: FDISK_ALIGN_{UP,DOWN,NEAREST}
+ *
+ * This function aligns @lba to the "grain" (see fdisk_get_grain()). If the
+ * device uses alignment offset then the result is moved according the offset
+ * to be on the physical boundary.
+ *
+ * Returns: alignment LBA.
+ */
+fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction)
+{
+	fdisk_sector_t res;
+
+	if (lba_is_aligned(cxt, lba))
+		res = lba;
+	else {
+		fdisk_sector_t sects_in_phy = cxt->grain / cxt->sector_size;
+
+		if (lba < cxt->first_lba)
+			res = cxt->first_lba;
+
+		else if (direction == FDISK_ALIGN_UP)
+			res = ((lba + sects_in_phy) / sects_in_phy) * sects_in_phy;
+
+		else if (direction == FDISK_ALIGN_DOWN)
+			res = (lba / sects_in_phy) * sects_in_phy;
+
+		else /* FDISK_ALIGN_NEAREST */
+			res = ((lba + sects_in_phy / 2) / sects_in_phy) * sects_in_phy;
+
+		if (cxt->alignment_offset && !lba_is_aligned(cxt, res) &&
+		    res > cxt->alignment_offset / cxt->sector_size) {
+			/*
+			 * apply alignment_offset
+			 *
+			 * On disk with alignment compensation physical blocks starts
+			 * at LBA < 0 (usually LBA -1). It means we have to move LBA
+			 * according the offset to be on the physical boundary.
+			 */
+			/* fprintf(stderr, "LBA: %llu apply alignment_offset\n", res); */
+			res -= (max(cxt->phy_sector_size, cxt->min_io_size) -
+					cxt->alignment_offset) / cxt->sector_size;
+
+			if (direction == FDISK_ALIGN_UP && res < lba)
+				res += sects_in_phy;
+		}
+	}
+
+	if (lba != res)
+		DBG(CXT, ul_debugobj(cxt, "LBA %ju -aligned-to-> %ju",
+				(uintmax_t) lba,
+				(uintmax_t) res));
+	return res;
+}
+
+/**
+ * fdisk_align_lba_in_range:
+ * @cxt: context
+ * @lba: LBA
+ * @start: range start
+ * @stop: range stop
+ *
+ * Align @lba, the result has to be between @start and @stop
+ *
+ * Returns: aligned LBA
+ */
+fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
+				  fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop)
+{
+	fdisk_sector_t res;
+
+	start = fdisk_align_lba(cxt, start, FDISK_ALIGN_UP);
+	stop = fdisk_align_lba(cxt, stop, FDISK_ALIGN_DOWN);
+	lba = fdisk_align_lba(cxt, lba, FDISK_ALIGN_NEAREST);
+
+	if (lba < start)
+		res = start;
+	else if (lba > stop)
+		res = stop;
+	else
+		res = lba;
+
+	DBG(CXT, ul_debugobj(cxt, "LBA %ju range:<%ju..%ju>, result: %ju",
+				(uintmax_t) lba,
+				(uintmax_t) start,
+				(uintmax_t) stop,
+				(uintmax_t) res));
+	return res;
+}
+
+/**
+ * fdisk_lba_is_phy_aligned:
+ * @cxt: context
+ * @lba: LBA to check
+ *
+ * Check if the @lba is aligned to physical sector boundary.
+ *
+ * Returns: 1 if aligned.
+ */
+int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	return lba_is_phy_aligned(cxt, lba);
+}
+
+static unsigned long get_sector_size(int fd)
+{
+	int sect_sz;
+
+	if (!blkdev_get_sector_size(fd, &sect_sz))
+		return (unsigned long) sect_sz;
+	return DEFAULT_SECTOR_SIZE;
+}
+
+static void recount_geometry(struct fdisk_context *cxt)
+{
+	if (!cxt->geom.heads)
+		cxt->geom.heads = 255;
+	if (!cxt->geom.sectors)
+		cxt->geom.sectors = 63;
+
+	cxt->geom.cylinders = cxt->total_sectors /
+		(cxt->geom.heads * cxt->geom.sectors);
+}
+
+/**
+ * fdisk_override_geometry:
+ * @cxt: fdisk context
+ * @cylinders: user specified cylinders
+ * @heads: user specified heads
+ * @sectors: user specified sectors
+ *
+ * Overrides auto-discovery. The function fdisk_reset_device_properties()
+ * restores the original setting.
+ *
+ * The difference between fdisk_override_geometry() and fdisk_save_user_geometry()
+ * is that saved user geometry is persistent setting and it's applied always
+ * when device is assigned to the context or device properties are reseted.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_override_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors)
+{
+	if (!cxt)
+		return -EINVAL;
+	if (heads)
+		cxt->geom.heads = heads;
+	if (sectors)
+		cxt->geom.sectors = sectors;
+
+	if (cylinders)
+		cxt->geom.cylinders = cylinders;
+	else
+		recount_geometry(cxt);
+
+	fdisk_reset_alignment(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "override C/H/S: %u/%u/%u",
+		(unsigned) cxt->geom.cylinders,
+		(unsigned) cxt->geom.heads,
+		(unsigned) cxt->geom.sectors));
+
+	return 0;
+}
+
+/**
+ * fdisk_save_user_geometry:
+ * @cxt: context
+ * @cylinders: C
+ * @heads: H
+ * @sectors: S
+ *
+ * Save user defined geometry to use it for partitioning.
+ *
+ * The user properties are applied by fdisk_assign_device() or
+ * fdisk_reset_device_properties().
+
+ * Returns: <0 on error, 0 on success.
+ */
+int fdisk_save_user_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors)
+{
+	if (!cxt)
+		return -EINVAL;
+
+	if (heads)
+		cxt->user_geom.heads = heads > 256 ? 0 : heads;
+	if (sectors)
+		cxt->user_geom.sectors = sectors >= 64 ? 0 : sectors;
+	if (cylinders)
+		cxt->user_geom.cylinders = cylinders;
+
+	DBG(CXT, ul_debugobj(cxt, "user C/H/S: %u/%u/%u",
+				(unsigned) cxt->user_geom.cylinders,
+				(unsigned) cxt->user_geom.heads,
+				(unsigned) cxt->user_geom.sectors));
+
+	return 0;
+}
+
+/**
+ * fdisk_save_user_sector_size:
+ * @cxt: context
+ * @phy: physical sector size
+ * @log: logicla sector size
+ *
+ * Save user defined sector sizes to use it for partitioning.
+ *
+ * The user properties are applied by fdisk_assign_device() or 
+ * fdisk_reset_device_properties().
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int fdisk_save_user_sector_size(struct fdisk_context *cxt,
+				unsigned int phy,
+				unsigned int log)
+{
+	if (!cxt)
+		return -EINVAL;
+
+	DBG(CXT, ul_debugobj(cxt, "user phy/log sector size: %u/%u", phy, log));
+
+	cxt->user_pyh_sector = phy;
+	cxt->user_log_sector = log;
+
+	return 0;
+}
+
+/**
+ * fdisk_has_user_device_properties:
+ * @cxt: context
+ *
+ * Returns: 1 if user specified any properties
+ */
+int fdisk_has_user_device_properties(struct fdisk_context *cxt)
+{
+	return (cxt->user_pyh_sector
+		    || cxt->user_log_sector
+		    || cxt->user_geom.heads
+		    || cxt->user_geom.sectors
+		    || cxt->user_geom.cylinders);
+}
+
+int fdisk_apply_user_device_properties(struct fdisk_context *cxt)
+{
+	if (!cxt)
+		return -EINVAL;
+
+	DBG(CXT, ul_debugobj(cxt, "appling user device properties"));
+
+	if (cxt->user_pyh_sector)
+		cxt->phy_sector_size = cxt->user_pyh_sector;
+	if (cxt->user_log_sector)
+		cxt->sector_size = cxt->min_io_size =
+			cxt->io_size = cxt->user_log_sector;
+
+	if (cxt->user_geom.heads)
+		cxt->geom.heads = cxt->user_geom.heads;
+	if (cxt->user_geom.sectors)
+		cxt->geom.sectors = cxt->user_geom.sectors;
+
+	if (cxt->user_geom.cylinders)
+		cxt->geom.cylinders = cxt->user_geom.cylinders;
+	else if (cxt->user_geom.heads || cxt->user_geom.sectors)
+		recount_geometry(cxt);
+
+	fdisk_reset_alignment(cxt);
+	if (cxt->firstsector_bufsz != cxt->sector_size)
+		fdisk_read_firstsector(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "new C/H/S: %u/%u/%u",
+		(unsigned) cxt->geom.cylinders,
+		(unsigned) cxt->geom.heads,
+		(unsigned) cxt->geom.sectors));
+	DBG(CXT, ul_debugobj(cxt, "new log/phy sector size: %u/%u",
+		(unsigned) cxt->sector_size,
+		(unsigned) cxt->phy_sector_size));
+
+	return 0;
+}
+
+void fdisk_zeroize_device_properties(struct fdisk_context *cxt)
+{
+	assert(cxt);
+
+	cxt->io_size = 0;
+	cxt->optimal_io_size = 0;
+	cxt->min_io_size = 0;
+	cxt->phy_sector_size = 0;
+	cxt->sector_size = 0;
+	cxt->alignment_offset = 0;
+	cxt->grain = 0;
+	cxt->first_lba = 0;
+	cxt->last_lba = 0;
+	cxt->total_sectors = 0;
+
+	memset(&cxt->geom, 0, sizeof(struct fdisk_geometry));
+}
+
+/**
+ * fdisk_reset_device_properties:
+ * @cxt: context
+ *
+ * Resets and discovery topology (I/O limits), geometry, re-read the first
+ * rector on the device if necessary and apply user device setting (geometry
+ * and sector size), then initialize alignment according to label driver (see
+ * fdisk_reset_alignment()).
+ *
+ * You don't have to use this function by default, fdisk_assign_device() is
+ * smart enough to initialize all necessary setting.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_reset_device_properties(struct fdisk_context *cxt)
+{
+	int rc;
+
+	if (!cxt)
+		return -EINVAL;
+
+	DBG(CXT, ul_debugobj(cxt, "*** reseting device properties"));
+
+	fdisk_zeroize_device_properties(cxt);
+	fdisk_discover_topology(cxt);
+	fdisk_discover_geometry(cxt);
+
+	rc = fdisk_read_firstsector(cxt);
+	if (rc)
+		return rc;
+
+	fdisk_apply_user_device_properties(cxt);
+	return 0;
+}
+
+/*
+ * Generic (label independent) geometry
+ */
+int fdisk_discover_geometry(struct fdisk_context *cxt)
+{
+	fdisk_sector_t nsects;
+
+	assert(cxt);
+	assert(cxt->geom.heads == 0);
+
+	DBG(CXT, ul_debugobj(cxt, "%s: discovering geometry...", cxt->dev_path));
+
+	/* get number of 512-byte sectors, and convert it the real sectors */
+	if (!blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &nsects))
+		cxt->total_sectors = (nsects / (cxt->sector_size >> 9));
+
+	DBG(CXT, ul_debugobj(cxt, "total sectors: %ju (ioctl=%ju)",
+				(uintmax_t) cxt->total_sectors,
+				(uintmax_t) nsects));
+
+	/* what the kernel/bios thinks the geometry is */
+	blkdev_get_geometry(cxt->dev_fd, &cxt->geom.heads, (unsigned int *) &cxt->geom.sectors);
+
+	/* obtained heads and sectors */
+	recount_geometry(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "result: C/H/S: %u/%u/%u",
+			       (unsigned) cxt->geom.cylinders,
+			       (unsigned) cxt->geom.heads,
+			       (unsigned) cxt->geom.sectors));
+	return 0;
+}
+
+int fdisk_discover_topology(struct fdisk_context *cxt)
+{
+#ifdef HAVE_LIBBLKID
+	blkid_probe pr;
+#endif
+	assert(cxt);
+	assert(cxt->sector_size == 0);
+
+	DBG(CXT, ul_debugobj(cxt, "%s: discovering topology...", cxt->dev_path));
+#ifdef HAVE_LIBBLKID
+	DBG(CXT, ul_debugobj(cxt, "initialize libblkid prober"));
+
+	pr = blkid_new_probe();
+	if (pr && blkid_probe_set_device(pr, cxt->dev_fd, 0, 0) == 0) {
+		blkid_topology tp = blkid_probe_get_topology(pr);
+
+		if (tp) {
+			cxt->min_io_size = blkid_topology_get_minimum_io_size(tp);
+			cxt->optimal_io_size = blkid_topology_get_optimal_io_size(tp);
+			cxt->phy_sector_size = blkid_topology_get_physical_sector_size(tp);
+			cxt->alignment_offset = blkid_topology_get_alignment_offset(tp);
+
+			/* I/O size used by fdisk */
+			cxt->io_size = cxt->optimal_io_size;
+			if (!cxt->io_size)
+				/* optimal IO is optional, default to minimum IO */
+				cxt->io_size = cxt->min_io_size;
+		}
+	}
+	blkid_free_probe(pr);
+#endif
+
+	cxt->sector_size = get_sector_size(cxt->dev_fd);
+	if (!cxt->phy_sector_size) /* could not discover physical size */
+		cxt->phy_sector_size = cxt->sector_size;
+
+	/* no blkid or error, use default values */
+	if (!cxt->min_io_size)
+		cxt->min_io_size = cxt->sector_size;
+	if (!cxt->io_size)
+		cxt->io_size = cxt->sector_size;
+
+	DBG(CXT, ul_debugobj(cxt, "result: log/phy sector size: %ld/%ld",
+			cxt->sector_size, cxt->phy_sector_size));
+	DBG(CXT, ul_debugobj(cxt, "result: fdisk/min/optimal io: %ld/%ld/%ld",
+		       cxt->io_size, cxt->optimal_io_size, cxt->min_io_size));
+	return 0;
+}
+
+static int has_topology(struct fdisk_context *cxt)
+{
+	/*
+	 * Assume that the device provides topology info if
+	 * optimal_io_size is set or alignment_offset is set or
+	 * minimum_io_size is not power of 2.
+	 */
+	if (cxt &&
+	    (cxt->optimal_io_size ||
+	     cxt->alignment_offset ||
+	     !is_power_of_2(cxt->min_io_size)))
+		return 1;
+	return 0;
+}
+
+/*
+ * The LBA of the first partition is based on the device geometry and topology.
+ * This offset is generic (and recommended) for all labels.
+ *
+ * Returns: 0 on error or number of logical sectors.
+ */
+static fdisk_sector_t topology_get_first_lba(struct fdisk_context *cxt)
+{
+	fdisk_sector_t x = 0, res;
+
+	if (!cxt)
+		return 0;
+
+	if (!cxt->io_size)
+		fdisk_discover_topology(cxt);
+
+	/*
+	 * Align the begin of partitions to:
+	 *
+	 * a) topology
+	 *  a2) alignment offset
+	 *  a1) or physical sector (minimal_io_size, aka "grain")
+	 *
+	 * b) or default to 1MiB (2048 sectrors, Windows Vista default)
+	 *
+	 * c) or for very small devices use 1 phy.sector
+	 */
+	if (has_topology(cxt)) {
+		if (cxt->alignment_offset)
+			x = cxt->alignment_offset;
+		else if (cxt->io_size > 2048 * 512)
+			x = cxt->io_size;
+	}
+	/* default to 1MiB */
+	if (!x)
+		x = 2048 * 512;
+
+	res = x / cxt->sector_size;
+
+	/* don't use huge offset on small devices */
+	if (cxt->total_sectors <= res * 4)
+		res = cxt->phy_sector_size / cxt->sector_size;
+
+	return res;
+}
+
+static unsigned long topology_get_grain(struct fdisk_context *cxt)
+{
+	unsigned long res;
+
+	if (!cxt)
+		return 0;
+
+	if (!cxt->io_size)
+		fdisk_discover_topology(cxt);
+
+	res = cxt->io_size;
+
+	/* use 1MiB grain always when possible */
+	if (res < 2048 * 512)
+		res = 2048 * 512;
+
+	/* don't use huge grain on small devices */
+	if (cxt->total_sectors <= (res * 4 / cxt->sector_size))
+		res = cxt->phy_sector_size;
+
+	return res;
+}
+
+/**
+ * fdisk_reset_alignment:
+ * @cxt: fdisk context
+ *
+ * Resets alignment setting to the default and label specific values. This
+ * function does not change device properties (I/O limits, geometry etc.).
+ *
+ * Returns: 0 on success, < 0 in case of error.
+ */
+int fdisk_reset_alignment(struct fdisk_context *cxt)
+{
+	int rc = 0;
+
+	if (!cxt)
+		return -EINVAL;
+
+	DBG(CXT, ul_debugobj(cxt, "reseting alignment..."));
+
+	/* default */
+	cxt->grain = topology_get_grain(cxt);
+	cxt->first_lba = topology_get_first_lba(cxt);
+	cxt->last_lba = cxt->total_sectors - 1;
+
+	/* overwrite default by label stuff */
+	if (cxt->label && cxt->label->op->reset_alignment)
+		rc = cxt->label->op->reset_alignment(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "alignment reseted to: "
+			    "first LBA=%ju, last LBA=%ju, grain=%lu [rc=%d]",
+			    (uintmax_t) cxt->first_lba, (uintmax_t) cxt->last_lba,
+			    cxt->grain,	rc));
+	return rc;
+}
+
+
+fdisk_sector_t fdisk_scround(struct fdisk_context *cxt, fdisk_sector_t num)
+{
+	fdisk_sector_t un = fdisk_get_units_per_sector(cxt);
+	return (num + un - 1) / un;
+}
+
+fdisk_sector_t fdisk_cround(struct fdisk_context *cxt, fdisk_sector_t num)
+{
+	return fdisk_use_cylinders(cxt) ?
+			(num / fdisk_get_units_per_sector(cxt)) + 1 : num;
+}
+
+/**
+ * fdisk_reread_partition_table:
+ * @cxt: context
+ *
+ * Force *kernel* to re-read partition table on block devices.
+ *
+ * Returns: 0 on success, < 0 in case of error.
+ */
+int fdisk_reread_partition_table(struct fdisk_context *cxt)
+{
+	int i;
+	struct stat statbuf;
+
+	assert(cxt);
+	assert(cxt->dev_fd >= 0);
+
+	i = fstat(cxt->dev_fd, &statbuf);
+	if (i == 0 && S_ISBLK(statbuf.st_mode)) {
+		sync();
+#ifdef BLKRRPART
+		fdisk_info(cxt, _("Calling ioctl() to re-read partition table."));
+		i = ioctl(cxt->dev_fd, BLKRRPART);
+#else
+		errno = ENOSYS;
+		i = 1;
+#endif
+	}
+
+	if (i) {
+		fdisk_warn(cxt, _("Re-reading the partition table failed."));
+		fdisk_info(cxt,	_(
+			"The kernel still uses the old table. The "
+			"new table will be used at the next reboot "
+			"or after you run partprobe(8) or kpartx(8)."));
+		return -errno;
+	}
+
+	return 0;
+}
diff --git a/libblkid/libfdisk/src/ask.c b/libblkid/libfdisk/src/ask.c
new file mode 100644
index 0000000..7e0c3c2
--- /dev/null
+++ b/libblkid/libfdisk/src/ask.c
@@ -0,0 +1,1044 @@
+
+#include "strutils.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: ask
+ * @title: Ask
+ * @short_description: interface for dialog driven partitioning, warning and info messages
+ *
+ */
+
+static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask);
+
+
+/**
+ * fdisk_set_ask:
+ * @cxt: context
+ * @ask_cb: callback
+ * @data: callback data
+ *
+ * Set callback for dialog driven partitioning and library warnings/errors.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_set_ask(struct fdisk_context *cxt,
+		int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
+		void *data)
+{
+	assert(cxt);
+
+	cxt->ask_cb = ask_cb;
+	cxt->ask_data = data;
+	return 0;
+}
+
+struct fdisk_ask *fdisk_new_ask(void)
+{
+	struct fdisk_ask *ask = calloc(1, sizeof(struct fdisk_ask));
+	DBG(ASK, ul_debugobj(ask, "alloc"));
+	ask->refcount = 1;
+	return ask;
+}
+
+void fdisk_reset_ask(struct fdisk_ask *ask)
+{
+	int refcount;
+
+	assert(ask);
+	free(ask->query);
+
+	DBG(ASK, ul_debugobj(ask, "reset"));
+	refcount = ask->refcount;
+
+	if (fdisk_is_ask(ask, MENU))
+		fdisk_ask_menu_reset_items(ask);
+
+	memset(ask, 0, sizeof(*ask));
+	ask->refcount = refcount;
+}
+
+/**
+ * fdisk_ref_ask:
+ * @ask: ask instance
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_ask(struct fdisk_ask *ask)
+{
+	if (ask)
+		ask->refcount++;
+}
+
+
+/**
+ * fdisk_unref_ask:
+ * @ask: ask instance
+ *
+ * De-incremparts reference counter, on zero the @ask is automatically
+ * deallocated.
+ */
+void fdisk_unref_ask(struct fdisk_ask *ask)
+{
+	if (!ask)
+		return;
+	ask->refcount--;
+
+	if (ask->refcount <= 0) {
+		fdisk_reset_ask(ask);
+		DBG(ASK, ul_debugobj(ask, "free"));
+		free(ask);
+	}
+}
+
+/**
+ * fdisk_ask_get_query:
+ * @ask: ask instance
+ *
+ * Returns: pointer to dialog string.
+ */
+const char *fdisk_ask_get_query(struct fdisk_ask *ask)
+{
+	assert(ask);
+	return ask->query;
+}
+
+int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str)
+{
+	assert(ask);
+	return !strdup_to_struct_member(ask, query, str) ? -ENOMEM : 0;
+}
+
+/**
+ * fdisk_ask_get_type:
+ * @ask: ask instance
+ *
+ * Returns: FDISK_ASKTYPE_*
+ */
+int fdisk_ask_get_type(struct fdisk_ask *ask)
+{
+	assert(ask);
+	return ask->type;
+}
+
+int fdisk_ask_set_type(struct fdisk_ask *ask, int type)
+{
+	assert(ask);
+	ask->type = type;
+	return 0;
+}
+
+int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask)
+{
+	int rc;
+
+	assert(ask);
+	assert(cxt);
+
+	DBG(ASK, ul_debugobj(ask, "do_ask for '%s'",
+				ask->query ? ask->query :
+				ask->type == FDISK_ASKTYPE_INFO  ? "info" :
+				ask->type == FDISK_ASKTYPE_WARNX ? "warnx" :
+				ask->type == FDISK_ASKTYPE_WARN  ? "warn" :
+				"?nothing?"));
+
+	if (!cxt->ask_cb) {
+		DBG(ASK, ul_debugobj(ask, "no ask callback specified!"));
+		return -EINVAL;
+	}
+
+	rc = cxt->ask_cb(cxt, ask, cxt->ask_data);
+
+	DBG(ASK, ul_debugobj(ask, "do_ask done [rc=%d]", rc));
+	return rc;
+}
+
+#define is_number_ask(a)  (fdisk_is_ask(a, NUMBER) || fdisk_is_ask(a, OFFSET))
+
+/**
+ * fdisk_ask_number_get_range:
+ * @ask: ask instance
+ *
+ * Returns: string with range (e.g. "1,3,5-10")
+ */
+const char *fdisk_ask_number_get_range(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.range;
+}
+
+int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	ask->data.num.range = range;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_default:
+ * @ask: ask instance
+ *
+ * Returns: default number
+ *
+ */
+uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.dfl;
+}
+
+int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt)
+{
+	assert(ask);
+	ask->data.num.dfl = dflt;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_low:
+ * @ask: ask instance
+ *
+ * Returns: minimal possible number when ask for numbers in range
+ */
+uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.low;
+}
+
+int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low)
+{
+	assert(ask);
+	ask->data.num.low = low;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_high:
+ * @ask: ask instance
+ *
+ * Returns: maximal possible number when ask for numbers in range
+ */
+uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.hig;
+}
+
+int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high)
+{
+	assert(ask);
+	ask->data.num.hig = high;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_result:
+ * @ask: ask instance
+ *
+ * Returns: result
+ */
+uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.result;
+}
+
+/**
+ * fdisk_ask_number_set_result:
+ * @ask: ask instance
+ * @result: dialog result
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result)
+{
+	assert(ask);
+	ask->data.num.result = result;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_base:
+ * @ask: ask instance
+ *
+ * Returns: base when user specify number in relative notation (+size)
+ */
+uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.base;
+}
+
+int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base)
+{
+	assert(ask);
+	ask->data.num.base = base;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_get_unit:
+ * @ask: ask instance
+ *
+ * Returns: number of bytes per the unit
+ */
+uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.unit;
+}
+
+int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit)
+{
+	assert(ask);
+	ask->data.num.unit = unit;
+	return 0;
+}
+
+int fdisk_ask_number_is_relative(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.relative;
+}
+
+/**
+ * fdisk_ask_number_set_relative
+ * @ask: ask instance
+ * @relative: 0 or 1
+ *
+ * Inform libfdisk that user specified number in relative notation rather than
+ * by explicit number. This info allows to fdisk do some optimization (e.g.
+ * align end of partiton, etc.)
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative)
+{
+	assert(ask);
+	ask->data.num.relative = relative ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_ask_number_inchars:
+ * @ask: ask instance
+ *
+ * For example for BSD is normal to address partition by chars rather than by
+ * number (first partition is 'a').
+ *
+ * Returns: 1 if number should be presented as chars
+ *
+ */
+int fdisk_ask_number_inchars(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_number_ask(ask));
+	return ask->data.num.inchars;
+}
+
+/*
+ * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur'
+ */
+#define tochar(num)	((int) ('a' + num - 1))
+static char *mk_string_list(char *ptr, size_t *len, size_t *begin,
+			    size_t *run, ssize_t cur, int inchar)
+{
+	int rlen;
+
+	if (cur != -1) {
+		if (!*begin) {			/* begin of the list */
+			*begin = cur + 1;
+			return ptr;
+		}
+
+		if (*begin + *run == cur) {	/* no gap, continue */
+			(*run)++;
+			return ptr;
+		}
+	} else if (!*begin) {
+		*ptr = '\0';
+		return ptr;		/* end of empty list */
+	}
+
+					/* add to the list */
+	if (!*run)
+		rlen = inchar ? snprintf(ptr, *len, "%c,", tochar(*begin)) :
+				snprintf(ptr, *len, "%zu,", *begin);
+	else if (*run == 1)
+		rlen = inchar ?
+			snprintf(ptr, *len, "%c,%c,", tochar(*begin), tochar(*begin + 1)) :
+			snprintf(ptr, *len, "%zu,%zu,", *begin, *begin + 1);
+	else
+		rlen = inchar ?
+			snprintf(ptr, *len, "%c-%c,", tochar(*begin), tochar(*begin + *run)) :
+			snprintf(ptr, *len, "%zu-%zu,", *begin, *begin + *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;
+
+	if (cur == -1 && *begin) {
+		/* end of the list */
+		*(ptr - 1) = '\0';	/* remove tailing ',' from the list */
+		return ptr;
+	}
+
+	*begin = cur + 1;
+	*run = 0;
+
+	return ptr;
+}
+
+/**
+ * fdisk_ask_partnum:
+ * @cxt: context
+ * @partnum: returns partition number
+ * @wantnew: 0|1
+ *
+ * High-level API to ask for used or unused partition number.
+ *
+ * Returns: 0 on success, < 0 on error, 1 if no free/used partition
+ */
+int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew)
+{
+	int rc = 0, inchar = 0;
+	char range[BUFSIZ], *ptr = range;
+	size_t i, len = sizeof(range), begin = 0, run = 0;
+	struct fdisk_ask *ask = NULL;
+	__typeof__(ask->data.num) *num;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(partnum);
+
+	if (cxt->label && cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
+		inchar = 1;
+
+	DBG(ASK, ul_debug("%s: asking for %s partition number "
+			  "(max: %zu, inchar: %s)",
+			cxt->label->name,
+			wantnew ? "new" : "used",
+			cxt->label->nparts_max,
+			inchar ? "yes" : "not"));
+
+	ask = fdisk_new_ask();
+	if (!ask)
+		return -ENOMEM;
+
+	fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+	num = &ask->data.num;
+
+	ask->data.num.inchars = inchar ? 1 : 0;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		int used = fdisk_is_partition_used(cxt, i);
+
+		if (wantnew && !used) {
+			ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
+			if (!ptr) {
+				rc = -EINVAL;
+				break;
+			}
+			if (!num->low)
+				num->dfl = num->low = i + 1;
+			num->hig = i + 1;
+		} else if (!wantnew && used) {
+			ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
+			if (!num->low)
+				num->low = i + 1;
+			num->dfl = num->hig = i + 1;
+		}
+	}
+
+	DBG(ASK, ul_debugobj(ask, "ask limits: low: %ju, high: %ju, default: %ju",
+				num->low, num->hig, num->dfl));
+
+	if (!rc && !wantnew && num->low == num->hig) {
+		if (num->low > 0) {
+			/* only one existing partiton, don't ask, return the number */
+			fdisk_ask_number_set_result(ask, num->low);
+			fdisk_info(cxt, _("Selected partition %ju"), num->low);
+
+		} else if (num->low == 0) {
+			fdisk_warnx(cxt, _("No partition is defined yet!"));
+			rc = 1;
+		}
+		goto dont_ask;
+	}
+	if (!rc && wantnew && num->low == num->hig) {
+		if (num->low > 0) {
+			/* only one free partition, don't ask, return the number */
+			fdisk_ask_number_set_result(ask, num->low);
+			fdisk_info(cxt, _("Selected partition %ju"), num->low);
+		}
+		if (num->low == 0) {
+			fdisk_warnx(cxt, _("No free partition available!"));
+			rc = 1;
+		}
+		goto dont_ask;
+	}
+	if (!rc) {
+		mk_string_list(ptr, &len, &begin, &run, -1, inchar);	/* terminate the list */
+		rc = fdisk_ask_number_set_range(ask, range);
+	}
+	if (!rc)
+		rc = fdisk_ask_set_query(ask, _("Partition number"));
+	if (!rc)
+		rc = fdisk_do_ask(cxt, ask);
+
+dont_ask:
+	if (!rc) {
+		*partnum = fdisk_ask_number_get_result(ask);
+		if (*partnum)
+			*partnum -= 1;
+	}
+	DBG(ASK, ul_debugobj(ask, "result: %ju [rc=%d]\n", fdisk_ask_number_get_result(ask), rc));
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/**
+ * fdisk_ask_number:
+ * @cxt: context
+ * @low: minimal possible number
+ * @dflt: default suggestion
+ * @high: maximal possible number
+ * @query: question string
+ * @result: returns result
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_ask_number(struct fdisk_context *cxt,
+		     uintmax_t low,
+		     uintmax_t dflt,
+		     uintmax_t high,
+		     const char *query,
+		     uintmax_t *result)
+{
+	struct fdisk_ask *ask;
+	int rc;
+
+	assert(cxt);
+
+	ask = fdisk_new_ask();
+	if (!ask)
+		return -ENOMEM;
+
+	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+	if (!rc)
+		fdisk_ask_number_set_low(ask, low);
+	if (!rc)
+		fdisk_ask_number_set_default(ask, dflt);
+	if (!rc)
+		fdisk_ask_number_set_high(ask, high);
+	if (!rc)
+		fdisk_ask_set_query(ask, query);
+	if (!rc)
+		rc = fdisk_do_ask(cxt, ask);
+	if (!rc)
+		*result = fdisk_ask_number_get_result(ask);
+
+	DBG(ASK, ul_debugobj(ask, "result: %ju [rc=%d]\n", *result, rc));
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/**
+ * fdisk_ask_string_get_result:
+ * @ask: ask instance
+ *
+ * Returns: pointer to dialog result
+ */
+char *fdisk_ask_string_get_result(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, STRING));
+	return ask->data.str.result;
+}
+
+/**
+ * fdisk_ask_string_set_result:
+ * @ask: ask instance
+ * @result: pointer to allocated buffer with string
+ *
+ * You don't have to care about the @result deallocation, libfdisk is going to
+ * deallocate the result when destroy @ask instance.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result)
+{
+	assert(ask);
+	ask->data.str.result = result;
+	return 0;
+}
+
+/**
+ * fdisk_ask_string:
+ * @cxt: context:
+ * @query: question string
+ * @result: returns allocated buffer
+ *
+ * High-level API to ask for strings. Don't forget to deallocate the @result.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_ask_string(struct fdisk_context *cxt,
+		     const char *query,
+		     char **result)
+{
+	struct fdisk_ask *ask;
+	int rc;
+
+	assert(cxt);
+
+	ask = fdisk_new_ask();
+	if (!ask)
+		return -ENOMEM;
+
+	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_STRING);
+	if (!rc)
+		fdisk_ask_set_query(ask, query);
+	if (!rc)
+		rc = fdisk_do_ask(cxt, ask);
+	if (!rc)
+		*result = fdisk_ask_string_get_result(ask);
+
+	DBG(ASK, ul_debugobj(ask, "result: %s [rc=%d]\n", *result, rc));
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/**
+ * fdisk_ask_yesno:
+ * @cxt: context
+ * @query: question string
+ * @result: returns 0 (no) or 1 (yes)
+ *
+ * Hight-level API to ask Yes/No questions
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_yesno(struct fdisk_context *cxt,
+		     const char *query,
+		     int *result)
+{
+	struct fdisk_ask *ask;
+	int rc;
+
+	assert(cxt);
+
+	ask = fdisk_new_ask();
+	if (!ask)
+		return -ENOMEM;
+
+	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_YESNO);
+	if (!rc)
+		fdisk_ask_set_query(ask, query);
+	if (!rc)
+		rc = fdisk_do_ask(cxt, ask);
+	if (!rc)
+		*result = fdisk_ask_yesno_get_result(ask) == 1 ? 1 : 0;
+
+	DBG(ASK, ul_debugobj(ask, "result: %d [rc=%d]\n", *result, rc));
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/**
+ * fdisk_ask_yesno_get_result:
+ * @ask: ask instance
+ *
+ * Returns: 0 or 1
+ */
+int fdisk_ask_yesno_get_result(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, YESNO));
+	return ask->data.yesno.result;
+}
+
+/**
+ * fdisk_ask_yesno_set_result:
+ * @ask: ask instance
+ * @result: 1 or 0
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result)
+{
+	assert(ask);
+	ask->data.yesno.result = result;
+	return 0;
+}
+
+/*
+ * menu
+ */
+int fdisk_ask_menu_set_default(struct fdisk_ask *ask, int dfl)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+	ask->data.menu.dfl = dfl;
+	return 0;
+}
+
+/**
+ * fdisk_ask_menu_get_default:
+ * @ask: ask instance
+ *
+ * Returns: default menu item key
+ */
+int fdisk_ask_menu_get_default(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+	return ask->data.menu.dfl;
+}
+
+/**
+ * fdisk_ask_menu_set_result:
+ * @ask: ask instance
+ * @key: result
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+	ask->data.menu.result = key;
+	DBG(ASK, ul_debugobj(ask, "menu result: %c\n", key));
+	return 0;
+
+}
+
+/**
+ * fdisk_ask_menu_get_result:
+ * @ask: ask instance
+ * @key: returns selected menu item key
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key)
+{
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+	if (key)
+		*key =  ask->data.menu.result;
+	return 0;
+}
+
+/**
+ * fdisk_ask_menu_get_item:
+ * @ask: ask menu instance
+ * @idx: wanted menu item index
+ * @key: returns key of the menu item
+ * @name: returns name of the menu item
+ * @desc: returns description of the menu item
+ *
+ * Returns: 0 on success, <0 on error, >0 if idx out-of-range
+ */
+int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
+			    const char **name, const char **desc)
+{
+	size_t i;
+	struct ask_menuitem *mi;
+
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+
+	for (i = 0, mi = ask->data.menu.first; mi; mi = mi->next, i++) {
+		if (i == idx)
+			break;
+	}
+
+	if (!mi)
+		return 1;	/* no more items */
+	if (key)
+		*key = mi->key;
+	if (name)
+		*name = mi->name;
+	if (desc)
+		*desc = mi->desc;
+	return 0;
+}
+
+static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask)
+{
+	struct ask_menuitem *mi;
+
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+
+	for (mi = ask->data.menu.first; mi; ) {
+		struct ask_menuitem *next = mi->next;
+		free(mi);
+		mi = next;
+	}
+}
+
+/**
+ * fdisk_ask_menu_get_nitems:
+ * @ask: ask instance
+ *
+ * Returns: number of menu items
+ */
+size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask)
+{
+	struct ask_menuitem *mi;
+	size_t n;
+
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+
+	for (n = 0, mi = ask->data.menu.first; mi; mi = mi->next, n++);
+
+	return n;
+}
+
+int fdisk_ask_menu_add_item(struct fdisk_ask *ask, int key,
+			const char *name, const char *desc)
+{
+	struct ask_menuitem *mi;
+
+	assert(ask);
+	assert(fdisk_is_ask(ask, MENU));
+
+	mi = calloc(1, sizeof(*mi));
+	if (!mi)
+		return -ENOMEM;
+	mi->key = key;
+	mi->name = name;
+	mi->desc = desc;
+
+	if (!ask->data.menu.first)
+		ask->data.menu.first = mi;
+	else {
+	        struct ask_menuitem *last = ask->data.menu.first;
+
+		while (last->next)
+			last = last->next;
+		last->next = mi;
+	}
+
+	DBG(ASK, ul_debugobj(ask, "new menu item: %c, \"%s\" (%s)\n", mi->key, mi->name, mi->desc));
+	return 0;
+}
+
+
+/*
+ * print-like
+ */
+
+#define is_print_ask(a) (fdisk_is_ask(a, WARN) || fdisk_is_ask(a, WARNX) || fdisk_is_ask(a, INFO))
+
+/**
+ * fdisk_ask_print_get_errno:
+ * @ask: ask instance
+ *
+ * Returns: error number for warning/error messages
+ */
+int fdisk_ask_print_get_errno(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_print_ask(ask));
+	return ask->data.print.errnum;
+}
+
+int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum)
+{
+	assert(ask);
+	ask->data.print.errnum = errnum;
+	return 0;
+}
+
+/**
+ * fdisk_ask_print_get_mesg:
+ * @ask: ask instance
+ *
+ * Returns: pointer to message
+ */
+const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask)
+{
+	assert(ask);
+	assert(is_print_ask(ask));
+	return ask->data.print.mesg;
+}
+
+/* does not reallocate the message! */
+int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg)
+{
+	assert(ask);
+	ask->data.print.mesg = mesg;
+	return 0;
+}
+
+static int do_vprint(struct fdisk_context *cxt, int errnum, int type,
+		     const char *fmt, va_list va)
+{
+	struct fdisk_ask *ask;
+	int rc;
+	char *mesg;
+
+	assert(cxt);
+
+	if (vasprintf(&mesg, fmt, va) < 0)
+		return -ENOMEM;
+
+	ask = fdisk_new_ask();
+	if (!ask) {
+		free(mesg);
+		return -ENOMEM;
+	}
+
+	fdisk_ask_set_type(ask, type);
+	fdisk_ask_print_set_mesg(ask, mesg);
+	if (errnum >= 0)
+		fdisk_ask_print_set_errno(ask, errnum);
+	rc = fdisk_do_ask(cxt, ask);
+
+	fdisk_unref_ask(ask);
+	free(mesg);
+	return rc;
+}
+
+/**
+ * fdisk_info:
+ * @cxt: context
+ * @fmt: printf-like formatted string
+ * @...: variable parametrs
+ *
+ * High-level API to print info messages,
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+
+	assert(cxt);
+	va_start(ap, fmt);
+	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, fmt, ap);
+	va_end(ap);
+	return rc;
+}
+
+/**
+ * fdisk_info:
+ * @cxt: context
+ * @fmt: printf-like formatted string
+ * @...: variable parametrs
+ *
+ * High-level API to print warning message (errno expected)
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+
+	assert(cxt);
+	va_start(ap, fmt);
+	rc = do_vprint(cxt, errno, FDISK_ASKTYPE_WARN, fmt, ap);
+	va_end(ap);
+	return rc;
+}
+
+/**
+ * fdisk_warnx:
+ * @cxt: context
+ * @fmt: printf-like formatted string
+ * @...: variable options
+ *
+ * High-level API to print warning message
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+
+	assert(cxt);
+	va_start(ap, fmt);
+	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_WARNX, fmt, ap);
+	va_end(ap);
+	return rc;
+}
+
+int fdisk_info_new_partition(
+			struct fdisk_context *cxt,
+			int num, fdisk_sector_t start, fdisk_sector_t stop,
+			struct fdisk_parttype *t)
+{
+	int rc;
+	char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE,
+				     (uint64_t)(stop - start + 1) * cxt->sector_size);
+
+	rc = fdisk_info(cxt,
+			_("Created a new partition %d of type '%s' and of size %s."),
+			num, t ? t->name : _("Unknown"), str);
+	free(str);
+	return rc;
+}
+
+#ifdef TEST_PROGRAM
+int test_ranges(struct fdisk_test *ts, int argc, char *argv[])
+{
+	/*                1  -  3,       6,    8, 9,   11    13 */
+	size_t nums[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 };
+	size_t numx[] = { 0, 0, 0 };
+	char range[BUFSIZ], *ptr = range;
+	size_t i, len = sizeof(range), begin = 0, run = 0;
+
+	for (i = 0; i < ARRAY_SIZE(nums); i++) {
+		if (!nums[i])
+			continue;
+		ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
+	}
+	mk_string_list(ptr, &len, &begin, &run, -1, 0);
+	printf("list: '%s'\n", range);
+
+	ptr = range;
+	len = sizeof(range), begin = 0, run = 0;
+	for (i = 0; i < ARRAY_SIZE(numx); i++) {
+		if (!numx[i])
+			continue;
+		ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
+	}
+	mk_string_list(ptr, &len, &begin, &run, -1, 0);
+	printf("empty list: '%s'\n", range);
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct fdisk_test tss[] = {
+	{ "--ranges",  test_ranges,    "generates ranges" },
+	{ NULL }
+	};
+
+	return fdisk_run_test(tss, argc, argv);
+}
+
+#endif
diff --git a/libblkid/libfdisk/src/bsd.c b/libblkid/libfdisk/src/bsd.c
new file mode 100644
index 0000000..618a3ee
--- /dev/null
+++ b/libblkid/libfdisk/src/bsd.c
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
+ *
+ * Based on the original code from fdisk
+ *    written by Bernhard Fastenrath (fasten@informatik.uni-bonn.de)
+ *    with code from the NetBSD disklabel command.
+ *
+ *    Arnaldo Carvalho de Melo <acme@conectiva.com.br>, March 1999
+ *    David Huggins-Daines <dhuggins@linuxcare.com>, January 2000
+ */
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#include "nls.h"
+#include "blkdev.h"
+#include "fdiskP.h"
+#include "pt-mbr.h"
+#include "pt-bsd.h"
+#include "all-io.h"
+
+
+/**
+ * SECTION: bsd
+ * @title: BSD
+ * @short_description: disk label specific functions
+ *
+ */
+
+static const char *bsd_dktypenames[] = {
+	"unknown",
+	"SMD",
+	"MSCP",
+	"old DEC",
+	"SCSI",
+	"ESDI",
+	"ST506",
+	"HP-IB",
+	"HP-FL",
+	"type 9",
+	"floppy",
+	0
+};
+#define BSD_DKMAXTYPES	(ARRAY_SIZE(bsd_dktypenames) - 1)
+
+static struct fdisk_parttype bsd_fstypes[] = {
+        {BSD_FS_UNUSED, "unused"},
+	{BSD_FS_SWAP,   "swap"},
+	{BSD_FS_V6,     "Version 6"},
+	{BSD_FS_V7,     "Version 7"},
+	{BSD_FS_SYSV,   "System V"},
+	{BSD_FS_V71K,   "4.1BSD"},
+	{BSD_FS_V8,     "Eighth Edition"},
+	{BSD_FS_BSDFFS, "4.2BSD"},
+#ifdef __alpha__
+	{BSD_FS_EXT2,   "ext2"},
+#else
+	{BSD_FS_MSDOS,  "MS-DOS"},
+#endif
+	{BSD_FS_BSDLFS, "4.4LFS"},
+	{BSD_FS_OTHER,  "unknown"},
+	{BSD_FS_HPFS,   "HPFS"},
+	{BSD_FS_ISO9660,"ISO-9660"},
+	{BSD_FS_BOOT,   "boot"},
+	{BSD_FS_ADOS,   "ADOS"},
+	{BSD_FS_HFS,    "HFS"},
+	{BSD_FS_ADVFS,	"AdvFS"},
+	{ 0, NULL }
+};
+#define BSD_FSMAXTYPES (ARRAY_SIZE(bsd_fstypes)-1)
+
+/*
+ * in-memory fdisk BSD stuff
+ */
+struct fdisk_bsd_label {
+	struct fdisk_label	head;		/* generic part */
+
+	struct dos_partition *dos_part;		/* parent */
+	struct bsd_disklabel bsd;		/* on disk label */
+#if defined (__alpha__)
+	/* We access this through a u_int64_t * when checksumming */
+	char bsdbuffer[BSD_BBSIZE] __attribute__((aligned(8)));
+#else
+	char bsdbuffer[BSD_BBSIZE];
+#endif
+};
+
+static int bsd_list_disklabel(struct fdisk_context *cxt);
+static int bsd_initlabel(struct fdisk_context *cxt);
+static int bsd_readlabel(struct fdisk_context *cxt);
+static void sync_disks(struct fdisk_context *cxt);
+
+static inline struct fdisk_bsd_label *self_label(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, BSD));
+
+	return (struct fdisk_bsd_label *) cxt->label;
+}
+
+static inline struct bsd_disklabel *self_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, BSD));
+
+	return &((struct fdisk_bsd_label *) cxt->label)->bsd;
+}
+
+static struct fdisk_parttype *bsd_partition_parttype(
+		struct fdisk_context *cxt,
+		struct bsd_partition *p)
+{
+	struct fdisk_parttype *t
+		= fdisk_label_get_parttype_from_code(cxt->label, p->p_fstype);
+	return t ? : fdisk_new_unknown_parttype(p->p_fstype, NULL);
+}
+
+
+#if defined (__alpha__)
+static void alpha_bootblock_checksum (char *boot)
+{
+	uint64_t *dp = (uint64_t *) boot, sum = 0;
+	int i;
+
+	for (i = 0; i < 63; i++)
+		sum += dp[i];
+	dp[63] = sum;
+}
+#endif /* __alpha__ */
+
+#define HIDDEN_MASK	0x10
+
+static int is_bsd_partition_type(int type)
+{
+	return (type == MBR_FREEBSD_PARTITION ||
+		type == (MBR_FREEBSD_PARTITION ^ HIDDEN_MASK) ||
+		type == MBR_NETBSD_PARTITION ||
+		type == (MBR_NETBSD_PARTITION ^ HIDDEN_MASK) ||
+		type == MBR_OPENBSD_PARTITION ||
+		type == (MBR_OPENBSD_PARTITION ^ HIDDEN_MASK));
+}
+
+/*
+ * look for DOS partition usable for nested BSD partition table
+ */
+static int bsd_assign_dos_partition(struct fdisk_context *cxt)
+{
+	struct fdisk_bsd_label *l = self_label(cxt);
+	size_t i;
+
+	for (i = 0; i < 4; i++) {
+		fdisk_sector_t ss;
+
+		l->dos_part = fdisk_dos_get_partition(cxt->parent, i);
+
+		if (!l->dos_part || !is_bsd_partition_type(l->dos_part->sys_ind))
+			continue;
+
+		ss = dos_partition_get_start(l->dos_part);
+		if (!ss) {
+			fdisk_warnx(cxt, _("Partition %zd: has invalid starting "
+					   "sector 0."), i + 1);
+			return -1;
+		}
+
+		if (cxt->parent->dev_path) {
+			free(cxt->dev_path);
+			cxt->dev_path = fdisk_partname(
+						cxt->parent->dev_path, i + 1);
+		}
+
+		DBG(LABEL, ul_debug("partition %zu assigned to BSD", i + 1));
+		return 0;
+	}
+
+	fdisk_warnx(cxt, _("There is no *BSD partition on %s."),
+				cxt->parent->dev_path);
+	free(cxt->dev_path);
+	cxt->dev_path = NULL;
+	l->dos_part = NULL;
+	return 1;
+}
+
+static int bsd_probe_label(struct fdisk_context *cxt)
+{
+	int rc = 0;
+
+	if (cxt->parent)
+		rc = bsd_assign_dos_partition(cxt);	/* nested BSD partiotn table */
+	if (!rc)
+		rc = bsd_readlabel(cxt);
+	if (!rc)
+		return 1;	/* found BSD */
+	return 0;		/* not found */
+}
+
+static int set_parttype(
+		struct fdisk_context *cxt,
+		size_t partnum,
+		struct fdisk_parttype *t)
+{
+	struct bsd_partition *p;
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	if (partnum >= d->d_npartitions || !t || t->code > UINT8_MAX)
+		return -EINVAL;
+
+	p = &d->d_partitions[partnum];
+	if (t->code == p->p_fstype)
+		return 0;
+
+	p->p_fstype = t->code;
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int bsd_add_partition(struct fdisk_context *cxt,
+			     struct fdisk_partition *pa,
+			     size_t *partno)
+{
+	struct fdisk_bsd_label *l = self_label(cxt);
+	struct bsd_disklabel *d = self_disklabel(cxt);
+	size_t i;
+	unsigned int begin = 0, end;
+	int rc = 0;
+
+	rc = fdisk_partition_next_partno(pa, cxt, &i);
+	if (rc)
+		return rc;
+	if (i >= BSD_MAXPARTITIONS)
+		return -ERANGE;
+	if (l->dos_part) {
+		begin = dos_partition_get_start(l->dos_part);
+		end = begin + dos_partition_get_size(l->dos_part) - 1;
+	} else
+		end = d->d_secperunit - 1;
+
+	/*
+	 * First sector
+	 */
+	if (pa && pa->start_follow_default)
+		;
+	else if (pa && fdisk_partition_has_start(pa)) {
+		if (pa->start < begin || pa->start > end)
+			return -ERANGE;
+		begin = pa->start;
+	} else {
+		struct fdisk_ask *ask = fdisk_new_ask();
+
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_query(ask,
+			fdisk_use_cylinders(cxt) ?
+			_("First cylinder") : _("First sector"));
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+		fdisk_ask_number_set_low(ask, fdisk_cround(cxt, begin));
+		fdisk_ask_number_set_default(ask, fdisk_cround(cxt, begin));
+		fdisk_ask_number_set_high(ask, fdisk_cround(cxt, end));
+
+		rc = fdisk_do_ask(cxt, ask);
+		begin = fdisk_ask_number_get_result(ask);
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			begin = (begin - 1) * d->d_secpercyl;
+	}
+
+	/*
+	 * Last sector
+	 */
+	if (pa && pa->end_follow_default)
+		;
+	else if (pa && fdisk_partition_has_size(pa)) {
+		if (begin + pa->size > end)
+			return -ERANGE;
+		end = begin + pa->size - 1ULL;
+	} else {
+		/* ask user by dialog */
+		struct fdisk_ask *ask = fdisk_new_ask();
+
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+		if (fdisk_use_cylinders(cxt)) {
+			fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
+			fdisk_ask_number_set_unit(ask,
+				     cxt->sector_size *
+				     fdisk_get_units_per_sector(cxt));
+		} else {
+			fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
+			fdisk_ask_number_set_unit(ask,cxt->sector_size);
+		}
+
+		fdisk_ask_number_set_low(ask, fdisk_cround(cxt, begin));
+		fdisk_ask_number_set_default(ask, fdisk_cround(cxt, end));
+		fdisk_ask_number_set_high(ask, fdisk_cround(cxt, end));
+		fdisk_ask_number_set_base(ask, fdisk_cround(cxt, begin));
+
+		rc = fdisk_do_ask(cxt, ask);
+		end = fdisk_ask_number_get_result(ask);
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			end = end * d->d_secpercyl - 1;
+	}
+
+	d->d_partitions[i].p_size   = end - begin + 1;
+	d->d_partitions[i].p_offset = begin;
+	d->d_partitions[i].p_fstype = BSD_FS_UNUSED;
+
+	if (i >= d->d_npartitions)
+		d->d_npartitions = i + 1;
+	cxt->label->nparts_cur = d->d_npartitions;
+
+	if (pa && pa->type)
+		set_parttype(cxt, i, pa->type);
+
+	fdisk_label_set_changed(cxt->label, 1);
+	if (partno)
+		*partno = i;
+	return 0;
+}
+
+static int bsd_set_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct bsd_partition *p;
+	struct fdisk_bsd_label *l = self_label(cxt);
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	if (n >= d->d_npartitions)
+		return -EINVAL;
+
+	p = &d->d_partitions[n];
+
+	/* we have to stay within parental DOS partition */
+	if (l->dos_part && (fdisk_partition_has_start(pa) ||
+			    fdisk_partition_has_size(pa))) {
+
+		fdisk_sector_t dosbegin = dos_partition_get_start(l->dos_part);
+		fdisk_sector_t dosend = dosbegin + dos_partition_get_size(l->dos_part) - 1;
+		fdisk_sector_t begin = fdisk_partition_has_start(pa) ? pa->start : p->p_offset;
+		fdisk_sector_t end = begin + (fdisk_partition_has_size(pa) ? pa->size : p->p_size) - 1;
+
+		if (begin < dosbegin || begin > dosend)
+			return -ERANGE;
+		if (end < dosbegin || end > dosend)
+			return -ERANGE;
+	}
+
+	if (pa->type) {
+		int rc = set_parttype(cxt, n, pa->type);
+		if (rc)
+			return rc;
+	}
+
+	if (fdisk_partition_has_start(pa))
+		d->d_partitions[n].p_offset = pa->start;
+	if (fdisk_partition_has_size(pa))
+		d->d_partitions[n].p_size = pa->size;
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+
+/* Returns 0 on success, < 0 on error. */
+static int bsd_create_disklabel(struct fdisk_context *cxt)
+{
+	int rc, yes = 0;
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	fdisk_info(cxt, _("The device %s does not contain BSD disklabel."), cxt->dev_path);
+	rc = fdisk_ask_yesno(cxt,
+			_("Do you want to create a BSD disklabel?"),
+			&yes);
+	if (rc)
+		return rc;
+	if (!yes)
+		return 1;
+	if (cxt->parent) {
+		rc = bsd_assign_dos_partition(cxt);
+		if (rc == 1)
+			/* not found DOS partition usable for BSD label */
+			rc = -EINVAL;
+	}
+	if (rc)
+		return rc;
+
+	rc = bsd_initlabel(cxt);
+	if (!rc) {
+		int org = fdisk_is_details(cxt);
+
+		cxt->label->nparts_cur = d->d_npartitions;
+		cxt->label->nparts_max = BSD_MAXPARTITIONS;
+
+		fdisk_enable_details(cxt, 1);
+		bsd_list_disklabel(cxt);
+		fdisk_enable_details(cxt, org);
+	}
+
+	return rc;
+}
+
+static int bsd_delete_part(
+		struct fdisk_context *cxt,
+		size_t partnum)
+{
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	d->d_partitions[partnum].p_size   = 0;
+	d->d_partitions[partnum].p_offset = 0;
+	d->d_partitions[partnum].p_fstype = BSD_FS_UNUSED;
+
+	if (d->d_npartitions == partnum + 1)
+		while (!d->d_partitions[d->d_npartitions - 1].p_size)
+			d->d_npartitions--;
+
+	cxt->label->nparts_cur = d->d_npartitions;
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int bsd_list_disklabel(struct fdisk_context *cxt)
+{
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, BSD));
+
+	if (fdisk_is_details(cxt)) {
+		fdisk_info(cxt, "# %s:", cxt->dev_path);
+
+		if ((unsigned) d->d_type < BSD_DKMAXTYPES)
+			fdisk_info(cxt, _("type: %s"), bsd_dktypenames[d->d_type]);
+		else
+			fdisk_info(cxt, _("type: %d"), d->d_type);
+
+		fdisk_info(cxt, _("disk: %.*s"), (int) sizeof(d->d_typename), d->d_typename);
+		fdisk_info(cxt, _("label: %.*s"), (int) sizeof(d->d_packname), d->d_packname);
+
+		fdisk_info(cxt, _("flags: %s"),
+			d->d_flags & BSD_D_REMOVABLE ? _(" removable") :
+			d->d_flags & BSD_D_ECC ? _(" ecc") :
+			d->d_flags & BSD_D_BADSECT ? _(" badsect") : "");
+
+		/* On various machines the fields of *lp are short/int/long */
+		/* In order to avoid problems, we cast them all to long. */
+		fdisk_info(cxt, _("bytes/sector: %ld"), (long) d->d_secsize);
+		fdisk_info(cxt, _("sectors/track: %ld"), (long) d->d_nsectors);
+		fdisk_info(cxt, _("tracks/cylinder: %ld"), (long) d->d_ntracks);
+		fdisk_info(cxt, _("sectors/cylinder: %ld"), (long) d->d_secpercyl);
+		fdisk_info(cxt, _("cylinders: %ld"), (long) d->d_ncylinders);
+		fdisk_info(cxt, _("rpm: %d"), d->d_rpm);
+		fdisk_info(cxt, _("interleave: %d"), d->d_interleave);
+		fdisk_info(cxt, _("trackskew: %d"), d->d_trackskew);
+		fdisk_info(cxt, _("cylinderskew: %d"), d->d_cylskew);
+		fdisk_info(cxt, _("headswitch: %ld (milliseconds)"), (long) d->d_headswitch);
+		fdisk_info(cxt, _("track-to-track seek: %ld (milliseconds)"), (long) d->d_trkseek);
+	}
+
+	fdisk_info(cxt, _("partitions: %d"), d->d_npartitions);
+
+	return 0;
+}
+
+static int bsd_get_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct bsd_partition *p;
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, BSD));
+
+	if (n >= d->d_npartitions)
+		return -EINVAL;
+
+	p = &d->d_partitions[n];
+
+	pa->used = p->p_size ? 1 : 0;
+	if (!pa->used)
+		return 0;
+
+	if (fdisk_use_cylinders(cxt) && d->d_secpercyl) {
+		pa->start_post = p->p_offset % d->d_secpercyl ? '*' : ' ';
+		pa->end_post = (p->p_offset + p->p_size) % d->d_secpercyl ? '*' : ' ';
+	}
+
+	pa->start = p->p_offset;
+	pa->size = p->p_size;
+	pa->type = bsd_partition_parttype(cxt, p);
+
+	if (p->p_fstype == BSD_FS_UNUSED || p->p_fstype == BSD_FS_BSDFFS) {
+		pa->fsize = p->p_fsize;
+		pa->bsize = p->p_fsize * p->p_frag;
+	}
+	if (p->p_fstype == BSD_FS_BSDFFS)
+		pa->cpg = p->p_cpg;
+
+	return 0;
+}
+
+static uint32_t ask_uint32(struct fdisk_context *cxt,
+		uint32_t dflt, char *mesg)
+{
+	uintmax_t res;
+
+	if (fdisk_ask_number(cxt, min(dflt, (uint32_t) 1), dflt,
+				UINT32_MAX, mesg, &res) == 0)
+		return res;
+	return dflt;
+}
+
+static uint16_t ask_uint16(struct fdisk_context *cxt,
+		uint16_t dflt, char *mesg)
+{
+	uintmax_t res;
+
+	if (fdisk_ask_number(cxt, min(dflt, (uint16_t) 1),
+				dflt, UINT16_MAX, mesg, &res) == 0)
+		return res;
+	return dflt;
+}
+
+/**
+ * fdisk_bsd_edit_disklabel:
+ * @cxt: context
+ *
+ * Edits fields in BSD disk label.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt)
+{
+	struct bsd_disklabel *d = self_disklabel(cxt);
+	uintmax_t res;
+
+#if defined (__alpha__) || defined (__ia64__)
+	if (fdisk_ask_number(cxt, DEFAULT_SECTOR_SIZE, d->d_secsize,
+			     UINT32_MAX, _("bytes/sector"), &res) == 0)
+		d->d_secsize = res;
+
+	d->d_nsectors = ask_uint32(cxt, d->d_nsectors, _("sectors/track"));
+	d->d_ntracks = ask_uint32(cxt, d->d_ntracks, _("tracks/cylinder"));
+	d->d_ncylinders = ask_uint32(cxt, d->d_ncylinders  ,_("cylinders"));
+#endif
+	if (fdisk_ask_number(cxt, 1, d->d_nsectors * d->d_ntracks,
+			     d->d_nsectors * d->d_ntracks,
+			     _("sectors/cylinder"), &res) == 0)
+		d->d_secpercyl = res;
+
+	d->d_rpm = ask_uint16(cxt, d->d_rpm, _("rpm"));
+	d->d_interleave = ask_uint16(cxt, d->d_interleave, _("interleave"));
+	d->d_trackskew = ask_uint16(cxt, d->d_trackskew, _("trackskew"));
+	d->d_cylskew = ask_uint16(cxt, d->d_cylskew, _("cylinderskew"));
+
+	d->d_headswitch = ask_uint32(cxt, d->d_headswitch, _("headswitch"));
+	d->d_trkseek = ask_uint32(cxt, d->d_trkseek, _("track-to-track seek"));
+
+	d->d_secperunit = d->d_secpercyl * d->d_ncylinders;
+	return 0;
+}
+
+static int bsd_get_bootstrap(struct fdisk_context *cxt,
+			char *path, void *ptr, int size)
+{
+	int fd;
+
+	if ((fd = open(path, O_RDONLY)) < 0) {
+		fdisk_warn(cxt, _("cannot open %s"), path);
+		return -errno;
+	}
+
+	if (read_all(fd, ptr, size) != size) {
+		fdisk_warn(cxt, _("cannot read %s"), path);
+		close(fd);
+		return -errno;
+	}
+
+	fdisk_info(cxt, _("The bootstrap file %s successfully loaded."), path);
+	close (fd);
+	return 0;
+}
+
+/**
+ * fdisk_bsd_write_bootstrap:
+ * @cxt: context
+ *
+ * Install bootstrap file to the BSD device
+ */
+int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt)
+{
+	struct bsd_disklabel dl, *d = self_disklabel(cxt);
+	struct fdisk_bsd_label *l = self_label(cxt);
+	char *name = d->d_type == BSD_DTYPE_SCSI ? "sd" : "wd";
+	char buf[BUFSIZ];
+	char *res, *dp, *p;
+	int rc;
+	fdisk_sector_t sector;
+
+	snprintf(buf, sizeof(buf),
+		_("Bootstrap: %1$sboot -> boot%1$s (default %1$s)"),
+		name);
+	rc = fdisk_ask_string(cxt, buf, &res);
+	if (rc)
+		goto done;
+	if (res && *res)
+		name = res;
+
+	snprintf(buf, sizeof(buf), "%s/%sboot", BSD_LINUX_BOOTDIR, name);
+	rc = bsd_get_bootstrap(cxt, buf, l->bsdbuffer,	(int) d->d_secsize);
+	if (rc)
+		goto done;
+
+	/* We need a backup of the disklabel (might have changed). */
+	dp = &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE];
+	memmove(&dl, dp, sizeof(struct bsd_disklabel));
+
+	/* The disklabel will be overwritten by 0's from bootxx anyway */
+	memset(dp, 0, sizeof(struct bsd_disklabel));
+
+	snprintf(buf, sizeof(buf), "%s/boot%s", BSD_LINUX_BOOTDIR, name);
+	rc = bsd_get_bootstrap(cxt, buf,
+			&l->bsdbuffer[d->d_secsize],
+			(int) d->d_bbsize - d->d_secsize);
+	if (rc)
+		goto done;
+
+	/* check end of the bootstrap */
+	for (p = dp; p < dp + sizeof(struct bsd_disklabel); p++) {
+		if (!*p)
+			continue;
+		fdisk_warnx(cxt, _("Bootstrap overlaps with disklabel!"));
+		return -EINVAL;
+	}
+
+	/* move disklabel back */
+	memmove(dp, &dl, sizeof(struct bsd_disklabel));
+
+	sector = 0;
+	if (l->dos_part)
+		sector = dos_partition_get_start(l->dos_part);
+#if defined (__alpha__)
+	alpha_bootblock_checksum(l->bsdbuffer);
+#endif
+	if (lseek(cxt->dev_fd, (off_t) sector * DEFAULT_SECTOR_SIZE, SEEK_SET) == -1) {
+		fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path);
+		rc = -errno;
+		goto done;
+	}
+	if (write_all(cxt->dev_fd, l->bsdbuffer, BSD_BBSIZE)) {
+		fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path);
+		rc = -errno;
+		goto done;
+	}
+
+	fdisk_info(cxt, _("Bootstrap installed on %s."), cxt->dev_path);
+	sync_disks(cxt);
+
+	rc = 0;
+done:
+	free(res);
+	return rc;
+}
+
+static unsigned short bsd_dkcksum (struct bsd_disklabel *lp)
+{
+	unsigned short *start, *end;
+	unsigned short sum = 0;
+
+	start = (unsigned short *) lp;
+	end = (unsigned short *) &lp->d_partitions[lp->d_npartitions];
+	while (start < end)
+		sum ^= *start++;
+	return sum;
+}
+
+static int bsd_initlabel (struct fdisk_context *cxt)
+{
+	struct fdisk_bsd_label *l = self_label(cxt);
+	struct bsd_disklabel *d = self_disklabel(cxt);
+	struct bsd_partition *pp;
+
+	memset (d, 0, sizeof (struct bsd_disklabel));
+
+	d -> d_magic = BSD_DISKMAGIC;
+
+	if (strncmp (cxt->dev_path, "/dev/sd", 7) == 0)
+		d -> d_type = BSD_DTYPE_SCSI;
+	else
+		d -> d_type = BSD_DTYPE_ST506;
+
+#if !defined (__alpha__)
+	d -> d_flags = BSD_D_DOSPART;
+#else
+	d -> d_flags = 0;
+#endif
+	d -> d_secsize = DEFAULT_SECTOR_SIZE;		/* bytes/sector  */
+	d -> d_nsectors = cxt->geom.sectors;		/* sectors/track */
+	d -> d_ntracks = cxt->geom.heads;		/* tracks/cylinder (heads) */
+	d -> d_ncylinders = cxt->geom.cylinders;
+	d -> d_secpercyl  = cxt->geom.sectors * cxt->geom.heads;/* sectors/cylinder */
+	if (d -> d_secpercyl == 0)
+		d -> d_secpercyl = 1;		/* avoid segfaults */
+	d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
+
+	d -> d_rpm = 3600;
+	d -> d_interleave = 1;
+	d -> d_trackskew = 0;
+	d -> d_cylskew = 0;
+	d -> d_headswitch = 0;
+	d -> d_trkseek = 0;
+
+	d -> d_magic2 = BSD_DISKMAGIC;
+	d -> d_bbsize = BSD_BBSIZE;
+	d -> d_sbsize = BSD_SBSIZE;
+
+	if (l->dos_part) {
+		d->d_npartitions = 4;
+
+		pp = &d->d_partitions[2];	/* Partition C should be the NetBSD partition */
+		pp->p_offset = dos_partition_get_start(l->dos_part);
+		pp->p_size   = dos_partition_get_size(l->dos_part);
+		pp->p_fstype = BSD_FS_UNUSED;
+
+		pp = &d -> d_partitions[3];	/* Partition D should be the whole disk */
+		pp->p_offset = 0;
+		pp->p_size   = d->d_secperunit;
+		pp->p_fstype = BSD_FS_UNUSED;
+	} else {
+		d->d_npartitions = 3;
+
+		pp = &d->d_partitions[2];	/* Partition C should be the whole disk */
+		pp->p_offset = 0;
+		pp->p_size   = d->d_secperunit;
+		pp->p_fstype = BSD_FS_UNUSED;
+	}
+
+	return 0;
+}
+
+/*
+ * Read a bsd_disklabel from sector 0 or from the starting sector of p.
+ * If it has the right magic, return 0.
+ */
+static int bsd_readlabel(struct fdisk_context *cxt)
+{
+	struct fdisk_bsd_label *l;
+	struct bsd_disklabel *d;
+	int t;
+	off_t offset = 0;
+
+	l = self_label(cxt);
+	d = self_disklabel(cxt);
+
+	if (l->dos_part)
+		/* BSD is nested within DOS partition, get the begin of the
+		 * partition. Note that DOS uses native sector size. */
+		offset = dos_partition_get_start(l->dos_part) * cxt->sector_size;
+
+	if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1)
+		return -1;
+	if (read_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer)) < 0)
+		return errno ? -errno : -1;
+
+	/* The offset to begin of the disk label. Note that BSD uses
+	 * 512-byte (default) sectors. */
+	memmove(d, &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE
+			      + BSD_LABELOFFSET], sizeof(*d));
+
+	if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC) {
+		DBG(LABEL, ul_debug("not found magic"));
+		return -1;
+	}
+
+	for (t = d->d_npartitions; t < BSD_MAXPARTITIONS; t++) {
+		d->d_partitions[t].p_size   = 0;
+		d->d_partitions[t].p_offset = 0;
+		d->d_partitions[t].p_fstype = BSD_FS_UNUSED;
+	}
+
+	if (d->d_npartitions > BSD_MAXPARTITIONS)
+		fdisk_warnx(cxt, ("Too many partitions (%d, maximum is %d)."),
+				d->d_npartitions, BSD_MAXPARTITIONS);
+
+	/* let's follow in-PT geometry */
+	cxt->geom.sectors = d->d_nsectors;
+	cxt->geom.heads = d->d_ntracks;
+	cxt->geom.cylinders = d->d_ncylinders;
+
+	cxt->label->nparts_cur = d->d_npartitions;
+	cxt->label->nparts_max = BSD_MAXPARTITIONS;
+	DBG(LABEL, ul_debug("read BSD label"));
+	return 0;
+}
+
+static int bsd_write_disklabel(struct fdisk_context *cxt)
+{
+	off_t offset = 0;
+	struct fdisk_bsd_label *l = self_label(cxt);
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+
+	if (l->dos_part)
+		offset = dos_partition_get_start(l->dos_part) * cxt->sector_size;
+
+	d->d_checksum = 0;
+	d->d_checksum = bsd_dkcksum(d);
+
+	/* Update label within boot block. */
+	memmove(&l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE
+			   + BSD_LABELOFFSET], d, sizeof(*d));
+
+#if defined (__alpha__) && BSD_LABELSECTOR == 0
+	/* Write the checksum to the end of the first sector. */
+	alpha_bootblock_checksum(l->bsdbuffer);
+#endif
+	if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1) {
+		fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path);
+		return -errno;
+	}
+	if (write_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer))) {
+		fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path);
+		return -errno;
+	}
+	sync_disks(cxt);
+
+	fdisk_info(cxt, _("Disklabel written to %s."), cxt->dev_path);
+	return 0;
+}
+
+static void sync_disks(struct fdisk_context *cxt)
+{
+	fdisk_info(cxt, _("Syncing disks."));
+	sync();
+}
+
+static int bsd_translate_fstype (int linux_type)
+{
+	switch (linux_type) {
+	case 0x01: /* DOS 12-bit FAT   */
+	case 0x04: /* DOS 16-bit <32M  */
+	case 0x06: /* DOS 16-bit >=32M */
+	case 0xe1: /* DOS access       */
+	case 0xe3: /* DOS R/O          */
+#if !defined (__alpha__)
+	case 0xf2: /* DOS secondary    */
+		return BSD_FS_MSDOS;
+#endif
+	case 0x07: /* OS/2 HPFS        */
+		return BSD_FS_HPFS;
+	default:
+		break;
+	}
+
+	return BSD_FS_OTHER;
+}
+
+/**
+ * fdisk_bsd_link_partition:
+ * @cxt: context
+ *
+ * Links partition from parent (DOS) to nested BSD partition table.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_bsd_link_partition(struct fdisk_context *cxt)
+{
+	size_t k, i;
+	int rc;
+	struct dos_partition *p;
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	if (!cxt->parent || !fdisk_is_label(cxt->parent, DOS)) {
+		fdisk_warnx(cxt, _("BSD label is not nested within a DOS partition."));
+		return -EINVAL;
+	}
+
+	/* ask for DOS partition */
+	rc = fdisk_ask_partnum(cxt->parent, &k, FALSE);
+	if (rc)
+		return rc;
+	/* ask for BSD partition */
+	rc = fdisk_ask_partnum(cxt, &i, TRUE);
+	if (rc)
+		return rc;
+
+	if (i >= BSD_MAXPARTITIONS)
+		return -EINVAL;
+
+	p = fdisk_dos_get_partition(cxt->parent, k);
+
+	d->d_partitions[i].p_size   = dos_partition_get_size(p);
+	d->d_partitions[i].p_offset = dos_partition_get_start(p);
+	d->d_partitions[i].p_fstype = bsd_translate_fstype(p->sys_ind);
+
+	if (i >= d->d_npartitions)
+		d->d_npartitions = i + 1;
+
+	cxt->label->nparts_cur = d->d_npartitions;
+	fdisk_label_set_changed(cxt->label, 1);
+
+	fdisk_info(cxt, _("BSD partition '%c' linked to DOS partition %zu."),
+			'a' + (int) i, k + 1);
+	return 0;
+}
+
+
+static int bsd_partition_is_used(
+		struct fdisk_context *cxt,
+		size_t partnum)
+{
+	struct bsd_disklabel *d = self_disklabel(cxt);
+
+	if (partnum >= BSD_MAXPARTITIONS)
+		return 0;
+
+	return d->d_partitions[partnum].p_size ? 1 : 0;
+}
+
+
+static const struct fdisk_label_operations bsd_operations =
+{
+	.probe		= bsd_probe_label,
+	.list		= bsd_list_disklabel,
+	.write		= bsd_write_disklabel,
+	.create		= bsd_create_disklabel,
+
+	.del_part	= bsd_delete_part,
+	.get_part	= bsd_get_partition,
+	.set_part	= bsd_set_partition,
+	.add_part	= bsd_add_partition,
+
+	.part_is_used   = bsd_partition_is_used,
+};
+
+static const struct fdisk_field bsd_fields[] =
+{
+	{ FDISK_FIELD_DEVICE,	N_("Slice"),	  1,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),    5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	  8,	0 },
+	{ FDISK_FIELD_FSIZE,	N_("Fsize"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_BSIZE,	N_("Bsize"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CPG,	N_("Cpg"),	  5,	FDISK_FIELDFL_NUMBER }
+};
+
+/*
+ * allocates BSD label driver
+ */
+struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_bsd_label *bsd;
+
+	assert(cxt);
+
+	bsd = calloc(1, sizeof(*bsd));
+	if (!bsd)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) bsd;
+	lb->name = "bsd";
+	lb->id = FDISK_DISKLABEL_BSD;
+	lb->op = &bsd_operations;
+	lb->parttypes = bsd_fstypes;
+	lb->nparttypes = ARRAY_SIZE(bsd_fstypes) - 1;
+
+	lb->fields = bsd_fields;
+	lb->nfields = ARRAY_SIZE(bsd_fields);
+
+	lb->flags |= FDISK_LABEL_FL_INCHARS_PARTNO;
+	lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+	return lb;
+}
diff --git a/libblkid/libfdisk/src/context.c b/libblkid/libfdisk/src/context.c
new file mode 100644
index 0000000..2a4d377
--- /dev/null
+++ b/libblkid/libfdisk/src/context.c
@@ -0,0 +1,1017 @@
+#ifdef HAVE_LIBBLKID
+# include <blkid.h>
+#endif
+
+#include "fdiskP.h"
+
+
+/**
+ * SECTION: context
+ * @title: Context
+ * @short_description: stores info about device, labels etc.
+ *
+ * The library distinguish between three types of partitioning objects.
+ *
+ * on-disk data
+ *    - disk label specific
+ *    - probed and read  by disklabel drivers when assign device to the context
+ *      or when switch to another disk label type
+ *    - only fdisk_write_disklabel() modify on-disk data
+ *
+ * in-memory data
+ *    - generic data and disklabel specific data stored in struct fdisk_label
+ *    - all partitioning operations are based on in-memory data only
+ *
+ * struct fdisk_partition
+ *    - provides abstraction to present partitions to users
+ *    - fdisk_partition is possible to gather to fdisk_table container
+ *    - used as unified template for new partitions
+ *    - the struct fdisk_partition is always completely independent object and
+ *      any change to the object has no effect to in-memory (or on-disk) label data
+ */
+
+/**
+ * fdisk_new_context:
+ *
+ * Returns: newly allocated libfdisk handler
+ */
+struct fdisk_context *fdisk_new_context(void)
+{
+	struct fdisk_context *cxt;
+
+	cxt = calloc(1, sizeof(*cxt));
+	if (!cxt)
+		return NULL;
+
+	DBG(CXT, ul_debugobj(cxt, "alloc"));
+	cxt->dev_fd = -1;
+	cxt->refcount = 1;
+
+	/*
+	 * Allocate label specific structs.
+	 *
+	 * This is necessary (for example) to store label specific
+	 * context setting.
+	 */
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_gpt_label(cxt);
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt);
+	cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt);
+
+	return cxt;
+}
+
+static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
+{
+	struct fdisk_context *parent;
+
+	assert(cxt);
+	assert(cxt->parent);
+
+	parent = cxt->parent;
+
+	cxt->alignment_offset = parent->alignment_offset;
+	cxt->ask_cb =		parent->ask_cb;
+	cxt->ask_data =		parent->ask_data;
+	cxt->dev_fd =		parent->dev_fd;
+	cxt->first_lba =        parent->first_lba;
+	cxt->firstsector_bufsz = parent->firstsector_bufsz;
+	cxt->firstsector =	parent->firstsector;
+	cxt->geom =		parent->geom;
+	cxt->grain =            parent->grain;
+	cxt->io_size =          parent->io_size;
+	cxt->last_lba =		parent->last_lba;
+	cxt->min_io_size =      parent->min_io_size;
+	cxt->optimal_io_size =  parent->optimal_io_size;
+	cxt->phy_sector_size =  parent->phy_sector_size;
+	cxt->readonly =		parent->readonly;
+	cxt->script =		parent->script;
+	fdisk_ref_script(cxt->script);
+	cxt->sector_size =      parent->sector_size;
+	cxt->total_sectors =    parent->total_sectors;
+	cxt->user_geom =	parent->user_geom;
+	cxt->user_log_sector =	parent->user_log_sector;
+	cxt->user_pyh_sector =  parent->user_pyh_sector;
+
+	/* parent <--> nested independent setting, initialize for new nested 
+	 * contexts only */
+	if (isnew) {
+		cxt->listonly =	parent->listonly;
+		cxt->display_details =	parent->display_details;
+		cxt->display_in_cyl_units = parent->display_in_cyl_units;
+	}
+
+	free(cxt->dev_path);
+	cxt->dev_path = NULL;
+
+	if (parent->dev_path) {
+		cxt->dev_path =	strdup(parent->dev_path);
+		if (!cxt->dev_path)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/**
+ * fdisk_new_nested_context:
+ * @parent: parental context
+ * @name: optional label name (e.g. "bsd")
+ *
+ * Create a new nested fdisk context for nested disk labels (e.g. BSD or PMBR).
+ * The function also probes for the nested label on the device if device is
+ * already assigned to parent.
+ *
+ * The new context is initialized according to @parent and both context shares
+ * some settings and file descriptor to the device. The child propagate some
+ * changes (like fdisk_assign_device()) to parent, but it does not work
+ * vice-versa. The behavior is undefined if you assign another device to
+ * parent.
+ *
+ * Returns: new context for nested partition table.
+ */
+struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
+				const char *name)
+{
+	struct fdisk_context *cxt;
+	struct fdisk_label *lb = NULL;
+
+	assert(parent);
+
+	cxt = calloc(1, sizeof(*cxt));
+	if (!cxt)
+		return NULL;
+
+	DBG(CXT, ul_debugobj(parent, "alloc nested [%p]", cxt));
+	cxt->refcount = 1;
+
+	fdisk_ref_context(parent);
+	cxt->parent = parent;
+
+	if (init_nested_from_parent(cxt, 1) != 0)
+		return NULL;
+
+	if (name) {
+		if (strcmp(name, "bsd") == 0)
+			lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
+		else if (strcmp(name, "dos") == 0)
+			lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
+	}
+
+	if (lb && parent->dev_fd >= 0) {
+		DBG(CXT, ul_debugobj(cxt, "probing for nested %s", lb->name));
+
+		cxt->label = lb;
+
+		if (lb->op->probe(cxt) == 1)
+			__fdisk_switch_label(cxt, lb);
+		else {
+			DBG(CXT, ul_debugobj(cxt, "not found %s label", lb->name));
+			if (lb->op->deinit)
+				lb->op->deinit(lb);
+			cxt->label = NULL;
+		}
+	}
+
+	return cxt;
+}
+
+
+/**
+ * fdisk_ref_context:
+ * @cxt: context pointer
+ *
+ * Increments reference counter.
+ */
+void fdisk_ref_context(struct fdisk_context *cxt)
+{
+	if (cxt)
+		cxt->refcount++;
+}
+
+/**
+ * fdisk_get_label:
+ * @cxt: context instance
+ * @name: label name (e.g. "gpt")
+ *
+ * If no @name specified then returns the current context label.
+ *
+ * The label is allocated and maintained within the context #cxt. There is
+ * nothing like reference counting for labels, you cannot delallocate the
+ * label.
+ *
+ * Returns: label struct or NULL in case of error.
+ */
+struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
+{
+	size_t i;
+
+	assert(cxt);
+
+	if (!name)
+		return cxt->label;
+
+	for (i = 0; i < cxt->nlabels; i++)
+		if (cxt->labels[i]
+		    && strcmp(cxt->labels[i]->name, name) == 0)
+			return cxt->labels[i];
+
+	DBG(CXT, ul_debugobj(cxt, "failed to found %s label driver", name));
+	return NULL;
+}
+
+/**
+ * fdisk_next_label:
+ * @cxt: context instance
+ * @lb: returns pointer to the next label
+ *
+ * <informalexample>
+ *   <programlisting>
+ *      // print all supported labels
+ *	struct fdisk_context *cxt = fdisk_new_context();
+ *	struct fdisk_label *lb = NULL;
+ *
+ *	while (fdisk_next_label(cxt, &lb) == 0)
+ *		print("label name: %s\n", fdisk_label_get_name(lb));
+ *	fdisk_unref_context(cxt);
+ *   </programlisting>
+ * </informalexample>
+ *
+ * Returns: <0 in case of error, 0 on success, 1 at the end.
+ */
+int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb)
+{
+	size_t i;
+	struct fdisk_label *res = NULL;
+
+	if (!lb || !cxt)
+		return -EINVAL;
+
+	if (!*lb)
+		res = cxt->labels[0];
+	else {
+		for (i = 1; i < cxt->nlabels; i++) {
+			if (*lb == cxt->labels[i - 1]) {
+				res = cxt->labels[i];
+				break;
+			}
+		}
+	}
+
+	*lb = res;
+	return res ? 0 : 1;
+}
+
+/**
+ * fdisk_get_nlabels:
+ * @cxt: context
+ *
+ * Returns: number of supported label types
+ */
+size_t fdisk_get_nlabels(struct fdisk_context *cxt)
+{
+	return cxt ? cxt->nlabels : 0;
+}
+
+int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb)
+{
+	if (!lb || !cxt)
+		return -EINVAL;
+	if (lb->disabled) {
+		DBG(CXT, ul_debugobj(cxt, "*** attempt to switch to disabled label %s -- ignore!", lb->name));
+		return -EINVAL;
+	}
+	cxt->label = lb;
+	DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
+	return 0;
+}
+
+/**
+ * fdisk_has_label:
+ * @cxt: fdisk context
+ *
+ * Returns: return 1 if there is label on the device.
+ */
+int fdisk_has_label(struct fdisk_context *cxt)
+{
+	return cxt && cxt->label;
+}
+
+/**
+ * fdisk_get_npartitions:
+ * @cxt: context
+ *
+ * The maximal number of the partitions depends on disklabel and does not
+ * have to describe the real limit of PT.
+ *
+ * For example the limit for MBR without extend partition is 4, with extended
+ * partition it's unlimited (so the function returns the current number of all
+ * partitions in this case).
+ *
+ * And for example for GPT it depends on space allocated on disk for array of
+ * entry records (usually 128).
+ *
+ * It's fine to use fdisk_get_npartitions() in loops, but don't forget that
+ * partition may be unused (see fdisk_is_partition_used()).
+ *
+ * <informalexample>
+ *   <programlisting>
+ *	struct fdisk_partition *pa = NULL;
+ *	size_t i, nmax = fdisk_get_npartitions(cxt);
+ *
+ *	for (i = 0; i < nmax; i++) {
+ *		if (!fdisk_is_partition_used(cxt, i))
+ *			continue;
+ *		... do something ...
+ *	}
+ *   </programlisting>
+ * </informalexample>
+ *
+ * Note that the recommended way to list partitions is to use
+ * fdisk_get_partitions() and struct fdisk_table than ask disk driver for each
+ * individual partitions.
+ *
+ * Returns: maximal number of partitions for the current label.
+ */
+size_t fdisk_get_npartitions(struct fdisk_context *cxt)
+{
+	return cxt && cxt->label ? cxt->label->nparts_max : 0;
+}
+
+/**
+ * fdisk_is_labeltype:
+ * @cxt: fdisk context
+ * @id: FDISK_DISKLABEL_*
+ *
+ * See also fdisk_is_label() macro in libfdisk.h.
+ *
+ * Returns: return 1 if the current label is @id
+ */
+int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id)
+{
+	assert(cxt);
+
+	return cxt->label && fdisk_label_get_type(cxt->label) == id;
+}
+
+/**
+ * fdisk_get_parent:
+ * @cxt: nested fdisk context
+ *
+ * Returns: pointer to parental context, or NULL
+ */
+struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->parent;
+}
+
+static void reset_context(struct fdisk_context *cxt)
+{
+	size_t i;
+
+	DBG(CXT, ul_debugobj(cxt, "*** resetting context"));
+
+	/* reset drives' private data */
+	for (i = 0; i < cxt->nlabels; i++)
+		fdisk_deinit_label(cxt->labels[i]);
+
+	if (cxt->parent) {
+		/* the first sector may be independent on parent */
+		if (cxt->parent->firstsector != cxt->firstsector)
+			free(cxt->firstsector);
+	} else {
+		/* we close device only in primary context */
+		if (cxt->dev_fd > -1)
+			close(cxt->dev_fd);
+		free(cxt->firstsector);
+	}
+
+	free(cxt->dev_path);
+	cxt->dev_path = NULL;
+
+	cxt->dev_fd = -1;
+	cxt->firstsector = NULL;
+	cxt->firstsector_bufsz = 0;
+
+	fdisk_zeroize_device_properties(cxt);
+
+	fdisk_unref_script(cxt->script);
+	cxt->script = NULL;
+
+	cxt->label = NULL;
+}
+
+/*
+ * This function prints a warning if the device is not wiped (e.g. wipefs(8).
+ * Please don't call this function if there is already a PT.
+ *
+ * Returns: 0 if nothing found, < 0 on error, 1 if found a signature
+ */
+static int warn_wipe(struct fdisk_context *cxt)
+{
+#ifdef HAVE_LIBBLKID
+	blkid_probe pr;
+#endif
+	int rc = 0;
+
+	assert(cxt);
+
+	if (fdisk_has_label(cxt) || cxt->dev_fd < 0)
+		return -EINVAL;
+#ifdef HAVE_LIBBLKID
+	DBG(CXT, ul_debugobj(cxt, "wipe check: initialize libblkid prober"));
+
+	pr = blkid_new_probe();
+	if (!pr)
+		return -ENOMEM;
+	rc = blkid_probe_set_device(pr, cxt->dev_fd, 0, 0);
+	if (rc)
+		return rc;
+
+	blkid_probe_enable_superblocks(pr, 1);
+	blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
+	blkid_probe_enable_partitions(pr, 1);
+
+	/* we care about the first found FS/raid, so don't call blkid_do_probe()
+	 * in loop or don't use blkid_do_fullprobe() ... */
+	rc = blkid_do_probe(pr);
+	if (rc == 0) {
+		const char *name = NULL;
+
+		if (blkid_probe_lookup_value(pr, "TYPE", &name, 0) == 0 ||
+		    blkid_probe_lookup_value(pr, "PTTYPE", &name, 0) == 0) {
+			fdisk_warnx(cxt, _(
+				"%s: device contains a valid '%s' signature; it is "
+				"strongly recommended to wipe the device with "
+				"wipefs(8) if this is unexpected, in order to "
+				"avoid possible collisions"), cxt->dev_path, name);
+			rc = 1;
+		}
+	}
+
+	blkid_free_probe(pr);
+#endif
+	return rc;
+}
+
+/**
+ * fdisk_assign_device:
+ * @cxt: context
+ * @fname: path to the device to be handled
+ * @readonly: how to open the device
+ *
+ * Open the device, discovery topology, geometry, detect disklabel and switch
+ * the current label driver to reflect the probing result.
+ *
+ * Note that this function resets all generic setting in context. If the @cxt
+ * is nested context then the device is assigned to the parental context and
+ * necessary properties are copied to the @cxt. The change is propagated in
+ * child->parent direction only. It's impossible to use a different device for
+ * primary and nested contexts.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_assign_device(struct fdisk_context *cxt,
+			const char *fname, int readonly)
+{
+	int fd;
+
+	DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
+	assert(cxt);
+
+	/* redirect request to parent */
+	if (cxt->parent) {
+		int rc, org = fdisk_is_listonly(cxt->parent);
+
+		/* assign_device() is sensitive to "listonly" mode, so let's
+		 * follow the current context setting for the parent to avoid 
+		 * unwanted extra warnings. */
+		fdisk_enable_listonly(cxt->parent, fdisk_is_listonly(cxt));
+
+		rc = fdisk_assign_device(cxt->parent, fname, readonly);
+		fdisk_enable_listonly(cxt->parent, org);
+
+		if (!rc)
+			rc = init_nested_from_parent(cxt, 0);
+		if (!rc)
+			fdisk_probe_labels(cxt);
+		return rc;
+	}
+
+	reset_context(cxt);
+
+	fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC);
+	if (fd < 0)
+		return -errno;
+
+	cxt->readonly = readonly;
+	cxt->dev_fd = fd;
+	cxt->dev_path = strdup(fname);
+	if (!cxt->dev_path)
+		goto fail;
+
+	fdisk_discover_topology(cxt);
+	fdisk_discover_geometry(cxt);
+
+	if (fdisk_read_firstsector(cxt) < 0)
+		goto fail;
+
+	/* detect labels and apply labes specific stuff (e.g geomery)
+	 * to the context */
+	fdisk_probe_labels(cxt);
+
+	/* let's apply user geometry *after* label prober
+	 * to make it possible to override in-label setting */
+	fdisk_apply_user_device_properties(cxt);
+
+	/* warn about obsolete stuff on the device if we aren't in
+	 * list-only mode and there is not PT yet */
+	if (!fdisk_is_listonly(cxt) && !fdisk_has_label(cxt))
+		warn_wipe(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s]",
+			      fname, readonly ? "READ-ONLY" : "READ-WRITE"));
+	return 0;
+fail:
+	DBG(CXT, ul_debugobj(cxt, "failed to assign device"));
+	return -errno;
+}
+
+/**
+ * fdisk_deassign_device:
+ * @cxt: context
+ * @nosync: disable fsync()
+ *
+ * Close device and call fsync(). If the @cxt is nested context than the
+ * request is redirected to the parent.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
+{
+	assert(cxt);
+	assert(cxt->dev_fd >= 0);
+
+	if (cxt->parent) {
+		int rc = fdisk_deassign_device(cxt->parent, nosync);
+
+		if (!rc)
+			rc = init_nested_from_parent(cxt, 0);
+		return rc;
+	}
+
+	if (cxt->readonly)
+		close(cxt->dev_fd);
+	else {
+		if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) {
+			fdisk_warn(cxt, _("%s: close device failed"),
+					cxt->dev_path);
+			return -errno;
+		}
+
+		if (!nosync) {
+			fdisk_info(cxt, _("Syncing disks."));
+			sync();
+		}
+	}
+
+	free(cxt->dev_path);
+	cxt->dev_path = NULL;
+
+	cxt->dev_fd = -1;
+
+	return 0;
+}
+
+/**
+ * fdisk_is_readonly:
+ * @cxt: context
+ *
+ * Returns: 1 if device open readonly
+ */
+int fdisk_is_readonly(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->readonly;
+}
+
+/**
+ * fdisk_unref_context:
+ * @cxt: fdisk context
+ *
+ * Deallocates context struct.
+ */
+void fdisk_unref_context(struct fdisk_context *cxt)
+{
+	int i;
+
+	if (!cxt)
+		return;
+
+	cxt->refcount--;
+	if (cxt->refcount <= 0) {
+		DBG(CXT, ul_debugobj(cxt, "freeing context %p for %s", cxt, cxt->dev_path));
+
+		reset_context(cxt);	/* this is sensitive to parent<->child relationship! */
+
+		/* deallocate label's private stuff */
+		for (i = 0; i < cxt->nlabels; i++) {
+			if (!cxt->labels[i])
+				continue;
+			if (cxt->labels[i]->op->free)
+				cxt->labels[i]->op->free(cxt->labels[i]);
+			else
+				free(cxt->labels[i]);
+		}
+
+		fdisk_unref_context(cxt->parent);
+		cxt->parent = NULL;
+
+		free(cxt);
+	}
+}
+
+
+/**
+ * fdisk_enable_details:
+ * @cxt: context
+ * @enable: true/flase
+ *
+ * Enables or disables "details" display mode. This function has effect to
+ * fdisk_partition_to_string() function.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_enable_details(struct fdisk_context *cxt, int enable)
+{
+	assert(cxt);
+	cxt->display_details = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_is_details:
+ * @cxt: context
+ *
+ * Returns: 1 if details are enabled
+ */
+int fdisk_is_details(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->display_details == 1;
+}
+
+/**
+ * fdisk_enable_listonly:
+ * @cxt: context
+ * @enable: true/flase
+ *
+ * Just list partition only, don't care about another details, mistakes, ...
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_enable_listonly(struct fdisk_context *cxt, int enable)
+{
+	assert(cxt);
+	cxt->listonly = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_is_listonly:
+ * @cxt: context
+ *
+ * Returns: 1 if list-only mode enabled
+ */
+int fdisk_is_listonly(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->listonly == 1;
+}
+
+
+/**
+ * fdisk_set_unit:
+ * @cxt: context
+ * @str: "cylinder" or "sector".
+ *
+ * This is pure shit, unfortunately for example Sun addresses begin of the
+ * partition by cylinders...
+ *
+ * Returns: 0 on succes, <0 on error.
+ */
+int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
+{
+	assert(cxt);
+
+	cxt->display_in_cyl_units = 0;
+
+	if (!str)
+		return 0;
+
+	if (strcmp(str, "cylinder") == 0 || strcmp(str, "cylinders") == 0)
+		cxt->display_in_cyl_units = 1;
+
+	else if (strcmp(str, "sector") == 0 || strcmp(str, "sectors") == 0)
+		cxt->display_in_cyl_units = 0;
+
+	DBG(CXT, ul_debugobj(cxt, "display unit: %s", fdisk_get_unit(cxt, 0)));
+	return 0;
+}
+
+/**
+ * fdisk_get_unit:
+ * @cxt: context
+ * @n: FDISK_PLURAL or FDISK_SINGULAR
+ *
+ * Returns: unit name.
+ */
+const char *fdisk_get_unit(struct fdisk_context *cxt, int n)
+{
+	assert(cxt);
+
+	if (fdisk_use_cylinders(cxt))
+		return P_("cylinder", "cylinders", n);
+	return P_("sector", "sectors", n);
+}
+
+/**
+ * fdisk_use_cylinders:
+ * @cxt: context
+ *
+ * Returns: 1 if user wants to display in cylinders.
+ */
+int fdisk_use_cylinders(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->display_in_cyl_units == 1;
+}
+
+/**
+ * fdisk_get_units_per_sector:
+ * @cxt: context
+ *
+ * This is necessary only for brain dead situations when we use "cylinders";
+ *
+ * Returns: number of "units" per sector, default is 1 if display unit is sector.
+ */
+unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt)
+{
+	assert(cxt);
+
+	if (fdisk_use_cylinders(cxt)) {
+		assert(cxt->geom.heads);
+		return cxt->geom.heads * cxt->geom.sectors;
+	}
+	return 1;
+}
+
+/**
+ * fdisk_get_optimal_iosize:
+ * @cxt: context
+ *
+ * The optimal I/O is optional and does not have to be provided by device,
+ * anyway libfdisk never returns zero. If the optimal I/O size is not provided
+ * then libfdisk returns minimal I/O size or sector size.
+ *
+ * Returns: optimal I/O size in bytes.
+ */
+unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->optimal_io_size ? cxt->optimal_io_size : cxt->io_size;
+}
+
+/**
+ * fdisk_get_minimal_iosize:
+ * @cxt: context
+ *
+ * Returns: minimal I/O size in bytes
+ */
+unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->min_io_size;
+}
+
+/**
+ * fdisk_get_physector_size:
+ * @cxt: context
+ *
+ * Returns: physical sector size in bytes
+ */
+unsigned long fdisk_get_physector_size(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->phy_sector_size;
+}
+
+/**
+ * fdisk_get_sector_size:
+ * @cxt: context
+ *
+ * Returns: logical sector size in bytes
+ */
+unsigned long fdisk_get_sector_size(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->sector_size;
+}
+
+/**
+ * fdisk_get_alignment_offset
+ * @cxt: context
+ *
+ * The alignment offset is offset between logical and physical sectors. For
+ * backward compatibility the first logical sector on 4K disks does no have to
+ * start on the same place like physical sectors.
+ *
+ * Returns: alignment offset in bytes
+ */
+unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->alignment_offset;
+}
+
+/**
+ * fdisk_get_grain_size:
+ * @cxt: context
+ *
+ * Returns: grain in bytes used to align partitions (usually 1MiB)
+ */
+unsigned long fdisk_get_grain_size(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->grain;
+}
+
+/**
+ * fdisk_get_first_lba:
+ * @cxt: context
+ *
+ * Returns: first possible LBA on disk for data partitions.
+ */
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->first_lba;
+}
+
+/**
+ * fdisk_set_first_lba:
+ * @cxt: fdisk context
+ * @lba: first possible logical sector for data
+ *
+ * It's strongly recommended to use the default library setting. The first LBA
+ * is always reseted by fdisk_assign_device(), fdisk_override_geometry()
+ * and fdisk_reset_alignment(). This is very low level function and library
+ * does not check if your setting makes any sense.
+ *
+ * This function is necessary only when you want to work with very unusual
+ * partition tables like GPT protective MBR or hybrid partition tables on
+ * bootable media where the first partition may start on very crazy offsets.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	assert(cxt);
+	DBG(CXT, ul_debugobj(cxt, "setting first LBA from %ju to %ju",
+			(uintmax_t) cxt->first_lba, (uintmax_t) lba));
+	cxt->first_lba = lba;
+	return 0;
+}
+
+/**
+ * fdisk_get_last_lba:
+ * @cxt: fdisk context
+ *
+ * Note that the device has to be already assigned.
+ *
+ * Returns: last possible LBA on device
+ */
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
+{
+	return cxt->last_lba;
+}
+
+/**
+ * fdisk_set_last_lba:
+ * @cxt: fdisk context
+ * @lba: last possible logical sector
+ *
+ * It's strongly recommended to use the default library setting. The last LBA
+ * is always reseted by fdisk_assign_device(), fdisk_override_geometry() and
+ * fdisk_reset_alignment().
+ *
+ * The default is number of sectors on the device, but maybe modified by the
+ * current disklabel driver (for example GPT uses and of disk for backup
+ * header, so last_lba is smaller than total number of sectors).
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+	assert(cxt);
+
+	if (lba > cxt->total_sectors - 1 && lba < 1)
+		return -ERANGE;
+	cxt->last_lba = lba;
+	return 0;
+}
+
+
+/**
+ * fdisk_get_nsectors:
+ * @cxt: context
+ *
+ * Returns: size of the device in logical sectors.
+ */
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->total_sectors;
+}
+
+/**
+ * fdisk_get_devname:
+ * @cxt: context
+ *
+ * Returns: device name.
+ */
+const char *fdisk_get_devname(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->dev_path;
+}
+
+/**
+ * fdisk_get_devfd:
+ * @cxt: context
+ *
+ * Retruns: device file descriptor.
+ */
+int fdisk_get_devfd(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->dev_fd;
+}
+
+/**
+ * fdisk_get_geom_heads:
+ * @cxt: context
+ *
+ * Returns: number of geometry heads.
+ */
+unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->geom.heads;
+}
+/**
+ * fdisk_get_geom_sectors:
+ * @cxt: context
+ *
+ * Returns: number of geometry sectors.
+ */
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->geom.sectors;
+
+}
+
+/**
+ * fdisk_get_geom_cylinders:
+ * @cxt: context
+ *
+ * Returns: number of geometry cylinders
+ */
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->geom.cylinders;
+}
+
+int fdisk_missing_geometry(struct fdisk_context *cxt)
+{
+	int rc;
+
+	assert(cxt);
+
+	if (!cxt || !cxt->label)
+		return 0;
+
+	rc = (fdisk_label_require_geometry(cxt->label) &&
+		    (!cxt->geom.heads || !cxt->geom.sectors
+				      || !cxt->geom.cylinders));
+
+	if (rc && !fdisk_is_listonly(cxt))
+		fdisk_warnx(cxt, _("Incomplete geometry setting."));
+
+	return rc;
+}
+
diff --git a/libblkid/libfdisk/src/dos.c b/libblkid/libfdisk/src/dos.c
new file mode 100644
index 0000000..2a06707
--- /dev/null
+++ b/libblkid/libfdisk/src/dos.c
@@ -0,0 +1,2331 @@
+/*
+ *
+ * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
+ *                    2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * This is re-written version for libfdisk, the original was fdiskdoslabel.c
+ * from util-linux fdisk.
+ */
+#include "c.h"
+#include "nls.h"
+#include "randutils.h"
+#include "pt-mbr.h"
+#include "strutils.h"
+
+#include "fdiskP.h"
+
+#include <ctype.h>
+
+#define MAXIMUM_PARTS	60
+#define ACTIVE_FLAG     0x80
+
+/**
+ * SECTION: dos
+ * @title: DOS (MBR)
+ * @short_description: disk label specific functions
+ *
+ */
+
+
+#define IS_EXTENDED(i) \
+	((i) == MBR_DOS_EXTENDED_PARTITION \
+	 || (i) == MBR_W95_EXTENDED_PARTITION \
+	 || (i) == MBR_LINUX_EXTENDED_PARTITION)
+
+/*
+ * per partition table entry data
+ *
+ * The four primary partitions have the same sectorbuffer
+ * and have NULL ex_entry.
+ *
+ * Each logical partition table entry has two pointers, one for the
+ * partition and one link to the next one.
+ */
+struct pte {
+	struct dos_partition *pt_entry;	/* on-disk MBR entry */
+	struct dos_partition *ex_entry;	/* on-disk EBR entry */
+	fdisk_sector_t offset;	        /* disk sector number */
+	unsigned char *sectorbuffer;	/* disk sector contents */
+
+	unsigned int changed : 1,
+		     private_sectorbuffer : 1;
+};
+
+/*
+ * in-memory fdisk GPT stuff
+ */
+struct fdisk_dos_label {
+	struct fdisk_label	head;		/* generic part */
+
+	struct pte	ptes[MAXIMUM_PARTS];	/* partition */
+	fdisk_sector_t	ext_offset;		/* start of the ext.partition */
+	size_t		ext_index;		/* ext.partition index (if ext_offset is set) */
+	unsigned int	compatible : 1,		/* is DOS compatible? */
+			non_pt_changed : 1;	/* MBR, but no PT changed */
+};
+
+/*
+ * Partition types
+ */
+static struct fdisk_parttype dos_parttypes[] = {
+	#include "pt-mbr-partnames.h"
+};
+
+#define set_hsc(h,s,c,sector) { \
+		s = sector % cxt->geom.sectors + 1;			\
+		sector /= cxt->geom.sectors;				\
+		h = sector % cxt->geom.heads;				\
+		sector /= cxt->geom.heads;				\
+		c = sector & 0xff;					\
+		s |= (sector >> 2) & 0xc0;				\
+	}
+
+
+#define sector(s)	((s) & 0x3f)
+#define cylinder(s, c)	((c) | (((s) & 0xc0) << 2))
+
+#define alignment_required(_x)	((_x)->grain != (_x)->sector_size)
+
+#define is_dos_compatible(_x) \
+		   (fdisk_is_label(_x, DOS) && \
+                    fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
+
+#define cround(c, n)	fdisk_cround(c, n)
+
+
+static inline struct fdisk_dos_label *self_label(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	return (struct fdisk_dos_label *) cxt->label;
+}
+
+static inline struct pte *self_pte(struct fdisk_context *cxt, size_t i)
+{
+	struct fdisk_dos_label *l = self_label(cxt);
+
+	if (i >= ARRAY_SIZE(l->ptes))
+		return NULL;
+
+	return &l->ptes[i];
+}
+
+static inline struct dos_partition *self_partition(
+				struct fdisk_context *cxt,
+				size_t i)
+{
+	struct pte *pe = self_pte(cxt, i);
+	return pe ? pe->pt_entry : NULL;
+}
+
+struct dos_partition *fdisk_dos_get_partition(
+				struct fdisk_context *cxt,
+				size_t i)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	return self_partition(cxt, i);
+}
+
+static struct fdisk_parttype *dos_partition_parttype(
+		struct fdisk_context *cxt,
+		struct dos_partition *p)
+{
+	struct fdisk_parttype *t
+		= fdisk_label_get_parttype_from_code(cxt->label, p->sys_ind);
+	return t ? : fdisk_new_unknown_parttype(p->sys_ind, NULL);
+}
+
+/*
+ * Linux kernel cares about partition size only. Things like
+ * partition type or so are completely irrelevant -- kzak Nov-2013
+ */
+static int is_used_partition(struct dos_partition *p)
+{
+	return p && dos_partition_get_size(p) != 0;
+}
+
+static void partition_set_changed(
+				struct fdisk_context *cxt,
+				size_t i,
+				int changed)
+{
+	struct pte *pe = self_pte(cxt, i);
+
+	if (!pe)
+		return;
+
+	DBG(LABEL, ul_debug("DOS: setting %zu partition to %s", i,
+				changed ? "changed" : "unchanged"));
+
+	pe->changed = changed ? 1 : 0;
+	if (changed)
+		fdisk_label_set_changed(cxt->label, 1);
+}
+
+static fdisk_sector_t get_abs_partition_start(struct pte *pe)
+{
+	assert(pe);
+	assert(pe->pt_entry);
+
+	return pe->offset + dos_partition_get_start(pe->pt_entry);
+}
+
+static fdisk_sector_t get_abs_partition_end(struct pte *pe)
+{
+	fdisk_sector_t size;
+
+	assert(pe);
+	assert(pe->pt_entry);
+
+	size = dos_partition_get_size(pe->pt_entry);
+	return get_abs_partition_start(pe) + size - (size ? 1 : 0);
+}
+
+static int is_cleared_partition(struct dos_partition *p)
+{
+	return !(!p || p->boot_ind || p->bh || p->bs || p->bc ||
+		 p->sys_ind || p->eh || p->es || p->ec ||
+		 dos_partition_get_start(p) || dos_partition_get_size(p));
+}
+
+static int get_partition_unused_primary(struct fdisk_context *cxt,
+					struct fdisk_partition *pa,
+					size_t *partno)
+{
+	size_t org, n;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(partno);
+
+	org = cxt->label->nparts_max;
+
+	cxt->label->nparts_max = 4;
+	rc = fdisk_partition_next_partno(pa, cxt, &n);
+	cxt->label->nparts_max = org;
+
+	if (rc == 1) {
+		fdisk_info(cxt, _("All primary partitions have been defined already."));
+		rc = -1;
+	} else if (rc == 0)
+		*partno = n;
+	return rc;
+}
+
+static int seek_sector(struct fdisk_context *cxt, fdisk_sector_t secno)
+{
+	off_t offset = (off_t) secno * cxt->sector_size;
+
+	return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
+}
+
+static int read_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
+			unsigned char *buf)
+{
+	int rc = seek_sector(cxt, secno);
+	ssize_t r;
+
+	if (rc < 0)
+		return rc;
+
+	r = read(cxt->dev_fd, buf, cxt->sector_size);
+	if (r == (ssize_t) cxt->sector_size)
+		return 0;
+	if (r < 0)
+		return -errno;
+	return -1;
+}
+
+/* Allocate a buffer and read a partition table sector */
+static int read_pte(struct fdisk_context *cxt, size_t pno, fdisk_sector_t offset)
+{
+	int rc;
+	unsigned char *buf;
+	struct pte *pe = self_pte(cxt, pno);
+
+	buf = calloc(1, cxt->sector_size);
+	if (!buf)
+		return -ENOMEM;
+
+	DBG(LABEL, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
+				pno, (uintmax_t) offset, buf));
+
+	pe->offset = offset;
+	pe->sectorbuffer = buf;
+	pe->private_sectorbuffer = 1;
+
+	rc = read_sector(cxt, offset, pe->sectorbuffer);
+	if (rc) {
+		fdisk_warn(cxt, _("Failed to read extended partition table "
+				"(offset=%ju)"), (uintmax_t) offset);
+		return rc;
+	}
+
+	pe->changed = 0;
+	pe->pt_entry = pe->ex_entry = NULL;
+	return 0;
+}
+
+
+static void clear_partition(struct dos_partition *p)
+{
+	if (!p)
+		return;
+	p->boot_ind = 0;
+	p->bh = 0;
+	p->bs = 0;
+	p->bc = 0;
+	p->sys_ind = 0;
+	p->eh = 0;
+	p->es = 0;
+	p->ec = 0;
+	dos_partition_set_start(p,0);
+	dos_partition_set_size(p,0);
+}
+
+static void dos_init(struct fdisk_context *cxt)
+{
+	struct fdisk_dos_label *l = self_label(cxt);
+	size_t i;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	DBG(LABEL, ul_debug("DOS: initialize, first sector buffer %p", cxt->firstsector));
+
+	cxt->label->nparts_max = 4;	/* default, unlimited number of logical */
+
+	l->ext_index = 0;
+	l->ext_offset = 0;
+	l->non_pt_changed = 0;
+
+	memset(l->ptes, 0, sizeof(l->ptes));
+
+	for (i = 0; i < 4; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		pe->pt_entry = mbr_get_partition(cxt->firstsector, i);
+		pe->ex_entry = NULL;
+		pe->offset = 0;
+		pe->sectorbuffer = cxt->firstsector;
+		pe->private_sectorbuffer = 0;
+		pe->changed = 0;
+	}
+
+	if (fdisk_is_listonly(cxt))
+		return;
+	/*
+	 * Various warnings...
+	 */
+	if (fdisk_missing_geometry(cxt))
+		fdisk_warnx(cxt, _("You can set geometry from the extra functions menu."));
+
+	if (is_dos_compatible(cxt)) {
+		fdisk_warnx(cxt, _("DOS-compatible mode is deprecated."));
+
+		if (cxt->sector_size != cxt->phy_sector_size)
+			fdisk_info(cxt, _(
+		"The device presents a logical sector size that is smaller than "
+		"the physical sector size. Aligning to a physical sector (or optimal "
+		"I/O) size boundary is recommended, or performance may be impacted."));
+	}
+
+	if (fdisk_use_cylinders(cxt))
+		fdisk_warnx(cxt, _("Cylinders as display units are deprecated."));
+
+	if (cxt->total_sectors > UINT_MAX) {
+		uint64_t bytes = cxt->total_sectors * cxt->sector_size;
+		char *szstr = size_to_human_string(SIZE_SUFFIX_SPACE
+					   | SIZE_SUFFIX_3LETTER, bytes);
+		fdisk_warnx(cxt,
+		_("The size of this disk is %s (%ju bytes). DOS "
+		  "partition table format can not be used on drives for "
+		  "volumes larger than %lu bytes for %lu-byte "
+		  "sectors. Use GUID partition table format (GPT)."),
+			szstr, bytes,
+			UINT_MAX * cxt->sector_size,
+			cxt->sector_size);
+		free(szstr);
+	}
+}
+
+/* callback called by libfdisk */
+static void dos_deinit(struct fdisk_label *lb)
+{
+	size_t i;
+	struct fdisk_dos_label *l = (struct fdisk_dos_label *) lb;
+
+	for (i = 0; i < ARRAY_SIZE(l->ptes); i++) {
+		struct pte *pe = &l->ptes[i];
+
+		if (pe->private_sectorbuffer && pe->sectorbuffer) {
+			DBG(LABEL, ul_debug("DOS: freeing pte %zu sector buffer %p",
+						i, pe->sectorbuffer));
+			free(pe->sectorbuffer);
+		}
+		pe->sectorbuffer = NULL;
+		pe->private_sectorbuffer = 0;
+	}
+
+	memset(l->ptes, 0, sizeof(l->ptes));
+}
+
+static void reset_pte(struct pte *pe)
+{
+	assert(pe);
+
+	if (pe->private_sectorbuffer) {
+		DBG(LABEL, ul_debug("  --> freeing pte sector buffer %p",
+					pe->sectorbuffer));
+		free(pe->sectorbuffer);
+	}
+	memset(pe, 0, sizeof(struct pte));
+}
+
+static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum)
+{
+	struct fdisk_dos_label *l;
+	struct pte *pe;
+	struct dos_partition *p;
+	struct dos_partition *q;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	pe = self_pte(cxt, partnum);
+	if (!pe)
+		return -EINVAL;
+
+	DBG(LABEL, ul_debug("DOS: delete partiton %zu (max=%zu)", partnum,
+				cxt->label->nparts_max));
+
+	l = self_label(cxt);
+	p = pe->pt_entry;
+	q = pe->ex_entry;
+
+	/* Note that for the fifth partition (partnum == 4) we don't actually
+	   decrement partitions. */
+	if (partnum < 4) {
+		DBG(LABEL, ul_debug("--> delete primary"));
+		if (IS_EXTENDED(p->sys_ind) && partnum == l->ext_index) {
+			cxt->label->nparts_max = 4;
+			l->ptes[l->ext_index].ex_entry = NULL;
+			l->ext_offset = 0;
+			l->ext_index = 0;
+		}
+		partition_set_changed(cxt, partnum, 1);
+		clear_partition(p);
+	} else if (!q->sys_ind && partnum > 4) {
+		DBG(LABEL, ul_debug("--> delete logical [last in the chain]"));
+		reset_pte(&l->ptes[partnum]);
+		--cxt->label->nparts_max;
+		--partnum;
+		/* clear link to deleted partition */
+		clear_partition(l->ptes[partnum].ex_entry);
+		partition_set_changed(cxt, partnum, 1);
+	} else {
+		DBG(LABEL, ul_debug("--> delete logical [move down]"));
+		if (partnum > 4) {
+			DBG(LABEL, ul_debug(" --> delete %zu logical link", partnum));
+			p = l->ptes[partnum - 1].ex_entry;
+			*p = *q;
+			dos_partition_set_start(p, dos_partition_get_start(q));
+			dos_partition_set_size(p, dos_partition_get_size(q));
+			partition_set_changed(cxt, partnum - 1, 1);
+
+		} else if (cxt->label->nparts_max > 5) {
+			DBG(LABEL, ul_debug(" --> delete first logical link"));
+			pe = &l->ptes[5];	/* second logical */
+
+			if (pe->pt_entry)	/* prevent SEGFAULT */
+				dos_partition_set_start(pe->pt_entry,
+					       get_abs_partition_start(pe) -
+					       l->ext_offset);
+			pe->offset = l->ext_offset;
+			partition_set_changed(cxt, 5, 1);
+		}
+
+		if (cxt->label->nparts_max > 5) {
+			DBG(LABEL, ul_debug(" --> move ptes"));
+			cxt->label->nparts_max--;
+			reset_pte(&l->ptes[partnum]);
+			while (partnum < cxt->label->nparts_max) {
+				DBG(LABEL, ul_debug("  --> moving pte %zu <-- %zu", partnum, partnum + 1));
+				l->ptes[partnum] = l->ptes[partnum + 1];
+				partnum++;
+			}
+			memset(&l->ptes[partnum], 0, sizeof(struct pte));
+		} else {
+			DBG(LABEL, ul_debug(" --> the only logical: clear only"));
+			clear_partition(l->ptes[partnum].pt_entry);
+			cxt->label->nparts_max--;
+
+			if (partnum == 4) {
+				DBG(LABEL, ul_debug("  --> clear last logical"));
+				reset_pte(&l->ptes[partnum]);
+				partition_set_changed(cxt, l->ext_index, 1);
+			}
+		}
+	}
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static void read_extended(struct fdisk_context *cxt, size_t ext)
+{
+	size_t i;
+	struct pte *pex, *pe;
+	struct dos_partition *p, *q;
+	struct fdisk_dos_label *l = self_label(cxt);
+
+	l->ext_index = ext;
+	pex = self_pte(cxt, ext);
+	pex->ex_entry = pex->pt_entry;
+
+	p = pex->pt_entry;
+	if (!dos_partition_get_start(p)) {
+		fdisk_warnx(cxt, _("Bad offset in primary extended partition."));
+		return;
+	}
+
+	DBG(LABEL, ul_debug("DOS: Reading extended %zu", ext));
+
+	while (IS_EXTENDED (p->sys_ind)) {
+		pe = self_pte(cxt, cxt->label->nparts_max);
+
+		if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
+			/* This is not a Linux restriction, but
+			   this program uses arrays of size MAXIMUM_PARTS.
+			   Do not try to `improve' this test. */
+			struct pte *pre = self_pte(cxt,
+						cxt->label->nparts_max - 1);
+			fdisk_warnx(cxt,
+			_("Omitting partitions after #%zu. They will be deleted "
+			  "if you save this partition table."),
+				cxt->label->nparts_max);
+
+			clear_partition(pre->ex_entry);
+			partition_set_changed(cxt,
+					cxt->label->nparts_max - 1, 1);
+			return;
+		}
+
+		if (read_pte(cxt, cxt->label->nparts_max, l->ext_offset +
+						dos_partition_get_start(p)))
+			return;
+
+		if (!l->ext_offset)
+			l->ext_offset = dos_partition_get_start(p);
+
+		assert(pe->sectorbuffer);
+		q = p = mbr_get_partition(pe->sectorbuffer, 0);
+
+		for (i = 0; i < 4; i++, p++) {
+			if (!dos_partition_get_size(p))
+				continue;
+
+			if (IS_EXTENDED (p->sys_ind)) {
+				if (pe->ex_entry)
+					fdisk_warnx(cxt, _(
+					"Extra link pointer in partition "
+					"table %zu."),
+						cxt->label->nparts_max + 1);
+				else
+					pe->ex_entry = p;
+			} else if (p->sys_ind) {
+				if (pe->pt_entry)
+					fdisk_warnx(cxt, _(
+					"Ignoring extra data in partition "
+					"table %zu."),
+						cxt->label->nparts_max + 1);
+				else
+					pe->pt_entry = p;
+			}
+		}
+
+		/* very strange code here... */
+		if (!pe->pt_entry) {
+			if (q != pe->ex_entry)
+				pe->pt_entry = q;
+			else
+				pe->pt_entry = q + 1;
+		}
+		if (!pe->ex_entry) {
+			if (q != pe->pt_entry)
+				pe->ex_entry = q;
+			else
+				pe->ex_entry = q + 1;
+		}
+
+		p = pe->ex_entry;
+		cxt->label->nparts_cur = ++cxt->label->nparts_max;
+
+		DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: link: type=%x,  start=%u, size=%u; "
+				                         " data: type=%x, start=%u, size=%u",
+				    (uintmax_t) pe->offset,
+				    pe->ex_entry->sys_ind,
+				    dos_partition_get_start(pe->ex_entry),
+				    dos_partition_get_size(pe->ex_entry),
+				    pe->pt_entry->sys_ind,
+				    dos_partition_get_start(pe->pt_entry),
+				    dos_partition_get_size(pe->pt_entry)));
+
+	}
+
+	/* remove last empty EBR */
+	pe = self_pte(cxt, cxt->label->nparts_max - 1);
+	if (is_cleared_partition(pe->ex_entry) &&
+	    is_cleared_partition(pe->pt_entry)) {
+		DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe->offset));
+		reset_pte(pe);
+		cxt->label->nparts_max--;
+		cxt->label->nparts_cur--;
+	}
+
+	/* remove empty links */
+ remove:
+	q = self_partition(cxt, 4);
+	for (i = 4; i < cxt->label->nparts_max; i++) {
+		p = self_partition(cxt, i);
+
+		if (!dos_partition_get_size(p) &&
+		    (cxt->label->nparts_max > 5 || q->sys_ind)) {
+			fdisk_info(cxt, _("omitting empty partition (%zu)"), i+1);
+			dos_delete_partition(cxt, i);
+			goto remove; 	/* numbering changed */
+		}
+	}
+
+	DBG(LABEL, ul_debug("DOS: nparts_max: %zu", cxt->label->nparts_max));
+}
+
+static int dos_get_disklabel_id(struct fdisk_context *cxt, char **id)
+{
+	unsigned int num;
+
+	assert(cxt);
+	assert(id);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	num = mbr_get_id(cxt->firstsector);
+	if (asprintf(id, "0x%08x", num) > 0)
+		return 0;
+
+	return -ENOMEM;
+}
+
+static int dos_create_disklabel(struct fdisk_context *cxt)
+{
+	unsigned int id = 0;
+	int rc, has_id = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	DBG(LABEL, ul_debug("DOS: creating new disklabel"));
+
+	if (cxt->script) {
+		char *end = NULL;
+		const char *s = fdisk_script_get_header(cxt->script, "label-id");
+
+		if (s) {
+			errno = 0;
+			id = strtol(s, &end, 16);
+			if (!errno && end && s < end)
+				has_id = 1;
+		}
+	}
+
+	/* random disk signature */
+	if (!has_id)
+		random_get_bytes(&id, sizeof(id));
+
+	dos_init(cxt);
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+	fdisk_label_set_changed(cxt->label, 1);
+
+	/* Generate an MBR ID for this disk */
+	mbr_set_id(cxt->firstsector, id);
+
+	/* Put MBR signature */
+	mbr_set_magic(cxt->firstsector);
+
+	fdisk_info(cxt, _("Created a new DOS disklabel with disk "
+			 "identifier 0x%08x."), id);
+	return 0;
+}
+
+static int dos_set_disklabel_id(struct fdisk_context *cxt)
+{
+	char *end = NULL, *str = NULL;
+	unsigned int id, old;
+	struct fdisk_dos_label *l;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	DBG(LABEL, ul_debug("DOS: setting Id"));
+
+	l = self_label(cxt);
+	old = mbr_get_id(cxt->firstsector);
+	rc = fdisk_ask_string(cxt,
+			_("Enter the new disk identifier"), &str);
+	if (rc)
+		return rc;
+
+	errno = 0;
+	id = strtoul(str, &end, 0);
+	if (errno || str == end || (end && *end)) {
+		fdisk_warnx(cxt, _("Incorrect value."));
+		return -EINVAL;
+	}
+
+
+	mbr_set_id(cxt->firstsector, id);
+	l->non_pt_changed = 1;
+	fdisk_label_set_changed(cxt->label, 1);
+
+	fdisk_info(cxt, _("Disk identifier changed from 0x%08x to 0x%08x."),
+			old, id);
+	return 0;
+}
+
+static void get_partition_table_geometry(struct fdisk_context *cxt,
+			unsigned int *ph, unsigned int *ps)
+{
+	unsigned char *bufp = cxt->firstsector;
+	struct dos_partition *p;
+	int i, h, s, hh, ss;
+	int first = 1;
+	int bad = 0;
+
+	hh = ss = 0;
+	for (i = 0; i < 4; i++) {
+		p = mbr_get_partition(bufp, i);
+		if (p->sys_ind != 0) {
+			h = p->eh + 1;
+			s = (p->es & 077);
+			if (first) {
+				hh = h;
+				ss = s;
+				first = 0;
+			} else if (hh != h || ss != s)
+				bad = 1;
+		}
+	}
+
+	if (!first && !bad) {
+		*ph = hh;
+		*ps = ss;
+	}
+
+	DBG(LABEL, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
+}
+
+static int dos_reset_alignment(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	/* overwrite necessary stuff by DOS deprecated stuff */
+	if (is_dos_compatible(cxt)) {
+		DBG(LABEL, ul_debug("DOS: reseting alignemnt for DOS-comaptiblem PT"));
+		if (cxt->geom.sectors)
+			cxt->first_lba = cxt->geom.sectors;	/* usually 63 */
+
+		cxt->grain = cxt->sector_size;			/* usually 512 */
+	}
+
+	return 0;
+}
+
+/* TODO: move to include/pt-dos.h and share with libblkid */
+#define AIX_MAGIC_STRING	"\xC9\xC2\xD4\xC1"
+#define AIX_MAGIC_STRLEN	(sizeof(AIX_MAGIC_STRING) - 1)
+
+static int dos_probe_label(struct fdisk_context *cxt)
+{
+	size_t i;
+	unsigned int h = 0, s = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	/* ignore disks with AIX magic number */
+	if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
+		return 0;
+
+	if (!mbr_is_valid_magic(cxt->firstsector))
+		return 0;
+
+	dos_init(cxt);
+
+	get_partition_table_geometry(cxt, &h, &s);
+	if (h && s) {
+		cxt->geom.heads = h;
+	        cxt->geom.sectors = s;
+	}
+
+	for (i = 0; i < 4; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		if (is_used_partition(pe->pt_entry))
+			cxt->label->nparts_cur++;
+
+		if (IS_EXTENDED (pe->pt_entry->sys_ind)) {
+			if (cxt->label->nparts_max != 4)
+				fdisk_warnx(cxt, _(
+				"Ignoring extra extended partition %zu"),
+					i + 1);
+			else
+				read_extended(cxt, i);
+		}
+	}
+
+	for (i = 3; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+		struct fdisk_dos_label *l = self_label(cxt);
+
+		if (!mbr_is_valid_magic(pe->sectorbuffer)) {
+			fdisk_info(cxt, _(
+			"Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
+			"be corrected by w(rite)."),
+				pe->sectorbuffer[510],
+				pe->sectorbuffer[511],
+				i + 1);
+			partition_set_changed(cxt, i, 1);
+
+			/* mark also extended as changed to update the first EBR
+			 * in situation that there is no logical partitions at all */
+			partition_set_changed(cxt, l->ext_index, 1);
+		}
+	}
+
+	return 1;
+}
+
+static void set_partition(struct fdisk_context *cxt,
+			  int i, int doext, fdisk_sector_t start,
+			  fdisk_sector_t stop, int sysid, int boot)
+{
+	struct pte *pe = self_pte(cxt, i);
+	struct dos_partition *p;
+	fdisk_sector_t offset;
+
+	assert(!FDISK_IS_UNDEF(start));
+	assert(!FDISK_IS_UNDEF(stop));
+
+	if (doext) {
+		struct fdisk_dos_label *l = self_label(cxt);
+		p = pe->ex_entry;
+		offset = l->ext_offset;
+	} else {
+		p = pe->pt_entry;
+		offset = pe->offset;
+	}
+
+	DBG(LABEL, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, stop=%zu, sysid=%02x",
+				i, doext ? " [extended]" : "",
+				(size_t) offset,
+				(size_t) (start -  offset),
+				(size_t) (stop - start + 1),
+				sysid));
+
+	p->boot_ind = boot ? ACTIVE_FLAG : 0;
+	p->sys_ind = sysid;
+	dos_partition_set_start(p, start - offset);
+	dos_partition_set_size(p, stop - start + 1);
+
+	if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
+		start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
+	set_hsc(p->bh, p->bs, p->bc, start);
+	if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
+		stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
+	set_hsc(p->eh, p->es, p->ec, stop);
+	partition_set_changed(cxt, i, 1);
+}
+
+static fdisk_sector_t get_unused_start(struct fdisk_context *cxt,
+				 int part_n, fdisk_sector_t start,
+				 fdisk_sector_t first[], fdisk_sector_t last[])
+{
+	size_t i;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		fdisk_sector_t lastplusoff;
+		struct pte *pe = self_pte(cxt, i);
+
+		if (start == pe->offset)
+			start += cxt->first_lba;
+		lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba);
+		if (start >= first[i] && start <= lastplusoff)
+			start = lastplusoff + 1;
+	}
+
+	return start;
+}
+
+static void fill_bounds(struct fdisk_context *cxt,
+			fdisk_sector_t *first, fdisk_sector_t *last)
+{
+	size_t i;
+	struct pte *pe = self_pte(cxt, 0);
+	struct dos_partition *p;
+
+	for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
+		p = pe->pt_entry;
+		if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
+			first[i] = 0xffffffff;
+			last[i] = 0;
+		} else {
+			first[i] = get_abs_partition_start(pe);
+			last[i]  = get_abs_partition_end(pe);
+		}
+	}
+}
+
+static int get_start_from_user(	struct fdisk_context *cxt,
+				fdisk_sector_t *start,
+				fdisk_sector_t low,
+				fdisk_sector_t dflt,
+				fdisk_sector_t limit,
+				struct fdisk_partition *pa)
+{
+	assert(start);
+
+	/* try to use tepmlate from 'pa' */
+	if (pa && pa->start_follow_default)
+		*start = dflt;
+
+	else if (pa && fdisk_partition_has_start(pa)) {
+		DBG(LABEL, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
+				(uintmax_t) pa->start, (uintmax_t) low, (uintmax_t) limit));
+		*start = pa->start;
+		if (*start < low || *start > limit) {
+			fdisk_warnx(cxt, _("Start sector %ju out of range."),
+					(uintmax_t) *start);
+			return -ERANGE;
+		}
+	} else {
+		/* ask user by dialog */
+		struct fdisk_ask *ask = fdisk_new_ask();
+		int rc;
+
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_query(ask,
+			fdisk_use_cylinders(cxt) ?
+				_("First cylinder") : _("First sector"));
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+		fdisk_ask_number_set_low(ask, fdisk_cround(cxt, low));
+		fdisk_ask_number_set_default(ask, fdisk_cround(cxt, dflt));
+		fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
+
+		rc = fdisk_do_ask(cxt, ask);
+		*start = fdisk_ask_number_get_result(ask);
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt)) {
+		        *start = (*start - 1)
+				* fdisk_get_units_per_sector(cxt);
+			if (*start < low)
+				*start = low;
+		}
+	}
+
+	DBG(LABEL, ul_debug("DOS: start is %ju", (uintmax_t) *start));
+	return 0;
+}
+
+static fdisk_sector_t get_possible_last(struct fdisk_context *cxt, size_t n)
+{
+	fdisk_sector_t limit;
+
+	if (n >= 4) {
+		/* logical partitions */
+		struct fdisk_dos_label *l = self_label(cxt);
+		struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+		if (!ext_pe)
+			return 0;
+		limit = get_abs_partition_end(ext_pe);
+	} else {
+		/* primary partitions */
+		if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
+			limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
+		else
+			limit = cxt->total_sectors - 1;
+
+		if (limit > UINT_MAX)
+			limit = UINT_MAX;
+	}
+
+	DBG(LABEL, ul_debug("DOS: last possible sector for #%zu is %ju",
+				n, (uintmax_t) limit));
+	return limit;
+}
+
+/* returns last free sector for area addressed by @start, the first[] and
+ * last[] are fill_bounds() results */
+static fdisk_sector_t get_unused_last(struct fdisk_context *cxt, size_t n,
+				fdisk_sector_t start,
+				fdisk_sector_t first[], fdisk_sector_t last[])
+{
+	size_t i;
+	fdisk_sector_t limit = get_possible_last(cxt, n);
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		if (start < pe->offset && limit >= pe->offset)
+			limit = pe->offset - 1;
+		if (start < first[i] && limit >= first[i])
+			limit = first[i] - 1;
+	}
+
+	DBG(LABEL, ul_debug("DOS: unused sector for #%zu is %ju",
+				n, (uintmax_t) limit));
+	return limit;
+}
+
+static int add_partition(struct fdisk_context *cxt, size_t n,
+			 struct fdisk_partition *pa)
+{
+	int sys, read = 0, rc, isrel = 0;
+	size_t i;
+	struct fdisk_dos_label *l = self_label(cxt);
+	struct dos_partition *p = self_partition(cxt, n);
+	struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+	fdisk_sector_t start, stop = 0, limit, temp,
+		first[cxt->label->nparts_max],
+		last[cxt->label->nparts_max];
+
+	DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
+
+	sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
+
+	if (is_used_partition(p)) {
+		fdisk_warnx(cxt, _("Partition %zu is already defined.  "
+			           "Delete it before re-adding it."),
+				n + 1);
+		return -EINVAL;
+	}
+	fill_bounds(cxt, first, last);
+	limit = get_possible_last(cxt, n);
+
+	if (n < 4) {
+		if (cxt->parent && fdisk_is_label(cxt->parent, GPT))
+			start = 1;		/* Bad boy modifies hybrid MBR */
+		else {
+			if (cxt->script && pa && fdisk_partition_has_start(pa)
+			    && pa->start < cxt->first_lba
+			    && pa->start >= 1)
+				fdisk_set_first_lba(cxt, 1);
+
+			start = cxt->first_lba;
+		}
+
+		if (l->ext_offset) {
+			assert(ext_pe);
+			first[l->ext_index] = l->ext_offset;
+			last[l->ext_index] = get_abs_partition_end(ext_pe);
+		}
+	} else {
+		assert(ext_pe);
+
+		if (cxt->script && pa && fdisk_partition_has_start(pa)
+		    && pa->start >= l->ext_offset
+		    && pa->start < l->ext_offset + cxt->first_lba)
+			fdisk_set_first_lba(cxt, 1);
+
+		start = l->ext_offset + cxt->first_lba;
+	}
+
+	if (fdisk_use_cylinders(cxt))
+		for (i = 0; i < cxt->label->nparts_max; i++) {
+			first[i] = (fdisk_cround(cxt, first[i]) - 1)
+				* fdisk_get_units_per_sector(cxt);
+		}
+
+	/*
+	 * Ask for first sector
+	 */
+	do {
+		fdisk_sector_t dflt, aligned;
+
+		temp = start;
+		dflt = start = get_unused_start(cxt, n, start, first, last);
+
+		if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
+		    && cxt->first_lba > 1
+		    && temp == start - cxt->first_lba) {
+			fdisk_set_first_lba(cxt, 1);
+			start = pa->start;
+		}
+
+		/* the default sector should be aligned and unused */
+		do {
+			aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
+			dflt = get_unused_start(cxt, n, aligned, first, last);
+		} while (dflt != aligned && dflt > aligned && dflt < limit);
+
+		if (dflt >= limit)
+			dflt = start;
+		if (start > limit)
+			break;
+		if (start >= temp + fdisk_get_units_per_sector(cxt)
+		    && read) {
+			fdisk_info(cxt, _("Sector %llu is already allocated."),
+					temp);
+			temp = start;
+			read = 0;
+			if (pa && (fdisk_partition_has_start(pa) ||
+				   pa->start_follow_default))
+				break;
+		}
+
+		if (!read && start == temp) {
+			rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
+			if (rc)
+				return rc;
+			read = 1;
+		}
+	} while (start != temp || !read);
+
+	if (n == 4) {
+		/* The first EBR is stored at begin of the extended partition */
+		struct pte *pe = self_pte(cxt, n);
+		pe->offset = l->ext_offset;
+
+	} else if (n > 4) {
+		/* The second (and another) EBR */
+		struct pte *pe = self_pte(cxt, n);
+
+		pe->offset = start - cxt->first_lba;
+		if (pe->offset == l->ext_offset) { /* must be corrected */
+			pe->offset++;
+			if (cxt->first_lba == 1)
+				start++;
+		}
+	}
+
+	limit = get_unused_last(cxt, n, start, first, last);
+
+	if (start > limit) {
+		fdisk_info(cxt, _("No free sectors available."));
+		if (n > 4)
+			cxt->label->nparts_max--;
+		return -ENOSPC;
+	}
+
+	/*
+	 * Ask for last sector
+	 */
+	if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
+		stop = limit;
+	else if (pa && pa->end_follow_default)
+		stop = limit;
+	else if (pa && fdisk_partition_has_size(pa)) {
+		stop = start + pa->size - 1;
+		isrel = pa->size_explicit ? 0 : 1;
+	} else {
+		/* ask user by dialog */
+		struct fdisk_ask *ask = fdisk_new_ask();
+
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+		if (fdisk_use_cylinders(cxt)) {
+			fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
+			fdisk_ask_number_set_unit(ask,
+				     cxt->sector_size *
+				     fdisk_get_units_per_sector(cxt));
+		} else {
+			fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
+			fdisk_ask_number_set_unit(ask,cxt->sector_size);
+		}
+
+		fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
+		fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
+		fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
+		fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start));	/* base for relative input */
+
+		rc = fdisk_do_ask(cxt, ask);
+		stop = fdisk_ask_number_get_result(ask);
+		isrel = fdisk_ask_number_is_relative(ask);
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt)) {
+			stop = stop * fdisk_get_units_per_sector(cxt) - 1;
+			if (stop >limit)
+				stop = limit;
+		}
+	}
+
+	DBG(LABEL, ul_debug("DOS: raw stop: %ju", (uintmax_t) stop));
+
+	if (stop > limit)
+		stop = limit;
+
+	if (stop < limit) {
+		if (isrel && alignment_required(cxt)) {
+			/* the last sector has not been exactly requested (but
+			 * defined by +size{K,M,G} convention), so be smart and
+			 * align the end of the partition. The next partition
+			 * will start at phy.block boundary.
+			 */
+			stop = fdisk_align_lba_in_range(cxt, stop, start, limit) - 1;
+			if (stop > limit)
+				stop = limit;
+		}
+	}
+
+	set_partition(cxt, n, 0, start, stop, sys, pa && pa->boot == 1 ? 1 : 0);
+	if (n > 4) {
+		struct pte *pe = self_pte(cxt, n);
+		set_partition(cxt, n - 1, 1, pe->offset, stop,
+					MBR_DOS_EXTENDED_PARTITION, 0);
+	}
+
+	/* report */
+	{
+		struct fdisk_parttype *t =
+			fdisk_label_get_parttype_from_code(cxt->label, sys);
+		fdisk_info_new_partition(cxt, n + 1, start, stop, t);
+		fdisk_unref_parttype(t);
+	}
+
+
+	if (IS_EXTENDED(sys)) {
+		struct pte *pen = self_pte(cxt, n);
+
+		l->ext_index = n;
+		l->ext_offset = start;
+		pen->ex_entry = p;
+	}
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int add_logical(struct fdisk_context *cxt,
+		       struct fdisk_partition *pa,
+		       size_t *partno)
+{
+	struct pte *pe;
+	int rc;
+
+	assert(cxt);
+	assert(partno);
+	assert(cxt->label);
+	assert(self_label(cxt)->ext_offset);
+
+	DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
+	pe = self_pte(cxt, cxt->label->nparts_max);
+
+	if (!pe->sectorbuffer) {
+		pe->sectorbuffer = calloc(1, cxt->sector_size);
+		if (!pe->sectorbuffer)
+			return -ENOMEM;
+		DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
+					cxt->label->nparts_max, pe->sectorbuffer));
+		pe->private_sectorbuffer = 1;
+	}
+	pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
+	pe->ex_entry = pe->pt_entry + 1;
+	pe->offset = 0;
+	partition_set_changed(cxt, cxt->label->nparts_max, 1);
+
+	cxt->label->nparts_max++;
+
+	/* this message makes sense only when we use extended/primary/logical
+	 * dialog. The dialog is disable for scripts, see dos_add_partition() */
+	if (!cxt->script)
+		fdisk_info(cxt, _("Adding logical partition %zu"),
+				cxt->label->nparts_max);
+	*partno = cxt->label->nparts_max - 1;
+	rc = add_partition(cxt, *partno, pa);
+
+	if (rc) {
+		/* reset on error */
+		cxt->label->nparts_max--;
+		pe->pt_entry = NULL;
+		pe->ex_entry = NULL;
+		pe->offset = 0;
+		pe->changed = 0;
+	}
+
+	return rc;
+}
+
+static void check(struct fdisk_context *cxt, size_t n,
+	   unsigned int h, unsigned int s, unsigned int c,
+	   unsigned int start)
+{
+	unsigned int total, real_s, real_c;
+
+	if (!is_dos_compatible(cxt))
+		return;
+
+	real_s = sector(s) - 1;
+	real_c = cylinder(s, c);
+	total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
+
+	if (!total)
+		fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
+	if (h >= cxt->geom.heads)
+		fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
+				   "maximum %d"), n, h + 1, cxt->geom.heads);
+	if (real_s >= cxt->geom.sectors)
+		fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
+				   "maximum %llu"), n, s, cxt->geom.sectors);
+	if (real_c >= cxt->geom.cylinders)
+		fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
+				   "maximum %llu"),
+				n, real_c + 1,
+				cxt->geom.cylinders);
+
+	if (cxt->geom.cylinders <= 1024 && start != total)
+		fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
+				   "disagrees with total %u"), n, start, total);
+}
+
+/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
+ * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
+ * Jan.  1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
+ * Lubkin Oct.  1991). */
+
+static void
+long2chs(struct fdisk_context *cxt, unsigned long ls,
+	 unsigned int *c, unsigned int *h, unsigned int *s) {
+	int spc = cxt->geom.heads * cxt->geom.sectors;
+
+	*c = ls / spc;
+	ls = ls % spc;
+	*h = ls / cxt->geom.sectors;
+	*s = ls % cxt->geom.sectors + 1;	/* sectors count from 1 */
+}
+
+static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
+			      size_t partition)
+{
+	unsigned int pbc, pbh, pbs;	/* physical beginning c, h, s */
+	unsigned int pec, peh, pes;	/* physical ending c, h, s */
+	unsigned int lbc, lbh, lbs;	/* logical beginning c, h, s */
+	unsigned int lec, leh, les;	/* logical ending c, h, s */
+
+	if (!is_dos_compatible(cxt))
+		return;
+
+	if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
+		return;		/* do not check extended partitions */
+
+	/* physical beginning c, h, s */
+	pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
+	pbh = p->bh;
+	pbs = p->bs & 0x3f;
+
+	/* physical ending c, h, s */
+	pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
+	peh = p->eh;
+	pes = p->es & 0x3f;
+
+	/* compute logical beginning (c, h, s) */
+	long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
+
+	/* compute logical ending (c, h, s) */
+	long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
+
+	/* Same physical / logical beginning? */
+	if (cxt->geom.cylinders <= 1024
+	    && (pbc != lbc || pbh != lbh || pbs != lbs)) {
+		fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
+			"beginnings (non-Linux?): "
+			"phys=(%d, %d, %d), logical=(%d, %d, %d)"),
+			partition + 1,
+			pbc, pbh, pbs,
+			lbc, lbh, lbs);
+	}
+
+	/* Same physical / logical ending? */
+	if (cxt->geom.cylinders <= 1024
+	    && (pec != lec || peh != leh || pes != les)) {
+		fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
+			"endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
+			partition + 1,
+			pec, peh, pes,
+			lec, leh, les);
+	}
+
+	/* Ending on cylinder boundary? */
+	if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
+		fdisk_warnx(cxt, _("Partition %zu: does not end on "
+				   "cylinder boundary."),
+			partition + 1);
+	}
+}
+
+static int dos_verify_disklabel(struct fdisk_context *cxt)
+{
+	size_t i, j;
+	fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
+	fdisk_sector_t first[cxt->label->nparts_max],
+		       last[cxt->label->nparts_max];
+	struct dos_partition *p;
+	struct fdisk_dos_label *l = self_label(cxt);
+
+	assert(fdisk_is_label(cxt, DOS));
+
+	fill_bounds(cxt, first, last);
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		p = self_partition(cxt, i);
+		if (is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
+			check_consistency(cxt, p, i);
+			if (get_abs_partition_start(pe) < first[i])
+				fdisk_warnx(cxt, _(
+					"Partition %zu: bad start-of-data."),
+					 i + 1);
+
+			check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
+			total += last[i] + 1 - first[i];
+
+			if (i == 0)
+				total += get_abs_partition_start(pe) - 1;
+
+			for (j = 0; j < i; j++) {
+				if ((first[i] >= first[j] && first[i] <= last[j])
+				    || ((last[i] <= last[j] && last[i] >= first[j]))) {
+
+					fdisk_warnx(cxt, _("Partition %zu: "
+						"overlaps partition %zu."),
+						j + 1, i + 1);
+
+					total += first[i] >= first[j] ?
+						first[i] : first[j];
+					total -= last[i] <= last[j] ?
+						last[i] : last[j];
+				}
+			}
+		}
+	}
+
+	if (l->ext_offset) {
+		fdisk_sector_t e_last;
+		struct pte *ext_pe = self_pte(cxt, l->ext_index);
+
+		e_last = get_abs_partition_end(ext_pe);
+
+		for (i = 4; i < cxt->label->nparts_max; i++) {
+			total++;
+			p = self_partition(cxt, i);
+
+			if (!p->sys_ind) {
+				if (i != 4 || i + 1 < cxt->label->nparts_max)
+					fdisk_warnx(cxt,
+						_("Partition %zu: empty."),
+						i + 1);
+			} else if (first[i] < l->ext_offset
+				   || last[i] > e_last) {
+
+				fdisk_warnx(cxt, _("Logical partition %zu: "
+					"not entirely in partition %zu."),
+					i + 1, l->ext_index + 1);
+			}
+		}
+	}
+
+	if (total > n_sectors)
+		fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
+			"than the maximum %llu."), total, n_sectors);
+	else if (total < n_sectors)
+		fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
+			"sectors."), n_sectors - total, cxt->sector_size);
+
+	return 0;
+}
+
+/*
+ * Ask the user for new partition type information (logical, extended).
+ * This function calls the actual partition adding logic - add_partition.
+ *
+ * API callback.
+ */
+static int dos_add_partition(struct fdisk_context *cxt,
+			     struct fdisk_partition *pa,
+			     size_t *partno)
+{
+	size_t i, free_primary = 0, free_sectors = 0;
+	fdisk_sector_t last = 0, grain;
+	int rc = 0;
+	struct fdisk_dos_label *l;
+	struct pte *ext_pe;
+	size_t res;		/* partno */
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	DBG(LABEL, ul_debug("DOS: new partition wanted"));
+
+	l = self_label(cxt);
+	ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+	/*
+	 * partition template (@pa) based partitioning
+	 */
+
+	/* pa specifies start within extended partition, add logical */
+	if (pa && fdisk_partition_has_start(pa) && ext_pe
+	    && pa->start >= l->ext_offset
+	    && pa->start <= get_abs_partition_end(ext_pe)) {
+		DBG(LABEL, ul_debug("DOS: pa template %p: add logical", pa));
+		rc = add_logical(cxt, pa, &res);
+		goto done;
+
+	/* pa specifies that extended partition is wanted */
+	} else if (pa && pa->type && pa->type->code == MBR_DOS_EXTENDED_PARTITION) {
+		DBG(LABEL, ul_debug("DOS: pa template %p: add extened", pa));
+		if (l->ext_offset) {
+			fdisk_warnx(cxt, _("Extended partition already exists."));
+			return -EINVAL;
+		}
+		rc = get_partition_unused_primary(cxt, pa, &res);
+		if (rc == 0) {
+			rc = add_partition(cxt, res, pa);
+			goto done;
+		}
+
+	/* pa specifies start, but outside extended partition */
+	} else if (pa && fdisk_partition_has_start(pa) && l->ext_offset) {
+		DBG(LABEL, ul_debug("DOS: pa template %p: add primary", pa));
+		rc = get_partition_unused_primary(cxt, pa, &res);
+		if (rc == 0) {
+			rc = add_partition(cxt, res, pa);
+			goto done;
+		}
+	}
+
+	/*
+	 * dialog driven partitioning (it does not mean that @pa template is
+	 * completely ignored!)
+	 */
+
+	/* check if there is space for primary partition */
+	grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
+	last = cxt->first_lba;
+
+	for (i = 0; i < 4; i++) {
+		struct dos_partition *p = self_partition(cxt, i);
+
+		if (is_used_partition(p)) {
+			fdisk_sector_t start = dos_partition_get_start(p);
+			if (last + grain <= start)
+				free_sectors = 1;
+			last = start + dos_partition_get_size(p);
+		} else
+			free_primary++;
+	}
+	if (last + grain < cxt->total_sectors - 1)
+		free_sectors = 1;
+
+	if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
+		fdisk_info(cxt, _("The maximum number of partitions has "
+				  "been created."));
+		return -EINVAL;
+	}
+	rc = 1;
+
+	if (!free_primary || !free_sectors) {
+		DBG(LABEL, ul_debug("DOS: primary impossible, add logical"));
+		if (l->ext_offset) {
+			if (!pa || fdisk_partition_has_start(pa)) {
+				if (!free_primary)
+					fdisk_info(cxt, _("All primary partitions are in use."));
+				else if (!free_sectors)
+					fdisk_info(cxt, _("All space for primary partitions is in use."));
+			}
+			rc = add_logical(cxt, pa, &res);
+		} else {
+			fdisk_info(cxt,
+			     _(	"Impossible to create another primary partition. "
+				"If you want to create more partitions, you must "
+				"replace a primary partition with an extended "
+				"partition first."));
+			return -EINVAL;
+		}
+	} else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
+		fdisk_info(cxt, _("All logical partitions are in use. "
+				  "Adding a primary partition."));
+		rc = get_partition_unused_primary(cxt, pa, &res);
+		if (rc == 0)
+			rc = add_partition(cxt, res, pa);
+	} else {
+		char hint[BUFSIZ];
+		struct fdisk_ask *ask;
+		int c;
+
+		/* the default layout for scripts is to create primary partitions */
+		if (cxt->script) {
+			rc = get_partition_unused_primary(cxt, pa, &res);
+			if (rc == 0)
+				rc = add_partition(cxt, res, pa);
+			goto done;
+		}
+
+		ask = fdisk_new_ask();
+		if (!ask)
+			return -ENOMEM;
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
+		fdisk_ask_set_query(ask, _("Partition type"));
+		fdisk_ask_menu_set_default(ask, free_primary == 1
+						&& !l->ext_offset ? 'e' : 'p');
+		snprintf(hint, sizeof(hint),
+				_("%zu primary, %d extended, %zu free"),
+				4 - (l->ext_offset ? 1 : 0) - free_primary,
+				l->ext_offset ? 1 : 0,
+				free_primary);
+
+		fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
+		if (!l->ext_offset)
+			fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
+		else
+			fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
+
+		rc = fdisk_do_ask(cxt, ask);
+		if (rc)
+			return rc;
+		fdisk_ask_menu_get_result(ask, &c);
+		fdisk_unref_ask(ask);
+
+		if (c == 'p') {
+			rc = get_partition_unused_primary(cxt, pa, &res);
+			if (rc == 0)
+				rc = add_partition(cxt, res, pa);
+			goto done;
+		} else if (c == 'l' && l->ext_offset) {
+			rc = add_logical(cxt, pa, &res);
+			goto done;
+		} else if (c == 'e' && !l->ext_offset) {
+			rc = get_partition_unused_primary(cxt, pa, &res);
+			if (rc == 0) {
+				struct fdisk_partition *xpa = NULL;
+				struct fdisk_parttype *t;
+
+				t = fdisk_label_get_parttype_from_code(cxt->label,
+						MBR_DOS_EXTENDED_PARTITION);
+				if (!pa) {
+					pa = xpa = fdisk_new_partition();
+					if (!xpa)
+						return -ENOMEM;
+				}
+				fdisk_partition_set_type(pa, t);
+				rc = add_partition(cxt, res, pa);
+				if (xpa) {
+					fdisk_unref_partition(xpa);
+					pa = NULL;
+				}
+			}
+			goto done;
+		} else
+			fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
+	}
+done:
+	if (rc == 0) {
+		cxt->label->nparts_cur++;
+		if (partno)
+			*partno = res;
+	}
+	return rc;
+}
+
+static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
+			       unsigned char *buf)
+{
+	int rc;
+
+	rc = seek_sector(cxt, secno);
+	if (rc != 0) {
+		fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
+				(uintmax_t) secno);
+		return rc;
+	}
+
+	DBG(LABEL, ul_debug("DOS: writting to sector %ju", (uintmax_t) secno));
+
+	if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
+		return -errno;
+	return 0;
+}
+
+static int dos_write_disklabel(struct fdisk_context *cxt)
+{
+	struct fdisk_dos_label *l = self_label(cxt);
+	size_t i;
+	int rc = 0, mbr_changed = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	mbr_changed = l->non_pt_changed;
+
+	/* MBR (primary partitions) */
+	if (!mbr_changed) {
+		for (i = 0; i < 4; i++) {
+			struct pte *pe = self_pte(cxt, i);
+			if (pe->changed)
+				mbr_changed = 1;
+		}
+	}
+	if (mbr_changed) {
+		mbr_set_magic(cxt->firstsector);
+		rc = write_sector(cxt, 0, cxt->firstsector);
+		if (rc)
+			goto done;
+	}
+
+	if (cxt->label->nparts_max <= 4 && l->ext_offset) {
+		/* we have empty extended partition, check if the partition has
+		 * been modified and then cleanup possible remaining EBR  */
+		struct pte *pe = self_pte(cxt, l->ext_index);
+		unsigned char empty[512] = { 0 };
+		fdisk_sector_t off = pe ? get_abs_partition_start(pe) : 0;
+
+		if (off && pe->changed) {
+			mbr_set_magic(empty);
+			write_sector(cxt, off, empty);
+		}
+	}
+
+	/* EBR (logical partitions) */
+	for (i = 4; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		if (!pe->changed || !pe->offset || !pe->sectorbuffer)
+			continue;
+
+		mbr_set_magic(pe->sectorbuffer);
+		rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
+		if (rc)
+			goto done;
+	}
+
+done:
+	return rc;
+}
+
+static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
+		const char **name, off_t *offset, size_t *size)
+{
+	assert(cxt);
+
+	*name = NULL;
+	*offset = 0;
+	*size = 0;
+
+	switch (n) {
+	case 0:
+		*name = "MBR";
+		*offset = 0;
+		*size = 512;
+		break;
+	default:
+		/* extended partitions */
+		if (n - 1 + 4 < cxt->label->nparts_max) {
+			struct pte *pe = self_pte(cxt, n - 1 + 4);
+
+			assert(pe->private_sectorbuffer);
+
+			*name = "EBR";
+			*offset = pe->offset * cxt->sector_size;
+			*size = 512;
+		} else
+			return 1;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Check whether partition entries are ordered by their starting positions.
+ * Return 0 if OK. Return i if partition i should have been earlier.
+ * Two separate checks: primary and logical partitions.
+ */
+static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
+{
+	size_t last_p_start_pos = 0, p_start_pos;
+	size_t i, last_i = 0;
+
+	for (i = 0 ; i < cxt->label->nparts_max; i++) {
+
+		struct pte *pe = self_pte(cxt, i);
+		struct dos_partition *p = pe->pt_entry;
+
+		if (i == 4) {
+			last_i = 4;
+			last_p_start_pos = 0;
+		}
+		if (is_used_partition(p)) {
+			p_start_pos = get_abs_partition_start(pe);
+
+			if (last_p_start_pos > p_start_pos) {
+				if (prev)
+					*prev = last_i;
+				return i;
+			}
+
+			last_p_start_pos = p_start_pos;
+			last_i = i;
+		}
+	}
+	return 0;
+}
+
+static int dos_list_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	return 0;
+}
+
+static int dos_get_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct dos_partition *p;
+	struct pte *pe;
+	struct fdisk_dos_label *lb;
+
+	assert(cxt);
+	assert(pa);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	lb = self_label(cxt);
+	pe = self_pte(cxt, n);
+	p = pe->pt_entry;
+	pa->used = !is_cleared_partition(p);
+	if (!pa->used)
+		return 0;
+
+	pa->type = dos_partition_parttype(cxt, p);
+	pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
+	pa->start = get_abs_partition_start(pe);
+	pa->size = dos_partition_get_size(p);
+	pa->container = lb->ext_offset && n == lb->ext_index;
+
+	if (n >= 4)
+		pa->parent_partno = lb->ext_index;
+
+	if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
+		return -ENOMEM;
+
+	/* start C/H/S */
+	if (asprintf(&pa->start_chs, "%d/%d/%d",
+				cylinder(p->bs, p->bc),
+				sector(p->bs),
+				p->bh) < 0)
+		return -ENOMEM;
+
+	/* end C/H/S */
+	if (asprintf(&pa->end_chs, "%d/%d/%d",
+				cylinder(p->es, p->ec),
+				sector(p->es),
+				p->eh) < 0)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int dos_set_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct fdisk_dos_label *l;
+	struct dos_partition *p;
+	struct pte *pe;
+	fdisk_sector_t start, size;
+
+	assert(cxt);
+	assert(pa);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	if (n >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	if (pa->type && IS_EXTENDED(pa->type->code)) {
+		fdisk_warnx(cxt, _("You cannot change a partition into an "
+			"extended one or vice versa. Delete it first."));
+		return -EINVAL;
+	}
+
+	if (pa->type && !pa->type->code)
+		fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
+				   "Having partitions of type 0 is probably unwise."));
+	l = self_label(cxt);
+	p = self_partition(cxt, n);
+	pe = self_pte(cxt, n);
+
+	FDISK_INIT_UNDEF(start);
+	FDISK_INIT_UNDEF(size);
+
+	if (fdisk_partition_has_start(pa))
+		start = pa->start;
+	if (fdisk_partition_has_size(pa))
+		size = pa->size;
+
+	if (pa->end_follow_default) {
+		fdisk_sector_t first[cxt->label->nparts_max],
+			 last[cxt->label->nparts_max],
+			 xlast;
+		struct pte *ext = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL; 
+
+		fill_bounds(cxt, first, last);
+
+		if (ext && l->ext_offset) {
+			first[l->ext_index] = l->ext_offset;
+			last[l->ext_index] = get_abs_partition_end(ext);
+		}
+		if (FDISK_IS_UNDEF(start))
+			start = get_abs_partition_start(pe);
+
+		DBG(LABEL, ul_debug("DOS: #%zu    now %ju +%ju sectors",
+			n, (uintmax_t) start, (uintmax_t) dos_partition_get_size(p)));
+
+		xlast = get_unused_last(cxt, n, start, first, last);
+		size = xlast ? xlast - start + 1: dos_partition_get_size(p);
+
+		DBG(LABEL, ul_debug("DOS: #%zu wanted %ju +%ju sectors",
+			n, (uintmax_t) start, (uintmax_t) size));
+	}
+
+	if (!FDISK_IS_UNDEF(start) || !FDISK_IS_UNDEF(size)) {
+		DBG(LABEL, ul_debug("DOS: resize partition"));
+
+		if (FDISK_IS_UNDEF(start))
+			start = get_abs_partition_start(pe);
+		if (FDISK_IS_UNDEF(size))
+			size = dos_partition_get_size(p);
+
+		set_partition(cxt, n, 0, start, start + size - 1,
+				pa->type  ? pa->type->code : p->sys_ind,
+				pa->boot == 1);
+	} else {
+		DBG(LABEL, ul_debug("DOS: keep size, modify properties"));
+		if (pa->type)
+			p->sys_ind = pa->type->code;
+		if (!FDISK_IS_UNDEF(pa->boot))
+			p->boot_ind = pa->boot == 1 ? ACTIVE_FLAG : 0;
+	}
+
+	partition_set_changed(cxt, n, 1);
+	return 0;
+}
+
+static void print_chain_of_logicals(struct fdisk_context *cxt)
+{
+	size_t i;
+	struct fdisk_dos_label *l = self_label(cxt);
+
+	fputc('\n', stdout);
+
+	for (i = 4; i < cxt->label->nparts_max; i++) {
+		struct pte *pe = self_pte(cxt, i);
+
+		fprintf(stderr, "#%02zu EBR [%10ju], "
+			"data[start=%10ju (%10ju), size=%10ju], "
+			"link[start=%10ju (%10ju), size=%10ju]\n",
+			i, (uintmax_t) pe->offset,
+			/* data */
+			(uintmax_t) dos_partition_get_start(pe->pt_entry),
+			(uintmax_t) get_abs_partition_start(pe),
+			(uintmax_t) dos_partition_get_size(pe->pt_entry),
+			/* link */
+			(uintmax_t) dos_partition_get_start(pe->ex_entry),
+			(uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
+			(uintmax_t) dos_partition_get_size(pe->ex_entry));
+	}
+}
+
+static int cmp_ebr_offsets(const void *a, const void *b)
+{
+	struct pte *ae = (struct pte *) a,
+		   *be = (struct pte *) b;
+
+	if (ae->offset == 0 && be->offset == 0)
+		return 0;
+	if (ae->offset == 0)
+		return 1;
+	if (be->offset == 0)
+		return -1;
+
+	return cmp_numbers(ae->offset, be->offset);
+}
+
+/*
+ * Fix the chain of logicals.
+ *
+ * The function does not modify data partitions within EBR tables
+ * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
+ * (pte->ex_entry) between EBR tables.
+ *
+ */
+static void fix_chain_of_logicals(struct fdisk_context *cxt)
+{
+	struct fdisk_dos_label *l = self_label(cxt);
+	struct pte *last;
+	size_t i;
+
+	DBG(LABEL, print_chain_of_logicals(cxt));
+
+	/* Sort chain by EBR offsets */
+	qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
+			cmp_ebr_offsets);
+
+again:
+	/* Sort data partitions by start */
+	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
+		struct pte *cur = self_pte(cxt, i),
+			   *nxt = self_pte(cxt, i + 1);
+
+		if (get_abs_partition_start(cur) >
+		    get_abs_partition_start(nxt)) {
+
+			struct dos_partition tmp = *cur->pt_entry;
+			fdisk_sector_t cur_start = get_abs_partition_start(cur),
+				 nxt_start = get_abs_partition_start(nxt);
+
+			/* swap data partitions */
+			*cur->pt_entry = *nxt->pt_entry;
+			*nxt->pt_entry = tmp;
+
+			/* Recount starts according to EBR offsets, the absolute
+			 * address tas to be still the same! */
+			dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
+			dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
+
+			partition_set_changed(cxt, i, 1);
+			partition_set_changed(cxt, i + 1, 1);
+			goto again;
+		}
+	}
+
+	/* Update EBR links */
+	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
+		struct pte *cur = self_pte(cxt, i),
+			   *nxt = self_pte(cxt, i + 1);
+
+		fdisk_sector_t noff = nxt->offset - l->ext_offset,
+			 ooff = dos_partition_get_start(cur->ex_entry);
+
+		if (noff == ooff)
+			continue;
+
+		DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
+			(uintmax_t) cur->offset,
+			(uintmax_t) ooff, (uintmax_t) noff));
+
+		set_partition(cxt, i, 1, nxt->offset,
+				get_abs_partition_end(nxt),
+				MBR_DOS_EXTENDED_PARTITION, 0);
+	}
+
+	/* always terminate the chain ! */
+	last = self_pte(cxt, cxt->label->nparts_max - 1);
+	if (last) {
+		clear_partition(last->ex_entry);
+		partition_set_changed(cxt, cxt->label->nparts_max - 1, 1);
+	}
+
+	DBG(LABEL, print_chain_of_logicals(cxt));
+}
+
+static int dos_reorder(struct fdisk_context *cxt)
+{
+	struct pte *pei, *pek;
+	size_t i,k;
+
+	if (!wrong_p_order(cxt, NULL)) {
+		fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
+		return 0;
+	}
+
+	while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
+		/* partition i should have come earlier, move it */
+		/* We have to move data in the MBR */
+		struct dos_partition *pi, *pk, *pe, pbuf;
+		pei = self_pte(cxt, i);
+		pek = self_pte(cxt, k);
+
+		pe = pei->ex_entry;
+		pei->ex_entry = pek->ex_entry;
+		pek->ex_entry = pe;
+
+		pi = pei->pt_entry;
+		pk = pek->pt_entry;
+
+		memmove(&pbuf, pi, sizeof(struct dos_partition));
+		memmove(pi, pk, sizeof(struct dos_partition));
+		memmove(pk, &pbuf, sizeof(struct dos_partition));
+
+		partition_set_changed(cxt, i, 1);
+		partition_set_changed(cxt, k, 1);
+	}
+
+	if (i)
+		fix_chain_of_logicals(cxt);
+
+	fdisk_info(cxt, _("Done."));
+	return 0;
+}
+
+/* TODO: use fdisk_set_partition() API */
+int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
+{
+	struct pte *pe;
+	struct dos_partition *p;
+	unsigned int new, free_start, curr_start, last;
+	uintmax_t res = 0;
+	size_t x;
+	int rc;
+
+	assert(cxt);
+	assert(fdisk_is_label(cxt, DOS));
+
+	pe = self_pte(cxt, i);
+	p = pe->pt_entry;
+
+	if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
+		fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
+		return 0;
+	}
+
+	/* the default start is at the second sector of the disk or at the
+	 * second sector of the extended partition
+	 */
+	free_start = pe->offset ? pe->offset + 1 : 1;
+
+	curr_start = get_abs_partition_start(pe);
+
+	/* look for a free space before the current start of the partition */
+	for (x = 0; x < cxt->label->nparts_max; x++) {
+		unsigned int end;
+		struct pte *prev_pe = self_pte(cxt, x);
+		struct dos_partition *prev_p = prev_pe->pt_entry;
+
+		if (!prev_p)
+			continue;
+		end = get_abs_partition_start(prev_pe)
+		      + dos_partition_get_size(prev_p);
+
+		if (is_used_partition(prev_p) &&
+		    end > free_start && end <= curr_start)
+			free_start = end;
+	}
+
+	last = get_abs_partition_end(pe);
+
+	rc = fdisk_ask_number(cxt, free_start, curr_start, last,
+			_("New beginning of data"), &res);
+	if (rc)
+		return rc;
+
+	new = res - pe->offset;
+
+	if (new != dos_partition_get_size(p)) {
+		unsigned int sects = dos_partition_get_size(p)
+				+ dos_partition_get_start(p) - new;
+
+		dos_partition_set_size(p, sects);
+		dos_partition_set_start(p, new);
+
+		partition_set_changed(cxt, i, 1);
+	}
+
+	return rc;
+}
+
+static int dos_partition_is_used(
+		struct fdisk_context *cxt,
+		size_t i)
+{
+	struct dos_partition *p;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	if (i >= cxt->label->nparts_max)
+		return 0;
+
+	p = self_partition(cxt, i);
+
+	return p && !is_cleared_partition(p);
+}
+
+static int dos_toggle_partition_flag(
+		struct fdisk_context *cxt,
+		size_t i,
+		unsigned long flag)
+{
+	struct dos_partition *p;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, DOS));
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	p = self_partition(cxt, i);
+
+	switch (flag) {
+	case DOS_FLAG_ACTIVE:
+		if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
+			fdisk_warnx(cxt, _("Partition %zu: is an extended "
+					"partition."), i + 1);
+
+		p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
+		partition_set_changed(cxt, i, 1);
+		fdisk_info(cxt,	p->boot_ind ?
+			_("The bootable flag on partition %zu is enabled now.") :
+			_("The bootable flag on partition %zu is disabled now."),
+			i + 1);
+		break;
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+static const struct fdisk_field dos_fields[] =
+{
+	/* basic */
+	{ FDISK_FIELD_DEVICE,	N_("Device"),	 10,	0 },
+	{ FDISK_FIELD_BOOT,	N_("Boot"),	  1,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+	{ FDISK_FIELD_TYPEID,	N_("Id"),	  2,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	0.1,	0 },
+
+	/* expert mode */
+	{ FDISK_FIELD_SADDR,	N_("Start-C/H/S"), 1,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_EADDR,	N_("End-C/H/S"),   1,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_ATTR,	N_("Attrs"),	   2,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
+
+};
+
+static const struct fdisk_label_operations dos_operations =
+{
+	.probe		= dos_probe_label,
+	.write		= dos_write_disklabel,
+	.verify		= dos_verify_disklabel,
+	.create		= dos_create_disklabel,
+	.locate		= dos_locate_disklabel,
+	.list		= dos_list_disklabel,
+	.reorder	= dos_reorder,
+	.get_id		= dos_get_disklabel_id,
+	.set_id		= dos_set_disklabel_id,
+
+	.get_part	= dos_get_partition,
+	.set_part	= dos_set_partition,
+	.add_part	= dos_add_partition,
+	.del_part	= dos_delete_partition,
+
+	.part_toggle_flag = dos_toggle_partition_flag,
+	.part_is_used	= dos_partition_is_used,
+
+	.reset_alignment = dos_reset_alignment,
+
+	.deinit		= dos_deinit,
+};
+
+/*
+ * allocates DOS in-memory stuff
+ */
+struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_dos_label *dos;
+
+	assert(cxt);
+
+	dos = calloc(1, sizeof(*dos));
+	if (!dos)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) dos;
+	lb->name = "dos";
+	lb->id = FDISK_DISKLABEL_DOS;
+	lb->op = &dos_operations;
+	lb->parttypes = dos_parttypes;
+	lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
+	lb->fields = dos_fields;
+	lb->nfields = ARRAY_SIZE(dos_fields);
+
+	return lb;
+}
+
+/**
+ * fdisk_dos_enable_compatible:
+ * @lb: DOS label (see fdisk_get_label())
+ * @enable: 0 or 1
+ *
+ * Enables deprecated DOS compatible mode, in this mode library checks for
+ * cylinders boundary, cases about CHS addressing and another obscure things.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
+{
+	struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
+
+	if (!lb)
+		return -EINVAL;
+
+	dos->compatible = enable;
+	if (enable)
+		lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+	return 0;
+}
+
+/**
+ * fdisk_dos_is_compatible:
+ * @lb: DOS label
+ *
+ * Returns: 0 if DOS compatibility disabled, 1 if enabled
+ */
+int fdisk_dos_is_compatible(struct fdisk_label *lb)
+{
+	return ((struct fdisk_dos_label *) lb)->compatible;
+}
diff --git a/libblkid/libfdisk/src/fdiskP.h b/libblkid/libfdisk/src/fdiskP.h
new file mode 100644
index 0000000..b169a9f
--- /dev/null
+++ b/libblkid/libfdisk/src/fdiskP.h
@@ -0,0 +1,438 @@
+/*
+ * fdiskP.h - private library header file
+ *
+ * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#ifndef _LIBFDISK_PRIVATE_H
+#define _LIBFDISK_PRIVATE_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+
+#include "c.h"
+#include "libfdisk.h"
+
+#include "nls.h"		/* temporary before dialog API will be implamented */
+#include "list.h"
+#include "debug.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+/* features */
+#define CONFIG_LIBFDISK_ASSERT
+
+#ifdef CONFIG_LIBFDISK_ASSERT
+#include <assert.h>
+#endif
+
+/*
+ * Debug
+ */
+#define LIBFDISK_DEBUG_HELP	(1 << 0)
+#define LIBFDISK_DEBUG_INIT	(1 << 1)
+#define LIBFDISK_DEBUG_CXT	(1 << 2)
+#define LIBFDISK_DEBUG_LABEL    (1 << 3)
+#define LIBFDISK_DEBUG_ASK      (1 << 4)
+#define LIBFDISK_DEBUG_PART	(1 << 6)
+#define LIBFDISK_DEBUG_PARTTYPE	(1 << 7)
+#define LIBFDISK_DEBUG_TAB	(1 << 8)
+#define LIBFDISK_DEBUG_SCRIPT	(1 << 9)
+#define LIBFDISK_DEBUG_ALL	0xFFFF
+
+UL_DEBUG_DECLARE_MASK(libfdisk);
+#define DBG(m, x)	__UL_DBG(libfdisk, LIBFDISK_DEBUG_, m, x)
+#define ON_DBG(m, x)	__UL_DBG_CALL(libfdisk, LIBFDISK_DEBUG_, m, x)
+#define DBG_FLUSH	__UL_DBG_FLUSH(libfdisk, LIBFDISK_DEBUG_)
+
+#ifdef TEST_PROGRAM
+struct fdisk_test {
+	const char	*name;
+	int		(*body)(struct fdisk_test *ts, int argc, char *argv[]);
+	const char	*usage;
+};
+
+/* test.c */
+extern int fdisk_run_test(struct fdisk_test *tests, int argc, char *argv[]);
+#endif
+
+
+/*
+ * Generic iterator
+ */
+struct fdisk_iter {
+        struct list_head        *p;		/* current position */
+        struct list_head        *head;		/* start position */
+	int			direction;	/* FDISK_ITER_{FOR,BACK}WARD */
+};
+
+#define IS_ITER_FORWARD(_i)	((_i)->direction == FDISK_ITER_FORWARD)
+#define IS_ITER_BACKWARD(_i)	((_i)->direction == FDISK_ITER_BACKWARD)
+
+#define FDISK_ITER_INIT(itr, list) \
+	do { \
+		(itr)->p = IS_ITER_FORWARD(itr) ? \
+				(list)->next : (list)->prev; \
+		(itr)->head = (list); \
+	} while(0)
+
+#define FDISK_ITER_ITERATE(itr, res, restype, member) \
+	do { \
+		res = list_entry((itr)->p, restype, member); \
+		(itr)->p = IS_ITER_FORWARD(itr) ? \
+				(itr)->p->next : (itr)->p->prev; \
+	} while(0)
+
+/*
+ * Partition types
+ */
+struct fdisk_parttype {
+	unsigned int	code;		/* type as number or zero */
+	char		*name;		/* description */
+	char		*typestr;	/* type as string or NULL */
+
+	unsigned int	flags;		/* FDISK_PARTTYPE_* flags */
+	int		refcount;	/* reference counter for allocated types */
+};
+
+enum {
+	FDISK_PARTTYPE_UNKNOWN		= (1 << 1),
+	FDISK_PARTTYPE_INVISIBLE	= (1 << 2),
+	FDISK_PARTTYPE_ALLOCATED	= (1 << 3)
+};
+
+#define fdisk_parttype_is_invisible(_x)	((_x) && ((_x)->flags & FDISK_PARTTYPE_INVISIBLE))
+#define fdisk_parttype_is_allocated(_x)	((_x) && ((_x)->flags & FDISK_PARTTYPE_ALLOCATED))
+
+struct fdisk_partition {
+	int		refcount;		/* reference counter */
+
+	size_t		partno;			/* partition number */
+	size_t		parent_partno;		/* for logical partitions */
+
+	fdisk_sector_t	start;			/* first sectors */
+	fdisk_sector_t	size;			/* size in sectors */
+
+	char		*name;			/* partition name */
+	char		*uuid;			/* partition UUID */
+	char		*attrs;			/* partition flags/attributes converted to string */
+	struct fdisk_parttype	*type;		/* partition type */
+
+	struct list_head	parts;		/* list of partitions */
+
+	/* extra fields for partition_to_string() */
+	char		start_post;		/* start postfix  (e.g. '+') */
+	char		end_post;		/* end postfix */
+	char		size_post;		/* size postfix */
+
+	uint64_t	fsize;			/* bsd junk */
+	uint64_t	bsize;
+	uint64_t	cpg;
+
+	char		*start_chs;		/* start C/H/S in string */
+	char		*end_chs;		/* end C/H/S in string */
+
+	unsigned int	boot;			/* MBR: bootable */
+
+	unsigned int	container : 1,			/* container partition (e.g. extended partition) */
+			end_follow_default : 1,		/* use default end */
+			freespace : 1,			/* this is free space */
+			partno_follow_default : 1,	/* use default partno */
+			size_explicit : 1,		/* don't align the size */
+			start_follow_default : 1,	/* use default start */
+			used : 1,			/* partition already used */
+			wholedisk : 1;			/* special system partition */
+};
+
+#define FDISK_INIT_UNDEF(_x)	((_x) = (__typeof__(_x)) -1)
+#define FDISK_IS_UNDEF(_x)	((_x) == (__typeof__(_x)) -1)
+
+struct fdisk_table {
+	struct list_head	parts;		/* partitions */
+	int			refcount;
+	size_t			nents;		/* number of partitions */
+};
+
+/*
+ * Legacy CHS based geometry
+ */
+struct fdisk_geometry {
+	unsigned int heads;
+	fdisk_sector_t sectors;
+	fdisk_sector_t cylinders;
+};
+
+/*
+ * Label specific operations
+ */
+struct fdisk_label_operations {
+	/* probe disk label */
+	int (*probe)(struct fdisk_context *cxt);
+	/* write in-memory changes to disk */
+	int (*write)(struct fdisk_context *cxt);
+	/* verify the partition table */
+	int (*verify)(struct fdisk_context *cxt);
+	/* create new disk label */
+	int (*create)(struct fdisk_context *cxt);
+	/* list disklabel details */
+	int (*list)(struct fdisk_context *cxt);
+	/* returns offset and size of the 'n' part of the PT */
+	int (*locate)(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size);
+	/* reorder partitions */
+	int (*reorder)(struct fdisk_context *cxt);
+
+	/* get disk label ID */
+	int (*get_id)(struct fdisk_context *cxt, char **id);
+	/* set disk label ID */
+	int (*set_id)(struct fdisk_context *cxt);
+
+
+	/* new partition */
+	int (*add_part)(struct fdisk_context *cxt, struct fdisk_partition *pa,
+						size_t *partno);
+	/* delete partition */
+	int (*del_part)(struct fdisk_context *cxt, size_t partnum);
+
+	/* fill in partition struct */
+	int (*get_part)(struct fdisk_context *cxt, size_t n,
+						struct fdisk_partition *pa);
+	/* modify partition */
+	int (*set_part)(struct fdisk_context *cxt, size_t n,
+						struct fdisk_partition *pa);
+
+	/* return state of the partition */
+	int (*part_is_used)(struct fdisk_context *cxt, size_t partnum);
+
+	int (*part_toggle_flag)(struct fdisk_context *cxt, size_t i, unsigned long flag);
+
+	/* refresh alignment setting */
+	int (*reset_alignment)(struct fdisk_context *cxt);
+
+	/* free in-memory label stuff */
+	void (*free)(struct fdisk_label *lb);
+
+	/* deinit in-memory label stuff */
+	void (*deinit)(struct fdisk_label *lb);
+};
+
+/*
+ * The fields describes how to display libfdisk_partition
+ */
+struct fdisk_field {
+	int		id;		/* FDISK_FIELD_* */
+	const char	*name;		/* field name */
+	double		width;		/* field width (compatible with libsmartcols whint) */
+	int		flags;		/* FDISK_FIELDFL_* */
+};
+
+/* note that the defauls is to display a column always */
+enum {
+	FDISK_FIELDFL_DETAIL	= (1 << 1),	/* only display if fdisk_is_details() */
+	FDISK_FIELDFL_EYECANDY	= (1 << 2),	/* don't display if fdisk_is_details() */
+	FDISK_FIELDFL_NUMBER	= (1 << 3),	/* column display numbers */
+};
+
+/*
+ * Generic label
+ */
+struct fdisk_label {
+	const char		*name;		/* label name */
+	enum fdisk_labeltype	id;		/* FDISK_DISKLABEL_* */
+	struct fdisk_parttype	*parttypes;	/* supported partitions types */
+	size_t			nparttypes;	/* number of items in parttypes[] */
+
+	size_t			nparts_max;	/* maximal number of partitions */
+	size_t			nparts_cur;	/* number of currently used partitions */
+
+	int			flags;		/* FDISK_LABEL_FL_* flags */
+
+	unsigned int		changed:1,	/* label has been modified */
+				disabled:1;	/* this driver is disabled at all */
+
+	const struct fdisk_field *fields;	/* all possible fields */
+	size_t			nfields;
+
+	const struct fdisk_label_operations *op;
+};
+
+
+/* label driver flags */
+enum {
+	FDISK_LABEL_FL_REQUIRE_GEOMETRY = (1 << 2),
+	FDISK_LABEL_FL_INCHARS_PARTNO   = (1 << 3)
+};
+
+/* label allocators */
+extern struct fdisk_label *fdisk_new_gpt_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt);
+
+
+struct ask_menuitem {
+	char	key;
+	const char	*name;
+	const char	*desc;
+
+	struct ask_menuitem *next;
+};
+
+/* fdisk dialog -- note that nothing from this stuff will be directly exported,
+ * we will have get/set() function for everything.
+ */
+struct fdisk_ask {
+	int		type;		/* FDISK_ASKTYPE_* */
+	char		*query;
+
+	int		refcount;
+
+	union {
+		/* FDISK_ASKTYPE_{NUMBER,OFFSET} */
+		struct ask_number {
+			uint64_t	hig;		/* high limit */
+			uint64_t	low;		/* low limit */
+			uint64_t	dfl;		/* default */
+			uint64_t	result;
+			uint64_t	base;		/* for relative results */
+			uint64_t	unit;		/* unit for offsets */
+			const char	*range;		/* by library generated list */
+			unsigned int	relative :1,
+					inchars  :1;
+		} num;
+		/* FDISK_ASKTYPE_{WARN,WARNX,..} */
+		struct ask_print {
+			const char	*mesg;
+			int		errnum;		/* errno */
+		} print;
+		/* FDISK_ASKTYPE_YESNO */
+		struct ask_yesno {
+			int		result;		/* TRUE or FALSE */
+		} yesno;
+		/* FDISK_ASKTYPE_STRING */
+		struct ask_string {
+			char		*result;	/* allocated */
+		} str;
+		/* FDISK_ASKTYPE_MENU */
+		struct ask_menu {
+			int		dfl;		/* default meni item */
+			int		result;
+			struct ask_menuitem *first;
+		} menu;
+	} data;
+};
+
+struct fdisk_context {
+	int dev_fd;         /* device descriptor */
+	char *dev_path;     /* device path */
+	int refcount;
+
+	unsigned char *firstsector; /* buffer with master boot record */
+	unsigned long firstsector_bufsz;
+
+	/* topology */
+	unsigned long io_size;		/* I/O size used by fdisk */
+	unsigned long optimal_io_size;	/* optional I/O returned by device */
+	unsigned long min_io_size;	/* minimal I/O size */
+	unsigned long phy_sector_size;	/* physical size */
+	unsigned long sector_size;	/* logical size */
+	unsigned long alignment_offset;
+
+	unsigned int readonly : 1,		/* don't write to the device */
+		     display_in_cyl_units : 1,	/* for obscure labels */
+		     display_details : 1,	/* expert display mode */
+		     listonly : 1;		/* list partition, nothing else */
+
+	/* alignment */
+	unsigned long grain;		/* alignment unit */
+	fdisk_sector_t first_lba;		/* recommended begin of the first partition */
+	fdisk_sector_t last_lba;		/* recomennded end of last partition */
+
+	/* geometry */
+	fdisk_sector_t total_sectors;	/* in logical sectors */
+	struct fdisk_geometry geom;
+
+	/* user setting to overwrite device default */
+	struct fdisk_geometry user_geom;
+	unsigned long user_pyh_sector;
+	unsigned long user_log_sector;
+
+	struct fdisk_label *label;	/* current label, pointer to labels[] */
+
+	size_t nlabels;			/* number of initialized label drivers */
+	struct fdisk_label *labels[8];	/* all supported labels,
+					 * FIXME: use any enum rather than hardcoded number */
+
+	int	(*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *);	/* fdisk dialogs callback */
+	void	*ask_data;		/* ask_cb() data */
+
+	struct fdisk_context	*parent;	/* for nested PT */
+	struct fdisk_script	*script;	/* what we want to follow */
+};
+
+/* partition.c */
+int fdisk_partition_next_partno(struct fdisk_partition *pa,
+				       struct fdisk_context *cxt,
+				       size_t *n);
+
+/* context.c */
+extern int __fdisk_switch_label(struct fdisk_context *cxt,
+				    struct fdisk_label *lb);
+extern int fdisk_missing_geometry(struct fdisk_context *cxt);
+
+/* alignment.c */
+fdisk_sector_t fdisk_scround(struct fdisk_context *cxt, fdisk_sector_t num);
+fdisk_sector_t fdisk_cround(struct fdisk_context *cxt, fdisk_sector_t num);
+
+extern int fdisk_discover_geometry(struct fdisk_context *cxt);
+extern int fdisk_discover_topology(struct fdisk_context *cxt);
+
+extern int fdisk_apply_user_device_properties(struct fdisk_context *cxt);
+extern void fdisk_zeroize_device_properties(struct fdisk_context *cxt);
+
+/* utils.c */
+extern int fdisk_init_firstsector_buffer(struct fdisk_context *cxt);
+extern int fdisk_read_firstsector(struct fdisk_context *cxt);
+extern char *fdisk_partname(const char *dev, size_t partno);
+
+/* label.c */
+extern int fdisk_probe_labels(struct fdisk_context *cxt);
+extern void fdisk_deinit_label(struct fdisk_label *lb);
+
+/* ask.c */
+struct fdisk_ask *fdisk_new_ask(void);
+void fdisk_reset_ask(struct fdisk_ask *ask);
+int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str);
+int fdisk_ask_set_type(struct fdisk_ask *ask, int type);
+int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask);
+int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range);
+int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt);
+int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low);
+int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high);
+int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base);
+int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit);
+int fdisk_ask_number_is_relative(struct fdisk_ask *ask);
+int fdisk_ask_menu_set_default(struct fdisk_ask *ask, int dfl);
+int fdisk_ask_menu_add_item(struct fdisk_ask *ask, int key,
+			const char *name, const char *desc);
+int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum);
+int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg);
+int fdisk_info_new_partition(
+			struct fdisk_context *cxt,
+			int num, fdisk_sector_t start, fdisk_sector_t stop,
+			struct fdisk_parttype *t);
+
+/* dos.c */
+extern struct dos_partition *fdisk_dos_get_partition(
+				struct fdisk_context *cxt,
+				size_t i);
+
+#endif /* _LIBFDISK_PRIVATE_H */
diff --git a/libblkid/libfdisk/src/gpt.c b/libblkid/libfdisk/src/gpt.c
new file mode 100644
index 0000000..8c1c96c
--- /dev/null
+++ b/libblkid/libfdisk/src/gpt.c
@@ -0,0 +1,2565 @@
+/*
+ * Copyright (C) 2007 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * GUID Partition Table (GPT) support. Based on UEFI Specs 2.3.1
+ * Chapter 5: GUID Partition Table (GPT) Disk Layout (Jun 27th, 2012).
+ * Some ideas and inspiration from GNU parted and gptfdisk.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <uuid.h>
+
+#include "fdiskP.h"
+
+#include "nls.h"
+#include "crc32.h"
+#include "blkdev.h"
+#include "bitops.h"
+#include "strutils.h"
+#include "all-io.h"
+
+/**
+ * SECTION: gpt
+ * @title: UEFI GPT
+ * @short_description: specific functionality
+ */
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645LL /* EFI PART */
+#define GPT_HEADER_REVISION_V1_02 0x00010200
+#define GPT_HEADER_REVISION_V1_00 0x00010000
+#define GPT_HEADER_REVISION_V0_99 0x00009900
+#define GPT_HEADER_MINSZ          92 /* bytes */
+
+#define GPT_PMBR_LBA        0
+#define GPT_MBR_PROTECTIVE  1
+#define GPT_MBR_HYBRID      2
+
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 0x00000001
+
+#define EFI_PMBR_OSTYPE     0xEE
+#define MSDOS_MBR_SIGNATURE 0xAA55
+#define GPT_PART_NAME_LEN   (72 / sizeof(uint16_t))
+#define GPT_NPARTITIONS     128
+
+/* Globally unique identifier */
+struct gpt_guid {
+	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];
+};
+
+
+/* only checking that the GUID is 0 is enough to verify an empty partition. */
+#define GPT_UNUSED_ENTRY_GUID						\
+	((struct gpt_guid) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00,	\
+			     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
+
+/* Linux native partition type */
+#define GPT_DEFAULT_ENTRY_TYPE "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
+
+/*
+ * Attribute bits
+ */
+enum {
+	/* UEFI specific */
+	GPT_ATTRBIT_REQ      = 0,
+	GPT_ATTRBIT_NOBLOCK  = 1,
+	GPT_ATTRBIT_LEGACY   = 2,
+
+	/* GUID specific (range 48..64)*/
+	GPT_ATTRBIT_GUID_FIRST	= 48,
+	GPT_ATTRBIT_GUID_COUNT	= 16
+};
+
+#define GPT_ATTRSTR_REQ		"RequiredPartiton"
+#define GPT_ATTRSTR_NOBLOCK	"NoBlockIOProtocol"
+#define GPT_ATTRSTR_LEGACY	"LegacyBIOSBootable"
+
+/* The GPT Partition entry array contains an array of GPT entries. */
+struct gpt_entry {
+	struct gpt_guid     type; /* purpose and type of the partition */
+	struct gpt_guid     partition_guid;
+	uint64_t            lba_start;
+	uint64_t            lba_end;
+	uint64_t            attrs;
+	uint16_t            name[GPT_PART_NAME_LEN];
+}  __attribute__ ((packed));
+
+/* GPT header */
+struct gpt_header {
+	uint64_t            signature; /* header identification */
+	uint32_t            revision; /* header version */
+	uint32_t            size; /* in bytes */
+	uint32_t            crc32; /* header CRC checksum */
+	uint32_t            reserved1; /* must be 0 */
+	uint64_t            my_lba; /* LBA of block that contains this struct (LBA 1) */
+	uint64_t            alternative_lba; /* backup GPT header */
+	uint64_t            first_usable_lba; /* first usable logical block for partitions */
+	uint64_t            last_usable_lba; /* last usable logical block for partitions */
+	struct gpt_guid     disk_guid; /* unique disk identifier */
+	uint64_t            partition_entry_lba; /* LBA of start of partition entries array */
+	uint32_t            npartition_entries; /* total partition entries - normally 128 */
+	uint32_t            sizeof_partition_entry; /* bytes for each GUID pt */
+	uint32_t            partition_entry_array_crc32; /* partition CRC checksum */
+	uint8_t             reserved2[512 - 92]; /* must all be 0 */
+} __attribute__ ((packed));
+
+struct gpt_record {
+	uint8_t             boot_indicator; /* unused by EFI, set to 0x80 for bootable */
+	uint8_t             start_head; /* unused by EFI, pt start in CHS */
+	uint8_t             start_sector; /* unused by EFI, pt start in CHS */
+	uint8_t             start_track;
+	uint8_t             os_type; /* EFI and legacy non-EFI OS types */
+	uint8_t             end_head; /* unused by EFI, pt end in CHS */
+	uint8_t             end_sector; /* unused by EFI, pt end in CHS */
+	uint8_t             end_track; /* unused by EFI, pt end in CHS */
+	uint32_t            starting_lba; /* used by EFI - start addr of the on disk pt */
+	uint32_t            size_in_lba; /* used by EFI - size of pt in LBA */
+} __attribute__ ((packed));
+
+/* Protected MBR and legacy MBR share same structure */
+struct gpt_legacy_mbr {
+	uint8_t             boot_code[440];
+	uint32_t            unique_mbr_signature;
+	uint16_t            unknown;
+	struct gpt_record   partition_record[4];
+	uint16_t            signature;
+} __attribute__ ((packed));
+
+/*
+ * Here be dragons!
+ * See: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
+ */
+#define DEF_GUID(_u, _n) \
+	{ \
+		.typestr = (_u), \
+		.name = (_n),    \
+	}
+
+static struct fdisk_parttype gpt_parttypes[] =
+{
+	/* Generic OS */
+	DEF_GUID("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", N_("EFI System")),
+
+	DEF_GUID("024DEE41-33E7-11D3-9D69-0008C781F39F", N_("MBR partition scheme")),
+	DEF_GUID("D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", N_("Intel Fast Flash")),
+
+	/* Hah!IdontneedEFI */
+	DEF_GUID("21686148-6449-6E6F-744E-656564454649", N_("BIOS boot")),
+
+	/* Windows */
+	DEF_GUID("E3C9E316-0B5C-4DB8-817D-F92DF00215AE", N_("Microsoft reserved")),
+	DEF_GUID("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", N_("Microsoft basic data")),
+	DEF_GUID("5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", N_("Microsoft LDM metadata")),
+	DEF_GUID("AF9B60A0-1431-4F62-BC68-3311714A69AD", N_("Microsoft LDM data")),
+	DEF_GUID("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", N_("Windows recovery environment")),
+	DEF_GUID("37AFFC90-EF7D-4E96-91C3-2D7AE055B174", N_("IBM General Parallel Fs")),
+	DEF_GUID("E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", N_("Microsoft Storage Spaces")),
+
+	/* HP-UX */
+	DEF_GUID("75894C1E-3AEB-11D3-B7C1-7B03A0000000", N_("HP-UX data")),
+	DEF_GUID("E2A1E728-32E3-11D6-A682-7B03A0000000", N_("HP-UX service")),
+
+	/* Linux (http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec) */
+	DEF_GUID("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", N_("Linux swap")),
+	DEF_GUID("0FC63DAF-8483-4772-8E79-3D69D8477DE4", N_("Linux filesystem")),
+	DEF_GUID("3B8F8425-20E0-4F3B-907F-1A25A76F98E8", N_("Linux server data")),
+	DEF_GUID("44479540-F297-41B2-9AF7-D131D5F0458A", N_("Linux root (x86)")),
+	DEF_GUID("4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", N_("Linux root (x86-64)")),
+	DEF_GUID("8DA63339-0007-60C0-C436-083AC8230908", N_("Linux reserved")),
+	DEF_GUID("933AC7E1-2EB4-4F13-B844-0E14E2AEF915", N_("Linux home")),
+	DEF_GUID("A19D880F-05FC-4D3B-A006-743F0F84911E", N_("Linux RAID")),
+	DEF_GUID("BC13C2FF-59E6-4262-A352-B275FD6F7172", N_("Linux extended boot")),
+	DEF_GUID("E6D6D379-F507-44C2-A23C-238F2A3DF928", N_("Linux LVM")),
+
+	/* FreeBSD */
+	DEF_GUID("516E7CB4-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD data")),
+	DEF_GUID("83BD6B9D-7F41-11DC-BE0B-001560B84F0F", N_("FreeBSD boot")),
+	DEF_GUID("516E7CB5-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD swap")),
+	DEF_GUID("516E7CB6-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD UFS")),
+	DEF_GUID("516E7CBA-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD ZFS")),
+	DEF_GUID("516E7CB8-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD Vinum")),
+
+	/* Apple OSX */
+	DEF_GUID("48465300-0000-11AA-AA11-00306543ECAC", N_("Apple HFS/HFS+")),
+	DEF_GUID("55465300-0000-11AA-AA11-00306543ECAC", N_("Apple UFS")),
+	DEF_GUID("52414944-0000-11AA-AA11-00306543ECAC", N_("Apple RAID")),
+	DEF_GUID("52414944-5F4F-11AA-AA11-00306543ECAC", N_("Apple RAID offline")),
+	DEF_GUID("426F6F74-0000-11AA-AA11-00306543ECAC", N_("Apple boot")),
+	DEF_GUID("4C616265-6C00-11AA-AA11-00306543ECAC", N_("Apple label")),
+	DEF_GUID("5265636F-7665-11AA-AA11-00306543ECAC", N_("Apple TV recovery")),
+	DEF_GUID("53746F72-6167-11AA-AA11-00306543ECAC", N_("Apple Core storage")),
+
+	/* Solaris */
+	DEF_GUID("6A82CB45-1DD2-11B2-99A6-080020736631", N_("Solaris boot")),
+	DEF_GUID("6A85CF4D-1DD2-11B2-99A6-080020736631", N_("Solaris root")),
+	/* same as Apple ZFS */
+	DEF_GUID("6A898CC3-1DD2-11B2-99A6-080020736631", N_("Solaris /usr & Apple ZFS")),
+	DEF_GUID("6A87C46F-1DD2-11B2-99A6-080020736631", N_("Solaris swap")),
+	DEF_GUID("6A8B642B-1DD2-11B2-99A6-080020736631", N_("Solaris backup")),
+	DEF_GUID("6A8EF2E9-1DD2-11B2-99A6-080020736631", N_("Solaris /var")),
+	DEF_GUID("6A90BA39-1DD2-11B2-99A6-080020736631", N_("Solaris /home")),
+	DEF_GUID("6A9283A5-1DD2-11B2-99A6-080020736631", N_("Solaris alternate sector")),
+	DEF_GUID("6A945A3B-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 1")),
+	DEF_GUID("6A9630D1-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 2")),
+	DEF_GUID("6A980767-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 3")),
+	DEF_GUID("6A96237F-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 4")),
+	DEF_GUID("6A8D2AC7-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 5")),
+
+	/* NetBSD */
+	DEF_GUID("49F48D32-B10E-11DC-B99B-0019D1879648", N_("NetBSD swap")),
+	DEF_GUID("49F48D5A-B10E-11DC-B99B-0019D1879648", N_("NetBSD FFS")),
+	DEF_GUID("49F48D82-B10E-11DC-B99B-0019D1879648", N_("NetBSD LFS")),
+	DEF_GUID("2DB519C4-B10E-11DC-B99B-0019D1879648", N_("NetBSD concatenated")),
+	DEF_GUID("2DB519EC-B10E-11DC-B99B-0019D1879648", N_("NetBSD encrypted")),
+	DEF_GUID("49F48DAA-B10E-11DC-B99B-0019D1879648", N_("NetBSD RAID")),
+
+	/* ChromeOS */
+	DEF_GUID("FE3A2A5D-4F32-41A7-B725-ACCC3285A309", N_("ChromeOS kernel")),
+	DEF_GUID("3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", N_("ChromeOS root fs")),
+	DEF_GUID("2E0A753D-9E48-43B0-8337-B15192CB1B5E", N_("ChromeOS reserved")),
+
+	/* MidnightBSD */
+	DEF_GUID("85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD data")),
+	DEF_GUID("85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD boot")),
+	DEF_GUID("85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD swap")),
+	DEF_GUID("0394Ef8B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD UFS")),
+	DEF_GUID("85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD ZFS")),
+	DEF_GUID("85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD Vinum")),
+};
+
+/* gpt_entry macros */
+#define gpt_partition_start(_e)		le64_to_cpu((_e)->lba_start)
+#define gpt_partition_end(_e)		le64_to_cpu((_e)->lba_end)
+
+/*
+ * in-memory fdisk GPT stuff
+ */
+struct fdisk_gpt_label {
+	struct fdisk_label	head;		/* generic part */
+
+	/* gpt specific part */
+	struct gpt_header	*pheader;	/* primary header */
+	struct gpt_header	*bheader;	/* backup header */
+	struct gpt_entry	*ents;		/* entries (partitions) */
+};
+
+static void gpt_deinit(struct fdisk_label *lb);
+
+static inline struct fdisk_gpt_label *self_label(struct fdisk_context *cxt)
+{
+	return (struct fdisk_gpt_label *) cxt->label;
+}
+
+/*
+ * Returns the partition length, or 0 if end is before beginning.
+ */
+static uint64_t gpt_partition_size(const struct gpt_entry *e)
+{
+	uint64_t start = gpt_partition_start(e);
+	uint64_t end = gpt_partition_end(e);
+
+	return start > end ? 0 : end - start + 1ULL;
+}
+
+/* prints UUID in the real byte order! */
+static void gpt_debug_uuid(const char *mesg, struct gpt_guid *guid)
+{
+	const unsigned char *uuid = (unsigned char *) guid;
+
+	fprintf(stderr, "%s: "
+		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+		mesg,
+		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]);
+}
+
+/*
+ * 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(struct gpt_guid *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 string_to_guid(const char *in, struct gpt_guid *guid)
+{
+	if (uuid_parse(in, (unsigned char *) guid))	/* BE */
+		return -1;
+	swap_efi_guid(guid);				/* LE */
+	return 0;
+}
+
+static char *guid_to_string(const struct gpt_guid *guid, char *out)
+{
+	struct gpt_guid u = *guid;	/* LE */
+
+	swap_efi_guid(&u);		/* BE */
+	uuid_unparse_upper((unsigned char *) &u, out);
+
+	return out;
+}
+
+static struct fdisk_parttype *gpt_partition_parttype(
+		struct fdisk_context *cxt,
+		const struct gpt_entry *e)
+{
+	struct fdisk_parttype *t;
+	char str[37];
+
+	guid_to_string(&e->type, str);
+	t = fdisk_label_get_parttype_from_string(cxt->label, str);
+	return t ? : fdisk_new_unknown_parttype(0, str);
+}
+
+static void gpt_entry_set_type(struct gpt_entry *e, struct gpt_guid *uuid)
+{
+	e->type = *uuid;
+	DBG(LABEL, gpt_debug_uuid("new type", &(e->type)));
+}
+
+static void gpt_entry_set_name(struct gpt_entry *e, char *str)
+{
+	char name[GPT_PART_NAME_LEN] = { 0 };
+	size_t i, sz = strlen(str);
+
+	if (sz) {
+		if (sz > GPT_PART_NAME_LEN)
+			sz = GPT_PART_NAME_LEN;
+		memcpy(name, str, sz);
+	}
+
+	for (i = 0; i < GPT_PART_NAME_LEN; i++)
+		e->name[i] = cpu_to_le16((uint16_t) name[i]);
+}
+
+static int gpt_entry_set_uuid(struct gpt_entry *e, char *str)
+{
+	struct gpt_guid uuid;
+	int rc;
+
+	rc = string_to_guid(str, &uuid);
+	if (rc)
+		return rc;
+
+	e->partition_guid = uuid;
+	return 0;
+}
+
+
+static const char *gpt_get_header_revstr(struct gpt_header *header)
+{
+	if (!header)
+		goto unknown;
+
+	switch (header->revision) {
+	case GPT_HEADER_REVISION_V1_02:
+		return "1.2";
+	case GPT_HEADER_REVISION_V1_00:
+		return "1.0";
+	case GPT_HEADER_REVISION_V0_99:
+		return "0.99";
+	default:
+		goto unknown;
+	}
+
+unknown:
+	return "unknown";
+}
+
+static inline int partition_unused(const struct gpt_entry *e)
+{
+	return !memcmp(&e->type, &GPT_UNUSED_ENTRY_GUID,
+			sizeof(struct gpt_guid));
+}
+
+/*
+ * Builds a clean new valid protective MBR - will wipe out any existing data.
+ * Returns 0 on success, otherwise < 0 on error.
+ */
+static int gpt_mknew_pmbr(struct fdisk_context *cxt)
+{
+	struct gpt_legacy_mbr *pmbr = NULL;
+	int rc;
+
+	if (!cxt || !cxt->firstsector)
+		return -ENOSYS;
+
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+
+	pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
+
+	pmbr->signature = cpu_to_le16(MSDOS_MBR_SIGNATURE);
+	pmbr->partition_record[0].os_type      = EFI_PMBR_OSTYPE;
+	pmbr->partition_record[0].start_sector = 1;
+	pmbr->partition_record[0].end_head     = 0xFE;
+	pmbr->partition_record[0].end_sector   = 0xFF;
+	pmbr->partition_record[0].end_track    = 0xFF;
+	pmbr->partition_record[0].starting_lba = cpu_to_le32(1);
+	pmbr->partition_record[0].size_in_lba  =
+		cpu_to_le32(min((uint32_t) cxt->total_sectors - 1, 0xFFFFFFFF));
+
+	return 0;
+}
+
+/* some universal differences between the headers */
+static void gpt_mknew_header_common(struct fdisk_context *cxt,
+				    struct gpt_header *header, uint64_t lba)
+{
+	if (!cxt || !header)
+		return;
+
+	header->my_lba = cpu_to_le64(lba);
+
+	if (lba == GPT_PRIMARY_PARTITION_TABLE_LBA) { /* primary */
+		header->alternative_lba = cpu_to_le64(cxt->total_sectors - 1);
+		header->partition_entry_lba = cpu_to_le64(2);
+	} else { /* backup */
+		uint64_t esz = le32_to_cpu(header->npartition_entries) * sizeof(struct gpt_entry);
+		uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size;
+
+		header->alternative_lba = cpu_to_le64(GPT_PRIMARY_PARTITION_TABLE_LBA);
+		header->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1 - esects);
+	}
+}
+
+/*
+ * Builds a new GPT header (at sector lba) from a backup header2.
+ * If building a primary header, then backup is the secondary, and vice versa.
+ *
+ * Always pass a new (zeroized) header to build upon as we don't
+ * explicitly zero-set some values such as CRCs and reserved.
+ *
+ * Returns 0 on success, otherwise < 0 on error.
+ */
+static int gpt_mknew_header_from_bkp(struct fdisk_context *cxt,
+				     struct gpt_header *header,
+				     uint64_t lba,
+				     struct gpt_header *header2)
+{
+	if (!cxt || !header || !header2)
+		return -ENOSYS;
+
+	header->signature              = header2->signature;
+	header->revision               = header2->revision;
+	header->size                   = header2->size;
+	header->npartition_entries     = header2->npartition_entries;
+	header->sizeof_partition_entry = header2->sizeof_partition_entry;
+	header->first_usable_lba       = header2->first_usable_lba;
+	header->last_usable_lba        = header2->last_usable_lba;
+
+	memcpy(&header->disk_guid,
+	       &header2->disk_guid, sizeof(header2->disk_guid));
+	gpt_mknew_header_common(cxt, header, lba);
+
+	return 0;
+}
+
+static struct gpt_header *gpt_copy_header(struct fdisk_context *cxt,
+			   struct gpt_header *src)
+{
+	struct gpt_header *res;
+
+	if (!cxt || !src)
+		return NULL;
+
+	res = calloc(1, sizeof(*res));
+	if (!res) {
+		fdisk_warn(cxt, _("failed to allocate GPT header"));
+		return NULL;
+	}
+
+	res->my_lba                 = src->alternative_lba;
+	res->alternative_lba        = src->my_lba;
+
+	res->signature              = src->signature;
+	res->revision               = src->revision;
+	res->size                   = src->size;
+	res->npartition_entries     = src->npartition_entries;
+	res->sizeof_partition_entry = src->sizeof_partition_entry;
+	res->first_usable_lba       = src->first_usable_lba;
+	res->last_usable_lba        = src->last_usable_lba;
+
+	memcpy(&res->disk_guid, &src->disk_guid, sizeof(src->disk_guid));
+
+
+	if (res->my_lba == GPT_PRIMARY_PARTITION_TABLE_LBA)
+		res->partition_entry_lba = cpu_to_le64(2);
+	else {
+		uint64_t esz = le32_to_cpu(src->npartition_entries) * sizeof(struct gpt_entry);
+		uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size;
+
+		res->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1 - esects);
+	}
+
+	return res;
+}
+
+static void count_first_last_lba(struct fdisk_context *cxt,
+				 uint64_t *first, uint64_t *last)
+{
+	uint64_t esz = 0;
+
+	assert(cxt);
+
+	esz = sizeof(struct gpt_entry) * GPT_NPARTITIONS / cxt->sector_size;
+	*last = cxt->total_sectors - 2 - esz;
+	*first = esz + 2;
+
+	if (*first < cxt->first_lba && cxt->first_lba < *last)
+		/* Align according to topology */
+		*first = cxt->first_lba;
+}
+
+/*
+ * Builds a clean new GPT header (currently under revision 1.0).
+ *
+ * Always pass a new (zeroized) header to build upon as we don't
+ * explicitly zero-set some values such as CRCs and reserved.
+ *
+ * Returns 0 on success, otherwise < 0 on error.
+ */
+static int gpt_mknew_header(struct fdisk_context *cxt,
+			    struct gpt_header *header, uint64_t lba)
+{
+	uint64_t first, last;
+	int has_id = 0;
+
+	if (!cxt || !header)
+		return -ENOSYS;
+
+	header->signature = cpu_to_le64(GPT_HEADER_SIGNATURE);
+	header->revision  = cpu_to_le32(GPT_HEADER_REVISION_V1_00);
+	header->size      = cpu_to_le32(sizeof(struct gpt_header));
+
+	/*
+	 * 128 partitions are the default. It can go beyond that, but
+	 * we're creating a de facto header here, so no funny business.
+	 */
+	header->npartition_entries     = cpu_to_le32(GPT_NPARTITIONS);
+	header->sizeof_partition_entry = cpu_to_le32(sizeof(struct gpt_entry));
+
+	count_first_last_lba(cxt, &first, &last);
+	header->first_usable_lba = cpu_to_le64(first);
+	header->last_usable_lba  = cpu_to_le64(last);
+
+	gpt_mknew_header_common(cxt, header, lba);
+
+	if (cxt->script) {
+		const char *id = fdisk_script_get_header(cxt->script, "label-id");
+		if (id && string_to_guid(id, &header->disk_guid) == 0)
+			has_id = 1;
+	}
+
+	if (!has_id) {
+		uuid_generate_random((unsigned char *) &header->disk_guid);
+		swap_efi_guid(&header->disk_guid);
+	}
+	return 0;
+}
+
+/*
+ * Checks if there is a valid protective MBR partition table.
+ * Returns 0 if it is invalid or failure. Otherwise, return
+ * GPT_MBR_PROTECTIVE or GPT_MBR_HYBRID, depeding on the detection.
+ */
+static int valid_pmbr(struct fdisk_context *cxt)
+{
+	int i, part = 0, ret = 0; /* invalid by default */
+	struct gpt_legacy_mbr *pmbr = NULL;
+	uint32_t sz_lba = 0;
+
+	if (!cxt->firstsector)
+		goto done;
+
+	pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
+
+	if (le16_to_cpu(pmbr->signature) != MSDOS_MBR_SIGNATURE)
+		goto done;
+
+	/* LBA of the GPT partition header */
+	if (pmbr->partition_record[0].starting_lba !=
+	    cpu_to_le32(GPT_PRIMARY_PARTITION_TABLE_LBA))
+		goto done;
+
+	/* seems like a valid MBR was found, check DOS primary partitions */
+	for (i = 0; i < 4; i++) {
+		if (pmbr->partition_record[i].os_type == EFI_PMBR_OSTYPE) {
+			/*
+			 * Ok, we at least know that there's a protective MBR,
+			 * now check if there are other partition types for
+			 * hybrid MBR.
+			 */
+			part = i;
+			ret = GPT_MBR_PROTECTIVE;
+			goto check_hybrid;
+		}
+	}
+
+	if (ret != GPT_MBR_PROTECTIVE)
+		goto done;
+check_hybrid:
+	for (i = 0 ; i < 4; i++) {
+		if ((pmbr->partition_record[i].os_type != EFI_PMBR_OSTYPE) &&
+		    (pmbr->partition_record[i].os_type != 0x00))
+			ret = GPT_MBR_HYBRID;
+	}
+
+	/*
+	 * Protective MBRs take up the lesser of the whole disk
+	 * or 2 TiB (32bit LBA), ignoring the rest of the disk.
+	 * Some partitioning programs, nonetheless, choose to set
+	 * the size to the maximum 32-bit limitation, disregarding
+	 * the disk size.
+	 *
+	 * Hybrid MBRs do not necessarily comply with this.
+	 *
+	 * Consider a bad value here to be a warning to support dd-ing
+	 * an image from a smaller disk to a bigger disk.
+	 */
+	if (ret == GPT_MBR_PROTECTIVE) {
+		sz_lba = le32_to_cpu(pmbr->partition_record[part].size_in_lba);
+		if (sz_lba != (uint32_t) cxt->total_sectors - 1 && sz_lba != 0xFFFFFFFF) {
+			fdisk_warnx(cxt, _("GPT PMBR size mismatch (%u != %u) "
+					   "will be corrected by w(rite)."),
+					sz_lba,
+					(uint32_t) cxt->total_sectors - 1);
+			fdisk_label_set_changed(cxt->label, 1);
+		}
+	}
+done:
+	return ret;
+}
+
+static uint64_t last_lba(struct fdisk_context *cxt)
+{
+	struct stat s;
+	uint64_t sectors = 0;
+
+	memset(&s, 0, sizeof(s));
+	if (fstat(cxt->dev_fd, &s) == -1) {
+		fdisk_warn(cxt, _("gpt: stat() failed"));
+		return 0;
+	}
+
+	if (S_ISBLK(s.st_mode))
+		sectors = cxt->total_sectors - 1;
+	else if (S_ISREG(s.st_mode))
+		sectors = ((uint64_t) s.st_size /
+			   (uint64_t) cxt->sector_size) - 1ULL;
+	else
+		fdisk_warnx(cxt, _("gpt: cannot handle files with mode %o"), s.st_mode);
+
+	DBG(LABEL, ul_debug("GPT last LBA: %ju", sectors));
+	return sectors;
+}
+
+static ssize_t read_lba(struct fdisk_context *cxt, uint64_t lba,
+			void *buffer, const size_t bytes)
+{
+	off_t offset = lba * cxt->sector_size;
+
+	if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1)
+		return -1;
+	return read(cxt->dev_fd, buffer, bytes) != bytes;
+}
+
+
+/* Returns the GPT entry array */
+static struct gpt_entry *gpt_read_entries(struct fdisk_context *cxt,
+					 struct gpt_header *header)
+{
+	ssize_t sz;
+	struct gpt_entry *ret = NULL;
+	off_t offset;
+
+	assert(cxt);
+	assert(header);
+
+	sz = le32_to_cpu(header->npartition_entries) *
+	     le32_to_cpu(header->sizeof_partition_entry);
+
+	ret = calloc(1, sz);
+	if (!ret)
+		return NULL;
+	offset = le64_to_cpu(header->partition_entry_lba) *
+		       cxt->sector_size;
+
+	if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+		goto fail;
+	if (sz != read(cxt->dev_fd, ret, sz))
+		goto fail;
+
+	return ret;
+
+fail:
+	free(ret);
+	return NULL;
+}
+
+static inline uint32_t count_crc32(const unsigned char *buf, size_t len)
+{
+	return (crc32(~0L, buf, len) ^ ~0L);
+}
+
+/*
+ * Recompute header and partition array 32bit CRC checksums.
+ * This function does not fail - if there's corruption, then it
+ * will be reported when checksuming it again (ie: probing or verify).
+ */
+static void gpt_recompute_crc(struct gpt_header *header, struct gpt_entry *ents)
+{
+	uint32_t crc = 0;
+	size_t entry_sz = 0;
+
+	if (!header)
+		return;
+
+	/* header CRC */
+	header->crc32 = 0;
+	crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
+	header->crc32 = cpu_to_le32(crc);
+
+	/* partition entry array CRC */
+	header->partition_entry_array_crc32 = 0;
+	entry_sz = le32_to_cpu(header->npartition_entries) *
+		le32_to_cpu(header->sizeof_partition_entry);
+
+	crc = count_crc32((unsigned char *) ents, entry_sz);
+	header->partition_entry_array_crc32 = cpu_to_le32(crc);
+}
+
+/*
+ * Compute the 32bit CRC checksum of the partition table header.
+ * Returns 1 if it is valid, otherwise 0.
+ */
+static int gpt_check_header_crc(struct gpt_header *header, struct gpt_entry *ents)
+{
+	uint32_t crc, orgcrc = le32_to_cpu(header->crc32);
+
+	header->crc32 = 0;
+	crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
+	header->crc32 = cpu_to_le32(orgcrc);
+
+	if (crc == le32_to_cpu(header->crc32))
+		return 1;
+
+	/*
+	 * If we have checksum mismatch it may be due to stale data,
+	 * like a partition being added or deleted. Recompute the CRC again
+	 * and make sure this is not the case.
+	 */
+	if (ents) {
+		gpt_recompute_crc(header, ents);
+		orgcrc = le32_to_cpu(header->crc32);
+		header->crc32 = 0;
+		crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
+		header->crc32 = cpu_to_le32(orgcrc);
+
+		return crc == le32_to_cpu(header->crc32);
+	}
+
+	return 0;
+}
+
+/*
+ * It initializes the partition entry array.
+ * Returns 1 if the checksum is valid, otherwise 0.
+ */
+static int gpt_check_entryarr_crc(struct gpt_header *header,
+				  struct gpt_entry *ents)
+{
+	int ret = 0;
+	ssize_t entry_sz;
+	uint32_t crc;
+
+	if (!header || !ents)
+		goto done;
+
+	entry_sz = le32_to_cpu(header->npartition_entries) *
+		   le32_to_cpu(header->sizeof_partition_entry);
+
+	if (!entry_sz)
+		goto done;
+
+	crc = count_crc32((unsigned char *) ents, entry_sz);
+	ret = (crc == le32_to_cpu(header->partition_entry_array_crc32));
+done:
+	return ret;
+}
+
+static int gpt_check_lba_sanity(struct fdisk_context *cxt, struct gpt_header *header)
+{
+	int ret = 0;
+	uint64_t lu, fu, lastlba = last_lba(cxt);
+
+	fu = le64_to_cpu(header->first_usable_lba);
+	lu = le64_to_cpu(header->last_usable_lba);
+
+	/* check if first and last usable LBA make sense */
+	if (lu < fu) {
+		DBG(LABEL, ul_debug("error: header last LBA is before first LBA"));
+		goto done;
+	}
+
+	/* check if first and last usable LBAs with the disk's last LBA */
+	if (fu > lastlba || lu > lastlba) {
+		DBG(LABEL, ul_debug("error: header LBAs are after the disk's last LBA"));
+		goto done;
+	}
+
+	/* the header has to be outside usable range */
+	if (fu < GPT_PRIMARY_PARTITION_TABLE_LBA &&
+	    GPT_PRIMARY_PARTITION_TABLE_LBA < lu) {
+		DBG(LABEL, ul_debug("error: header outside of usable range"));
+		goto done;
+	}
+
+	ret = 1; /* sane */
+done:
+	return ret;
+}
+
+/* Check if there is a valid header signature */
+static int gpt_check_signature(struct gpt_header *header)
+{
+	return header->signature == cpu_to_le64(GPT_HEADER_SIGNATURE);
+}
+
+/*
+ * Return the specified GPT Header, or NULL upon failure/invalid.
+ * Note that all tests must pass to ensure a valid header,
+ * we do not rely on only testing the signature for a valid probe.
+ */
+static struct gpt_header *gpt_read_header(struct fdisk_context *cxt,
+					  uint64_t lba,
+					  struct gpt_entry **_ents)
+{
+	struct gpt_header *header = NULL;
+	struct gpt_entry *ents = NULL;
+	uint32_t hsz;
+
+	if (!cxt)
+		return NULL;
+
+	header = calloc(1, sizeof(*header));
+	if (!header)
+		return NULL;
+
+	/* read and verify header */
+	if (read_lba(cxt, lba, header, sizeof(struct gpt_header)) != 0)
+		goto invalid;
+
+	if (!gpt_check_signature(header))
+		goto invalid;
+
+	if (!gpt_check_header_crc(header, NULL))
+		goto invalid;
+
+	/* read and verify entries */
+	ents = gpt_read_entries(cxt, header);
+	if (!ents)
+		goto invalid;
+
+	if (!gpt_check_entryarr_crc(header, ents))
+		goto invalid;
+
+	if (!gpt_check_lba_sanity(cxt, header))
+		goto invalid;
+
+	/* valid header must be at MyLBA */
+	if (le64_to_cpu(header->my_lba) != lba)
+		goto invalid;
+
+	/* make sure header size is between 92 and sector size bytes */
+	hsz = le32_to_cpu(header->size);
+	if (hsz < GPT_HEADER_MINSZ || hsz > cxt->sector_size)
+		goto invalid;
+
+	if (_ents)
+		*_ents = ents;
+	else
+		free(ents);
+
+	DBG(LABEL, ul_debug("found valid GPT Header on LBA %ju", lba));
+	return header;
+invalid:
+	free(header);
+	free(ents);
+
+	DBG(LABEL, ul_debug("read GPT Header on LBA %ju failed", lba));
+	return NULL;
+}
+
+
+static int gpt_locate_disklabel(struct fdisk_context *cxt, int n,
+		const char **name, off_t *offset, size_t *size)
+{
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+
+	*name = NULL;
+	*offset = 0;
+	*size = 0;
+
+	switch (n) {
+	case 0:
+		*name = "PMBR";
+		*offset = 0;
+		*size = 512;
+		break;
+	case 1:
+		*name = _("GPT Header");
+		*offset = GPT_PRIMARY_PARTITION_TABLE_LBA * cxt->sector_size;
+		*size = sizeof(struct gpt_header);
+		break;
+	case 2:
+		*name = _("GPT Entries");
+		gpt = self_label(cxt);
+		*offset = le64_to_cpu(gpt->pheader->partition_entry_lba) * cxt->sector_size;
+		*size = le32_to_cpu(gpt->pheader->npartition_entries) *
+			 le32_to_cpu(gpt->pheader->sizeof_partition_entry);
+		break;
+	default:
+		return 1;			/* no more chunks */
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * Returns the number of partitions that are in use.
+ */
+static unsigned partitions_in_use(struct gpt_header *header,
+				  struct gpt_entry *ents)
+{
+	uint32_t i, used = 0;
+
+	if (!header || ! ents)
+		return 0;
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++)
+		if (!partition_unused(&ents[i]))
+			used++;
+	return used;
+}
+
+
+/*
+ * Check if a partition is too big for the disk (sectors).
+ * Returns the faulting partition number, otherwise 0.
+ */
+static uint32_t check_too_big_partitions(struct gpt_header *header,
+				   struct gpt_entry *ents, uint64_t sectors)
+{
+	uint32_t i;
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+		if (partition_unused(&ents[i]))
+			continue;
+		if (gpt_partition_end(&ents[i]) >= sectors)
+			return i + 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if a partition ends before it begins
+ * Returns the faulting partition number, otherwise 0.
+ */
+static uint32_t check_start_after_end_paritions(struct gpt_header *header,
+						struct gpt_entry *ents)
+{
+	uint32_t i;
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+		if (partition_unused(&ents[i]))
+			continue;
+		if (gpt_partition_start(&ents[i]) > gpt_partition_end(&ents[i]))
+			return i + 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if partition e1 overlaps with partition e2.
+ */
+static inline int partition_overlap(struct gpt_entry *e1, struct gpt_entry *e2)
+{
+	uint64_t start1 = gpt_partition_start(e1);
+	uint64_t end1   = gpt_partition_end(e1);
+	uint64_t start2 = gpt_partition_start(e2);
+	uint64_t end2   = gpt_partition_end(e2);
+
+	return (start1 && start2 && (start1 <= end2) != (end1 < start2));
+}
+
+/*
+ * Find any partitions that overlap.
+ */
+static uint32_t check_overlap_partitions(struct gpt_header *header,
+					 struct gpt_entry *ents)
+{
+	uint32_t i, j;
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++)
+		for (j = 0; j < i; j++) {
+			if (partition_unused(&ents[i]) ||
+			    partition_unused(&ents[j]))
+				continue;
+			if (partition_overlap(&ents[i], &ents[j])) {
+				DBG(LABEL, ul_debug("GPT partitions overlap detected [%u vs. %u]", i, j));
+				return i + 1;
+			}
+		}
+
+	return 0;
+}
+
+/*
+ * Find the first available block after the starting point; returns 0 if
+ * there are no available blocks left, or error. From gdisk.
+ */
+static uint64_t find_first_available(struct gpt_header *header,
+				     struct gpt_entry *ents, uint64_t start)
+{
+	uint64_t first;
+	uint32_t i, first_moved = 0;
+
+	uint64_t fu, lu;
+
+	if (!header || !ents)
+		return 0;
+
+	fu = le64_to_cpu(header->first_usable_lba);
+	lu = le64_to_cpu(header->last_usable_lba);
+
+	/*
+	 * Begin from the specified starting point or from the first usable
+	 * LBA, whichever is greater...
+	 */
+	first = start < fu ? fu : start;
+
+	/*
+	 * Now search through all partitions; if first is within an
+	 * existing partition, move it to the next sector after that
+	 * partition and repeat. If first was moved, set firstMoved
+	 * flag; repeat until firstMoved is not set, so as to catch
+	 * cases where partitions are out of sequential order....
+	 */
+	do {
+		first_moved = 0;
+		for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+			if (partition_unused(&ents[i]))
+				continue;
+			if (first < gpt_partition_start(&ents[i]))
+				continue;
+			if (first <= gpt_partition_end(&ents[i])) {
+				first = gpt_partition_end(&ents[i]) + 1;
+				first_moved = 1;
+			}
+		}
+	} while (first_moved == 1);
+
+	if (first > lu)
+		first = 0;
+
+	return first;
+}
+
+
+/* Returns last available sector in the free space pointed to by start. From gdisk. */
+static uint64_t find_last_free(struct gpt_header *header,
+			       struct gpt_entry *ents, uint64_t start)
+{
+	uint32_t i;
+	uint64_t nearest_start;
+
+	if (!header || !ents)
+		return 0;
+
+	nearest_start = le64_to_cpu(header->last_usable_lba);
+
+	for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+		uint64_t ps = gpt_partition_start(&ents[i]);
+
+		if (nearest_start > ps && ps > start)
+			nearest_start = ps - 1;
+	}
+
+	return nearest_start;
+}
+
+/* Returns the last free sector on the disk. From gdisk. */
+static uint64_t find_last_free_sector(struct gpt_header *header,
+				      struct gpt_entry *ents)
+{
+	uint32_t i, last_moved;
+	uint64_t last = 0;
+
+	if (!header || !ents)
+		goto done;
+
+	/* start by assuming the last usable LBA is available */
+	last = le64_to_cpu(header->last_usable_lba);
+	do {
+		last_moved = 0;
+		for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+			if ((last >= gpt_partition_start(&ents[i])) &&
+			    (last <= gpt_partition_end(&ents[i]))) {
+				last = gpt_partition_start(&ents[i]) - 1;
+				last_moved = 1;
+			}
+		}
+	} while (last_moved == 1);
+done:
+	return last;
+}
+
+/*
+ * Finds the first available sector in the largest block of unallocated
+ * space on the disk. Returns 0 if there are no available blocks left.
+ * From gdisk.
+ */
+static uint64_t find_first_in_largest(struct gpt_header *header,
+				      struct gpt_entry *ents)
+{
+	uint64_t start = 0, first_sect, last_sect;
+	uint64_t segment_size, selected_size = 0, selected_segment = 0;
+
+	if (!header || !ents)
+		goto done;
+
+	do {
+		first_sect =  find_first_available(header, ents, start);
+		if (first_sect != 0) {
+			last_sect = find_last_free(header, ents, first_sect);
+			segment_size = last_sect - first_sect + 1;
+
+			if (segment_size > selected_size) {
+				selected_size = segment_size;
+				selected_segment = first_sect;
+			}
+			start = last_sect + 1;
+		}
+	} while (first_sect != 0);
+
+done:
+	return selected_segment;
+}
+
+/*
+ * Find the total number of free sectors, the number of segments in which
+ * they reside, and the size of the largest of those segments. From gdisk.
+ */
+static uint64_t get_free_sectors(struct fdisk_context *cxt, struct gpt_header *header,
+				 struct gpt_entry *ents, uint32_t *nsegments,
+				 uint64_t *largest_segment)
+{
+	uint32_t num = 0;
+	uint64_t first_sect, last_sect;
+	uint64_t largest_seg = 0, segment_sz;
+	uint64_t totfound = 0, start = 0; /* starting point for each search */
+
+	if (!cxt->total_sectors)
+		goto done;
+
+	do {
+		first_sect = find_first_available(header, ents, start);
+		if (first_sect) {
+			last_sect = find_last_free(header, ents, first_sect);
+			segment_sz = last_sect - first_sect + 1;
+
+			if (segment_sz > largest_seg)
+				largest_seg = segment_sz;
+			totfound += segment_sz;
+			num++;
+			start = last_sect + 1;
+		}
+	} while (first_sect);
+
+done:
+	if (nsegments)
+		*nsegments = num;
+	if (largest_segment)
+		*largest_segment = largest_seg;
+
+	return totfound;
+}
+
+static int gpt_probe_label(struct fdisk_context *cxt)
+{
+	int mbr_type;
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	/* TODO: it would be nice to support scenario when GPT headers are OK,
+	 *       but PMBR is corrupt */
+	mbr_type = valid_pmbr(cxt);
+	if (!mbr_type)
+		goto failed;
+
+	DBG(LABEL, ul_debug("found a %s MBR", mbr_type == GPT_MBR_PROTECTIVE ?
+			    "protective" : "hybrid"));
+
+	/* primary header */
+	gpt->pheader = gpt_read_header(cxt, GPT_PRIMARY_PARTITION_TABLE_LBA,
+				       &gpt->ents);
+
+	if (gpt->pheader)
+		/* primary OK, try backup from alternative LBA */
+		gpt->bheader = gpt_read_header(cxt,
+					le64_to_cpu(gpt->pheader->alternative_lba),
+					NULL);
+	else
+		/* primary corrupted -- try last LBA */
+		gpt->bheader = gpt_read_header(cxt, last_lba(cxt), &gpt->ents);
+
+	if (!gpt->pheader && !gpt->bheader)
+		goto failed;
+
+	/* primary OK, backup corrupted -- recovery */
+	if (gpt->pheader && !gpt->bheader) {
+		fdisk_warnx(cxt, _("The backup GPT table is corrupt, but the "
+				  "primary appears OK, so that will be used."));
+		gpt->bheader = gpt_copy_header(cxt, gpt->pheader);
+		if (!gpt->bheader)
+			goto failed;
+		gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	/* primary corrupted, backup OK -- recovery */
+	} else if (!gpt->pheader && gpt->bheader) {
+		fdisk_warnx(cxt, _("The primary GPT table is corrupt, but the "
+				  "backup appears OK, so that will be used."));
+		gpt->pheader = gpt_copy_header(cxt, gpt->bheader);
+		if (!gpt->pheader)
+			goto failed;
+		gpt_recompute_crc(gpt->pheader, gpt->ents);
+	}
+
+	cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries);
+	cxt->label->nparts_cur = partitions_in_use(gpt->pheader, gpt->ents);
+	return 1;
+failed:
+	DBG(LABEL, ul_debug("GPT probe failed"));
+	gpt_deinit(cxt->label);
+	return 0;
+}
+
+/*
+ * Stolen from libblkid - can be removed once partition semantics
+ * are added to the fdisk API.
+ */
+static char *encode_to_utf8(unsigned char *src, size_t count)
+{
+	uint16_t c;
+	char *dest;
+	size_t i, j, len = count;
+
+	dest = calloc(1, count);
+	if (!dest)
+		return NULL;
+
+	for (j = i = 0; i + 2 <= count; i += 2) {
+		/* always little endian */
+		c = (src[i+1] << 8) | src[i];
+		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 dest;
+}
+
+static int gpt_entry_attrs_to_string(struct gpt_entry *e, char **res)
+{
+	unsigned int n, count = 0;
+	size_t l;
+	char *bits, *p;
+	uint64_t attrs;
+
+	assert(e);
+	assert(res);
+
+	*res = NULL;
+	attrs = le64_to_cpu(e->attrs);
+	if (!attrs)
+		return 0;	/* no attributes at all */
+
+	bits = (char *) &attrs;
+
+	/* Note that sizeof() is correct here, we need separators between
+	 * the strings so also count \0 is correct */
+	*res = calloc(1, sizeof(GPT_ATTRSTR_NOBLOCK) +
+			 sizeof(GPT_ATTRSTR_REQ) +
+			 sizeof(GPT_ATTRSTR_LEGACY) +
+			 sizeof("GUID:") + (GPT_ATTRBIT_GUID_COUNT * 3));
+	if (!*res)
+		return -errno;
+
+	p = *res;
+	if (isset(bits, GPT_ATTRBIT_REQ)) {
+		memcpy(p, GPT_ATTRSTR_REQ, (l = sizeof(GPT_ATTRSTR_REQ)));
+		p += l - 1;
+	}
+	if (isset(bits, GPT_ATTRBIT_NOBLOCK)) {
+		if (p > *res)
+			*p++ = ' ';
+		memcpy(p, GPT_ATTRSTR_NOBLOCK, (l = sizeof(GPT_ATTRSTR_NOBLOCK)));
+		p += l - 1;
+	}
+	if (isset(bits, GPT_ATTRBIT_LEGACY)) {
+		if (p > *res)
+			*p++ = ' ';
+		memcpy(p, GPT_ATTRSTR_LEGACY, (l = sizeof(GPT_ATTRSTR_LEGACY)));
+		p += l - 1;
+	}
+
+	for (n = GPT_ATTRBIT_GUID_FIRST;
+	     n < GPT_ATTRBIT_GUID_FIRST + GPT_ATTRBIT_GUID_COUNT; n++) {
+
+		if (!isset(bits, n))
+			continue;
+		if (!count) {
+			if (p > *res)
+				*p++ = ' ';
+			p += sprintf(p, "GUID:%u", n);
+		} else
+			p += sprintf(p, ",%u", n);
+		count++;
+	}
+
+	return 0;
+}
+
+static int gpt_entry_attrs_from_string(
+			struct fdisk_context *cxt,
+			struct gpt_entry *e,
+			const char *str)
+{
+	const char *p = str;
+	uint64_t attrs = 0;
+	char *bits;
+
+	assert(e);
+	assert(p);
+
+	DBG(LABEL, ul_debug("GPT: parsing string attributes '%s'", p));
+
+	bits = (char *) &attrs;
+
+	while (p && *p) {
+		int bit = -1;
+
+		while (isblank(*p)) p++;
+		if (!*p)
+			break;
+
+		DBG(LABEL, ul_debug(" parsing item '%s'", p));
+
+		if (strncmp(p, "GUID:", 5) == 0) {
+			p += 5;
+			continue;
+		} else if (strncmp(p, GPT_ATTRSTR_REQ,
+					sizeof(GPT_ATTRSTR_REQ) - 1) == 0) {
+			bit = GPT_ATTRBIT_REQ;
+			p += sizeof(GPT_ATTRSTR_REQ) - 1;
+		} else if (strncmp(p, GPT_ATTRSTR_LEGACY,
+					sizeof(GPT_ATTRSTR_LEGACY) - 1) == 0) {
+			bit = GPT_ATTRBIT_LEGACY;
+			p += sizeof(GPT_ATTRSTR_LEGACY) - 1;
+		} else if (strncmp(p, GPT_ATTRSTR_NOBLOCK,
+					sizeof(GPT_ATTRSTR_NOBLOCK) - 1) == 0) {
+			bit = GPT_ATTRBIT_NOBLOCK;
+			p += sizeof(GPT_ATTRSTR_NOBLOCK) - 1;
+		} else if (isdigit((unsigned int) *p)) {
+			char *end = NULL;
+
+			errno = 0;
+			bit = strtol(p, &end, 0);
+			if (errno || !end || end == str
+			    || bit < GPT_ATTRBIT_GUID_FIRST
+			    || bit >= GPT_ATTRBIT_GUID_FIRST + GPT_ATTRBIT_GUID_COUNT)
+				bit = -1;
+			else
+				p = end;
+		}
+
+		if (bit < 0) {
+			fdisk_warnx(cxt, _("unssuported GPT attribute bit '%s'"), p);
+			return -EINVAL;
+		}
+
+		setbit(bits, bit);
+
+		while (isblank(*p)) p++;
+		if (*p == ',')
+			p++;
+	}
+
+	e->attrs = cpu_to_le64(attrs);
+	return 0;
+}
+
+static int gpt_get_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_entry *e;
+	char u_str[37];
+	int rc = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if ((uint32_t) n >= le32_to_cpu(gpt->pheader->npartition_entries))
+		return -EINVAL;
+
+	gpt = self_label(cxt);
+	e = &gpt->ents[n];
+
+	pa->used = !partition_unused(e) || gpt_partition_start(e);
+	if (!pa->used)
+		return 0;
+
+	pa->start = gpt_partition_start(e);
+	pa->size = gpt_partition_size(e);
+	pa->type = gpt_partition_parttype(cxt, e);
+
+	if (guid_to_string(&e->partition_guid, u_str)) {
+		pa->uuid = strdup(u_str);
+		if (!pa->uuid) {
+			rc = -errno;
+			goto done;
+		}
+	} else
+		pa->uuid = NULL;
+
+	rc = gpt_entry_attrs_to_string(e, &pa->attrs);
+	if (rc)
+		goto done;
+
+	pa->name = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
+	return 0;
+done:
+	fdisk_reset_partition(pa);
+	return rc;
+}
+
+
+static int gpt_set_partition(struct fdisk_context *cxt, size_t n,
+			     struct fdisk_partition *pa)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_entry *e;
+	int rc = 0;
+	uint64_t start, end;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if ((uint32_t) n >= le32_to_cpu(gpt->pheader->npartition_entries))
+		return -EINVAL;
+
+	FDISK_INIT_UNDEF(start);
+	FDISK_INIT_UNDEF(end);
+
+	gpt = self_label(cxt);
+	e = &gpt->ents[n];
+
+	if (pa->uuid) {
+		char new_u[37], old_u[37];
+
+		guid_to_string(&e->partition_guid, old_u);
+		rc = gpt_entry_set_uuid(e, pa->uuid);
+		if (rc)
+			return rc;
+		guid_to_string(&e->partition_guid, new_u);
+		fdisk_info(cxt, _("Partition UUID changed from %s to %s."),
+			old_u, new_u);
+	}
+
+	if (pa->name) {
+		char *old = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
+		gpt_entry_set_name(e, pa->name);
+
+		fdisk_info(cxt, _("Partition name changed from '%s' to '%.*s'."),
+			old, (int) GPT_PART_NAME_LEN, pa->name);
+		free(old);
+	}
+
+	if (pa->type && pa->type->typestr) {
+		struct gpt_guid typeid;
+
+		rc = string_to_guid(pa->type->typestr, &typeid);
+		if (rc)
+			return rc;
+		gpt_entry_set_type(e, &typeid);
+	}
+	if (pa->attrs) {
+		rc = gpt_entry_attrs_from_string(cxt, e, pa->attrs);
+		if (rc)
+			return rc;
+	}
+
+	if (fdisk_partition_has_start(pa))
+		start = pa->start;
+	if (fdisk_partition_has_size(pa))
+		end = gpt_partition_start(e) + pa->size - 1ULL;
+
+	if (pa->end_follow_default) {
+		/* enlarge */
+		if (!FDISK_IS_UNDEF(start))
+			start = gpt_partition_start(e);
+		end = find_last_free(gpt->bheader, gpt->ents, start);
+		if (!end)
+			FDISK_INIT_UNDEF(end);
+	}
+
+	if (!FDISK_IS_UNDEF(start))
+		e->lba_start = cpu_to_le64(start);
+	if (!FDISK_IS_UNDEF(end))
+		e->lba_end = cpu_to_le64(end);
+
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return rc;
+}
+
+
+/*
+ * List label partitions.
+ */
+static int gpt_list_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	if (fdisk_is_details(cxt)) {
+		struct gpt_header *h = self_label(cxt)->pheader;
+
+		fdisk_info(cxt, _("First LBA: %ju"), h->first_usable_lba);
+		fdisk_info(cxt, _("Last LBA: %ju"), h->last_usable_lba);
+		/* TRANSLATORS: The LBA (Logical Block Address) of the backup GPT header. */
+		fdisk_info(cxt, _("Alternative LBA: %ju"), h->alternative_lba);
+		/* TRANSLATORS: The start of the array of partition entries. */
+		fdisk_info(cxt, _("Partition entries LBA: %ju"), h->partition_entry_lba);
+		fdisk_info(cxt, _("Allocated partition entries: %u"), h->npartition_entries);
+	}
+
+	return 0;
+}
+
+/*
+ * Write partitions.
+ * Returns 0 on success, or corresponding error otherwise.
+ */
+static int gpt_write_partitions(struct fdisk_context *cxt,
+				struct gpt_header *header, struct gpt_entry *ents)
+{
+	off_t offset = le64_to_cpu(header->partition_entry_lba) * cxt->sector_size;
+	uint32_t nparts = le32_to_cpu(header->npartition_entries);
+	uint32_t totwrite = nparts * le32_to_cpu(header->sizeof_partition_entry);
+	ssize_t rc;
+
+	if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+		goto fail;
+
+	rc = write(cxt->dev_fd, ents, totwrite);
+	if (rc > 0 && totwrite == (uint32_t) rc)
+		return 0;
+fail:
+	return -errno;
+}
+
+/*
+ * Write a GPT header to a specified LBA
+ * Returns 0 on success, or corresponding error otherwise.
+ */
+static int gpt_write_header(struct fdisk_context *cxt,
+			    struct gpt_header *header, uint64_t lba)
+{
+	off_t offset = lba * cxt->sector_size;
+
+	if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+		goto fail;
+	if (cxt->sector_size ==
+	    (size_t) write(cxt->dev_fd, header, cxt->sector_size))
+		return 0;
+fail:
+	return -errno;
+}
+
+/*
+ * Write the protective MBR.
+ * Returns 0 on success, or corresponding error otherwise.
+ */
+static int gpt_write_pmbr(struct fdisk_context *cxt)
+{
+	off_t offset;
+	struct gpt_legacy_mbr *pmbr = NULL;
+
+	assert(cxt);
+	assert(cxt->firstsector);
+
+	pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
+
+	/* zero out the legacy partitions */
+	memset(pmbr->partition_record, 0, sizeof(pmbr->partition_record));
+
+	pmbr->signature = cpu_to_le16(MSDOS_MBR_SIGNATURE);
+	pmbr->partition_record[0].os_type      = EFI_PMBR_OSTYPE;
+	pmbr->partition_record[0].start_sector = 1;
+	pmbr->partition_record[0].end_head     = 0xFE;
+	pmbr->partition_record[0].end_sector   = 0xFF;
+	pmbr->partition_record[0].end_track    = 0xFF;
+	pmbr->partition_record[0].starting_lba = cpu_to_le32(1);
+
+	/*
+	 * Set size_in_lba to the size of the disk minus one. If the size of the disk
+	 * is too large to be represented by a 32bit LBA (2Tb), set it to 0xFFFFFFFF.
+	 */
+	if (cxt->total_sectors - 1 > 0xFFFFFFFFULL)
+		pmbr->partition_record[0].size_in_lba = cpu_to_le32(0xFFFFFFFF);
+	else
+		pmbr->partition_record[0].size_in_lba =
+			cpu_to_le32(cxt->total_sectors - 1UL);
+
+	offset = GPT_PMBR_LBA * cxt->sector_size;
+	if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+		goto fail;
+
+	/* pMBR covers the first sector (LBA) of the disk */
+	if (write_all(cxt->dev_fd, pmbr, cxt->sector_size))
+		goto fail;
+	return 0;
+fail:
+	return -errno;
+}
+
+/*
+ * Writes in-memory GPT and pMBR data to disk.
+ * Returns 0 if successful write, otherwise, a corresponding error.
+ * Any indication of error will abort the operation.
+ */
+static int gpt_write_disklabel(struct fdisk_context *cxt)
+{
+	struct fdisk_gpt_label *gpt;
+	int mbr_type;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	mbr_type = valid_pmbr(cxt);
+
+	/* check that disk is big enough to handle the backup header */
+	if (le64_to_cpu(gpt->pheader->alternative_lba) > cxt->total_sectors)
+		goto err0;
+
+	/* check that the backup header is properly placed */
+	if (le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1)
+		/* TODO: correct this (with user authorization) and write */
+		goto err0;
+
+	if (check_overlap_partitions(gpt->pheader, gpt->ents))
+		goto err0;
+
+	/* recompute CRCs for both headers */
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	/*
+	 * UEFI requires writing in this specific order:
+	 *   1) backup partition tables
+	 *   2) backup GPT header
+	 *   3) primary partition tables
+	 *   4) primary GPT header
+	 *   5) protective MBR
+	 *
+	 * If any write fails, we abort the rest.
+	 */
+	if (gpt_write_partitions(cxt, gpt->bheader, gpt->ents) != 0)
+		goto err1;
+	if (gpt_write_header(cxt, gpt->bheader,
+			     le64_to_cpu(gpt->pheader->alternative_lba)) != 0)
+		goto err1;
+	if (gpt_write_partitions(cxt, gpt->pheader, gpt->ents) != 0)
+		goto err1;
+	if (gpt_write_header(cxt, gpt->pheader, GPT_PRIMARY_PARTITION_TABLE_LBA) != 0)
+		goto err1;
+
+	if (mbr_type == GPT_MBR_HYBRID)
+		fdisk_warnx(cxt, _("The device contains hybrid MBR -- writing GPT only. "
+				   "You have to sync the MBR manually."));
+	else if (gpt_write_pmbr(cxt) != 0)
+		goto err1;
+
+	DBG(LABEL, ul_debug("GPT write success"));
+	return 0;
+err0:
+	DBG(LABEL, ul_debug("GPT write failed: incorrect input"));
+	errno = EINVAL;
+	return -EINVAL;
+err1:
+	DBG(LABEL, ul_debug("GPT write failed: %m"));
+	return -errno;
+}
+
+/*
+ * Verify data integrity and report any found problems for:
+ *   - primary and backup header validations
+ *   - paritition validations
+ */
+static int gpt_verify_disklabel(struct fdisk_context *cxt)
+{
+	int nerror = 0;
+	unsigned int ptnum;
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if (!gpt || !gpt->bheader) {
+		nerror++;
+		fdisk_warnx(cxt, _("Disk does not contain a valid backup header."));
+	}
+
+	if (!gpt_check_header_crc(gpt->pheader, gpt->ents)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid primary header CRC checksum."));
+	}
+	if (gpt->bheader && !gpt_check_header_crc(gpt->bheader, gpt->ents)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid backup header CRC checksum."));
+	}
+
+	if (!gpt_check_entryarr_crc(gpt->pheader, gpt->ents)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid partition entry checksum."));
+	}
+
+	if (!gpt_check_lba_sanity(cxt, gpt->pheader)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid primary header LBA sanity checks."));
+	}
+	if (gpt->bheader && !gpt_check_lba_sanity(cxt, gpt->bheader)) {
+		nerror++;
+		fdisk_warnx(cxt, _("Invalid backup header LBA sanity checks."));
+	}
+
+	if (le64_to_cpu(gpt->pheader->my_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA) {
+		nerror++;
+		fdisk_warnx(cxt, _("MyLBA mismatch with real position at primary header."));
+	}
+	if (gpt->bheader && le64_to_cpu(gpt->bheader->my_lba) != last_lba(cxt)) {
+		nerror++;
+		fdisk_warnx(cxt, _("MyLBA mismatch with real position at backup header."));
+
+	}
+	if (le64_to_cpu(gpt->pheader->alternative_lba) >= cxt->total_sectors) {
+		nerror++;
+		fdisk_warnx(cxt, _("Disk is too small to hold all data."));
+	}
+
+	/*
+	 * if the GPT is the primary table, check the alternateLBA
+	 * to see if it is a valid GPT
+	 */
+	if (gpt->bheader && (le64_to_cpu(gpt->pheader->my_lba) !=
+			     le64_to_cpu(gpt->bheader->alternative_lba))) {
+		nerror++;
+		fdisk_warnx(cxt, _("Primary and backup header mismatch."));
+	}
+
+	ptnum = check_overlap_partitions(gpt->pheader, gpt->ents);
+	if (ptnum) {
+		nerror++;
+		fdisk_warnx(cxt, _("Partition %u overlaps with partition %u."),
+				ptnum, ptnum+1);
+	}
+
+	ptnum = check_too_big_partitions(gpt->pheader, gpt->ents, cxt->total_sectors);
+	if (ptnum) {
+		nerror++;
+		fdisk_warnx(cxt, _("Partition %u is too big for the disk."),
+				ptnum);
+	}
+
+	ptnum = check_start_after_end_paritions(gpt->pheader, gpt->ents);
+	if (ptnum) {
+		nerror++;
+		fdisk_warnx(cxt, _("Partition %u ends before it starts."),
+				ptnum);
+	}
+
+	if (!nerror) { /* yay :-) */
+		uint32_t nsegments = 0;
+		uint64_t free_sectors = 0, largest_segment = 0;
+		char *strsz = NULL;
+
+		fdisk_info(cxt, _("No errors detected."));
+		fdisk_info(cxt, _("Header version: %s"), gpt_get_header_revstr(gpt->pheader));
+		fdisk_info(cxt, _("Using %u out of %d partitions."),
+		       partitions_in_use(gpt->pheader, gpt->ents),
+		       le32_to_cpu(gpt->pheader->npartition_entries));
+
+		free_sectors = get_free_sectors(cxt, gpt->pheader, gpt->ents,
+						&nsegments, &largest_segment);
+		if (largest_segment)
+			strsz = size_to_human_string(SIZE_SUFFIX_SPACE | SIZE_SUFFIX_3LETTER,
+					largest_segment * cxt->sector_size);
+
+		fdisk_info(cxt,
+			   P_("A total of %ju free sectors is available in %u segment.",
+			      "A total of %ju free sectors is available in %u segments "
+			      "(the largest is %s).", nsegments),
+			   free_sectors, nsegments, strsz);
+		free(strsz);
+
+	} else
+		fdisk_warnx(cxt,
+			P_("%d error detected.", "%d errors detected.", nerror),
+			nerror);
+
+	return 0;
+}
+
+/* Delete a single GPT partition, specified by partnum. */
+static int gpt_delete_partition(struct fdisk_context *cxt,
+				size_t partnum)
+{
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if (partnum >= cxt->label->nparts_max
+	    ||  partition_unused(&gpt->ents[partnum]))
+		return -EINVAL;
+
+	/* hasta la vista, baby! */
+	memset(&gpt->ents[partnum], 0, sizeof(struct gpt_entry));
+	if (!partition_unused(&gpt->ents[partnum]))
+		return -EINVAL;
+	else {
+		gpt_recompute_crc(gpt->pheader, gpt->ents);
+		gpt_recompute_crc(gpt->bheader, gpt->ents);
+		cxt->label->nparts_cur--;
+		fdisk_label_set_changed(cxt->label, 1);
+	}
+
+	return 0;
+}
+
+
+/* Performs logical checks to add a new partition entry */
+static int gpt_add_partition(
+		struct fdisk_context *cxt,
+		struct fdisk_partition *pa,
+		size_t *partno)
+{
+	uint64_t user_f, user_l;	/* user input ranges for first and last sectors */
+	uint64_t disk_f, disk_l;	/* first and last available sector ranges on device*/
+	uint64_t dflt_f, dflt_l;	/* largest segment (default) */
+	struct gpt_guid typeid;
+	struct fdisk_gpt_label *gpt;
+	struct gpt_header *pheader;
+	struct gpt_entry *e, *ents;
+	struct fdisk_ask *ask = NULL;
+	size_t partnum;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	pheader = gpt->pheader;
+	ents = gpt->ents;
+
+	rc = fdisk_partition_next_partno(pa, cxt, &partnum);
+	if (rc) {
+		DBG(LABEL, ul_debug("GPT failed to get next partno"));
+		return rc;
+	}
+	if (!partition_unused(&ents[partnum])) {
+		fdisk_warnx(cxt, _("Partition %zu is already defined.  "
+			           "Delete it before re-adding it."), partnum +1);
+		return -ERANGE;
+	}
+	if (le32_to_cpu(pheader->npartition_entries) ==
+			partitions_in_use(pheader, ents)) {
+		fdisk_warnx(cxt, _("All partitions are already in use."));
+		return -ENOSPC;
+	}
+	if (!get_free_sectors(cxt, pheader, ents, NULL, NULL)) {
+		fdisk_warnx(cxt, _("No free sectors available."));
+		return -ENOSPC;
+	}
+
+	string_to_guid(pa && pa->type && pa->type->typestr ?
+				pa->type->typestr:
+				GPT_DEFAULT_ENTRY_TYPE, &typeid);
+
+	disk_f = find_first_available(pheader, ents, pheader->first_usable_lba);
+
+	/* if first sector no explicitly defined then ignore small gaps before
+	 * the first partition */
+	if ((!pa || !fdisk_partition_has_start(pa))
+	    && !partition_unused(&ents[0])
+	    && disk_f < gpt_partition_start(&ents[0])) {
+
+		do {
+			uint64_t x;
+			DBG(LABEL, ul_debug("testing first sector %ju", disk_f));
+			disk_f = find_first_available(pheader, ents, disk_f);
+			if (!disk_f)
+				break;
+			x = find_last_free(pheader, ents, disk_f);
+			if (x - disk_f >= cxt->grain / cxt->sector_size)
+				break;
+			DBG(LABEL, ul_debug("first sector %ju addresses to small space, continue...", disk_f));
+			disk_f = x + 1;
+		} while(1);
+
+		if (disk_f == 0)
+			disk_f = find_first_available(pheader, ents, pheader->first_usable_lba);
+	}
+
+	disk_l = find_last_free_sector(pheader, ents);
+
+	/* the default is the largest free space */
+	dflt_f = find_first_in_largest(pheader, ents);
+	dflt_l = find_last_free(pheader, ents, dflt_f);
+
+	/* align the default in range <dflt_f,dflt_l>*/
+	dflt_f = fdisk_align_lba_in_range(cxt, dflt_f, dflt_f, dflt_l);
+
+	/* first sector */
+	if (pa && pa->start_follow_default) {
+		user_f = dflt_f;
+
+	} else if (pa && fdisk_partition_has_start(pa)) {
+		DBG(LABEL, ul_debug("first sector defined: %ju", pa->start));
+		if (pa->start != find_first_available(pheader, ents, pa->start)) {
+			fdisk_warnx(cxt, _("Sector %ju already used."), pa->start);
+			return -ERANGE;
+		}
+		user_f = pa->start;
+	} else {
+		/*  ask by dialog */
+		for (;;) {
+			if (!ask)
+				ask = fdisk_new_ask();
+			else
+				fdisk_reset_ask(ask);
+
+			/* First sector */
+			fdisk_ask_set_query(ask, _("First sector"));
+			fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+			fdisk_ask_number_set_low(ask,     disk_f);	/* minimal */
+			fdisk_ask_number_set_default(ask, dflt_f);	/* default */
+			fdisk_ask_number_set_high(ask,    disk_l);	/* maximal */
+
+			rc = fdisk_do_ask(cxt, ask);
+			if (rc)
+				goto done;
+
+			user_f = fdisk_ask_number_get_result(ask);
+			if (user_f != find_first_available(pheader, ents, user_f)) {
+				fdisk_warnx(cxt, _("Sector %ju already used."), user_f);
+				continue;
+			}
+			break;
+		}
+	}
+
+
+	/* Last sector */
+	dflt_l = find_last_free(pheader, ents, user_f);
+
+	if (pa && pa->end_follow_default) {
+		user_l = dflt_l;
+
+	} else if (pa && fdisk_partition_has_size(pa)) {
+		user_l = user_f + pa->size - 1;
+		DBG(LABEL, ul_debug("size defined: %ju, end: %ju (last possible: %ju)",
+					pa->size, user_l, dflt_l));
+		if (user_l != dflt_l && !pa->size_explicit)
+			user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1;
+
+	} else {
+		for (;;) {
+			if (!ask)
+				ask = fdisk_new_ask();
+			else
+				fdisk_reset_ask(ask);
+
+			fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
+			fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+			fdisk_ask_number_set_low(ask,     user_f);	/* minimal */
+			fdisk_ask_number_set_default(ask, dflt_l);	/* default */
+			fdisk_ask_number_set_high(ask,    dflt_l);	/* maximal */
+			fdisk_ask_number_set_base(ask,    user_f);	/* base for relative input */
+			fdisk_ask_number_set_unit(ask,    cxt->sector_size);
+
+			rc = fdisk_do_ask(cxt, ask);
+			if (rc)
+				goto done;
+
+			user_l = fdisk_ask_number_get_result(ask);
+			if (fdisk_ask_number_is_relative(ask)) {
+				user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1;
+
+				/* no space for anything useful, use all space
+				if (user_l + (cxt->grain / cxt->sector_size) > dflt_l)
+					user_l = dflt_l;
+				*/
+			}
+
+			if (user_l > user_f && user_l <= disk_l)
+				break;
+		}
+	}
+
+
+	if (user_f > user_l || partnum >= cxt->label->nparts_max) {
+		fdisk_warnx(cxt, _("Could not create partition %zu"), partnum + 1);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	assert(!FDISK_IS_UNDEF(user_l));
+	assert(!FDISK_IS_UNDEF(user_f));
+
+	e = &ents[partnum];
+	e->lba_end = cpu_to_le64(user_l);
+	e->lba_start = cpu_to_le64(user_f);
+
+	gpt_entry_set_type(e, &typeid);
+
+	if (pa && pa->uuid) {
+		/* Sometimes it's necessary to create a copy of the PT and
+		 * reuse already defined UUID
+		 */
+		rc = gpt_entry_set_uuid(e, pa->uuid);
+		if (rc)
+			goto done;
+	} else {
+		/* Any time a new partition entry is created a new GUID must be
+		 * generated for that partition, and every partition is guaranteed
+		 * to have a unique GUID.
+		 */
+		uuid_generate_random((unsigned char *) &e->partition_guid);
+		swap_efi_guid(&e->partition_guid);
+	}
+
+	if (pa && pa->name && *pa->name)
+		gpt_entry_set_name(e, pa->name);
+	if (pa && pa->attrs)
+		gpt_entry_attrs_from_string(cxt, e, pa->attrs);
+
+	DBG(LABEL, ul_debug("GPT new partition: partno=%zu, start=%ju, end=%ju, size=%ju",
+				partnum,
+				gpt_partition_start(e),
+				gpt_partition_end(e),
+				gpt_partition_size(e)));
+
+	gpt_recompute_crc(gpt->pheader, ents);
+	gpt_recompute_crc(gpt->bheader, ents);
+
+	/* report result */
+	{
+		struct fdisk_parttype *t;
+
+		cxt->label->nparts_cur++;
+		fdisk_label_set_changed(cxt->label, 1);
+
+		t = gpt_partition_parttype(cxt, &ents[partnum]);
+		fdisk_info_new_partition(cxt, partnum + 1, user_f, user_l, t);
+		fdisk_unref_parttype(t);
+	}
+
+	rc = 0;
+	if (partno)
+		*partno = partnum;
+done:
+	fdisk_unref_ask(ask);
+	return rc;
+}
+
+/*
+ * Create a new GPT disklabel - destroys any previous data.
+ */
+static int gpt_create_disklabel(struct fdisk_context *cxt)
+{
+	int rc = 0;
+	ssize_t esz = 0;
+	char str[37];
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	/* label private stuff has to be empty, see gpt_deinit() */
+	assert(gpt->pheader == NULL);
+	assert(gpt->bheader == NULL);
+
+	/*
+	 * When no header, entries or pmbr is set, we're probably
+	 * dealing with a new, empty disk - so always allocate memory
+	 * to deal with the data structures whatever the case is.
+	 */
+	rc = gpt_mknew_pmbr(cxt);
+	if (rc < 0)
+		goto done;
+
+	/* primary */
+	gpt->pheader = calloc(1, sizeof(*gpt->pheader));
+	if (!gpt->pheader) {
+		rc = -ENOMEM;
+		goto done;
+	}
+	rc = gpt_mknew_header(cxt, gpt->pheader, GPT_PRIMARY_PARTITION_TABLE_LBA);
+	if (rc < 0)
+		goto done;
+
+	/* backup ("copy" primary) */
+	gpt->bheader = calloc(1, sizeof(*gpt->bheader));
+	if (!gpt->bheader) {
+		rc = -ENOMEM;
+		goto done;
+	}
+	rc = gpt_mknew_header_from_bkp(cxt, gpt->bheader,
+			last_lba(cxt), gpt->pheader);
+	if (rc < 0)
+		goto done;
+
+	esz = le32_to_cpu(gpt->pheader->npartition_entries) *
+	      le32_to_cpu(gpt->pheader->sizeof_partition_entry);
+	gpt->ents = calloc(1, esz);
+	if (!gpt->ents) {
+		rc = -ENOMEM;
+		goto done;
+	}
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries);
+	cxt->label->nparts_cur = 0;
+
+	guid_to_string(&gpt->pheader->disk_guid, str);
+	fdisk_label_set_changed(cxt->label, 1);
+	fdisk_info(cxt, _("Created a new GPT disklabel (GUID: %s)."), str);
+done:
+	return rc;
+}
+
+static int gpt_get_disklabel_id(struct fdisk_context *cxt, char **id)
+{
+	struct fdisk_gpt_label *gpt;
+	char str[37];
+
+	assert(cxt);
+	assert(id);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	guid_to_string(&gpt->pheader->disk_guid, str);
+
+	*id = strdup(str);
+	if (!*id)
+		return -ENOMEM;
+	return 0;
+}
+
+static int gpt_set_disklabel_id(struct fdisk_context *cxt)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_guid uuid;
+	char *str, *old, *new;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	if (fdisk_ask_string(cxt,
+			_("Enter new disk UUID (in 8-4-4-4-12 format)"), &str))
+		return -EINVAL;
+
+	rc = string_to_guid(str, &uuid);
+	free(str);
+
+	if (rc) {
+		fdisk_warnx(cxt, _("Failed to parse your UUID."));
+		return rc;
+	}
+
+	gpt_get_disklabel_id(cxt, &old);
+
+	gpt->pheader->disk_guid = uuid;
+	gpt->bheader->disk_guid = uuid;
+
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+	gpt_get_disklabel_id(cxt, &new);
+
+	fdisk_info(cxt, _("Disk identifier changed from %s to %s."), old, new);
+
+	free(old);
+	free(new);
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int gpt_part_is_used(struct fdisk_context *cxt, size_t i)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_entry *e;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+
+	if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
+		return 0;
+	e = &gpt->ents[i];
+
+	return !partition_unused(e) || gpt_partition_start(e);
+}
+
+/**
+ * fdisk_gpt_is_hybrid:
+ * @cxt: context
+ *
+ * The regular GPT contains PMBR (dummy protective MBR) where the protective
+ * MBR does not address any partitions.
+ *
+ * Hybrid GPT contains regular MBR where this partition table addresses the
+ * same partitions as GPT. It's recommended to not use hybrid GPT due to MBR
+ * limits.
+ *
+ * The libfdisk does not provide functionality to sync GPT and MBR, you have to
+ * directly access and modify (P)MBR (see fdisk_new_nested_context()).
+ *
+ * Returns: 1 if partition table detected as hybrid otherwise return 0
+ */
+int fdisk_gpt_is_hybrid(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return valid_pmbr(cxt) == GPT_MBR_HYBRID;
+}
+
+static int gpt_toggle_partition_flag(
+		struct fdisk_context *cxt,
+		size_t i,
+		unsigned long flag)
+{
+	struct fdisk_gpt_label *gpt;
+	uint64_t attrs, tmp;
+	char *bits;
+	const char *name = NULL;
+	int bit = -1, rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	DBG(LABEL, ul_debug("GPT entry attribute change requested partno=%zu", i));
+	gpt = self_label(cxt);
+
+	if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
+		return -EINVAL;
+
+	attrs = le64_to_cpu(gpt->ents[i].attrs);
+	bits = (char *) &attrs;
+
+	switch (flag) {
+	case GPT_FLAG_REQUIRED:
+		bit = GPT_ATTRBIT_REQ;
+		name = GPT_ATTRSTR_REQ;
+		break;
+	case GPT_FLAG_NOBLOCK:
+		bit = GPT_ATTRBIT_NOBLOCK;
+		name = GPT_ATTRSTR_NOBLOCK;
+		break;
+	case GPT_FLAG_LEGACYBOOT:
+		bit = GPT_ATTRBIT_LEGACY;
+		name = GPT_ATTRSTR_LEGACY;
+		break;
+	case GPT_FLAG_GUIDSPECIFIC:
+		rc = fdisk_ask_number(cxt, 48, 48, 63, _("Enter GUID specific bit"), &tmp);
+		if (rc)
+			return rc;
+		bit = tmp;
+		break;
+	default:
+		/* already specified PT_FLAG_GUIDSPECIFIC bit */
+		if (flag >= 48 && flag <= 63) {
+			bit = flag;
+			flag = GPT_FLAG_GUIDSPECIFIC;
+		}
+		break;
+	}
+
+	if (bit < 0) {
+		fdisk_warnx(cxt, _("failed to toggle unsupported bit %lu"), flag);
+		return -EINVAL;
+	}
+
+	if (!isset(bits, bit))
+		setbit(bits, bit);
+	else
+		clrbit(bits, bit);
+
+	gpt->ents[i].attrs = cpu_to_le64(attrs);
+
+	if (flag == GPT_FLAG_GUIDSPECIFIC)
+		fdisk_info(cxt, isset(bits, bit) ?
+			_("The GUID specific bit %d on partition %zu is enabled now.") :
+			_("The GUID specific bit %d on partition %zu is disabled now."),
+			bit, i + 1);
+	else
+		fdisk_info(cxt, isset(bits, bit) ?
+			_("The %s flag on partition %zu is enabled now.") :
+			_("The %s flag on partition %zu is disabled now."),
+			name, i + 1);
+
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+static int gpt_entry_cmp_start(const void *a, const void *b)
+{
+	struct gpt_entry *ae = (struct gpt_entry *) a,
+			 *be = (struct gpt_entry *) b;
+	int au = partition_unused(ae),
+	    bu = partition_unused(be);
+
+	if (au && bu)
+		return 0;
+	if (au)
+		return 1;
+	if (bu)
+		return -1;
+
+	return cmp_numbers(gpt_partition_start(ae), gpt_partition_start(be));
+}
+
+/* sort partition by start sector */
+static int gpt_reorder(struct fdisk_context *cxt)
+{
+	struct fdisk_gpt_label *gpt;
+	size_t nparts;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	nparts = le32_to_cpu(gpt->pheader->npartition_entries);
+
+	qsort(gpt->ents, nparts, sizeof(struct gpt_entry),
+			gpt_entry_cmp_start);
+
+	gpt_recompute_crc(gpt->pheader, gpt->ents);
+	gpt_recompute_crc(gpt->bheader, gpt->ents);
+	fdisk_label_set_changed(cxt->label, 1);
+
+	fdisk_info(cxt, _("Done."));
+	return 0;
+}
+
+static int gpt_reset_alignment(struct fdisk_context *cxt)
+{
+	struct fdisk_gpt_label *gpt;
+	struct gpt_header *h;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, GPT));
+
+	gpt = self_label(cxt);
+	h = gpt ? gpt->pheader : NULL;
+
+	if (h) {
+		/* always follow existing table */
+		cxt->first_lba = h->first_usable_lba;
+		cxt->last_lba  = h->last_usable_lba;
+	} else {
+		/* estimate ranges for GPT */
+		uint64_t first, last;
+
+		count_first_last_lba(cxt, &first, &last);
+
+		if (cxt->first_lba < first)
+			cxt->first_lba = first;
+		if (cxt->last_lba > last)
+			cxt->last_lba = last;
+	}
+
+	return 0;
+}
+/*
+ * Deinitialize fdisk-specific variables
+ */
+static void gpt_deinit(struct fdisk_label *lb)
+{
+	struct fdisk_gpt_label *gpt = (struct fdisk_gpt_label *) lb;
+
+	if (!gpt)
+		return;
+
+	free(gpt->ents);
+	free(gpt->pheader);
+	free(gpt->bheader);
+
+	gpt->ents = NULL;
+	gpt->pheader = NULL;
+	gpt->bheader = NULL;
+}
+
+static const struct fdisk_label_operations gpt_operations =
+{
+	.probe		= gpt_probe_label,
+	.write		= gpt_write_disklabel,
+	.verify		= gpt_verify_disklabel,
+	.create		= gpt_create_disklabel,
+	.list		= gpt_list_disklabel,
+	.locate		= gpt_locate_disklabel,
+	.reorder	= gpt_reorder,
+	.get_id		= gpt_get_disklabel_id,
+	.set_id		= gpt_set_disklabel_id,
+
+	.get_part	= gpt_get_partition,
+	.set_part	= gpt_set_partition,
+	.add_part	= gpt_add_partition,
+	.del_part	= gpt_delete_partition,
+
+	.part_is_used	= gpt_part_is_used,
+	.part_toggle_flag = gpt_toggle_partition_flag,
+
+	.deinit		= gpt_deinit,
+
+	.reset_alignment = gpt_reset_alignment
+};
+
+static const struct fdisk_field gpt_fields[] =
+{
+	/* basic */
+	{ FDISK_FIELD_DEVICE,	N_("Device"),	 10,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	0.1,	FDISK_FIELDFL_EYECANDY },
+	/* expert */
+	{ FDISK_FIELD_TYPEID,	N_("Type-UUID"), 36,	FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_UUID,	N_("UUID"),	 36,	FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_NAME,	N_("Name"),	0.2,	FDISK_FIELDFL_DETAIL },
+	{ FDISK_FIELD_ATTR,	N_("Attrs"),	  0,	FDISK_FIELDFL_DETAIL }
+};
+
+/*
+ * allocates GPT in-memory stuff
+ */
+struct fdisk_label *fdisk_new_gpt_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_gpt_label *gpt;
+
+	assert(cxt);
+
+	gpt = calloc(1, sizeof(*gpt));
+	if (!gpt)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) gpt;
+	lb->name = "gpt";
+	lb->id = FDISK_DISKLABEL_GPT;
+	lb->op = &gpt_operations;
+	lb->parttypes = gpt_parttypes;
+	lb->nparttypes = ARRAY_SIZE(gpt_parttypes);
+
+	lb->fields = gpt_fields;
+	lb->nfields = ARRAY_SIZE(gpt_fields);
+
+	return lb;
+}
diff --git a/libblkid/libfdisk/src/init.c b/libblkid/libfdisk/src/init.c
new file mode 100644
index 0000000..61acb0a
--- /dev/null
+++ b/libblkid/libfdisk/src/init.c
@@ -0,0 +1,55 @@
+
+#include "fdiskP.h"
+
+
+/**
+ * SECTION: init
+ * @title: Library initialization
+ * @short_description: initialize debug stuff
+ *
+ */
+
+UL_DEBUG_DEFINE_MASK(libfdisk);
+UL_DEBUG_DEFINE_MASKNAMES(libfdisk) =
+{
+	{ "all",	LIBFDISK_DEBUG_ALL,	"info about all subsystems" },
+	{ "ask",	LIBFDISK_DEBUG_ASK,	"fdisk dialogs" },
+	{ "help",	LIBFDISK_DEBUG_HELP,	"this help" },
+	{ "cxt",	LIBFDISK_DEBUG_CXT,	"library context (handler)" },
+	{ "label",	LIBFDISK_DEBUG_LABEL,	"disk label utils" },
+	{ "part",	LIBFDISK_DEBUG_PART,	"partition utils" },
+	{ "parttype",	LIBFDISK_DEBUG_PARTTYPE,"partition type utils" },
+	{ "script",	LIBFDISK_DEBUG_SCRIPT,	"sfdisk-like scripts" },
+	{ "tab",	LIBFDISK_DEBUG_TAB,	"table utils"},
+	{ NULL, 0 }
+};
+
+/**
+ * fdisk_init_debug:
+ * @mask: debug mask (0xffff to enable full debuging)
+ *
+ * If the @mask is not specified then this function reads
+ * LIBFDISK_DEBUG environment variable to get the mask.
+ *
+ * Already initialized debugging stuff cannot be changed. It does not
+ * have effect to call this function twice.
+ *
+ * It's strongly recommended to use fdisk_init_debug(0) in your code.
+ */
+void fdisk_init_debug(int mask)
+{
+	if (libfdisk_debug_mask)
+		return;
+
+	__UL_INIT_DEBUG(libfdisk, LIBFDISK_DEBUG_, mask, LIBFDISK_DEBUG);
+
+
+	if (libfdisk_debug_mask != LIBFDISK_DEBUG_INIT
+	    && libfdisk_debug_mask != (LIBFDISK_DEBUG_HELP|LIBFDISK_DEBUG_INIT)) {
+
+		DBG(INIT, ul_debug("library debug mask: 0x%04x", libfdisk_debug_mask));
+	}
+
+	ON_DBG(HELP, ul_debug_print_masks("LIBFDISK_DEBUG",
+				UL_DEBUG_MASKNAMES(libfdisk)));
+}
diff --git a/libblkid/libfdisk/src/iter.c b/libblkid/libfdisk/src/iter.c
new file mode 100644
index 0000000..9a0b080
--- /dev/null
+++ b/libblkid/libfdisk/src/iter.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: iter
+ * @title: Iterator
+ * @short_description: unified iterator
+ *
+ * The iterator keeps the direction and the last position for access to the
+ * internal library tables/lists.
+ *
+ * It's very unusual to use the same iterator on multiple places in your
+ * application or share the same iterator, for this purpose libfdisk does not
+ * provide reference counting for this object. It's recommended to initialize
+ * the iterator by fdisk_new_iter() at begin of your function and then
+ * fdisk_free_iter() before you return from the function. 
+ *
+ * Don't forget to call fdisk_reset_iter() if you want to use the iterator more
+ * than once.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "fdiskP.h"
+
+/**
+ * fdisk_new_iter:
+ * @direction: FDISK_INTER_{FOR,BACK}WARD direction
+ *
+ * Returns: newly allocated generic libmount iterator.
+ */
+struct fdisk_iter *fdisk_new_iter(int direction)
+{
+	struct fdisk_iter *itr = calloc(1, sizeof(*itr));
+	if (!itr)
+		return NULL;
+	itr->direction = direction;
+	return itr;
+}
+
+/**
+ * fdisk_free_iter:
+ * @itr: iterator pointer
+ *
+ * Deallocates the iterator.
+ */
+void fdisk_free_iter(struct fdisk_iter *itr)
+{
+	free(itr);
+}
+
+/**
+ * fdisk_reset_iter:
+ * @itr: iterator pointer
+ * @direction: FDISK_INTER_{FOR,BACK}WARD or -1 to keep the direction unchanged
+ *
+ * Resets the iterator.
+ */
+void fdisk_reset_iter(struct fdisk_iter *itr, int direction)
+{
+	if (direction == -1)
+		direction = itr->direction;
+
+	memset(itr, 0, sizeof(*itr));
+	itr->direction = direction;
+}
+
+/**
+ * fdisk_iter_get_direction:
+ * @itr: iterator pointer
+ *
+ * Returns: FDISK_INTER_{FOR,BACK}WARD
+ */
+int fdisk_iter_get_direction(struct fdisk_iter *itr)
+{
+	return itr->direction;
+}
diff --git a/libblkid/libfdisk/src/label.c b/libblkid/libfdisk/src/label.c
new file mode 100644
index 0000000..750cfca
--- /dev/null
+++ b/libblkid/libfdisk/src/label.c
@@ -0,0 +1,569 @@
+
+#include "fdiskP.h"
+
+
+/**
+ * SECTION: label
+ * @title: Label
+ * @short_description: disk label (PT) specific data and functions
+ *
+ * The fdisk_new_context() initializes all label drivers, and allocate
+ * per-label specific data struct. This concept allows to store label specific
+ * settings to the label driver independently on the currently active label
+ * driver. Note that label struct cannot be deallocated, so there is no
+ * reference counting for fdisk_label objects. All is destroyed by
+ * fdisk_unref_context() only.
+ *
+ * Anyway, all label drives share in-memory first sector. The function
+ * fdisk_create_disklabel() overwrites the sector. But it's possible that
+ * label driver also uses another buffers, for example GPT uses more than only
+ * the first sector.
+ *
+ * All label operations are in-memory only, except fdisk_write_disklabel().
+ *
+ * All functions that use "struct fdisk_context" rather than "struct
+ * fdisk_label" use the currently active label driver.
+ */
+
+
+int fdisk_probe_labels(struct fdisk_context *cxt)
+{
+	size_t i;
+
+	cxt->label = NULL;
+
+	for (i = 0; i < cxt->nlabels; i++) {
+		struct fdisk_label *lb = cxt->labels[i];
+		struct fdisk_label *org = fdisk_get_label(cxt, NULL);
+		int rc;
+
+		if (!lb->op->probe)
+			continue;
+		if (lb->disabled) {
+			DBG(CXT, ul_debugobj(cxt, "%s: disabled -- ignore", lb->name));
+			continue;
+		}
+		DBG(CXT, ul_debugobj(cxt, "probing for %s", lb->name));
+
+		cxt->label = lb;
+		rc = lb->op->probe(cxt);
+		cxt->label = org;
+
+		if (rc != 1) {
+			if (lb->op->deinit)
+				lb->op->deinit(lb);	/* for sure */
+			continue;
+		}
+
+		__fdisk_switch_label(cxt, lb);
+		return 0;
+	}
+
+	DBG(CXT, ul_debugobj(cxt, "no label found"));
+	return 1; /* not found */
+}
+
+/**
+ * fdisk_label_get_name:
+ * @lb: label
+ *
+ * Returns: label name
+ */
+const char *fdisk_label_get_name(const struct fdisk_label *lb)
+{
+	return lb ? lb->name : NULL;
+}
+
+/**
+ * fdisk_label_is_labeltype:
+ * @lb: label
+ *
+ * Returns: FDISK_DISKLABEL_*.
+ */
+int fdisk_label_get_type(const struct fdisk_label *lb)
+{
+	return lb->id;
+}
+
+/**
+ * fdisk_label_require_geometry:
+ * @lb: label
+ *
+ * Returns: 1 if label requires CHS geometry
+ */
+int fdisk_label_require_geometry(const struct fdisk_label *lb)
+{
+	assert(lb);
+
+	return lb->flags & FDISK_LABEL_FL_REQUIRE_GEOMETRY ? 1 : 0;
+}
+
+/**
+ * fdisk_label_get_fields_ids
+ * @lb: label (or NULL for the current label)
+ * @cxt: context
+ * @ids: returns allocated array with FDISK_FIELD_* IDs
+ * @nids: returns number of items in fields
+ *
+ * This function returns the default fields for the label.
+ *
+ * Note that the set of the default fields depends on fdisk_enable_details()
+ * function. If the details are enabled then this function usually returns more
+ * fields.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_label_get_fields_ids(
+		const struct fdisk_label *lb,
+		struct fdisk_context *cxt,
+		int **ids, size_t *nids)
+{
+	size_t i, n;
+	int *c;
+
+	assert(cxt);
+
+	if (!lb)
+		lb = cxt->label;
+	if (!lb)
+		return -EINVAL;
+	if (!lb->fields || !lb->nfields)
+		return -ENOSYS;
+	c = calloc(lb->nfields, sizeof(int));
+	if (!c)
+		return -ENOMEM;
+	for (n = 0, i = 0; i < lb->nfields; i++) {
+		int id = lb->fields[i].id;
+
+		if ((fdisk_is_details(cxt) &&
+				(lb->fields[i].flags & FDISK_FIELDFL_EYECANDY))
+		     || (!fdisk_is_details(cxt) &&
+				(lb->fields[i].flags & FDISK_FIELDFL_DETAIL))
+		     || (id == FDISK_FIELD_SECTORS &&
+			         fdisk_use_cylinders(cxt))
+		     || (id == FDISK_FIELD_CYLINDERS &&
+			         !fdisk_use_cylinders(cxt)))
+			continue;
+
+		c[n++] = id;
+	}
+	if (ids)
+		*ids = c;
+	else
+		free(c);
+	if (nids)
+		*nids = n;
+	return 0;
+}
+
+/**
+ * fdisk_label_get_field:
+ * @lb: label
+ * @id: FDISK_FIELD_*
+ *
+ * The field struct describes data stored in struct fdisk_partition. The info
+ * about data is usable for example to generate human readable output (e.g.
+ * fdisk 'p'rint command). See fdisk_partition_to_stirng() and fdisk code.
+ *
+ * Returns: pointer to static instance of the field.
+ */
+const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id)
+{
+	size_t i;
+
+	assert(lb);
+	assert(id > 0);
+
+	for (i = 0; i < lb->nfields; i++) {
+		if (lb->fields[i].id == id)
+			return &lb->fields[i];
+	}
+
+	return NULL;
+}
+
+/**
+ * fdisk_label_get_field_by_name
+ * @lb: label
+ * @name: field name
+ *
+ * Returns: pointer to static instance of the field.
+ */
+const struct fdisk_field *fdisk_label_get_field_by_name(
+				const struct fdisk_label *lb,
+				const char *name)
+{
+	size_t i;
+
+	assert(lb);
+	assert(name);
+
+	for (i = 0; i < lb->nfields; i++) {
+		if (lb->fields[i].name && strcasecmp(lb->fields[i].name, name) == 0)
+			return &lb->fields[i];
+	}
+
+	return NULL;
+}
+
+
+/**
+ * fdisk_field_get_id:
+ * @field: field instance
+ *
+ * Returns: field Id (FDISK_FIELD_*)
+ */
+int fdisk_field_get_id(const struct fdisk_field *field)
+{
+	return field ? field->id : -EINVAL;
+}
+
+/**
+ * fdisk_field_get_name:
+ * @field: field instance
+ *
+ * Returns: field name
+ */
+const char *fdisk_field_get_name(const struct fdisk_field *field)
+{
+	return field ? field->name : NULL;
+}
+
+/**
+ * fdisk_field_get_width:
+ * @field: field instance
+ *
+ * Returns: libsmartcols compatible width.
+ */
+double fdisk_field_get_width(const struct fdisk_field *field)
+{
+	return field ? field->width : -EINVAL;
+}
+
+/**
+ * fdisk_field_is_number:
+ * @field: field instance
+ *
+ * Returns: 1 if field represent number
+ */
+int fdisk_field_is_number(const struct fdisk_field *field)
+{
+	return field->flags ? field->flags & FDISK_FIELDFL_NUMBER : 0;
+}
+
+
+/**
+ * fdisk_write_disklabel:
+ * @cxt: fdisk context
+ *
+ * Write in-memory changes to disk. Be careful!
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_write_disklabel(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label || cxt->readonly)
+		return -EINVAL;
+	if (!cxt->label->op->write)
+		return -ENOSYS;
+	return cxt->label->op->write(cxt);
+}
+
+/**
+ * fdisk_verify_disklabel:
+ * @cxt: fdisk context
+ *
+ * Verifies the partition table.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_verify_disklabel(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->verify)
+		return -ENOSYS;
+	if (fdisk_missing_geometry(cxt))
+		return -EINVAL;
+
+	return cxt->label->op->verify(cxt);
+}
+
+/**
+ * fdisk_list_disklabel:
+ * @cxt: fdisk context
+ *
+ * Lists details about disklabel, but no partitions.
+ *
+ * This function uses libfdisk ASK interface to print data. The details about
+ * partitions table are printed by FDISK_ASKTYPE_INFO.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_list_disklabel(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->list)
+		return -ENOSYS;
+
+	return cxt->label->op->list(cxt);
+}
+
+/**
+ * fdisk_create_disklabel:
+ * @cxt: fdisk context
+ * @name: label name
+ *
+ * Creates a new disk label of type @name. If @name is NULL, then it will
+ * create a default system label type, either SUN or DOS. The function
+ * automaticaly switches the current label driver to @name. The function
+ * fdisk_get_label() returns the current label driver.
+ *
+ * The function modifies in-memory data only.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name)
+{
+	int haslabel = 0;
+	struct fdisk_label *lb;
+
+	if (!cxt)
+		return -EINVAL;
+
+	if (!name) { /* use default label creation */
+#ifdef __sparc__
+		name = "sun";
+#else
+		name = "dos";
+#endif
+	}
+
+	if (cxt->label) {
+		fdisk_deinit_label(cxt->label);
+		haslabel = 1;
+	}
+
+	lb = fdisk_get_label(cxt, name);
+	if (!lb || lb->disabled)
+		return -EINVAL;
+	if (!lb->op->create)
+		return -ENOSYS;
+
+	__fdisk_switch_label(cxt, lb);
+
+	if (haslabel && !cxt->parent)
+		fdisk_reset_device_properties(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "create a new %s label", lb->name));
+	return cxt->label->op->create(cxt);
+}
+
+/**
+ * fdisk_locate_disklabel:
+ * @cxt: context
+ * @n: N item
+ * @name: return item name
+ * @offset: return offset where is item
+ * @size: of the item
+ *
+ * Locate disklabel and returns info about @n item of the label. For example
+ * GPT is composed from two items, PMBR and GPT, n=0 return offset to PMBR and n=1
+ * return offset to GPT. For more details see 'D' expect fdisk command.
+ *
+ * Returns: 0 on succes, <0 on error, 1 no more items.
+ */
+int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name,
+			   off_t *offset, size_t *size)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->locate)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "locating %d chunk of %s.", n, cxt->label->name));
+	return cxt->label->op->locate(cxt, n, name, offset, size);
+}
+
+
+/**
+ * fdisk_get_disklabel_id:
+ * @cxt: fdisk context
+ * @id: returns pointer to allocated string (MBR Id or GPT dirk UUID)
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->get_id)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "asking for disk %s ID", cxt->label->name));
+	return cxt->label->op->get_id(cxt, id);
+}
+
+/**
+ * fdisk_set_disklabel_id:
+ * @cxt: fdisk context
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_set_disklabel_id(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->set_id)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "setting %s disk ID", cxt->label->name));
+	return cxt->label->op->set_id(cxt);
+}
+
+/**
+ * fdisk_set_partition_type:
+ * @cxt: fdisk context
+ * @partnum: partition number
+ * @t: new type
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_set_partition_type(struct fdisk_context *cxt,
+			     size_t partnum,
+			     struct fdisk_parttype *t)
+{
+	if (!cxt || !cxt->label || !t)
+		return -EINVAL;
+
+
+	if (cxt->label->op->set_part) {
+		struct fdisk_partition *pa = fdisk_new_partition();
+		int rc;
+
+		if (!pa)
+			return -ENOMEM;
+		fdisk_partition_set_type(pa, t);
+
+		DBG(CXT, ul_debugobj(cxt, "partition: %zd: set type", partnum));
+		rc = cxt->label->op->set_part(cxt, partnum, pa);
+		fdisk_unref_partition(pa);
+		return rc;
+	}
+
+	return -ENOSYS;
+}
+
+
+/**
+ * fdisk_toggle_partition_flag:
+ * @cxt: fdisk context
+ * @partnum: partition number
+ * @flag: flag ID
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_toggle_partition_flag(struct fdisk_context *cxt,
+			       size_t partnum,
+			       unsigned long flag)
+{
+	int rc;
+
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->part_toggle_flag)
+		return -ENOSYS;
+
+	rc = cxt->label->op->part_toggle_flag(cxt, partnum, flag);
+
+	DBG(CXT, ul_debugobj(cxt, "partition: %zd: toggle: 0x%04lx [rc=%d]", partnum, flag, rc));
+	return rc;
+}
+
+/**
+ * fdisk_reorder_partitions
+ * @cxt: fdisk context
+ *
+ * Sort partitions according to the partition start sector.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_reorder_partitions(struct fdisk_context *cxt)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->reorder)
+		return -ENOSYS;
+
+	return cxt->label->op->reorder(cxt);
+}
+
+/*
+ * Resets the current used label driver to initial state
+ */
+void fdisk_deinit_label(struct fdisk_label *lb)
+{
+	assert(lb);
+
+	/* private label information */
+	if (lb->op->deinit)
+		lb->op->deinit(lb);
+}
+
+/**
+ * fdisk_label_set_changed:
+ * @lb: label
+ * @changed: 0/1
+ *
+ * Marks in-memory data as changed, to force fdisk_write_disklabel() to write
+ * to device. This should be unnecessar by default, the library keeps track
+ * about changes.
+ */
+void fdisk_label_set_changed(struct fdisk_label *lb, int changed)
+{
+	assert(lb);
+	lb->changed = changed ? 1 : 0;
+}
+
+/**
+ * fdisk_label_is_changed:
+ * @lb: label
+ *
+ * Returns: 1 if in-memory data has been changed.
+ */
+int fdisk_label_is_changed(const struct fdisk_label *lb)
+{
+	assert(lb);
+	return lb ? lb->changed : 0;
+}
+
+/**
+ * fdisk_label_set_disabled:
+ * @lb: label
+ * @disabled: 0 or 1
+ *
+ * Mark label as disabled, then libfdisk is going to ignore the label when
+ * probe device for labels.
+ */
+void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled)
+{
+	assert(lb);
+
+	DBG(LABEL, ul_debug("%s label %s",
+				lb->name,
+				disabled ? "DISABLED" : "ENABLED"));
+	lb->disabled = disabled ? 1 : 0;
+}
+
+/**
+ * fdisk_label_is_disabled:
+ * @lb: label
+ *
+ * Returns: 1 if label driver disabled.
+ */
+int fdisk_label_is_disabled(const struct fdisk_label *lb)
+{
+	assert(lb);
+	return lb ? lb->disabled : 0;
+}
diff --git a/libblkid/libfdisk/src/libfdisk.h b/libblkid/libfdisk/src/libfdisk.h
new file mode 100644
index 0000000..844e17e
--- /dev/null
+++ b/libblkid/libfdisk/src/libfdisk.h
@@ -0,0 +1,579 @@
+/*
+ * libfdisk.h - libfdisk API
+ *
+ * Copyright (C) 2012-2014 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 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 _LIBFDISK_H
+#define _LIBFDISK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+/**
+ * LIBFDISK_VERSION:
+ *
+ * Library version string
+ */
+#define LIBFDISK_VERSION   "2.25.0"
+
+/**
+ * fdisk_context:
+ *
+ * Basic library handler.
+ */
+struct fdisk_context;
+
+/**
+ * fdisk_label:
+ *
+ * Disk label specific driver and setting.
+ */
+struct fdisk_label;
+
+/**
+ * fdisk_parttype:
+ *
+ * Partition type.
+ */
+struct fdisk_parttype;
+
+/**
+ * fdisk_partition:
+ *
+ * Partition abstraction (and template).
+ */
+struct fdisk_partition;
+
+/**
+ * fdisk_ask:
+ *
+ * Ask API handler for dialogs with users.
+ */
+struct fdisk_ask;
+
+/**
+ * fdisk_iter:
+ *
+ * Unified iterator.
+ */
+struct fdisk_iter;
+
+/**
+ * fdisk_table:
+ *
+ * Container for fdisk_partition objects
+ */
+struct fdisk_table;
+
+/**
+ * fdisk_field
+ *
+ * Output field description.
+ */
+struct fdisk_field;
+
+/**
+ * fdisk_script
+ *
+ * library handler for sfdisk compatible scripts
+ */
+struct fdisk_script;
+
+/**
+ * fdisk_sector_t
+ *
+ * LBA adresses type
+ */
+typedef uint64_t fdisk_sector_t;
+
+/**
+ * fdisk_labeltype:
+ *
+ * Supported partition table types (labels)
+ */
+enum fdisk_labeltype {
+	FDISK_DISKLABEL_DOS = (1 << 1),
+	FDISK_DISKLABEL_SUN = (1 << 2),
+	FDISK_DISKLABEL_SGI = (1 << 3),
+	FDISK_DISKLABEL_BSD = (1 << 4),
+	FDISK_DISKLABEL_GPT = (1 << 5)
+};
+
+/**
+ * fdisk_asktype:
+ *
+ * Ask API dialog types
+ */
+enum fdisk_asktype {
+	FDISK_ASKTYPE_NONE = 0,
+	FDISK_ASKTYPE_NUMBER,
+	FDISK_ASKTYPE_OFFSET,
+	FDISK_ASKTYPE_WARN,
+	FDISK_ASKTYPE_WARNX,
+	FDISK_ASKTYPE_INFO,
+	FDISK_ASKTYPE_YESNO,
+	FDISK_ASKTYPE_STRING,
+	FDISK_ASKTYPE_MENU
+};
+
+/* init.c */
+extern void fdisk_init_debug(int mask);
+
+/* context.h */
+
+#define FDISK_PLURAL	0
+#define FDISK_SINGULAR	1
+
+struct fdisk_context *fdisk_new_context(void);
+struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent, const char *name);
+void fdisk_unref_context(struct fdisk_context *cxt);
+void fdisk_ref_context(struct fdisk_context *cxt);
+
+struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt);
+size_t fdisk_get_npartitions(struct fdisk_context *cxt);
+
+struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name);
+int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb);
+size_t fdisk_get_nlabels(struct fdisk_context *cxt);
+
+int fdisk_has_label(struct fdisk_context *cxt);
+int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id);
+#define fdisk_is_label(c, x) fdisk_is_labeltype(c, FDISK_DISKLABEL_ ## x)
+
+
+int fdisk_assign_device(struct fdisk_context *cxt,
+			const char *fname, int readonly);
+int fdisk_deassign_device(struct fdisk_context *cxt, int nosync);
+int fdisk_is_readonly(struct fdisk_context *cxt);
+
+int fdisk_enable_details(struct fdisk_context *cxt, int enable);
+int fdisk_is_details(struct fdisk_context *cxt);
+
+int fdisk_enable_listonly(struct fdisk_context *cxt, int enable);
+int fdisk_is_listonly(struct fdisk_context *cxt);
+
+int fdisk_set_unit(struct fdisk_context *cxt, const char *str);
+const char *fdisk_get_unit(struct fdisk_context *cxt, int n);
+int fdisk_use_cylinders(struct fdisk_context *cxt);
+unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt);
+
+unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_physector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_sector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt);
+unsigned long fdisk_get_grain_size(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt);
+const char *fdisk_get_devname(struct fdisk_context *cxt);
+int fdisk_get_devfd(struct fdisk_context *cxt);
+
+unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt);
+
+
+
+/* parttype.c */
+struct fdisk_parttype *fdisk_new_parttype(void);
+void fdisk_ref_parttype(struct fdisk_parttype *t);
+void fdisk_unref_parttype(struct fdisk_parttype *t);
+int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_code(struct fdisk_parttype *t, int code);
+size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n);
+int fdisk_label_has_code_parttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype_from_code(
+				const struct fdisk_label *lb,
+				unsigned int code);
+struct fdisk_parttype *fdisk_label_get_parttype_from_string(
+				const struct fdisk_label *lb,
+				const char *str);
+struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
+						  const char *typestr);
+struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type);
+struct fdisk_parttype *fdisk_label_parse_parttype(
+				const struct fdisk_label *lb,
+				const char *str);
+const char *fdisk_parttype_get_string(const struct fdisk_parttype *t);
+unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t);
+const char *fdisk_parttype_get_name(const struct fdisk_parttype *t);
+int fdisk_parttype_is_unknown(const struct fdisk_parttype *t);
+
+/* label.c */
+
+/**
+ * fdisk_fieldtype
+ *
+ * Types of fdisk_field
+ */
+enum fdisk_fieldtype {
+	FDISK_FIELD_NONE = 0,
+
+	/* generic */
+	FDISK_FIELD_DEVICE,
+	FDISK_FIELD_START,
+	FDISK_FIELD_END,
+	FDISK_FIELD_SECTORS,
+	FDISK_FIELD_CYLINDERS,
+	FDISK_FIELD_SIZE,
+	FDISK_FIELD_TYPE,
+	FDISK_FIELD_TYPEID,
+
+	/* label specific */
+	FDISK_FIELD_ATTR,
+	FDISK_FIELD_BOOT,
+	FDISK_FIELD_BSIZE,
+	FDISK_FIELD_CPG,
+	FDISK_FIELD_EADDR,
+	FDISK_FIELD_FSIZE,
+	FDISK_FIELD_NAME,
+	FDISK_FIELD_SADDR,
+	FDISK_FIELD_UUID,
+
+	FDISK_NFIELDS		/* must be last */
+};
+
+int fdisk_label_get_type(const struct fdisk_label *lb);
+const char *fdisk_label_get_name(const struct fdisk_label *lb);
+int fdisk_label_require_geometry(const struct fdisk_label *lb);
+
+
+extern int fdisk_write_disklabel(struct fdisk_context *cxt);
+extern int fdisk_verify_disklabel(struct fdisk_context *cxt);
+extern int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name);
+extern int fdisk_list_disklabel(struct fdisk_context *cxt);
+extern int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size);
+
+extern int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id);
+extern int fdisk_set_disklabel_id(struct fdisk_context *cxt);
+
+extern int fdisk_get_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition **pa);
+extern int fdisk_set_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition *pa);
+extern int fdisk_add_partition(struct fdisk_context *cxt, struct fdisk_partition *pa, size_t *partno);
+extern int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno);
+
+extern int fdisk_delete_all_partitions(struct fdisk_context *cxt);
+
+extern int fdisk_set_partition_type(struct fdisk_context *cxt, size_t partnum,
+			     struct fdisk_parttype *t);
+
+
+extern int fdisk_label_get_fields_ids(
+			const struct fdisk_label *lb,
+			struct fdisk_context *cxt,
+			int **ids, size_t *nids);
+
+extern const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id);
+extern const struct fdisk_field *fdisk_label_get_field_by_name(
+			const struct fdisk_label *lb,
+			const char *name);
+
+extern int fdisk_field_get_id(const struct fdisk_field *field);
+extern const char *fdisk_field_get_name(const struct fdisk_field *field);
+extern double fdisk_field_get_width(const struct fdisk_field *field);
+extern int fdisk_field_is_number(const struct fdisk_field *field);
+
+
+extern void fdisk_label_set_changed(struct fdisk_label *lb, int changed);
+extern int fdisk_label_is_changed(const struct fdisk_label *lb);
+
+extern void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled);
+extern int fdisk_label_is_disabled(const struct fdisk_label *lb);
+
+extern int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n);
+
+extern int fdisk_toggle_partition_flag(struct fdisk_context *cxt, size_t partnum, unsigned long flag);
+
+extern struct fdisk_partition *fdisk_new_partition(void);
+extern void fdisk_reset_partition(struct fdisk_partition *pa);
+extern void fdisk_ref_partition(struct fdisk_partition *pa);
+extern void fdisk_unref_partition(struct fdisk_partition *pa);
+extern int fdisk_partition_is_freespace(struct fdisk_partition *pa);
+
+int fdisk_partition_set_start(struct fdisk_partition *pa, uint64_t off);
+int fdisk_partition_unset_start(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_start(struct fdisk_partition *pa);
+int fdisk_partition_has_start(struct fdisk_partition *pa);
+int fdisk_partition_cmp_start(struct fdisk_partition *a,
+			      struct fdisk_partition *b);
+int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable);
+int fdisk_partition_start_is_default(struct fdisk_partition *pa);
+
+int fdisk_partition_set_size(struct fdisk_partition *pa, uint64_t sz);
+int fdisk_partition_unset_size(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_size(struct fdisk_partition *pa);
+int fdisk_partition_has_size(struct fdisk_partition *pa);
+int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable);
+
+int fdisk_partition_has_end(struct fdisk_partition *pa);
+fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa);
+
+int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num);
+int fdisk_partition_unset_partno(struct fdisk_partition *pa);
+size_t fdisk_partition_get_partno(struct fdisk_partition *pa);
+int fdisk_partition_has_partno(struct fdisk_partition *pa);
+int fdisk_partition_cmp_partno(struct fdisk_partition *a,
+	                               struct fdisk_partition *b);
+int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable);
+
+
+extern int fdisk_partition_set_type(struct fdisk_partition *pa, struct fdisk_parttype *type);
+extern struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa);
+extern int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name);
+extern const char *fdisk_partition_get_name(struct fdisk_partition *pa);
+extern int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid);
+extern int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs);
+extern const char *fdisk_partition_get_uuid(struct fdisk_partition *pa);
+extern const char *fdisk_partition_get_attrs(struct fdisk_partition *pa);
+extern int fdisk_partition_is_nested(struct fdisk_partition *pa);
+extern int fdisk_partition_is_container(struct fdisk_partition *pa);
+extern int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent);
+extern int fdisk_partition_is_used(struct fdisk_partition *pa);
+extern int fdisk_partition_is_bootable(struct fdisk_partition *pa);
+extern int fdisk_partition_to_string(struct fdisk_partition *pa,
+				     struct fdisk_context *cxt,
+				     int id, char **data);
+
+int fdisk_partition_next_partno(struct fdisk_partition *pa,
+				       struct fdisk_context *cxt,
+				       size_t *n);
+
+extern int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable);
+extern int fdisk_partition_end_is_default(struct fdisk_partition *pa);
+
+extern int fdisk_reorder_partitions(struct fdisk_context *cxt);
+
+/* table.c */
+extern struct fdisk_table *fdisk_new_table(void);
+extern int fdisk_reset_table(struct fdisk_table *tb);
+extern void fdisk_ref_table(struct fdisk_table *tb);
+extern void fdisk_unref_table(struct fdisk_table *tb);
+extern size_t fdisk_table_get_nents(struct fdisk_table *tb);
+extern int fdisk_table_is_empty(struct fdisk_table *tb);
+extern int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+extern int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+
+extern int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb);
+extern int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb);
+
+extern int fdisk_table_wrong_order(struct fdisk_table *tb);
+extern int fdisk_table_sort_partitions(struct fdisk_table *tb,
+			int (*cmp)(struct fdisk_partition *,
+				   struct fdisk_partition *));
+
+extern int fdisk_table_next_partition(
+			struct fdisk_table *tb,
+			struct fdisk_iter *itr,
+			struct fdisk_partition **pa);
+
+extern struct fdisk_partition *fdisk_table_get_partition(
+			struct fdisk_table *tb,
+			size_t n);
+extern int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb);
+
+/* alignment.c */
+#define FDISK_ALIGN_UP		1
+#define FDISK_ALIGN_DOWN	2
+#define FDISK_ALIGN_NEAREST	3
+
+fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction);
+fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
+				  fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop);
+int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba);
+
+int fdisk_override_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors);
+int fdisk_save_user_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors);
+int fdisk_save_user_sector_size(struct fdisk_context *cxt,
+				unsigned int phy,
+				unsigned int log);
+int fdisk_has_user_device_properties(struct fdisk_context *cxt);
+int fdisk_reset_alignment(struct fdisk_context *cxt);
+int fdisk_reset_device_properties(struct fdisk_context *cxt);
+int fdisk_reread_partition_table(struct fdisk_context *cxt);
+
+/* iter.c */
+enum {
+
+	FDISK_ITER_FORWARD = 0,
+	FDISK_ITER_BACKWARD
+};
+extern struct fdisk_iter *fdisk_new_iter(int direction);
+extern void fdisk_free_iter(struct fdisk_iter *itr);
+extern void fdisk_reset_iter(struct fdisk_iter *itr, int direction);
+extern int fdisk_iter_get_direction(struct fdisk_iter *itr);
+
+
+/* dos.c */
+#define DOS_FLAG_ACTIVE	1
+
+extern int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i);
+extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable);
+extern int fdisk_dos_is_compatible(struct fdisk_label *lb);
+
+/* sun.h */
+extern int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_xcyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_ilfact(struct fdisk_context *cxt);
+extern int fdisk_sun_set_rspeed(struct fdisk_context *cxt);
+extern int fdisk_sun_set_pcylcount(struct fdisk_context *cxt);
+
+/* bsd.c */
+extern int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt);
+extern int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt);
+extern int fdisk_bsd_link_partition(struct fdisk_context *cxt);
+
+/* sgi.h */
+#define SGI_FLAG_BOOT	1
+#define SGI_FLAG_SWAP	2
+extern int fdisk_sgi_set_bootfile(struct fdisk_context *cxt);
+extern int fdisk_sgi_create_info(struct fdisk_context *cxt);
+
+/* gpt */
+
+/* GPT partition attributes */
+enum {
+	/* System partition (disk partitioning utilities must preserve the
+	 * partition as is) */
+	GPT_FLAG_REQUIRED = 1,
+
+	/* EFI firmware should ignore the content of the partition and not try
+	 * to read from it */
+	GPT_FLAG_NOBLOCK,
+
+	/* Legacy BIOS bootable  */
+	GPT_FLAG_LEGACYBOOT,
+
+	/* bites 48-63, Defined and used by the individual partition type.
+	 *
+	 * The flag GPT_FLAG_GUIDSPECIFIC forces libfdisk to ask (by ask API)
+	 * for a bit number. If you want to toggle specific bit and avoid any
+	 * dialog, then use the bit number (in range 48..63). For example:
+	 *
+	 * // start dialog to ask for bit number
+	 * fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC);
+	 *
+	 * // toggle bit 60
+	 * fdisk_toggle_partition_flag(cxt, n, 60);
+	 */
+	GPT_FLAG_GUIDSPECIFIC
+};
+
+extern int fdisk_gpt_is_hybrid(struct fdisk_context *cxt);
+
+
+/* script.c */
+struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt);
+struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
+						 const char *filename);
+void fdisk_ref_script(struct fdisk_script *dp);
+void fdisk_unref_script(struct fdisk_script *dp);
+
+const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name);
+int fdisk_script_set_header(struct fdisk_script *dp, const char *name, const char *data);
+struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp);
+int fdisk_script_get_nlines(struct fdisk_script *dp);
+
+int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt);
+int fdisk_script_write_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz);
+
+int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt);
+
+int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp);
+int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+
+
+/* ask.c */
+#define fdisk_is_ask(a, x) (fdisk_ask_get_type(a) == FDISK_ASKTYPE_ ## x)
+
+int fdisk_set_ask(struct fdisk_context *cxt,
+		int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
+		void *data);
+
+
+void fdisk_ref_ask(struct fdisk_ask *ask);
+void fdisk_unref_ask(struct fdisk_ask *ask);
+const char *fdisk_ask_get_query(struct fdisk_ask *ask);
+int fdisk_ask_get_type(struct fdisk_ask *ask);
+const char *fdisk_ask_number_get_range(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask);
+int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result);
+uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask);
+int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative);
+int fdisk_ask_number_inchars(struct fdisk_ask *ask);
+int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew);
+
+int fdisk_ask_number(struct fdisk_context *cxt,
+		     uintmax_t low,
+		     uintmax_t dflt,
+		     uintmax_t high,
+		     const char *query,
+		     uintmax_t *result);
+char *fdisk_ask_string_get_result(struct fdisk_ask *ask);
+int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result);
+int fdisk_ask_string(struct fdisk_context *cxt,
+		     const char *query,
+		     char **result);
+int fdisk_ask_yesno(struct fdisk_context *cxt,
+		     const char *query,
+		     int *result);
+int fdisk_ask_yesno_get_result(struct fdisk_ask *ask);
+int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result);
+int fdisk_ask_menu_get_default(struct fdisk_ask *ask);
+int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key);
+int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key);
+int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
+			    const char **name, const char **desc);
+size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask);
+int fdisk_ask_print_get_errno(struct fdisk_ask *ask);
+const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask);
+
+int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...);
+
+/* utils.h */
+extern char *fdisk_partname(const char *dev, size_t partno);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFDISK_H */
diff --git a/libblkid/libfdisk/src/libfdisk.h.in b/libblkid/libfdisk/src/libfdisk.h.in
new file mode 100644
index 0000000..f82d5bd
--- /dev/null
+++ b/libblkid/libfdisk/src/libfdisk.h.in
@@ -0,0 +1,579 @@
+/*
+ * libfdisk.h - libfdisk API
+ *
+ * Copyright (C) 2012-2014 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 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 _LIBFDISK_H
+#define _LIBFDISK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+/**
+ * LIBFDISK_VERSION:
+ *
+ * Library version string
+ */
+#define LIBFDISK_VERSION   "@LIBFDISK_VERSION@"
+
+/**
+ * fdisk_context:
+ *
+ * Basic library handler.
+ */
+struct fdisk_context;
+
+/**
+ * fdisk_label:
+ *
+ * Disk label specific driver and setting.
+ */
+struct fdisk_label;
+
+/**
+ * fdisk_parttype:
+ *
+ * Partition type.
+ */
+struct fdisk_parttype;
+
+/**
+ * fdisk_partition:
+ *
+ * Partition abstraction (and template).
+ */
+struct fdisk_partition;
+
+/**
+ * fdisk_ask:
+ *
+ * Ask API handler for dialogs with users.
+ */
+struct fdisk_ask;
+
+/**
+ * fdisk_iter:
+ *
+ * Unified iterator.
+ */
+struct fdisk_iter;
+
+/**
+ * fdisk_table:
+ *
+ * Container for fdisk_partition objects
+ */
+struct fdisk_table;
+
+/**
+ * fdisk_field
+ *
+ * Output field description.
+ */
+struct fdisk_field;
+
+/**
+ * fdisk_script
+ *
+ * library handler for sfdisk compatible scripts
+ */
+struct fdisk_script;
+
+/**
+ * fdisk_sector_t
+ *
+ * LBA adresses type
+ */
+typedef uint64_t fdisk_sector_t;
+
+/**
+ * fdisk_labeltype:
+ *
+ * Supported partition table types (labels)
+ */
+enum fdisk_labeltype {
+	FDISK_DISKLABEL_DOS = (1 << 1),
+	FDISK_DISKLABEL_SUN = (1 << 2),
+	FDISK_DISKLABEL_SGI = (1 << 3),
+	FDISK_DISKLABEL_BSD = (1 << 4),
+	FDISK_DISKLABEL_GPT = (1 << 5)
+};
+
+/**
+ * fdisk_asktype:
+ *
+ * Ask API dialog types
+ */
+enum fdisk_asktype {
+	FDISK_ASKTYPE_NONE = 0,
+	FDISK_ASKTYPE_NUMBER,
+	FDISK_ASKTYPE_OFFSET,
+	FDISK_ASKTYPE_WARN,
+	FDISK_ASKTYPE_WARNX,
+	FDISK_ASKTYPE_INFO,
+	FDISK_ASKTYPE_YESNO,
+	FDISK_ASKTYPE_STRING,
+	FDISK_ASKTYPE_MENU
+};
+
+/* init.c */
+extern void fdisk_init_debug(int mask);
+
+/* context.h */
+
+#define FDISK_PLURAL	0
+#define FDISK_SINGULAR	1
+
+struct fdisk_context *fdisk_new_context(void);
+struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent, const char *name);
+void fdisk_unref_context(struct fdisk_context *cxt);
+void fdisk_ref_context(struct fdisk_context *cxt);
+
+struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt);
+size_t fdisk_get_npartitions(struct fdisk_context *cxt);
+
+struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name);
+int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb);
+size_t fdisk_get_nlabels(struct fdisk_context *cxt);
+
+int fdisk_has_label(struct fdisk_context *cxt);
+int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id);
+#define fdisk_is_label(c, x) fdisk_is_labeltype(c, FDISK_DISKLABEL_ ## x)
+
+
+int fdisk_assign_device(struct fdisk_context *cxt,
+			const char *fname, int readonly);
+int fdisk_deassign_device(struct fdisk_context *cxt, int nosync);
+int fdisk_is_readonly(struct fdisk_context *cxt);
+
+int fdisk_enable_details(struct fdisk_context *cxt, int enable);
+int fdisk_is_details(struct fdisk_context *cxt);
+
+int fdisk_enable_listonly(struct fdisk_context *cxt, int enable);
+int fdisk_is_listonly(struct fdisk_context *cxt);
+
+int fdisk_set_unit(struct fdisk_context *cxt, const char *str);
+const char *fdisk_get_unit(struct fdisk_context *cxt, int n);
+int fdisk_use_cylinders(struct fdisk_context *cxt);
+unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt);
+
+unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_physector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_sector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt);
+unsigned long fdisk_get_grain_size(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt);
+const char *fdisk_get_devname(struct fdisk_context *cxt);
+int fdisk_get_devfd(struct fdisk_context *cxt);
+
+unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt);
+
+
+
+/* parttype.c */
+struct fdisk_parttype *fdisk_new_parttype(void);
+void fdisk_ref_parttype(struct fdisk_parttype *t);
+void fdisk_unref_parttype(struct fdisk_parttype *t);
+int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_code(struct fdisk_parttype *t, int code);
+size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n);
+int fdisk_label_has_code_parttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype_from_code(
+				const struct fdisk_label *lb,
+				unsigned int code);
+struct fdisk_parttype *fdisk_label_get_parttype_from_string(
+				const struct fdisk_label *lb,
+				const char *str);
+struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
+						  const char *typestr);
+struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type);
+struct fdisk_parttype *fdisk_label_parse_parttype(
+				const struct fdisk_label *lb,
+				const char *str);
+const char *fdisk_parttype_get_string(const struct fdisk_parttype *t);
+unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t);
+const char *fdisk_parttype_get_name(const struct fdisk_parttype *t);
+int fdisk_parttype_is_unknown(const struct fdisk_parttype *t);
+
+/* label.c */
+
+/**
+ * fdisk_fieldtype
+ *
+ * Types of fdisk_field
+ */
+enum fdisk_fieldtype {
+	FDISK_FIELD_NONE = 0,
+
+	/* generic */
+	FDISK_FIELD_DEVICE,
+	FDISK_FIELD_START,
+	FDISK_FIELD_END,
+	FDISK_FIELD_SECTORS,
+	FDISK_FIELD_CYLINDERS,
+	FDISK_FIELD_SIZE,
+	FDISK_FIELD_TYPE,
+	FDISK_FIELD_TYPEID,
+
+	/* label specific */
+	FDISK_FIELD_ATTR,
+	FDISK_FIELD_BOOT,
+	FDISK_FIELD_BSIZE,
+	FDISK_FIELD_CPG,
+	FDISK_FIELD_EADDR,
+	FDISK_FIELD_FSIZE,
+	FDISK_FIELD_NAME,
+	FDISK_FIELD_SADDR,
+	FDISK_FIELD_UUID,
+
+	FDISK_NFIELDS		/* must be last */
+};
+
+int fdisk_label_get_type(const struct fdisk_label *lb);
+const char *fdisk_label_get_name(const struct fdisk_label *lb);
+int fdisk_label_require_geometry(const struct fdisk_label *lb);
+
+
+extern int fdisk_write_disklabel(struct fdisk_context *cxt);
+extern int fdisk_verify_disklabel(struct fdisk_context *cxt);
+extern int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name);
+extern int fdisk_list_disklabel(struct fdisk_context *cxt);
+extern int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size);
+
+extern int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id);
+extern int fdisk_set_disklabel_id(struct fdisk_context *cxt);
+
+extern int fdisk_get_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition **pa);
+extern int fdisk_set_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition *pa);
+extern int fdisk_add_partition(struct fdisk_context *cxt, struct fdisk_partition *pa, size_t *partno);
+extern int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno);
+
+extern int fdisk_delete_all_partitions(struct fdisk_context *cxt);
+
+extern int fdisk_set_partition_type(struct fdisk_context *cxt, size_t partnum,
+			     struct fdisk_parttype *t);
+
+
+extern int fdisk_label_get_fields_ids(
+			const struct fdisk_label *lb,
+			struct fdisk_context *cxt,
+			int **ids, size_t *nids);
+
+extern const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id);
+extern const struct fdisk_field *fdisk_label_get_field_by_name(
+			const struct fdisk_label *lb,
+			const char *name);
+
+extern int fdisk_field_get_id(const struct fdisk_field *field);
+extern const char *fdisk_field_get_name(const struct fdisk_field *field);
+extern double fdisk_field_get_width(const struct fdisk_field *field);
+extern int fdisk_field_is_number(const struct fdisk_field *field);
+
+
+extern void fdisk_label_set_changed(struct fdisk_label *lb, int changed);
+extern int fdisk_label_is_changed(const struct fdisk_label *lb);
+
+extern void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled);
+extern int fdisk_label_is_disabled(const struct fdisk_label *lb);
+
+extern int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n);
+
+extern int fdisk_toggle_partition_flag(struct fdisk_context *cxt, size_t partnum, unsigned long flag);
+
+extern struct fdisk_partition *fdisk_new_partition(void);
+extern void fdisk_reset_partition(struct fdisk_partition *pa);
+extern void fdisk_ref_partition(struct fdisk_partition *pa);
+extern void fdisk_unref_partition(struct fdisk_partition *pa);
+extern int fdisk_partition_is_freespace(struct fdisk_partition *pa);
+
+int fdisk_partition_set_start(struct fdisk_partition *pa, uint64_t off);
+int fdisk_partition_unset_start(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_start(struct fdisk_partition *pa);
+int fdisk_partition_has_start(struct fdisk_partition *pa);
+int fdisk_partition_cmp_start(struct fdisk_partition *a,
+			      struct fdisk_partition *b);
+int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable);
+int fdisk_partition_start_is_default(struct fdisk_partition *pa);
+
+int fdisk_partition_set_size(struct fdisk_partition *pa, uint64_t sz);
+int fdisk_partition_unset_size(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_size(struct fdisk_partition *pa);
+int fdisk_partition_has_size(struct fdisk_partition *pa);
+int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable);
+
+int fdisk_partition_has_end(struct fdisk_partition *pa);
+fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa);
+
+int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num);
+int fdisk_partition_unset_partno(struct fdisk_partition *pa);
+size_t fdisk_partition_get_partno(struct fdisk_partition *pa);
+int fdisk_partition_has_partno(struct fdisk_partition *pa);
+int fdisk_partition_cmp_partno(struct fdisk_partition *a,
+	                               struct fdisk_partition *b);
+int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable);
+
+
+extern int fdisk_partition_set_type(struct fdisk_partition *pa, struct fdisk_parttype *type);
+extern struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa);
+extern int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name);
+extern const char *fdisk_partition_get_name(struct fdisk_partition *pa);
+extern int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid);
+extern int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs);
+extern const char *fdisk_partition_get_uuid(struct fdisk_partition *pa);
+extern const char *fdisk_partition_get_attrs(struct fdisk_partition *pa);
+extern int fdisk_partition_is_nested(struct fdisk_partition *pa);
+extern int fdisk_partition_is_container(struct fdisk_partition *pa);
+extern int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent);
+extern int fdisk_partition_is_used(struct fdisk_partition *pa);
+extern int fdisk_partition_is_bootable(struct fdisk_partition *pa);
+extern int fdisk_partition_to_string(struct fdisk_partition *pa,
+				     struct fdisk_context *cxt,
+				     int id, char **data);
+
+int fdisk_partition_next_partno(struct fdisk_partition *pa,
+				       struct fdisk_context *cxt,
+				       size_t *n);
+
+extern int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable);
+extern int fdisk_partition_end_is_default(struct fdisk_partition *pa);
+
+extern int fdisk_reorder_partitions(struct fdisk_context *cxt);
+
+/* table.c */
+extern struct fdisk_table *fdisk_new_table(void);
+extern int fdisk_reset_table(struct fdisk_table *tb);
+extern void fdisk_ref_table(struct fdisk_table *tb);
+extern void fdisk_unref_table(struct fdisk_table *tb);
+extern size_t fdisk_table_get_nents(struct fdisk_table *tb);
+extern int fdisk_table_is_empty(struct fdisk_table *tb);
+extern int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+extern int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+
+extern int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb);
+extern int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb);
+
+extern int fdisk_table_wrong_order(struct fdisk_table *tb);
+extern int fdisk_table_sort_partitions(struct fdisk_table *tb,
+			int (*cmp)(struct fdisk_partition *,
+				   struct fdisk_partition *));
+
+extern int fdisk_table_next_partition(
+			struct fdisk_table *tb,
+			struct fdisk_iter *itr,
+			struct fdisk_partition **pa);
+
+extern struct fdisk_partition *fdisk_table_get_partition(
+			struct fdisk_table *tb,
+			size_t n);
+extern int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb);
+
+/* alignment.c */
+#define FDISK_ALIGN_UP		1
+#define FDISK_ALIGN_DOWN	2
+#define FDISK_ALIGN_NEAREST	3
+
+fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction);
+fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
+				  fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop);
+int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba);
+
+int fdisk_override_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors);
+int fdisk_save_user_geometry(struct fdisk_context *cxt,
+			    unsigned int cylinders,
+			    unsigned int heads,
+			    unsigned int sectors);
+int fdisk_save_user_sector_size(struct fdisk_context *cxt,
+				unsigned int phy,
+				unsigned int log);
+int fdisk_has_user_device_properties(struct fdisk_context *cxt);
+int fdisk_reset_alignment(struct fdisk_context *cxt);
+int fdisk_reset_device_properties(struct fdisk_context *cxt);
+int fdisk_reread_partition_table(struct fdisk_context *cxt);
+
+/* iter.c */
+enum {
+
+	FDISK_ITER_FORWARD = 0,
+	FDISK_ITER_BACKWARD
+};
+extern struct fdisk_iter *fdisk_new_iter(int direction);
+extern void fdisk_free_iter(struct fdisk_iter *itr);
+extern void fdisk_reset_iter(struct fdisk_iter *itr, int direction);
+extern int fdisk_iter_get_direction(struct fdisk_iter *itr);
+
+
+/* dos.c */
+#define DOS_FLAG_ACTIVE	1
+
+extern int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i);
+extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable);
+extern int fdisk_dos_is_compatible(struct fdisk_label *lb);
+
+/* sun.h */
+extern int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_xcyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_ilfact(struct fdisk_context *cxt);
+extern int fdisk_sun_set_rspeed(struct fdisk_context *cxt);
+extern int fdisk_sun_set_pcylcount(struct fdisk_context *cxt);
+
+/* bsd.c */
+extern int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt);
+extern int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt);
+extern int fdisk_bsd_link_partition(struct fdisk_context *cxt);
+
+/* sgi.h */
+#define SGI_FLAG_BOOT	1
+#define SGI_FLAG_SWAP	2
+extern int fdisk_sgi_set_bootfile(struct fdisk_context *cxt);
+extern int fdisk_sgi_create_info(struct fdisk_context *cxt);
+
+/* gpt */
+
+/* GPT partition attributes */
+enum {
+	/* System partition (disk partitioning utilities must preserve the
+	 * partition as is) */
+	GPT_FLAG_REQUIRED = 1,
+
+	/* EFI firmware should ignore the content of the partition and not try
+	 * to read from it */
+	GPT_FLAG_NOBLOCK,
+
+	/* Legacy BIOS bootable  */
+	GPT_FLAG_LEGACYBOOT,
+
+	/* bites 48-63, Defined and used by the individual partition type.
+	 *
+	 * The flag GPT_FLAG_GUIDSPECIFIC forces libfdisk to ask (by ask API)
+	 * for a bit number. If you want to toggle specific bit and avoid any
+	 * dialog, then use the bit number (in range 48..63). For example:
+	 *
+	 * // start dialog to ask for bit number
+	 * fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC);
+	 *
+	 * // toggle bit 60
+	 * fdisk_toggle_partition_flag(cxt, n, 60);
+	 */
+	GPT_FLAG_GUIDSPECIFIC
+};
+
+extern int fdisk_gpt_is_hybrid(struct fdisk_context *cxt);
+
+
+/* script.c */
+struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt);
+struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
+						 const char *filename);
+void fdisk_ref_script(struct fdisk_script *dp);
+void fdisk_unref_script(struct fdisk_script *dp);
+
+const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name);
+int fdisk_script_set_header(struct fdisk_script *dp, const char *name, const char *data);
+struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp);
+int fdisk_script_get_nlines(struct fdisk_script *dp);
+
+int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt);
+int fdisk_script_write_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz);
+
+int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt);
+
+int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp);
+int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+
+
+/* ask.c */
+#define fdisk_is_ask(a, x) (fdisk_ask_get_type(a) == FDISK_ASKTYPE_ ## x)
+
+int fdisk_set_ask(struct fdisk_context *cxt,
+		int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
+		void *data);
+
+
+void fdisk_ref_ask(struct fdisk_ask *ask);
+void fdisk_unref_ask(struct fdisk_ask *ask);
+const char *fdisk_ask_get_query(struct fdisk_ask *ask);
+int fdisk_ask_get_type(struct fdisk_ask *ask);
+const char *fdisk_ask_number_get_range(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask);
+int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result);
+uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask);
+int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative);
+int fdisk_ask_number_inchars(struct fdisk_ask *ask);
+int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew);
+
+int fdisk_ask_number(struct fdisk_context *cxt,
+		     uintmax_t low,
+		     uintmax_t dflt,
+		     uintmax_t high,
+		     const char *query,
+		     uintmax_t *result);
+char *fdisk_ask_string_get_result(struct fdisk_ask *ask);
+int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result);
+int fdisk_ask_string(struct fdisk_context *cxt,
+		     const char *query,
+		     char **result);
+int fdisk_ask_yesno(struct fdisk_context *cxt,
+		     const char *query,
+		     int *result);
+int fdisk_ask_yesno_get_result(struct fdisk_ask *ask);
+int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result);
+int fdisk_ask_menu_get_default(struct fdisk_ask *ask);
+int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key);
+int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key);
+int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
+			    const char **name, const char **desc);
+size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask);
+int fdisk_ask_print_get_errno(struct fdisk_ask *ask);
+const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask);
+
+int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...);
+
+/* utils.h */
+extern char *fdisk_partname(const char *dev, size_t partno);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFDISK_H */
diff --git a/libblkid/libfdisk/src/libfdisk.sym b/libblkid/libfdisk/src/libfdisk.sym
new file mode 100644
index 0000000..bf85d4e
--- /dev/null
+++ b/libblkid/libfdisk/src/libfdisk.sym
@@ -0,0 +1,234 @@
+/*
+ * The symbol versioning ensures that a new application requiring symbol foo;
+ * can't run with old libblkid.so not providing foo;
+ * version info can't enforce this since we never change the SONAME.
+ *
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ */
+MOUNT_2.26 {
+global:
+	fdisk_add_partition;
+	fdisk_align_lba;
+	fdisk_align_lba_in_range;
+	fdisk_apply_script;
+	fdisk_apply_script_headers;
+	fdisk_apply_table;
+	fdisk_ask_get_query;
+	fdisk_ask_get_type;
+	fdisk_ask_menu_get_default;
+	fdisk_ask_menu_get_item;
+	fdisk_ask_menu_get_nitems;
+	fdisk_ask_menu_get_result;
+	fdisk_ask_menu_set_result;
+	fdisk_ask_number;
+	fdisk_ask_number_get_base;
+	fdisk_ask_number_get_default;
+	fdisk_ask_number_get_high;
+	fdisk_ask_number_get_low;
+	fdisk_ask_number_get_range;
+	fdisk_ask_number_get_result;
+	fdisk_ask_number_get_unit;
+	fdisk_ask_number_inchars;
+	fdisk_ask_number_set_relative;
+	fdisk_ask_number_set_result;
+	fdisk_ask_partnum;
+	fdisk_ask_print_get_errno;
+	fdisk_ask_print_get_mesg;
+	fdisk_ask_string;
+	fdisk_ask_string_get_result;
+	fdisk_ask_string_set_result;
+	fdisk_ask_yesno;
+	fdisk_ask_yesno_get_result;
+	fdisk_ask_yesno_set_result;
+	fdisk_assign_device;
+	fdisk_bsd_edit_disklabel;
+	fdisk_bsd_link_partition;
+	fdisk_bsd_write_bootstrap;
+	fdisk_copy_parttype;
+	fdisk_create_disklabel;
+	fdisk_deassign_device;
+	fdisk_delete_all_partitions;
+	fdisk_delete_partition;
+	fdisk_dos_enable_compatible;
+	fdisk_dos_is_compatible;
+	fdisk_dos_move_begin;
+	fdisk_enable_details;
+	fdisk_enable_listonly;
+	fdisk_field_get_id;
+	fdisk_field_get_name;
+	fdisk_field_get_width;
+	fdisk_field_is_number;
+	fdisk_free_iter;
+	fdisk_get_alignment_offset;
+	fdisk_get_devfd;
+	fdisk_get_devname;
+	fdisk_get_disklabel_id;
+	fdisk_get_first_lba;
+	fdisk_get_freespaces;
+	fdisk_get_geom_cylinders;
+	fdisk_get_geom_heads;
+	fdisk_get_geom_sectors;
+	fdisk_get_grain_size;
+	fdisk_get_label;
+	fdisk_get_last_lba;
+	fdisk_get_minimal_iosize;
+	fdisk_get_nlabels;
+	fdisk_get_npartitions;
+	fdisk_get_nsectors;
+	fdisk_get_optimal_iosize;
+	fdisk_get_parent;
+	fdisk_get_partition;
+	fdisk_get_partitions;
+	fdisk_get_physector_size;
+	fdisk_get_script;
+	fdisk_get_sector_size;
+	fdisk_get_unit;
+	fdisk_get_units_per_sector;
+	fdisk_gpt_is_hybrid;
+	fdisk_has_label;
+	fdisk_has_user_device_properties;
+	fdisk_info;
+	fdisk_init_debug;
+	fdisk_is_details;
+	fdisk_is_labeltype;
+	fdisk_is_listonly;
+	fdisk_is_partition_used;
+	fdisk_is_readonly;
+	fdisk_iter_get_direction;
+	fdisk_label_get_field;
+	fdisk_label_get_field_by_name;
+	fdisk_label_get_fields_ids;
+	fdisk_label_get_name;
+	fdisk_label_get_nparttypes;
+	fdisk_label_get_parttype;
+	fdisk_label_get_parttype_from_code;
+	fdisk_label_get_parttype_from_string;
+	fdisk_label_get_type;
+	fdisk_label_has_code_parttypes;
+	fdisk_label_is_changed;
+	fdisk_label_is_disabled;
+	fdisk_label_parse_parttype;
+	fdisk_label_require_geometry;
+	fdisk_label_set_changed;
+	fdisk_label_set_disabled;
+	fdisk_lba_is_phy_aligned;
+	fdisk_list_disklabel;
+	fdisk_locate_disklabel;
+	fdisk_new_context;
+	fdisk_new_iter;
+	fdisk_new_nested_context;
+	fdisk_new_partition;
+	fdisk_new_parttype;
+	fdisk_new_script;
+	fdisk_new_script_from_file;
+	fdisk_new_table;
+	fdisk_new_unknown_parttype;
+	fdisk_next_label;
+	fdisk_override_geometry;
+	fdisk_partition_cmp_partno;
+	fdisk_partition_cmp_start;
+	fdisk_partition_end_follow_default;
+	fdisk_partition_end_is_default;
+	fdisk_partition_get_attrs;
+	fdisk_partition_get_end;
+	fdisk_partition_get_name;
+	fdisk_partition_get_parent;
+	fdisk_partition_get_partno;
+	fdisk_partition_get_size;
+	fdisk_partition_get_start;
+	fdisk_partition_get_type;
+	fdisk_partition_get_uuid;
+	fdisk_partition_has_end;
+	fdisk_partition_has_partno;
+	fdisk_partition_has_size;
+	fdisk_partition_has_start;
+	fdisk_partition_is_bootable;
+	fdisk_partition_is_container;
+	fdisk_partition_is_freespace;
+	fdisk_partition_is_nested;
+	fdisk_partition_is_used;
+	fdisk_partition_next_partno;
+	fdisk_partition_partno_follow_default;
+	fdisk_partition_set_attrs;
+	fdisk_partition_set_name;
+	fdisk_partition_set_partno;
+	fdisk_partition_set_size;
+	fdisk_partition_set_start;
+	fdisk_partition_set_type;
+	fdisk_partition_set_uuid;
+	fdisk_partition_size_explicit;
+	fdisk_partition_start_follow_default;
+	fdisk_partition_start_is_default;
+	fdisk_toggle_partition_flag;
+	fdisk_partition_to_string;
+	fdisk_partition_unset_partno;
+	fdisk_partition_unset_size;
+	fdisk_partition_unset_start;
+	fdisk_partname;
+	fdisk_parttype_get_code;
+	fdisk_parttype_get_name;
+	fdisk_parttype_get_string;
+	fdisk_parttype_is_unknown;
+	fdisk_parttype_set_code;
+	fdisk_parttype_set_name;
+	fdisk_parttype_set_typestr;
+	fdisk_ref_ask;
+	fdisk_ref_context;
+	fdisk_ref_partition;
+	fdisk_ref_parttype;
+	fdisk_ref_script;
+	fdisk_ref_table;
+	fdisk_reorder_partitions;
+	fdisk_reread_partition_table;
+	fdisk_reset_alignment;
+	fdisk_reset_device_properties;
+	fdisk_reset_iter;
+	fdisk_reset_partition;
+	fdisk_reset_table;
+	fdisk_save_user_geometry;
+	fdisk_save_user_sector_size;
+	fdisk_script_get_header;
+	fdisk_script_get_nlines;
+	fdisk_script_get_table;
+	fdisk_script_read_context;
+	fdisk_script_read_file;
+	fdisk_script_read_line;
+	fdisk_script_set_header;
+	fdisk_script_write_file;
+	fdisk_set_ask;
+	fdisk_set_disklabel_id;
+	fdisk_set_first_lba;
+	fdisk_set_last_lba;
+	fdisk_set_partition;
+	fdisk_set_partition_type;
+	fdisk_set_script;
+	fdisk_set_unit;
+	fdisk_sgi_create_info;
+	fdisk_sgi_set_bootfile;
+	fdisk_sun_set_alt_cyl;
+	fdisk_sun_set_ilfact;
+	fdisk_sun_set_pcylcount;
+	fdisk_sun_set_rspeed;
+	fdisk_sun_set_xcyl;
+	fdisk_table_add_partition;
+	fdisk_table_get_nents;
+	fdisk_table_get_partition;
+	fdisk_table_is_empty;
+	fdisk_table_next_partition;
+	fdisk_table_remove_partition;
+	fdisk_table_sort_partitions;
+	fdisk_table_wrong_order;
+	fdisk_unref_ask;
+	fdisk_unref_context;
+	fdisk_unref_partition;
+	fdisk_unref_parttype;
+	fdisk_unref_script;
+	fdisk_unref_table;
+	fdisk_use_cylinders;
+	fdisk_verify_disklabel;
+	fdisk_warn;
+	fdisk_warnx;
+	fdisk_write_disklabel;
+local:
+	*;
+};
diff --git a/libblkid/libfdisk/src/partition.c b/libblkid/libfdisk/src/partition.c
new file mode 100644
index 0000000..8f84027
--- /dev/null
+++ b/libblkid/libfdisk/src/partition.c
@@ -0,0 +1,963 @@
+
+#include "c.h"
+#include "strutils.h"
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: partition
+ * @title: Partition
+ * @short_description: generic label independent partition abstraction
+ *
+ * The fdisk_partition provides label independent abstraction. The partitions
+ * are not directly connected with partition table (label) data. Any change to
+ * fdisk_partition does not affects in-memory or on-disk label data.
+ *
+ * The fdisk_partition is possible to use as a template for
+ * fdisk_add_partition() or fdisk_set_partition() operations.
+ */
+
+static void init_partition(struct fdisk_partition *pa)
+{
+	FDISK_INIT_UNDEF(pa->size);
+	FDISK_INIT_UNDEF(pa->start);
+	FDISK_INIT_UNDEF(pa->partno);
+	FDISK_INIT_UNDEF(pa->parent_partno);
+	FDISK_INIT_UNDEF(pa->boot);
+
+	INIT_LIST_HEAD(&pa->parts);
+}
+
+/**
+ * fdisk_new_partition:
+ *
+ * Returns: new instance.
+ */
+struct fdisk_partition *fdisk_new_partition(void)
+{
+	struct fdisk_partition *pa = calloc(1, sizeof(*pa));
+
+	pa->refcount = 1;
+	init_partition(pa);
+	DBG(PART, ul_debugobj(pa, "alloc"));
+	return pa;
+}
+
+/**
+ * fdisk_reset_partition:
+ * @pa: partition
+ *
+ * Resets partition content.
+ */
+void fdisk_reset_partition(struct fdisk_partition *pa)
+{
+	int ref;
+
+	if (!pa)
+		return;
+
+	DBG(PART, ul_debugobj(pa, "reset"));
+	ref = pa->refcount;
+
+	fdisk_unref_parttype(pa->type);
+	free(pa->name);
+	free(pa->uuid);
+	free(pa->attrs);
+
+	memset(pa, 0, sizeof(*pa));
+	pa->refcount = ref;
+
+	init_partition(pa);
+}
+
+/**
+ * fdisk_ref_partition:
+ * @pa: partition pointer
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_partition(struct fdisk_partition *pa)
+{
+	if (pa)
+		pa->refcount++;
+}
+
+/**
+ * fdisk_unref_partition:
+ * @pa: partition pointer
+ *
+ * De-incremparts reference counter, on zero the @pa is automatically
+ * deallocated.
+ */
+void fdisk_unref_partition(struct fdisk_partition *pa)
+{
+	if (!pa)
+		return;
+
+	pa->refcount--;
+	if (pa->refcount <= 0) {
+		fdisk_reset_partition(pa);
+		list_del(&pa->parts);
+		DBG(PART, ul_debugobj(pa, "free"));
+		free(pa);
+	}
+}
+
+/**
+ * fdisk_partition_set_start:
+ * @pa: partition
+ * @off: offset in sectors, maximal is UINT64_MAX-1
+ *
+ * Note that zero is valid offset too. Use fdisk_partition_unset_start() to
+ * undefine the offset. 
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_start(struct fdisk_partition *pa, fdisk_sector_t off)
+{
+	if (!pa)
+		return -EINVAL;
+	if (FDISK_IS_UNDEF(off))
+		return -ERANGE;
+	pa->start = off;
+	return 0;
+}
+
+/**
+ * fdisk_partition_unset_start:
+ * @pa: partition
+ *
+ * Sets the size as undefined. See fdisk_partition_has_start().
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_unset_start(struct fdisk_partition *pa)
+{
+	if (!pa)
+		return -EINVAL;
+	FDISK_INIT_UNDEF(pa->start);
+	return 0;
+}
+
+/**
+ * fdisk_partition_get_start:
+ * @pa: partition
+ *
+ * The zero is also valid offset. The function may return random undefined
+ * value when start offset is undefined (for example after
+ * fdisk_partition_unset_start()). Always use fdisk_partition_has_start() to be
+ * sure that you work with valid numbers.
+ *
+ * Returns: start offset in sectors
+ */
+fdisk_sector_t fdisk_partition_get_start(struct fdisk_partition *pa)
+{
+	return pa->start;
+}
+
+/**
+ * fdisk_partition_has_start:
+ * @pa: partition
+ *
+ * Returns: 1 or 0
+ */
+int fdisk_partition_has_start(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->start);
+}
+
+
+/**
+ * fdisk_partition_cmp_start:
+ * @a: partition
+ * @b: partition
+ *
+ * Compares partitons according to start offset, See fdisk_sort_table().
+ *
+ * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
+ */
+int fdisk_partition_cmp_start(struct fdisk_partition *a,
+			      struct fdisk_partition *b)
+{
+	int no_a = FDISK_IS_UNDEF(a->start),
+	    no_b = FDISK_IS_UNDEF(b->start);
+
+	if (no_a && no_b)
+		return 0;
+	if (no_a)
+		return -1;
+	if (no_b)
+		return 1;
+
+	return cmp_numbers(a->start, b->start);
+}
+
+/**
+ * fdisk_partition_start_follow_default
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * to use the first possible space for the new partition.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable)
+{
+	if (!pa)
+		return -EINVAL;
+	pa->start_follow_default = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_partition_start_is_default:
+ * @pa: partition
+ *
+ * See fdisk_partition_start_follow_default().
+ *
+ * Returns: 1 if the partition follows default
+ */
+int fdisk_partition_start_is_default(struct fdisk_partition *pa)
+{
+	assert(pa);
+	return pa->start_follow_default;
+}
+
+
+/**
+ * fdisk_partition_set_size:
+ * @pa: partition
+ * @sz: size in sectors, maximal is UIN64_MAX-1
+ *
+ * Note that zero is valid size too. Use fdisk_partition_unset_size() to
+ * undefine the size.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_size(struct fdisk_partition *pa, fdisk_sector_t sz)
+{
+	if (!pa)
+		return -EINVAL;
+	if (FDISK_IS_UNDEF(sz))
+		return -ERANGE;
+	pa->size = sz;
+	return 0;
+}
+
+/**
+ * fdisk_partition_unset_size:
+ * @pa: partition
+ *
+ * Sets the size as undefined. See fdisk_partition_has_size().
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_unset_size(struct fdisk_partition *pa)
+{
+	if (!pa)
+		return -EINVAL;
+	FDISK_INIT_UNDEF(pa->size);
+	return 0;
+}
+
+/**
+ * fdisk_partition_get_size:
+ * @pa: partition
+ *
+ * The zero is also valid size. The function may return random undefined
+ * value when size is undefined (for example after fdisk_partition_unset_size()).
+ * Always use fdisk_partition_has_size() to be sure that you work with valid 
+ * numbers.
+ *
+ * Returns: size offset in sectors
+ */
+fdisk_sector_t fdisk_partition_get_size(struct fdisk_partition *pa)
+{
+	return pa->size;
+}
+
+/**
+ * fdisk_partition_has_size:
+ * @pa: partition
+ *
+ * Returns: 1 or 0
+ */
+int fdisk_partition_has_size(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->size);
+}
+
+/**
+ * fdisk_partition_size_explicit:
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * By default libfdisk aligns the size when add the new partition (by
+ * fdisk_add_partrition()). If you want to disable this functionality use
+ * @enable = 1.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable)
+{
+	if (!pa)
+		return -EINVAL;
+	pa->size_explicit = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_partition_set_partno:
+ * @pa: partition
+ * @num: partitin number (0 is the first partition, maximal is SIZE_MAX-1)
+ *
+ * Note that zero is valid partno too. Use fdisk_partition_unset_partno() to
+ * undefine the partno.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num)
+{
+	if (!pa)
+		return -EINVAL;
+	if (FDISK_IS_UNDEF(num))
+		return -ERANGE;
+	pa->partno = num;
+	return 0;
+}
+
+/**
+ * fdisk_partition_unset_partno:
+ * @pa: partition
+ *
+ * Sets the partno as undefined. See fdisk_partition_has_partno().
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_unset_partno(struct fdisk_partition *pa)
+{
+	if (!pa)
+		return -EINVAL;
+	FDISK_INIT_UNDEF(pa->partno);
+	return 0;
+}
+
+/**
+ * fdisk_partition_get_partno:
+ * @pa: partition
+ *
+ * The zero is also valid parition number. The function may return random
+ * value when partno is undefined (for example after fdisk_partition_unset_partno()).
+ * Always use fdisk_partition_has_partno() to be sure that you work with valid
+ * numbers.
+ *
+ * Returns: partition number (0 is the first partition)
+ */
+size_t fdisk_partition_get_partno(struct fdisk_partition *pa)
+{
+	return pa->partno;
+}
+
+/**
+ * fdisk_partition_has_partno:
+ * @pa: partition
+ *
+ * Returns: 1 or 0
+ */
+int fdisk_partition_has_partno(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->partno);
+}
+
+
+/**
+ * fdisk_partition_cmp_partno:
+ * @a: partition
+ * @b: partition
+ *
+ * Compares partitons according to partition number See fdisk_sort_table().
+ *
+ * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
+ */
+int fdisk_partition_cmp_partno(struct fdisk_partition *a,
+			       struct fdisk_partition *b)
+{
+	return a->partno - b->partno;
+}
+
+/**
+ * fdisk_partition_partno_follow_default
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * to add a new partition to the default (next) position.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable)
+{
+	if (!pa)
+		return -EINVAL;
+	pa->partno_follow_default = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_partition_set_type:
+ * @pa: partition
+ * @type: partition type
+ *
+ * Sets parition type.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_type(struct fdisk_partition *pa,
+			     struct fdisk_parttype *type)
+{
+	if (!pa)
+		return -EINVAL;
+
+	fdisk_ref_parttype(type);
+	fdisk_unref_parttype(pa->type);
+	pa->type = type;
+
+	return 0;
+}
+
+/**
+ * fdisk_partition_get_type:
+ * @pa: partition
+ *
+ * Returns: pointer to partition type.
+ */
+struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa)
+{
+	return pa ? pa->type : NULL;
+}
+
+int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name)
+{
+	char *p = NULL;
+
+	if (!pa)
+		return -EINVAL;
+	if (name) {
+	       p = strdup(name);
+	       if (!p)
+		       return -ENOMEM;
+	}
+	free(pa->name);
+	pa->name = p;
+	return 0;
+}
+
+const char *fdisk_partition_get_name(struct fdisk_partition *pa)
+{
+	return pa ? pa->name : NULL;
+}
+
+int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid)
+{
+	char *p = NULL;
+
+	if (!pa)
+		return -EINVAL;
+	if (uuid) {
+	       p = strdup(uuid);
+	       if (!p)
+		       return -ENOMEM;
+	}
+	free(pa->uuid);
+	pa->uuid = p;
+	return 0;
+}
+
+/**
+ * fdisk_partition_has_end:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition has defined last sector
+ */
+int fdisk_partition_has_end(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->start) && !FDISK_IS_UNDEF(pa->size);
+}
+
+/**
+ * fdisk_partition_get_end:
+ * @pa: partition
+ *
+ * This function may returns absolute non-sense, always check
+ * fdisk_partition_has_end().
+ *
+ * Note that partition end is defined by fdisk_partition_set_start() and
+ * fdisk_partition_set_size().
+ *
+ * Returns: last partition sector LBA.
+ */
+fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa)
+{
+	return pa->start + pa->size - (pa->size == 0 ? 0 : 1);
+}
+
+/**
+ * fdisk_partition_end_follow_default
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * to use all the possible space for the new partition.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable)
+{
+	if (!pa)
+		return -EINVAL;
+	pa->end_follow_default = enable ? 1 : 0;
+	return 0;
+}
+
+/**
+ * fdisk_partition_end_is_default:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition follows default
+ */
+int fdisk_partition_end_is_default(struct fdisk_partition *pa)
+{
+	assert(pa);
+	return pa->end_follow_default;
+}
+
+const char *fdisk_partition_get_uuid(struct fdisk_partition *pa)
+{
+	return pa ? pa->uuid : NULL;
+}
+
+const char *fdisk_partition_get_attrs(struct fdisk_partition *pa)
+{
+	return pa ? pa->attrs : NULL;
+}
+
+int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs)
+{
+	char *p = NULL;
+
+	if (!pa)
+		return -EINVAL;
+	if (attrs) {
+	       p = strdup(attrs);
+	       if (!p)
+		       return -ENOMEM;
+	}
+	free(pa->attrs);
+	pa->attrs = p;
+	return 0;
+}
+
+int fdisk_partition_is_nested(struct fdisk_partition *pa)
+{
+	return pa && !FDISK_IS_UNDEF(pa->parent_partno);
+}
+
+int fdisk_partition_is_container(struct fdisk_partition *pa)
+{
+	return pa && pa->container;
+}
+
+int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent)
+{
+	if (pa && parent)
+		*parent = pa->parent_partno;
+	else
+		return -EINVAL;
+	return 0;
+}
+
+int fdisk_partition_is_used(struct fdisk_partition *pa)
+{
+	return pa && pa->used;
+}
+
+int fdisk_partition_is_bootable(struct fdisk_partition *pa)
+{
+	return pa && pa->boot == 1;
+}
+
+int fdisk_partition_is_freespace(struct fdisk_partition *pa)
+{
+	return pa && pa->freespace;
+}
+
+int fdisk_partition_next_partno(
+		struct fdisk_partition *pa,
+		struct fdisk_context *cxt,
+		size_t *n)
+{
+	assert(cxt);
+	assert(n);
+
+	if (pa && pa->partno_follow_default) {
+		size_t i;
+
+		DBG(PART, ul_debugobj(pa, "next partno (follow default)"));
+
+		for (i = 0; i < cxt->label->nparts_max; i++) {
+			if (!fdisk_is_partition_used(cxt, i)) {
+				*n = i;
+				return 0;
+			}
+		}
+		return -ERANGE;
+
+	} else if (pa && fdisk_partition_has_partno(pa)) {
+
+		DBG(PART, ul_debugobj(pa, "next partno (specified=%zu)", pa->partno));
+
+		if (pa->partno >= cxt->label->nparts_max)
+			return -ERANGE;
+		*n = pa->partno;
+	} else
+		return fdisk_ask_partnum(cxt, n, 1);
+
+	return 0;
+}
+
+/**
+ * fdisk_partition_to_string:
+ * @pa: partition
+ * @cxt: context
+ * @id: field (FDISK_FIELD_*)
+ * @data: returns string with allocated data
+ *
+ * Returns info about partition converted to printable string.
+ *
+ * For example
+ * <informalexample>
+ *   <programlisting>
+ *      struct fdisk_parition *pa;
+ *
+ *      fdisk_get_partition(cxt, 0, &pa);
+ *	fdisk_partition_to_string(pa, FDISK_FIELD_UUID, &data);
+ *	printf("first partition uuid: %s\n", data);
+ *	free(data);
+ *	fdisk_unref_partition(pa);
+ *   </programlisting>
+ * </informalexample>
+ *
+ * returns UUID for the first partition.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_partition_to_string(struct fdisk_partition *pa,
+			      struct fdisk_context *cxt,
+			      int id,
+			      char **data)
+{
+	char *p = NULL;
+	int rc = 0;
+	uint64_t x;
+
+	if (!pa || !cxt)
+		return -EINVAL;
+
+	switch (id) {
+	case FDISK_FIELD_DEVICE:
+		if (pa->freespace)
+			p = strdup(_("Free space"));
+		else if (fdisk_partition_has_partno(pa) && cxt->dev_path) {
+			if (cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
+				rc = asprintf(&p, "%c", (int) pa->partno + 'a');
+			else
+				p = fdisk_partname(cxt->dev_path, pa->partno + 1);
+		}
+		break;
+	case FDISK_FIELD_BOOT:
+		if (fdisk_partition_is_bootable(pa))
+			rc = asprintf(&p, "%c", pa->boot ? '*' : ' ');
+		break;
+	case FDISK_FIELD_START:
+		if (fdisk_partition_has_start(pa)) {
+			x = fdisk_cround(cxt, pa->start);
+			rc = pa->start_post ?
+				asprintf(&p, "%ju%c", x, pa->start_post) :
+				asprintf(&p, "%ju", x);
+		}
+		break;
+	case FDISK_FIELD_END:
+		if (fdisk_partition_has_end(pa)) {
+			x = fdisk_cround(cxt, fdisk_partition_get_end(pa));
+			rc = pa->end_post ?
+					asprintf(&p, "%ju%c", x, pa->end_post) :
+					asprintf(&p, "%ju", x);
+		}
+		break;
+	case FDISK_FIELD_SIZE:
+		if (fdisk_partition_has_size(pa)) {
+			uint64_t sz = pa->size * cxt->sector_size;
+
+			if (fdisk_is_details(cxt)) {
+				rc = pa->size_post ?
+						asprintf(&p, "%ju%c", sz, pa->size_post) :
+						asprintf(&p, "%ju", sz);
+			} else {
+				p = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
+				if (!p)
+					rc = -ENOMEM;
+			}
+		}
+		break;
+	case FDISK_FIELD_CYLINDERS:
+		rc = asprintf(&p, "%ju", (uintmax_t)
+			fdisk_cround(cxt, fdisk_partition_has_size(pa) ? pa->size : 0));
+		break;
+	case FDISK_FIELD_SECTORS:
+		rc = asprintf(&p, "%ju",
+			fdisk_partition_has_size(pa) ? (uintmax_t) pa->size : 0);
+		break;
+	case FDISK_FIELD_BSIZE:
+		rc = asprintf(&p, "%ju", pa->bsize);
+		break;
+	case FDISK_FIELD_FSIZE:
+		rc = asprintf(&p, "%ju", pa->fsize);
+		break;
+	case FDISK_FIELD_CPG:
+		rc = asprintf(&p, "%ju", pa->cpg);
+		break;
+	case FDISK_FIELD_TYPE:
+		p = pa->type && pa->type->name ? strdup(pa->type->name) : NULL;
+		break;
+	case FDISK_FIELD_TYPEID:
+		if (pa->type && fdisk_parttype_get_string(pa->type))
+			rc = asprintf(&p, "%s", fdisk_parttype_get_string(pa->type));
+		else if (pa->type)
+			rc = asprintf(&p, "%x", fdisk_parttype_get_code(pa->type));
+		break;
+	case FDISK_FIELD_UUID:
+		p = pa->uuid ? strdup(pa->uuid) : NULL;
+		break;
+	case FDISK_FIELD_NAME:
+		p = pa->name ? strdup(pa->name) : NULL;
+		break;
+	case FDISK_FIELD_ATTR:
+		p = pa->attrs ? strdup(pa->attrs) : NULL;
+		break;
+	case FDISK_FIELD_SADDR:
+		p = pa->start_chs ? strdup(pa->start_chs) : NULL;
+		break;
+	case FDISK_FIELD_EADDR:
+		p = pa->end_chs ? strdup(pa->end_chs) : NULL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (rc < 0)
+		rc = -ENOMEM;
+	else if (rc > 0)
+		rc = 0;
+
+	if (data)
+		*data = p;
+	return rc;
+}
+
+/**
+ * fdisk_get_partition:
+ * @cxt: context
+ * @partno: partition number (0 is the first partition)
+ * @pa: returns data about partition
+ *
+ * Reads disklabel and fills in @pa with data about partition @n.
+ *
+ * Note that partno may address unused partition and then this function does
+ * not fill anything to @pa.  See fdisk_is_partition_used(). If @pa points to
+ * NULL then the function allocates a newly allocated fdisk_partition struct,
+ * use fdisk_unref_partition() to deallocate.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_partition(struct fdisk_context *cxt, size_t partno,
+			struct fdisk_partition **pa)
+{
+	int rc;
+	struct fdisk_partition *np = NULL;
+
+	if (!cxt || !cxt->label || !pa)
+		return -EINVAL;
+	if (!cxt->label->op->get_part)
+		return -ENOSYS;
+	if (!fdisk_is_partition_used(cxt, partno))
+		return -EINVAL;
+
+	if (!*pa) {
+		np = *pa = fdisk_new_partition();
+		if (!*pa)
+			return -ENOMEM;
+	} else
+		fdisk_reset_partition(*pa);
+
+	(*pa)->partno = partno;
+	rc = cxt->label->op->get_part(cxt, partno, *pa);
+
+	if (rc) {
+		if (np) {
+			fdisk_unref_partition(np);
+			*pa = NULL;
+		} else
+			fdisk_reset_partition(*pa);
+	} else
+		(*pa)->size_explicit = 1;
+	return rc;
+}
+
+/**
+ * fdisk_set_partition:
+ * @cxt: context
+ * @partno: partition number (0 is the first partition)
+ * @pa: new partition setting
+ *
+ * Modifies disklabel according to setting with in @pa.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_set_partition(struct fdisk_context *cxt, size_t partno,
+			struct fdisk_partition *pa)
+{
+	if (!cxt || !cxt->label || !pa)
+		return -EINVAL;
+	if (!cxt->label->op->set_part)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "setting partition %zu %p (start=%ju, end=%ju, size=%ju, "
+		    "defaults(start=%s, end=%s, partno=%s)",
+		    partno, pa,
+		    (uintmax_t) fdisk_partition_get_start(pa),
+		    (uintmax_t) fdisk_partition_get_end(pa),
+		    (uintmax_t) fdisk_partition_get_size(pa),
+		    pa->start_follow_default ? "yes" : "no",
+		    pa->end_follow_default ? "yes" : "no",
+		    pa->partno_follow_default ? "yes" : "no"));
+
+	return cxt->label->op->set_part(cxt, partno, pa);
+}
+
+/**
+ * fdisk_add_partition:
+ * @cxt: fdisk context
+ * @pa: template for the partition (or NULL)
+ * @partno: NULL or returns new partition number
+ *
+ * If @pa is not specified or any @pa item is missiong the libfdisk will ask by
+ * fdisk_ask_ API.
+ *
+ * Adds a new partition to disklabel.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_add_partition(struct fdisk_context *cxt,
+			struct fdisk_partition *pa,
+			size_t *partno)
+{
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->add_part)
+		return -ENOSYS;
+	if (fdisk_missing_geometry(cxt))
+		return -EINVAL;
+
+	if (pa)
+		DBG(CXT, ul_debugobj(cxt, "adding new partition %p (start=%ju, end=%ju, size=%ju, "
+			    "defaults(start=%s, end=%s, partno=%s)",
+			    pa,
+			    (uintmax_t) fdisk_partition_get_start(pa),
+			    (uintmax_t) fdisk_partition_get_end(pa),
+			    (uintmax_t) fdisk_partition_get_size(pa),
+			    pa->start_follow_default ? "yes" : "no",
+			    pa->end_follow_default ? "yes" : "no",
+			    pa->partno_follow_default ? "yes" : "no"));
+	else
+		DBG(CXT, ul_debugobj(cxt, "adding partition"));
+
+	rc = cxt->label->op->add_part(cxt, pa, partno);
+
+	DBG(CXT, ul_debugobj(cxt, "add partition done (rc=%d)", rc));
+	return rc;
+}
+
+/**
+ * fdisk_delete_partition:
+ * @cxt: fdisk context
+ * @partno: partition number to delete (0 is the first partition)
+ *
+ * Deletes a @partno partition from disklabel.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->del_part)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "deleting %s partition number %zd",
+				cxt->label->name, partno));
+	return cxt->label->op->del_part(cxt, partno);
+}
+
+/**
+ * fdisk_delete_all_partitions:
+ * @cxt: fdisk context
+ *
+ * Delete all used partitions from disklabel.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_delete_all_partitions(struct fdisk_context *cxt)
+{
+	size_t i;
+	int rc;
+
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+
+		if (!fdisk_is_partition_used(cxt, i))
+			continue;
+		rc = fdisk_delete_partition(cxt, i);
+		if (rc)
+			break;
+	}
+
+	return rc;
+}
+
+/**
+ * fdisk_is_partition_used:
+ * @cxt: context
+ * @n: partition number (0 is the first partition)
+ *
+ * This is faster than fdisk_get_partition() + fdisk_partition_is_used().
+ *
+ * Returns: 0 or 1
+ */
+int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n)
+{
+	if (!cxt || !cxt->label)
+		return -EINVAL;
+	if (!cxt->label->op->part_is_used)
+		return -ENOSYS;
+
+	return cxt->label->op->part_is_used(cxt, n);
+}
+
diff --git a/libblkid/libfdisk/src/parttype.c b/libblkid/libfdisk/src/parttype.c
new file mode 100644
index 0000000..aedf4e8
--- /dev/null
+++ b/libblkid/libfdisk/src/parttype.c
@@ -0,0 +1,401 @@
+
+#include <ctype.h>
+
+#include "nls.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: parttype
+ * @title: Partition types
+ * @short_description: abstraction to partition types
+ *
+ * There are two basic types of parttypes, string based (e.g. GPT)
+ * and code/hex based (e.g. MBR).
+ */
+
+/**
+ * fdisk_new_parttype:
+ *
+ * It's recommended to use fdisk_label_get_parttype_from_code() or
+ * fdisk_label_get_parttype_from_string() for well known types rather
+ * than allocate a new instance.
+ *
+ * Returns: new instance.
+ */
+struct fdisk_parttype *fdisk_new_parttype(void)
+{
+	struct fdisk_parttype *t = calloc(1, sizeof(*t));
+
+	t->refcount = 1;
+	t->flags = FDISK_PARTTYPE_ALLOCATED;
+	DBG(PARTTYPE, ul_debugobj(t, "alloc"));
+	return t;
+}
+
+/**
+ * fdisk_ref_parttype:
+ * @t: partition type
+ *
+ * Incremparts reference counter for allocated types
+ */
+void fdisk_ref_parttype(struct fdisk_parttype *t)
+{
+	if (fdisk_parttype_is_allocated(t))
+		t->refcount++;
+}
+
+/**
+ * fdisk_unref_parttype
+ * @t: partition pointer
+ *
+ * De-incremparts reference counter, on zero the @t is automatically
+ * deallocated.
+ */
+void fdisk_unref_parttype(struct fdisk_parttype *t)
+{
+	if (!fdisk_parttype_is_allocated(t))
+		return;
+
+	t->refcount--;
+	if (t->refcount <= 0) {
+		DBG(PARTTYPE, ul_debugobj(t, "free"));
+		free(t->typestr);
+		free(t->name);
+		free(t);
+	}
+}
+
+/**
+ * fdisk_parttype_set_name:
+ * @t: partition type
+ * @str: type name
+ *
+ * Sets type name to allocated partition type, for static types
+ * it returns -EINVAL.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str)
+{
+	char *p = NULL;
+
+	if (!t || !fdisk_parttype_is_allocated(t))
+		return -EINVAL;
+	if (str) {
+		p = strdup(str);
+		if (!p)
+			return -ENOMEM;
+	}
+
+	free(t->name);
+	t->name = p;
+	return 0;
+}
+
+/**
+ * fdisk_parttype_set_typestr:
+ * @t: partition type
+ * @str: type identificator (e.g. GUID for GPT)
+ *
+ * Sets type string to allocated partition type, for static types
+ * it returns -EINVAL. Don't use this function for MBR, see
+ * fdisk_parttype_set_code().
+ *
+ * Return: 0 on success, <0 on error
+ */
+int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str)
+{
+	char *p = NULL;
+
+	if (!t || !fdisk_parttype_is_allocated(t))
+		return -EINVAL;
+	if (str) {
+		p = strdup(str);
+		if (!p)
+			return -ENOMEM;
+	}
+
+	free(t->typestr);
+	t->typestr = p;
+	return 0;
+}
+
+/**
+ * fdisk_parttype_set_code:
+ * @t: partition type
+ * @code: type identificator (e.g. MBR type codes)
+ *
+ * Sets type code to allocated partition type, for static types it returns
+ * -EINVAL. Don't use this function for GPT, see fdisk_parttype_set_typestr().
+ *
+ * Return: 0 on success, <0 on error
+ */
+int fdisk_parttype_set_code(struct fdisk_parttype *t, int code)
+{
+	if (!t || !fdisk_parttype_is_allocated(t))
+		return -EINVAL;
+	t->code = code;
+	return 0;
+}
+
+/**
+ * fdisk_label_get_nparttypes:
+ * @lb: label
+ *
+ * Returns: number of types supported by label.
+ */
+size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb)
+{
+	if (!lb)
+		return 0;
+	return lb->nparttypes;
+}
+
+/**
+ * fdisk_label_get_parttype:
+ * @lb: label
+ * @n: number
+ *
+ * Returns: return parttype
+ */
+struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n)
+{
+	if (!lb || n >= lb->nparttypes)
+		return NULL;
+	return &lb->parttypes[n];
+}
+
+/**
+ * fdisk_label_has_code_parttypes:
+ * @lb: label
+ *
+ * Returns: 1 if the label uses code as partition type
+ *          identifiers (e.g. MBR) or 0.
+ */
+int fdisk_label_has_code_parttypes(const struct fdisk_label *lb)
+{
+	assert(lb);
+
+	if (lb->parttypes && lb->parttypes[0].typestr)
+		return 0;
+	return 1;
+}
+
+
+/**
+ * fdisk_label_get_parttype_from_code:
+ * @lb: label
+ * @code: code to search for
+ *
+ * Search for partition type in label-specific table. The result
+ * is pointer to static array of label types.
+ *
+ * Returns: partition type or NULL upon failure or invalid @code.
+ */
+struct fdisk_parttype *fdisk_label_get_parttype_from_code(
+				const struct fdisk_label *lb,
+				unsigned int code)
+{
+	size_t i;
+
+	assert(lb);
+
+	if (!lb->nparttypes)
+		return NULL;
+
+	for (i = 0; i < lb->nparttypes; i++)
+		if (lb->parttypes[i].code == code)
+			return &lb->parttypes[i];
+	return NULL;
+}
+
+/**
+ * fdisk_label_get_parttype_from_string:
+ * @lb: label
+ * @str: string to search for
+ *
+ * Search for partition type in label-specific table. The result
+ * is pointer to static array of label types.
+ *
+ * Returns: partition type or NULL upon failure or invalid @str.
+ */
+struct fdisk_parttype *fdisk_label_get_parttype_from_string(
+				const struct fdisk_label *lb,
+				const char *str)
+{
+	size_t i;
+
+	assert(lb);
+
+	if (!lb->nparttypes)
+		return NULL;
+
+	for (i = 0; i < lb->nparttypes; i++)
+		if (lb->parttypes[i].typestr
+		    && strcasecmp(lb->parttypes[i].typestr, str) == 0)
+			return &lb->parttypes[i];
+
+	return NULL;
+}
+
+/**
+ * fdisk_new_unknown_parttype:
+ * @code: type as number
+ * @typestr: type as string
+
+ * Allocates new 'unknown' partition type. Use fdisk_unref_parttype() to
+ * deallocate.
+ *
+ * Returns: newly allocated partition type, or NULL upon failure.
+ */
+struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
+						  const char *typestr)
+{
+	struct fdisk_parttype *t = fdisk_new_parttype();
+
+	if (!t)
+		return NULL;
+
+	fdisk_parttype_set_name(t, _("unknown"));
+	fdisk_parttype_set_code(t, code);
+	fdisk_parttype_set_typestr(t, typestr);
+	t->flags |= FDISK_PARTTYPE_UNKNOWN;
+
+	return t;
+}
+
+/**
+ * fdisk_copy_parttype:
+ * @type: type to copy
+ *
+ * Use fdisk_unref_parttype() to deallocate.
+ *
+ * Returns: newly allocated partition type, or NULL upon failure.
+ */
+struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type)
+{
+	struct fdisk_parttype *t = fdisk_new_parttype();
+
+	if (!t)
+		return NULL;
+
+	fdisk_parttype_set_name(t, type->name);
+	fdisk_parttype_set_code(t, type->code);
+	fdisk_parttype_set_typestr(t, type->typestr);
+
+	return t;
+}
+
+/**
+ * fdisk_label_parse_parttype:
+ * @lb: label
+ * @str: string to parse from
+ *
+ * Parses partition type from @str according to the label. Thefunction returns
+ * a pointer to static table of the partition types, or newly allocated
+ * partition type for unknown types (see fdisk_parttype_is_unknown(). It's
+ * safe to call fdisk_unref_parttype() for all results.
+ *
+ * Returns: pointer to type or NULL on error.
+ */
+struct fdisk_parttype *fdisk_label_parse_parttype(
+				const struct fdisk_label *lb,
+				const char *str)
+{
+	struct fdisk_parttype *types, *ret;
+	unsigned int code = 0;
+	char *typestr = NULL, *end = NULL;
+
+	assert(lb);
+
+	if (!lb->nparttypes)
+		return NULL;
+
+	DBG(LABEL, ul_debugobj((void *) lb, "parsing '%s' (%s) partition type",
+				str, lb->name));
+	types = lb->parttypes;
+
+	if (types[0].typestr == NULL && isxdigit(*str)) {
+
+		errno = 0;
+		code = strtol(str, &end, 16);
+
+		if (errno || *end != '\0') {
+			DBG(LABEL, ul_debugobj((void *) lb, "parsing failed: %m"));
+			return NULL;
+		}
+		ret = fdisk_label_get_parttype_from_code(lb, code);
+		if (ret)
+			goto done;
+	} else {
+		int i;
+
+		/* maybe specified by type string (e.g. UUID) */
+		ret = fdisk_label_get_parttype_from_string(lb, str);
+		if (ret)
+			goto done;
+
+		/* maybe specified by order number */
+		errno = 0;
+		i = strtol(str, &end, 0);
+		if (errno == 0 && *end == '\0' && i > 0
+		    && i - 1 < (int) lb->nparttypes) {
+			ret = &types[i - 1];
+			goto done;
+		}
+	}
+
+	ret = fdisk_new_unknown_parttype(code, typestr);
+done:
+	DBG(PARTTYPE, ul_debugobj(ret, "returns parsed '%s' partition type", ret->name));
+	return ret;
+}
+
+/**
+ * fdisk_parttype_get_string:
+ * @t: type
+ *
+ * Returns: partition type string (e.g. GUID for GPT)
+ */
+const char *fdisk_parttype_get_string(const struct fdisk_parttype *t)
+{
+	assert(t);
+	return t->typestr && *t->typestr ? t->typestr : NULL;
+}
+
+/**
+ * fdisk_parttype_get_code:
+ * @t: type
+ *
+ * Returns: partition type code (e.g. for MBR)
+ */
+unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t)
+{
+	assert(t);
+	return t->code;
+}
+
+/**
+ * fdisk_parttype_get_name:
+ * @t: type
+ *
+ * Returns: partition type human readable name
+ */
+const char *fdisk_parttype_get_name(const struct fdisk_parttype *t)
+{
+	assert(t);
+	return t->name;
+}
+
+/**
+ * fdisk_parttype_is_unknown:
+ * @t: type
+ *
+ * Checks for example result from fdisk_label_parse_parttype().
+ *
+ * Returns: 1 is type is "unknonw" or 0.
+ */
+int fdisk_parttype_is_unknown(const struct fdisk_parttype *t)
+{
+	return t && (t->flags & FDISK_PARTTYPE_UNKNOWN) ? 1 : 0;
+}
diff --git a/libblkid/libfdisk/src/script.c b/libblkid/libfdisk/src/script.c
new file mode 100644
index 0000000..83bda99
--- /dev/null
+++ b/libblkid/libfdisk/src/script.c
@@ -0,0 +1,1235 @@
+
+#include "fdiskP.h"
+#include "strutils.h"
+
+/**
+ * SECTION: script
+ * @title: Script
+ * @short_description: text based sfdisk compatible description of partition table
+ *
+ * The libfdisk scripts are based on original sfdisk script (dumps).  Each
+ * script has two parts: script headers and partition table entries
+ * (partitions).
+ *
+ * For more details about script format see sfdisk man page.
+ */
+
+/* script header (e.g. unit: sectors) */
+struct fdisk_scriptheader {
+	struct list_head	headers;
+	char			*name;
+	char			*data;
+};
+
+/* script control struct */
+struct fdisk_script {
+	struct fdisk_table	*table;
+	struct list_head	headers;
+	struct fdisk_context	*cxt;
+
+	int			refcount;
+
+	/* parser's state */
+	size_t			nlines;
+	int			fmt;		/* input format */
+	struct fdisk_label	*label;
+};
+
+
+static void fdisk_script_free_header(struct fdisk_script *dp, struct fdisk_scriptheader *fi)
+{
+	if (!fi)
+		return;
+
+	DBG(SCRIPT, ul_debugobj(fi, "free header %s", fi->name));
+	free(fi->name);
+	free(fi->data);
+	list_del(&fi->headers);
+	free(fi);
+}
+
+/**
+ * fdisk_new_script:
+ * @cxt: context
+ *
+ * The script hold fdisk_table and additional information to read/write
+ * script to the file.
+ *
+ * Returns: newly allocated script struct.
+ */
+struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt)
+{
+	struct fdisk_script *dp = NULL;
+
+	dp = calloc(1, sizeof(*dp));
+	if (!dp)
+		return NULL;
+
+	DBG(SCRIPT, ul_debugobj(dp, "alloc"));
+	dp->refcount = 1;
+	dp->cxt = cxt;
+	fdisk_ref_context(cxt);
+
+	dp->table = fdisk_new_table();
+	if (!dp->table) {
+		fdisk_unref_script(dp);
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&dp->headers);
+	return dp;
+}
+
+/**
+ * fdisk_new_script_from_file:
+ * @cxt: context
+ * @filename: path to the script file
+ *
+ * Allocates a new script and reads script from @filename.
+ *
+ * Returns: new script instance or NULL in case of error (check errno for more details).
+ */
+struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
+						 const char *filename)
+{
+	int rc;
+	FILE *f;
+	struct fdisk_script *dp, *res = NULL;
+
+	assert(cxt);
+	assert(filename);
+
+	DBG(SCRIPT, ul_debug("opening %s", filename));
+	f = fopen(filename, "r");
+	if (!f)
+		return NULL;
+
+	dp = fdisk_new_script(cxt);
+	if (!dp)
+		goto done;
+
+	rc = fdisk_script_read_file(dp, f);
+	if (rc) {
+		errno = -rc;
+		goto done;
+	}
+
+	res = dp;
+done:
+	fclose(f);
+	if (!res)
+		fdisk_unref_script(dp);
+	else
+		errno = 0;
+
+	return res;
+}
+
+/**
+ * fdisk_ref_script:
+ * @dp: script pointer
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_script(struct fdisk_script *dp)
+{
+	if (dp)
+		dp->refcount++;
+}
+
+static void fdisk_reset_script(struct fdisk_script *dp)
+{
+	assert(dp);
+
+	DBG(SCRIPT, ul_debugobj(dp, "reset"));
+	fdisk_unref_table(dp->table);
+	dp->table = NULL;
+
+	while (!list_empty(&dp->headers)) {
+		struct fdisk_scriptheader *fi = list_entry(dp->headers.next,
+						  struct fdisk_scriptheader, headers);
+		fdisk_script_free_header(dp, fi);
+	}
+	INIT_LIST_HEAD(&dp->headers);
+}
+
+/**
+ * fdisk_unref_script:
+ * @dp: script pointer
+ *
+ * De-incremparts reference counter, on zero the @dp is automatically
+ * deallocated.
+ */
+void fdisk_unref_script(struct fdisk_script *dp)
+{
+	if (!dp)
+		return;
+
+	dp->refcount--;
+	if (dp->refcount <= 0) {
+		fdisk_reset_script(dp);
+		fdisk_unref_context(dp->cxt);
+		DBG(SCRIPT, ul_debugobj(dp, "free script"));
+		free(dp);
+	}
+}
+
+static struct fdisk_scriptheader *script_get_header(struct fdisk_script *dp,
+						     const char *name)
+{
+	struct list_head *p;
+
+	list_for_each(p, &dp->headers) {
+		struct fdisk_scriptheader *fi = list_entry(p, struct fdisk_scriptheader, headers);
+
+		if (strcasecmp(fi->name, name) == 0)
+			return fi;
+	}
+
+	return NULL;
+}
+
+/**
+ * fdisk_script_get_header:
+ * @dp: script instance
+ * @name: header name
+ *
+ * Returns: pointer to header data or NULL.
+ */
+const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name)
+{
+	struct fdisk_scriptheader *fi;
+
+	assert(dp);
+	assert(name);
+
+	fi = script_get_header(dp, name);
+	return fi ? fi->data : NULL;
+}
+
+
+/**
+ * fdisk_script_set_header:
+ * @dp: script instance
+ * @name: header name
+ * @data: header data (or NULL)
+ *
+ * The headers are used as global options (in script) for whole partition
+ * table, always one header per line.
+ *
+ * If no @data specified then the header is removed. If header does not exist
+ * and @data specified then a new header added.
+ *
+ * Note that libfdisk allows to specify arbitrary custom header, the default
+ * build-in headers are "unit" and "label", and some label specific headers
+ * (for example "uuid" and "name" for GPT).
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_script_set_header(struct fdisk_script *dp,
+			    const char *name,
+			    const char *data)
+{
+	struct fdisk_scriptheader *fi;
+
+	assert(dp);
+	assert(name);
+
+	if (!dp || !name)
+		return -EINVAL;
+
+	fi = script_get_header(dp, name);
+	if (!fi && !data)
+		return 0;	/* want to remove header that does not exist, success */
+
+	if (!data) {
+		/* no data, remove the header */
+		fdisk_script_free_header(dp, fi);
+		return 0;
+	}
+
+	if (!fi) {
+		/* new header */
+		fi = calloc(1, sizeof(*fi));
+		if (!fi)
+			return -ENOMEM;
+		INIT_LIST_HEAD(&fi->headers);
+		fi->name = strdup(name);
+		fi->data = strdup(data);
+		if (!fi->data || !fi->name) {
+			fdisk_script_free_header(dp, fi);
+			return -ENOMEM;
+		}
+		list_add_tail(&fi->headers, &dp->headers);
+	} else {
+		/* update existing */
+		char *x = strdup(data);
+
+		if (!x)
+			return -ENOMEM;
+		free(fi->data);
+		fi->data = x;
+	}
+
+	if (strcmp(name, "label") == 0)
+		dp->label = NULL;
+
+	return 0;
+}
+
+/**
+ * fdisk_script_get_table:
+ * @dp: script
+ *
+ * The table (container with partitions) is possible to create by
+ * fdisk_script_read_context() or fdisk_script_read_file(), otherwise
+ * this function returns NULL.
+ *
+ * Returns: NULL or script.
+ */
+struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp)
+{
+	assert(dp);
+	return dp ? dp->table : NULL;
+}
+
+static struct fdisk_label *script_get_label(struct fdisk_script *dp)
+{
+	assert(dp);
+	assert(dp->cxt);
+
+	if (!dp->label) {
+		dp->label = fdisk_get_label(dp->cxt,
+					fdisk_script_get_header(dp, "label"));
+		DBG(SCRIPT, ul_debugobj(dp, "label '%s'", dp->label ? dp->label->name : ""));
+	}
+	return dp->label;
+}
+
+/**
+ * fdisk_script_get_nlines:
+ * @dp: script
+ *
+ * Returns: number of parsed lines or <0 on error.
+ */
+int fdisk_script_get_nlines(struct fdisk_script *dp)
+{
+	assert(dp);
+	return dp->nlines;
+}
+
+/**
+ * fdisk_script_read_context:
+ * @dp: script
+ * @cxt: context
+ *
+ * Reads data from the @cxt context (on disk partition table) into the script.
+ * If the context is no specified than defaults to context used for fdisk_new_script().
+ *
+ * Return: 0 on success, <0 on error.
+ */
+int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	int rc;
+	char *p = NULL;
+
+	assert(dp);
+
+	if (!cxt)
+		cxt = dp->cxt;
+
+	if (!dp || !cxt)
+		return -EINVAL;
+
+	DBG(SCRIPT, ul_debugobj(dp, "reading context into script"));
+	fdisk_reset_script(dp);
+
+	lb = fdisk_get_label(cxt, NULL);
+	if (!lb)
+		return -EINVAL;
+
+	/* allocate and fill new table */
+	rc = fdisk_get_partitions(cxt, &dp->table);
+	if (rc)
+		return rc;
+
+	/* generate headers */
+	rc = fdisk_script_set_header(dp, "label", fdisk_label_get_name(lb));
+
+	if (!rc && fdisk_get_disklabel_id(cxt, &p) == 0 && p) {
+		rc = fdisk_script_set_header(dp, "label-id", p);
+		free(p);
+	}
+	if (!rc && cxt->dev_path)
+		rc = fdisk_script_set_header(dp, "device", cxt->dev_path);
+	if (!rc)
+		rc = fdisk_script_set_header(dp, "unit", "sectors");
+
+	/* TODO: label specific headers (e.g. uuid for GPT) */
+
+	DBG(SCRIPT, ul_debugobj(dp, "read context done [rc=%d]", rc));
+	return rc;
+}
+
+/**
+ * fdisk_script_write_file:
+ * @dp: script
+ * @f: output file
+ *
+ * Writes script @dp to the ile @f.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_script_write_file(struct fdisk_script *dp, FILE *f)
+{
+	struct list_head *h;
+	struct fdisk_partition *pa;
+	struct fdisk_iter itr;
+	const char *devname = NULL;
+
+	assert(dp);
+	assert(f);
+
+	DBG(SCRIPT, ul_debugobj(dp, "writing script to file"));
+
+	/* script headers */
+	list_for_each(h, &dp->headers) {
+		struct fdisk_scriptheader *fi = list_entry(h, struct fdisk_scriptheader, headers);
+		fprintf(f, "%s: %s\n", fi->name, fi->data);
+		if (strcmp(fi->name, "device") == 0)
+			devname = fi->data;
+	}
+
+	if (!dp->table) {
+		DBG(SCRIPT, ul_debugobj(dp, "script table empty"));
+		return 0;
+	}
+
+	DBG(SCRIPT, ul_debugobj(dp, "%zu entries", fdisk_table_get_nents(dp->table)));
+
+	fputc('\n', f);
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	while (fdisk_table_next_partition(dp->table, &itr, &pa) == 0) {
+		char *p = NULL;
+
+		if (devname)
+			p = fdisk_partname(devname, pa->partno + 1);
+		if (p) {
+			DBG(SCRIPT, ul_debugobj(dp, "write %s entry", p));
+			fprintf(f, "%s :", p);
+		} else
+			fprintf(f, "%zu :", pa->partno + 1);
+
+		if (fdisk_partition_has_start(pa))
+			fprintf(f, " start=%12ju", pa->start);
+		if (fdisk_partition_has_size(pa))
+			fprintf(f, ", size=%12ju", pa->size);
+
+		if (pa->type && fdisk_parttype_get_string(pa->type))
+			fprintf(f, ", type=%s", fdisk_parttype_get_string(pa->type));
+		else if (pa->type)
+			fprintf(f, ", type=%x", fdisk_parttype_get_code(pa->type));
+
+		if (pa->uuid)
+			fprintf(f, ", uuid=%s", pa->uuid);
+		if (pa->name && *pa->name)
+			fprintf(f, ", name=\"%s\"", pa->name);
+
+		/* for MBR attr=80 means bootable */
+		if (pa->attrs) {
+			struct fdisk_label *lb = script_get_label(dp);
+
+			if (!lb || fdisk_label_get_type(lb) != FDISK_DISKLABEL_DOS)
+				fprintf(f, ", attrs=\"%s\"", pa->attrs);
+		}
+		if (fdisk_partition_is_bootable(pa))
+			fprintf(f, ", bootable");
+		fputc('\n', f);
+	}
+
+	DBG(SCRIPT, ul_debugobj(dp, "write script done"));
+	return 0;
+}
+
+static inline int is_header_line(const char *s)
+{
+	const char *p = strchr(s, ':');
+
+	if (!p || p == s || !*(p + 1) || strchr(s, '='))
+		return 0;
+
+	return 1;
+}
+
+/* parses "<name>: value", note modifies @s*/
+static int parse_header_line(struct fdisk_script *dp, char *s)
+{
+	int rc = -EINVAL;
+	char *name, *value;
+
+	DBG(SCRIPT, ul_debugobj(dp, "   parse header '%s'", s));
+
+	if (!s || !*s)
+		return -EINVAL;
+
+	name = s;
+	value = strchr(s, ':');
+	if (!value)
+		goto done;
+	*value = '\0';
+	value++;
+
+	ltrim_whitespace((unsigned char *) name);
+	rtrim_whitespace((unsigned char *) name);
+	ltrim_whitespace((unsigned char *) value);
+	rtrim_whitespace((unsigned char *) value);
+
+	if (strcmp(name, "label") == 0) {
+		if (dp->cxt && !fdisk_get_label(dp->cxt, value))
+			goto done;			/* unknown label name */
+	} else if (strcmp(name, "unit") == 0) {
+		if (strcmp(value, "sectors") != 0)
+			goto done;			/* only "sectors" supported */
+	} else if (strcmp(name, "label-id") == 0
+		   || strcmp(name, "device") == 0) {
+		;					/* whatever is posssible */
+	} else
+		goto done;				/* unknown header */
+
+	if (*name && *value)
+		rc = fdisk_script_set_header(dp, name, value);
+done:
+	if (rc)
+		DBG(SCRIPT, ul_debugobj(dp, "header parse error: "
+				"[rc=%d, name='%s', value='%s']",
+				rc, name, value));
+	return rc;
+
+}
+
+/* returns zero terminated string with next token and @str is updated */
+static char *next_token(char **str)
+{
+	char *tk_begin = NULL,
+	     *tk_end = NULL,
+	     *end = NULL,
+	     *p;
+	int open_quote = 0;
+
+	for (p = *str; p && *p; p++) {
+		if (!tk_begin) {
+			if (isblank(*p))
+				continue;
+			tk_begin = *p == '"' ? p + 1 : p;
+		}
+		if (*p == '"')
+			open_quote ^= 1;
+		if (open_quote)
+			continue;
+		if (isblank(*p) || *p == ',' || *p == ';' || *p == '"' )
+			tk_end = p;
+		else if (*(p + 1) == '\0')
+			tk_end = p + 1;
+		if (tk_begin && tk_end)
+			break;
+	}
+
+	if (!tk_end)
+		return NULL;
+	end = isblank(*tk_end) ? (char *) skip_blank(tk_end) : tk_end;
+	if (*end == ',' || *end == ';')
+		end++;
+
+	*tk_end = '\0';
+	*str = end;
+	return tk_begin;
+}
+
+static int next_number(char **s, uint64_t *num, int *power)
+{
+	char *tk;
+	int rc = -EINVAL;
+
+	assert(num);
+	assert(s);
+
+	tk = next_token(s);
+	if (tk)
+		rc = parse_size(tk, (uintmax_t *) num, power);
+	return rc;
+}
+
+static int next_string(char **s, char **str)
+{
+	char *tk;
+	int rc = -EINVAL;
+
+	assert(s);
+	assert(str);
+
+	tk = next_token(s);
+	if (tk) {
+		*str = strdup(tk);
+		rc = !*str ? -ENOMEM : 0;
+	}
+	return rc;
+}
+
+static int partno_from_devname(char *s)
+{
+	int pno;
+	size_t sz;
+	char *end, *p;
+
+	sz = rtrim_whitespace((unsigned char *)s);
+	p = s + sz - 1;
+
+	while (p > s && isdigit(*(p - 1)))
+		p--;
+
+	errno = 0;
+	pno = strtol(p, &end, 10);
+	if (errno || !end || p == end)
+		return -1;
+	return pno - 1;
+}
+
+/* dump format
+ * <device>: start=<num>, size=<num>, type=<string>, ...
+ */
+static int parse_script_line(struct fdisk_script *dp, char *s)
+{
+	char *p, *x;
+	struct fdisk_partition *pa;
+	int rc = 0;
+	uint64_t num;
+	int pno;
+
+	assert(dp);
+	assert(s);
+
+	DBG(SCRIPT, ul_debugobj(dp, "   parse script line: '%s'", s));
+
+	pa = fdisk_new_partition();
+	if (!pa)
+		return -ENOMEM;
+
+	fdisk_partition_start_follow_default(pa, 1);
+	fdisk_partition_end_follow_default(pa, 1);
+	fdisk_partition_partno_follow_default(pa, 1);
+
+	/* set partno */
+	p = strchr(s, ':');
+	x = strchr(s, '=');
+	if (p && (!x || p < x)) {
+		*p = '\0';
+		p++;
+
+		pno = partno_from_devname(s);
+		if (pno >= 0) {
+			fdisk_partition_partno_follow_default(pa, 0);
+			fdisk_partition_set_partno(pa, pno);
+		}
+	} else
+		p = s;
+
+	while (rc == 0 && p && *p) {
+
+		DBG(SCRIPT, ul_debugobj(dp, " parsing '%s'", p));
+		p = (char *) skip_blank(p);
+
+		if (!strncasecmp(p, "start=", 6)) {
+			p += 6;
+			rc = next_number(&p, &num, NULL);
+			if (!rc) {
+				fdisk_partition_set_start(pa, num);
+				fdisk_partition_start_follow_default(pa, 0);
+			}
+		} else if (!strncasecmp(p, "size=", 5)) {
+			int pow = 0;
+
+			p += 5;
+			rc = next_number(&p, &num, &pow);
+			if (!rc) {
+				if (pow)	/* specified as <num><suffix> */
+					num /= dp->cxt->sector_size;
+				else		/* specified as number of sectors */
+					fdisk_partition_size_explicit(pa, 1);
+				fdisk_partition_set_size(pa, num);
+				fdisk_partition_end_follow_default(pa, 0);
+			}
+
+		} else if (!strncasecmp(p, "bootable", 8)) {
+			char *tk = next_token(&p);
+			if (strcmp(tk, "bootable") == 0)
+				pa->boot = 1;
+			else
+				rc = -EINVAL;
+
+		} else if (!strncasecmp(p, "attrs=", 6)) {
+			p += 6;
+			rc = next_string(&p, &pa->attrs);
+
+		} else if (!strncasecmp(p, "uuid=", 5)) {
+			p += 5;
+			rc = next_string(&p, &pa->uuid);
+
+		} else if (!strncasecmp(p, "name=", 5)) {
+			p += 5;
+			rc = next_string(&p, &pa->name);
+
+		} else if (!strncasecmp(p, "type=", 5) ||
+
+			   !strncasecmp(p, "Id=", 3)) {		/* backward compatiility */
+			char *type;
+
+			p += (*p == 'I' ? 3 : 5);		/* "Id=" or "type=" */
+
+			rc = next_string(&p, &type);
+			if (rc)
+				break;
+			pa->type = fdisk_label_parse_parttype(
+					script_get_label(dp), type);
+			free(type);
+
+			if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
+				rc = -EINVAL;
+				fdisk_unref_parttype(pa->type);
+				pa->type = NULL;
+				break;
+			}
+
+		} else {
+			DBG(SCRIPT, ul_debugobj(dp, "script parse error: unknown field '%s'", p));
+			rc = -EINVAL;
+			break;
+		}
+	}
+
+	if (!rc)
+		rc = fdisk_table_add_partition(dp->table, pa);
+	if (rc)
+		DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
+
+	fdisk_unref_partition(pa);
+	return rc;
+}
+
+/* original sfdisk supports partition types shortcuts like 'L' = Linux native
+ */
+static struct fdisk_parttype *translate_type_shortcuts(struct fdisk_script *dp, char *str)
+{
+	struct fdisk_label *lb;
+	const char *type = NULL;
+
+	if (strlen(str) != 1)
+		return NULL;
+
+	lb = script_get_label(dp);
+	if (!lb)
+		return NULL;
+
+	if (lb->id == FDISK_DISKLABEL_DOS) {
+		switch (*str) {
+		case 'L':	/* Linux */
+			type = "83";
+			break;
+		case 'S':	/* Swap */
+			type = "82";
+			break;
+		case 'E':	/* Dos extended */
+			type = "05";
+			break;
+		case 'X':	/* Linux extended */
+			type = "85";
+			break;
+		}
+	} else if (lb->id == FDISK_DISKLABEL_GPT) {
+		switch (*str) {
+		case 'L':	/* Linux */
+			type = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
+			break;
+		case 'S':	/* Swap */
+			type = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F";
+			break;
+		case 'H':	/* Home */
+			type = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915";
+			break;
+		}
+	}
+
+	return type ? fdisk_label_parse_parttype(lb, type) : NULL;
+}
+
+/* simple format:
+ * <start>, <size>, <type>, <bootable>, ...
+ */
+static int parse_commas_line(struct fdisk_script *dp, char *s)
+{
+	int rc = 0;
+	char *p = s, *str;
+	struct fdisk_partition *pa;
+	enum { ITEM_START, ITEM_SIZE, ITEM_TYPE, ITEM_BOOTABLE };
+	int item = -1;
+
+	assert(dp);
+	assert(s);
+
+	pa = fdisk_new_partition();
+	if (!pa)
+		return -ENOMEM;
+
+	fdisk_partition_start_follow_default(pa, 1);
+	fdisk_partition_end_follow_default(pa, 1);
+	fdisk_partition_partno_follow_default(pa, 1);
+
+	while (rc == 0 && p && *p) {
+		uint64_t num;
+		char *begin;
+
+		p = (char *) skip_blank(p);
+		item++;
+
+		DBG(SCRIPT, ul_debugobj(dp, " parsing item %d ('%s')", item, p));
+		begin = p;
+
+		switch (item) {
+		case ITEM_START:
+			if (*p == ',' || *p == ';')
+				fdisk_partition_start_follow_default(pa, 1);
+			else {
+				rc = next_number(&p, &num, NULL);
+				if (!rc)
+					fdisk_partition_set_start(pa, num);
+				fdisk_partition_start_follow_default(pa, 0);
+			}
+			break;
+		case ITEM_SIZE:
+			if (*p == ',' || *p == ';' || *p == '+')
+				fdisk_partition_end_follow_default(pa, 1);
+			else {
+				int pow = 0;
+				rc = next_number(&p, &num, &pow);
+				if (!rc) {
+					if (pow) /* specified as <size><suffix> */
+						num /= dp->cxt->sector_size;
+					else	 /* specified as number of sectors */
+						fdisk_partition_size_explicit(pa, 1);
+					fdisk_partition_set_size(pa, num);
+				}
+				fdisk_partition_end_follow_default(pa, 0);
+			}
+			break;
+		case ITEM_TYPE:
+			if (*p == ',' || *p == ';')
+				break;	/* use default type */
+
+			rc = next_string(&p, &str);
+			if (rc)
+				break;
+
+			pa->type = translate_type_shortcuts(dp, str);
+			if (!pa->type)
+				pa->type = fdisk_label_parse_parttype(
+						script_get_label(dp), str);
+			free(str);
+
+			if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
+				rc = -EINVAL;
+				fdisk_unref_parttype(pa->type);
+				pa->type = NULL;
+				break;
+			}
+			break;
+		case ITEM_BOOTABLE:
+			if (*p == ',' || *p == ';')
+				break;
+			else {
+				char *tk = next_token(&p);
+				if (tk && *tk == '*' && *(tk + 1) == '\0')
+					pa->boot = 1;
+				else if (tk && *tk == '-' && *(tk + 1) == '\0')
+					pa->boot = 0;
+				else
+					rc = -EINVAL;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (begin == p)
+			p++;
+	}
+
+	if (!rc)
+		rc = fdisk_table_add_partition(dp->table, pa);
+	if (rc)
+		DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
+
+	fdisk_unref_partition(pa);
+	return rc;
+}
+
+/* modifies @s ! */
+int fdisk_script_read_buffer(struct fdisk_script *dp, char *s)
+{
+	int rc = 0;
+
+	assert(dp);
+	assert(s);
+
+	DBG(SCRIPT, ul_debugobj(dp, "  parsing buffer"));
+
+	s = (char *) skip_blank(s);
+	if (!s || !*s)
+		return 0;	/* nothing baby, ignore */
+
+	if (!dp->table) {
+		dp->table = fdisk_new_table();
+		if (!dp->table)
+			return -ENOMEM;
+	}
+
+	/* parse header lines only if no partition specified yet */
+	if (fdisk_table_is_empty(dp->table) && is_header_line(s))
+		rc = parse_header_line(dp, s);
+
+	/* parse script format */
+	else if (strchr(s, '='))
+		rc = parse_script_line(dp, s);
+
+	/* parse simple <value>, ... format */
+	else
+		rc = parse_commas_line(dp, s);
+
+	if (rc)
+		DBG(SCRIPT, ul_debugobj(dp, "%zu: parse error [rc=%d]",
+				dp->nlines, rc));
+	return rc;
+}
+
+/**
+ * fdisk_script_read_line:
+ * @dp: script
+ * @f: file
+ * @buf: buffer to store one line of the file
+ * @bufsz: buffer size
+ *
+ * Reads next line into dump.
+ *
+ * Returns: 0 on success, <0 on error, 1 when nothing to read.
+ */
+int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz)
+{
+	char *s;
+
+	assert(dp);
+	assert(f);
+
+	DBG(SCRIPT, ul_debugobj(dp, " parsing line %zu", dp->nlines));
+
+	/* read the next non-blank non-comment line */
+	do {
+		if (fgets(buf, bufsz, f) == NULL)
+			return 1;
+		dp->nlines++;
+		s = strchr(buf, '\n');
+		if (!s) {
+			/* Missing final newline?  Otherwise an extremely */
+			/* long line - assume file was corrupted */
+			if (feof(f)) {
+				DBG(SCRIPT, ul_debugobj(dp, "no final newline"));
+				s = strchr(buf, '\0');
+			} else {
+				DBG(SCRIPT, ul_debugobj(dp,
+					"%zu: missing newline at line", dp->nlines));
+				return -EINVAL;
+			}
+		}
+
+		*s = '\0';
+		if (--s >= buf && *s == '\r')
+			*s = '\0';
+		s = (char *) skip_blank(buf);
+	} while (*s == '\0' || *s == '#');
+
+	return fdisk_script_read_buffer(dp, s);
+}
+
+
+/**
+ * fdisk_script_read_file:
+ * @dp: script
+ * @f: input file
+ *
+ * Reads file @f into script @dp.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_script_read_file(struct fdisk_script *dp, FILE *f)
+{
+	char buf[BUFSIZ];
+	int rc;
+
+	assert(dp);
+	assert(f);
+
+	DBG(SCRIPT, ul_debugobj(dp, "parsing file"));
+
+	while (!feof(f)) {
+		rc = fdisk_script_read_line(dp, f, buf, sizeof(buf));
+		if (rc)
+			break;
+	}
+
+	if (rc == 1)
+		rc = 0;		/* end of file */
+
+	DBG(SCRIPT, ul_debugobj(dp, "parsing file done [rc=%d]", rc));
+	return rc;
+}
+
+/**
+ * fdisk_set_script:
+ * @cxt: context
+ * @dp: script (or NULL to remove previous reference)
+ *
+ * Sets reference to the @dp script. The script headers might be used by label
+ * drivers to overwrite built-in defaults (for example disk label Id) and label
+ * driver might optimize the default semantic to be more usable for scripts
+ * (for example to not ask for primary/logical/extended partition type).
+ *
+ * Note that script also contains reference to the fdisk context (see
+ * fdisk_new_script()). This context may be completely independent on
+ * context used for fdisk_set_script().
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp)
+{
+	assert(cxt);
+
+	/* unref old */
+	if (cxt->script)
+		fdisk_unref_script(cxt->script);
+
+	/* ref new */
+	cxt->script = dp;
+	if (cxt->script) {
+		DBG(CXT, ul_debugobj(cxt, "setting reference to script %p", cxt->script));
+		fdisk_ref_script(cxt->script);
+	}
+
+	return 0;
+}
+
+/**
+ * fdisk_get_script:
+ * @cxt: context
+ *
+ * Returns: the current script or NULL.
+ */
+struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	return cxt->script;
+}
+
+/**
+ * fdisk_apply_script_headers:
+ * @cxt: context
+ * @dp: script
+ *
+ * Associte context @cxt with script @dp and creates a new empty disklabel.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp)
+{
+	const char *name;
+
+	assert(cxt);
+	assert(dp);
+
+	DBG(SCRIPT, ul_debugobj(dp, "applying script headers"));
+	fdisk_set_script(cxt, dp);
+
+	/* create empty label */
+	name = fdisk_script_get_header(dp, "label");
+	if (!name)
+		return -EINVAL;
+
+	return fdisk_create_disklabel(cxt, name);
+}
+
+/**
+ * fdisk_apply_script:
+ * @cxt: context
+ * @dp: script
+ *
+ * This function creates a new disklabel and partition within context @cxt. You
+ * have to call fdisk_write_disklabel() to apply changes to the device.
+ *
+ * Returns: 0 on error, <0 on error.
+ */
+int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp)
+{
+	int rc;
+	struct fdisk_script *old;
+
+	assert(dp);
+	assert(cxt);
+
+	DBG(CXT, ul_debugobj(cxt, "applying script %p", dp));
+
+	old = fdisk_get_script(cxt);
+
+	/* create empty disk label */
+	rc = fdisk_apply_script_headers(cxt, dp);
+
+	/* create partitions */
+	if (!rc && dp->table)
+		rc = fdisk_apply_table(cxt, dp->table);
+
+	fdisk_set_script(cxt, old);
+	DBG(CXT, ul_debugobj(cxt, "script done [rc=%d]", rc));
+	return rc;
+}
+
+#ifdef TEST_PROGRAM
+int test_dump(struct fdisk_test *ts, int argc, char *argv[])
+{
+	char *devname = argv[1];
+	struct fdisk_context *cxt;
+	struct fdisk_script *dp;
+
+	cxt = fdisk_new_context();
+	fdisk_assign_device(cxt, devname, 1);
+
+	dp = fdisk_new_script(cxt);
+	fdisk_script_read_context(dp, NULL);
+
+	fdisk_script_write_file(dp, stdout);
+	fdisk_unref_script(dp);
+	fdisk_unref_context(cxt);
+
+	return 0;
+}
+
+int test_read(struct fdisk_test *ts, int argc, char *argv[])
+{
+	char *filename = argv[1];
+	struct fdisk_script *dp;
+	struct fdisk_context *cxt;
+	FILE *f;
+
+	if (!(f = fopen(filename, "r")))
+		err(EXIT_FAILURE, "%s: cannot open", filename);
+
+	cxt = fdisk_new_context();
+	dp = fdisk_new_script(cxt);
+
+	fdisk_script_read_file(dp, f);
+	fclose(f);
+
+	fdisk_script_write_file(dp, stdout);
+	fdisk_unref_script(dp);
+	fdisk_unref_context(cxt);
+
+	return 0;
+}
+
+int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
+{
+	char buf[BUFSIZ];
+	struct fdisk_script *dp;
+	struct fdisk_context *cxt;
+	int rc = 0;
+
+	cxt = fdisk_new_context();
+	dp = fdisk_new_script(cxt);
+	fdisk_script_set_header(dp, "label", "dos");
+
+	printf("<start>, <size>, <type>, <bootable: *|->\n");
+	do {
+		struct fdisk_partition *pa;
+		size_t n = fdisk_table_get_nents(dp->table);
+
+		printf(" #%zu :\n", n + 1);
+		rc = fdisk_script_read_line(dp, stdin, buf, sizeof(buf));
+
+		if (rc == 0) {
+			pa = fdisk_table_get_partition(dp->table, n);
+			printf(" #%zu  %12ju %12ju\n",	n + 1,
+						fdisk_partition_get_start(pa),
+						fdisk_partition_get_size(pa));
+		}
+	} while (rc == 0);
+
+	if (!rc)
+		fdisk_script_write_file(dp, stdout);
+	fdisk_unref_script(dp);
+	fdisk_unref_context(cxt);
+
+	return rc;
+}
+
+int test_apply(struct fdisk_test *ts, int argc, char *argv[])
+{
+	char *devname = argv[1], *scriptname = argv[2];
+	struct fdisk_context *cxt;
+	struct fdisk_script *dp = NULL;
+	struct fdisk_table *tb = NULL;
+	struct fdisk_iter *itr = NULL;
+	struct fdisk_partition *pa = NULL;
+	int rc;
+
+	cxt = fdisk_new_context();
+	fdisk_assign_device(cxt, devname, 0);
+
+	dp = fdisk_new_script_from_file(cxt, scriptname);
+	if (!dp)
+		return -errno;
+
+	rc = fdisk_apply_script(cxt, dp);
+	if (rc)
+		goto done;
+	fdisk_unref_script(dp);
+
+	/* list result */
+	fdisk_list_disklabel(cxt);
+	fdisk_get_partitions(cxt, &tb);
+
+	itr = fdisk_new_iter(FDISK_ITER_FORWARD);
+	while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
+		printf(" #%zu  %12ju %12ju\n",	fdisk_partition_get_partno(pa),
+						fdisk_partition_get_start(pa),
+						fdisk_partition_get_size(pa));
+	}
+
+done:
+	fdisk_free_iter(itr);
+	fdisk_unref_table(tb);
+
+	/*fdisk_write_disklabel(cxt);*/
+	fdisk_unref_context(cxt);
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct fdisk_test tss[] = {
+	{ "--dump",    test_dump,    "<device>            dump PT as script" },
+	{ "--read",    test_read,    "<file>              read PT script from file" },
+	{ "--apply",   test_apply,   "<device> <file>     try apply script from file to device" },
+	{ "--stdin",   test_stdin,   "                    read input like sfdisk" },
+	{ NULL }
+	};
+
+	return fdisk_run_test(tss, argc, argv);
+}
+
+#endif
diff --git a/libblkid/libfdisk/src/sgi.c b/libblkid/libfdisk/src/sgi.c
new file mode 100644
index 0000000..cd4cedf
--- /dev/null
+++ b/libblkid/libfdisk/src/sgi.c
@@ -0,0 +1,1185 @@
+/*
+ *
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *               2013 Karel Zak <kzak@redhat.com>
+ *
+ * This is a re-written version for libfdisk, the original was fdisksgilabel.c
+ * from util-linux fdisk, by:
+ *
+ *               Andreas Neuper, Sep 1998,
+ *               Arnaldo Carvalho de Melo <acme@conectiva.com.br>, Mar 1999,
+ *               Phillip Kesling <pkesling@sgi.com>, Mar 2003.
+ */
+
+#include "c.h"
+#include "nls.h"
+#include "all-io.h"
+
+#include "blkdev.h"
+
+#include "bitops.h"
+#include "pt-sgi.h"
+#include "pt-mbr.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: sgi
+ * @title: SGI
+ * @short_description: disk label specific functions
+ *
+ */
+
+/*
+ * in-memory fdisk SGI stuff
+ */
+struct fdisk_sgi_label {
+	struct fdisk_label	head;		/* generic fdisk part */
+	struct sgi_disklabel	*header;	/* on-disk data (pointer to cxt->firstsector) */
+
+	struct sgi_freeblocks {
+		unsigned int first;
+		unsigned int last;
+	} freelist[SGI_MAXPARTITIONS + 1];
+};
+
+static struct fdisk_parttype sgi_parttypes[] =
+{
+	{SGI_TYPE_VOLHDR,	N_("SGI volhdr")},
+	{SGI_TYPE_TRKREPL,	N_("SGI trkrepl")},
+	{SGI_TYPE_SECREPL,	N_("SGI secrepl")},
+	{SGI_TYPE_SWAP,		N_("SGI raw")},
+	{SGI_TYPE_BSD,		N_("SGI bsd")},
+	{SGI_TYPE_SYSV,		N_("SGI sysv")},
+	{SGI_TYPE_ENTIRE_DISK,	N_("SGI volume")},
+	{SGI_TYPE_EFS,		N_("SGI efs")},
+	{SGI_TYPE_LVOL,		N_("SGI lvol")},
+	{SGI_TYPE_RLVOL,	N_("SGI rlvol")},
+	{SGI_TYPE_XFS,		N_("SGI xfs")},
+	{SGI_TYPE_XFSLOG,	N_("SGI xfslog")},
+	{SGI_TYPE_XLV,		N_("SGI xlv")},
+	{SGI_TYPE_XVM,		N_("SGI xvm")},
+	{MBR_LINUX_SWAP_PARTITION, N_("Linux swap")},
+	{MBR_LINUX_DATA_PARTITION, N_("Linux native")},
+	{MBR_LINUX_LVM_PARTITION, N_("Linux LVM")},
+	{MBR_LINUX_RAID_PARTITION, N_("Linux RAID")},
+	{0, NULL }
+};
+
+static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i );
+static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i );
+static int sgi_get_bootpartition(struct fdisk_context *cxt);
+static int sgi_get_swappartition(struct fdisk_context *cxt);
+
+/* Returns a pointer buffer with on-disk data. */
+static inline struct sgi_disklabel *self_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	return ((struct fdisk_sgi_label *) cxt->label)->header;
+}
+
+/* Returns in-memory fdisk data. */
+static inline struct fdisk_sgi_label *self_label(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	return (struct fdisk_sgi_label *) cxt->label;
+}
+
+/*
+ * Information within second on-disk block
+ */
+#define	SGI_INFO_MAGIC		0x00072959
+
+struct sgi_info {
+	unsigned int   magic;		/* looks like a magic number */
+	unsigned int   a2;
+	unsigned int   a3;
+	unsigned int   a4;
+	unsigned int   b1;
+	unsigned short b2;
+	unsigned short b3;
+	unsigned int   c[16];
+	unsigned short d[3];
+	unsigned char  scsi_string[50];
+	unsigned char  serial[137];
+	unsigned short check1816;
+	unsigned char  installer[225];
+};
+
+static struct sgi_info *sgi_new_info(void)
+{
+	struct sgi_info *info = calloc(1, sizeof(struct sgi_info));
+
+	if (!info)
+		return NULL;
+
+	info->magic = cpu_to_be32(SGI_INFO_MAGIC);
+	info->b1 = cpu_to_be32(-1);
+	info->b2 = cpu_to_be16(-1);
+	info->b3 = cpu_to_be16(1);
+
+	/* You may want to replace this string !!!!!!! */
+	strcpy((char *) info->scsi_string, "IBM OEM 0662S12         3 30");
+	strcpy((char *) info->serial, "0000");
+	info->check1816 = cpu_to_be16(18 * 256 + 16);
+	strcpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994");
+
+	return info;
+}
+
+static void sgi_free_info(struct sgi_info *info)
+{
+	free(info);
+}
+
+/**
+ * fdisk_sgi_create_info:
+ * @cxt: context
+ *
+ * This function add hint about SGI label (e.g. set "sgilabel" as volume name)
+ * to the first SGI volume. This is probably old SGI convention without any
+ * effect to the device partitioning.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_sgi_create_info(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+
+	/* I keep SGI's habit to write the sgilabel to the second block */
+	sgilabel->volume[0].block_num = cpu_to_be32(2);
+	sgilabel->volume[0].num_bytes = cpu_to_be32(sizeof(struct sgi_info));
+	strncpy((char *) sgilabel->volume[0].name, "sgilabel", 8);
+
+	fdisk_info(cxt, _("SGI info created on second sector."));
+	return 0;
+}
+
+
+/*
+ * only dealing with free blocks here
+ */
+static void set_freelist(struct fdisk_context *cxt,
+		size_t i, unsigned int f, unsigned int l)
+{
+	struct fdisk_sgi_label *sgi = self_label(cxt);
+
+	if (i < ARRAY_SIZE(sgi->freelist)) {
+		sgi->freelist[i].first = f;
+		sgi->freelist[i].last = l;
+	}
+}
+
+static void add_to_freelist(struct fdisk_context *cxt,
+		unsigned int f, unsigned int l)
+{
+	struct fdisk_sgi_label *sgi = self_label(cxt);
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) {
+		if (sgi->freelist[i].last == 0)
+			break;
+	}
+	set_freelist(cxt, i, f, l);
+}
+
+static void clear_freelist(struct fdisk_context *cxt)
+{
+	struct fdisk_sgi_label *sgi = self_label(cxt);
+
+	memset(sgi->freelist, 0, sizeof(sgi->freelist));
+}
+
+static unsigned int is_in_freelist(struct fdisk_context *cxt, unsigned int b)
+{
+	struct fdisk_sgi_label *sgi = self_label(cxt);
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) {
+		if (sgi->freelist[i].first <= b
+		    && sgi->freelist[i].last >= b)
+			return sgi->freelist[i].last;
+	}
+
+	return 0;
+}
+
+
+static int sgi_get_nsect(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be16_to_cpu(sgilabel->devparam.nsect);
+}
+
+static int sgi_get_ntrks(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be16_to_cpu(sgilabel->devparam.ntrks);
+}
+
+static size_t count_used_partitions(struct fdisk_context *cxt)
+{
+	size_t i, ct = 0;
+
+	for (i = 0; i < cxt->label->nparts_max; i++)
+		ct += sgi_get_num_sectors(cxt, i) > 0;
+
+	return ct;
+}
+
+static int sgi_probe_label(struct fdisk_context *cxt)
+{
+	struct fdisk_sgi_label *sgi;
+	struct sgi_disklabel *sgilabel;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+	assert(sizeof(struct sgi_disklabel) <= 512);
+
+	/* map first sector to header */
+	sgi = (struct fdisk_sgi_label *) cxt->label;
+	sgi->header = (struct sgi_disklabel *) cxt->firstsector;
+	sgilabel = sgi->header;
+
+	if (be32_to_cpu(sgilabel->magic) != SGI_LABEL_MAGIC) {
+		sgi->header = NULL;
+		return 0;
+	}
+
+	/*
+	 * test for correct checksum
+	 */
+	if (sgi_pt_checksum(sgilabel) != 0)
+		fdisk_warnx(cxt, _("Detected an SGI disklabel with wrong checksum."));
+
+	clear_freelist(cxt);
+	cxt->label->nparts_max = SGI_MAXPARTITIONS;
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+	return 1;
+}
+
+static int sgi_list_table(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	struct sgi_device_parameter *sgiparam = &sgilabel->devparam;
+	int rc = 0;
+
+	if (fdisk_is_details(cxt))
+		fdisk_info(cxt, _(
+			"Label geometry: %d heads, %llu sectors\n"
+			"                %llu cylinders, %d physical cylinders\n"
+			"                %d extra sects/cyl, interleave %d:1\n"),
+			cxt->geom.heads, cxt->geom.sectors,
+			cxt->geom.cylinders, be16_to_cpu(sgiparam->pcylcount),
+			(int) sgiparam->sparecyl, be16_to_cpu(sgiparam->ilfact));
+
+	fdisk_info(cxt, _("Bootfile: %s"), sgilabel->boot_file);
+	return rc;
+}
+
+static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be32_to_cpu(sgilabel->partitions[i].first_block);
+}
+
+static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be32_to_cpu(sgilabel->partitions[i].num_blocks);
+}
+
+static int sgi_get_sysid(struct fdisk_context *cxt, int i)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be32_to_cpu(sgilabel->partitions[i].type);
+}
+
+static int sgi_get_bootpartition(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be16_to_cpu(sgilabel->root_part_num);
+}
+
+static int sgi_get_swappartition(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+	return be16_to_cpu(sgilabel->swap_part_num);
+}
+
+static unsigned int sgi_get_lastblock(struct fdisk_context *cxt)
+{
+	return cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders;
+}
+
+static struct fdisk_parttype *sgi_get_parttype(struct fdisk_context *cxt, size_t n)
+{
+	struct fdisk_parttype *t;
+
+	if (n >= cxt->label->nparts_max)
+		return NULL;
+
+	t = fdisk_label_get_parttype_from_code(cxt->label, sgi_get_sysid(cxt, n));
+	return t ? : fdisk_new_unknown_parttype(sgi_get_sysid(cxt, n), NULL);
+}
+
+/* fdisk_get_partition() backend */
+static int sgi_get_partition(struct fdisk_context *cxt, size_t n, struct fdisk_partition *pa)
+{
+	fdisk_sector_t start, len;
+
+	pa->used = sgi_get_num_sectors(cxt, n) > 0;
+	if (!pa->used)
+		return 0;
+
+	start = sgi_get_start_sector(cxt, n);
+	len = sgi_get_num_sectors(cxt, n);
+
+	pa->type = sgi_get_parttype(cxt, n);
+	pa->size = len;
+	pa->start = start;
+
+	if (pa->type && pa->type->code == SGI_TYPE_ENTIRE_DISK)
+		pa->wholedisk = 1;
+
+	pa->attrs = sgi_get_swappartition(cxt) == (int) n ? "swap" :
+		    sgi_get_bootpartition(cxt) == (int) n ? "boot" : NULL;
+	if (pa->attrs)
+		pa->attrs = strdup(pa->attrs);
+
+	return 0;
+}
+
+
+static int sgi_check_bootfile(struct fdisk_context *cxt, const char *name)
+{
+	size_t sz;
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+
+	sz = strlen(name);
+
+	if (sz < 3) {
+		/* "/a\n" is minimum */
+		fdisk_warnx(cxt, _("Invalid bootfile!  The bootfile must "
+				   "be an absolute non-zero pathname, "
+				   "e.g. \"/unix\" or \"/unix.save\"."));
+		return -EINVAL;
+
+	} else if (sz > sizeof(sgilabel->boot_file)) {
+		fdisk_warnx(cxt, P_("Name of bootfile is too long: %zu byte maximum.",
+				    "Name of bootfile is too long: %zu bytes maximum.",
+				    sizeof(sgilabel->boot_file)),
+			    sizeof(sgilabel->boot_file));
+		return -EINVAL;
+
+	} else if (*name != '/') {
+		fdisk_warnx(cxt, _("Bootfile must have a fully qualified pathname."));
+		return -EINVAL;
+	}
+
+	if (strncmp(name, (char *) sgilabel->boot_file,
+				sizeof(sgilabel->boot_file))) {
+		fdisk_warnx(cxt, _("Be aware that the bootfile is not checked "
+				   "for existence.  SGI's default is \"/unix\", "
+				   "and for backup \"/unix.save\"."));
+		return 0;	/* filename is correct and did change */
+	}
+
+	return 1;	/* filename did not change */
+}
+
+/**
+ * fdisk_sgi_set_bootfile:
+ * @cxt: context
+ *
+ * Allows to set SGI boot file. The function uses Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_sgi_set_bootfile(struct fdisk_context *cxt)
+{
+	int rc = 0;
+	size_t sz;
+	char *name = NULL;
+	struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+
+	fdisk_info(cxt, _("The current boot file is: %s"), sgilabel->boot_file);
+
+	rc = fdisk_ask_string(cxt, _("Enter of the new boot file"), &name);
+	if (rc == 0)
+		rc = sgi_check_bootfile(cxt, name);
+	if (rc) {
+		if (rc == 1)
+			fdisk_info(cxt, _("Boot file is unchanged."));
+		goto done;
+	}
+
+	memset(sgilabel->boot_file, 0, sizeof(sgilabel->boot_file));
+	sz = strlen(name);
+
+	assert(sz <= sizeof(sgilabel->boot_file));	/* see sgi_check_bootfile() */
+
+	memcpy(sgilabel->boot_file, name, sz);
+
+	fdisk_info(cxt, _("Bootfile has been changed to \"%s\"."), name);
+done:
+	free(name);
+	return rc;
+}
+
+static int sgi_write_disklabel(struct fdisk_context *cxt)
+{
+	struct sgi_disklabel *sgilabel;
+	struct sgi_info *info = NULL;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	sgilabel = self_disklabel(cxt);
+	sgilabel->csum = 0;
+	sgilabel->csum = cpu_to_be32(sgi_pt_checksum(sgilabel));
+
+	assert(sgi_pt_checksum(sgilabel) == 0);
+
+	if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+		goto err;
+	if (write_all(cxt->dev_fd, sgilabel, DEFAULT_SECTOR_SIZE))
+		goto err;
+	if (!strncmp((char *) sgilabel->volume[0].name, "sgilabel", 8)) {
+		/*
+		 * Keep this habit of first writing the "sgilabel".
+		 * I never tested whether it works without. (AN 1998-10-02)
+		 */
+		int infostartblock
+			= be32_to_cpu(sgilabel->volume[0].block_num);
+
+		if (lseek(cxt->dev_fd, (off_t) infostartblock *
+					DEFAULT_SECTOR_SIZE, SEEK_SET) < 0)
+			goto err;
+		info = sgi_new_info();
+		if (!info)
+			goto err;
+		if (write_all(cxt->dev_fd, info, sizeof(*info)))
+			goto err;
+	}
+
+	sgi_free_info(info);
+	return 0;
+err:
+	sgi_free_info(info);
+	return -errno;
+}
+
+static int compare_start(struct fdisk_context *cxt,
+			 const void *x, const void *y)
+{
+	/*
+	 * Sort according to start sectors and prefer the largest partition:
+	 * entry zero is the entire-disk entry.
+	 */
+	unsigned int i = *(int *) x;
+	unsigned int j = *(int *) y;
+	unsigned int a = sgi_get_start_sector(cxt, i);
+	unsigned int b = sgi_get_start_sector(cxt, j);
+	unsigned int c = sgi_get_num_sectors(cxt, i);
+	unsigned int d = sgi_get_num_sectors(cxt, j);
+
+	if (a == b)
+		return (d > c) ? 1 : (d == c) ? 0 : -1;
+	return (a > b) ? 1 : -1;
+}
+
+static void generic_swap(void *a0, void *b0, int size)
+{
+	char *a = a0, *b = b0;
+
+	for (; size > 0; --size, a++, b++) {
+		char t = *a;
+		*a = *b;
+		*b = t;
+	}
+}
+
+
+/* heap sort, based on Matt Mackall's linux kernel version */
+static void sort(void *base0, size_t num, size_t size, struct fdisk_context *cxt,
+		 int (*cmp_func)(struct fdisk_context *, const void *, const void *))
+{
+	/* pre-scale counters for performance */
+	int i = (num/2 - 1) * size;
+	size_t n = num * size, c, r;
+	char *base = base0;
+
+	/* heapify */
+	for ( ; i >= 0; i -= size) {
+		for (r = i; r * 2 + size < n; r  = c) {
+			c = r * 2 + size;
+			if (c < n - size &&
+			    cmp_func(cxt, base + c, base + c + size) < 0)
+				c += size;
+			if (cmp_func(cxt, base + r, base + c) >= 0)
+				break;
+			generic_swap(base + r, base + c, size);
+		}
+	}
+
+	/* sort */
+	for (i = n - size; i > 0; i -= size) {
+		generic_swap(base, base + i, size);
+		for (r = 0; r * 2 + size < (size_t) i; r = c) {
+			c = r * 2 + size;
+			if (c < i - size &&
+			    cmp_func(cxt, base + c, base + c + size) < 0)
+				c += size;
+			if (cmp_func(cxt, base + r, base + c) >= 0)
+				break;
+			generic_swap(base + r, base + c, size);
+		}
+	}
+}
+
+static int verify_disklabel(struct fdisk_context *cxt, int verbose)
+{
+	int Index[SGI_MAXPARTITIONS];	/* list of valid partitions */
+	int sortcount = 0;		/* number of used partitions, i.e. non-zero lengths */
+	int entire = 0, i = 0;
+	unsigned int start = 0;
+	long long gap = 0;	/* count unused blocks */
+	unsigned int lastblock = sgi_get_lastblock(cxt);
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	clear_freelist(cxt);
+	memset(Index, 0, sizeof(Index));
+
+	for (i=0; i < SGI_MAXPARTITIONS; i++) {
+		if (sgi_get_num_sectors(cxt, i) != 0) {
+			Index[sortcount++] = i;
+			if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK
+			    && entire++ == 1) {
+				if (verbose)
+					fdisk_info(cxt, _("More than one entire "
+						"disk entry present."));
+			}
+		}
+	}
+	if (sortcount == 0) {
+		if (verbose)
+			fdisk_info(cxt, _("No partitions defined."));
+		if (lastblock)
+			add_to_freelist(cxt, 0, lastblock);
+		return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
+	}
+
+	sort(Index, sortcount, sizeof(Index[0]), cxt, compare_start);
+
+	if (sgi_get_sysid(cxt, Index[0]) == SGI_TYPE_ENTIRE_DISK) {
+		if (verbose && Index[0] != 10)
+			fdisk_info(cxt, _("IRIX likes it when partition 11 "
+					  "covers the entire disk."));
+
+		if (verbose && sgi_get_start_sector(cxt, Index[0]) != 0)
+			fdisk_info(cxt, _("The entire disk partition should "
+					  "start at block 0, not at block %d."),
+				   sgi_get_start_sector(cxt, Index[0]));
+
+		if (verbose && sgi_get_num_sectors(cxt, Index[0]) != lastblock)
+			DBG(LABEL, ul_debug(
+				"entire disk partition=%ds, but disk=%ds",
+				sgi_get_num_sectors(cxt, Index[0]),
+				lastblock));
+		lastblock = sgi_get_num_sectors(cxt, Index[0]);
+	} else if (verbose) {
+		fdisk_info(cxt, _("Partition 11 should cover the entire disk."));
+		DBG(LABEL, ul_debug("sysid=%d\tpartition=%d",
+			       sgi_get_sysid(cxt, Index[0]), Index[0]+1));
+	}
+	for (i=1, start=0; i<sortcount; i++) {
+		int cylsize = sgi_get_nsect(cxt) * sgi_get_ntrks(cxt);
+
+		if (verbose && cylsize
+		    && (sgi_get_start_sector(cxt, Index[i]) % cylsize) != 0)
+			DBG(LABEL, ul_debug("partition %d does not start on "
+					"cylinder boundary.", Index[i]+1));
+
+		if (verbose && cylsize
+		    && sgi_get_num_sectors(cxt, Index[i]) % cylsize != 0)
+			DBG(LABEL, ul_debug("partition %d does not end on "
+					"cylinder boundary.", Index[i]+1));
+
+		/* We cannot handle several "entire disk" entries. */
+		if (sgi_get_sysid(cxt, Index[i]) == SGI_TYPE_ENTIRE_DISK)
+			continue;
+
+		if (start > sgi_get_start_sector(cxt, Index[i])) {
+			if (verbose)
+				fdisk_info(cxt,
+					   P_("Partitions %d and %d overlap by %d sector.",
+					      "Partitions %d and %d overlap by %d sectors.",
+					      start - sgi_get_start_sector(cxt, Index[i])),
+					   Index[i-1]+1, Index[i]+1,
+					   start - sgi_get_start_sector(cxt, Index[i]));
+			if (gap >  0) gap = -gap;
+			if (gap == 0) gap = -1;
+		}
+		if (start < sgi_get_start_sector(cxt, Index[i])) {
+			if (verbose)
+				fdisk_info(cxt,
+					   P_("Unused gap of %8u sector: sector %8u",
+					      "Unused gap of %8u sectors: sectors %8u-%u",
+					      sgi_get_start_sector(cxt, Index[i]) - start),
+					   sgi_get_start_sector(cxt, Index[i]) - start,
+					   start, sgi_get_start_sector(cxt, Index[i])-1);
+			gap += sgi_get_start_sector(cxt, Index[i]) - start;
+			add_to_freelist(cxt, start,
+					sgi_get_start_sector(cxt, Index[i]));
+		}
+		start = sgi_get_start_sector(cxt, Index[i])
+			+ sgi_get_num_sectors(cxt, Index[i]);
+		/* Align free space on cylinder boundary. */
+		if (cylsize && start % cylsize)
+			start += cylsize - (start % cylsize);
+
+		DBG(LABEL, ul_debug("%2d:%12d\t%12d\t%12d", Index[i],
+				       sgi_get_start_sector(cxt, Index[i]),
+				       sgi_get_num_sectors(cxt, Index[i]),
+				       sgi_get_sysid(cxt, Index[i])));
+	}
+	if (start < lastblock) {
+		if (verbose)
+			fdisk_info(cxt, P_("Unused gap of %8u sector: sector %8u",
+					   "Unused gap of %8u sectors: sectors %8u-%u",
+					   lastblock - start),
+				   lastblock - start, start, lastblock-1);
+		gap += lastblock - start;
+		add_to_freelist(cxt, start, lastblock);
+	}
+	/*
+	 * Done with arithmetics. Go for details now.
+	 */
+	if (verbose) {
+		if (sgi_get_bootpartition(cxt) < 0
+		    || !sgi_get_num_sectors(cxt, sgi_get_bootpartition(cxt)))
+			fdisk_info(cxt, _("The boot partition does not exist."));
+
+		if (sgi_get_swappartition(cxt) < 0
+		    || !sgi_get_num_sectors(cxt, sgi_get_swappartition(cxt)))
+			fdisk_info(cxt, _("The swap partition does not exist."));
+
+		else if (sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != SGI_TYPE_SWAP
+		    && sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != MBR_LINUX_SWAP_PARTITION)
+			fdisk_info(cxt, _("The swap partition has no swap type."));
+
+		if (sgi_check_bootfile(cxt, "/unix"))
+			fdisk_info(cxt, _("You have chosen an unusual bootfile name."));
+	}
+
+	return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
+}
+
+static int sgi_verify_disklabel(struct fdisk_context *cxt)
+{
+	return verify_disklabel(cxt, 1);
+}
+
+static int sgi_gaps(struct fdisk_context *cxt)
+{
+	/*
+	 * returned value is:
+	 *  = 0 : disk is properly filled to the rim
+	 *  < 0 : there is an overlap
+	 *  > 0 : there is still some vacant space
+	 */
+	return verify_disklabel(cxt, 0);
+}
+
+/* Returns partition index of first entry marked as entire disk. */
+static int sgi_entire(struct fdisk_context *cxt)
+{
+	size_t i;
+
+	for (i = 0; i < SGI_MAXPARTITIONS; i++)
+		if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK)
+			return i;
+	return -1;
+}
+
+static int set_partition(struct fdisk_context *cxt, size_t i,
+			     unsigned int start, unsigned int length, int sys)
+{
+	struct sgi_disklabel *sgilabel;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	sgilabel = self_disklabel(cxt);
+	sgilabel->partitions[i].type = cpu_to_be32(sys);
+	sgilabel->partitions[i].num_blocks = cpu_to_be32(length);
+	sgilabel->partitions[i].first_block = cpu_to_be32(start);
+
+	fdisk_label_set_changed(cxt->label, 1);
+
+	if (sgi_gaps(cxt) < 0)	/* rebuild freelist */
+		fdisk_warnx(cxt, _("Partition overlap on the disk."));
+	if (length) {
+		struct fdisk_parttype *t =
+				fdisk_label_get_parttype_from_code(cxt->label, sys);
+		fdisk_info_new_partition(cxt, i + 1, start, start + length, t);
+	}
+
+	return 0;
+}
+
+static void sgi_set_entire(struct fdisk_context *cxt)
+{
+	size_t n;
+
+	for (n = 10; n < cxt->label->nparts_max; n++) {
+		if (!sgi_get_num_sectors(cxt, n)) {
+			set_partition(cxt, n, 0, sgi_get_lastblock(cxt), SGI_TYPE_ENTIRE_DISK);
+			break;
+		}
+	}
+}
+
+static void sgi_set_volhdr(struct fdisk_context *cxt)
+{
+	size_t n;
+
+	for (n = 8; n < cxt->label->nparts_max; n++) {
+		if (!sgi_get_num_sectors(cxt, n)) {
+			/* Choose same default volume header size as IRIX fx uses. */
+			if (4096 < sgi_get_lastblock(cxt))
+				set_partition(cxt, n, 0, 4096, SGI_TYPE_VOLHDR);
+			break;
+		}
+	}
+}
+
+static int sgi_delete_partition(struct fdisk_context *cxt, size_t partnum)
+{
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+
+	if (partnum > cxt->label->nparts_max)
+		return -EINVAL;
+
+	rc = set_partition(cxt, partnum, 0, 0, 0);
+
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+
+	return rc;
+}
+
+static int sgi_add_partition(struct fdisk_context *cxt,
+			     struct fdisk_partition *pa,
+			     size_t *partno)
+{
+	struct fdisk_sgi_label *sgi;
+	char mesg[256];
+	unsigned int first = 0, last = 0;
+	struct fdisk_ask *ask;
+	int sys = pa && pa->type ? pa->type->code : SGI_TYPE_XFS;
+	int rc;
+	size_t n;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	rc = fdisk_partition_next_partno(pa, cxt, &n);
+	if (rc)
+		return rc;
+	if (n == 10)
+		sys = SGI_TYPE_ENTIRE_DISK;
+	else if (n == 8)
+		sys = 0;
+
+	sgi = self_label(cxt);
+
+	if (sgi_get_num_sectors(cxt, n)) {
+		fdisk_warnx(cxt, _("Partition %zu is already defined.  "
+				   "Delete it before re-adding it."), n + 1);
+		return -EINVAL;
+	}
+	if (!cxt->script && sgi_entire(cxt) == -1 &&  sys != SGI_TYPE_ENTIRE_DISK) {
+		fdisk_info(cxt, _("Attempting to generate entire disk entry automatically."));
+		sgi_set_entire(cxt);
+		sgi_set_volhdr(cxt);
+	}
+	if (sgi_gaps(cxt) == 0 && sys != SGI_TYPE_ENTIRE_DISK) {
+		fdisk_warnx(cxt, _("The entire disk is already covered with partitions."));
+		return -EINVAL;
+	}
+	if (sgi_gaps(cxt) < 0) {
+		fdisk_warnx(cxt, _("You got a partition overlap on the disk. Fix it first!"));
+		return -EINVAL;
+	}
+
+	if (sys == SGI_TYPE_ENTIRE_DISK) {
+		first = 0;
+		last = sgi_get_lastblock(cxt);
+	} else {
+		first = sgi->freelist[0].first;
+		last  = sgi->freelist[0].last;
+	}
+
+	/* first sector */
+	if (pa && pa->start_follow_default)
+		;
+	else if (pa && fdisk_partition_has_start(pa)) {
+		first = pa->start;
+		last = is_in_freelist(cxt, first);
+
+		if (sys != SGI_TYPE_ENTIRE_DISK && !last)
+			return -ERANGE;
+	} else {
+		snprintf(mesg, sizeof(mesg), _("First %s"),
+				fdisk_get_unit(cxt, FDISK_SINGULAR));
+		ask = fdisk_new_ask();
+		if (!ask)
+			return -ENOMEM;
+
+		fdisk_ask_set_query(ask, mesg);
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+
+		fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
+		fdisk_ask_number_set_default(ask, fdisk_scround(cxt, first));	/* default */
+		fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, last) - 1); /* maximal */
+
+		rc = fdisk_do_ask(cxt, ask);
+		first = fdisk_ask_number_get_result(ask);
+		fdisk_unref_ask(ask);
+
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			first *= fdisk_get_units_per_sector(cxt);
+	}
+
+	if (first && sys == SGI_TYPE_ENTIRE_DISK)
+		fdisk_info(cxt, _("It is highly recommended that the "
+				  "eleventh partition covers the entire "
+				  "disk and is of type 'SGI volume'."));
+	if (!last)
+		last = is_in_freelist(cxt, first);
+
+	/* last sector */
+	if (pa && pa->end_follow_default)
+		last -= 1ULL;
+	else if (pa && fdisk_partition_has_size(pa)) {
+		if (first + pa->size - 1ULL > last)
+			return -ERANGE;
+		last = first + pa->size - 1ULL;
+	} else {
+		snprintf(mesg, sizeof(mesg),
+			 _("Last %s or +%s or +size{K,M,G,T,P}"),
+			 fdisk_get_unit(cxt, FDISK_SINGULAR),
+			 fdisk_get_unit(cxt, FDISK_PLURAL));
+
+		ask = fdisk_new_ask();
+		if (!ask)
+			return -ENOMEM;
+
+		fdisk_ask_set_query(ask, mesg);
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+		fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
+		fdisk_ask_number_set_default(ask, fdisk_scround(cxt, last) - 1);/* default */
+		fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, last) - 1);/* maximal */
+		fdisk_ask_number_set_base(ask,    fdisk_scround(cxt, first));
+
+		if (fdisk_use_cylinders(cxt))
+			fdisk_ask_number_set_unit(ask,
+				     cxt->sector_size *
+				     fdisk_get_units_per_sector(cxt));
+		else
+			fdisk_ask_number_set_unit(ask,cxt->sector_size);
+
+		rc = fdisk_do_ask(cxt, ask);
+		last = fdisk_ask_number_get_result(ask) + 1;
+
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			last *= fdisk_get_units_per_sector(cxt);
+	}
+
+	if (sys == SGI_TYPE_ENTIRE_DISK
+	    && (first != 0 || last != sgi_get_lastblock(cxt)))
+		fdisk_info(cxt, _("It is highly recommended that the "
+				  "eleventh partition covers the entire "
+				  "disk and is of type 'SGI volume'."));
+
+	set_partition(cxt, n, first, last - first, sys);
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+	if (partno)
+		*partno = n;
+	return 0;
+}
+
+static int sgi_create_disklabel(struct fdisk_context *cxt)
+{
+	struct fdisk_sgi_label *sgi;
+	struct sgi_disklabel *sgilabel;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+#ifdef HDIO_GETGEO
+	if (cxt->geom.heads && cxt->geom.sectors) {
+		fdisk_sector_t llsectors;
+
+		if (blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &llsectors) == 0) {
+			/* the get device size ioctl was successful */
+			fdisk_sector_t llcyls;
+			int sec_fac = cxt->sector_size / 512;
+
+			llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
+			cxt->geom.cylinders = llcyls;
+			if (cxt->geom.cylinders != llcyls)	/* truncated? */
+				cxt->geom.cylinders = ~0;
+		} else {
+			/* otherwise print error and use truncated version */
+			fdisk_warnx(cxt,
+				_("BLKGETSIZE ioctl failed on %s. "
+				  "Using geometry cylinder value of %llu. "
+				  "This value may be truncated for devices "
+				  "> 33.8 GB."), cxt->dev_path, cxt->geom.cylinders);
+		}
+	}
+#endif
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+
+	sgi = (struct fdisk_sgi_label *) cxt->label;
+	sgi->header = (struct sgi_disklabel *) cxt->firstsector;
+
+	sgilabel = sgi->header;
+
+	sgilabel->magic = cpu_to_be32(SGI_LABEL_MAGIC);
+	sgilabel->root_part_num = cpu_to_be16(0);
+	sgilabel->swap_part_num = cpu_to_be16(1);
+
+	/* sizeof(sgilabel->boot_file) = 16 > 6 */
+	memset(sgilabel->boot_file, 0, 16);
+	strcpy((char *) sgilabel->boot_file, "/unix");
+
+	sgilabel->devparam.skew			= (0);
+	sgilabel->devparam.gap1			= (0);
+	sgilabel->devparam.gap2			= (0);
+	sgilabel->devparam.sparecyl			= (0);
+	sgilabel->devparam.pcylcount	= cpu_to_be16(cxt->geom.cylinders);
+	sgilabel->devparam.head_vol0	= cpu_to_be16(0);
+	sgilabel->devparam.ntrks	= cpu_to_be16(cxt->geom.heads);
+	/* tracks/cylinder (heads) */
+	sgilabel->devparam.cmd_tag_queue_depth	= (0);
+	sgilabel->devparam.unused0			= (0);
+	sgilabel->devparam.unused1	= cpu_to_be16(0);
+	sgilabel->devparam.nsect	= cpu_to_be16(cxt->geom.sectors);
+	/* sectors/track */
+	sgilabel->devparam.bytes	= cpu_to_be16(cxt->sector_size);
+	sgilabel->devparam.ilfact	= cpu_to_be16(1);
+	sgilabel->devparam.flags	= cpu_to_be32(
+			SGI_DEVPARAM_TRACK_FWD
+			| SGI_DEVPARAM_IGNORE_ERRORS
+			| SGI_DEVPARAM_RESEEK);
+	sgilabel->devparam.datarate	= cpu_to_be32(0);
+	sgilabel->devparam.retries_on_error	= cpu_to_be32(1);
+	sgilabel->devparam.ms_per_word		= cpu_to_be32(0);
+	sgilabel->devparam.xylogics_gap1	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_syncdelay	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_readdelay	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_gap2	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_readgate	= cpu_to_be16(0);
+	sgilabel->devparam.xylogics_writecont	= cpu_to_be16(0);
+
+	memset(&(sgilabel->volume), 0,
+			sizeof(struct sgi_volume) * SGI_MAXVOLUMES);
+	memset(&(sgilabel->partitions), 0,
+			sizeof(struct sgi_partition) * SGI_MAXPARTITIONS);
+	cxt->label->nparts_max = SGI_MAXPARTITIONS;
+
+	/* don't create default layout when a script defined */
+	if (!cxt->script) {
+		sgi_set_entire(cxt);
+		sgi_set_volhdr(cxt);
+	}
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+
+	fdisk_info(cxt, _("Created a new SGI disklabel."));
+	return 0;
+}
+
+static int sgi_set_partition(struct fdisk_context *cxt,
+		size_t i,
+		struct fdisk_partition *pa)
+{
+	struct sgi_disklabel *sgilabel;
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	sgilabel = self_disklabel(cxt);
+
+	if (pa->type) {
+		struct fdisk_parttype *t = pa->type;
+
+		if (t->code > UINT32_MAX)
+			return -EINVAL;
+
+		if (sgi_get_num_sectors(cxt, i) == 0)	/* caught already before, ... */ {
+			fdisk_warnx(cxt, _("Sorry, only for non-empty partitions you can change the tag."));
+			return -EINVAL;
+		}
+
+		if ((i == 10 && t->code != SGI_TYPE_ENTIRE_DISK)
+		    || (i == 8 && t->code != 0))
+			fdisk_info(cxt, _("Consider leaving partition 9 as volume header (0), "
+					  "and partition 11 as entire volume (6), "
+					  "as IRIX expects it."));
+
+		if (cxt->script == NULL
+		    && ((t->code != SGI_TYPE_ENTIRE_DISK) && (t->code != SGI_TYPE_VOLHDR))
+		    && (sgi_get_start_sector(cxt, i) < 1)) {
+			int yes = 0;
+			fdisk_ask_yesno(cxt,
+				_("It is highly recommended that the partition at offset 0 "
+				  "is of type \"SGI volhdr\", the IRIX system will rely on it to "
+				  "retrieve from its directory standalone tools like sash and fx. "
+				  "Only the \"SGI volume\" entire disk section may violate this. "
+				  "Are you sure about tagging this partition differently?"), &yes);
+			if (!yes)
+				return 1;
+		}
+
+		sgilabel->partitions[i].type = cpu_to_be32(t->code);
+	}
+
+	if (fdisk_partition_has_start(pa))
+		sgilabel->partitions[i].first_block = cpu_to_be32(pa->start);
+	if (fdisk_partition_has_size(pa))
+		sgilabel->partitions[i].num_blocks = cpu_to_be32(pa->size);
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+
+static int sgi_partition_is_used(
+		struct fdisk_context *cxt,
+		size_t i)
+{
+	assert(cxt);
+	assert(fdisk_is_label(cxt, SGI));
+
+	if (i >= cxt->label->nparts_max)
+		return 0;
+	return sgi_get_num_sectors(cxt, i) ? 1 : 0;
+}
+
+static int sgi_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag)
+{
+	struct sgi_disklabel *sgilabel;
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SGI));
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	sgilabel = self_disklabel(cxt);
+
+	switch (flag) {
+	case SGI_FLAG_BOOT:
+		sgilabel->root_part_num =
+			be16_to_cpu(sgilabel->root_part_num) == i ?
+			0 : cpu_to_be16(i);
+		fdisk_label_set_changed(cxt->label, 1);
+		break;
+	case SGI_FLAG_SWAP:
+		sgilabel->swap_part_num =
+			be16_to_cpu(sgilabel->swap_part_num) == i ?
+			0 : cpu_to_be16(i);
+		fdisk_label_set_changed(cxt->label, 1);
+		break;
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+static const struct fdisk_field sgi_fields[] =
+{
+	{ FDISK_FIELD_DEVICE,	N_("Device"),	 10,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+	{ FDISK_FIELD_TYPEID,	N_("Id"),	  2,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	0.1,	FDISK_FIELDFL_EYECANDY },
+	{ FDISK_FIELD_ATTR,	N_("Attrs"),	  0,	FDISK_FIELDFL_NUMBER }
+};
+
+static const struct fdisk_label_operations sgi_operations =
+{
+	.probe		= sgi_probe_label,
+	.write		= sgi_write_disklabel,
+	.verify		= sgi_verify_disklabel,
+	.create		= sgi_create_disklabel,
+	.list		= sgi_list_table,
+
+	.get_part	= sgi_get_partition,
+	.set_part	= sgi_set_partition,
+	.add_part	= sgi_add_partition,
+	.del_part	= sgi_delete_partition,
+
+	.part_is_used	= sgi_partition_is_used,
+	.part_toggle_flag = sgi_toggle_partition_flag
+};
+
+/* Allocates an SGI label driver. */
+struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_sgi_label *sgi;
+
+	assert(cxt);
+
+	sgi = calloc(1, sizeof(*sgi));
+	if (!sgi)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) sgi;
+	lb->name = "sgi";
+	lb->id = FDISK_DISKLABEL_SGI;
+	lb->op = &sgi_operations;
+	lb->parttypes = sgi_parttypes;
+	lb->nparttypes = ARRAY_SIZE(sgi_parttypes) - 1;
+	lb->fields = sgi_fields;
+	lb->nfields = ARRAY_SIZE(sgi_fields);
+
+	lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+	return lb;
+}
diff --git a/libblkid/libfdisk/src/sun.c b/libblkid/libfdisk/src/sun.c
new file mode 100644
index 0000000..babff62
--- /dev/null
+++ b/libblkid/libfdisk/src/sun.c
@@ -0,0 +1,1130 @@
+/*
+ * Copyright (C) 2013 Karel Zak <kzak@redhat.com>
+ *
+ * Based on original code from fdisk:
+ *   Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
+ *   Merged with fdisk for other architectures, aeb, June 1998.
+ *   Arnaldo Carvalho de Melo <acme@conectiva.com.br> Mar 1999, Internationalization
+ */
+#include <stdio.h>		/* stderr */
+#include <stdlib.h>		/* qsort */
+#include <string.h>		/* strstr */
+#include <unistd.h>		/* write */
+#include <sys/ioctl.h>		/* ioctl */
+
+#include "nls.h"
+#include "blkdev.h"
+#include "bitops.h"
+
+#include "fdiskP.h"
+#include "pt-sun.h"
+#include "all-io.h"
+
+
+/**
+ * SECTION: sun
+ * @title: SUN
+ * @short_description: disk label specific functions
+ *
+ */
+
+/*
+ * in-memory fdisk SUN stuff
+ */
+struct fdisk_sun_label {
+	struct fdisk_label	head;		/* generic part */
+	struct sun_disklabel   *header;		/* on-disk data (pointer to cxt->firstsector) */
+};
+
+static struct fdisk_parttype sun_parttypes[] = {
+	{SUN_TAG_UNASSIGNED, N_("Unassigned")},
+	{SUN_TAG_BOOT, N_("Boot")},
+	{SUN_TAG_ROOT, N_("SunOS root")},
+	{SUN_TAG_SWAP, N_("SunOS swap")},
+	{SUN_TAG_USR, N_("SunOS usr")},
+	{SUN_TAG_WHOLEDISK, N_("Whole disk")},
+	{SUN_TAG_STAND, N_("SunOS stand")},
+	{SUN_TAG_VAR, N_("SunOS var")},
+	{SUN_TAG_HOME, N_("SunOS home")},
+	{SUN_TAG_ALTSCTR, N_("SunOS alt sectors")},
+	{SUN_TAG_CACHE, N_("SunOS cachefs")},
+	{SUN_TAG_RESERVED, N_("SunOS reserved")},
+	{SUN_TAG_LINUX_SWAP, N_("Linux swap")},
+	{SUN_TAG_LINUX_NATIVE, N_("Linux native")},
+	{SUN_TAG_LINUX_LVM, N_("Linux LVM")},
+	{SUN_TAG_LINUX_RAID, N_("Linux raid autodetect")},
+	{ 0, NULL }
+};
+
+/* return poiter buffer with on-disk data */
+static inline struct sun_disklabel *self_disklabel(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	return ((struct fdisk_sun_label *) cxt->label)->header;
+}
+
+/* return in-memory sun fdisk data */
+static inline struct fdisk_sun_label *self_label(struct fdisk_context *cxt)
+{
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	return (struct fdisk_sun_label *) cxt->label;
+}
+
+static void set_partition(struct fdisk_context *cxt, size_t i,
+		uint32_t start,uint32_t stop, uint16_t sysid)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	struct fdisk_parttype *t =
+			fdisk_label_get_parttype_from_code(cxt->label, sysid);
+
+	sunlabel->vtoc.infos[i].id = cpu_to_be16(sysid);
+	sunlabel->vtoc.infos[i].flags = cpu_to_be16(0);
+	sunlabel->partitions[i].start_cylinder =
+		cpu_to_be32(start / (cxt->geom.heads * cxt->geom.sectors));
+	sunlabel->partitions[i].num_sectors = cpu_to_be32(stop - start);
+	fdisk_label_set_changed(cxt->label, 1);
+
+	fdisk_info_new_partition(cxt, i + 1, start, stop, t);
+}
+
+static size_t count_used_partitions(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	size_t ct = 0, i;
+
+	assert(sunlabel);
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		if (sunlabel->partitions[i].num_sectors)
+			ct++;
+	}
+	return ct;
+}
+
+static int sun_probe_label(struct fdisk_context *cxt)
+{
+	struct fdisk_sun_label *sun;
+	struct sun_disklabel *sunlabel;
+	unsigned short *ush;
+	int csum;
+	int need_fixing = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	/* map first sector to header */
+	sun = (struct fdisk_sun_label *) cxt->label;
+	sun->header = (struct sun_disklabel *) cxt->firstsector;
+	sunlabel = sun->header;
+
+	if (be16_to_cpu(sunlabel->magic) != SUN_LABEL_MAGIC) {
+		sun->header = NULL;
+		return 0;		/* failed */
+	}
+
+	ush = ((unsigned short *) (sunlabel + 1)) - 1;
+	for (csum = 0; ush >= (unsigned short *)sunlabel;)
+		csum ^= *ush--;
+
+	if (csum) {
+		fdisk_warnx(cxt, _("Detected sun disklabel with wrong checksum. "
+			      "Probably you'll have to set all the values, "
+			      "e.g. heads, sectors, cylinders and partitions "
+			      "or force a fresh label (s command in main menu)"));
+		return 1;
+	}
+
+	cxt->label->nparts_max = SUN_MAXPARTITIONS;
+	cxt->geom.heads = be16_to_cpu(sunlabel->nhead);
+	cxt->geom.cylinders = be16_to_cpu(sunlabel->ncyl);
+	cxt->geom.sectors = be16_to_cpu(sunlabel->nsect);
+
+	if (be32_to_cpu(sunlabel->vtoc.version) != SUN_VTOC_VERSION) {
+		fdisk_warnx(cxt, _("Detected sun disklabel with wrong version [%d]."),
+			be32_to_cpu(sunlabel->vtoc.version));
+		need_fixing = 1;
+	}
+	if (be32_to_cpu(sunlabel->vtoc.sanity) != SUN_VTOC_SANITY) {
+		fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.sanity [0x%08x]."),
+			be32_to_cpu(sunlabel->vtoc.sanity));
+		need_fixing = 1;
+	}
+	if (be16_to_cpu(sunlabel->vtoc.nparts) != SUN_MAXPARTITIONS) {
+		fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.nparts [%u]."),
+			be16_to_cpu(sunlabel->vtoc.nparts));
+		need_fixing = 1;
+	}
+	if (need_fixing) {
+		fdisk_warnx(cxt, _("Warning: Wrong values need to be fixed up and "
+			           "will be corrected by w(rite)"));
+
+		sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION);
+		sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY);
+		sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS);
+
+		ush = (unsigned short *)sunlabel;
+		csum = 0;
+		while(ush < (unsigned short *)(&sunlabel->csum))
+			csum ^= *ush++;
+		sunlabel->csum = csum;
+
+		fdisk_label_set_changed(cxt->label, 1);
+	}
+
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+
+	return 1;
+}
+
+static void ask_geom(struct fdisk_context *cxt)
+{
+	uintmax_t res;
+
+	assert(cxt);
+
+	if (fdisk_ask_number(cxt, 1, 1, 1024, _("Heads"), &res) == 0)
+		cxt->geom.heads = res;
+	if (fdisk_ask_number(cxt, 1, 1, 1024, _("Sectors/track"), &res) == 0)
+		cxt->geom.sectors = res;
+	if (fdisk_ask_number(cxt, 1, 1, USHRT_MAX, _("Cylinders"), &res) == 0)
+		cxt->geom.cylinders = res;
+}
+
+static int sun_create_disklabel(struct fdisk_context *cxt)
+{
+	unsigned int ndiv;
+	struct fdisk_sun_label *sun;		/* libfdisk sun handler */
+	struct sun_disklabel *sunlabel;	/* on disk data */
+	int rc = 0;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	/* map first sector to header */
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+
+	sun = (struct fdisk_sun_label *) cxt->label;
+	sun->header = (struct sun_disklabel *) cxt->firstsector;
+
+	sunlabel = sun->header;
+
+	cxt->label->nparts_max = SUN_MAXPARTITIONS;
+
+	sunlabel->magic = cpu_to_be16(SUN_LABEL_MAGIC);
+	sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION);
+	sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY);
+	sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS);
+
+#ifdef HDIO_GETGEO
+	if (cxt->geom.heads && cxt->geom.sectors) {
+		fdisk_sector_t llsectors;
+
+		if (blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &llsectors) == 0) {
+			int sec_fac = cxt->sector_size / 512;
+			fdisk_sector_t llcyls;
+
+			llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
+			cxt->geom.cylinders = llcyls;
+			if (cxt->geom.cylinders != llcyls)
+				cxt->geom.cylinders = ~0;
+		} else {
+			fdisk_warnx(cxt,
+				_("BLKGETSIZE ioctl failed on %s. "
+				  "Using geometry cylinder value of %llu. "
+				  "This value may be truncated for devices "
+				  "> 33.8 GB."),
+				cxt->dev_path, cxt->geom.cylinders);
+		}
+	} else
+#endif
+		ask_geom(cxt);
+
+	sunlabel->acyl   = cpu_to_be16(0);
+	sunlabel->pcyl   = cpu_to_be16(cxt->geom.cylinders);
+	sunlabel->rpm    = cpu_to_be16(5400);
+	sunlabel->intrlv = cpu_to_be16(1);
+	sunlabel->apc    = cpu_to_be16(0);
+
+	sunlabel->nhead  = cpu_to_be16(cxt->geom.heads);
+	sunlabel->nsect  = cpu_to_be16(cxt->geom.sectors);
+	sunlabel->ncyl   = cpu_to_be16(cxt->geom.cylinders);
+
+	snprintf((char *) sunlabel->label_id, sizeof(sunlabel->label_id),
+		 "Linux cyl %ju alt %u hd %u sec %ju",
+		 (uintmax_t) cxt->geom.cylinders,
+		 be16_to_cpu(sunlabel->acyl),
+		 cxt->geom.heads,
+		 (uintmax_t) cxt->geom.sectors);
+
+	if (cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors >= 150 * 2048) {
+	        ndiv = cxt->geom.cylinders - (50 * 2048 / (cxt->geom.heads * cxt->geom.sectors)); /* 50M swap */
+	} else
+	        ndiv = cxt->geom.cylinders * 2 / 3;
+
+	/* create the default layout only if no-script defined */
+	if (!cxt->script) {
+		set_partition(cxt, 0, 0, ndiv * cxt->geom.heads * cxt->geom.sectors,
+			  SUN_TAG_LINUX_NATIVE);
+		set_partition(cxt, 1, ndiv * cxt->geom.heads * cxt->geom.sectors,
+			  cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors,
+			  SUN_TAG_LINUX_SWAP);
+		sunlabel->vtoc.infos[1].flags |= cpu_to_be16(SUN_FLAG_UNMNT);
+
+		set_partition(cxt, 2, 0,
+			  cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors,
+			  SUN_TAG_WHOLEDISK);
+	}
+
+	{
+		unsigned short *ush = (unsigned short *)sunlabel;
+		unsigned short csum = 0;
+		while(ush < (unsigned short *)(&sunlabel->csum))
+			csum ^= *ush++;
+		sunlabel->csum = csum;
+	}
+
+	fdisk_label_set_changed(cxt->label, 1);
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+
+	fdisk_info(cxt, _("Created a new Sun disklabel."));
+	return 0;
+}
+
+static int sun_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag)
+{
+	struct sun_disklabel *sunlabel;
+	struct sun_info *p;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	sunlabel = self_disklabel(cxt);
+	p = &sunlabel->vtoc.infos[i];
+
+	switch (flag) {
+	case SUN_FLAG_UNMNT:
+		p->flags ^= cpu_to_be16(SUN_FLAG_UNMNT);
+		fdisk_label_set_changed(cxt->label, 1);
+		break;
+	case SUN_FLAG_RONLY:
+		p->flags ^= cpu_to_be16(SUN_FLAG_RONLY);
+		fdisk_label_set_changed(cxt->label, 1);
+		break;
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+static void fetch_sun(struct fdisk_context *cxt,
+		      uint32_t *starts,
+		      uint32_t *lens,
+		      uint32_t *start,
+		      uint32_t *stop)
+{
+	struct sun_disklabel *sunlabel;
+	int continuous = 1;
+	size_t i;
+
+	assert(cxt);
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+
+	*start = 0;
+	*stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		struct sun_partition *part = &sunlabel->partitions[i];
+		struct sun_info *info = &sunlabel->vtoc.infos[i];
+
+		if (part->num_sectors &&
+		    be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED &&
+		    be16_to_cpu(info->id) != SUN_TAG_WHOLEDISK) {
+			starts[i] = be32_to_cpu(part->start_cylinder) *
+				     cxt->geom.heads * cxt->geom.sectors;
+			lens[i] = be32_to_cpu(part->num_sectors);
+			if (continuous) {
+				if (starts[i] == *start)
+					*start += lens[i];
+				else if (starts[i] + lens[i] >= *stop)
+					*stop = starts[i];
+				else
+					continuous = 0;
+				        /* There will be probably more gaps
+					  than one, so lets check afterwards */
+			}
+		} else {
+			starts[i] = 0;
+			lens[i] = 0;
+		}
+	}
+}
+
+#ifdef HAVE_QSORT_R
+static int verify_sun_cmp(int *a, int *b, void *data)
+{
+    unsigned int *verify_sun_starts = (unsigned int *) data;
+
+    if (*a == -1)
+	    return 1;
+    if (*b == -1)
+	    return -1;
+    if (verify_sun_starts[*a] > verify_sun_starts[*b])
+	    return 1;
+    return -1;
+}
+#endif
+
+static int sun_verify_disklabel(struct fdisk_context *cxt)
+{
+    uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS], start, stop;
+    uint32_t i,j,k,starto,endo;
+#ifdef HAVE_QSORT_R
+    int array[SUN_MAXPARTITIONS];
+    unsigned int *verify_sun_starts;
+#endif
+    assert(cxt);
+    assert(cxt->label);
+    assert(fdisk_is_label(cxt, SUN));
+
+    fetch_sun(cxt, starts, lens, &start, &stop);
+
+    for (k = 0; k < 7; k++) {
+	for (i = 0; i < SUN_MAXPARTITIONS; i++) {
+	    if (k && (lens[i] % (cxt->geom.heads * cxt->geom.sectors)))
+	        fdisk_warnx(cxt, _("Partition %u doesn't end on cylinder boundary."), i+1);
+	    if (lens[i]) {
+	        for (j = 0; j < i; j++)
+	            if (lens[j]) {
+	                if (starts[j] == starts[i]+lens[i]) {
+	                    starts[j] = starts[i]; lens[j] += lens[i];
+	                    lens[i] = 0;
+	                } else if (starts[i] == starts[j]+lens[j]){
+	                    lens[j] += lens[i];
+	                    lens[i] = 0;
+	                } else if (!k) {
+	                    if (starts[i] < starts[j]+lens[j] &&
+				starts[j] < starts[i]+lens[i]) {
+	                        starto = starts[i];
+	                        if (starts[j] > starto)
+					starto = starts[j];
+	                        endo = starts[i]+lens[i];
+	                        if (starts[j]+lens[j] < endo)
+					endo = starts[j]+lens[j];
+	                        fdisk_warnx(cxt, _("Partition %u overlaps with others in "
+				       "sectors %u-%u."), i+1, starto, endo);
+	                    }
+	                }
+	            }
+	    }
+	}
+    }
+
+#ifdef HAVE_QSORT_R
+    for (i = 0; i < SUN_MAXPARTITIONS; i++) {
+        if (lens[i])
+            array[i] = i;
+        else
+            array[i] = -1;
+    }
+    verify_sun_starts = starts;
+
+    qsort_r(array,ARRAY_SIZE(array),sizeof(array[0]),
+	  (int (*)(const void *,const void *,void *)) verify_sun_cmp,
+	  verify_sun_starts);
+
+    if (array[0] == -1) {
+	fdisk_info(cxt, _("No partitions defined."));
+	return 0;
+    }
+    stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
+    if (starts[array[0]])
+        fdisk_warnx(cxt, _("Unused gap - sectors 0-%u."), starts[array[0]]);
+    for (i = 0; i < 7 && array[i+1] != -1; i++) {
+        fdisk_warnx(cxt, _("Unused gap - sectors %u-%u."),
+	       (starts[array[i]] + lens[array[i]]),
+	       starts[array[i+1]]);
+    }
+    start = (starts[array[i]] + lens[array[i]]);
+    if (start < stop)
+        fdisk_warnx(cxt, _("Unused gap - sectors %u-%u."), start, stop);
+#endif
+    return 0;
+}
+
+
+static int is_free_sector(struct fdisk_context *cxt,
+		fdisk_sector_t s, uint32_t starts[], uint32_t lens[])
+{
+	size_t i;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		if (lens[i] && starts[i] <= s
+		    && starts[i] + lens[i] > s)
+			return 0;
+	}
+	return 1;
+}
+
+static int sun_add_partition(
+		struct fdisk_context *cxt,
+		struct fdisk_partition *pa,
+		size_t *partno)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS];
+	struct sun_partition *part;
+	struct sun_info *info;
+	uint32_t start, stop, stop2;
+	int whole_disk = 0;
+	int sys = pa && pa->type ? pa->type->code : SUN_TAG_LINUX_NATIVE;
+	int rc;
+	size_t n;
+
+	char mesg[256];
+	size_t i;
+	unsigned int first, last;
+
+	rc = fdisk_partition_next_partno(pa, cxt, &n);
+	if (rc)
+		return rc;
+
+	part = &sunlabel->partitions[n];
+	info = &sunlabel->vtoc.infos[n];
+
+	if (part->num_sectors && be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED) {
+		fdisk_info(cxt, _("Partition %zu is already defined.  Delete "
+			"it before re-adding it."), n + 1);
+		return -EINVAL;
+	}
+
+	fetch_sun(cxt, starts, lens, &start, &stop);
+
+	if (stop <= start) {
+		if (n == 2)
+			whole_disk = 1;
+		else {
+			fdisk_info(cxt, _("Other partitions already cover the "
+				"whole disk. Delete some/shrink them before retry."));
+			return -EINVAL;
+		}
+	}
+
+	if (pa && pa->start_follow_default)
+		first = start;
+	else if (pa && fdisk_partition_has_start(pa)) {
+		first = pa->start;
+
+		if (!whole_disk && !is_free_sector(cxt, first, starts, lens))
+			return -ERANGE;
+	} else {
+		struct fdisk_ask *ask;
+
+		snprintf(mesg, sizeof(mesg), _("First %s"),
+				fdisk_get_unit(cxt, FDISK_SINGULAR));
+		for (;;) {
+			ask = fdisk_new_ask();
+			if (!ask)
+				return -ENOMEM;
+
+			fdisk_ask_set_query(ask, mesg);
+			fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+
+			if (whole_disk) {
+				fdisk_ask_number_set_low(ask,     0);	/* minimal */
+				fdisk_ask_number_set_default(ask, 0);	/* default */
+				fdisk_ask_number_set_high(ask,    0);	/* maximal */
+			} else {
+				fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, start));	/* minimal */
+				fdisk_ask_number_set_default(ask, fdisk_scround(cxt, start));	/* default */
+				fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop));	/* maximal */
+			}
+			rc = fdisk_do_ask(cxt, ask);
+			first = fdisk_ask_number_get_result(ask);
+			fdisk_unref_ask(ask);
+			if (rc)
+				return rc;
+
+			if (fdisk_use_cylinders(cxt))
+				first *= fdisk_get_units_per_sector(cxt);
+
+			/* ewt asks to add: "don't start a partition at cyl 0"
+			   However, edmundo@rano.demon.co.uk writes:
+			   "In addition to having a Sun partition table, to be able to
+			   boot from the disc, the first partition, /dev/sdX1, must
+			   start at cylinder 0. This means that /dev/sdX1 contains
+			   the partition table and the boot block, as these are the
+			   first two sectors of the disc. Therefore you must be
+			   careful what you use /dev/sdX1 for. In particular, you must
+			   not use a partition starting at cylinder 0 for Linux swap,
+			   as that would overwrite the partition table and the boot
+			   block. You may, however, use such a partition for a UFS
+			   or EXT2 file system, as these file systems leave the first
+			   1024 bytes undisturbed. */
+			/* On the other hand, one should not use partitions
+			   starting at block 0 in an md, or the label will
+			   be trashed. */
+			if (!is_free_sector(cxt, first, starts,  lens) && !whole_disk) {
+				if (n == 2 && !first) {
+				    whole_disk = 1;
+				    break;
+				}
+				fdisk_warnx(cxt, _("Sector %d is already allocated"), first);
+			} else
+				break;
+		}
+	}
+
+	if (n == 2 && first != 0)
+		fdisk_warnx(cxt, _("It is highly recommended that the "
+				   "third partition covers the whole disk "
+				   "and is of type `Whole disk'"));
+
+	if (!fdisk_use_cylinders(cxt)) {
+		/* Starting sector has to be properly aligned */
+		int cs = cxt->geom.heads * cxt->geom.sectors;
+		int x = first % cs;
+
+		if (x) {
+			fdisk_info(cxt, _("Aligning the first sector from %u to %u "
+					  "to be on cylinder boundary."),
+					first, first + cs - x);
+			first += cs - x;
+		}
+	}
+
+	stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;	/* ancient */
+	stop2 = stop;
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		if (starts[i] > first && starts[i] < stop)
+			stop = starts[i];
+	}
+
+	/* last */
+	if (pa && pa->end_follow_default)
+		last = whole_disk || (n == 2 && !first) ? stop2 : stop;
+	else if (pa && fdisk_partition_has_size(pa)) {
+		last = first + pa->size - 1ULL;
+
+		if (!whole_disk && last > stop)
+			return -ERANGE;
+	} else {
+		struct fdisk_ask *ask = fdisk_new_ask();
+
+		if (!ask)
+			return -ENOMEM;
+
+		snprintf(mesg, sizeof(mesg),
+			 _("Last %s or +%s or +size{K,M,G,T,P}"),
+			 fdisk_get_unit(cxt, FDISK_SINGULAR),
+			 fdisk_get_unit(cxt, FDISK_PLURAL));
+		fdisk_ask_set_query(ask, mesg);
+		fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+		if (whole_disk) {
+			fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, stop2));	/* minimal */
+			fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2));	/* default */
+			fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop2));	/* maximal */
+			fdisk_ask_number_set_base(ask,    0);
+		} else if (n == 2 && !first) {
+			fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
+			fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2));	/* default */
+			fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop2));	/* maximal */
+			fdisk_ask_number_set_base(ask,	  fdisk_scround(cxt, first));
+		} else {
+			fdisk_ask_number_set_low(ask,     fdisk_scround(cxt, first));	/* minimal */
+			fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop));	/* default */
+			fdisk_ask_number_set_high(ask,    fdisk_scround(cxt, stop));	/* maximal */
+			fdisk_ask_number_set_base(ask,    fdisk_scround(cxt, first));
+		}
+
+		if (fdisk_use_cylinders(cxt))
+			fdisk_ask_number_set_unit(ask,
+				     cxt->sector_size *
+				     fdisk_get_units_per_sector(cxt));
+		else
+			fdisk_ask_number_set_unit(ask,	cxt->sector_size);
+
+		rc = fdisk_do_ask(cxt, ask);
+		last = fdisk_ask_number_get_result(ask);
+
+		fdisk_unref_ask(ask);
+		if (rc)
+			return rc;
+		if (fdisk_use_cylinders(cxt))
+			last *= fdisk_get_units_per_sector(cxt);
+	}
+
+	if (n == 2 && !first) {
+		if (last >= stop2) {
+		    whole_disk = 1;
+		    last = stop2;
+		} else if (last > stop) {
+		    fdisk_warnx(cxt,
+   _("You haven't covered the whole disk with the 3rd partition, but your value\n"
+     "%lu %s covers some other partition. Your entry has been changed\n"
+     "to %lu %s"),
+			(unsigned long) fdisk_scround(cxt, last), fdisk_get_unit(cxt, FDISK_SINGULAR),
+			(unsigned long) fdisk_scround(cxt, stop), fdisk_get_unit(cxt, FDISK_SINGULAR));
+		    last = stop;
+		}
+	} else if (!whole_disk && last > stop)
+		last = stop;
+
+	if (whole_disk)
+		sys = SUN_TAG_WHOLEDISK;
+
+	set_partition(cxt, n, first, last, sys);
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+	if (partno)
+		*partno = n;
+	return 0;
+}
+
+static int sun_delete_partition(struct fdisk_context *cxt,
+		size_t partnum)
+{
+	struct sun_disklabel *sunlabel;
+	struct sun_partition *part;
+	struct sun_info *info;
+	unsigned int nsec;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+	part = &sunlabel->partitions[partnum];
+	info = &sunlabel->vtoc.infos[partnum];
+
+	if (partnum == 2 &&
+	    be16_to_cpu(info->id) == SUN_TAG_WHOLEDISK &&
+	    !part->start_cylinder &&
+	    (nsec = be32_to_cpu(part->num_sectors))
+	    == cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders)
+		fdisk_info(cxt, _("If you want to maintain SunOS/Solaris compatibility, "
+			 "consider leaving this "
+			 "partition as Whole disk (5), starting at 0, with %u "
+			 "sectors"), nsec);
+	info->id = cpu_to_be16(SUN_TAG_UNASSIGNED);
+	part->num_sectors = 0;
+	cxt->label->nparts_cur = count_used_partitions(cxt);
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+
+static int sun_list_disklabel(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+
+	if (fdisk_is_details(cxt)) {
+		fdisk_info(cxt,
+		_("Label geometry: %d rpm, %d alternate and %d physical cylinders,\n"
+		  "                %d extra sects/cyl, interleave %d:1"),
+		       be16_to_cpu(sunlabel->rpm),
+		       be16_to_cpu(sunlabel->acyl),
+		       be16_to_cpu(sunlabel->pcyl),
+		       be16_to_cpu(sunlabel->apc),
+		       be16_to_cpu(sunlabel->intrlv));
+		fdisk_info(cxt, _("Label ID: %s"), sunlabel->label_id);
+		fdisk_info(cxt, _("Volume ID: %s"),
+		       *sunlabel->vtoc.volume_id ? sunlabel->vtoc.volume_id : _("<none>"));
+	}
+
+	return 0;
+}
+
+static struct fdisk_parttype *sun_get_parttype(
+		struct fdisk_context *cxt,
+		size_t n)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	struct fdisk_parttype *t;
+
+	if (n >= cxt->label->nparts_max)
+		return NULL;
+
+	t = fdisk_label_get_parttype_from_code(cxt->label,
+			be16_to_cpu(sunlabel->vtoc.infos[n].id));
+	return t ? : fdisk_new_unknown_parttype(be16_to_cpu(sunlabel->vtoc.infos[n].id), NULL);
+}
+
+
+static int sun_get_partition(struct fdisk_context *cxt, size_t n,
+			      struct fdisk_partition *pa)
+{
+	struct sun_disklabel *sunlabel;
+	struct sun_partition *part;
+	uint16_t flags;
+	uint32_t start, len;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	if (n >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	sunlabel = self_disklabel(cxt);
+	part = &sunlabel->partitions[n];
+
+	pa->used = part->num_sectors ? 1 : 0;
+	if (!pa->used)
+		return 0;
+
+	flags = be16_to_cpu(sunlabel->vtoc.infos[n].flags);
+	start = be32_to_cpu(part->start_cylinder)
+			* cxt->geom.heads * cxt->geom.sectors;
+	len = be32_to_cpu(part->num_sectors);
+
+	pa->type = sun_get_parttype(cxt, n);
+	if (pa->type && pa->type->code == SUN_TAG_WHOLEDISK)
+		pa->wholedisk = 1;
+
+	if (flags & SUN_FLAG_UNMNT || flags & SUN_FLAG_RONLY) {
+		if (asprintf(&pa->attrs, "%c%c",
+				flags & SUN_FLAG_UNMNT ? 'u' : ' ',
+				flags & SUN_FLAG_RONLY ? 'r' : ' ') < 0)
+			return -ENOMEM;
+	}
+
+	pa->start = start;
+	pa->size = len;
+
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_alt_cyl:
+ * @cxt: context
+ *
+ * Sets number of alternative cylinders. This function uses libfdisk Ask API
+ * for dialog with user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 0,			/* low */
+			be16_to_cpu(sunlabel->acyl),		/* default */
+			65535,					/* high */
+			_("Number of alternate cylinders"),	/* query */
+			&res);					/* result */
+	if (rc)
+		return rc;
+
+	sunlabel->acyl = cpu_to_be16(res);
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_xcyl:
+ * @cxt: context
+ *
+ * Sets number of extra sectors per cylinder. This function uses libfdisk Ask API
+ * for dialog with user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_xcyl(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 0,			/* low */
+			be16_to_cpu(sunlabel->apc),		/* default */
+			cxt->geom.sectors,			/* high */
+			_("Extra sectors per cylinder"),	/* query */
+			&res);					/* result */
+	if (rc)
+		return rc;
+	sunlabel->apc = cpu_to_be16(res);
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_ilfact:
+ * @cxt: context
+ *
+ * Sets interleave factor. This function uses libfdisk Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_ilfact(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 1,			/* low */
+			be16_to_cpu(sunlabel->intrlv),		/* default */
+			32,					/* high */
+			_("Interleave factor"),	/* query */
+			&res);					/* result */
+	if (rc)
+		return rc;
+	sunlabel->intrlv = cpu_to_be16(res);
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_rspeed
+ * @cxt: context
+ *
+ * Sets rotation speed. This function uses libfdisk Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_rspeed(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 1,			/* low */
+			be16_to_cpu(sunlabel->rpm),		/* default */
+			USHRT_MAX,				/* high */
+			_("Rotation speed (rpm)"),		/* query */
+			&res);					/* result */
+	if (rc)
+		return rc;
+	sunlabel->rpm = cpu_to_be16(res);
+	return 0;
+}
+
+/**
+ * fdisk_sun_set_pcylcount
+ * @cxt: context
+ *
+ * Sets number of physical cylinders. This function uses libfdisk Ask API for
+ * dialog with user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_pcylcount(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel = self_disklabel(cxt);
+	uintmax_t res;
+	int rc = fdisk_ask_number(cxt, 0,			/* low */
+			be16_to_cpu(sunlabel->pcyl),		/* default */
+			USHRT_MAX,				/* high */
+			_("Number of physical cylinders"),	/* query */
+			&res);					/* result */
+	if (!rc)
+		return rc;
+	sunlabel->pcyl = cpu_to_be16(res);
+	return 0;
+}
+
+static int sun_write_disklabel(struct fdisk_context *cxt)
+{
+	struct sun_disklabel *sunlabel;
+	unsigned short *ush;
+	unsigned short csum = 0;
+	const size_t sz = sizeof(struct sun_disklabel);
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+
+	/* Maybe geometry has been modified */
+	sunlabel->nhead = cpu_to_be16(cxt->geom.heads);
+	sunlabel->nsect = cpu_to_be16(cxt->geom.sectors);
+
+	if (cxt->geom.cylinders != be16_to_cpu(sunlabel->ncyl))
+		sunlabel->ncyl = cpu_to_be16( cxt->geom.cylinders
+				      - be16_to_cpu(sunlabel->acyl) );
+
+	ush = (unsigned short *) sunlabel;
+
+	while(ush < (unsigned short *)(&sunlabel->csum))
+		csum ^= *ush++;
+	sunlabel->csum = csum;
+	if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+		return -errno;
+	if (write_all(cxt->dev_fd, sunlabel, sz) != 0)
+		return -errno;
+
+	return 0;
+}
+
+static int sun_set_partition(
+		struct fdisk_context *cxt,
+		size_t i,
+		struct fdisk_partition *pa)
+{
+	struct sun_disklabel *sunlabel;
+	struct sun_partition *part;
+	struct sun_info *info;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	sunlabel = self_disklabel(cxt);
+
+	if (i >= cxt->label->nparts_max)
+		return -EINVAL;
+
+	if (pa->type) {
+		struct fdisk_parttype *t = pa->type;
+
+		if (t->code > UINT16_MAX)
+			return -EINVAL;
+
+		if (i == 2 && t->code != SUN_TAG_WHOLEDISK)
+			fdisk_info(cxt, _("Consider leaving partition 3 as Whole disk (5),\n"
+			         "as SunOS/Solaris expects it and even Linux likes it.\n"));
+
+		part = &sunlabel->partitions[i];
+		info = &sunlabel->vtoc.infos[i];
+
+		if (cxt->script == NULL &&
+		    t->code == SUN_TAG_LINUX_SWAP && !part->start_cylinder) {
+			int yes, rc;
+
+			rc = fdisk_ask_yesno(cxt,
+			      _("It is highly recommended that the partition at offset 0\n"
+			      "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
+			      "there may destroy your partition table and bootblock.\n"
+			      "Are you sure you want to tag the partition as Linux swap?"), &yes);
+			if (rc)
+				return rc;
+			if (!yes)
+				return 1;
+		}
+
+		switch (t->code) {
+		case SUN_TAG_SWAP:
+		case SUN_TAG_LINUX_SWAP:
+			/* swaps are not mountable by default */
+			info->flags |= cpu_to_be16(SUN_FLAG_UNMNT);
+			break;
+		default:
+			/* assume other types are mountable;
+			   user can change it anyway */
+			info->flags &= ~cpu_to_be16(SUN_FLAG_UNMNT);
+			break;
+		}
+		info->id = cpu_to_be16(t->code);
+	}
+
+	if (fdisk_partition_has_start(pa))
+		sunlabel->partitions[i].start_cylinder =
+			cpu_to_be32(pa->start / (cxt->geom.heads * cxt->geom.sectors));
+	if (fdisk_partition_has_size(pa))
+		sunlabel->partitions[i].num_sectors = cpu_to_be32(pa->size);
+
+	fdisk_label_set_changed(cxt->label, 1);
+	return 0;
+}
+
+
+static int sun_reset_alignment(struct fdisk_context *cxt __attribute__((__unused__)))
+{
+	return 0;
+}
+
+
+static int sun_partition_is_used(
+		struct fdisk_context *cxt,
+		size_t i)
+{
+	struct sun_disklabel *sunlabel;
+
+	assert(cxt);
+	assert(cxt->label);
+	assert(fdisk_is_label(cxt, SUN));
+
+	if (i >= cxt->label->nparts_max)
+		return 0;
+
+	sunlabel = self_disklabel(cxt);
+	return sunlabel->partitions[i].num_sectors ? 1 : 0;
+}
+
+static const struct fdisk_field sun_fields[] =
+{
+	{ FDISK_FIELD_DEVICE,	N_("Device"),	 10,	0 },
+	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SECTORS,	N_("Sectors"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPEID,	N_("Id"),	  2,	FDISK_FIELDFL_NUMBER },
+	{ FDISK_FIELD_TYPE,	N_("Type"),	0.1,	0 },
+	{ FDISK_FIELD_ATTR,	N_("Flags"),	  0,	FDISK_FIELDFL_NUMBER }
+};
+
+const struct fdisk_label_operations sun_operations =
+{
+	.probe		= sun_probe_label,
+	.write		= sun_write_disklabel,
+	.verify		= sun_verify_disklabel,
+	.create		= sun_create_disklabel,
+	.list		= sun_list_disklabel,
+
+	.get_part	= sun_get_partition,
+	.set_part	= sun_set_partition,
+	.add_part	= sun_add_partition,
+	.del_part	= sun_delete_partition,
+
+	.part_is_used	= sun_partition_is_used,
+	.part_toggle_flag = sun_toggle_partition_flag,
+
+	.reset_alignment = sun_reset_alignment,
+};
+
+/*
+ * allocates SUN label driver
+ */
+struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt)
+{
+	struct fdisk_label *lb;
+	struct fdisk_sun_label *sun;
+
+	assert(cxt);
+
+	sun = calloc(1, sizeof(*sun));
+	if (!sun)
+		return NULL;
+
+	/* initialize generic part of the driver */
+	lb = (struct fdisk_label *) sun;
+	lb->name = "sun";
+	lb->id = FDISK_DISKLABEL_SUN;
+	lb->op = &sun_operations;
+	lb->parttypes = sun_parttypes;
+	lb->nparttypes = ARRAY_SIZE(sun_parttypes) - 1;
+	lb->fields = sun_fields;
+	lb->nfields = ARRAY_SIZE(sun_fields);
+	lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+	return lb;
+}
diff --git a/libblkid/libfdisk/src/table.c b/libblkid/libfdisk/src/table.c
new file mode 100644
index 0000000..1add09f
--- /dev/null
+++ b/libblkid/libfdisk/src/table.c
@@ -0,0 +1,664 @@
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: table
+ * @title: Table
+ * @short_description: container for fdisk partitions
+ *
+ * The fdisk_table is simple container for fdisk_partitions. The table is no
+ * directly connected to label data (partition table), and table changes don't
+ * affect in-memory or on-disk data.
+ */
+
+/**
+ * fdisk_new_table:
+ *
+ * The table is a container for struct fdisk_partition entries. The container
+ * does not have any real connection with label (partition table) and with
+ * real on-disk data.
+ *
+ * Returns: newly allocated table struct.
+ */
+struct fdisk_table *fdisk_new_table(void)
+{
+	struct fdisk_table *tb = NULL;
+
+	tb = calloc(1, sizeof(*tb));
+	if (!tb)
+		return NULL;
+
+	DBG(TAB, ul_debugobj(tb, "alloc"));
+	tb->refcount = 1;
+	INIT_LIST_HEAD(&tb->parts);
+	return tb;
+}
+
+/**
+ * fdisk_reset_table:
+ * @tb: tab pointer
+ *
+ * Removes all entries (partitions) from the table. The parititons with zero
+ * reference count will be deallocated. This function does not modify partition
+ * table.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int fdisk_reset_table(struct fdisk_table *tb)
+{
+	if (!tb)
+		return -EINVAL;
+
+	DBG(TAB, ul_debugobj(tb, "reset"));
+
+	while (!list_empty(&tb->parts)) {
+		struct fdisk_partition *pa = list_entry(tb->parts.next,
+				                  struct fdisk_partition, parts);
+		fdisk_table_remove_partition(tb, pa);
+	}
+
+	tb->nents = 0;
+	return 0;
+}
+
+/**
+ * fdisk_ref_table:
+ * @tb: table pointer
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_table(struct fdisk_table *tb)
+{
+	if (tb)
+		tb->refcount++;
+}
+
+/**
+ * fdisk_unref_table:
+ * @tb: table pointer
+ *
+ * De-incremparts reference counter, on zero the @tb is automatically
+ * deallocated.
+ */
+void fdisk_unref_table(struct fdisk_table *tb)
+{
+	if (!tb)
+		return;
+
+	tb->refcount--;
+	if (tb->refcount <= 0) {
+		fdisk_reset_table(tb);
+
+		DBG(TAB, ul_debugobj(tb, "free"));
+		free(tb);
+	}
+}
+
+/**
+ * fdisk_table_is_empty:
+ * @tb: pointer to tab
+ *
+ * Returns: 1 if the table is without filesystems, or 0.
+ */
+int fdisk_table_is_empty(struct fdisk_table *tb)
+{
+	return tb == NULL || list_empty(&tb->parts) ? 1 : 0;
+}
+
+/**
+ * fdisk_table_get_nents:
+ * @tb: pointer to tab
+ *
+ * Returns: number of entries in table.
+ */
+size_t fdisk_table_get_nents(struct fdisk_table *tb)
+{
+	return tb ? tb->nents : 0;
+}
+
+/**
+ * fdisk_table_next_partition:
+ * @tb: tab pointer
+ * @itr: iterator
+ * @pa: returns the next tab entry
+ *
+ * Returns: 0 on success, negative number in case of error or 1 at the end of list.
+ *
+ * Example:
+ * <informalexample>
+ *   <programlisting>
+ *	while(fdisk_table_next_partition(tb, itr, &pa) == 0) {
+ *		...
+ *	}
+ *   </programlisting>
+ * </informalexample>
+ */
+int fdisk_table_next_partition(
+			struct fdisk_table *tb,
+			struct fdisk_iter *itr,
+			struct fdisk_partition **pa)
+{
+	int rc = 1;
+
+	assert(tb);
+	assert(itr);
+	assert(pa);
+
+	if (!tb || !itr || !pa)
+		return -EINVAL;
+	*pa = NULL;
+
+	if (!itr->head)
+		FDISK_ITER_INIT(itr, &tb->parts);
+	if (itr->p != itr->head) {
+		FDISK_ITER_ITERATE(itr, *pa, struct fdisk_partition, parts);
+		rc = 0;
+	}
+
+	return rc;
+}
+
+struct fdisk_partition *fdisk_table_get_partition(
+			struct fdisk_table *tb,
+			size_t n)
+{
+	struct fdisk_partition *pa = NULL;
+	struct fdisk_iter itr;
+
+	if (!tb)
+		return NULL;
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+	while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+		if (n == 0)
+			return pa;
+		n--;
+	}
+
+	return NULL;
+}
+
+/**
+ * fdisk_table_add_partition
+ * @tb: tab pointer
+ * @pa: new entry
+ *
+ * Adds a new entry to table and increment @pa reference counter. Don't forget to
+ * use fdisk_unref_pa() after fdisk_table_add_partition() if you want to keep
+ * the @pa referenced by the table only.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
+{
+	assert(tb);
+	assert(pa);
+
+	if (!tb || !pa)
+		return -EINVAL;
+
+	fdisk_ref_partition(pa);
+	list_add_tail(&pa->parts, &tb->parts);
+	tb->nents++;
+
+	DBG(TAB, ul_debugobj(tb, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]",
+			pa,
+			(uintmax_t) fdisk_partition_get_start(pa),
+			(uintmax_t) fdisk_partition_get_end(pa),
+			(uintmax_t) fdisk_partition_get_size(pa),
+			fdisk_partition_is_freespace(pa) ? "freespace" : "",
+			fdisk_partition_is_nested(pa)    ? "nested"    : "",
+			fdisk_partition_is_container(pa) ? "container" : "primary"));
+	return 0;
+}
+
+/* inserts @pa after @poz */
+static int table_insert_partition(
+			struct fdisk_table *tb,
+			struct fdisk_partition *poz,
+			struct fdisk_partition *pa)
+{
+	assert(tb);
+	assert(pa);
+
+	fdisk_ref_partition(pa);
+	if (poz)
+		list_add(&pa->parts, &poz->parts);
+	else
+		list_add(&pa->parts, &tb->parts);
+	tb->nents++;
+
+	DBG(TAB, ul_debugobj(tb, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]",
+			pa, poz ? poz : NULL,
+			(uintmax_t) fdisk_partition_get_start(pa),
+			(uintmax_t) fdisk_partition_get_end(pa),
+			(uintmax_t) fdisk_partition_get_size(pa),
+			fdisk_partition_is_freespace(pa) ? "freespace" : "",
+			fdisk_partition_is_nested(pa)    ? "nested"    : "",
+			fdisk_partition_is_container(pa) ? "container" : ""));
+	return 0;
+}
+
+/**
+ * fdisk_table_remove_partition
+ * @tb: tab pointer
+ * @pa: new entry
+ *
+ * Removes the @pa from the table and de-increment reference counter of the @pa. The
+ * partition with zero reference counter will be deallocated. Don't forget to use
+ * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want
+ * to use @pa later.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
+{
+	assert(tb);
+	assert(pa);
+
+	if (!tb || !pa)
+		return -EINVAL;
+
+	DBG(TAB, ul_debugobj(tb, "remove entry %p", pa));
+	list_del(&pa->parts);
+	INIT_LIST_HEAD(&pa->parts);
+
+	fdisk_unref_partition(pa);
+	tb->nents--;
+
+	return 0;
+}
+
+/**
+ * fdisk_get_partitions
+ * @cxt: fdisk context
+ * @tb: returns table
+ *
+ * This function adds partitions from disklabel to @table, it allocates a new
+ * table if if @table points to NULL.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb)
+{
+	size_t i;
+
+	if (!cxt || !cxt->label || !tb)
+		return -EINVAL;
+	if (!cxt->label->op->get_part)
+		return -ENOSYS;
+
+	DBG(CXT, ul_debugobj(cxt, "get table"));
+
+	if (!*tb && !(*tb = fdisk_new_table()))
+		return -ENOMEM;
+
+	for (i = 0; i < cxt->label->nparts_max; i++) {
+		struct fdisk_partition *pa = NULL;
+
+		if (fdisk_get_partition(cxt, i, &pa) != 0)
+			continue;
+		if (fdisk_partition_is_used(pa))
+			fdisk_table_add_partition(*tb, pa);
+		fdisk_unref_partition(pa);
+	}
+
+	return 0;
+}
+
+static void debug_print_table(struct fdisk_table *tb)
+{
+	struct fdisk_iter itr;
+	struct fdisk_partition *pa;
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	while (fdisk_table_next_partition(tb, &itr, &pa) == 0)
+		ul_debugobj(tb, "partition %p [partno=%zu, start=%ju, end=%ju, size=%ju] ",
+			    pa, pa->partno,
+			    (uintmax_t) fdisk_partition_get_start(pa),
+			    (uintmax_t) fdisk_partition_get_end(pa),
+			    (uintmax_t) fdisk_partition_get_size(pa));
+
+}
+
+
+typedef	int (*fdisk_partcmp_t)(struct fdisk_partition *, struct fdisk_partition *);
+
+static int cmp_parts_wrapper(struct list_head *a, struct list_head *b, void *data)
+{
+	struct fdisk_partition *pa = list_entry(a, struct fdisk_partition, parts),
+			       *pb = list_entry(b, struct fdisk_partition, parts);
+
+	fdisk_partcmp_t cmp = (fdisk_partcmp_t) data;
+
+	return cmp(pa, pb);
+}
+
+
+/**
+ * fdisk_table_sort_partitions:
+ * @tb: table
+ * @cmp: compare function
+ *
+ * Sort partition in the table.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_table_sort_partitions(struct fdisk_table *tb,
+			int (*cmp)(struct fdisk_partition *,
+				   struct fdisk_partition *))
+{
+	if (!tb)
+		return -EINVAL;
+
+	DBG(TAB, ul_debugobj(tb, "Before sort:"));
+	ON_DBG(TAB, debug_print_table(tb));
+
+	list_sort(&tb->parts, cmp_parts_wrapper, (void *) cmp);
+
+	DBG(TAB, ul_debugobj(tb, "After sort:"));
+	ON_DBG(TAB, debug_print_table(tb));
+
+	return 0;
+}
+
+/* allocates a new freespace description */
+static int new_freespace(struct fdisk_context *cxt,
+			 fdisk_sector_t start,
+			 fdisk_sector_t end,
+			 struct fdisk_partition *parent,
+			 struct fdisk_partition **pa)
+{
+	assert(cxt);
+	assert(pa);
+
+	*pa = NULL;
+
+	if (start == end)
+		return 0;
+	*pa = fdisk_new_partition();
+	if (!*pa)
+		return -ENOMEM;
+
+	assert(start);
+	assert(end);
+	assert(end > start);
+
+	(*pa)->freespace = 1;
+	(*pa)->start = fdisk_align_lba_in_range(cxt, start, start, end);
+	(*pa)->size = end - (*pa)->start + 1ULL;
+
+	if (parent)
+		(*pa)->parent_partno = parent->partno;
+	return 0;
+}
+
+/* add freespace description to the right place within @tb */
+static int table_add_freespace(
+			struct fdisk_context *cxt,
+			struct fdisk_table *tb,
+			fdisk_sector_t start,
+			fdisk_sector_t end,
+			struct fdisk_partition *parent)
+{
+	struct fdisk_partition *pa, *x, *real_parent = NULL, *best = NULL;
+	struct fdisk_iter itr;
+	int rc = 0;
+
+	assert(tb);
+
+	rc = new_freespace(cxt, start, end, parent, &pa);
+	if (rc)
+		return -ENOMEM;
+	if (!pa)
+		return 0;
+
+	assert(fdisk_partition_has_start(pa));
+	assert(fdisk_partition_has_end(pa));
+
+	DBG(TAB, ul_debugobj(tb, "adding freespace"));
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	if (parent && fdisk_partition_has_partno(parent)) {
+		while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
+			if (!fdisk_partition_has_partno(x))
+				continue;
+			if (x->partno == parent->partno) {
+				real_parent = x;
+				break;
+			}
+		}
+		if (!real_parent) {
+			DBG(TAB, ul_debugobj(tb, "not found freespace parent (partno=%zu)",
+					parent->partno));
+			fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+		}
+	}
+
+	while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
+		fdisk_sector_t end, best_end = 0;
+
+		if (!fdisk_partition_has_end(x))
+			continue;
+
+		end = fdisk_partition_get_end(x);
+		if (best)
+			best_end = fdisk_partition_get_end(best);
+
+		if (end < pa->start && (!best || best_end < end))
+			best = x;
+	}
+
+	if (!best && real_parent)
+		best = real_parent;
+	rc = table_insert_partition(tb, best, pa);
+
+	fdisk_unref_partition(pa);
+
+	DBG(TAB, ul_debugobj(tb, "adding freespace DONE [rc=%d]", rc));
+	return rc;
+}
+
+/* analyze @cont(ainer) in @parts and add all detected freespace into @tb, note
+ * that @parts has to be sorted by partition starts */
+static int check_container_freespace(struct fdisk_context *cxt,
+				     struct fdisk_table *parts,
+				     struct fdisk_table *tb,
+				     struct fdisk_partition *cont)
+{
+	struct fdisk_iter itr;
+	struct fdisk_partition *pa;
+	fdisk_sector_t x, last, grain, lastplusoff;
+	int rc = 0;
+
+	assert(cxt);
+	assert(parts);
+	assert(tb);
+	assert(cont);
+	assert(fdisk_partition_has_start(cont));
+
+	DBG(TAB, ul_debugobj(tb, "analyze container 0x%p", cont));
+
+	last = fdisk_partition_get_start(cont);
+	grain = cxt->grain > cxt->sector_size ?	cxt->grain / cxt->sector_size : 1;
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+	DBG(CXT, ul_debugobj(cxt, "initialized:  last=%ju, grain=%ju", last, grain));
+
+	while (fdisk_table_next_partition(parts, &itr, &pa) == 0) {
+
+		DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", pa->partno, pa->start));
+
+		if (!pa->used || !fdisk_partition_is_nested(pa)
+			      || !fdisk_partition_has_start(pa))
+			continue;
+
+		DBG(CXT, ul_debugobj(cxt, "freespace container analyze: partno=%zu, start=%ju, end=%ju",
+					pa->partno,
+					(uintmax_t) fdisk_partition_get_start(pa),
+					(uintmax_t) fdisk_partition_get_end(pa)));
+
+		lastplusoff = last + cxt->first_lba;
+		if (pa->start > lastplusoff && pa->start - lastplusoff > grain)
+			rc = table_add_freespace(cxt, tb, lastplusoff, pa->start, cont);
+		if (rc)
+			goto done;
+		last = fdisk_partition_get_end(pa);
+	}
+
+	/* free-space remaining in extended partition */
+	x = fdisk_partition_get_start(cont) + fdisk_partition_get_size(cont) - 1;
+	lastplusoff = last + cxt->first_lba;
+	if (lastplusoff < x && x - lastplusoff > grain) {
+		DBG(TAB, ul_debugobj(tb, "add remaining space in container 0x%p", cont));
+		rc = table_add_freespace(cxt, tb, lastplusoff, x, cont);
+	}
+
+done:
+	DBG(TAB, ul_debugobj(tb, "analyze container 0x%p DONE [rc=%d]", cont, rc));
+	return rc;
+}
+
+
+/**
+ * fdisk_get_freespaces
+ * @cxt: fdisk context
+ * @tb: returns table
+ *
+ * This function adds freespace (described by fdisk_partition) to @table, it
+ * allocates a new table if the @table points to NULL.
+ *
+ * Note that free space smaller than grain (see fdisk_get_grain()) is ignored.
+
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb)
+{
+	int rc = 0;
+	fdisk_sector_t last, grain;
+	struct fdisk_table *parts = NULL;
+	struct fdisk_partition *pa;
+	struct fdisk_iter itr;
+
+	DBG(CXT, ul_debugobj(cxt, "get freespace"));
+
+	if (!cxt || !cxt->label || !tb)
+		return -EINVAL;
+	if (!*tb && !(*tb = fdisk_new_table()))
+		return -ENOMEM;
+
+	rc = fdisk_get_partitions(cxt, &parts);
+	if (rc)
+		goto done;
+
+	fdisk_table_sort_partitions(parts, fdisk_partition_cmp_start);
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	last = cxt->first_lba;
+	grain = cxt->grain > cxt->sector_size ?	cxt->grain / cxt->sector_size : 1;
+
+	DBG(CXT, ul_debugobj(cxt, "initialized:  last=%ju, grain=%ju", last, grain));
+
+	/* analyze gaps between partitions */
+	while (rc == 0 && fdisk_table_next_partition(parts, &itr, &pa) == 0) {
+
+		DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", pa->partno, pa->start));
+
+		if (!pa->used || pa->wholedisk || fdisk_partition_is_nested(pa)
+			      || !fdisk_partition_has_start(pa))
+			continue;
+		DBG(CXT, ul_debugobj(cxt, "freespace analyze: partno=%zu, start=%ju, end=%ju",
+					pa->partno,
+					(uintmax_t) fdisk_partition_get_start(pa),
+					(uintmax_t) fdisk_partition_get_end(pa)));
+		if (last + grain <= pa->start) {
+			rc = table_add_freespace(cxt, *tb,
+				last + (last > cxt->first_lba ? 1 : 0),
+				pa->start - 1, NULL);
+		}
+		/* add gaps between logical partitions */
+		if (fdisk_partition_is_container(pa))
+			rc = check_container_freespace(cxt, parts, *tb, pa);
+		last = fdisk_partition_get_end(pa);
+	}
+
+	/* add free-space behind last partition to the end of the table (so
+	 * don't use table_add_freespace()) */
+	if (rc == 0 && last + grain < cxt->total_sectors - 1) {
+		DBG(CXT, ul_debugobj(cxt, "freespace behind last partition detected"));
+		rc = new_freespace(cxt,
+			last + (last > cxt->first_lba ? 1 : 0),
+			cxt->last_lba, NULL, &pa);
+		if (pa) {
+			fdisk_table_add_partition(*tb, pa);
+			fdisk_unref_partition(pa);
+		}
+	}
+
+done:
+	fdisk_unref_table(parts);
+
+	DBG(CXT, ul_debugobj(cxt, "get freespace DONE [rc=%d]", rc));
+	return rc;
+}
+
+/**
+ * fdisk_table_wrong_order:
+ * @tb: table
+ *
+ * Returns: 1 of the table is not in disk order
+ */
+int fdisk_table_wrong_order(struct fdisk_table *tb)
+{
+	struct fdisk_partition *pa;
+	struct fdisk_iter itr;
+	fdisk_sector_t last = 0;
+
+	DBG(TAB, ul_debugobj(tb, "wrong older check"));
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+		if (!fdisk_partition_has_start(pa))
+			continue;
+		if (pa->start < last)
+			return 1;
+		last = pa->start;
+	}
+	return 0;
+}
+
+/**
+ * fdisk_apply_table:
+ * @cxt: context
+ * @tb: table
+ *
+ * Add partitions from table @tb to the in-memory disk label. See
+ * fdisk_add_partition(), fdisk_delete_all_partitions(). The partitons
+ * that does not define start (or does not follow the default start)
+ * are ingored.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb)
+{
+	struct fdisk_partition *pa;
+	struct fdisk_iter itr;
+	int rc = 0;
+
+	assert(cxt);
+	assert(tb);
+
+	DBG(TAB, ul_debugobj(tb, "applying to context %p", cxt));
+
+	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+	while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+		if (!fdisk_partition_has_start(pa) && !pa->start_follow_default)
+			continue;
+		rc = fdisk_add_partition(cxt, pa, NULL);
+		if (rc)
+			break;
+	}
+
+	return rc;
+}
+
diff --git a/libblkid/libfdisk/src/test.c b/libblkid/libfdisk/src/test.c
new file mode 100644
index 0000000..31ed7e0
--- /dev/null
+++ b/libblkid/libfdisk/src/test.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Routines for TEST_PROGRAMs
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifndef TEST_PROGRAM
+#define TEST_PROGRAM
+#endif
+
+#include "fdiskP.h"
+
+int fdisk_run_test(struct fdisk_test *tests, int argc, char *argv[])
+{
+	int rc = -1;
+	struct fdisk_test *ts;
+
+	assert(tests);
+	assert(argc);
+	assert(argv);
+
+	if (argc < 2 ||
+	    strcmp(argv[1], "--help") == 0 ||
+	    strcmp(argv[1], "-h") == 0)
+		goto usage;
+
+	fdisk_init_debug(0);
+
+	for (ts = tests; ts->name; ts++) {
+		if (strcmp(ts->name, argv[1]) == 0) {
+			rc = ts->body(ts, argc - 1, argv + 1);
+			if (rc)
+				printf("FAILED [rc=%d]", rc);
+			break;
+		}
+	}
+
+	if (rc < 0 && ts->name == NULL)
+		goto usage;
+
+	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+usage:
+	printf("\nUsage:\n\t%s <test> [testoptions]\nTests:\n",
+			program_invocation_short_name);
+	for (ts = tests; ts->name; ts++) {
+		printf("\t%-15s", ts->name);
+		if (ts->usage)
+			printf(" %s\n", ts->usage);
+	}
+	printf("\n");
+	return EXIT_FAILURE;
+}
diff --git a/libblkid/libfdisk/src/utils.c b/libblkid/libfdisk/src/utils.c
new file mode 100644
index 0000000..482a306
--- /dev/null
+++ b/libblkid/libfdisk/src/utils.c
@@ -0,0 +1,154 @@
+
+#include "fdiskP.h"
+#include "pathnames.h"
+
+#include <ctype.h>
+
+/**
+ * SECTION: utils
+ * @title: Utils
+ * @short_description: misc fdisk functions
+ */
+
+/*
+ * Zeros in-memory first sector buffer
+ */
+int fdisk_init_firstsector_buffer(struct fdisk_context *cxt)
+{
+	if (!cxt)
+		return -EINVAL;
+
+	if (!cxt->firstsector || cxt->firstsector_bufsz != cxt->sector_size) {
+		/* Let's allocate a new buffer if no allocated yet, or the
+		 * current buffer has incorrect size */
+		if (!cxt->parent || cxt->parent->firstsector != cxt->firstsector)
+			free(cxt->firstsector);
+
+		DBG(CXT, ul_debugobj(cxt, "initialize in-memory first sector "
+				"buffer [sector_size=%lu]", cxt->sector_size));
+		cxt->firstsector = calloc(1, cxt->sector_size);
+		if (!cxt->firstsector)
+			return -ENOMEM;
+
+		cxt->firstsector_bufsz = cxt->sector_size;
+		return 0;
+	}
+
+	DBG(CXT, ul_debugobj(cxt, "zeroize in-memory first sector buffer"));
+	memset(cxt->firstsector, 0, cxt->firstsector_bufsz);
+	return 0;
+}
+
+int fdisk_read_firstsector(struct fdisk_context *cxt)
+{
+	ssize_t r;
+	int rc;
+
+	assert(cxt);
+	assert(cxt->sector_size);
+
+	rc = fdisk_init_firstsector_buffer(cxt);
+	if (rc)
+		return rc;
+
+	assert(cxt->sector_size == cxt->firstsector_bufsz);
+
+	DBG(CXT, ul_debugobj(cxt, "reading first sector "
+				"buffer [sector_size=%lu]", cxt->sector_size));
+
+	r = lseek(cxt->dev_fd, 0, SEEK_SET);
+	if (r == -1)
+	{
+		DBG(CXT, ul_debugobj(cxt, "failed to seek to first sector %m"));
+		return -errno;
+	}
+
+	r = read(cxt->dev_fd, cxt->firstsector, cxt->sector_size);
+
+	if (r != cxt->sector_size) {
+		if (!errno)
+			errno = EINVAL;	/* probably too small file/device */
+		DBG(CXT, ul_debugobj(cxt, "failed to read first sector %m"));
+		return -errno;
+	}
+
+	return 0;
+}
+
+/**
+ * fdisk_partname:
+ * @dev: device name
+ * @partno: partition name
+ *
+ * Return: allocated buffer with partition name, use free() to deallocate.
+ */
+char *fdisk_partname(const char *dev, size_t partno)
+{
+	char *res = NULL;
+	const char *p = "";
+	int w = 0;
+
+	if (!dev || !*dev) {
+		if (asprintf(&res, "%zd", partno) > 0)
+			return res;
+		return NULL;
+	}
+
+	w = strlen(dev);
+	if (isdigit(dev[w - 1]))
+#ifdef __GNU__
+		p = "s";
+#else
+		p = "p";
+#endif
+
+	/* devfs kludge - note: fdisk partition names are not supposed
+	   to equal kernel names, so there is no reason to do this */
+	if (strcmp(dev + w - 4, "disc") == 0) {
+		w -= 4;
+		p = "part";
+	}
+
+	/* udev names partitions by appending -partN
+	   e.g. ata-SAMSUNG_SV8004H_0357J1FT712448-part1 */
+	if ((strncmp(dev, _PATH_DEV_BYID, sizeof(_PATH_DEV_BYID) - 1) == 0) ||
+	     strncmp(dev, _PATH_DEV_BYPATH, sizeof(_PATH_DEV_BYPATH) - 1) == 0) {
+	       p = "-part";
+	}
+
+	if (asprintf(&res, "%.*s%s%zu", w, dev, p, partno) > 0)
+		return res;
+
+	return NULL;
+}
+
+#ifdef TEST_PROGRAM
+struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; }
+struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; }
+
+int test_partnames(struct fdisk_test *ts, int argc, char *argv[])
+{
+	size_t i;
+	const char *disk = argv[1];
+
+	for (i = 0; i < 5; i++) {
+		char *p = fdisk_partname(disk, i + 1);
+		if (p)
+			printf("%zu: '%s'\n", i + 1, p);
+		free(p);
+	}
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct fdisk_test tss[] = {
+		{ "--partnames",  test_partnames,  "<diskname>" },
+		{ NULL }
+	};
+
+	return fdisk_run_test(tss, argc, argv);
+}
+
+#endif
diff --git a/libblkid/libuuid/COPYING b/libblkid/libuuid/COPYING
new file mode 100644
index 0000000..0e902cf
--- /dev/null
+++ b/libblkid/libuuid/COPYING
@@ -0,0 +1,5 @@
+This library is free software; you can redistribute it and/or
+modify it under the terms of the Modified BSD License.
+
+The complete text of the license is available in the
+../Documentation/licenses/COPYING.BSD-3 file.
diff --git a/libblkid/libuuid/Makemodule.am b/libblkid/libuuid/Makemodule.am
new file mode 100644
index 0000000..166be5c
--- /dev/null
+++ b/libblkid/libuuid/Makemodule.am
@@ -0,0 +1,10 @@
+if BUILD_LIBUUID
+
+include libuuid/man/Makemodule.am
+include libuuid/src/Makemodule.am
+
+pkgconfig_DATA += libuuid/uuid.pc
+PATHFILES      += libuuid/uuid.pc
+EXTRA_DIST     += libuuid/COPYING
+
+endif # BUILD_LIBUUID
diff --git a/libblkid/libuuid/man/.gitignore b/libblkid/libuuid/man/.gitignore
new file mode 100644
index 0000000..7957ad2
--- /dev/null
+++ b/libblkid/libuuid/man/.gitignore
@@ -0,0 +1,3 @@
+uuid_generate_random.3
+uuid_generate_time.3
+uuid_generate_time_safe.3
diff --git a/libblkid/libuuid/man/Makemodule.am b/libblkid/libuuid/man/Makemodule.am
new file mode 100644
index 0000000..81287d5
--- /dev/null
+++ b/libblkid/libuuid/man/Makemodule.am
@@ -0,0 +1,14 @@
+
+dist_man_MANS += \
+	libuuid/man/uuid.3 \
+	libuuid/man/uuid_clear.3 \
+	libuuid/man/uuid_compare.3 \
+	libuuid/man/uuid_copy.3 \
+	libuuid/man/uuid_generate.3 \
+	libuuid/man/uuid_is_null.3 \
+	libuuid/man/uuid_parse.3 \
+	libuuid/man/uuid_time.3 \
+	libuuid/man/uuid_unparse.3 \
+	libuuid/man/uuid_generate_random.3 \
+	libuuid/man/uuid_generate_time.3 \
+	libuuid/man/uuid_generate_time_safe.3
diff --git a/libblkid/libuuid/man/uuid.3 b/libblkid/libuuid/man/uuid.3
new file mode 100644
index 0000000..37b0499
--- /dev/null
+++ b/libblkid/libuuid/man/uuid.3
@@ -0,0 +1,65 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\"    including the disclaimer of warranties.
+.\" 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, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created  Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid \- DCE compatible Universally Unique Identifier library
+.SH SYNOPSIS
+.B #include <uuid.h>
+.SH DESCRIPTION
+The UUID library is used to generate unique identifiers for objects
+that may be accessible beyond the local system.  This library
+generates UUIDs compatible with those created by the Open Software
+Foundation (OSF) Distributed Computing Environment (DCE) utility
+.BR uuidgen .
+.sp
+The UUIDs generated by this library can be reasonably expected to be
+unique within a system, and unique across all systems.  They could
+be used, for instance, to generate unique HTTP cookies across multiple
+web servers without communication between the servers, and without fear
+of a name clash.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH "SEE ALSO"
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_time (3),
+.BR uuid_unparse (3)
diff --git a/libblkid/libuuid/man/uuid_clear.3 b/libblkid/libuuid/man/uuid_clear.3
new file mode 100644
index 0000000..70fca02
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_clear.3
@@ -0,0 +1,62 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\"    including the disclaimer of warranties.
+.\" 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, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created  Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_CLEAR 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_clear \- reset value of UUID variable to the NULL value
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "void uuid_clear(uuid_t " uu );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_clear
+function sets the value of the supplied uuid variable
+.I uu
+to the NULL value.
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_compare.3 b/libblkid/libuuid/man/uuid_compare.3
new file mode 100644
index 0000000..f91181a
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_compare.3
@@ -0,0 +1,68 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\"    including the disclaimer of warranties.
+.\" 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, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created  Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_COMPARE 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_compare \- compare whether two UUIDs are the same
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "int uuid_compare(uuid_t " uu1 ", uuid_t " uu2)
+.fi
+.SH DESCRIPTION
+The
+.B uuid_compare
+function compares the two supplied uuid variables
+.IR uu1 " and " uu2
+to each other.
+.SH RETURN VALUE
+Returns an integer less than, equal to, or greater than zero if
+.I uu1
+is found,  respectively, to be lexicographically less than, equal, or
+greater than
+.IR uu2 .
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_copy.3 b/libblkid/libuuid/man/uuid_copy.3
new file mode 100644
index 0000000..5159fa6
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_copy.3
@@ -0,0 +1,64 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\"    including the disclaimer of warranties.
+.\" 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, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created  Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_COPY 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_copy \- copy a UUID value
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "void uuid_copy(uuid_t " dst ", uuid_t " src);
+.fi
+.SH DESCRIPTION
+The
+.B uuid_copy
+function copies the UUID variable
+.IR src " to " dst .
+.SH RETURN VALUE
+The copied UUID is returned in the location pointed to by
+.IR dst .
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_generate.3 b/libblkid/libuuid/man/uuid_generate.3
new file mode 100644
index 0000000..19904d7
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_generate.3
@@ -0,0 +1,126 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\"    including the disclaimer of warranties.
+.\" 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, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created  Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_GENERATE 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_generate, uuid_generate_random, uuid_generate_time,
+uuid_generate_time_safe \- create a new unique UUID value
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "void uuid_generate(uuid_t " out );
+.BI "void uuid_generate_random(uuid_t " out );
+.BI "void uuid_generate_time(uuid_t " out );
+.BI "int uuid_generate_time_safe(uuid_t " out );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_generate
+function creates a new universally unique identifier (UUID).  The uuid will
+be generated based on high-quality randomness from
+.IR /dev/urandom ,
+if available.  If it is not available, then
+.B uuid_generate
+will use an alternative algorithm which uses the current time, the
+local ethernet MAC address (if available), and random data generated
+using a pseudo-random generator.
+.sp
+The
+.B uuid_generate_random
+function forces the use of the all-random UUID format, even if
+a high-quality random number generator (i.e.,
+.IR /dev/urandom )
+is not available, in which case a pseudo-random
+generator will be substituted.  Note that the use of a pseudo-random
+generator may compromise the uniqueness of UUIDs
+generated in this fashion.
+.sp
+The
+.B uuid_generate_time
+function forces the use of the alternative algorithm which uses the
+current time and the local ethernet MAC address (if available).
+This algorithm used to be the default one used to generate UUID, but
+because of the use of the ethernet MAC address, it can leak
+information about when and where the UUID was generated.  This can cause
+privacy problems in some applications, so the
+.B uuid_generate
+function only uses this algorithm if a high-quality source of
+randomness is not available.  To guarantee uniqueness of UUIDs generated
+by concurrently running processes, the uuid library uses global
+clock state counter (if the process has permissions to gain exclusive access
+to this file) and/or the
+.B uuidd
+daemon, if it is running already or can be spawned by the process (if
+installed and the process has enough permissions to run it).  If neither of
+these two synchronization mechanisms can be used, it is theoretically possible
+that two concurrently running processes obtain the same UUID(s).  To tell
+whether the UUID has been generated in a safe manner, use
+.BR uuid_generate_time_safe .
+.sp
+The
+.B uuid_generate_time_safe
+is similar to
+.BR uuid_generate_time ,
+except that it returns a value which denotes whether any of the synchronization
+mechanisms (see above) has been used.
+.sp
+The UUID is 16 bytes (128 bits) long, which gives approximately 3.4x10^38
+unique values (there are approximately 10^80 elementary particles in
+the universe according to Carl Sagan's
+.IR Cosmos ).
+The new UUID can reasonably be considered unique among all UUIDs created
+on the local system, and among UUIDs created on other systems in the past
+and in the future.
+.SH RETURN VALUE
+The newly created UUID is returned in the memory location pointed to by
+.IR out .
+.B uuid_generate_time_safe
+returns zero if the UUID has been generated in a safe manner, \-1 otherwise.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuidgen (1),
+.BR uuidd (8),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_time (3),
+.BR uuid_unparse (3)
diff --git a/libblkid/libuuid/man/uuid_is_null.3 b/libblkid/libuuid/man/uuid_is_null.3
new file mode 100644
index 0000000..86a7a50
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_is_null.3
@@ -0,0 +1,64 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\"    including the disclaimer of warranties.
+.\" 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, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created  Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_IS_NULL 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_is_null \- compare the value of the UUID to the NULL value
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "int uuid_is_null(uuid_t " uu );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_is_null
+function compares the value of the supplied UUID variable
+.I uu
+to the NULL value.  If the value is equal to the NULL UUID, 1 is returned,
+otherwise 0 is returned.
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_time (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_parse.3 b/libblkid/libuuid/man/uuid_parse.3
new file mode 100644
index 0000000..31a5926
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_parse.3
@@ -0,0 +1,73 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\"    including the disclaimer of warranties.
+.\" 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, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created  Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_PARSE 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_parse \- convert an input UUID string into binary representation
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "int uuid_parse( char *" in ", uuid_t " uu );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_parse
+function converts the UUID string given by
+.I in
+into the binary representation.  The input UUID is a string of the form
+1b4e28ba\-2fa1\-11d2\-883f\-b9a761bde3fb (in
+.BR printf (3)
+format "%08x\-%04x\-%04x\-%04x\-%012x", 36 bytes plus the trailing '\e0').
+.SH RETURN VALUE
+Upon successfully parsing the input string, 0 is returned, and the UUID is
+stored in the location pointed to by
+.IR uu ,
+otherwise \-1 is returned.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_time (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_time.3 b/libblkid/libuuid/man/uuid_time.3
new file mode 100644
index 0000000..483676b
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_time.3
@@ -0,0 +1,78 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\"    including the disclaimer of warranties.
+.\" 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, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created  Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_TIME 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_time \- extract the time at which the UUID was created
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "time_t uuid_time(uuid_t " uu ", struct timeval *" ret_tv )
+.fi
+.SH DESCRIPTION
+The
+.B uuid_time
+function extracts the time at which the supplied time-based UUID
+.I uu
+was created.  Note that the UUID creation time is only encoded within
+certain types of UUIDs.  This function can only reasonably expect to
+extract the creation time for UUIDs created with the
+.BR uuid_generate_time (3)
+and
+.BR uuid_generate_time_safe (3)
+functions.  It may or may not work with UUIDs created by other mechanisms.
+.SH "RETURN VALUES"
+The time at which the UUID was created, in seconds since January 1, 1970 GMT
+(the epoch), is returned (see
+.BR time "(2))."
+The time at which the UUID was created, in seconds and microseconds since
+the epoch, is also stored in the location pointed to by
+.I ret_tv
+(see
+.BR gettimeofday "(2))."
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_unparse.3 b/libblkid/libuuid/man/uuid_unparse.3
new file mode 100644
index 0000000..1e0116d
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_unparse.3
@@ -0,0 +1,81 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\"    including the disclaimer of warranties.
+.\" 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, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created  Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_UNPARSE 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_unparse \- convert an UUID from binary representation to a string
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "void uuid_unparse(uuid_t " uu ", char *" out );
+.BI "void uuid_unparse_upper(uuid_t " uu ", char *" out );
+.BI "void uuid_unparse_lower(uuid_t " uu ", char *" out );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_unparse
+function converts the supplied UUID
+.I uu
+from the binary representation into a 36-byte string (plus tailing '\e0')
+of the form 1b4e28ba\-2fa1\-11d2\-883f\-0016d3cca427 and stores this
+value in the character string pointed to by
+.IR out .
+The case of the hex digits returned by
+.B uuid_unparse
+may be upper or lower case, and is
+dependent on the system-dependent local default.
+.PP
+If the case of the
+hex digits is important then the functions
+.B uuid_unparse_upper
+and
+.B uuid_unparse_lower
+may be used.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_time (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3)
+.ad
diff --git a/libblkid/libuuid/src/Makemodule.am b/libblkid/libuuid/src/Makemodule.am
new file mode 100644
index 0000000..061aff2
--- /dev/null
+++ b/libblkid/libuuid/src/Makemodule.am
@@ -0,0 +1,61 @@
+
+check_PROGRAMS += test_uuid
+test_uuid_SOURCES = libuuid/src/test_uuid.c
+test_uuid_LDADD = libuuid.la $(SOCKET_LIBS)
+test_uuid_CFLAGS = -I$(ul_libuuid_incdir)
+
+# includes
+uuidincdir = $(includedir)/uuid
+uuidinc_HEADERS = libuuid/src/uuid.h
+
+usrlib_exec_LTLIBRARIES += libuuid.la
+
+libuuid_la_SOURCES = \
+	libuuid/src/clear.c \
+	libuuid/src/compare.c \
+	libuuid/src/copy.c \
+	libuuid/src/gen_uuid.c \
+	libuuid/src/isnull.c \
+	libuuid/src/pack.c \
+	libuuid/src/parse.c \
+	libuuid/src/unpack.c \
+	libuuid/src/unparse.c \
+	libuuid/src/uuidd.h \
+	libuuid/src/uuidd.h \
+	libuuid/src/uuidP.h \
+	libuuid/src/uuid_time.c \
+	$(uuidinc_HEADERS) \
+	lib/randutils.c
+
+libuuid_la_DEPENDENCIES = libuuid/src/libuuid.sym
+libuuid_la_LIBADD       = $(SOCKET_LIBS)
+
+libuuid_la_CFLAGS = \
+	 $(SOLIB_CFLAGS) \
+	 -I$(ul_libuuid_incdir) \
+	 -Ilibuuid/src
+
+libuuid_la_LDFLAGS = \
+	$(SOLIB_LDFLAGS) \
+	-Wl,--version-script=$(top_srcdir)/libuuid/src/libuuid.sym \
+	-version-info $(LIBUUID_VERSION_INFO)
+
+EXTRA_DIST += libuuid/src/libuuid.sym
+
+# move lib from $(usrlib_execdir) to $(libdir) if needed
+install-exec-hook-libuuid:
+	if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/libuuid.so"; then \
+		mkdir -p $(DESTDIR)$(libdir); \
+		mv $(DESTDIR)$(usrlib_execdir)/libuuid.so.* $(DESTDIR)$(libdir); \
+		so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libuuid.so); \
+		so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
+		(cd $(DESTDIR)$(usrlib_execdir) && \
+			rm -f libuuid.so && \
+			$(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libuuid.so); \
+	fi
+
+uninstall-hook-libuuid:
+	rm -f $(DESTDIR)$(libdir)/libuuid.so*
+
+INSTALL_EXEC_HOOKS += install-exec-hook-libuuid
+UNINSTALL_HOOKS += uninstall-hook-libuuid
diff --git a/libblkid/libuuid/src/clear.c b/libblkid/libuuid/src/clear.c
new file mode 100644
index 0000000..2d91fee
--- /dev/null
+++ b/libblkid/libuuid/src/clear.c
@@ -0,0 +1,43 @@
+/*
+ * clear.c -- Clear a UUID
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "string.h"
+
+#include "uuidP.h"
+
+void uuid_clear(uuid_t uu)
+{
+	memset(uu, 0, 16);
+}
+
diff --git a/libblkid/libuuid/src/compare.c b/libblkid/libuuid/src/compare.c
new file mode 100644
index 0000000..8f3437a
--- /dev/null
+++ b/libblkid/libuuid/src/compare.c
@@ -0,0 +1,55 @@
+/*
+ * compare.c --- compare whether or not two UUIDs are the same
+ *
+ * Returns 0 if the two UUIDs are different, and 1 if they are the same.
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "uuidP.h"
+#include <string.h>
+
+#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1);
+
+int uuid_compare(const uuid_t uu1, const uuid_t uu2)
+{
+	struct uuid	uuid1, uuid2;
+
+	uuid_unpack(uu1, &uuid1);
+	uuid_unpack(uu2, &uuid2);
+
+	UUCMP(uuid1.time_low, uuid2.time_low);
+	UUCMP(uuid1.time_mid, uuid2.time_mid);
+	UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version);
+	UUCMP(uuid1.clock_seq, uuid2.clock_seq);
+	return memcmp(uuid1.node, uuid2.node, 6);
+}
+
diff --git a/libblkid/libuuid/src/copy.c b/libblkid/libuuid/src/copy.c
new file mode 100644
index 0000000..ead33aa
--- /dev/null
+++ b/libblkid/libuuid/src/copy.c
@@ -0,0 +1,45 @@
+/*
+ * copy.c --- copy UUIDs
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "uuidP.h"
+
+void uuid_copy(uuid_t dst, const uuid_t src)
+{
+	unsigned char		*cp1;
+	const unsigned char	*cp2;
+	int			i;
+
+	for (i=0, cp1 = dst, cp2 = src; i < 16; i++)
+		*cp1++ = *cp2++;
+}
diff --git a/libblkid/libuuid/src/gen_uuid.c b/libblkid/libuuid/src/gen_uuid.c
new file mode 100644
index 0000000..eb79339
--- /dev/null
+++ b/libblkid/libuuid/src/gen_uuid.c
@@ -0,0 +1,545 @@
+/*
+ * gen_uuid.c --- generate a DCE-compatible uuid
+ *
+ * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#define UUID MYUUID
+#endif
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NET_IF_DL_H
+#include <net/if_dl.h>
+#endif
+#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
+#include <sys/syscall.h>
+#endif
+
+#include "all-io.h"
+#include "uuidP.h"
+#include "uuidd.h"
+#include "randutils.h"
+#include "c.h"
+
+#ifdef HAVE_TLS
+#define THREAD_LOCAL static __thread
+#else
+#define THREAD_LOCAL static
+#endif
+
+#ifdef _WIN32
+static void gettimeofday (struct timeval *tv, void *dummy)
+{
+	FILETIME	ftime;
+	uint64_t	n;
+
+	GetSystemTimeAsFileTime (&ftime);
+	n = (((uint64_t) ftime.dwHighDateTime << 32)
+	     + (uint64_t) ftime.dwLowDateTime);
+	if (n) {
+		n /= 10;
+		n -= ((369 * 365 + 89) * (uint64_t) 86400) * 1000000;
+	}
+
+	tv->tv_sec = n / 1000000;
+	tv->tv_usec = n % 1000000;
+}
+
+static int getuid (void)
+{
+	return 1;
+}
+#endif
+
+/*
+ * Get the ethernet hardware address, if we can find it...
+ *
+ * XXX for a windows version, probably should use GetAdaptersInfo:
+ * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451
+ * commenting out get_node_id just to get gen_uuid to compile under windows
+ * is not the right way to go!
+ */
+static int get_node_id(unsigned char *node_id)
+{
+#ifdef HAVE_NET_IF_H
+	int		sd;
+	struct ifreq	ifr, *ifrp;
+	struct ifconf	ifc;
+	char buf[1024];
+	int		n, i;
+	unsigned char	*a;
+#ifdef HAVE_NET_IF_DL_H
+	struct sockaddr_dl *sdlp;
+#endif
+
+/*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is
+ * just sizeof(struct ifreq)
+ */
+#ifdef HAVE_SA_LEN
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+     sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN */
+
+	sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+	if (sd < 0) {
+		return -1;
+	}
+	memset(buf, 0, sizeof(buf));
+	ifc.ifc_len = sizeof(buf);
+	ifc.ifc_buf = buf;
+	if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
+		close(sd);
+		return -1;
+	}
+	n = ifc.ifc_len;
+	for (i = 0; i < n; i+= ifreq_size(*ifrp) ) {
+		ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
+		strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
+#ifdef SIOCGIFHWADDR
+		if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
+			continue;
+		a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
+#else
+#ifdef SIOCGENADDR
+		if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
+			continue;
+		a = (unsigned char *) ifr.ifr_enaddr;
+#else
+#ifdef HAVE_NET_IF_DL_H
+		sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr;
+		if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
+			continue;
+		a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen];
+#else
+		/*
+		 * XXX we don't have a way of getting the hardware
+		 * address
+		 */
+		close(sd);
+		return 0;
+#endif /* HAVE_NET_IF_DL_H */
+#endif /* SIOCGENADDR */
+#endif /* SIOCGIFHWADDR */
+		if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
+			continue;
+		if (node_id) {
+			memcpy(node_id, a, 6);
+			close(sd);
+			return 1;
+		}
+	}
+	close(sd);
+#endif
+	return 0;
+}
+
+/* Assume that the gettimeofday() has microsecond granularity */
+#define MAX_ADJUSTMENT 10
+
+/*
+ * Get clock from global sequence clock counter.
+ *
+ * Return -1 if the clock counter could not be opened/locked (in this case
+ * pseudorandom value is returned in @ret_clock_seq), otherwise return 0.
+ */
+static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
+		     uint16_t *ret_clock_seq, int *num)
+{
+	THREAD_LOCAL int		adjustment = 0;
+	THREAD_LOCAL struct timeval	last = {0, 0};
+	THREAD_LOCAL int		state_fd = -2;
+	THREAD_LOCAL FILE		*state_f;
+	THREAD_LOCAL uint16_t		clock_seq;
+	struct timeval			tv;
+	uint64_t			clock_reg;
+	mode_t				save_umask;
+	int				len;
+	int				ret = 0;
+
+	if (state_fd == -2) {
+		save_umask = umask(0);
+		state_fd = open(LIBUUID_CLOCK_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0660);
+		(void) umask(save_umask);
+		if (state_fd != -1) {
+			state_f = fdopen(state_fd, "r+" UL_CLOEXECSTR);
+			if (!state_f) {
+				close(state_fd);
+				state_fd = -1;
+				ret = -1;
+			}
+		}
+		else
+			ret = -1;
+	}
+	if (state_fd >= 0) {
+		rewind(state_f);
+		while (flock(state_fd, LOCK_EX) < 0) {
+			if ((errno == EAGAIN) || (errno == EINTR))
+				continue;
+			fclose(state_f);
+			close(state_fd);
+			state_fd = -1;
+			ret = -1;
+			break;
+		}
+	}
+	if (state_fd >= 0) {
+		unsigned int cl;
+		unsigned long tv1, tv2;
+		int a;
+
+		if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n",
+			   &cl, &tv1, &tv2, &a) == 4) {
+			clock_seq = cl & 0x3FFF;
+			last.tv_sec = tv1;
+			last.tv_usec = tv2;
+			adjustment = a;
+		}
+	}
+
+	if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
+		random_get_bytes(&clock_seq, sizeof(clock_seq));
+		clock_seq &= 0x3FFF;
+		gettimeofday(&last, 0);
+		last.tv_sec--;
+	}
+
+try_again:
+	gettimeofday(&tv, 0);
+	if ((tv.tv_sec < last.tv_sec) ||
+	    ((tv.tv_sec == last.tv_sec) &&
+	     (tv.tv_usec < last.tv_usec))) {
+		clock_seq = (clock_seq+1) & 0x3FFF;
+		adjustment = 0;
+		last = tv;
+	} else if ((tv.tv_sec == last.tv_sec) &&
+	    (tv.tv_usec == last.tv_usec)) {
+		if (adjustment >= MAX_ADJUSTMENT)
+			goto try_again;
+		adjustment++;
+	} else {
+		adjustment = 0;
+		last = tv;
+	}
+
+	clock_reg = tv.tv_usec*10 + adjustment;
+	clock_reg += ((uint64_t) tv.tv_sec)*10000000;
+	clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
+
+	if (num && (*num > 1)) {
+		adjustment += *num - 1;
+		last.tv_usec += adjustment / 10;
+		adjustment = adjustment % 10;
+		last.tv_sec += last.tv_usec / 1000000;
+		last.tv_usec = last.tv_usec % 1000000;
+	}
+
+	if (state_fd >= 0) {
+		rewind(state_f);
+		len = fprintf(state_f,
+			      "clock: %04x tv: %016lu %08lu adj: %08d\n",
+			      clock_seq, last.tv_sec, last.tv_usec, adjustment);
+		fflush(state_f);
+		if (ftruncate(state_fd, len) < 0) {
+			fprintf(state_f, "                   \n");
+			fflush(state_f);
+		}
+		rewind(state_f);
+		flock(state_fd, LOCK_UN);
+	}
+
+	*clock_high = clock_reg >> 32;
+	*clock_low = clock_reg;
+	*ret_clock_seq = clock_seq;
+	return ret;
+}
+
+#if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H)
+/*
+ * Try using the uuidd daemon to generate the UUID
+ *
+ * Returns 0 on success, non-zero on failure.
+ */
+static int get_uuid_via_daemon(int op, uuid_t out, int *num)
+{
+	char op_buf[64];
+	int op_len;
+	int s;
+	ssize_t ret;
+	int32_t reply_len = 0, expected = 16;
+	struct sockaddr_un srv_addr;
+
+	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+		return -1;
+
+	srv_addr.sun_family = AF_UNIX;
+	strcpy(srv_addr.sun_path, UUIDD_SOCKET_PATH);
+
+	if (connect(s, (const struct sockaddr *) &srv_addr,
+		    sizeof(struct sockaddr_un)) < 0)
+		goto fail;
+
+	op_buf[0] = op;
+	op_len = 1;
+	if (op == UUIDD_OP_BULK_TIME_UUID) {
+		memcpy(op_buf+1, num, sizeof(*num));
+		op_len += sizeof(*num);
+		expected += sizeof(*num);
+	}
+
+	ret = write(s, op_buf, op_len);
+	if (ret < 1)
+		goto fail;
+
+	ret = read_all(s, (char *) &reply_len, sizeof(reply_len));
+	if (ret < 0)
+		goto fail;
+
+	if (reply_len != expected)
+		goto fail;
+
+	ret = read_all(s, op_buf, reply_len);
+
+	if (op == UUIDD_OP_BULK_TIME_UUID)
+		memcpy(op_buf+16, num, sizeof(int));
+
+	memcpy(out, op_buf, 16);
+
+	close(s);
+	return ((ret == expected) ? 0 : -1);
+
+fail:
+	close(s);
+	return -1;
+}
+
+#else /* !defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H) */
+static int get_uuid_via_daemon(int op, uuid_t out, int *num)
+{
+	return -1;
+}
+#endif
+
+int __uuid_generate_time(uuid_t out, int *num)
+{
+	static unsigned char node_id[6];
+	static int has_init = 0;
+	struct uuid uu;
+	uint32_t	clock_mid;
+	int ret;
+
+	if (!has_init) {
+		if (get_node_id(node_id) <= 0) {
+			random_get_bytes(node_id, 6);
+			/*
+			 * Set multicast bit, to prevent conflicts
+			 * with IEEE 802 addresses obtained from
+			 * network cards
+			 */
+			node_id[0] |= 0x01;
+		}
+		has_init = 1;
+	}
+	ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
+	uu.clock_seq |= 0x8000;
+	uu.time_mid = (uint16_t) clock_mid;
+	uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
+	memcpy(uu.node, node_id, 6);
+	uuid_pack(&uu, out);
+	return ret;
+}
+
+/*
+ * Generate time-based UUID and store it to @out
+ *
+ * Tries to guarantee uniqueness of the generated UUIDs by obtaining them from the uuidd daemon,
+ * or, if uuidd is not usable, by using the global clock state counter (see get_clock()).
+ * If neither of these is possible (e.g. because of insufficient permissions), it generates
+ * the UUID anyway, but returns -1. Otherwise, returns 0.
+ */
+static int uuid_generate_time_generic(uuid_t out) {
+#ifdef HAVE_TLS
+	THREAD_LOCAL int		num = 0;
+	THREAD_LOCAL struct uuid	uu;
+	THREAD_LOCAL time_t		last_time = 0;
+	time_t				now;
+
+	if (num > 0) {
+		now = time(0);
+		if (now > last_time+1)
+			num = 0;
+	}
+	if (num <= 0) {
+		num = 1000;
+		if (get_uuid_via_daemon(UUIDD_OP_BULK_TIME_UUID,
+					out, &num) == 0) {
+			last_time = time(0);
+			uuid_unpack(out, &uu);
+			num--;
+			return 0;
+		}
+		num = 0;
+	}
+	if (num > 0) {
+		uu.time_low++;
+		if (uu.time_low == 0) {
+			uu.time_mid++;
+			if (uu.time_mid == 0)
+				uu.time_hi_and_version++;
+		}
+		num--;
+		uuid_pack(&uu, out);
+		return 0;
+	}
+#else
+	if (get_uuid_via_daemon(UUIDD_OP_TIME_UUID, out, 0) == 0)
+		return 0;
+#endif
+
+	return __uuid_generate_time(out, 0);
+}
+
+/*
+ * Generate time-based UUID and store it to @out.
+ *
+ * Discards return value from uuid_generate_time_generic()
+ */
+void uuid_generate_time(uuid_t out)
+{
+	(void)uuid_generate_time_generic(out);
+}
+
+
+int uuid_generate_time_safe(uuid_t out)
+{
+	return uuid_generate_time_generic(out);
+}
+
+
+void __uuid_generate_random(uuid_t out, int *num)
+{
+	uuid_t	buf;
+	struct uuid uu;
+	int i, n;
+
+	if (!num || !*num)
+		n = 1;
+	else
+		n = *num;
+
+	for (i = 0; i < n; i++) {
+		random_get_bytes(buf, sizeof(buf));
+		uuid_unpack(buf, &uu);
+
+		uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
+		uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF)
+			| 0x4000;
+		uuid_pack(&uu, out);
+		out += sizeof(uuid_t);
+	}
+}
+
+void uuid_generate_random(uuid_t out)
+{
+	int	num = 1;
+	/* No real reason to use the daemon for random uuid's -- yet */
+
+	__uuid_generate_random(out, &num);
+}
+
+/*
+ * Check whether good random source (/dev/random or /dev/urandom)
+ * is available.
+ */
+static int have_random_source(void)
+{
+	struct stat s;
+
+	return (!stat("/dev/random", &s) || !stat("/dev/urandom", &s));
+}
+
+
+/*
+ * This is the generic front-end to uuid_generate_random and
+ * uuid_generate_time.  It uses uuid_generate_random only if
+ * /dev/urandom is available, since otherwise we won't have
+ * high-quality randomness.
+ */
+void uuid_generate(uuid_t out)
+{
+	if (have_random_source())
+		uuid_generate_random(out);
+	else
+		uuid_generate_time(out);
+}
diff --git a/libblkid/libuuid/src/isnull.c b/libblkid/libuuid/src/isnull.c
new file mode 100644
index 0000000..931e7e7
--- /dev/null
+++ b/libblkid/libuuid/src/isnull.c
@@ -0,0 +1,48 @@
+/*
+ * isnull.c --- Check whether or not the UUID is null
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "uuidP.h"
+
+/* Returns 1 if the uuid is the NULL uuid */
+int uuid_is_null(const uuid_t uu)
+{
+	const unsigned char 	*cp;
+	int			i;
+
+	for (i=0, cp = uu; i < 16; i++)
+		if (*cp++)
+			return 0;
+	return 1;
+}
+
diff --git a/libblkid/libuuid/src/libuuid.sym b/libblkid/libuuid/src/libuuid.sym
new file mode 100644
index 0000000..28a2076
--- /dev/null
+++ b/libblkid/libuuid/src/libuuid.sym
@@ -0,0 +1,48 @@
+/*
+ * The symbol versioning ensures that a new application requiring symbol 'foo'
+ * can't run with old libbrary.so not providing 'foo' - the global SONAME
+ * version info can't enforce this since we never change the SONAME.
+ *
+ * The original libuuid from e2fsprogs (<=1.41.5) does not to use
+ * symbol versioning -- all the original symbols are in UUID_1.0 now.
+ *
+ * Copyright (C) 2011-2014 Karel Zak <kzak@redhat.com>
+ */
+UUID_1.0 {
+global:
+	uuid_clear;
+	uuid_compare;
+	uuid_copy;
+	uuid_generate;
+	uuid_generate_random;
+	uuid_generate_time;
+	uuid_is_null;
+	uuid_parse;
+	uuid_unparse;
+	uuid_unparse_lower;
+	uuid_unparse_upper;
+	uuid_time;
+	uuid_type;
+	uuid_variant;
+};
+
+/*
+ * version(s) since util-linux 2.20
+ */
+UUID_2.20 {
+global:
+	uuid_generate_time_safe;
+} UUID_1.0;
+
+
+/*
+ * __uuid_* this is not part of the official API, this is
+ * uuidd (uuid daemon) specific stuff. Hell.
+ */
+UUIDD_PRIVATE {
+global:
+	__uuid_generate_time;
+	__uuid_generate_random;
+local:
+	*;
+};
diff --git a/libblkid/libuuid/src/pack.c b/libblkid/libuuid/src/pack.c
new file mode 100644
index 0000000..6e12476
--- /dev/null
+++ b/libblkid/libuuid/src/pack.c
@@ -0,0 +1,69 @@
+/*
+ * Internal routine for packing UUIDs
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_pack(const struct uuid *uu, uuid_t ptr)
+{
+	uint32_t	tmp;
+	unsigned char	*out = ptr;
+
+	tmp = uu->time_low;
+	out[3] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[2] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[1] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[0] = (unsigned char) tmp;
+
+	tmp = uu->time_mid;
+	out[5] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[4] = (unsigned char) tmp;
+
+	tmp = uu->time_hi_and_version;
+	out[7] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[6] = (unsigned char) tmp;
+
+	tmp = uu->clock_seq;
+	out[9] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[8] = (unsigned char) tmp;
+
+	memcpy(out+10, uu->node, 6);
+}
+
diff --git a/libblkid/libuuid/src/parse.c b/libblkid/libuuid/src/parse.c
new file mode 100644
index 0000000..074383e
--- /dev/null
+++ b/libblkid/libuuid/src/parse.c
@@ -0,0 +1,79 @@
+/*
+ * parse.c --- UUID parsing
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "uuidP.h"
+
+int uuid_parse(const char *in, uuid_t uu)
+{
+	struct uuid	uuid;
+	int 		i;
+	const char	*cp;
+	char		buf[3];
+
+	if (strlen(in) != 36)
+		return -1;
+	for (i=0, cp = in; i <= 36; i++,cp++) {
+		if ((i == 8) || (i == 13) || (i == 18) ||
+		    (i == 23)) {
+			if (*cp == '-')
+				continue;
+			else
+				return -1;
+		}
+		if (i== 36)
+			if (*cp == 0)
+				continue;
+		if (!isxdigit(*cp))
+			return -1;
+	}
+	uuid.time_low = strtoul(in, NULL, 16);
+	uuid.time_mid = strtoul(in+9, NULL, 16);
+	uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
+	uuid.clock_seq = strtoul(in+19, NULL, 16);
+	cp = in+24;
+	buf[2] = 0;
+	for (i=0; i < 6; i++) {
+		buf[0] = *cp++;
+		buf[1] = *cp++;
+		uuid.node[i] = strtoul(buf, NULL, 16);
+	}
+
+	uuid_pack(&uuid, uu);
+	return 0;
+}
diff --git a/libblkid/libuuid/src/test_uuid.c b/libblkid/libuuid/src/test_uuid.c
new file mode 100644
index 0000000..e03138f
--- /dev/null
+++ b/libblkid/libuuid/src/test_uuid.c
@@ -0,0 +1,180 @@
+/*
+ * tst_uuid.c --- test program from the UUID library
+ *
+ * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#define UUID MYUUID
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "uuid.h"
+
+static int test_uuid(const char * uuid, int isValid)
+{
+	static const char * validStr[2] = {"invalid", "valid"};
+	uuid_t uuidBits;
+	int parsedOk;
+
+	parsedOk = uuid_parse(uuid, uuidBits) == 0;
+
+	printf("%s is %s", uuid, validStr[isValid]);
+	if (parsedOk != isValid) {
+		printf(" but uuid_parse says %s\n", validStr[parsedOk]);
+		return 1;
+	}
+	printf(", OK\n");
+	return 0;
+}
+
+#ifdef __GNUC__
+#define ATTR(x) __attribute__(x)
+#else
+#define ATTR(x)
+#endif
+
+int
+main(int argc ATTR((unused)) , char **argv ATTR((unused)))
+{
+	uuid_t		buf, tst;
+	char		str[100];
+	struct timeval	tv;
+	time_t		time_reg;
+	unsigned char	*cp;
+	int i;
+	int failed = 0;
+	int type, variant;
+
+	uuid_generate(buf);
+	uuid_unparse(buf, str);
+	printf("UUID generate = %s\n", str);
+	printf("UUID: ");
+	for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
+		printf("%02x", *cp++);
+	}
+	printf("\n");
+	type = uuid_type(buf); 	variant = uuid_variant(buf);
+	printf("UUID type = %d, UUID variant = %d\n", type, variant);
+	if (variant != UUID_VARIANT_DCE) {
+		printf("Incorrect UUID Variant; was expecting DCE!\n");
+		failed++;
+	}
+	printf("\n");
+
+	uuid_generate_random(buf);
+	uuid_unparse(buf, str);
+	printf("UUID random string = %s\n", str);
+	printf("UUID: ");
+	for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
+		printf("%02x", *cp++);
+	}
+	printf("\n");
+	type = uuid_type(buf); 	variant = uuid_variant(buf);
+	printf("UUID type = %d, UUID variant = %d\n", type, variant);
+	if (variant != UUID_VARIANT_DCE) {
+		printf("Incorrect UUID Variant; was expecting DCE!\n");
+		failed++;
+	}
+	if (type != 4) {
+		printf("Incorrect UUID type; was expecting "
+		       "4 (random type)!\n");
+		failed++;
+	}
+	printf("\n");
+
+	uuid_generate_time(buf);
+	uuid_unparse(buf, str);
+	printf("UUID string = %s\n", str);
+	printf("UUID time: ");
+	for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
+		printf("%02x", *cp++);
+	}
+	printf("\n");
+	type = uuid_type(buf); 	variant = uuid_variant(buf);
+	printf("UUID type = %d, UUID variant = %d\n", type, variant);
+	if (variant != UUID_VARIANT_DCE) {
+		printf("Incorrect UUID Variant; was expecting DCE!\n");
+		failed++;
+	}
+	if (type != 1) {
+		printf("Incorrect UUID type; was expecting "
+		       "1 (time-based type)!\\n");
+		failed++;
+	}
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+	time_reg = uuid_time(buf, &tv);
+	printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
+	       ctime(&time_reg));
+	uuid_parse(str, tst);
+	if (!uuid_compare(buf, tst))
+		printf("UUID parse and compare succeeded.\n");
+	else {
+		printf("UUID parse and compare failed!\n");
+		failed++;
+	}
+	uuid_clear(tst);
+	if (uuid_is_null(tst))
+		printf("UUID clear and is null succeeded.\n");
+	else {
+		printf("UUID clear and is null failed!\n");
+		failed++;
+	}
+	uuid_copy(buf, tst);
+	if (!uuid_compare(buf, tst))
+		printf("UUID copy and compare succeeded.\n");
+	else {
+		printf("UUID copy and compare failed!\n");
+		failed++;
+	}
+	failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981b", 1);
+	failed += test_uuid("84949CC5-4701-4A84-895B-354C584A981B", 1);
+	failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981bc", 0);
+	failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981", 0);
+	failed += test_uuid("84949cc5x4701-4a84-895b-354c584a981b", 0);
+	failed += test_uuid("84949cc504701-4a84-895b-354c584a981b", 0);
+	failed += test_uuid("84949cc5-470104a84-895b-354c584a981b", 0);
+	failed += test_uuid("84949cc5-4701-4a840895b-354c584a981b", 0);
+	failed += test_uuid("84949cc5-4701-4a84-895b0354c584a981b", 0);
+	failed += test_uuid("g4949cc5-4701-4a84-895b-354c584a981b", 0);
+	failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981g", 0);
+
+	if (failed) {
+		printf("%d failures.\n", failed);
+		exit(1);
+	}
+	return 0;
+}
diff --git a/libblkid/libuuid/src/unpack.c b/libblkid/libuuid/src/unpack.c
new file mode 100644
index 0000000..beaaff3
--- /dev/null
+++ b/libblkid/libuuid/src/unpack.c
@@ -0,0 +1,63 @@
+/*
+ * Internal routine for unpacking UUID
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_unpack(const uuid_t in, struct uuid *uu)
+{
+	const uint8_t	*ptr = in;
+	uint32_t		tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->time_low = tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->time_mid = tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->time_hi_and_version = tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->clock_seq = tmp;
+
+	memcpy(uu->node, ptr, 6);
+}
+
diff --git a/libblkid/libuuid/src/unparse.c b/libblkid/libuuid/src/unparse.c
new file mode 100644
index 0000000..a95bbb0
--- /dev/null
+++ b/libblkid/libuuid/src/unparse.c
@@ -0,0 +1,76 @@
+/*
+ * unparse.c -- convert a UUID to string
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "uuidP.h"
+
+static const char *fmt_lower =
+	"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
+
+static const char *fmt_upper =
+	"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X";
+
+#ifdef UUID_UNPARSE_DEFAULT_UPPER
+#define FMT_DEFAULT fmt_upper
+#else
+#define FMT_DEFAULT fmt_lower
+#endif
+
+static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt)
+{
+	struct uuid uuid;
+
+	uuid_unpack(uu, &uuid);
+	sprintf(out, fmt,
+		uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+		uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+		uuid.node[0], uuid.node[1], uuid.node[2],
+		uuid.node[3], uuid.node[4], uuid.node[5]);
+}
+
+void uuid_unparse_lower(const uuid_t uu, char *out)
+{
+	uuid_unparse_x(uu, out,	fmt_lower);
+}
+
+void uuid_unparse_upper(const uuid_t uu, char *out)
+{
+	uuid_unparse_x(uu, out,	fmt_upper);
+}
+
+void uuid_unparse(const uuid_t uu, char *out)
+{
+	uuid_unparse_x(uu, out, FMT_DEFAULT);
+}
diff --git a/libblkid/libuuid/src/uuid.h b/libblkid/libuuid/src/uuid.h
new file mode 100644
index 0000000..30bd4c0
--- /dev/null
+++ b/libblkid/libuuid/src/uuid.h
@@ -0,0 +1,104 @@
+/*
+ * Public include file for the UUID library
+ *
+ * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifndef _UUID_UUID_H
+#define _UUID_UUID_H
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+typedef unsigned char uuid_t[16];
+
+/* UUID Variant definitions */
+#define UUID_VARIANT_NCS	0
+#define UUID_VARIANT_DCE	1
+#define UUID_VARIANT_MICROSOFT	2
+#define UUID_VARIANT_OTHER	3
+
+/* UUID Type definitions */
+#define UUID_TYPE_DCE_TIME   1
+#define UUID_TYPE_DCE_RANDOM 4
+
+/* Allow UUID constants to be defined */
+#ifdef __GNUC__
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+	static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#else
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+	static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* clear.c */
+extern void uuid_clear(uuid_t uu);
+
+/* compare.c */
+extern int uuid_compare(const uuid_t uu1, const uuid_t uu2);
+
+/* copy.c */
+extern void uuid_copy(uuid_t dst, const uuid_t src);
+
+/* gen_uuid.c */
+extern void uuid_generate(uuid_t out);
+extern void uuid_generate_random(uuid_t out);
+extern void uuid_generate_time(uuid_t out);
+extern int uuid_generate_time_safe(uuid_t out);
+
+/* isnull.c */
+extern int uuid_is_null(const uuid_t uu);
+
+/* parse.c */
+extern int uuid_parse(const char *in, uuid_t uu);
+
+/* unparse.c */
+extern void uuid_unparse(const uuid_t uu, char *out);
+extern void uuid_unparse_lower(const uuid_t uu, char *out);
+extern void uuid_unparse_upper(const uuid_t uu, char *out);
+
+/* uuid_time.c */
+extern time_t uuid_time(const uuid_t uu, struct timeval *ret_tv);
+extern int uuid_type(const uuid_t uu);
+extern int uuid_variant(const uuid_t uu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UUID_UUID_H */
diff --git a/libblkid/libuuid/src/uuidP.h b/libblkid/libuuid/src/uuidP.h
new file mode 100644
index 0000000..86a5e26
--- /dev/null
+++ b/libblkid/libuuid/src/uuidP.h
@@ -0,0 +1,61 @@
+/*
+ * uuid.h -- private header file for uuids
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "uuid.h"
+
+#define LIBUUID_CLOCK_FILE	"/var/lib/libuuid/clock.txt"
+
+/*
+ * Offset between 15-Oct-1582 and 1-Jan-70
+ */
+#define TIME_OFFSET_HIGH 0x01B21DD2
+#define TIME_OFFSET_LOW  0x13814000
+
+struct uuid {
+	uint32_t	time_low;
+	uint16_t	time_mid;
+	uint16_t	time_hi_and_version;
+	uint16_t	clock_seq;
+	uint8_t	node[6];
+};
+
+
+/*
+ * prototypes
+ */
+void uuid_pack(const struct uuid *uu, uuid_t ptr);
+void uuid_unpack(const uuid_t in, struct uuid *uu);
diff --git a/libblkid/libuuid/src/uuid_time.c b/libblkid/libuuid/src/uuid_time.c
new file mode 100644
index 0000000..f25f5c9
--- /dev/null
+++ b/libblkid/libuuid/src/uuid_time.c
@@ -0,0 +1,171 @@
+/*
+ * uuid_time.c --- Interpret the time field from a uuid.  This program
+ * 	violates the UUID abstraction barrier by reaching into the guts
+ *	of a UUID and interpreting it.
+ *
+ * Copyright (C) 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#define UUID MYUUID
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#include "uuidP.h"
+
+time_t uuid_time(const uuid_t uu, struct timeval *ret_tv)
+{
+	struct timeval		tv;
+	struct uuid		uuid;
+	uint32_t		high;
+	uint64_t		clock_reg;
+
+	uuid_unpack(uu, &uuid);
+
+	high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16);
+	clock_reg = uuid.time_low | ((uint64_t) high << 32);
+
+	clock_reg -= (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
+	tv.tv_sec = clock_reg / 10000000;
+	tv.tv_usec = (clock_reg % 10000000) / 10;
+
+	if (ret_tv)
+		*ret_tv = tv;
+
+	return tv.tv_sec;
+}
+
+int uuid_type(const uuid_t uu)
+{
+	struct uuid		uuid;
+
+	uuid_unpack(uu, &uuid);
+	return ((uuid.time_hi_and_version >> 12) & 0xF);
+}
+
+int uuid_variant(const uuid_t uu)
+{
+	struct uuid		uuid;
+	int			var;
+
+	uuid_unpack(uu, &uuid);
+	var = uuid.clock_seq;
+
+	if ((var & 0x8000) == 0)
+		return UUID_VARIANT_NCS;
+	if ((var & 0x4000) == 0)
+		return UUID_VARIANT_DCE;
+	if ((var & 0x2000) == 0)
+		return UUID_VARIANT_MICROSOFT;
+	return UUID_VARIANT_OTHER;
+}
+
+#ifdef DEBUG
+static const char *variant_string(int variant)
+{
+	switch (variant) {
+	case UUID_VARIANT_NCS:
+		return "NCS";
+	case UUID_VARIANT_DCE:
+		return "DCE";
+	case UUID_VARIANT_MICROSOFT:
+		return "Microsoft";
+	default:
+		return "Other";
+	}
+}
+
+
+int
+main(int argc, char **argv)
+{
+	uuid_t		buf;
+	time_t		time_reg;
+	struct timeval	tv;
+	int		type, variant;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s uuid\n", argv[0]);
+		exit(1);
+	}
+	if (uuid_parse(argv[1], buf)) {
+		fprintf(stderr, "Invalid UUID: %s\n", argv[1]);
+		exit(1);
+	}
+	variant = uuid_variant(buf);
+	type = uuid_type(buf);
+	time_reg = uuid_time(buf, &tv);
+
+	printf("UUID variant is %d (%s)\n", variant, variant_string(variant));
+	if (variant != UUID_VARIANT_DCE) {
+		printf("Warning: This program only knows how to interpret "
+		       "DCE UUIDs.\n\tThe rest of the output is likely "
+		       "to be incorrect!!\n");
+	}
+	printf("UUID type is %d", type);
+	switch (type) {
+	case 1:
+		printf(" (time based)\n");
+		break;
+	case 2:
+		printf(" (DCE)\n");
+		break;
+	case 3:
+		printf(" (name-based)\n");
+		break;
+	case 4:
+		printf(" (random)\n");
+		break;
+	default:
+		printf("\n");
+	}
+	if (type != 1) {
+		printf("Warning: not a time-based UUID, so UUID time "
+		       "decoding will likely not work!\n");
+	}
+	printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
+	       ctime(&time_reg));
+
+	return 0;
+}
+#endif
diff --git a/libblkid/libuuid/src/uuidd.h b/libblkid/libuuid/src/uuidd.h
new file mode 100644
index 0000000..2f70968
--- /dev/null
+++ b/libblkid/libuuid/src/uuidd.h
@@ -0,0 +1,54 @@
+/*
+ * Definitions used by the uuidd daemon
+ *
+ * Copyright (C) 2007 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifndef _UUID_UUIDD_H
+#define _UUID_UUIDD_H
+
+#define UUIDD_DIR		_PATH_LOCALSTATEDIR "/uuidd"
+#define UUIDD_SOCKET_PATH	UUIDD_DIR "/request"
+#define UUIDD_PIDFILE_PATH	UUIDD_DIR "/uuidd.pid"
+#define UUIDD_PATH		"/usr/sbin/uuidd"
+
+#define UUIDD_OP_GETPID			0
+#define UUIDD_OP_GET_MAXOP		1
+#define UUIDD_OP_TIME_UUID		2
+#define UUIDD_OP_RANDOM_UUID		3
+#define UUIDD_OP_BULK_TIME_UUID		4
+#define UUIDD_OP_BULK_RANDOM_UUID	5
+#define UUIDD_MAX_OP			UUIDD_OP_BULK_RANDOM_UUID
+
+extern int __uuid_generate_time(uuid_t out, int *num);
+extern void __uuid_generate_random(uuid_t out, int *num);
+
+#endif /* _UUID_UUID_H */
diff --git a/libblkid/libuuid/uuid.pc.in b/libblkid/libuuid/uuid.pc.in
new file mode 100644
index 0000000..875de19
--- /dev/null
+++ b/libblkid/libuuid/uuid.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@usrlib_execdir@
+includedir=@includedir@
+
+Name: uuid
+Description: Universally unique id library
+Version: @LIBUUID_VERSION@
+Requires:
+Cflags: -I${includedir}/uuid
+Libs: -L${libdir} -luuid
diff --git a/libblkid/samples/.gitignore b/libblkid/samples/.gitignore
new file mode 100644
index 0000000..4efeb62
--- /dev/null
+++ b/libblkid/samples/.gitignore
@@ -0,0 +1,4 @@
+mkfs
+partitions
+superblocks
+topology
diff --git a/libblkid/samples/Makemodule.am b/libblkid/samples/Makemodule.am
new file mode 100644
index 0000000..0ffbf14
--- /dev/null
+++ b/libblkid/samples/Makemodule.am
@@ -0,0 +1,22 @@
+
+check_PROGRAMS += \
+	sample-mkfs \
+	sample-partitions \
+	sample-superblocks \
+	sample-topology
+
+sample_mkfs_SOURCES = libblkid/samples/mkfs.c
+sample_mkfs_LDADD = libblkid.la
+sample_mkfs_CFLAGS = -I$(ul_libblkid_incdir)
+
+sample_partitions_SOURCES = libblkid/samples/partitions.c
+sample_partitions_LDADD = libblkid.la
+sample_partitions_CFLAGS = -I$(ul_libblkid_incdir)
+
+sample_superblocks_SOURCES = libblkid/samples/superblocks.c
+sample_superblocks_LDADD = libblkid.la
+sample_superblocks_CFLAGS = -I$(ul_libblkid_incdir)
+
+sample_topology_SOURCES = libblkid/samples/topology.c
+sample_topology_LDADD = libblkid.la
+sample_topology_CFLAGS = -I$(ul_libblkid_incdir)
diff --git a/libblkid/samples/mkfs.c b/libblkid/samples/mkfs.c
new file mode 100644
index 0000000..5c3ebe7
--- /dev/null
+++ b/libblkid/samples/mkfs.c
@@ -0,0 +1,78 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	char *devname;
+	blkid_probe pr;
+	blkid_topology tp;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device>  "
+			"-- checks based on libblkid for mkfs-like programs.\n",
+			program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	devname = argv[1];
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr)
+		err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+				devname);
+
+	/*
+	 * check Filesystems / Partitions overwrite
+	 */
+
+	/* enable partitions probing (superblocks are enabled by default) */
+	blkid_probe_enable_partitions(pr, TRUE);
+
+	rc = blkid_do_fullprobe(pr);
+	if (rc == -1)
+		errx(EXIT_FAILURE, "%s: blkid_do_fullprobe() failed", devname);
+	else if (rc == 0) {
+		const char *type;
+
+		if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL))
+			errx(EXIT_FAILURE, "%s: appears to contain an existing "
+					"%s superblock", devname, type);
+
+		if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL))
+			errx(EXIT_FAILURE, "%s: appears to contain an partition "
+					"table (%s)", devname, type);
+	}
+
+	/*
+	 * get topology details
+	 */
+	tp = blkid_probe_get_topology(pr);
+	if (!tp)
+		errx(EXIT_FAILURE, "%s: failed to read topology", devname);
+
+
+	/* ... your mkfs.<type> code or so ...
+
+	off = blkid_topology_get_alignment_offset(tp);
+
+	 */
+
+	blkid_free_probe(pr);
+
+	return EXIT_SUCCESS;
+}
diff --git a/libblkid/samples/partitions.c b/libblkid/samples/partitions.c
new file mode 100644
index 0000000..fe0ad48
--- /dev/null
+++ b/libblkid/samples/partitions.c
@@ -0,0 +1,96 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+	int i, nparts;
+	char *devname;
+	blkid_probe pr;
+	blkid_partlist ls;
+	blkid_parttable root_tab;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device|file>  "
+				"-- prints partitions\n",
+				program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	devname = argv[1];
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr)
+		err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+				devname);
+	/* Binary interface */
+	ls = blkid_probe_get_partitions(pr);
+	if (!ls)
+		errx(EXIT_FAILURE, "%s: failed to read partitions\n", devname);
+
+	/*
+	 * Print info about the primary (root) partition table
+	 */
+	root_tab = blkid_partlist_get_table(ls);
+	if (!root_tab)
+		errx(EXIT_FAILURE, "%s: does not contains any "
+				 "known partition table\n", devname);
+
+	printf("size: %jd, sector size: %u, PT: %s, offset: %jd, id=%s\n---\n",
+		blkid_probe_get_size(pr),
+		blkid_probe_get_sectorsize(pr),
+		blkid_parttable_get_type(root_tab),
+		blkid_parttable_get_offset(root_tab),
+		blkid_parttable_get_id(root_tab));
+
+	/*
+	 * List partitions
+	 */
+	nparts = blkid_partlist_numof_partitions(ls);
+	if (!nparts)
+		goto done;
+
+	for (i = 0; i < nparts; i++) {
+		const char *p;
+		blkid_partition par = blkid_partlist_get_partition(ls, i);
+		blkid_parttable tab = blkid_partition_get_table(par);
+
+		printf("#%d: %10llu %10llu  0x%x",
+			blkid_partition_get_partno(par),
+			(unsigned long long) blkid_partition_get_start(par),
+			(unsigned long long) blkid_partition_get_size(par),
+			blkid_partition_get_type(par));
+
+		if (root_tab != tab)
+			/* subpartition (BSD, Minix, ...) */
+			printf(" (%s)", blkid_parttable_get_type(tab));
+
+		p = blkid_partition_get_name(par);
+		if (p)
+			printf(" name='%s'", p);
+		p = blkid_partition_get_uuid(par);
+		if (p)
+			printf(" uuid='%s'", p);
+		p = blkid_partition_get_type_string(par);
+		if (p)
+			printf(" type='%s'", p);
+
+		putc('\n', stdout);
+	}
+
+done:
+	blkid_free_probe(pr);
+	return EXIT_SUCCESS;
+}
diff --git a/libblkid/samples/superblocks.c b/libblkid/samples/superblocks.c
new file mode 100644
index 0000000..20e39c9
--- /dev/null
+++ b/libblkid/samples/superblocks.c
@@ -0,0 +1,67 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	char *devname;
+	blkid_probe pr;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device>  "
+				"-- prints superblocks details about the device\n",
+				program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	devname = argv[1];
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr)
+		err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+				devname);
+
+	/* enable topology probing */
+	blkid_probe_enable_superblocks(pr, TRUE);
+
+	/* set all flags */
+	blkid_probe_set_superblocks_flags(pr,
+			BLKID_SUBLKS_LABEL | BLKID_SUBLKS_LABELRAW |
+			BLKID_SUBLKS_UUID | BLKID_SUBLKS_UUIDRAW |
+			BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
+			BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION |
+			BLKID_SUBLKS_MAGIC);
+
+	rc = blkid_do_safeprobe(pr);
+	if (rc == -1)
+		errx(EXIT_FAILURE, "%s: blkid_do_safeprobe() failed", devname);
+	else if (rc == 1)
+		warnx("%s: cannot gather information about superblocks", devname);
+	else {
+		int i, nvals = blkid_probe_numof_values(pr);
+
+		for (i = 0; i < nvals; i++) {
+			const char *name, *data;
+
+			blkid_probe_get_value(pr, i, &name, &data, NULL);
+			printf("\t%s = %s\n", name, data);
+		}
+	}
+
+	blkid_free_probe(pr);
+	return EXIT_SUCCESS;
+}
diff --git a/libblkid/samples/topology.c b/libblkid/samples/topology.c
new file mode 100644
index 0000000..de1c3a5
--- /dev/null
+++ b/libblkid/samples/topology.c
@@ -0,0 +1,86 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	char *devname;
+	blkid_probe pr;
+	blkid_topology tp;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device>  "
+				"-- prints topology details about the device\n",
+				program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	devname = argv[1];
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr)
+		err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+				devname);
+	/*
+	 * Binary interface
+	 */
+	tp = blkid_probe_get_topology(pr);
+	if (tp) {
+		printf("----- binary interface:\n");
+		printf("\talignment offset     : %lu\n",
+				blkid_topology_get_alignment_offset(tp));
+		printf("\tminimum io size      : %lu\n",
+				blkid_topology_get_minimum_io_size(tp));
+		printf("\toptimal io size      : %lu\n",
+				blkid_topology_get_optimal_io_size(tp));
+		printf("\tlogical sector size  : %lu\n",
+				blkid_topology_get_logical_sector_size(tp));
+		printf("\tphysical sector size : %lu\n",
+				blkid_topology_get_physical_sector_size(tp));
+	}
+
+	/*
+	 * NAME=value interface
+	 */
+
+	/* enable topology probing */
+	blkid_probe_enable_topology(pr, TRUE);
+
+	/* disable superblocks probing (enabled by default) */
+	blkid_probe_enable_superblocks(pr, FALSE);
+
+	rc = blkid_do_fullprobe(pr);
+	if (rc == -1)
+		errx(EXIT_FAILURE, "%s: blkid_do_fullprobe() failed", devname);
+	else if (rc == 1)
+		warnx("%s: missing topology information", devname);
+	else {
+		int i, nvals = blkid_probe_numof_values(pr);
+
+		printf("----- NAME=value interface (values: %d):\n", nvals);
+
+		for (i = 0; i < nvals; i++) {
+			const char *name, *data;
+
+			blkid_probe_get_value(pr, i, &name, &data, NULL);
+			printf("\t%s = %s\n", name, data);
+		}
+	}
+
+	blkid_free_probe(pr);
+	return EXIT_SUCCESS;
+}
diff --git a/libblkid/src/.gitignore b/libblkid/src/.gitignore
new file mode 100644
index 0000000..af34f58
--- /dev/null
+++ b/libblkid/src/.gitignore
@@ -0,0 +1 @@
+blkid.h
diff --git a/libblkid/src/bitops.h b/libblkid/src/bitops.h
new file mode 100644
index 0000000..7439ece
--- /dev/null
+++ b/libblkid/src/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/src/blkdev.h b/libblkid/src/blkdev.h
new file mode 100644
index 0000000..ade0887
--- /dev/null
+++ b/libblkid/src/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/src/blkid.h.in b/libblkid/src/blkid.h.in
new file mode 100644
index 0000000..4f5fe2a
--- /dev/null
+++ b/libblkid/src/blkid.h.in
@@ -0,0 +1,414 @@
+/*
+ * 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>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BLKID_VERSION   "@LIBBLKID_VERSION@"
+#define BLKID_DATE      "@LIBBLKID_DATE@"
+
+/**
+ * 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_init_debug(int mask);
+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);
+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);
+
+/* 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);
+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);
+
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+					 const char *type,
+					 const char *value);
+
+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));
+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));
+
+extern dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr)
+			__ul_attribute__((nonnull));
+
+extern int blkid_probe_is_wholedisk(blkid_probe pr)
+			__ul_attribute__((nonnull));
+
+extern blkid_loff_t blkid_probe_get_size(blkid_probe pr);
+extern blkid_loff_t blkid_probe_get_offset(blkid_probe pr);
+extern unsigned int blkid_probe_get_sectorsize(blkid_probe pr);
+extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr);
+
+extern int blkid_probe_get_fd(blkid_probe pr);
+
+/*
+ * superblocks probing
+ */
+extern int blkid_known_fstype(const char *fstype);
+
+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_BADCSUM	(1 << 10) /* allow a bad checksum */
+
+#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);
+
+extern unsigned long blkid_topology_get_alignment_offset(blkid_topology tp)
+			__ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp)
+			__ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp)
+			__ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp)
+			__ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp)
+			__ul_attribute__((nonnull));
+
+/*
+ * partitions probing
+ */
+extern int blkid_known_pttype(const char *pttype);
+
+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);
+
+extern int blkid_partlist_numof_partitions(blkid_partlist ls);
+extern blkid_parttable blkid_partlist_get_table(blkid_partlist ls);
+extern blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n);
+extern blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n);
+extern blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno);
+extern blkid_parttable blkid_partition_get_table(blkid_partition par);
+
+extern const char *blkid_partition_get_name(blkid_partition par);
+extern const char *blkid_partition_get_uuid(blkid_partition par);
+extern int blkid_partition_get_partno(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_start(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_size(blkid_partition par);
+
+extern int blkid_partition_get_type(blkid_partition par)
+			__ul_attribute__((nonnull));
+
+extern const char *blkid_partition_get_type_string(blkid_partition par);
+
+extern unsigned long long blkid_partition_get_flags(blkid_partition par)
+			__ul_attribute__((nonnull));
+
+extern int blkid_partition_is_logical(blkid_partition par)
+			__ul_attribute__((nonnull));
+extern int blkid_partition_is_extended(blkid_partition par)
+			__ul_attribute__((nonnull));
+extern int blkid_partition_is_primary(blkid_partition par)
+			__ul_attribute__((nonnull));
+
+extern const char *blkid_parttable_get_type(blkid_parttable tab);
+extern const char *blkid_parttable_get_id(blkid_parttable tab);
+
+extern blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab);
+extern blkid_partition blkid_parttable_get_parent(blkid_parttable tab);
+
+/*
+ * 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);
+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));
+
+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/src/blkidP.h b/libblkid/src/blkidP.h
new file mode 100644
index 0000000..fbf4e71
--- /dev/null
+++ b/libblkid/src/blkidP.h
@@ -0,0 +1,545 @@
+/*
+ * 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
+
+/* 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 "debug.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 */
+#define BLKID_FL_NOSCAN_DEV	(1 << 4)	/* do not scan this device */
+
+/* private per-probing flags */
+#define BLKID_PROBE_FL_IGNORE_PT (1 << 1)	/* ignore partition table */
+
+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_PROBE_OK	 0
+#define BLKID_PROBE_NONE 1
+
+#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
+
+#define BLKID_DEBUG_HELP	(1 << 0)
+#define BLKID_DEBUG_INIT	(1 << 1)
+#define BLKID_DEBUG_CACHE	(1 << 2)
+#define BLKID_DEBUG_CONFIG	(1 << 3)
+#define BLKID_DEBUG_DEV		(1 << 4)
+#define BLKID_DEBUG_DEVNAME	(1 << 5)
+#define BLKID_DEBUG_DEVNO	(1 << 6)
+#define BLKID_DEBUG_EVALUATE	(1 << 7)
+#define BLKID_DEBUG_LOWPROBE	(1 << 8)
+#define BLKID_DEBUG_PROBE	(1 << 9)
+#define BLKID_DEBUG_READ	(1 << 10)
+#define BLKID_DEBUG_SAVE	(1 << 11)
+#define BLKID_DEBUG_TAG		(1 << 12)
+#define BLKID_DEBUG_ALL		0xFFFF		/* (1 << 16) aka FFFF is expected by API */
+
+UL_DEBUG_DECLARE_MASK(libblkid);
+#define DBG(m, x)	__UL_DBG(libblkid, BLKID_DEBUG_, m, x)
+#define ON_DBG(m, x)    __UL_DBG_CALL(libblkid, BLKID_DEBUG_, m, x)
+
+extern void blkid_debug_dump_dev(blkid_dev dev);
+extern void blkid_debug_dump_tag(blkid_tag tag);
+
+
+/* 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 int blkid_probe_verify_csum(blkid_probe pr, uint64_t csum, uint64_t expected)
+			__attribute__((nonnull));
+
+extern void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len)
+			__attribute__((nonnull));
+extern int blkid_uuid_is_empty(const unsigned char *buf, size_t len);
+
+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/src/c.h b/libblkid/src/c.h
new file mode 100644
index 0000000..6fcbc6d
--- /dev/null
+++ b/libblkid/src/c.h
@@ -0,0 +1,295 @@
+/*
+ * 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 cmp_numbers
+# define cmp_numbers(x, y) __extension__ ({     \
+        __typeof__(x) _a = (x);                 \
+        __typeof__(y) _b = (y);                 \
+        (void) (&_a == &_b);                    \
+        _a == _b ? 0 : _a > _b ? 1 : -1; })
+#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>
+
+#ifdef O_CLOEXEC
+#define UL_CLOEXECSTR   "e"
+#else
+#define UL_CLOEXECSTR   ""
+#endif
+
+#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(255);
+
+	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/src/cache.c b/libblkid/src/cache.c
new file mode 100644
index 0000000..b576df8
--- /dev/null
+++ b/libblkid/src/cache.c
@@ -0,0 +1,226 @@
+/*
+ * 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"
+
+/**
+ * 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.
+ */
+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(CACHE, ul_debug("creating blkid cache (using %s)",
+				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(CACHE, ul_debug("freeing cache struct"));
+
+	/* DBG(CACHE, ul_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(CACHE, ul_debug("warning: unfreed tag %s=%s",
+						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(CACHE, ul_debug("freeing %s", dev->bid_name));
+			blkid_free_dev(dev);
+			cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+		} else {
+			DBG(CACHE, ul_debug("Device %s exists", dev->bid_name));
+		}
+	}
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(BLKID_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/src/canonicalize.h b/libblkid/src/canonicalize.h
new file mode 100644
index 0000000..7a18aca
--- /dev/null
+++ b/libblkid/src/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/src/closestream.h b/libblkid/src/closestream.h
new file mode 100644
index 0000000..7842456
--- /dev/null
+++ b/libblkid/src/closestream.h
@@ -0,0 +1,71 @@
+#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 == EPIPE))
+			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);
+}
+
+#ifndef HAVE_FSYNC
+static inline int
+fsync(int fd __attribute__((__unused__)))
+{
+	return 0;
+}
+#endif
+
+static inline int
+close_fd(int fd)
+{
+	const int fsync_fail = (fsync(fd) != 0);
+	const int close_fail = (close(fd) != 0);
+
+	if (fsync_fail || close_fail)
+		return EOF;
+	return 0;
+}
+
+#endif /* UTIL_LINUX_CLOSESTREAM_H */
diff --git a/libblkid/src/colors.h b/libblkid/src/colors.h
new file mode 100644
index 0000000..97efc48
--- /dev/null
+++ b/libblkid/src/colors.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@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_BROWN		"\033[33m"	/* well, brown */
+#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"
+
+/* --color[=WHEN] */
+enum colortmode {
+	UL_COLORMODE_AUTO = 0,
+	UL_COLORMODE_NEVER,
+	UL_COLORMODE_ALWAYS,
+	UL_COLORMODE_UNDEF,
+
+	__UL_NCOLORMODES	/* last */
+};
+
+extern int colormode_from_string(const char *str);
+extern int colormode_or_err(const char *str, const char *errmsg);
+
+/* Initialize the global variable UL_COLOR_TERM_OK */
+extern int colors_init(int mode, const char *util_name);
+
+/* Returns 1 or 0 */
+extern int colors_wanted(void);
+
+/* temporary enable/disable colors */
+extern void colors_off(void);
+extern void colors_on(void);
+
+
+/* Set the color */
+extern void color_fenable(const char *seq, FILE *f);
+
+extern void color_scheme_fenable(const char *name, const char *dflt, FILE *f);
+extern const char *color_scheme_get_sequence(const char *name, const char *dflt);
+
+static inline void color_enable(const char *seq)
+{
+	color_fenable(seq, stdout);
+}
+
+static inline void color_scheme_enable(const char *name, const char *dflt)
+{
+	color_scheme_fenable(name, dflt, stdout);
+}
+
+/* Reset colors to default */
+extern void color_fdisable(FILE *f);
+
+static inline void color_disable(void)
+{
+	color_fdisable(stdout);
+}
+
+/* converts "red" to UL_COLOR_RED, etc. */
+extern const char *color_sequence_from_colorname(const char *str);
+
+
+#endif /* UTIL_LINUX_COLORS_H */
diff --git a/libblkid/src/config.c b/libblkid/src/config.c
new file mode 100644
index 0000000..3c7f312
--- /dev/null
+++ b/libblkid/src/config.c
@@ -0,0 +1,198 @@
+/*
+ * 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(CONFIG, ul_debug(
+		"config file: unknown evaluation method '%s'.", 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(CONFIG, ul_debug(
+					"config file: missing newline at line '%s'.",
+					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(CONFIG, ul_debug(
+			"config file: unknown option '%s'.", 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(CONFIG, ul_debug("reading config file: %s.", filename));
+
+	f = fopen(filename, "r" UL_CLOEXECSTR);
+	if (!f) {
+		DBG(CONFIG, ul_debug("%s: does not exist, using built-in default", filename));
+		goto dflt;
+	}
+	while (!feof(f)) {
+		if (parse_next(f, conf)) {
+			DBG(CONFIG, ul_debug("%s: parse error", 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(BLKID_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/src/crc32.h b/libblkid/src/crc32.h
new file mode 100644
index 0000000..2616910
--- /dev/null
+++ b/libblkid/src/crc32.h
@@ -0,0 +1,10 @@
+#ifndef UL_NG_CRC32_H
+#define UL_NG_CRC32_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len);
+
+#endif
+
diff --git a/libblkid/src/debug.h b/libblkid/src/debug.h
new file mode 100644
index 0000000..0229ab3
--- /dev/null
+++ b/libblkid/src/debug.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_DEBUG_H
+#define UTIL_LINUX_DEBUG_H
+
+
+/*
+ * util-linux debug macros
+ *
+ * The debug stuff is based on <name>_debug_mask that controls what outputs is
+ * expected. The mask is usually initialized by <NAME>_DEBUG= env.variable
+ *
+ * After successful initialization the flag <PREFIX>_DEBUG_INIT is always set
+ * to the mask (this flag is required). The <PREFIX> is usually library API
+ * prefix (e.g. MNT_) or program name (e.g. CFDISK_)
+ *
+ * In the code is possible to use
+ *
+ *	DBG(FOO, ul_debug("this is output for foo"));
+ *
+ * where for the FOO has to be defined <PREFIX>_DEBUG_FOO.
+ *
+ * It's possible to initialize the mask by comma delimited strings with
+ * subsystem names (e.g. "LIBMOUNT_DEBUG=options,tab"). In this case is
+ * necessary to define mask names array. This functionality is optional.
+ *
+ * It's stringly recommended to use UL_* macros to define/declare/use
+ * the debug stuff.
+ *
+ * See disk-utils/cfdisk.c: cfdisk_init_debug()  for programs debug
+ *  or libmount/src/init.c: mnt_init_debug()     for library debug
+ *
+ */
+
+#include <stdarg.h>
+#include <string.h>
+
+struct ul_debug_maskname {
+	const char *name;
+	int mask;
+	const char *help;
+};
+#define UL_DEBUG_EMPTY_MASKNAMES {{ NULL, 0, NULL }}
+#define UL_DEBUG_DEFINE_MASKNAMES(m) static const struct ul_debug_maskname m ## _masknames[]
+#define UL_DEBUG_MASKNAMES(m)	m ## _masknames
+
+#define UL_DEBUG_DEFINE_MASK(m) int m ## _debug_mask
+#define UL_DEBUG_DECLARE_MASK(m) extern UL_DEBUG_DEFINE_MASK(m)
+
+/* p - flag prefix, m - flag postfix */
+#define UL_DEBUG_DEFINE_FLAG(p, m) p ## m
+
+/* l - library name, p - flag prefix, m - flag postfix, x - function */
+#define __UL_DBG(l, p, m, x) \
+	do { \
+		if ((p ## m) & l ## _debug_mask) { \
+			fprintf(stderr, "%d: %s: %8s: ", getpid(), # l, # m); \
+			x; \
+		} \
+	} while (0)
+
+#define __UL_DBG_CALL(l, p, m, x) \
+	do { \
+		if ((p ## m) & l ## _debug_mask) { \
+			x; \
+		} \
+	} while (0)
+
+#define __UL_DBG_FLUSH(l, p) \
+	do { \
+		if (l ## _debug_mask && \
+		    l ## _debug_mask != p ## INIT) { \
+			fflush(stderr); \
+		} \
+	} while (0)
+
+
+#define __UL_INIT_DEBUG(lib, pref, mask, env) \
+	do { \
+		if (lib ## _debug_mask & pref ## INIT) \
+		; \
+		else if (!mask) { \
+			char *str = getenv(# env); \
+			if (str) \
+				lib ## _debug_mask = ul_debug_parse_envmask(lib ## _masknames, str); \
+		} else \
+			lib ## _debug_mask = mask; \
+		lib ## _debug_mask |= pref ## INIT; \
+	} while (0)
+
+
+static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+ul_debug(const char *mesg, ...)
+{
+	va_list ap;
+	va_start(ap, mesg);
+	vfprintf(stderr, mesg, ap);
+	va_end(ap);
+	fputc('\n', stderr);
+}
+
+static inline void __attribute__ ((__format__ (__printf__, 2, 3)))
+ul_debugobj(void *handler, const char *mesg, ...)
+{
+	va_list ap;
+
+	if (handler)
+		fprintf(stderr, "[%p]: ", handler);
+	va_start(ap, mesg);
+	vfprintf(stderr, mesg, ap);
+	va_end(ap);
+	fputc('\n', stderr);
+}
+
+static inline int ul_debug_parse_envmask(
+			const struct ul_debug_maskname flagnames[],
+			const char *mask)
+{
+	int res;
+	char *ptr;
+
+	/* let's check for a numeric mask first */
+	res = strtoul(mask, &ptr, 0);
+
+	/* perhaps it's a comma-separated string? */
+	if (ptr && *ptr && flagnames && flagnames[0].name) {
+		char *msbuf, *ms, *name;
+		res = 0;
+
+		ms = msbuf = strdup(mask);
+		if (!ms)
+			return res;
+
+		while ((name = strtok_r(ms, ",", &ptr))) {
+			const struct ul_debug_maskname *d;
+			ms = ptr;
+
+			for (d = flagnames; d && d->name; d++) {
+				if (strcmp(name, d->name) == 0) {
+					res |= d->mask;
+					break;
+				}
+			}
+			/* nothing else we can do by OR-ing the mask */
+			if (res == 0xffff)
+				break;
+		}
+		free(msbuf);
+	} else if (ptr && strcmp(ptr, "all") == 0)
+		res = 0xffff;
+
+	return res;
+}
+
+static inline void ul_debug_print_masks(
+			const char *env,
+			const struct ul_debug_maskname flagnames[])
+{
+	const struct ul_debug_maskname *d;
+
+	if (!flagnames)
+		return;
+
+	fprintf(stderr, "Available \"%s=<name>[,...]|<mask>\" debug masks:\n",
+			env);
+	for (d = flagnames; d && d->name; d++) {
+		if (!d->help)
+			continue;
+		fprintf(stderr, "   %-8s [0x%04x] : %s\n",
+				d->name, d->mask, d->help);
+	}
+}
+
+#endif /* UTIL_LINUX_DEBUG_H */
diff --git a/libblkid/src/dev.c b/libblkid/src/dev.c
new file mode 100644
index 0000000..9491331
--- /dev/null
+++ b/libblkid/src/dev.c
@@ -0,0 +1,274 @@
+/*
+ * 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(DEV,
+	    ul_debug("  freeing dev %s (%s)", dev->bid_name, dev->bid_type ?
+		   dev->bid_type : "(null)"));
+	DBG(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
+ */
+const char *blkid_dev_devname(blkid_dev dev)
+{
+	return dev ? dev->bid_name : NULL;
+}
+
+void blkid_debug_dump_dev(blkid_dev dev)
+{
+	struct list_head *p;
+
+	if (!dev) {
+		printf("  dev: NULL\n");
+		return;
+	}
+
+	fprintf(stderr, "  dev: name = %s\n", dev->bid_name);
+	fprintf(stderr, "  dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno);
+	fprintf(stderr, "  dev: TIME=\"%ld.%ld\"\n", (long)dev->bid_time, (long)dev->bid_utime);
+	fprintf(stderr, "  dev: PRI=\"%d\"\n", dev->bid_pri);
+	fprintf(stderr, "  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)
+			fprintf(stderr, "    tag: %s=\"%s\"\n", tag->bit_name,
+			       tag->bit_val);
+		else
+			fprintf(stderr, "    tag: NULL\n");
+	}
+}
+
+/*
+ * 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;
+};
+
+blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
+{
+	blkid_dev_iterate iter;
+
+	if (!cache) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	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;
+}
+
+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
+ */
+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;
+}
+
+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/src/devname.c b/libblkid/src/devname.c
new file mode 100644
index 0000000..fdbb5c9
--- /dev/null
+++ b/libblkid/src/devname.c
@@ -0,0 +1,675 @@
+/*
+ * 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(DEVNAME, ul_debug("found devname %s in cache", 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(DEVNAME, ul_debug("opening %s", lvm_device));
+	if ((lvf = fopen(lvm_device, "r" UL_CLOEXECSTR)) == NULL) {
+		DBG(DEVNAME, ul_debug("%s: (%d) %m", 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(DEVNAME, ul_debug("probing LVM devices under %s", 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(DEVNAME, ul_debug("LVM dev %s: devno 0x%04X",
+						  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" UL_CLOEXECSTR);
+	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(DEVNAME, ul_debug("Checking partition %s (%d, %d)",
+					  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++) {
+		DIR		*dir;
+		struct dirent	*iter;
+
+		DBG(DEVNAME, ul_debug("probing UBI volumes under %s",
+					  *dirname));
+
+		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(DEVNAME, ul_debug("UBI vol %s/%s: devno 0x%04X",
+				  *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" UL_CLOEXECSTR);
+	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(DEVNAME, ul_debug("read partition name %s", 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(DEVNAME, ul_debug("partition dev %s, devno 0x%04X",
+				   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(DEVNAME, ul_debug("freeing %s",
+						       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(DEVNAME, ul_debug("whole dev %s, devno 0x%04X",
+				   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) {
+			if (sysfs_read_int(&sysfs, "removable", &removable) != 0)
+				removable = 0;
+			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(PROBE, ul_debug("Begin blkid_probe_all()"));
+	ret = probe_all(cache, 0);
+	if (ret == 0) {
+		cache->bic_time = time(0);
+		cache->bic_flags |= BLKID_BIC_FL_PROBED;
+	}
+	DBG(PROBE, ul_debug("End blkid_probe_all() [rc=%d]", 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(PROBE, ul_debug("Begin blkid_probe_all_new()"));
+	ret = probe_all(cache, 1);
+	DBG(PROBE, ul_debug("End blkid_probe_all_new() [rc=%d]", 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(PROBE, ul_debug("Begin blkid_probe_all_removable()"));
+	ret = probe_all_removable(cache);
+	DBG(PROBE, ul_debug("End blkid_probe_all_removable() [rc=%d]", ret));
+	return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(BLKID_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/src/devno.c b/libblkid/src/devno.c
new file mode 100644
index 0000000..f4a36e4
--- /dev/null
+++ b/libblkid/src/devno.c
@@ -0,0 +1,375 @@
+/*
+ * 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(DEVNO, ul_debug("found 0x%llx at %s", (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(DEVNO, ul_debug("directory %s", 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(DEVNO, ul_debug("blkid: couldn't find devno 0x%04lx",
+			   (unsigned long) devno));
+	} else {
+		DBG(DEVNO, ul_debug("found devno 0x%04llx as %s", (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" UL_CLOEXECSTR);
+	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(DEVNO, ul_debug("major %d %s associated with '%s' driver",
+			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(BLKID_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/src/encode.c b/libblkid/src/encode.c
new file mode 100644
index 0000000..ff57be4
--- /dev/null
+++ b/libblkid/src/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/src/env.h b/libblkid/src/env.h
new file mode 100644
index 0000000..a53d310
--- /dev/null
+++ b/libblkid/src/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/src/evaluate.c b/libblkid/src/evaluate.c
new file mode 100644
index 0000000..ffbe097
--- /dev/null
+++ b/libblkid/src/evaluate.c
@@ -0,0 +1,324 @@
+/*
+ * 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 "closestream.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(EVALUATE, ul_debug("%s: %s verification %s",
+			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(EVALUATE, ul_debug("%s: uevent '%s' requested", 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" UL_CLOEXECSTR);
+	if (f) {
+		rc = 0;
+		if (fputs(action, f) >= 0)
+			rc = 0;
+		if (close_stream(f) != 0)
+			DBG(EVALUATE, ul_debug("write failed: %s", uevent));
+	}
+	DBG(EVALUATE, ul_debug("%s: send uevent %s",
+			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(EVALUATE, ul_debug("evaluating by udev %s=%s", 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(EVALUATE, ul_debug("unsupported token %s", token));
+		return NULL;	/* unsupported tag */
+	}
+
+	len = strlen(dev);
+	if (blkid_encode_string(value, &dev[len], sizeof(dev) - len) != 0)
+		return NULL;
+
+	DBG(EVALUATE, ul_debug("expected udev link: %s", 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(EVALUATE, ul_debug("failed to evaluate by udev"));
+
+	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(EVALUATE, ul_debug("evaluating by blkid scan %s=%s", 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(EVALUATE, ul_debug("evaluating  %s%s%s", 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(EVALUATE, ul_debug("%s=%s evaluated as %s", 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/src/exec_shell.h b/libblkid/src/exec_shell.h
new file mode 100644
index 0000000..a2aa757
--- /dev/null
+++ b/libblkid/src/exec_shell.h
@@ -0,0 +1 @@
+extern void __attribute__((__noreturn__)) exec_shell(void);
diff --git a/libblkid/src/fileutils.h b/libblkid/src/fileutils.h
new file mode 100644
index 0000000..3353f69
--- /dev/null
+++ b/libblkid/src/fileutils.h
@@ -0,0 +1,33 @@
+#ifndef UTIL_LINUX_FILEUTILS
+#define UTIL_LINUX_FILEUTILS
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "c.h"
+
+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+" UL_CLOEXECSTR))) {
+		close(fd);
+		return NULL;
+	}
+	return ret;
+}
+
+extern int get_fd_tabsize(void);
+
+extern int mkdir_p(const char *path, mode_t mode);
+extern char *stripoff_last_component(char *path);
+
+#endif /* UTIL_LINUX_FILEUTILS */
diff --git a/libblkid/src/getsize.c b/libblkid/src/getsize.c
new file mode 100644
index 0000000..abe6ebc
--- /dev/null
+++ b/libblkid/src/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/src/init.c b/libblkid/src/init.c
new file mode 100644
index 0000000..eead6c7
--- /dev/null
+++ b/libblkid/src/init.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008-2013 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: init
+ * @title: Library initialization
+ * @short_description: initialize debuging
+ */
+
+#include <stdarg.h>
+
+#include "blkidP.h"
+
+UL_DEBUG_DEFINE_MASK(libblkid);
+UL_DEBUG_DEFINE_MASKNAMES(libblkid) =
+{
+	{ "all", BLKID_DEBUG_ALL,	"info about all subsystems" },
+	{ "cache", BLKID_DEBUG_CACHE,	"blkid tags cache" },
+	{ "config", BLKID_DEBUG_CONFIG, "config file utils" },
+	{ "dev", BLKID_DEBUG_DEV,       "device utils" },
+	{ "devname", BLKID_DEBUG_DEVNAME, "/proc/partitions evaluation" },
+	{ "devno", BLKID_DEBUG_DEVNO,	"convertions to device name" },
+	{ "evaluate", BLKID_DEBUG_EVALUATE, "tags resolving" },
+	{ "help", BLKID_DEBUG_HELP,	"this help" },
+	{ "lowprobe", BLKID_DEBUG_LOWPROBE, "superblock/raids/partitions probing" },
+	{ "probe", BLKID_DEBUG_PROBE,	"devices verification" },
+	{ "read", BLKID_DEBUG_READ,	"cache parsing" },
+	{ "save", BLKID_DEBUG_SAVE,	"cache writing" },
+	{ "tag", BLKID_DEBUG_TAG,	"tags utils" },
+	{ NULL, 0, NULL }
+};
+
+/**
+ * blkid_init_debug:
+ * @mask: debug mask (0xffff to enable full debuging)
+ *
+ * If the @mask is not specified then this function reads
+ * LIBBLKID_DEBUG environment variable to get the mask.
+ *
+ * Already initialized debugging stuff cannot be changed. It does not
+ * have effect to call this function twice.
+ */
+void blkid_init_debug(int mask)
+{
+	if (libblkid_debug_mask)
+		return;
+
+	__UL_INIT_DEBUG(libblkid, BLKID_DEBUG_, mask, LIBBLKID_DEBUG);
+
+	if (libblkid_debug_mask != BLKID_DEBUG_INIT
+	    && libblkid_debug_mask != (BLKID_DEBUG_HELP|BLKID_DEBUG_INIT)) {
+		const char *ver = NULL;
+		const char *date = NULL;
+
+		blkid_get_library_version(&ver, &date);
+		DBG(INIT, ul_debug("library debug mask: 0x%04x", libblkid_debug_mask));
+		DBG(INIT, ul_debug("library version: %s [%s]", ver, date));
+
+	}
+	ON_DBG(HELP, ul_debug_print_masks("LIBBLKID_DEBUG",
+				UL_DEBUG_MASKNAMES(libblkid)));
+}
diff --git a/libblkid/src/ismounted.h b/libblkid/src/ismounted.h
new file mode 100644
index 0000000..57918cb
--- /dev/null
+++ b/libblkid/src/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/src/libblkid.sym b/libblkid/src/libblkid.sym
new file mode 100644
index 0000000..6b3cf08
--- /dev/null
+++ b/libblkid/src/libblkid.sym
@@ -0,0 +1,166 @@
+/*
+ * The symbol versioning ensures that a new application requiring symbol 'foo'
+ * can't run with old library.so not providing 'foo' - the global SONAME
+ * version info can't enforce this since we never change the SONAME.
+ *
+ * The original libblkid from e2fsprogs (<=1.41.4) does not to use
+ * symbol versioning -- all the original symbols are in BLKID_1.0 now.
+ *
+ * Copyright (C) 2009-2014 Karel Zak <kzak@redhat.com> 
+ */
+BLKID_1.0 {
+global:
+	blkid_dev_devname;
+	blkid_dev_has_tag;
+	blkid_dev_iterate_begin;
+	blkid_dev_iterate_end;
+	blkid_dev_next;
+	blkid_devno_to_devname;
+	blkid_dev_set_search;
+	blkid_find_dev_with_tag;
+	blkid_gc_cache;
+	blkid_get_cache;
+	blkid_get_dev;
+	blkid_get_devname;
+	blkid_get_dev_size;
+	blkid_get_library_version;
+	blkid_get_tag_value;
+	blkid_known_fstype;
+	blkid_parse_tag_string;
+	blkid_parse_version_string;
+	blkid_probe_all;
+	blkid_probe_all_new;
+	blkid_put_cache;
+	blkid_tag_iterate_begin;
+	blkid_tag_iterate_end;
+	blkid_tag_next;
+	blkid_verify;
+local:
+	*;
+};
+
+
+/*
+ * symbols since util-linux 2.15
+ */
+BLKID_2.15 {
+global:
+	blkid_do_probe;
+	blkid_do_safeprobe;
+	blkid_encode_string;
+	blkid_evaluate_tag;
+	blkid_free_probe;
+	blkid_new_probe;
+	blkid_probe_filter_types;
+	blkid_probe_filter_usage;
+	blkid_probe_get_value;
+	blkid_probe_has_value;
+	blkid_probe_invert_filter;
+	blkid_probe_lookup_value;
+	blkid_probe_numof_values;
+	blkid_probe_reset_filter;
+	blkid_probe_set_device;
+	blkid_probe_set_request;
+	blkid_reset_probe;
+	blkid_safe_string;
+	blkid_send_uevent;
+} BLKID_1.0;
+
+/*
+ * symbols since util-linux 2.17
+ */
+BLKID_2.17 {
+global:
+	blkid_devno_to_wholedisk;
+	blkid_do_fullprobe;
+	blkid_known_pttype;
+	blkid_new_probe_from_filename;
+	blkid_partition_get_name;
+	blkid_partition_get_partno;
+	blkid_partition_get_size;
+	blkid_partition_get_start;
+	blkid_partition_get_table;
+	blkid_partition_get_type;
+	blkid_partition_get_type_string;
+	blkid_partition_get_uuid;
+	blkid_partition_is_extended;
+	blkid_partition_is_logical;
+	blkid_partition_is_primary;
+	blkid_partlist_get_partition;
+	blkid_partlist_numof_partitions;
+	blkid_parttable_get_offset;
+	blkid_parttable_get_parent;
+	blkid_parttable_get_type;
+	blkid_probe_enable_partitions;
+	blkid_probe_enable_superblocks;
+	blkid_probe_enable_topology;
+	blkid_probe_filter_partitions_type;
+	blkid_probe_filter_superblocks_type;
+	blkid_probe_filter_superblocks_usage;
+	blkid_probe_get_devno;
+	blkid_probe_get_partitions;
+	blkid_probe_get_sectorsize;
+	blkid_probe_get_sectors;
+	blkid_probe_get_size;
+	blkid_probe_get_topology;
+	blkid_probe_invert_partitions_filter;
+	blkid_probe_invert_superblocks_filter;
+	blkid_probe_reset_partitions_filter;
+	blkid_probe_reset_superblocks_filter;
+	blkid_probe_set_partitions_flags;
+	blkid_probe_set_superblocks_flags;
+	blkid_topology_get_alignment_offset;
+	blkid_topology_get_logical_sector_size;
+	blkid_topology_get_minimum_io_size;
+	blkid_topology_get_optimal_io_size;
+	blkid_topology_get_physical_sector_size;
+} BLKID_2.15;
+
+/*
+ * symbols since util-linux 2.18
+ */
+BLKID_2.18 {
+global:
+	blkid_partition_get_flags;
+	blkid_partlist_devno_to_partition;
+	blkid_partlist_get_table;
+	blkid_probe_all_removable;
+	blkid_probe_get_fd;
+	blkid_probe_get_offset;
+	blkid_probe_get_wholedisk_devno;
+	blkid_probe_is_wholedisk;
+} BLKID_2.17;
+
+/*
+ * symbols since util-linux 2.20
+ */
+BLKID_2.20 {
+global:
+	blkid_evaluate_spec;
+	blkid_superblocks_get_name;
+} BLKID_2.18;
+
+/*
+ * symbols since util-linux 2.21
+ */
+BLKID_2.21 {
+global:
+	blkid_do_wipe;
+} BLKID_2.20;
+
+/*
+ * symbols since util-linux 2.23
+ */
+BLKID_2.23 {
+global:
+	blkid_probe_step_back;
+	blkid_parttable_get_id;
+	blkid_init_debug;
+} BLKID_2.21;
+
+/*
+ * symbols since util-linux 2.25
+ */
+BLKID_2.25 {
+	blkid_partlist_get_partition_by_partno;
+} BLKID_2.23;
diff --git a/libblkid/src/linux_version.h b/libblkid/src/linux_version.h
new file mode 100644
index 0000000..a6a1e99
--- /dev/null
+++ b/libblkid/src/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/src/list.h b/libblkid/src/list.h
new file mode 100644
index 0000000..7b60672
--- /dev/null
+++ b/libblkid/src/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/src/llseek.c b/libblkid/src/llseek.c
new file mode 100644
index 0000000..6733478
--- /dev/null
+++ b/libblkid/src/llseek.c
@@ -0,0 +1,142 @@
+/*
+ * 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__
+
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+
+#define my_llseek lseek64
+
+#elif defined(HAVE_LLSEEK)
+#include <syscall.h>
+
+#ifndef HAVE_LLSEEK_PROTOTYPE
+extern long long llseek(int fd, long long offset, int origin);
+#endif
+
+#define my_llseek llseek
+
+#else	/* ! HAVE_LLSEEK */
+
+#if SIZEOF_LONG == SIZEOF_LONG_LONG
+
+#define llseek lseek
+
+#else /* SIZEOF_LONG != SIZEOF_LONG_LONG */
+
+#include <linux/unistd.h>
+
+#ifndef __NR__llseek
+#define __NR__llseek            140
+#endif
+
+#ifndef __i386__
+static int _llseek(unsigned int, unsigned long, unsigned long,
+		   blkid_loff_t *, unsigned int);
+
+static _syscall5(int, _llseek, unsigned int, fd, unsigned long, offset_high,
+		 unsigned long, offset_low, blkid_loff_t *, result,
+		 unsigned int, origin)
+#endif
+/*
+static blkid_loff_t my_llseek(int fd, blkid_loff_t offset, int origin)
+{
+	blkid_loff_t result;
+	int retval;
+
+#ifndef __i386__
+	retval = _llseek(fd, ((unsigned long long) offset) >> 32,
+			 ((unsigned long long)offset) & 0xffffffff,
+			 &result, origin);
+#else
+	retval = syscall(__NR__llseek, fd, ((unsigned long long) offset) >> 32,
+			 ((unsigned long long)offset) & 0xffffffff,
+			 &result, origin);
+#endif
+	return (retval == -1 ? (blkid_loff_t) retval : result);
+}
+*/
+#endif	/* __alpha__ || __ia64__ */
+
+#endif /* HAVE_LLSEEK */
+
+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;
+	}
+
+	result = lseek64(fd, offset, whence);
+	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/src/loopdev.h b/libblkid/src/loopdev.h
new file mode 100644
index 0000000..573a569
--- /dev/null
+++ b/libblkid/src/loopdev.h
@@ -0,0 +1,193 @@
+#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 ioctl 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	info_failed:1;	/* LOOP_GET_STATUS ioctl failed */
+	unsigned int    control_ok:1;	/* /dev/loop-control success */
+
+	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 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 int loopcxt_add_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);
+extern int loopcxt_set_capacity(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/src/mangle.h b/libblkid/src/mangle.h
new file mode 100644
index 0000000..ec492b5
--- /dev/null
+++ b/libblkid/src/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/src/match.h b/libblkid/src/match.h
new file mode 100644
index 0000000..94440c2
--- /dev/null
+++ b/libblkid/src/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/src/mbsalign.h b/libblkid/src/mbsalign.h
new file mode 100644
index 0000000..5eaf606
--- /dev/null
+++ b/libblkid/src/mbsalign.h
@@ -0,0 +1,56 @@
+/* Align/Truncate a string in a given screen width
+   Copyright (C) 2009-2010 Free Software Foundation, Inc.
+   Copyright (C) 2010-2013 Karel Zak <kzak@redhat.com>
+
+   This program 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 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 UTIL_LINUX_MBSALIGN_H
+# define UTIL_LINUX_MBSALIGN_H
+# 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);
+
+extern size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz);
+extern size_t mbs_safe_width(const char *s);
+
+extern char *mbs_safe_encode(const char *s, size_t *width);
+extern char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf);
+extern size_t mbs_safe_encode_size(size_t bytes);
+
+#endif /* UTIL_LINUX_MBSALIGN_H */
diff --git a/libblkid/src/md5.h b/libblkid/src/md5.h
new file mode 100644
index 0000000..d997e37
--- /dev/null
+++ b/libblkid/src/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/src/nls.h b/libblkid/src/nls.h
new file mode 100644
index 0000000..3eabfe6
--- /dev/null
+++ b/libblkid/src/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/src/partitions/aix.c b/libblkid/src/partitions/aix.c
new file mode 100644
index 0000000..4efdfa3
--- /dev/null
+++ b/libblkid/src/partitions/aix.c
@@ -0,0 +1,57 @@
+/*
+ * 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 BLKID_PROBE_OK;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		return BLKID_PROBE_NONE;
+
+	tab = blkid_partlist_new_parttable(ls, "aix", 0);
+	if (!tab)
+		return -ENOMEM;
+
+	return BLKID_PROBE_OK;
+}
+
+/*
+ * 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 behavior. 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/src/partitions/aix.h b/libblkid/src/partitions/aix.h
new file mode 100644
index 0000000..f767c5a
--- /dev/null
+++ b/libblkid/src/partitions/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/src/partitions/bsd.c b/libblkid/src/partitions/bsd.c
new file mode 100644
index 0000000..d83f2cf
--- /dev/null
+++ b/libblkid/src/partitions/bsd.c
@@ -0,0 +1,179 @@
+/*
+ * 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"
+#include "pt-bsd.h"
+
+/* 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;
+	int rc = BLKID_PROBE_NONE;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return rc;
+
+	data = blkid_probe_get_sector(pr, BLKID_MAG_SECTOR(mag));
+	if (!data) {
+		if (errno)
+			rc = -errno;
+		goto nothing;
+	}
+
+	l = (struct bsd_disklabel *) data + BLKID_MAG_LASTOFFSET(mag);
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto nothing;
+
+	/* 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 MBR_FREEBSD_PARTITION:
+			name = "freebsd";
+			break;
+		case MBR_NETBSD_PARTITION:
+			name = "netbsd";
+			break;
+		case MBR_OPENBSD_PARTITION:
+			name = "openbsd";
+			break;
+		default:
+			DBG(LOWPROBE, ul_debug(
+				"WARNING: BSD label detected on unknown (0x%x) "
+				"primary partition",
+				blkid_partition_get_type(parent)));
+			break;
+		}
+	}
+
+	tab = blkid_partlist_new_parttable(ls, name, BLKID_MAG_OFFSET(mag));
+	if (!tab) {
+		rc = -ENOMEM;
+		goto nothing;
+	}
+
+	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(LOWPROBE, ul_debug(
+			"WARNING: ignore %d more BSD partitions",
+			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_partition_get_start(parent) == start
+			   && blkid_partition_get_size(parent) == size) {
+			DBG(LOWPROBE, ul_debug(
+				"WARNING: BSD partition (%d) same like parent, "
+				"ignore", i));
+			continue;
+		}
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(LOWPROBE, ul_debug(
+				"WARNING: BSD partition (%d) overflow "
+				"detected, ignore", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par) {
+			rc = -ENOMEM;
+			goto nothing;
+		}
+
+		blkid_partition_set_type(par, p->p_fstype);
+	}
+
+	return BLKID_PROBE_OK;
+
+nothing:
+	return rc;
+}
+
+
+/*
+ * 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      |             |
+ * ------------------------+-------------+------------
+ * sparc64 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/src/partitions/dos.c b/libblkid/src/partitions/dos.c
new file mode 100644
index 0000000..2539908
--- /dev/null
+++ b/libblkid/src/partitions/dos.c
@@ -0,0 +1,307 @@
+/*
+ * 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 "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[] = {
+	{ MBR_FREEBSD_PARTITION, &bsd_pt_idinfo },
+	{ MBR_NETBSD_PARTITION, &bsd_pt_idinfo },
+	{ MBR_OPENBSD_PARTITION, &bsd_pt_idinfo },
+	{ MBR_UNIXWARE_PARTITION, &unixware_pt_idinfo },
+	{ MBR_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo },
+	{ MBR_MINIX_PARTITION, &minix_pt_idinfo }
+};
+
+static inline int is_extended(struct dos_partition *p)
+{
+	return (p->sys_ind == MBR_DOS_EXTENDED_PARTITION ||
+		p->sys_ind == MBR_W95_EXTENDED_PARTITION ||
+		p->sys_ind == MBR_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 BLKID_PROBE_OK;
+		data = blkid_probe_get_sector(pr, cur_start);
+		if (!data) {
+			if (errno)
+				return -errno;
+			goto leave;	/* malformed partition? */
+		}
+
+		if (!mbr_is_valid_magic(data))
+			goto leave;
+
+		p0 = mbr_get_partition(data, 0);
+
+		/* 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_get_start(p) * ssf;
+			size = dos_partition_get_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)
+				return -ENOMEM;
+
+			blkid_partition_set_type(par, p->sys_ind);
+			blkid_partition_set_flags(par, p->boot_ind);
+			blkid_partition_gen_uuid(par);
+			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_get_start(p) * ssf;
+			size = dos_partition_get_size(p) * ssf;
+
+			if (size && is_extended(p))
+				break;
+		}
+		if (i == 4)
+			goto leave;
+
+		cur_start = ex_start + start;
+		cur_size = size;
+	}
+leave:
+	return BLKID_PROBE_OK;
+}
+
+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;
+	char idstr[37];
+
+
+	data = blkid_probe_get_sector(pr, 0);
+	if (!data) {
+		if (errno)
+			return -errno;
+		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) == 1) {
+		DBG(LOWPROBE, ul_debug("probably FAT -- ignore"));
+		goto nothing;
+	}
+
+	p0 = mbr_get_partition(data, 0);
+
+	/*
+	 * 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(LOWPROBE, ul_debug("missing boot indicator -- ignore"));
+			goto nothing;
+		}
+
+	/*
+	 * GPT uses valid MBR
+	 */
+	for (p = p0, i = 0; i < 4; i++, p++) {
+		if (p->sys_ind == MBR_GPT_PARTITION) {
+			DBG(LOWPROBE, ul_debug("probably GPT -- ignore"));
+			goto nothing;
+		}
+	}
+
+	blkid_probe_use_wiper(pr, MBR_PT_OFFSET, 512 - MBR_PT_OFFSET);
+
+	id = mbr_get_id(data);
+	if (id)
+		snprintf(idstr, sizeof(idstr), "%08x", id);
+
+	/*
+	 * Well, all checks pass, it's MS-DOS partiton table
+	 */
+	if (blkid_partitions_need_typeonly(pr)) {
+		/* Non-binary interface -- caller does not ask for details
+		 * about partitions, just set generic varibles only. */
+		if (id)
+			blkid_partitions_strcpy_ptuuid(pr, idstr);
+		return 0;
+	}
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto nothing;
+
+	/* 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", MBR_PT_OFFSET);
+	if (!tab)
+		return -ENOMEM;
+
+	if (id)
+		blkid_parttable_set_id(tab, (unsigned char *) idstr);
+
+	/* Parse primary partitions */
+	for (p = p0, i = 0; i < 4; i++, p++) {
+		blkid_partition par;
+
+		start = dos_partition_get_start(p) * ssf;
+		size = dos_partition_get_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)
+			return -ENOMEM;
+
+		blkid_partition_set_type(par, p->sys_ind);
+		blkid_partition_set_flags(par, p->boot_ind);
+		blkid_partition_gen_uuid(par);
+	}
+
+	/* 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_get_start(p) * ssf;
+		size = dos_partition_get_size(p) * ssf;
+
+		if (!size)
+			continue;
+		if (is_extended(p) &&
+		    parse_dos_extended(pr, tab, start, size, ssf) == -1)
+			goto nothing;
+	}
+
+	/* 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;
+			int rc;
+
+			if (!dos_partition_get_size(p) || is_extended(p))
+				continue;
+
+			for (n = 0; n < ARRAY_SIZE(dos_nested); n++) {
+				if (dos_nested[n].type != p->sys_ind)
+					continue;
+
+				rc = blkid_partitions_do_subprobe(pr,
+						blkid_partlist_get_partition(ls, i),
+						dos_nested[n].id);
+				if (rc < 0)
+					return rc;
+				break;
+			}
+		}
+	}
+	return BLKID_PROBE_OK;
+
+nothing:
+	return BLKID_PROBE_NONE;
+}
+
+
+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/src/partitions/gpt.c b/libblkid/src/partitions/gpt.c
new file mode 100644
index 0000000..665577f
--- /dev/null
+++ b/libblkid/src/partitions/gpt.c
@@ -0,0 +1,470 @@
+/*
+ * 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 <limits.h>
+
+#include "partitions.h"
+#include "crc32.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 *has)
+{
+	int flags = blkid_partitions_get_flags(pr);
+	unsigned char *data;
+	struct dos_partition *p;
+	int i;
+
+	if (has)
+		*has = 0;
+	if (flags & BLKID_PARTS_FORCE_GPT)
+		goto ok;			/* skip PMBR check */
+
+	data = blkid_probe_get_sector(pr, 0);
+	if (!data) {
+		if (errno)
+			return -errno;
+		goto failed;
+	}
+
+	if (!mbr_is_valid_magic(data))
+		goto failed;
+
+	for (i = 0, p = mbr_get_partition(data, 0); i < 4; i++, p++) {
+		if (p->sys_ind == MBR_GPT_PARTITION)
+			goto ok;
+	}
+failed:
+	return 0;
+ok:
+	if (has)
+		*has = 1;
+	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(LOWPROBE, ul_debug("GPT header corrupted"));
+		return NULL;
+	}
+
+	/* Valid header has to be at MyLBA */
+	if (le64_to_cpu(h->my_lba) != lba) {
+		DBG(LOWPROBE, ul_debug(
+			"GPT->MyLBA mismatch with real position"));
+		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(LOWPROBE, ul_debug(
+			"GPT->{First,Last}UsableLBA out of range"));
+		return NULL;
+	}
+
+	/* The header has to be outside usable range */
+	if (fu < lba && lba < lu) {
+		DBG(LOWPROBE, ul_debug("GPT header is inside usable area"));
+		return NULL;
+	}
+
+	if (le32_to_cpu(h->num_partition_entries) == 0 ||
+	    le32_to_cpu(h->sizeof_partition_entry) == 0 ||
+	    ULONG_MAX / le32_to_cpu(h->num_partition_entries) < le32_to_cpu(h->sizeof_partition_entry)) {
+		DBG(LOWPROBE, ul_debug("GPT entries undefined"));
+		return NULL;
+	}
+
+	/* Size of blocks with GPT entries */
+	esz = le32_to_cpu(h->num_partition_entries) *
+			le32_to_cpu(h->sizeof_partition_entry);
+
+	/* 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(LOWPROBE, ul_debug("GPT entries unreadable"));
+		return NULL;
+	}
+
+	/* Validate entries */
+	crc = count_crc32((unsigned char *) *ents, esz);
+	if (crc != le32_to_cpu(h->partition_entry_array_crc32)) {
+		DBG(LOWPROBE, ul_debug("GPT entries corrupted"));
+		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;
+	int ret;
+
+	if (last_lba(pr, &lastlba))
+		goto nothing;
+
+	ret = is_pmbr_valid(pr, NULL);
+	if (ret < 0)
+		return ret;
+	else if (ret == 0)
+		goto nothing;
+
+	errno = 0;
+	h = get_gpt_header(pr, &hdr, &e, (lba = GPT_PRIMARY_LBA), lastlba);
+	if (!h && !errno)
+		h = get_gpt_header(pr, &hdr, &e, (lba = lastlba), lastlba);
+
+	if (!h) {
+		if (errno)
+			return -errno;
+		goto nothing;
+	}
+
+	blkid_probe_use_wiper(pr, lba * blkid_probe_get_size(pr), 8);
+
+	if (blkid_probe_set_magic(pr, blkid_probe_get_sectorsize(pr) * lba,
+			      sizeof(GPT_HEADER_SIGNATURE_STR) - 1,
+			      (unsigned char *) GPT_HEADER_SIGNATURE_STR))
+		goto err;
+
+	guid = h->disk_guid;
+	swap_efi_guid(&guid);
+
+	if (blkid_partitions_need_typeonly(pr)) {
+		/* Non-binary interface -- caller does not ask for details
+		 * about partitions, just set generic varibles only. */
+		blkid_partitions_set_ptuuid(pr, (unsigned char *) &guid);
+		return BLKID_PROBE_OK;
+	}
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto nothing;
+
+	tab = blkid_partlist_new_parttable(ls, "gpt",
+				blkid_probe_get_sectorsize(pr) * lba);
+	if (!tab)
+		goto err;
+
+	blkid_parttable_set_uuid(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(LOWPROBE, ul_debug(
+				"GPT entry[%d] overflows usable area - ignore",
+				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, le64_to_cpu(e->attributes));
+	}
+
+	return BLKID_PROBE_OK;
+
+nothing:
+	return BLKID_PROBE_NONE;
+
+err:
+	return -ENOMEM;
+}
+
+
+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
+};
+
+
+
+/* probe for *alone* protective MBR */
+static int probe_pmbr_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	int has = 0;
+	struct gpt_entry *e;
+	uint64_t lastlba = 0;
+	struct gpt_header hdr;
+
+	if (last_lba(pr, &lastlba))
+		goto nothing;
+
+	is_pmbr_valid(pr, &has);
+	if (!has)
+		goto nothing;
+
+	if (!get_gpt_header(pr, &hdr, &e, GPT_PRIMARY_LBA, lastlba) &&
+	    !get_gpt_header(pr, &hdr, &e, lastlba, lastlba))
+		return 0;
+nothing:
+	return 1;
+}
+
+const struct blkid_idinfo pmbr_pt_idinfo =
+{
+	.name		= "PMBR",
+	.probefunc	= probe_pmbr_pt,
+	.magics		=
+	{
+		{ .magic = "\x55\xAA", .len = 2, .sboff = 510 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/src/partitions/mac.c b/libblkid/src/partitions/mac.c
new file mode 100644
index 0000000..4282605
--- /dev/null
+++ b/libblkid/src/partitions/mac.c
@@ -0,0 +1,192 @@
+/*
+ * 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) {
+		if (errno)
+			return -errno;
+		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) {
+		if (errno)
+			return -errno;
+		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 nothing;
+
+	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) {
+			if (errno)
+				return -errno;
+			goto nothing;
+		}
+		if (!has_part_signature(p))
+			goto nothing;
+
+		if (be32_to_cpu(p->map_count) != nblks) {
+			DBG(LOWPROBE, ul_debug(
+				"mac: inconsisten map_count in partition map, "
+				"entry[0]: %d, entry[%d]: %d",
+				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 BLKID_PROBE_OK;
+
+nothing:
+	return BLKID_PROBE_NONE;
+err:
+	return -ENOMEM;
+}
+
+/*
+ * 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/src/partitions/minix.c b/libblkid/src/partitions/minix.c
new file mode 100644
index 0000000..43c9d9a
--- /dev/null
+++ b/libblkid/src/partitions/minix.c
@@ -0,0 +1,102 @@
+/*
+ * 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 "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) {
+		if (errno)
+			return -errno;
+		goto nothing;
+	}
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto nothing;
+
+	/* 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) != MBR_MINIX_PARTITION)
+		goto nothing;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return BLKID_PROBE_OK;
+
+	tab = blkid_partlist_new_parttable(ls, "minix", MBR_PT_OFFSET);
+	if (!tab)
+		goto err;
+
+	for (i = 0, p = mbr_get_partition(data, 0);
+			i < MINIX_MAXPARTITIONS; i++, p++) {
+
+		uint32_t start, size;
+		blkid_partition par;
+
+		if (p->sys_ind != MBR_MINIX_PARTITION)
+			continue;
+
+		start = dos_partition_get_start(p);
+		size = dos_partition_get_size(p);
+
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(LOWPROBE, ul_debug(
+				"WARNING: minix partition (%d) overflow "
+				"detected, ignore", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, p->sys_ind);
+		blkid_partition_set_flags(par, p->boot_ind);
+	}
+
+	return BLKID_PROBE_OK;
+
+nothing:
+	return BLKID_PROBE_NONE;
+err:
+	return -ENOMEM;
+}
+
+/* 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/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c
new file mode 100644
index 0000000..4853f97
--- /dev/null
+++ b/libblkid/src/partitions/partitions.c
@@ -0,0 +1,1512 @@
+/*
+ * 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.).
+ *
+ * @PTUUID: partition table id (uuid for gpt, hex for dos).
+
+ * @PART_ENTRY_SCHEME: partition table type
+ *
+ * @PART_ENTRY_NAME: partition name (gpt and mac only)
+ *
+ * @PART_ENTRY_UUID: partition UUID (gpt, or pseudo IDs for MBR)
+ *
+ * @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,
+	&pmbr_pt_idinfo,	/* always after GPT */
+	&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 (in bytes) */
+	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 (512-bytes sectors) */
+	blkid_loff_t	size;		/* size of the partitions (512-bytes sectors) */
+
+	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(LOWPROBE, ul_debug("partlist reset"));
+}
+
+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(LOWPROBE, ul_debug("parts: initialized partitions list (%p, size=%d)",
+		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(LOWPROBE, ul_debug("parts: create a new partition table "
+		       "(%p, type=%s, offset=%"PRId64")", 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.
+		 */
+		void *tmp = realloc(ls->parts, (ls->nparts_max + 32) *
+					sizeof(struct blkid_struct_partition));
+		if (!tmp)
+			return NULL;
+		ls->parts = tmp;
+		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(LOWPROBE, ul_debug("parts: add partition (%p start=%"
+		PRId64 ", size=%" PRId64 ", table=%p)",
+		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 = BLKID_PROBE_NONE;		/* default is nothing */
+
+	if (pr->size <= 0 || (id->minsz && id->minsz > pr->size))
+		goto nothing;	/* the device is too small */
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		goto nothing;
+
+	rc = blkid_probe_get_idmag(pr, id, &off, &mag);
+	if (rc != BLKID_PROBE_OK)
+		goto nothing;
+
+	/* final check by probing function */
+	if (id->probefunc) {
+		DBG(LOWPROBE, ul_debug(
+			"%s: ---> call probefunc()", id->name));
+		rc = id->probefunc(pr, mag);
+		if (rc < 0) {
+			/* reset after error */
+			reset_partlist(blkid_probe_get_partlist(pr));
+			if (chn && !chn->binary)
+				blkid_probe_chain_reset_vals(pr, chn);
+			DBG(LOWPROBE, ul_debug("%s probefunc failed, rc %d",
+						  id->name, rc));
+		}
+		if (rc == BLKID_PROBE_OK && mag && chn && !chn->binary)
+			rc = blkid_probe_set_magic(pr, off, mag->len,
+					(unsigned char *) mag->magic);
+
+		DBG(LOWPROBE, ul_debug("%s: <--- (rc = %d)", id->name, rc));
+	}
+
+	return rc;
+
+nothing:
+	return BLKID_PROBE_NONE;
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int partitions_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+	int rc = BLKID_PROBE_NONE;
+	size_t i;
+
+	if (!pr || chn->idx < -1)
+		return -EINVAL;
+
+	blkid_probe_chain_reset_vals(pr, chn);
+
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		return BLKID_PROBE_NONE;
+
+	if (chn->binary)
+		partitions_init_data(chn);
+
+	if (!pr->wipe_size && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_PT))
+		goto details_only;
+
+	DBG(LOWPROBE, ul_debug("--> starting probing loop [PARTS idx=%d]",
+		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 */
+		rc = idinfo_probe(pr, idinfos[i], chn);
+		if (rc < 0)
+			break;
+		if (rc != BLKID_PROBE_OK)
+			continue;
+
+		name = idinfos[i]->name;
+
+		if (!chn->binary)
+			/*
+			 * Non-binary interface, set generic variables. Note
+			 * that the another variables could be set in prober
+			 * functions.
+			 */
+			blkid_probe_set_value(pr, "PTTYPE",
+						(unsigned char *) name,
+						strlen(name) + 1);
+
+		DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [PARTS idx=%d]",
+			name, chn->idx));
+		rc = BLKID_PROBE_OK;
+		break;
+	}
+
+	if (rc != BLKID_PROBE_OK) {
+		DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed=%d) [PARTS idx=%d]",
+			rc, chn->idx));
+	}
+
+details_only:
+	/*
+	 * Gather PART_ENTRY_* values if the current device is a partition.
+	 */
+	if ((rc == BLKID_PROBE_OK || rc == BLKID_PROBE_NONE) && !chn->binary &&
+	    (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) {
+
+		int xrc = blkid_partitions_probe_partition(pr);
+
+		/* partition entry probing is optional, and "not-found" from
+		 * this sub-probing must not to overwrite previous success. */
+		if (xrc < 0)
+			rc = xrc;			/* always propagate errors */
+		else if (rc == BLKID_PROBE_NONE)
+			rc = xrc;
+	}
+
+	DBG(LOWPROBE, ul_debug("partitions probe done [rc=%d]",	rc));
+	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;
+	blkid_partlist ls;
+	blkid_loff_t sz, off;
+
+	DBG(LOWPROBE, ul_debug(
+		"parts: ----> %s subprobe requested (parent=%p)",
+		id->name, parent));
+
+	if (!pr || !parent || !parent->size)
+		return -EINVAL;
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		return BLKID_PROBE_NONE;
+
+	/* 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(LOWPROBE, ul_debug(
+			"ERROR: parts: <---- '%s' subprobe: overflow detected.",
+			id->name));
+		return -ENOSPC;
+	}
+
+	/* create private prober */
+	prc = blkid_clone_probe(pr);
+	if (!prc)
+		return -ENOMEM;
+
+	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(LOWPROBE, ul_debug(
+		"parts: <---- %s subprobe done (parent=%p, rc=%d)",
+		id->name, parent, rc));
+
+	return rc;
+}
+
+static int blkid_partitions_probe_partition(blkid_probe pr)
+{
+	blkid_probe disk_pr = NULL;
+	blkid_partlist ls;
+	blkid_partition par;
+	dev_t devno;
+
+	DBG(LOWPROBE, ul_debug("parts: start probing for partition entry"));
+
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		goto nothing;
+
+	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)
+		goto nothing;
+	else {
+		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));
+	}
+
+	DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [success]"));
+	return BLKID_PROBE_OK;
+
+nothing:
+	DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [nothing]"));
+	return BLKID_PROBE_NONE;
+
+
+}
+
+/*
+ * 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 = NULL;
+	blkid_partlist ls = NULL;
+	blkid_loff_t start, end;
+	int nparts, i, rc = 0;
+
+	DBG(LOWPROBE, ul_debug(
+		"=> checking if off=%jd size=%jd covered by PT",
+		offset, size));
+
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		goto done;
+
+	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(LOWPROBE, ul_debug("partition #%d overflows "
+				"device (off=%" PRId64 " size=%" PRId64 ")",
+				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(LOWPROBE, ul_debug("<= %s covered by PT", 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_get_partition_by_partno
+ * @ls: partitions list
+ * @n: the partition number (e.g. 'N' from sda'N')
+ *
+ * This does not assume any order of the input blkid_partlist.  And correctly
+ * handles "out of order" partition tables.  partition N is located after
+ * partition N+1 on the disk.
+ *
+ * Returns: partition object or NULL in case or error.
+ */
+blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n)
+{
+	int i, nparts;
+	blkid_partition par;
+
+	if (!ls)
+		return NULL;
+
+	nparts = blkid_partlist_numof_partitions(ls);
+	for (i = 0; i < nparts; i++) {
+		par = blkid_partlist_get_partition(ls, i);
+		if (n == blkid_partition_get_partno(par))
+			return par;
+	}
+	return NULL;
+}
+
+
+/**
+ * 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(LOWPROBE, ul_debug("triyng to convert devno 0x%llx to partition",
+			(long long) devno));
+
+	if (sysfs_init(&sysfs, devno, NULL)) {
+		DBG(LOWPROBE, ul_debug("failed t init sysfs context"));
+		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(LOWPROBE, ul_debug("mapped by DM, using partno %d", 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(LOWPROBE, ul_debug("searching by offset/size"));
+
+	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(LOWPROBE, ul_debug("not found partition for device"));
+	return NULL;
+}
+
+
+int blkid_parttable_set_uuid(blkid_parttable tab, const unsigned char *id)
+{
+	if (!tab)
+		return -1;
+
+	blkid_unparse_uuid(id, tab->id, sizeof(tab->id));
+	return 0;
+}
+
+int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id)
+{
+	if (!tab)
+		return -1;
+
+	strncpy(tab->id, (const char *) id, sizeof(tab->id));
+	return 0;
+}
+
+/* set PTUUID variable for non-binary API */
+int blkid_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+
+	if (chn->binary || blkid_uuid_is_empty(uuid, 16))
+		return 0;
+
+	v = blkid_probe_assign_value(pr, "PTUUID");
+
+	blkid_unparse_uuid(uuid, (char *) v->data, sizeof(v->data));
+	v->len = 37;
+
+	return 0;
+}
+
+/* set PTUUID variable for non-binary API for tables where
+ * the ID is just a string */
+int blkid_partitions_strcpy_ptuuid(blkid_probe pr, char *str)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+	size_t len;
+
+	if (chn->binary || !str || !*str)
+		return 0;
+
+	len = strlen((char *) str);
+	if (len > BLKID_PROBVAL_BUFSIZ)
+		len = BLKID_PROBVAL_BUFSIZ;
+
+	v = blkid_probe_assign_value(pr, "PTUUID");
+	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;
+}
+
+/**
+ * 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 : 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 == MBR_DOS_EXTENDED_PARTITION ||
+                   par->type == MBR_W95_EXTENDED_PARTITION ||
+		   par->type == MBR_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;
+}
+
+int blkid_partition_gen_uuid(blkid_partition par)
+{
+	if (!par || !par->tab || !*par->tab->id)
+		return -1;
+
+	snprintf(par->uuid, sizeof(par->uuid), "%s-%02x",
+			par->tab->id, par->partno);
+	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/src/partitions/partitions.h b/libblkid/src/partitions/partitions.h
new file mode 100644
index 0000000..3651bbb
--- /dev/null
+++ b/libblkid/src/partitions/partitions.h
@@ -0,0 +1,71 @@
+#ifndef BLKID_PARTITIONS_H
+#define BLKID_PARTITIONS_H
+
+#include "blkidP.h"
+#include "pt-mbr.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_uuid(blkid_parttable tab, const unsigned char *id);
+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_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid);
+extern int blkid_partitions_strcpy_ptuuid(blkid_probe pr, char *str);
+
+
+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_gen_uuid(blkid_partition par);
+
+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 pmbr_pt_idinfo;
+extern const struct blkid_idinfo ultrix_pt_idinfo;
+
+#endif /* BLKID_PARTITIONS_H */
diff --git a/libblkid/src/partitions/sgi.c b/libblkid/src/partitions/sgi.c
new file mode 100644
index 0000000..99c0bf1
--- /dev/null
+++ b/libblkid/src/partitions/sgi.c
@@ -0,0 +1,87 @@
+/*
+ * 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"
+#include "pt-sgi.h"
+
+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) {
+		if (errno)
+			return -errno;
+		goto nothing;
+	}
+
+	if (sgi_pt_checksum(l)) {
+		DBG(LOWPROBE, ul_debug(
+			"detected corrupted sgi disk label -- ignore"));
+		goto nothing;
+	}
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return BLKID_PROBE_OK;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto nothing;
+
+	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) {
+			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 BLKID_PROBE_OK;
+
+nothing:
+	return BLKID_PROBE_NONE;
+err:
+	return -ENOMEM;
+}
+
+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/src/partitions/solaris_x86.c b/libblkid/src/partitions/solaris_x86.c
new file mode 100644
index 0000000..4ac9be5
--- /dev/null
+++ b/libblkid/src/partitions/solaris_x86.c
@@ -0,0 +1,154 @@
+/*
+ * 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) {
+		if (errno)
+			return -errno;
+		goto nothing;
+	}
+
+	if (le32_to_cpu(l->v_version) != 1) {
+		DBG(LOWPROBE, ul_debug(
+			"WARNING: unsupported solaris x86 version %d, ignore",
+			le32_to_cpu(l->v_version)));
+		goto nothing;
+	}
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return BLKID_PROBE_OK;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto nothing;
+
+	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(LOWPROBE, ul_debug(
+				"WARNING: solaris partition (%d) overflow "
+				"detected, ignore", 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 BLKID_PROBE_OK;
+
+nothing:
+	return BLKID_PROBE_NONE;
+err:
+	return -ENOMEM;
+}
+
+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/src/partitions/sun.c b/libblkid/src/partitions/sun.c
new file mode 100644
index 0000000..22ee450
--- /dev/null
+++ b/libblkid/src/partitions/sun.c
@@ -0,0 +1,125 @@
+/*
+ * 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 "pt-sun.h"
+#include "partitions.h"
+
+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) {
+		if (errno)
+			return -errno;
+		goto nothing;
+	}
+
+	if (sun_pt_checksum(l)) {
+		DBG(LOWPROBE, ul_debug(
+			"detected corrupted sun disk label -- ignore"));
+		goto nothing;
+	}
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return BLKID_PROBE_OK;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto nothing;
+
+	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->nhead) * be16_to_cpu(l->nsect);
+
+	DBG(LOWPROBE, ul_debug("Sun VTOC sanity=%u version=%u nparts=%u",
+			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 BLKID_PROBE_OK;
+
+nothing:
+	return BLKID_PROBE_NONE;
+err:
+	return -ENOMEM;
+}
+
+
+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/src/partitions/ultrix.c b/libblkid/src/partitions/ultrix.c
new file mode 100644
index 0000000..9c060be
--- /dev/null
+++ b/libblkid/src/partitions/ultrix.c
@@ -0,0 +1,99 @@
+/*
+ * 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) {
+		if (errno)
+			return -errno;
+		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 BLKID_PROBE_OK;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto nothing;
+
+	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 BLKID_PROBE_OK;
+nothing:
+	return BLKID_PROBE_NONE;
+err:
+	return -ENOMEM;
+}
+
+const struct blkid_idinfo ultrix_pt_idinfo =
+{
+	.name		= "ultrix",
+	.probefunc	= probe_ultrix_pt,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/partitions/unixware.c b/libblkid/src/partitions/unixware.c
new file mode 100644
index 0000000..6742bcf
--- /dev/null
+++ b/libblkid/src/partitions/unixware.c
@@ -0,0 +1,197 @@
+/*
+ * 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) {
+		if (errno)
+			return -errno;
+		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 BLKID_PROBE_OK;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto nothing;
+
+	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(LOWPROBE, ul_debug(
+				"WARNING: unixware partition (%d) overflow "
+				"detected, ignore", 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 BLKID_PROBE_OK;
+
+nothing:
+	return BLKID_PROBE_NONE;
+err:
+	return -ENOMEM;
+}
+
+
+/*
+ * 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/src/pathnames.h b/libblkid/src/pathnames.h
new file mode 100644
index 0000000..0d21b98
--- /dev/null
+++ b/libblkid/src/pathnames.h
@@ -0,0 +1,196 @@
+/*
+ * 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"
+
+#define _PATH_NOLOGIN_TXT	"/etc/nologin.txt"
+
+#ifndef _PATH_MAILDIR
+#define	_PATH_MAILDIR		"/var/spool/mail"
+#endif
+#define	_PATH_MOTDFILE		"/etc/motd"
+#define	_PATH_NOLOGIN		"/etc/nologin"
+#define	_PATH_VAR_NOLOGIN	"/var/run/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"
+
+#define _PATH_TERMCOLORS_DIRNAME "terminal-colors.d"
+#define _PATH_TERMCOLORS_DIR	"/etc/" _PATH_TERMCOLORS_DIRNAME
+
+/* 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_OS_RELEASE	"/etc/os-release"
+#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_UIDMAP	"/proc/self/uid_map"
+#define _PATH_PROC_GIDMAP	"/proc/self/gid_map"
+
+#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_MEM		"/dev/mem"
+
+#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 */
+#ifdef CONFIG_ADJTIME_PATH
+# define _PATH_ADJTIME		CONFIG_ADJTIME_PATH
+#else
+# define _PATH_ADJTIME		"/etc/adjtime"
+#endif
+
+#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/src/probe.c b/libblkid/src/probe.c
new file mode 100644
index 0000000..76ac099
--- /dev/null
+++ b/libblkid/src/probe.c
@@ -0,0 +1,1834 @@
+/*
+ * 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>
+#include <limits.h>
+
+#ifdef HAVE_LIBUUID
+# include <uuid.h>
+#endif
+
+#include "blkidP.h"
+#include <blkid.h>
+#include "all-io.h"
+#include "sysfs.h"
+#include "strutils.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(LOWPROBE, ul_debug("allocate a new probe %p", 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(LOWPROBE, ul_debug("allocate a probe clone"));
+
+	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(LOWPROBE, ul_debug("free probe %p", 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;
+}
+
+static const char *blkid_probe_get_probername(blkid_probe pr)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	if (chn && chn->idx >= 0 && chn->idx < chn->driver->nidinfos)
+		return chn->driver->idinfos[chn->idx]->name;
+
+	return NULL;
+}
+
+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(LOWPROBE, ul_debug("returning %s binary data", 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(LOWPROBE, ul_debug("%d: %s: %s",
+			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(LOWPROBE, ul_debug("probing filter inverted"));
+	/* 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(LOWPROBE, ul_debug("%s: a new probing type-filter initialized",
+		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) {
+		errno = EINVAL;
+		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(LOWPROBE, ul_debug("\treuse buffer: off=%jd len=%jd pr=%p",
+							x->off, x->len, pr));
+			bf = x;
+			break;
+		}
+	}
+	if (!bf) {
+		ssize_t ret;
+
+		if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0) {
+			errno = 0;
+			return NULL;
+		}
+
+		/* someone trying to overflow some buffers? */
+		if (len > ULONG_MAX - sizeof(struct blkid_bufinfo)) {
+			errno = ENOMEM;
+			return NULL;
+		}
+
+		/* allocate info and space for data by why call */
+		bf = calloc(1, sizeof(struct blkid_bufinfo) + len);
+		if (!bf) {
+			errno = ENOMEM;
+			return NULL;
+		}
+
+		bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo);
+		bf->len = len;
+		bf->off = off;
+		INIT_LIST_HEAD(&bf->bufs);
+
+		DBG(LOWPROBE, ul_debug("\tbuffer read: off=%jd len=%jd pr=%p",
+				off, len, pr));
+
+		ret = read(pr->fd, bf->data, len);
+		if (ret != (ssize_t) len) {
+			DBG(LOWPROBE, ul_debug("\tbuffer read: return %zd error %m", ret));
+			free(bf);
+			if (ret >= 0)
+				errno = 0;
+			return NULL;
+		}
+		list_add_tail(&bf->bufs, &pr->buffers);
+	}
+
+	errno = 0;
+	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(LOWPROBE, ul_debug("reseting probing buffers pr=%p", 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(LOWPROBE, ul_debug("buffers summary: %"PRIu64" bytes "
+			"by %"PRIu64" read() call(s)",
+			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(LOWPROBE, ul_debug("failed to get device size"));
+				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;
+
+	if (S_ISBLK(sb.st_mode) && sysfs_devno_is_lvm_private(sb.st_rdev)) {
+		DBG(LOWPROBE, ul_debug("ignore private LVM device"));
+		pr->flags |= BLKID_FL_NOSCAN_DEV;
+	}
+
+#ifdef CDROM_GET_CAPABILITY
+	else 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(LOWPROBE, ul_debug("ready for low-probing, offset=%jd, size=%jd",
+				pr->off, pr->size));
+	DBG(LOWPROBE, ul_debug("whole-disk: %s, regfile: %s",
+		blkid_probe_is_wholedisk(pr) ?"YES" : "NO",
+		S_ISREG(pr->mode) ? "YES" : "NO"));
+
+	return 0;
+err:
+	DBG(LOWPROBE, ul_debug("failed to prepare a device for low-probing"));
+	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(LOWPROBE, ul_debug(
+		"changing probing area pr=%p: size=%llu, off=%llu "
+		"-to-> size=%llu, off=%llu",
+		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;
+}
+
+/*
+ * Check for matching magic value.
+ * Returns BLKID_PROBE_OK if found, BLKID_PROBE_NONE if not found
+ * or no magic present, or negative value on error.
+ */
+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 && errno)
+			return -errno;
+		if (buf && !memcmp(mag->magic,
+				buf + (mag->sboff & 0x3ff), mag->len)) {
+			DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%ld",
+				mag->sboff, mag->kboff));
+			if (offset)
+				*offset = off + (mag->sboff & 0x3ff);
+			if (res)
+				*res = mag;
+			return BLKID_PROBE_OK;
+		}
+		mag++;
+	}
+
+	if (id && id->magics[0].magic)
+		/* magic string(s) defined, but not found */
+		return BLKID_PROBE_NONE;
+
+	return BLKID_PROBE_OK;
+}
+
+static inline void blkid_probe_start(blkid_probe pr)
+{
+	if (pr) {
+		DBG(LOWPROBE, ul_debug("%p: start probe", 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(LOWPROBE, ul_debug("%p: end probe", 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;
+
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		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(LOWPROBE, ul_debug("chain probe %s %s (idx=%d)",
+				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 (if you want to errase also superblock with broken check
+ * sums then use BLKID_SUBLKS_BADCSUM too).
+ *
+ * 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(LOWPROBE, ul_debug(
+	    "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);
+ *
+ *      blkid_probe_enable_partitions(pr, 1);
+ *      blkid_probe_set_partitions_flags(pr, BLKID_PARTS_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(LOWPROBE, ul_debug("step back: moving %s chain index to %d",
+			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(LOWPROBE, ul_debug("step back: moving to previous chain"));
+
+		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;
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		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(LOWPROBE, ul_debug("chain safeprobe %s %s",
+				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;
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		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(LOWPROBE, ul_debug("chain fullprobe %s: %s",
+				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(LOWPROBE, ul_debug("assigning %s [%s]", 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(LOWPROBE, ul_debug("un-assigning %s [%s]", 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;
+}
+
+int blkid_probe_verify_csum(blkid_probe pr, uint64_t csum, uint64_t expected)
+{
+	if (csum != expected) {
+		struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+		DBG(LOWPROBE, ul_debug(
+				"incorrect checksum for type %s,"
+				" got %jX, expected %jX",
+				blkid_probe_get_probername(pr),
+				csum, expected));
+		/*
+		 * Accept bad checksum if BLKID_SUBLKS_BADCSUM flags is set
+		 */
+		if (chn->driver->id == BLKID_CHAIN_SUBLKS
+		    && (chn->flags & BLKID_SUBLKS_BADCSUM)) {
+			blkid_probe_set_value(pr, "SBBADCSUM", (unsigned char *) "1", 2);
+			goto accept;
+		}
+		return 0;	/* bad checksum */
+	}
+
+accept:
+	return 1;
+}
+
+/**
+ * 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(LOWPROBE, ul_debug("allocate a wholedisk probe"));
+
+		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(LOWPROBE, ul_debug("returning %s value", 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(LOWPROBE, ul_debug("returning %s value", 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
+
+/* like uuid_is_null() from libuuid, but works with arbitrary size of UUID */
+int blkid_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;
+}
+
+/* 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)
+{
+	return rtrim_whitespace(str);
+}
+
+/* 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)
+{
+	return ltrim_whitespace(str);
+}
+
+/*
+ * 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(LOWPROBE, ul_debug("zeroize wiper"));
+		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(LOWPROBE,
+		ul_debug("wiper set to %s::%s off=%jd size=%jd",
+			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(LOWPROBE, ul_debug("previously wiped area modified "
+				       " -- ignore previous results"));
+		blkid_probe_set_wiper(pr, 0, 0);
+		blkid_probe_chain_reset_vals(pr, chn);
+	}
+}
diff --git a/libblkid/src/pt-bsd.h b/libblkid/src/pt-bsd.h
new file mode 100644
index 0000000..9bf47a5
--- /dev/null
+++ b/libblkid/src/pt-bsd.h
@@ -0,0 +1,156 @@
+#ifndef UTIL_LINUX_PT_BSD_H
+#define UTIL_LINUX_PT_BSD_H
+
+#define BSD_MAXPARTITIONS	16
+#define BSD_FS_UNUSED		0
+
+#ifndef BSD_DISKMAGIC
+# define BSD_DISKMAGIC     ((uint32_t) 0x82564557)
+#endif
+
+#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
+
+#if defined (__alpha__) || defined (__powerpc__) || \
+    defined (__ia64__) || defined (__hppa__)
+# define BSD_LABELSECTOR   0
+# define BSD_LABELOFFSET   64
+#else
+# define BSD_LABELSECTOR   1
+# define BSD_LABELOFFSET   0
+#endif
+
+#define	BSD_BBSIZE        8192		/* size of boot area, with label */
+#define	BSD_SBSIZE        8192		/* max size of fs superblock */
+
+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));
+
+
+/* d_type values: */
+#define	BSD_DTYPE_SMD		1		/* SMD, XSMD; VAX hp/up */
+#define	BSD_DTYPE_MSCP		2		/* MSCP */
+#define	BSD_DTYPE_DEC		3		/* other DEC (rk, rl) */
+#define	BSD_DTYPE_SCSI		4		/* SCSI */
+#define	BSD_DTYPE_ESDI		5		/* ESDI interface */
+#define	BSD_DTYPE_ST506		6		/* ST506 etc. */
+#define	BSD_DTYPE_HPIB		7		/* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL		8		/* HP Fiber-link */
+#define	BSD_DTYPE_FLOPPY	10		/* floppy */
+
+/* d_subtype values: */
+#define BSD_DSTYPE_INDOSPART	0x8		/* is inside dos partition */
+#define BSD_DSTYPE_DOSPART(s)	((s) & 3)	/* dos partition number */
+#define BSD_DSTYPE_GEOMETRY	0x10		/* drive params in label */
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define	BSD_FS_UNUSED	0		/* unused */
+#define	BSD_FS_SWAP    	1		/* swap */
+#define	BSD_FS_V6      	2		/* Sixth Edition */
+#define	BSD_FS_V7      	3		/* Seventh Edition */
+#define	BSD_FS_SYSV    	4		/* System V */
+#define	BSD_FS_V71K    	5		/* V7 with 1K blocks (4.1, 2.9) */
+#define	BSD_FS_V8      	6		/* Eighth Edition, 4K blocks */
+#define	BSD_FS_BSDFFS	7		/* 4.2BSD fast file system */
+#define	BSD_FS_BSDLFS	9		/* 4.4BSD log-structured file system */
+#define	BSD_FS_OTHER	10		/* in use, but unknown/unsupported */
+#define	BSD_FS_HPFS	11		/* OS/2 high-performance file system */
+#define	BSD_FS_ISO9660	12		/* ISO-9660 filesystem (cdrom) */
+#define BSD_FS_ISOFS	BSD_FS_ISO9660
+#define	BSD_FS_BOOT	13		/* partition contains bootstrap */
+#define BSD_FS_ADOS	14		/* AmigaDOS fast file system */
+#define BSD_FS_HFS	15		/* Macintosh HFS */
+#define BSD_FS_ADVFS	16		/* Digital Unix AdvFS */
+
+/* this is annoying, but it's also the way it is :-( */
+#ifdef __alpha__
+#define	BSD_FS_EXT2	8		/* ext2 file system */
+#else
+#define	BSD_FS_MSDOS	8		/* MS-DOS file system */
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define	BSD_D_REMOVABLE	0x01		/* removable media */
+#define	BSD_D_ECC      	0x02		/* supports ECC */
+#define	BSD_D_BADSECT	0x04		/* supports bad sector forw. */
+#define	BSD_D_RAMDISK	0x08		/* disk emulator */
+#define	BSD_D_CHAIN    	0x10		/* can do back-back transfers */
+#define	BSD_D_DOSPART	0x20		/* within MSDOS partition */
+
+#endif /* UTIL_LINUX_PT_BSD_H */
diff --git a/libblkid/src/pt-mbr.h b/libblkid/src/pt-mbr.h
new file mode 100644
index 0000000..1279e3c
--- /dev/null
+++ b/libblkid/src/pt-mbr.h
@@ -0,0 +1,178 @@
+#ifndef UTIL_LINUX_PT_MBR_H
+#define UTIL_LINUX_PT_MBR_H
+
+struct dos_partition {
+	unsigned char boot_ind;		/* 0x80 - active */
+	unsigned char bh, bs, bc;	/* begin CHS */
+	unsigned char sys_ind;
+	unsigned char eh, es, ec;	/* end CHS */
+	unsigned char start_sect[4];
+	unsigned char nr_sects[4];
+} __attribute__((packed));
+
+#define MBR_PT_OFFSET		0x1be
+
+static inline struct dos_partition *mbr_get_partition(unsigned char *mbr, int i)
+{
+	return (struct dos_partition *)
+		(mbr + MBR_PT_OFFSET + (i * sizeof(struct dos_partition)));
+}
+
+/* assemble badly aligned little endian integer */
+static inline unsigned int __dos_assemble_4le(const unsigned char *p)
+{
+	return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+static inline void __dos_store_4le(unsigned char *p, unsigned int val)
+{
+	p[0] = (val & 0xff);
+	p[1] = ((val >> 8) & 0xff);
+	p[2] = ((val >> 16) & 0xff);
+	p[3] = ((val >> 24) & 0xff);
+}
+
+static inline unsigned int dos_partition_get_start(struct dos_partition *p)
+{
+	return __dos_assemble_4le(&(p->start_sect[0]));
+}
+
+static inline void dos_partition_set_start(struct dos_partition *p, unsigned int n)
+{
+	__dos_store_4le(p->start_sect, n);
+}
+
+static inline unsigned int dos_partition_get_size(struct dos_partition *p)
+{
+	return __dos_assemble_4le(&(p->nr_sects[0]));
+}
+
+static inline void dos_partition_set_size(struct dos_partition *p, unsigned int n)
+{
+	__dos_store_4le(p->nr_sects, n);
+}
+
+static inline int mbr_is_valid_magic(const unsigned char *mbr)
+{
+	return mbr[510] == 0x55 && mbr[511] == 0xaa ? 1 : 0;
+}
+
+static inline void mbr_set_magic(unsigned char *b)
+{
+	b[510] = 0x55;
+	b[511] = 0xaa;
+}
+
+static inline unsigned int mbr_get_id(const unsigned char *mbr)
+{
+	return __dos_assemble_4le(&mbr[440]);
+}
+
+static inline void mbr_set_id(unsigned char *b, unsigned int id)
+{
+	__dos_store_4le(&b[440], id);
+}
+
+enum {
+	MBR_EMPTY_PARTITION		= 0x00,
+	MBR_FAT12_PARTITION		= 0x01,
+	MBR_XENIX_ROOT_PARTITION	= 0x02,
+	MBR_XENIX_USR_PARTITION		= 0x03,
+	MBR_FAT16_LESS32M_PARTITION	= 0x04,
+	MBR_DOS_EXTENDED_PARTITION	= 0x05,
+	MBR_FAT16_PARTITION		= 0x06, /* DOS 16-bit >=32M */
+	MBR_HPFS_NTFS_PARTITION		= 0x07, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+	MBR_AIX_PARTITION		= 0x08, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+	MBR_AIX_BOOTABLE_PARTITION	= 0x09, /* AIX data or Coherent */
+	MBR_OS2_BOOTMNGR_PARTITION	= 0x0a, /* OS/2 Boot Manager */
+	MBR_W95_FAT32_PARTITION		= 0x0b,
+	MBR_W95_FAT32_LBA_PARTITION	= 0x0c, /* LBA really is `Extended Int 13h' */
+	MBR_W95_FAT16_LBA_PARTITION	= 0x0e,
+	MBR_W95_EXTENDED_PARTITION	= 0x0f,
+	MBR_OPUS_PARTITION		= 0x10,
+	MBR_HIDDEN_FAT12_PARTITION	= 0x11,
+	MBR_COMPAQ_DIAGNOSTICS_PARTITION = 0x12,
+	MBR_HIDDEN_FAT16_L32M_PARTITION	= 0x14,
+	MBR_HIDDEN_FAT16_PARTITION	= 0x16,
+	MBR_HIDDEN_HPFS_NTFS_PARTITION	= 0x17,
+	MBR_AST_SMARTSLEEP_PARTITION	= 0x18,
+	MBR_HIDDEN_W95_FAT32_PARTITION	= 0x1b,
+	MBR_HIDDEN_W95_FAT32LBA_PARTITION = 0x1c,
+	MBR_HIDDEN_W95_FAT16LBA_PARTITION = 0x1e,
+	MBR_NEC_DOS_PARTITION		= 0x24,
+	MBR_PLAN9_PARTITION		= 0x39,
+	MBR_PARTITIONMAGIC_PARTITION	= 0x3c,
+	MBR_VENIX80286_PARTITION	= 0x40,
+	MBR_PPC_PREP_BOOT_PARTITION	= 0x41,
+	MBR_SFS_PARTITION		= 0x42,
+	MBR_QNX_4X_PARTITION		= 0x4d,
+	MBR_QNX_4X_2ND_PARTITION	= 0x4e,
+	MBR_QNX_4X_3RD_PARTITION	= 0x4f,
+	MBR_DM_PARTITION		= 0x50,
+	MBR_DM6_AUX1_PARTITION		= 0x51, /* (or Novell) */
+	MBR_CPM_PARTITION		= 0x52, /* CP/M or Microport SysV/AT */
+	MBR_DM6_AUX3_PARTITION		= 0x53,
+	MBR_DM6_PARTITION		= 0x54,
+	MBR_EZ_DRIVE_PARTITION		= 0x55,
+	MBR_GOLDEN_BOW_PARTITION	= 0x56,
+	MBR_PRIAM_EDISK_PARTITION	= 0x5c,
+	MBR_SPEEDSTOR_PARTITION		= 0x61,
+	MBR_GNU_HURD_PARTITION		= 0x63, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+	MBR_UNIXWARE_PARTITION		= MBR_GNU_HURD_PARTITION,
+	MBR_NETWARE_286_PARTITION	= 0x64,
+	MBR_NETWARE_386_PARTITION	= 0x65,
+	MBR_DISKSECURE_MULTIBOOT_PARTITION = 0x70,
+	MBR_PC_IX_PARTITION		= 0x75,
+	MBR_OLD_MINIX_PARTITION		= 0x80, /* Minix 1.4a and earlier */
+	MBR_MINIX_PARTITION		= 0x81, /* Minix 1.4b and later */
+	MBR_LINUX_SWAP_PARTITION	= 0x82,
+	MBR_SOLARIS_X86_PARTITION	= MBR_LINUX_SWAP_PARTITION,
+	MBR_LINUX_DATA_PARTITION	= 0x83,
+	MBR_OS2_HIDDEN_DRIVE_PARTITION	= 0x84,
+	MBR_LINUX_EXTENDED_PARTITION	= 0x85,
+	MBR_NTFS_VOL_SET1_PARTITION	= 0x86,
+	MBR_NTFS_VOL_SET2_PARTITION	= 0x87,
+	MBR_LINUX_PLAINTEXT_PARTITION	= 0x88,
+	MBR_LINUX_LVM_PARTITION		= 0x8e,
+	MBR_AMOEBA_PARTITION		= 0x93,
+	MBR_AMOEBA_BBT_PARTITION	= 0x94, /* (bad block table) */
+	MBR_BSD_OS_PARTITION		= 0x9f, /* BSDI */
+	MBR_THINKPAD_HIBERNATION_PARTITION = 0xa0,
+	MBR_FREEBSD_PARTITION		= 0xa5, /* various BSD flavours */
+	MBR_OPENBSD_PARTITION		= 0xa6,
+	MBR_NEXTSTEP_PARTITION		= 0xa7,
+	MBR_DARWIN_UFS_PARTITION	= 0xa8,
+	MBR_NETBSD_PARTITION		= 0xa9,
+	MBR_DARWIN_BOOT_PARTITION	= 0xab,
+	MBR_HFS_HFS_PARTITION		= 0xaf,
+	MBR_BSDI_FS_PARTITION		= 0xb7,
+	MBR_BSDI_SWAP_PARTITION		= 0xb8,
+	MBR_BOOTWIZARD_HIDDEN_PARTITION	= 0xbb,
+	MBR_SOLARIS_BOOT_PARTITION	= 0xbe,
+	MBR_SOLARIS_PARTITION		= 0xbf,
+	MBR_DRDOS_FAT12_PARTITION	= 0xc1,
+	MBR_DRDOS_FAT16_L32M_PARTITION	= 0xc4,
+	MBR_DRDOS_FAT16_PARTITION	= 0xc6,
+	MBR_SYRINX_PARTITION		= 0xc7,
+	MBR_NONFS_DATA_PARTITION	= 0xda,
+	MBR_CPM_CTOS_PARTITION		= 0xdb, /* CP/M or Concurrent CP/M or Concurrent DOS or CTOS */
+	MBR_DELL_UTILITY_PARTITION	= 0xde, /* Dell PowerEdge Server utilities */
+	MBR_BOOTIT_PARTITION		= 0xdf, /* BootIt EMBRM */
+	MBR_DOS_ACCESS_PARTITION	= 0xe1, /* DOS access or SpeedStor 12-bit FAT extended partition */
+	MBR_DOS_RO_PARTITION		= 0xe3, /* DOS R/O or SpeedStor */
+	MBR_SPEEDSTOR_EXTENDED_PARTITION = 0xe4, /* SpeedStor 16-bit FAT extended partition < 1024 cyl. */
+	MBR_BEOS_FS_PARTITION		= 0xeb,
+	MBR_GPT_PARTITION		= 0xee, /* Intel EFI GUID Partition Table */
+	MBR_EFI_SYSTEM_PARTITION	= 0xef, /* Intel EFI System Partition */
+	MBR_LINUX_PARISC_BOOT_PARTITION	= 0xf0, /* Linux/PA-RISC boot loader */
+	MBR_SPEEDSTOR1_PARTITION	= 0xf1,
+	MBR_SPEEDSTOR2_PARTITION	= 0xf4, /* SpeedStor large partition */
+	MBR_DOS_SECONDARY_PARTITION	= 0xf2, /* DOS 3.3+ secondary */
+	MBR_VMWARE_VMFS_PARTITION	= 0xfb,
+	MBR_VMWARE_VMKCORE_PARTITION	= 0xfc, /* VMware kernel dump partition */
+	MBR_LINUX_RAID_PARTITION	= 0xfd, /* New (2.2.x) raid partition with autodetect using persistent superblock */
+	MBR_LANSTEP_PARTITION		= 0xfe, /* SpeedStor >1024 cyl. or LANstep */
+	MBR_XENIX_BBT_PARTITION		= 0xff, /* Xenix Bad Block Table */
+};
+
+#endif /* UTIL_LINUX_PT_MBR_H */
diff --git a/libblkid/src/read.c b/libblkid/src/read.c
new file mode 100644
index 0000000..81ab0df
--- /dev/null
+++ b/libblkid/src/read.c
@@ -0,0 +1,510 @@
+/*
+ * 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(READ, ul_debug("found device header: %8s", 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(READ, ul_debug("found device trailer %9s", *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(READ, ul_debug("blkid: short line parsing dev: %s", *cp));
+		return -BLKID_ERR_CACHE;
+	}
+	start = skip_over_blank(start + 1);
+	end = skip_over_word(start);
+
+	DBG(READ, ul_debug("device should be %*s",
+			       (int)(end - start), start));
+
+	if (**cp == '>')
+		*cp = end;
+	else
+		(*cp)++;
+
+	*tmp = '\0';
+
+	if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) {
+		DBG(READ, ul_debug("blkid: missing </device> ending: %s", end));
+	} else if (tmp)
+		*tmp = '\0';
+
+	if (end - start <= 1) {
+		DBG(READ, ul_debug("blkid: empty device name: %s", *cp));
+		return -BLKID_ERR_CACHE;
+	}
+
+	name = strndup(start, end - start);
+	if (name == NULL)
+		return -BLKID_ERR_MEM;
+
+	DBG(READ, ul_debug("found dev %s", 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 == '"') {
+		char *p = end = *value + 1;
+
+		/* convert 'foo\"bar'  to 'foo"bar' */
+		while (*p) {
+			if (*p == '\\') {
+				p++;
+				*end = *p;
+			} else {
+				*end = *p;
+				if (*p == '"')
+					break;
+			}
+			p++;
+			end++;
+		}
+
+		if (*end != '"') {
+			DBG(READ, ul_debug("unbalanced quotes at: %s", *value));
+			*cp = *value;
+			return -BLKID_ERR_CACHE;
+		}
+		(*value)++;
+		*end = '\0';
+		end = ++p;
+	} 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(READ, ul_debug("    tag: %s=\"%s\"", 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(READ, ul_debug("line: %s", 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(READ, ul_debug("blkid: device %s has no TYPE",dev->bid_name));
+		blkid_free_dev(dev);
+		goto done;
+	}
+
+	DBG(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(CACHE, ul_debug("skipping re-read of %s",
+					cache->bic_filename));
+		goto errout;
+	}
+
+	DBG(CACHE, ul_debug("reading cache file %s",
+				cache->bic_filename));
+
+	file = fdopen(fd, "r" UL_CLOEXECSTR);
+	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(READ, ul_debug("blkid: bad format on line %d", 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(BLKID_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/src/resolve.c b/libblkid/src/resolve.c
new file mode 100644
index 0000000..59f0fea
--- /dev/null
+++ b/libblkid/src/resolve.c
@@ -0,0 +1,130 @@
+/*
+ * 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(TAG, ul_debug("looking for %s on %s", 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(TAG, ul_debug("looking for %s%s%s %s", 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(BLKID_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/src/save.c b/libblkid/src/save.c
new file mode 100644
index 0000000..c2a9753
--- /dev/null
+++ b/libblkid/src/save.c
@@ -0,0 +1,244 @@
+/*
+ * 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 "closestream.h"
+
+#include "blkidP.h"
+
+
+static void save_quoted(const char *data, FILE *file)
+{
+	const char *p;
+
+	fputc('"', file);
+	for (p = data; p && *p; p++) {
+		if ((unsigned char) *p == 0x22 ||		/* " */
+		    (unsigned char) *p == 0x5c)			/* \ */
+			fputc('\\', file);
+
+		fputc(*p, file);
+	}
+	fputc('"', file);
+}
+static int save_dev(blkid_dev dev, FILE *file)
+{
+	struct list_head *p;
+
+	if (!dev || dev->bid_name[0] != '/')
+		return 0;
+
+	DBG(SAVE, ul_debug("device %s, type %s", 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);
+
+		fputc(' ', file);			/* space between tags */
+		fputs(tag->bit_name, file);		/* tag NAME */
+		fputc('=', file);			/* separator between NAME and VALUE */
+		save_quoted(tag->bit_val, file);	/* tag "VALUE" */
+	}
+	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(SAVE, ul_debug("skipping cache file write"));
+		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(SAVE, ul_debug("can't create %s directory for cache file",
+					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(SAVE, ul_debug("can't write to cache file %s", 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(SAVE, ul_debug("%s: fchmod failed", filename));
+				else if ((file = fdopen(fd, "w" UL_CLOEXECSTR)))
+					opened = tmp;
+				if (!file)
+					close(fd);
+			}
+		}
+	}
+
+	if (!file) {
+		file = fopen(filename, "w" UL_CLOEXECSTR);
+		opened = filename;
+	}
+
+	DBG(SAVE, ul_debug("writing cache file %s (really %s)",
+		   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;
+	}
+
+	if (close_stream(file) != 0)
+		DBG(SAVE, ul_debug("write failed: %s", filename));
+
+	if (opened != filename) {
+		if (ret < 0) {
+			unlink(opened);
+			DBG(SAVE, ul_debug("unlinked temp cache %s", opened));
+		} else {
+			char *backup;
+
+			backup = malloc(strlen(filename) + 5);
+			if (backup) {
+				sprintf(backup, "%s.old", filename);
+				unlink(backup);
+				if (link(filename, backup)) {
+					DBG(SAVE, ul_debug("can't link %s to %s",
+							filename, backup));
+				}
+				free(backup);
+			}
+			if (rename(opened, filename)) {
+				ret = errno;
+				DBG(SAVE, ul_debug("can't rename %s to %s",
+						opened, filename));
+			} else {
+				DBG(SAVE, ul_debug("moved temp cache %s", 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(BLKID_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/src/strutils.h b/libblkid/src/strutils.h
new file mode 100644
index 0000000..4d8463a
--- /dev/null
+++ b/libblkid/src/strutils.h
@@ -0,0 +1,204 @@
+#ifndef UTIL_LINUX_STRUTILS
+#define UTIL_LINUX_STRUTILS
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+/* default strtoxx_or_err() exit code */
+#ifndef STRTOXX_EXIT_CODE
+# define STRTOXX_EXIT_CODE EXIT_FAILURE
+#endif
+
+
+extern int parse_size(const char *str, uintmax_t *res, int *power);
+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);
+
+extern void strtotimeval_or_err(const char *str, struct timeval *tv,
+		const char *errmesg);
+
+extern int isdigit_string(const char *str);
+
+#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;
+}
+
+static inline char *strdup_to_offset(void *stru, size_t offset, const char *str)
+{
+	char *n = NULL;
+	char **o = (char **) ((char *) stru + offset);
+
+	if (str) {
+		n = strdup(str);
+		if (!n)
+			return NULL;
+	}
+
+	free(*o);
+	*o = n;
+	return n;
+}
+
+#define strdup_to_struct_member(_s, _m, _str) \
+		strdup_to_offset((void *) _s, offsetof(__typeof__(*(_s)), _m), _str)
+
+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);
+
+/*
+ * Match string beginning.
+ */
+static inline const char *startswith(const char *s, const char *prefix)
+{
+	size_t sz = prefix ? strlen(prefix) : 0;
+
+        if (s && sz && strncmp(s, prefix, sz) == 0)
+                return s + sz;
+	return NULL;
+}
+
+/*
+ * Case insensitive match string beginning.
+ */
+static inline const char *startswith_no_case(const char *s, const char *prefix)
+{
+	size_t sz = prefix ? strlen(prefix) : 0;
+
+        if (s && sz && strncasecmp(s, prefix, sz) == 0)
+                return s + sz;
+	return NULL;
+}
+
+/*
+ * Match string ending.
+ */
+static inline const char *endswith(const char *s, const char *postfix)
+{
+	size_t sl = s ? strlen(s) : 0;
+	size_t pl = postfix ? strlen(postfix) : 0;
+
+	if (pl == 0)
+		return (char *)s + sl;
+	if (sl < pl)
+		return NULL;
+	if (memcmp(s + sl - pl, postfix, pl) != 0)
+		return NULL;
+	return (char *)s + sl - pl;
+}
+
+/*
+ * Skip leading white space.
+ */
+static inline const char *skip_space(const char *p)
+{
+	while (isspace(*p))
+		++p;
+	return p;
+}
+
+static inline const char *skip_blank(const char *p)
+{
+	while (isblank(*p))
+		++p;
+	return p;
+}
+
+
+/* Removes whitespace from the right-hand side of a string (trailing
+ * whitespace).
+ *
+ * Returns size of the new string (without \0).
+ */
+static inline size_t rtrim_whitespace(unsigned char *str)
+{
+	size_t i = strlen((char *) str);
+
+	while (i) {
+		i--;
+		if (!isspace(str[i])) {
+			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).
+ */
+static inline size_t 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;
+}
+
+#endif
diff --git a/libblkid/src/superblocks/adaptec_raid.c b/libblkid/src/superblocks/adaptec_raid.c
new file mode 100644
index 0000000..65fd5b8
--- /dev/null
+++ b/libblkid/src/superblocks/adaptec_raid.c
@@ -0,0 +1,116 @@
+/*
+ * 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 BLKID_PROBE_NONE;
+
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return BLKID_PROBE_NONE;
+
+	off = ((pr->size / 0x200)-1) * 0x200;
+	ad = (struct adaptec_metadata *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct adaptec_metadata));
+	if (!ad)
+		return errno ? -errno : BLKID_PROBE_NONE;;
+
+	if (ad->smagic != be32_to_cpu(AD_SIGNATURE))
+		return BLKID_PROBE_NONE;
+	if (ad->b0idcode != be32_to_cpu(AD_MAGIC))
+		return BLKID_PROBE_NONE;
+	if (blkid_probe_sprintf_version(pr, "%u", ad->resver) != 0)
+		return BLKID_PROBE_NONE;
+	if (blkid_probe_set_magic(pr, off, sizeof(ad->b0idcode),
+				(unsigned char *) &ad->b0idcode))
+		return BLKID_PROBE_NONE;
+
+	return BLKID_PROBE_OK;
+}
+
+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/src/superblocks/bcache.c b/libblkid/src/superblocks/bcache.c
new file mode 100644
index 0000000..b3e397b
--- /dev/null
+++ b/libblkid/src/superblocks/bcache.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 Rolf Fokkens <rolf@fokkens.nl>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Based on code fragments from bcache-tools by Kent Overstreet:
+ * http://evilpiepirate.org/git/bcache-tools.git
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "superblocks.h"
+#include "crc64.h"
+
+#define SB_LABEL_SIZE      32
+#define SB_JOURNAL_BUCKETS 256U
+
+#define node(i, j)         ((i)->d + (j))
+#define end(i)             node(i, (i)->keys)
+
+static const char bcache_magic[] = {
+	0xc6, 0x85, 0x73, 0xf6, 0x4e, 0x1a, 0x45, 0xca,
+	0x82, 0x65, 0xf5, 0x7f, 0x48, 0xba, 0x6d, 0x81
+};
+
+struct bcache_super_block {
+	uint64_t		csum;
+	uint64_t		offset;	/* sector where this sb was written */
+	uint64_t		version;
+
+	uint8_t			magic[16];
+
+	uint8_t			uuid[16];
+	union {
+		uint8_t		set_uuid[16];
+		uint64_t	set_magic;
+	};
+	uint8_t			label[SB_LABEL_SIZE];
+
+	uint64_t		flags;
+	uint64_t		seq;
+	uint64_t		pad[8];
+
+	union {
+	struct {
+		/* Cache devices */
+		uint64_t	nbuckets;	/* device size */
+
+		uint16_t	block_size;	/* sectors */
+		uint16_t	bucket_size;	/* sectors */
+
+		uint16_t	nr_in_set;
+		uint16_t	nr_this_dev;
+	};
+	struct {
+		/* Backing devices */
+		uint64_t	data_offset;
+
+		/*
+		 * block_size from the cache device section is still used by
+		 * backing devices, so don't add anything here until we fix
+		 * things to not need it for backing devices anymore
+		 */
+	};
+	};
+
+	uint32_t		last_mount;	/* time_t */
+
+	uint16_t		first_bucket;
+	union {
+		uint16_t	njournal_buckets;
+		uint16_t	keys;
+	};
+	uint64_t		d[SB_JOURNAL_BUCKETS];	/* journal buckets */
+};
+
+/* magic string */
+#define BCACHE_SB_MAGIC     bcache_magic
+/* magic string len */
+#define BCACHE_SB_MAGIC_LEN sizeof (bcache_magic)
+/* super block offset */
+#define BCACHE_SB_OFF       0x1000
+/* supper block offset in kB */
+#define BCACHE_SB_KBOFF     (BCACHE_SB_OFF >> 10)
+/* magic string offset within super block */
+#define BCACHE_SB_MAGIC_OFF offsetof (struct bcache_super_block, magic)
+
+static uint64_t bcache_crc64(struct bcache_super_block *bcs)
+{
+	unsigned char *data = (unsigned char *) bcs;
+	size_t sz;
+
+	data += 8;		/* skip csum field */
+	sz = (unsigned char *) end(bcs) - data;
+
+	return crc64(0xFFFFFFFFFFFFFFFFULL, data, sz) ^ 0xFFFFFFFFFFFFFFFFULL;
+}
+
+static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct bcache_super_block *bcs;
+
+	bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block);
+	if (!bcs)
+		return errno ? -errno : BLKID_PROBE_NONE;
+
+	if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512)
+		return BLKID_PROBE_NONE;
+	if (!blkid_probe_verify_csum(pr, bcache_crc64(bcs), le64_to_cpu(bcs->csum)))
+		return BLKID_PROBE_NONE;
+
+	if (blkid_probe_set_uuid(pr, bcs->uuid) < 0)
+		return BLKID_PROBE_NONE;
+
+	return BLKID_PROBE_OK;
+};
+
+const struct blkid_idinfo bcache_idinfo =
+{
+	.name		= "bcache",
+	.usage		= BLKID_USAGE_OTHER,
+	.probefunc	= probe_bcache,
+	.minsz		= 8192,
+	.magics		=
+	{
+		{ .magic = BCACHE_SB_MAGIC
+		, .len   = BCACHE_SB_MAGIC_LEN
+		, .kboff = BCACHE_SB_KBOFF
+		, .sboff = BCACHE_SB_MAGIC_OFF
+		} ,
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/src/superblocks/befs.c b/libblkid/src/superblocks/befs.c
new file mode 100644
index 0000000..e4453bc
--- /dev/null
+++ b/libblkid/src/superblocks/befs.c
@@ -0,0 +1,482 @@
+/*
+ * 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 "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;
+
+	errno = 0;
+	bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0,
+					sizeof(struct bplustree_header), fs_le);
+	if (!bh)
+		return errno ? -errno : -ENOENT;
+
+	if ((int32_t) FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC)
+		return -ENOENT;
+
+	node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le);
+
+	do {
+		errno = 0;
+		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 errno ? -errno : -ENOENT;
+
+		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 errno ? -errno : BLKID_PROBE_NONE;
+
+	if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+		return BLKID_PROBE_NONE;
+
+	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 errno ? -errno : BLKID_PROBE_NONE;
+
+		if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+			return BLKID_PROBE_NONE;
+
+		value = get_key_value(pr, bs, bi, KEY_NAME, fs_le);
+		if (value < 0)
+			return value == -ENOENT ? BLKID_PROBE_NONE : 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 errno ? -errno : BLKID_PROBE_NONE;
+
+			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 errno ? -errno : BLKID_PROBE_NONE;
+
+				*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 errno ? -errno : BLKID_PROBE_NONE;
+
+	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 BLKID_PROBE_NONE;
+
+	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 BLKID_PROBE_OK;
+}
+
+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/src/superblocks/bfs.c b/libblkid/src/superblocks/bfs.c
new file mode 100644
index 0000000..8a34c58
--- /dev/null
+++ b/libblkid/src/superblocks/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/src/superblocks/btrfs.c b/libblkid/src/superblocks/btrfs.c
new file mode 100644
index 0000000..7ce3dff
--- /dev/null
+++ b/libblkid/src/superblocks/btrfs.c
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+	bfs = blkid_probe_get_sb(pr, mag, struct btrfs_super_block);
+	if (!bfs)
+		return errno ? -errno : 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 },
+	  { NULL }
+	}
+};
+
diff --git a/libblkid/src/superblocks/cramfs.c b/libblkid/src/superblocks/cramfs.c
new file mode 100644
index 0000000..6d01b0b
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/ddf_raid.c b/libblkid/src/superblocks/ddf_raid.c
new file mode 100644
index 0000000..fc2c39d
--- /dev/null
+++ b/libblkid/src/superblocks/ddf_raid.c
@@ -0,0 +1,141 @@
+/*
+ * 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 errno ? -errno : 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)
+			return errno ? -errno : 1;
+
+		if (memcmp(buf, &ddf->signature, 4) != 0)
+			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/src/superblocks/drbd.c b/libblkid/src/superblocks/drbd.c
new file mode 100644
index 0000000..e88e9f3
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/drbdproxy_datalog.c b/libblkid/src/superblocks/drbdproxy_datalog.c
new file mode 100644
index 0000000..af59722
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/exfat.c b/libblkid/src/superblocks/exfat.c
new file mode 100644
index 0000000..b526560
--- /dev/null
+++ b/libblkid/src/superblocks/exfat.c
@@ -0,0 +1,148 @@
+/*
+ * 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 errno ? -errno : BLKID_PROBE_NONE;
+
+	label = find_label(pr, sb);
+	if (label)
+		blkid_probe_set_utf8label(pr, label->name,
+				min(label->length * 2, 30), BLKID_ENC_UTF16LE);
+	else if (errno)
+		return -errno;
+
+	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 BLKID_PROBE_OK;
+}
+
+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/src/superblocks/ext.c b/libblkid/src/superblocks/ext.c
new file mode 100644
index 0000000..5b1d179
--- /dev/null
+++ b/libblkid/src/superblocks/ext.c
@@ -0,0 +1,365 @@
+/*
+ * 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 "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
+
+/*
+ * Starting in 2.6.29, ext4 can be used to support filesystems
+ * without a journal.
+ */
+#define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29)
+
+/*
+ * 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(PROBE, ul_debug("ext2_sb.compat = %08X:%08X:%08X",
+		   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 errno ? -errno : 1;
+	if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
+		return 1;
+
+	ext_get_info(pr, 2, es);
+	blkid_probe_set_uuid_as(pr, es->s_uuid, "LOGUUID");
+
+	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 errno ? -errno : 1;
+
+	/* Distinguish between ext3 and ext2 */
+	if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
+		return 1;
+
+	/* Any features which ext2 doesn't understand */
+	if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+	    (fi  & EXT2_FEATURE_INCOMPAT_UNSUPPORTED))
+		return 1;
+
+	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 errno ? -errno : 1;
+
+	/* ext3 requires journal */
+	if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+		return 1;
+
+	/* Any features which ext3 doesn't understand */
+	if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+	    (fi  & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+		return 1;
+
+	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 errno ? -errno : 1;
+
+	/* Distinguish from jbd */
+	if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+		return 1;
+
+	if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS))
+		return 1;
+
+	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 errno ? -errno : 1;
+
+	/* Distinguish from jbd */
+	if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+		return 1;
+
+	/* Ext4 has at least one feature which ext3 doesn't understand */
+	if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) &&
+	    !(fi  & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+		return 1;
+
+	/*
+	 * 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)
+		return 1;
+
+	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/src/superblocks/f2fs.c b/libblkid/src/superblocks/f2fs.c
new file mode 100644
index 0000000..2bf0f5e
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/gfs.c b/libblkid/src/superblocks/gfs.c
new file mode 100644
index 0000000..1b26558
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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 errno ? -errno : 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/src/superblocks/hfs.c b/libblkid/src/superblocks/hfs.c
new file mode 100644
index 0000000..fe57241
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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 errno ? -errno : 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 errno ? -errno : 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 errno ? -errno : 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 errno ? -errno : 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/src/superblocks/highpoint_raid.c b/libblkid/src/superblocks/highpoint_raid.c
new file mode 100644
index 0000000..ad3b538
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/hpfs.c b/libblkid/src/superblocks/hpfs.c
new file mode 100644
index 0000000..0565d37
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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 errno ? -errno : 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 errno ? -errno : 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/src/superblocks/iso9660.c b/libblkid/src/superblocks/iso9660.c
new file mode 100644
index 0000000..d099467
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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 errno ? -errno : 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/src/superblocks/iso9660.h b/libblkid/src/superblocks/iso9660.h
new file mode 100644
index 0000000..a8d729d
--- /dev/null
+++ b/libblkid/src/superblocks/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/src/superblocks/isw_raid.c b/libblkid/src/superblocks/isw_raid.c
new file mode 100644
index 0000000..065c2b2
--- /dev/null
+++ b/libblkid/src/superblocks/isw_raid.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 <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 errno ? -errno : 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/src/superblocks/jfs.c b/libblkid/src/superblocks/jfs.c
new file mode 100644
index 0000000..ac684d8
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/jmicron_raid.c b/libblkid/src/superblocks/jmicron_raid.c
new file mode 100644
index 0000000..ca79867
--- /dev/null
+++ b/libblkid/src/superblocks/jmicron_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 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 errno ? -errno : 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/src/superblocks/linux_raid.c b/libblkid/src/superblocks/linux_raid.c
new file mode 100644
index 0000000..cf29141
--- /dev/null
+++ b/libblkid/src/superblocks/linux_raid.c
@@ -0,0 +1,267 @@
+/*
+ * 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 errno ? -errno : 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 errno ? -errno : 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;
+	int ret = BLKID_PROBE_NONE;
+
+	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;
+		ret = probe_raid0(pr, sboff);
+		if (ret < 1)
+			return ret;	/* error */
+
+		/* version 1.0 at the end of the device */
+		sboff = (pr->size & ~(0x1000 - 1)) - 0x2000;
+		ret = probe_raid1(pr, sboff);
+		if (ret < 0)
+			return ret;	/* error */
+		if (ret == 0)
+			ver = "1.0";
+	}
+
+	if (!ver) {
+		/* version 1.1 at the start of the device */
+		ret = probe_raid1(pr, 0);
+		if (ret == 0)
+			ver = "1.1";
+
+		/* version 1.2 at 4k offset from the start */
+		else if (ret == BLKID_PROBE_NONE) {
+			ret = probe_raid1(pr, 0x1000);
+			if (ret == 0)
+				ver = "1.2";
+		}
+	}
+
+	if (ver) {
+		blkid_probe_set_version(pr, ver);
+		return BLKID_PROBE_OK;
+	}
+	return ret;
+}
+
+
+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/src/superblocks/lsi_raid.c b/libblkid/src/superblocks/lsi_raid.c
new file mode 100644
index 0000000..697b0fe
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/luks.c b/libblkid/src/superblocks/luks.c
new file mode 100644
index 0000000..00696f2
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/lvm.c b/libblkid/src/superblocks/lvm.c
new file mode 100644
index 0000000..3c6df74
--- /dev/null
+++ b/libblkid/src/superblocks/lvm.c
@@ -0,0 +1,226 @@
+/*
+ * 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 errno ? -errno : 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 (!blkid_probe_verify_csum(
+		pr, lvm2_calc_crc(
+			&label->offset_xl, LVM2_LABEL_SIZE -
+			((char *) &label->offset_xl - (char *) label)),
+			le32_to_cpu(label->crc_xl)))
+		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 errno ? -errno : 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 errno ? -errno : 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/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
new file mode 100644
index 0000000..9ea49fe
--- /dev/null
+++ b/libblkid/src/superblocks/minix.c
@@ -0,0 +1,161 @@
+/*
+ * 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-2013 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"
+
+#define minix_swab16(doit, num)	((uint16_t) (doit ? swab16(num) : num))
+#define minix_swab32(doit, num)	((uint32_t) (doit ? swab32(num) : num))
+
+static int get_minix_version(const unsigned char *data, int *other_endian)
+{
+	struct minix_super_block *sb = (struct minix_super_block *) data;
+	struct minix3_super_block *sb3 = (struct minix3_super_block *) data;
+	int version = 0;
+
+	*other_endian = 0;
+
+	switch (sb->s_magic) {
+	case MINIX_SUPER_MAGIC:
+	case MINIX_SUPER_MAGIC2:
+		version = 1;
+		break;
+	case MINIX2_SUPER_MAGIC:
+	case MINIX2_SUPER_MAGIC2:
+		version = 2;
+		break;
+	default:
+		if (sb3->s_magic == MINIX3_SUPER_MAGIC)
+			version = 3;
+		break;
+	}
+
+	if (!version) {
+		*other_endian = 1;
+
+		switch (swab16(sb->s_magic)) {
+		case MINIX_SUPER_MAGIC:
+		case MINIX_SUPER_MAGIC2:
+			version = 1;
+			break;
+		case MINIX2_SUPER_MAGIC:
+		case MINIX2_SUPER_MAGIC2:
+			version = 2;
+			break;
+		default:
+			if (sb3->s_magic == MINIX3_SUPER_MAGIC)
+				version = 3;
+			break;
+		}
+	}
+	if (!version)
+		return -1;
+
+	DBG(LOWPROBE, ul_debug("minix version %d detected [%s]", version,
+#if defined(WORDS_BIGENDIAN)
+	*other_endian ? "LE" : "BE"
+#else
+	*other_endian ? "BE" : "LE"
+#endif
+	));
+	return version;
+}
+
+static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	unsigned char *ext;
+	const unsigned char *data;
+	int version = 0, swabme = 0;
+
+	data = blkid_probe_get_buffer(pr, 1024,
+			max(sizeof(struct minix_super_block),
+			    sizeof(struct minix3_super_block)));
+	if (!data)
+		return errno ? -errno : 1;
+	version = get_minix_version(data, &swabme);
+	if (version < 1)
+		return 1;
+
+	if (version <= 2) {
+		struct minix_super_block *sb = (struct minix_super_block *) data;
+		int zones, ninodes, imaps, zmaps, firstz;
+
+		if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+			return 1;
+
+		zones = version == 2 ? minix_swab32(swabme, sb->s_zones) :
+				       minix_swab16(swabme, sb->s_nzones);
+		ninodes = minix_swab16(swabme, sb->s_ninodes);
+		imaps   = minix_swab16(swabme, sb->s_imap_blocks);
+		zmaps   = minix_swab16(swabme, sb->s_zmap_blocks);
+		firstz  = minix_swab16(swabme, sb->s_firstdatazone);
+
+		/* sanity checks to be sure that the FS is really minix */
+		if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1)
+			return 1;
+		if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1)
+			return 1;
+
+	} else if (version == 3) {
+		struct minix3_super_block *sb = (struct minix3_super_block *) data;
+
+		if (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)
+		return errno ? -errno : 1;
+	else if (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 - LE */
+		{ .magic = "\177\023", .len = 2, .kboff = 1, .sboff = 0x10 },
+		{ .magic = "\217\023", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+		/* version 1 - BE */
+		{ .magic = "\023\177", .len = 2, .kboff = 1, .sboff = 0x10 },
+		{ .magic = "\023\217", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+		/* version 2 - LE */
+		{ .magic = "\150\044", .len = 2, .kboff = 1, .sboff = 0x10 },
+		{ .magic = "\170\044", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+		/* version 2 - BE */
+		{ .magic = "\044\150", .len = 2, .kboff = 1, .sboff = 0x10 },
+		{ .magic = "\044\170", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+		/* version 3 - LE */
+		{ .magic = "\132\115", .len = 2, .kboff = 1, .sboff = 0x18 },
+
+		/* version 3 - BE */
+		{ .magic = "\115\132", .len = 2, .kboff = 1, .sboff = 0x18 },
+
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/src/superblocks/netware.c b/libblkid/src/superblocks/netware.c
new file mode 100644
index 0000000..af81cf5
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/nilfs.c b/libblkid/src/superblocks/nilfs.c
new file mode 100644
index 0000000..3f03901
--- /dev/null
+++ b/libblkid/src/superblocks/nilfs.c
@@ -0,0 +1,138 @@
+/*
+ * 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];
+};
+
+#define NILFS_SB_MAGIC		0x3434
+#define NILFS_SB_OFFSET		0x400
+
+static int nilfs_valid_sb(blkid_probe pr, 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;
+
+	if (!sb || le16_to_cpu(sb->s_magic) != NILFS_SB_MAGIC)
+		return 0;
+
+	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);
+
+	return blkid_probe_verify_csum(pr, crc, le32_to_cpu(sb->s_sum));
+}
+
+static int probe_nilfs2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct nilfs_super_block *sb, *sbp, *sbb;
+	int valid[2], swp = 0;
+
+	/* primary */
+	sbp = (struct nilfs_super_block *) blkid_probe_get_buffer(
+			pr, NILFS_SB_OFFSET, sizeof(struct nilfs_super_block));
+	if (!sbp)
+		return errno ? -errno : 1;
+	/* backup */
+	sbb = (struct nilfs_super_block *) blkid_probe_get_buffer(
+			pr, ((pr->size / 0x200) - 8) * 0x200, sizeof(struct nilfs_super_block));
+	if (!sbb)
+		return errno ? -errno : 1;
+
+	/*
+	 * Compare two super blocks and set 1 in swp if the secondary
+	 * super block is valid and newer.  Otherwise, set 0 in swp.
+	 */
+	valid[0] = nilfs_valid_sb(pr, sbp);
+	valid[1] = nilfs_valid_sb(pr, sbb);
+	if (!valid[0] && !valid[1])
+		return 1;
+
+	swp = valid[1] && (!valid[0] ||
+			   le64_to_cpu(sbp->s_last_cno) >
+			   le64_to_cpu(sbb->s_last_cno));
+	sb = swp ? sbb : sbp;
+
+	DBG(LOWPROBE, ul_debug("nilfs2: primary=%d, backup=%d, swap=%d",
+				valid[0], valid[1], swp));
+
+	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,
+	/* default min.size is 128MiB, but 1MiB for "mkfs.nilfs2 -b 1024 -B 16" */
+	.minsz		= (1024 * 1024),
+	.magics		= BLKID_NONE_MAGIC
+};
diff --git a/libblkid/src/superblocks/ntfs.c b/libblkid/src/superblocks/ntfs.c
new file mode 100644
index 0000000..8ff9ccd
--- /dev/null
+++ b/libblkid/src/superblocks/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		= 0x60,
+	MFT_RECORD_ATTR_END			= 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 errno ? -errno : 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(LOWPROBE, ul_debug("NTFS: sector_size=%d, mft_record_size=%d, "
+			"sectors_per_cluster=%d, nr_clusters=%ju "
+			"cluster_offset=%jd",
+			(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 errno ? -errno : 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 errno ? -errno : 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 (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_END)
+			break;
+		if (le32_to_cpu(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/src/superblocks/nvidia_raid.c b/libblkid/src/superblocks/nvidia_raid.c
new file mode 100644
index 0000000..5db8ec2
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/ocfs.c b/libblkid/src/superblocks/ocfs.c
new file mode 100644
index 0000000..3fe199d
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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 errno ? -errno : 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 errno ? -errno : 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 errno ? -errno : 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/src/superblocks/promise_raid.c b/libblkid/src/superblocks/promise_raid.c
new file mode 100644
index 0000000..678460a
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/refs.c b/libblkid/src/superblocks/refs.c
new file mode 100644
index 0000000..ea81f20
--- /dev/null
+++ b/libblkid/src/superblocks/refs.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 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"
+
+
+const struct blkid_idinfo refs_idinfo =
+{
+	.name		= "ReFS",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.magics		=
+	{
+		{ .magic = "\000\000\000ReFS\000", .len = 8 },
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/src/superblocks/reiserfs.c b/libblkid/src/superblocks/reiserfs.c
new file mode 100644
index 0000000..edbaaa9
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 1;
+
+	blocksize = le16_to_cpu(rs->rs_blocksize);
+
+	/* The blocksize must be at least 512B */
+	if ((blocksize >> 9) == 0)
+		return 1;
+
+	/* 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 1;
+
+	/* 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 errno ? -errno : 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/src/superblocks/romfs.c b/libblkid/src/superblocks/romfs.c
new file mode 100644
index 0000000..8e63c10
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/silicon_raid.c b/libblkid/src/superblocks/silicon_raid.c
new file mode 100644
index 0000000..edbefbc
--- /dev/null
+++ b/libblkid/src/superblocks/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 uint16_t silraid_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--) {
+		uint16_t x = *p++;
+		sum += le16_to_cpu(x);
+	}
+
+	return (-sum & 0xFFFF);
+}
+
+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 errno ? -errno : 1;
+
+	if (le32_to_cpu(sil->magic) != SILICON_MAGIC)
+		return 1;
+	if (sil->disk_number >= 8)
+		return 1;
+	if (!blkid_probe_verify_csum(pr, silraid_checksum(sil), le16_to_cpu(sil->checksum1)))
+		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/src/superblocks/squashfs.c b/libblkid/src/superblocks/squashfs.c
new file mode 100644
index 0000000..8ed2838
--- /dev/null
+++ b/libblkid/src/superblocks/squashfs.c
@@ -0,0 +1,101 @@
+/*
+ * 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;
+	uint16_t major;
+	uint16_t minor;
+
+	sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block);
+	if (!sq)
+		return errno ? -errno : 1;
+
+	major = le16_to_cpu(sq->s_major);
+	minor = le16_to_cpu(sq->s_minor);
+	if (major < 4)
+		return 1;
+
+	blkid_probe_sprintf_version(pr, "%u.%u", major, minor);
+
+	return 0;
+}
+
+static int probe_squashfs3(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct sqsh_super_block *sq;
+	uint16_t major;
+	uint16_t minor;
+
+	sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block);
+	if (!sq)
+		return errno ? -errno : 1;
+
+	if (strcmp(mag->magic, "sqsh") == 0) {
+		major = be16_to_cpu(sq->s_major);
+		minor = be16_to_cpu(sq->s_minor);
+	} else {
+		major = le16_to_cpu(sq->s_major);
+		minor = le16_to_cpu(sq->s_minor);
+	}
+
+	if (major > 3)
+		return 1;
+
+	blkid_probe_sprintf_version(pr, "%u.%u", major, minor);
+
+	return 0;
+}
+
+const struct blkid_idinfo squashfs_idinfo =
+{
+	.name		= "squashfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_squashfs,
+	.magics		=
+	{
+		{ .magic = "hsqs", .len = 4 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo squashfs3_idinfo =
+{
+	.name		= "squashfs3",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_squashfs3,
+	.magics		=
+	{
+		{ .magic = "sqsh", .len = 4 }, /* big endian */
+		{ .magic = "hsqs", .len = 4 }, /* little endian */
+		{ NULL }
+	}
+};
+
diff --git a/libblkid/src/superblocks/superblocks.c b/libblkid/src/superblocks/superblocks.c
new file mode 100644
index 0000000..80bd6e5
--- /dev/null
+++ b/libblkid/src/superblocks/superblocks.c
@@ -0,0 +1,813 @@
+/*
+ * 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)
+ *
+ * @LOGUUID: external log UUID (e.g. xfs)
+ *
+ * @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,
+
+	&bcache_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,
+	&xfs_log_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,
+	&refs_idinfo,
+	&cramfs_idinfo,
+	&romfs_idinfo,
+	&minix_idinfo,
+	&gfs_idinfo,
+	&gfs2_idinfo,
+	&ocfs_idinfo,
+	&ocfs2_idinfo,
+	&oracleasm_idinfo,
+	&vxfs_idinfo,
+	&squashfs_idinfo,
+	&squashfs3_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(LOWPROBE, ul_debug("a new probing usage-filter initialized"));
+	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;
+	int rc = BLKID_PROBE_NONE;
+
+	if (!pr || chn->idx < -1)
+		return -EINVAL;
+
+	blkid_probe_chain_reset_vals(pr, chn);
+
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		return BLKID_PROBE_NONE;
+
+	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 */
+		return BLKID_PROBE_NONE;
+
+	DBG(LOWPROBE, ul_debug("--> starting probing loop [SUBLKS idx=%d]",
+		chn->idx));
+
+	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;
+
+		chn->idx = i;
+		id = idinfos[i];
+
+		if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) {
+			DBG(LOWPROBE, ul_debug("filter out: %s", id->name));
+			rc = BLKID_PROBE_NONE;
+			continue;
+		}
+
+		if (id->minsz && id->minsz > pr->size) {
+			rc = BLKID_PROBE_NONE;
+			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)) {
+			rc = BLKID_PROBE_NONE;
+			continue;
+		}
+
+		/* don't probe for RAIDs on floppies */
+		if ((id->usage & BLKID_USAGE_RAID) && blkid_probe_is_tiny(pr)) {
+			rc = BLKID_PROBE_NONE;
+			continue;
+		}
+
+		DBG(LOWPROBE, ul_debug("[%zd] %s:", i, id->name));
+
+		rc = blkid_probe_get_idmag(pr, id, &off, &mag);
+		if (rc < 0)
+			break;
+		if (rc != BLKID_PROBE_OK)
+			continue;
+
+		/* final check by probing function */
+		if (id->probefunc) {
+			DBG(LOWPROBE, ul_debug("\tcall probefunc()"));
+			rc = id->probefunc(pr, mag);
+			if (rc != BLKID_PROBE_OK) {
+				blkid_probe_chain_reset_vals(pr, chn);
+				if (rc < 0)
+					break;
+				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(LOWPROBE, ul_debug("failed to set result -- ignore"));
+			continue;
+		}
+
+		DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [SUBLKS idx=%d]",
+			id->name, chn->idx));
+		return BLKID_PROBE_OK;
+	}
+
+	DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed=%d) [SUBLKS idx=%d]",
+			rc, chn->idx));
+	return rc;
+}
+
+/*
+ * 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;
+
+	if (pr->flags & BLKID_FL_NOSCAN_DEV)
+		return BLKID_PROBE_NONE;
+
+	while ((rc = superblocks_probe(pr, chn)) == 0) {
+
+		if (blkid_probe_is_tiny(pr) && !count)
+			return BLKID_PROBE_OK;	/* floppy or so -- returns the first result. */
+
+		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(LOWPROBE, ul_debug("ERROR: superblocks chain: "
+			       "ambivalent result detected (%d filesystems)!",
+			       count));
+		return -2;		/* error, ambivalent result (more FS) */
+	}
+	if (!count)
+		return BLKID_PROBE_NONE;
+
+	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 BLKID_PROBE_OK;
+}
+
+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;
+}
+
+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 (blkid_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 (blkid_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/src/superblocks/superblocks.h b/libblkid/src/superblocks/superblocks.h
new file mode 100644
index 0000000..3bbfb9c
--- /dev/null
+++ b/libblkid/src/superblocks/superblocks.h
@@ -0,0 +1,99 @@
+/*
+ * 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 xfs_log_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 refs_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 squashfs3_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;
+extern const struct blkid_idinfo bcache_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/src/superblocks/swap.c b/libblkid/src/superblocks/swap.c
new file mode 100644
index 0000000..3f21391
--- /dev/null
+++ b/libblkid/src/superblocks/swap.c
@@ -0,0 +1,178 @@
+/*
+ * 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 errno ? -errno : 1;
+
+	/* SWAPSPACE2 - check for wrong version or zeroed pagecount */
+	if (strcmp(version, "1") == 0) {
+		if (hdr->version != 1 && swab32(hdr->version) != 1) {
+			DBG(LOWPROBE, ul_debug("incorrect swap version"));
+			return 1;
+		}
+		if (hdr->lastpage == 0) {
+			DBG(LOWPROBE, ul_debug("not set last swap page"));
+			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 errno ? -errno : 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, "0");
+		return 0;
+
+	} else if (!memcmp(mag->magic, "SWAPSPACE2", mag->len))
+		return swap_set_info(pr, "1");
+
+	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/src/superblocks/sysv.c b/libblkid/src/superblocks/sysv.c
new file mode 100644
index 0000000..4b34591
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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 errno ? -errno : 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/src/superblocks/ubifs.c b/libblkid/src/superblocks/ubifs.c
new file mode 100644
index 0000000..dc84260
--- /dev/null
+++ b/libblkid/src/superblocks/ubifs.c
@@ -0,0 +1,121 @@
+/*
+ * 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 errno ? -errno : 1;
+
+	blkid_probe_set_uuid(pr, sb->uuid);
+	blkid_probe_sprintf_version(pr, "w%dr%d",
+			le32_to_cpu(sb->fmt_version),
+			le32_to_cpu(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/src/superblocks/udf.c b/libblkid/src/superblocks/udf.c
new file mode 100644
index 0000000..5cde3cc
--- /dev/null
+++ b/libblkid/src/superblocks/udf.c
@@ -0,0 +1,201 @@
+/*
+ * 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 pbs[2];
+	unsigned int b;
+	unsigned int type;
+	unsigned int count;
+	unsigned int loc;
+	unsigned int i;
+
+	/* The block size of a UDF filesystem is that of the underlying
+	 * storage; we check later on for the special case of image files,
+	 * which may have the 2048-byte block size of optical media. */
+	pbs[0] = blkid_probe_get_sectorsize(pr);
+	pbs[1] = 0x800;
+
+	/* check for a Volume Structure Descriptor (VSD); each is
+	 * 2048 bytes long */
+	for (b = 0; b < 0x8000; b += 0x800) {
+		vsd = (struct volume_structure_descriptor *)
+			blkid_probe_get_buffer(pr,
+					UDF_VSD_OFFSET + b,
+					sizeof(*vsd));
+		if (!vsd)
+			return errno ? -errno : 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 * 0x800),
+					sizeof(*vsd));
+		if (!vsd)
+			return errno ? -errno : 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), checking block size */
+	for (i = 0; i < 2; i++) {
+		vd = (struct volume_descriptor *)
+			blkid_probe_get_buffer(pr, 256 * pbs[i], sizeof(*vd));
+		if (!vd)
+			return errno ? -errno : 1;
+
+		type = le16_to_cpu(vd->tag.id);
+		if (type == 2) /* TAG_ID_AVDP */
+			goto real_blksz;
+	}
+	return 0;
+
+real_blksz:
+	/* Use the actual block size from here on out */
+	bs = pbs[i];
+
+	/* get descriptor 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 errno ? -errno : 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));
+		if (!vd)
+			return errno ? -errno : 1;
+		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/src/superblocks/ufs.c b/libblkid/src/superblocks/ufs.c
new file mode 100644
index 0000000..6ef2acd
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/vfat.c b/libblkid/src/superblocks/vfat.c
new file mode 100644
index 0000000..f38deac
--- /dev/null
+++ b/libblkid/src/superblocks/vfat.c
@@ -0,0 +1,431 @@
+/*
+ * 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(LOWPROBE, ul_debug("\tlook for label in root-dir "
+			"(entries: %d, offset: %jd)", 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(LOWPROBE, ul_debug("\tfound fs LABEL at entry %d", 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;
+	int rc;
+
+	rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag);
+	if (rc < 0)
+		return rc;	/* error */
+	if (rc != BLKID_PROBE_OK || !mag)
+		return 0;
+
+	ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
+	if (!ms)
+		return errno ? -errno : 0;
+	vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
+	if (!vs)
+		return errno ? -errno : 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 errno ? -errno : 1;
+
+	vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
+	if (!vs)
+		return errno ? -errno : 1;
+
+	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 errno ? -errno : 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/src/superblocks/via_raid.c b/libblkid/src/superblocks/via_raid.c
new file mode 100644
index 0000000..ee3ab65
--- /dev/null
+++ b/libblkid/src/superblocks/via_raid.c
@@ -0,0 +1,91 @@
+/*
+ * 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;
+}
+
+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 errno ? -errno : 1;
+
+	if (le16_to_cpu(v->signature) != VIA_SIGNATURE)
+		return 1;
+	if (v->version_number > 2)
+		return 1;
+	if (!blkid_probe_verify_csum(pr, via_checksum(v), v->checksum))
+		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/src/superblocks/vmfs.c b/libblkid/src/superblocks/vmfs.c
new file mode 100644
index 0000000..fac87ab
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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 errno ? -errno : 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/src/superblocks/vxfs.c b/libblkid/src/superblocks/vxfs.c
new file mode 100644
index 0000000..19d284c
--- /dev/null
+++ b/libblkid/src/superblocks/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 errno ? -errno : 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/src/superblocks/xfs.c b/libblkid/src/superblocks/xfs.c
new file mode 100644
index 0000000..a6c04a2
--- /dev/null
+++ b/libblkid/src/superblocks/xfs.c
@@ -0,0 +1,277 @@
+/*
+ * 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) 2013 Eric Sandeen <sandeen@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 {
+	uint32_t	sb_magicnum;	/* magic number == XFS_SB_MAGIC */
+	uint32_t	sb_blocksize;	/* logical block size, bytes */
+	uint64_t	sb_dblocks;	/* number of data blocks */
+	uint64_t	sb_rblocks;	/* number of realtime blocks */
+	uint64_t	sb_rextents;	/* number of realtime extents */
+	unsigned char	sb_uuid[16];	/* file system unique id */
+	uint64_t	sb_logstart;	/* starting block of log if internal */
+	uint64_t	sb_rootino;	/* root inode number */
+	uint64_t	sb_rbmino;	/* bitmap inode for realtime extents */
+	uint64_t	sb_rsumino;	/* summary inode for rt bitmap */
+	uint32_t	sb_rextsize;	/* realtime extent size, blocks */
+	uint32_t	sb_agblocks;	/* size of an allocation group */
+	uint32_t	sb_agcount;	/* number of allocation groups */
+	uint32_t	sb_rbmblocks;	/* number of rt bitmap blocks */
+	uint32_t	sb_logblocks;	/* number of log blocks */
+
+	uint16_t	sb_versionnum;	/* header version == XFS_SB_VERSION */
+	uint16_t	sb_sectsize;	/* volume sector size, bytes */
+	uint16_t	sb_inodesize;	/* inode size, bytes */
+	uint16_t	sb_inopblock;	/* inodes per block */
+	char		sb_fname[12];	/* file system name */
+	uint8_t		sb_blocklog;	/* log2 of sb_blocksize */
+	uint8_t		sb_sectlog;	/* log2 of sb_sectsize */
+	uint8_t		sb_inodelog;	/* log2 of sb_inodesize */
+	uint8_t		sb_inopblog;	/* log2 of sb_inopblock */
+	uint8_t		sb_agblklog;	/* log2 of sb_agblocks (rounded up) */
+	uint8_t		sb_rextslog;	/* log2 of sb_rextents */
+	uint8_t		sb_inprogress;	/* mkfs is in progress, don't mount */
+	uint8_t		sb_imax_pct;	/* max % of fs for inode space */
+					/* statistics */
+	uint64_t	sb_icount;	/* allocated inodes */
+	uint64_t	sb_ifree;	/* free inodes */
+	uint64_t	sb_fdblocks;	/* free data blocks */
+	uint64_t	sb_frextents;	/* free realtime extents */
+
+	/* this is not all... but enough for libblkid */
+
+} __attribute__((packed));
+
+#define XFS_MIN_BLOCKSIZE_LOG	9	/* i.e. 512 bytes */
+#define XFS_MAX_BLOCKSIZE_LOG	16	/* i.e. 65536 bytes */
+#define XFS_MIN_BLOCKSIZE	(1 << XFS_MIN_BLOCKSIZE_LOG)
+#define XFS_MAX_BLOCKSIZE	(1 << XFS_MAX_BLOCKSIZE_LOG)
+#define XFS_MIN_SECTORSIZE_LOG	9	/* i.e. 512 bytes */
+#define XFS_MAX_SECTORSIZE_LOG	15	/* i.e. 32768 bytes */
+#define XFS_MIN_SECTORSIZE	(1 << XFS_MIN_SECTORSIZE_LOG)
+#define XFS_MAX_SECTORSIZE	(1 << XFS_MAX_SECTORSIZE_LOG)
+
+#define	XFS_DINODE_MIN_LOG	8
+#define	XFS_DINODE_MAX_LOG	11
+#define	XFS_DINODE_MIN_SIZE	(1 << XFS_DINODE_MIN_LOG)
+#define	XFS_DINODE_MAX_SIZE	(1 << XFS_DINODE_MAX_LOG)
+
+#define	XFS_MAX_RTEXTSIZE	(1024 * 1024 * 1024)	/* 1GB */
+#define	XFS_DFL_RTEXTSIZE	(64 * 1024)	        /* 64kB */
+#define	XFS_MIN_RTEXTSIZE	(4 * 1024)		/* 4kB */
+
+#define XFS_MIN_AG_BLOCKS	64
+#define XFS_MAX_DBLOCKS(s) ((uint64_t)(s)->sb_agcount * (s)->sb_agblocks)
+#define XFS_MIN_DBLOCKS(s) ((uint64_t)((s)->sb_agcount - 1) *	\
+			 (s)->sb_agblocks + XFS_MIN_AG_BLOCKS)
+
+
+static void sb_from_disk(struct xfs_super_block *from,
+			 struct xfs_super_block *to)
+{
+
+	to->sb_magicnum = be32_to_cpu(from->sb_magicnum);
+	to->sb_blocksize = be32_to_cpu(from->sb_blocksize);
+	to->sb_dblocks = be64_to_cpu(from->sb_dblocks);
+	to->sb_rblocks = be64_to_cpu(from->sb_rblocks);
+	to->sb_rextents = be64_to_cpu(from->sb_rextents);
+	to->sb_logstart = be64_to_cpu(from->sb_logstart);
+	to->sb_rootino = be64_to_cpu(from->sb_rootino);
+	to->sb_rbmino = be64_to_cpu(from->sb_rbmino);
+	to->sb_rsumino = be64_to_cpu(from->sb_rsumino);
+	to->sb_rextsize = be32_to_cpu(from->sb_rextsize);
+	to->sb_agblocks = be32_to_cpu(from->sb_agblocks);
+	to->sb_agcount = be32_to_cpu(from->sb_agcount);
+	to->sb_rbmblocks = be32_to_cpu(from->sb_rbmblocks);
+	to->sb_logblocks = be32_to_cpu(from->sb_logblocks);
+	to->sb_versionnum = be16_to_cpu(from->sb_versionnum);
+	to->sb_sectsize = be16_to_cpu(from->sb_sectsize);
+	to->sb_inodesize = be16_to_cpu(from->sb_inodesize);
+	to->sb_inopblock = be16_to_cpu(from->sb_inopblock);
+	to->sb_blocklog = from->sb_blocklog;
+	to->sb_sectlog = from->sb_sectlog;
+	to->sb_inodelog = from->sb_inodelog;
+	to->sb_inopblog = from->sb_inopblog;
+	to->sb_agblklog = from->sb_agblklog;
+	to->sb_rextslog = from->sb_rextslog;
+	to->sb_inprogress = from->sb_inprogress;
+	to->sb_imax_pct = from->sb_imax_pct;
+	to->sb_icount = be64_to_cpu(from->sb_icount);
+	to->sb_ifree = be64_to_cpu(from->sb_ifree);
+	to->sb_fdblocks = be64_to_cpu(from->sb_fdblocks);
+	to->sb_frextents = be64_to_cpu(from->sb_frextents);
+}
+
+static int xfs_verify_sb(struct xfs_super_block *ondisk)
+{
+	struct xfs_super_block sb, *sbp = &sb;
+
+	/* beXX_to_cpu(), but don't convert UUID and fsname! */
+	sb_from_disk(ondisk, sbp);
+
+	/* sanity checks, we don't want to rely on magic string only */
+	if (sbp->sb_agcount <= 0					||
+	    sbp->sb_sectsize < XFS_MIN_SECTORSIZE			||
+	    sbp->sb_sectsize > XFS_MAX_SECTORSIZE			||
+	    sbp->sb_sectlog < XFS_MIN_SECTORSIZE_LOG			||
+	    sbp->sb_sectlog > XFS_MAX_SECTORSIZE_LOG			||
+	    sbp->sb_sectsize != (1 << sbp->sb_sectlog)			||
+	    sbp->sb_blocksize < XFS_MIN_BLOCKSIZE			||
+	    sbp->sb_blocksize > XFS_MAX_BLOCKSIZE			||
+	    sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG			||
+	    sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG			||
+	    sbp->sb_blocksize != (1 << sbp->sb_blocklog)		||
+	    sbp->sb_inodesize < XFS_DINODE_MIN_SIZE			||
+	    sbp->sb_inodesize > XFS_DINODE_MAX_SIZE			||
+	    sbp->sb_inodelog < XFS_DINODE_MIN_LOG			||
+	    sbp->sb_inodelog > XFS_DINODE_MAX_LOG			||
+	    sbp->sb_inodesize != (1 << sbp->sb_inodelog)		||
+	    (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog)	||
+	    (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE)	||
+	    (sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE)	||
+	    (sbp->sb_imax_pct > 100 /* zero sb_imax_pct is valid */)	||
+	    sbp->sb_dblocks == 0					||
+	    sbp->sb_dblocks > XFS_MAX_DBLOCKS(sbp)			||
+	    sbp->sb_dblocks < XFS_MIN_DBLOCKS(sbp))
+		return 0;
+
+	/* TODO: version 5 has also checksum CRC32, maybe we can check it too */
+
+	return 1;
+}
+
+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 errno ? -errno : 1;
+
+	if (!xfs_verify_sb(xs))
+		return 1;
+
+	if (strlen(xs->sb_fname))
+		blkid_probe_set_label(pr, (unsigned char *) xs->sb_fname,
+				sizeof(xs->sb_fname));
+	blkid_probe_set_uuid(pr, xs->sb_uuid);
+	return 0;
+}
+
+const struct blkid_idinfo xfs_idinfo =
+{
+	.name		= "xfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_xfs,
+	.magics		=
+	{
+		{ .magic = "XFSB", .len = 4 },
+		{ NULL }
+	}
+};
+
+struct xlog_rec_header {
+	uint32_t	h_magicno;
+	uint32_t	h_dummy1[1];
+	uint32_t	h_version;
+	uint32_t	h_len;
+	uint32_t	h_dummy2[71];
+	uint32_t	h_fmt;
+	unsigned char	h_uuid[16];
+} __attribute__((packed));
+
+#define XLOG_HEADER_MAGIC_NUM 0xFEEDbabe
+
+/*
+ * For very small filesystems, the minimum log size
+ * can be smaller, but that seems vanishingly unlikely
+ * when used with an external log (which is used for
+ * performance reasons; tiny conflicts with that goal).
+ */
+#define XFS_MIN_LOG_BYTES	(10 * 1024 * 1024)
+
+#define XLOG_FMT_LINUX_LE	1
+#define XLOG_FMT_LINUX_BE	2
+#define XLOG_FMT_IRIX_BE	3
+
+#define XLOG_VERSION_1		1
+#define XLOG_VERSION_2		2	/* Large IClogs, Log sunit */
+#define XLOG_VERSION_OKBITS	(XLOG_VERSION_1 | XLOG_VERSION_2)
+
+static int xlog_valid_rec_header(struct xlog_rec_header *rhead)
+{
+	uint32_t hlen;
+
+	if (rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
+		return 0;
+
+	if (!rhead->h_version ||
+            (be32_to_cpu(rhead->h_version) & (~XLOG_VERSION_OKBITS)))
+		return 0;
+
+	/* LR body must have data or it wouldn't have been written */
+	hlen = be32_to_cpu(rhead->h_len);
+	if (hlen <= 0 || hlen > INT_MAX)
+		return 0;
+
+	if (rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_LE) &&
+	    rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_BE) &&
+	    rhead->h_fmt != cpu_to_be32(XLOG_FMT_IRIX_BE))
+		return 0;
+
+	return 1;
+}
+
+/* xlog record header will be in some sector in the first 256k */
+static int probe_xfs_log(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	int i;
+	struct xlog_rec_header *rhead;
+	unsigned char *buf;
+
+	buf = blkid_probe_get_buffer(pr, 0, 256*1024);
+	if (!buf)
+		return errno ? -errno : 1;
+
+	if (memcmp(buf, "XFSB", 4) == 0)
+		return 1;			/* this is regular XFS, ignore */
+
+	/* check the first 512 512-byte sectors */
+	for (i = 0; i < 512; i++) {
+		rhead = (struct xlog_rec_header *)&buf[i*512];
+
+		if (xlog_valid_rec_header(rhead)) {
+			blkid_probe_set_uuid_as(pr, rhead->h_uuid, "LOGUUID");
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+const struct blkid_idinfo xfs_log_idinfo =
+{
+	.name		= "xfs_external_log",
+	.usage		= BLKID_USAGE_OTHER,
+	.probefunc	= probe_xfs_log,
+	.magics		= BLKID_NONE_MAGIC,
+	.minsz		= XFS_MIN_LOG_BYTES,
+};
diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c
new file mode 100644
index 0000000..86da59d
--- /dev/null
+++ b/libblkid/src/superblocks/zfs.c
@@ -0,0 +1,233 @@
+/*
+ * 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 <limits.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...)	fprintf(stderr, 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);
+			if (nvs->nvs_strlen > UINT_MAX - sizeof(*nvs))
+				break;
+			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...)	fprintf(stderr, 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, ub_offset = 0;
+	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 errno ? -errno : 1;
+
+		if (ub->ub_magic == UBERBLOCK_MAGIC) {
+			ub_offset = offset;
+			found++;
+		}
+
+		if ((swab_endian = (ub->ub_magic == swab_magic))) {
+			ub_offset = offset;
+			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, ub_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/libblkid/src/sysfs.h b/libblkid/src/sysfs.h
new file mode 100644
index 0000000..1de624a
--- /dev/null
+++ b/libblkid/src/sysfs.h
@@ -0,0 +1,94 @@
+/*
+ * 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 int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str);
+extern int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num);
+
+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 char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz);
+extern int sysfs_next_subsystem(struct sysfs_cxt *cxt, char *devchain, char **subsys);
+extern int sysfs_is_hotpluggable(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_devno_is_lvm_private(dev_t devno);
+extern int sysfs_devno_is_wholedisk(dev_t devno);
+
+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/src/tag.c b/libblkid/src/tag.c
new file mode 100644
index 0000000..6af8062
--- /dev/null
+++ b/libblkid/src/tag.c
@@ -0,0 +1,481 @@
+/*
+ * 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;
+}
+
+void blkid_debug_dump_tag(blkid_tag tag)
+{
+	if (!tag) {
+		fprintf(stderr, "    tag: NULL\n");
+		return;
+	}
+
+	fprintf(stderr, "    tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
+}
+
+void blkid_free_tag(blkid_tag tag)
+{
+	if (!tag)
+		return;
+
+	DBG(TAG, ul_debug("    freeing tag %s=%s", tag->bit_name,
+		   tag->bit_val ? tag->bit_val : "(NULL)"));
+	DBG(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;
+}
+
+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(TAG, ul_debug("    found cache tag head %s", 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(TAG, ul_debug("    creating new cache tag head %s", 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(TAG, ul_debug("trying to parse '%s' as a tag", 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 (ret_val) {
+		value = value && *value ? strdup(value) : NULL;
+		if (!value)
+			goto errout;
+		*ret_val = value;
+	}
+
+	if (ret_type)
+		*ret_type = name;
+	else
+		free(name);
+
+	return 0;
+
+errout:
+	DBG(TAG, ul_debug("parse error: '%s'", token));
+	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;
+};
+
+blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
+{
+	blkid_tag_iterate	iter;
+
+	if (!dev) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	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
+ */
+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;
+}
+
+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.
+ */
+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(TAG, ul_debug("looking for %s=%s in cache", 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/src/topology/dm.c b/libblkid/src/topology/dm.c
new file mode 100644
index 0000000..e061632
--- /dev/null
+++ b/libblkid/src/topology/dm.c
@@ -0,0 +1,135 @@
+/*
+ * 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(LOWPROBE, ul_debug("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(LOWPROBE, ul_debug("Failed to execute %s: errno=%d", cmd, errno));
+		exit(1);
+	}
+	case -1:
+		DBG(LOWPROBE, ul_debug("Failed to forking: errno=%d", errno));
+		goto nothing;
+	default:
+		break;
+	}
+
+	stream = fdopen(dmpipe[0], "r" UL_CLOEXECSTR);
+	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/src/topology/evms.c b/libblkid/src/topology/evms.c
new file mode 100644
index 0000000..7a4fd55
--- /dev/null
+++ b/libblkid/src/topology/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/src/topology/ioctl.c b/libblkid/src/topology/ioctl.c
new file mode 100644
index 0000000..3aba09e
--- /dev/null
+++ b/libblkid/src/topology/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/src/topology/lvm.c b/libblkid/src/topology/lvm.c
new file mode 100644
index 0000000..bd079d4
--- /dev/null
+++ b/libblkid/src/topology/lvm.c
@@ -0,0 +1,147 @@
+/*
+ * 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(LOWPROBE, ul_debug("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(LOWPROBE, ul_debug("Failed to execute %s: errno=%d", cmd, errno));
+		exit(1);
+	}
+	case -1:
+		DBG(LOWPROBE, ul_debug("Failed to forking: errno=%d", errno));
+		goto nothing;
+	default:
+		break;
+	}
+
+	stream = fdopen(lvpipe[0], "r" UL_CLOEXECSTR);
+	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/src/topology/md.c b/libblkid/src/topology/md.c
new file mode 100644
index 0000000..5eba947
--- /dev/null
+++ b/libblkid/src/topology/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/src/topology/sysfs.c b/libblkid/src/topology/sysfs.c
new file mode 100644
index 0000000..a04b20a
--- /dev/null
+++ b/libblkid/src/topology/sysfs.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/src/topology/topology.c b/libblkid/src/topology/topology.c
new file mode 100644
index 0000000..93fa380
--- /dev/null
+++ b/libblkid/src/topology/topology.c
@@ -0,0 +1,362 @@
+/*
+ * 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 -EINVAL;	/* nothing, works with block devices only */
+
+	if (chn->binary) {
+		DBG(LOWPROBE, ul_debug("initialize topology binary data"));
+
+		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 -ENOMEM;
+		}
+	}
+
+	blkid_probe_chain_reset_vals(pr, chn);
+
+	DBG(LOWPROBE, ul_debug("--> starting probing loop [TOPOLOGY idx=%d]",
+		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(LOWPROBE, ul_debug("%s: call probefunc()", 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(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [TOPOLOGY idx=%d]",
+			id->name, chn->idx));
+		return BLKID_PROBE_OK;
+	}
+
+	DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed) [TOPOLOGY idx=%d]",
+		chn->idx));
+	return BLKID_PROBE_NONE;
+}
+
+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/src/topology/topology.h b/libblkid/src/topology/topology.h
new file mode 100644
index 0000000..6d2f433
--- /dev/null
+++ b/libblkid/src/topology/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/src/verify.c b/libblkid/src/verify.c
new file mode 100644
index 0000000..06f6d53
--- /dev/null
+++ b/libblkid/src/verify.c
@@ -0,0 +1,224 @@
+/*
+ * 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"
+#include "sysfs.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(PROBE, ul_debug("blkid_verify: error %m (%d) while "
+			   "trying to stat %s", errno,
+			   dev->bid_name));
+	open_err:
+		if ((errno == EPERM) || (errno == EACCES) || (errno == ENOENT)) {
+			/* We don't have read permission, just return cache data. */
+			DBG(PROBE, ul_debug("returning unverified data for %s",
+						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(PROBE, ul_debug("need to revalidate %s (cache time %lu, stat time %lu,\t"
+		   "time since last check %lu)",
+		   dev->bid_name, (unsigned long)dev->bid_time,
+		   (unsigned long)st.st_mtime, (unsigned long)diff));
+#else
+	DBG(PROBE, ul_debug("need to revalidate %s (cache time %lu.%lu, stat time %lu.%lu,\t"
+		   "time since last check %lu)",
+		   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 (sysfs_devno_is_lvm_private(st.st_rdev)) {
+		blkid_free_dev(dev);
+		return NULL;
+	}
+	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(PROBE, ul_debug("blkid_verify: error %m (%d) while "
+					"opening %s", 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(PROBE, ul_debug("%s: devno 0x%04llx, type %s",
+			   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/src/version.c b/libblkid/src/version.c
new file mode 100644
index 0000000..9d129f7
--- /dev/null
+++ b/libblkid/src/version.c
@@ -0,0 +1,62 @@
+/*
+ * 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>
+
+/* 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/src/widechar.h b/libblkid/src/widechar.h
new file mode 100644
index 0000000..b023b5f
--- /dev/null
+++ b/libblkid/src/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/src/xalloc.h b/libblkid/src/xalloc.h
new file mode 100644
index 0000000..f012fb2
--- /dev/null
+++ b/libblkid/src/xalloc.h
@@ -0,0 +1,118 @@
+/*
+ * 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 __attribute__((warn_unused_result)) *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 char * __attribute__((warn_unused_result)) xstrndup(const char *str, size_t size)
+{
+        char *ret;
+
+        if (!str)
+                return NULL;
+
+        ret = strndup(str, size);
+
+        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 int xvasprintf(char **strp, const char *fmt, va_list ap)
+{
+	int ret = vasprintf(&(*strp), fmt, ap);
+	if (ret < 0)
+		err(XALLOC_EXIT_CODE, "cannot allocate string");
+	return ret;
+}
+
+
+static inline char * __attribute__((warn_unused_result)) xgethostname(void)
+{
+	char *name;
+	size_t sz = get_hostname_max() + 1;
+
+	name = xmalloc(sizeof(char) * sz);
+
+	if (gethostname(name, sz) != 0) {
+		free(name);
+		return NULL;
+	}
+	name[sz - 1] = '\0';
+	return name;
+}
+
+#endif
diff --git a/libcrecovery/Android.mk b/libcrecovery/Android.mk
new file mode 100644
index 0000000..8434d51
--- /dev/null
+++ b/libcrecovery/Android.mk
@@ -0,0 +1,17 @@
+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)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := system.c popen.c
+LOCAL_MODULE := libcrecovery
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_SHARED_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..c5dd550
--- /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)  signal(SIGINT, SIG_IGN);
+	quitsave = (sig_t) signal(SIGQUIT, SIG_IGN);
+	pid = waitpid(pid, (int *)&pstat, 0);
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+	(void)signal(SIGINT, intsave);
+	(void)signal(SIGQUIT, quitsave);
+	return (pid == -1 ? -1 : pstat);
+}
diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk
new file mode 100644
index 0000000..774e918
--- /dev/null
+++ b/libmincrypt/Android.mk
@@ -0,0 +1,27 @@
+# Copyright 2008 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmincrypttwrp
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)/libmincrypt/includes
+LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmincrypttwrp
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)/libmincrypt/includes
+LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmincrypttwrp
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)/libmincrypt/includes
+LOCAL_SRC_FILES := dsa_sig.c p256.c p256_ec.c p256_ecdsa.c rsa.c sha.c sha256.c
+LOCAL_CFLAGS := -Wall -Werror
+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/dsa_sig.c b/libmincrypt/dsa_sig.c
new file mode 100644
index 0000000..101314b
--- /dev/null
+++ b/libmincrypt/dsa_sig.c
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+
+#include "mincrypt/dsa_sig.h"
+#include "mincrypt/p256.h"
+
+/**
+ * Trims off the leading zero bytes and copy it to a buffer aligning it to the end.
+ */
+static inline int trim_to_p256_bytes(unsigned char dst[P256_NBYTES], unsigned char *src,
+        int src_len) {
+    int dst_offset;
+    while (*src == '\0' && src_len > 0) {
+        src++;
+        src_len--;
+    }
+    if (src_len > P256_NBYTES || src_len < 1) {
+        return 0;
+    }
+    dst_offset = P256_NBYTES - src_len;
+    memset(dst, 0, dst_offset);
+    memcpy(dst + dst_offset, src, src_len);
+    return 1;
+}
+
+/**
+ * Unpacks the ASN.1 DSA signature sequence.
+ */
+int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int) {
+    /*
+     * Structure is:
+     *   0x30 0xNN  SEQUENCE + s_length
+     *     0x02 0xNN  INTEGER + r_length
+     *       0xAA 0xBB ..   r_length bytes of "r" (offset 4)
+     *     0x02 0xNN  INTEGER + s_length
+     *       0xMM 0xNN ..   s_length bytes of "s" (offset 6 + r_len)
+     */
+    int seq_len;
+    unsigned char r_bytes[P256_NBYTES];
+    unsigned char s_bytes[P256_NBYTES];
+    int r_len;
+    int s_len;
+
+    memset(r_bytes, 0, sizeof(r_bytes));
+    memset(s_bytes, 0, sizeof(s_bytes));
+
+    /*
+     * Must have at least:
+     * 2 bytes sequence header and length
+     * 2 bytes R integer header and length
+     * 1 byte of R
+     * 2 bytes S integer header and length
+     * 1 byte of S
+     *
+     * 8 bytes total
+     */
+    if (sig_len < 8 || sig[0] != 0x30 || sig[2] != 0x02) {
+        return 0;
+    }
+
+    seq_len = sig[1];
+    if ((seq_len <= 0) || (seq_len + 2 != sig_len)) {
+        return 0;
+    }
+
+    r_len = sig[3];
+    /*
+     * Must have at least:
+     * 2 bytes for R header and length
+     * 2 bytes S integer header and length
+     * 1 byte of S
+     */
+    if ((r_len < 1) || (r_len > seq_len - 5) || (sig[4 + r_len] != 0x02)) {
+        return 0;
+    }
+    s_len = sig[5 + r_len];
+
+    /**
+     * Must have:
+     * 2 bytes for R header and length
+     * r_len bytes for R
+     * 2 bytes S integer header and length
+     */
+    if ((s_len < 1) || (s_len != seq_len - 4 - r_len)) {
+        return 0;
+    }
+
+    /*
+     * ASN.1 encoded integers are zero-padded for positive integers. Make sure we have
+     * a correctly-sized buffer and that the resulting integer isn't too large.
+     */
+    if (!trim_to_p256_bytes(r_bytes, &sig[4], r_len)
+            || !trim_to_p256_bytes(s_bytes, &sig[6 + r_len], s_len)) {
+        return 0;
+    }
+
+    p256_from_bin(r_bytes, r_int);
+    p256_from_bin(s_bytes, s_int);
+
+    return 1;
+}
diff --git a/libmincrypt/includes/mincrypt/dsa_sig.h b/libmincrypt/includes/mincrypt/dsa_sig.h
new file mode 100644
index 0000000..b0d91cd
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/dsa_sig.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_
+
+#include "mincrypt/p256.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns 0 if input sig is not a valid ASN.1 sequence
+int dsa_sig_unpack(unsigned char* sig, int sig_len, p256_int* r_int, p256_int* s_int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSTEM_CORE_INCLUDE_MINCRYPT_DSA_SIG_H_ */
diff --git a/libmincrypt/includes/mincrypt/hash-internal.h b/libmincrypt/includes/mincrypt/hash-internal.h
new file mode 100644
index 0000000..c813b44
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/hash-internal.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 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 SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_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  // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
diff --git a/libmincrypt/includes/mincrypt/p256.h b/libmincrypt/includes/mincrypt/p256.h
new file mode 100644
index 0000000..465a1b9
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/p256.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
+
+// Collection of routines manipulating 256 bit unsigned integers.
+// Just enough to implement ecdsa-p256 and related algorithms.
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define P256_BITSPERDIGIT 32
+#define P256_NDIGITS 8
+#define P256_NBYTES 32
+
+typedef int p256_err;
+typedef uint32_t p256_digit;
+typedef int32_t p256_sdigit;
+typedef uint64_t p256_ddigit;
+typedef int64_t p256_sddigit;
+
+// Defining p256_int as struct to leverage struct assigment.
+typedef struct {
+  p256_digit a[P256_NDIGITS];
+} p256_int;
+
+extern const p256_int SECP256r1_n;  // Curve order
+extern const p256_int SECP256r1_p;  // Curve prime
+extern const p256_int SECP256r1_b;  // Curve param
+
+// Initialize a p256_int to zero.
+void p256_init(p256_int* a);
+
+// Clear a p256_int to zero.
+void p256_clear(p256_int* a);
+
+// Return bit. Index 0 is least significant.
+int p256_get_bit(const p256_int* a, int index);
+
+// b := a % MOD
+void p256_mod(
+    const p256_int* MOD,
+    const p256_int* a,
+    p256_int* b);
+
+// c := a * (top_b | b) % MOD
+void p256_modmul(
+    const p256_int* MOD,
+    const p256_int* a,
+    const p256_digit top_b,
+    const p256_int* b,
+    p256_int* c);
+
+// b := 1 / a % MOD
+// MOD best be SECP256r1_n
+void p256_modinv(
+    const p256_int* MOD,
+    const p256_int* a,
+    p256_int* b);
+
+// b := 1 / a % MOD
+// MOD best be SECP256r1_n
+// Faster than p256_modinv()
+void p256_modinv_vartime(
+    const p256_int* MOD,
+    const p256_int* a,
+    p256_int* b);
+
+// b := a << (n % P256_BITSPERDIGIT)
+// Returns the bits shifted out of most significant digit.
+p256_digit p256_shl(const p256_int* a, int n, p256_int* b);
+
+// b := a >> (n % P256_BITSPERDIGIT)
+void p256_shr(const p256_int* a, int n, p256_int* b);
+
+int p256_is_zero(const p256_int* a);
+int p256_is_odd(const p256_int* a);
+int p256_is_even(const p256_int* a);
+
+// Returns -1, 0 or 1.
+int p256_cmp(const p256_int* a, const p256_int *b);
+
+// c: = a - b
+// Returns -1 on borrow.
+int p256_sub(const p256_int* a, const p256_int* b, p256_int* c);
+
+// c := a + b
+// Returns 1 on carry.
+int p256_add(const p256_int* a, const p256_int* b, p256_int* c);
+
+// c := a + (single digit)b
+// Returns carry 1 on carry.
+int p256_add_d(const p256_int* a, p256_digit b, p256_int* c);
+
+// ec routines.
+
+// {out_x,out_y} := nG
+void p256_base_point_mul(const p256_int *n,
+                         p256_int *out_x,
+                         p256_int *out_y);
+
+// {out_x,out_y} := n{in_x,in_y}
+void p256_point_mul(const p256_int *n,
+                    const p256_int *in_x,
+                    const p256_int *in_y,
+                    p256_int *out_x,
+                    p256_int *out_y);
+
+// {out_x,out_y} := n1G + n2{in_x,in_y}
+void p256_points_mul_vartime(
+    const p256_int *n1, const p256_int *n2,
+    const p256_int *in_x, const p256_int *in_y,
+    p256_int *out_x, p256_int *out_y);
+
+// Return whether point {x,y} is on curve.
+int p256_is_valid_point(const p256_int* x, const p256_int* y);
+
+// Outputs big-endian binary form. No leading zero skips.
+void p256_to_bin(const p256_int* src, uint8_t dst[P256_NBYTES]);
+
+// Reads from big-endian binary form,
+// thus pre-pad with leading zeros if short.
+void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst);
+
+#define P256_DIGITS(x) ((x)->a)
+#define P256_DIGIT(x,y) ((x)->a[y])
+
+#define P256_ZERO {{0}}
+#define P256_ONE {{1}}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // SYSTEM_CORE_INCLUDE_MINCRYPT_LITE_P256_H_
diff --git a/libmincrypt/includes/mincrypt/p256_ecdsa.h b/libmincrypt/includes/mincrypt/p256_ecdsa.h
new file mode 100644
index 0000000..da339fa
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/p256_ecdsa.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
+
+// Using current directory as relative include path here since
+// this code typically gets lifted into a variety of build systems
+// and directory structures.
+#include "p256.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns 0 if {r,s} is not a signature on message for
+// public key {key_x,key_y}.
+//
+// Note: message is a p256_int.
+// Convert from a binary string using p256_from_bin().
+int p256_ecdsa_verify(const p256_int* key_x,
+                      const p256_int* key_y,
+                      const p256_int* message,
+                      const p256_int* r, const p256_int* s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // SYSTEM_CORE_INCLUDE_MINCRYPT_P256_ECDSA_H_
diff --git a/libmincrypt/includes/mincrypt/rsa.h b/libmincrypt/includes/mincrypt/rsa.h
new file mode 100644
index 0000000..3d0556b
--- /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 SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_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 // SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_
diff --git a/libmincrypt/includes/mincrypt/sha.h b/libmincrypt/includes/mincrypt/sha.h
new file mode 100644
index 0000000..ef60aab
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/sha.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2005 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 SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_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  // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_
diff --git a/libmincrypt/includes/mincrypt/sha256.h b/libmincrypt/includes/mincrypt/sha256.h
new file mode 100644
index 0000000..3a87c31
--- /dev/null
+++ b/libmincrypt/includes/mincrypt/sha256.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 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 SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_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  // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
diff --git a/libmincrypt/p256.c b/libmincrypt/p256.c
new file mode 100644
index 0000000..555a07a
--- /dev/null
+++ b/libmincrypt/p256.c
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ */
+
+// This is an implementation of the P256 elliptic curve group. It's written to
+// be portable 32-bit, although it's still constant-time.
+//
+// WARNING: Implementing these functions in a constant-time manner is far from
+//          obvious. Be careful when touching this code.
+//
+// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "mincrypt/p256.h"
+
+const p256_int SECP256r1_n =  // curve order
+  {{0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, -1, -1, 0, -1}};
+
+const p256_int SECP256r1_p =  // curve field size
+  {{-1, -1, -1, 0, 0, 0, 1, -1 }};
+
+const p256_int SECP256r1_b =  // curve b
+  {{0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0,
+    0x769886bc, 0xb3ebbd55, 0xaa3a93e7, 0x5ac635d8}};
+
+void p256_init(p256_int* a) {
+  memset(a, 0, sizeof(*a));
+}
+
+void p256_clear(p256_int* a) { p256_init(a); }
+
+int p256_get_bit(const p256_int* scalar, int bit) {
+  return (P256_DIGIT(scalar, bit / P256_BITSPERDIGIT)
+              >> (bit & (P256_BITSPERDIGIT - 1))) & 1;
+}
+
+int p256_is_zero(const p256_int* a) {
+  int i, result = 0;
+  for (i = 0; i < P256_NDIGITS; ++i) result |= P256_DIGIT(a, i);
+  return !result;
+}
+
+// top, c[] += a[] * b
+// Returns new top
+static p256_digit mulAdd(const p256_int* a,
+                         p256_digit b,
+                         p256_digit top,
+                         p256_digit* c) {
+  int i;
+  p256_ddigit carry = 0;
+
+  for (i = 0; i < P256_NDIGITS; ++i) {
+    carry += *c;
+    carry += (p256_ddigit)P256_DIGIT(a, i) * b;
+    *c++ = (p256_digit)carry;
+    carry >>= P256_BITSPERDIGIT;
+  }
+  return top + (p256_digit)carry;
+}
+
+// top, c[] -= top_a, a[]
+static p256_digit subTop(p256_digit top_a,
+                         const p256_digit* a,
+                         p256_digit top_c,
+                         p256_digit* c) {
+  int i;
+  p256_sddigit borrow = 0;
+
+  for (i = 0; i < P256_NDIGITS; ++i) {
+    borrow += *c;
+    borrow -= *a++;
+    *c++ = (p256_digit)borrow;
+    borrow >>= P256_BITSPERDIGIT;
+  }
+  borrow += top_c;
+  borrow -= top_a;
+  top_c = (p256_digit)borrow;
+  assert((borrow >> P256_BITSPERDIGIT) == 0);
+  return top_c;
+}
+
+// top, c[] -= MOD[] & mask (0 or -1)
+// returns new top.
+static p256_digit subM(const p256_int* MOD,
+                       p256_digit top,
+                       p256_digit* c,
+                       p256_digit mask) {
+  int i;
+  p256_sddigit borrow = 0;
+  for (i = 0; i < P256_NDIGITS; ++i) {
+    borrow += *c;
+    borrow -= P256_DIGIT(MOD, i) & mask;
+    *c++ = (p256_digit)borrow;
+    borrow >>= P256_BITSPERDIGIT;
+  }
+  return top + (p256_digit)borrow;
+}
+
+// top, c[] += MOD[] & mask (0 or -1)
+// returns new top.
+static p256_digit addM(const p256_int* MOD,
+                       p256_digit top,
+                       p256_digit* c,
+                       p256_digit mask) {
+  int i;
+  p256_ddigit carry = 0;
+  for (i = 0; i < P256_NDIGITS; ++i) {
+    carry += *c;
+    carry += P256_DIGIT(MOD, i) & mask;
+    *c++ = (p256_digit)carry;
+    carry >>= P256_BITSPERDIGIT;
+  }
+  return top + (p256_digit)carry;
+}
+
+// c = a * b mod MOD. c can be a and/or b.
+void p256_modmul(const p256_int* MOD,
+                 const p256_int* a,
+                 const p256_digit top_b,
+                 const p256_int* b,
+                 p256_int* c) {
+  p256_digit tmp[P256_NDIGITS * 2 + 1] = { 0 };
+  p256_digit top = 0;
+  int i;
+
+  // Multiply/add into tmp.
+  for (i = 0; i < P256_NDIGITS; ++i) {
+    if (i) tmp[i + P256_NDIGITS - 1] = top;
+    top = mulAdd(a, P256_DIGIT(b, i), 0, tmp + i);
+  }
+
+  // Multiply/add top digit
+  tmp[i + P256_NDIGITS - 1] = top;
+  top = mulAdd(a, top_b, 0, tmp + i);
+
+  // Reduce tmp, digit by digit.
+  for (; i >= 0; --i) {
+    p256_digit reducer[P256_NDIGITS] = { 0 };
+    p256_digit top_reducer;
+
+    // top can be any value at this point.
+    // Guestimate reducer as top * MOD, since msw of MOD is -1.
+    top_reducer = mulAdd(MOD, top, 0, reducer);
+
+    // Subtract reducer from top | tmp.
+    top = subTop(top_reducer, reducer, top, tmp + i);
+
+    // top is now either 0 or 1. Make it 0, fixed-timing.
+    assert(top <= 1);
+
+    top = subM(MOD, top, tmp + i, ~(top - 1));
+
+    assert(top == 0);
+
+    // We have now reduced the top digit off tmp. Fetch new top digit.
+    top = tmp[i + P256_NDIGITS - 1];
+  }
+
+  // tmp might still be larger than MOD, yet same bit length.
+  // Make sure it is less, fixed-timing.
+  addM(MOD, 0, tmp, subM(MOD, 0, tmp, -1));
+
+  memcpy(c, tmp, P256_NBYTES);
+}
+int p256_is_odd(const p256_int* a) { return P256_DIGIT(a, 0) & 1; }
+int p256_is_even(const p256_int* a) { return !(P256_DIGIT(a, 0) & 1); }
+
+p256_digit p256_shl(const p256_int* a, int n, p256_int* b) {
+  int i;
+  p256_digit top = P256_DIGIT(a, P256_NDIGITS - 1);
+
+  n %= P256_BITSPERDIGIT;
+  for (i = P256_NDIGITS - 1; i > 0; --i) {
+    p256_digit accu = (P256_DIGIT(a, i) << n);
+    accu |= (P256_DIGIT(a, i - 1) >> (P256_BITSPERDIGIT - n));
+    P256_DIGIT(b, i) = accu;
+  }
+  P256_DIGIT(b, i) = (P256_DIGIT(a, i) << n);
+
+  top = (p256_digit)((((p256_ddigit)top) << n) >> P256_BITSPERDIGIT);
+
+  return top;
+}
+
+void p256_shr(const p256_int* a, int n, p256_int* b) {
+  int i;
+
+  n %= P256_BITSPERDIGIT;
+  for (i = 0; i < P256_NDIGITS - 1; ++i) {
+    p256_digit accu = (P256_DIGIT(a, i) >> n);
+    accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - n));
+    P256_DIGIT(b, i) = accu;
+  }
+  P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> n);
+}
+
+static void p256_shr1(const p256_int* a, int highbit, p256_int* b) {
+  int i;
+
+  for (i = 0; i < P256_NDIGITS - 1; ++i) {
+    p256_digit accu = (P256_DIGIT(a, i) >> 1);
+    accu |= (P256_DIGIT(a, i + 1) << (P256_BITSPERDIGIT - 1));
+    P256_DIGIT(b, i) = accu;
+  }
+  P256_DIGIT(b, i) = (P256_DIGIT(a, i) >> 1) |
+      (highbit << (P256_BITSPERDIGIT - 1));
+}
+
+// Return -1, 0, 1 for a < b, a == b or a > b respectively.
+int p256_cmp(const p256_int* a, const p256_int* b) {
+  int i;
+  p256_sddigit borrow = 0;
+  p256_digit notzero = 0;
+
+  for (i = 0; i < P256_NDIGITS; ++i) {
+    borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i);
+    // Track whether any result digit is ever not zero.
+    // Relies on !!(non-zero) evaluating to 1, e.g., !!(-1) evaluating to 1.
+    notzero |= !!((p256_digit)borrow);
+    borrow >>= P256_BITSPERDIGIT;
+  }
+  return (int)borrow | notzero;
+}
+
+// c = a - b. Returns borrow: 0 or -1.
+int p256_sub(const p256_int* a, const p256_int* b, p256_int* c) {
+  int i;
+  p256_sddigit borrow = 0;
+
+  for (i = 0; i < P256_NDIGITS; ++i) {
+    borrow += (p256_sddigit)P256_DIGIT(a, i) - P256_DIGIT(b, i);
+    if (c) P256_DIGIT(c, i) = (p256_digit)borrow;
+    borrow >>= P256_BITSPERDIGIT;
+  }
+  return (int)borrow;
+}
+
+// c = a + b. Returns carry: 0 or 1.
+int p256_add(const p256_int* a, const p256_int* b, p256_int* c) {
+  int i;
+  p256_ddigit carry = 0;
+
+  for (i = 0; i < P256_NDIGITS; ++i) {
+    carry += (p256_ddigit)P256_DIGIT(a, i) + P256_DIGIT(b, i);
+    if (c) P256_DIGIT(c, i) = (p256_digit)carry;
+    carry >>= P256_BITSPERDIGIT;
+  }
+  return (int)carry;
+}
+
+// b = a + d. Returns carry, 0 or 1.
+int p256_add_d(const p256_int* a, p256_digit d, p256_int* b) {
+  int i;
+  p256_ddigit carry = d;
+
+  for (i = 0; i < P256_NDIGITS; ++i) {
+    carry += (p256_ddigit)P256_DIGIT(a, i);
+    if (b) P256_DIGIT(b, i) = (p256_digit)carry;
+    carry >>= P256_BITSPERDIGIT;
+  }
+  return (int)carry;
+}
+
+// b = 1/a mod MOD, binary euclid.
+void p256_modinv_vartime(const p256_int* MOD,
+                         const p256_int* a,
+                         p256_int* b) {
+  p256_int R = P256_ZERO;
+  p256_int S = P256_ONE;
+  p256_int U = *MOD;
+  p256_int V = *a;
+
+  for (;;) {
+    if (p256_is_even(&U)) {
+      p256_shr1(&U, 0, &U);
+      if (p256_is_even(&R)) {
+        p256_shr1(&R, 0, &R);
+      } else {
+        // R = (R+MOD)/2
+        p256_shr1(&R, p256_add(&R, MOD, &R), &R);
+      }
+    } else if (p256_is_even(&V)) {
+      p256_shr1(&V, 0, &V);
+      if (p256_is_even(&S)) {
+        p256_shr1(&S, 0, &S);
+      } else {
+        // S = (S+MOD)/2
+        p256_shr1(&S, p256_add(&S, MOD, &S) , &S);
+      }
+    } else {  // U,V both odd.
+      if (!p256_sub(&V, &U, NULL)) {
+        p256_sub(&V, &U, &V);
+        if (p256_sub(&S, &R, &S)) p256_add(&S, MOD, &S);
+        if (p256_is_zero(&V)) break;  // done.
+      } else {
+        p256_sub(&U, &V, &U);
+        if (p256_sub(&R, &S, &R)) p256_add(&R, MOD, &R);
+      }
+    }
+  }
+
+  p256_mod(MOD, &R, b);
+}
+
+void p256_mod(const p256_int* MOD,
+              const p256_int* in,
+              p256_int* out) {
+  if (out != in) *out = *in;
+  addM(MOD, 0, P256_DIGITS(out), subM(MOD, 0, P256_DIGITS(out), -1));
+}
+
+// Verify y^2 == x^3 - 3x + b mod p
+// and 0 < x < p and 0 < y < p
+int p256_is_valid_point(const p256_int* x, const p256_int* y) {
+  p256_int y2, x3;
+
+  if (p256_cmp(&SECP256r1_p, x) <= 0 ||
+      p256_cmp(&SECP256r1_p, y) <= 0 ||
+      p256_is_zero(x) ||
+      p256_is_zero(y)) return 0;
+
+  p256_modmul(&SECP256r1_p, y, 0, y, &y2);  // y^2
+
+  p256_modmul(&SECP256r1_p, x, 0, x, &x3);  // x^2
+  p256_modmul(&SECP256r1_p, x, 0, &x3, &x3);  // x^3
+  if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3);  // x^3 - x
+  if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3);  // x^3 - 2x
+  if (p256_sub(&x3, x, &x3)) p256_add(&x3, &SECP256r1_p, &x3);  // x^3 - 3x
+  if (p256_add(&x3, &SECP256r1_b, &x3))  // x^3 - 3x + b
+    p256_sub(&x3, &SECP256r1_p, &x3);
+
+  return p256_cmp(&y2, &x3) == 0;
+}
+
+void p256_from_bin(const uint8_t src[P256_NBYTES], p256_int* dst) {
+  int i;
+  const uint8_t* p = &src[0];
+
+  for (i = P256_NDIGITS - 1; i >= 0; --i) {
+    P256_DIGIT(dst, i) =
+        (p[0] << 24) |
+        (p[1] << 16) |
+        (p[2] << 8) |
+        p[3];
+    p += 4;
+  }
+}
diff --git a/libmincrypt/p256_ec.c b/libmincrypt/p256_ec.c
new file mode 100644
index 0000000..90262cc
--- /dev/null
+++ b/libmincrypt/p256_ec.c
@@ -0,0 +1,1279 @@
+/*
+ * 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.
+ */
+
+// This is an implementation of the P256 elliptic curve group. It's written to
+// be portable 32-bit, although it's still constant-time.
+//
+// WARNING: Implementing these functions in a constant-time manner is far from
+//          obvious. Be careful when touching this code.
+//
+// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "mincrypt/p256.h"
+
+typedef uint8_t u8;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint64_t u64;
+
+/* Our field elements are represented as nine 32-bit limbs.
+ *
+ * The value of an felem (field element) is:
+ *   x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228)
+ *
+ * That is, each limb is alternately 29 or 28-bits wide in little-endian
+ * order.
+ *
+ * This means that an felem hits 2**257, rather than 2**256 as we would like. A
+ * 28, 29, ... pattern would cause us to hit 2**256, but that causes problems
+ * when multiplying as terms end up one bit short of a limb which would require
+ * much bit-shifting to correct.
+ *
+ * Finally, the values stored in an felem are in Montgomery form. So the value
+ * |y| is stored as (y*R) mod p, where p is the P-256 prime and R is 2**257.
+ */
+typedef u32 limb;
+#define NLIMBS 9
+typedef limb felem[NLIMBS];
+
+static const limb kBottom28Bits = 0xfffffff;
+static const limb kBottom29Bits = 0x1fffffff;
+
+/* kOne is the number 1 as an felem. It's 2**257 mod p split up into 29 and
+ * 28-bit words. */
+static const felem kOne = {
+    2, 0, 0, 0xffff800,
+    0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff,
+    0
+};
+static const felem kZero = {0};
+static const felem kP = {
+    0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff,
+    0, 0, 0x200000, 0xf000000,
+    0xfffffff
+};
+static const felem k2P = {
+    0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff,
+    0, 0, 0x400000, 0xe000000,
+    0x1fffffff
+};
+/* kPrecomputed contains precomputed values to aid the calculation of scalar
+ * multiples of the base point, G. It's actually two, equal length, tables
+ * concatenated.
+ *
+ * The first table contains (x,y) felem pairs for 16 multiples of the base
+ * point, G.
+ *
+ *   Index  |  Index (binary) | Value
+ *       0  |           0000  | 0G (all zeros, omitted)
+ *       1  |           0001  | G
+ *       2  |           0010  | 2**64G
+ *       3  |           0011  | 2**64G + G
+ *       4  |           0100  | 2**128G
+ *       5  |           0101  | 2**128G + G
+ *       6  |           0110  | 2**128G + 2**64G
+ *       7  |           0111  | 2**128G + 2**64G + G
+ *       8  |           1000  | 2**192G
+ *       9  |           1001  | 2**192G + G
+ *      10  |           1010  | 2**192G + 2**64G
+ *      11  |           1011  | 2**192G + 2**64G + G
+ *      12  |           1100  | 2**192G + 2**128G
+ *      13  |           1101  | 2**192G + 2**128G + G
+ *      14  |           1110  | 2**192G + 2**128G + 2**64G
+ *      15  |           1111  | 2**192G + 2**128G + 2**64G + G
+ *
+ * The second table follows the same style, but the terms are 2**32G,
+ * 2**96G, 2**160G, 2**224G.
+ *
+ * This is ~2KB of data. */
+static const limb kPrecomputed[NLIMBS * 2 * 15 * 2] = {
+    0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, 0x119e7edc, 0xd4a6eab, 0x3120bee,
+    0x1d2aac15, 0xf25357c, 0x19e45cdd, 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3,
+    0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, 0x1fe49073, 0x3fa36cc, 0x5ebcd2c,
+    0xb402f2f, 0x15c70bf, 0x1561925c, 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22,
+    0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, 0x56c7109, 0xa267a00, 0xb57c050,
+    0x98fb57, 0xaa837cc, 0x60c0792, 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b,
+    0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, 0x1d96a5a9, 0x843a649, 0xc3ab0fa,
+    0x6e2e7d5, 0x7673a2a, 0x178b65e8, 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2,
+    0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, 0x172db40f, 0x83e277d, 0xb0dd609,
+    0xfd1da12, 0x35c6e52, 0x19ede20c, 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581,
+    0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, 0xc35df9f, 0x48764cd, 0x76dbcca,
+    0xca4b366, 0xe9303ab, 0x1a7480e7, 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33,
+    0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6,
+    0x14e614aa, 0x9246317, 0x1bc83aca, 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd,
+    0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, 0x6a2901a, 0x69a8556, 0x7e7c0,
+    0x9b7d8d3, 0x309a80, 0x1ad05f7f, 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881,
+    0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, 0xbe80c51, 0xc22be3e, 0xe35e65a,
+    0x1aeb7aa0, 0xc375315, 0xf67bc99, 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26,
+    0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, 0x1a90c502, 0x2f32042, 0xa17769b,
+    0xafd8c7c, 0x8191c6e, 0x1dcdb237, 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023,
+    0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, 0x2fc513c, 0x407f75c, 0xbaab133,
+    0x9705fe9, 0xb88b8e7, 0x734c993, 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa,
+    0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, 0xbc62f16, 0x2b6fcc7, 0xf5a4e29,
+    0x13560b66, 0xc0b6ac2, 0x51ae690, 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc,
+    0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, 0x1f83de85, 0x27de188, 0x66f70b8,
+    0x181cd51f, 0x96b6e4c, 0x188f2335, 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59,
+    0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, 0x79061, 0x823d9d2, 0x8213f39,
+    0x1128ae0b, 0xd095d05, 0xb85c0c2, 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689,
+    0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, 0x1051a729, 0x4be3499, 0x52b23aa,
+    0x109f307e, 0x6f5b6bb, 0x1f84e1e7, 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3,
+    0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, 0x1a4a7529, 0xcb7beb1, 0xb2a78a1,
+    0x1ab21f1f, 0x6361ccf, 0x6c9179d, 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f,
+    0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, 0xc50124c, 0x50daa90, 0xb13f72,
+    0xb06aa75, 0x70f5cc6, 0x1649e5aa, 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d,
+    0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, 0x18c9e11e, 0x20bca9a, 0x66f496b,
+    0x3eef294, 0x67500d2, 0xd7f613c, 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a,
+    0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, 0x17fa56ff, 0x65ef930, 0x21dc4a,
+    0x2b37659, 0x450fe17, 0xb357b65, 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f,
+    0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, 0x725522b, 0xdc78583, 0x40eeabb,
+    0x1fde328a, 0xbd61d96, 0xd28c387, 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc,
+    0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, 0x6316abb, 0x2413c8e, 0x5425bf9,
+    0x1bed3e3a, 0xf272274, 0x1f5e7326, 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce,
+    0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, 0x1dc4ef73, 0x8956f34, 0xe4b5cf2,
+    0x1b0d3a18, 0x3194a36, 0x6c2641f, 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca,
+    0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, 0x10e3edc9, 0x9c19bf2, 0x5882229,
+    0x1b2b4162, 0xa5cef1a, 0x1543622b, 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57,
+    0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, 0xa038113, 0xa4a1769, 0x11fbc6c,
+    0x917e50e, 0xeec3da8, 0x169d6eac, 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa,
+    0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, 0x196142cc, 0x7bf0fa9, 0x957651,
+    0x4e0f215, 0xed439f8, 0x3f46bd5, 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec,
+    0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, 0x19d1c12d, 0xf20bd46, 0x1951fa7,
+    0xa3656c3, 0x523a425, 0xfcd0692, 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c,
+    0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, 0x2e74779, 0x576138, 0x9587927,
+    0x133130fa, 0xbe05516, 0x9f4d619, 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298,
+    0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, 0x14e2f5d8, 0xf858d3a, 0x942eea8,
+    0xda5b765, 0x6edafff, 0xa9d18cc, 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2,
+    0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, 0x4c146c0, 0x6bdf55a, 0x4e4457d,
+    0x16152289, 0xac78ec2, 0x1a59c5a2, 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4,
+    0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, 0x81d55d7, 0xa5bef68, 0xb7b30d8,
+    0xbe73d6f, 0xaa88141, 0xd976c81, 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78,
+};
+
+
+/* Field element operations: */
+
+/* NON_ZERO_TO_ALL_ONES returns:
+ *   0xffffffff for 0 < x <= 2**31
+ *   0 for x == 0 or x > 2**31.
+ *
+ * x must be a u32 or an equivalent type such as limb. */
+#define NON_ZERO_TO_ALL_ONES(x) ((((u32)(x) - 1) >> 31) - 1)
+
+/* felem_reduce_carry adds a multiple of p in order to cancel |carry|,
+ * which is a term at 2**257.
+ *
+ * On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28.
+ * On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29. */
+static void felem_reduce_carry(felem inout, limb carry) {
+  const u32 carry_mask = NON_ZERO_TO_ALL_ONES(carry);
+
+  inout[0] += carry << 1;
+  inout[3] += 0x10000000 & carry_mask;
+  /* carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the
+   * previous line therefore this doesn't underflow. */
+  inout[3] -= carry << 11;
+  inout[4] += (0x20000000 - 1) & carry_mask;
+  inout[5] += (0x10000000 - 1) & carry_mask;
+  inout[6] += (0x20000000 - 1) & carry_mask;
+  inout[6] -= carry << 22;
+  /* This may underflow if carry is non-zero but, if so, we'll fix it in the
+   * next line. */
+  inout[7] -= 1 & carry_mask;
+  inout[7] += carry << 25;
+}
+
+/* felem_sum sets out = in+in2.
+ *
+ * On entry, in[i]+in2[i] must not overflow a 32-bit word.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */
+static void felem_sum(felem out, const felem in, const felem in2) {
+  limb carry = 0;
+  unsigned i;
+
+  for (i = 0;; i++) {
+    out[i] = in[i] + in2[i];
+    out[i] += carry;
+    carry = out[i] >> 29;
+    out[i] &= kBottom29Bits;
+
+    i++;
+    if (i == NLIMBS)
+      break;
+
+    out[i] = in[i] + in2[i];
+    out[i] += carry;
+    carry = out[i] >> 28;
+    out[i] &= kBottom28Bits;
+  }
+
+  felem_reduce_carry(out, carry);
+}
+
+#define two31m3 (((limb)1) << 31) - (((limb)1) << 3)
+#define two30m2 (((limb)1) << 30) - (((limb)1) << 2)
+#define two30p13m2 (((limb)1) << 30) + (((limb)1) << 13) - (((limb)1) << 2)
+#define two31m2 (((limb)1) << 31) - (((limb)1) << 2)
+#define two31p24m2 (((limb)1) << 31) + (((limb)1) << 24) - (((limb)1) << 2)
+#define two30m27m2 (((limb)1) << 30) - (((limb)1) << 27) - (((limb)1) << 2)
+
+/* zero31 is 0 mod p. */
+static const felem zero31 = { two31m3, two30m2, two31m2, two30p13m2, two31m2, two30m2, two31p24m2, two30m27m2, two31m2 };
+
+/* felem_diff sets out = in-in2.
+ *
+ * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and
+ *           in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_diff(felem out, const felem in, const felem in2) {
+  limb carry = 0;
+  unsigned i;
+
+   for (i = 0;; i++) {
+    out[i] = in[i] - in2[i];
+    out[i] += zero31[i];
+    out[i] += carry;
+    carry = out[i] >> 29;
+    out[i] &= kBottom29Bits;
+
+    i++;
+    if (i == NLIMBS)
+      break;
+
+    out[i] = in[i] - in2[i];
+    out[i] += zero31[i];
+    out[i] += carry;
+    carry = out[i] >> 28;
+    out[i] &= kBottom28Bits;
+  }
+
+  felem_reduce_carry(out, carry);
+}
+
+/* felem_reduce_degree sets out = tmp/R mod p where tmp contains 64-bit words
+ * with the same 29,28,... bit positions as an felem.
+ *
+ * The values in felems are in Montgomery form: x*R mod p where R = 2**257.
+ * Since we just multiplied two Montgomery values together, the result is
+ * x*y*R*R mod p. We wish to divide by R in order for the result also to be
+ * in Montgomery form.
+ *
+ * On entry: tmp[i] < 2**64
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 */
+static void felem_reduce_degree(felem out, u64 tmp[17]) {
+   /* The following table may be helpful when reading this code:
+    *
+    * Limb number:   0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10...
+    * Width (bits):  29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29
+    * Start bit:     0 | 29| 57| 86|114|143|171|200|228|257|285
+    *   (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285 */
+  limb tmp2[18], carry, x, xMask;
+  unsigned i;
+
+  /* tmp contains 64-bit words with the same 29,28,29-bit positions as an
+   * felem. So the top of an element of tmp might overlap with another
+   * element two positions down. The following loop eliminates this
+   * overlap. */
+  tmp2[0] = (limb)(tmp[0] & kBottom29Bits);
+
+  /* In the following we use "(limb) tmp[x]" and "(limb) (tmp[x]>>32)" to try
+   * and hint to the compiler that it can do a single-word shift by selecting
+   * the right register rather than doing a double-word shift and truncating
+   * afterwards. */
+  tmp2[1] = ((limb) tmp[0]) >> 29;
+  tmp2[1] |= (((limb)(tmp[0] >> 32)) << 3) & kBottom28Bits;
+  tmp2[1] += ((limb) tmp[1]) & kBottom28Bits;
+  carry = tmp2[1] >> 28;
+  tmp2[1] &= kBottom28Bits;
+
+  for (i = 2; i < 17; i++) {
+    tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25;
+    tmp2[i] += ((limb)(tmp[i - 1])) >> 28;
+    tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 4) & kBottom29Bits;
+    tmp2[i] += ((limb) tmp[i]) & kBottom29Bits;
+    tmp2[i] += carry;
+    carry = tmp2[i] >> 29;
+    tmp2[i] &= kBottom29Bits;
+
+    i++;
+    if (i == 17)
+      break;
+    tmp2[i] = ((limb)(tmp[i - 2] >> 32)) >> 25;
+    tmp2[i] += ((limb)(tmp[i - 1])) >> 29;
+    tmp2[i] += (((limb)(tmp[i - 1] >> 32)) << 3) & kBottom28Bits;
+    tmp2[i] += ((limb) tmp[i]) & kBottom28Bits;
+    tmp2[i] += carry;
+    carry = tmp2[i] >> 28;
+    tmp2[i] &= kBottom28Bits;
+  }
+
+  tmp2[17] = ((limb)(tmp[15] >> 32)) >> 25;
+  tmp2[17] += ((limb)(tmp[16])) >> 29;
+  tmp2[17] += (((limb)(tmp[16] >> 32)) << 3);
+  tmp2[17] += carry;
+
+  /* Montgomery elimination of terms.
+   *
+   * Since R is 2**257, we can divide by R with a bitwise shift if we can
+   * ensure that the right-most 257 bits are all zero. We can make that true by
+   * adding multiplies of p without affecting the value.
+   *
+   * So we eliminate limbs from right to left. Since the bottom 29 bits of p
+   * are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0.
+   * We can do that for 8 further limbs and then right shift to eliminate the
+   * extra factor of R. */
+  for (i = 0;; i += 2) {
+    tmp2[i + 1] += tmp2[i] >> 29;
+    x = tmp2[i] & kBottom29Bits;
+    xMask = NON_ZERO_TO_ALL_ONES(x);
+    tmp2[i] = 0;
+
+    /* The bounds calculations for this loop are tricky. Each iteration of
+     * the loop eliminates two words by adding values to words to their
+     * right.
+     *
+     * The following table contains the amounts added to each word (as an
+     * offset from the value of i at the top of the loop). The amounts are
+     * accounted for from the first and second half of the loop separately
+     * and are written as, for example, 28 to mean a value <2**28.
+     *
+     * Word:                   3   4   5   6   7   8   9   10
+     * Added in top half:     28  11      29  21  29  28
+     *                                        28  29
+     *                                            29
+     * Added in bottom half:      29  10      28  21  28   28
+     *                                            29
+     *
+     * The value that is currently offset 7 will be offset 5 for the next
+     * iteration and then offset 3 for the iteration after that. Therefore
+     * the total value added will be the values added at 7, 5 and 3.
+     *
+     * The following table accumulates these values. The sums at the bottom
+     * are written as, for example, 29+28, to mean a value < 2**29+2**28.
+     *
+     * Word:                   3   4   5   6   7   8   9  10  11  12  13
+     *                        28  11  10  29  21  29  28  28  28  28  28
+     *                            29  28  11  28  29  28  29  28  29  28
+     *                                    29  28  21  21  29  21  29  21
+     *                                        10  29  28  21  28  21  28
+     *                                        28  29  28  29  28  29  28
+     *                                            11  10  29  10  29  10
+     *                                            29  28  11  28  11
+     *                                                    29      29
+     *                        --------------------------------------------
+     *                                                30+ 31+ 30+ 31+ 30+
+     *                                                28+ 29+ 28+ 29+ 21+
+     *                                                21+ 28+ 21+ 28+ 10
+     *                                                10  21+ 10  21+
+     *                                                    11      11
+     *
+     * So the greatest amount is added to tmp2[10] and tmp2[12]. If
+     * tmp2[10/12] has an initial value of <2**29, then the maximum value
+     * will be < 2**31 + 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32,
+     * as required. */
+    tmp2[i + 3] += (x << 10) & kBottom28Bits;
+    tmp2[i + 4] += (x >> 18);
+
+    tmp2[i + 6] += (x << 21) & kBottom29Bits;
+    tmp2[i + 7] += x >> 8;
+
+    /* At position 200, which is the starting bit position for word 7, we
+     * have a factor of 0xf000000 = 2**28 - 2**24. */
+    tmp2[i + 7] += 0x10000000 & xMask;
+    /* Word 7 is 28 bits wide, so the 2**28 term exactly hits word 8. */
+    tmp2[i + 8] += (x - 1) & xMask;
+    tmp2[i + 7] -= (x << 24) & kBottom28Bits;
+    tmp2[i + 8] -= x >> 4;
+
+    tmp2[i + 8] += 0x20000000 & xMask;
+    tmp2[i + 8] -= x;
+    tmp2[i + 8] += (x << 28) & kBottom29Bits;
+    tmp2[i + 9] += ((x >> 1) - 1) & xMask;
+
+    if (i+1 == NLIMBS)
+      break;
+    tmp2[i + 2] += tmp2[i + 1] >> 28;
+    x = tmp2[i + 1] & kBottom28Bits;
+    xMask = NON_ZERO_TO_ALL_ONES(x);
+    tmp2[i + 1] = 0;
+
+    tmp2[i + 4] += (x << 11) & kBottom29Bits;
+    tmp2[i + 5] += (x >> 18);
+
+    tmp2[i + 7] += (x << 21) & kBottom28Bits;
+    tmp2[i + 8] += x >> 7;
+
+    /* At position 199, which is the starting bit of the 8th word when
+     * dealing with a context starting on an odd word, we have a factor of
+     * 0x1e000000 = 2**29 - 2**25. Since we have not updated i, the 8th
+     * word from i+1 is i+8. */
+    tmp2[i + 8] += 0x20000000 & xMask;
+    tmp2[i + 9] += (x - 1) & xMask;
+    tmp2[i + 8] -= (x << 25) & kBottom29Bits;
+    tmp2[i + 9] -= x >> 4;
+
+    tmp2[i + 9] += 0x10000000 & xMask;
+    tmp2[i + 9] -= x;
+    tmp2[i + 10] += (x - 1) & xMask;
+  }
+
+  /* We merge the right shift with a carry chain. The words above 2**257 have
+   * widths of 28,29,... which we need to correct when copying them down.  */
+  carry = 0;
+  for (i = 0; i < 8; i++) {
+    /* The maximum value of tmp2[i + 9] occurs on the first iteration and
+     * is < 2**30+2**29+2**28. Adding 2**29 (from tmp2[i + 10]) is
+     * therefore safe. */
+    out[i] = tmp2[i + 9];
+    out[i] += carry;
+    out[i] += (tmp2[i + 10] << 28) & kBottom29Bits;
+    carry = out[i] >> 29;
+    out[i] &= kBottom29Bits;
+
+    i++;
+    out[i] = tmp2[i + 9] >> 1;
+    out[i] += carry;
+    carry = out[i] >> 28;
+    out[i] &= kBottom28Bits;
+  }
+
+  out[8] = tmp2[17];
+  out[8] += carry;
+  carry = out[8] >> 29;
+  out[8] &= kBottom29Bits;
+
+  felem_reduce_carry(out, carry);
+}
+
+/* felem_square sets out=in*in.
+ *
+ * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_square(felem out, const felem in) {
+  u64 tmp[17];
+
+  tmp[0] = ((u64) in[0]) * in[0];
+  tmp[1] = ((u64) in[0]) * (in[1] << 1);
+  tmp[2] = ((u64) in[0]) * (in[2] << 1) +
+           ((u64) in[1]) * (in[1] << 1);
+  tmp[3] = ((u64) in[0]) * (in[3] << 1) +
+           ((u64) in[1]) * (in[2] << 1);
+  tmp[4] = ((u64) in[0]) * (in[4] << 1) +
+           ((u64) in[1]) * (in[3] << 2) + ((u64) in[2]) * in[2];
+  tmp[5] = ((u64) in[0]) * (in[5] << 1) + ((u64) in[1]) *
+           (in[4] << 1) + ((u64) in[2]) * (in[3] << 1);
+  tmp[6] = ((u64) in[0]) * (in[6] << 1) + ((u64) in[1]) *
+           (in[5] << 2) + ((u64) in[2]) * (in[4] << 1) +
+           ((u64) in[3]) * (in[3] << 1);
+  tmp[7] = ((u64) in[0]) * (in[7] << 1) + ((u64) in[1]) *
+           (in[6] << 1) + ((u64) in[2]) * (in[5] << 1) +
+           ((u64) in[3]) * (in[4] << 1);
+  /* tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + 2**60 + 2**60,
+   * which is < 2**64 as required. */
+  tmp[8] = ((u64) in[0]) * (in[8] << 1) + ((u64) in[1]) *
+           (in[7] << 2) + ((u64) in[2]) * (in[6] << 1) +
+           ((u64) in[3]) * (in[5] << 2) + ((u64) in[4]) * in[4];
+  tmp[9] = ((u64) in[1]) * (in[8] << 1) + ((u64) in[2]) *
+           (in[7] << 1) + ((u64) in[3]) * (in[6] << 1) +
+           ((u64) in[4]) * (in[5] << 1);
+  tmp[10] = ((u64) in[2]) * (in[8] << 1) + ((u64) in[3]) *
+            (in[7] << 2) + ((u64) in[4]) * (in[6] << 1) +
+            ((u64) in[5]) * (in[5] << 1);
+  tmp[11] = ((u64) in[3]) * (in[8] << 1) + ((u64) in[4]) *
+            (in[7] << 1) + ((u64) in[5]) * (in[6] << 1);
+  tmp[12] = ((u64) in[4]) * (in[8] << 1) +
+            ((u64) in[5]) * (in[7] << 2) + ((u64) in[6]) * in[6];
+  tmp[13] = ((u64) in[5]) * (in[8] << 1) +
+            ((u64) in[6]) * (in[7] << 1);
+  tmp[14] = ((u64) in[6]) * (in[8] << 1) +
+            ((u64) in[7]) * (in[7] << 1);
+  tmp[15] = ((u64) in[7]) * (in[8] << 1);
+  tmp[16] = ((u64) in[8]) * in[8];
+
+  felem_reduce_degree(out, tmp);
+}
+
+/* felem_mul sets out=in*in2.
+ *
+ * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and
+ *           in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_mul(felem out, const felem in, const felem in2) {
+  u64 tmp[17];
+
+  tmp[0] = ((u64) in[0]) * in2[0];
+  tmp[1] = ((u64) in[0]) * (in2[1] << 0) +
+           ((u64) in[1]) * (in2[0] << 0);
+  tmp[2] = ((u64) in[0]) * (in2[2] << 0) + ((u64) in[1]) *
+           (in2[1] << 1) + ((u64) in[2]) * (in2[0] << 0);
+  tmp[3] = ((u64) in[0]) * (in2[3] << 0) + ((u64) in[1]) *
+           (in2[2] << 0) + ((u64) in[2]) * (in2[1] << 0) +
+           ((u64) in[3]) * (in2[0] << 0);
+  tmp[4] = ((u64) in[0]) * (in2[4] << 0) + ((u64) in[1]) *
+           (in2[3] << 1) + ((u64) in[2]) * (in2[2] << 0) +
+           ((u64) in[3]) * (in2[1] << 1) +
+           ((u64) in[4]) * (in2[0] << 0);
+  tmp[5] = ((u64) in[0]) * (in2[5] << 0) + ((u64) in[1]) *
+           (in2[4] << 0) + ((u64) in[2]) * (in2[3] << 0) +
+           ((u64) in[3]) * (in2[2] << 0) + ((u64) in[4]) *
+           (in2[1] << 0) + ((u64) in[5]) * (in2[0] << 0);
+  tmp[6] = ((u64) in[0]) * (in2[6] << 0) + ((u64) in[1]) *
+           (in2[5] << 1) + ((u64) in[2]) * (in2[4] << 0) +
+           ((u64) in[3]) * (in2[3] << 1) + ((u64) in[4]) *
+           (in2[2] << 0) + ((u64) in[5]) * (in2[1] << 1) +
+           ((u64) in[6]) * (in2[0] << 0);
+  tmp[7] = ((u64) in[0]) * (in2[7] << 0) + ((u64) in[1]) *
+           (in2[6] << 0) + ((u64) in[2]) * (in2[5] << 0) +
+           ((u64) in[3]) * (in2[4] << 0) + ((u64) in[4]) *
+           (in2[3] << 0) + ((u64) in[5]) * (in2[2] << 0) +
+           ((u64) in[6]) * (in2[1] << 0) +
+           ((u64) in[7]) * (in2[0] << 0);
+  /* tmp[8] has the greatest value but doesn't overflow. See logic in
+   * felem_square. */
+  tmp[8] = ((u64) in[0]) * (in2[8] << 0) + ((u64) in[1]) *
+           (in2[7] << 1) + ((u64) in[2]) * (in2[6] << 0) +
+           ((u64) in[3]) * (in2[5] << 1) + ((u64) in[4]) *
+           (in2[4] << 0) + ((u64) in[5]) * (in2[3] << 1) +
+           ((u64) in[6]) * (in2[2] << 0) + ((u64) in[7]) *
+           (in2[1] << 1) + ((u64) in[8]) * (in2[0] << 0);
+  tmp[9] = ((u64) in[1]) * (in2[8] << 0) + ((u64) in[2]) *
+           (in2[7] << 0) + ((u64) in[3]) * (in2[6] << 0) +
+           ((u64) in[4]) * (in2[5] << 0) + ((u64) in[5]) *
+           (in2[4] << 0) + ((u64) in[6]) * (in2[3] << 0) +
+           ((u64) in[7]) * (in2[2] << 0) +
+           ((u64) in[8]) * (in2[1] << 0);
+  tmp[10] = ((u64) in[2]) * (in2[8] << 0) + ((u64) in[3]) *
+            (in2[7] << 1) + ((u64) in[4]) * (in2[6] << 0) +
+            ((u64) in[5]) * (in2[5] << 1) + ((u64) in[6]) *
+            (in2[4] << 0) + ((u64) in[7]) * (in2[3] << 1) +
+            ((u64) in[8]) * (in2[2] << 0);
+  tmp[11] = ((u64) in[3]) * (in2[8] << 0) + ((u64) in[4]) *
+            (in2[7] << 0) + ((u64) in[5]) * (in2[6] << 0) +
+            ((u64) in[6]) * (in2[5] << 0) + ((u64) in[7]) *
+            (in2[4] << 0) + ((u64) in[8]) * (in2[3] << 0);
+  tmp[12] = ((u64) in[4]) * (in2[8] << 0) + ((u64) in[5]) *
+            (in2[7] << 1) + ((u64) in[6]) * (in2[6] << 0) +
+            ((u64) in[7]) * (in2[5] << 1) +
+            ((u64) in[8]) * (in2[4] << 0);
+  tmp[13] = ((u64) in[5]) * (in2[8] << 0) + ((u64) in[6]) *
+            (in2[7] << 0) + ((u64) in[7]) * (in2[6] << 0) +
+            ((u64) in[8]) * (in2[5] << 0);
+  tmp[14] = ((u64) in[6]) * (in2[8] << 0) + ((u64) in[7]) *
+            (in2[7] << 1) + ((u64) in[8]) * (in2[6] << 0);
+  tmp[15] = ((u64) in[7]) * (in2[8] << 0) +
+            ((u64) in[8]) * (in2[7] << 0);
+  tmp[16] = ((u64) in[8]) * (in2[8] << 0);
+
+  felem_reduce_degree(out, tmp);
+}
+
+static void felem_assign(felem out, const felem in) {
+  memcpy(out, in, sizeof(felem));
+}
+
+/* felem_inv calculates |out| = |in|^{-1}
+ *
+ * Based on Fermat's Little Theorem:
+ *   a^p = a (mod p)
+ *   a^{p-1} = 1 (mod p)
+ *   a^{p-2} = a^{-1} (mod p)
+ */
+static void felem_inv(felem out, const felem in) {
+  felem ftmp, ftmp2;
+  /* each e_I will hold |in|^{2^I - 1} */
+  felem e2, e4, e8, e16, e32, e64;
+  unsigned i;
+
+  felem_square(ftmp, in); /* 2^1 */
+  felem_mul(ftmp, in, ftmp); /* 2^2 - 2^0 */
+  felem_assign(e2, ftmp);
+  felem_square(ftmp, ftmp); /* 2^3 - 2^1 */
+  felem_square(ftmp, ftmp); /* 2^4 - 2^2 */
+  felem_mul(ftmp, ftmp, e2); /* 2^4 - 2^0 */
+  felem_assign(e4, ftmp);
+  felem_square(ftmp, ftmp); /* 2^5 - 2^1 */
+  felem_square(ftmp, ftmp); /* 2^6 - 2^2 */
+  felem_square(ftmp, ftmp); /* 2^7 - 2^3 */
+  felem_square(ftmp, ftmp); /* 2^8 - 2^4 */
+  felem_mul(ftmp, ftmp, e4); /* 2^8 - 2^0 */
+  felem_assign(e8, ftmp);
+  for (i = 0; i < 8; i++) {
+    felem_square(ftmp, ftmp);
+  } /* 2^16 - 2^8 */
+  felem_mul(ftmp, ftmp, e8); /* 2^16 - 2^0 */
+  felem_assign(e16, ftmp);
+  for (i = 0; i < 16; i++) {
+    felem_square(ftmp, ftmp);
+  } /* 2^32 - 2^16 */
+  felem_mul(ftmp, ftmp, e16); /* 2^32 - 2^0 */
+  felem_assign(e32, ftmp);
+  for (i = 0; i < 32; i++) {
+    felem_square(ftmp, ftmp);
+  } /* 2^64 - 2^32 */
+  felem_assign(e64, ftmp);
+  felem_mul(ftmp, ftmp, in); /* 2^64 - 2^32 + 2^0 */
+  for (i = 0; i < 192; i++) {
+    felem_square(ftmp, ftmp);
+  } /* 2^256 - 2^224 + 2^192 */
+
+  felem_mul(ftmp2, e64, e32); /* 2^64 - 2^0 */
+  for (i = 0; i < 16; i++) {
+    felem_square(ftmp2, ftmp2);
+  } /* 2^80 - 2^16 */
+  felem_mul(ftmp2, ftmp2, e16); /* 2^80 - 2^0 */
+  for (i = 0; i < 8; i++) {
+    felem_square(ftmp2, ftmp2);
+  } /* 2^88 - 2^8 */
+  felem_mul(ftmp2, ftmp2, e8); /* 2^88 - 2^0 */
+  for (i = 0; i < 4; i++) {
+    felem_square(ftmp2, ftmp2);
+  } /* 2^92 - 2^4 */
+  felem_mul(ftmp2, ftmp2, e4); /* 2^92 - 2^0 */
+  felem_square(ftmp2, ftmp2); /* 2^93 - 2^1 */
+  felem_square(ftmp2, ftmp2); /* 2^94 - 2^2 */
+  felem_mul(ftmp2, ftmp2, e2); /* 2^94 - 2^0 */
+  felem_square(ftmp2, ftmp2); /* 2^95 - 2^1 */
+  felem_square(ftmp2, ftmp2); /* 2^96 - 2^2 */
+  felem_mul(ftmp2, ftmp2, in); /* 2^96 - 3 */
+
+  felem_mul(out, ftmp2, ftmp); /* 2^256 - 2^224 + 2^192 + 2^96 - 3 */
+}
+
+/* felem_scalar_3 sets out=3*out.
+ *
+ * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_scalar_3(felem out) {
+  limb carry = 0;
+  unsigned i;
+
+  for (i = 0;; i++) {
+    out[i] *= 3;
+    out[i] += carry;
+    carry = out[i] >> 29;
+    out[i] &= kBottom29Bits;
+
+    i++;
+    if (i == NLIMBS)
+      break;
+
+    out[i] *= 3;
+    out[i] += carry;
+    carry = out[i] >> 28;
+    out[i] &= kBottom28Bits;
+  }
+
+  felem_reduce_carry(out, carry);
+}
+
+/* felem_scalar_4 sets out=4*out.
+ *
+ * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_scalar_4(felem out) {
+  limb carry = 0, next_carry;
+  unsigned i;
+
+  for (i = 0;; i++) {
+    next_carry = out[i] >> 27;
+    out[i] <<= 2;
+    out[i] &= kBottom29Bits;
+    out[i] += carry;
+    carry = next_carry + (out[i] >> 29);
+    out[i] &= kBottom29Bits;
+
+    i++;
+    if (i == NLIMBS)
+      break;
+
+    next_carry = out[i] >> 26;
+    out[i] <<= 2;
+    out[i] &= kBottom28Bits;
+    out[i] += carry;
+    carry = next_carry + (out[i] >> 28);
+    out[i] &= kBottom28Bits;
+  }
+
+  felem_reduce_carry(out, carry);
+}
+
+/* felem_scalar_8 sets out=8*out.
+ *
+ * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
+ * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. */
+static void felem_scalar_8(felem out) {
+  limb carry = 0, next_carry;
+  unsigned i;
+
+  for (i = 0;; i++) {
+    next_carry = out[i] >> 26;
+    out[i] <<= 3;
+    out[i] &= kBottom29Bits;
+    out[i] += carry;
+    carry = next_carry + (out[i] >> 29);
+    out[i] &= kBottom29Bits;
+
+    i++;
+    if (i == NLIMBS)
+      break;
+
+    next_carry = out[i] >> 25;
+    out[i] <<= 3;
+    out[i] &= kBottom28Bits;
+    out[i] += carry;
+    carry = next_carry + (out[i] >> 28);
+    out[i] &= kBottom28Bits;
+  }
+
+  felem_reduce_carry(out, carry);
+}
+
+/* felem_is_zero_vartime returns 1 iff |in| == 0. It takes a variable amount of
+ * time depending on the value of |in|. */
+static char felem_is_zero_vartime(const felem in) {
+  limb carry;
+  int i;
+  limb tmp[NLIMBS];
+
+  felem_assign(tmp, in);
+
+  /* First, reduce tmp to a minimal form. */
+  do {
+    carry = 0;
+    for (i = 0;; i++) {
+      tmp[i] += carry;
+      carry = tmp[i] >> 29;
+      tmp[i] &= kBottom29Bits;
+
+      i++;
+      if (i == NLIMBS)
+        break;
+
+      tmp[i] += carry;
+      carry = tmp[i] >> 28;
+      tmp[i] &= kBottom28Bits;
+    }
+
+    felem_reduce_carry(tmp, carry);
+  } while (carry);
+
+  /* tmp < 2**257, so the only possible zero values are 0, p and 2p. */
+  return memcmp(tmp, kZero, sizeof(tmp)) == 0 ||
+         memcmp(tmp, kP, sizeof(tmp)) == 0 ||
+         memcmp(tmp, k2P, sizeof(tmp)) == 0;
+}
+
+
+/* Group operations:
+ *
+ * Elements of the elliptic curve group are represented in Jacobian
+ * coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in
+ * Jacobian form. */
+
+/* point_double sets {x_out,y_out,z_out} = 2*{x,y,z}.
+ *
+ * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l */
+static void point_double(felem x_out, felem y_out, felem z_out, const felem x,
+                         const felem y, const felem z) {
+  felem delta, gamma, alpha, beta, tmp, tmp2;
+
+  felem_square(delta, z);
+  felem_square(gamma, y);
+  felem_mul(beta, x, gamma);
+
+  felem_sum(tmp, x, delta);
+  felem_diff(tmp2, x, delta);
+  felem_mul(alpha, tmp, tmp2);
+  felem_scalar_3(alpha);
+
+  felem_sum(tmp, y, z);
+  felem_square(tmp, tmp);
+  felem_diff(tmp, tmp, gamma);
+  felem_diff(z_out, tmp, delta);
+
+  felem_scalar_4(beta);
+  felem_square(x_out, alpha);
+  felem_diff(x_out, x_out, beta);
+  felem_diff(x_out, x_out, beta);
+
+  felem_diff(tmp, beta, x_out);
+  felem_mul(tmp, alpha, tmp);
+  felem_square(tmp2, gamma);
+  felem_scalar_8(tmp2);
+  felem_diff(y_out, tmp, tmp2);
+}
+
+/* point_add_mixed sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,1}.
+ * (i.e. the second point is affine.)
+ *
+ * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ *
+ * Note that this function does not handle P+P, infinity+P nor P+infinity
+ * correctly. */
+static void point_add_mixed(felem x_out, felem y_out, felem z_out,
+                            const felem x1, const felem y1, const felem z1,
+                            const felem x2, const felem y2) {
+  felem z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp;
+
+  felem_square(z1z1, z1);
+  felem_sum(tmp, z1, z1);
+
+  felem_mul(u2, x2, z1z1);
+  felem_mul(z1z1z1, z1, z1z1);
+  felem_mul(s2, y2, z1z1z1);
+  felem_diff(h, u2, x1);
+  felem_sum(i, h, h);
+  felem_square(i, i);
+  felem_mul(j, h, i);
+  felem_diff(r, s2, y1);
+  felem_sum(r, r, r);
+  felem_mul(v, x1, i);
+
+  felem_mul(z_out, tmp, h);
+  felem_square(rr, r);
+  felem_diff(x_out, rr, j);
+  felem_diff(x_out, x_out, v);
+  felem_diff(x_out, x_out, v);
+
+  felem_diff(tmp, v, x_out);
+  felem_mul(y_out, tmp, r);
+  felem_mul(tmp, y1, j);
+  felem_diff(y_out, y_out, tmp);
+  felem_diff(y_out, y_out, tmp);
+}
+
+/* point_add sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,z2}.
+ *
+ * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ *
+ * Note that this function does not handle P+P, infinity+P nor P+infinity
+ * correctly. */
+static void point_add(felem x_out, felem y_out, felem z_out, const felem x1,
+                      const felem y1, const felem z1, const felem x2,
+                      const felem y2, const felem z2) {
+  felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp;
+
+  felem_square(z1z1, z1);
+  felem_square(z2z2, z2);
+  felem_mul(u1, x1, z2z2);
+
+  felem_sum(tmp, z1, z2);
+  felem_square(tmp, tmp);
+  felem_diff(tmp, tmp, z1z1);
+  felem_diff(tmp, tmp, z2z2);
+
+  felem_mul(z2z2z2, z2, z2z2);
+  felem_mul(s1, y1, z2z2z2);
+
+  felem_mul(u2, x2, z1z1);
+  felem_mul(z1z1z1, z1, z1z1);
+  felem_mul(s2, y2, z1z1z1);
+  felem_diff(h, u2, u1);
+  felem_sum(i, h, h);
+  felem_square(i, i);
+  felem_mul(j, h, i);
+  felem_diff(r, s2, s1);
+  felem_sum(r, r, r);
+  felem_mul(v, u1, i);
+
+  felem_mul(z_out, tmp, h);
+  felem_square(rr, r);
+  felem_diff(x_out, rr, j);
+  felem_diff(x_out, x_out, v);
+  felem_diff(x_out, x_out, v);
+
+  felem_diff(tmp, v, x_out);
+  felem_mul(y_out, tmp, r);
+  felem_mul(tmp, s1, j);
+  felem_diff(y_out, y_out, tmp);
+  felem_diff(y_out, y_out, tmp);
+}
+
+/* point_add_or_double_vartime sets {x_out,y_out,z_out} = {x1,y1,z1} +
+ *                                                        {x2,y2,z2}.
+ *
+ * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ *
+ * This function handles the case where {x1,y1,z1}={x2,y2,z2}. */
+static void point_add_or_double_vartime(
+    felem x_out, felem y_out, felem z_out, const felem x1, const felem y1,
+    const felem z1, const felem x2, const felem y2, const felem z2) {
+  felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp;
+  char x_equal, y_equal;
+
+  felem_square(z1z1, z1);
+  felem_square(z2z2, z2);
+  felem_mul(u1, x1, z2z2);
+
+  felem_sum(tmp, z1, z2);
+  felem_square(tmp, tmp);
+  felem_diff(tmp, tmp, z1z1);
+  felem_diff(tmp, tmp, z2z2);
+
+  felem_mul(z2z2z2, z2, z2z2);
+  felem_mul(s1, y1, z2z2z2);
+
+  felem_mul(u2, x2, z1z1);
+  felem_mul(z1z1z1, z1, z1z1);
+  felem_mul(s2, y2, z1z1z1);
+  felem_diff(h, u2, u1);
+  x_equal = felem_is_zero_vartime(h);
+  felem_sum(i, h, h);
+  felem_square(i, i);
+  felem_mul(j, h, i);
+  felem_diff(r, s2, s1);
+  y_equal = felem_is_zero_vartime(r);
+  if (x_equal && y_equal) {
+    point_double(x_out, y_out, z_out, x1, y1, z1);
+    return;
+  }
+  felem_sum(r, r, r);
+  felem_mul(v, u1, i);
+
+  felem_mul(z_out, tmp, h);
+  felem_square(rr, r);
+  felem_diff(x_out, rr, j);
+  felem_diff(x_out, x_out, v);
+  felem_diff(x_out, x_out, v);
+
+  felem_diff(tmp, v, x_out);
+  felem_mul(y_out, tmp, r);
+  felem_mul(tmp, s1, j);
+  felem_diff(y_out, y_out, tmp);
+  felem_diff(y_out, y_out, tmp);
+}
+
+/* copy_conditional sets out=in if mask = 0xffffffff in constant time.
+ *
+ * On entry: mask is either 0 or 0xffffffff. */
+static void copy_conditional(felem out, const felem in, limb mask) {
+  int i;
+
+  for (i = 0; i < NLIMBS; i++) {
+    const limb tmp = mask & (in[i] ^ out[i]);
+    out[i] ^= tmp;
+  }
+}
+
+/* select_affine_point sets {out_x,out_y} to the index'th entry of table.
+ * On entry: index < 16, table[0] must be zero. */
+static void select_affine_point(felem out_x, felem out_y, const limb* table,
+                                limb index) {
+  limb i, j;
+
+  memset(out_x, 0, sizeof(felem));
+  memset(out_y, 0, sizeof(felem));
+
+  for (i = 1; i < 16; i++) {
+    limb mask = i ^ index;
+    mask |= mask >> 2;
+    mask |= mask >> 1;
+    mask &= 1;
+    mask--;
+    for (j = 0; j < NLIMBS; j++, table++) {
+      out_x[j] |= *table & mask;
+    }
+    for (j = 0; j < NLIMBS; j++, table++) {
+      out_y[j] |= *table & mask;
+    }
+  }
+}
+
+/* select_jacobian_point sets {out_x,out_y,out_z} to the index'th entry of
+ * table. On entry: index < 16, table[0] must be zero. */
+static void select_jacobian_point(felem out_x, felem out_y, felem out_z,
+                                  const limb* table, limb index) {
+  limb i, j;
+
+  memset(out_x, 0, sizeof(felem));
+  memset(out_y, 0, sizeof(felem));
+  memset(out_z, 0, sizeof(felem));
+
+  /* The implicit value at index 0 is all zero. We don't need to perform that
+   * iteration of the loop because we already set out_* to zero. */
+  table += 3 * NLIMBS;
+
+  // Hit all entries to obscure cache profiling.
+  for (i = 1; i < 16; i++) {
+    limb mask = i ^ index;
+    mask |= mask >> 2;
+    mask |= mask >> 1;
+    mask &= 1;
+    mask--;
+    for (j = 0; j < NLIMBS; j++, table++) {
+      out_x[j] |= *table & mask;
+    }
+    for (j = 0; j < NLIMBS; j++, table++) {
+      out_y[j] |= *table & mask;
+    }
+    for (j = 0; j < NLIMBS; j++, table++) {
+      out_z[j] |= *table & mask;
+    }
+  }
+}
+
+/* scalar_base_mult sets {nx,ny,nz} = scalar*G where scalar is a little-endian
+ * number. Note that the value of scalar must be less than the order of the
+ * group. */
+static void scalar_base_mult(felem nx, felem ny, felem nz,
+                             const p256_int* scalar) {
+  int i, j;
+  limb n_is_infinity_mask = -1, p_is_noninfinite_mask, mask;
+  u32 table_offset;
+
+  felem px, py;
+  felem tx, ty, tz;
+
+  memset(nx, 0, sizeof(felem));
+  memset(ny, 0, sizeof(felem));
+  memset(nz, 0, sizeof(felem));
+
+  /* The loop adds bits at positions 0, 64, 128 and 192, followed by
+   * positions 32,96,160 and 224 and does this 32 times. */
+  for (i = 0; i < 32; i++) {
+    if (i) {
+      point_double(nx, ny, nz, nx, ny, nz);
+    }
+    table_offset = 0;
+    for (j = 0; j <= 32; j += 32) {
+      char bit0 = p256_get_bit(scalar, 31 - i + j);
+      char bit1 = p256_get_bit(scalar, 95 - i + j);
+      char bit2 = p256_get_bit(scalar, 159 - i + j);
+      char bit3 = p256_get_bit(scalar, 223 - i + j);
+      limb index = bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3);
+
+      select_affine_point(px, py, kPrecomputed + table_offset, index);
+      table_offset += 30 * NLIMBS;
+
+      /* Since scalar is less than the order of the group, we know that
+       * {nx,ny,nz} != {px,py,1}, unless both are zero, which we handle
+       * below. */
+      point_add_mixed(tx, ty, tz, nx, ny, nz, px, py);
+      /* The result of point_add_mixed is incorrect if {nx,ny,nz} is zero
+       * (a.k.a.  the point at infinity). We handle that situation by
+       * copying the point from the table. */
+      copy_conditional(nx, px, n_is_infinity_mask);
+      copy_conditional(ny, py, n_is_infinity_mask);
+      copy_conditional(nz, kOne, n_is_infinity_mask);
+
+      /* Equally, the result is also wrong if the point from the table is
+       * zero, which happens when the index is zero. We handle that by
+       * only copying from {tx,ty,tz} to {nx,ny,nz} if index != 0. */
+      p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index);
+      mask = p_is_noninfinite_mask & ~n_is_infinity_mask;
+      copy_conditional(nx, tx, mask);
+      copy_conditional(ny, ty, mask);
+      copy_conditional(nz, tz, mask);
+      /* If p was not zero, then n is now non-zero. */
+      n_is_infinity_mask &= ~p_is_noninfinite_mask;
+    }
+  }
+}
+
+/* point_to_affine converts a Jacobian point to an affine point. If the input
+ * is the point at infinity then it returns (0, 0) in constant time. */
+static void point_to_affine(felem x_out, felem y_out, const felem nx,
+                            const felem ny, const felem nz) {
+  felem z_inv, z_inv_sq;
+  felem_inv(z_inv, nz);
+  felem_square(z_inv_sq, z_inv);
+  felem_mul(x_out, nx, z_inv_sq);
+  felem_mul(z_inv, z_inv, z_inv_sq);
+  felem_mul(y_out, ny, z_inv);
+}
+
+/* scalar_base_mult sets {nx,ny,nz} = scalar*{x,y}. */
+static void scalar_mult(felem nx, felem ny, felem nz, const felem x,
+                        const felem y, const p256_int* scalar) {
+  int i;
+  felem px, py, pz, tx, ty, tz;
+  felem precomp[16][3];
+  limb n_is_infinity_mask, index, p_is_noninfinite_mask, mask;
+
+  /* We precompute 0,1,2,... times {x,y}. */
+  memset(precomp, 0, sizeof(felem) * 3);
+  memcpy(&precomp[1][0], x, sizeof(felem));
+  memcpy(&precomp[1][1], y, sizeof(felem));
+  memcpy(&precomp[1][2], kOne, sizeof(felem));
+
+  for (i = 2; i < 16; i += 2) {
+    point_double(precomp[i][0], precomp[i][1], precomp[i][2],
+                 precomp[i / 2][0], precomp[i / 2][1], precomp[i / 2][2]);
+
+    point_add_mixed(precomp[i + 1][0], precomp[i + 1][1], precomp[i + 1][2],
+                    precomp[i][0], precomp[i][1], precomp[i][2], x, y);
+  }
+
+  memset(nx, 0, sizeof(felem));
+  memset(ny, 0, sizeof(felem));
+  memset(nz, 0, sizeof(felem));
+  n_is_infinity_mask = -1;
+
+  /* We add in a window of four bits each iteration and do this 64 times. */
+  for (i = 0; i < 256; i += 4) {
+    if (i) {
+      point_double(nx, ny, nz, nx, ny, nz);
+      point_double(nx, ny, nz, nx, ny, nz);
+      point_double(nx, ny, nz, nx, ny, nz);
+      point_double(nx, ny, nz, nx, ny, nz);
+    }
+
+    index = (p256_get_bit(scalar, 255 - i - 0) << 3) |
+            (p256_get_bit(scalar, 255 - i - 1) << 2) |
+            (p256_get_bit(scalar, 255 - i - 2) << 1) |
+            p256_get_bit(scalar, 255 - i - 3);
+
+    /* See the comments in scalar_base_mult about handling infinities. */
+    select_jacobian_point(px, py, pz, precomp[0][0], index);
+    point_add(tx, ty, tz, nx, ny, nz, px, py, pz);
+    copy_conditional(nx, px, n_is_infinity_mask);
+    copy_conditional(ny, py, n_is_infinity_mask);
+    copy_conditional(nz, pz, n_is_infinity_mask);
+
+    p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index);
+    mask = p_is_noninfinite_mask & ~n_is_infinity_mask;
+
+    copy_conditional(nx, tx, mask);
+    copy_conditional(ny, ty, mask);
+    copy_conditional(nz, tz, mask);
+    n_is_infinity_mask &= ~p_is_noninfinite_mask;
+  }
+}
+
+#define kRDigits {2, 0, 0, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfffffffd, 1} // 2^257 mod p256.p
+
+#define kRInvDigits {0x80000000, 1, 0xffffffff, 0, 0x80000001, 0xfffffffe, 1, 0x7fffffff}  // 1 / 2^257 mod p256.p
+
+static const p256_int kR = { kRDigits };
+static const p256_int kRInv = { kRInvDigits };
+
+/* to_montgomery sets out = R*in. */
+static void to_montgomery(felem out, const p256_int* in) {
+  p256_int in_shifted;
+  int i;
+
+  p256_init(&in_shifted);
+  p256_modmul(&SECP256r1_p, in, 0, &kR, &in_shifted);
+
+  for (i = 0; i < NLIMBS; i++) {
+    if ((i & 1) == 0) {
+      out[i] = P256_DIGIT(&in_shifted, 0) & kBottom29Bits;
+      p256_shr(&in_shifted, 29, &in_shifted);
+    } else {
+      out[i] = P256_DIGIT(&in_shifted, 0) & kBottom28Bits;
+      p256_shr(&in_shifted, 28, &in_shifted);
+    }
+  }
+
+  p256_clear(&in_shifted);
+}
+
+/* from_montgomery sets out=in/R. */
+static void from_montgomery(p256_int* out, const felem in) {
+  p256_int result, tmp;
+  int i, top;
+
+  p256_init(&result);
+  p256_init(&tmp);
+
+  p256_add_d(&tmp, in[NLIMBS - 1], &result);
+  for (i = NLIMBS - 2; i >= 0; i--) {
+    if ((i & 1) == 0) {
+      top = p256_shl(&result, 29, &tmp);
+    } else {
+      top = p256_shl(&result, 28, &tmp);
+    }
+    top |= p256_add_d(&tmp, in[i], &result);
+  }
+
+  p256_modmul(&SECP256r1_p, &kRInv, top, &result, out);
+
+  p256_clear(&result);
+  p256_clear(&tmp);
+}
+
+/* p256_base_point_mul sets {out_x,out_y} = nG, where n is < the
+ * order of the group. */
+void p256_base_point_mul(const p256_int* n, p256_int* out_x, p256_int* out_y) {
+  felem x, y, z;
+
+  scalar_base_mult(x, y, z, n);
+
+  {
+    felem x_affine, y_affine;
+
+    point_to_affine(x_affine, y_affine, x, y, z);
+    from_montgomery(out_x, x_affine);
+    from_montgomery(out_y, y_affine);
+  }
+}
+
+/* p256_points_mul_vartime sets {out_x,out_y} = n1*G + n2*{in_x,in_y}, where
+ * n1 and n2 are < the order of the group.
+ *
+ * As indicated by the name, this function operates in variable time. This
+ * is safe because it's used for signature validation which doesn't deal
+ * with secrets. */
+void p256_points_mul_vartime(
+    const p256_int* n1, const p256_int* n2, const p256_int* in_x,
+    const p256_int* in_y, p256_int* out_x, p256_int* out_y) {
+  felem x1, y1, z1, x2, y2, z2, px, py;
+
+  /* If both scalars are zero, then the result is the point at infinity. */
+  if (p256_is_zero(n1) != 0 && p256_is_zero(n2) != 0) {
+    p256_clear(out_x);
+    p256_clear(out_y);
+    return;
+  }
+
+  to_montgomery(px, in_x);
+  to_montgomery(py, in_y);
+  scalar_base_mult(x1, y1, z1, n1);
+  scalar_mult(x2, y2, z2, px, py, n2);
+
+  if (p256_is_zero(n2) != 0) {
+    /* If n2 == 0, then {x2,y2,z2} is zero and the result is just
+         * {x1,y1,z1}. */
+  } else if (p256_is_zero(n1) != 0) {
+    /* If n1 == 0, then {x1,y1,z1} is zero and the result is just
+         * {x2,y2,z2}. */
+    memcpy(x1, x2, sizeof(x2));
+    memcpy(y1, y2, sizeof(y2));
+    memcpy(z1, z2, sizeof(z2));
+  } else {
+    /* This function handles the case where {x1,y1,z1} == {x2,y2,z2}. */
+    point_add_or_double_vartime(x1, y1, z1, x1, y1, z1, x2, y2, z2);
+  }
+
+  point_to_affine(px, py, x1, y1, z1);
+  from_montgomery(out_x, px);
+  from_montgomery(out_y, py);
+}
diff --git a/libmincrypt/p256_ecdsa.c b/libmincrypt/p256_ecdsa.c
new file mode 100644
index 0000000..f2264b0
--- /dev/null
+++ b/libmincrypt/p256_ecdsa.c
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+
+#include "mincrypt/p256_ecdsa.h"
+#include "mincrypt/p256.h"
+
+int p256_ecdsa_verify(const p256_int* key_x, const p256_int* key_y,
+                      const p256_int* message,
+                      const p256_int* r, const p256_int* s) {
+  p256_int u, v;
+
+  // Check public key.
+  if (!p256_is_valid_point(key_x, key_y)) return 0;
+
+  // Check r and s are != 0 % n.
+  p256_mod(&SECP256r1_n, r, &u);
+  p256_mod(&SECP256r1_n, s, &v);
+  if (p256_is_zero(&u) || p256_is_zero(&v)) return 0;
+
+  p256_modinv_vartime(&SECP256r1_n, s, &v);
+  p256_modmul(&SECP256r1_n, message, 0, &v, &u);  // message / s % n
+  p256_modmul(&SECP256r1_n, r, 0, &v, &v);  // r / s % n
+
+  p256_points_mul_vartime(&u, &v,
+                          key_x, key_y,
+                          &u, &v);
+
+  p256_mod(&SECP256r1_n, &u, &u);  // (x coord % p) % n
+  return p256_cmp(r, &u) == 0;
+}
+
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/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
new file mode 100644
index 0000000..d464c77
--- /dev/null
+++ b/libpixelflinger/Android.mk
@@ -0,0 +1,102 @@
+LOCAL_PATH:= system/core/libpixelflinger
+include $(CLEAR_VARS)
+
+#
+# C/C++ and ARMv5 objects
+#
+
+include $(CLEAR_VARS)
+
+ifneq ($(wildcard system/core/libpixelflinger/codeflinger/x86/X86Assembler.cpp),)
+    ifeq ($(TARGET_ARCH),x86)
+        TW_HAVE_X86_ACCELERATED_PIXELFLINGER := true
+    endif
+endif
+
+PIXELFLINGER_SRC_FILES += \
+    codeflinger/CodeCache.cpp \
+    codeflinger/tinyutils/SharedBuffer.cpp \
+    codeflinger/tinyutils/VectorImpl.cpp \
+    format.cpp \
+    clear.cpp \
+    raster.cpp \
+    buffer.cpp
+
+ifneq ($(TW_HAVE_X86_ACCELERATED_PIXELFLINGER),true)
+PIXELFLINGER_SRC_FILES += \
+	codeflinger/ARMAssemblerInterface.cpp \
+	codeflinger/ARMAssemblerProxy.cpp \
+	codeflinger/GGLAssembler.cpp \
+	codeflinger/load_store.cpp \
+	codeflinger/blending.cpp \
+	codeflinger/texturing.cpp \
+	fixed.cpp.arm \
+	picker.cpp.arm \
+	pixelflinger.cpp.arm \
+	trap.cpp.arm \
+	scanline.cpp.arm
+else
+PIXELFLINGER_SRC_FILES_x86 := \
+	codeflinger/x86/X86Assembler.cpp \
+	codeflinger/x86/GGLX86Assembler.cpp \
+	codeflinger/x86/load_store.cpp \
+	codeflinger/x86/blending.cpp \
+	codeflinger/x86/texturing.cpp \
+	fixed.cpp \
+	picker.cpp \
+	pixelflinger.cpp \
+	trap.cpp \
+	scanline.cpp
+
+PIXELFLINGER_C_INCLUDES_x86 := \
+	external/libenc
+
+endif
+
+PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer
+
+PIXELFLINGER_SRC_FILES_arm := \
+	codeflinger/ARMAssembler.cpp \
+	codeflinger/disassem.c \
+	col32cb16blend.S \
+	t32cb16blend.S \
+
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+PIXELFLINGER_SRC_FILES_arm += col32cb16blend_neon.S
+PIXELFLINGER_CFLAGS_arm += -D__ARM_HAVE_NEON
+endif
+
+PIXELFLINGER_SRC_FILES_arm64 := \
+	codeflinger/Arm64Assembler.cpp \
+	codeflinger/Arm64Disassembler.cpp \
+	arch-arm64/col32cb16blend.S \
+	arch-arm64/t32cb16blend.S \
+
+ifndef ARCH_MIPS_REV6
+PIXELFLINGER_SRC_FILES_mips := \
+	codeflinger/MIPSAssembler.cpp \
+	codeflinger/mips_disassem.c \
+	arch-mips/t32cb16blend.S \
+
+endif
+
+#
+# Static library version
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libpixelflinger_twrp
+LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
+LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
+LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
+LOCAL_SRC_FILES_x86 := $(PIXELFLINGER_SRC_FILES_x86)
+LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
+LOCAL_C_INCLUDES_x86 := $(PIXELFLINGER_C_INCLUDES_x86)
+ifeq ($(TW_HAVE_X86_ACCELERATED_PIXELFLINGER),true)
+LOCAL_WHOLE_STATIC_LIBRARIES += libenc
+LOCAL_C_INCLUDES += external/libenc
+endif
+include $(BUILD_STATIC_LIBRARY)
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..1831990
--- /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 j;
+	size_t size, i;
+
+	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..6ed9e60
--- /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 <stdio.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..69e08bd
--- /dev/null
+++ b/libtar/extract.c
@@ -0,0 +1,570 @@
+/*
+**  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>
+#include <string.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, i;
+	//uid_t uid;
+	//gid_t gid;
+	int fdout;
+	int 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 -= tar_min(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 k;
+	size_t size, i;
+	char buf[T_BLOCKSIZE];
+
+	if (!TH_ISREG(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	size = th_get_size(t);
+	for (i = size; i > 0; i -= tar_min(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..d2c4d00
--- /dev/null
+++ b/libtar/libtar.h
@@ -0,0 +1,311 @@
+/*
+**  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);
+
+#define tar_min(x, y) (x < y ? x : y)
+
+/***** 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.old/Android.mk b/minadbd.old/Android.mk
new file mode 100644
index 0000000..19c8b60
--- /dev/null
+++ b/minadbd.old/Android.mk
@@ -0,0 +1,30 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for adb
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# minadbd library
+# =========================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	adb.c \
+	fdevent.c \
+	fuse_adb_provider.c \
+	transport.c \
+	transport_usb.c \
+	sockets.c \
+	services.c \
+	usb_linux_client.c \
+	utils.c
+
+LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := libminadbd
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../
+LOCAL_SHARED_LIBRARIES := libfusesideload libcutils libc
+include $(BUILD_SHARED_LIBRARY)
diff --git a/minadbd.old/README.txt b/minadbd.old/README.txt
new file mode 100644
index 0000000..c9df484
--- /dev/null
+++ b/minadbd.old/README.txt
@@ -0,0 +1,39 @@
+The contents of this directory are copied from system/core/adb, with
+the following changes:
+
+adb.c
+  - much support for host mode and non-linux OS's stripped out; this
+    version only runs as adbd on the device.
+  - always setuid/setgid's itself to the shell user
+  - only uses USB transport
+  - references to JDWP removed
+  - main() removed
+  - all ADB_HOST and win32 code removed
+  - removed listeners, logging code, background server (for host)
+
+adb.h
+  - minor changes to match adb.c changes
+
+sockets.c
+  - references to JDWP removed
+  - ADB_HOST code removed
+
+services.c
+  - all services except echo_service (which is commented out) removed
+  - all host mode support removed
+  - sideload_service() added; this is the only service supported.  It
+    receives a single blob of data, writes it to a fixed filename, and
+    makes the process exit.
+
+Android.mk
+  - only builds in adbd mode; builds as static library instead of a
+    standalone executable.
+
+sysdeps.h
+  - changes adb_creat() to use O_NOFOLLOW
+
+transport.c
+  - removed ADB_HOST code
+
+transport_usb.c
+  - removed ADB_HOST code
diff --git a/minadbd.old/adb.c b/minadbd.old/adb.c
new file mode 100644
index 0000000..c35e830
--- /dev/null
+++ b/minadbd.old/adb.c
@@ -0,0 +1,418 @@
+/*
+ * 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.
+ */
+
+#define  TRACE_TAG   TRACE_ADB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "sysdeps.h"
+#include "adb.h"
+
+#include <private/android_filesystem_config.h>
+
+#if ADB_TRACE
+ADB_MUTEX_DEFINE( D_lock );
+#endif
+
+int HOST = 0;
+
+static const char *adb_device_banner = "sideload";
+
+char ADB_SIDELOAD_FILENAME[255];
+
+void fatal(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr, "error: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    exit(-1);
+}
+
+void fatal_errno(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr, "error: %s: ", strerror(errno));
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    exit(-1);
+}
+
+int   adb_trace_mask;
+
+/* read a comma/space/colum/semi-column separated list of tags
+ * from the ADB_TRACE environment variable and build the trace
+ * mask from it. note that '1' and 'all' are special cases to
+ * enable all tracing
+ */
+void  adb_trace_init(void)
+{
+    const char*  p = getenv("ADB_TRACE");
+    const char*  q;
+
+    static const struct {
+        const char*  tag;
+        int           flag;
+    } tags[] = {
+        { "1", 0 },
+        { "all", 0 },
+        { "adb", TRACE_ADB },
+        { "sockets", TRACE_SOCKETS },
+        { "packets", TRACE_PACKETS },
+        { "rwx", TRACE_RWX },
+        { "usb", TRACE_USB },
+        { "sync", TRACE_SYNC },
+        { "sysdeps", TRACE_SYSDEPS },
+        { "transport", TRACE_TRANSPORT },
+        { "jdwp", TRACE_JDWP },
+        { "services", TRACE_SERVICES },
+        { NULL, 0 }
+    };
+
+    if (p == NULL)
+            return;
+
+    /* use a comma/column/semi-colum/space separated list */
+    while (*p) {
+        int  len, tagn;
+
+        q = strpbrk(p, " ,:;");
+        if (q == NULL) {
+            q = p + strlen(p);
+        }
+        len = q - p;
+
+        for (tagn = 0; tags[tagn].tag != NULL; tagn++)
+        {
+            int  taglen = strlen(tags[tagn].tag);
+
+            if (len == taglen && !memcmp(tags[tagn].tag, p, len) )
+            {
+                int  flag = tags[tagn].flag;
+                if (flag == 0) {
+                    adb_trace_mask = ~0;
+                    return;
+                }
+                adb_trace_mask |= (1 << flag);
+                break;
+            }
+        }
+        p = q;
+        if (*p)
+            p++;
+    }
+}
+
+
+apacket *get_apacket(void)
+{
+    apacket *p = malloc(sizeof(apacket));
+    if(p == 0) fatal("failed to allocate an apacket");
+    memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
+    return p;
+}
+
+void put_apacket(apacket *p)
+{
+    free(p);
+}
+
+void handle_online(void)
+{
+    D("adb: online\n");
+}
+
+void handle_offline(atransport *t)
+{
+    D("adb: offline\n");
+    //Close the associated usb
+    run_transport_disconnects(t);
+}
+
+#if TRACE_PACKETS
+#define DUMPMAX 32
+void print_packet(const char *label, apacket *p)
+{
+    char *tag;
+    char *x;
+    unsigned count;
+
+    switch(p->msg.command){
+    case A_SYNC: tag = "SYNC"; break;
+    case A_CNXN: tag = "CNXN" ; break;
+    case A_OPEN: tag = "OPEN"; break;
+    case A_OKAY: tag = "OKAY"; break;
+    case A_CLSE: tag = "CLSE"; break;
+    case A_WRTE: tag = "WRTE"; break;
+    default: tag = "????"; break;
+    }
+
+    fprintf(stderr, "%s: %s %08x %08x %04x \"",
+            label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
+    count = p->msg.data_length;
+    x = (char*) p->data;
+    if(count > DUMPMAX) {
+        count = DUMPMAX;
+        tag = "\n";
+    } else {
+        tag = "\"\n";
+    }
+    while(count-- > 0){
+        if((*x >= ' ') && (*x < 127)) {
+            fputc(*x, stderr);
+        } else {
+            fputc('.', stderr);
+        }
+        x++;
+    }
+    fprintf(stderr, tag);
+}
+#endif
+
+static void send_ready(unsigned local, unsigned remote, atransport *t)
+{
+    D("Calling send_ready \n");
+    apacket *p = get_apacket();
+    p->msg.command = A_OKAY;
+    p->msg.arg0 = local;
+    p->msg.arg1 = remote;
+    send_packet(p, t);
+}
+
+static void send_close(unsigned local, unsigned remote, atransport *t)
+{
+    D("Calling send_close \n");
+    apacket *p = get_apacket();
+    p->msg.command = A_CLSE;
+    p->msg.arg0 = local;
+    p->msg.arg1 = remote;
+    send_packet(p, t);
+}
+
+static void send_connect(atransport *t)
+{
+    D("Calling send_connect \n");
+    apacket *cp = get_apacket();
+    cp->msg.command = A_CNXN;
+    cp->msg.arg0 = A_VERSION;
+    cp->msg.arg1 = MAX_PAYLOAD;
+    snprintf((char*) cp->data, sizeof cp->data, "%s::",
+            HOST ? "host" : adb_device_banner);
+    cp->msg.data_length = strlen((char*) cp->data) + 1;
+    send_packet(cp, t);
+}
+
+void parse_banner(char *banner, atransport *t)
+{
+    char *type, *product, *end;
+
+    D("parse_banner: %s\n", banner);
+    type = banner;
+    product = strchr(type, ':');
+    if(product) {
+        *product++ = 0;
+    } else {
+        product = "";
+    }
+
+        /* remove trailing ':' */
+    end = strchr(product, ':');
+    if(end) *end = 0;
+
+        /* save product name in device structure */
+    if (t->product == NULL) {
+        t->product = strdup(product);
+    } else if (strcmp(product, t->product) != 0) {
+        free(t->product);
+        t->product = strdup(product);
+    }
+
+    if(!strcmp(type, "bootloader")){
+        D("setting connection_state to CS_BOOTLOADER\n");
+        t->connection_state = CS_BOOTLOADER;
+        update_transports();
+        return;
+    }
+
+    if(!strcmp(type, "device")) {
+        D("setting connection_state to CS_DEVICE\n");
+        t->connection_state = CS_DEVICE;
+        update_transports();
+        return;
+    }
+
+    if(!strcmp(type, "recovery")) {
+        D("setting connection_state to CS_RECOVERY\n");
+        t->connection_state = CS_RECOVERY;
+        update_transports();
+        return;
+    }
+
+    if(!strcmp(type, "sideload")) {
+        D("setting connection_state to CS_SIDELOAD\n");
+        t->connection_state = CS_SIDELOAD;
+        update_transports();
+        return;
+    }
+
+    t->connection_state = CS_HOST;
+}
+
+void handle_packet(apacket *p, atransport *t)
+{
+    asocket *s;
+
+    D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],
+            ((char*) (&(p->msg.command)))[1],
+            ((char*) (&(p->msg.command)))[2],
+            ((char*) (&(p->msg.command)))[3]);
+    print_packet("recv", p);
+
+    switch(p->msg.command){
+    case A_SYNC:
+        if(p->msg.arg0){
+            send_packet(p, t);
+            if(HOST) send_connect(t);
+        } else {
+            t->connection_state = CS_OFFLINE;
+            handle_offline(t);
+            send_packet(p, t);
+        }
+        return;
+
+    case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
+            /* XXX verify version, etc */
+        if(t->connection_state != CS_OFFLINE) {
+            t->connection_state = CS_OFFLINE;
+            handle_offline(t);
+        }
+        parse_banner((char*) p->data, t);
+        handle_online();
+        if(!HOST) send_connect(t);
+        break;
+
+    case A_OPEN: /* OPEN(local-id, 0, "destination") */
+        if(t->connection_state != CS_OFFLINE) {
+            char *name = (char*) p->data;
+            name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
+            s = create_local_service_socket(name);
+            if(s == 0) {
+                send_close(0, p->msg.arg0, t);
+            } else {
+                s->peer = create_remote_socket(p->msg.arg0, t);
+                s->peer->peer = s;
+                send_ready(s->id, s->peer->id, t);
+                s->ready(s);
+            }
+        }
+        break;
+
+    case A_OKAY: /* READY(local-id, remote-id, "") */
+        if(t->connection_state != CS_OFFLINE) {
+            if((s = find_local_socket(p->msg.arg1))) {
+                if(s->peer == 0) {
+                    s->peer = create_remote_socket(p->msg.arg0, t);
+                    s->peer->peer = s;
+                }
+                s->ready(s);
+            }
+        }
+        break;
+
+    case A_CLSE: /* CLOSE(local-id, remote-id, "") */
+        if(t->connection_state != CS_OFFLINE) {
+            if((s = find_local_socket(p->msg.arg1))) {
+                s->close(s);
+            }
+        }
+        break;
+
+    case A_WRTE:
+        if(t->connection_state != CS_OFFLINE) {
+            if((s = find_local_socket(p->msg.arg1))) {
+                unsigned rid = p->msg.arg0;
+                p->len = p->msg.data_length;
+
+                if(s->enqueue(s, p) == 0) {
+                    D("Enqueue the socket\n");
+                    send_ready(s->id, rid, t);
+                }
+                return;
+            }
+        }
+        break;
+
+    default:
+        printf("handle_packet: what is %08x?!\n", p->msg.command);
+    }
+
+    put_apacket(p);
+}
+
+static void adb_cleanup(void)
+{
+    usb_cleanup();
+}
+
+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.
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    init_transport_registration();
+
+    // The minimal version of adbd only uses USB.
+    if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
+        // listen on USB
+        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();
+
+    usb_cleanup();
+
+    return 0;
+}
diff --git a/minadbd.old/adb.h b/minadbd.old/adb.h
new file mode 100644
index 0000000..08ee989
--- /dev/null
+++ b/minadbd.old/adb.h
@@ -0,0 +1,427 @@
+/*
+ * 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 __ADB_H
+#define __ADB_H
+
+#include <limits.h>
+
+#include "transport.h"  /* readx(), writex() */
+#include "fdevent.h"
+
+#define MAX_PAYLOAD 4096
+
+#define A_SYNC 0x434e5953
+#define A_CNXN 0x4e584e43
+#define A_OPEN 0x4e45504f
+#define A_OKAY 0x59414b4f
+#define A_CLSE 0x45534c43
+#define A_WRTE 0x45545257
+
+#define A_VERSION 0x01000000        // ADB protocol version
+
+#define ADB_VERSION_MAJOR 1         // Used for help/version information
+#define ADB_VERSION_MINOR 0         // Used for help/version information
+
+#define ADB_SERVER_VERSION    29    // Increment this when we want to force users to start a new adb server
+
+typedef struct amessage amessage;
+typedef struct apacket apacket;
+typedef struct asocket asocket;
+typedef struct aservice aservice;
+typedef struct atransport atransport;
+typedef struct adisconnect  adisconnect;
+typedef struct usb_handle usb_handle;
+
+struct amessage {
+    unsigned command;       /* command identifier constant      */
+    unsigned arg0;          /* first argument                   */
+    unsigned arg1;          /* second argument                  */
+    unsigned data_length;   /* length of payload (0 is allowed) */
+    unsigned data_check;    /* checksum of data payload         */
+    unsigned magic;         /* command ^ 0xffffffff             */
+};
+
+struct apacket
+{
+    apacket *next;
+
+    unsigned len;
+    unsigned char *ptr;
+
+    amessage msg;
+    unsigned char data[MAX_PAYLOAD];
+};
+
+/* An asocket represents one half of a connection between a local and
+** remote entity.  A local asocket is bound to a file descriptor.  A
+** remote asocket is bound to the protocol engine.
+*/
+struct asocket {
+        /* chain pointers for the local/remote list of
+        ** asockets that this asocket lives in
+        */
+    asocket *next;
+    asocket *prev;
+
+        /* the unique identifier for this asocket
+        */
+    unsigned id;
+
+        /* flag: set when the socket's peer has closed
+        ** but packets are still queued for delivery
+        */
+    int    closing;
+
+        /* the asocket we are connected to
+        */
+
+    asocket *peer;
+
+        /* For local asockets, the fde is used to bind
+        ** us to our fd event system.  For remote asockets
+        ** these fields are not used.
+        */
+    fdevent fde;
+    int fd;
+
+        /* queue of apackets waiting to be written
+        */
+    apacket *pkt_first;
+    apacket *pkt_last;
+
+        /* enqueue is called by our peer when it has data
+        ** for us.  It should return 0 if we can accept more
+        ** data or 1 if not.  If we return 1, we must call
+        ** peer->ready() when we once again are ready to
+        ** receive data.
+        */
+    int (*enqueue)(asocket *s, apacket *pkt);
+
+        /* ready is called by the peer when it is ready for
+        ** us to send data via enqueue again
+        */
+    void (*ready)(asocket *s);
+
+        /* close is called by the peer when it has gone away.
+        ** we are not allowed to make any further calls on the
+        ** peer once our close method is called.
+        */
+    void (*close)(asocket *s);
+
+        /* socket-type-specific extradata */
+    void *extra;
+
+    	/* A socket is bound to atransport */
+    atransport *transport;
+};
+
+
+/* the adisconnect structure is used to record a callback that
+** will be called whenever a transport is disconnected (e.g. by the user)
+** this should be used to cleanup objects that depend on the
+** transport (e.g. remote sockets, etc...)
+*/
+struct  adisconnect
+{
+    void        (*func)(void*  opaque, atransport*  t);
+    void*         opaque;
+    adisconnect*  next;
+    adisconnect*  prev;
+};
+
+
+/* a transport object models the connection to a remote device or emulator
+** there is one transport per connected device/emulator. a "local transport"
+** connects through TCP (for the emulator), while a "usb transport" through
+** USB (for real devices)
+**
+** note that kTransportHost doesn't really correspond to a real transport
+** object, it's a special value used to indicate that a client wants to
+** connect to a service implemented within the ADB server itself.
+*/
+typedef enum transport_type {
+        kTransportUsb,
+        kTransportLocal,
+        kTransportAny,
+        kTransportHost,
+} transport_type;
+
+struct atransport
+{
+    atransport *next;
+    atransport *prev;
+
+    int (*read_from_remote)(apacket *p, atransport *t);
+    int (*write_to_remote)(apacket *p, atransport *t);
+    void (*close)(atransport *t);
+    void (*kick)(atransport *t);
+
+    int fd;
+    int transport_socket;
+    fdevent transport_fde;
+    int ref_count;
+    unsigned sync_token;
+    int connection_state;
+    transport_type type;
+
+        /* usb handle or socket fd as needed */
+    usb_handle *usb;
+    int sfd;
+
+        /* used to identify transports for clients */
+    char *serial;
+    char *product;
+    int adb_port; // Use for emulators (local transport)
+
+        /* a list of adisconnect callbacks called when the transport is kicked */
+    int          kicked;
+    adisconnect  disconnects;
+};
+
+
+void print_packet(const char *label, apacket *p);
+
+asocket *find_local_socket(unsigned id);
+void install_local_socket(asocket *s);
+void remove_socket(asocket *s);
+void close_all_sockets(atransport *t);
+
+#define  LOCAL_CLIENT_PREFIX  "emulator-"
+
+asocket *create_local_socket(int fd);
+asocket *create_local_service_socket(const char *destination);
+
+asocket *create_remote_socket(unsigned id, atransport *t);
+void connect_to_remote(asocket *s, const char *destination);
+void connect_to_smartsocket(asocket *s);
+
+void fatal(const char *fmt, ...);
+void fatal_errno(const char *fmt, ...);
+
+void handle_packet(apacket *p, atransport *t);
+void send_packet(apacket *p, atransport *t);
+
+void get_my_path(char *s, size_t maxLen);
+int launch_server(int server_port);
+int adb_main(const char* path);
+
+
+/* transports are ref-counted
+** get_device_transport does an acquire on your behalf before returning
+*/
+void init_transport_registration(void);
+int  list_transports(char *buf, size_t  bufsize);
+void update_transports(void);
+
+asocket*  create_device_tracker(void);
+
+/* Obtain a transport from the available transports.
+** If state is != CS_ANY, only transports in that state are considered.
+** If serial is non-NULL then only the device with that serial will be chosen.
+** If no suitable transport is found, error is set.
+*/
+atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char **error_out);
+void   add_transport_disconnect( atransport*  t, adisconnect*  dis );
+void   remove_transport_disconnect( atransport*  t, adisconnect*  dis );
+void   run_transport_disconnects( atransport*  t );
+void   kick_transport( atransport*  t );
+
+/* initialize a transport object's func pointers and state */
+#if ADB_HOST
+int get_available_local_transport_index();
+#endif
+void init_usb_transport(atransport *t, usb_handle *usb, int state);
+
+/* for MacOS X cleanup */
+void close_usb_devices();
+
+/* these should only be used for the "adb disconnect" command */
+void unregister_transport(atransport *t);
+void unregister_all_tcp_transports();
+
+void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable);
+
+/* this should only be used for transports with connection_state == CS_NOPERM */
+void unregister_usb_transport(usb_handle *usb);
+
+atransport *find_transport(const char *serial);
+#if ADB_HOST
+atransport* find_emulator_transport_by_adb_port(int adb_port);
+#endif
+
+int service_to_fd(const char *name);
+#if ADB_HOST
+asocket *host_service_to_socket(const char*  name, const char *serial);
+#endif
+
+#if !ADB_HOST
+typedef enum {
+    BACKUP,
+    RESTORE
+} BackupOperation;
+int backup_service(BackupOperation operation, char* args);
+void framebuffer_service(int fd, void *cookie);
+void log_service(int fd, void *cookie);
+void remount_service(int fd, void *cookie);
+char * get_log_file_path(const char * log_name);
+#endif
+
+/* packet allocator */
+apacket *get_apacket(void);
+void put_apacket(apacket *p);
+
+int check_header(apacket *p);
+int check_data(apacket *p);
+
+/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */
+
+#define  ADB_TRACE    1
+
+/* IMPORTANT: if you change the following list, don't
+ * forget to update the corresponding 'tags' table in
+ * the adb_trace_init() function implemented in adb.c
+ */
+typedef enum {
+    TRACE_ADB = 0,   /* 0x001 */
+    TRACE_SOCKETS,
+    TRACE_PACKETS,
+    TRACE_TRANSPORT,
+    TRACE_RWX,       /* 0x010 */
+    TRACE_USB,
+    TRACE_SYNC,
+    TRACE_SYSDEPS,
+    TRACE_JDWP,      /* 0x100 */
+    TRACE_SERVICES,
+} AdbTrace;
+
+#if ADB_TRACE
+
+  extern int     adb_trace_mask;
+  extern unsigned char    adb_trace_output_count;
+  void    adb_trace_init(void);
+
+#  define ADB_TRACING  ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
+
+  /* you must define TRACE_TAG before using this macro */
+#  define  D(...)                                      \
+        do {                                           \
+            if (ADB_TRACING) {                         \
+                int save_errno = errno;                \
+                adb_mutex_lock(&D_lock);               \
+                fprintf(stderr, "%s::%s():",           \
+                        __FILE__, __FUNCTION__);       \
+                errno = save_errno;                    \
+                fprintf(stderr, __VA_ARGS__ );         \
+                fflush(stderr);                        \
+                adb_mutex_unlock(&D_lock);             \
+                errno = save_errno;                    \
+           }                                           \
+        } while (0)
+#  define  DR(...)                                     \
+        do {                                           \
+            if (ADB_TRACING) {                         \
+                int save_errno = errno;                \
+                adb_mutex_lock(&D_lock);               \
+                errno = save_errno;                    \
+                fprintf(stderr, __VA_ARGS__ );         \
+                fflush(stderr);                        \
+                adb_mutex_unlock(&D_lock);             \
+                errno = save_errno;                    \
+           }                                           \
+        } while (0)
+#else
+#  define  D(...)          ((void)0)
+#  define  DR(...)         ((void)0)
+#  define  ADB_TRACING     0
+#endif
+
+
+#if !TRACE_PACKETS
+#define print_packet(tag,p) do {} while (0)
+#endif
+
+#if ADB_HOST_ON_TARGET
+/* adb and adbd are coexisting on the target, so use 5038 for adb
+ * to avoid conflicting with adbd's usage of 5037
+ */
+#  define DEFAULT_ADB_PORT 5038
+#else
+#  define DEFAULT_ADB_PORT 5037
+#endif
+
+#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555
+
+#define ADB_CLASS              0xff
+#define ADB_SUBCLASS           0x42
+#define ADB_PROTOCOL           0x1
+
+
+void local_init(int port);
+int  local_connect(int  port);
+int  local_connect_arbitrary_ports(int console_port, int adb_port);
+
+/* usb host/client interface */
+void usb_init();
+void usb_cleanup();
+int usb_write(usb_handle *h, const void *data, int len);
+int usb_read(usb_handle *h, void *data, int len);
+int usb_close(usb_handle *h);
+void usb_kick(usb_handle *h);
+
+/* used for USB device detection */
+#if ADB_HOST
+int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
+#endif
+
+unsigned host_to_le32(unsigned n);
+int adb_commandline(int argc, char **argv);
+
+int connection_state(atransport *t);
+
+#define CS_ANY       -1
+#define CS_OFFLINE    0
+#define CS_BOOTLOADER 1
+#define CS_DEVICE     2
+#define CS_HOST       3
+#define CS_RECOVERY   4
+#define CS_NOPERM     5 /* Insufficient permissions to communicate with the device */
+#define CS_SIDELOAD   6
+#define CS_UNAUTHORIZED 7
+
+extern int HOST;
+extern int SHELL_EXIT_NOTIFY_FD;
+
+#define CHUNK_SIZE (64*1024)
+
+#if !ADB_HOST
+#define USB_ADB_PATH     "/dev/android_adb"
+
+#define USB_FFS_ADB_PATH  "/dev/usb-ffs/adb/"
+#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x
+
+#define USB_FFS_ADB_EP0   USB_FFS_ADB_EP(ep0)
+#define USB_FFS_ADB_OUT   USB_FFS_ADB_EP(ep1)
+#define USB_FFS_ADB_IN    USB_FFS_ADB_EP(ep2)
+#endif
+
+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/minadbd.old/fdevent.c b/minadbd.old/fdevent.c
new file mode 100644
index 0000000..5c374a7
--- /dev/null
+++ b/minadbd.old/fdevent.c
@@ -0,0 +1,695 @@
+/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
+**
+** Copyright 2006, Brian Swetland <swetland@frotz.net>
+**
+** 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 <sys/ioctl.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <fcntl.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "fdevent.h"
+#include "transport.h"
+#include "sysdeps.h"
+
+
+/* !!! Do not enable DEBUG for the adb that will run as the server:
+** both stdout and stderr are used to communicate between the client
+** and server. Any extra output will cause failures.
+*/
+#define DEBUG 0   /* non-0 will break adb server */
+
+// This socket is used when a subproc shell service exists.
+// It wakes up the fdevent_loop() and cause the correct handling
+// of the shell's pseudo-tty master. I.e. force close it.
+int SHELL_EXIT_NOTIFY_FD = -1;
+
+static void fatal(const char *fn, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr, "%s:", fn);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+    abort();
+}
+
+#define FATAL(x...) fatal(__FUNCTION__, x)
+
+#if DEBUG
+#define D(...) \
+    do { \
+        adb_mutex_lock(&D_lock);               \
+        int save_errno = errno;                \
+        fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__);  \
+        errno = save_errno;                    \
+        fprintf(stderr, __VA_ARGS__);          \
+        adb_mutex_unlock(&D_lock);             \
+        errno = save_errno;                    \
+    } while(0)
+static void dump_fde(fdevent *fde, const char *info)
+{
+    adb_mutex_lock(&D_lock);
+    fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
+            fde->state & FDE_READ ? 'R' : ' ',
+            fde->state & FDE_WRITE ? 'W' : ' ',
+            fde->state & FDE_ERROR ? 'E' : ' ',
+            info);
+    adb_mutex_unlock(&D_lock);
+}
+#else
+#define D(...) ((void)0)
+#define dump_fde(fde, info) do { } while(0)
+#endif
+
+#define FDE_EVENTMASK  0x00ff
+#define FDE_STATEMASK  0xff00
+
+#define FDE_ACTIVE     0x0100
+#define FDE_PENDING    0x0200
+#define FDE_CREATED    0x0400
+
+static void fdevent_plist_enqueue(fdevent *node);
+static void fdevent_plist_remove(fdevent *node);
+static fdevent *fdevent_plist_dequeue(void);
+static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata);
+
+static fdevent list_pending = {
+    .next = &list_pending,
+    .prev = &list_pending,
+};
+
+static fdevent **fd_table = 0;
+static int fd_table_max = 0;
+
+#ifdef CRAPTASTIC
+//HAVE_EPOLL
+
+#include <sys/epoll.h>
+
+static int epoll_fd = -1;
+
+static void fdevent_init()
+{
+        /* XXX: what's a good size for the passed in hint? */
+    epoll_fd = epoll_create(256);
+
+    if(epoll_fd < 0) {
+        perror("epoll_create() failed");
+        exit(1);
+    }
+
+        /* mark for close-on-exec */
+    fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+    struct epoll_event ev;
+
+    memset(&ev, 0, sizeof(ev));
+    ev.events = 0;
+    ev.data.ptr = fde;
+
+#if 0
+    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+        perror("epoll_ctl() failed\n");
+        exit(1);
+    }
+#endif
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+    struct epoll_event ev;
+
+    memset(&ev, 0, sizeof(ev));
+    ev.events = 0;
+    ev.data.ptr = fde;
+
+        /* technically we only need to delete if we
+        ** were actively monitoring events, but let's
+        ** be aggressive and do it anyway, just in case
+        ** something's out of sync
+        */
+    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+    struct epoll_event ev;
+    int active;
+
+    active = (fde->state & FDE_EVENTMASK) != 0;
+
+    memset(&ev, 0, sizeof(ev));
+    ev.events = 0;
+    ev.data.ptr = fde;
+
+    if(events & FDE_READ) ev.events |= EPOLLIN;
+    if(events & FDE_WRITE) ev.events |= EPOLLOUT;
+    if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
+
+    fde->state = (fde->state & FDE_STATEMASK) | events;
+
+    if(active) {
+            /* we're already active. if we're changing to *no*
+            ** events being monitored, we need to delete, otherwise
+            ** we need to just modify
+            */
+        if(ev.events) {
+            if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
+                perror("epoll_ctl() failed\n");
+                exit(1);
+            }
+        } else {
+            if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
+                perror("epoll_ctl() failed\n");
+                exit(1);
+            }
+        }
+    } else {
+            /* we're not active.  if we're watching events, we need
+            ** to add, otherwise we can just do nothing
+            */
+        if(ev.events) {
+            if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+                perror("epoll_ctl() failed\n");
+                exit(1);
+            }
+        }
+    }
+}
+
+static void fdevent_process()
+{
+    struct epoll_event events[256];
+    fdevent *fde;
+    int i, n;
+
+    n = epoll_wait(epoll_fd, events, 256, -1);
+
+    if(n < 0) {
+        if(errno == EINTR) return;
+        perror("epoll_wait");
+        exit(1);
+    }
+
+    for(i = 0; i < n; i++) {
+        struct epoll_event *ev = events + i;
+        fde = ev->data.ptr;
+
+        if(ev->events & EPOLLIN) {
+            fde->events |= FDE_READ;
+        }
+        if(ev->events & EPOLLOUT) {
+            fde->events |= FDE_WRITE;
+        }
+        if(ev->events & (EPOLLERR | EPOLLHUP)) {
+            fde->events |= FDE_ERROR;
+        }
+        if(fde->events) {
+            if(fde->state & FDE_PENDING) continue;
+            fde->state |= FDE_PENDING;
+            fdevent_plist_enqueue(fde);
+        }
+    }
+}
+
+#else /* USE_SELECT */
+
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+#else
+#include <sys/select.h>
+#endif
+
+static fd_set read_fds;
+static fd_set write_fds;
+static fd_set error_fds;
+
+static int select_n = 0;
+
+static void fdevent_init(void)
+{
+    FD_ZERO(&read_fds);
+    FD_ZERO(&write_fds);
+    FD_ZERO(&error_fds);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+    if(fde->fd >= select_n) {
+        select_n = fde->fd + 1;
+    }
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+    int i, n;
+
+    FD_CLR(fde->fd, &read_fds);
+    FD_CLR(fde->fd, &write_fds);
+    FD_CLR(fde->fd, &error_fds);
+
+    for(n = 0, i = 0; i < select_n; i++) {
+        if(fd_table[i] != 0) n = i;
+    }
+    select_n = n + 1;
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+    if(events & FDE_READ) {
+        FD_SET(fde->fd, &read_fds);
+    } else {
+        FD_CLR(fde->fd, &read_fds);
+    }
+    if(events & FDE_WRITE) {
+        FD_SET(fde->fd, &write_fds);
+    } else {
+        FD_CLR(fde->fd, &write_fds);
+    }
+    if(events & FDE_ERROR) {
+        FD_SET(fde->fd, &error_fds);
+    } else {
+        FD_CLR(fde->fd, &error_fds);
+    }
+
+    fde->state = (fde->state & FDE_STATEMASK) | events;
+}
+
+/* Looks at fd_table[] for bad FDs and sets bit in fds.
+** Returns the number of bad FDs.
+*/
+static int fdevent_fd_check(fd_set *fds)
+{
+    int i, n = 0;
+    fdevent *fde;
+
+    for(i = 0; i < select_n; i++) {
+        fde = fd_table[i];
+        if(fde == 0) continue;
+        if(fcntl(i, F_GETFL, NULL) < 0) {
+            FD_SET(i, fds);
+            n++;
+            // fde->state |= FDE_DONT_CLOSE;
+
+        }
+    }
+    return n;
+}
+
+#if !DEBUG
+static inline void dump_all_fds(const char *extra_msg) {}
+#else
+static void dump_all_fds(const char *extra_msg)
+{
+int i;
+    fdevent *fde;
+    // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
+    char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
+    size_t max_chars = FD_SETSIZE * 6 + 1;
+    int printed_out;
+#define SAFE_SPRINTF(...)                                                    \
+    do {                                                                     \
+        printed_out = snprintf(pb, max_chars, __VA_ARGS__);                  \
+        if (printed_out <= 0) {                                              \
+            D("... snprintf failed.\n");                                     \
+            return;                                                          \
+        }                                                                    \
+        if (max_chars < (unsigned int)printed_out) {                         \
+            D("... snprintf out of space.\n");                               \
+            return;                                                          \
+        }                                                                    \
+        pb += printed_out;                                                   \
+        max_chars -= printed_out;                                            \
+    } while(0)
+
+    for(i = 0; i < select_n; i++) {
+        fde = fd_table[i];
+        SAFE_SPRINTF("%d", i);
+        if(fde == 0) {
+            SAFE_SPRINTF("? ");
+            continue;
+        }
+        if(fcntl(i, F_GETFL, NULL) < 0) {
+            SAFE_SPRINTF("b");
+        }
+        SAFE_SPRINTF(" ");
+    }
+    D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
+}
+#endif
+
+static void fdevent_process()
+{
+    int i, n;
+    fdevent *fde;
+    unsigned events;
+    fd_set rfd, wfd, efd;
+
+    memcpy(&rfd, &read_fds, sizeof(fd_set));
+    memcpy(&wfd, &write_fds, sizeof(fd_set));
+    memcpy(&efd, &error_fds, sizeof(fd_set));
+
+    dump_all_fds("pre select()");
+
+    n = select(select_n, &rfd, &wfd, &efd, NULL);
+    int saved_errno = errno;
+    D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0);
+
+    dump_all_fds("post select()");
+
+    if(n < 0) {
+        switch(saved_errno) {
+        case EINTR: return;
+        case EBADF:
+            // Can't trust the FD sets after an error.
+            FD_ZERO(&wfd);
+            FD_ZERO(&efd);
+            FD_ZERO(&rfd);
+            break;
+        default:
+            D("Unexpected select() error=%d\n", saved_errno);
+            return;
+        }
+    }
+    if(n <= 0) {
+        // We fake a read, as the rest of the code assumes
+        // that errors will be detected at that point.
+        n = fdevent_fd_check(&rfd);
+    }
+
+    for(i = 0; (i < select_n) && (n > 0); i++) {
+        events = 0;
+        if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
+        if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
+        if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
+
+        if(events) {
+            fde = fd_table[i];
+            if(fde == 0)
+              FATAL("missing fde for fd %d\n", i);
+
+            fde->events |= events;
+
+            D("got events fde->fd=%d events=%04x, state=%04x\n",
+                fde->fd, fde->events, fde->state);
+            if(fde->state & FDE_PENDING) continue;
+            fde->state |= FDE_PENDING;
+            fdevent_plist_enqueue(fde);
+        }
+    }
+}
+
+#endif
+
+static void fdevent_register(fdevent *fde)
+{
+    if(fde->fd < 0) {
+        FATAL("bogus negative fd (%d)\n", fde->fd);
+    }
+
+    if(fde->fd >= fd_table_max) {
+        int oldmax = fd_table_max;
+        if(fde->fd > 32000) {
+            FATAL("bogus huuuuge fd (%d)\n", fde->fd);
+        }
+        if(fd_table_max == 0) {
+            fdevent_init();
+            fd_table_max = 256;
+        }
+        while(fd_table_max <= fde->fd) {
+            fd_table_max *= 2;
+        }
+        fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
+        if(fd_table == 0) {
+            FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+        }
+        memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
+    }
+
+    fd_table[fde->fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+    if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
+        FATAL("fd out of range (%d)\n", fde->fd);
+    }
+
+    if(fd_table[fde->fd] != fde) {
+        FATAL("fd_table out of sync [%d]\n", fde->fd);
+    }
+
+    fd_table[fde->fd] = 0;
+
+    if(!(fde->state & FDE_DONT_CLOSE)) {
+        dump_fde(fde, "close");
+        adb_close(fde->fd);
+    }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+    fdevent *list = &list_pending;
+
+    node->next = list;
+    node->prev = list->prev;
+    node->prev->next = node;
+    list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+    node->prev->next = node->next;
+    node->next->prev = node->prev;
+    node->next = 0;
+    node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+    fdevent *list = &list_pending;
+    fdevent *node = list->next;
+
+    if(node == list) return 0;
+
+    list->next = node->next;
+    list->next->prev = list;
+    node->next = 0;
+    node->prev = 0;
+
+    return node;
+}
+
+static void fdevent_call_fdfunc(fdevent* fde)
+{
+    unsigned events = fde->events;
+    fde->events = 0;
+    if(!(fde->state & FDE_PENDING)) return;
+    fde->state &= (~FDE_PENDING);
+    dump_fde(fde, "callback");
+    fde->func(fde->fd, events, fde->arg);
+}
+
+static void fdevent_subproc_event_func(int fd, unsigned ev, void *userdata)
+{
+
+    D("subproc handling on fd=%d ev=%04x\n", fd, ev);
+
+    // Hook oneself back into the fde's suitable for select() on read.
+    if((fd < 0) || (fd >= fd_table_max)) {
+        FATAL("fd %d out of range for fd_table \n", fd);
+    }
+    fdevent *fde = fd_table[fd];
+    fdevent_add(fde, FDE_READ);
+
+    if(ev & FDE_READ){
+      int subproc_fd;
+
+      if(readx(fd, &subproc_fd, sizeof(subproc_fd))) {
+          FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
+      }
+      if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
+          D("subproc_fd %d out of range 0, fd_table_max=%d\n",
+            subproc_fd, fd_table_max);
+          return;
+      }
+      fdevent *subproc_fde = fd_table[subproc_fd];
+      if(!subproc_fde) {
+          D("subproc_fd %d cleared from fd_table\n", subproc_fd);
+          return;
+      }
+      if(subproc_fde->fd != subproc_fd) {
+          // Already reallocated?
+          D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
+          return;
+      }
+
+      subproc_fde->force_eof = 1;
+
+      int rcount = 0;
+      ioctl(subproc_fd, FIONREAD, &rcount);
+      D("subproc with fd=%d  has rcount=%d err=%d\n",
+        subproc_fd, rcount, errno);
+
+      if(rcount) {
+        // If there is data left, it will show up in the select().
+        // This works because there is no other thread reading that
+        // data when in this fd_func().
+        return;
+      }
+
+      D("subproc_fde.state=%04x\n", subproc_fde->state);
+      subproc_fde->events |= FDE_READ;
+      if(subproc_fde->state & FDE_PENDING) {
+        return;
+      }
+      subproc_fde->state |= FDE_PENDING;
+      fdevent_call_fdfunc(subproc_fde);
+    }
+}
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
+{
+    fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+    if(fde == 0) return 0;
+    fdevent_install(fde, fd, func, arg);
+    fde->state |= FDE_CREATED;
+    return fde;
+}
+
+void fdevent_destroy(fdevent *fde)
+{
+    if(fde == 0) return;
+    if(!(fde->state & FDE_CREATED)) {
+        FATAL("fde %p not created by fdevent_create()\n", fde);
+    }
+    fdevent_remove(fde);
+}
+
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
+{
+    memset(fde, 0, sizeof(fdevent));
+    fde->state = FDE_ACTIVE;
+    fde->fd = fd;
+    fde->force_eof = 0;
+    fde->func = func;
+    fde->arg = arg;
+
+#ifndef HAVE_WINSOCK
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+    fdevent_register(fde);
+    dump_fde(fde, "connect");
+    fdevent_connect(fde);
+    fde->state |= FDE_ACTIVE;
+}
+
+void fdevent_remove(fdevent *fde)
+{
+    if(fde->state & FDE_PENDING) {
+        fdevent_plist_remove(fde);
+    }
+
+    if(fde->state & FDE_ACTIVE) {
+        fdevent_disconnect(fde);
+        dump_fde(fde, "disconnect");
+        fdevent_unregister(fde);
+    }
+
+    fde->state = 0;
+    fde->events = 0;
+}
+
+
+void fdevent_set(fdevent *fde, unsigned events)
+{
+    events &= FDE_EVENTMASK;
+
+    if((fde->state & FDE_EVENTMASK) == events) return;
+
+    if(fde->state & FDE_ACTIVE) {
+        fdevent_update(fde, events);
+        dump_fde(fde, "update");
+    }
+
+    fde->state = (fde->state & FDE_STATEMASK) | events;
+
+    if(fde->state & FDE_PENDING) {
+            /* if we're pending, make sure
+            ** we don't signal an event that
+            ** is no longer wanted.
+            */
+        fde->events &= (~events);
+        if(fde->events == 0) {
+            fdevent_plist_remove(fde);
+            fde->state &= (~FDE_PENDING);
+        }
+    }
+}
+
+void fdevent_add(fdevent *fde, unsigned events)
+{
+    fdevent_set(
+        fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+}
+
+void fdevent_del(fdevent *fde, unsigned events)
+{
+    fdevent_set(
+        fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+}
+
+void fdevent_subproc_setup()
+{
+    int s[2];
+
+    if(adb_socketpair(s)) {
+        FATAL("cannot create shell-exit socket-pair\n");
+    }
+    SHELL_EXIT_NOTIFY_FD = s[0];
+    fdevent *fde;
+    fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
+    if(!fde)
+      FATAL("cannot create fdevent for shell-exit handler\n");
+    fdevent_add(fde, FDE_READ);
+}
+
+void fdevent_loop()
+{
+    fdevent *fde;
+    fdevent_subproc_setup();
+
+    for(;;) {
+        D("--- ---- waiting for events\n");
+
+        fdevent_process();
+
+        while((fde = fdevent_plist_dequeue())) {
+            fdevent_call_fdfunc(fde);
+        }
+    }
+}
diff --git a/minadbd.old/fdevent.h b/minadbd.old/fdevent.h
new file mode 100644
index 0000000..a0ebe2a
--- /dev/null
+++ b/minadbd.old/fdevent.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2006 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 __FDEVENT_H
+#define __FDEVENT_H
+
+#include <stdint.h>  /* for int64_t */
+
+/* events that may be observed */
+#define FDE_READ              0x0001
+#define FDE_WRITE             0x0002
+#define FDE_ERROR             0x0004
+#define FDE_TIMEOUT           0x0008
+
+/* features that may be set (via the events set/add/del interface) */
+#define FDE_DONT_CLOSE        0x0080
+
+typedef struct fdevent fdevent;
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+
+/* Allocate and initialize a new fdevent object
+ * Note: use FD_TIMER as 'fd' to create a fd-less object
+ * (used to implement timers).
+*/
+fdevent *fdevent_create(int fd, fd_func func, void *arg);
+
+/* Uninitialize and deallocate an fdevent object that was
+** created by fdevent_create()
+*/
+void fdevent_destroy(fdevent *fde);
+
+/* Initialize an fdevent object that was externally allocated
+*/
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
+
+/* Uninitialize an fdevent object that was initialized by
+** fdevent_install()
+*/
+void fdevent_remove(fdevent *item);
+
+/* Change which events should cause notifications
+*/
+void fdevent_set(fdevent *fde, unsigned events);
+void fdevent_add(fdevent *fde, unsigned events);
+void fdevent_del(fdevent *fde, unsigned events);
+
+void fdevent_set_timeout(fdevent *fde, int64_t  timeout_ms);
+
+/* loop forever, handling events.
+*/
+void fdevent_loop();
+
+struct fdevent 
+{
+    fdevent *next;
+    fdevent *prev;
+
+    int fd;
+    int force_eof;
+
+    unsigned short state;
+    unsigned short events;
+
+    fd_func func;
+    void *arg;
+};
+
+
+#endif
diff --git a/minadbd.old/fuse_adb_provider.c b/minadbd.old/fuse_adb_provider.c
new file mode 100644
index 0000000..f80533a
--- /dev/null
+++ b/minadbd.old/fuse_adb_provider.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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 <stdio.h>
+#include <errno.h>
+
+#include "adb.h"
+#include "fuse_sideload.h"
+
+struct adb_data {
+    int sfd;  // file descriptor for the adb channel
+
+    uint64_t file_size;
+    uint32_t block_size;
+};
+
+static int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
+    struct adb_data* ad = (struct adb_data*)cookie;
+
+    char buf[10];
+    snprintf(buf, sizeof(buf), "%08u", block);
+    if (writex(ad->sfd, buf, 8) < 0) {
+        fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
+        return -EIO;
+    }
+
+    if (readx(ad->sfd, buffer, fetch_size) < 0) {
+        fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
+        return -EIO;
+    }
+
+    return 0;
+}
+
+static void close_adb(void* cookie) {
+    struct adb_data* ad = (struct adb_data*)cookie;
+
+    writex(ad->sfd, "DONEDONE", 8);
+}
+
+int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) {
+    struct adb_data ad;
+    struct provider_vtab vtab;
+
+    ad.sfd = sfd;
+    ad.file_size = file_size;
+    ad.block_size = block_size;
+
+    vtab.read_block = read_block_adb;
+    vtab.close = close_adb;
+
+    return run_fuse_sideload(&vtab, &ad, file_size, block_size);
+}
diff --git a/minadbd.old/fuse_adb_provider.h b/minadbd.old/fuse_adb_provider.h
new file mode 100644
index 0000000..0eb1f79
--- /dev/null
+++ b/minadbd.old/fuse_adb_provider.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 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 __FUSE_ADB_PROVIDER_H
+#define __FUSE_ADB_PROVIDER_H
+
+int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size);
+
+#endif
diff --git a/minadbd.old/mutex_list.h b/minadbd.old/mutex_list.h
new file mode 100644
index 0000000..652dd73
--- /dev/null
+++ b/minadbd.old/mutex_list.h
@@ -0,0 +1,26 @@
+/* the list of mutexes used by adb */
+/* #ifndef __MUTEX_LIST_H
+ * Do not use an include-guard. This file is included once to declare the locks
+ * and once in win32 to actually do the runtime initialization.
+ */
+#ifndef ADB_MUTEX
+#error ADB_MUTEX not defined when including this file
+#endif
+ADB_MUTEX(dns_lock)
+ADB_MUTEX(socket_list_lock)
+ADB_MUTEX(transport_lock)
+#if ADB_HOST
+ADB_MUTEX(local_transports_lock)
+#endif
+ADB_MUTEX(usb_lock)
+
+// Sadly logging to /data/adb/adb-... is not thread safe.
+//  After modifying adb.h::D() to count invocations:
+//   DEBUG(jpa):0:Handling main()
+//   DEBUG(jpa):1:[ usb_init - starting thread ]
+// (Oopsies, no :2:, and matching message is also gone.)
+//   DEBUG(jpa):3:[ usb_thread - opening device ]
+//   DEBUG(jpa):4:jdwp control socket started (10)
+ADB_MUTEX(D_lock)
+
+#undef ADB_MUTEX
diff --git a/minadbd.old/services.c b/minadbd.old/services.c
new file mode 100644
index 0000000..218b84a
--- /dev/null
+++ b/minadbd.old/services.c
@@ -0,0 +1,147 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+#include "fdevent.h"
+#include "fuse_adb_provider.h"
+
+#define  TRACE_TAG  TRACE_SERVICES
+#include "adb.h"
+
+typedef struct stinfo stinfo;
+
+struct stinfo {
+    void (*func)(int fd, void *cookie);
+    int fd;
+    void *cookie;
+};
+
+
+void *service_bootstrap_func(void *x)
+{
+    stinfo *sti = x;
+    sti->func(sti->fd, sti->cookie);
+    free(sti);
+    return 0;
+}
+
+static void sideload_host_service(int sfd, void* cookie)
+{
+    char* saveptr;
+    const char* s = strtok_r(cookie, ":", &saveptr);
+    uint64_t file_size = strtoull(s, NULL, 10);
+    s = strtok_r(NULL, ":", &saveptr);
+    uint32_t block_size = strtoul(s, NULL, 10);
+
+    printf("sideload-host file size %llu block size %lu\n", file_size, block_size);
+
+    int result = run_adb_fuse(sfd, file_size, block_size);
+
+    printf("sideload_host finished\n");
+    sleep(1);
+    exit(result == 0 ? 0 : 1);
+}
+
+#if 0
+static void echo_service(int fd, void *cookie)
+{
+    char buf[4096];
+    int r;
+    char *p;
+    int c;
+
+    for(;;) {
+        r = read(fd, buf, 4096);
+        if(r == 0) goto done;
+        if(r < 0) {
+            if(errno == EINTR) continue;
+            else goto done;
+        }
+
+        c = r;
+        p = buf;
+        while(c > 0) {
+            r = write(fd, p, c);
+            if(r > 0) {
+                c -= r;
+                p += r;
+                continue;
+            }
+            if((r < 0) && (errno == EINTR)) continue;
+            goto done;
+        }
+    }
+done:
+    close(fd);
+}
+#endif
+
+static int create_service_thread(void (*func)(int, void *), void *cookie)
+{
+    stinfo *sti;
+    adb_thread_t t;
+    int s[2];
+
+    if(adb_socketpair(s)) {
+        printf("cannot create service socket pair\n");
+        return -1;
+    }
+
+    sti = malloc(sizeof(stinfo));
+    if(sti == 0) fatal("cannot allocate stinfo");
+    sti->func = func;
+    sti->cookie = cookie;
+    sti->fd = s[1];
+
+    if(adb_thread_create( &t, service_bootstrap_func, sti)){
+        free(sti);
+        adb_close(s[0]);
+        adb_close(s[1]);
+        printf("cannot create service thread\n");
+        return -1;
+    }
+
+    D("service thread started, %d:%d\n",s[0], s[1]);
+    return s[0];
+}
+
+int service_to_fd(const char *name)
+{
+    int ret = -1;
+
+    if (!strncmp(name, "sideload:", 9)) {
+        // this exit status causes recovery to print a special error
+        // message saying to use a newer adb (that supports
+        // sideload-host).
+        exit(3);
+    } else if (!strncmp(name, "sideload-host:", 14)) {
+        ret = create_service_thread(sideload_host_service, (void*)(name + 14));
+#if 0
+    } else if(!strncmp(name, "echo:", 5)){
+        ret = create_service_thread(echo_service, 0);
+#endif
+    }
+    if (ret >= 0) {
+        close_on_exec(ret);
+    }
+    return ret;
+}
diff --git a/minadbd.old/sockets.c b/minadbd.old/sockets.c
new file mode 100644
index 0000000..817410d
--- /dev/null
+++ b/minadbd.old/sockets.c
@@ -0,0 +1,731 @@
+/*
+ * 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 <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "sysdeps.h"
+
+#define  TRACE_TAG  TRACE_SOCKETS
+#include "adb.h"
+
+ADB_MUTEX_DEFINE( socket_list_lock );
+
+static void local_socket_close_locked(asocket *s);
+
+int sendfailmsg(int fd, const char *reason)
+{
+    char buf[9];
+    int len;
+    len = strlen(reason);
+    if(len > 0xffff) len = 0xffff;
+    snprintf(buf, sizeof buf, "FAIL%04x", len);
+    if(writex(fd, buf, 8)) return -1;
+    return writex(fd, reason, len);
+}
+
+//extern int online;
+
+static unsigned local_socket_next_id = 1;
+
+static asocket local_socket_list = {
+    .next = &local_socket_list,
+    .prev = &local_socket_list,
+};
+
+/* the the list of currently closing local sockets.
+** these have no peer anymore, but still packets to
+** write to their fd.
+*/
+static asocket local_socket_closing_list = {
+    .next = &local_socket_closing_list,
+    .prev = &local_socket_closing_list,
+};
+
+asocket *find_local_socket(unsigned id)
+{
+    asocket *s;
+    asocket *result = NULL;
+
+    adb_mutex_lock(&socket_list_lock);
+    for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
+        if (s->id == id) {
+            result = s;
+            break;
+        }
+    }
+    adb_mutex_unlock(&socket_list_lock);
+
+    return result;
+}
+
+static void
+insert_local_socket(asocket*  s, asocket*  list)
+{
+    s->next       = list;
+    s->prev       = s->next->prev;
+    s->prev->next = s;
+    s->next->prev = s;
+}
+
+
+void install_local_socket(asocket *s)
+{
+    adb_mutex_lock(&socket_list_lock);
+
+    s->id = local_socket_next_id++;
+    insert_local_socket(s, &local_socket_list);
+
+    adb_mutex_unlock(&socket_list_lock);
+}
+
+void remove_socket(asocket *s)
+{
+    // socket_list_lock should already be held
+    if (s->prev && s->next)
+    {
+        s->prev->next = s->next;
+        s->next->prev = s->prev;
+        s->next = 0;
+        s->prev = 0;
+        s->id = 0;
+    }
+}
+
+void close_all_sockets(atransport *t)
+{
+    asocket *s;
+
+        /* this is a little gross, but since s->close() *will* modify
+        ** the list out from under you, your options are limited.
+        */
+    adb_mutex_lock(&socket_list_lock);
+restart:
+    for(s = local_socket_list.next; s != &local_socket_list; s = s->next){
+        if(s->transport == t || (s->peer && s->peer->transport == t)) {
+            local_socket_close_locked(s);
+            goto restart;
+        }
+    }
+    adb_mutex_unlock(&socket_list_lock);
+}
+
+static int local_socket_enqueue(asocket *s, apacket *p)
+{
+    D("LS(%d): enqueue %d\n", s->id, p->len);
+
+    p->ptr = p->data;
+
+        /* if there is already data queue'd, we will receive
+        ** events when it's time to write.  just add this to
+        ** the tail
+        */
+    if(s->pkt_first) {
+        goto enqueue;
+    }
+
+        /* write as much as we can, until we
+        ** would block or there is an error/eof
+        */
+    while(p->len > 0) {
+        int r = adb_write(s->fd, p->ptr, p->len);
+        if(r > 0) {
+            p->len -= r;
+            p->ptr += r;
+            continue;
+        }
+        if((r == 0) || (errno != EAGAIN)) {
+            D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
+            s->close(s);
+            return 1; /* not ready (error) */
+        } else {
+            break;
+        }
+    }
+
+    if(p->len == 0) {
+        put_apacket(p);
+        return 0; /* ready for more data */
+    }
+
+enqueue:
+    p->next = 0;
+    if(s->pkt_first) {
+        s->pkt_last->next = p;
+    } else {
+        s->pkt_first = p;
+    }
+    s->pkt_last = p;
+
+        /* make sure we are notified when we can drain the queue */
+    fdevent_add(&s->fde, FDE_WRITE);
+
+    return 1; /* not ready (backlog) */
+}
+
+static void local_socket_ready(asocket *s)
+{
+        /* far side is ready for data, pay attention to
+           readable events */
+    fdevent_add(&s->fde, FDE_READ);
+//    D("LS(%d): ready()\n", s->id);
+}
+
+static void local_socket_close(asocket *s)
+{
+    adb_mutex_lock(&socket_list_lock);
+    local_socket_close_locked(s);
+    adb_mutex_unlock(&socket_list_lock);
+}
+
+// be sure to hold the socket list lock when calling this
+static void local_socket_destroy(asocket  *s)
+{
+    apacket *p, *n;
+    D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd);
+
+        /* IMPORTANT: the remove closes the fd
+        ** that belongs to this socket
+        */
+    fdevent_remove(&s->fde);
+
+        /* dispose of any unwritten data */
+    for(p = s->pkt_first; p; p = n) {
+        D("LS(%d): discarding %d bytes\n", s->id, p->len);
+        n = p->next;
+        put_apacket(p);
+    }
+    remove_socket(s);
+    free(s);
+}
+
+
+static void local_socket_close_locked(asocket *s)
+{
+    D("entered. LS(%d) fd=%d\n", s->id, s->fd);
+    if(s->peer) {
+        D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
+          s->id, s->peer->id, s->peer->fd);
+        s->peer->peer = 0;
+        // tweak to avoid deadlock
+        if (s->peer->close == local_socket_close) {
+            local_socket_close_locked(s->peer);
+        } else {
+            s->peer->close(s->peer);
+        }
+        s->peer = 0;
+    }
+
+        /* If we are already closing, or if there are no
+        ** pending packets, destroy immediately
+        */
+    if (s->closing || s->pkt_first == NULL) {
+        int   id = s->id;
+        local_socket_destroy(s);
+        D("LS(%d): closed\n", id);
+        return;
+    }
+
+        /* otherwise, put on the closing list
+        */
+    D("LS(%d): closing\n", s->id);
+    s->closing = 1;
+    fdevent_del(&s->fde, FDE_READ);
+    remove_socket(s);
+    D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd);
+    insert_local_socket(s, &local_socket_closing_list);
+}
+
+static void local_socket_event_func(int fd, unsigned ev, void *_s)
+{
+    asocket *s = _s;
+
+    D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
+
+    /* put the FDE_WRITE processing before the FDE_READ
+    ** in order to simplify the code.
+    */
+    if(ev & FDE_WRITE){
+        apacket *p;
+
+        while((p = s->pkt_first) != 0) {
+            while(p->len > 0) {
+                int r = adb_write(fd, p->ptr, p->len);
+                if(r > 0) {
+                    p->ptr += r;
+                    p->len -= r;
+                    continue;
+                }
+                if(r < 0) {
+                    /* returning here is ok because FDE_READ will
+                    ** be processed in the next iteration loop
+                    */
+                    if(errno == EAGAIN) return;
+                    if(errno == EINTR) continue;
+                }
+                D(" closing after write because r=%d and errno is %d\n", r, errno);
+                s->close(s);
+                return;
+            }
+
+            if(p->len == 0) {
+                s->pkt_first = p->next;
+                if(s->pkt_first == 0) s->pkt_last = 0;
+                put_apacket(p);
+            }
+        }
+
+            /* if we sent the last packet of a closing socket,
+            ** we can now destroy it.
+            */
+        if (s->closing) {
+            D(" closing because 'closing' is set after write\n");
+            s->close(s);
+            return;
+        }
+
+            /* no more packets queued, so we can ignore
+            ** writable events again and tell our peer
+            ** to resume writing
+            */
+        fdevent_del(&s->fde, FDE_WRITE);
+        s->peer->ready(s->peer);
+    }
+
+
+    if(ev & FDE_READ){
+        apacket *p = get_apacket();
+        unsigned char *x = p->data;
+        size_t avail = MAX_PAYLOAD;
+        int r;
+        int is_eof = 0;
+
+        while(avail > 0) {
+            r = adb_read(fd, x, avail);
+            D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu\n",
+              s->id, s->fd, r, r<0?errno:0, avail);
+            if(r > 0) {
+                avail -= r;
+                x += r;
+                continue;
+            }
+            if(r < 0) {
+                if(errno == EAGAIN) break;
+                if(errno == EINTR) continue;
+            }
+
+                /* r = 0 or unhandled error */
+            is_eof = 1;
+            break;
+        }
+        D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
+          s->id, s->fd, r, is_eof, s->fde.force_eof);
+        if((avail == MAX_PAYLOAD) || (s->peer == 0)) {
+            put_apacket(p);
+        } else {
+            p->len = MAX_PAYLOAD - avail;
+
+            r = s->peer->enqueue(s->peer, p);
+            D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r);
+
+            if(r < 0) {
+                    /* error return means they closed us as a side-effect
+                    ** and we must return immediately.
+                    **
+                    ** note that if we still have buffered packets, the
+                    ** socket will be placed on the closing socket list.
+                    ** this handler function will be called again
+                    ** to process FDE_WRITE events.
+                    */
+                return;
+            }
+
+            if(r > 0) {
+                    /* if the remote cannot accept further events,
+                    ** we disable notification of READs.  They'll
+                    ** be enabled again when we get a call to ready()
+                    */
+                fdevent_del(&s->fde, FDE_READ);
+            }
+        }
+        /* Don't allow a forced eof if data is still there */
+        if((s->fde.force_eof && !r) || is_eof) {
+            D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof);
+            s->close(s);
+        }
+    }
+
+    if(ev & FDE_ERROR){
+            /* this should be caught be the next read or write
+            ** catching it here means we may skip the last few
+            ** bytes of readable data.
+            */
+//        s->close(s);
+        D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
+
+        return;
+    }
+}
+
+asocket *create_local_socket(int fd)
+{
+    asocket *s = calloc(1, sizeof(asocket));
+    if (s == NULL) fatal("cannot allocate socket");
+    s->fd = fd;
+    s->enqueue = local_socket_enqueue;
+    s->ready = local_socket_ready;
+    s->close = local_socket_close;
+    install_local_socket(s);
+
+    fdevent_install(&s->fde, fd, local_socket_event_func, s);
+/*    fdevent_add(&s->fde, FDE_ERROR); */
+    //fprintf(stderr, "Created local socket in create_local_socket \n");
+    D("LS(%d): created (fd=%d)\n", s->id, s->fd);
+    return s;
+}
+
+asocket *create_local_service_socket(const char *name)
+{
+    asocket *s;
+    int fd;
+
+    fd = service_to_fd(name);
+    if(fd < 0) return 0;
+
+    s = create_local_socket(fd);
+    D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
+    return s;
+}
+
+/* a Remote socket is used to send/receive data to/from a given transport object
+** it needs to be closed when the transport is forcibly destroyed by the user
+*/
+typedef struct aremotesocket {
+    asocket      socket;
+    adisconnect  disconnect;
+} aremotesocket;
+
+static int remote_socket_enqueue(asocket *s, apacket *p)
+{
+    D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
+      s->id, s->fd, s->peer->fd);
+    p->msg.command = A_WRTE;
+    p->msg.arg0 = s->peer->id;
+    p->msg.arg1 = s->id;
+    p->msg.data_length = p->len;
+    send_packet(p, s->transport);
+    return 1;
+}
+
+static void remote_socket_ready(asocket *s)
+{
+    D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n",
+      s->id, s->fd, s->peer->fd);
+    apacket *p = get_apacket();
+    p->msg.command = A_OKAY;
+    p->msg.arg0 = s->peer->id;
+    p->msg.arg1 = s->id;
+    send_packet(p, s->transport);
+}
+
+static void remote_socket_close(asocket *s)
+{
+    D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
+      s->id, s->fd, s->peer?s->peer->fd:-1);
+    apacket *p = get_apacket();
+    p->msg.command = A_CLSE;
+    if(s->peer) {
+        p->msg.arg0 = s->peer->id;
+        s->peer->peer = 0;
+        D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
+          s->id, s->peer->id, s->peer->fd);
+        s->peer->close(s->peer);
+    }
+    p->msg.arg1 = s->id;
+    send_packet(p, s->transport);
+    D("RS(%d): closed\n", s->id);
+    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+    free(s);
+}
+
+static void remote_socket_disconnect(void*  _s, atransport*  t)
+{
+    asocket*  s    = _s;
+    asocket*  peer = s->peer;
+
+    D("remote_socket_disconnect RS(%d)\n", s->id);
+    if (peer) {
+        peer->peer = NULL;
+        peer->close(peer);
+    }
+    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+    free(s);
+}
+
+asocket *create_remote_socket(unsigned id, atransport *t)
+{
+    asocket *s = calloc(1, sizeof(aremotesocket));
+    adisconnect*  dis = &((aremotesocket*)s)->disconnect;
+
+    if (s == NULL) fatal("cannot allocate socket");
+    s->id = id;
+    s->enqueue = remote_socket_enqueue;
+    s->ready = remote_socket_ready;
+    s->close = remote_socket_close;
+    s->transport = t;
+
+    dis->func   = remote_socket_disconnect;
+    dis->opaque = s;
+    add_transport_disconnect( t, dis );
+    D("RS(%d): created\n", s->id);
+    return s;
+}
+
+void connect_to_remote(asocket *s, const char *destination)
+{
+    D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd);
+    apacket *p = get_apacket();
+    int len = strlen(destination) + 1;
+
+    if(len > (MAX_PAYLOAD-1)) {
+        fatal("destination oversized");
+    }
+
+    D("LS(%d): connect('%s')\n", s->id, destination);
+    p->msg.command = A_OPEN;
+    p->msg.arg0 = s->id;
+    p->msg.data_length = len;
+    strcpy((char*) p->data, destination);
+    send_packet(p, s->transport);
+}
+
+
+/* this is used by magic sockets to rig local sockets to
+   send the go-ahead message when they connect */
+static void local_socket_ready_notify(asocket *s)
+{
+    s->ready = local_socket_ready;
+    s->close = local_socket_close;
+    adb_write(s->fd, "OKAY", 4);
+    s->ready(s);
+}
+
+/* this is used by magic sockets to rig local sockets to
+   send the failure message if they are closed before
+   connected (to avoid closing them without a status message) */
+static void local_socket_close_notify(asocket *s)
+{
+    s->ready = local_socket_ready;
+    s->close = local_socket_close;
+    sendfailmsg(s->fd, "closed");
+    s->close(s);
+}
+
+unsigned unhex(unsigned char *s, int len)
+{
+    unsigned n = 0, c;
+
+    while(len-- > 0) {
+        switch((c = *s++)) {
+        case '0': case '1': case '2':
+        case '3': case '4': case '5':
+        case '6': case '7': case '8':
+        case '9':
+            c -= '0';
+            break;
+        case 'a': case 'b': case 'c':
+        case 'd': case 'e': case 'f':
+            c = c - 'a' + 10;
+            break;
+        case 'A': case 'B': case 'C':
+        case 'D': case 'E': case 'F':
+            c = c - 'A' + 10;
+            break;
+        default:
+            return 0xffffffff;
+        }
+
+        n = (n << 4) | c;
+    }
+
+    return n;
+}
+
+/* skip_host_serial return the position in a string
+   skipping over the 'serial' parameter in the ADB protocol,
+   where parameter string may be a host:port string containing
+   the protocol delimiter (colon). */
+char *skip_host_serial(char *service) {
+    char *first_colon, *serial_end;
+
+    first_colon = strchr(service, ':');
+    if (!first_colon) {
+        /* No colon in service string. */
+        return NULL;
+    }
+    serial_end = first_colon;
+    if (isdigit(serial_end[1])) {
+        serial_end++;
+        while ((*serial_end) && isdigit(*serial_end)) {
+            serial_end++;
+        }
+        if ((*serial_end) != ':') {
+            // Something other than numbers was found, reset the end.
+            serial_end = first_colon;
+        }
+    }
+    return serial_end;
+}
+
+static int smart_socket_enqueue(asocket *s, apacket *p)
+{
+    unsigned len;
+
+    D("SS(%d): enqueue %d\n", s->id, p->len);
+
+    if(s->pkt_first == 0) {
+        s->pkt_first = p;
+        s->pkt_last = p;
+    } else {
+        if((s->pkt_first->len + p->len) > MAX_PAYLOAD) {
+            D("SS(%d): overflow\n", s->id);
+            put_apacket(p);
+            goto fail;
+        }
+
+        memcpy(s->pkt_first->data + s->pkt_first->len,
+               p->data, p->len);
+        s->pkt_first->len += p->len;
+        put_apacket(p);
+
+        p = s->pkt_first;
+    }
+
+        /* don't bother if we can't decode the length */
+    if(p->len < 4) return 0;
+
+    len = unhex(p->data, 4);
+    if((len < 1) ||  (len > 1024)) {
+        D("SS(%d): bad size (%d)\n", s->id, len);
+        goto fail;
+    }
+
+    D("SS(%d): len is %d\n", s->id, len );
+        /* can't do anything until we have the full header */
+    if((len + 4) > p->len) {
+        D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
+        return 0;
+    }
+
+    p->data[len + 4] = 0;
+
+    D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
+
+    if (s->transport == NULL) {
+        char* error_string = "unknown failure";
+        s->transport = acquire_one_transport (CS_ANY,
+                kTransportAny, NULL, &error_string);
+
+        if (s->transport == NULL) {
+            sendfailmsg(s->peer->fd, error_string);
+            goto fail;
+        }
+    }
+
+    if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) {
+           /* if there's no remote we fail the connection
+            ** right here and terminate it
+            */
+        sendfailmsg(s->peer->fd, "device offline (x)");
+        goto fail;
+    }
+
+
+        /* instrument our peer to pass the success or fail
+        ** message back once it connects or closes, then
+        ** detach from it, request the connection, and
+        ** tear down
+        */
+    s->peer->ready = local_socket_ready_notify;
+    s->peer->close = local_socket_close_notify;
+    s->peer->peer = 0;
+        /* give him our transport and upref it */
+    s->peer->transport = s->transport;
+
+    connect_to_remote(s->peer, (char*) (p->data + 4));
+    s->peer = 0;
+    s->close(s);
+    return 1;
+
+fail:
+        /* we're going to close our peer as a side-effect, so
+        ** return -1 to signal that state to the local socket
+        ** who is enqueueing against us
+        */
+    s->close(s);
+    return -1;
+}
+
+static void smart_socket_ready(asocket *s)
+{
+    D("SS(%d): ready\n", s->id);
+}
+
+static void smart_socket_close(asocket *s)
+{
+    D("SS(%d): closed\n", s->id);
+    if(s->pkt_first){
+        put_apacket(s->pkt_first);
+    }
+    if(s->peer) {
+        s->peer->peer = 0;
+        s->peer->close(s->peer);
+        s->peer = 0;
+    }
+    free(s);
+}
+
+asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act))
+{
+    D("Creating smart socket \n");
+    asocket *s = calloc(1, sizeof(asocket));
+    if (s == NULL) fatal("cannot allocate socket");
+    s->enqueue = smart_socket_enqueue;
+    s->ready = smart_socket_ready;
+    s->close = smart_socket_close;
+    s->extra = action_cb;
+
+    D("SS(%d): created %p\n", s->id, action_cb);
+    return s;
+}
+
+void smart_socket_action(asocket *s, const char *act)
+{
+
+}
+
+void connect_to_smartsocket(asocket *s)
+{
+    D("Connecting to smart socket \n");
+    asocket *ss = create_smart_socket(smart_socket_action);
+    s->peer = ss;
+    ss->peer = s;
+    s->ready(s);
+}
diff --git a/minadbd.old/sysdeps.h b/minadbd.old/sysdeps.h
new file mode 100644
index 0000000..800ddb7
--- /dev/null
+++ b/minadbd.old/sysdeps.h
@@ -0,0 +1,494 @@
+/*
+ * 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.
+ */
+
+/* this file contains system-dependent definitions used by ADB
+ * they're related to threads, sockets and file descriptors
+ */
+#ifndef _ADB_SYSDEPS_H
+#define _ADB_SYSDEPS_H
+
+#ifdef __CYGWIN__
+#  undef _WIN32
+#endif
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <process.h>
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+
+#define OS_PATH_SEPARATOR '\\'
+#define OS_PATH_SEPARATOR_STR "\\"
+
+typedef CRITICAL_SECTION          adb_mutex_t;
+
+#define  ADB_MUTEX_DEFINE(x)     adb_mutex_t   x
+
+/* declare all mutexes */
+/* For win32, adb_sysdeps_init() will do the mutex runtime initialization. */
+#define  ADB_MUTEX(x)   extern adb_mutex_t  x;
+#include "mutex_list.h"
+
+extern void  adb_sysdeps_init(void);
+
+static __inline__ void adb_mutex_lock( adb_mutex_t*  lock )
+{
+    EnterCriticalSection( lock );
+}
+
+static __inline__ void  adb_mutex_unlock( adb_mutex_t*  lock )
+{
+    LeaveCriticalSection( lock );
+}
+
+typedef struct { unsigned  tid; }  adb_thread_t;
+
+typedef  void*  (*adb_thread_func_t)(void*  arg);
+
+typedef  void (*win_thread_func_t)(void*  arg);
+
+static __inline__ int  adb_thread_create( adb_thread_t  *thread, adb_thread_func_t  func, void*  arg)
+{
+    thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
+    if (thread->tid == (unsigned)-1L) {
+        return -1;
+    }
+    return 0;
+}
+
+static __inline__ void  close_on_exec(int  fd)
+{
+    /* nothing really */
+}
+
+extern void  disable_tcp_nagle(int  fd);
+
+#define  lstat    stat   /* no symlinks on Win32 */
+
+#define  S_ISLNK(m)   0   /* no symlinks on Win32 */
+
+static __inline__  int    adb_unlink(const char*  path)
+{
+    int  rc = unlink(path);
+
+    if (rc == -1 && errno == EACCES) {
+        /* unlink returns EACCES when the file is read-only, so we first */
+        /* try to make it writable, then unlink again...                  */
+        rc = chmod(path, _S_IREAD|_S_IWRITE );
+        if (rc == 0)
+            rc = unlink(path);
+    }
+    return rc;
+}
+#undef  unlink
+#define unlink  ___xxx_unlink
+
+static __inline__ int  adb_mkdir(const char*  path, int mode)
+{
+	return _mkdir(path);
+}
+#undef   mkdir
+#define  mkdir  ___xxx_mkdir
+
+extern int  adb_open(const char*  path, int  options);
+extern int  adb_creat(const char*  path, int  mode);
+extern int  adb_read(int  fd, void* buf, int len);
+extern int  adb_write(int  fd, const void*  buf, int  len);
+extern int  adb_lseek(int  fd, int  pos, int  where);
+extern int  adb_shutdown(int  fd);
+extern int  adb_close(int  fd);
+
+static __inline__ int  unix_close(int fd)
+{
+    return close(fd);
+}
+#undef   close
+#define  close   ____xxx_close
+
+static __inline__  int  unix_read(int  fd, void*  buf, size_t  len)
+{
+    return read(fd, buf, len);
+}
+#undef   read
+#define  read  ___xxx_read
+
+static __inline__  int  unix_write(int  fd, const void*  buf, size_t  len)
+{
+    return write(fd, buf, len);
+}
+#undef   write
+#define  write  ___xxx_write
+
+static __inline__ int  adb_open_mode(const char* path, int options, int mode)
+{
+    return adb_open(path, options);
+}
+
+static __inline__ int  unix_open(const char*  path, int options,...)
+{
+    if ((options & O_CREAT) == 0)
+    {
+        return  open(path, options);
+    }
+    else
+    {
+        int      mode;
+        va_list  args;
+        va_start( args, options );
+        mode = va_arg( args, int );
+        va_end( args );
+        return open(path, options, mode);
+    }
+}
+#define  open    ___xxx_unix_open
+
+
+/* normally provided by <cutils/misc.h> */
+extern void*  load_file(const char*  pathname, unsigned*  psize);
+
+/* normally provided by <cutils/sockets.h> */
+extern int socket_loopback_client(int port, int type);
+extern int socket_network_client(const char *host, int port, int type);
+extern int socket_loopback_server(int port, int type);
+extern int socket_inaddr_any_server(int port, int type);
+
+/* normally provided by "fdevent.h" */
+
+#define FDE_READ              0x0001
+#define FDE_WRITE             0x0002
+#define FDE_ERROR             0x0004
+#define FDE_DONT_CLOSE        0x0080
+
+typedef struct fdevent fdevent;
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg);
+void     fdevent_destroy(fdevent *fde);
+void     fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
+void     fdevent_remove(fdevent *item);
+void     fdevent_set(fdevent *fde, unsigned events);
+void     fdevent_add(fdevent *fde, unsigned events);
+void     fdevent_del(fdevent *fde, unsigned events);
+void     fdevent_loop();
+
+struct fdevent {
+    fdevent *next;
+    fdevent *prev;
+
+    int fd;
+    int force_eof;
+
+    unsigned short state;
+    unsigned short events;
+
+    fd_func func;
+    void *arg;
+};
+
+static __inline__ void  adb_sleep_ms( int  mseconds )
+{
+    Sleep( mseconds );
+}
+
+extern int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen);
+
+#undef   accept
+#define  accept  ___xxx_accept
+
+static __inline__  int  adb_socket_setbufsize( int   fd, int  bufsize )
+{
+    int opt = bufsize;
+    return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt));
+}
+
+extern int  adb_socketpair( int  sv[2] );
+
+static __inline__  char*  adb_dirstart( const char*  path )
+{
+    char*  p  = strchr(path, '/');
+    char*  p2 = strchr(path, '\\');
+
+    if ( !p )
+        p = p2;
+    else if ( p2 && p2 > p )
+        p = p2;
+
+    return p;
+}
+
+static __inline__  char*  adb_dirstop( const char*  path )
+{
+    char*  p  = strrchr(path, '/');
+    char*  p2 = strrchr(path, '\\');
+
+    if ( !p )
+        p = p2;
+    else if ( p2 && p2 > p )
+        p = p2;
+
+    return p;
+}
+
+static __inline__  int  adb_is_absolute_host_path( const char*  path )
+{
+    return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
+}
+
+#else /* !_WIN32 a.k.a. Unix */
+
+#include "fdevent.h"
+#include <cutils/sockets.h>
+#include <cutils/properties.h>
+#include <cutils/misc.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string.h>
+
+#define OS_PATH_SEPARATOR '/'
+#define OS_PATH_SEPARATOR_STR "/"
+
+typedef  pthread_mutex_t          adb_mutex_t;
+
+#define  ADB_MUTEX_INITIALIZER    PTHREAD_MUTEX_INITIALIZER
+#define  adb_mutex_init           pthread_mutex_init
+#define  adb_mutex_lock           pthread_mutex_lock
+#define  adb_mutex_unlock         pthread_mutex_unlock
+#define  adb_mutex_destroy        pthread_mutex_destroy
+
+#define  ADB_MUTEX_DEFINE(m)      adb_mutex_t   m = PTHREAD_MUTEX_INITIALIZER
+
+#define  adb_cond_t               pthread_cond_t
+#define  adb_cond_init            pthread_cond_init
+#define  adb_cond_wait            pthread_cond_wait
+#define  adb_cond_broadcast       pthread_cond_broadcast
+#define  adb_cond_signal          pthread_cond_signal
+#define  adb_cond_destroy         pthread_cond_destroy
+
+/* declare all mutexes */
+#define  ADB_MUTEX(x)   extern adb_mutex_t  x;
+#include "mutex_list.h"
+
+static __inline__ void  close_on_exec(int  fd)
+{
+    fcntl( fd, F_SETFD, FD_CLOEXEC );
+}
+
+static __inline__ int  unix_open(const char*  path, int options,...)
+{
+    if ((options & O_CREAT) == 0)
+    {
+        return  open(path, options);
+    }
+    else
+    {
+        int      mode;
+        va_list  args;
+        va_start( args, options );
+        mode = va_arg( args, int );
+        va_end( args );
+        return open(path, options, mode);
+    }
+}
+
+static __inline__ int  adb_open_mode( const char*  pathname, int  options, int  mode )
+{
+    return open( pathname, options, mode );
+}
+
+static __inline__  int  adb_creat(const char*  path, int  mode)
+{
+    int  fd = open(path, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode);
+
+    if ( fd < 0 )
+        return -1;
+
+    close_on_exec(fd);
+    return fd;
+}
+#undef   creat
+#define  creat  ___xxx_creat
+
+static __inline__ int  adb_open( const char*  pathname, int  options )
+{
+    int  fd = open( pathname, options );
+    if (fd < 0)
+        return -1;
+    close_on_exec( fd );
+    return fd;
+}
+#undef   open
+#define  open    ___xxx_open
+
+static __inline__ int  adb_shutdown(int fd)
+{
+    return shutdown(fd, SHUT_RDWR);
+}
+#undef   shutdown
+#define  shutdown   ____xxx_shutdown
+
+static __inline__ int  adb_close(int fd)
+{
+    return close(fd);
+}
+#undef   close
+#define  close   ____xxx_close
+
+
+static __inline__  int  adb_read(int  fd, void*  buf, size_t  len)
+{
+    return read(fd, buf, len);
+}
+
+#undef   read
+#define  read  ___xxx_read
+
+static __inline__  int  adb_write(int  fd, const void*  buf, size_t  len)
+{
+    return write(fd, buf, len);
+}
+#undef   write
+#define  write  ___xxx_write
+
+static __inline__ int   adb_lseek(int  fd, int  pos, int  where)
+{
+    return lseek(fd, pos, where);
+}
+#undef   lseek
+#define  lseek   ___xxx_lseek
+
+static __inline__  int    adb_unlink(const char*  path)
+{
+    return  unlink(path);
+}
+#undef  unlink
+#define unlink  ___xxx_unlink
+
+static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
+{
+    int fd;
+
+    fd = accept(serverfd, addr, addrlen);
+    if (fd >= 0)
+        close_on_exec(fd);
+
+    return fd;
+}
+
+#undef   accept
+#define  accept  ___xxx_accept
+
+#define  unix_read   adb_read
+#define  unix_write  adb_write
+#define  unix_close  adb_close
+
+typedef  pthread_t                 adb_thread_t;
+
+typedef void*  (*adb_thread_func_t)( void*  arg );
+
+static __inline__ int  adb_thread_create( adb_thread_t  *pthread, adb_thread_func_t  start, void*  arg )
+{
+    pthread_attr_t   attr;
+
+    pthread_attr_init (&attr);
+    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+
+    return pthread_create( pthread, &attr, start, arg );
+}
+
+static __inline__  int  adb_socket_setbufsize( int   fd, int  bufsize )
+{
+    int opt = bufsize;
+    return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+}
+
+static __inline__ void  disable_tcp_nagle(int fd)
+{
+    int  on = 1;
+    setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
+}
+
+
+static __inline__ int  unix_socketpair( int  d, int  type, int  protocol, int sv[2] )
+{
+    return socketpair( d, type, protocol, sv );
+}
+
+static __inline__ int  adb_socketpair( int  sv[2] )
+{
+    int  rc;
+
+    rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
+    if (rc < 0)
+        return -1;
+
+    close_on_exec( sv[0] );
+    close_on_exec( sv[1] );
+    return 0;
+}
+
+#undef   socketpair
+#define  socketpair   ___xxx_socketpair
+
+static __inline__ void  adb_sleep_ms( int  mseconds )
+{
+    usleep( mseconds*1000 );
+}
+
+static __inline__ int  adb_mkdir(const char*  path, int mode)
+{
+    return mkdir(path, mode);
+}
+#undef   mkdir
+#define  mkdir  ___xxx_mkdir
+
+static __inline__ void  adb_sysdeps_init(void)
+{
+}
+
+static __inline__ char*  adb_dirstart(const char*  path)
+{
+    return strchr(path, '/');
+}
+
+static __inline__ char*  adb_dirstop(const char*  path)
+{
+    return strrchr(path, '/');
+}
+
+static __inline__  int  adb_is_absolute_host_path( const char*  path )
+{
+    return path[0] == '/';
+}
+
+#endif /* !_WIN32 */
+
+#endif /* _ADB_SYSDEPS_H */
diff --git a/minadbd.old/transport.c b/minadbd.old/transport.c
new file mode 100644
index 0000000..92679f5
--- /dev/null
+++ b/minadbd.old/transport.c
@@ -0,0 +1,803 @@
+/*
+ * 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 <errno.h>
+
+#include "sysdeps.h"
+
+#define   TRACE_TAG  TRACE_TRANSPORT
+#include "adb.h"
+
+static void transport_unref(atransport *t);
+
+static atransport transport_list = {
+    .next = &transport_list,
+    .prev = &transport_list,
+};
+
+ADB_MUTEX_DEFINE( transport_lock );
+
+#if ADB_TRACE
+#define MAX_DUMP_HEX_LEN 16
+static void  dump_hex( const unsigned char*  ptr, size_t  len )
+{
+    int  nn, len2 = len;
+    // Build a string instead of logging each character.
+    // MAX chars in 2 digit hex, one space, MAX chars, one '\0'.
+    char buffer[MAX_DUMP_HEX_LEN *2 + 1 + MAX_DUMP_HEX_LEN + 1 ], *pb = buffer;
+
+    if (len2 > MAX_DUMP_HEX_LEN) len2 = MAX_DUMP_HEX_LEN;
+
+    for (nn = 0; nn < len2; nn++) {
+        sprintf(pb, "%02x", ptr[nn]);
+        pb += 2;
+    }
+    sprintf(pb++, " ");
+
+    for (nn = 0; nn < len2; nn++) {
+        int  c = ptr[nn];
+        if (c < 32 || c > 127)
+            c = '.';
+        *pb++ =  c;
+    }
+    *pb++ = '\0';
+    DR("%s\n", buffer);
+}
+#endif
+
+void
+kick_transport(atransport*  t)
+{
+    if (t && !t->kicked)
+    {
+        int  kicked;
+
+        adb_mutex_lock(&transport_lock);
+        kicked = t->kicked;
+        if (!kicked)
+            t->kicked = 1;
+        adb_mutex_unlock(&transport_lock);
+
+        if (!kicked)
+            t->kick(t);
+    }
+}
+
+void
+run_transport_disconnects(atransport*  t)
+{
+    adisconnect*  dis = t->disconnects.next;
+
+    D("%s: run_transport_disconnects\n", t->serial);
+    while (dis != &t->disconnects) {
+        adisconnect*  next = dis->next;
+        dis->func( dis->opaque, t );
+        dis = next;
+    }
+}
+
+#if ADB_TRACE
+static void
+dump_packet(const char* name, const char* func, apacket* p)
+{
+    unsigned  command = p->msg.command;
+    int       len     = p->msg.data_length;
+    char      cmd[9];
+    char      arg0[12], arg1[12];
+    int       n;
+
+    for (n = 0; n < 4; n++) {
+        int  b = (command >> (n*8)) & 255;
+        if (b < 32 || b >= 127)
+            break;
+        cmd[n] = (char)b;
+    }
+    if (n == 4) {
+        cmd[4] = 0;
+    } else {
+        /* There is some non-ASCII name in the command, so dump
+            * the hexadecimal value instead */
+        snprintf(cmd, sizeof cmd, "%08x", command);
+    }
+
+    if (p->msg.arg0 < 256U)
+        snprintf(arg0, sizeof arg0, "%d", p->msg.arg0);
+    else
+        snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0);
+
+    if (p->msg.arg1 < 256U)
+        snprintf(arg1, sizeof arg1, "%d", p->msg.arg1);
+    else
+        snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
+
+    D("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ",
+        name, func, cmd, arg0, arg1, len);
+    dump_hex(p->data, len);
+}
+#endif /* ADB_TRACE */
+
+static int
+read_packet(int  fd, const char* name, apacket** ppacket)
+{
+    char *p = (char*)ppacket;  /* really read a packet address */
+    int   r;
+    int   len = sizeof(*ppacket);
+    char  buff[8];
+    if (!name) {
+        snprintf(buff, sizeof buff, "fd=%d", fd);
+        name = buff;
+    }
+    while(len > 0) {
+        r = adb_read(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p   += r;
+        } else {
+            D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
+            if((r < 0) && (errno == EINTR)) continue;
+            return -1;
+        }
+    }
+
+#if ADB_TRACE
+    if (ADB_TRACING) {
+        dump_packet(name, "from remote", *ppacket);
+    }
+#endif
+    return 0;
+}
+
+static int
+write_packet(int  fd, const char* name, apacket** ppacket)
+{
+    char *p = (char*) ppacket;  /* we really write the packet address */
+    int r, len = sizeof(ppacket);
+    char buff[8];
+    if (!name) {
+        snprintf(buff, sizeof buff, "fd=%d", fd);
+        name = buff;
+    }
+
+#if ADB_TRACE
+    if (ADB_TRACING) {
+        dump_packet(name, "to remote", *ppacket);
+    }
+#endif
+    len = sizeof(ppacket);
+    while(len > 0) {
+        r = adb_write(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p += r;
+        } else {
+            D("%s: write_packet (fd=%d) error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
+            if((r < 0) && (errno == EINTR)) continue;
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static void transport_socket_events(int fd, unsigned events, void *_t)
+{
+    atransport *t = _t;
+    D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events);
+    if(events & FDE_READ){
+        apacket *p = 0;
+        if(read_packet(fd, t->serial, &p)){
+            D("%s: failed to read packet from transport socket on fd %d\n", t->serial, fd);
+        } else {
+            handle_packet(p, (atransport *) _t);
+        }
+    }
+}
+
+void send_packet(apacket *p, atransport *t)
+{
+    unsigned char *x;
+    unsigned sum;
+    unsigned count;
+
+    p->msg.magic = p->msg.command ^ 0xffffffff;
+
+    count = p->msg.data_length;
+    x = (unsigned char *) p->data;
+    sum = 0;
+    while(count-- > 0){
+        sum += *x++;
+    }
+    p->msg.data_check = sum;
+
+    print_packet("send", p);
+
+    if (t == NULL) {
+        D("Transport is null \n");
+        // Zap errno because print_packet() and other stuff have errno effect.
+        errno = 0;
+        fatal_errno("Transport is null");
+    }
+
+    if(write_packet(t->transport_socket, t->serial, &p)){
+        fatal_errno("cannot enqueue packet on transport socket");
+    }
+}
+
+/* The transport is opened by transport_register_func before
+** the input and output threads are started.
+**
+** The output thread issues a SYNC(1, token) message to let
+** the input thread know to start things up.  In the event
+** of transport IO failure, the output thread will post a
+** SYNC(0,0) message to ensure shutdown.
+**
+** The transport will not actually be closed until both
+** threads exit, but the input thread will kick the transport
+** on its way out to disconnect the underlying device.
+*/
+
+static void *output_thread(void *_t)
+{
+    atransport *t = _t;
+    apacket *p;
+
+    D("%s: starting transport output thread on fd %d, SYNC online (%d)\n",
+       t->serial, t->fd, t->sync_token + 1);
+    p = get_apacket();
+    p->msg.command = A_SYNC;
+    p->msg.arg0 = 1;
+    p->msg.arg1 = ++(t->sync_token);
+    p->msg.magic = A_SYNC ^ 0xffffffff;
+    if(write_packet(t->fd, t->serial, &p)) {
+        put_apacket(p);
+        D("%s: failed to write SYNC packet\n", t->serial);
+        goto oops;
+    }
+
+    D("%s: data pump started\n", t->serial);
+    for(;;) {
+        p = get_apacket();
+
+        if(t->read_from_remote(p, t) == 0){
+            D("%s: received remote packet, sending to transport\n",
+              t->serial);
+            if(write_packet(t->fd, t->serial, &p)){
+                put_apacket(p);
+                D("%s: failed to write apacket to transport\n", t->serial);
+                goto oops;
+            }
+        } else {
+            D("%s: remote read failed for transport\n", t->serial);
+            put_apacket(p);
+            break;
+        }
+    }
+
+    D("%s: SYNC offline for transport\n", t->serial);
+    p = get_apacket();
+    p->msg.command = A_SYNC;
+    p->msg.arg0 = 0;
+    p->msg.arg1 = 0;
+    p->msg.magic = A_SYNC ^ 0xffffffff;
+    if(write_packet(t->fd, t->serial, &p)) {
+        put_apacket(p);
+        D("%s: failed to write SYNC apacket to transport", t->serial);
+    }
+
+oops:
+    D("%s: transport output thread is exiting\n", t->serial);
+    kick_transport(t);
+    transport_unref(t);
+    return 0;
+}
+
+static void *input_thread(void *_t)
+{
+    atransport *t = _t;
+    apacket *p;
+    int active = 0;
+
+    D("%s: starting transport input thread, reading from fd %d\n",
+       t->serial, t->fd);
+
+    for(;;){
+        if(read_packet(t->fd, t->serial, &p)) {
+            D("%s: failed to read apacket from transport on fd %d\n",
+               t->serial, t->fd );
+            break;
+        }
+        if(p->msg.command == A_SYNC){
+            if(p->msg.arg0 == 0) {
+                D("%s: transport SYNC offline\n", t->serial);
+                put_apacket(p);
+                break;
+            } else {
+                if(p->msg.arg1 == t->sync_token) {
+                    D("%s: transport SYNC online\n", t->serial);
+                    active = 1;
+                } else {
+                    D("%s: transport ignoring SYNC %d != %d\n",
+                      t->serial, p->msg.arg1, t->sync_token);
+                }
+            }
+        } else {
+            if(active) {
+                D("%s: transport got packet, sending to remote\n", t->serial);
+                t->write_to_remote(p, t);
+            } else {
+                D("%s: transport ignoring packet while offline\n", t->serial);
+            }
+        }
+
+        put_apacket(p);
+    }
+
+    // this is necessary to avoid a race condition that occured when a transport closes
+    // while a client socket is still active.
+    close_all_sockets(t);
+
+    D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd);
+    kick_transport(t);
+    transport_unref(t);
+    return 0;
+}
+
+
+static int transport_registration_send = -1;
+static int transport_registration_recv = -1;
+static fdevent transport_registration_fde;
+
+void  update_transports(void)
+{
+    // nothing to do on the device side
+}
+
+typedef struct tmsg tmsg;
+struct tmsg
+{
+    atransport *transport;
+    int         action;
+};
+
+static int
+transport_read_action(int  fd, struct tmsg*  m)
+{
+    char *p   = (char*)m;
+    int   len = sizeof(*m);
+    int   r;
+
+    while(len > 0) {
+        r = adb_read(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p   += r;
+        } else {
+            if((r < 0) && (errno == EINTR)) continue;
+            D("transport_read_action: on fd %d, error %d: %s\n",
+              fd, errno, strerror(errno));
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int
+transport_write_action(int  fd, struct tmsg*  m)
+{
+    char *p   = (char*)m;
+    int   len = sizeof(*m);
+    int   r;
+
+    while(len > 0) {
+        r = adb_write(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p   += r;
+        } else {
+            if((r < 0) && (errno == EINTR)) continue;
+            D("transport_write_action: on fd %d, error %d: %s\n",
+              fd, errno, strerror(errno));
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static void transport_registration_func(int _fd, unsigned ev, void *data)
+{
+    tmsg m;
+    adb_thread_t output_thread_ptr;
+    adb_thread_t input_thread_ptr;
+    int s[2];
+    atransport *t;
+
+    if(!(ev & FDE_READ)) {
+        return;
+    }
+
+    if(transport_read_action(_fd, &m)) {
+        fatal_errno("cannot read transport registration socket");
+    }
+
+    t = m.transport;
+
+    if(m.action == 0){
+        D("transport: %s removing and free'ing %d\n", t->serial, t->transport_socket);
+
+            /* IMPORTANT: the remove closes one half of the
+            ** socket pair.  The close closes the other half.
+            */
+        fdevent_remove(&(t->transport_fde));
+        adb_close(t->fd);
+
+        adb_mutex_lock(&transport_lock);
+        t->next->prev = t->prev;
+        t->prev->next = t->next;
+        adb_mutex_unlock(&transport_lock);
+
+        run_transport_disconnects(t);
+
+        if (t->product)
+            free(t->product);
+        if (t->serial)
+            free(t->serial);
+
+        memset(t,0xee,sizeof(atransport));
+        free(t);
+
+        update_transports();
+        return;
+    }
+
+    /* don't create transport threads for inaccessible devices */
+    if (t->connection_state != CS_NOPERM) {
+        /* initial references are the two threads */
+        t->ref_count = 2;
+
+        if(adb_socketpair(s)) {
+            fatal_errno("cannot open transport socketpair");
+        }
+
+        D("transport: %s (%d,%d) starting\n", t->serial, s[0], s[1]);
+
+        t->transport_socket = s[0];
+        t->fd = s[1];
+
+        fdevent_install(&(t->transport_fde),
+                        t->transport_socket,
+                        transport_socket_events,
+                        t);
+
+        fdevent_set(&(t->transport_fde), FDE_READ);
+
+        if(adb_thread_create(&input_thread_ptr, input_thread, t)){
+            fatal_errno("cannot create input thread");
+        }
+
+        if(adb_thread_create(&output_thread_ptr, output_thread, t)){
+            fatal_errno("cannot create output thread");
+        }
+    }
+
+        /* put us on the master device list */
+    adb_mutex_lock(&transport_lock);
+    t->next = &transport_list;
+    t->prev = transport_list.prev;
+    t->next->prev = t;
+    t->prev->next = t;
+    adb_mutex_unlock(&transport_lock);
+
+    t->disconnects.next = t->disconnects.prev = &t->disconnects;
+
+    update_transports();
+}
+
+void init_transport_registration(void)
+{
+    int s[2];
+
+    if(adb_socketpair(s)){
+        fatal_errno("cannot open transport registration socketpair");
+    }
+
+    transport_registration_send = s[0];
+    transport_registration_recv = s[1];
+
+    fdevent_install(&transport_registration_fde,
+                    transport_registration_recv,
+                    transport_registration_func,
+                    0);
+
+    fdevent_set(&transport_registration_fde, FDE_READ);
+}
+
+/* the fdevent select pump is single threaded */
+static void register_transport(atransport *transport)
+{
+    tmsg m;
+    m.transport = transport;
+    m.action = 1;
+    D("transport: %s registered\n", transport->serial);
+    if(transport_write_action(transport_registration_send, &m)) {
+        fatal_errno("cannot write transport registration socket\n");
+    }
+}
+
+static void remove_transport(atransport *transport)
+{
+    tmsg m;
+    m.transport = transport;
+    m.action = 0;
+    D("transport: %s removed\n", transport->serial);
+    if(transport_write_action(transport_registration_send, &m)) {
+        fatal_errno("cannot write transport registration socket\n");
+    }
+}
+
+
+static void transport_unref_locked(atransport *t)
+{
+    t->ref_count--;
+    if (t->ref_count == 0) {
+        D("transport: %s unref (kicking and closing)\n", t->serial);
+        if (!t->kicked) {
+            t->kicked = 1;
+            t->kick(t);
+        }
+        t->close(t);
+        remove_transport(t);
+    } else {
+        D("transport: %s unref (count=%d)\n", t->serial, t->ref_count);
+    }
+}
+
+static void transport_unref(atransport *t)
+{
+    if (t) {
+        adb_mutex_lock(&transport_lock);
+        transport_unref_locked(t);
+        adb_mutex_unlock(&transport_lock);
+    }
+}
+
+void add_transport_disconnect(atransport*  t, adisconnect*  dis)
+{
+    adb_mutex_lock(&transport_lock);
+    dis->next       = &t->disconnects;
+    dis->prev       = dis->next->prev;
+    dis->prev->next = dis;
+    dis->next->prev = dis;
+    adb_mutex_unlock(&transport_lock);
+}
+
+void remove_transport_disconnect(atransport*  t, adisconnect*  dis)
+{
+    dis->prev->next = dis->next;
+    dis->next->prev = dis->prev;
+    dis->next = dis->prev = dis;
+}
+
+
+atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out)
+{
+    atransport *t;
+    atransport *result = NULL;
+    int ambiguous = 0;
+
+retry:
+    if (error_out)
+        *error_out = "device not found";
+
+    adb_mutex_lock(&transport_lock);
+    for (t = transport_list.next; t != &transport_list; t = t->next) {
+        if (t->connection_state == CS_NOPERM) {
+        if (error_out)
+            *error_out = "insufficient permissions for device";
+            continue;
+        }
+
+        /* check for matching serial number */
+        if (serial) {
+            if (t->serial && !strcmp(serial, t->serial)) {
+                result = t;
+                break;
+            }
+        } else {
+            if (ttype == kTransportUsb && t->type == kTransportUsb) {
+                if (result) {
+                    if (error_out)
+                        *error_out = "more than one device";
+                    ambiguous = 1;
+                    result = NULL;
+                    break;
+                }
+                result = t;
+            } else if (ttype == kTransportLocal && t->type == kTransportLocal) {
+                if (result) {
+                    if (error_out)
+                        *error_out = "more than one emulator";
+                    ambiguous = 1;
+                    result = NULL;
+                    break;
+                }
+                result = t;
+            } else if (ttype == kTransportAny) {
+                if (result) {
+                    if (error_out)
+                        *error_out = "more than one device and emulator";
+                    ambiguous = 1;
+                    result = NULL;
+                    break;
+                }
+                result = t;
+            }
+        }
+    }
+    adb_mutex_unlock(&transport_lock);
+
+    if (result) {
+         /* offline devices are ignored -- they are either being born or dying */
+        if (result && result->connection_state == CS_OFFLINE) {
+            if (error_out)
+                *error_out = "device offline";
+            result = NULL;
+        }
+         /* check for required connection state */
+        if (result && state != CS_ANY && result->connection_state != state) {
+            if (error_out)
+                *error_out = "invalid device state";
+            result = NULL;
+        }
+    }
+
+    if (result) {
+        /* found one that we can take */
+        if (error_out)
+            *error_out = NULL;
+    } else if (state != CS_ANY && (serial || !ambiguous)) {
+        adb_sleep_ms(1000);
+        goto retry;
+    }
+
+    return result;
+}
+
+void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)
+{
+    atransport *t = calloc(1, sizeof(atransport));
+    D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
+      serial ? serial : "");
+    init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM));
+    if(serial) {
+        t->serial = strdup(serial);
+    }
+    register_transport(t);
+}
+
+/* this should only be used for transports with connection_state == CS_NOPERM */
+void unregister_usb_transport(usb_handle *usb)
+{
+    atransport *t;
+    adb_mutex_lock(&transport_lock);
+    for(t = transport_list.next; t != &transport_list; t = t->next) {
+        if (t->usb == usb && t->connection_state == CS_NOPERM) {
+            t->next->prev = t->prev;
+            t->prev->next = t->next;
+            break;
+        }
+     }
+    adb_mutex_unlock(&transport_lock);
+}
+
+#undef TRACE_TAG
+#define TRACE_TAG  TRACE_RWX
+
+int readx(int fd, void *ptr, size_t len)
+{
+    char *p = ptr;
+    int r;
+#if ADB_TRACE
+    size_t len0 = len;
+#endif
+    D("readx: fd=%d wanted=%d\n", fd, (int)len);
+    while(len > 0) {
+        r = adb_read(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p += r;
+        } else {
+            if (r < 0) {
+                D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+                if (errno == EINTR)
+                    continue;
+            } else {
+                D("readx: fd=%d disconnected\n", fd);
+            }
+            return -1;
+        }
+    }
+
+#if ADB_TRACE
+    D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len);
+    dump_hex( ptr, len0 );
+#endif
+    return 0;
+}
+
+int writex(int fd, const void *ptr, size_t len)
+{
+    char *p = (char*) ptr;
+    int r;
+
+#if ADB_TRACE
+    D("writex: fd=%d len=%d: ", fd, (int)len);
+    dump_hex( ptr, len );
+#endif
+    while(len > 0) {
+        r = adb_write(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p += r;
+        } else {
+            if (r < 0) {
+                D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+                if (errno == EINTR)
+                    continue;
+            } else {
+                D("writex: fd=%d disconnected\n", fd);
+            }
+            return -1;
+        }
+    }
+    return 0;
+}
+
+int check_header(apacket *p)
+{
+    if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
+        D("check_header(): invalid magic\n");
+        return -1;
+    }
+
+    if(p->msg.data_length > MAX_PAYLOAD) {
+        D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length);
+        return -1;
+    }
+
+    return 0;
+}
+
+int check_data(apacket *p)
+{
+    unsigned count, sum;
+    unsigned char *x;
+
+    count = p->msg.data_length;
+    x = p->data;
+    sum = 0;
+    while(count-- > 0) {
+        sum += *x++;
+    }
+
+    if(sum != p->msg.data_check) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
diff --git a/minadbd.old/transport.h b/minadbd.old/transport.h
new file mode 100644
index 0000000..992e052
--- /dev/null
+++ b/minadbd.old/transport.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#ifndef __TRANSPORT_H
+#define __TRANSPORT_H
+
+/* convenience wrappers around read/write that will retry on
+** EINTR and/or short read/write.  Returns 0 on success, -1
+** on error or EOF.
+*/
+int readx(int fd, void *ptr, size_t len);
+int writex(int fd, const void *ptr, size_t len);
+#endif   /* __TRANSPORT_H */
diff --git a/minadbd.old/transport_usb.c b/minadbd.old/transport_usb.c
new file mode 100644
index 0000000..91cbf61
--- /dev/null
+++ b/minadbd.old/transport_usb.c
@@ -0,0 +1,121 @@
+/*
+ * 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 <sysdeps.h>
+
+#define  TRACE_TAG  TRACE_TRANSPORT
+#include "adb.h"
+
+#ifdef HAVE_BIG_ENDIAN
+#define H4(x)	(((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
+static inline void fix_endians(apacket *p)
+{
+    p->msg.command     = H4(p->msg.command);
+    p->msg.arg0        = H4(p->msg.arg0);
+    p->msg.arg1        = H4(p->msg.arg1);
+    p->msg.data_length = H4(p->msg.data_length);
+    p->msg.data_check  = H4(p->msg.data_check);
+    p->msg.magic       = H4(p->msg.magic);
+}
+unsigned host_to_le32(unsigned n)
+{
+    return H4(n);
+}
+#else
+#define fix_endians(p) do {} while (0)
+unsigned host_to_le32(unsigned n)
+{
+    return n;
+}
+#endif
+
+static int remote_read(apacket *p, atransport *t)
+{
+    if(usb_read(t->usb, &p->msg, sizeof(amessage))){
+        D("remote usb: read terminated (message)\n");
+        return -1;
+    }
+
+    fix_endians(p);
+
+    if(check_header(p)) {
+        D("remote usb: check_header failed\n");
+        return -1;
+    }
+
+    if(p->msg.data_length) {
+        if(usb_read(t->usb, p->data, p->msg.data_length)){
+            D("remote usb: terminated (data)\n");
+            return -1;
+        }
+    }
+
+    if(check_data(p)) {
+        D("remote usb: check_data failed\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int remote_write(apacket *p, atransport *t)
+{
+    unsigned size = p->msg.data_length;
+
+    fix_endians(p);
+
+    if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
+        D("remote usb: 1 - write terminated\n");
+        return -1;
+    }
+    if(p->msg.data_length == 0) return 0;
+    if(usb_write(t->usb, &p->data, size)) {
+        D("remote usb: 2 - write terminated\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static void remote_close(atransport *t)
+{
+    usb_close(t->usb);
+    t->usb = 0;
+}
+
+static void remote_kick(atransport *t)
+{
+    usb_kick(t->usb);
+}
+
+void init_usb_transport(atransport *t, usb_handle *h, int state)
+{
+    D("transport: usb\n");
+    t->close = remote_close;
+    t->kick = remote_kick;
+    t->read_from_remote = remote_read;
+    t->write_to_remote = remote_write;
+    t->sync_token = 1;
+    t->connection_state = state;
+    t->type = kTransportUsb;
+    t->usb = h;
+
+    HOST = 0;
+}
diff --git a/minadbd.old/usb_linux_client.c b/minadbd.old/usb_linux_client.c
new file mode 100644
index 0000000..ec32bcf
--- /dev/null
+++ b/minadbd.old/usb_linux_client.c
@@ -0,0 +1,493 @@
+/*
+ * 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 <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define   TRACE_TAG  TRACE_USB
+#include "adb.h"
+
+#define MAX_PACKET_SIZE_FS	64
+#define MAX_PACKET_SIZE_HS	512
+
+#define cpu_to_le16(x)  htole16(x)
+#define cpu_to_le32(x)  htole32(x)
+
+struct usb_handle
+{
+    int fd;
+    adb_cond_t notify;
+    adb_mutex_t lock;
+
+    int (*write)(usb_handle *h, const void *data, int len);
+    int (*read)(usb_handle *h, void *data, int len);
+    void (*kick)(usb_handle *h);
+
+    int control;
+    int bulk_out; /* "out" from the host's perspective => source for adbd */
+    int bulk_in;  /* "in" from the host's perspective => sink for adbd */
+};
+
+static const struct {
+    struct usb_functionfs_descs_head header;
+    struct {
+        struct usb_interface_descriptor intf;
+        struct usb_endpoint_descriptor_no_audio source;
+        struct usb_endpoint_descriptor_no_audio sink;
+    } __attribute__((packed)) fs_descs, hs_descs;
+} __attribute__((packed)) descriptors = {
+    .header = {
+        .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+        .length = cpu_to_le32(sizeof(descriptors)),
+        .fs_count = 3,
+        .hs_count = 3,
+    },
+    .fs_descs = {
+        .intf = {
+            .bLength = sizeof(descriptors.fs_descs.intf),
+            .bDescriptorType = USB_DT_INTERFACE,
+            .bInterfaceNumber = 0,
+            .bNumEndpoints = 2,
+            .bInterfaceClass = ADB_CLASS,
+            .bInterfaceSubClass = ADB_SUBCLASS,
+            .bInterfaceProtocol = ADB_PROTOCOL,
+            .iInterface = 1, /* first string from the provided table */
+        },
+        .source = {
+            .bLength = sizeof(descriptors.fs_descs.source),
+            .bDescriptorType = USB_DT_ENDPOINT,
+            .bEndpointAddress = 1 | USB_DIR_OUT,
+            .bmAttributes = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+        },
+        .sink = {
+            .bLength = sizeof(descriptors.fs_descs.sink),
+            .bDescriptorType = USB_DT_ENDPOINT,
+            .bEndpointAddress = 2 | USB_DIR_IN,
+            .bmAttributes = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+        },
+    },
+    .hs_descs = {
+        .intf = {
+            .bLength = sizeof(descriptors.hs_descs.intf),
+            .bDescriptorType = USB_DT_INTERFACE,
+            .bInterfaceNumber = 0,
+            .bNumEndpoints = 2,
+            .bInterfaceClass = ADB_CLASS,
+            .bInterfaceSubClass = ADB_SUBCLASS,
+            .bInterfaceProtocol = ADB_PROTOCOL,
+            .iInterface = 1, /* first string from the provided table */
+        },
+        .source = {
+            .bLength = sizeof(descriptors.hs_descs.source),
+            .bDescriptorType = USB_DT_ENDPOINT,
+            .bEndpointAddress = 1 | USB_DIR_OUT,
+            .bmAttributes = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+        },
+        .sink = {
+            .bLength = sizeof(descriptors.hs_descs.sink),
+            .bDescriptorType = USB_DT_ENDPOINT,
+            .bEndpointAddress = 2 | USB_DIR_IN,
+            .bmAttributes = USB_ENDPOINT_XFER_BULK,
+            .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+        },
+    },
+};
+
+#define STR_INTERFACE_ "ADB Interface"
+
+static const struct {
+    struct usb_functionfs_strings_head header;
+    struct {
+        __le16 code;
+        const char str1[sizeof(STR_INTERFACE_)];
+    } __attribute__((packed)) lang0;
+} __attribute__((packed)) strings = {
+    .header = {
+        .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+        .length = cpu_to_le32(sizeof(strings)),
+        .str_count = cpu_to_le32(1),
+        .lang_count = cpu_to_le32(1),
+    },
+    .lang0 = {
+        cpu_to_le16(0x0409), /* en-us */
+        STR_INTERFACE_,
+    },
+};
+
+void usb_cleanup()
+{
+    // nothing to do here
+}
+
+static void *usb_adb_open_thread(void *x)
+{
+    struct usb_handle *usb = (struct usb_handle *)x;
+    int fd;
+
+    while (1) {
+        // wait until the USB device needs opening
+        adb_mutex_lock(&usb->lock);
+        while (usb->fd != -1)
+            adb_cond_wait(&usb->notify, &usb->lock);
+        adb_mutex_unlock(&usb->lock);
+
+        D("[ usb_thread - opening device ]\n");
+        do {
+            /* XXX use inotify? */
+            fd = unix_open("/dev/android_adb", O_RDWR);
+            if (fd < 0) {
+                // to support older kernels
+                fd = unix_open("/dev/android", O_RDWR);
+                fprintf(stderr, "usb_adb_open_thread: %d\n", fd );
+            }
+            if (fd < 0) {
+                adb_sleep_ms(1000);
+            }
+        } while (fd < 0);
+        D("[ opening device succeeded ]\n");
+
+        close_on_exec(fd);
+        usb->fd = fd;
+
+        D("[ usb_thread - registering device ]\n");
+        register_usb_transport(usb, 0, 1);
+    }
+
+    // never gets here
+    return 0;
+}
+
+static int usb_adb_write(usb_handle *h, const void *data, int len)
+{
+    int n;
+
+    D("about to write (fd=%d, len=%d)\n", h->fd, len);
+    n = adb_write(h->fd, data, len);
+    if(n != len) {
+        D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+            h->fd, n, errno, strerror(errno));
+        return -1;
+    }
+    D("[ done fd=%d ]\n", h->fd);
+    return 0;
+}
+
+static int usb_adb_read(usb_handle *h, void *data, int len)
+{
+    int n;
+
+    D("about to read (fd=%d, len=%d)\n", h->fd, len);
+    n = adb_read(h->fd, data, len);
+    if(n != len) {
+        D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+            h->fd, n, errno, strerror(errno));
+        return -1;
+    }
+    D("[ done fd=%d ]\n", h->fd);
+    return 0;
+}
+
+static void usb_adb_kick(usb_handle *h)
+{
+    D("usb_kick\n");
+    adb_mutex_lock(&h->lock);
+    adb_close(h->fd);
+    h->fd = -1;
+
+    // notify usb_adb_open_thread that we are disconnected
+    adb_cond_signal(&h->notify);
+    adb_mutex_unlock(&h->lock);
+}
+
+static void usb_adb_init()
+{
+    usb_handle *h;
+    adb_thread_t tid;
+    int fd;
+
+    h = calloc(1, sizeof(usb_handle));
+
+    h->write = usb_adb_write;
+    h->read = usb_adb_read;
+    h->kick = usb_adb_kick;
+    h->fd = -1;
+
+    adb_cond_init(&h->notify, 0);
+    adb_mutex_init(&h->lock, 0);
+
+    fprintf(stderr, "Starting to open usb_init()\n");
+    // Open the file /dev/android_adb_enable to trigger 
+    // the enabling of the adb USB function in the kernel.
+    // We never touch this file again - just leave it open
+    // indefinitely so the kernel will know when we are running
+    // and when we are not.
+    fd = unix_open("/dev/android_adb_enable", O_RDWR);
+    fprintf(stderr, "unix_open to open usb_init(): %d\n", fd);
+    if (fd < 0) {
+       D("failed to open /dev/android_adb_enable\n");
+    } else {
+        close_on_exec(fd);
+    }
+
+    printf("[ usb_init - starting thread ]\n");
+    if(adb_thread_create(&tid, usb_adb_open_thread, h)){
+        fatal_errno("cannot create usb thread");
+        fprintf(stderr, "cannot create the usb thread()\n");
+    }
+}
+
+
+static void init_functionfs(struct usb_handle *h)
+{
+    ssize_t ret;
+
+    D("OPENING %s\n", USB_FFS_ADB_EP0);
+    h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
+    if (h->control < 0) {
+        D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno);
+        goto err;
+    }
+
+    ret = adb_write(h->control, &descriptors, sizeof(descriptors));
+    if (ret < 0) {
+        D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno);
+        goto err;
+    }
+
+    ret = adb_write(h->control, &strings, sizeof(strings));
+    if (ret < 0) {
+        D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno);
+        goto err;
+    }
+
+    h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
+    if (h->bulk_out < 0) {
+        D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno);
+        goto err;
+    }
+
+    h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
+    if (h->bulk_in < 0) {
+        D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno);
+        goto err;
+    }
+
+    return;
+
+err:
+    if (h->bulk_in > 0) {
+        adb_close(h->bulk_in);
+        h->bulk_in = -1;
+    }
+    if (h->bulk_out > 0) {
+        adb_close(h->bulk_out);
+        h->bulk_out = -1;
+    }
+    if (h->control > 0) {
+        adb_close(h->control);
+        h->control = -1;
+    }
+    return;
+}
+
+static void *usb_ffs_open_thread(void *x)
+{
+    struct usb_handle *usb = (struct usb_handle *)x;
+
+    while (1) {
+        // wait until the USB device needs opening
+        adb_mutex_lock(&usb->lock);
+        while (usb->control != -1)
+            adb_cond_wait(&usb->notify, &usb->lock);
+        adb_mutex_unlock(&usb->lock);
+
+        while (1) {
+            init_functionfs(usb);
+
+            if (usb->control >= 0)
+                break;
+
+            adb_sleep_ms(1000);
+        }
+
+        D("[ usb_thread - registering device ]\n");
+        register_usb_transport(usb, 0, 1);
+    }
+
+    // never gets here
+    return 0;
+}
+
+static int bulk_write(int bulk_in, const char *buf, size_t length)
+{
+    size_t count = 0;
+    int ret;
+
+    do {
+        ret = adb_write(bulk_in, buf + count, length - count);
+        if (ret < 0) {
+            if (errno != EINTR)
+                return ret;
+        } else {
+            count += ret;
+        }
+    } while (count < length);
+
+    D("[ bulk_write done fd=%d ]\n", bulk_in);
+    return count;
+}
+
+static int usb_ffs_write(usb_handle *h, const void *data, int len)
+{
+    int n;
+
+    D("about to write (fd=%d, len=%d)\n", h->bulk_in, len);
+    n = bulk_write(h->bulk_in, data, len);
+    if (n != len) {
+        D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+            h->bulk_in, n, errno, strerror(errno));
+        return -1;
+    }
+    D("[ done fd=%d ]\n", h->bulk_in);
+    return 0;
+}
+
+static int bulk_read(int bulk_out, char *buf, size_t length)
+{
+    size_t count = 0;
+    int ret;
+
+    do {
+        ret = adb_read(bulk_out, buf + count, length - count);
+        if (ret < 0) {
+            if (errno != EINTR) {
+                D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n",
+                                           bulk_out, length, count);
+                return ret;
+            }
+        } else {
+            count += ret;
+        }
+    } while (count < length);
+
+    return count;
+}
+
+static int usb_ffs_read(usb_handle *h, void *data, int len)
+{
+    int n;
+
+    D("about to read (fd=%d, len=%d)\n", h->bulk_out, len);
+    n = bulk_read(h->bulk_out, data, len);
+    if (n != len) {
+        D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+            h->bulk_out, n, errno, strerror(errno));
+        return -1;
+    }
+    D("[ done fd=%d ]\n", h->bulk_out);
+    return 0;
+}
+
+static void usb_ffs_kick(usb_handle *h)
+{
+    int err;
+
+    err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
+    if (err < 0)
+        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno);
+
+    err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
+    if (err < 0)
+        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
+
+    adb_mutex_lock(&h->lock);
+    adb_close(h->control);
+    adb_close(h->bulk_out);
+    adb_close(h->bulk_in);
+    h->control = h->bulk_out = h->bulk_in = -1;
+
+    // notify usb_ffs_open_thread that we are disconnected
+    adb_cond_signal(&h->notify);
+    adb_mutex_unlock(&h->lock);
+}
+
+static void usb_ffs_init()
+{
+    usb_handle *h;
+    adb_thread_t tid;
+
+    D("[ usb_init - using FunctionFS ]\n");
+
+    h = calloc(1, sizeof(usb_handle));
+
+    h->write = usb_ffs_write;
+    h->read = usb_ffs_read;
+    h->kick = usb_ffs_kick;
+
+    h->control  = -1;
+    h->bulk_out = -1;
+    h->bulk_out = -1;
+
+    adb_cond_init(&h->notify, 0);
+    adb_mutex_init(&h->lock, 0);
+
+    D("[ usb_init - starting thread ]\n");
+    if (adb_thread_create(&tid, usb_ffs_open_thread, h)){
+        fatal_errno("[ cannot create usb thread ]\n");
+    }
+}
+
+void usb_init()
+{
+    if (access(USB_FFS_ADB_EP0, F_OK) == 0)
+        usb_ffs_init();
+    else
+        usb_adb_init();
+}
+
+int usb_write(usb_handle *h, const void *data, int len)
+{
+    return h->write(h, data, len);
+}
+
+int usb_read(usb_handle *h, void *data, int len)
+{
+    return h->read(h, data, len);
+}
+int usb_close(usb_handle *h)
+{
+    // nothing to do here
+    return 0;
+}
+
+void usb_kick(usb_handle *h)
+{
+    h->kick(h);
+}
diff --git a/minadbd.old/utils.c b/minadbd.old/utils.c
new file mode 100644
index 0000000..91518ba
--- /dev/null
+++ b/minadbd.old/utils.c
@@ -0,0 +1,106 @@
+/*
+ * 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 "utils.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+char*
+buff_addc (char*  buff, char*  buffEnd, int  c)
+{
+    int  avail = buffEnd - buff;
+
+    if (avail <= 0)  /* already in overflow mode */
+        return buff;
+
+    if (avail == 1) {  /* overflowing, the last byte is reserved for zero */
+        buff[0] = 0;
+        return buff + 1;
+    }
+
+    buff[0] = (char) c;  /* add char and terminating zero */
+    buff[1] = 0;
+    return buff + 1;
+}
+
+char*
+buff_adds (char*  buff, char*  buffEnd, const char*  s)
+{
+    int  slen = strlen(s);
+
+    return buff_addb(buff, buffEnd, s, slen);
+}
+
+char*
+buff_addb (char*  buff, char*  buffEnd, const void*  data, int  len)
+{
+    int  avail = (buffEnd - buff);
+
+    if (avail <= 0 || len <= 0)  /* already overflowing */
+        return buff;
+
+    if (len > avail)
+        len = avail;
+
+    memcpy(buff, data, len);
+
+    buff += len;
+
+    /* ensure there is a terminating zero */
+    if (buff >= buffEnd) {  /* overflow */
+        buff[-1] = 0;
+    } else
+        buff[0] = 0;
+
+    return buff;
+}
+
+char*
+buff_add  (char*  buff, char*  buffEnd, const char*  format, ... )
+{
+    int      avail;
+
+    avail = (buffEnd - buff);
+
+    if (avail > 0) {
+        va_list  args;
+        int      nn;
+
+        va_start(args, format);
+        nn = vsnprintf( buff, avail, format, args);
+        va_end(args);
+
+        if (nn < 0) {
+            /* some C libraries return -1 in case of overflow,
+             * but they will also do that if the format spec is
+             * invalid. We assume ADB is not buggy enough to
+             * trigger that last case. */
+            nn = avail;
+        }
+        else if (nn > avail) {
+            nn = avail;
+        }
+
+        buff += nn;
+
+        /* ensure that there is a terminating zero */
+        if (buff >= buffEnd)
+            buff[-1] = 0;
+        else
+            buff[0] = 0;
+    }
+    return buff;
+}
diff --git a/minadbd.old/utils.h b/minadbd.old/utils.h
new file mode 100644
index 0000000..f70ecd2
--- /dev/null
+++ b/minadbd.old/utils.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+#ifndef _ADB_UTILS_H
+#define _ADB_UTILS_H
+
+/* bounded buffer functions */
+
+/* all these functions are used to append data to a bounded buffer.
+ *
+ * after each operation, the buffer is guaranteed to be zero-terminated,
+ * even in the case of an overflow. they all return the new buffer position
+ * which allows one to use them in succession, only checking for overflows
+ * at the end. For example:
+ *
+ *    BUFF_DECL(temp,p,end,1024);
+ *    char*    p;
+ *
+ *    p = buff_addc(temp, end, '"');
+ *    p = buff_adds(temp, end, string);
+ *    p = buff_addc(temp, end, '"');
+ *
+ *    if (p >= end) {
+ *        overflow detected. note that 'temp' is
+ *        zero-terminated for safety. 
+ *    }
+ *    return strdup(temp);
+ */
+
+/* tries to add a character to the buffer, in case of overflow
+ * this will only write a terminating zero and return buffEnd.
+ */
+char*   buff_addc (char*  buff, char*  buffEnd, int  c);
+
+/* tries to add a string to the buffer */
+char*   buff_adds (char*  buff, char*  buffEnd, const char*  s);
+
+/* tries to add a bytes to the buffer. the input can contain zero bytes,
+ * but a terminating zero will always be appended at the end anyway
+ */
+char*   buff_addb (char*  buff, char*  buffEnd, const void*  data, int  len);
+
+/* tries to add a formatted string to a bounded buffer */
+char*   buff_add  (char*  buff, char*  buffEnd, const char*  format, ... );
+
+/* convenience macro used to define a bounded buffer, as well as
+ * a 'cursor' and 'end' variables all in one go.
+ *
+ * note: this doesn't place an initial terminating zero in the buffer,
+ * you need to use one of the buff_ functions for this. or simply
+ * do _cursor[0] = 0 manually.
+ */
+#define  BUFF_DECL(_buff,_cursor,_end,_size)   \
+    char   _buff[_size], *_cursor=_buff, *_end = _cursor + (_size)
+
+#endif /* _ADB_UTILS_H */
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index a7a3e08..36e14a5 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -14,15 +14,16 @@
     adb_main.cpp \
     fuse_adb_provider.cpp \
     services.cpp \
+    ../fuse_sideload.c
 
 LOCAL_MODULE := libminadbd
 LOCAL_CFLAGS := $(minadbd_cflags)
 LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration
-LOCAL_C_INCLUDES := bootable/recovery system/core/adb
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. system/core/adb
 LOCAL_WHOLE_STATIC_LIBRARIES := libadbd
-LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_SHARED_LIBRARIES := libbase liblog libmincrypttwrp libcutils libc
 
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
 
diff --git a/minui.old/Android.mk b/minui.old/Android.mk
new file mode 100644
index 0000000..7d8e3a7
--- /dev/null
+++ b/minui.old/Android.mk
@@ -0,0 +1,140 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := 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 \
+    system/core/include/pixelflinger
+
+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
+    ifeq ($(TARGET_CUSTOM_KERNEL_HEADERS),)
+      LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include
+    else
+      LOCAL_C_INCLUDES += $(TARGET_CUSTOM_KERNEL_HEADERS)
+    endif
+  endif
+else
+  LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include
+endif
+
+ifeq ($(TW_NEW_ION_HEAP), true)
+  LOCAL_CFLAGS += -DNEW_ION_HEAP
+endif
+
+LOCAL_STATIC_LIBRARY := libpng
+LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_static
+LOCAL_MODULE := libminui
+
+# 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
+ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),)
+  LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=$(BOARD_USE_CUSTOM_RECOVERY_FONT)
+endif
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := 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
+    ifeq ($(TARGET_CUSTOM_KERNEL_HEADERS),)
+      LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include
+    else
+      LOCAL_C_INCLUDES += $(TARGET_CUSTOM_KERNEL_HEADERS)
+    endif
+  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
+# 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
+ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),)
+  LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=$(BOARD_USE_CUSTOM_RECOVERY_FONT)
+endif
+
+LOCAL_CFLAGS += -DFASTMMI_FEATURE
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/minui.old/events.c b/minui.old/events.c
new file mode 100644
index 0000000..b8cf15a
--- /dev/null
+++ b/minui.old/events.c
@@ -0,0 +1,219 @@
+/*
+ * 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/epoll.h>
+
+#include <linux/input.h>
+
+#include "minui.h"
+
+#define MAX_DEVICES 16
+#define MAX_MISC_FDS 16
+
+#define BITS_PER_LONG (sizeof(unsigned long) * 8)
+#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
+
+#define test_bit(bit, array) \
+    ((array)[(bit)/BITS_PER_LONG] & (1 << ((bit) % BITS_PER_LONG)))
+
+struct fd_info {
+    int fd;
+    ev_callback cb;
+    void *data;
+};
+
+static int epollfd;
+static struct epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS];
+static int npolledevents;
+
+static struct fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
+
+static unsigned ev_count = 0;
+static unsigned ev_dev_count = 0;
+static unsigned ev_misc_count = 0;
+
+int ev_init(ev_callback input_cb, void *data)
+{
+    DIR *dir;
+    struct dirent *de;
+    int fd;
+    struct epoll_event ev;
+    bool epollctlfail = false;
+
+    epollfd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
+    if (epollfd == -1)
+        return -1;
+
+    dir = opendir("/dev/input");
+    if(dir != 0) {
+        while((de = readdir(dir))) {
+            unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
+
+//            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;
+
+            /* read the evbits of the input device */
+            if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0) {
+                close(fd);
+                continue;
+            }
+
+            /* TODO: add ability to specify event masks. For now, just assume
+             * that only EV_KEY and EV_REL event types are ever needed. */
+            if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits)) {
+                close(fd);
+                continue;
+            }
+
+#ifdef EPOLLWAKEUP
+            ev.events = EPOLLIN | EPOLLWAKEUP;
+#else
+            ev.events = EPOLLIN;
+#endif
+            ev.data.ptr = (void *)&ev_fdinfo[ev_count];
+            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) {
+                close(fd);
+                epollctlfail = true;
+                continue;
+            }
+
+            ev_fdinfo[ev_count].fd = fd;
+            ev_fdinfo[ev_count].cb = input_cb;
+            ev_fdinfo[ev_count].data = data;
+            ev_count++;
+            ev_dev_count++;
+            if(ev_dev_count == MAX_DEVICES) break;
+        }
+    }
+
+    if (epollctlfail && !ev_count) {
+        close(epollfd);
+        epollfd = -1;
+        return -1;
+    }
+
+    return 0;
+}
+
+int ev_add_fd(int fd, ev_callback cb, void *data)
+{
+    struct epoll_event ev;
+    int ret;
+
+    if (ev_misc_count == MAX_MISC_FDS || cb == NULL)
+        return -1;
+
+#ifdef EPOLLWAKEUP
+    ev.events = EPOLLIN | EPOLLWAKEUP;
+#else
+    ev.events = EPOLLIN;
+#endif
+    ev.data.ptr = (void *)&ev_fdinfo[ev_count];
+    ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
+    if (!ret) {
+        ev_fdinfo[ev_count].fd = fd;
+        ev_fdinfo[ev_count].cb = cb;
+        ev_fdinfo[ev_count].data = data;
+        ev_count++;
+        ev_misc_count++;
+    }
+
+    return ret;
+}
+
+int ev_get_epollfd(void)
+{
+    return epollfd;
+}
+
+void ev_exit(void)
+{
+    while (ev_count > 0) {
+        close(ev_fdinfo[--ev_count].fd);
+    }
+    ev_misc_count = 0;
+    ev_dev_count = 0;
+    close(epollfd);
+}
+
+int ev_wait(int timeout)
+{
+    npolledevents = epoll_wait(epollfd, polledevents, ev_count, timeout);
+    if (npolledevents <= 0)
+        return -1;
+    return 0;
+}
+
+void ev_dispatch(void)
+{
+    int n;
+    int ret;
+
+    for (n = 0; n < npolledevents; n++) {
+        struct fd_info *fdi = polledevents[n].data.ptr;
+        ev_callback cb = fdi->cb;
+        if (cb)
+            cb(fdi->fd, polledevents[n].events, fdi->data);
+    }
+}
+
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev)
+{
+    int r;
+
+    if (epevents & EPOLLIN) {
+        r = read(fd, ev, sizeof(*ev));
+        if (r == sizeof(*ev))
+            return 0;
+    }
+    return -1;
+}
+
+int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data)
+{
+    unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];
+    unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
+    unsigned i;
+    int ret;
+
+    for (i = 0; i < ev_dev_count; i++) {
+        int code;
+
+        memset(key_bits, 0, sizeof(key_bits));
+        memset(ev_bits, 0, sizeof(ev_bits));
+
+        ret = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
+        if (ret < 0 || !test_bit(EV_KEY, ev_bits))
+            continue;
+
+        ret = ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits);
+        if (ret < 0)
+            continue;
+
+        for (code = 0; code <= KEY_MAX; code++) {
+            if (test_bit(code, key_bits))
+                set_key_cb(code, 1, data);
+        }
+    }
+
+    return 0;
+}
diff --git a/minui.old/font_10x18.h b/minui.old/font_10x18.h
new file mode 100644
index 0000000..7f96465
--- /dev/null
+++ b/minui.old/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/minui.old/font_7x16.h b/minui.old/font_7x16.h
new file mode 100644
index 0000000..0f72b53
--- /dev/null
+++ b/minui.old/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/minui.old/graphics.c b/minui.old/graphics.c
new file mode 100644
index 0000000..cce02a9
--- /dev/null
+++ b/minui.old/graphics.c
@@ -0,0 +1,510 @@
+/*
+ * 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>
+
+#ifdef BOARD_USE_CUSTOM_RECOVERY_FONT
+#include BOARD_USE_CUSTOM_RECOVERY_FONT
+#else
+#include "font_10x18.h"
+#endif
+
+#include "minui.h"
+
+#if defined(RECOVERY_BGRA)
+#define PIXEL_FORMAT GGL_PIXEL_FORMAT_BGRA_8888
+#define PIXEL_SIZE   4
+#elif defined(RECOVERY_RGBX)
+#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888
+#define PIXEL_SIZE   4
+#else
+#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565
+#define PIXEL_SIZE   2
+#endif
+
+#define NUM_BUFFERS 2
+
+typedef struct {
+    GGLSurface* texture;
+    unsigned cwidth;
+    unsigned cheight;
+} GRFont;
+
+static GRFont *gr_font = 0;
+static GGLContext *gr_context = 0;
+static GGLSurface gr_font_texture;
+static GGLSurface gr_framebuffer[NUM_BUFFERS];
+static GGLSurface gr_mem_surface;
+static unsigned gr_active_fb = 0;
+static unsigned double_buffering = 0;
+static int overscan_percent = OVERSCAN_PERCENT;
+static int overscan_offset_x = 0;
+static int overscan_offset_y = 0;
+
+static int gr_fb_fd = -1;
+static int gr_vt_fd = -1;
+
+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;
+
+    double_buffering = 1;
+
+    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 long) 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);
+}
+
+int gr_measure(const char *s)
+{
+    return gr_font->cwidth * strlen(s);
+}
+
+void gr_font_size(int *x, int *y)
+{
+    *x = gr_font->cwidth;
+    *y = gr_font->cheight;
+}
+
+int gr_text(int x, int y, const char *s, ...)
+{
+    return gr_text_impl(x, y, s, 0);
+}
+
+int gr_text_impl(int x, int y, const char *s, int bold)
+{
+    GGLContext *gl = gr_context;
+    GRFont *font = gr_font;
+    unsigned off;
+
+    if (!font->texture) return x;
+
+    bold = bold && (font->texture->height != font->cheight);
+
+    x += overscan_offset_x;
+    y += overscan_offset_y;
+
+    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;
+        if (off < 96) {
+            gl->texCoord2i(gl, (off * font->cwidth) - x,
+                           (bold ? font->cheight : 0) - y);
+            gl->recti(gl, x, y, x + font->cwidth, y + font->cheight);
+        }
+        x += font->cwidth;
+    }
+
+    return x;
+}
+
+void gr_texticon(int x, int y, gr_surface icon) {
+    if (gr_context == NULL || icon == NULL) {
+        return;
+    }
+    GGLContext* gl = gr_context;
+
+    x += overscan_offset_x;
+    y += overscan_offset_y;
+
+    gl->bindTexture(gl, (GGLSurface*) icon);
+    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);
+
+    int w = gr_get_width(icon);
+    int h = gr_get_height(icon);
+
+    gl->texCoord2i(gl, -x, -y);
+    gl->recti(gl, x, y, x+gr_get_width(icon), y+gr_get_height(icon));
+}
+
+void gr_fill(int x1, int y1, int x2, int y2)
+{
+    x1 += overscan_offset_x;
+    y1 += overscan_offset_y;
+
+    x2 += overscan_offset_x;
+    y2 += overscan_offset_y;
+
+    GGLContext *gl = gr_context;
+    gl->disable(gl, GGL_TEXTURE_2D);
+    gl->recti(gl, x1, y1, x2, y2);
+}
+
+void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
+    if (gr_context == NULL || source == NULL) {
+        return;
+    }
+    GGLContext *gl = gr_context;
+
+    dx += overscan_offset_x;
+    dy += overscan_offset_y;
+
+    gl->bindTexture(gl, (GGLSurface*) source);
+    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);
+}
+
+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;
+}
+
+static void gr_init_font(void)
+{
+    gr_font = calloc(sizeof(*gr_font), 1);
+
+    int res = res_create_surface("font", (void**)&(gr_font->texture));
+    if (res == 0) {
+        // The font image should be a 96x2 array of character images.  The
+        // columns are the printable ASCII characters 0x20 - 0x7f.  The
+        // top row is regular text; the bottom row is bold.
+        gr_font->cwidth = gr_font->texture->width / 96;
+        gr_font->cheight = gr_font->texture->height / 2;
+    } else {
+        printf("failed to read font: res=%d\n", res);
+
+        // fall back to the compiled-in font.
+        gr_font->texture = malloc(sizeof(*gr_font->texture));
+        gr_font->texture->width = font.width;
+        gr_font->texture->height = font.height;
+        gr_font->texture->stride = font.width;
+
+        unsigned char* bits = malloc(font.width * font.height);
+        gr_font->texture->data = (void*) bits;
+
+        unsigned char data;
+        unsigned char* in = font.rundata;
+        while((data = *in++)) {
+            memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
+            bits += (data & 0x7f);
+        }
+
+        gr_font->cwidth = font.cwidth;
+        gr_font->cheight = font.cheight;
+    }
+
+    // interpret the grayscale as alpha
+    gr_font->texture->format = GGL_PIXEL_FORMAT_A_8;
+}
+
+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.
+        perror("can't open /dev/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) {
+        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);
+
+    gr_fb_blank(true);
+    gr_fb_blank(false);
+
+    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 - 2*overscan_offset_x;
+}
+
+int gr_fb_height(void)
+{
+    return gr_framebuffer[0].height - 2*overscan_offset_y;
+}
+
+gr_pixel *gr_fb_data(void)
+{
+    return (unsigned short *) gr_mem_surface.data;
+}
+
+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
+    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);
+#endif
+}
+
+void gr_get_memory_surface(gr_surface surface)
+{
+    get_memory_surface( (GGLSurface*) surface);
+}
+
+// These are new graphics functions from 5.0 that were not available in
+// 4.4 that are required by charger and healthd
+void gr_clear()
+{
+	return;
+}
+
diff --git a/minui.old/graphics_overlay.c b/minui.old/graphics_overlay.c
new file mode 100644
index 0000000..d793b57
--- /dev/null
+++ b/minui.old/graphics_overlay.c
@@ -0,0 +1,316 @@
+/*
+ * 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);
+#ifdef NEW_ION_HEAP
+    ionAllocData.heap_id_mask =
+#else
+    ionAllocData.heap_mask =
+#endif
+            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.old/include/linux/msm_ion.h b/minui.old/include/linux/msm_ion.h
new file mode 100644
index 0000000..121a5a7
--- /dev/null
+++ b/minui.old/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.old/include/linux/msm_mdp.h b/minui.old/include/linux/msm_mdp.h
new file mode 100644
index 0000000..abdcd29
--- /dev/null
+++ b/minui.old/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.old/minui.h b/minui.old/minui.h
new file mode 100644
index 0000000..4c629c1
--- /dev/null
+++ b/minui.old/minui.h
@@ -0,0 +1,105 @@
+/*
+ * 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_
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* gr_surface;
+typedef unsigned short gr_pixel;
+
+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);
+void gr_fb_blank(bool blank);
+
+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);
+
+// 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);
+unsigned int gr_get_height(gr_surface surface);
+
+// input event structure, include <linux/input.h> for the definition.
+// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
+struct input_event;
+
+typedef int (*ev_callback)(int fd, uint32_t epevents, void *data);
+typedef int (*ev_set_key_callback)(int code, int value, void *data);
+
+int ev_init(ev_callback input_cb, void *data);
+void ev_exit(void);
+int ev_add_fd(int fd, ev_callback cb, void *data);
+int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data);
+
+/* timeout has the same semantics as for poll
+ *    0 : don't block
+ *  < 0 : block forever
+ *  > 0 : block for 'timeout' milliseconds
+ */
+int ev_wait(int timeout);
+
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev);
+void ev_dispatch(void);
+int ev_get_epollfd(void);
+
+// Resources
+
+// Returns 0 if no error, else negative.
+int res_create_surface(const char* name, gr_surface* pSurface);
+
+// Load an array of display surfaces from a single PNG image.  The PNG
+// should have a 'Frames' text chunk whose value is the number of
+// frames this image represents.  The pixel data itself is interlaced
+// by row.
+int res_create_multi_display_surface(const char* name,
+                                     int* frames, gr_surface** pSurface);
+
+int res_create_localized_surface(const char* name, gr_surface* pSurface);
+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);
+}
+
+// These are new graphics functions from 5.0 that were not available in
+// 4.4 that are required by charger and healthd
+void gr_clear();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/minui.old/mkfont.c b/minui.old/mkfont.c
new file mode 100644
index 0000000..61a5ede
--- /dev/null
+++ b/minui.old/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/minui.old/resources.c b/minui.old/resources.c
new file mode 100644
index 0000000..ed25e45
--- /dev/null
+++ b/minui.old/resources.c
@@ -0,0 +1,479 @@
+/*
+ * 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 "minui.h"
+
+#ifdef FASTMMI_FEATURE
+char *locale = NULL;
+#else
+extern char* locale;
+#endif
+
+// 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 * y;
+}
+
+#define SURFACE_DATA_ALIGNMENT 8
+
+static GGLSurface* malloc_surface(size_t data_size) {
+    unsigned char* temp = malloc(sizeof(GGLSurface) + data_size + SURFACE_DATA_ALIGNMENT);
+    if (temp == NULL) return NULL;
+    GGLSurface* surface = (GGLSurface*) temp;
+    surface->data = temp + sizeof(GGLSurface) +
+        (SURFACE_DATA_ALIGNMENT - (sizeof(GGLSurface) % SURFACE_DATA_ALIGNMENT));
+    return surface;
+}
+
+static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
+                    png_uint_32* width, png_uint_32* height, png_byte* channels) {
+    char resPath[256];
+    unsigned char header[8];
+    int result = 0;
+
+    snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
+    resPath[sizeof(resPath)-1] = '\0';
+    FILE* 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_init_io(*png_ptr, fp);
+    png_set_sig_bytes(*png_ptr, sizeof(header));
+    png_read_info(*png_ptr, *info_ptr);
+
+    int color_type, bit_depth;
+    png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth,
+            &color_type, NULL, NULL, NULL);
+
+    *channels = png_get_channels(*png_ptr, *info_ptr);
+
+    if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) {
+        // 8-bit RGB images: great, nothing to do.
+    } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
+        // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
+        png_set_expand_gray_1_2_4_to_8(*png_ptr);
+    } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
+        // paletted images: expand to 8-bit RGB.  Note that we DON'T
+        // currently expand the tRNS chunk (if any) to an alpha
+        // channel, because minui doesn't support alpha channels in
+        // general.
+        png_set_palette_to_rgb(*png_ptr);
+        *channels = 3;
+    } else {
+        fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n",
+                bit_depth, *channels, color_type);
+        result = -7;
+        goto exit;
+    }
+
+    return result;
+
+  exit:
+    if (result < 0) {
+        png_destroy_read_struct(png_ptr, info_ptr, NULL);
+    }
+    if (fp != NULL) {
+        fclose(fp);
+    }
+
+    return result;
+}
+
+// "display" surfaces are transformed into the framebuffer's required
+// pixel format (currently only RGBX is supported) at load time, so
+// gr_blit() can be nothing more than a memcpy() for each row.  The
+// next two functions are the only ones that know anything about the
+// framebuffer pixel format; they need to be modified if the
+// framebuffer format changes (but nothing else should).
+
+// Allocate and return a gr_surface sufficient for storing an image of
+// the indicated size in the framebuffer pixel format.
+static GGLSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
+    GGLSurface* surface;
+
+    surface = (GGLSurface*) malloc_surface(width * height * 4);
+    if (surface == NULL) return NULL;
+
+    surface->version = sizeof(GGLSurface);
+    surface->width = width;
+    surface->height = height;
+    surface->stride = width;
+
+    return surface;
+}
+
+// Copy 'input_row' to 'output_row', transforming it to the
+// framebuffer pixel format.  The input format depends on the value of
+// 'channels':
+//
+//   1 - input is 8-bit grayscale
+//   3 - input is 24-bit RGB
+//   4 - input is 32-bit RGBA/RGBX
+//
+// 'width' is the number of pixels in the row.
+static void transform_rgb_to_draw(unsigned char* input_row,
+                                  unsigned char* output_row,
+                                  int channels, int width) {
+    int x;
+    unsigned char* ip = input_row;
+    unsigned char* op = output_row;
+
+    switch (channels) {
+        case 1:
+            // expand gray level to RGBX
+            for (x = 0; x < width; ++x) {
+                *op++ = *ip;
+                *op++ = *ip;
+                *op++ = *ip;
+                *op++ = 0xff;
+                ip++;
+            }
+            break;
+
+        case 3:
+            // expand RGBA to RGBX
+            for (x = 0; x < width; ++x) {
+                *op++ = *ip++;
+                *op++ = *ip++;
+                *op++ = *ip++;
+                *op++ = 0xff;
+            }
+            break;
+
+        case 4:
+            // copy RGBA to RGBX
+            memcpy(output_row, input_row, width*4);
+            break;
+    }
+}
+
+int res_create_surface(const char* name, gr_surface* pSurface) {
+    GGLSurface* surface = NULL;
+    int result = 0;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+    png_uint_32 width, height;
+    png_byte channels;
+
+    *pSurface = NULL;
+
+    result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
+    if (result < 0) return result;
+
+    surface = init_display_surface(width, height);
+    if (surface == NULL) {
+        result = -8;
+        goto exit;
+    }
+
+    unsigned char* p_row = malloc(width * 4);
+    unsigned int y;
+    for (y = 0; y < height; ++y) {
+        png_read_row(png_ptr, p_row, NULL);
+        transform_rgb_to_draw(p_row, surface->data + y * width * 4, channels, width);
+    }
+    free(p_row);
+
+    if (channels == 3)
+        surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
+    else
+        surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
+
+    *pSurface = (gr_surface) surface;
+
+  exit:
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    if (result < 0 && surface != NULL) free(surface);
+    return result;
+}
+
+int res_create_multi_display_surface(const char* name, int* frames, gr_surface** pSurface) {
+    gr_surface* surface = NULL;
+    int result = 0;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+    png_uint_32 width, height;
+    png_byte channels;
+    int i;
+
+    *pSurface = NULL;
+    *frames = -1;
+
+    result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
+    if (result < 0) return result;
+
+    *frames = 1;
+    png_textp text;
+    int num_text;
+    if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
+        for (i = 0; i < num_text; ++i) {
+            if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) {
+                *frames = atoi(text[i].text);
+                break;
+            }
+        }
+        printf("  found frames = %d\n", *frames);
+    }
+
+    if (height % *frames != 0) {
+        printf("bad height (%d) for frame count (%d)\n", height, *frames);
+        result = -9;
+        goto exit;
+    }
+
+    surface = malloc(*frames * sizeof(GGLSurface));
+    if (surface == NULL) {
+        result = -8;
+        goto exit;
+    }
+    for (i = 0; i < *frames; ++i) {
+        surface[i] = init_display_surface(width, height / *frames);
+        if (surface[i] == NULL) {
+            result = -8;
+            goto exit;
+        }
+    }
+
+    unsigned char* p_row = malloc(width * 4);
+    unsigned int y;
+    for (y = 0; y < height; ++y) {
+        png_read_row(png_ptr, p_row, NULL);
+        int frame = y % *frames;
+        GGLSurface* p = (GGLSurface*) surface[frame];
+        unsigned char* out_row = p->data +
+            (y / *frames) * width * 4;
+        transform_rgb_to_draw(p_row, out_row, channels, width);
+    }
+    free(p_row);
+
+    for (i = 0; i < *frames; ++i) {
+        GGLSurface* p = (GGLSurface*) surface[i];
+        if (channels == 3)
+            p->format = GGL_PIXEL_FORMAT_RGBX_8888;
+        else
+            p->format = GGL_PIXEL_FORMAT_RGBA_8888;
+    }
+
+    *pSurface = (gr_surface*) surface;
+
+exit:
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+    if (result < 0) {
+        if (surface) {
+            for (i = 0; i < *frames; ++i) {
+                if (surface[i]) free(surface[i]);
+            }
+            free(surface);
+        }
+    }
+    return result;
+}
+
+static int matches_locale(const char* loc) {
+    if (locale == NULL) return 0;
+
+    if (strcmp(loc, locale) == 0) return 1;
+
+    // if loc does *not* have an underscore, and it matches the start
+    // of locale, and the next character in locale *is* an underscore,
+    // that's a match.  For instance, loc == "en" matches locale ==
+    // "en_US".
+
+    int i;
+    for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i);
+    if (loc[i] == '_') return 0;
+
+    return (strncmp(locale, loc, i) == 0 && locale[i] == '_');
+}
+
+int res_create_localized_surface(const char* name, gr_surface* pSurface) {
+    char resPath[256];
+    GGLSurface* surface = NULL;
+    int result = 0;
+    unsigned char header[8];
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+
+    *pSurface = NULL;
+
+    snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
+    resPath[sizeof(resPath)-1] = '\0';
+    FILE* 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_init_io(png_ptr, fp);
+    png_set_sig_bytes(png_ptr, sizeof(header));
+    png_read_info(png_ptr, info_ptr);
+
+    int color_type, bit_depth;
+    size_t width, height;
+
+    png_get_IHDR(png_ptr, info_ptr, width, height, &bit_depth,
+            &color_type, NULL, NULL, NULL);
+
+    png_byte* channels = png_get_channels(png_ptr, info_ptr);
+    size_t stride = 4 * width;
+
+    if (!(bit_depth == 8 &&
+          (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {
+        return -7;
+        goto exit;
+    }
+
+    unsigned char* row = malloc(width);
+    int y;
+    for (y = 0; y < height; ++y) {
+        png_read_row(png_ptr, row, NULL);
+        int w = (row[1] << 8) | row[0];
+        int h = (row[3] << 8) | row[2];
+        int len = row[4];
+        char* loc = row+5;
+
+        if (y+1+h >= height || matches_locale(loc)) {
+            printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
+
+            surface = malloc(sizeof(GGLSurface));
+            if (surface == NULL) {
+                result = -8;
+                goto exit;
+            }
+            unsigned char* pData = malloc(w*h);
+
+            surface->version = sizeof(GGLSurface);
+            surface->width = w;
+            surface->height = h;
+            surface->stride = w; /* Yes, pixels, not bytes */
+            surface->data = pData;
+            surface->format = GGL_PIXEL_FORMAT_A_8;
+
+            int i;
+            for (i = 0; i < h; ++i, ++y) {
+                png_read_row(png_ptr, row, NULL);
+                memcpy(pData + i*w, row, w);
+            }
+
+            *pSurface = (gr_surface) surface;
+            break;
+        } else {
+            int i;
+            for (i = 0; i < h; ++i, ++y) {
+                png_read_row(png_ptr, row, NULL);
+            }
+        }
+    }
+
+exit:
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+    if (fp != NULL) {
+        fclose(fp);
+    }
+    if (result < 0) {
+        if (surface) {
+            free(surface);
+        }
+    }
+    return result;
+}
+
+void res_free_surface(gr_surface surface) {
+    GGLSurface* pSurface = (GGLSurface*) surface;
+    if (pSurface) {
+        free(pSurface);
+    }
+}
diff --git a/minui.old/roboto_10x18.h b/minui.old/roboto_10x18.h
new file mode 100644
index 0000000..3119c81
--- /dev/null
+++ b/minui.old/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/minui.old/roboto_15x24.h b/minui.old/roboto_15x24.h
new file mode 100644
index 0000000..7271d74
--- /dev/null
+++ b/minui.old/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/minui.old/roboto_23x41.h b/minui.old/roboto_23x41.h
new file mode 100644
index 0000000..6e7566e
--- /dev/null
+++ b/minui.old/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/minui/Android.mk b/minui/Android.mk
index 97724fb..d50bf10 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -9,6 +9,26 @@
     graphics_fbdev.cpp \
     resources.cpp \
 
+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
+    ifeq ($(TARGET_CUSTOM_KERNEL_HEADERS),)
+      LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include
+    else
+      LOCAL_C_INCLUDES += $(TARGET_CUSTOM_KERNEL_HEADERS)
+    endif
+  endif
+else
+  LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minui/include
+endif
+
+ifeq ($(TW_NEW_ION_HEAP), true)
+  LOCAL_CFLAGS += -DNEW_ION_HEAP
+endif
+
 LOCAL_WHOLE_STATIC_LIBRARIES += libadf
 LOCAL_WHOLE_STATIC_LIBRARIES += libdrm
 LOCAL_STATIC_LIBRARIES += libpng
@@ -37,6 +57,18 @@
   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)
 
 # Used by OEMs for factory test images.
diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp
index 997e9ca..512a7d0 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -59,11 +59,25 @@
 
 static void fbdev_blank(minui_backend* backend __unused, 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
     int ret;
 
     ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
     if (ret < 0)
         perror("ioctl(): blank");
+#endif
 }
 
 static void set_displayed_framebuffer(unsigned n)
diff --git a/minui/minui.h b/minui/minui.h
index bdde083..18173b1 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -17,6 +17,8 @@
 #ifndef _MINUI_H_
 #define _MINUI_H_
 
+#ifndef TW_USE_OLD_MINUI_H
+
 #include <sys/types.h>
 
 #include <functional>
@@ -120,4 +122,95 @@
 // functions.
 void res_free_surface(GRSurface* surface);
 
+#else //ifndef TW_USE_OLD_MINUI_H
+
+// This the old minui.old/minui.h for compatibility with building TWRP
+// in pre 6.0 trees.
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
 #endif
+
+typedef void* gr_surface;
+typedef unsigned short gr_pixel;
+
+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);
+void gr_fb_blank(bool blank);
+
+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);
+
+// 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);
+unsigned int gr_get_height(gr_surface surface);
+
+// input event structure, include <linux/input.h> for the definition.
+// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
+struct input_event;
+
+typedef int (*ev_callback)(int fd, uint32_t epevents, void *data);
+typedef int (*ev_set_key_callback)(int code, int value, void *data);
+
+int ev_init(ev_callback input_cb, void *data);
+void ev_exit(void);
+int ev_add_fd(int fd, ev_callback cb, void *data);
+int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data);
+
+/* timeout has the same semantics as for poll
+ *    0 : don't block
+ *  < 0 : block forever
+ *  > 0 : block for 'timeout' milliseconds
+ */
+int ev_wait(int timeout);
+
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev);
+void ev_dispatch(void);
+int ev_get_epollfd(void);
+
+// Resources
+
+// Returns 0 if no error, else negative.
+int res_create_surface(const char* name, gr_surface* pSurface);
+
+// Load an array of display surfaces from a single PNG image.  The PNG
+// should have a 'Frames' text chunk whose value is the number of
+// frames this image represents.  The pixel data itself is interlaced
+// by row.
+int res_create_multi_display_surface(const char* name,
+                                     int* frames, gr_surface** pSurface);
+
+int res_create_localized_surface(const char* name, gr_surface* pSurface);
+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);
+}
+
+// These are new graphics functions from 5.0 that were not available in
+// 4.4 that are required by charger and healthd
+void gr_clear();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ifndef TW_USE_OLD_MINUI_H
+#endif // ifndef _MINUI_H_
diff --git a/minuitwrp/Android.mk b/minuitwrp/Android.mk
new file mode 100644
index 0000000..2f30343
--- /dev/null
+++ b/minuitwrp/Android.mk
@@ -0,0 +1,161 @@
+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
+    ifeq ($(TARGET_CUSTOM_KERNEL_HEADERS),)
+      LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minuitwrp/include
+    else
+      LOCAL_C_INCLUDES += $(TARGET_CUSTOM_KERNEL_HEADERS)
+    endif
+  endif
+else
+  LOCAL_C_INCLUDES += $(commands_recovery_local_path)/minuitwrp/include
+endif
+
+ifeq ($(TW_NEW_ION_HEAP), true)
+  LOCAL_CFLAGS += -DNEW_ION_HEAP
+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
+
+ifeq ($(TW_DISABLE_DOUBLE_BUFFERING), true)
+LOCAL_CFLAGS += -DTW_DISABLE_DOUBLE_BUFFERING
+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
+
+ifeq ($(TW_IGNORE_MT_POSITION_0), true)
+LOCAL_CFLAGS += -DTW_IGNORE_MT_POSITION_0
+endif
+
+ifeq ($(TW_IGNORE_ABS_MT_TRACKING_ID), true)
+LOCAL_CFLAGS += -DTW_IGNORE_ABS_MT_TRACKING_ID
+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
+    ifeq ($(TW_THEME),)
+        # This converts the old DEVICE_RESOLUTION flag to the new TW_THEME flag
+        PORTRAIT_MDPI := 320x480 480x800 480x854 540x960
+        PORTRAIT_HDPI := 720x1280 800x1280 1080x1920 1200x1920 1440x2560 1600x2560
+        WATCH_MDPI := 240x240 280x280 320x320
+        LANDSCAPE_MDPI := 800x480 1024x600 1024x768
+        LANDSCAPE_HDPI := 1280x800 1920x1200 2560x1600
+        ifneq ($(filter $(DEVICE_RESOLUTION), $(PORTRAIT_MDPI)),)
+            TW_THEME := portrait_mdpi
+        else ifneq ($(filter $(DEVICE_RESOLUTION), $(PORTRAIT_HDPI)),)
+            TW_THEME := portrait_hdpi
+        else ifneq ($(filter $(DEVICE_RESOLUTION), $(WATCH_MDPI)),)
+            TW_THEME := watch_mdpi
+        else ifneq ($(filter $(DEVICE_RESOLUTION), $(LANDSCAPE_MDPI)),)
+            TW_THEME := landscape_mdpi
+        else ifneq ($(filter $(DEVICE_RESOLUTION), $(LANDSCAPE_HDPI)),)
+            TW_THEME := landscape_hdpi
+        endif
+    endif
+    ifeq ($(TW_THEME), portrait_mdpi)
+        LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=\"roboto_10x18.h\"
+    else ifeq ($(TW_THEME), portrait_hdpi)
+        LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=\"roboto_15x24.h\"
+    else ifeq ($(TW_THEME), watch_mdpi)
+        LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=\"font_7x16.h\"
+    else ifeq ($(TW_THEME), landscape_mdpi)
+        LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=\"roboto_10x18.h\"
+    else ifeq ($(TW_THEME), landscape_hdpi)
+        LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=\"roboto_15x24.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_CFLAGS += -DTWRES=\"$(TWRES_PATH)\"
+LOCAL_SHARED_LIBRARIES += libz libc libcutils libjpeg libpng
+LOCAL_STATIC_LIBRARIES += libpixelflinger_twrp
+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..4e10e72
--- /dev/null
+++ b/minuitwrp/events.c
@@ -0,0 +1,792 @@
+/*
+ * 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 <stdio.h>
+#include <string.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))
+            {
+#ifndef TW_IGNORE_MT_POSITION_0
+                e->mt_p.x = 0;
+                e->mt_p.y = 0;
+                lastWasSynReport = 1;
+#endif
+#ifdef _EVENT_LOGGING
+#ifndef TW_IGNORE_MT_POSITION_0
+                printf("EV: %s => EV_ABS  ABS_MT_POSITION  %d, set x and y to 0 and lastWasSynReport to 1\n", e->deviceName, ev->value);
+#else
+                printf("Ignoring ABS_MT_POSITION 0\n", e->deviceName, ev->value);
+#endif
+#endif
+            }
+            else
+            {
+                lastWasSynReport = 0;
+                e->mt_p.x = (ev->value & 0x7FFF0000) >> 16;
+                e->mt_p.y = (ev->value & 0xFFFF);
+#ifdef _EVENT_LOGGING
+                printf("EV: %s => EV_ABS  ABS_MT_POSITION  %d, set x: %d and y: %d and lastWasSynReport to 0\n", e->deviceName, ev->value, (ev->value & 0x7FFF0000) >> 16, (ev->value & 0xFFFF));
+#endif
+            }
+            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
+#ifdef TW_IGNORE_ABS_MT_TRACKING_ID
+#ifdef _EVENT_LOGGING
+            printf("EV: %s => EV_ABS ABS_MT_TRACKING_ID %d ignored\n", e->deviceName, ev->value);
+#endif
+            return 1;
+#endif
+            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;
+        case ABS_MT_SLOT:
+            printf("EV: %s => ABS_MT_SLOT %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;
+
+    // Special case, we'll ignore touches on 0,0 because it usually means
+    // that we received extra data after our last sync and x and y were
+    // reset to 0. We should not be using 0,0 anyway.
+    if (x == 0 && y == 0)
+        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, int timeout_ms)
+{
+    int r;
+    unsigned n;
+    struct timeval curr;
+
+    gettimeofday(&curr, NULL);
+    if(curr.tv_sec - lastInputStat.tv_sec >= 2)
+    {
+        struct stat st;
+        stat("/dev/input", &st);
+        if (st.st_mtime > lastInputMTime)
+        {
+            printf("Reloading input devices\n");
+            ev_exit();
+            ev_init();
+            lastInputMTime = st.st_mtime;
+        }
+        lastInputStat = curr;
+    }
+
+    r = poll(ev_fds, ev_count, timeout_ms);
+
+    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;
+                }
+            }
+        }
+        return -1;
+    }
+
+    return -2;
+}
+
+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..6dfbc23
--- /dev/null
+++ b/minuitwrp/graphics.c
@@ -0,0 +1,935 @@
+/*
+ * 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 <string.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, index = 0;
+    void *bits;
+
+    fd = open("/dev/graphics/fb0", O_RDWR);
+    
+    while (fd < 0 && index < 30) {
+        usleep(1000);
+        fd = open("/dev/graphics/fb0", O_RDWR);
+        index++;
+    }
+    if (fd < 0) {
+        perror("cannot open fb0\n");
+        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++;
+
+#ifndef TW_DISABLE_DOUBLE_BUFFERING
+    /* check if we can use double buffering */
+    if (vi.yres * fi.line_length * 2 > fi.smem_len)
+#else
+    printf("TW_DISABLE_DOUBLE_BUFFERING := true\n");
+#endif
+        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 = (GGLubyte*) (((unsigned long) bits) + vi.yres * fi.line_length);
+#else
+    fb->stride = vi.xres_virtual;
+    fb->data = (GGLubyte*) (((unsigned long) 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;
+        for (i = 0; i < vi.yres; i++) {
+            for (j = 0; j < vi.xres; j++) {
+                memcpy(gr_framebuffer[gr_active_fb].data + (i * vi.xres_virtual + j) * PIXEL_SIZE,
+                       gr_mem_surface.data + ((vi.yres - i - 1) * vi.xres_virtual + vi.xres - j - 1) * PIXEL_SIZE, PIXEL_SIZE);
+            }
+        }
+#else
+        /* 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);
+#endif
+
+        /* 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;
+        }
+    }
+
+    gl->disable(gl, GGL_TEXTURE_2D);
+
+    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;
+			}
+        }
+    }
+
+    gl->disable(gl, GGL_TEXTURE_2D);
+
+    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;
+        }
+    }
+
+    gl->disable(gl, GGL_TEXTURE_2D);
+
+    return x;
+}
+
+void gr_clip(int x, int y, int w, int h)
+{
+    GGLContext *gl = gr_context;
+    gl->scissor(gl, x, y, w, h);
+    gl->enable(gl, GGL_SCISSOR_TEST);
+}
+
+void gr_noclip()
+{
+    GGLContext *gl = gr_context;
+    gl->scissor(gl, 0, 0, gr_fb_width(), gr_fb_height());
+    gl->disable(gl, GGL_SCISSOR_TEST);
+}
+
+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->recti(gl, x, y, x + w, y + h);
+
+    if(gr_is_curr_clr_opaque)
+        gl->enable(gl, GGL_BLEND);
+}
+
+void gr_line(int x0, int y0, int x1, int y1, int width)
+{
+    GGLContext *gl = gr_context;
+
+    if(gr_is_curr_clr_opaque)
+        gl->disable(gl, GGL_BLEND);
+
+    const int coords0[2] = { x0 << 4, y0 << 4 };
+    const int coords1[2] = { x1 << 4, y1 << 4 };
+    gl->linex(gl, coords0, coords1, width << 4);
+
+    if(gr_is_curr_clr_opaque)
+        gl->enable(gl, GGL_BLEND);
+}
+
+gr_surface gr_render_circle(int radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+    int rx, ry;
+    GGLSurface *surface;
+    const int diameter = radius*2 + 1;
+    const int radius_check = radius*radius + radius*0.8;
+    const uint32_t px = (a << 24) | (b << 16) | (g << 8) | r;
+    uint32_t *data;
+
+    surface = malloc(sizeof(GGLSurface));
+    memset(surface, 0, sizeof(GGLSurface));
+
+    data = malloc(diameter * diameter * 4);
+    memset(data, 0, diameter * diameter * 4);
+
+    surface->version = sizeof(surface);
+    surface->width = diameter;
+    surface->height = diameter;
+    surface->stride = diameter;
+    surface->data = (GGLubyte*)data;
+    surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
+
+    for(ry = -radius; ry <= radius; ++ry)
+        for(rx = -radius; rx <= radius; ++rx)
+            if(rx*rx+ry*ry <= radius_check)
+                *(data + diameter*(radius + ry) + (radius+rx)) = px;
+
+    return surface;
+}
+
+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);
+    gl->disable(gl, GGL_TEXTURE_2D);
+
+    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, TWRES "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..80e64d7
--- /dev/null
+++ b/minuitwrp/graphics_overlay.c
@@ -0,0 +1,461 @@
+/*
+ * 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 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);
+#ifdef NEW_ION_HEAP
+    ionAllocData.heap_id_mask =
+#else
+    ionAllocData.heap_mask =
+#endif
+            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..ea1182d
--- /dev/null
+++ b/minuitwrp/graphics_utils.c
@@ -0,0 +1,113 @@
+/*
+ * 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 <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;
+    volatile int res = -1;
+    GGLContext *gl = NULL;
+    GGLSurface surface;
+    uint8_t * volatile img_data = NULL;
+    uint8_t *ptr;
+    FILE * volatile fp = NULL;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+
+    fp = fopen(dest, "wb");
+    if(!fp)
+        goto exit;
+
+    img_data = malloc(gr_mem_surface.stride * vi.yres * 4);
+    surface.version = sizeof(surface);
+    surface.width = gr_mem_surface.width;
+    surface.height = gr_mem_surface.height;
+    surface.stride = gr_mem_surface.stride;
+    surface.data = img_data;
+    surface.format = GGL_PIXEL_FORMAT_RGBA_8888;
+
+    gglInit(&gl);
+    gl->colorBuffer(gl, &surface);
+    gl->activeTexture(gl, 0);
+
+    if(gr_mem_surface.format == GGL_PIXEL_FORMAT_RGBX_8888)
+        gl->disable(gl, GGL_BLEND);
+
+    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);
+
+    // To remove the alpha channel for PNG_COLOR_TYPE_RGB format,
+    png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
+
+    ptr = img_data;
+    stride_bytes = surface.stride*4;
+    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..6033da7
--- /dev/null
+++ b/minuitwrp/minui.h
@@ -0,0 +1,100 @@
+/*
+ * 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_clip(int x, int y, int w, int h);
+void gr_noclip();
+void gr_fill(int x, int y, int w, int h);
+void gr_line(int x0, int y0, int x1, int y1, int width);
+gr_surface gr_render_circle(int radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
+
+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);
+
+// Functions in graphics_utils.c
+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, int timeout_ms);
+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);
+int res_scale_surface(gr_surface source, gr_surface* destination, float scale_w, float scale_h);
+
+// 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..0e12460
--- /dev/null
+++ b/minuitwrp/resources.c
@@ -0,0 +1,443 @@
+/*
+ * 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 <string.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;
+}
+
+#define SURFACE_DATA_ALIGNMENT 8
+
+static GGLSurface* malloc_surface(size_t data_size) {
+    unsigned char* temp = malloc(sizeof(GGLSurface) + data_size + SURFACE_DATA_ALIGNMENT);
+    if (temp == NULL) return NULL;
+    GGLSurface* surface = (GGLSurface*) temp;
+    surface->data = temp + sizeof(GGLSurface) +
+        (SURFACE_DATA_ALIGNMENT - (sizeof(GGLSurface) % SURFACE_DATA_ALIGNMENT));
+    return surface;
+}
+
+static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
+                    png_uint_32* width, png_uint_32* height, png_byte* channels, FILE** fpp) {
+    char resPath[256];
+    unsigned char header[8];
+    int result = 0;
+
+    snprintf(resPath, sizeof(resPath)-1, TWRES "images/%s.png", name);
+    printf("open_png %s\n", resPath);
+    resPath[sizeof(resPath)-1] = '\0';
+    FILE* fp = fopen(resPath, "rb");
+    if (fp == NULL) {
+        fp = fopen(name, "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_init_io(*png_ptr, fp);
+    png_set_sig_bytes(*png_ptr, sizeof(header));
+    png_read_info(*png_ptr, *info_ptr);
+
+    int color_type, bit_depth;
+    png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth,
+            &color_type, NULL, NULL, NULL);
+
+    *channels = png_get_channels(*png_ptr, *info_ptr);
+
+    /*if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) {
+        // 8-bit RGB images: great, nothing to do.
+    } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
+        // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
+        png_set_expand_gray_1_2_4_to_8(*png_ptr);
+    } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
+        // paletted images: expand to 8-bit RGB.  Note that we DON'T
+        // currently expand the tRNS chunk (if any) to an alpha
+        // channel, because minui doesn't support alpha channels in
+        // general.
+        png_set_palette_to_rgb(*png_ptr);
+        *channels = 3;
+    } else {
+        fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n",
+                bit_depth, *channels, color_type);
+        result = -7;
+        goto exit;
+    }*/
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        png_set_palette_to_rgb(png_ptr);
+    }
+
+    *fpp = fp;
+    return result;
+
+  exit:
+    if (result < 0) {
+        png_destroy_read_struct(png_ptr, info_ptr, NULL);
+    }
+    if (fp != NULL) {
+        fclose(fp);
+    }
+
+    return result;
+}
+
+// "display" surfaces are transformed into the framebuffer's required
+// pixel format (currently only RGBX is supported) at load time, so
+// gr_blit() can be nothing more than a memcpy() for each row.  The
+// next two functions are the only ones that know anything about the
+// framebuffer pixel format; they need to be modified if the
+// framebuffer format changes (but nothing else should).
+
+// Allocate and return a gr_surface sufficient for storing an image of
+// the indicated size in the framebuffer pixel format.
+static GGLSurface* init_display_surface(png_uint_32 width, png_uint_32 height) {
+    GGLSurface* surface;
+
+    surface = (GGLSurface*) malloc_surface(width * height * 4);
+    if (surface == NULL) return NULL;
+
+    surface->version = sizeof(GGLSurface);
+    surface->width = width;
+    surface->height = height;
+    surface->stride = width;
+
+    return surface;
+}
+
+// Copy 'input_row' to 'output_row', transforming it to the
+// framebuffer pixel format.  The input format depends on the value of
+// 'channels':
+//
+//   1 - input is 8-bit grayscale
+//   3 - input is 24-bit RGB
+//   4 - input is 32-bit RGBA/RGBX
+//
+// 'width' is the number of pixels in the row.
+static void transform_rgb_to_draw(unsigned char* input_row,
+                                  unsigned char* output_row,
+                                  int channels, int width) {
+    int x;
+    unsigned char* ip = input_row;
+    unsigned char* op = output_row;
+
+    switch (channels) {
+        case 1:
+            // expand gray level to RGBX
+            for (x = 0; x < width; ++x) {
+                *op++ = *ip;
+                *op++ = *ip;
+                *op++ = *ip;
+                *op++ = 0xff;
+                ip++;
+            }
+            break;
+
+        case 3:
+            // expand RGBA to RGBX
+            for (x = 0; x < width; ++x) {
+                *op++ = *ip++;
+                *op++ = *ip++;
+                *op++ = *ip++;
+                *op++ = 0xff;
+            }
+            break;
+
+        case 4:
+            // copy RGBA to RGBX
+            memcpy(output_row, input_row, width*4);
+            break;
+    }
+}
+
+int res_create_surface_png(const char* name, gr_surface* pSurface) {
+    GGLSurface* surface = NULL;
+    int result = 0;
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+    png_uint_32 width, height;
+    png_byte channels;
+    FILE* fp;
+
+    *pSurface = NULL;
+
+    result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels, &fp);
+    if (result < 0) return result;
+
+    surface = init_display_surface(width, height);
+    if (surface == NULL) {
+        result = -8;
+        goto exit;
+    }
+
+    unsigned char* p_row = malloc(width * 4);
+    unsigned int y;
+    for (y = 0; y < height; ++y) {
+        png_read_row(png_ptr, p_row, NULL);
+        transform_rgb_to_draw(p_row, surface->data + y * width * 4, channels, width);
+    }
+    free(p_row);
+
+    if (channels == 3)
+        surface->format = GGL_PIXEL_FORMAT_RGBX_8888;
+    else
+        surface->format = GGL_PIXEL_FORMAT_RGBA_8888;
+
+    *pSurface = (gr_surface) surface;
+
+  exit:
+    fclose(fp);
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    if (result < 0 && surface != NULL) 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, TWRES "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);
+    }
+}
+
+// Scale image function
+int res_scale_surface(gr_surface source, gr_surface* destination, float scale_w, float scale_h) {
+    GGLContext *gl = NULL;
+    GGLSurface* sc_mem_surface = NULL;
+    *destination = NULL;
+    GGLSurface *surface = (GGLSurface*)source;
+    int w = gr_get_width(source), h = gr_get_height(source);
+    int sx = 0, sy = 0, dx = 0, dy = 0;
+    float dw = (float)w * scale_w;
+    float dh = (float)h * scale_h;
+
+    // Create a new surface that is the appropriate size
+    sc_mem_surface = init_display_surface((int)dw, (int)dh);
+    if (!sc_mem_surface) {
+        printf("gr_scale_surface failed to init_display_surface\n");
+        return -1;
+    }
+    sc_mem_surface->format = surface->format;
+
+    // Initialize the context
+    gglInit(&gl);
+    gl->colorBuffer(gl, sc_mem_surface);
+    gl->activeTexture(gl, 0);
+
+    // Enable or disable blending based on source surface format
+    if (surface->format == GGL_PIXEL_FORMAT_RGBX_8888) {
+        gl->disable(gl, GGL_BLEND);
+    } else {
+        gl->enable(gl, GGL_BLEND);
+        gl->blendFunc(gl, GGL_ONE, GGL_ZERO);
+    }
+
+    // Bind our source surface to the context
+    gl->bindTexture(gl, surface);
+
+    // Deal with the scaling
+    gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_MIN_FILTER, GGL_LINEAR);
+    gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_MAG_FILTER, GGL_LINEAR);
+    gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP);
+    gl->texParameteri(gl, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
+    gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
+    gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
+    gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
+    gl->enable(gl, GGL_TEXTURE_2D);
+
+    int32_t grad[8];
+    memset(grad, 0, sizeof(grad));
+    // s, dsdx, dsdy, scale, t, dtdx, dtdy, tscale   <- this is wrong!
+    // This api uses block floating-point for S and T texture coordinates.
+    // All values are given in 16.16, scaled by 'scale'. In other words,
+    // set scale to 0, for 16.16 values.
+
+    // s, dsdx, dsdy, t, dtdx, dtdy, sscale, tscale
+    float dsdx = (float)w / dw;
+    float dtdy = (float)h / dh;
+    grad[0] = ((float)sx - (dsdx * dx)) * 65536;
+    grad[1] = dsdx * 65536;
+    grad[3] = ((float)sy - (dtdy * dy)) * 65536;
+    grad[5] = dtdy * 65536;
+//    printf("blit: w=%d h=%d dx=%d dy=%d dw=%f dh=%f dsdx=%f dtdy=%f s0=%x dsdx=%x t0=%x dtdy=%x\n",
+//                    w,   h,    dx,   dy,   dw,   dh,   dsdx,   dtdy, grad[0], grad[1], grad[3], grad[5]);
+    gl->texCoordGradScale8xv(gl, 0 /*tmu*/, grad);
+
+    // draw / scale the source surface to our target context
+    gl->recti(gl, dx, dy, dx + dw, dy + dh);
+    gglUninit(gl);
+    gl = NULL;
+    // put the scaled surface in our destination
+    *destination = (gr_surface*) sc_mem_surface;
+    // free memory used in the source
+    res_free_surface(source);
+    source = NULL;
+    return 0;
+}
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..8f62ff2
--- /dev/null
+++ b/minuitwrp/truetype.c
@@ -0,0 +1,801 @@
+#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_bytes; // number of bytes from C string rendered, not number of UTF8 characters!
+    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;
+}
+
+int utf8_to_unicode(unsigned char* pIn, unsigned int *pOut)
+{
+    int utf_bytes = 1;
+    unsigned int unicode = 0;
+    unsigned char tmp;
+    tmp = *pIn++;
+    if (tmp < 0x80)
+    {
+        *pOut = tmp;
+    }
+    else
+    {
+        unsigned int high_bit_mask = 0x3F;
+        unsigned int high_bit_shift = 0;
+        int total_bits = 0;
+        while((tmp & 0xC0) == 0xC0)
+        {
+            utf_bytes ++;
+            if(utf_bytes > 6)
+            {
+                *pOut = tmp;
+                return 1;
+            }
+            tmp = 0xFF & (tmp << 1);
+            total_bits += 6;
+            high_bit_mask >>= 1;
+            high_bit_shift++;
+            unicode <<= 6;
+            unicode |= (*pIn++) & 0x3F;
+        }
+        unicode |= ((tmp >> high_bit_shift) & high_bit_mask) << total_bits;
+        *pOut = unicode;
+    }
+
+    return utf_bytes;
+}
+
+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);
+
+    // FIXME: if glyph->left is negative and everything else is 0 (e.g. letter 'j' in Roboto-Regular),
+    // the result might end up being before the buffer - I'm not sure how to properly handle this.
+    if(dest_itr < dest->data)
+        dest_itr = dest->data;
+
+    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 void gr_ttf_calcMaxFontHeight(TrueTypeFont *f)
+{
+    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;
+}
+
+// returns number of bytes from const char *text rendered to fit max_width, not number of UTF8 characters!
+static int gr_ttf_render_text(TrueTypeFont *font, GGLSurface *surface, const char *text, int max_width)
+{
+    TrueTypeFont *f = font;
+    TrueTypeCacheEntry *ent;
+    int bytes_rendered = 0, total_w = 0;
+    int utf_bytes = 0;
+    unsigned int unicode = 0;
+    int i, x, diff, char_idx, prev_idx = 0;
+    int height, base;
+    FT_Vector delta;
+    uint8_t *data = NULL;
+    const char *text_itr = text;
+    int *char_idxs;
+    int char_idxs_len = 0;
+
+    char_idxs = malloc(strlen(text) * sizeof(int));
+
+    while(*text_itr)
+    {
+        utf_bytes = utf8_to_unicode(text_itr, &unicode);
+        text_itr += utf_bytes;
+        bytes_rendered += utf_bytes;
+
+        char_idx = FT_Get_Char_Index(f->face, unicode);
+        char_idxs[char_idxs_len] = char_idx;
+
+        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;
+        ++char_idxs_len;
+    }
+
+    if(font->max_height == -1)
+        gr_ttf_calcMaxFontHeight(font);
+
+    if(font->max_height == -1)
+    {
+        free(char_idxs);
+        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 < char_idxs_len; ++i)
+    {
+        char_idx = char_idxs[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;
+    }
+
+    free(char_idxs);
+    return bytes_rendered;
+}
+
+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_bytes = gr_ttf_render_text(font, &res->surface, text, max_width);
+        if(res->rendered_bytes < 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_bytes = 0, total_w = 0;
+    int utf_bytes, prev_utf_bytes = 0;
+    unsigned int unicode = 0;
+    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_bytes = e->rendered_bytes;
+        pthread_mutex_unlock(&f->mutex);
+        return max_bytes;
+    }
+
+    while(*s)
+    {
+        utf_bytes = utf8_to_unicode(s, &unicode);
+        s += utf_bytes;
+
+        char_idx = FT_Get_Char_Index(f->face, unicode);
+        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)
+        {
+            max_bytes -= prev_utf_bytes;
+            break;
+        }
+        prev_utf_bytes = utf_bytes;
+
+        ent = gr_ttf_glyph_cache_get(f, char_idx);
+        if(!ent)
+            continue;
+
+        total_w += ent->glyph->root.advance.x >> 16;
+        max_bytes += utf_bytes;
+    }
+    pthread_mutex_unlock(&f->mutex);
+    return max_bytes;
+}
+
+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_bytes;
+
+    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);
+    gl->disable(gl, GGL_TEXTURE_2D);
+
+    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)
+        gr_ttf_calcMaxFontHeight(f);
+    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..6cca092 100644
--- a/minzip/Android.mk
+++ b/minzip/Android.mk
@@ -14,8 +14,38 @@
 
 LOCAL_STATIC_LIBRARIES := libselinux
 
+LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)
+
 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/minzip/SysUtil.c b/minzip/SysUtil.c
index b160c9e..1858cd5 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -118,7 +118,12 @@
 
     // Reserve enough contiguous address space for the whole file.
     unsigned char* reserve;
+#if (PLATFORM_SDK_VERSION >= 21)
     reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+#else
+    // Older versions of Android do not have mmap64 so we will just use mmap instead
+    reserve = mmap(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+#endif
     if (reserve == MAP_FAILED) {
         LOGW("failed to reserve address space: %s\n", strerror(errno));
         return -1;
@@ -140,8 +145,12 @@
             LOGW("failed to parse range %d in block map\n", i);
             return -1;
         }
-
+#if (PLATFORM_SDK_VERSION >= 21)
         void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize);
+#else
+        // Older versions of Android do not have mmap64 so we will just use mmap instead
+        void* addr = mmap(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize);
+#endif
         if (addr == MAP_FAILED) {
             LOGW("failed to map block %d: %s\n", i, strerror(errno));
             return -1;
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..2a380ae 100644
--- a/mtdutils/Android.mk
+++ b/mtdutils/Android.mk
@@ -1,18 +1,60 @@
+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_SHARED_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_STATIC_LIBRARIES := libmtdutils
-LOCAL_SHARED_LIBRARIES := libcutils liblog libc
+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_SHARED_LIBRARIES := libmtdutils 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 cc30334..14be57f 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;
@@ -327,8 +324,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;
@@ -344,12 +341,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;
         }
@@ -432,11 +429,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 (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos ||
                 TEMP_FAILURE_RETRY(write(fd, data, size)) != size) {
                 printf("mtd: write error at 0x%08lx (%s)\n",
@@ -466,7 +471,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;
     }
 
@@ -539,9 +549,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;
     }
 
@@ -559,3 +575,224 @@
     free(ctx);
     return r;
 }
+
+/* Return the offset of the first good block at or after pos (which
+ * might be pos itself).
+ */
+off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
+    int i;
+    for (i = 0; i < ctx->bad_block_count; ++i) {
+        if (ctx->bad_block_offsets[i] == pos) {
+            pos += ctx->partition->erase_size;
+        } else if (ctx->bad_block_offsets[i] > pos) {
+            return pos;
+        }
+    }
+    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 8059d6a..6cbf37e 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);
@@ -55,8 +51,11 @@
 off_t mtd_erase_blocks(MtdWriteContext *, int blocks);  /* 0 ok, -1 for all */
 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..2040425
--- /dev/null
+++ b/mtp/Android.mk
@@ -0,0 +1,76 @@
+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++ libdl libcutils libutils libaosprecovery
+
+ifneq ($(wildcard external/stlport/Android.mk),)
+    LOCAL_SHARED_LIBRARIES += libstlport
+else
+    LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+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 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++ libdl libcutils libutils libaosprecovery
+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..a0ff8da
--- /dev/null
+++ b/mtp/MtpDatabase.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_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 void 					destroyDB(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/MtpMessage.hpp b/mtp/MtpMessage.hpp
new file mode 100644
index 0000000..272da17
--- /dev/null
+++ b/mtp/MtpMessage.hpp
@@ -0,0 +1,33 @@
+/*
+ * 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 _MTPMESSAGE_HPP
+#define _MTPMESSAGE_HPP
+
+#define MTP_MESSAGE_ADD_STORAGE    1
+#define MTP_MESSAGE_REMOVE_STORAGE 2
+
+struct mtpmsg {
+	int message_type; // 1 is add, 2 is remove, see above
+	unsigned int storage_id;
+	const char* display;
+	const char* path;
+	uint64_t maxFileSize;
+};
+
+#endif //_MTPMESSAGE_HPP
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..9cd6732
--- /dev/null
+++ b/mtp/MtpServer.cpp
@@ -0,0 +1,1379 @@
+/*
+ * 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 "../set_metadata.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(MtpDatabase* database, bool ptp,
+					int fileGroup, int filePerm, int directoryPerm)
+	:	mDatabase(database),
+		mPtp(ptp),
+		mFileGroup(fileGroup),
+		mFilePermission(filePerm),
+		mDirectoryPermission(directoryPerm),
+		mSessionID(0),
+		mSessionOpen(false),
+		mSendObjectHandle(kInvalidObjectHandle),
+		mSendObjectFormat(0),
+		mSendObjectFileSize(0)
+{
+	mFD = -1;
+}
+
+MtpServer::~MtpServer() {
+}
+
+void MtpServer::addStorage(MtpStorage* storage) {
+	android::Mutex::Autolock autoLock(mMutex);
+	MTPD("addStorage(): storage: %x\n", storage);
+	if (getStorage(storage->getStorageID()) != NULL) {
+		MTPE("MtpServer::addStorage Storage for storage ID %i already exists.\n", storage->getStorageID());
+		return;
+	}
+	mDatabase->createDB(storage, storage->getStorageID());
+	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) {
+			MTPD("MtpServer::removeStorage calling sendStoreRemoved\n");
+			// First lock the mutex so that the inotify thread and main
+			// thread do not do anything while we remove the storage
+			// item, and to make sure we don't remove the item while an
+			// operation is in progress
+			mDatabase->lockMutex();
+			// Grab the storage ID before we delete the item from the
+			// database
+			MtpStorageID storageID = storage->getStorageID();
+			// Remove the item from the mStorages from the vector. At
+			// this point the main thread will no longer be able to find
+			// this storage item anymore.
+			mStorages.removeAt(i);
+			// Destroy the storage item, free up all the memory, kill
+			// the inotify thread.
+			mDatabase->destroyDB(storageID);
+			// Tell the host OS that the storage item is gone.
+			sendStoreRemoved(storageID);
+			// Unlock any remaining mutexes on other storage devices.
+			// If no storage devices exist anymore this will do nothing.
+			mDatabase->unlockMutex();
+			break;
+		}
+	}
+	MTPD("MtpServer::removeStorage DONE\n");
+}
+
+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) {
+	if (fd < 0)
+		return;
+
+	mFD = fd;
+	MTPI("MtpServer::run fd: %d\n", fd);
+
+	while (1) {
+		MTPD("About to read device...\n");
+		int ret = mRequest.read(fd);
+		if (ret < 0) {
+			if (errno == ECANCELED) {
+				// return to top of loop and wait for next command
+				MTPD("request read returned %d ECANCELED, starting over\n", ret);
+				continue;
+			}
+			MTPE("request read returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
+			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) {
+				if (errno == ECANCELED) {
+					// return to top of loop and wait for next command
+					MTPD("data read returned %d ECANCELED, starting over\n", ret);
+					continue;
+				}
+				MTPD("data read returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
+				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) {
+					if (errno == ECANCELED) {
+						// return to top of loop and wait for next command
+						MTPD("data write returned %d ECANCELED, starting over\n", ret);
+						continue;
+					}
+					MTPE("data write returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
+					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) {
+				if (errno == ECANCELED) {
+					// return to top of loop and wait for next command
+					MTPD("response write returned %d ECANCELED, starting over\n", ret);
+					continue;
+				}
+				MTPE("response write returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
+				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(); // This doesn't actually do anything but was carry over from AOSP
+	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);
+	MTPD("MtpServer::sendStoreRemoved done\n");
+}
+
+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
+	MTPD("maxFileSize: %ld\n", maxFileSize); 
+	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);
+		tw_set_default_metadata((const char *)path);
+
+		// 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);
+	tw_set_default_metadata((const char *)mSendObjectFilePath);
+
+	if (ret < 0) {
+		unlink(mSendObjectFilePath);
+		if (errno == ECANCELED)
+			result = MTP_RESPONSE_TRANSACTION_CANCELLED;
+		else {
+		    	MTPD("errno: %d\n", errno);
+			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 result;
+}
+
+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..9443311
--- /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(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(int fd);
+
+    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..5a69548
--- /dev/null
+++ b/mtp/MtpStorage.cpp
@@ -0,0 +1,856 @@
+/*
+ * 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>
+#include "../tw_atomic.hpp"
+
+#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;
+	inotify_fd = -1;
+	// Threading has not started yet so we should be safe to set these directly instead of using atomics
+	inotify_thread_kill.set_value(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");
+		pthread_mutex_destroy(&mtpMutex);
+		use_mutex = false;
+	}
+}
+
+MtpStorage::~MtpStorage() {
+	if (inotify_thread) {
+		inotify_thread_kill.set_value(1);
+		MTPD("joining inotify_thread after sending the kill notification.\n");
+		pthread_join(inotify_thread, NULL); // There's not much we can do if there's an error here
+		inotify_thread = 0;
+		MTPD("~MtpStorage removing inotify watches and closing inotify_fd\n");
+		for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+			inotify_rm_watch(inotify_fd, i->first);
+		}
+		close(inotify_fd);
+		inotifymap.clear();
+	}
+	// Deleting the root tree causes a cascade in btree.cpp that ends up
+	// deleting all of the trees and nodes.
+	delete mtpmap[0];
+	mtpmap.clear();
+	if (use_mutex) {
+		use_mutex = false;
+		MTPD("~MtpStorage destroying mutexes\n");
+		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) {
+		sendEvents = true;
+		MTPD("inotify_init\n");
+		inotify_fd = inotify_init();
+		if (inotify_fd < 0) {
+			MTPE("Can't run inotify_init for mtp server: %s\n", strerror(errno));
+		} else {
+			MTPD("Starting inotify thread\n");
+			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;
+	pthread_attr_t tattr;
+
+	if (pthread_attr_init(&tattr)) {
+		MTPE("Unable to pthread_attr_init\n");
+		return 0;
+	}
+	if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) {
+		MTPE("Error setting pthread_attr_setdetachstate\n");
+		return 0;
+	}
+	ThreadPtr inotifyptr = &MtpStorage::inotify_t;
+	PThreadPtr p = *(PThreadPtr*)&inotifyptr;
+	pthread_create(&thread, &tattr, p, this);
+	if (pthread_attr_destroy(&tattr)) {
+		MTPE("Failed to pthread_attr_destroy\n");
+	}
+	return thread;
+}
+
+int MtpStorage::addInotify(Tree* tree) {
+	if (inotify_fd < 0) {
+		MTPE("inotify_fd not set or error: %i\n", inotify_fd);
+		return -1;
+	}
+	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];
+	fd_set fdset;
+	struct timeval seltmout;
+	int sel_ret;
+
+	MTPD("inotify thread starting.\n");
+
+	while (inotify_thread_kill.get_value() == 0) {
+		FD_ZERO(&fdset);
+		FD_SET(inotify_fd, &fdset);
+		seltmout.tv_sec = 0;
+		seltmout.tv_usec = 25000;
+		sel_ret = select(inotify_fd + 1, &fdset, NULL, NULL, &seltmout);
+		if (sel_ret == 0)
+			continue;
+		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 && inotify_thread_kill.get_value() == 0) {
+			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;
+		}
+	}
+	MTPD("inotify_thread_kill received!\n");
+	// This cleanup is handled in the destructor.
+	/*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..245debf
--- /dev/null
+++ b/mtp/MtpStorage.h
@@ -0,0 +1,120 @@
+/*
+ * 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"
+#include "../tw_atomic.hpp"
+
+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
+	TWAtomicInt inotify_thread_kill;
+};
+
+#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..4ad59fd
--- /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>
+// Not available in 5.0
+//#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..b73789b
--- /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 <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;
+	entries.clear();
+}
+
+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..e1aad36
--- /dev/null
+++ b/mtp/btree.hpp
@@ -0,0 +1,82 @@
+/*
+ * 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 <string>
+#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..17053f1
--- /dev/null
+++ b/mtp/mtp_MtpDatabase.cpp
@@ -0,0 +1,871 @@
+/*
+ * 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) {
+	MTPD("MyMtpDatabase::createDB called\n");
+	storagemap[storageID] = storage;
+	storage->createDB();
+}
+
+void MyMtpDatabase::destroyDB(MtpStorageID storageID) {
+	MtpStorage* storage = storagemap[storageID];
+	storagemap.erase(storageID);
+	delete storage;
+}
+
+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..49e5913
--- /dev/null
+++ b/mtp/mtp_MtpDatabase.hpp
@@ -0,0 +1,146 @@
+/*
+ * 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);
+	void					destroyDB(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..8d6038c
--- /dev/null
+++ b/mtp/mtp_MtpServer.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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 <pthread.h>
+
+#include "mtp_MtpServer.hpp"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpDebug.h"
+#include "MtpMessage.hpp"
+
+#include <string>
+
+void twmtp_MtpServer::start()
+{
+	usePtp =  false;
+	MyMtpDatabase* mtpdb = new MyMtpDatabase();
+	/* Sleep for a bit before we open the MTP USB device because some
+	 * devices are not ready due to the kernel not responding to our
+	 * sysfs requests right away.
+	 */
+	usleep(800000);
+#ifdef USB_MTP_DEVICE
+#define STRINGIFY(x) #x
+#define EXPAND(x) STRINGIFY(x)
+	const char* mtp_device = EXPAND(USB_MTP_DEVICE);
+	MTPI("Using '%s' for MTP device.\n", EXPAND(USB_MTP_DEVICE));
+#else
+	const char* mtp_device = "/dev/mtp_usb";
+#endif
+	int fd = open(mtp_device, O_RDWR);
+	if (fd < 0) {
+		MTPE("could not open MTP driver, errno: %d\n", errno);
+		return;
+	}
+	MTPD("fd: %d\n", fd);
+	server = new MtpServer(mtpdb, usePtp, 0, 0664, 0775);
+	refserver = server;
+	MTPI("created new mtpserver object\n");
+	add_storage();
+	MTPD("Starting add / remove mtppipe monitor thread\n");
+	pthread_t thread;
+	ThreadPtr mtpptr = &twmtp_MtpServer::mtppipe_thread;
+	PThreadPtr p = *(PThreadPtr*)&mtpptr;
+	pthread_create(&thread, NULL, p, this);
+	// This loop restarts the MTP process if the device is unplugged and replugged in
+	while (true) {
+		server->run(fd);
+		fd = open(mtp_device, O_RDWR);
+		usleep(800000);
+	}
+}
+
+void twmtp_MtpServer::set_storages(storages* mtpstorages) {
+	stores = mtpstorages;
+}
+
+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);
+
+	MTPD("twmtp_MtpServer::add_storage count of storage devices: %i\n", stores->size());
+	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;
+				uint64_t maxFileSize = stores->at(i)->maxFileSize;
+				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) {
+			MTPD("twmtp_MtpServer::remove_storage calling removeStorage\n");
+			server->removeStorage(storage);
+		}
+	} else
+		MTPD("server is null in remove_storage");
+	MTPD("twmtp_MtpServer::remove_storage DONE\n");
+}
+
+int twmtp_MtpServer::mtppipe_thread(void)
+{
+	if (mtp_read_pipe == -1) {
+		MTPD("mtppipe_thread exiting because mtp_read_pipe not set\n");
+		return 0;
+	}
+	MTPD("Starting twmtp_MtpServer::mtppipe_thread\n");
+	int read_count;
+	struct mtpmsg mtp_message;
+	while (1) {
+		read_count = ::read(mtp_read_pipe, &mtp_message, sizeof(mtp_message));
+		MTPD("read %i from mtppipe\n", read_count);
+		if (read_count == sizeof(mtp_message)) {
+			if (mtp_message.message_type == MTP_MESSAGE_ADD_STORAGE) {
+				MTPI("mtppipe add storage %i '%s'\n", mtp_message.storage_id, mtp_message.path);
+				if (mtp_message.storage_id) {
+					long reserveSpace = 1;
+					bool removable = false;
+					MtpStorage* storage = new MtpStorage(mtp_message.storage_id, mtp_message.path, mtp_message.display, reserveSpace, removable, mtp_message.maxFileSize, refserver);
+					server->addStorage(storage);
+					MTPD("mtppipe done adding storage\n");
+				} else {
+					MTPE("Invalid storage ID %i specified\n", mtp_message.storage_id);
+				}
+			} else if (mtp_message.message_type == MTP_MESSAGE_REMOVE_STORAGE) {
+				MTPI("mtppipe remove storage %i\n", mtp_message.storage_id);
+				remove_storage(mtp_message.storage_id);
+				MTPD("mtppipe done removing storage\n");
+			} else {
+				MTPE("Unknown mtppipe message value: %i\n", mtp_message.message_type);
+			}
+		} else {
+			MTPE("twmtp_MtpServer::mtppipe_thread unexpected read_count %i\n", read_count);
+			close(mtp_read_pipe);
+			break;
+		}
+	}
+	MTPD("twmtp_MtpServer::mtppipe_thread closing\n");
+	return 0;
+}
+
+void twmtp_MtpServer::set_read_pipe(int pipe)
+{
+	mtp_read_pipe = pipe;
+}
diff --git a/mtp/mtp_MtpServer.hpp b/mtp/mtp_MtpServer.hpp
new file mode 100755
index 0000000..99f63d5
--- /dev/null
+++ b/mtp/mtp_MtpServer.hpp
@@ -0,0 +1,65 @@
+/*
+ * 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;
+	uint64_t maxFileSize;
+} storage;
+
+typedef std::vector<storage*> storages;
+
+class twmtp_MtpServer {
+	public:
+		void start();
+		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);
+		void set_read_pipe(int pipe);
+		storages *stores;
+	private:
+		typedef int (twmtp_MtpServer::*ThreadPtr)(void);
+		typedef void* (*PThreadPtr)(void *);
+		int mtppipe_thread(void);
+		bool usePtp;
+		MtpServer* server;
+		MtpServer* refserver;
+		int mtp_read_pipe;
+
+};
+#endif
diff --git a/mtp/node.cpp b/mtp/node.cpp
new file mode 100755
index 0000000..207a37a
--- /dev/null
+++ b/mtp/node.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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 <vector>
+#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 = ((uint64_t)storageID << 32) + handle;
+	off_t file_size = 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...
+		// however, Microsoft's own impl (Zune, etc.) does not support persistent UIDs either
+	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..d47b8fa
--- /dev/null
+++ b/mtp/twrpMtp.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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;
+	mtp_read_pipe = -1;
+}
+
+int twrpMtp::start(void) {
+	MTPI("Starting MTP\n");
+	twmtp_MtpServer *mtp = new twmtp_MtpServer();
+	mtp->set_storages(mtpstorages);
+	mtp->set_read_pipe(mtp_read_pipe);
+	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(int mtppipe[2]) {
+	pid_t pid;
+	if ((pid = fork()) == -1) {
+		MTPE("MTP fork failed.\n");
+		return 0;
+	}
+	if (pid == 0) {
+		// Child process
+		close(mtppipe[1]); // Child closes write side
+		mtp_read_pipe = mtppipe[0];
+		start();
+		MTPD("MTP child process exited.\n");
+		close(mtppipe[0]);
+		_exit(0);
+	} else {
+		return pid;
+	}
+	return 0;
+}
+
+void twrpMtp::addStorage(std::string display, std::string path, int mtpid, uint64_t maxFileSize) {
+	s = new storage;
+	s->display = display;
+	s->mount = path;
+	s->mtpid = mtpid;
+	s->maxFileSize = maxFileSize;
+	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..ec7cd4b
--- /dev/null
+++ b/mtp/twrpMtp.hpp
@@ -0,0 +1,48 @@
+/*
+ * 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(int mtppipe[2]);
+		void addStorage(std::string display, std::string path, int mtpid, uint64_t maxFileSize);
+	private:
+		int start(void);
+		typedef int (twrpMtp::*ThreadPtr)(void);
+		typedef void* (*PThreadPtr)(void *);
+		storages *mtpstorages;
+		storage *s;
+		int mtp_read_pipe;
+};
+#endif
diff --git a/openaes/Android.mk b/openaes/Android.mk
new file mode 100644
index 0000000..ef6fcb0
--- /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 src/ftime.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 src/ftime.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/ftime.c b/openaes/src/ftime.c
new file mode 100644
index 0000000..d918209
--- /dev/null
+++ b/openaes/src/ftime.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <time.h>
+#include "ftime.h"
+
+// This was removed from POSIX 2008.
+int ftime(struct timeb* tb) {
+  struct timeval  tv;
+  struct timezone tz;
+
+  if (gettimeofday(&tv, &tz) < 0)
+    return -1;
+
+  tb->time    = tv.tv_sec;
+  tb->millitm = (tv.tv_usec + 500) / 1000;
+
+  if (tb->millitm == 1000) {
+    ++tb->time;
+    tb->millitm = 0;
+  }
+
+  tb->timezone = tz.tz_minuteswest;
+  tb->dstflag  = tz.tz_dsttime;
+
+  return 0;
+}
diff --git a/openaes/src/ftime.h b/openaes/src/ftime.h
new file mode 100644
index 0000000..80fbab7
--- /dev/null
+++ b/openaes/src/ftime.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Only used by ftime, which was removed from POSIX 2008.
+struct timeb {
+  time_t          time;
+  unsigned short  millitm;
+  short           timezone;
+  short           dstflag;
+};
+
+int ftime(struct timeb* tb);
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..0cc0394
--- /dev/null
+++ b/openaes/src/oaes_lib.c
@@ -0,0 +1,1431 @@
+/* 
+ * ---------------------------------------------------------------------------
+ * 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 "ftime.h"
+#include <malloc.h>
+#include <string.h>
+#include <unistd.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..cf24cba
--- /dev/null
+++ b/openrecoveryscript.cpp
@@ -0,0 +1,592 @@
+/*
+	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 <sys/types.h>
+#include <sys/wait.h>
+
+#include "twrp-functions.hpp"
+#include "partitions.hpp"
+#include "twcommon.h"
+#include "openrecoveryscript.hpp"
+#include "variables.h"
+#include "adb_install.h"
+#include "data.hpp"
+#include "adb_install.h"
+#include "fuse_sideload.h"
+extern "C" {
+	#include "twinstall.h"
+	#include "gui/gui.h"
+	#include "cutils/properties.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
+				size_t len = strlen(value);
+				tok = strtok(value, " ");
+				strcpy(value1, tok);
+				if (len > strlen(value1) + 1) {
+					char *val2 = value + strlen(value1) + 1;
+					gui_print("Setting '%s' to '%s'\n", value1, val2);
+					DataManager::SetValue(value1, val2);
+				} else {
+					gui_print("Setting '%s' to empty\n", value1);
+					DataManager::SetValue(value1, "");
+				}
+			} 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;
+				pid_t sideload_child_pid;
+
+				gui_print("Starting ADB sideload feature...\n");
+				ret_val = apply_from_adb("/", &sideload_child_pid);
+				if (ret_val != 0) {
+					if (ret_val == -2)
+						gui_print("You need adb 1.0.32 or newer to sideload to this device.\n");
+					ret_val = 1; // failure
+				} else if (TWinstall_zip(FUSE_SIDELOAD_HOST_PATHNAME, &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
+				if (sideload_child_pid != 0) {
+					LOGINFO("Signaling child sideload process to exit.\n");
+					struct stat st;
+					// Calling stat() on this magic filename signals the minadbd
+					// subprocess to shut down.
+					stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+					int status;
+					LOGINFO("Waiting for child sideload process to exit.\n");
+					waitpid(sideload_child_pid, &status, 0);
+				}
+				property_set("ctl.start", "adbd");
+				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;
+
+	if (Zip.substr(0, 1) == "@") {
+		// This is a special file that contains a map of blocks on the data partition
+		Full_Path = Zip.substr(1);
+		if (!PartitionManager.Mount_By_Path(Full_Path, true) || !TWFunc::Path_Exists(Full_Path)) {
+			gui_print("Unable to install via mapped zip '%s'\n", Full_Path.c_str());
+			return 1;
+		}
+		gui_print("Installing mapped zip file '%s'\n", Full_Path.c_str());
+	} else if (!TWFunc::Path_Exists(Zip)) {
+		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", "");
+#ifdef TW_OEM_BUILD
+	DataManager::SetValue("tw_action_text1", "Running Recovery Commands");
+	DataManager::SetValue("tw_complete_text1", "Recovery Commands Complete");
+#else
+	DataManager::SetValue("tw_action_text1", "Running OpenRecoveryScript");
+	DataManager::SetValue("tw_complete_text1", "OpenRecoveryScript Complete");
+#endif
+	DataManager::SetValue("tw_action_text2", "");
+	DataManager::SetValue("tw_has_cancel", 0);
+	DataManager::SetValue("tw_show_reboot", 0);
+	if (gui_startPage("action_page", 0, 1) != 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..8c5b412
--- /dev/null
+++ b/partition.cpp
@@ -0,0 +1,2371 @@
+/*
+	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/include/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"
+#include "set_metadata.h"
+extern "C" {
+	#include "mtdutils/mtdutils.h"
+	#include "mtdutils/mounts.h"
+#ifdef USE_EXT4
+	#include "make_ext4fs.h"
+#endif
+
+#ifdef TW_INCLUDE_CRYPTO
+	#include "crypto/lollipop/cryptfs.h"
+#else
+	#define CRYPT_FOOTER_OFFSET 0x4000
+#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;
+	Mount_To_Decrypt = 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;
+	Crypto_Key_Location = "footer";
+	MTP_Storage_ID = 0;
+	Can_Flash_Img = false;
+	Mount_Read_Only = false;
+}
+
+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;
+			Mount_Read_Only = true;
+		} else if (Mount_Point == "/data") {
+			UnMount(false); // added in case /data is mounted as tmpfs for qcom hardware decrypt
+			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) {
+					set_partition_data(Actual_Block_Device.c_str(), Crypto_Key_Location.c_str(), Fstab_File_System.c_str());
+					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_PWTYPE, cryptfs_get_password_type());
+						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;
+			Can_Flash_Img = true;
+		} else if (Mount_Point == "/recovery") {
+			Display_Name = "Recovery";
+			Backup_Display_Name = Display_Name;
+			Can_Flash_Img = true;
+		} else if (Mount_Point == "/system_image") {
+			Display_Name = "System Image";
+			Backup_Display_Name = Display_Name;
+			Can_Flash_Img = false;
+			Can_Be_Backed_Up = true;
+		}
+	}
+
+	// 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;
+			Is_Settings_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 = (unsigned long)(atol(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 ((ptr_len > 12 && strncmp(ptr, "encryptable=", 12) == 0) || (ptr_len > 13 && strncmp(ptr, "forceencrypt=", 13) == 0)) {
+			ptr += 12;
+			if (*ptr == '=') ptr++;
+			if (*ptr == '\"') ptr++;
+
+			Crypto_Key_Location = ptr;
+			if (Crypto_Key_Location.substr(Crypto_Key_Location.size() - 1, 1) == "\"") {
+				Crypto_Key_Location.resize(Crypto_Key_Location.size() - 1);
+			}
+		} else if (ptr_len > 8 && strncmp(ptr, "mounttodecrypt", 14) == 0) {
+			Mount_To_Decrypt = true;
+		} else if (strncmp(ptr, "flashimg", 8) == 0) {
+			if (ptr_len == 8) {
+				Can_Flash_Img = true;
+			} else if (ptr_len == 10) {
+				ptr += 9;
+				if (*ptr == '1' || *ptr == 'y' || *ptr == 'Y') {
+					Can_Flash_Img = true;
+				} else {
+					Can_Flash_Img = false;
+				}
+			}
+		} 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;
+	unsigned long flags = Mount_Flags;
+
+	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 (Current_File_System == "ntfs" && TWFunc::Path_Exists("/sbin/ntfs-3g")) {
+		string cmd;
+		if (Mount_Read_Only)
+			cmd = "/sbin/ntfs-3g -o ro " + Actual_Block_Device + " " + Mount_Point;
+		else
+			cmd = "/sbin/ntfs-3g " + Actual_Block_Device + " " + Mount_Point;
+		LOGINFO("cmd: '%s'\n", cmd.c_str());
+		if (TWFunc::Exec_Cmd(cmd) == 0) {
+			return true;
+		} else {
+			LOGINFO("ntfs-3g failed to mount, trying regular mount method.\n");
+		}
+	}
+
+	if (Mount_Read_Only)
+		flags |= MS_RDONLY;
+
+	if (Fstab_File_System == "yaffs2") {
+		// mount an MTD partition as a YAFFS2 filesystem.
+		flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
+		if (Mount_Read_Only)
+			flags |= MS_RDONLY;
+		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;
+		}
+	}
+
+	string mount_fs = Current_File_System;
+	if (Current_File_System == "exfat" && TWFunc::Path_Exists("/sys/module/texfat"))
+		mount_fs = "texfat";
+
+	if (!exfat_mounted &&
+		mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), mount_fs.c_str(), flags, Mount_Options.c_str()) != 0 &&
+		mount(Actual_Block_Device.c_str(), Mount_Point.c_str(), mount_fs.c_str(), 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(), 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
+	}
+
+	if (Removable)
+		Update_Size(Display_Error);
+
+	if (!Symlink_Mount_Point.empty() && TWFunc::Path_Exists(Symlink_Path)) {
+		string Command = "mount -o bind '" + 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)
+			PartitionManager.Remove_MTP_Storage(MTP_Storage_ID);
+
+		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;
+	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;
+
+	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;
+	} 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 (New_File_System == "ntfs")
+			wiped = Wipe_NTFS();
+		else {
+			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) {
+		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) {
+		PartitionManager.Add_MTP_Storage(MTP_Storage_ID);
+	}
+	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 (Mount_Read_Only)
+		return false;
+	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;
+	else if (Current_File_System == "ntfs" && TWFunc::Path_Exists("/sbin/ntfsfix"))
+		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 -fp " + 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;
+		}
+	}
+	if (Current_File_System == "ntfs") {
+		if (!TWFunc::Path_Exists("/sbin/ntfsfix")) {
+			gui_print("ntfsfix does not exist! Cannot repair!\n");
+			return false;
+		}
+		if (!UnMount(true))
+			return false;
+		gui_print("Repairing %s using ntfsfix...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "/sbin/ntfsfix " + 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::Can_Resize() {
+	if (Mount_Read_Only)
+		return false;
+	if ((Current_File_System == "ext2" || Current_File_System == "ext3" || Current_File_System == "ext4") && TWFunc::Path_Exists("/sbin/resize2fs"))
+		return true;
+	return false;
+}
+
+bool TWPartition::Resize() {
+	string command;
+
+	if (Current_File_System == "ext2" || Current_File_System == "ext3" || Current_File_System == "ext4") {
+		if (!Can_Repair()) {
+			LOGERR("Cannot resize %s because %s cannot be repaired before resizing.\n", Display_Name.c_str(), Display_Name.c_str());
+			return false;
+		}
+		if (!TWFunc::Path_Exists("/sbin/resize2fs")) {
+			gui_print("resize2fs does not exist! Cannot resize!\n");
+			return false;
+		}
+		// Repair will unmount so no need to do it twice
+		gui_print("Repairing %s before resizing.\n", Display_Name.c_str());
+		if (!Repair())
+			return false;
+		gui_print("Resizing %s using resize2fs...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "/sbin/resize2fs " + Actual_Block_Device;
+		if (Length != 0) {
+			unsigned int block_device_size;
+			int fd, ret;
+
+			fd = open(Actual_Block_Device.c_str(), O_RDONLY);
+			if (fd < 0) {
+				LOGERR("Resize: Failed to open '%s'\n", Actual_Block_Device.c_str());
+				return false;
+			}
+			ret = ioctl(fd, BLKGETSIZE, &block_device_size);
+			close(fd);
+			if (ret) {
+				LOGERR("Resize: ioctl error\n");
+				return false;
+			}
+			unsigned long long Actual_Size = (unsigned long long)(block_device_size) * 512LLU;
+			unsigned long long Block_Count;
+			if (Length < 0) {
+				// Reduce overall size by this length
+				Block_Count = (Actual_Size / 1024LLU) - ((unsigned long long)(Length * -1) / 1024LLU);
+			} else {
+				// This is the size, not a size reduction
+				Block_Count = ((unsigned long long)(Length) / 1024LLU);
+			}
+			char temp[256];
+			sprintf(temp, "%llu", Block_Count);
+			command += " ";
+			command += temp;
+			command += "K";
+		}
+		LOGINFO("Resize command: %s\n", command.c_str());
+		if (TWFunc::Exec_Cmd(command) == 0) {
+			Update_Size(true);
+			gui_print("Done.\n");
+			return true;
+		} else {
+			Update_Size(true);
+			LOGERR("Unable to resize '%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, pid_t &tar_fork_pid) {
+	if (Backup_Method == FILES) {
+		return Backup_Tar(backup_folder, overall_size, other_backups_size, tar_fork_pid);
+	}
+	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)) {
+		return Restore_Image(restore_folder, total_restore_size, already_restored_size, Restore_File_System);
+	}
+
+	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 = "";
+#ifdef TW_INCLUDE_CRYPTO
+	if (Is_Decrypted) {
+		if (!UnMount(true))
+			return false;
+		if (delete_crypto_blk_dev((char*)("userdata")) != 0) {
+			LOGERR("Error deleting crypto block device, continuing anyway.\n");
+		}
+	}
+#endif
+	Is_Decrypted = false;
+	Is_Encrypted = false;
+	Find_Actual_Block_Device();
+	if (Crypto_Key_Location == "footer") {
+		int newlen, fd;
+		if (Length != 0) {
+			newlen = Length;
+			if (newlen < 0)
+				newlen = newlen * -1;
+		} else {
+			newlen = CRYPT_FOOTER_OFFSET;
+		}
+		if ((fd = open(Actual_Block_Device.c_str(), O_RDWR)) < 0) {
+			gui_print_color("warning", "Unable to open '%s' to wipe crypto key\n", Actual_Block_Device.c_str());
+		} else {
+			unsigned int block_count;
+			if ((ioctl(fd, BLKGETSIZE, &block_count)) == -1) {
+				gui_print_color("warning", "Unable to get block size for wiping crypto footer.\n");
+			} else {
+				off64_t offset = ((off64_t)block_count * 512) - newlen;
+				if (lseek64(fd, offset, SEEK_SET) == -1) {
+					gui_print_color("warning", "Unable to lseek64 for wiping crypto footer.\n");
+				} else {
+					void* buffer = malloc(newlen);
+					if (!buffer) {
+						gui_print_color("warning", "Failed to malloc for wiping crypto footer.\n");
+					} else {
+						memset(buffer, 0, newlen);
+						int ret = write(fd, buffer, newlen);
+						if (ret != newlen) {
+							gui_print_color("warning", "Failed to wipe crypto footer.\n");
+						} else {
+							LOGINFO("Successfully wiped crypto footer.\n");
+						}
+					}
+				}
+			}
+			close(fd);
+		}
+	} else {
+		string Command = "flash_image " + Crypto_Key_Location + " /dev/zero";
+		TWFunc::Exec_Cmd(Command);
+	}
+	if (Wipe(Fstab_File_System)) {
+		Has_Data_Media = Save_Data_Media;
+		if (Has_Data_Media && !Symlink_Mount_Point.empty()) {
+			Recreate_Media_Folder();
+			if (Mount(false))
+				PartitionManager.Add_MTP_Storage(MTP_Storage_ID);
+		}
+		DataManager::SetValue(TW_IS_ENCRYPTED, 0);
+#ifndef TW_OEM_BUILD
+		gui_print("You may need to reboot recovery to be able to use /data again.\n");
+#endif
+		return true;
+	} else {
+		Has_Data_Media = Save_Data_Media;
+		LOGERR("Unable to format to remove encryption.\n");
+		if (Has_Data_Media && Mount(false))
+			PartitionManager.Add_MTP_Storage(MTP_Storage_ID);
+		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 (!Mount(true))
+		return false;
+	// This is the only wipe that leaves the partition mounted, so we
+	// must manually remove the partition from MTP if it is a storage
+	// partition.
+	if (Is_Storage)
+		PartitionManager.Remove_MTP_Storage(MTP_Storage_ID);
+
+	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 -t 1";
+		if (!Is_Decrypted && Length != 0) {
+			// Only use length if we're not decrypted
+			char len[32];
+			int mod_length = Length;
+			if (Length < 0)
+				mod_length *= -1;
+			sprintf(len, "%i", mod_length);
+			command += " -r ";
+			command += len;
+		}
+		command += " " + 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_NTFS() {
+	string command;
+
+	if (TWFunc::Path_Exists("/sbin/mkntfs")) {
+		if (!UnMount(true))
+			return false;
+
+		gui_print("Formatting %s using mkntfs...\n", Display_Name.c_str());
+		Find_Actual_Block_Device();
+		command = "mkntfs " + 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_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;
+
+	// 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, pid_t &tar_fork_pid) {
+	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, tar_fork_pid) != 0)
+		return false;
+	return true;
+}
+
+bool TWPartition::Backup_DD(string backup_folder) {
+	char back_name[255], block_size[32], dd_count[32];
+	string Full_FileName, Command, DD_BS, DD_COUNT;
+	int use_compression;
+	unsigned long long DD_Block_Size, DD_Count;
+
+	DD_Block_Size = 16 * 1024 * 1024;
+	while (Backup_Size % DD_Block_Size != 0) DD_Block_Size >>= 1;
+
+	DD_Count = Backup_Size / DD_Block_Size;
+
+	sprintf(dd_count, "%llu", DD_Count);
+	DD_COUNT = dd_count;
+
+	sprintf(block_size, "%llu", DD_Block_Size);
+	DD_BS = block_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 + " count=" + DD_COUNT;
+	LOGINFO("Backup command: '%s'\n", Command.c_str());
+	TWFunc::Exec_Cmd(Command);
+	tw_set_default_metadata(Full_FileName.c_str());
+	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);
+	tw_set_default_metadata(Full_FileName.c_str());
+	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_Image(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size, string Restore_File_System) {
+	string Full_FileName;
+	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 (Restore_File_System == "emmc") {
+		if (!Flash_Image_DD(Full_FileName))
+			return false;
+	} else if (Restore_File_System == "mtd" || Restore_File_System == "bml") {
+		if (!Flash_Image_FI(Full_FileName))
+			return false;
+	}
+	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;
+
+	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", 0770);
+		string Internal_path = DataManager::GetStrValue("tw_internal_path");
+		if (!Internal_path.empty()) {
+			LOGINFO("Recreating %s folder.\n", Internal_path.c_str());
+			mkdir(Internal_path.c_str(), 0770);
+		}
+#ifdef TW_INTERNAL_STORAGE_PATH
+		mkdir(EXPAND(TW_INTERNAL_STORAGE_PATH), 0770);
+#endif
+#ifdef HAVE_SELINUX
+		// Afterwards, we will try to set the
+		// default metadata that we were hopefully able to get during
+		// early boot.
+		tw_set_default_metadata("/data/media");
+		if (!Internal_path.empty())
+			tw_set_default_metadata(Internal_path.c_str());
+#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);
+	}
+}
+
+uint64_t TWPartition::Get_Max_FileSize() {
+	uint64_t maxFileSize = 0;
+	const uint64_t constGB = (uint64_t) 1024 * 1024 * 1024;
+	const uint64_t constTB = (uint64_t) constGB * 1024;
+	const uint64_t constPB = (uint64_t) constTB * 1024;
+	const uint64_t constEB = (uint64_t) constPB * 1024;
+	if (Current_File_System == "ext4")
+		maxFileSize = 16 * constTB; //16 TB
+	else if (Current_File_System == "vfat")
+		maxFileSize = 4 * constGB; //4 GB
+	else if (Current_File_System == "ntfs")
+		maxFileSize = 256 * constTB; //256 TB
+	else if (Current_File_System == "exfat")
+		maxFileSize = 16 * constPB; //16 PB
+	else if (Current_File_System == "ext3")
+		maxFileSize = 2 * constTB; //2 TB
+	else if (Current_File_System == "f2fs")
+		maxFileSize = 3.94 * constTB; //3.94 TB
+	else
+		maxFileSize = 100000000L;
+	LOGINFO("Get_Max_FileSize::maxFileSize: %llu\n", maxFileSize);
+	return maxFileSize - 1;
+}
+
+bool TWPartition::Flash_Image(string Filename) {
+	string Restore_File_System;
+
+	LOGINFO("Image filename is: %s\n", Filename.c_str());
+
+	if (Backup_Method == FILES) {
+		LOGERR("Cannot flash images to file systems\n");
+		return false;
+	} else if (!Can_Flash_Img) {
+		LOGERR("Cannot flash images to partitions %s\n", Display_Name.c_str());
+		return false;
+	} else {
+		if (!Find_Partition_Size()) {
+			LOGERR("Unable to find partition size for '%s'\n", Mount_Point.c_str());
+			return false;
+		}
+		unsigned long long image_size = TWFunc::Get_File_Size(Filename);
+		if (image_size > Size) {
+			LOGERR("Size (%llu bytes) of image '%s' is larger than target device '%s' (%llu bytes)\n",
+				image_size, Filename.c_str(), Actual_Block_Device.c_str(), Size);
+			return false;
+		}
+		if (Backup_Method == DD)
+			return Flash_Image_DD(Filename);
+		else if (Backup_Method == FLASH_UTILS)
+			return Flash_Image_FI(Filename);
+	}
+
+	LOGERR("Unknown flash method for '%s'\n", Mount_Point.c_str());
+	return false;
+}
+
+bool TWPartition::Flash_Image_DD(string Filename) {
+	string Command;
+
+	gui_print("Flashing %s...\n", Display_Name.c_str());
+	Command = "dd bs=8388608 if='" + Filename + "' of=" + Actual_Block_Device;
+	LOGINFO("Flash command: '%s'\n", Command.c_str());
+	TWFunc::Exec_Cmd(Command);
+	return true;
+}
+
+bool TWPartition::Flash_Image_FI(string Filename) {
+	string Command;
+
+	gui_print("Flashing %s...\n", Display_Name.c_str());
+	// 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 + " '" + Filename + "'";
+	LOGINFO("Flash command: '%s'\n", Command.c_str());
+	TWFunc::Exec_Cmd(Command);
+	return true;
+}
+
+void TWPartition::Change_Mount_Read_Only(bool new_value) {
+	Mount_Read_Only = new_value;
+}
+
+int TWPartition::Check_Lifetime_Writes() {
+	bool original_read_only = Mount_Read_Only;
+	int ret = 1;
+
+	Mount_Read_Only = true;
+	if (Mount(false)) {
+		Find_Actual_Block_Device();
+		string block = basename(Actual_Block_Device.c_str());
+		string file = "/sys/fs/" + Current_File_System + "/" + block + "/lifetime_write_kbytes";
+		string result;
+		if (TWFunc::Path_Exists(file)) {
+			if (TWFunc::read_file(file, result) != 0) {
+				LOGINFO("Check_Lifetime_Writes of '%s' failed to read_file\n", file.c_str());
+			} else {
+				LOGINFO("Check_Lifetime_Writes result: '%s'\n", result.c_str());
+				if (result == "0") {
+					ret = 0;
+				}
+			}
+		} else {
+			LOGINFO("Check_Lifetime_Writes file does not exist '%s'\n", file.c_str());
+		}
+		UnMount(true);
+	} else {
+		LOGINFO("Check_Lifetime_Writes failed to mount '%s'\n", Mount_Point.c_str());
+	}
+	Mount_Read_Only = original_read_only;
+	return ret;
+}
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
new file mode 100644
index 0000000..f254439
--- /dev/null
+++ b/partitionmanager.cpp
@@ -0,0 +1,2256 @@
+/*
+	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 <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"
+#include "set_metadata.h"
+#include "tw_atomic.hpp"
+
+#ifdef TW_HAS_MTP
+#include "mtp/mtp_MtpServer.hpp"
+#include "mtp/twrpMtp.hpp"
+#include "mtp/MtpMessage.hpp"
+#endif
+
+extern "C" {
+	#include "cutils/properties.h"
+}
+
+#ifdef TW_INCLUDE_CRYPTO
+	#include "crypto/lollipop/cryptfs.h"
+#endif
+
+extern bool datamedia;
+
+TWPartitionManager::TWPartitionManager(void) {
+	mtp_was_enabled = false;
+	mtp_write_fd = -1;
+	stop_backup.set_value(0);
+	tar_fork_pid = 0;
+}
+
+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;
+	unsigned int storageid = 1 << 16;	// upper 16 bits are for physical storage device, we pretend to have only one
+
+	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 (partition->Is_Storage) {
+				++storageid;
+				partition->MTP_Storage_ID = storageid;
+			}
+			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;
+			// Since /data was not considered a storage partition earlier, we still need to assign an MTP ID
+			++storageid;
+			Dat->MTP_Storage_ID = storageid;
+		}
+	}
+	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);
+	}
+#ifdef TW_INCLUDE_CRYPTO
+	TWPartition* Decrypt_Data = Find_Partition_By_Path("/data");
+	if (Decrypt_Data && Decrypt_Data->Is_Encrypted && !Decrypt_Data->Is_Decrypted) {
+		int password_type = cryptfs_get_password_type();
+		if (password_type == CRYPT_TYPE_DEFAULT) {
+			LOGINFO("Device is encrypted with the default password, attempting to decrypt.\n");
+			if (Decrypt_Device("default_password") == 0) {
+				gui_print("Successfully decrypted with default password.\n");
+				DataManager::SetValue(TW_IS_ENCRYPTED, 0);
+			} else {
+				LOGERR("Unable to decrypt with default password.");
+				LOGERR("You may need to perform a Format Data.\n");
+			}
+		} else {
+			DataManager::SetValue("TW_CRYPTO_TYPE", password_type);
+		}
+	}
+#endif
+	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 ");
+	if (Part->Mount_To_Decrypt)
+		printf("Mount_To_Decrypt ");
+	if (Part->Can_Flash_Img)
+		printf("Can_Flash_Img ");
+	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->Crypto_Key_Location.empty() && Part->Crypto_Key_Location != "footer")
+		printf("   Crypto_Key_Location: %s\n", Part->Crypto_Key_Location.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: %lu\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());
+	if (Part->MTP_Storage_ID)
+		printf("   MTP_Storage_ID: %i\n", Part->MTP_Storage_ID);
+	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 use_compression;
+	float pos;
+	unsigned long long total_size, current_size;
+
+	string backup_log = Backup_Folder + "recovery.log";
+
+	if (Part == NULL)
+		return true;
+
+	DataManager::GetValue(TW_USE_COMPRESSION_VAR, use_compression);
+
+	total_size = *file_bytes + *img_bytes;
+	current_size = *file_bytes + *img_bytes - *file_bytes_remaining - *img_bytes_remaining;
+	// Set the position
+	pos = ((float)(current_size) / (float)(total_size));
+	DataManager::SetProgress(pos);
+
+	TWFunc::SetPerformanceMode(true);
+	time(&start);
+
+	if (Part->Backup(Backup_Folder, &total_size, &current_size, tar_fork_pid)) {
+		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, tar_fork_pid)) {
+						TWFunc::SetPerformanceMode(false);
+						Clean_Backup_Folder(Backup_Folder);
+						TWFunc::copy_file("/tmp/recovery.log", backup_log, 0644);
+						tw_set_default_metadata(backup_log.c_str());
+						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);
+		int 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 {
+		Clean_Backup_Folder(Backup_Folder);
+		TWFunc::copy_file("/tmp/recovery.log", backup_log, 0644);
+		tw_set_default_metadata(backup_log.c_str());
+		TWFunc::SetPerformanceMode(false);
+		return false;
+	}
+	return 0;
+}
+
+void TWPartitionManager::Clean_Backup_Folder(string Backup_Folder) {
+	DIR *d = opendir(Backup_Folder.c_str());
+	struct dirent *p;
+	int r;
+
+	gui_print("Backup Failed.\nCleaning Backup Folder\n");
+
+	if (d == NULL) {
+		LOGERR("Error opening dir: '%s'\n", Backup_Folder.c_str());
+		return;
+	}
+
+	while ((p = readdir(d))) {
+		if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
+			continue;
+
+		string path = Backup_Folder + p->d_name;
+
+		size_t dot = path.find_last_of(".") + 1;
+		if (path.substr(dot) == "win" || path.substr(dot) == "md5" || path.substr(dot) == "info") {
+			r = unlink(path.c_str());
+			if (r != 0) {
+				LOGINFO("Unable to unlink '%s: %s'\n", path.c_str(), strerror(errno));
+			}
+		}
+	}
+	closedir(d);
+}
+
+int TWPartitionManager::Cancel_Backup() {
+	string Backup_Folder, Backup_Name, Full_Backup_Path;
+
+	stop_backup.set_value(1);
+
+	if (tar_fork_pid != 0) {
+		DataManager::GetValue(TW_BACKUP_NAME, Backup_Name);
+		DataManager::GetValue(TW_BACKUPS_FOLDER_VAR, Backup_Folder);
+		Full_Backup_Path = Backup_Folder + "/" + Backup_Name + "/";
+		LOGINFO("Killing pid: %d\n", tar_fork_pid);
+		kill(tar_fork_pid, SIGUSR2);
+		while (kill(tar_fork_pid, 0) == 0) {
+			usleep(1000);
+		}
+		LOGINFO("Backup_Run stopped and returning false, backup cancelled.\n");
+		LOGINFO("Removing directory %s\n", Full_Backup_Path.c_str());
+		TWFunc::removeDir(Full_Backup_Path, false);
+		tar_fork_pid = 0;
+	}
+
+	return 0;
+}
+
+int TWPartitionManager::Run_Backup(void) {
+	int check, do_md5, partition_count = 0, disable_free_space_check = 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;
+	stop_backup.set_value(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;
+	}
+
+	DataManager::GetValue("tw_disable_free_space", disable_free_space_check);
+	if (!disable_free_space_check) {
+		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()) {
+		if (stop_backup.get_value() != 0)
+			return -1;
+		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);
+	tw_set_default_metadata(backup_log.c_str());
+	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) {
+				if (restore_part->Mount_Read_Only) {
+					LOGERR("Cannot restore %s -- mounted read only.\n", restore_part->Backup_Display_Name.c_str());
+					return false;
+				}
+				if (check_md5 > 0 && !restore_part->Check_MD5(Restore_Name))
+					return false;
+				partition_count++;
+				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) {
+#ifdef TW_OEM_BUILD
+			if ((*iter)->Mount_Point == "/data") {
+				if (!(*iter)->Wipe_Encryption())
+					ret = false;
+			} else {
+#endif
+				if (!(*iter)->Wipe())
+					ret = false;
+#ifdef TW_OEM_BUILD
+			}
+#endif
+		} 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");
+		Remove_MTP_Storage(dat->MTP_Storage_ID);
+		TWFunc::removeDir("/data/media", false);
+		dat->Recreate_Media_Folder();
+		Add_MTP_Storage(dat->MTP_Storage_ID);
+		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;
+}
+
+int TWPartitionManager::Resize_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)->Resize();
+			found = true;
+		} else if ((*iter)->Is_SubPartition && (*iter)->SubPartition_Of == Local_Path) {
+			(*iter)->Resize();
+		}
+	}
+	if (found) {
+		return ret;
+	} else if (Display_Error) {
+		LOGERR("Resize: Unable to find partition for path '%s'\n", Local_Path.c_str());
+	} else {
+		LOGINFO("Resize: 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;
+	std::vector<TWPartition*>::iterator iter;
+
+	property_set("ro.crypto.state", "encrypted");
+
+	// Mount any partitions that need to be mounted for decrypt
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Mount_To_Decrypt) {
+			(*iter)->Mount(true);
+		}
+	}
+
+	strcpy(cPassword, Password.c_str());
+	int pwret = cryptfs_check_passwd(cPassword);
+
+	// Unmount any partitions that were needed for decrypt
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Mount_To_Decrypt) {
+			(*iter)->UnMount(false);
+		}
+	}
+
+	if (pwret != 0) {
+		LOGERR("Failed to decrypt data.\n");
+		return -1;
+	}
+
+	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);
+
+			// 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");
+				DataManager::SetValue("tw_settings_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);
+#ifdef HAVE_SELINUX
+	if (result == 0 && DataManager::GetIntValue("tw_fixperms_restorecon") == 1)
+		result = perms.fixContexts();
+#endif
+	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;
+
+	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); // Must disable MTP for USB Storage
+	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;
+		}
+	}
+	property_set("sys.storage.ums_enabled", "1");
+	return true;
+error_handle:
+	if (mtp_was_enabled)
+		if (!Enable_MTP())
+			Disable_MTP();
+	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 if (ListType == "flashimg") {
+		for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+			if ((*iter)->Can_Flash_Img && (*iter)->Is_Present) {
+				struct PartitionList part;
+				part.Display_Name = (*iter)->Backup_Display_Name;
+				part.Mount_Point = (*iter)->Backup_Path;
+				part.selected = 0;
+				Partition_List->push_back(part);
+			}
+		}
+	} 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");
+
+	int mtppipe[2];
+
+	if (pipe(mtppipe) < 0) {
+		LOGERR("Error creating MTP pipe\n");
+		return false;
+	}
+
+	char old_value[PROPERTY_VALUE_MAX];
+	property_get("sys.usb.config", old_value, "error");
+	if (strcmp(old_value, "error") != 0 && strcmp(old_value, "mtp,adb") != 0) {
+		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.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");
+	}
+	/* 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"));
+	mtppid = mtp->forkserver(mtppipe);
+	if (mtppid) {
+		close(mtppipe[0]); // Host closes read side
+		mtp_write_fd = mtppipe[1];
+		DataManager::SetValue("tw_mtp_enabled", 1);
+		Add_All_MTP_Storage();
+		return true;
+	} else {
+		close(mtppipe[0]);
+		close(mtppipe[1]);
+		LOGERR("Failed to enable MTP\n");
+		return false;
+	}
+#else
+	LOGERR("MTP support not included\n");
+#endif
+	DataManager::SetValue("tw_mtp_enabled", 0);
+	return false;
+}
+
+void TWPartitionManager::Add_All_MTP_Storage(void) {
+#ifdef TW_HAS_MTP
+	std::vector<TWPartition*>::iterator iter;
+
+	if (!mtppid)
+		return; // MTP is not enabled
+
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->Is_Storage && (*iter)->Is_Present && (*iter)->Mount(false))
+			Add_Remove_MTP_Storage((*iter), MTP_MESSAGE_ADD_STORAGE);
+	}
+#else
+	return;
+#endif
+}
+
+bool TWPartitionManager::Disable_MTP(void) {
+	char old_value[PROPERTY_VALUE_MAX];
+	property_get("sys.usb.config", old_value, "error");
+	if (strcmp(old_value, "adb") != 0) {
+		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);
+		usleep(2000);
+	}
+#ifdef TW_HAS_MTP
+	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);
+		close(mtp_write_fd);
+		mtp_write_fd = -1;
+	}
+#endif
+	property_set("sys.usb.config", "adb");
+#ifdef TW_HAS_MTP
+	DataManager::SetValue("tw_mtp_enabled", 0);
+	return true;
+#endif
+	return false;
+}
+
+TWPartition* TWPartitionManager::Find_Partition_By_MTP_Storage_ID(unsigned int Storage_ID) {
+	std::vector<TWPartition*>::iterator iter;
+
+	for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+		if ((*iter)->MTP_Storage_ID == Storage_ID)
+			return (*iter);
+	}
+	return NULL;
+}
+
+bool TWPartitionManager::Add_Remove_MTP_Storage(TWPartition* Part, int message_type) {
+#ifdef TW_HAS_MTP
+	struct mtpmsg mtp_message;
+
+	if (!mtppid)
+		return false; // MTP is disabled
+
+	if (mtp_write_fd < 0) {
+		LOGINFO("MTP: mtp_write_fd is not set\n");
+		return false;
+	}
+
+	if (Part) {
+		if (Part->MTP_Storage_ID == 0)
+			return false;
+		if (message_type == MTP_MESSAGE_REMOVE_STORAGE) {
+			mtp_message.message_type = MTP_MESSAGE_REMOVE_STORAGE; // Remove
+			LOGINFO("sending message to remove %i\n", Part->MTP_Storage_ID);
+			mtp_message.storage_id = Part->MTP_Storage_ID;
+			if (write(mtp_write_fd, &mtp_message, sizeof(mtp_message)) <= 0) {
+				LOGINFO("error sending message to remove storage %i\n", Part->MTP_Storage_ID);
+				return false;
+			} else {
+				LOGINFO("Message sent, remove storage ID: %i\n", Part->MTP_Storage_ID);
+				return true;
+			}
+		} else if (message_type == MTP_MESSAGE_ADD_STORAGE && Part->Is_Mounted()) {
+			mtp_message.message_type = MTP_MESSAGE_ADD_STORAGE; // Add
+			mtp_message.storage_id = Part->MTP_Storage_ID;
+			mtp_message.path = Part->Storage_Path.c_str();
+			mtp_message.display = Part->Storage_Name.c_str();
+			mtp_message.maxFileSize = Part->Get_Max_FileSize();
+			LOGINFO("sending message to add %i '%s' '%s'\n", mtp_message.storage_id, mtp_message.path, mtp_message.display);
+			if (write(mtp_write_fd, &mtp_message, sizeof(mtp_message)) <= 0) {
+				LOGINFO("error sending message to add storage %i\n", Part->MTP_Storage_ID);
+				return false;
+			} else {
+				LOGINFO("Message sent, add storage ID: %i\n", Part->MTP_Storage_ID);
+				return true;
+			}
+		} else {
+			LOGERR("Unknown MTP message type: %i\n", message_type);
+		}
+	} else {
+		// This hopefully never happens as the error handling should
+		// occur in the calling function.
+		LOGINFO("TWPartitionManager::Add_Remove_MTP_Storage NULL partition given\n");
+	}
+	return true;
+#else
+	LOGERR("MTP support not included\n");
+	DataManager::SetValue("tw_mtp_enabled", 0);
+	return false;
+#endif
+}
+
+bool TWPartitionManager::Add_MTP_Storage(string Mount_Point) {
+#ifdef TW_HAS_MTP
+	TWPartition* Part = PartitionManager.Find_Partition_By_Path(Mount_Point);
+	if (Part) {
+		return PartitionManager.Add_Remove_MTP_Storage(Part, MTP_MESSAGE_ADD_STORAGE);
+	} else {
+		LOGINFO("TWFunc::Add_MTP_Storage unable to locate partition for '%s'\n", Mount_Point.c_str());
+	}
+#endif
+	return false;
+}
+
+bool TWPartitionManager::Add_MTP_Storage(unsigned int Storage_ID) {
+#ifdef TW_HAS_MTP
+	TWPartition* Part = PartitionManager.Find_Partition_By_MTP_Storage_ID(Storage_ID);
+	if (Part) {
+		return PartitionManager.Add_Remove_MTP_Storage(Part, MTP_MESSAGE_ADD_STORAGE);
+	} else {
+		LOGINFO("TWFunc::Add_MTP_Storage unable to locate partition for %i\n", Storage_ID);
+	}
+#endif
+	return false;
+}
+
+bool TWPartitionManager::Remove_MTP_Storage(string Mount_Point) {
+#ifdef TW_HAS_MTP
+	TWPartition* Part = PartitionManager.Find_Partition_By_Path(Mount_Point);
+	if (Part) {
+		return PartitionManager.Add_Remove_MTP_Storage(Part, MTP_MESSAGE_REMOVE_STORAGE);
+	} else {
+		LOGINFO("TWFunc::Remove_MTP_Storage unable to locate partition for '%s'\n", Mount_Point.c_str());
+	}
+#endif
+	return false;
+}
+
+bool TWPartitionManager::Remove_MTP_Storage(unsigned int Storage_ID) {
+#ifdef TW_HAS_MTP
+	TWPartition* Part = PartitionManager.Find_Partition_By_MTP_Storage_ID(Storage_ID);
+	if (Part) {
+		return PartitionManager.Add_Remove_MTP_Storage(Part, MTP_MESSAGE_REMOVE_STORAGE);
+	} else {
+		LOGINFO("TWFunc::Remove_MTP_Storage unable to locate partition for %i\n", Storage_ID);
+	}
+#endif
+	return false;
+}
+
+bool TWPartitionManager::Flash_Image(string Filename) {
+	int check, partition_count = 0;
+	TWPartition* flash_part = NULL;
+	string Flash_List, flash_path;
+	size_t start_pos = 0, end_pos = 0;
+
+	gui_print("\n[IMAGE FLASH STARTED]\n\n");
+	gui_print("Image to flash: '%s'\n", Filename.c_str());
+
+	if (!Mount_Current_Storage(true))
+		return false;
+
+	gui_print("Calculating restore details...\n");
+	DataManager::GetValue("tw_flash_partition", Flash_List);
+	if (!Flash_List.empty()) {
+		end_pos = Flash_List.find(";", start_pos);
+		while (end_pos != string::npos && start_pos < Flash_List.size()) {
+			flash_path = Flash_List.substr(start_pos, end_pos - start_pos);
+			flash_part = Find_Partition_By_Path(flash_path);
+			if (flash_part != NULL) {
+				partition_count++;
+				if (partition_count > 1) {
+					LOGERR("Too many partitions selected for flashing.\n");
+					return false;
+				}
+			} else {
+				LOGERR("Unable to locate '%s' partition for flashing (flash list).\n", flash_path.c_str());
+				return false;
+			}
+			start_pos = end_pos + 1;
+			end_pos = Flash_List.find(";", start_pos);
+		}
+	}
+
+	if (partition_count == 0) {
+		LOGERR("No partitions selected for flashing.\n");
+		return false;
+	}
+
+	DataManager::SetProgress(0.0);
+	if (flash_part) {
+		if (!flash_part->Flash_Image(Filename))
+			return false;
+	} else {
+		LOGERR("Invalid flash partition specified.\n");
+		return false;
+	}
+	gui_print_color("highlight", "[IMAGE FLASH COMPLETED]\n\n");
+	return true;
+}
diff --git a/partitions.hpp b/partitions.hpp
new file mode 100644
index 0000000..afcd474
--- /dev/null
+++ b/partitions.hpp
@@ -0,0 +1,261 @@
+/*
+	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 __TWRP_Partition_Manager
+#define __TWRP_Partition_Manager
+
+#include <vector>
+#include <string>
+#include "twrpDU.hpp"
+#include "tw_atomic.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
+	uint64_t Get_Max_FileSize();					  	  //get partition maxFileSie
+	bool Repair();                                                            // Repairs the current file system
+	bool Can_Resize();                                                        // Checks to see if we have everything needed to be able to resize the current file system
+	bool Resize();                                                            // Resizes the current file system
+	bool Backup(string backup_folder, const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &tar_fork_pid); // 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
+	bool Flash_Image(string Filename);                                        // Flashes an image to the partition
+	void Change_Mount_Read_Only(bool new_value);                              // Changes Mount_Read_Only to new_value
+	int Check_Lifetime_Writes();
+
+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
+	string Crypto_Key_Location;                                               // Location of the crypto key used for decrypting encrypted data partitions
+	unsigned int MTP_Storage_ID;
+
+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_NTFS();                                                         // Uses mkntfs 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, pid_t &tar_fork_pid); // 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_Image(string restore_folder, const unsigned long long *total_restore_size, unsigned long long *already_restored_size, string Restore_File_System); // Restore using dd for images
+	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
+	bool Flash_Image_DD(string Filename);                                     // Flashes an image to the partition using dd
+	bool Flash_Image_FI(string Filename);                                     // Flashes an image to the partition using flash_image for mtd nand
+
+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
+	bool Mount_To_Decrypt;                                                    // Mount this partition during decrypt (/vendor, /firmware, etc in case we need proprietary libs or firmware files)
+	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
+	unsigned long 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)
+	bool Can_Flash_Img;                                                       // Indicates if this partition can have images flashed to it via the GUI
+	bool Mount_Read_Only;                                                     // Only mount this partition as read-only
+
+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
+	int Resize_By_Path(string Path, bool Display_Error);                      // Resizes 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 Cancel_Backup();                                                      // Signals partition backup to cancel
+	void Clean_Backup_Folder(string Backup_Folder);				  // Clean Backup Folder on Error
+	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
+	void Add_All_MTP_Storage();                                               // Adds all storage objects for MTP
+	bool Disable_MTP();                                                       // Disables MTP
+	bool Add_MTP_Storage(string Mount_Point);                                 // Adds or removes an MTP Storage partition
+	bool Add_MTP_Storage(unsigned int Storage_ID);                            // Adds or removes an MTP Storage partition
+	bool Remove_MTP_Storage(string Mount_Point);                              // Adds or removes an MTP Storage partition
+	bool Remove_MTP_Storage(unsigned int Storage_ID);                         // Adds or removes an MTP Storage partition
+	bool Flash_Image(string Filename);                                        // Flashes an image to a selected partition from the partition list
+
+	TWAtomicInt stop_backup;
+
+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_Partition_By_MTP_Storage_ID(unsigned int Storage_ID);   // Returns a pointer to a partition based on MTP Storage ID
+	bool Add_Remove_MTP_Storage(TWPartition* Part, int message_type);   // Adds or removes an MTP Storage partition
+	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;
+	int mtp_write_fd;
+	pid_t tar_fork_pid;
+
+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..9fff2f9
--- /dev/null
+++ b/pigz/Android.mk
@@ -0,0 +1,40 @@
+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) $@
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION),16 17 18))
+ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+
+# We need this so that the installed files could be picked up based on the
+# local module name
+ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
+	$(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS)
+endif
+
+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..74db3fb
--- /dev/null
+++ b/pigz/yarn.c
@@ -0,0 +1,389 @@
+/* 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(), */
+#include <signal.h>     /* sigaction, SIGUSR1 */
+    /* 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 */
+#include <string.h>     /* memset */
+
+/* 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;
+
+void thread_exit_handler(int sig)
+{ 
+    printf("this signal is %d \n", sig);
+    pthread_exit(0);
+}
+
+/* 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;
+    struct sigaction actions;
+
+    memset(&actions, 0, sizeof(actions)); 
+    sigemptyset(&actions.sa_mask);
+    actions.sa_flags = 0; 
+    actions.sa_handler = thread_exit_handler;
+    ret = sigaction(SIGUSR1,&actions,NULL);
+
+    /* 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_kill(off_course->id, SIGUSR1)) != 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 100644
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..25c3147
--- /dev/null
+++ b/prebuilt/Android.mk
@@ -0,0 +1,430 @@
+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)/sh
+	RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so
+	ifneq (,$(filter $(PLATFORM_SDK_VERSION), 23))
+	    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/toybox
+	endif
+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)/resize2fs
+ifneq ($(TARGET_ARCH), x86_64)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/linker
+endif
+ifeq ($(TARGET_ARCH), x86_64)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/linker64
+endif
+ifeq ($(TARGET_ARCH), arm64)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/linker64
+endif
+#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)/libcrecovery.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libusbhost.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_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
+ifneq ($(wildcard external/e2fsprogs/lib/quota/Android.mk),)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_quota.so
+endif
+ifneq ($(wildcard external/e2fsprogs/lib/ext2fs/Android.mk),)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2fs.so
+endif
+ifneq ($(wildcard external/e2fsprogs/lib/blkid/Android.mk),)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_blkid.so
+endif
+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)/libutil-linux.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)/libfusesideload.so
+ifeq (,$(filter $(PLATFORM_SDK_VERSION), 23))
+    # These libraries are no longer present in M
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libstlport.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgccdemangle.so
+endif
+ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22))
+    # libraries from lollipop
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbacktrace.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libunwind.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libunwind-ptrace.so
+    # Dynamically loaded by lollipop libc and may prevent unmounting system if it is not present in sbin
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libnetd_client.so
+else
+    ifneq (,$(filter $(PLATFORM_SDK_VERSION), 23))
+        # Android M libraries
+        RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbacktrace.so
+        RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libunwind.so
+        RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbase.so
+        RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libc++.so
+        # Dynamically loaded by libc and may prevent unmounting system if it is not present in sbin
+        RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libnetd_client.so
+    else
+        # Not available in lollipop
+        RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcorkscrew.so
+    endif
+endif
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmincrypttwrp.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)/libcryptfslollipop.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libhardware.so
+    ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+        RELINK_SOURCE_FILES += $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/libcryptfs_hw.so
+    endif
+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)
+    ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22))
+        RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT_SBIN)/mkfs.f2fs
+    else
+        RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/mkfs.f2fs
+    endif
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/fsck.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
+ifneq ($(TW_RECOVERY_ADDITIONAL_RELINK_FILES),)
+    RELINK_SOURCE_FILES += $(TW_RECOVERY_ADDITIONAL_RELINK_FILES)
+endif
+ifneq ($(wildcard external/pcre/Android.mk),)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libpcre.so
+endif
+ifeq ($(TW_INCLUDE_NTFS_3G),true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/ntfs-3g
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/ntfsfix
+    RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/mkntfs
+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)
+
+#permissive.sh
+include $(CLEAR_VARS)
+LOCAL_MODULE := permissive.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)
+
+#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)$(TWHTCD_PATH)
+	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)$(TWHTCD_PATH)
+	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)$(TWHTCD_PATH)
+	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)$(TWHTCD_PATH)
+	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)$(TWHTCD_PATH)
+	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)$(TWHTCD_PATH)
+	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)$(TWHTCD_PATH)
+	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)$(TWHTCD_PATH)
+	LOCAL_SRC_FILES := $(LOCAL_MODULE)
+	include $(BUILD_PREBUILT)
+endif
+
+ifneq ($(TW_EXCLUDE_SUPERSU), true)
+	ifeq ($(TARGET_ARCH), arm)
+		#chattr.pie
+		include $(CLEAR_VARS)
+		LOCAL_MODULE := chattr.pie
+		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)
+
+		#libsupol.so
+		include $(CLEAR_VARS)
+		LOCAL_MODULE := libsupol.so
+		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)
+
+		#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)
+
+		#supolicy
+		include $(CLEAR_VARS)
+		LOCAL_MODULE := supolicy
+		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
+	ifeq ($(TARGET_ARCH), arm64)
+		#libsupol.so
+		include $(CLEAR_VARS)
+		LOCAL_MODULE := libsupol.soarm64
+		LOCAL_MODULE_STEM := libsupol.so
+		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)
+
+		#su binary
+		include $(CLEAR_VARS)
+		LOCAL_MODULE := suarm64
+		LOCAL_MODULE_STEM := 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)
+
+		#supolicy
+		include $(CLEAR_VARS)
+		LOCAL_MODULE := supolicyarm64
+		LOCAL_MODULE_STEM := supolicy
+		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
+
+	#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)
+
+	#install-supersu.sh
+	include $(CLEAR_VARS)
+	LOCAL_MODULE := install-supersu.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/chattr.pie b/prebuilt/chattr.pie
new file mode 100644
index 0000000..4739728
--- /dev/null
+++ b/prebuilt/chattr.pie
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 100644
index 0000000..f62cf0b
--- /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/install-supersu.sh b/prebuilt/install-supersu.sh
new file mode 100755
index 0000000..2bca4c3
--- /dev/null
+++ b/prebuilt/install-supersu.sh
@@ -0,0 +1,405 @@
+#!/sbin/sh
+#
+# SuperSU installer ZIP 
+# Copyright (c) 2012-2014 - Chainfire
+#
+# To install SuperSU properly, aside from cleaning old versions and
+# other superuser-type apps from the system, the following files need to
+# be installed:
+#
+# API   source                        target                              chmod   chcon                       required
+#
+# 7-19  common/Superuser.apk          /system/app/Superuser.apk           0644    u:object_r:system_file:s0   gui
+# 20+   common/Superuser.apk          /system/app/SuperSU/SuperSU.apk     0644    u:object_r:system_file:s0   gui
+#
+# 17+   common/install-recovery.sh    /system/etc/install-recovery.sh     0755    *1                          required
+# 17+                                 /system/bin/install-recovery.sh     (symlink to /system/etc/...)        required
+# *1: same as /system/bin/toolbox: u:object_r:system_file:s0 if API < 20, u:object_r:toolbox_exec:s0 if API >= 20
+#
+# 7+    ARCH/su                       /system/xbin/su                     *2      u:object_r:system_file:s0   required
+# 7+                                  /system/bin/.ext/.su                *2      u:object_r:system_file:s0   gui
+# 17+                                 /system/xbin/daemonsu               0755    u:object_r:system_file:s0   required
+# 17+                                 /system/xbin/sugote                 0755    u:object_r:zygote_exec:s0   required
+# *2: 06755 if API < 18, 0755 if API >= 18
+#
+# 19+   ARCH/supolicy                 /system/xbin/supolicy               0755    u:object_r:system_file:s0   required
+# 19+   ARCH/libsupol.so              /system/lib(64)/libsupol.so         0644    u:object_r:system_file:s0   required
+#
+# 17+   /system/bin/sh or mksh *3     /system/xbin/sugote-mksh            0755    u:object_r:system_file:s0   required
+# *3: which one (or both) are available depends on API
+#
+# 21+   /system/bin/app_process32 *4  /system/bin/app_process32_original  0755    u:object_r:zygote_exec:s0   required
+# 21+   /system/bin/app_process64 *4  /system/bin/app_process64_original  0755    u:object_r:zygote_exec:s0   required
+# 21+   /system/bin/app_processXX *4  /system/bin/app_process_init        0755    u:object_r:system_file:s0   required
+# 21+                                 /system/bin/app_process             (symlink to /system/xbin/daemonsu)  required
+# 21+                             *4  /system/bin/app_process32           (symlink to /system/xbin/daemonsu)  required
+# 21+                             *4  /system/bin/app_process64           (symlink to /system/xbin/daemonsu)  required
+# *4: Only do this for the relevant bits. On a 64 bits system, leave the 32 bits files alone, or dynamic linker errors
+#     will prevent the system from fully working in subtle ways. The bits of the su binary must also match!
+#
+# 17+   common/99SuperSUDaemon *5     /system/etc/init.d/99SuperSUDaemon  0755    u:object_r:system_file:s0   optional
+# *5: only place this file if /system/etc/init.d is present
+#
+# 17+   'echo 1 >' or 'touch' *6      /system/etc/.installed_su_daemon    0644    u:object_r:system_file:s0   optional
+# *6: the file just needs to exist or some recoveries will nag you. Even with it there, it may still happen.
+#
+# It may seem some files are installed multiple times needlessly, but
+# it only seems that way. Installing files differently or symlinking
+# instead of copying (unless specified) will lead to issues eventually.
+#
+# The following su binary versions are included in the full package. Each
+# should be installed only if the system has the same or newer API level 
+# as listed. The script may fall back to a different binary on older API
+# levels. supolicy are all ndk/pie/19+ for 32 bit, ndk/pie/20+ for 64 bit.
+#
+# binary        ARCH/path   build type      API
+#
+# arm-v5te      arm         aosp static     7+
+# x86           x86         aosp static     7+
+#
+# arm-v7a       armv7       ndk pie         17+
+# mips          mips        ndk pie         17+
+#
+# arm64-v8a     arm64       ndk pie         20+
+# mips64        mips64      ndk pie         20+
+# x86_64        x64         ndk pie         20+
+#
+# Note that if SELinux is set to enforcing, the daemonsu binary expects 
+# to be run at startup (usually from install-recovery.sh, 99SuperSUDaemon,
+# or app_process) from u:r:init:s0 or u:r:kernel:s0 contexts. Depending
+# on the current policies, it can also deal with u:r:init_shell:s0 and
+# u:r:toolbox:s0 contexts. Any other context will lead to issues eventually.
+#
+# After installation, run '/system/xbin/su --install', which may need to
+# perform some additional installation steps. Ideally, at one point,
+# a lot of this script will be moved there.
+#
+# The included chattr(.pie) binaries are used to remove ext2's immutable
+# flag on some files. This flag is no longer set by SuperSU's OTA
+# survival since API level 18, so there is no need for the 64 bit versions.
+# Note that chattr does not need to be installed to the system, it's just
+# used by this script, and not supported by the busybox used in older
+# recoveries.
+#
+# Non-static binaries are supported to be PIE (Position Independent 
+# Executable) from API level 16, and required from API level 20 (which will
+# refuse to execute non-static non-PIE). 
+#
+# The script performs serveral actions in various ways, sometimes
+# multiple times, due to different recoveries and firmwares behaving
+# differently, and it thus being required for the correct result.
+
+OUTFD=$2
+ZIP=$3
+
+SYSTEMLIB=/system/lib
+
+ui_print() {
+  echo -n -e "echo $1\n" > /proc/self/fd/$OUTFD
+  echo -n -e "echo\n" > /proc/self/fd/$OUTFD
+}
+
+ch_con() {
+  LD_LIBRARY_PATH=$SYSTEMLIB /system/toolbox chcon -h u:object_r:system_file:s0 $1
+  LD_LIBRARY_PATH=$SYSTEMLIB /system/bin/toolbox chcon -h u:object_r:system_file:s0 $1
+  chcon -h u:object_r:system_file:s0 $1
+  LD_LIBRARY_PATH=$SYSTEMLIB /system/toolbox chcon u:object_r:system_file:s0 $1
+  LD_LIBRARY_PATH=$SYSTEMLIB /system/bin/toolbox chcon u:object_r:system_file:s0 $1
+  chcon u:object_r:system_file:s0 $1
+}
+
+ch_con_ext() {
+  LD_LIBRARY_PATH=$SYSTEMLIB /system/toolbox chcon $2 $1
+  LD_LIBRARY_PATH=$SYSTEMLIB /system/bin/toolbox chcon $2 $1
+  chcon $2 $1
+}
+
+ln_con() {
+  LD_LIBRARY_PATH=$SYSTEMLIB /system/toolbox ln -s $1 $2
+  LD_LIBRARY_PATH=$SYSTEMLIB /system/bin/toolbox ln -s $1 $2
+  ln -s $1 $2
+  ch_con $2
+}
+
+set_perm() {
+  chown $1.$2 $4
+  chown $1:$2 $4
+  chmod $3 $4
+  ch_con $4
+  ch_con_ext $4 $5
+}
+
+cp_perm() {
+  rm $5
+  cat $4 > $5
+  set_perm $1 $2 $3 $5 $6
+}
+
+echo "*********************"
+echo "SuperSU installer ZIP"
+echo "*********************"
+
+echo "- Mounting /system, /data and rootfs"
+mount /system
+mount /data
+mount -o rw,remount /system
+mount -o rw,remount /system /system
+mount -o rw,remount /
+mount -o rw,remount / /
+
+cat /system/bin/toolbox > /system/toolbox
+chmod 0755 /system/toolbox
+ch_con /system/toolbox
+
+API=$(cat /system/build.prop | grep "ro.build.version.sdk=" | dd bs=1 skip=21 count=2)
+ABI=$(cat /system/build.prop /default.prop | grep -m 1 "ro.product.cpu.abi=" | dd bs=1 skip=19 count=3)
+ABILONG=$(cat /system/build.prop /default.prop | grep -m 1 "ro.product.cpu.abi=" | dd bs=1 skip=19)
+ABI2=$(cat /system/build.prop /default.prop | grep -m 1 "ro.product.cpu.abi2=" | dd bs=1 skip=20 count=3)
+SUMOD=06755
+SUGOTE=false
+SUPOLICY=false
+INSTALL_RECOVERY_CONTEXT=u:object_r:system_file:s0
+MKSH=/system/bin/mksh
+PIE=
+ARCH=arm
+APKFOLDER=false
+APKNAME=/system/app/Superuser.apk
+APPPROCESS=false
+APPPROCESS64=false
+if [ "$ABI" = "x86" ]; then ARCH=x86; fi;
+if [ "$ABI2" = "x86" ]; then ARCH=x86; fi;
+if [ "$API" -eq "$API" ]; then
+  if [ "$API" -ge "17" ]; then
+    SUGOTE=true
+    PIE=.pie
+    if [ "$ABILONG" = "armeabi-v7a" ]; then ARCH=armv7; fi;
+    if [ "$ABI" = "mip" ]; then ARCH=mips; fi;
+    if [ "$ABILONG" = "mips" ]; then ARCH=mips; fi;
+  fi
+  if [ "$API" -ge "18" ]; then
+    SUMOD=0755
+  fi
+  if [ "$API" -ge "20" ]; then
+    if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; SYSTEMLIB=/system/lib64; APPPROCESS64=true; fi;
+    if [ "$ABILONG" = "mips64" ]; then ARCH=mips64; SYSTEMLIB=/system/lib64; APPPROCESS64=true; fi;
+    if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; SYSTEMLIB=/system/lib64; APPPROCESS64=true; fi;
+    APKFOLDER=true
+    APKNAME=/system/app/SuperSU/SuperSU.apk
+  fi
+  if [ "$API" -ge "19" ]; then
+    SUPOLICY=true
+    if [ "$(LD_LIBRARY_PATH=$SYSTEMLIB /system/toolbox ls -lZ /system/bin/toolbox | grep toolbox_exec > /dev/null; echo $?)" -eq "0" ]; then 
+      INSTALL_RECOVERY_CONTEXT=u:object_r:toolbox_exec:s0
+    fi
+  fi
+  if [ "$API" -ge "21" ]; then
+    APPPROCESS=true
+  fi
+fi
+if [ ! -f $MKSH ]; then
+  MKSH=/system/bin/sh
+fi
+
+#echo "DBG [$API] [$ABI] [$ABI2] [$ABILONG] [$ARCH] [$MKSH]"
+
+# Don't extract in TWRP
+#echo "- Extracting files"
+#cd /tmp
+#mkdir supersu
+#cd supersu
+#unzip -o "$ZIP"
+
+BIN=/supersu
+COM=/supersu
+
+echo "- Disabling OTA survival"
+chmod 0755 /supersu/chattr$PIE
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/bin/su
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/xbin/su
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/bin/.ext/.su
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/xbin/daemonsu
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/xbin/sugote
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/xbin/sugote_mksh
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/xbin/supolicy
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/lib/libsupol.so
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/lib64/libsupol.so
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/etc/install-recovery.sh
+LD_LIBRARY_PATH=$SYSTEMLIB $BIN/chattr$PIE -i /system/bin/install-recovery.sh
+
+echo "- Removing old files"
+
+if [ -f "/system/bin/install-recovery.sh" ]; then
+  if [ ! -f "/system/bin/install-recovery_original.sh" ]; then
+    mv /system/bin/install-recovery.sh /system/bin/install-recovery_original.sh
+    ch_con /system/bin/install-recovery_original.sh
+  fi
+fi
+if [ -f "/system/etc/install-recovery.sh" ]; then
+  if [ ! -f "/system/etc/install-recovery_original.sh" ]; then
+    mv /system/etc/install-recovery.sh /system/etc/install-recovery_original.sh
+    ch_con /system/etc/install-recovery_original.sh
+  fi
+fi
+
+rm -f /system/bin/su
+rm -f /system/xbin/su
+rm -f /system/xbin/daemonsu
+rm -f /system/xbin/sugote
+rm -f /system/xbin/sugote-mksh
+rm -f /system/xbin/supolicy
+rm -f /system/lib/libsupol.so
+rm -f /system/lib64/libsupol.so
+rm -f /system/bin/.ext/.su
+rm -f /system/bin/install-recovery.sh
+rm -f /system/etc/install-recovery.sh
+rm -f /system/etc/init.d/99SuperSUDaemon
+rm -f /system/etc/.installed_su_daemon
+
+rm -f /system/app/Superuser.apk
+rm -f /system/app/Superuser.odex
+rm -rf /system/app/Superuser
+rm -f /system/app/SuperUser.apk
+rm -f /system/app/SuperUser.odex
+rm -rf /system/app/SuperUser
+rm -f /system/app/superuser.apk
+rm -f /system/app/superuser.odex
+rm -rf /system/app/superuser
+rm -f /system/app/Supersu.apk
+rm -f /system/app/Supersu.odex
+rm -rf /system/app/Supersu
+rm -f /system/app/SuperSU.apk
+rm -f /system/app/SuperSU.odex
+rm -rf /system/app/SuperSU
+rm -f /system/app/supersu.apk
+rm -f /system/app/supersu.odex
+rm -rf /system/app/supersu
+rm -f /system/app/VenomSuperUser.apk
+rm -f /system/app/VenomSuperUser.odex
+rm -rf /system/app/VenomSuperUser
+rm -f /data/dalvik-cache/*com.noshufou.android.su*
+rm -f /data/dalvik-cache/*/*com.noshufou.android.su*
+rm -f /data/dalvik-cache/*com.koushikdutta.superuser*
+rm -f /data/dalvik-cache/*/*com.koushikdutta.superuser*
+rm -f /data/dalvik-cache/*com.mgyun.shua.su*
+rm -f /data/dalvik-cache/*/*com.mgyun.shua.su*
+rm -f /data/dalvik-cache/*com.m0narx.su*
+rm -f /data/dalvik-cache/*/*com.m0narx.su*
+rm -f /data/dalvik-cache/*Superuser.apk*
+rm -f /data/dalvik-cache/*/*Superuser.apk*
+rm -f /data/dalvik-cache/*SuperUser.apk*
+rm -f /data/dalvik-cache/*/*SuperUser.apk*
+rm -f /data/dalvik-cache/*superuser.apk*
+rm -f /data/dalvik-cache/*/*superuser.apk*
+rm -f /data/dalvik-cache/*VenomSuperUser.apk*
+rm -f /data/dalvik-cache/*/*VenomSuperUser.apk*
+rm -f /data/dalvik-cache/*eu.chainfire.supersu*
+rm -f /data/dalvik-cache/*/*eu.chainfire.supersu*
+rm -f /data/dalvik-cache/*Supersu.apk*
+rm -f /data/dalvik-cache/*/*Supersu.apk*
+rm -f /data/dalvik-cache/*SuperSU.apk*
+rm -f /data/dalvik-cache/*/*SuperSU.apk*
+rm -f /data/dalvik-cache/*supersu.apk*
+rm -f /data/dalvik-cache/*/*supersu.apk*
+rm -f /data/dalvik-cache/*.oat
+rm -f /data/app/com.noshufou.android.su*
+rm -f /data/app/com.koushikdutta.superuser*
+rm -f /data/app/com.mgyun.shua.su*
+rm -f /data/app/com.m0narx.su*
+rm -f /data/app/eu.chainfire.supersu-*
+rm -f /data/app/eu.chainfire.supersu.apk
+
+echo "- Creating space"
+if ($APKFOLDER); then
+  cp /system/app/Maps/Maps.apk /Maps.apk
+  cp /system/app/GMS_Maps/GMS_Maps.apk /GMS_Maps.apk
+  cp /system/app/YouTube/YouTube.apk /YouTube.apk
+  rm /system/app/Maps/Maps.apk
+  rm /system/app/GMS_Maps/GMS_Maps.apk
+  rm /system/app/YouTube/YouTube.apk
+else
+  cp /system/app/Maps.apk /Maps.apk
+  cp /system/app/GMS_Maps.apk /GMS_Maps.apk
+  cp /system/app/YouTube.apk /YouTube.apk
+  rm /system/app/Maps.apk
+  rm /system/app/GMS_Maps.apk
+  rm /system/app/YouTube.apk
+fi
+
+echo "- Placing files"
+
+mkdir /system/bin/.ext
+set_perm 0 0 0777 /system/bin/.ext
+cp_perm 0 0 $SUMOD $BIN/su /system/bin/.ext/.su
+cp_perm 0 0 $SUMOD $BIN/su /system/xbin/su
+cp_perm 0 0 0755 $BIN/su /system/xbin/daemonsu
+if ($SUGOTE); then
+  cp_perm 0 0 0755 $BIN/su /system/xbin/sugote u:object_r:zygote_exec:s0
+  cp_perm 0 0 0755 $MKSH /system/xbin/sugote-mksh
+fi
+if ($SUPOLICY); then
+  cp_perm 0 0 0755 $BIN/supolicy /system/xbin/supolicy
+  cp_perm 0 0 0644 $BIN/libsupol.so $SYSTEMLIB/libsupol.so
+fi
+if ($APKFOLDER); then
+  mkdir /system/app/SuperSU
+  set_perm 0 0 0755 /system/app/SuperSU
+fi
+cp_perm 0 0 0644 $COM/Superuser.apk $APKNAME
+cp_perm 0 0 0755 $COM/install-recovery.sh /system/etc/install-recovery.sh
+ln_con /system/etc/install-recovery.sh /system/bin/install-recovery.sh
+if ($APPPROCESS); then
+  rm /system/bin/app_process
+  ln_con /system/xbin/daemonsu /system/bin/app_process
+  if ($APPPROCESS64); then
+    if [ ! -f "/system/bin/app_process64_original" ]; then
+      mv /system/bin/app_process64 /system/bin/app_process64_original
+    else
+      rm /system/bin/app_process64
+    fi
+    ln_con /system/xbin/daemonsu /system/bin/app_process64
+    if [ ! -f "/system/bin/app_process_init" ]; then
+      cp_perm 0 2000 0755 /system/bin/app_process64_original /system/bin/app_process_init
+    fi
+  else
+    if [ ! -f "/system/bin/app_process32_original" ]; then
+      mv /system/bin/app_process32 /system/bin/app_process32_original
+    else
+      rm /system/bin/app_process32
+    fi
+    ln_con /system/xbin/daemonsu /system/bin/app_process32
+    if [ ! -f "/system/bin/app_process_init" ]; then
+      cp_perm 0 2000 0755 /system/bin/app_process32_original /system/bin/app_process_init
+    fi
+  fi
+fi
+cp_perm 0 0 0744 $COM/99SuperSUDaemon /system/etc/init.d/99SuperSUDaemon
+echo 1 > /system/etc/.installed_su_daemon
+set_perm 0 0 0644 /system/etc/.installed_su_daemon
+
+echo "- Restoring files"
+if ($APKFOLDER); then
+  cp_perm 0 0 0644 /Maps.apk /system/app/Maps/Maps.apk
+  cp_perm 0 0 0644 /GMS_Maps.apk /system/app/GMS_Maps/GMS_Maps.apk
+  cp_perm 0 0 0644 /YouTube.apk /system/app/YouTube/YouTube.apk
+  rm /Maps.apk
+  rm /GMS_Maps.apk
+  rm /YouTube.apk
+else
+  cp_perm 0 0 0644 /Maps.apk /system/app/Maps.apk
+  cp_perm 0 0 0644 /GMS_Maps.apk /system/app/GMS_Maps.apk
+  cp_perm 0 0 0644 /YouTube.apk /system/app/YouTube.apk
+  rm /Maps.apk
+  rm /GMS_Maps.apk
+  rm /YouTube.apk
+fi
+
+echo "- Post-installation script"
+rm /system/toolbox
+LD_LIBRARY_PATH=$SYSTEMLIB /system/xbin/su --install
+
+echo "- Unmounting /system and /data"
+umount /system
+umount /data
+
+echo "- Done !"
+exit 0
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/libsupol.so b/prebuilt/libsupol.so
new file mode 100644
index 0000000..8a81a15
--- /dev/null
+++ b/prebuilt/libsupol.so
Binary files differ
diff --git a/prebuilt/libsupol.soarm64 b/prebuilt/libsupol.soarm64
new file mode 100644
index 0000000..5030806
--- /dev/null
+++ b/prebuilt/libsupol.soarm64
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/permissive.sh b/prebuilt/permissive.sh
new file mode 100755
index 0000000..f1046f9
--- /dev/null
+++ b/prebuilt/permissive.sh
@@ -0,0 +1,8 @@
+#!/sbin/sh
+
+# We use this shell script because the script will follow symlinks and
+# different trees will use different binaries to supply the setenforce
+# tool. Before M we use toolbox, M and beyond will use toybox. The init
+# binary and init.rc will not follow symlinks.
+
+setenforce 0
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..2dd5614
--- /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/linker64\x0|/sbin/linker64\x0\x0\x0\x0\x0\x0\x0|g" $src | sed "s|/system/bin/linker\x0|/sbin/linker\x0\x0\x0\x0\x0\x0\x0|g" | sed "s|/system/bin/sh\x0|/sbin/sh\x0\x0\x0\x0\x0\x0\x0|g" | sed "s|/system/lib64\x0|/sbin\x0\x0\x0\x0\x0\x0\x0\x0\x0|g" | sed "s|/system/lib\x0|/sbin\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 100644
index 0000000..38679b4
--- /dev/null
+++ b/prebuilt/su
Binary files differ
diff --git a/prebuilt/suarm64 b/prebuilt/suarm64
new file mode 100644
index 0000000..a2b4dc8
--- /dev/null
+++ b/prebuilt/suarm64
Binary files differ
diff --git a/prebuilt/supolicy b/prebuilt/supolicy
new file mode 100644
index 0000000..55db212
--- /dev/null
+++ b/prebuilt/supolicy
Binary files differ
diff --git a/prebuilt/supolicyarm64 b/prebuilt/supolicyarm64
new file mode 100644
index 0000000..ea8ac20
--- /dev/null
+++ b/prebuilt/supolicyarm64
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_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-hdpi/images/dummyfile b/res-hdpi/images/dummyfile
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/res-hdpi/images/dummyfile
diff --git a/res-hdpi/images/erasing_text.png b/res-hdpi/images/erasing_text.png
deleted file mode 100644
index 774244c..0000000
--- a/res-hdpi/images/erasing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png
deleted file mode 100644
index 64a57ec..0000000
--- a/res-hdpi/images/error_text.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/icon_error.png b/res-hdpi/images/icon_error.png
deleted file mode 100644
index cb3d1ab..0000000
--- a/res-hdpi/images/icon_error.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/icon_installing.png b/res-hdpi/images/icon_installing.png
deleted file mode 100644
index c2c0201..0000000
--- a/res-hdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png
deleted file mode 100644
index 33b54f1..0000000
--- a/res-hdpi/images/installing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png
deleted file mode 100644
index 9927ecb..0000000
--- a/res-hdpi/images/no_command_text.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/progress_empty.png b/res-hdpi/images/progress_empty.png
deleted file mode 100644
index 7258183..0000000
--- a/res-hdpi/images/progress_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/progress_fill.png b/res-hdpi/images/progress_fill.png
deleted file mode 100644
index becf87b..0000000
--- a/res-hdpi/images/progress_fill.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/stage_empty.png b/res-hdpi/images/stage_empty.png
deleted file mode 100644
index 251ec19..0000000
--- a/res-hdpi/images/stage_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-hdpi/images/stage_fill.png b/res-hdpi/images/stage_fill.png
deleted file mode 100644
index 1ab79e8..0000000
--- a/res-hdpi/images/stage_fill.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/dummyfile b/res-mdpi/images/dummyfile
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/res-mdpi/images/dummyfile
diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png
deleted file mode 100644
index fd86c3f..0000000
--- a/res-mdpi/images/erasing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png
deleted file mode 100644
index f1b44c9..0000000
--- a/res-mdpi/images/error_text.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/icon_error.png b/res-mdpi/images/icon_error.png
deleted file mode 100644
index cb3d1ab..0000000
--- a/res-mdpi/images/icon_error.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/icon_installing.png b/res-mdpi/images/icon_installing.png
deleted file mode 100644
index c2c0201..0000000
--- a/res-mdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png
deleted file mode 100644
index 064b2a3..0000000
--- a/res-mdpi/images/installing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png
deleted file mode 100644
index 1f29b89..0000000
--- a/res-mdpi/images/no_command_text.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/progress_empty.png b/res-mdpi/images/progress_empty.png
deleted file mode 100644
index 7258183..0000000
--- a/res-mdpi/images/progress_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/progress_fill.png b/res-mdpi/images/progress_fill.png
deleted file mode 100644
index becf87b..0000000
--- a/res-mdpi/images/progress_fill.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/stage_empty.png b/res-mdpi/images/stage_empty.png
deleted file mode 100644
index 251ec19..0000000
--- a/res-mdpi/images/stage_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-mdpi/images/stage_fill.png b/res-mdpi/images/stage_fill.png
deleted file mode 100644
index 1ab79e8..0000000
--- a/res-mdpi/images/stage_fill.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/dummyfile b/res-xhdpi/images/dummyfile
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/res-xhdpi/images/dummyfile
diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png
deleted file mode 100644
index f88e0e6..0000000
--- a/res-xhdpi/images/erasing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png
deleted file mode 100644
index c3a4cc6..0000000
--- a/res-xhdpi/images/error_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/icon_error.png b/res-xhdpi/images/icon_error.png
deleted file mode 100644
index cb3d1ab..0000000
--- a/res-xhdpi/images/icon_error.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/icon_installing.png b/res-xhdpi/images/icon_installing.png
deleted file mode 100644
index c2c0201..0000000
--- a/res-xhdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png
deleted file mode 100644
index a4dacd0..0000000
--- a/res-xhdpi/images/installing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png
deleted file mode 100644
index eb34e94..0000000
--- a/res-xhdpi/images/no_command_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/progress_empty.png b/res-xhdpi/images/progress_empty.png
deleted file mode 100644
index 7258183..0000000
--- a/res-xhdpi/images/progress_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/progress_fill.png b/res-xhdpi/images/progress_fill.png
deleted file mode 100644
index becf87b..0000000
--- a/res-xhdpi/images/progress_fill.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/stage_empty.png b/res-xhdpi/images/stage_empty.png
deleted file mode 100644
index 251ec19..0000000
--- a/res-xhdpi/images/stage_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-xhdpi/images/stage_fill.png b/res-xhdpi/images/stage_fill.png
deleted file mode 100644
index 1ab79e8..0000000
--- a/res-xhdpi/images/stage_fill.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/dummyfile b/res-xxhdpi/images/dummyfile
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/res-xxhdpi/images/dummyfile
diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png
deleted file mode 100644
index c87fd52..0000000
--- a/res-xxhdpi/images/erasing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png
deleted file mode 100644
index 486e951..0000000
--- a/res-xxhdpi/images/error_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/icon_error.png b/res-xxhdpi/images/icon_error.png
deleted file mode 100644
index cb3d1ab..0000000
--- a/res-xxhdpi/images/icon_error.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/icon_installing.png b/res-xxhdpi/images/icon_installing.png
deleted file mode 100644
index c2c0201..0000000
--- a/res-xxhdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png
deleted file mode 100644
index ef6e8f3..0000000
--- a/res-xxhdpi/images/installing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png
deleted file mode 100644
index cc98bb1..0000000
--- a/res-xxhdpi/images/no_command_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/progress_empty.png b/res-xxhdpi/images/progress_empty.png
deleted file mode 100644
index 7258183..0000000
--- a/res-xxhdpi/images/progress_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/progress_fill.png b/res-xxhdpi/images/progress_fill.png
deleted file mode 100644
index becf87b..0000000
--- a/res-xxhdpi/images/progress_fill.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/stage_empty.png b/res-xxhdpi/images/stage_empty.png
deleted file mode 100644
index 251ec19..0000000
--- a/res-xxhdpi/images/stage_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-xxhdpi/images/stage_fill.png b/res-xxhdpi/images/stage_fill.png
deleted file mode 100644
index 1ab79e8..0000000
--- a/res-xxhdpi/images/stage_fill.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/dummyfile b/res-xxxhdpi/images/dummyfile
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/res-xxxhdpi/images/dummyfile
diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png
deleted file mode 100644
index 612e7a3..0000000
--- a/res-xxxhdpi/images/erasing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png
deleted file mode 100644
index 50d2fad..0000000
--- a/res-xxxhdpi/images/error_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/icon_error.png b/res-xxxhdpi/images/icon_error.png
deleted file mode 100644
index cb3d1ab..0000000
--- a/res-xxxhdpi/images/icon_error.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/icon_installing.png b/res-xxxhdpi/images/icon_installing.png
deleted file mode 100644
index c2c0201..0000000
--- a/res-xxxhdpi/images/icon_installing.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png
deleted file mode 100644
index 9bd093b..0000000
--- a/res-xxxhdpi/images/installing_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png
deleted file mode 100644
index 6354e6a..0000000
--- a/res-xxxhdpi/images/no_command_text.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/progress_empty.png b/res-xxxhdpi/images/progress_empty.png
deleted file mode 100644
index 7258183..0000000
--- a/res-xxxhdpi/images/progress_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/progress_fill.png b/res-xxxhdpi/images/progress_fill.png
deleted file mode 100644
index becf87b..0000000
--- a/res-xxxhdpi/images/progress_fill.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/stage_empty.png b/res-xxxhdpi/images/stage_empty.png
deleted file mode 100644
index 251ec19..0000000
--- a/res-xxxhdpi/images/stage_empty.png
+++ /dev/null
Binary files differ
diff --git a/res-xxxhdpi/images/stage_fill.png b/res-xxxhdpi/images/stage_fill.png
deleted file mode 100644
index 1ab79e8..0000000
--- a/res-xxxhdpi/images/stage_fill.png
+++ /dev/null
Binary files differ
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 2bd457e..167499e 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"
@@ -73,6 +75,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);
@@ -126,6 +132,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);
@@ -168,6 +178,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 ff95915..e699538 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -37,6 +37,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;
@@ -303,6 +309,7 @@
 
 // Updates only the progress bar, if possible, otherwise redraws the screen.
 // Should only be called with updateMutex locked.
+
 void ScreenRecoveryUI::update_progress_locked() {
     if (show_text || !pagesIdentical) {
         draw_screen_locked();    // Must redraw the whole screen
@@ -513,6 +520,9 @@
     vsnprintf(buf, 256, fmt, ap);
     va_end(ap);
 
+	gui_print("%s", buf);
+	return;
+
     fputs(buf, stdout);
 
     pthread_mutex_lock(&updateMutex);
diff --git a/set_metadata.c b/set_metadata.c
new file mode 100644
index 0000000..2e1d769
--- /dev/null
+++ b/set_metadata.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Team Win Recovery 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.
+ */
+
+/*
+ * The purpose of these functions is to try to get and set the proper
+ * file permissions, SELinux contexts, owner, and group so that these
+ * files are accessible when we boot up to normal Android via MTP and to
+ * file manager apps. During early boot we try to read the contexts and
+ * owner / group info from /data/media or from /data/media/0 and store
+ * them in static variables. From there, we'll try to set the same
+ * contexts, owner, and group information on most files we create during
+ * operations like backups, copying the log, and MTP operations.
+ */
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "selinux/selinux.h"
+
+static security_context_t selinux_context;
+struct stat s;
+static int has_stat = 0;
+
+int tw_get_context(const char* filename) {
+	if (lgetfilecon(filename, &selinux_context) >= 0) {
+		printf("tw_get_context got selinux context: %s\n", selinux_context);
+		return 0;
+	} else {
+		printf("tw_get_context failed to get selinux context\n");
+		selinux_context = NULL;
+	}
+	return -1;
+}
+
+int tw_get_stat(const char* filename) {
+	if (lstat(filename, &s) == 0) {
+		has_stat = 1;
+		return 0;
+	}
+	printf("tw_get_stat failed to lstat '%s'\n", filename);
+	return -1;
+}
+
+int tw_get_default_metadata(const char* filename) {
+	if (tw_get_context(filename) == 0 && tw_get_stat(filename) == 0)
+		return 0;
+	return -1;
+}
+
+// Most of this logging is disabled to prevent log spam if we are trying
+// to set contexts and permissions on file systems that do not support
+// these types of things (e.g. vfat / FAT / FAT32).
+int tw_set_default_metadata(const char* filename) {
+	int ret = 0;
+	struct stat st;
+
+	if (selinux_context == NULL) {
+		//printf("selinux_context was null, '%s'\n", filename);
+		ret = -1;
+	} else if (lsetfilecon(filename, selinux_context) < 0) {
+		//printf("Failed to set default contexts on '%s'.\n", filename);
+		ret = -1;
+	}
+
+	if (lstat(filename, &st) == 0 && st.st_mode & S_IFREG && chmod(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) < 0) {
+		//printf("Failed to chmod '%s'\n", filename);
+		ret = -1;
+	}
+
+	if (has_stat && chown(filename, s.st_uid, s.st_gid) < 0) {
+		//printf("Failed to lchown '%s'.\n", filename);
+		ret = -1;
+	}
+	//printf("Done trying to set defaults on '%s'\n");
+	return ret;
+}
diff --git a/set_metadata.h b/set_metadata.h
new file mode 100644
index 0000000..835c31c
--- /dev/null
+++ b/set_metadata.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 The Team Win Recovery 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.
+ */
+
+/*
+ * The purpose of these functions is to try to get and set the proper
+ * file permissions, SELinux contexts, owner, and group so that these
+ * files are accessible when we boot up to normal Android via MTP and to
+ * file manager apps. During early boot we try to read the contexts and
+ * owner / group info from /data/media or from /data/media/0 and store
+ * them in static variables. From there, we'll try to set the same
+ * contexts, owner, and group information on most files we create during
+ * operations like backups, copying the log, and MTP operations.
+ */
+
+#ifndef _RECOVERY_SET_CONTEXTS_H
+#define _RECOVERY_SET_CONTEXTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/stat.h>
+#include "selinux/selinux.h"
+
+int tw_get_default_metadata(const char* filename);
+int tw_set_default_metadata(const char* filename);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_RECOVERY_SET_CONTEXTS_H
diff --git a/tarWrite.c b/tarWrite.c
new file mode 100644
index 0000000..98fa14c
--- /dev/null
+++ b/tarWrite.c
@@ -0,0 +1,96 @@
+/*
+	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 <stdlib.h>
+#include <string.h>
+#include <unistd.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) != (int)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;
+	if (buffer_status)
+		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..d54bfa4
--- /dev/null
+++ b/toolbox/Android.mk
@@ -0,0 +1,273 @@
+LOCAL_PATH := system/core/toolbox
+include $(CLEAR_VARS)
+
+OUR_TOOLS := \
+    start \
+    stop \
+    
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    OUR_TOOLS += \
+        getprop \
+        setprop
+endif
+
+# 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
+
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+        TOOLS_FOR_SELINUX += \
+            load_policy \
+            getenforce \
+            chcon \
+            restorecon \
+            runcon \
+            getsebool \
+            setsebool
+    endif
+
+    OUR_TOOLS += $(filter-out $(RECOVERY_BUSYBOX_TOOLS), $(TOOLS_FOR_SELINUX))
+
+    # toolbox setenforce is used during init, so it needs to be included here
+    # symlink is omitted at the very end if busybox already provides this
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+       OUR_TOOLS += setenforce
+    endif
+endif
+
+ifeq ($(TW_USE_TOOLBOX), true)
+    ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0)
+        # These are the only toolbox tools in M. The rest are now in toybox.
+        BSD_TOOLS := \
+            dd \
+            du \
+
+        OUR_TOOLS := \
+            df \
+            iftop \
+            ioctl \
+            ionice \
+            log \
+            ls \
+            lsof \
+            mount \
+            nandread \
+            newfs_msdos \
+            ps \
+            prlimit \
+            renice \
+            sendevent \
+            start \
+            stop \
+            top \
+            uptime \
+            watchprops \
+
+    else
+        ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22))
+            OUR_TOOLS += \
+                mknod \
+                nohup
+            BSD_TOOLS := \
+                cat \
+                chown \
+                cp \
+                dd \
+                du \
+                grep \
+                kill \
+                ln \
+                mv \
+                printenv \
+                rm \
+                rmdir \
+                sleep \
+                sync
+        else
+            OUR_TOOLS += \
+                cat \
+                chown \
+                dd \
+                du \
+                kill \
+                ln \
+                mv \
+                printenv \
+                rm \
+                rmdir \
+                setconsole \
+                sleep \
+                sync
+        endif
+
+        OUR_TOOLS += \
+            chmod \
+            clear \
+            cmp \
+            date \
+            df \
+            dmesg \
+            getevent \
+            hd \
+            id \
+            ifconfig \
+            iftop \
+            insmod \
+            ioctl \
+            ionice \
+            log \
+            lsmod \
+            lsof \
+            md5 \
+            mkdir \
+            mkswap \
+            mount \
+            nandread \
+            netstat \
+            newfs_msdos \
+            notify \
+            ps \
+            readlink \
+            renice \
+            rmmod \
+            route \
+            schedtop \
+            sendevent \
+            smd \
+            swapoff \
+            swapon \
+            top \
+            touch \
+            umount \
+            uptime \
+            vmstat \
+            watchprops \
+            wipe
+        ifneq ($(TWHAVE_SELINUX), true)
+            OUR_TOOLS += ls
+        endif
+    endif
+endif
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22))
+    ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+        OUR_TOOLS += r
+    endif
+endif
+
+LOCAL_SRC_FILES := \
+    toolbox.c \
+    $(patsubst %,%.c,$(OUR_TOOLS))
+
+ifneq ($(wildcard system/core/toolbox/dynarray.c),)
+    LOCAL_SRC_FILES += dynarray.c
+endif
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22))
+    LOCAL_SRC_FILES += \
+        pwcache.c \
+        upstream-netbsd/lib/libc/gen/getbsize.c \
+        upstream-netbsd/lib/libc/gen/humanize_number.c \
+        upstream-netbsd/lib/libc/stdlib/strsuftoll.c \
+        upstream-netbsd/lib/libc/string/swab.c \
+        upstream-netbsd/lib/libutil/raise_default_signal.c
+endif
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22 23))
+    LOCAL_CFLAGS += \
+        -std=gnu99 \
+        -Werror -Wno-unused-parameter \
+        -I$(LOCAL_PATH)/upstream-netbsd/include \
+        -include bsd-compatibility.h
+endif
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22))
+    LOCAL_C_INCLUDES += external/openssl/include
+else
+    LOCAL_C_INCLUDES += bionic/libc/bionic
+endif
+
+LOCAL_SHARED_LIBRARIES += libcutils
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22))
+    ifeq ($(TW_USE_TOOLBOX), true)
+        LOCAL_SHARED_LIBRARIES += libcrypto
+    endif
+else
+    LOCAL_SHARED_LIBRARIES += \
+        libc \
+        liblog
+endif
+
+ifeq ($(TWHAVE_SELINUX), true)
+    LOCAL_SHARED_LIBRARIES += libselinux
+endif
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22 23))
+    # libusbhost is only used by lsusb, and that isn't usually included in toolbox.
+    # The linker strips out all the unused library code in the normal case.
+    LOCAL_STATIC_LIBRARIES := libusbhost
+    LOCAL_WHOLE_STATIC_LIBRARIES := $(patsubst %,libtoolbox_%,$(BSD_TOOLS))
+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) below
+include $(BUILD_EXECUTABLE)
+
+$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22 23))
+    ALL_TOOLS := $(BSD_TOOLS) $(OUR_TOOLS)
+else
+    ALL_TOOLS := $(OUR_TOOLS)
+endif
+
+TOOLS_H := $(intermediates)/tools.h
+$(TOOLS_H): PRIVATE_TOOLS := $(ALL_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)
+
+ifeq ($(TWHAVE_SELINUX), true)
+    # toolbox setenforce is used during init in non-symlink form, so it was
+    # required to be included as part of the suite above. if busybox already
+    # provides setenforce, we can omit the toolbox symlink
+    TEMP_TOOLS := $(filter-out $(RECOVERY_BUSYBOX_TOOLS), $(ALL_TOOLS))
+    ALL_TOOLS := $(TEMP_TOOLS)
+endif
+
+# Make /sbin/toolbox launchers for each tool
+SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(ALL_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)
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION),16 17 18))
+    # Only needed if the build system lacks support for LOCAL_ADDITIONAL_DEPENDENCIES
+    ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+    ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
+        $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS)
+endif
+
+SYMLINKS :=
+ALL_TOOLS :=
+BSD_TOOLS :=
+OUR_TOOLS :=
+TEMP_TOOLS :=
+TOOLS_FOR_SELINUX :=
diff --git a/toybox/Android.mk b/toybox/Android.mk
new file mode 100644
index 0000000..a879151
--- /dev/null
+++ b/toybox/Android.mk
@@ -0,0 +1,371 @@
+#
+# Copyright (C) 2014 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.
+#
+
+ifneq ($(wildcard external/toybox/Android.mk),)
+
+ifeq ($(TW_USE_TOOLBOX), true)
+
+LOCAL_PATH := external/toybox
+
+#
+# To update:
+#
+
+#  git remote add toybox https://github.com/landley/toybox.git
+#  git fetch toybox
+#  git merge toybox/master
+#  mm -j32
+#  # (Make any necessary Android.mk changes and test the new toybox.)
+#  git push aosp HEAD:master  # Push directly, avoiding gerrit.
+#  git push aosp HEAD:refs/for/master  # Push to gerrit.
+#
+#  # Now commit any necessary Android.mk changes like normal:
+#  repo start post-sync .
+#  git commit -a
+
+
+#
+# To add a toy:
+#
+
+#  make menuconfig
+#  # (Select the toy you want to add.)
+#  make clean && make  # Regenerate the generated files.
+#  # Edit LOCAL_SRC_FILES below to add the toy.
+#  # If you just want to use it as "toybox x" rather than "x", you can stop now.
+#  # If you want this toy to have a symbolic link in /system/bin, add the toy to ALL_TOOLS.
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    lib/args.c \
+    lib/dirtree.c \
+    lib/getmountlist.c \
+    lib/help.c \
+    lib/interestingtimes.c \
+    lib/lib.c \
+    lib/llist.c \
+    lib/net.c \
+    lib/portability.c \
+    lib/xwrap.c \
+    main.c \
+    toys/android/getenforce.c \
+    toys/android/getprop.c \
+    toys/android/load_policy.c \
+    toys/android/restorecon.c \
+    toys/android/runcon.c \
+    toys/android/setenforce.c \
+    toys/android/setprop.c \
+    toys/lsb/dmesg.c \
+    toys/lsb/hostname.c \
+    toys/lsb/killall.c \
+    toys/lsb/md5sum.c \
+    toys/lsb/mknod.c \
+    toys/lsb/mktemp.c \
+    toys/lsb/mount.c \
+    toys/lsb/pidof.c \
+    toys/lsb/seq.c \
+    toys/lsb/sync.c \
+    toys/lsb/umount.c \
+    toys/other/acpi.c \
+    toys/other/base64.c \
+    toys/other/blkid.c \
+    toys/other/blockdev.c \
+    toys/other/bzcat.c \
+    toys/other/chcon.c \
+    toys/other/chroot.c \
+    toys/other/clear.c \
+    toys/other/dos2unix.c \
+    toys/other/fallocate.c \
+    toys/other/free.c \
+    toys/other/freeramdisk.c \
+    toys/other/fsfreeze.c \
+    toys/other/help.c \
+    toys/other/ifconfig.c \
+    toys/other/inotifyd.c \
+    toys/other/insmod.c \
+    toys/other/losetup.c \
+    toys/other/lsattr.c \
+    toys/other/lsmod.c \
+    toys/other/lsusb.c \
+    toys/other/makedevs.c \
+    toys/other/mkswap.c \
+    toys/other/modinfo.c \
+    toys/other/mountpoint.c \
+    toys/other/nbd_client.c \
+    toys/other/netcat.c \
+    toys/other/partprobe.c \
+    toys/other/pivot_root.c \
+    toys/other/pmap.c \
+    toys/other/printenv.c \
+    toys/other/pwdx.c \
+    toys/other/readlink.c \
+    toys/other/realpath.c \
+    toys/other/rev.c \
+    toys/other/rfkill.c \
+    toys/other/rmmod.c \
+    toys/other/setsid.c \
+    toys/other/stat.c \
+    toys/other/swapoff.c \
+    toys/other/swapon.c \
+    toys/other/switch_root.c \
+    toys/other/sysctl.c \
+    toys/other/tac.c \
+    toys/other/taskset.c \
+    toys/other/timeout.c \
+    toys/other/truncate.c \
+    toys/other/usleep.c \
+    toys/other/vconfig.c \
+    toys/other/vmstat.c \
+    toys/other/which.c \
+    toys/other/yes.c \
+    toys/pending/dd.c \
+    toys/pending/expr.c \
+    toys/pending/hwclock.c \
+    toys/pending/more.c \
+    toys/pending/pgrep.c \
+    toys/pending/netstat.c \
+    toys/pending/route.c \
+    toys/pending/tar.c \
+    toys/pending/top.c \
+    toys/pending/tr.c \
+    toys/pending/traceroute.c \
+    toys/posix/basename.c \
+    toys/posix/cal.c \
+    toys/posix/cat.c \
+    toys/posix/chgrp.c \
+    toys/posix/chmod.c \
+    toys/posix/cksum.c \
+    toys/posix/cmp.c \
+    toys/posix/comm.c \
+    toys/posix/cp.c \
+    toys/posix/cpio.c \
+    toys/posix/cut.c \
+    toys/posix/date.c \
+    toys/posix/df.c \
+    toys/posix/dirname.c \
+    toys/posix/du.c \
+    toys/posix/echo.c \
+    toys/posix/env.c \
+    toys/posix/expand.c \
+    toys/posix/false.c \
+    toys/posix/find.c \
+    toys/posix/grep.c \
+    toys/posix/head.c \
+    toys/posix/id.c \
+    toys/posix/kill.c \
+    toys/posix/ln.c \
+    toys/posix/ls.c \
+    toys/posix/mkdir.c \
+    toys/posix/mkfifo.c \
+    toys/posix/nice.c \
+    toys/posix/nl.c \
+    toys/posix/nohup.c \
+    toys/posix/od.c \
+    toys/posix/paste.c \
+    toys/posix/patch.c \
+    toys/posix/printf.c \
+    toys/posix/pwd.c \
+    toys/posix/renice.c \
+    toys/posix/rm.c \
+    toys/posix/rmdir.c \
+    toys/posix/sed.c \
+    toys/posix/sleep.c \
+    toys/posix/sort.c \
+    toys/posix/split.c \
+    toys/posix/strings.c \
+    toys/posix/tail.c \
+    toys/posix/tee.c \
+    toys/posix/time.c \
+    toys/posix/touch.c \
+    toys/posix/true.c \
+    toys/posix/tty.c \
+    toys/posix/uname.c \
+    toys/posix/uniq.c \
+    toys/posix/wc.c \
+    toys/posix/xargs.c \
+
+LOCAL_CFLAGS += \
+    -std=c99 \
+    -Os \
+    -Wno-char-subscripts \
+    -Wno-sign-compare \
+    -Wno-string-plus-int \
+    -Wno-uninitialized \
+    -Wno-unused-parameter \
+    -funsigned-char \
+    -ffunction-sections -fdata-sections \
+    -fno-asynchronous-unwind-tables \
+
+toybox_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
+LOCAL_CFLAGS += -DTOYBOX_VERSION='"$(toybox_version)"'
+
+LOCAL_CLANG := true
+
+LOCAL_SHARED_LIBRARIES := libcutils libselinux
+
+LOCAL_MODULE := toybox_recovery
+LOCAL_MODULE_STEM := toybox
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_MODULE_TAGS := optional
+
+# dupes: dd df du ls mount renice
+# useless?: freeramdisk fsfreeze install makedevs mkfifo nbd-client
+#           partprobe pivot_root pwdx rev rfkill switch_root tty vconfig
+# prefer BSD netcat instead?: nc netcat
+# prefer efs2progs instead?: blkid chattr lsattr
+
+ALL_TOOLS := \
+    acpi \
+    basename \
+    blkid \
+    blockdev \
+    bzcat \
+    cal \
+    cat \
+    chcon \
+    chgrp \
+    chmod \
+    chown \
+    chroot \
+    cksum \
+    clear \
+    comm \
+    cmp \
+    cp \
+    cpio \
+    cut \
+    date \
+    dirname \
+    dmesg \
+    dos2unix \
+    echo \
+    env \
+    expand \
+    expr \
+    fallocate \
+    false \
+    find \
+    free \
+    getenforce \
+    getprop \
+    groups \
+    head \
+    hostname \
+    hwclock \
+    id \
+    ifconfig \
+    inotifyd \
+    insmod \
+    kill \
+    load_policy \
+    ln \
+    logname \
+    losetup \
+    lsmod \
+    lsusb \
+    md5sum \
+    mkdir \
+    mknod \
+    mkswap \
+    mktemp \
+    modinfo \
+    more \
+    mountpoint \
+    mv \
+    netstat \
+    nice \
+    nl \
+    nohup \
+    od \
+    paste \
+    patch \
+    pgrep \
+    pidof \
+    pkill \
+    pmap \
+    printenv \
+    printf \
+    pwd \
+    readlink \
+    realpath \
+    restorecon \
+    rm \
+    rmdir \
+    rmmod \
+    route \
+    runcon \
+    sed \
+    seq \
+    setenforce \
+    setprop \
+    setsid \
+    sha1sum \
+    sleep \
+    sort \
+    split \
+    stat \
+    strings \
+    swapoff \
+    swapon \
+    sync \
+    sysctl \
+    tac \
+    tail \
+    tar \
+    taskset \
+    tee \
+    time \
+    timeout \
+    touch \
+    tr \
+    true \
+    truncate \
+    umount \
+    uname \
+    uniq \
+    unix2dos \
+    usleep \
+    vmstat \
+    wc \
+    which \
+    whoami \
+    xargs \
+    yes \
+
+# Install the symlinks.
+LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(ALL_TOOLS),ln -sf toybox $(TARGET_RECOVERY_ROOT_OUT)/sbin/$(t);)
+
+include $(BUILD_EXECUTABLE)
+
+# Make /sbin/toolbox launchers for each tool
+SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(ALL_TOOLS))
+$(SYMLINKS): TOYBOX_BINARY := $(LOCAL_MODULE_STEM)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+	@echo "Symlink: $@ -> $(TOYBOX_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(TOYBOX_BINARY) $@
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := toybox_symlinks
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(SYMLINKS)
+include $(BUILD_PHONY_PACKAGE)
+
+endif
+
+endif
diff --git a/tw_atomic.cpp b/tw_atomic.cpp
new file mode 100644
index 0000000..31cdd85
--- /dev/null
+++ b/tw_atomic.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Team Win Recovery 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 <pthread.h>
+#include <stdio.h>
+#include "tw_atomic.hpp"
+
+/*
+ * According to this documentation:
+ * https://developer.android.com/training/articles/smp.html
+ * it is recommended to use mutexes instead of atomics. This class
+ * provides us with a wrapper to make "atomic" variables easy to use.
+ */
+
+TWAtomicInt::TWAtomicInt(int initial_value /* = 0 */) {
+	if (pthread_mutex_init(&mutex_lock, NULL) != 0) {
+		// This should hopefully never happen. If it does, the
+		// operations will not be atomic, but we will allow things to
+		// continue anyway after logging the issue and just hope for
+		// the best.
+		printf("TWAtomic error initializing mutex.\n");
+		use_mutex = false;
+	} else {
+		use_mutex = true;
+	}
+	value = initial_value;
+}
+
+TWAtomicInt::~TWAtomicInt() {
+	if (use_mutex)
+		pthread_mutex_destroy(&mutex_lock);
+}
+
+void TWAtomicInt::set_value(int new_value) {
+	if (use_mutex) {
+		pthread_mutex_lock(&mutex_lock);
+		value = new_value;
+		pthread_mutex_unlock(&mutex_lock);
+	} else {
+		value = new_value;
+	}
+}
+
+int TWAtomicInt::get_value(void) {
+	int ret_val;
+
+	if (use_mutex) {
+		pthread_mutex_lock(&mutex_lock);
+		ret_val = value;
+		pthread_mutex_unlock(&mutex_lock);
+	} else {
+		ret_val = value;
+	}
+	return ret_val;
+}
diff --git a/tw_atomic.hpp b/tw_atomic.hpp
new file mode 100644
index 0000000..0f29f02
--- /dev/null
+++ b/tw_atomic.hpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Team Win Recovery 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 _TWATOMIC_HPP_HEADER
+#define _TWATOMIC_HPP_HEADER
+
+#include <pthread.h>
+
+class TWAtomicInt
+{
+public:
+	TWAtomicInt(int initial_value = 0);
+	~TWAtomicInt();
+	void set_value(int new_value);
+	int get_value();
+
+private:
+	int value;
+	bool use_mutex;
+	pthread_mutex_t mutex_lock;
+};
+
+#endif //_TWATOMIC_HPP_HEADER
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..fb7b667
--- /dev/null
+++ b/twinstall.cpp
@@ -0,0 +1,301 @@
+/*
+	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 "mtdutils/mounts.h"
+#include "mtdutils/mtdutils.h"
+#include "minzip/SysUtil.h"
+#include "minzip/Zip.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;
+
+	if (strcmp(path, "error") == 0) {
+		LOGERR("Failed to get adb sideload file: '%s'\n", path);
+		return INSTALL_CORRUPT;
+	}
+
+	gui_print("Installing '%s'...\n", path);
+	if (strlen(path) < 9 || strncmp(path, "/sideload", 9) != 0) {
+		gui_print("Checking for MD5 file...\n");
+		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);
+
+	MemMapping map;
+	if (sysMapFile(path, &map) != 0) {
+		LOGERR("Failed to sysMapFile '%s'\n", path);
+        return -1;
+    }
+
+	if (zip_verify) {
+		gui_print("Verifying zip signature...\n");
+		ret_val = verify_file(map.addr, map.length);
+		if (ret_val != VERIFY_SUCCESS) {
+			LOGERR("Zip signature verification failed: %i\n", ret_val);
+			sysReleaseMap(&map);
+			return -1;
+		} else {
+			gui_print("Zip signature verified successfully.\n");
+		}
+	}
+	ret_val = mzOpenZipArchive(map.addr, map.length, &Zip);
+	if (ret_val != 0) {
+		LOGERR("Zip file is corrupt!\n", path);
+		sysReleaseMap(&map);
+		return INSTALL_CORRUPT;
+	}
+	ret_val = Run_Update_Binary(path, &Zip, wipe_cache);
+	sysReleaseMap(&map);
+	return ret_val;
+}
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..6780e1d
--- /dev/null
+++ b/twrp-functions.cpp
@@ -0,0 +1,1046 @@
+/*
+	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 <algorithm>
+#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"
+#include "cutils/android_reboot.h"
+#include <sys/reboot.h>
+#endif // ndef BUILD_TWRPTAR_MAIN
+#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
+	#include "openaes/inc/oaes_lib.h"
+#endif
+
+extern "C" {
+	#include "libcrecovery/common.h"
+	#include "set_metadata.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)) {
+		if (fgets(buffer, 128, exec) != NULL) {
+			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 (WIFSIGNALED(*status)) {
+			LOGERR("%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 RC=%d\n", Child_Name.c_str(), WEXITSTATUS(*status)); // Success
+		} else {
+			LOGERR("%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)
+			LOGERR("%s no child process exist\n", Child_Name.c_str());
+		else {
+			LOGERR("%s Unexpected error %d\n", Child_Name.c_str(), errno);
+			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(TWHTCD_PATH "htcdumlocksys", "/system/bin/htcdumlock", 0755);
+	if (!Path_Exists("/system/bin/flash_image")) {
+		gui_print("Installing flash_image...\n");
+		copy_file(TWHTCD_PATH "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(TWHTCD_PATH "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(TWHTCD_PATH "libbmlutils.so", "/system/lib/libbmlutils.so", 0644);
+		copy_file(TWHTCD_PATH "libflashutils.so", "/system/lib/libflashutils.so", 0644);
+		copy_file(TWHTCD_PATH "libmmcutils.so", "/system/lib/libmmcutils.so", 0644);
+		copy_file(TWHTCD_PATH "libmtdutils.so", "/system/lib/libmtdutils.so", 0644);
+	}
+	gui_print("Installing HTC Dumlock app...\n");
+	mkdir("/data/app", 0777);
+	unlink("/data/app/com.teamwin.htcdumlock*");
+	copy_file(TWHTCD_PATH "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 (!TWFunc::Path_Exists(wholePath)) {
+			if (mkdir(wholePath.c_str(), 0777)) {
+				LOGERR("Unable to create folder: %s  (errno=%d)\n", wholePath.c_str(), errno);
+				return false;
+			} else {
+				tw_set_default_metadata(wholePath.c_str());
+			}
+		}
+
+		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 (set_bootloader_message(&boot) != 0)
+			LOGERR("Unable to set bootloader message.\n");
+	}
+
+	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();
+	Update_Log_File();
+
+	switch (command) {
+		case rb_current:
+		case rb_system:
+			Update_Intent_File("s");
+			sync();
+			check_and_run_script("/sbin/rebootsystem.sh", "reboot system");
+#ifdef ANDROID_RB_PROPERTY
+			return property_set(ANDROID_RB_PROPERTY, "reboot,");
+#elif defined(ANDROID_RB_RESTART)
+			return android_reboot(ANDROID_RB_RESTART, 0, 0);
+#else
+			return reboot(RB_AUTOBOOT);
+#endif
+		case rb_recovery:
+			check_and_run_script("/sbin/rebootrecovery.sh", "reboot recovery");
+#ifdef ANDROID_RB_PROPERTY
+			return property_set(ANDROID_RB_PROPERTY, "reboot,recovery");
+#else
+			return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, (void*) "recovery");
+#endif
+		case rb_bootloader:
+			check_and_run_script("/sbin/rebootbootloader.sh", "reboot bootloader");
+#ifdef ANDROID_RB_PROPERTY
+			return property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
+#else
+			return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, (void*) "bootloader");
+#endif
+		case rb_poweroff:
+			check_and_run_script("/sbin/poweroff.sh", "power off");
+#ifdef ANDROID_RB_PROPERTY
+			return property_set(ANDROID_RB_PROPERTY, "shutdown,");
+#elif defined(ANDROID_RB_POWEROFF)
+			return android_reboot(ANDROID_RB_POWEROFF, 0, 0);
+#else
+			return reboot(RB_POWER_OFF);
+#endif
+		case rb_download:
+			check_and_run_script("/sbin/rebootdownload.sh", "reboot download");
+#ifdef ANDROID_RB_PROPERTY
+			return property_set(ANDROID_RB_PROPERTY, "reboot,download");
+#else
+			return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, (void*) "download");
+#endif
+		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 dir: '%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);
+}
+
+bool TWFunc::Install_SuperSU(void) {
+	if (!PartitionManager.Mount_By_Path("/system", true))
+		return false;
+
+	check_and_run_script("/supersu/install-supersu.sh", "SuperSU");
+	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);
+	}
+	replace(Backup_Name.begin(), Backup_Name.end(), ' ', '_');
+	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");
+
+	// 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();
+}
+
+void TWFunc::Disable_Stock_Recovery_Replace(void) {
+	if (PartitionManager.Mount_By_Path("/system", false)) {
+		// Disable flashing of stock recovery
+		if (TWFunc::Path_Exists("/system/recovery-from-boot.p")) {
+			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");
+			sync();
+		}
+		PartitionManager.UnMount_By_Path("/system", false);
+	}
+}
+
+#endif // ndef BUILD_TWRPTAR_MAIN
diff --git a/twrp-functions.hpp b/twrp-functions.hpp
new file mode 100644
index 0000000..d49cd84
--- /dev/null
+++ b/twrp-functions.hpp
@@ -0,0 +1,99 @@
+/*
+	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 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
+	static void Disable_Stock_Recovery_Replace(); // Disable stock ROMs from replacing TWRP with stock recovery
+
+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..c6d98c0
--- /dev/null
+++ b/twrp.cpp
@@ -0,0 +1,404 @@
+/*
+		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 <signal.h>
+
+#include "cutils/properties.h"
+extern "C" {
+#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 "set_metadata.h"
+}
+#include "twcommon.h"
+#include "twrp-functions.hpp"
+#include "data.hpp"
+#include "partitions.hpp"
+#include "openrecoveryscript.hpp"
+#include "variables.h"
+#include "twrpDU.hpp"
+#ifdef TW_USE_NEW_MINADBD
+#include "adb.h"
+#else
+extern "C" {
+#include "minadbd.old/adb.h"
+}
+#endif
+
+#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);
+
+	signal(SIGPIPE, SIG_IGN);
+
+	// Handle ADB sideload
+	if (argc == 3 && strcmp(argv[1], "--adbd") == 0) {
+		property_set("ctl.stop", "adbd");
+#ifdef TW_USE_NEW_MINADBD
+		adb_main(0, DEFAULT_ADB_PORT);
+#else
+		adb_main(argv[2]);
+#endif
+		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)\n", 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_misc_device("emmc", misc->Actual_Block_Device.c_str());
+			} else if (misc->Current_File_System == "mtd") {
+				set_misc_device("mtd", 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", 1, 1) != 0) {
+			LOGERR("Failed to start decrypt GUI page.\n");
+		} else {
+			// Check for and load custom theme if present
+			gui_loadCustomResources();
+		}
+	} else if (datamedia) {
+		if (tw_get_default_metadata(DataManager::GetSettingsStoragePath().c_str()) != 0) {
+			LOGINFO("Failed to get default contexts and file mode for storage files.\n");
+		} else {
+			LOGINFO("Got default contexts and file mode for storage files.\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_mtp_enabled") == 1 && ((DataManager::GetIntValue(TW_IS_ENCRYPTED) != 0 && DataManager::GetIntValue(TW_IS_DECRYPTED) != 0) || DataManager::GetIntValue(TW_IS_ENCRYPTED) == 0)) {
+			LOGINFO("Enabling MTP during startup\n");
+			if (!PartitionManager.Enable_MTP())
+				PartitionManager.Disable_MTP();
+			else
+				gui_print("MTP Enabled\n");
+		} else {
+			PartitionManager.Disable_MTP();
+		}
+		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);
+		PartitionManager.Disable_MTP();
+	}
+#else
+	PartitionManager.Disable_MTP();
+#endif
+
+#ifndef TW_OEM_BUILD
+	// Check if system has never been changed
+	TWPartition* sys = PartitionManager.Find_Partition_By_Path("/system");
+	if (sys) {
+		if ((DataManager::GetIntValue("tw_mount_system_ro") == 0 && sys->Check_Lifetime_Writes() == 0) || DataManager::GetIntValue("tw_mount_system_ro") == 2) {
+			if (DataManager::GetIntValue("tw_never_show_system_ro_page") == 0) {
+				DataManager::SetValue("tw_back", "main");
+				if (gui_startPage("system_readonly", 1, 1) != 0) {
+					LOGERR("Failed to start system_readonly GUI page.\n");
+				}
+			} else if (DataManager::GetIntValue("tw_mount_system_ro") == 0) {
+				sys->Change_Mount_Read_Only(false);
+			}
+		} else if (DataManager::GetIntValue("tw_mount_system_ro") == 1) {
+			// Do nothing, user selected to leave system read only
+		} else {
+			sys->Change_Mount_Read_Only(false);
+		}
+	}
+#endif
+
+	// Launch the main GUI
+	gui_start();
+
+#ifndef TW_OEM_BUILD
+	// Disable flashing of stock recovery
+	TWFunc::Disable_Stock_Recovery_Replace();
+	// Check for su to see if the device is rooted or not
+	if (PartitionManager.Mount_By_Path("/system", false) && DataManager::GetIntValue("tw_mount_system_ro") == 0) {
+		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", 1, 1) != 0) {
+				LOGERR("Failed to start SuperSU install page.\n");
+			}
+		}
+		sync();
+		PartitionManager.UnMount_By_Path("/system", false);
+	}
+#endif
+
+	// 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);
+
+	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..8380d2c
--- /dev/null
+++ b/twrpDigest.cpp
@@ -0,0 +1,148 @@
+/*
+	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"
+#include "set_metadata.h"
+
+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);
+	tw_set_default_metadata(md5file.c_str());
+	LOGINFO("MD5 for %s: %s\n", md5fn.c_str(), md5string.c_str());
+	return 0;
+}
+
+int twrpDigest::read_md5digest(void) {
+	size_t 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..b6e4410
--- /dev/null
+++ b/twrpTar.cpp
@@ -0,0 +1,1409 @@
+
+/*
+	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 "set_metadata.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 <csignal>
+#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"
+extern "C" {
+	#include "set_metadata.h"
+}
+#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;
+	Archive_Current_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;
+}
+
+void twrpTar::Signal_Kill(int signum) {
+	_exit(255);
+}
+
+int twrpTar::createTarFork(const unsigned long long *overall_size, const unsigned long long *other_backups_size, pid_t &fork_pid) {
+	int status = 0;
+	pid_t rc_pid, tar_fork_pid;
+	int progress_pipe[2], ret;
+
+	file_count = 0;
+
+	if (pipe(progress_pipe) < 0) {
+		LOGERR("Error creating progress tracking pipe\n");
+		return -1;
+	}
+	if ((tar_fork_pid = fork()) == -1) {
+		LOGINFO("create tar failed to fork.\n");
+		close(progress_pipe[0]);
+		close(progress_pipe[1]);
+		return -1;
+	}
+
+	if (tar_fork_pid == 0) {
+		// Child process
+		// Child closes input side of progress pipe
+		signal(SIGUSR2, twrpTar::Signal_Kill);
+		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, total_size;
+			unsigned enc_thread_id = 1, regular_thread_id = 0, i, start_thread_id = 1, core_count = 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      : %u\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, %u threads for %u 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)(intptr_t)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;
+
+		fork_pid = tar_fork_pid;
+
+		// 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(tar_fork_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 rc_pid, tar_fork_pid;
+	int progress_pipe[2], ret;
+
+	if (pipe(progress_pipe) < 0) {
+		LOGERR("Error creating progress tracking pipe\n");
+		return -1;
+	}
+
+	tar_fork_pid = fork();
+	if (tar_fork_pid >= 0) // fork was successful
+	{
+		if (tar_fork_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;
+				unsigned thread_count = 0, i, start_thread_id = 1;
+				int 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)(intptr_t)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(tar_fork_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);
+	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;
+	}
+#ifndef BUILD_TWRPTAR_MAIN
+	tw_set_default_metadata(tarfn.c_str());
+#endif
+	return 0;
+}
+
+int twrpTar::removeEOT(string tarFile) {
+	char* charTarFile = (char*) tarFile.c_str();
+	off_t tarFileEnd = 0;
+	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 (tarFileEnd > 0 && 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..a486c41
--- /dev/null
+++ b/twrpTar.hpp
@@ -0,0 +1,102 @@
+/*
+        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, pid_t &fork_pid);
+	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);
+	static void Signal_Kill(int signum);
+
+	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;
+	unsigned thread_id;
+};
diff --git a/twrpTarMain/Android.mk b/twrpTarMain/Android.mk
new file mode 100644
index 0000000..6dd7d15
--- /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:= optional
+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:= optional
+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 1a0b079..ecedcfc 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"
@@ -173,9 +175,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 c7d4d37..bd769f9 100644
--- a/uncrypt/Android.mk
+++ b/uncrypt/Android.mk
@@ -16,8 +16,10 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := uncrypt.cpp
+LOCAL_C_INCLUDES += $(commands_recovery_local_path)
 
+LOCAL_SRC_FILES := uncrypt.cpp
+LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := uncrypt
 
 LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils
diff --git a/updater/Android.mk b/updater/Android.mk
index ff02a33..bc763a0 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -24,14 +24,29 @@
 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
+ifneq ($(wildcard external/lz4/Android.mk),)
+    LOCAL_STATIC_LIBRARIES += liblz4-static
+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 += libcutils liblog libstdc++ libc
 LOCAL_STATIC_LIBRARIES += libselinux
 tune2fs_static_libraries := \
@@ -41,7 +56,10 @@
  libext2_uuid_static \
  libext2_e2p \
  libext2fs
-LOCAL_STATIC_LIBRARIES += libtune2fs $(tune2fs_static_libraries)
+ifneq ($(wildcard external/e2fsprogs/misc/tune2fs.h),)
+    LOCAL_STATIC_LIBRARIES += libtune2fs $(tune2fs_static_libraries)
+    LOCAL_CFLAGS += -DHAVE_LIBTUNE2FS
+endif
 
 LOCAL_C_INCLUDES += external/e2fsprogs/misc
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
diff --git a/updater/install.c b/updater/install.c
index 01a5dd2..d0e7d1a 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -45,8 +45,12 @@
 #include "mtdutils/mounts.h"
 #include "mtdutils/mtdutils.h"
 #include "updater.h"
+#include "applypatch/applypatch.h"
+#include "flashutils/flashutils.h"
 #include "install.h"
+#ifdef HAVE_LIBTUNE2FS
 #include "tune2fs.h"
+#endif
 
 #ifdef USE_EXT4
 #include "make_ext4fs.h"
@@ -1062,66 +1066,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);
@@ -1541,6 +1493,7 @@
 }
 
 Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) {
+#ifdef HAVE_LIBTUNE2FS
     if (argc == 0) {
         return ErrorAbort(state, "%s() expects args, got %d", name, argc);
     }
@@ -1569,6 +1522,9 @@
         return ErrorAbort(state, "%s() returned error code %d", name, result);
     }
     return StringValue(strdup("t"));
+#else
+    return ErrorAbort(state, "%s() support not present, no libtune2fs", name);
+#endif // HAVE_LIBTUNE2FS
 }
 
 void RegisterInstallFunctions() {
diff --git a/updater/updater.c b/updater/updater.c
index 661f695..479675d 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 <string.h>
 
 #include "edify/expr.h"
@@ -34,6 +35,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;
 
@@ -96,6 +99,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();
@@ -114,11 +134,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..d5df8d4
--- /dev/null
+++ b/variables.h
@@ -0,0 +1,185 @@
+/*
+ * 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.7.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_CRYPTO_PWTYPE            "tw_crypto_pwtype"
+#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 61e5adf..98c7337 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -31,7 +31,9 @@
 #include <stdio.h>
 #include <string.h>
 
-extern RecoveryUI* ui;
+//extern RecoveryUI* ui;
+
+#define PUBLIC_KEYS_FILE "/res/keys"
 
 /*
  * Simple version of PKCS#7 SignedData extraction. This extracts the
@@ -111,10 +113,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:
     //
@@ -217,7 +225,7 @@
 
         double f = so_far / (double)signed_len;
         if (f > frac + 0.02 || size == so_far) {
-            ui->SetProgress(f);
+            //ui->SetProgress(f);
             frac = f;
         }
     }
@@ -288,6 +296,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");
diff --git a/verifier.h b/verifier.h
index 15f8d98..17ab257 100644
--- a/verifier.h
+++ b/verifier.h
@@ -20,6 +20,12 @@
 #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 {
     p256_int x;
     p256_int y;
@@ -42,8 +48,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 82546ed..0c4503f 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -23,7 +23,9 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
+/*
 #include "common.h"
+*/
 #include "verifier.h"
 #include "ui.h"
 #include "mincrypt/sha.h"
@@ -242,7 +244,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;